Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Playing with Music: Ruby and Sonic Pi

Josep Egea
November 14, 2022

Playing with Music: Ruby and Sonic Pi

Sonic Pi is a fantastic tool for playing music and learning to code. And it uses Ruby!!!

In this talk we'll have a quick overview of Sonic Pi's capabilities and explore how much it can benefit from the power and flexibility provided by Ruby.

And we'll try to play some music while we're at it!!

A Madrid.rb talk: https://www.madridrb.com/events/october-2022-playing-with-music-ruby-and-sonic-pi-715

Josep Egea

November 14, 2022
Tweet

More Decks by Josep Egea

Other Decks in Programming

Transcript

  1. THANKS FOR COMING!!! Thanks to members of Madrid.rb Thanks to

    people from outside of Madrid! Thanks to our Sponsors! We ❤️ you!
  2. I'M NOT A MUSICIAN!! But I do love music!! Specially,

    I love to play with music And I love to play with Ruby So Ruby and Music mixed must be super fun!!
  3. WITH MUCH TO LOVE ABOUT IT A completely self-sufficient music

    environment Open source Oriented to education It uses Ruby (and many other cool technologies!!!)
  4. SOME OF ITS FEATURES It can synthesize sounds It can

    work with samples It has convenient music helpers It can apply great effects It can manage timing and concurrence It can interact with other instruments
  5. APPLYING EFFECTS use_bpm 120 with_fx :reverb, room:1 do 3.times do

    use_synth :blade use_synth_defaults amp: 1 play :c sleep 1 play :e sleep 1 play :g sleep 2 end end
  6. MANAGING CONCURRENCE in_thread do c = chord :c, :major loop

    do play c.pick sleep 0.5 end end in_thread do loop do sample :drum_bass_hard sleep 1 sample :drum_snare_hard sleep 1 end end
  7. INTERACTING WITH OTHER INSTRUMENTS use_bpm 90 in_thread do loop do

    use_real_time note, velocity = sync "/midi:lpk25:1/note_on" synth :piano, note: note, amp: velocity / 127.0 end end in_thread do loop do sample :drum_bass_hard sleep 1 sample :drum_snare_hard sleep 1 end end
  8. MAKING IT MORE INTERESTING manual_beats = { 48 => :drum_bass_hard,

    50 => :drum_snare_hard, 52 => :drum_splash_hard } in_thread do loop do use_real_time note, velocity = sync "/midi:lpk25:1/note_on" if manual_beats[note] sample manual_beats[note] else synth :piano, note: note, amp: velocity / 127.0 end end end
  9. OR EVEN MORE … live_audio :guitar, input: 1, amp: 1

    with_fx :lpf, cutoff: 115 do with_fx :distortion, distort: 0.99 do live_audio :guitar, input: 1, amp: 1 end end
  10. DJ PERFORMANCES Sam Aaron live coding an ambient electro set

    w/ Sonic Pi Sam Aaron live coding an ambient electro set w/ Sonic Pi
  11. LET'S START WITH A RIFF song_tick = 0.125 riff_notes =

    [:g5, :a5, :fs6, :a5, :g5, :a5, :d6, :a5].ring riff_note_duration = song_tick * 2 riff_idx = 0 use_synth :supersaw live_loop :riff do note = riff_notes[riff_idx] play note sleep riff_note_duration riff_idx += 1 end
  12. LET'S ADD SOME DRUMS live_loop :drums_1 do sample :drum_bass_hard sleep

    song_tick * 4 end live_loop :drums_2 do sleep song_tick * 4 sample :drum_snare_hard sleep song_tick * 4 end
  13. A BASS LINE… chord_values = [:D2, :C2, :G1, :D2].ring chords

    = chord_values.map { |c| chord(c, :major) }.ring bass_idx = 0 live_loop :bass do use_synth :bass_foundation chord_notes = chords[bass_idx / 16] play chord_notes.pick sleep song_tick * 2 bass_idx += 1 end
  14. MAYBE SOME STRINGS string_chord_values = [:D4, :C4, :G3, :D4].ring string_chords

    = string_chord_values .map { |c| chord(c, :major) }.ring strings_idx = 0 live_loop :strings do use_synth :zawa chord_notes = string_chords[strings_idx] play chord_notes, amp: 2, range: 10, phase: 2, attack: 1, sustain: song_tick * 30 sleep song_tick * 32 strings_idx += 1 end
  15. WHAT IF WE WANT A MORE COMPLEX RHYTHM? def make_bool_ring(pattern)

    pattern.chars .reject { |c| c == '|' } .map { |c| c == '*' } .ring end alias mbr make_bool_ring def sample_pattern_live_loop(*args, **kwargs) sample_patterns = kwargs.delete(:sample_patterns) sample_tick = kwargs.delete(:sample_tick) sample_idx = 0 live_loop(*args, **kwargs) do sample_patterns.each do |sample_key, pattern| sample sample_key if pattern[sample_idx] end sleep sample_tick sample_idx += 1 end end drum_patterns = {
  16. AND HOW DO WE COMBINE OUR PIECES? DEFAULT_TIME_PARTS = {

    half_pulse: 2, pulse: 2, bar: 4, chord: 2, verse: 4 } class SongController < Object attr_accessor :time_parts, :song_parts, :tick_name, :tick_time def initialize(time_parts: DEFAULT_TIME_PARTS, song_parts:, tic @time_parts = time_parts @song_parts = song_parts @tick_name = tick_name @tick_time = tick_time @total_ticks = time_parts.values.reduce(1, &:*) end def run(start_part = 0) @idx = start_part * @total_ticks $sonic_pi.live_loop @tick_name, delay: 1 do $sonic_pi.cue @tick_name
  17. DID I DO MY JOB? Are you more in love

    with Ruby? Did you have fun?