blob: 932614bbc58109dc6bdd1501b5f971b213291eed [file]
import { Layout, Context } from "rive/host";
import { Audio, AudioHandle, AudioProcessor } from "rive/audio";
import { LaneState } from "rive/lanes";
import { Ring, Snapshot } from "rive/shared";
export class Gain {
value: f64 = 0;
}
// What the main lane hands a ramp: gains in, a meter back.
export class Link {
gains: Ring<Gain> = Ring.create<Gain>(256);
meter: Snapshot<Meter> = Snapshot.create<Meter>();
}
export class Meter {
frames: f64 = 0;
gain: f64 = 0;
}
// A ramp in period 100 frames on the engine's clock, scaled by the latest
// gain the main lane sent, right channel inverted. Its output depends only
// on its messages and startFrame, so hosts can check it exactly. A block
// reads and publishes through values made up front, so it never allocates.
export class Ramp extends AudioProcessor<Link> {
link: Link? = null;
gain: f64 = 1;
frames: f64 = 0;
next: Gain = new Gain();
meter: Meter = new Meter();
override init(sampleRate: f64, maxFrames: i32, channels: i32, link: Link): void {
this.link = link;
console.log("ramp init " + channels.toString() + " channels");
}
override process(out: StaticArray<f32>, frames: i32, startFrame: f64): void {
let link = this.link!;
while (link.gains.receiveInto(this.next)) {
this.gain = this.next.value;
}
for (let i = 0; i < frames; i++) {
let value = <f32>(this.gain * (((startFrame + <f64>i) % 100) / 100));
out[i * 2] = value;
out[i * 2 + 1] = -value;
}
this.frames += <f64>frames;
this.meter.frames = this.frames;
this.meter.gain = this.gain;
link.meter.publish(this.meter);
}
}
export class Thrower extends AudioProcessor<string> {
message: string = "";
override init(sampleRate: f64, maxFrames: i32, channels: i32, message: string): void {
this.message = message;
}
override process(out: StaticArray<f32>, frames: i32, startFrame: f64): void {
throw new Error(this.message);
}
}
export class Main extends Layout {
context: Context? = null;
link: Link = new Link();
ramp: AudioHandle? = null;
thrower: AudioHandle? = null;
reportedRunning: bool = false;
reportedThrow: bool = false;
reportedMeter: bool = false;
override init(context: Context): bool {
this.context = context;
let gain = new Gain();
gain.value = 2.0;
this.link.gains.send(gain);
this.ramp = Audio.start(() => new Ramp(), this.link);
this.thrower = Audio.start(() => new Thrower(), "bad block");
return true;
}
override advance(seconds: f64): bool {
let context = this.context!;
let ramp = this.ramp!;
if (!this.reportedRunning && ramp.state == LaneState.running) {
this.reportedRunning = true;
context.log("processor running");
}
let failed = this.thrower!.failed;
if (!this.reportedThrow && failed != null) {
this.reportedThrow = true;
context.log("processor threw " + failed);
}
let meter = this.link.meter.latest();
if (!this.reportedMeter && meter != null && meter.value.frames >= 512) {
this.reportedMeter = true;
context.log("processor meter " + meter.value.frames.toString() + " gain " + meter.value.gain.toString());
}
return true;
}
}