updated skills

This commit is contained in:
2026-06-09 21:06:27 +01:00
parent db1ce43ee1
commit 427a5bd919
1283 changed files with 24684 additions and 2313 deletions

View File

@@ -34,10 +34,10 @@ import { AudioDocument } from "@suaveplan/audio-core";
import { BeatDetector } from "@suaveplan/audio-analysis-beat";
const doc = new AudioDocument({
sampleRate: 48_000,
channels: 1,
sourceLength: pcm.length,
pages: [{ channelData: [pcm], startSample: 0 }],
sampleRate: 48_000,
channels: 1,
sourceLength: pcm.length,
pages: [{ channelData: [pcm], startSample: 0 }],
});
const result = await new BeatDetector().analyze(doc);
@@ -65,4 +65,3 @@ console.log(result.bpm, result.beats.length, result.confidence);
See per-module `.md` files for the deep documentation of each algorithm and
entry point.

View File

@@ -30,9 +30,9 @@ gives a confidence near 1, scattered onsets give one near 0.
import { buildBeatGrid } from "@suaveplan/audio-analysis-beat/algorithms/beat-grid";
const result = buildBeatGrid({
period: 60 / 120, // 120 BPM
duration: 10,
onsets: [0.5, 1.0, 1.5, 2.0],
period: 60 / 120, // 120 BPM
duration: 10,
onsets: [0.5, 1.0, 1.5, 2.0],
});
console.log(result.beats.length, result.phase, result.confidence);
```
@@ -46,7 +46,7 @@ import { buildBeatGrid } from "@suaveplan/audio-analysis-beat/algorithms/beat-gr
// more tightly — this is how the BeatDetector breaks ties between candidate
// tempi when an autocorrelation sweep returns two near-equal peaks.
const onsets = new Float64Array([
0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0,
0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0,
]);
const a = buildBeatGrid({ period: 60 / 120, duration: 4.2, onsets, phaseTrials: 48 });

View File

@@ -29,9 +29,9 @@ import { combTempo } from "@suaveplan/audio-analysis-beat/algorithms/comb-tempo"
// ODF sampled every 1024 samples at 48 kHz → hopSeconds = 1024/48000.
const result = combTempo(odf, {
minBpm: 60,
maxBpm: 200,
hopSeconds: 1024 / 48_000,
minBpm: 60,
maxBpm: 200,
hopSeconds: 1024 / 48_000,
});
console.log(result.bpm); // e.g. 120
```
@@ -40,8 +40,8 @@ console.log(result.bpm); // e.g. 120
```ts
import {
combScore,
combTempo,
combScore,
combTempo,
} from "@suaveplan/audio-analysis-beat/algorithms/comb-tempo";
// Draw a tempo-confidence curve over the search range manually:
@@ -50,8 +50,8 @@ const min = 60;
const max = 200;
const curve = new Float64Array(max - min + 1);
for (let bpm = min; bpm <= max; bpm++) {
const period = Math.max(1, Math.round((60 / bpm) / hopSeconds));
curve[bpm - min] = combScore(odf, period);
const period = Math.max(1, Math.round((60 / bpm) / hopSeconds));
curve[bpm - min] = combScore(odf, period);
}
// Fall back to combTempo for the canonical winner including octave handling.

View File

@@ -37,8 +37,8 @@ const odf = energyOdf(energies);
```ts
import {
blockEnergy,
energyOdfFromSamples,
blockEnergy,
energyOdfFromSamples,
} from "@suaveplan/audio-analysis-beat/algorithms/energy-odf";
const sampleRate = 48_000;
@@ -53,10 +53,10 @@ const hopSeconds = blockSize / sampleRate;
let bestIdx = 0;
let best = 0;
for (let i = 0; i < Math.floor(1 / hopSeconds); i++) {
if (odf[i] > best) {
best = odf[i];
bestIdx = i;
}
if (odf[i] > best) {
best = odf[i];
bestIdx = i;
}
}
console.log("onset at", bestIdx * hopSeconds, "seconds");
console.log("avg first-block energy", blockEnergy([left.subarray(0, blockSize)]));

View File

