@tw2gem/audio-converter
Version:
TypeScript utilities to convert audio formats (e.g., PCM, mu-law) and resample audio for real-time streaming with Twilio or AI models.
89 lines (66 loc) • 2.85 kB
text/typescript
export class AudioConverter {
private static base64ToUint8Array(base64: string): Uint8Array {
const binary = Buffer.from(base64, 'base64');
return new Uint8Array(binary);
}
private static base64ToInt16Array(base64: string): Int16Array {
const buffer = Buffer.from(base64, 'base64');
return new Int16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2);
}
private static muLawToPCM(muLawSample: number): number {
const BIAS = 0x84;
muLawSample = ~muLawSample;
const sign = muLawSample & 0x80;
const exponent = (muLawSample >> 4) & 0x07;
const mantissa = muLawSample & 0x0F;
let sample = ((mantissa << 3) + BIAS) << exponent;
if (sign !== 0) sample = -sample;
return sample;
}
static pcmToMuLaw(sample: number): number {
const BIAS = 0x84;
const CLIP = 32635;
const sign = (sample >> 8) & 0x80;
if (sign !== 0) sample = -sample;
if (sample > CLIP) sample = CLIP;
sample += BIAS;
let exponent = 7;
for (let expMask = 0x4000; (sample & expMask) === 0 && exponent > 0; expMask >>= 1) {
exponent--;
}
const mantissa = (sample >> (exponent + 3)) & 0x0F;
const muLawByte = ~(sign | (exponent << 4) | mantissa);
return muLawByte & 0xFF;
}
static convertBase64MuLawToBase64PCM16k(base64: string): string {
const muLawBytes = this.base64ToUint8Array(base64);
const pcm8000 = new Int16Array(muLawBytes.length);
for (let i = 0; i < muLawBytes.length; i++) {
pcm8000[i] = this.muLawToPCM(muLawBytes[i]);
}
const pcm16000 = new Int16Array(pcm8000.length * 2);
for (let i = 0; i < pcm8000.length; i++) {
const sample = pcm8000[i];
pcm16000[2 * i] = sample;
pcm16000[2 * i + 1] = sample;
}
const buffer = Buffer.from(pcm16000.buffer);
return buffer.toString('base64');
}
static convertBase64PCM24kToBase64MuLaw8k(base64: string): string {
const pcm24k = this.base64ToInt16Array(base64);
const samples8k = Math.floor(pcm24k.length / 3);
const interpolated = new Int16Array(samples8k);
for (let i = 0; i < samples8k; i++) {
const a = pcm24k[i * 3];
const b = pcm24k[i * 3 + 1] ?? a;
const c = pcm24k[i * 3 + 2] ?? b;
interpolated[i] = Math.round((a + b + c) / 3);
}
const muLaw = new Uint8Array(samples8k);
for (let i = 0; i < samples8k; i++) {
muLaw[i] = this.pcmToMuLaw(interpolated[i]);
}
return Buffer.from(muLaw).toString('base64');
}
}