Using Sonic Pi To Play Music With Ruby

Engineering Insights

Kevin Murphy
#
Min Read
Published On
March 13, 2025
Updated On
March 17, 2025
Using Sonic Pi To Play Music With Ruby

Ruby Software Design Concert Series

  1. Dependency Injection: Plug In
  2. Shedding a Light on Duck Typing
  3. Synthesizing A Composition With Delegation
  4. Inheritance: Derivative Songwriting
  5. Using Sonic Pi To Play Music With Ruby
  6. Stringing Code Together To Play Music

Setting the Stage

My RubyConf 2020 talk about Ruby's Coverage module uses examples about playing live music. As such, I had the ambitious goal of delivering a live performance of some music during the presentation. This ended up getting cut for a variety of reasons (time, concern about the audio working on the streaming platform, the reality of ambition turning into actual work to do), but I built out the structure to support this for one instrument, the guitar. This is the first of two posts that'll describe the work that I did to support this.

First, I had to figure out if it was possible to make this happen. I wanted to hook into my existing code samples and trigger musical notes from them somehow. As such, I decided to build my first amplifier, virtually, without fear of blowing up any capacitors.

Parts List

In the earlier post on dependency injection, I created a PracticeAmplifier class that did nothing so I could use it in tests, rather than the "regular" amplifier.

What the "regular" amplifier does is interface with Sonic Pi, which is awesome software that'll make sound and music driven by code. Sonic Pi comes with an IDE of sorts that you can use to program the composition you'd like to play, and get immediate feedback from hearing how your code is translated into audio. It's a great way to lose track of time for a night or two (or more). However, I was envisioning controlling my audio from the code examples directly. I didn't want to have to work within the IDE.

To get around using the IDE directly, I found the sonic-pi-cli gem. Its principal use case is to be used directly in the terminal. However, it's a gem, and written in ruby, and the core functionality is available in a class that you can use in any of your code.

Wiring Schematic

With enough knowledge and conviction to be dangerous, I set out wiring up my amplifier. The CLI requires that Sonic Pi itself is running, and first ensures it can communicate with it - and to do so, it needs to know what port the software is running on. Sonic Pi used to always run on the same port; however, it has since changed to run on a dynamically-determined port.

The CLI already implemented the functionality to find the port to send to the SonicPi class, so for demonstration purposes, I copied that in my initializer.

class Amplifier
  def initialize
    @port = find_port
    @speaker = SonicPi.new(@port)
  end

  private

  def find_port
    # Code from sonic-pi-cli
  end
end

Needing to find the port is now something that the SonicPi class can do by itself as of version v0.2.0; however, this work preceded that.

The rest of the functionality in the Amplifier class is now to delegate commands to the @speaker.

class Amplifier
  def play(sound)
    @speaker.run(sound)
  end
end

Rock On

Using this amplifier still requires knowing all the correct commands to send to Sonic Pi, and Sonic Pi must be running; however, we can now trigger it to execute these commands from outside of its IDE. We have a way to send sound out of our ruby code.

In our next post, we'll take a look at how we generate the sound to send from a guitar to an amplifier.

Learn more about how The Gnar builds Ruby on Rails applications.

Written by
Kevin Murphy
, The Gnar Company

Related Insights

See All Articles
Engineering Insights
Why Your AI Coding Agent Keeps Making Bad Decisions (And How to Fix It)

Why Your AI Coding Agent Keeps Making Bad Decisions (And How to Fix It)

AI coding agents making bad decisions? The frustration comes from two fixable problems: assumptions and code quality. Here's how to get consistently good results.
Product Insights
From Dashboards to Decisions: Why Traditional BI Can't Keep Up

From Dashboards to Decisions: Why Traditional BI Can't Keep Up

Stop waiting days for dashboards. Learn how BI2AI uses LLMs and RAG to eliminate the analyst bottleneck and turn complex data into instant executive decisions.
Product Insights
Are Your Legacy Systems Bleeding You Money?

Are Your Legacy Systems Bleeding You Money?

Technical debt now accounts for 40% of IT balance sheets, with companies paying a 10-20% surcharge on every new initiative just to work around existing problems. Meanwhile, organizations with high technical debt deliver new features 25-50% slower than competitors. Features on your six-month roadmap? They're shipping them in three weeks.
Previous
Next
See All Articles