Compositional music composition using Scala, SuperCollider, and Web Audio.
Scala Other

README.md

Compose

A compositional music composition library.

Copyright 2015-2016 Dave Gurnell. Licensed Apache 2.

Build Status Coverage status Maven Central Join the chat at https://gitter.im/underscoreio/scala

Overview

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 using compose-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.

Running a Quick Demo

On the JVM:

  1. Install SuperCollider 3.7+
  2. Set the environment variable SC_HOME to point to Content/Resources inside your SuperCollider directory:

    export SC_HOME=/path/to/SuperCollider.app/Content/Resources
  3. Run sbt demoJVM/run

In Javascript:

  1. Compile the JS version with sbt demoJS/fastOptJS
  2. .

Using Compose as a Library

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!

Sample Credits

The samples in the samples directory are sourced from the following places:

  • bell.wav, beep1.wav, and beep2.wav created with Ableton Live;
  • kick.wav, snare.wav, and hat.wav from 808 Trapstep Volume 1 by [TRISAMPLES][trisamples];
  • meow.wav by Mr Smith, sourced from soundbible.com.