[fix](trx-frontend-http,trx-backend-soapysdr): fix audio rate and stereo copy
JS: fix stereo AudioData channel copy — frame.copyTo with planeIndex:0 only fills the left plane; reading it as interleaved data caused every other sample to be zero, making WFM stereo play at half speed. Now calls copyTo per channel with the correct planeIndex. Rust WFM: replace integer output_decim/output_counter in WfmStereoDecoder with a fractional phase accumulator (output_phase_inc = audio_rate / composite_rate). Integer division caused the effective output rate to drift from audio_sample_rate when the SDR rate is not an exact multiple (e.g. 2 MHz SDR → 250 kHz composite → ~50 kHz output instead of 48 kHz, making audio play 4% slow). Rust non-WFM: add resample_phase/resample_phase_inc to ChannelDsp and use a fractional-phase resampler in process_block for non-WFM paths, ensuring exactly audio_sample_rate samples/sec regardless of SDR rate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -2504,8 +2504,6 @@ function startRxAudio() {
|
||||
const sampleRate = (streamInfo && streamInfo.sample_rate) || 48000;
|
||||
opusDecoder = new AudioDecoder({
|
||||
output: (frame) => {
|
||||
const buf = new Float32Array(frame.numberOfFrames * frame.numberOfChannels);
|
||||
frame.copyTo(buf, { planeIndex: 0 });
|
||||
const forceMono = frame.numberOfChannels >= 2
|
||||
&& wfmAudioModeEl
|
||||
&& wfmAudioModeEl.value === "mono"
|
||||
@@ -2514,21 +2512,21 @@ function startRxAudio() {
|
||||
const outChannels = forceMono ? 1 : frame.numberOfChannels;
|
||||
const ab = audioCtx.createBuffer(outChannels, frame.numberOfFrames, frame.sampleRate);
|
||||
if (forceMono) {
|
||||
// Mix all planes down to mono
|
||||
const monoData = new Float32Array(frame.numberOfFrames);
|
||||
for (let i = 0; i < frame.numberOfFrames; i++) {
|
||||
let sum = 0;
|
||||
for (let ch = 0; ch < frame.numberOfChannels; ch++) {
|
||||
sum += buf[i * frame.numberOfChannels + ch];
|
||||
}
|
||||
monoData[i] = sum / frame.numberOfChannels;
|
||||
for (let ch = 0; ch < frame.numberOfChannels; ch++) {
|
||||
const plane = new Float32Array(frame.numberOfFrames);
|
||||
frame.copyTo(plane, { planeIndex: ch });
|
||||
for (let i = 0; i < frame.numberOfFrames; i++) monoData[i] += plane[i];
|
||||
}
|
||||
const inv = 1 / frame.numberOfChannels;
|
||||
for (let i = 0; i < frame.numberOfFrames; i++) monoData[i] *= inv;
|
||||
ab.copyToChannel(monoData, 0);
|
||||
} else {
|
||||
// Copy each plane directly — AudioData uses planar layout (f32-planar)
|
||||
for (let ch = 0; ch < frame.numberOfChannels; ch++) {
|
||||
const chData = new Float32Array(frame.numberOfFrames);
|
||||
for (let i = 0; i < frame.numberOfFrames; i++) {
|
||||
chData[i] = buf[i * frame.numberOfChannels + ch];
|
||||
}
|
||||
frame.copyTo(chData, { planeIndex: ch });
|
||||
ab.copyToChannel(chData, ch);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user