summaryrefslogtreecommitdiff
path: root/client/index.js
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2019-08-05 02:06:29 +0200
committerJules Laplace <julescarbon@gmail.com>2019-08-05 02:06:29 +0200
commit626c348af622b9bb66000d1a49dbe007131649ef (patch)
tree59127e1e69a209c1f0823050c17979cd5253d312 /client/index.js
sonifications
Diffstat (limited to 'client/index.js')
-rw-r--r--client/index.js204
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')
+}
+
+
+