diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2018-03-08 02:54:23 +0100 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2018-03-08 02:54:23 +0100 |
| commit | 25033d7d9d393e444069ed82cfa0c914f9770f36 (patch) | |
| tree | f9daf6596ddb1cbb24ac19f58a1b35f79ce44981 /client | |
| parent | 735b1f59762023fa652fb6fe36bf8312a350300c (diff) | |
build
Diffstat (limited to 'client')
| -rw-r--r-- | client/draw.js | 112 | ||||
| -rw-r--r-- | client/index.js | 74 | ||||
| -rw-r--r-- | client/lib/spectrum.js | 89 |
3 files changed, 189 insertions, 86 deletions
diff --git a/client/draw.js b/client/draw.js index 3dab322..bed5f80 100644 --- a/client/draw.js +++ b/client/draw.js @@ -1,7 +1,3 @@ -import Tone from 'tone' - -import output from './lib/output' - import { browser, requestAudioContext, randint, randrange, clamp, mod, @@ -13,9 +9,14 @@ import color from './lib/color' let w, h let rx, ry +const pixels_per_second = 1024 + const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') +const scratch = document.createElement('canvas') +const scratchCtx = scratch.getContext('2d') + document.body.appendChild(canvas) document.body.addEventListener('resize', resize) resize() @@ -94,7 +95,6 @@ function waveform(pcm, sr, pos, zoom){ var x0 = 0 var y0 = 20 var ymid = y0 + half_height - var pixels_per_second = 1024 var max_width_in_seconds = width / pixels_per_second var max_width_in_samples = max_width_in_seconds * sr var pcm_length = pcm.length @@ -120,69 +120,22 @@ function waveform(pcm, sr, pos, zoom){ ctx.restore() } -const signalWindows = require('signal-windows').windows -const FFTJS = require('fft.js') -const fft_size = 2048 -const fft_overlap = fft_size / 4 -const half_fft_size = fft_size / 2 -const fft = new FFTJS(fft_size) +function spectrum(spec, x0, y0, ww, hh){ + const data = spec.data + const fft_size = spec.fft_size + const half_fft_size = spec.fft_size / 2 + const spec_len = data.length -function toSpectrum(pcm){ - const ham = signalWindows.construct('ham', fft_size) - const pcm_in = new Array(fft_size) - const pcm_length = pcm.length - const pcm_q_length = Math.ceil(pcm_length / fft_size) * fft_size - let i, j, fft_out, spec = []; - for (i = 0; i < pcm_q_length; i += fft_overlap) { - for (j = 0; j < fft_size; j++) { - pcm_in[j] = pcm[i+j] * ham[j] || 0 - } - fft_out = fft.createComplexArray() - fft.realTransform(fft_out, pcm_in) - fft.completeSpectrum(fft_out) - spec.push(fft_out) - } - return spec -} -function fromSpectrum(spec, sr){ - const spec_len = spec.length - const ham = signalWindows.construct('ham', fft_size) - const out = fft.createComplexArray() - const pcm_length = fft_overlap * spec_len - const audioBuffer = Tone.context.createBuffer(1, pcm_length, sr) - const pcm = audioBuffer.getChannelData(0); - let i, j, u, v, _r, _i, col - for (i = 0; i < spec_len; i++) { - col = spec[i] - 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 - } - } - const player = new Tone.Player(audioBuffer) - player.connect(output) - player.start(Tone.now() + pcm_length / sr) - // console.log(Tone.now() + pcm_length / sr) - return audioBuffer -} -function spectrum(pcm, sr){ - sr = sr || 44100 - const spec = toSpectrum(pcm) - - ctx.save() - - const scratch = document.createElement('canvas') - scratch.width = spec.length + scratch.width = data.length scratch.height = half_fft_size - const scratchCtx = scratch.getContext('2d') var imageData = ctx.createImageData(scratch.width, scratch.height) - var data = imageData.data + var pixels = imageData.data + + let i, j, u, v, _r, _i, col, hsl - let i, j, u, v, _r, _i, col, hsl, spec_len = spec.length for (i = 0; i < spec_len; i++) { - col = spec[i] + col = data[i] for (j = 0; j < half_fft_size; j++) { u = ((half_fft_size - j) * spec_len + i) * 4 @@ -191,36 +144,35 @@ function spectrum(pcm, sr){ _i = mod(col[v+1], Math.PI*2) / (Math.PI*2) hsl = color.hsl2rgb((_i + 1) / 2, 1.0, 1 - Math.abs(_r / 10)) // red - real part - // data[u] = _r * 127 + 127 + // pixels[u] = _r * 127 + 127 // // green - imag part - // data[u+1] = _i * 127 + 127 + // pixels[u+1] = _i * 127 + 127 // // blue - magnitude - // data[u+2] = Math.sqrt(Math.pow(_r, 2) + Math.pow(_i, 2)) * 128 + 127 - // data[u+3] = 255 - data[u] = hsl[0] - data[u+1] = hsl[1] - data[u+2] = hsl[2] - data[u+3] = 255 + // pixels[u+2] = Math.sqrt(Math.pow(_r, 2) + Math.pow(_i, 2)) * 128 + 127 + // pixels[u+3] = 255 + pixels[u] = hsl[0] + pixels[u+1] = hsl[1] + pixels[u+2] = hsl[2] + pixels[u+3] = 255 } } scratchCtx.putImageData(imageData, 0, 0) - var pcm_length = pcm.length - var pixels_per_second = 1024 + var pcm_length = spec.fft_overlap * spec_len + + x0 = x0 || 0 + y0 = y0 || Math.floor(h/4) + ww = ww || window.innerWidth + hh = hh || h/4 - const width = Math.round(pcm_length / sr * pixels_per_second) // ok not really this - const height = Math.floor(h*2/3) + const width = Math.round(pcm_length / spec.sr * pixels_per_second) + const height = Math.floor(hh) - const x0 = 0 - const y0 = Math.floor(h/4) + 20 + ctx.save() clear(1, x0, y0, w, height) ctx.drawImage(scratch, x0, y0, width, height) - ctx.restore() - - const new_pcm = fromSpectrum(spec, sr) - console.log(new_pcm) } export default { diff --git a/client/index.js b/client/index.js index 7a01b55..2b09f5f 100644 --- a/client/index.js +++ b/client/index.js @@ -7,6 +7,7 @@ import keys from './lib/keys' import color from './lib/color' import mouse from './lib/mouse' import output from './lib/output' +import spectrum from './lib/spectrum' import { Hall } from './lib/hall' @@ -34,9 +35,11 @@ let samplers = {} let sampler requestAudioContext( () => { - samplers.smash = new Sampler('samples/smash/g{}.mp3', 12) + // samplers.smash = new Sampler('samples/smash/g{}.mp3', 12) + samplers.earth = new Sampler('samples/earth/earth{}.wav', 20) // samplers.glass = new Sampler('samples/glass/0{}Particle.mp3', 20) // samplers.kalimba = new Sampler('samples/kalimba/380731__cabled-mess__sansula-08-c-raw.wav', 10) + sampler = samplers.earth samplers.choice = (m,n) => { const r = Math.random() if (r < m) return samplers.smash @@ -45,7 +48,7 @@ requestAudioContext( () => { } Tone.Buffer.on('load', function(){ console.log('all buffers are loaded.') - redraw() + // redraw() }) }) @@ -59,19 +62,78 @@ function redraw(){ draw.clear() } +function manipulate(spec){ + const data = spec.data + const sr = spec.sr + const fft_size = spec.fft_size + const fft_overlap = spec.fft_overlap + const spec_len = data.length + + let i, j, u, v, _r, _i + + let aa = [] + for (i = 0; i < fft_size; i++) { + aa[i] = i + } + shuffle(aa) + + let new_data = [], new_col, col + let bands = 2 << 4 + let band, band_index + let band_size = Math.floor(fft_size / bands) + for (i = 0; i < spec_len; i++) { + col = data[i] + new_col = new_data[i] = data[i].concat() + data[i][2] = 0 + for (j = 0; j < fft_size; j++) { + band = Math.floor(j / band_size) * band_size + band_index = j % band_size + + new_col[j] = col[ band + (band_size - band_index) ] + + // spectrum inversion + // new_col[j] = data[i][ fft_size - j - 1] + + // erase mirrored half of fft + new_col[j + fft_size] = 0 + } + new_col[2] = 0 + } + + spec.data = new_data //.reverse() + // col = data[i] + // for (j = 0; j < fft_size; j++) { + // _r = j*2 + // _i = j*2+1 + // col[_r] = col[_r] / 2 + // col[_i] = col[_i] + // } + // } +} + keys.listen(index => { // trigger(Math.random(), ((index+7) % SPEAKER_COUNT) / SPEAKER_COUNT, 0, samplers.smash) - const sample = samplers.smash.play(100, Tone.now(), output) - console.log(Tone.now()) + const sample = sampler.play(100, Tone.now(), output) const buf = sample._buffer.get() if (! buf) return const pcm = buf.getChannelData(0) const sr = buf.sampleRate const duration = buf.duration - console.log(duration.toFixed(2) + " s.") + const spec = spectrum.toSpectrum(pcm, sr) + draw.clear() draw.waveform(pcm) - draw.spectrum(pcm) + draw.spectrum(spec, 0, window.innerHeight/4 + 20) + + manipulate(spec) + + const audioBuffer = spectrum.fromSpectrum(spec) + const player = new Tone.Player(audioBuffer) + player.connect(output) + player.start(Tone.now() + pcm.length / sr) + + // const new_spec = spectrum.toSpectrum(audioBuffer.getChannelData(0), sr) + draw.spectrum(spec, 0, window.innerHeight * 1/2 + 40) }) mouse.register({ diff --git a/client/lib/spectrum.js b/client/lib/spectrum.js new file mode 100644 index 0000000..2b30792 --- /dev/null +++ b/client/lib/spectrum.js @@ -0,0 +1,89 @@ +import Tone from 'tone' + +import output from './output' + +const signalWindows = require('signal-windows').windows +const FFTJS = require('fft.js') + +const fft_size = 2 << 10 +const fft_overlap = fft_size / 4 +const half_fft_size = fft_size / 2 + +const fft = new FFTJS(fft_size) + +function toSpectrum(pcm, sr){ + sr = sr || 44100 + const ham = signalWindows.construct('ham', fft_size) + const pcm_in = new Array(fft_size) + const pcm_length = pcm.length + const pcm_q_length = Math.ceil(pcm_length / fft_size) * fft_size + let i, j, fft_out, data = []; + for (i = -fft_size; i < pcm_q_length; i += fft_overlap) { + for (j = 0; j < fft_size; j++) { + pcm_in[j] = pcm[i+j] * ham[j] || 0 + } + fft_out = fft.createComplexArray() + fft.realTransform(fft_out, pcm_in) + fft.completeSpectrum(fft_out) + data.push(fft_out) + } + return { + data, + sr, + fft_size, + fft_overlap, + } +} + +function fromSpectrum(spec){ + const data = spec.data + const sr = spec.sr + const fft_size = spec.fft_size + const fft_overlap = spec.fft_overlap + const spec_len = data.length + + const ham = signalWindows.construct('ham', fft_size) + const out = fft.createComplexArray() + const pcm_length = fft_overlap * spec_len + + const audioBuffer = Tone.context.createBuffer(1, pcm_length, sr) + const pcm = audioBuffer.getChannelData(0); + + let i, j, u, v, _r, _i, col + + for (i = 0; i < spec_len; i++) { + col = data[i] + 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 + } + } + + fadeInOut(pcm, fft_size) + + return audioBuffer +} + +function fadeInOut(pcm, fade_size){ + const pcm_length = pcm.length + let fade = 0, i + for (i = 0; i < fade_size; i++) { + fade = i / (fade_size) + fade *= fade + pcm[i] *= fade + pcm[pcm_length - i] *= fade + } +} +function fadeOut(pcm){ + +} + +export default { + toSpectrum, + fromSpectrum +} + |
