@kmholding/rtp-packetizer
v2.0.2
Published
High-performance RTP packetizer with true SRC resampling, 8↔48kHz, 5 interpolation methods & real-time diagnostic
Downloads
4,917
Readme
@kmholding/rtp-packetizer
A comprehensive TypeScript library for real-time audio processing, RTP (Real-Time Protocol) packet handling, and audio resampling. Perfect for VoIP applications, real-time audio streaming, and audio diagnostics.
Features
- RTP Packetization: Fully-featured RTP packet creation, parsing, and management
- Audio Processing:
- Volume gain control with clipping detection
- Audio normalization and peak analysis
- Fade in/fade out effects
- Volume statistics (Peak, RMS, dBFS)
- Codec Support:
- G.711 μ-law (PCMU)
- G.711 A-law (PCMA)
- 16-bit Linear PCM (L16)
- Resampling:
- Manual interpolation methods (Linear, Cubic, Lagrange)
- libsamplerate integration for high-quality resampling
- Unified interface for easy switching
- Network Diagnostics:
- Jitter and delay analysis
- Packet loss detection
- Quality rating and warnings
- Timing analysis reports
- Zero Dependencies: Core functionality works without external native modules
Installation
npm
npm install @kmholding/rtp-packetizeryarn
yarn add @kmholding/rtp-packetizerbun
bun add @kmholding/rtp-packetizerImport Styles
The library supports multiple ways to import, allowing you to only import what you need:
Main Entry Point (All exports)
import { AudioStream, AudioGain, RTPHelper, CodecHelper } from '@kmholding/rtp-packetizer'Core Classes
// Import audio processing classes
import {
AudioStream,
AudioGain,
AudioResampling,
AudioLibSampleRate,
AudioUnifiedResampler,
AudioDiagnostic,
} from '@kmholding/rtp-packetizer/core'Helper Utilities
// Import low-level helper utilities
import { RTPHelper, CodecHelper, BufferHelper } from '@kmholding/rtp-packetizer/helpers'Constants
// Import all constants and enums
import {
RESAMPLING_METHODS,
RESAMPLING_QUALITIES,
RESAMPLING_PROVIDES,
RTP_PAYLOAD_TYPES,
} from '@kmholding/rtp-packetizer/constants'Type Definitions
// Import TypeScript interfaces and types
import type {
AudioStream,
RTPHeader,
StreamingChannelConfig,
StreamingStats,
TimingAnalysisReport,
GainResult,
VolumeStats,
QualityAnalysis,
} from '@kmholding/rtp-packetizer/types'Quick Start
Basic RTP Streaming
import { AudioStream } from '@kmholding/rtp-packetizer/core'
import { RTP_PAYLOAD_TYPES } from '@kmholding/rtp-packetizer/constants'
// Create streaming instance
const libStreamCore = new AudioStream({
payloadType: RTP_PAYLOAD_TYPES.L16, // 16-bit PCM
prebufferFrames: 6,
})
// Initialize channel
const channelID = libStreamCore.initialize({
channelID: 'call-001',
externalHost: '192.168.1.100',
externalPort: 5060,
sampleRate: 8000,
frameSize: 160, // 20ms at 8kHz
enableLogging: true,
})
// Prepare audio (16-bit PCM)
const audioBuffer = Buffer.from([
/* 16-bit PCM data */
])
// Queue frames with optional RTP params
const framesQueued = libStreamCore.queueAudioFrames(channelID, audioBuffer, {
startSeqNum: 1000,
startTimestamp: 0,
ssrc: Math.floor(Math.random() * 0xffffffff),
})
console.log(`Queued ${framesQueued} frames`)
// Get and send packets
let packet
while ((packet = libStreamCore.getNextPacket(channelID))) {
console.log(`Seq: ${packet.seqNum}, Timestamp: ${packet.timestamp}`)
}
// Check stats
const stats = libStreamCore.getChannelStats(channelID)
console.log(`Bytes sent: ${stats?.totalBytesSent}`)Audio Gain Control
import { AudioGain } from '@kmholding/rtp-packetizer/core'
import { BufferHelper } from '@kmholding/rtp-packetizer/helpers'
const libBufferHelper = new BufferHelper()
const libGainCore = new AudioGain({
gain: 1.2, // 20% volume increase
peak: 0.9, // Target peak level
})
// Get audio as buffer
const audioBuffer = Buffer.from([
/* 16-bit PCM */
])
// Apply amplification
const result = libGainCore.amplify(audioBuffer, 1.2)
console.log(`Clipping: ${result.clippingRate.toFixed(2)}% (${result.clippingCount} samples)`)
// Analyze volume
const samples = libBufferHelper.bufferToInt16(result.buffer)
const stats = libGainCore.getVolumeStats(samples)
console.log(`Peak: ${stats.peak}, RMS: ${stats.rms}, dBFS: ${stats.dbfs.toFixed(2)}`)
// Normalize to 95% peak
const normalized = libGainCore.normalize(samples, 0.95)
// Apply fade effects
const fadedIn = libGainCore.fadeIn(normalized, 4000) // Fade in over 4000 samples
const fadedOut = libGainCore.fadeOut(fadedIn, 4000) // Fade outResampling
import { AudioUnifiedResampler } from '@kmholding/rtp-packetizer/core'
import { RESAMPLING_PROVIDES } from '@kmholding/rtp-packetizer/constants'
// Fast manual resampling
const libUnifiedManualCore = new AudioUnifiedResampler({
provide: RESAMPLING_PROVIDES.MANUAL,
method: 'LAGRANGE', // High quality interpolation
})
const audioData = new Int16Array([
/* 16000 Hz audio */
])
const resampled = await libUnifiedManualCore.resample(audioData, 16000, 8000)
console.log(`Resampled: ${audioData.length} → ${resampled.length} samples`)
// High-quality libsamplerate
const libUnifiedHQCore = new AudioUnifiedResampler({
provide: RESAMPLING_PROVIDES.LIBSAMPLERATE,
quality: 'SRC_SINC_BEST_QUALITY',
channels: 1,
})
const hqResult = await libUnifiedHQCore.resample(audioData, 16000, 8000)
console.log(`Best quality resampling done`)
libUnifiedHQCore.destroy() // Important: cleanup resourcesNetwork Diagnostics
import { AudioDiagnostic } from '@kmholding/rtp-packetizer/core'
const libDiagnosticCore = new AudioDiagnostic({ frameIntervalMs: 20 })
// Collect packet timing logs
const timingLogs = [
{ seq: 1, ts: 1000, sentAt: Date.now() },
{ seq: 2, ts: 1160, sentAt: Date.now() + 20 },
{ seq: 3, ts: 1320, sentAt: Date.now() + 25 }, // 5ms jitter
{ seq: 4, ts: 1480, sentAt: Date.now() + 45 }, // 25ms jitter
// ... more packets collected during streaming
]
// Analyze quality
const report = libDiagnosticCore.analyze(timingLogs, 20)
if (report) {
console.log(`Quality: ${report.quality.rating}`)
console.log(`Jitter: max=${report.jitters.max.toFixed(2)}ms, avg=${report.jitters.avg.toFixed(2)}ms`)
console.log(`Delay: min=${report.delays.min.toFixed(2)}ms, avg=${report.delays.avg.toFixed(2)}ms`)
if (report.errors.length > 0) {
report.errors.forEach((e) => console.error(`ERROR: ${e}`))
}
if (report.warnings.length > 0) {
report.warnings.forEach((w) => console.warn(`WARN: ${w}`))
}
// Print formatted report
console.log('\n' + libDiagnosticCore.formatReport(report))
}Core Classes
AudioStream
High-level RTP packet management for audio streaming.
Constructor:
import { AudioStream } from '@kmholding/rtp-packetizer/core'
import { RTP_PAYLOAD_TYPES } from '@kmholding/rtp-packetizer/constants'
const libStreamCore = new AudioStream({
prebufferFrames: 6, // Buffer 6 frames before sending
payloadType: RTP_PAYLOAD_TYPES.L16, // 16-bit PCM
})Basic Usage:
// 1. Initialize channel
const channelID = libStreamCore.initialize({
channelID: 'session-1',
externalHost: '10.0.0.1',
externalPort: 5060,
sampleRate: 8000, // Telephony standard
frameSize: 160, // 20ms frame
prebufferFrames: 6, // Pre-buffer 120ms
enableLogging: true,
})
// 2. Queue audio frames
const audioBuffer = Buffer.alloc(320) // 40ms of audio
const framesQueued = libStreamCore.queueAudioFrames(channelID, audioBuffer, {
startSeqNum: Math.floor(Math.random() * 65536),
startTimestamp: Math.floor(Math.random() * 0xffffffff),
ssrc: Math.floor(Math.random() * 0xffffffff),
})
// 3. Retrieve packets
let packet = libStreamCore.getNextPacket(channelID)
while (packet) {
console.log(`Sending packet: seq=${packet.seqNum}, ts=${packet.timestamp}`)
packet = libStreamCore.getNextPacket(channelID)
}
// 4. Monitor stats
const stats = libStreamCore.getChannelStats(channelID)
console.log(`Queued: ${stats?.totalPacketsQueued}, Sent: ${stats?.totalFramesSent}`)Methods:
initialize(config)- Initialize streaming channel- Returns: Channel ID string
queueAudioFrames(channelID, audioBuffer, options?)- Queue audio for transmission- Returns: Number of frames queued
getNextPacket(channelID)- Retrieve next queued packet- Returns:
QueuedRTPPacket | null
- Returns:
getChannelStats(channelID)- Get channel statistics- Returns:
StreamingStats
- Returns:
AudioGain
Volume control and audio normalization engine.
Constructor:
import { AudioGain } from '@kmholding/rtp-packetizer/core'
const libGainCore = new AudioGain({
gain: 1.2, // Volume multiplier
peak: 0.9, // Target peak level
threshold: 0.6, // Compression threshold
ratio: 4, // Compression ratio
})Basic Usage:
// 1. Analyze audio levels
const audioBuffer = Buffer.alloc(8000)
const stats = libGainCore.getVolumeStats(audioBuffer)
console.log(`Peak: ${stats.peak}, RMS: ${stats.rms}dB, dBFS: ${stats.dbfs}`)
// 2. Apply volume gain
const amplified = libGainCore.applyVolumeGain(audioBuffer, 1.5)
// 3. Normalize to target peak
const normalized = libGainCore.normalize(audioBuffer, 0.9)
// 4. Fade effects
const fadeInBuffer = libGainCore.fadeIn(audioBuffer, 4000) // 500ms fade at 8kHz
const fadeOutBuffer = libGainCore.fadeOut(audioBuffer, 4000)
// 5. Full amplification with stats
const { buffer: processed, stats: ampStats } = libGainCore.amplify(audioBuffer, 2.0)
console.log(`Processed: peak=${ampStats.peak}, rms=${ampStats.rms}`)Methods:
applyVolumeGain(samples, gain?)- Apply gain with clipping detection- Returns: Processed Buffer
amplify(buffer, gain?)- Amplify with statistics- Returns:
{ buffer: Buffer, stats: VolumeStats }
- Returns:
normalize(samples, targetPeak?)- Normalize to peak level- Returns: Normalized Buffer
getVolumeStats(samples)- Get peak, RMS, dBFS values- Returns:
VolumeStats
- Returns:
fadeIn(samples, fadeLengthSamples)- Fade in from silence- Returns: Faded Buffer
fadeOut(samples, fadeLengthSamples)- Fade out to silence- Returns: Faded Buffer
AudioResampling
CPU-efficient resampling using interpolation methods.
Constructor:
import { AudioResampling } from '@kmholding/rtp-packetizer/core'
import { RESAMPLING_METHODS } from '@kmholding/rtp-packetizer/constants'
const libResamplerCore = new AudioResampling({
method: RESAMPLING_METHODS.LAGRANGE, // Highest quality
})Basic Usage:
// 1. Generic resample
const audioBuffer = Buffer.alloc(16000) // 2 seconds at 8kHz
const resampled = libResamplerCore.resample(audioBuffer, 8000, 16000) // 8kHz → 16kHz
console.log(`Original: ${audioBuffer.length} bytes, Resampled: ${resampled.length} bytes`)
// 2. Use convenience methods
const to8kHz = libResamplerCore.resampleTo8kHz(audioBuffer)
const to16kHz = libResamplerCore.resampleTo16kHz(audioBuffer)
// 3. Different interpolation methods
const libLinearCore = new AudioResampling({ method: 'LINEAR' })
const libCubicCore = new AudioResampling({ method: 'CUBIC' })
const libLagrangeCore = new AudioResampling({ method: 'LAGRANGE' })
const fastResult = libLinearCore.resample(audioBuffer, 8000, 16000) // Fastest
const mediumResult = libCubicCore.resample(audioBuffer, 8000, 16000) // Balanced
const qualityResult = libLagrangeCore.resample(audioBuffer, 8000, 16000) // Best qualityMethods:
resample(samples, sourceSampleRate, targetSampleRate)- Generic resample- Returns: Resampled Buffer
resampleLinear()- Linear interpolation (fast, lower quality)- Returns: Resampled Buffer
resampleCubic()- Cubic interpolation (medium)- Returns: Resampled Buffer
resampleLagrange()- Lagrange interpolation (high quality)- Returns: Resampled Buffer
resampleTo8kHz()- Convenience: resample to 8kHz- Returns: Resampled Buffer
resampleTo16kHz()- Convenience: resample to 16kHz- Returns: Resampled Buffer
AudioLibSampleRate
High-quality resampling using libsamplerate (Secret Rabbit Code).
Constructor:
import { AudioLibSampleRate } from '@kmholding/rtp-packetizer/core'
import { RESAMPLING_QUALITIES } from '@kmholding/rtp-packetizer/constants'
const libResamplerCore = new AudioLibSampleRate({
quality: RESAMPLING_QUALITIES.SRC_SINC_BEST_QUALITY, // Highest quality
channels: 1, // Mono
})Basic Usage:
// 1. Initialize for resampling
const audioBuffer = Buffer.alloc(16000)
const initResult = await libResamplerCore.initialize(8000, 16000, {
channels: 1,
inputLength: audioBuffer.length,
})
console.log(`Initialized: ${initResult.success}`)
// 2. Resample audio
const resampled = libResamplerCore.resample(audioBuffer, 8000, 16000, 1)
console.log(`Input: ${audioBuffer.length}, Output: ${resampled.length}`)
// 3. Convenience methods
const to8kHz = await libResamplerCore.resampleTo8kHz(audioBuffer, 16000)
const to16kHz = await libResamplerCore.resampleTo16kHz(audioBuffer, 8000)
// 4. Clean up when done (IMPORTANT!)
libResamplerCore.destroyAll() // Free libsamplerate resourcesMethods:
async initialize(sourceSampleRate, targetSampleRate, config?)- Initialize resampler- Returns:
{ success: boolean }
- Returns:
resample(input, sourceSampleRate, targetSampleRate, channels?)- Resample buffer- Returns: Resampled Buffer
async resampleTo8kHz(input, sourceSampleRate, config?)- Resample to 8kHz- Returns: Resampled Buffer
async resampleTo16kHz(input, sourceSampleRate, config?)- Resample to 16kHz- Returns: Resampled Buffer
destroyAll()- IMPORTANT: Free libsamplerate resources- Returns: void
AudioUnifiedResampler
Unified interface for resampling (manual or libsamplerate).
Constructor:
import { AudioUnifiedResampler } from '@kmholding/rtp-packetizer/core'
import { RESAMPLING_PROVIDES } from '@kmholding/rtp-packetizer/constants'
// Option 1: CPU-efficient manual interpolation
const libUnifiedManualCore = new AudioUnifiedResampler({
provide: RESAMPLING_PROVIDES.MANUAL,
method: 'LAGRANGE', // Interpolation method
channels: 1,
})
// Option 2: High-quality libsamplerate
const libUnifiedHQCore = new AudioUnifiedResampler({
provide: RESAMPLING_PROVIDES.LIBSAMPLERATE,
quality: 'SRC_SINC_BEST_QUALITY',
channels: 1,
})Basic Usage:
const audioBuffer = Buffer.alloc(16000)
// 1. Manual resample (synchronous, lightweight)
const manualResult = await libUnifiedManualCore.resample(audioBuffer, 8000, 16000)
console.log(`Manual: ${audioBuffer.length} → ${manualResult.length} bytes`)
// 2. Libsamplerate resample (asynchronous, high quality)
const libResult = await libUnifiedHQCore.resample(audioBuffer, 8000, 16000)
console.log(`Libsamplerate: ${audioBuffer.length} → ${libResult.length} bytes`)
// 3. Choose provider based on quality requirements
if (requiresHighQuality) {
const resampled = await libUnifiedHQCore.resample(audio, 8000, 16000)
} else {
const resampled = await libUnifiedManualCore.resample(audio, 8000, 16000)
}
// 4. Clean up
libUnifiedHQCore.destroy() // For libsamplerate providerMethods:
async resample(samples, sourceSampleRate, targetSampleRate)- Resample buffer- Returns: Resampled Buffer
destroy()- Cleanup resources (important for libsamplerate)- Returns: void
AudioDiagnostic
Analyze RTP streaming quality, timing, and jitter metrics.
Constructor:
import { AudioDiagnostic } from '@kmholding/rtp-packetizer/core'
const libDiagnosticCore = new AudioDiagnostic({
frameIntervalMs: 20, // Expected frame duration
})Basic Usage:
// 1. Create timing log during streaming
const sendLog = [
{ seq: 1, ts: 1000, sentAt: 0 },
{ seq: 2, ts: 1160, sentAt: 20 },
{ seq: 3, ts: 1320, sentAt: 40 },
{ seq: 4, ts: 1480, sentAt: 60 },
]
// 2. Analyze stream quality
const report = libDiagnosticCore.analyze(sendLog, 20)
if (report) {
console.log(`Jitter: ${report.jitterMs}ms`)
console.log(`Quality: ${report.quality}`) // EXCELLENT, GOOD, FAIR, POOR
console.log(`Skipped Packets: ${report.skippedPackets}`)
console.log(`Timestamp Drift: ${report.timestampDriftMs}ms`)
}
// 3. Format readable report
const formattedReport = libDiagnosticCore.formatReport(report)
console.log(formattedReport)
// 4. Quality thresholds
// EXCELLENT: jitter ≤ 5ms
// GOOD: jitter ≤ 15ms
// FAIR: jitter ≤ 30ms
// POOR: jitter > 30msMethods:
analyze(sendLog, frameIntervalMs?)- Analyze packet timing- Input:
SendTimingLog[]with { seq, ts, sentAt } - Returns:
TimingAnalysisReport | null
- Input:
formatReport(report)- Format report as readable text- Returns: String with formatted analysis
Quality Ratings:
- Excellent (jitter ≤ 5ms) - Optimal streaming
- Good (jitter ≤ 15ms) - Acceptable for VoIP
- Fair (jitter ≤ 30ms) - Noticeable but manageable
- Poor (jitter > 30ms) - Quality degradation
Helper Classes
RTPHelper (Static)
Low-level RTP packet operations.
Methods:
parseRTPHeader(buffer)- Extract header from bytesparseRTPPacket(packet)- Parse complete packetcreateRTPHeader(header)- Create header bytescreateRTPPacket(header, payload)- Create full packetisValidRTPPacket(msg)- Validate packet structureisPacketLoss(lastSeq, currentSeq)- Detect losscalculateSequenceGap(lastSeq, currentSeq)- Count missing packetscalculateJitter(timestamps)- Calculate jitter statsrtpTimestampToMs(timestamp, sampleRate?)- Convert timestampmsToRtpTimestamp(ms, sampleRate?)- Convert to timestampgetPayloadTypeName(payloadType)- Get codec name
CodecHelper (Static)
Audio codec encoding/decoding.
Methods:
decodeULaw(buffer)- Decode μ-law to PCMlinearToULaw(sample)- Encode single samplelinearToULawBuffer(pcmBuffer)- Encode bufferdecodeALaw(buffer)- Decode A-law to PCMlinearToALaw(sample)- Encode to A-lawlinearToALawBuffer(pcmBuffer)- Encode bufferdecodePcmu(byte)- Alias for decodeULawlinearToPcmu(sample)- Alias for linearToULaw
BufferHelper (Static)
Buffer and array utilities.
Methods:
bufferToInt16(buffer)- Buffer → Int16Arrayint16ToBuffer(int16Array)- Int16Array → BufferconcatenateInt16Arrays(...arrays)- Merge arrayssliceInt16Array(array, start, end?)- Slice arraycloneInt16Array(array)- Copy arraylittleToBigEndian16(buffer)- Endian conversionbigToLittleEndian16(buffer)- Endian conversiongetBufferDurationMs(buffer, sampleRate)- Calculate durationallocateSilence(sampleCount)- Create silencetrimSilence(samples, threshold?)- Remove silence edges
Constants
RESAMPLING_METHODS = {
LINEAR: 'LINEAR',
LAGRANGE: 'LAGRANGE',
CUBIC: 'CUBIC',
}
RESAMPLING_QUALITIES = {
BEST_QUALITY: 'SRC_SINC_BEST_QUALITY',
MEDIUM_QUALITY: 'SRC_SINC_MEDIUM_QUALITY',
LINEAR: 'SRC_LINEAR',
}
RTP_PAYLOAD_TYPES = {
PCMU: 0, // G.711 μ-law
PCMA: 8, // G.711 A-law
GSM: 3, // GSM
L16: 11, // 16-bit PCM
OPUS: 111, // Opus
// ... and more
}Complete Usage Examples
Audio Processing Pipeline
import { AudioGain, AudioResampling } from '@kmholding/rtp-packetizer/core'
import { BufferHelper, CodecHelper } from '@kmholding/rtp-packetizer/helpers'
async function processAudio(inputFile: string): Promise<Buffer> {
const libBufferHelper = new BufferHelper()
const libCodecHelper = new CodecHelper()
// 1. Load audio (16kHz, 16-bit PCM)
const rawAudio = loadAudioFile(inputFile)
// 2. Normalize volume to 95% peak
const libGainCore = new AudioGain({ peak: 0.95 })
const samples = libBufferHelper.bufferToInt16(rawAudio)
const normalized = libGainCore.normalize(samples)
// 3. Resample to 8kHz for VoIP
const libResamplerCore = new AudioResampling({ method: 'LAGRANGE' })
const resampled = libResamplerCore.resample(normalized, 16000, 8000)
// 4. Apply fade in (500ms @ 8kHz = 4000 samples)
const fadedIn = libGainCore.fadeIn(resampled, 4000)
// 5. Encode to U-Law (PCMU)
const pcmBuffer = libBufferHelper.int16ToBuffer(fadedIn)
const ulawBuffer = libCodecHelper.linearToULawBuffer(pcmBuffer)
return ulawBuffer
}RTP Streaming with Diagnostics
import { AudioStream, AudioDiagnostic } from '@kmholding/rtp-packetizer/core'
import * as dgram from 'dgram'
class RTPSession {
private readonly libStreamCore = new AudioStream()
private readonly libDiagnosticCore = new AudioDiagnostic({ frameIntervalMs: 20 })
private timingLogs: any[] = []
private socket = dgram.createSocket('udp4')
initialize(remoteHost: string, remotePort: number) {
this.libStreamCore.initialize({
channelID: 'session-1',
externalHost: remoteHost,
externalPort: remotePort,
sampleRate: 8000,
})
}
sendAudio(audioBuffer: Buffer) {
const channelID = 'session-1'
this.libStreamCore.queueAudioFrames(channelID, audioBuffer)
let packet
while ((packet = this.libStreamCore.getNextPacket(channelID))) {
// Record for diagnostics
this.timingLogs.push({
seq: packet.seqNum,
ts: packet.timestamp,
sentAt: Date.now(),
})
// Send to remote
this.socket.send(packet.buffer, remotePort, remoteHost)
}
}
analyzeQuality() {
if (this.timingLogs.length < 10) {
return null
}
return this.libDiagnosticCore.analyze(this.timingLogs)
}
printDiagnostics() {
const report = this.analyzeQuality()
if (!report) {
return
}
console.log(this.libDiagnosticCore.formatReport(report))
if (report.errors.length > 0) {
report.errors.forEach((e) => console.error(` - ${e}`))
}
if (report.warnings.length > 0) {
report.warnings.forEach((w) => console.warn(` - ${w}`))
}
}
}Codec Conversion
import { CodecHelper, BufferHelper } from '@kmholding/rtp-packetizer/helpers'
const libBufferHelper = new BufferHelper()
const libCodecHelper = new CodecHelper()
// Convert PCM to PCMU
function wavToPcmu(wavFile: Buffer): Buffer {
return libCodecHelper.linearToULawBuffer(wavFile)
}
// Receive PCMU packets and reconstruct PCM
function pcmuToPcm(packets: Buffer[]): Buffer {
const pcmSamples = packets.map((p) => CodecHelper.decodePcmuBuffer(p))
const combined = libBufferHelper.concatenateInt16Arrays(...pcmSamples)
return libBufferHelper.int16ToBuffer(combined)
}
// Usage
const pcm = loadWavFile('input.wav')
const pcmu = wavToPcmu(pcm)
// ... transmit pcmu packets ...
const receivedPackets = [
/* PCMU data */
]
const reconstructed = pcmuToPcm(receivedPackets)
saveWavFile('output.wav', reconstructed)Performance
Resampling Speed (20ms frame @ 8kHz)
- LINEAR: 0.1ms
- CUBIC: 0.3ms
- LAGRANGE: 0.5ms
- LIBSAMPLERATE: 0.2-0.8ms
Memory per Channel
- ~10KB base
- 160 bytes per frame in queue
- Total: base + (frameSize × prebufferFrames × 2)
Tips
- Reuse class instances
- Use LIBSAMPLERATE for high quality, MANUAL for speed
- Call
destroy()on libsamplerate instances - Process in batches when possible
Troubleshooting
Audio Clipping?
if (result.clippingRate > 0) {
const normalized = gain.normalize(samples, 0.8)
}High Jitter?
libStreamCore.initialize({
frameSize: 80, // Smaller frames
prebufferFrames: 10, // More buffering
})Memory Leak?
try {
// Use resampler
} finally {
libResamplerCore.destroy() // Always cleanup
}License
MIT © K&M Holdings
