{hhr, 11-nov-2018}

The first version of Körper β uses as "sound organ" a quickly hacked patch based on LucreSynth, the ephemeral coupling between ScalaCollider and SoundProcesses. A custom programme is run that sets up an OSC node, builds an in-memory structure for the trajectories, creates an ad-hoc map of the timbre space, and creates an ad-hoc sound player responding to an `update` call from the OSC node.

Körper β:

Reimplementing the Piece

---

meta: true

project: Koerper

keywords: []

---

The OSC node combines state with the receiver function, e.g.


var lastFrame = Map.empty[Int, Traj]
var nextFrame = Map.empty[Int, Traj]
val cyclicIds0 = Set(0 until config.maxNumTraj: _*)
var cyclicIds = cyclicIds0
var idMap = Map.empty[Int, Int]

rcv.action = { (p, _) =>
  p match {
    case osc.Message("/f_new") =>
      val currentFrame = nextFrame
      lastFrame = currentFrame
      nextFrame = Map.empty
      val usedCyclic = currentFrame.valuesIterator.map(_.cId).toSet
      cyclicIds = cyclicIds0 -- usedCyclic
      soundOpt.foreach { sound =>
       val f1: Map[Int, Traj] = if (currentFrame.size >= config.minNumTraj) currentFrame else Map.empty
       system.step { implicit tx =>
         sound.update(f1)
  }
  }
 
      if (config.gui) Swing.onEDT {
        views.foreach(_.update(currentFrame))
      }

    case osc.Message("/t_set", id: Int,
...

 

The basic OSC node could be implemented using the `OscNode` object from Körper α - a plain object that is configured (port, IP, receiver `Action`) through its attribute map.


The problem with that approach is that there is no shared state between action response invocations.


Notice how the OSC receiver behaves very similar to an Akka actor. We have mutable state isolated and the invocation is single-threaded, so there is no problem maintaining state this way. Would it thus make sense to introduce an `Actor` object, analogous to `Action`, thus with Turing complete Scala program that evaluates to a `Receive` partial function? An imported DSL could add a simple interface to issue transactions and invoke auxiliary objects.


It doesn't seem reasonable to invent another AST style DSL for writing an actor body, right? We have mostly imperative stuff here that manipulates the state. Of course, we could also envision a purely functional state machine, but that will be equally difficult to reshape into a compiled AST different from plain Scala. We need to ensure there is no dependency on Akka API, as we want to be able to update to new versions without breaking the binary compatibility of the actor "program".

The `BetaSound` is much more tricky. First of all, sounds rely on `FreeSelfWhenDone` and maintenance through `syn.onEnd`. A kind of automatic voice management is in place, so `update` begins by distinguishing between a foreground and background action, depending on the arrival of a particular trajectory length (input argument).

 

`playFg` queries a timbre from a spatial index. This we could implement through an `Obj` version of `SkipOctree` (look into Imperfect Reconstruction). It chooses a channel based on `fgSoundMap`.

 

`updateBg` tracks an ongoing synth through the trajectory identifier and bgSoundMap`. If not found, a new sound element is created, with timbre search as before, and other parameters from the trajectory coordinate. If found, it's parameters are updated.

 

Furthermore, two "foreground" synths are run with `ReplaceOut`, one a regulator, the other simply a limiter and leak-dc. The regular has a laggy amplitude control based on a synth "count".

 

- - - - - - -

 

Could this be formalised as a pattern? Obviously we can't have strict pull style, because the OSC message pushes new events. But we could perhaps have a simple layer on top of patterns such that we pull once a new input value is provided at the source, i.e. then use the standard bottom up approach. From the OSC node we could imagine something akin to `Bind` to provide in-memory values.

 

Then the obstacles are

  • the missing `set` type for pattern events (to adjust running background synths)
  • a way to track the duration of the synths (we could use done-action and feeding back into the actor object and its state)

 

Without a 'set' type event, we could imagine that the background-updates are actually auxiliary patterns to a main pattern; that is to say, a parameter in the main bind is specified as a (nested) pattern itself.

 

What makes this complicated is that this sub-pattern again must be "push" driven.

A question for `Actor` is whether we really want Akka Actor and plain mutable state, or we remain on the Txn level. The latter would have as advantage:


  • "state body" code could be executed transactionally and thus have an easy hold on the attribute map for initialisation
  • no need to wrap in `atomic` or similar when deferring to other objects, such as invoking an auxiliary action

Possible disadvantages:

  • we need to expose `scala.concurrent.stm.Ref` and the like, making it more vulnerable to binary compatibility breakage (Scala 2.12 -> Scala 2.13 ; although that will probably be a problem in any case)
  • performance penality compared to plain mutable?
  • no true multi-threading

- - - -


Note that `BetaSound` already uses `TMap` etc.