diff --git a/dev/src/dsp-definitions/43-c4-blops-live.ts b/dev/src/dsp-definitions/43-c4-blops-live.ts index e5de8e7..62b2634 100644 --- a/dev/src/dsp-definitions/43-c4-blops-live.ts +++ b/dev/src/dsp-definitions/43-c4-blops-live.ts @@ -2,7 +2,48 @@ import type { DspDefinition } from "../types"; const dsp = ` import("stdfaust.lib"); -process = _ <: _,_; +import("stdfaust.lib"); + +// consts +peak = 0.65; +threshold = peak * 0.05; +highest_hz = 1320.0; +shortest_cycle = ma.SR / highest_hz; +key_latch_start = ma.SR * 0.001 * 30; +key_latch_end = ma.SR * 0.001 * 60; + +// utils +zero_crosses(x) = (x' > 0) != (x > 0); +crossed_up(x) = (x' < 0) & (x >= 0); +safe_divide(num, denom) = num / max(denom, 1) - num * (denom == 0); +is_first_tick = ba.time == 0; +is_note_on = _ : an.amp_follower_ar(0.00001, 0.01) > threshold; + +fx(x) = osc +with { + note_on = is_note_on(x); + note_on_time = ba.countup(10000, 1 - note_on); + + crosses = x : *(note_on) : crossed_up; + crosses_on = crosses + (1 - note_on); + cycle = ba.countup(10000, crosses_on') : ba.sAndH(crosses_on); + key = ba.if(cycle < shortest_cycle, 0.0, (ma.SR / cycle) : ba.hz2midikey : +(0.5) : floor); + + key_latch = (note_on_time >= key_latch_start) & (note_on_time < key_latch_end); + key_latched = key : ba.sAndH(key_latch); + + noise_trigger = note_on : ba.impulsify; + noise_env = en.adsr(0.0, 0.0, 1.0, 0.01, noise_trigger); + noise_osc = no.pink_noise : *(noise_env) : *(5.0); + + tone_trigger = noise_trigger : @(key_latch_start); + tone_env = en.adsr(0.0, 0.0, 1.0, 0.2, tone_trigger); + tone_osc = key_latched : ba.midikey2hz : os.square : *(tone_env); + + osc = noise_osc + tone_osc; +}; + +process = _,! : fx <: _,_; `; const dspDefinition: DspDefinition = { diff --git a/dev/src/dsp-definitions/44-c4-blops.ts b/dev/src/dsp-definitions/44-c4-blops.ts index d0f9029..f1e6e1a 100644 --- a/dev/src/dsp-definitions/44-c4-blops.ts +++ b/dev/src/dsp-definitions/44-c4-blops.ts @@ -5,34 +5,51 @@ import("stdfaust.lib"); // consts peak = 0.65; -threshold = peak * 0.02; +threshold = peak * 0.05; highest_hz = 1320.0; shortest_cycle = ma.SR / highest_hz; +key_latch_start = ma.SR * 0.001 * 30; +key_latch_end = ma.SR * 0.001 * 60; // utils zero_crosses(x) = (x' > 0) != (x > 0); crossed_up(x) = (x' < 0) & (x >= 0); safe_divide(num, denom) = num / max(denom, 1) - num * (denom == 0); +is_first_tick = ba.time == 0; +is_note_on = _ : an.amp_follower_ar(0.00001, 0.01) > threshold; // process input = _; -is_note_on = _ : an.amp_follower_ar(0.00001, 0.01) > threshold; - -devfn(x) = crosses,cycle,key,sine +devfn(x) = crosses,cycle,key,key_latched,osc with { note_on = is_note_on(x); + note_on_time = ba.countup(10000, 1 - note_on); + crosses = x : *(note_on) : crossed_up; crosses_on = crosses + (1 - note_on); cycle = ba.countup(10000, crosses_on') : ba.sAndH(crosses_on); key = ba.if(cycle < shortest_cycle, 0.0, (ma.SR / cycle) : ba.hz2midikey : +(0.5) : floor); - sine = key : ba.midikey2hz : os.osc; + + key_latch = (note_on_time >= key_latch_start) & (note_on_time < key_latch_end); + key_latched = key : ba.sAndH(key_latch); + + noise_trigger = note_on : ba.impulsify; + noise_env = en.adsr(0.0, 0.0, 1.0, 0.01, noise_trigger); + noise_osc = no.pink_noise : *(noise_env) : *(5.0); + + tone_trigger = noise_trigger : @(key_latch_start); + tone_env = en.adsr(0.0, 0.0, 1.0, 0.2, tone_trigger); + tone_osc = key_latched : ba.midikey2hz : os.square : *(tone_env); + + osc = noise_osc + tone_osc; }; -dev_crosses_on = _ : devfn : _,!,!,!; -dev_cycle = _ : devfn : !,_,!,!; -dev_key = _ : devfn : !,!,_,!; -dev_sine = _ : devfn : !,!,!,_; +dev_crosses_on = _ : devfn : _,!,!,!,!; +dev_cycle = _ : devfn : !,_,!,!,!; +dev_key = _ : devfn : !,!,_,!,!; +dev_key_latched = _ : devfn : !,!,!,_,!; +dev_osc = _ : devfn : !,!,!,!,_; `; const dspDefinition: DspDefinition = { @@ -44,7 +61,14 @@ const dspDefinition: DspDefinition = { type: "offline", channels: 1, sampleRate: 48000, - output: ["input", "dev_crosses_on", "dev_cycle", "dev_key", "dev_sine"], + output: [ + "input", + "dev_crosses_on", + "dev_cycle", + "dev_key", + "dev_key_latched", + "dev_osc", + ], outputLength: 48000 * 2.46, inputOffset: 1000, }; diff --git a/dev/src/offline-visualisations.tsx b/dev/src/offline-visualisations.tsx index 9b33041..d9c6dea 100644 --- a/dev/src/offline-visualisations.tsx +++ b/dev/src/offline-visualisations.tsx @@ -29,7 +29,7 @@ type PlotPanelProps = { export function PlotPanel(props: PlotPanelProps) { const { name, offlineResult, width, height, liveAudioContext } = props; - const [[pan, zoomWidth, highlight], setPlotState] = useState([0, 8, -1]); + const [[pan, zoomWidth, highlight], setPlotState] = useState([0, 4, -1]); const offlineResultFlattened: Output[] = []; offlineResult.forEach((output) => { @@ -72,12 +72,40 @@ export function PlotPanel(props: PlotPanelProps) { downloadWav(output.output, liveAudioContext, filename); }; + const handleZoomIn = (e: React.MouseEvent) => { + e.preventDefault(); + setPlotState(([p, z, h]) => [p, z * 2, h]); + }; + + const handleZoomOut = (e: React.MouseEvent) => { + e.preventDefault(); + setPlotState(([p, z, h]) => [p, z / 2, h]); + }; + return (
{output.name} {highlightValue}
+
+ + + + +
+
+ + - + +