summaryrefslogtreecommitdiff
path: root/client/lib/spectrum.js
diff options
context:
space:
mode:
Diffstat (limited to 'client/lib/spectrum.js')
-rw-r--r--client/lib/spectrum.js184
1 files changed, 171 insertions, 13 deletions
diff --git a/client/lib/spectrum.js b/client/lib/spectrum.js
index 2b30792..1dd9619 100644
--- a/client/lib/spectrum.js
+++ b/client/lib/spectrum.js
@@ -1,13 +1,12 @@
import Tone from 'tone'
-import output from './output'
+import { shuffle, quantize, mod } from './util'
-const signalWindows = require('signal-windows').windows
-const FFTJS = require('fft.js')
+import { windows as signalWindows } from 'signal-windows'
+import FFTJS from 'fft.js'
-const fft_size = 2 << 10
+const fft_size = 1024
const fft_overlap = fft_size / 4
-const half_fft_size = fft_size / 2
const fft = new FFTJS(fft_size)
@@ -49,15 +48,15 @@ function fromSpectrum(spec){
const audioBuffer = Tone.context.createBuffer(1, pcm_length, sr)
const pcm = audioBuffer.getChannelData(0);
- let i, j, u, v, _r, _i, col
+ let i, j, u, col
for (i = 0; i < spec_len; i++) {
col = data[i]
+ // for (j = fft_size; j < fft_size << 1; j++) {
+ // col[j] = 0
+ // }
fft.inverseTransform(out, col)
u = i * (fft_overlap)
- // for (j = fft_size * 1/4; j < fft_size * 3/4; j++) {
- // pcm[u+j] = out[j*2] / ham[j] || 0
- // }
for (j = 0; j < fft_size; j++) {
pcm[u+j] += out[j*2] * ham[j] || 0
}
@@ -78,12 +77,171 @@ function fadeInOut(pcm, fade_size){
pcm[pcm_length - i] *= fade
}
}
-function fadeOut(pcm){
-
+function rotatePhase(spec, theta){
+ let { data, fft_size } = spec
+ let i, j, col, len = data.length
+ for (i = 0; i < len; i++) {
+ col = data[i]
+ for (j = 0; j < fft_size; j++) {
+ col[j*2+1] += theta
+ }
+ }
+ return spec
+}
+
+function linearBins(spec, n){
+ n = n || 1
+
+ let bins = [], i, q_i
+ for (q_i = 0; q_i < n; q_i++) {
+ bins[q_i] = []
+ }
+ const step = Math.floor(spec.fft_size / n)
+ const len_quantize_n = quantize(spec.fft_size, n)
+ for (i = 0; i < len_quantize_n; i++) {
+ q_i = Math.floor(i/step)
+ bins[q_i] = bins[q_i] || []
+ bins[q_i].push(i)
+ }
+ // leftover bins get put at end
+ for (; i < spec.fft_size; i++) {
+ bins[q_i].push(i)
+ }
+ return bins
+}
+function logarithmicBins(spec){
+ let bins = [], i, j, q_i
+ let binCount = Math.log2(spec.fft_size) - 1
+ for (i = 0, q_i = 0, j = 0; i < binCount; i++) {
+ j += 1 << i
+ bins[i] = []
+ for (; q_i < j; q_i++) {
+ bins[i].push(q_i)
+ }
+ }
+ return bins
+}
+function concatBins(bins){
+ return bins.reduce((acc, cv) => acc.concat(cv), [])
+}
+function reverseBins(bins){
+ return bins.map( bin => bin.reverse() )
+}
+function minBins(bins){
+ return bins.map( bin => {
+ const b = bin[0]
+ return bin.map(() => b)
+ })
+}
+function maxBins(bins){
+ return bins.map( bin => {
+ const b = bin[bin.length-1]
+ return bin.map(() => b)
+ })
+}
+function rotateSpectrum(spec, n){
+ const { fft_size } = spec
+ if (n && n < 1) {
+ n -= 0.5
+ n *= fft_size
+ }
+ n = Math.floor(n)
+ let order = new Array(fft_size), i
+ for (i = 0; i < fft_size; i++) {
+ order[i] = mod(i + n, fft_size/2)
+ }
+ return reorderBins(spec, order)
+}
+function cloneSpectrum(spec){
+ const {
+ data,
+ fft_size,
+ sr, fft_overlap
+ } = spec
+ const spec_len = data.length
+
+ let new_data = new Array(spec_len)
+ let i
+ for (i = 0; i < spec_len; i++) {
+ new_data[i] = data[i].concat()
+ new_data[i][2] = 0
+ }
+
+ return {
+ data: new_data,
+ fft_size,
+ sr, fft_overlap,
+ }
+}
+function reverseSpectrum(spec){
+ let new_spec = cloneSpectrum(spec)
+ new_spec.data = new_spec.data.reverse()
+ return new_spec
+}
+function shuffleSpectrum(spec){
+ const { fft_size } = spec
+ let order = new Array(fft_size), i
+ for (i = 0; i < fft_size; i++) {
+ order[i] = i
+ }
+ shuffle(order)
+ return reorderBins(spec, order)
+}
+function invertSpectrum(spec){
+ const { fft_size } = spec
+ let order = new Array(fft_size), i
+ for (i = 0; i < fft_size; i++) {
+ order[i] = fft_size - i - 1
+ }
+ return reorderBins(spec, order)
+}
+function reorderBins(spec, order){
+ let new_spec = cloneSpectrum(spec)
+ const {
+ data,
+ sr,
+ fft_size,
+ fft_overlap,
+ } = spec
+ const spec_len = data.length
+ const { data: new_data } = new_spec
+
+ let i, j, col, new_col
+ for (j = order.length; j < fft_size; j++) {
+ order[j] = j
+ }
+
+ for (i = 0; i < spec_len; i++) {
+ col = data[i]
+ new_col = new_data[i] = data[i].concat()
+ col[0] = 0
+ // col[2] = 0
+ // col[4] = 0
+ for (j = 0; j < fft_size/2; j++) {
+ new_col[j*2] = col[order[j]*2]
+ new_col[j*2+1] = col[order[j]*2+1]
+ }
+ for (; j < fft_size; j++) {
+ new_col[j*2] = 0
+ new_col[j*2+1] = 0
+ }
+ }
+
+ return {
+ data: new_data,
+ sr, fft_size, fft_overlap,
+ }
}
export default {
- toSpectrum,
- fromSpectrum
+ toSpectrum, fromSpectrum,
+ fadeInOut,
+ cloneSpectrum,
+ reverseSpectrum, shuffleSpectrum, invertSpectrum, rotateSpectrum,
+ reorderBins,
+ linearBins, logarithmicBins,
+ concatBins,
+ reverseBins, minBins, maxBins,
+ rotatePhase,
}