diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2019-08-05 02:06:29 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2019-08-05 02:06:29 +0200 |
| commit | 626c348af622b9bb66000d1a49dbe007131649ef (patch) | |
| tree | 59127e1e69a209c1f0823050c17979cd5253d312 /client/index.js | |
sonifications
Diffstat (limited to 'client/index.js')
| -rw-r--r-- | client/index.js | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/client/index.js b/client/index.js new file mode 100644 index 0000000..83b5214 --- /dev/null +++ b/client/index.js @@ -0,0 +1,204 @@ +import * as util from './lib/util' +import * as data from './data' +import * as player from './player' + +/* initialization */ + +const mass_fields = [ + "date", "timestamp", + "fatalities", "injured", "total_victims", + "age", "case", "weapon_type", "weapon_details" +].reduce((a,b,i) => { + a[b] = i + return a +}, {}) + +const gv_fields = [ + "incident_id", "date", + "state", "city_or_county", "address", + "n_killed", "n_injured", + "incident_url", "source_url", "incident_url_fields_missing", + "congressional_district", + "gun_stolen", "gun_type", "incident_characteristics", + "latitude", "location_description", "longitude", + "n_guns_involved", + "notes", + "participant_age", "participant_age_group", "participant_gender", "participant_name", "participant_relationship", "participant_status", "participant_type", + "sources", + "state_house_district", "state_senate_district" +].reduce((a,b,i) => { + a[b] = i + return a +}, {}) + +const year_days_by_month = [ + 31, 28, 31, 30, + 31, 30, 31, 31, + 30, 31, 30, 31, + 0 +].reduce((a, b, i) => { + if (i === 0) { + return [b] + } + return a.concat(a[i-1] + b) +}, []) + +let i = 0, max_i = 0, mass_i = 0 +let datasets = {}, dataset = {}, bounds = {}, diff = [] +let play_fn = play_sequence +data.load().then(lists => { + console.log(lists) + + const ar15 = lists.ar_15_2016_18 + datasets['AR-15 2016-18'] = {} + datasets['AR-15 2016-18'].name = 'AR-15 (2016-18)' + datasets['AR-15 2016-18'].pedal = true + datasets['AR-15 2016-18'].play_fn = play_mass_shootings + const ar_lines = ar15.lines.map(l => { + if (l[gv_fields.incident_characteristics].match('Shots Fired - No Injuries')) { + return null + } + if (l[gv_fields.n_killed] + l[gv_fields.n_injured] < 4) return null + const [y, m, d] = l[gv_fields.date].split('-') + if (parseInt(y) > 2017) return null + const yy = (parseInt(y) - 2016) * 365 + const mm = year_days_by_month[parseInt(m)] + const dd = Math.floor(parseInt(d)) + 14 + const date = Math.floor((yy + mm + dd) / 7) + // console.log(date, y, m, d) + let total = l[gv_fields.n_killed] + l[gv_fields.n_injured] + if (l[gv_fields.n_killed] === 0) { + total = - l[gv_fields.n_injured] + } + return [ + date, + Math.log(Math.log(total + 10) + 1), + "** !!, $$, {} killed, [] injured".replace('**', l[gv_fields.date]).replace('!!', l[gv_fields.city_or_county]).replace('$$', l[gv_fields.state]).replace('{}', l[gv_fields.n_killed]).replace('[]', l[gv_fields.n_injured]), + l[gv_fields.n_killed], + l[gv_fields.n_injured], + ] + }).filter(n => !!n) + datasets['AR-15 2016-18'].dates = ar_lines.map(a => a[0]) + datasets['AR-15 2016-18'].dates.push(ar_lines.length) + datasets['AR-15 2016-18'].lines = [ar_lines.map(a => a[1])] + datasets['AR-15 2016-18'].labels = ar_lines.map(a => a[2]) + + const fm = lists.firearms_manufactured + datasets['Firearms Manufactured'] = {} + datasets['Firearms Manufactured'].name = 'Firearms Manufactured' + datasets['Firearms Manufactured'].play_fn = play_sequence + datasets['Firearms Manufactured'].h = fm.h.slice(1, 5) + datasets['Firearms Manufactured'].labels = fm.lines.map(l => l.slice(0, 1)) + datasets['Firearms Manufactured'].lines = fm.lines.map(l => l.slice(1, 5)) + + datasets["Mass Shootings"] = lists.mass_shootings_from_columbine + datasets["Mass Shootings"].name = "Mass Shootings" + datasets["Mass Shootings"].pedal = true + datasets["Mass Shootings"].isMass = true + datasets["Mass Shootings"].play_fn = play_mass_shootings + const lines = datasets["Mass Shootings"].lines.reverse() + const [min_y, ...rest_a] = lines[0][mass_fields.date].split('/') + const [max_y, ...rest_b] = lines[lines.length-1][mass_fields.date].split('/') + + datasets["Mass Shootings"].dates = lines.map(row => { + const [y, m, d] = row[mass_fields.date].split('/') + return (parseInt(y) - parseInt(min_y)) * 12 + parseInt(m) + }) + datasets["Mass Shootings"].max_i = (parseInt(max_y) - parseInt(min_y)) * 12 + parseInt(12) + // console.log('max i', max_i) + datasets["Mass Shootings"].data = lines + datasets["Mass Shootings"].lines = [lines.map(row => row[mass_fields.total_victims])] + ready() +}) + +/* play function for mass shooting data w/ custom timing */ + +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 + } + + if (cases.length) { + document.querySelector('#cases').innerHTML = cases.join('<br>') + } + + if (total <= mass_i) { + mass_i = 0 + i = 0 + } else { + i += 1 + } + + return [i, [], [], pedal_note] +} + +/* play the next note in sequence */ + +function play_sequence(i, bounds, diff, note_time, channel="all", exporting) { + const { rows, min, max } = bounds + const count = rows.length * rows[0].length + if (i >= count) i = 0 + 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 + const midi_note = play_note( norm(n, min, max) * nx.multiply.value, note_time, channel, exporting) + return [i, [midi_note], [128]] +} + +/* 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 })) + } +} + +/* build and bind the UI */ + +function ready() { + document.querySelector('.loading').classList.remove('loading') +} + + + |
