• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Ruby, Rock & Roll
 

Ruby, Rock & Roll

on

  • 2,167 views

Creating digital music using a Pure Ruby domain specific language

Creating digital music using a Pure Ruby domain specific language

Statistics

Views

Total Views
2,167
Views on SlideShare
2,158
Embed Views
9

Actions

Likes
1
Downloads
0
Comments
0

3 Embeds 9

http://www.linkedin.com 7
http://www.hanrss.com 1
https://twitter.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Ruby, Rock & Roll Ruby, Rock & Roll Presentation Transcript

  • Ruby, Rock and RollChang Sau SheongHP Labs SingaporeMay 2012
  • About
  •   me
  • About
  •   me
  • About
  •   me
  • About
  •   me
  • About
  •   me
  • About
  •   me
  • About
  •   me
  • About
  •   me
  • Musehttp://github.com/sausheong/muse
  • A
  •   (pure)
  •   Ruby
  •   domain- specific
  •   language
  •   for
  •    synthesizing
  •   music
  •   
  • Creating
  •   Muse•Create
  •   sound•Organize
  •   sound
  •   into
  •   music
  •   notes•Write
  •   notes
  •   to
  •   file•Wrap
  •   around
  •   with
  •   a
  •   domain- specific
  •   language
  • About
  •   sound•Sound
  •   is
  •   produced
  •   by
  •   vibration
  •   of
  •   something
  •    (air,
  •   water
  •   etc)•Vibration
  •   produces
  •   waves
  •   •Properties
  •   of
  •   sound
  •   wave
  •   include
  •   amplitude
  •    (volume)
  •   and
  •   frequency
  •   (pitch)
  • Higher
  •   amplitude
  •   means
  •   louder
  •   volume amplitude frequency
  •   =
  •   number
  •   of
  •    cycles
  •   in
  •   1
  •   second cycle More
  •   cycles
  •   in
  •   1
  •   second
  •    mean
  •   higher
  •   frequency,
  •    higher
  •   pitch
  • Make
  •   a
  •   sound•Sine
  •   wave
  •   is
  •   pure
  •   sound,
  •   all
  •   other
  •   sounds
  •   made
  •    by
  •   combining
  •   various
  •   sine
  •   waves•To
  •   make
  •   a
  •   sound,
  •   generate
  •   sine
  •   waves•To
  •   make
  •   a
  •   digital
  •   sound,
  •   generate
  •   digital
  •   sine
  •    waves
  •   and
  •   put
  •   them
  •   into
  •   sound
  •   files
  • The
  •   physics
  •   of
  •   music•Music
  •   is
  •   organized
  •   sound•Music
  •   notes
  •   are
  •   sounds
  •   with
  •   specific
  •   pitches
  •    (frequencies)
  •   and
  •   durations•Each
  •   note
  •   corresponds
  •   to
  •   a
  •   particular
  •    frequency,
  •   expressed
  •   in
  •   hertz
  •   (Hz)•Most
  •   music
  •   use
  •   a
  •   scale
  •   (sequence
  •   of
  •   notes)
  •    with
  •   12
  •   notes
  •   called
  •   the
  •   chromatic
  •   scale
  • same
  •   pitch
  •   class
  •   (octave) C# D# F# G# A#C D E F G A B C 1
  •   octave
  •   =
  •   12
  •   semi-tone
  •   notes
  • The
  •   physics
  •   of
  •   music•The
  •   frequencies
  •   of
  •   the
  •   notes
  •   are
  •   related
  •   to
  •    each
  •   other
  •   in
  •   a
  •   mathematical
  •   formula•The
  •   frequencies
  •   are
  •   defined
  •   around
  •   the
  •    standard
  •   pitch
  •   A4,
  •   which
  •   is
  •   440
  •   Hz
  •   (ISO
  •    16:1975
  •   standard)•Notes
  •   of
  •   the
  •   same
  •   pitch
  •   class
  •   (octave)
  •   are
  •   in
  •    the
  •   ratio
  •   of
  •   a
  •   power
  •   of
  •   2
  •   (1/4,
  •   1/2,
  •   2,
  •   4,
  •   8
  •    etc)
  •   
  • A A7 3520HzA A6 1760HzA A5 880HzA A4 440Hz
  • 12difference
  •   between
  •   12
  •   semi-tones
  •   (1
  •   octave)
  •   is
  •   
  •   2
  •   
  •   
  •   ×
  •   f
  •   
  •   or
  •   2f
  •    
  •    
  •    12 1 difference
  •   between
  •   2
  •   semi-tones
  •   is
  •   
  •   
  •   
  •   2 
  •   × 
  •   f
  •   
  •   or
  •    2 
  •    
  •    12 12 ×f
  • Getting
  •   the
  •   frequency•Get
  •   the
  •   frequency
  •   of
  •   the
  •   note
  •   (f),
  •   as
  •    compared
  •   to
  •   A4
  •   (440Hz)
  •   using
  •   the
  •   formula n f = 2 × 440Hz 12•where
  •   n
  •   =
  •   distance
  •   from
  •   A4.
  •   For
  •   eg
  •   C5
  •   is
  •   3
  •    notes
  •   (semi-tones)
  •   after
  •   A4,
  •   so
  •   frequency
  •   of
  •    C5
  •   is
  •    3 f = 2 12 × 440Hz = 523.25Hz
  • Create
  •   music
  •   notes•To
  •   get
  •   a
  •   sine
  •   wave
  •   representing
  •   a
  •   note s = sin(2π f )• We
  •   multiple
  •   f
  •   with
  •   2
  •   
  •   
  •   because
  •   we
  •   need
  •   to
  •    
  •    π turn
  •   the
  •   frequency
  •   into
  •   radians
  • Get
  •   note
  •   data
  •   samples 1250 626 0 44100
  •   samples take
  •   samples
  •   at
  •   regular
  •   intervals
  •   (44,100
  •    samples
  •   over
  •   a
  •   period
  •   of
  •   1
  •   second)
  •   and
  •    calculate
  •   the
  •   wave
  •   value
  •   at
  •   that
  •   sample
  • NOTES = %w(a ais b c cis d dis e f fis g gis)FREQUENCIES = { d4:-7, dis4:-6, e4:-5, f4: -4, fis4:-3, g4:-2, gis4:-1, a4: 0, ais4: 1, b4: 2, c5: 3, cis5: 4, d5: 5}# get the frequency of the pitchdef frequency_of(step) 440.0*(2**(step.to_f/12.0))endsample_rate = 44100.0 # 44100 Hzduration = 1 # 1 secstream = [] # data stream for left and right channelsfrequency = frequency_of(FREQUENCIES[:a4])# for the duration of 1 sec, step every 1/44100 times and# write the value(0.0..duration.to_f).step(1/sample_rate) do |i| x = (10000 * Math.sin(2 * Math::PI * frequency * i)).to_i stream << [x,x]end
  • [[0, 0], [626, 626], [1250, 1250], [1869, 1869], [2481, 2481], [3083, 3083], [3673, 3673], [4248, 4248], [4807, 4807], [5347, 5347], [5866, 5866], [6362,6362], [6832, 6832], [7276, 7276], [7692, 7692], [8077, 8077], [8431, 8431], [8751, 8751], [9037, 9037], [9287, 9287], [9501, 9501], [9678, 9678], [9816,9816], [9916, 9916], [9978, 9978], [9999, 9999], [9982, 9982], [9925, 9925], [9830, 9830], [9696, 9696], [9523, 9523], [9313, 9313], [9067, 9067], [8785,8785], [8469, 8469], [8119, 8119], [7737, 7737], [7325, 7325], [6884, 6884], [6416, 6416], [5923, 5923], [5407, 5407], [4869, 4869], [4313, 4313], [3739,3739], [3151, 3151], [2550, 2550], [1939, 1939], [1321, 1321], [697, 697], [71, 71], [-555, -555], [-1179, -1179], [-1799, -1799], [-2412, -2412], [-3015,-3015], [-3606, -3606], [-4184, -4184], [-4744, -4744], [-5287, -5287], [-5808, -5808], [-6307, -6307], [-6780, -6780], [-7227, -7227], [-7646, -7646], stream[-8035, -8035], [-8392, -8392], [-8716, -8716], [-9006, -9006], [-9261, -9261], [-9479, -9479], [-9660, -9660], [-9803, -9803], [-9907, -9907], [-9973,-9973], [-9999, -9999], [-9986, -9986], [-9934, -9934], [-9843, -9843], [-9713, -9713], [-9545, -9545], [-9339, -9339], [-9097, -9097], [-8819, -8819],[-8506, -8506], [-8160, -8160], [-7782, -7782], [-7373, -7373], [-6936, -6936], [-6471, -6471], [-5981, -5981], [-5467, -5467], [-4931, -4931], [-4377,-4377], [-3805, -3805], [-3218, -3218], [-2619, -2619], [-2009, -2009], [-1391, -1391], [-768, -768], [-142, -142], [484, 484], [1109, 1109], [1729, 1729],[2343, 2343], [2947, 2947], [3540, 3540], [4119, 4119], [4682, 4682], [5226, 5226], [5750, 5750], [6251, 6251], [6728, 6728], [7178, 7178], [7600, 7600],[7992, 7992], [8353, 8353], [8681, 8681], [8975, 8975], [9234, 9234], [9456, 9456], [9641, 9641], [9788, 9788], [9897, 9897], [9967, 9967], [9998, 9998],[9989, 9989], [9942, 9942], [9855, 9855], [9729, 9729], [9566, 9566], [9364, 9364], [9126, 9126], [8852, 8852], [8544, 8544], [8201, 8201], [7827, 7827],[7421, 7421], [6987, 6987], [6525, 6525], [6038, 6038], [5526, 5526], [4993, 4993], [4441, 4441], [3871, 3871], [3285, 3285], [2687, 2687], [2079, 2079],[1462, 1462], [839, 839], [213, 213], [-413, -413], [-1038, -1038], [-1659, -1659], [-2273, -2273], [-2879, -2879], [-3473, -3473], [-4054, -4054], [-4619,-4619], [-5165, -5165], [-5691, -5691], [-6195, -6195], [-6675, -6675], [-7128, -7128], [-7554, -7554], [-7949, -7949], [-8314, -8314], [-8645, -8645],[-8943, -8943], [-9206, -9206], [-9432, -9432], [-9622, -9622], [-9774, -9774], [-9887, -9887], [-9961, -9961], [-9996, -9996], [-9992, -9992], [-9949,-9949], [-9867, -9867], [-9746, -9746], [-9586, -9586], [-9389, -9389], [-9155, -9155], [-8885, -8885], [-8580, -8580], [-8242, -8242], [-7871, -7871],[-7469, -7469], [-7038, -7038], [-6579, -6579], [-6094, -6094], [-5586, -5586], [-5055, -5055], [-4504, -4504], ...]
  • [[0, 0], [626, 626], [1250, 1250], [1869, 1869], [2481, 2481], [3083, 3083], [3673, 3673], [4248, 4248], [4807, 4807], [5347, 5347], [5866, 5866], [6362,6362], [6832, 6832], [7276, 7276], [7692, 7692], [8077, 8077], [8431, 8431], [8751, 8751], [9037, 9037], [9287, 9287], [9501, 9501], [9678, 9678], [9816,9816], [9916, 9916], [9978, 9978], [9999, 9999], [9982, 9982], [9925, 9925], [9830, 9830], [9696, 9696], [9523, 9523], [9313, 9313], [9067, 9067], [8785,8785], [8469, 8469], [8119, 8119], [7737, 7737], [7325, 7325], [6884, 6884], [6416, 6416], [5923, 5923], [5407, 5407], [4869, 4869], [4313, 4313], [3739,3739], [3151, 3151], [2550, 2550], [1939, 1939], [1321, 1321], [697, 697], [71, 71], [-555, -555], [-1179, -1179], [-1799, -1799], [-2412, -2412], [-3015,-3015], [-3606, -3606], [-4184, -4184], [-4744, -4744], [-5287, -5287], [-5808, -5808], [-6307, -6307], [-6780, -6780], [-7227, -7227], [-7646, -7646], stream[-8035, -8035], [-8392, -8392], [-8716, -8716], [-9006, -9006], [-9261, -9261], [-9479, -9479], [-9660, -9660], [-9803, -9803], [-9907, -9907], [-9973,-9973], [-9999, -9999], [-9986, -9986], [-9934, -9934], [-9843, -9843], [-9713, -9713], [-9545, -9545], [-9339, -9339], [-9097, -9097], [-8819, -8819],[-8506, -8506], [-8160, -8160], [-7782, -7782], [-7373, -7373], [-6936, -6936], [-6471, -6471], [-5981, -5981], [-5467, -5467], [-4931, -4931], [-4377,-4377], [-3805, -3805], [-3218, -3218], [-2619, -2619], [-2009, -2009], [-1391, -1391], [-768, -768], [-142, -142], [484, 484], [1109, 1109], [1729, 1729],[2343, 2343], [2947, 2947], [3540, 3540], [4119, 4119], [4682, 4682], [5226, 5226], [5750, 5750], [6251, 6251], [6728, 6728], [7178, 7178], [7600, 7600],[7992, 7992], [8353, 8353], [8681, 8681], [8975, 8975], [9234, 9234], [9456, 9456], [9641, 9641], [9788, 9788], [9897, 9897], [9967, 9967], [9998, 9998],[9989, 9989], [9942, 9942], [9855, 9855], [9729, 9729], [9566, 9566], [9364, 9364], [9126, 9126], [8852, 8852], [8544, 8544], [8201, 8201], [7827, 7827],[7421, 7421], [6987, 6987], [6525, 6525], [6038, 6038], [5526, 5526], [4993, 4993], [4441, 4441], [3871, 3871], [3285, 3285], [2687, 2687], [2079, 2079],[1462, 1462], [839, 839], [213, 213], [-413, -413], [-1038, -1038], [-1659, -1659], [-2273, -2273], [-2879, -2879], [-3473, -3473], [-4054, -4054], [-4619,-4619], [-5165, -5165], [-5691, -5691], [-6195, -6195], [-6675, -6675], [-7128, -7128], [-7554, -7554], [-7949, -7949], [-8314, -8314], [-8645, -8645],[-8943, -8943], [-9206, -9206], [-9432, -9432], [-9622, -9622], [-9774, -9774], [-9887, -9887], [-9961, -9961], [-9996, -9996], [-9992, -9992], [-9949,-9949], [-9867, -9867], [-9746, -9746], [-9586, -9586], [-9389, -9389], [-9155, -9155], [-8885, -8885], [-8580, -8580], [-8242, -8242], [-7871, -7871],[-7469, -7469], [-7038, -7038], [-6579, -6579], [-6094, -6094], [-5586, -5586], [-5055, -5055], [-4504, -4504], ...]
  • Harmonics•Sine
  •   wave
  •   is
  •   the
  •   most
  •   basic
  •   waveform
  •   and
  •    produces
  •   the
  •   fundamental
  •   frequency,
  •   a
  •   pure
  •    sound•Any
  •   other
  •   sounds
  •   can
  •   be
  •   created
  •   by
  •   adding
  •    other
  •   waves
  •   at
  •   different
  •   frequencies
  •   and
  •    amplitudes
  •   (called
  •   harmonics)•Adding
  •   sine
  •   waves
  •   at
  •   different
  •   multiples
  •   of
  •    the
  •   same
  •   frequency
  •   changes
  •   how
  •   the
  •   same
  •   note
  •    sounds
  • y=sin(x)y
  •   =
  •   sin(2x) y
  •   =
  •   sin(x)
  •   +
  •   sin(2x)
  • def harmonics(input) Math.sin(2 * Math::PI * input) + Math.sin(2 * Math::PI * input * 2)end# for the duration of 1 sec, step every 1/44100 times and# write the value(0.0..duration.to_f).step(1/sample_rate) do |i| x = (10000 * harmonics(frequency * i)).to_i stream << [x,x]end
  • def harmonics(input) Math.sin(2 * Math::PI * input) + Math.sin(2 * Math::PI * input * 2)end# for the duration of 1 sec, step every 1/44100 times and# write the value(0.0..duration.to_f).step(1/sample_rate) do |i| x = (10000 * harmonics(frequency * i)).to_i stream << [x,x]end
  • Envelopes•Changing
  •   the
  •   volume
  •   of
  •   the
  •   note
  •   over
  •   the
  •    duration
  •   also
  •   changes
  •   how
  •   the
  •   note
  •   sounds def envelope(input, duration) Math.cos((Math::PI*input)/(2*duration.to_f)) end # for the duration of 1 sec, step every 1/44100 times and # write the value (0.0..duration.to_f).step(1/sample_rate) do |i| x = (10000 * Math.sin(2 * Math::PI * frequency * i) * envelope(i, duration)).to_i stream << [x,x] end
  • input
  •   x
  •   sin(duration)input
  •   x
  •   cos(duration)
  • input
  •   x
  •   sin(duration)input
  •   x
  •   cos(duration)
  • input
  •   x
  •   sin(duration)input
  •   x
  •   cos(duration)
  • Digital
  •   sound•Digital
  •   sound
  •   stored
  •   in
  •   compressed
  •   or
  •    uncompressed
  •   formats•Main
  •   uncompressed
  •   format
  •   is
  •   PCM
  •   (Pulse
  •   Code
  •    Modulation),
  •   stored
  •   as
  •   WAV
  •   (Windows)
  •   or
  •    AIFF
  •   (Mac)
  •   files•Standard
  •   form
  •   for
  •   all
  •   digital
  •   audio,
  •   digital
  •    representation
  •   of
  •   an
  •   analog
  •   signal
  • The
  •   WAV
  •   format•Audio
  •   file
  •   format
  •   for
  •   Windows,
  •   derived
  •   from
  •    RIFF
  •   (Resource
  •   Interchange
  •   File
  •   Format)•Bitstream
  •   format
  •   store
  •   data
  •   in
  •   chunks•Linear
  •   PCM
  •   encoded,
  •   2
  •   channels
  •   of
  •   44,100
  •    samples
  •   per
  •   second,
  •   16
  •   bits
  •   per
  •   sample
  • Create
  •   WAV
  •   files•Write
  •   manually
  •   using
  •   Array#pack
  •   (read
  •    manually
  •   using
  •   String#unpack)•Use
  •   the
  •   BinData
  •   library
  •   (http:// bindata.rubyforge.org)
  •   to
  •   represent
  •   WAV
  •    format
  •   and
  •   stuff
  •   data
  •   in•Data
  •   is
  •   just
  •   integers
  •   representing
  •   the
  •   sample
  • require bindataclass RiffChunk < BinData::Record int32be :chunk_id int32le :chunk_size int32be :formatendclass FormatChunk < BinData::Record int32be :chunk_id int32le :chunk_size int16le :audio_format int16le :num_channels int32le :sample_rate int32le :byte_rate int16le :block_align int16le :bits_per_sampleendclass DataChunk < BinData::Record int32be :chunk_id int32le :chunk_size array :stream do int16le :left int16le :right endendclass WavFormat < BinData::Record riff_chunk :riff_chunk format_chunk :format_chunk data_chunk :data_chunkend
  • class Wav SAMPLE_RATE = 44100 attr :wav, :file, :sample_rate, :format_chunk, :riff_chunk, :data_chunk def initialize(filename) @sample_rate = SAMPLE_RATE @file = File.open(filename, "wb") @riff_chunk = RiffChunk.new @riff_chunk.chunk_id = "RIFF".unpack("N").first @riff_chunk.format = "WAVE".unpack("N").first @format_chunk = FormatChunk.new @format_chunk.chunk_id = "fmt ".unpack("N").first @format_chunk.chunk_size = 16 @format_chunk.audio_format = 1 @format_chunk.num_channels = 2 @format_chunk.bits_per_sample = 16 @format_chunk.sample_rate = @sample_rate @format_chunk.byte_rate = @format_chunk.sample_rate * @format_chunk.num_channels * @format_chunk.bits_per_sample/2 @format_chunk.block_align = @format_chunk.num_channels * @format_chunk.bits_per_sample/2 @data_chunk = DataChunk.new @data_chunk.chunk_id = "data".unpack("N").first end
  • def write(stream_data) stream_data.each_with_index do |s,i| @data_chunk.stream[i].left = s[0] @data_chunk.stream[i].right = s[1] end @data_chunk.chunk_size = stream_data.length * @format_chunk.num_channels * @format_chunk.bits_per_sample/8 @riff_chunk.chunk_size = 36 + @data_chunk.chunk_size @wav = WavFormat.new @wav.riff_chunk = @riff_chunk @wav.format_chunk = @format_chunk @wav.data_chunk = @data_chunk @wav.write(@file) end def close @file.close endendwav_file = Wav.new(sine.wav)wav_file.write(stream)wav_file.close
  • Putting
  •   it
  •   all
  •    together
  • NOTES = %w(a ais b c cis d dis e f fis g gis)FREQUENCIES = { d4:-7, dis4:-6, e4:-5, f4:-4, fis4:-3, g4:-2, gis4:-1, a4: 0, ais4: 1, b4: 2, c5: 3, cis5: 4, d5: 5}# get the frequency of the pitchdef frequency_of(step) 440.0*(2**(step.to_f/12.0))endsample_rate = 44100.0 # 44100 Hzduration = 1 # 1 secstream = [] # data stream for left and right channelsfrequency = frequency_of(FREQUENCIES[:a4])# for the duration of 1 sec, step every 1/44100 times and# write the value(0.0..duration.to_f).step(1/sample_rate) do |i| x = (10000 * Math.sin(2 * Math::PI * frequency * i)).to_i stream << [x,x]endwav_file = Wav.new(sine.wav)wav_file.write(stream)wav_file.close
  • Domain
  •   specific
  •   language•Designed
  •   for
  •   a
  •   specific
  •   domain•Captures
  •   concepts
  •   of
  •   the
  •   domain
  •   including
  •    jargon•Is
  •   still
  •   Ruby!
  • Techniques
  •   used
  •   in
  •   Muse•DSL
  •   are
  •   methods
  •   in
  •   Song
  •   and
  •   Bar•Use
  •   instance_eval
  •   to
  •   evaluate
  •   music
  •   score•Use
  •   method_missing
  •   to
  •   catch
  •   names
  •   of
  •   notes
  •    and
  •   chords
  • class Song DSL
  •   are
  •   methods def self.record(name, options ={}, &block) ... def bar(id, options={}) ... @bars[id] << Bar.new(id, options) @bars[id].last end end class Bar def notes(&block) ... end end endrequire "muse"include MuseSong.record hotel_california, harmonic: guitar, bpm: 100 do bar(1).notes { d4 b:2; e4; fis4;} ...end
  • instance_evalclass Bar ... def notes(&block) instance_eval &block endendrequire "muse"include MuseSong.record hotel_california, harmonic: guitar, bpm: 100 do bar(1).notes { d4 b:2; e4; fis4;} ...end
  • method_missingclass Bar ... def method_missing(name, *args, &block) name = name.to_s if name.start_with? *NOTES ... end endendrequire "muse"include MuseSong.record hotel_california, harmonic: guitar, bpm: 100 do ... bar(3).notes { a3_d4_fis4; fis4; fis4 b:0.5;} ...end
  • Turk!h March
  • Wait,
  •   there’s
  •    more
  •   ...
  • Ottothe Algorithmic Composer
  • Algorithmic composition is thetechnique of using algorithms to create music - Wikipedia
  • Grab tweet from Twitter, create music from it
  • Scan a pictureGrab tweet from Twitter, create music from it
  • Get today’s weather report Scan a picture Grab tweet from Twitter, create music from it
  • The algorithm
  • Melody + Chords = Music
  • Grab tweet from Twittertext = Twitter.search("#reddot").last.text
  • Split tweet into wordswords = text.split
  • Convert each word into anumber using the touch tone keypad algorithm
  • KEYPAD = {a:2,b:2,c:2, d:3,e:3,f:3, g:4,h:4,i:4, j:5,k:5,l:5, m:6,n:6,o:6, p:7,q:7,r:7,s:7, t:8,u:8,v:8, w:9,x:9,y:9,z:9}def word_to_num(a_word) a_word.downcase.chars.inject("") { |memo,obj| memo + KEYPAD[obj.to_sym].to_s }end
  • Find the 7 most frequently found notesmusic = []words.each do |w| music << NOTES[note(w)]endsorted = music.counts.sort_by {|obj| obj[1]}.reversemost_frequent_7_notes = sorted.map {|obj| obj[0]}.first(7)
  • Determine the musical scale from the 7 notesMAJOR = { 0 => %w(c d e f g a b c d), 1 => %w(g a b c d e fis g a), 2 => %w(d e fis g a b cis d e), 3 => %w(a b cis d e fis gis a b), 4 => %w(e fis gis a b cis dis e fis), 5 => %w(b cis dis e fis gis ais b cis), 6 => %w(fis gis ais b cis dis f fis gis) }num_of_sharps = most_frequent_7_notes.inject(0) { |memo, obj| obj.end_with?("is") ? memo + 1 : memo }scale = MAJOR[num_of_sharps]
  • Get the chord progression for the scale (using I-IV-V)scale_chords = {}scale_chords[0] = ["#{scale[0]}", "#{scale[2]}", "#{scale[4]}"]scale_chords[1] = ["#{scale[3]}", "#{scale[5]}", "#{scale[7]}"]scale_chords[2] = ["#{scale[4]}", "#{scale[6]}", "#{scale[8]}"]
  • ✓Melody + Chords = Music
  • Start with the first note of the scale
  • Calculate the ‘distance’ between the current note and the nextdef distance(a_word) word_to_num(a_word).to_i % 5end
  • Determine if the next note goes ‘up’ or ‘down’ the scaledef direction(a_word) (word_to_num(a_word).to_i % 2) == 0 ? 1 : -1end
  • Get number of syllables for each word, each syllable is 1 beatdef syllables(a_word) word = a_word.downcase return 1 if word.length <= 3 word.sub!(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, ) word.sub!(/^y/, ) word.scan(/[aeiouy]{1,2}/).sizeend
  • Move from current note to next noteif direction(word) > 0 bar_notes << scale.next(distance(word))else bar_notes << scale.previous(distance(word))end
  • ✓ ✓Melody + Chords = Music
  • Break down into 4 words perbar, use Muse to write to WAV file
  • quadruplets.reverse.each_with_index do |quad, index| beats = 0.0 bar(index).notes { beats, bar_notes = [], [] quad.each do |word| beats << syllables(word).to_f/2 if direction(word) > 0 bar_notes << scale.next(distance(word)) else bar_notes << scale.previous(distance(word)) end end total_beats = beats.inject(:+) bar_notes.each_with_index do |n,i| note = n.chop octave = n[n.size-1] b = (beats[i]*4.0)/total_beats add_to_stream note_data(note, octave, b:b) end } bar(index, v:1.5).notes { ch = scale_chords[index % 3] chord = [ch[1], ch[2], ch[0]] add_to_stream note_data(ch[0].chop, 3, b:1) add_to_stream chord(chord, b:1) add_to_stream chord(chord, b:1) add_to_stream chord(chord, b:1) }
  • Demo
  • @sausheong+Sau Sheong Changsausheong@gmail.comsausheong@hp.comhttp://blog.saush.com