Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle different SampleRate due to OS, 48000 / 44100 #70

Merged
merged 7 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
BiquadFilterOptions,
} from './types';
import * as consts from './consts';
import * as utils from './utils';

/**
* Find peaks when the signal if greater than the threshold, then move 10_000 indexes (represents ~0.23s) to ignore the descending phase of the parabol
Expand All @@ -19,8 +20,9 @@ import * as consts from './consts';
* @param skipForwardIndexes Numbers of index to skip when a peak is detected
* @returns Peaks found that are greater than the threshold
*/
export function findPeaksAtThreshold(data: Float32Array, threshold: Threshold, offset = 0, skipForwardIndexes = consts.skipForwardIndexes): PeaksAndThreshold {
export function findPeaksAtThreshold(data: Float32Array, threshold: Threshold, audioSampleRate: number, offset = 0): PeaksAndThreshold {
const peaks: Peaks = [];
const skipForwardIndexes = utils.computeIndexesToSkip(0.25, audioSampleRate);

const {length} = data;

Expand Down Expand Up @@ -49,12 +51,12 @@ export function findPeaksAtThreshold(data: Float32Array, threshold: Threshold, o
* @param channelData Channel data
* @returns Suffisent amount of peaks in order to continue further the process
*/
export async function findPeaks(channelData: Float32Array): Promise<PeaksAndThreshold> {
export async function findPeaks(channelData: Float32Array, audioSampleRate: number): Promise<PeaksAndThreshold> {
let validPeaks: Peaks = [];
let validThreshold = 0;

await descendingOverThresholds(async threshold => {
const {peaks} = findPeaksAtThreshold(channelData, threshold);
const {peaks} = findPeaksAtThreshold(channelData, threshold, audioSampleRate);

/**
* Loop over peaks
Expand Down Expand Up @@ -85,6 +87,7 @@ export async function findPeaks(channelData: Float32Array): Promise<PeaksAndThre
*/
export function getBiquadFilter(context: OfflineAudioContext | AudioContext, options?: BiquadFilterOptions): BiquadFilterNode {
const lowpass = context.createBiquadFilter();

lowpass.type = 'lowpass';
lowpass.frequency.value = options?.frequencyValue ?? consts.frequencyValue;
lowpass.Q.value = options?.qualityValue ?? consts.qualityValue;
Expand Down Expand Up @@ -316,7 +319,7 @@ export function groupByTempo(audioSampleRate: number, intervalCounts: Interval[]
export async function analyzeFullBuffer(originalBuffer: AudioBuffer, options?: BiquadFilterOptions): Promise<Tempo[]> {
const buffer = await getOfflineLowPassSource(originalBuffer, options);
const channelData = buffer.getChannelData(0);
const {peaks} = await findPeaks(channelData);
const {peaks} = await findPeaks(channelData, buffer.sampleRate);
const intervals = identifyIntervals(peaks);
const tempos = groupByTempo(buffer.sampleRate, intervals);
const topCandidates = getTopCandidates(tempos);
Expand Down
1 change: 0 additions & 1 deletion src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export const startThreshold = 0.95;
export const minValidThreshold = 0.2;
export const minPeaks = 15;
export const thresholdStep = 0.05;
export const skipForwardIndexes = 10000;
export const frequencyValue = 200;
export const qualityValue = 1;

27 changes: 19 additions & 8 deletions src/realtime-bpm-analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
NextIndexPeaks,
BpmCandidates,
Threshold,
FindPeaksOptions,
PostMessageEvents,
} from './types';
import {
Expand Down Expand Up @@ -130,7 +131,14 @@ export class RealTimeBpmAnalyzer {
/**
* Mutate nextIndexPeaks and validPeaks if possible
*/
await this.findPeaks(channelData, bufferSize, currentMinIndex, currentMaxIndex, postMessage);
await this.findPeaks({
channelData,
bufferSize,
audioSampleRate,
currentMinIndex,
currentMaxIndex,
postMessage,
});

/**
* Increment chunk
Expand Down Expand Up @@ -160,13 +168,16 @@ export class RealTimeBpmAnalyzer {

/**
* Find the best threshold with enought peaks
* @param channelData Channel data
* @param bufferSize Buffer size
* @param currentMinIndex Current minimum index
* @param currentMaxIndex Current maximum index
* @param postMessage Function to post a message to the processor node
* @param options Find Peaks Options
*/
async findPeaks(channelData: Float32Array, bufferSize: number, currentMinIndex: number, currentMaxIndex: number, postMessage: (data: PostMessageEvents) => void): Promise<void> {
async findPeaks({
channelData,
bufferSize,
audioSampleRate,
currentMinIndex,
currentMaxIndex,
postMessage,
}: FindPeaksOptions): Promise<void> {
await descendingOverThresholds(async threshold => {
if (this.nextIndexPeaks[threshold] >= currentMaxIndex) {
return false;
Expand All @@ -177,7 +188,7 @@ export class RealTimeBpmAnalyzer {
*/
const offsetForNextPeak = this.nextIndexPeaks[threshold] % bufferSize; // 0 - 4095

const {peaks, threshold: atThreshold} = findPeaksAtThreshold(channelData, threshold, offsetForNextPeak);
const {peaks, threshold: atThreshold} = findPeaksAtThreshold(channelData, threshold, audioSampleRate, offsetForNextPeak);

/**
* Loop over peaks
Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ export type RealTimeBpmAnalyzerOptions = {
debug: boolean;
};

export type FindPeaksOptions = {
channelData: Float32Array;
bufferSize: number;
audioSampleRate: number;
currentMinIndex: number;
currentMaxIndex: number;
postMessage: (data: PostMessageEvents) => void;
};

export type ValidPeaks = Record<string, Peaks>;

export type NextIndexPeaks = Record<string, number>;
Expand Down
4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,7 @@ export function chunckAggregator(): (pcmData: Float32Array) => AggregateData {
};
};
}

export function computeIndexesToSkip(durationSeconds: number, sampleRate: number): number {
return Math.round(durationSeconds * sampleRate);
}
Loading
Loading