diff --git a/dev/public/audio/cycfi-q-pitch-test/1a-Low-E.wav b/dev/public/audio/cycfi-q-pitch-test/1a-Low-E.wav new file mode 100644 index 0000000..c644815 Binary files /dev/null and b/dev/public/audio/cycfi-q-pitch-test/1a-Low-E.wav differ diff --git a/dev/public/audio/cycfi-q-pitch-test/2c-A-24th.wav b/dev/public/audio/cycfi-q-pitch-test/2c-A-24th.wav new file mode 100644 index 0000000..912c33c Binary files /dev/null and b/dev/public/audio/cycfi-q-pitch-test/2c-A-24th.wav differ diff --git a/dev/public/audio/cycfi-q-pitch-test/GStaccato.wav b/dev/public/audio/cycfi-q-pitch-test/GStaccato.wav new file mode 100644 index 0000000..05213cf Binary files /dev/null and b/dev/public/audio/cycfi-q-pitch-test/GStaccato.wav differ diff --git a/dev/src/dsp-definitions/27-pitchtracker-2.ts b/dev/src/dsp-definitions/27-pitchtracker-2.ts index 248abbf..f120e7e 100644 --- a/dev/src/dsp-definitions/27-pitchtracker-2.ts +++ b/dev/src/dsp-definitions/27-pitchtracker-2.ts @@ -35,8 +35,8 @@ const dspDefinition: DspDefinition = { type: "offline", output: ["input", "pitch", "pitch_clean", "osc"], inputFile: "/audio/cycfi-q-pitch-test/Hammer-Pull High E.wav", - // inputOffset: 30400, - outputLength: 88200, + inputOffset: 26000, + outputLength: 26000, sampleRate: 48000, channels: 1, }; diff --git a/dev/src/dsp-definitions/29-autocorrelation-2.ts b/dev/src/dsp-definitions/29-autocorrelation-2.ts new file mode 100644 index 0000000..ab47d72 --- /dev/null +++ b/dev/src/dsp-definitions/29-autocorrelation-2.ts @@ -0,0 +1,106 @@ +import type { DspDefinition } from "../types"; + +const dsp = ` +import("stdfaust.lib"); + +// settings + +longest_detectable_period = ma.SR / 80.0; +shortest_detectable_period = ma.SR / 1318.0; + +sample_points = 64 * 2; +sample_stride = 4; + +// utils + +changed(x) = x != x'; +only_changed(x) = ba.if(changed(x), x, -1); +count_stride(total, stride, trig) = ba.countup((total - 1) * stride, trig) / stride : int; + +min_since(trig, x) = return +with { + loop(fb, x) = ba.if(trig, x, min(x, fb)); + return = x : loop ~ _; +}; + +mean_since(trig, x) = return +with { + count = ba.countup(9999, trig); + loop(fb, x) = (fb * (trig == 0)) + x; + return = x : loop ~ _ : /(count + 1); +}; + +// process + +input = _; +envelope = an.amp_follower_ar(0.0,0.1); +start_capture = envelope : >(0.1) : ba.impulsify : @(longest_detectable_period); + +samples_since_start_capture = ba.countup(99999, start_capture); +samples_since_capture_end = samples_since_start_capture - (sample_points - 1) * sample_stride; +start_detect_pitch = samples_since_capture_end >= shortest_detectable_period : ba.impulsify; +trigger_sample_capture = start_capture : count_stride(sample_points, sample_stride) : only_changed; + +each(i,curr_v) = result +with { + samples_ago = (sample_points - i - 1) * sample_stride; + offset_v = curr_v : @(samples_ago); + captured_v = ba.sAndH(trigger_sample_capture(offset_v) == i); + diff_v = curr_v - captured_v; + result = diff_v * diff_v; +}; + +autocorrelated_difference = _ <: par(i, sample_points, each(i)) :> _; + +culumative_mean_difference(v) = result +with { + acd = v : autocorrelated_difference; + mean_since_start_capture = acd : mean_since(v : start_detect_pitch); + result = acd : /(mean_since_start_capture) : min(20.0); +}; + +cmd = culumative_mean_difference; + +best_match_foo(v) = result +with { + min_since_start_capture = v : culumative_mean_difference : min_since(v : start_detect_pitch); + min_changed = min_since_start_capture : changed; + period = v : samples_since_capture_end : ba.sAndH(min_changed); + result = (ma.SR / (period + 0.001)) : max(20.0) : min(20000.0); +}; + +best_match = best_match_foo; +osc = best_match : os.osc * 0.4; +`; + +const dspDefinition: DspDefinition = { + id: "autocorrelation-2", + name: "Auto-correlation 2", + description: "Auto-correlates pitch 2", + dsp, + type: "offline", + output: [ + "input", + "trigger_sample_capture", + "samples_since_capture_end", + "autocorrelated_difference", + "cmd", + "best_match", + "osc", + ], + inputFile: "/audio/cycfi-q-pitch-test/GStaccato.wav", + inputOffset: 25400, + outputLength: 192000, // 27921, // 192000, + + // inputFile: "/audio/cycfi-q-pitch-test/2c-A-24th.wav", + // inputOffset: 7624, + // outputLength: 20000, // 27921, // 192000, + + // inputFile: "/audio/cycfi-q-pitch-test/1a-Low-E.wav", + // inputOffset: 47500, + // outputLength: 55000, // 27921, // 192000, + sampleRate: 48000, + channels: 1, +}; + +export default dspDefinition; diff --git a/dev/src/dsp-definitions/32-min-since.ts b/dev/src/dsp-definitions/32-min-since.ts new file mode 100644 index 0000000..1380ff7 --- /dev/null +++ b/dev/src/dsp-definitions/32-min-since.ts @@ -0,0 +1,36 @@ +import type { DspDefinition } from "../types"; + +const dsp = ` +import("stdfaust.lib"); + +min_since(trig, x) = return +with { + loop(fb, x) = ba.if(trig, x, min(x, fb)); + return = x : loop ~ _; +}; + +process = min_since; +`; + +const dspDefinition: DspDefinition = { + id: "min-since", + name: "Min since", + description: "Min since", + dsp, + type: "offline", + input: [ + [1, 0, 0, 0, 1, 0, 0, 0], + [4, 3, 2, 3, 9, 3, 4, 5], + ], + output: ["process"], + expect: { + process: [ + [4, 3, 2, 2, 9, 3, 3, 3], + [4, 3, 2, 2, 9, 3, 3, 3], + ], + }, + channels: 2, + sampleRate: 44100, +}; + +export default dspDefinition; diff --git a/dev/src/dsp-definitions/33-pitchtracker-3.ts b/dev/src/dsp-definitions/33-pitchtracker-3.ts new file mode 100644 index 0000000..77465b7 --- /dev/null +++ b/dev/src/dsp-definitions/33-pitchtracker-3.ts @@ -0,0 +1,70 @@ +import type { DspDefinition } from "../types"; + +const dsp = ` +import("stdfaust.lib"); + +// settings + +// utils + +reduce_while(reducer, trig, x) = x : loop ~ _ +with { + loop(prev, next) = ba.if(trig, next, reducer(prev, next)); +}; + +max_while = reduce_while(max); + +// process + +input = _; +envelope = an.amp_follower_ar(0.0,0.1); +start_note = envelope : >(0.1) : ba.impulsify; + +amplitude = an.amp_follower_ar(0.0,0.01) : max(0.05); +compress(x) = x / amplitude(x) * 10; + +prep = fi.dcblockerat(40.0) : max(0.0); + +zippy = prep : compress; +tick = prep : compress : >=(9) : ba.impulsify; +tick_rise = tick : ba.countup(9999); + + + +// tick = prep : zippy : >(0.5) : ==(0) : ba.countup(9999); // can get too swamped with similar sized spikes +// osc = best_match : os.osc * 0.4; +`; + +const dspDefinition: DspDefinition = { + id: "pitchtracker-3", + name: "Pitch tracker 3", + description: "Tracks pitch 3", + dsp, + type: "offline", + output: ["input", "tick", "tick_rise", "start_note", "amplitude", "zippy"], + + // inputFile: "/audio/cycfi-q-pitch-test/GStaccato.wav", + // inputOffset: 25400, + // outputLength: 192000, + + // inputFile: "/audio/cycfi-q-pitch-test/2c-A-24th.wav", + // inputOffset: 7624, + // outputLength: 20000, // 27921, // 192000, + + inputFile: "/audio/cycfi-q-pitch-test/1a-Low-E.wav", + inputOffset: 47500, + outputLength: 55000, // 27921, // 192000, + + // inputFile: "/audio/cycfi-q-pitch-test/Tapping D.wav", + // inputOffset: 30400, + // outputLength: 88200, + + // inputFile: "/audio/cycfi-q-pitch-test/Hammer-Pull High E.wav", + // inputOffset: 26000, + // outputLength: 40000, + + sampleRate: 48000, + channels: 1, +}; + +export default dspDefinition; diff --git a/dev/src/dsp-definitions/all.ts b/dev/src/dsp-definitions/all.ts index 006ab6f..280138f 100644 --- a/dev/src/dsp-definitions/all.ts +++ b/dev/src/dsp-definitions/all.ts @@ -28,8 +28,11 @@ import autocorrelation from "./25-autocorrelation"; import envelopeDetector from "./26-envelope-detector"; import pitchtracker2 from "./27-pitchtracker-2"; import averageSince from "./28-average-since"; +import autocorrelation2 from "./29-autocorrelation-2"; import echoloop from "./30-echoloop"; import echoloopLive from "./31-echoloop-live"; +import minSince from "./32-min-since"; +import pitchtracker3 from "./33-pitchtracker-3"; export const all: DspDefinition[] = [ sineWave, @@ -60,6 +63,9 @@ export const all: DspDefinition[] = [ envelopeDetector, pitchtracker2, averageSince, + autocorrelation2, echoloop, echoloopLive, + minSince, + pitchtracker3, ]; diff --git a/dev/src/offline-visualisations.tsx b/dev/src/offline-visualisations.tsx index 92089f2..9b33041 100644 --- a/dev/src/offline-visualisations.tsx +++ b/dev/src/offline-visualisations.tsx @@ -177,7 +177,9 @@ export function Plot(props: PlotProps) { startDragPan.current = pan; }; - const handleWheel = (e: React.WheelEvent) => { + const handleWheel = (e) => { + e.stopPropagation(); + e.preventDefault(); const { pixelY } = normalizeWheel(e); const dir = pixelY < 0 ? 2 : 0.5; setPlotState(([p, z, h]) => { @@ -208,6 +210,8 @@ export function Plot(props: PlotProps) { const canvas = canvasRef.current; const drawContext = canvas?.getContext("2d"); + canvas?.addEventListener("wheel", handleWheel); + if (drawContext) { drawContext.clearRect(0, 0, width, height); const max = width / zoomWidth; @@ -227,7 +231,19 @@ export function Plot(props: PlotProps) { drawContext.fillRect(px, Math.round(y), pw, 1); } } - }, [output, width, height, highlight, pan, zoomWidth, zoomHeight]); + return () => { + canvas?.removeEventListener("wheel", handleWheel); + }; + }, [ + output, + width, + height, + highlight, + pan, + zoomWidth, + zoomHeight, + handleWheel, + ]); return ( ); }