updated skills
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)]));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user