Created
January 16, 2024 17:59
-
-
Save shawngraham/ac6602b0a228d5b1c967d6983f98a82b to your computer and use it in GitHub Desktop.
1384: Letters from Barcelona. An experiment sonifying the metadata in an archive. Listen at https://soundcloud.com/fhg1711/1384-letters-from-barcelona-sonification-version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 1384: Letters from Barcelona - sonification version | |
# Data is travel time from Barcelona to Pisa and Barcelona to Avignon for letters (piano: to Pisa; string pluck: to Avignon). Duration is a function of the travel time scaled to 4 beats = 2 weeks. Notes are scored to correspond with the start date; pitch is a function of duration it took for the letter to reach its destination, so silences are meaningful. Longer journeys have higher and longer tones. The percussion plays the beat so that we can hear the progression of time; there is a background drone to indicate the passage of months. | |
# this code was rubber-ducked using GPT4-preview | |
require 'date' | |
ba_data = [9,9,8,7,9,8,8,8,12,9,8,9,10,8,8,8,8,11] #barcelona - avignon | |
ba_data_trip_start_date = ["1384-05-02","1384-05-09","1384-05-12","1384-05-20","1384-05-23","1384-05-26","1384-06-03","1384-06-15","1384-06-23","1384-07-11","1384-08-02","1384-08-06","1384-08-12","1384-09-01","1384-09-06","1384-09-06","1384-09-12","1384-09-19"] | |
bp_data = [26,22,34,23,23,21,30,26,24,14,30,19,7,28,25,22,48,22,23,29,16,33,31,44,41,10] #barcelona - pisa | |
bp_data_trip_start_date = ["1384-01-04","1384-01-22","1384-01-28","1384-02-08","1384-02-11","1384-02-13","1384-02-15","1384-02-19","1384-02-24","1384-03-11","1384-04-05","1384-04-29","1384-04-30","1384-05-09","1384-05-17","1384-05-26","1384-06-23","1384-07-02","1384-07-11","1384-08-06","1384-09-26","1384-11-11","1384-12-13","1384-12-19","1384-12-22","1384-12-29"] | |
# Trigger date to start playing notes from | |
trigger_date = "1384-01-01" | |
# 4 beats = 2 week | |
days_per_four_beats = 14 | |
define :median do |values| | |
sorted = values.sort | |
len = sorted.length | |
(sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0 | |
end | |
define :map_to_scale do |value, data, octave| | |
median_value = median(data) | |
scale_size = scale(octave, :minor_pentatonic).size | |
middle_index = scale_size / 2 | |
index_offset = (value - median_value).round | |
middle_index + index_offset | |
end | |
define :time_to_beats do |time_in_days| | |
beats_per_day = 4.0 / days_per_four_beats | |
time_in_days * beats_per_day | |
end | |
define :date_to_days do |start_date, date| | |
(Date.parse(date) - Date.parse(start_date)).to_i | |
end | |
define :schedule_note do |zipped_data, synth_type, synth_opts| | |
use_bpm 240 # Marching tempo | |
in_thread(name: synth_opts[:name]) do | |
zipped_data.each do |value, date| | |
days_since_trigger = date_to_days(trigger_date, date) | |
sleep days_since_trigger * (4.0 / days_per_four_beats) if days_since_trigger > 0 # Convert days since trigger to beats | |
note_duration = time_to_beats(value) | |
scale_notes = scale(:eb3, :minor_pentatonic) | |
note_index = map_to_scale(value, zipped_data.map(&:first), :eb3) | |
note_index %= scale_notes.size | |
note = scale_notes[note_index] | |
synth synth_type, {note: note, sustain: note_duration, release: 0.1}.merge(synth_opts) | |
sleep note_duration | |
end | |
end | |
end | |
define :play_monthly_voice do |trigger_date, scale_notes, synth_opts| | |
use_bpm 240 # Set BPM to make a beat equal to a day | |
beats_per_month = 16 # 4 weeks per month, 4 beats per week | |
in_thread(name: synth_opts[:name]) do | |
# Calculate total number of months based on the max 'days_since_trigger' for all trips | |
max_days_since_trigger = [bp_data_trip_start_date, ba_data_trip_start_date].flatten.max_by { |date| date_to_days(trigger_date, date) } | |
total_months = (date_to_days(trigger_date, max_days_since_trigger) / 30.0).ceil | |
# Play a note starting at the bottom of the scale and moving up in thirds every month | |
total_months.times do |month| | |
note_index = month * 1 # 2 Moving up by thirds in a scale is equivalent to skipping one note each time | |
note = scale_notes[note_index % scale_notes.size] # Ensure we loop back to the start of the scale if we run out of notes | |
octave_adjustment = (note_index / scale_notes.size) * 12 # Adjust the octave if we've looped the scale | |
adjusted_note = note + octave_adjustment | |
synth :winwood_lead, {note: adjusted_note, sustain: beats_per_month - 1, release: 1}.merge(synth_opts) # Play the note with the passed synth options | |
sleep beats_per_month # Wait for the next month | |
end | |
end | |
end | |
scale_notes = scale(:eb2, :minor_pentatonic) # Start at the bottom of the scale | |
play_monthly_voice(trigger_date, scale_notes, {name: :monthly_voice, amp: 0.1}) | |
# Precompute zipped data for Barcelona-Pisa | |
bp_zipped_data = bp_data.zip(bp_data_trip_start_date) | |
bp_zipped_data_sorted = bp_zipped_data.sort_by { |_, date| Date.parse(date) } | |
# Precompute zipped data for Barcelona-Avignon | |
ba_zipped_data = ba_data.zip(ba_data_trip_start_date) | |
ba_zipped_data_sorted = ba_zipped_data.sort_by { |_, date| Date.parse(date) } | |
# Schedule the voices using precomputed zipped data | |
schedule_note(bp_zipped_data_sorted, :piano, {name: :piano_voice, amp: 0.9, pan: -0.5, release: 0.3}) | |
schedule_note(ba_zipped_data_sorted, :pluck, {name: :pluck_voice, amp: 0.9, pan: 0.5, release: 0.3}) | |
# Background sostenuto voices using precomputed zipped data | |
schedule_note(bp_zipped_data_sorted, :prophet, {name: :prophet_voice, amp: 0.2, pan: -0.5, attack: 1, sustain: 4}) | |
schedule_note(ba_zipped_data_sorted, :saw, {name: :saw_voice, amp: 0.2, pan: 0.5, attack: 1, sustain: 4}) | |
# percussion # straight march | |
live_loop :march_drumming do | |
sample :bd_ada, amp: 0.5 | |
sleep 1 | |
sample :drum_bass_soft, amp: 0.5 | |
sleep 1 | |
sample :bd_ada, amp: 0.3 | |
sleep 1 | |
sample :drum_snare_hard, amp: 0.5 | |
sleep 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment