diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2018-05-17 19:53:06 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2018-05-17 19:53:06 +0200 |
| commit | 99c9ec8e78803f6b8a4b06abe625ab29ba33cc43 (patch) | |
| tree | c3df5488d345e8df8e142b5aca617bed736ab8fe /client | |
| parent | be072fb320b6b6c27e79899be9778219c69ab561 (diff) | |
test
Diffstat (limited to 'client')
| -rw-r--r-- | client/draw.js | 94 | ||||
| -rw-r--r-- | client/index.js | 248 | ||||
| -rw-r--r-- | client/lib/hall.js | 8 | ||||
| -rw-r--r-- | client/lib/spectrum.js | 34 |
4 files changed, 232 insertions, 152 deletions
diff --git a/client/draw.js b/client/draw.js index 3aff6a4..a732745 100644 --- a/client/draw.js +++ b/client/draw.js @@ -14,8 +14,8 @@ let rx, ry const pixels_per_second = 512 // 1024 const canvas = document.createElement('canvas') -document.body.appendChild(canvas) -document.body.addEventListener('resize', resize) +// document.body.appendChild(canvas) +// document.body.addEventListener('resize', resize) resize() recenter() requestAnimationFrame(animate) @@ -26,11 +26,11 @@ const ctx = canvas.getContext('2d') const scratch = document.createElement('canvas') const scratchCtx = scratch.getContext('2d-lodpi') -function resize(){ - w = canvas.width = window.innerWidth - h = canvas.height = window.innerHeight - canvas.style.width = window.innerWidth + 'px' - canvas.style.height = window.innerHeight + 'px' +function resize(ww, hh){ + w = canvas.width = ww || window.innerWidth + h = canvas.height = hh || window.innerHeight + canvas.style.width = w + 'px' + canvas.style.height = h + 'px' } function recenter(){ rx = randint(w), ry = randint(h) @@ -186,9 +186,85 @@ function spectrum(spec, x0, y0, ww, hh){ ctx.drawImage(scratch, x0, y0, width, height) ctx.restore() } +function raw_spectrum(spec, x0, y0, ww, hh, def_min_r, def_min_i){ + const data = spec.data + const fft_size = spec.fft_size + const half_fft_size = spec.fft_size / 2 + const spec_len = data.length + + const _scratch = document.createElement('canvas') + const _scratchCtx = _scratch.getContext('2d-lodpi') + _scratch.width = data.length + _scratch.height = half_fft_size + // console.log("spectrum w/h:", _scratch.width, _scratch.height) + + var imageData = _scratchCtx.createImageData(_scratch.width, _scratch.height) + var pixels = imageData.data + + let i, j, u, v, _r, _i, col, hsl + // let min_r = Infinity, max_r = -Infinity + // let min_i = Infinity, max_i = -Infinity + + // determined empirically.. + // let min_r = -60.4894057005308 + // let max_r = 107.23800966675353 + // let min_i = -59.4894057005308 + // let max_i = 108.23800966675353 + let min_r = -def_min_r + let max_r = def_min_r + let min_i = -def_min_i + let max_i = def_min_i + let delta_r = max_r - min_r + let delta_i = max_i - min_i + let mean_r = 0 + let mean_i = 0 + let sum_mean_r = 0, sum_mean_i = 0 + let real, imag + + for (i = 0; i < spec_len; i++) { + col = data[i] + mean_r = 0 + mean_i = 0 + + for (j = 0; j < half_fft_size; j++) { + u = (j * spec_len + i) * 4 + v = j * 2 + real = col[v] + imag = col[v+1] + mean_r += real + mean_i += imag + _r = clamp((real - min_r) / delta_r * 255, 0, 255) + _i = clamp((imag - min_i) / delta_i * 255, 0, 255) + + // hsl = color.hsl2rgb((_i + 1) / 2, 1.0, 1 - Math.abs(_r / 10)) + pixels[u+0] = _r + pixels[u+1] = _i + pixels[u+2] = 0 // hsl[2] + pixels[u+3] = 255 + + // min_r = Math.min(min_r, col[v]) + // max_r = Math.max(max_r, col[v]) + // min_i = Math.min(min_i, col[v]+1) + // max_i = Math.max(max_i, col[v]+1) + } + mean_r /= half_fft_size + mean_i /= half_fft_size + sum_mean_r += mean_r + sum_mean_i += mean_i + } + + sum_mean_r /= spec_len + sum_mean_i /= spec_len + // console.log(sum_mean_r, sum_mean_i) + // console.log("r:", min_r, max_r) + // console.log("i:", min_i, max_i) + _scratchCtx.putImageData(imageData, 0, 0) + + return { canvas: _scratch, imageData } +} export default { - canvas, ctx, onFrame, + canvas, ctx, onFrame, resize, triangle, clear, line, dot, - waveform, spectrum + waveform, spectrum, raw_spectrum, }
\ No newline at end of file diff --git a/client/index.js b/client/index.js index c4fc67d..18e94da 100644 --- a/client/index.js +++ b/client/index.js @@ -1,163 +1,131 @@ import Tone from 'tone' - -import Sampler from './lib/sampler' - import draw from './draw' import keys from './lib/keys' import mouse from './lib/mouse' import output from './lib/output' import spectrum from './lib/spectrum' -import { Hall } from './lib/hall' - import { requestAudioContext, - lerp, + lerp, clamp, mod, } from './lib/util' -// const root = 440 - -const HALLWAY_LENGTH = 147 -// const SPEAKER_COUNT = 16 - -// let notes = [299, 336, 374, 399, 449, 498, 561, 598].map(i => i/2) -// notes = notes.concat(notes.map(i => i/2)) - -let samplers = {} -let sampler +let selfDrag = false +let buffer +let players = [] +let gallery +let sr = 44100 +let last_i = 0 +let _r = 8, _i = 8 +function init(){ + requestAudioContext(ready) + document.body.addEventListener('dragover', dragover) + document.body.addEventListener('dragstart', dragstart) + document.body.addEventListener('drop', drop) + document.querySelector("#upload").addEventListener('change', handleFileSelect) + // draw.onFrame(() => {}) + draw.resize(256, 256) -requestAudioContext( () => { - // sampler = samplers.misc = new Sampler('samples/misc/glass.mp3', 2) - sampler = samplers.smash = new Sampler('samples/smash/g{}.mp3', 12) - // sampler = samplers.earth = new Sampler('samples/earth/earth{}.mp3', 20) - // sampler = samplers.glass = new Sampler('samples/glass/0{}Particle.mp3', 20) - // sampler = samplers.bubbles = new Sampler('samples/bubbles/bubbles{}.mp3', 10) - // sampler = samplers.kalimba = new Sampler('samples/kalimba/380731__cabled-mess__sansula-08-c-raw.wav', 10) - - samplers.choice = (m,n) => { - const r = Math.random() - if (r < m) return samplers.smash - if (r < m+n) return samplers.kalimba - return samplers.glass - } - Tone.Buffer.on('load', function(){ - console.log('all buffers are loaded.') - // redraw() + gallery = document.querySelector('#gallery') + mouse.register({ + move: (x, y) => { + } }) -}) - -// const hall = new Hall ({ -// length: HALLWAY_LENGTH, -// speakers: SPEAKER_COUNT, -// }) + keys.listen((z) => { + // console.log(z) + play(mod(z, players.length)) + }) +} +function ready(){ +} +function dragover (e) { + e.preventDefault() +} +function dragstart (e) { + selfDrag = true +} +function drop (e) { + e.stopPropagation() + e.preventDefault() -function redraw(){ - draw.clear() + if (e.dataTransfer && ! selfDrag) { + if (e.dataTransfer.files.length) { + handleFileSelect(e) + } + } + else { + handleFileSelect(e) + } + selfDrag = false } +function handleFileSelect(e){ + var files = e.dataTransfer ? e.dataTransfer.files : e.target.files + var file = files[0] + // console.log(files, file) + if (! file) return + load(file) +} +function load(file){ + players = [] + buffer = new Tone.Buffer(URL.createObjectURL(file), loadBuffer, (err) => console.error('err', err)) +} +function loadBuffer(){ + console.log('loaded buffer', buffer) + const pcm = buffer._buffer.getChannelData(0) + sr = buffer._buffer.sampleRate + if (! pcm) return -let last_lin_bins, last_sam -let timeout -function manipulate(x, y, pcm, spec){ - if (timeout) return null - timeout = setTimeout( () => { timeout = null }, 20 ) + const FRAME_LENGTH = 126 * 255 + const FRAME_OFFSET = FRAME_LENGTH - // reverseSpectrum, - // shuffleSpectrum, - // invertSpectrum + for (var offset = 0, count = 0, _len = pcm.length; offset < _len; offset += FRAME_OFFSET, count += 1) { + // console.log('generating', count, offset) + let player = render(pcm.slice(offset, offset+FRAME_LENGTH)) + register(player, count) + if (count > 20) break + // break + } + play(0) +} +function render(pcm){ + const fft = spectrum.toSpectrum(pcm, sr) + // console.log('render', fft) // const pcm_rev = pcm.slice().reverse() // const spec_rev = spectrum.toSpectrum(pcm_rev, spec.sr) - // return spec_rev - // return spectrum.reverseSpectrum(spec) - // const log_bins = spectrum.logarithmicBins(spec) - - // let lin_bins = (x * x * x * spec.fft_size/6)|0 - // let sam = (y * sampler.length)|0 + 1 - // if (lin_bins == last_lin_bins && sam == last_sam) return null - // last_lin_bins = lin_bins - // last_sam = sam - // const log_bins = spectrum.linearBins(spec, lin_bins) - // const new_bins = spectrum.reverseBins(log_bins) - // const concat_bins = spectrum.concatBins(new_bins) - // const new_spec = spectrum.reorderBins(spec, concat_bins) - // console.log(spec, new_spec) - - // let new_spec = spectrum.cloneSpectrum(spec) - // new_spec = spectrum.rotatePhase(new_spec, x * Math.PI) - - let new_spec = spectrum.rotateSpectrum(spec, (y + 0.5)%1) - - return new_spec + // const { buf, pcm } = spectrum.fromSpectrum(fft) + draw.clear() + const { canvas, imageData } = draw.raw_spectrum(fft, 0, 256, 0, 256, _r, _i) + const dataURL = canvas.toDataURL("image/png") + return { fft, canvas, imageData, dataURL } } +function play(i) { + // console.log('play', i) + last_i = i + let player = players[clamp(i, 0, players.length)] + // const { canvas, imageData } = draw.raw_spectrum(fft, 0, 256, 0, 256, 1, 1) + // console.log(_r, _i) + const { canvas, imageData } = draw.raw_spectrum(player.fft, 0, 256, 0, 256, _r, _i) + const new_fft = spectrum.fromImageData(player.imageData, 44100, _r, _i) + gallery.innerHTML = '' + gallery.appendChild(canvas) -keys.listen(i => { - trigger(xx + i/50, yy, 0, sampler) -}) - -let xx = 0.5, yy = 0.5 -mouse.register({ - down: (x, y) => { - redraw() - clearTimeout(timeout) - timeout = null - trigger(x, y, 0, sampler) - }, - move: (x, y) => { - xx = x - yy = y - }, - drag: (x, y) => { - trigger(x, y, 0, sampler) - }, - // up: (x, y) => { - // }, -}) - -function trigger(x, y, t, source){ - x = x || 0 - y = y || 0 - t = t || 0 - t += Tone.now() - source = source || sampler - // source = source || last_dist > 40 - // ? samplers.choice(0.2, 0.2) - // : samplers.choice((1-y) * 0.2, y*0.02) - // const freq = notes[Math.floor(x * notes.length)] - // const { speaker, player } = hall.play(source, y, freq, x, t) - - const { pcm, spec } = source.getWaveAndSpectrum(x) - if (! pcm) return - - const new_spec = manipulate(x, y, pcm, spec) - if (! new_spec) return - - const audioBuffer = spectrum.fromSpectrum(new_spec) - const player = new Tone.Player(audioBuffer) - player.connect(output) - player.start(Tone.now()) - - draw.onFrame(() => { - // INIT DRAWING PHASE - draw.clear() - - // DRAW INDIVIDUAL UI ELEMENTS - // draw.waveform(pcm) - // draw.spectrum(spec, 0, window.innerHeight/4 + 20) - - draw.waveform(audioBuffer.getChannelData(0)) - - // DRAW SPECTRUM - // const new_spec = spectrum.toSpectrum(audioBuffer.getChannelData(0), sr) - // draw.spectrum(new_spec, 0, window.innerHeight * 1/2 + 40) - draw.spectrum(new_spec, 0, window.innerHeight * 1/4 + 20, 0, window.innerHeight * 1/2) - - // DRAW FLAIR - draw.triangle( - lerp(x, 0, 1) * window.innerWidth, - lerp(y, 0, 1) * window.innerHeight - 20, - 40 - ) + // console.log(player.fft.data, new_fft.data) + const buf = spectrum.fromSpectrum(new_fft) + const _p = new Tone.Player(buf) + _p.connect(output) + _p.start(Tone.now()) + redraw(new_fft) +} +function redraw(new_fft){ + const { canvas, imageData } = draw.raw_spectrum(new_fft, 0, 256, 0, 256, _r, _i) + gallery.appendChild(canvas) +} +function register(player, i){ + // console.log('register', player) + players.push(player) + player.canvas.addEventListener('click', () => { + play(i) }) + // gallery.appendChild(player.canvas) } - - - +init() diff --git a/client/lib/hall.js b/client/lib/hall.js index 3fe390f..4126ae3 100644 --- a/client/lib/hall.js +++ b/client/lib/hall.js @@ -2,6 +2,14 @@ import Tone from 'tone' import output from './output' import { clamp, choice } from './util' +// const HALLWAY_LENGTH = 147 +// const SPEAKER_COUNT = 16 + +// const hall = new Hall ({ +// length: HALLWAY_LENGTH, +// speakers: SPEAKER_COUNT, +// }) + const SPEED_OF_SOUND = 0.0029154519 // ms/m const reverb = new Tone.Freeverb({ diff --git a/client/lib/spectrum.js b/client/lib/spectrum.js index c4e8c1e..59869c7 100644 --- a/client/lib/spectrum.js +++ b/client/lib/spectrum.js @@ -5,7 +5,7 @@ import { shuffle, quantize, mod } from './util' import { windows as signalWindows } from 'signal-windows' import FFTJS from 'fft.js' -const fft_size = 2048 +const fft_size = 512 const fft_overlap = fft_size / 4 const fft = new FFTJS(fft_size) @@ -55,6 +55,7 @@ function fromSpectrum(spec){ // for (j = fft_size; j < fft_size << 1; j++) { // col[j] = 0 // } + // if (i == 0) console.log(col) fft.inverseTransform(out, col) u = i * (fft_overlap) for (j = 0; j < fft_size; j++) { @@ -63,10 +64,37 @@ function fromSpectrum(spec){ } fadeInOut(pcm, fft_size) - + // console.log(pcm) return audioBuffer } +function fromImageData(imageData, sr, _r, _i) { + const pixels = imageData.data + const w = imageData.width + const h = imageData.height + let data = new Array(w) + let x, y, u, v, v2 + for (y = 0; y < h; y++) { + let col = data[y] = new Float32Array(h * 4) + for (x = 0; x < w; x++) { + u = (x * (w) + y) * 4 + v = x * 2 + col[v] = (pixels[u] / 255 - 0.5) * _r + col[v+1] = (pixels[u+1] / 255 - 0.5) * _i + v2 = (h-y + h) * 2 + col[v2] = col[v] + col[v2+1] = 0 // col[v+1] + } + col[h*2] = col[h*2+1] = col[h*2-1] = col[h*2-2] = 0 + } + const spec = { + data, + sr, + fft_size, fft_overlap + } + return spec +} + function binToHz(spec, i){ return (i / spec.fft_size) * spec.sr } @@ -238,7 +266,7 @@ function reorderBins(spec, order){ } export default { - toSpectrum, fromSpectrum, binToHz, + toSpectrum, fromSpectrum, fromImageData, binToHz, fadeInOut, cloneSpectrum, reverseSpectrum, shuffleSpectrum, invertSpectrum, rotateSpectrum, |
