A compositional music composition library.
Copyright 2015-2016 Dave Gurnell. Licensed Apache 2.
Compose is a library for writing songs using functional programming. It is split into a number of subprojects:
compose-core
- data structures and DSLs for building songs;compose-examples
- example songs written usingcompose-core
;compose-player
- players for the JVM and ScalaJS;compose-demo
- local project for quick experimentation (not published to Bintray).
You can read more about Compose on the Underscore blog.
Compose is cross-built for the JVM and ScalaJS. The JVM player uses ScalaCollider and Supercollider to play songs using a synthesizer (a sine wave by default). The ScalaJS player uses the web audio API to play songs using samples.
On the JVM:
-
Install SuperCollider 3.7+
-
Set the environment variable
SC_HOME
to point toContent/Resources
inside your SuperCollider directory:export SC_HOME=/path/to/SuperCollider.app/Content/Resources
-
Run
sbt demoJVM/run
In Javascript:
- Compile the JS version with
sbt demoJS/fastOptJS
- .
If you're working on the JVM, you can add Compose to your SBT build as follows:
libraryDependencies ++= Seq(
"io.underscore" %% "compose-core" % "<<VERSION>>",
"io.underscore" %% "compose-player" % "<<VERSION>>",
"io.underscore" %% "compose-examples" % "<<VERSION>>"
)
If you're working in ScalaJS, use the following settings instead:
libraryDependencies ++= Seq(
"io.underscore" %%% "compose-core" % "<<VERSION>>",
"io.underscore" %%% "compose-player" % "<<VERSION>>",
"io.underscore" %%% "compose-examples" % "<<VERSION>>"
)
Once you've added Compose to your build, you can write songs as follows:
import compose.core._
val song =
Note(Pitch.C3, Duration.Quarter) ~
Note(Pitch.E3, Duration.Quarter) ~
Note(Pitch.G3, Duration.Quarter) ~
Note(Pitch.G3, Duration.Whole)
There are numerous shortcuts and conversions to make this code easier to write. Check out the examples for inspiration.
You can play your song on the JVM as follows:
import compose.player._
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.{ Duration => Dur }
// Create a player:
ScalaColliderPlayer.withPlayer(4) { player: ScalaColliderPlayer =>
// Start the song playing:
val playing: Future[ScalaColliderPlayer.State] =
player.play(song, Tempo(180))
// Wait for the song to finish:
Await.result(playing, Dur.Inf)
}
On ScalaJS, the code looks like the following:
import compose.player._
import scala.concurrent.Await
import scala.concurrent.duration.{ Duration => Dur }
import scalajs.concurrent.JSExecutionContext.Implicits.queue
// Create a player:
val player: WebAudioPlayer = new WebAudioPlayer()
// Start the song playing:
val playing: Future[WebAudioPlayer.State] =
player.play(song, Tempo(180))
// Wait for the song to finish:
Await.result(playing, Dur.Inf)
That's all. If you have any questions, please ask on Gitter. Happy composing!
The samples in the samples
directory are sourced from the following places:
bell.wav
,beep1.wav
, andbeep2.wav
created with Ableton Live;kick.wav
,snare.wav
, andhat.wav
from 808 Trapstep Volume 1 by [TRISAMPLES][trisamples];meow.wav
by Mr Smith, sourced from soundbible.com.