/* play function for mass shooting data w/ custom timing */ // export const note_values = [ // [8, '8 measures', 8 * 512], // [4, '4 measures', 4 * 512], // [2, '2 measures', 2 * 512], // [1, 'whole note', 512], // [1/2, 'half note', 256], // [1/3, 'third note', [170, 170, 171]], // [1/4, 'quarter note', 128], // [1/5, 'fifth note', [51,51,51,51,52]], // [1/6, 'sixth note', [85, 85, 86, 85, 85, 86]], // [1/8, 'eighth note', 64], // [1/10, 'tenth note', [25,26,26,25,26,25,26,26,25,26]], // [1/12, 'twelfth note', [21,21,22, 21,21,22, 21,21,22, 21,21,22]], // [1/16, 'sixteenth note', 32], // [1/32, 'thirtysecond note', 16], // ] function play_mass_shootings(i, bounds, diff, note_time, channel="all", exporting) { const { min, max } = bounds const total = dataset.dates.length let pedal_note let notes = [], midi_notes = [] let cases = [] let timings let week = Math.floor((i)/4) % 4 let year = Math.floor((i - (4*4*3)) / 48) // + 2 console.log(year) let yy = -year if (year > 0) year += 1 let this_one = 0 // console.log(i, mass_i, dataset.dates[mass_i], channel, exporting) while (i >= dataset.dates[mass_i] && mass_i < total) { // console.log(i, dataset.dates[mass_i]) notes.push(dataset.lines[0][mass_i]) if (dataset.isMass) { cases.push(dataset.data[mass_i][mass_fields.date] + ' ' + dataset.data[mass_i][mass_fields.case] + ", " + dataset.data[mass_i][mass_fields.fatalities] + ' dead, ' + dataset.data[mass_i][mass_fields.injured] + ' injured') } else { cases.push(dataset.labels[mass_i]) // console.log(dataset.labels[mass_i]) } // console.log('push case', dataset.data[mass_i][mass_fields.date] + ' ' + dataset.data[mass_i][mass_fields.case]) mass_i += 1 this_one += 1 if (this_one >= 4) break } switch (notes.length) { default: case 0: break case 1: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value + yy, 128, channel, exporting, 0)) timings = [128] break case 2: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value + yy, 64, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value + yy, 64, channel, exporting, 64)) timings = [64, 64] break case 3: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value + yy, 43, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value + yy, 43, channel, exporting, 43)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value + yy, 42, channel, exporting, 85)) timings = [43, 43 ,42] break case 4: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value + yy, 32, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value + yy, 32, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value + yy, 32, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value + yy, 32, channel, exporting, 96)) timings = [32, 32, 32, 32] break case 5: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value + yy, 22, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value + yy, 21, channel, exporting, 22)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value + yy, 21, channel, exporting, 43)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value + yy, 32, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value + yy, 32, channel, exporting, 96)) timings = [22, 21, 21, 32, 32] break case 6: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value + yy, 22, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value + yy, 21, channel, exporting, 22)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value + yy, 21, channel, exporting, 43)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value + yy, 22, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value + yy, 21, channel, exporting, 86)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value + yy, 21, channel, exporting, 107)) timings = [22, 21, 21, 22, 21, 21] break case 7: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value, 16, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value, 16, channel, exporting, 16)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value, 16, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value, 16, channel, exporting, 48)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value, 22, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value, 21, channel, exporting, 86)) midi_notes.push(play_note( norm(notes[6], min, max) * nx.multiply.value, 21, channel, exporting, 107)) timings = [16, 16, 16, 16, 22, 21, 21] break case 8: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value, 16, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value, 16, channel, exporting, 16)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value, 16, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value, 16, channel, exporting, 48)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value, 16, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value, 16, channel, exporting, 80)) midi_notes.push(play_note( norm(notes[6], min, max) * nx.multiply.value, 16, channel, exporting, 96)) midi_notes.push(play_note( norm(notes[7], min, max) * nx.multiply.value, 16, channel, exporting, 112)) timings = [16, 16, 16, 16, 16, 16, 16, 16] break case 9: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value, 11, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value, 11, channel, exporting, 11)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value, 10, channel, exporting, 22)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value, 16, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value, 16, channel, exporting, 48)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value, 16, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[6], min, max) * nx.multiply.value, 16, channel, exporting, 80)) midi_notes.push(play_note( norm(notes[7], min, max) * nx.multiply.value, 16, channel, exporting, 96)) midi_notes.push(play_note( norm(notes[8], min, max) * nx.multiply.value, 16, channel, exporting, 112)) timings = [11, 11, 10, 16, 16, 16, 16, 16, 16] break case 10: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value, 11, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value, 11, channel, exporting, 11)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value, 10, channel, exporting, 22)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value, 11, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value, 11, channel, exporting, 43)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value, 10, channel, exporting, 54)) midi_notes.push(play_note( norm(notes[6], min, max) * nx.multiply.value, 16, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[7], min, max) * nx.multiply.value, 16, channel, exporting, 80)) midi_notes.push(play_note( norm(notes[8], min, max) * nx.multiply.value, 16, channel, exporting, 96)) midi_notes.push(play_note( norm(notes[9], min, max) * nx.multiply.value, 16, channel, exporting, 112)) timings = [11, 11, 10, 11, 11, 10, 16, 16, 16, 16] break case 11: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value, 11, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value, 11, channel, exporting, 11)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value, 10, channel, exporting, 22)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value, 11, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value, 11, channel, exporting, 43)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value, 10, channel, exporting, 54)) midi_notes.push(play_note( norm(notes[6], min, max) * nx.multiply.value, 11, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[7], min, max) * nx.multiply.value, 11, channel, exporting, 75)) midi_notes.push(play_note( norm(notes[8], min, max) * nx.multiply.value, 10, channel, exporting, 86)) midi_notes.push(play_note( norm(notes[9], min, max) * nx.multiply.value, 16, channel, exporting, 96)) midi_notes.push(play_note( norm(notes[10], min, max) * nx.multiply.value, 16, channel, exporting, 112)) timings = [11, 11, 10, 11, 11, 10, 11, 11, 10, 16, 16] break case 12: midi_notes.push(play_note( norm(notes[0], min, max) * nx.multiply.value, 11, channel, exporting, 0)) midi_notes.push(play_note( norm(notes[1], min, max) * nx.multiply.value, 11, channel, exporting, 11)) midi_notes.push(play_note( norm(notes[2], min, max) * nx.multiply.value, 10, channel, exporting, 22)) midi_notes.push(play_note( norm(notes[3], min, max) * nx.multiply.value, 11, channel, exporting, 32)) midi_notes.push(play_note( norm(notes[4], min, max) * nx.multiply.value, 11, channel, exporting, 43)) midi_notes.push(play_note( norm(notes[5], min, max) * nx.multiply.value, 10, channel, exporting, 54)) midi_notes.push(play_note( norm(notes[6], min, max) * nx.multiply.value, 11, channel, exporting, 64)) midi_notes.push(play_note( norm(notes[7], min, max) * nx.multiply.value, 11, channel, exporting, 75)) midi_notes.push(play_note( norm(notes[8], min, max) * nx.multiply.value, 10, channel, exporting, 86)) midi_notes.push(play_note( norm(notes[9], min, max) * nx.multiply.value, 11, channel, exporting, 96)) midi_notes.push(play_note( norm(notes[10], min, max) * nx.multiply.value, 11, channel, exporting, 107)) midi_notes.push(play_note( norm(notes[11], min, max) * nx.multiply.value, 10, channel, exporting, 118)) timings = [11, 11, 10, 11, 11, 10, 11, 11, 10, 11, 11, 10] break } const pedal_freq = scales.current().index(nx.pedal_tone.value - week + 4 - year, nx.octave.value) pedal_note = get_midi_note_for_frequency(pedal_freq) if (!exporting) { kalimba.play(pedal_freq, -12) if (cases.length) { document.querySelector('#cases').innerHTML = cases.join('
') } } if (total <= mass_i) { mass_i = 0 i = 0 } else { i += 1 } if (notes.length) { return [i, midi_notes, timings, pedal_note] } return [i, [], [], pedal_note] } function get_midi_note_for_frequency(freq){ let midi_note = ftom(freq) let cents = midi_note % 1 if (cents > 0.5) { midi_note += 1 cents -= 1 } cents *= 2 midi_note = Math.floor(midi_note) if (midi_note > 127) return 0 const note = Tone.Frequency(Math.floor(midi_note), "midi").toNote() // console.log(freq, midi_note, note) return note } /* play next note according to sonification */ function play_next(){ if (paused) return let note_time = 120000 / Tone.Transport.bpm.value * note_values[nx.timing.active][0] clearTimeout(playTimeout) playTimeout = setTimeout(play_next, note_time) let [new_i, notes, timings] = play_fn(i, bounds, diff, note_time) if (dataset.labels) { // const j = Math.floor(i / bounds.rows[0].length) // document.querySelector('#cases').innerHTML = dataset.labels[j] } i = new_i if (recording) { let timing = note_values[nx.timing.active][2] if (timing.length) timing = timing[i % timing.length] recorder.addEvent(new MidiWriter.NoteEvent({ pitch: notes, duration: 't' + timing })) } } function play() { i = 0 mass_i = 0 paused = false play_next() } function pause() { paused = true } /* bind selects */ function pick_dataset(key){ console.log('pick dataset:', key, datasets[key]) i = 0 mass_i = 0 window.location.hash = key dataset = datasets[key] bounds = get_bounds(dataset) diff = get_diff_bounds(bounds.rows) play_fn = dataset.play_fn max_i = dataset.max_i || (bounds.rows.length * bounds.rows[0].length) if (dataset.onPick) { dataset.onPick() } } /* build and bind the UI */ function ready() { scales.build_options(document.querySelector('#scale')) build_options(document.querySelector('#dataset'), datasets, pick_dataset) const dial_size = [50, 50] Tone.Transport.bpm.value = DEFAULT_BPM nx.tempo = new Nexus.Dial('#tempo', { size: dial_size, 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', { size: dial_size, min: 0, max: 2, step: 0.01, value: 0.8, }) update_value_on_change(nx.duration, '#duration', false) nx.offset = new Nexus.Dial('#offset', { size: dial_size, min: -24, max: 24, step: 1, value: -5, }) update_value_on_change(nx.offset, '#offset', true) nx.octave = new Nexus.Dial('#octave', { size: dial_size, min: -4, max: 4, step: 1, value: 0, }) update_value_on_change(nx.octave, '#octave', true) nx.multiply = new Nexus.Dial('#multiply', { size: dial_size, min: -64, max: 64, step: 1, value: 19, }) update_value_on_change(nx.multiply, '#multiply', true) nx.interval = new Nexus.Dial('#interval', { size: dial_size, min: -64, max: 64, step: 1, value: 10, }) update_value_on_change(nx.interval, '#interval', true) nx.pedal_tone = new Nexus.Dial('#pedal_tone', { size: dial_size, min: -24, max: 24, step: 1, value: -7, }) update_value_on_change(nx.pedal_tone, '#pedal_tone', true) const play_button = document.querySelector('#play') play_button.addEventListener('click', () => { play() }) const pause_button = document.querySelector('#pause') pause_button.addEventListener('click', () => { pause() }) const export_midi_button = document.querySelector('#export_midi') export_midi_button.addEventListener('click', () => { export_pattern_as_midi(dataset, bounds, diff, nx.tempo.value, nx.timing.active, play_fn, max_i) }) const record_midi_button = document.querySelector('#record_midi') record_midi_button.addEventListener('click', () => { if (recording) { record_midi_button.innerHTML = 'Record MIDI' document.body.classList.remove('recording') recording = false const writer = new MidiWriter.Writer([recorder]) const blob = dataURItoBlob(writer.dataUri()) saveAs(blob, 'Recording - ' + dataset.name + '.mid') } else { record_midi_button.innerHTML = 'Save Recording' document.body.classList.add('recording') recording = true recorder = new MidiWriter.Track() recorder.setTempo(nx.tempo.value) } }) document.querySelector('.loading').classList.remove('loading') document.querySelector('#scale').value = '14' scales.pick(14) const initial_dataset = decodeURIComponent(window.location.hash.substr(1)) || 'Mass Shootings' document.querySelector('#dataset').value = initial_dataset pick_dataset(initial_dataset) // play_next() } /* keys */ keys.listen(index => { nx.offset.value = index nx.offset.update(index) })