@@ -41,8 +41,8 @@ console.log(odf.length, "frames"); // ceil(192000 / 512) = 375
```ts
import {
magnitudeSpectrum,
spectralFlux,
magnitudeSpectrum,
spectralFlux,
} from "@suaveplan/audio-analysis-beat/algorithms/spectral-flux";
const fftSize = 2048;
@@ -55,8 +55,8 @@ const mono = new Float32Array(48_000 * 10); // 10 seconds
// thread per-frame work through other code, or thin the ODF on the fly.
const odfValues: number[] = [];
for (let start = 0; start + fftSize <= mono.length; start += hop) {
buf.set(mono.subarray(start, start + fftSize));
odfValues.push(spectralFlux(buf, prev, fftSize));
buf.set(mono.subarray(start, start + fftSize));
odfValues.push(spectralFlux(buf, prev, fftSize));
}
// Verify the magnitude spectrum has the expected shape on its own.

View File

@@ -42,10 +42,10 @@ import { AudioDocument } from "@suaveplan/audio-core";
import { BeatDetector } from "@suaveplan/audio-analysis-beat/detector/beat-detector";
const doc = new AudioDocument({
sampleRate: 48_000,
channels: 1,
sourceLength: 48_000 * 4,
pages: [{ startSample: 0, channelData: [Array.from(/* 4 s mono */)] }],
sampleRate: 48_000,
channels: 1,
sourceLength: 48_000 * 4,
pages: [{ startSample: 0, channelData: [Array.from(/* 4 s mono */)] }],
});
const detector = new BeatDetector();
@@ -60,11 +60,11 @@ import { AudioDocument } from "@suaveplan/audio-core";
import { BeatDetector } from "@suaveplan/audio-analysis-beat/detector/beat-detector";
const detector = new BeatDetector({
algorithm: "spectral-flux",
minBpm: 80,
maxBpm: 160,
fftSize: 2048,
hopSize: 1024,
algorithm: "spectral-flux",
minBpm: 80,
maxBpm: 160,
fftSize: 2048,
hopSize: 1024,
});
const controller = new AbortController();
@@ -73,10 +73,10 @@ setTimeout(() => controller.abort(), 5_000);
// Streaming refinement
let final;
for await (const partial of detector.analyzeStream(doc, {
signal: controller.signal,
signal: controller.signal,
})) {
final = partial;
console.log("BPM so far:", partial.bpm, partial.confidence);
final = partial;
console.log("BPM so far:", partial.bpm, partial.confidence);
}
// Or drive it through the audio-core AnalysisOp surface

View File

@@ -34,13 +34,13 @@ right log level without re-deriving it.
```ts
import {
AUDIO_ANALYSIS_BEAT_CODES,
AudioAnalysisBeatError,
AUDIO_ANALYSIS_BEAT_CODES,
AudioAnalysisBeatError,
} from "@suaveplan/audio-analysis-beat/error-codes";
throw new AudioAnalysisBeatError(
AUDIO_ANALYSIS_BEAT_CODES.EMPTY_DOCUMENT,
"audio-analysis-beat.empty-document",
AUDIO_ANALYSIS_BEAT_CODES.EMPTY_DOCUMENT,
"audio-analysis-beat.empty-document",
);
```
@@ -48,21 +48,21 @@ throw new AudioAnalysisBeatError(
```ts
import {
AUDIO_ANALYSIS_BEAT_CODES,
AUDIO_ANALYSIS_BEAT_ERROR_METADATA,
AudioAnalysisBeatError,
AUDIO_ANALYSIS_BEAT_CODES,
AUDIO_ANALYSIS_BEAT_ERROR_METADATA,
AudioAnalysisBeatError,
} from "@suaveplan/audio-analysis-beat/error-codes";
function logError(err: unknown): void {
if (!(err instanceof AudioAnalysisBeatError)) throw err;
const meta = AUDIO_ANALYSIS_BEAT_ERROR_METADATA[err.code];
console[meta.severity === "warning" ? "warn" : "error"](
`[${err.category}] ${err.code}: ${meta.description}`,
{ context: err.context, cause: err.cause },
);
if (err.code === AUDIO_ANALYSIS_BEAT_CODES.ABORTED) return;
if (err.code === AUDIO_ANALYSIS_BEAT_CODES.NO_BEATS_DETECTED) return;
throw err;
if (!(err instanceof AudioAnalysisBeatError)) throw err;
const meta = AUDIO_ANALYSIS_BEAT_ERROR_METADATA[err.code];
console[meta.severity === "warning" ? "warn" : "error"](
`[${err.category}] ${err.code}: ${meta.description}`,
{ context: err.context, cause: err.cause },
);
if (err.code === AUDIO_ANALYSIS_BEAT_CODES.ABORTED) return;
if (err.code === AUDIO_ANALYSIS_BEAT_CODES.NO_BEATS_DETECTED) return;
throw err;
}
```