Maple Leaf Rag with Clojure Sound

Need help with your custom Clojure software? I'm open to (selected) contract work.

June 25, 2022

Please share: .

These books fund my work! Please check them out.

I've just noticed that it's been one year since the last post on this blog. It's not because I've been lazy. I had just finished two books (check them out!) and I probably suffered a bit from the writer's exhaustion afterwards. I'm joking. I wasn't that exhausted; I've been busy with programming. Thanks to generous support from Clojurists Together, and thanks to people who bought or subscribed to my books, I've been able to take on some bigger chunks of time to work on Uncomplicate libraries, old and new. OK, enough talking; you're not here to read my reports, but to see some nice Clojure code.

A couple of months ago I released a new library - Clojure Sound(), but I haven't had time to write about it. Now that it's time to revive my blog, is there a better way to start than with music?

How to access Clojure Sound

First I'll show you a basic code example and then I'll tell you what Clojure Sound is some other time. It's a programming library, available from Clojars. Include Clojure Sound in your project.clj by adding [org.uncomplicate/clojure-sound "0.1.0"] to :dependencies. Start the REPL and connect your preferred Clojure editor.

First, we require the appropriate namespaces. In this example, we're using functions from core and midi.

(require '[uncomplicate.clojure-sound
             [core :refer :all]
             [midi :refer :all]])

Playing a nice song with virtual piano

We're now going to instruct a virtual piano on our computer to play something. But, instead of playing a few random notes, we would like to hear a wonderful song. I've chosen a cheerful Maple Leaf Rag. You might think: "What song is that?", but I can bet that you'll instantly recognize it as soon as you hear your REPL sing (I hope that you follow along by typing the code yourself).

There's plenty of ways to specify the notes that our virtual piano should play. Since I'm lazy, I'll take a score that already specifies all details in a MIDI file. Since the song is 123 years old, it's in public domain, so there are many free MIDI transcriptions. Please find a version on the Internet, rename the file to maple.mid, and put it in the classpath of your project. If you're not feeling adventurous, you can use the one I'm using.

The data that should be played is stored in sequences. Not Clojure sequences, but sound sequences (sorry about this name duplication, but that's a general domain-specific term). We can make a sequence in several ways, from adding each note programmatically, to loading the sequence stored in a MIDI file that someone else prepared. I access the file as a Clojure resource, and give it to Clojure Sound sequence function.

(def maple (sequence (clojure.java.io/resource "maple.mid")))
maple
#'user/maple
{:ticks 110592, :id 1336053642}

Now, we have a sequence object that contains more than 100.000 ticks (basically, things that our piano has to do). But, the music sheet is not going to play itself. It needs someone to see the actual instructions, and to press the right places at the instrument. This is what sequencer is for. Sequencer as just like a player. When a real person plays the instrument, that real person is the sequencer. In Clojure Sound, the sequencer function creates that player. In this example, I'll use the default sequencer, which already has its default instruments collection.

(def sqcr (sequencer))
#'user/sqcr

While sequencer plays music, it has to access the sound card on your computer, and produce side-effects, the actual sounds that you enjoy. That requires careful opening and closing protocols, so it interferes with other multimedia software on your computer as little as possible. Before we use our sequencer, we have to open it.

(open! sqcr)
:class RealTimeSequencer :status :open :micro-position 0 :description Software sequencer :name Real Time Sequencer :vendor Oracle Corporation :version Version 1.0

Now, we tell our sequencer to take the music sheet (the maple sequence) that it's going to perform.

(sequence! sqcr maple)
:class RealTimeSequencer :status :open :micro-position 0 :description Software sequencer :name Real Time Sequencer :vendor Oracle Corporation :version Version 1.0

Finally, we're ready to rock rag!

(start! sqcr)
:class RealTimeSequencer :status :open :micro-position 0 :description Software sequencer :name Real Time Sequencer :vendor Oracle Corporation :version Version 1.0

Normally, the Maple's gonna rag for 2-3 minutes. If you'd like to stop it, here's how to do it.

(stop! sqcr)
:class RealTimeSequencer :status :open :micro-position 145833 :description Software sequencer :name Real Time Sequencer :vendor Oracle Corporation :version Version 1.0

Finally, when you're tired from dancing, you can tell the player to go get some rest, too:

(require '[uncomplicate.commons.core :refer [close!]])
(close! sqcr)
:class RealTimeSequencer :status :closed :micro-position 0 :description Software sequencer :name Real Time Sequencer :vendor Oracle Corporation :version Version 1.0

That's it, I hope you're enjoyed this little demonstration. I'll be back with more music! :)

Maple Leaf Rag with Clojure Sound - June 25, 2022 - Dragan Djuric