audio-inspect
Version:
Lightweight yet powerful audio analysis library
763 lines (745 loc) • 22.8 kB
TypeScript
type AudioLike = AudioData | AudioBuffer;
type AudioSource = AudioLike | ArrayBuffer | ArrayBufferView | Blob | File | URL | string;
interface AudioData {
sampleRate: number;
channelData: Float32Array[];
duration: number;
numberOfChannels: number;
length: number;
}
type ChannelSelector = 'mix' | number | 'all' | readonly number[];
interface AudioDecoder {
name: string;
decode(input: ArrayBuffer | ArrayBufferView | Blob, options?: {
signal?: AbortSignal;
}): Promise<AudioData>;
}
type ResampleQuality = 'high' | 'fast';
interface AudioResampler {
(audio: AudioData, targetSampleRate: number, options?: {
signal?: AbortSignal;
}): Promise<AudioData> | AudioData;
}
interface LoadOptions {
sampleRate?: number;
resampleQuality?: ResampleQuality;
resampler?: AudioResampler;
channels?: number | 'mono' | 'stereo';
normalize?: boolean;
signal?: AbortSignal;
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
decoder?: AudioDecoder;
}
type WindowFunction = 'hann' | 'hamming' | 'blackman' | 'bartlett' | 'kaiser' | 'tukey' | 'rectangular' | 'none';
interface AmplitudeOptions {
channel?: ChannelSelector;
asDB?: boolean;
reference?: number;
truePeak?: boolean;
oversamplingFactor?: number;
interpolation?: 'linear' | 'cubic' | 'sinc';
}
interface ProgressOptions {
onProgress?: (percent: number, message?: string) => void;
}
interface BaseAnalysisResult {
sampleRate: number;
duration: number;
processingTime?: number;
}
interface WaveformAnalysisResult extends BaseAnalysisResult {
amplitudes: Float32Array;
timestamps?: Float32Array;
frameCount: number;
samplesPerFrame: number;
framesPerSecond: number;
maxAmplitude: number;
averageAmplitude: number;
}
interface PeaksAnalysisResult extends BaseAnalysisResult {
positions: Float32Array;
amplitudes: Float32Array;
times: Float32Array;
maxAmplitude: number;
averageAmplitude: number;
count: number;
}
interface RMSAnalysisResult extends BaseAnalysisResult {
value: number;
valueDB?: number;
channel: number;
}
type ErrorCode = 'INVALID_INPUT' | 'INVALID_STATE' | 'UNSUPPORTED_FORMAT' | 'DECODE_ERROR' | 'DECODE_BACKEND_MISSING' | 'NETWORK_ERROR' | 'PROCESSING_ERROR' | 'INITIALIZATION_FAILED' | 'WORKLET_NOT_SUPPORTED' | 'MODULE_LOAD_FAILED' | 'INSUFFICIENT_DATA' | 'MEMORY_ERROR' | 'ABORTED';
declare class AudioInspectError extends Error {
readonly name = "AudioInspectError";
readonly code: ErrorCode;
readonly cause?: unknown;
readonly details?: unknown;
constructor(code: ErrorCode, message: string, cause?: unknown, details?: unknown);
}
declare function isAudioInspectError(error: unknown): error is AudioInspectError;
declare function load(source: AudioSource, options?: LoadOptions): Promise<AudioData>;
interface EnergyOptions {
frameSize?: number;
hopSize?: number;
channel?: ChannelSelector;
normalized?: boolean;
windowFunction?: 'rectangular' | 'hann' | 'hamming' | 'blackman' | 'none';
}
interface EnergyResult {
times: Float32Array;
energies: Float32Array;
totalEnergy: number;
statistics: {
mean: number;
std: number;
max: number;
min: number;
};
}
type FFTProviderType = 'webfft' | 'native' | 'custom';
interface FFTResult$1 {
complex: Float32Array;
magnitude: Float32Array;
phase: Float32Array;
frequencies: Float32Array;
}
interface IFFTProvider {
readonly name: string;
readonly size: number;
readonly sampleRate: number;
fft(input: Float32Array): FFTResult$1 | Promise<FFTResult$1>;
dispose(): void;
profile?(): Promise<void>;
}
interface FFTProviderConfig {
type: FFTProviderType;
fftSize: number;
sampleRate: number;
enableProfiling?: boolean;
customProvider?: IFFTProvider;
}
interface FFTProviderResolver {
createProvider(config: FFTProviderConfig): Promise<IFFTProvider>;
}
interface FFTProviderRequest {
fftSize: number;
sampleRate: number;
provider?: FFTProviderType;
enableProfiling?: boolean;
fallbackToNative?: boolean;
resolver?: FFTProviderResolver;
}
interface FFTProviderCache {
getOrCreate(request: FFTProviderRequest): Promise<IFFTProvider>;
clear(): void;
}
type FFTNormalization = 'none' | 'amplitude';
type SpectrumScale = 'amplitude' | 'dbfs';
interface FFTOptions {
fftSize?: number;
windowFunction?: WindowFunction;
channel?: ChannelSelector;
provider?: FFTProviderType;
enableProfiling?: boolean;
providerCache?: FFTProviderCache | undefined;
normalization?: FFTNormalization;
}
interface SpectrumOptions extends FFTOptions {
minFrequency?: number;
maxFrequency?: number;
scale?: SpectrumScale;
}
interface SpectrogramOptions extends SpectrumOptions {
frameSize?: number;
hopSize?: number;
maxFrames?: number;
}
interface FFTAnalysisResult extends FFTResult$1 {
fftSize: number;
windowFunction: string;
providerName: string;
normalization: FFTNormalization;
}
interface SpectrumAnalysisResult {
frequencies: Float32Array;
values: Float32Array;
scale: SpectrumScale;
}
interface SpectrogramAnalysisResult {
times: Float32Array;
frequencies: Float32Array;
frames: Float32Array[];
frameCount: number;
frequencyBins: number;
scale: SpectrumScale;
latest: Float32Array;
}
interface LUFSOptions {
channelMode?: 'mono' | 'stereo';
gated?: boolean;
calculateShortTerm?: boolean;
calculateMomentary?: boolean;
/**
* Collect frame series in offline analysis.
* - true / 'both': collect both short-term and momentary series
* - 'shortTerm': collect only short-term series
* - 'momentary': collect only momentary series
*/
collectSeries?: boolean | 'shortTerm' | 'momentary' | 'both';
calculateLoudnessRange?: boolean;
calculateTruePeak?: boolean;
/**
* 'bs1770' uses the Annex 2 polyphase FIR true-peak estimator.
* 'interSamplePeak' keeps the legacy interpolation-based estimate.
*/
truePeakMethod?: 'bs1770' | 'interSamplePeak';
/**
* For 'bs1770', supported values are 2 or 4.
* For 'interSamplePeak', 2/4/8 are supported.
*/
truePeakOversamplingFactor?: 2 | 4 | 8;
truePeakInterpolation?: 'linear' | 'cubic' | 'sinc';
}
interface LUFSResult {
integrated: number;
shortTerm?: number;
momentary?: number;
loudnessRange?: number;
truePeak?: number[];
statistics?: {
percentile10: number;
percentile95: number;
};
series?: {
times: Float32Array;
shortTerm?: Float32Array;
momentary?: Float32Array;
};
}
interface SpectralRuntimeOptions {
provider?: FFTProviderType;
enableProfiling?: boolean;
providerCache?: FFTProviderCache | undefined;
}
interface SpectralFeaturesOptions extends SpectralRuntimeOptions {
fftSize?: number;
windowFunction?: 'hann' | 'hamming' | 'blackman' | 'none';
channel?: ChannelSelector;
minFrequency?: number;
maxFrequency?: number;
rolloffThreshold?: number;
}
interface SpectralFeaturesResult {
spectralCentroid: number;
spectralBandwidth: number;
spectralRolloff: number;
spectralFlatness: number;
spectralFlux?: number;
zeroCrossingRate: number;
frequencyRange: {
min: number;
max: number;
};
}
interface TimeVaryingSpectralOptions extends SpectralFeaturesOptions {
frameSize?: number;
hopSize?: number;
numFrames?: number;
}
interface TimeVaryingSpectralResult {
times: Float32Array;
spectralCentroid: Float32Array;
spectralBandwidth: Float32Array;
spectralRolloff: Float32Array;
spectralFlatness: Float32Array;
spectralFlux: Float32Array;
zeroCrossingRate: Float32Array;
frameInfo: {
frameSize: number;
hopSize: number;
numFrames: number;
};
}
interface SpectralEntropyOptions extends SpectralRuntimeOptions {
fftSize?: number;
windowFunction?: 'hann' | 'hamming' | 'blackman' | 'none';
channel?: ChannelSelector;
minFrequency?: number;
maxFrequency?: number;
}
interface SpectralEntropyResult {
entropy: number;
entropyNorm: number;
frequencyRange: {
min: number;
max: number;
};
}
interface SpectralCrestOptions extends SpectralRuntimeOptions {
fftSize?: number;
windowFunction?: 'hann' | 'hamming' | 'blackman' | 'none';
channel?: ChannelSelector;
minFrequency?: number;
maxFrequency?: number;
asDB?: boolean;
}
interface SpectralCrestResult {
crest: number;
crestDB?: number;
peak: number;
average: number;
frequencyRange: {
min: number;
max: number;
};
}
interface FramedTransformOptions extends SpectralRuntimeOptions {
frameSizeMs?: number;
hopSizeMs?: number;
fftSize?: number;
windowFunction?: WindowFunction;
channel?: ChannelSelector;
}
interface MelSpectrogramOptions extends FramedTransformOptions {
numMelFilters?: number;
minFrequency?: number;
maxFrequency?: number;
preEmphasis?: number;
power?: number;
logScale?: boolean;
logEpsilon?: number;
}
interface MelSpectrogramResult {
melSpectrogram: number[][];
times: Float32Array;
melFrequencies: Float32Array;
frameInfo: {
frameSizeMs: number;
hopSizeMs: number;
numFrames: number;
numBins: number;
};
frequencyRange: {
min: number;
max: number;
};
}
interface CQTOptions extends FramedTransformOptions {
fMin?: number;
binsPerOctave?: number;
numBins?: number;
preEmphasis?: number;
power?: number;
logScale?: boolean;
logEpsilon?: number;
}
interface CQTResult {
cqt: number[][];
times: Float32Array;
frequencies: Float32Array;
frameInfo: {
frameSizeMs: number;
hopSizeMs: number;
numFrames: number;
numBins: number;
binsPerOctave: number;
};
frequencyRange: {
min: number;
max: number;
};
}
interface MFCCOptions extends FramedTransformOptions {
numMelFilters?: number;
numMfccCoeffs?: number;
minFrequency?: number;
maxFrequency?: number;
preEmphasis?: number;
lifterCoeff?: number;
}
interface MFCCResult {
mfcc: number[][];
times: Float32Array;
frameInfo: {
frameSizeMs: number;
hopSizeMs: number;
numFrames: number;
numCoeffs: number;
};
frequencyRange: {
min: number;
max: number;
};
}
interface MFCCDeltaOptions extends MFCCOptions {
deltaWindowSize?: number;
computeDelta?: boolean;
computeDeltaDelta?: boolean;
}
interface MFCCDeltaResult extends MFCCResult {
delta?: number[][];
deltaDelta?: number[][];
}
interface StereoAnalysisOptions {
frameSize?: number;
hopSize?: number;
calculatePhase?: boolean;
calculateITD?: boolean;
calculateILD?: boolean;
provider?: FFTProviderType;
enableProfiling?: boolean;
providerCache?: FFTProviderCache | undefined;
}
interface StereoAnalysisResult {
correlation: number;
coherence?: Float32Array;
width: number;
widthFrequency?: Float32Array;
balance: number;
phaseDifference?: number;
phaseCorrelation?: number;
midSideRatio: number;
itd?: number;
ild?: number;
goniometer?: {
x: Float32Array;
y: Float32Array;
};
}
declare function getTimeVaryingStereoAnalysis(audio: AudioData, options?: StereoAnalysisOptions & {
windowSize?: number;
}): Promise<{
times: Float32Array;
correlation: Float32Array;
width: Float32Array;
balance: Float32Array;
}>;
interface PeaksOptions {
count?: number;
threshold?: number;
channel?: ChannelSelector;
minDistance?: number;
}
interface Peak {
position: number;
time: number;
amplitude: number;
}
interface PeaksResult {
peaks: Peak[];
maxAmplitude: number;
averageAmplitude: number;
}
interface WaveformOptions {
framesPerSecond?: number;
channel?: ChannelSelector;
method?: 'rms' | 'peak' | 'average';
}
interface WaveformPoint {
time: number;
amplitude: number;
}
interface WaveformResult {
waveform: WaveformPoint[];
maxAmplitude: number;
averageAmplitude: number;
frameCount: number;
samplesPerFrame: number;
}
interface WaveformAnalysisOptions extends ProgressOptions {
framesPerSecond?: number;
channel?: ChannelSelector;
method?: 'rms' | 'peak' | 'average';
}
interface PeaksAnalysisOptions extends ProgressOptions {
count?: number;
threshold?: number;
channel?: ChannelSelector;
minDistance?: number;
}
interface RMSAnalysisOptions extends ProgressOptions {
channel?: ChannelSelector;
asDB?: boolean;
reference?: number;
}
declare function getWaveformAnalysis(audio: AudioData, options?: WaveformAnalysisOptions): WaveformAnalysisResult;
declare function getPeaksAnalysis(audio: AudioData, options?: PeaksAnalysisOptions): PeaksAnalysisResult;
declare function getRMSAnalysis(audio: AudioData, options?: RMSAnalysisOptions): RMSAnalysisResult;
interface VADOptions {
channel?: ChannelSelector;
frameSizeMs?: number;
hopSizeMs?: number;
method?: 'energy' | 'zcr' | 'combined' | 'adaptive';
energyThreshold?: number;
zcrThresholdLow?: number;
zcrThresholdHigh?: number;
adaptiveAlpha?: number;
noiseFactor?: number;
minSilenceDurationMs?: number;
minSpeechDurationMs?: number;
preEmphasis?: boolean;
smoothing?: boolean;
}
interface VADSegment {
start: number;
end: number;
type: 'speech' | 'silence';
confidence?: number;
}
interface VADResult {
segments: VADSegment[];
speechRatio: number;
features?: {
energies: Float32Array;
zcrs: Float32Array;
decisions: Float32Array;
times: Float32Array;
};
}
interface CrestFactorOptions {
channel?: ChannelSelector;
windowSize?: number;
hopSize?: number;
method?: 'simple' | 'weighted';
}
interface CrestFactorResult {
crestFactor: number;
crestFactorLinear: number;
peak: number;
rms: number;
timeVarying?: {
times: Float32Array;
values: Float32Array;
valuesLinear: Float32Array;
peaks: Float32Array;
rmsValues: Float32Array;
} | undefined;
}
interface ZeroCrossingOptions {
channel?: ChannelSelector;
}
interface TimeVaryingStereoOptions extends StereoAnalysisOptions {
windowSize?: number;
}
type TimeVaryingStereoResult = Awaited<ReturnType<typeof getTimeVaryingStereoAnalysis>>;
type RMSOptions = AmplitudeOptions;
type PeakOptions = AmplitudeOptions;
type RMSResult = number;
type PeakResult = number;
type ZeroCrossingResult = number;
type FFTResult = FFTAnalysisResult;
type SpectrumResult = SpectrumAnalysisResult;
type SpectrogramResult = SpectrogramAnalysisResult;
type MFCCWithDeltaOptions = MFCCDeltaOptions;
type MFCCWithDeltaResult = MFCCDeltaResult;
type StereoOptions = StereoAnalysisOptions;
type StereoResult = StereoAnalysisResult;
interface FeatureRegistry {
rms: {
options: RMSOptions;
result: RMSResult;
};
rmsAnalysis: {
options: RMSAnalysisOptions;
result: ReturnType<typeof getRMSAnalysis>;
};
peak: {
options: PeakOptions;
result: PeakResult;
};
peaks: {
options: PeaksOptions;
result: PeaksResult;
};
peaksAnalysis: {
options: PeaksAnalysisOptions;
result: ReturnType<typeof getPeaksAnalysis>;
};
waveform: {
options: WaveformOptions;
result: WaveformResult;
};
waveformAnalysis: {
options: WaveformAnalysisOptions;
result: ReturnType<typeof getWaveformAnalysis>;
};
zeroCrossing: {
options: ZeroCrossingOptions;
result: ZeroCrossingResult;
};
energy: {
options: EnergyOptions;
result: EnergyResult;
};
fft: {
options: FFTOptions;
result: FFTResult;
};
spectrum: {
options: SpectrumOptions;
result: SpectrumResult;
};
spectrogram: {
options: SpectrogramOptions;
result: SpectrogramAnalysisResult;
};
spectralFeatures: {
options: SpectralFeaturesOptions;
result: SpectralFeaturesResult;
};
timeVaryingSpectralFeatures: {
options: TimeVaryingSpectralOptions;
result: TimeVaryingSpectralResult;
};
spectralEntropy: {
options: SpectralEntropyOptions;
result: SpectralEntropyResult;
};
spectralCrest: {
options: SpectralCrestOptions;
result: SpectralCrestResult;
};
melSpectrogram: {
options: MelSpectrogramOptions;
result: MelSpectrogramResult;
};
cqt: {
options: CQTOptions;
result: CQTResult;
};
mfcc: {
options: MFCCOptions;
result: MFCCResult;
};
mfccWithDelta: {
options: MFCCWithDeltaOptions;
result: MFCCWithDeltaResult;
};
lufs: {
options: LUFSOptions;
result: LUFSResult;
};
vad: {
options: VADOptions;
result: VADResult;
};
crestFactor: {
options: CrestFactorOptions;
result: CrestFactorResult;
};
stereo: {
options: StereoOptions;
result: StereoResult;
};
timeVaryingStereo: {
options: TimeVaryingStereoOptions;
result: TimeVaryingStereoResult;
};
}
type FeatureId = keyof FeatureRegistry & string;
type FeatureOptions<K extends FeatureId> = FeatureRegistry[K]['options'];
type FeatureResult<K extends FeatureId> = FeatureRegistry[K]['result'];
type FeatureSelection<T extends FeatureId = FeatureId> = {
[K in T]?: FeatureOptions<K> | true;
};
type FeatureInput<T extends FeatureId = FeatureId> = FeatureSelection<T> | readonly T[];
type SelectedFeatureIds<F> = F extends readonly (infer I)[] ? Extract<I, FeatureId> : Extract<keyof F, FeatureId>;
declare const FEATURES: readonly FeatureId[];
interface TimeRange {
start?: number;
end?: number;
}
interface AnalyzeProgressEvent<T extends FeatureId = FeatureId> {
phase: 'feature';
completed: number;
total: number;
feature: T;
}
interface AnalyzeRequest<F extends FeatureInput = FeatureInput> {
features: F;
range?: TimeRange;
continueOnError?: boolean;
onProgress?: (event: AnalyzeProgressEvent<SelectedFeatureIds<F>>) => void;
signal?: AbortSignal;
}
interface AnalyzeResult<T extends FeatureId = FeatureId> {
meta: {
sampleRate: number;
channels: number;
duration: number;
length: number;
range: Required<TimeRange>;
totalElapsedMs: number;
};
results: Partial<{
[K in T]: FeatureResult<K>;
}>;
errors: Partial<Record<T, AudioInspectError>>;
}
interface InspectRequest<F extends FeatureInput = FeatureInput> extends Omit<AnalyzeRequest<F>, 'signal'> {
load?: LoadOptions;
signal?: AbortSignal;
}
type InspectResult<T extends FeatureId = FeatureId> = AnalyzeResult<T> & {
meta: AnalyzeResult<T>['meta'] & {
loadElapsedMs: number;
};
};
declare function analyze<const F extends FeatureInput>(audio: AudioLike, request: AnalyzeRequest<F>): Promise<AnalyzeResult<SelectedFeatureIds<F>>>;
declare function inspect<const F extends FeatureInput>(source: AudioSource, request: InspectRequest<F>): Promise<InspectResult<SelectedFeatureIds<F>>>;
interface PrepareWorkletOptions {
moduleUrl?: string;
}
declare function prepareWorklet(context: BaseAudioContext, options?: PrepareWorkletOptions): Promise<void>;
type RealtimePolicyMode = 'allow' | 'warn' | 'strict';
type MonitorSource = AudioNode | MediaStream | HTMLMediaElement;
type MonitorState = 'idle' | 'running' | 'suspended' | 'closed';
type MonitorEngine = 'worklet';
interface MonitorOptions<F extends FeatureInput = FeatureInput> {
context: BaseAudioContext;
features: F;
source?: MonitorSource;
autoAttach?: boolean;
worklet?: PrepareWorkletOptions;
bufferSize?: number;
hopSize?: number;
inputChannelCount?: number;
output?: 'none' | 'destination' | AudioNode;
emit?: 'hop' | 'raf' | number;
realtimePolicy?: RealtimePolicyMode;
heavyFeatureInterval?: number;
}
interface MonitorFrame<T extends FeatureId = FeatureId> {
timestamp: number;
sampleIndex: number;
results: Partial<{
[K in T]: FeatureResult<K>;
}>;
}
interface MonitorErrorEvent {
code: string;
message: string;
cause?: unknown;
recoverable?: boolean;
}
interface MonitorSession<T extends FeatureId = FeatureId> {
readonly state: MonitorState;
readonly engine: MonitorEngine;
readonly features: ReadonlySet<FeatureId>;
readonly node: AudioNode;
setFeature<K extends FeatureId>(feature: K, options?: FeatureOptions<K> | true): Promise<void>;
removeFeature(feature: FeatureId): Promise<void>;
setFeatures<K extends FeatureId>(features: FeatureSelection<K>): Promise<void>;
read(): MonitorFrame<T> | null;
readFeature<K extends FeatureId>(feature: K): FeatureResult<K> | undefined;
onFrame(handler: (frame: MonitorFrame<T>) => void): () => void;
onError(handler: (event: MonitorErrorEvent) => void): () => void;
attach(source: MonitorSource): void;
detach(source?: MonitorSource): void;
suspend(): Promise<void>;
resume(): Promise<void>;
close(): Promise<void>;
}
declare function monitor<const F extends FeatureInput>(options: MonitorOptions<F>): Promise<MonitorSession<SelectedFeatureIds<F>>>;
export { type AnalyzeProgressEvent, type AnalyzeRequest, type AnalyzeResult, type AudioData, type AudioDecoder, AudioInspectError, type AudioLike, type AudioSource, type ChannelSelector, FEATURES, type FFTResult, type FeatureId, type FeatureInput, type FeatureOptions, type FeatureRegistry, type FeatureResult, type FeatureSelection, type InspectRequest, type InspectResult, type LoadOptions, type MFCCWithDeltaOptions, type MFCCWithDeltaResult, type MonitorEngine, type MonitorErrorEvent, type MonitorFrame, type MonitorOptions, type MonitorSession, type MonitorSource, type MonitorState, type PeakOptions, type PeakResult, type PrepareWorkletOptions, type RMSOptions, type RMSResult, type SelectedFeatureIds, type SpectrogramResult, type SpectrumResult, type StereoOptions, type StereoResult, type TimeRange, type TimeVaryingStereoOptions, type TimeVaryingStereoResult, type ZeroCrossingOptions, type ZeroCrossingResult, analyze, inspect, isAudioInspectError, load, monitor, prepareWorklet };