算法2026-05-03·10 分钟
算法知识库:音频处理算法实现
JavaScript/TypeScript 实现音频处理基础算法,如傅里叶变换、滤波器、音频压缩等。
音频处理算法实现
1. 离散傅里叶变换 (DFT)
ts
function dft(signal: number[]): { real: number[]; imag: number[] } {
const N = signal.length;
const real = new Array(N).fill(0);
const imag = new Array(N).fill(0);
for (let k = 0; k < N; k += 1) {
for (let n = 0; n < N; n += 1) {
const angle = (-2 * Math.PI * k * n) / N;
real[k] += signal[n] * Math.cos(angle);
imag[k] += signal[n] * Math.sin(angle);
}
}
return { real, imag };
}
function idft(real: number[], imag: number[]): number[] {
const N = real.length;
const signal = new Array(N).fill(0);
for (let n = 0; n < N; n += 1) {
for (let k = 0; k < N; k += 1) {
const angle = (2 * Math.PI * k * n) / N;
signal[n] += real[k] * Math.cos(angle) - imag[k] * Math.sin(angle);
}
signal[n] /= N;
}
return signal;
}2. 快速傅里叶变换 (FFT) - Cooley-Tukey 算法
ts
function fft(signal: number[]): { real: number[]; imag: number[] } {
const N = signal.length;
if (N <= 1) {
return { real: [signal[0] || 0], imag: [0] };
}
// 分离偶数和奇数索引
const even = [];
const odd = [];
for (let i = 0; i < N; i += 2) {
even.push(signal[i]);
if (i + 1 < N) {
odd.push(signal[i + 1]);
}
}
const evenFFT = fft(even);
const oddFFT = fft(odd);
const real = new Array(N).fill(0);
const imag = new Array(N).fill(0);
for (let k = 0; k < N / 2; k += 1) {
const angle = (-2 * Math.PI * k) / N;
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const evenReal = evenFFT.real[k];
const evenImag = evenFFT.imag[k];
const oddReal = oddFFT.real[k] || 0;
const oddImag = oddFFT.imag[k] || 0;
real[k] = evenReal + cos * oddReal - sin * oddImag;
imag[k] = evenImag + cos * oddImag + sin * oddReal;
real[k + N / 2] = evenReal - cos * oddReal + sin * oddImag;
imag[k + N / 2] = evenImag - cos * oddImag - sin * oddReal;
}
return { real, imag };
}
function ifft(real: number[], imag: number[]): number[] {
const N = real.length;
const conjugatedImag = imag.map(x => -x);
const result = fft(real.map((r, i) => r + conjugatedImag[i] * 1j || 0));
// 简化版:实际需要处理复数
return result.real.map(x => x / N);
}3. 数字滤波器
ts
class DigitalFilter {
private a: number[]; // 反馈系数
private b: number[]; // 前馈系数
private xHistory: number[]; // 输入历史
private yHistory: number[]; // 输出历史
constructor(b: number[], a: number[] = [1]) {
this.b = b;
this.a = a;
this.xHistory = new Array(b.length).fill(0);
this.yHistory = new Array(a.length - 1).fill(0);
}
process(input: number[]): number[] {
const output: number[] = [];
for (const x of input) {
// 更新输入历史
this.xHistory.unshift(x);
this.xHistory.pop();
// 计算输出
let y = 0;
// 前馈部分
for (let i = 0; i < this.b.length; i += 1) {
y += this.b[i] * this.xHistory[i];
}
// 反馈部分
for (let i = 1; i < this.a.length; i += 1) {
y -= this.a[i] * this.yHistory[i - 1];
}
// 更新输出历史
this.yHistory.unshift(y);
this.yHistory.pop();
output.push(y);
}
return output;
}
// 低通滤波器设计
static lowPassFilter(cutoffFreq: number, sampleRate: number, order: number = 2): DigitalFilter {
// 简化版 Butterworth 滤波器系数
const wc = (2 * Math.PI * cutoffFreq) / sampleRate;
const k = Math.tan(wc / 2);
const k2 = k * k;
let b: number[];
let a: number[];
if (order === 1) {
const norm = k + 1;
b = [k / norm];
a = [1, (k - 1) / norm];
} else {
// 二阶
const sqrt2 = Math.sqrt(2);
const norm = k2 + k * sqrt2 + 1;
b = [k2 / norm, (2 * k2) / norm, k2 / norm];
a = [1, (2 * (k2 - 1)) / norm, (k2 - k * sqrt2 + 1) / norm];
}
return new DigitalFilter(b, a);
}
}4. 音频压缩 (μ-law 压缩)
ts
function muLawCompress(sample: number, mu: number = 255): number {
// 归一化到 [-1, 1]
const normalized = Math.max(-1, Math.min(1, sample));
// μ-law 压缩
const sign = normalized < 0 ? -1 : 1;
const absSample = Math.abs(normalized);
let compressed: number;
if (absSample < 1 / (1 + mu)) {
compressed = (absSample * (1 + mu)) / mu;
} else {
compressed = 1 + Math.log(absSample * mu) / Math.log(1 + mu);
}
// 转换为 8 位量化
const quantized = Math.round(compressed * 127);
return sign * quantized;
}
function muLawExpand(compressed: number, mu: number = 255): number {
const sign = compressed < 0 ? -1 : 1;
const absCompressed = Math.abs(compressed) / 127;
let expanded: number;
if (absCompressed < 1 / (1 + mu)) {
expanded = (absCompressed * mu) / (1 + mu);
} else {
expanded = Math.pow(1 + mu, absCompressed - 1) / mu;
}
return sign * expanded;
}5. 频谱分析
ts
class SpectrumAnalyzer {
private fftSize: number;
constructor(fftSize: number = 2048) {
this.fftSize = fftSize;
}
analyze(signal: number[]): { frequencies: number[]; magnitudes: number[]; phases: number[] } {
// 应用汉宁窗
const windowed = this.applyHanningWindow(signal);
// 填充到 FFT 大小
const padded = this.zeroPad(windowed, this.fftSize);
// FFT
const { real, imag } = fft(padded);
// 计算幅度和相位
const magnitudes: number[] = [];
const phases: number[] = [];
const frequencies: number[] = [];
for (let i = 0; i < real.length / 2; i += 1) {
const magnitude = Math.sqrt(real[i] ** 2 + imag[i] ** 2);
const phase = Math.atan2(imag[i], real[i]);
magnitudes.push(magnitude);
phases.push(phase);
frequencies.push((i * 44100) / real.length); // 假设采样率 44.1kHz
}
return { frequencies, magnitudes, phases };
}
private applyHanningWindow(signal: number[]): number[] {
const windowed: number[] = [];
for (let i = 0; i < signal.length; i += 1) {
const window = 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / (signal.length - 1));
windowed.push(signal[i] * window);
}
return windowed;
}
private zeroPad(signal: number[], targetLength: number): number[] {
const padded = [...signal];
while (padded.length < targetLength) {
padded.push(0);
}
return padded;
}
// 计算频谱质心
static spectralCentroid(frequencies: number[], magnitudes: number[]): number {
let numerator = 0;
let denominator = 0;
for (let i = 0; i < frequencies.length; i += 1) {
numerator += frequencies[i] * magnitudes[i];
denominator += magnitudes[i];
}
return denominator > 0 ? numerator / denominator : 0;
}
// 计算频谱滚降
static spectralRolloff(frequencies: number[], magnitudes: number[], percentile: number = 0.85): number {
const totalEnergy = magnitudes.reduce((sum, mag) => sum + mag ** 2, 0);
const targetEnergy = totalEnergy * percentile;
let cumulativeEnergy = 0;
for (let i = 0; i < frequencies.length; i += 1) {
cumulativeEnergy += magnitudes[i] ** 2;
if (cumulativeEnergy >= targetEnergy) {
return frequencies[i];
}
}
return frequencies[frequencies.length - 1];
}
}6. 实现要点
- DFT/FFT 将时域信号转换为频域。
- 数字滤波器用于信号处理。
- μ-law 压缩减少动态范围。
- 频谱分析提取音频特征。
算法音频处理JavaScript