



























@@ -14,10 +14,35 @@ type ResampleKernel = {
1414phaseCount: number;
1515};
161617+const HOST_IS_LITTLE_ENDIAN = new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;
18+1719function clamp16(value: number): number {
1820return Math.max(-32768, Math.min(32767, value));
1921}
202223+function canUseInt16View(buffer: Buffer): boolean {
24+return HOST_IS_LITTLE_ENDIAN && buffer.byteOffset % Int16Array.BYTES_PER_ELEMENT === 0;
25+}
26+27+function int16View(buffer: Buffer): Int16Array {
28+return new Int16Array(
29+buffer.buffer,
30+buffer.byteOffset,
31+Math.floor(buffer.byteLength / Int16Array.BYTES_PER_ELEMENT),
32+);
33+}
34+35+function readInt16Samples(buffer: Buffer): Int16Array {
36+if (canUseInt16View(buffer)) {
37+return int16View(buffer);
38+}
39+const samples = new Int16Array(Math.floor(buffer.byteLength / Int16Array.BYTES_PER_ELEMENT));
40+for (let i = 0; i < samples.length; i += 1) {
41+samples[i] = buffer.readInt16LE(i * Int16Array.BYTES_PER_ELEMENT);
42+}
43+return samples;
44+}
45+2146function sinc(x: number): number {
2247if (x === 0) {
2348return 1;
@@ -65,8 +90,7 @@ function buildResampleKernel(
6590}
66916792function sampleBandlimitedWithCoefficients(
68-input: Buffer,
69-inputSamples: number,
93+input: Int16Array,
7094center: number,
7195coefficients: Float64Array,
7296): number {
@@ -75,25 +99,24 @@ function sampleBandlimitedWithCoefficients(
759976100for (let tap = -RESAMPLE_HALF_TAPS; tap <= RESAMPLE_HALF_TAPS; tap += 1) {
77101const sampleIndex = center + tap;
78-if (sampleIndex < 0 || sampleIndex >= inputSamples) {
102+if (sampleIndex < 0 || sampleIndex >= input.length) {
79103continue;
80104}
81105const coeff = coefficients[tap + RESAMPLE_HALF_TAPS] ?? 0;
82-weighted += input.readInt16LE(sampleIndex * 2) * coeff;
106+weighted += (input[sampleIndex] ?? 0) * coeff;
83107weightSum += coeff;
84108}
8510986110if (weightSum === 0) {
87-const nearest = Math.max(0, Math.min(inputSamples - 1, center));
88-return input.readInt16LE(nearest * 2);
111+const nearest = Math.max(0, Math.min(input.length - 1, center));
112+return input[nearest] ?? 0;
89113}
9011491115return weighted / weightSum;
92116}
9311794118function sampleBandlimited(
95-input: Buffer,
96-inputSamples: number,
119+input: Int16Array,
97120srcPos: number,
98121cutoffCyclesPerSample: number,
99122): number {
@@ -103,20 +126,20 @@ function sampleBandlimited(
103126104127for (let tap = -RESAMPLE_HALF_TAPS; tap <= RESAMPLE_HALF_TAPS; tap += 1) {
105128const sampleIndex = center + tap;
106-if (sampleIndex < 0 || sampleIndex >= inputSamples) {
129+if (sampleIndex < 0 || sampleIndex >= input.length) {
107130continue;
108131}
109132110133const distance = sampleIndex - srcPos;
111134const lowPass = 2 * cutoffCyclesPerSample * sinc(2 * cutoffCyclesPerSample * distance);
112135const coeff = lowPass * (RESAMPLE_WINDOW[tap + RESAMPLE_HALF_TAPS] ?? 0);
113-weighted += input.readInt16LE(sampleIndex * 2) * coeff;
136+weighted += (input[sampleIndex] ?? 0) * coeff;
114137weightSum += coeff;
115138}
116139117140if (weightSum === 0) {
118-const nearest = Math.max(0, Math.min(inputSamples - 1, Math.round(srcPos)));
119-return input.readInt16LE(nearest * 2);
141+const nearest = Math.max(0, Math.min(input.length - 1, Math.round(srcPos)));
142+return input[nearest] ?? 0;
120143}
121144122145return weighted / weightSum;
@@ -143,19 +166,25 @@ export function resamplePcm(
143166const cutoffCyclesPerSample = Math.max(0.01, downsampleCutoff * RESAMPLE_CUTOFF_GUARD);
144167const kernel = buildResampleKernel(inputSampleRate, outputSampleRate, cutoffCyclesPerSample);
145168169+const inputView = readInt16Samples(input);
170+const outputView = canUseInt16View(output) ? int16View(output) : undefined;
171+146172for (let i = 0; i < outputSamples; i += 1) {
147173const sample = Math.round(
148174kernel
149175 ? sampleBandlimitedWithCoefficients(
150-input,
151-inputSamples,
176+inputView,
152177Math.floor((i * inputSampleRate) / outputSampleRate),
153178kernel.coefficients[(i * kernel.inputStep) % kernel.phaseCount] ??
154179kernel.coefficients[0],
155180)
156- : sampleBandlimited(input, inputSamples, i * ratio, cutoffCyclesPerSample),
181+ : sampleBandlimited(inputView, i * ratio, cutoffCyclesPerSample),
157182);
158-output.writeInt16LE(clamp16(sample), i * 2);
183+if (outputView) {
184+outputView[i] = clamp16(sample);
185+} else {
186+output.writeInt16LE(clamp16(sample), i * 2);
187+}
159188}
160189161190return output;
@@ -166,19 +195,26 @@ export function resamplePcmTo8k(input: Buffer, inputSampleRate: number): Buffer
166195}
167196168197export function pcmToMulaw(pcm: Buffer): Buffer {
169-const samples = Math.floor(pcm.length / 2);
170-const mulaw = Buffer.alloc(samples);
198+const pcmView = readInt16Samples(pcm);
199+const mulaw = Buffer.alloc(pcmView.length);
171200172-for (let i = 0; i < samples; i += 1) {
173-const sample = pcm.readInt16LE(i * 2);
174-mulaw[i] = linearToMulaw(sample);
201+for (let i = 0; i < pcmView.length; i += 1) {
202+mulaw[i] = linearToMulaw(pcmView[i] ?? 0);
175203}
176204177205return mulaw;
178206}
179207180208export function mulawToPcm(mulaw: Buffer): Buffer {
181209const pcm = Buffer.alloc(mulaw.length * 2);
210+const pcmView = canUseInt16View(pcm) ? int16View(pcm) : undefined;
211+if (pcmView) {
212+for (let i = 0; i < mulaw.length; i += 1) {
213+pcmView[i] = clamp16(mulawToLinear(mulaw[i] ?? 0));
214+}
215+return pcm;
216+}
217+182218for (let i = 0; i < mulaw.length; i += 1) {
183219pcm.writeInt16LE(clamp16(mulawToLinear(mulaw[i] ?? 0)), i * 2);
184220}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。