All Downloads are FREE. Search and download functionalities are using the official Maven repository.

compose.player.ScalaColliderPlayer.scala Maven / Gradle / Ivy

package compose.player

import compose.core._
import de.sciss.synth._
import de.sciss.synth.ugen.{ Pitch => _, _ }
import de.sciss.synth.Ops._
import java.util.{Timer, TimerTask}
import scala.concurrent.{ExecutionContext => EC, _}

object ScalaColliderPlayer {
  val Freq = "freq"
  val Amp  = "amp"

  val sine = SynthDef("sine") {
    val freq = Freq.kr(440)
    val amp  = Amp.kr(0.0)
    val osc  = SinOsc.ar(freq, 0.0) * amp
    Out.ar(0, List(osc, osc))
  }

  def withPlayer[A](numChannels: Int = 4, synthDef: SynthDef = sine)(func: ScalaColliderPlayer => A) =
    Server.run { server =>
      val player = new ScalaColliderPlayer(numChannels, synthDef, server)
      try {
        func(player)
      } finally {
        player.free
      }
    }

  case class State(
    score     : Score,
    available : Seq[Synth],
    playing   : Map[Int, Synth]
  )
}

class ScalaColliderPlayer(numChannels: Int, synthDef: SynthDef, server: Server) extends Player[ScalaColliderPlayer.State] {
  import ScalaColliderPlayer._
  import Command._

  val timer = new Timer()
  val channels = (0 to numChannels).map(_ => synthDef.play())

  def free: Unit =
    channels.foreach(_.free)

  def initialise(score: Score): Future[State] =
    Future.successful(State(score, channels, Map.empty))

  override def shutdown(state: State): Future[State] =
    Future.successful(state.copy(available = Nil, playing = Map.empty))

  def playCommand(state: State, cmd: Command)(implicit ec: EC, tempo: Tempo): Future[State] = {
    cmd match {
      case NoteOn(id, pitch) =>
        state.available match {
          case Seq() => Future.successful(state)

          case channel +: remaining =>
            channel.set(Amp -> 1.0, Freq -> pitch.frequency)
            Future.successful(state.copy(available = remaining, playing = state.playing + (id -> channel)))
        }

      case NoteOff(id) =>
        state.playing.get(id) match {
          case Some(channel) =>
            channel.set(Amp -> 0.001)
            Future.successful(state.copy(available = state.available :+ channel, playing = state.playing - id))

          case None =>
            Future.successful(state)
        }

      case Wait(dur) =>
        val promise = Promise[State]
        val task = new TimerTask {
          def run(): Unit = promise.success(state)
        }
        timer.schedule(task, tempo.milliseconds(dur))
        promise.future
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy