diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2018-08-13 15:37:08 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2018-08-13 15:37:08 +0200 |
| commit | c23cc77ef96cb66ff00ebf92322fd268ba9a4d7f (patch) | |
| tree | 70aff23cc68c8a075556868c7b88373757156cde /client/index.js | |
| parent | 86213c887b101044ae2f3ea393fee927842dbde2 (diff) | |
new ui...
Diffstat (limited to 'client/index.js')
| -rw-r--r-- | client/index.js | 212 |
1 files changed, 193 insertions, 19 deletions
diff --git a/client/index.js b/client/index.js index f9a4902..29ec55b 100644 --- a/client/index.js +++ b/client/index.js @@ -1,6 +1,6 @@ import Tone from 'tone' import WebMidi from 'webmidi' -import 'nexusui' +import Nexus from 'nexusui' import keys from './lib/keys' import kalimba from './lib/kalimba' import scales from './lib/scales' @@ -8,9 +8,28 @@ import { mod, browser, requestAudioContext, ftom, mtof, tap } from './lib/util' import * as data from './data' -const nx = window.nx +const DEFAULT_BPM = 60 + +const nx = window.nx = {} let midi +const note_values = [ + [8, '8 measures'], + [4, '4 measures'], + [2, '2 measures'], + [1, 'whole note'], + [1/2, 'half note'], + [1/3, 'third note'], + [1/4, 'quarter note'], + [1/5, 'fifth note'], + [1/6, 'sixth note'], + [1/8, 'eighth note'], + [1/10, 'tenth note'], + [1/12, 'twelfth note'], + [1/16, 'sixteenth note'], + [1/32, 'thirtysecond note'], +] + WebMidi.enable(midi_ready) @@ -21,14 +40,106 @@ function midi_ready(err) { } if (!WebMidi.outputs.length) { console.error('no MIDI output found') + return } console.log(WebMidi.inputs) console.log(WebMidi.outputs) - midi = WebMidi.outputs[0] + if (WebMidi.outputs.length > 1) { + const filtered = WebMidi.outputs.filter(output => output.name.match(/prodipe/i)) + if (filtered.length) { + midi = filtered[0] + } + } else { + midi = WebMidi.outputs[0] + } + console.log(midi.name) +} +let i = 0 +data.load().then(lists => { + // nx.dataset.choices = Object.keys(lists) + // console.log(lists) + const list = lists.household_wealth + document.querySelector('#dataset_name').innerHTML = list.name.replace(/-/g, ' ') + // playSequence(list) + playIntervalSequence(list) +}) + +function playSequence(list){ + let { rows, min, max } = get_bounds(list) + let count = rows.length * rows[0].length + playNext() + function playNext() { + let note_time = 30000 / Tone.Transport.bpm.value + setTimeout(playNext, note_time) + const y = Math.floor(i / rows[0].length) + const x = i % rows[0].length + if (!x) console.log(y) + const n = rows[y][x] + i += 1 + if (i >= count) i = 0; + play( norm(n, min, max) * nx.multiply.value, note_time * nx.duration.value) + } +} + +function playIntervalSequence(list){ + let { rows, min, max } = get_bounds(list) + let diff = get_diff_bounds(rows) + let count = rows.length + playNext() + function playNext() { + let note_time = 120000 / Tone.Transport.bpm.value * note_values[nx.timing.active][0] + setTimeout(playNext, note_time) + const y = i % count + const row = rows[y] + const row_min = Math.min.apply(Math, row) + const row_max = Math.max.apply(Math, row) + const row_f0 = norm(row_min, min, max) + const row_root = row_f0 * nx.multiply.value + rows[y].forEach(n => { + const note = row_root + norm(n - row_min, diff.min, diff.max) * nx.interval.value + play(note, note_time * nx.duration.value) + }) + i += 1 + if (i > count) i = 0; + } } -function play(index){ - const freq = scales.current().index(index) +// function pick_dataset(name){ +// i = 0 +// // dataset +// } +function norm(n, min, max){ + return (n - min) / (max - min) +} +function get_diff_bounds(rows){ + const diffs = rows.map(row => { + const row_min = Math.min.apply(Math, row) + const row_max = Math.max.apply(Math, row) + return row_max - row_min + }) + const min = Math.min.apply(Math, diffs) + const max = Math.max.apply(Math, diffs) + return { min, max } +} +function get_bounds(dataset){ + let rows = dataset.lines + rows.forEach(row => row.shift()) + rows = rows.map(a => a.map(n => parseFloat(n))) + const max = rows.reduce((a,b) => { + return b.reduce((z,bb) => { + return Math.max(z, bb) + }, a) + }, -Infinity) + const min = rows.reduce((a,b) => { + return b.reduce((z,bb) => { + return Math.min(z, bb) + }, a) + }, Infinity) + return { rows, max, min } +} +function play(index, duration){ + // console.log(index) + const freq = scales.current().index(index + Math.round(nx.offset.value)) let midi_note = ftom(freq) let cents = midi_note % 1 if (cents > 0.5) { @@ -39,30 +150,93 @@ function play(index){ midi_note = Math.floor(midi_note) // console.log(freq, midi_note, cents.foFixed(1)) if (midi) { - midi.playNote(Tone.Frequency(Math.floor(midi_note), "midi").toNote(), "all", { duration: 90000 / Tone.Transport.bpm.value }) + duration = duration || 60000 / Tone.Transport.bpm.value + midi.playNote(Tone.Frequency(Math.floor(midi_note), "midi").toNote(), "all", { duration }) // cents - midi.sendPitchBend(cents, "all") + // midi.sendPitchBend(cents, "all") } else { kalimba.play(freq) } } -nx.onload = () => requestAudioContext(ready) +requestAudioContext(ready) function ready () { - nx.widgets.scale.choices = scales.names() - nx.widgets.scale.init() - nx.widgets.scale.on('*', e => scales.pick(e.value)) + scales.build_options(document.querySelector('#scale')) + // nx.colorize('#f4d142') + + Tone.Transport.bpm.value = DEFAULT_BPM + nx.tempo = new Nexus.Dial('#tempo', { + min: 10, + max: 300, + step: 1, + value: DEFAULT_BPM, + }) + update_value_on_change(nx.tempo, '#tempo', true, v => Tone.Transport.bpm.value = v) + + nx.timing = new Nexus.RadioButton('#timing', { + size: [400,25], + numberOfButtons: note_values.length, + active: 6, + }) + update_radio_value_on_change(nx.timing, '#timing', note_values) + + nx.duration = new Nexus.Dial('#duration', { + min: 0, + max: 2, + step: 0.01, + value: 0.8, + }) + update_value_on_change(nx.duration, '#duration', false) + + nx.offset = new Nexus.Dial('#offset', { + min: -24, + max: 24, + step: 1, + value: 0, + }) + update_value_on_change(nx.offset, '#offset', true) - // nx.widgets.shiftUp.on('*', tap(shiftUp)) + nx.multiply = new Nexus.Dial('#multiply', { + min: -64, + max: 64, + step: 1, + value: 7, + }) + update_value_on_change(nx.multiply, '#multiply', true) - nx.colorize('#f4d142') + nx.interval = new Nexus.Dial('#interval', { + min: -64, + max: 64, + step: 1, + value: 10, + }) + update_value_on_change(nx.interval, '#interval', true) - Tone.Transport.bpm.value = 108 - nx.widgets.tempo.min = 10 - nx.widgets.tempo.max = 1000 - nx.widgets.tempo.set({ value: 108 }) - nx.widgets.tempo.on('*', () => Tone.Transport.bpm.value = nx.widgets.tempo.val.value ) Tone.Transport.start() + document.querySelector('.loading').classList.remove('loading') +} +function update_value_on_change(el, id, is_int, fn) { + const label = document.querySelector(id + ' + .val') + const update = v => { + label.innerHTML = is_int ? parseInt(v) : v.toFixed(2) + fn && fn(v) + } + el.on('change', update) + update(el.value) +} +function update_radio_value_on_change(el, id, values, fn) { + let old_v = el.active + const label = document.querySelector(id + ' + .val') + const update = v => { + if (v === -1) { + v = el.active = old_v + } else { + old_v = v + } + label.innerHTML = values[v][1] + fn && fn(v) + } + el.on('change', update) + update(el.active) } - keys.listen(play) |
