Score

External Audio — a sound file played by another program

audio(uid:tape, src:"tape.wav", engine:external)

Oscilla draws the file's waveform, but does not play it. Another program plays it: SuperCollider, Pd, Max, or anything else that speaks OSC. That program reports where it is, and the cursor on the waveform shows that position.

Try it: the demo demo-tape-sync. Start one of the engines below, then press play. The playhead and the engine's cursor should cross every click on the waveform together. A gap between them is the error, in plain sight.

Why an external engine

What the browser keeps: nothing to install, and it works on a performer's phone alone. engine:external is a choice per cue.

Engines

The engines live in the demo project's folder, in demo-tape-sync/sc/ and demo-tape-sync/pd/, beside its score. They come along when you duplicate the demo into your own projects.

Engine File Notes
SuperCollider oscilla-tape.scd Several tapes at once, 1–8 channels, every message including rate (varispeed, reverse). Evaluate the whole file.
Pd vanilla 0.51+ oscilla-tape.pd One tape, streamed from disk with readsf~ (sample-exact at any length). Plays at 1×: rate is ignored. Run Pd at the file's sample rate.
Max (recipe below) Not yet tested. Build it from the protocol.

The Pd patch is generated by scripts/gen-oscilla-tape-pd.py: change the script and run it, rather than editing the patch's numbered connections by hand.

Both example engines report their position 20 times a second.

Parameters

Parameter Description
engine:external Played by the external engine (ext is the same).
latency: Milliseconds the cursor is drawn ahead of the reported position (negative: behind). See Latency.
speed: Sent as rate; it can follow() a signal.
amp: Sent with play, and as amp when it changes.
loop: 1 once, N times, 0 for ever: the engine does the looping.
in: Start position in seconds.

The cue otherwise behaves like any audio(): it starts when the playhead enters it and stops when it leaves, and hush() and stop cues reach it.

Published signals: <uid>.sec (the engine's position in seconds) and <uid>.t (0–1 through the file). Other cues can follow the sound's real position, e.g. follow(tape.t).

The cursor is dashed while no report has arrived, or none for a quarter of a second (the engine stopped, crashed or isn't running). It then stays where it was rather than run on.

The protocol

Oscilla → engine (UDP, Oscilla's --osc-out port, default 57120):

Address Arguments
/oscilla/tape/play uid, path, startSec, rate, loop, relPath, amp
/oscilla/tape/stop uid, fadeSec
/oscilla/tape/seek uid, sec
/oscilla/tape/rate uid, rate
/oscilla/tape/pause uid
/oscilla/tape/resume uid
/oscilla/tape/amp uid, amp

Engine → Oscilla (UDP, Oscilla's --osc-in port, default 57121):

Address Arguments
/<uid>/pos sec, rate
/<uid>/end (none): it has finished, optional

Several browsers on one score: every browser runs the same cue. The server passes on the first of each event (play, stop …) for each sound and drops the repeats that arrive within 300 ms, so the engine hears each play once, whichever browser sends it.

Max (untested recipe)

The protocol needs only standard objects:

  1. [udpreceive 57120] → [route /oscilla/tape/play /oscilla/tape/stop …].
  2. play: [unpack s s f f i s f]:
    • the path → [prepend open] → [sfplay~ 2]
    • start → [* 1000] → seek $1
    • then 1 to start
    • keep the uid for the reports
  3. Report on a [metro 50]: the position (your own count, or a position outlet) as /<uid>/pos sec rate, through [sprintf /%s/pos] and [udpsend 127.0.0.1 57121].
  4. End: on sfplay~'s end-of-file bang, send /<uid>/end.

If you build and test one, please send it in: it belongs next to the others.

Latency

A report says what the engine is computing; it is heard after the audio output latency (the engine's block plus the interface buffer, a few ms). The cursor is seen after the display's own lag (a frame, 17 ms at 60 Hz, plus the screen's). Measure once per setup:

  1. Play the demo's click file and film the screen and the speaker with a phone at 240 fps.
  2. Compare the frame where the cursor crosses a click on the waveform with the click's onset in the video's sound.
  3. If the picture is late, set latency: to the difference (positive); if early, negative.

Wi-Fi adds delay to a performer's tablet that no setting removes. For the tightest work, use a wired client or the laptop's own screen.

Locked to the score

A tape started by the playhead is kept where the playhead says it should be: its start point plus the playhead's distance into the element, in the score's time. It is this window's playhead that counts, the line you see, so the cursor sits on it even while the playhead is still easing into step with the server after a pause or a seek.

The cursor always shows what the engine reports, so whatever error is left is in plain sight.

Measured (one machine, SuperCollider, 60 frames a second): after the first two seconds the tape stays within 3.3 ms of the score at the median, 8.4 ms at the 95th percentile and 11 ms at worst, with no bias (−0.9 ms on average). One seek at the start; then about two rate messages a second.

Starting and seeking. The tape starts where the score is in it: entering the box plays it from the top; pressing play with the playhead already inside it, or seeking or jumping into it, plays it from that point. A seek within the box moves the tape with it; a seek out of the box stops it.

Not yet

See also

Tip: use ← → or ↑ ↓ to navigate the docs