Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing Music with Ruby

Writing Music with Ruby

As given at MtnWest Ruby Conference, on 3/10/2015

Ben Eggett

March 10, 2015
Tweet

Other Decks in Programming

Transcript

  1. ACCESS DEVELOPMENT For 30 years, Access Development has helped companies

    connect with their customers through America’s largest merchant discount and rewards network. Our programs help organizations acquire new customers, increase retention and generate more revenue. Access brings new customers to over 300,000 merchants, serving 99.6% of the U.S. population. Interested in what we do? Talk to me after
  2. WHAT IS MUSIC? ▸ Vibrations (sound) ▸ Vibrations as a

    specific frequency (notes) ▸ Specific notes (scales) ▸ Specific notes played together (chords) ▸ Specific chords in order (songs)
  3. LET'S BUILD A NOTE TERMS frequency - number of cycles

    per second sample rate - number of frames per second duration - how long we want the note to be
  4. LET'S BUILD A NOTE MATH total frames = duration x

    sample rate cycles per frame = frequency ÷ sample rate increment = 2π radians
  5. module MusicTheory class Note include MusicTheory::Output attr_accessor :frequency, :duration, :output_file_name

    def initialize(options = {}) @frequency = options[:frequency] || 440.0 # Note frequency in Hz @duration = options[:duration] || 1.0 # Number of seconds per note @output_file_name = options[:output_file_name] || 'note' # File name to write (without extension) end def total_frames # We want 1 second of the note, so we need 1 second's worth of frames (duration * sample_rate).to_i end def cycles_per_frame # each frame, we want this fraction of a cycle: frequency / sample_rate end def sine_wave_cycle # A cycle is a full sine wave, which is 2π radians: 2 * Math::PI * cycles_per_frame end def samples # So to create a note that's one second long, we need to write out all the samples in the sine waves phase = 0 total_frames.times.map do sample = (Math.sin phase).to_f phase += sine_wave_cycle sample end end end end
  6. WRITING A FILE Wav format is quite simple Easily writable

    in Ruby standard library. This is the only point where I'll be using a gem: wavefile Because writing wav files is not really the focus of this talk
  7. module MusicTheory module Output def sample_rate 22050 end def format

    WaveFile::Format.new :mono, :pcm_16, sample_rate end def buffer_format WaveFile::Format.new :mono, :float, sample_rate end def output_track WaveFile::Writer.new "#{output_file_name || 'music'}.wav", format do |writer| buffer = WaveFile::Buffer.new samples, buffer_format writer.write buffer end end def play output_track # unless File.file?("#{output_file_name}.wav") `afplay #{output_file_name}.wav` nil end end end
  8. Abstraction is one of the greatest visionary tools ever invented

    by human beings to imagine, decipher, and depict the world. — Jerry Saltz
  9. require 'music_theory/output' module MusicTheory class Octave include MusicTheory::Output attr_accessor :starting_note,

    :amount, :direction, :output_file_name, :all_notes def initialize(options = {}) @starting_note = options[:starting_note] || MusicTheory::Note.new # Note to start on @amount = options[:amount] || 2 # Number of octaves to repeat @direction = options[:direction] || 'asc' # Number of seconds per note @output_file_name = options[:output_file_name] || 'octave' # File name to write (without extension) build_octave end def build_octave @all_notes = [ starting_note ] amount.to_i.times do new_note = all_notes.last.clone if direction == 'asc' new_note.frequency = all_notes.last.frequency * 2 elsif direction == 'desc' new_note.frequency = all_notes.last.frequency / 2 end all_notes << new_note unless new_note.frequency > 20500 end end def samples all_notes.map(&:samples).flatten end end end
  10. TONES & SEMITONES Semitones are the smallest interval common in

    western music One fret on a guitar, adjacent note on piano Tone is two semitones
  11. MOVING SEMITONES There are 12 semitones in an octave (Western

    Music Theory) To move up one semitone, you multiply the pitch by a ratio r This ratio is constant as we move up Moving up 12 semitones doubles the pitch, so r^12 = 2 therefore r = 12th root of two = 1.059....
  12. MODES Difficult concept, though quite easy to understand in ruby.

    There are 7 modes altogether 7 modes because there are 7 notes in the major scale Simply put, modes are just rotations of the major scale - Ionian Mode
  13. Mode Tonic Interval sequence Example ------------------------------------------------------------- Ionian I T-T-s-T-T-T-s C-D-E-F-G-A-B-C

    Dorian II T-s-T-T-T-s-T D-E-F-G-A-B-C-D Phrygian III s-T-T-T-s-T-T E-F-G-A-B-C-D-E Lydian IV T-T-T-s-T-T-s F-G-A-B-C-D-E-F Mixolydian V T-T-s-T-T-s-T G-A-B-C-D-E-F-G Aeolian VI T-s-T-T-s-T-T A-B-C-D-E-F-G-A Locrian VII s-T-T-s-T-T-T B-C-D-E-F-G-A-B
  14. Mode Tonic Interval sequence Example ------------------------------------------------------------- Ionian I T-T-s-T-T-T-s C-D-E-F-G-A-B-C

    Dorian II T-s-T-T-T-s-T D-E-F-G-A-B-C-D Phrygian III s-T-T-T-s-T-T E-F-G-A-B-C-D-E Lydian IV T-T-T-s-T-T-s F-G-A-B-C-D-E-F Mixolydian V T-T-s-T-T-s-T G-A-B-C-D-E-F-G Aeolian VI T-s-T-T-s-T-T A-B-C-D-E-F-G-A Locrian VII s-T-T-s-T-T-T B-C-D-E-F-G-A-B Now, you ruby developers don't need to know all this, you just need to call rotate on the interval sequence and they will all be generated
  15. module MusicTheory class Modes S = 1 T = 2

    I = [T, T, S, T, T, T, S] II = I.rotate III = I.rotate(2) IV = I.rotate(3) V = I.rotate(4) VI = I.rotate(5) VII = I.rotate(6) CHROMATIC = [S,S,S,S,S,S,S,S,S,S,S,S] # Map the music theory names as class methods def self.ionian; I; end self.singleton_class.send(:alias_method, :major, :ionian) self.singleton_class.send(:alias_method, :i, :ionian) def self.dorian; II; end self.singleton_class.send(:alias_method, :ii, :dorian) def self.phrygian; III; end self.singleton_class.send(:alias_method, :iii, :phrygian) def self.lydian; IV; end self.singleton_class.send(:alias_method, :iv, :lydian) def self.mixolydian; V; end self.singleton_class.send(:alias_method, :v, :mixolydian) def self.aeolian; VI; end self.singleton_class.send(:alias_method, :minor, :aeolian) self.singleton_class.send(:alias_method, :vi, :aeolian) def self.locrian; VII; end self.singleton_class.send(:alias_method, :vii, :locrian) def self.chromatic; CHROMATIC; end end end
  16. require 'music_theory/output' module MusicTheory class Scale include MusicTheory::Output attr_accessor :starting_note,

    :output_file_name, :all_notes, :scale_type, :scale_notes, :duration, :frequency def initialize(scale_type = :major, options = {}) @scale_type = scale_type @duration = options[:duration] || 0.5 @frequency = options[:frequency] || 220.0 @starting_note = create_new_note(frequency) # Note to start on @output_file_name = options[:output_file_name] || 'scale' # File name to write (without extension) set_all_notes set_scale_notes end def twelfth_root_of_two 2 ** (1.0/12) end def mode MusicTheory::Modes.send(scale_type) end def set_scale_notes @scale_notes = [all_notes.first] note_index = 0 mode.each do |interval| note_index += interval scale_notes << all_notes[note_index] end end def create_new_note(note_frequency) MusicTheory::Note.new( frequency: note_frequency, duration: duration) end def set_all_notes @all_notes = [@starting_note] 12.times do new_note = create_new_note(all_notes.last.frequency * twelfth_root_of_two) all_notes << new_note end end def samples scale_notes.map(&:samples).flatten end end end
  17. THIRDS Thirds are intervals on a scale Can be major

    or minor: Major is 3 semitones, Minor is 4 Taken in groups of two: If you add T + T == 4 ; T + S == 3 Played sequentially, this is called arpeggio
  18. require 'music_theory/output' module MusicTheory class Third include MusicTheory::Output attr_accessor :scale,

    :all_notes def initialize(scale) @scale = scale @all_notes = [scale.scale_notes.first] current = 0 double_scale_notes = scale.scale_notes * 2 scale.mode.in_groups_of(2, false) do |group| current += group.sum all_notes << double_scale_notes[current] end all_notes.uniq! {|note| note.frequency} all_notes.sort_by! {|note| note.frequency} end def samples all_notes.map(&:samples).flatten end end end
  19. CHORDS Single Notes are easy To make a chord, we

    need to melt it all down. Clamp the values so they fit in a wav format
  20. require 'music_theory/output' module MusicTheory class Chord include MusicTheory::Output attr_accessor :third,

    :duration, :all_notes, :output_file_name def initialize(third, options = {}) @duration = options[:duration] || 2.0 @third = third @output_file_name = options[:output_file_name] || 'chord' # File name to write (without extension) end def flatten_third third.all_notes.each {|note| note.duration = duration} new_samples = [] sample_count = third.all_notes.first.samples.count third.samples.in_groups_of(sample_count).each do |group| group.each_with_index do |value, i| new_samples[i] ||= 0 new_samples[i] += value end end normalize_samples(new_samples) end def normalize_samples(new_samples) max = new_samples.map {|s| s.abs }.max multiplier = 1.0 / max new_samples.map!{ |s| multiplier * s } end def samples flatten_third end end end
  21. CHORD SCALES You can make different chords from a scale

    They will automatically be minor/major based on what will 'fit' into the scale pattern (mode) Each mode has a chord that you can make from it by moving up in thirds
  22. module MusicTheory module ScaleSteps # Concern for Scale def i

    self end def ii set_scale_step(1) end def iii set_scale_step(2) end def iv set_scale_step(3) end def v set_scale_step(4) end def vi set_scale_step(5) end def vii set_scale_step(6) end def viii set_scale_step(7) end private def set_scale_step(jump) MusicTheory::Scale.new scale_type, frequency: scale_notes[jump].frequency, duration: duration end end end
  23. CHORD PROGRESSIONS Now that you've built up all the chords

    you can use, you just play them in a specific order A common progression is I V VI IV One or more chord progressions is a song ^ NOW WE HAVE THE FUNDAMENTALS OF WRITING MUSIC
  24. Journey - Don't Stop Believing James Blunt - You're Beautiful

    Black Eyed Peas - Where Is The Love Alphaville - Forever Young Jason Mraz - I'm Yours Train - Hey Soul Sister The Caling - Wherever You Will Go Elton John - Can You Feel The Love Tonight Akon - Don't Matter John Denver - Take Me Home, Country Home
  25. Lady Gaga - Paparazzi U2- With Or Without You Maroon

    Five - She Will Be Loved The Beatles - Let It Be Bob Marley - No Woman No Cry Men At Work - Land Down Under Spice Girls - Two Become One AHa - Take On Me Green Day - When I Come Around Eagle Eye Cherry - Save Tonight
  26. Tomo - Africa Beyonce - If I Were A Boy

    The Smashing Pumpkins - Bullet Iwth Butterfly Wings The Offspring - Self Esteem Aqua - Barbie Girl Eminem & Rihanna - Love The Way You Lie Lady Gaga - Pokerface MGMT - Kids Bon Jovi - It's My Life The Gregory Brothers - Double Rainbow