summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2018-05-17 19:53:06 +0200
committerJules Laplace <julescarbon@gmail.com>2018-05-17 19:53:06 +0200
commit99c9ec8e78803f6b8a4b06abe625ab29ba33cc43 (patch)
treec3df5488d345e8df8e142b5aca617bed736ab8fe /client
parentbe072fb320b6b6c27e79899be9778219c69ab561 (diff)
test
Diffstat (limited to 'client')
-rw-r--r--client/draw.js94
-rw-r--r--client/index.js248
-rw-r--r--client/lib/hall.js8
-rw-r--r--client/lib/spectrum.js34
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,