The Song Type

The Song type is the top level data structure containing all elements required to define a song. Song is pure data — it holds no mutable state and has no side effects.

case class Song(
    title: Title,
    tempo: Tempo = Tempo(120),
    swing: Swing = Swing(0),
    mixer: Mixer
)

The title, tempo and swing, should be self explanatory and take provided types wrapping a string and integers for these values.

NOTE: Swing is yet to be implemented.

The Mixer Type

Mixer is where we will place all our Tracks and this will simulate the effect of a real world mixing desk, where we can simultaneously play and manipulate audio on multiple tracks.

case class Mixer(tracks: NonEmptyList[Track[?]])

NOTE: The NonEmptyList type is a type from the cats library which ensures that the list is never empty.

What is a mixing desk?

The Track Type

Tracks in turn, are where we will place:

case class Track[Settings](
    title: Title,
    musicalEvent: MusicalEvent,
    instrument: Instrument[Settings],
    playback: Playback,
    customSettings: Option[Settings] = None,
    insertFX: List[FX] = List.empty,
    sendFX: List[FX] = List.empty)

Playing a Song with the Sequencer

Song is pure data — to play it, wrap it in a Ref[IO, Song] and pass it to a Sequencer:

for
  songRef   <- Ref.of[IO, Song](mySong)
  sequencer <- Sequencer(songRef)
  _         <- sequencer.play()
yield ()

The Ref enables real-time updates to any song property while it's playing:

sequencer.updateSong(_.copy(tempo = Tempo(140)))

Next Step: Musical Events