5

I have a strange Problem. I'm using Web Audio to play a stream from the server. I do that the following way:

 var d2 = new DataView(evt.data);

var data = new Float32Array(d2.byteLength / Float32Array.BYTES_PER_ELEMENT);
for (var jj = 0; jj < data.length; ++jj) {
    data[jj] = d2.getFloat32(jj * Float32Array.BYTES_PER_ELEMENT, true);
}

var buffer = context.createBuffer(1, data.length, 44100);
buffer.getChannelData(0).set(data);

source = context.createBufferSource();
source.buffer = buffer;
source.start(startTime);
source.connect(context.destination);

startTime += buffer.duration;

This works fine. If i play the stream on my Computer i don't have any problems.

If i play the same stream on my Windows 8 tablet (same Chrome version) i have a lot of clicking sounds in the audio. There are multiple of them within one second. It kinda seams that on the end of each buffer i hear a click.

I don't understand the difference... The only difference i could find was that the samplingrate of the soundcard on my computer is 44100 and on the tablet it's 48000.

The transmitted stream is in 44100 and i don't have any samplerate problems. just the clicking sounds.

Does anybody have an idea why this is happening?

Thank you, metabolic

metabolic
  • 669
  • 1
  • 7
  • 24

2 Answers2

2

AudioBufferSourceNode resample their buffers to the AudioContext samplerate. As you can imagine, the API does not allow you to keep the resampler state between one AudioBufferSourceNode and the other, so there is a discontinuity between the two buffers.

I think the easiest way is to provide a stream at the sample-rate of the device, by resampling server-side. When the AudioWorkerNode will be ready and implemented, you'll be able to fix this yourself as well client side, but it's not.

Alternatively also you can just stream using an element, and pipe that to Web Audio API using AudioContext.createMediaElementSource().

padenot
  • 1,515
  • 8
  • 11
  • Thank you! I'm resampling the stream now on the serverside and wait until the AudioWorkerNode is implemented :). I did not find any information when this might be. Do you have any insight on this? – metabolic Feb 28 '15 at 23:30
  • We'll implement it when the spec is finished, and we don't really know when that will be the case, sorry. – padenot Mar 01 '15 at 15:10
1

I had the same issue, thanks to Padenot's answer I checked the sample rates. AudioContext.sampleRate defaulted to 44100, but the PCM data and AudioBuffer was 48000. Initialising the AudioContext with a matching sampleRate solved the problem:

var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext({
  latencyHint: 'interactive',
  sampleRate: 48000,
});

With this, I can schedule the playback of 20ms 48khz PCM16 AudioBuffers back-to-back without any clicks or distortion.

bain
  • 1,710
  • 14
  • 15
  • I am really very curious how you can schedule 20ms AudioBuffers back-to-back, given that AudioBufferSourceNode.start(when) takes a when parameter with units in _seconds_. – Todd Freed Feb 12 '22 at 04:21
  • @Todd the seconds value is interpreted as a real number (not integer), so it can be current time plus 0.02, then 0.04, etc. – bain Feb 13 '22 at 08:02
  • Thank you! One other question - don't you have to convert to Float32 PCM buffers? AudioBuffer.copyToChannel() seems to require Float32, how are you playing PCM16 buffers? – Todd Freed Feb 14 '22 at 20:59
  • 1
    @ToddFreed to convert pcm16 -> float32 for AudioContext, divide each value by the maximum value 32768, e.g. something like `audiobuf = Audio.context.createBuffer(1, pcm.length, 48000); chanpcm = audiobuf.getChannelData(0); for (i = 0; i < chanpcm.length; i += 1) { chanpcm[i] = pcm[i] / 32768.0; }` For other examples see `getFormatedValue` and `flush` in [pcm-player.js](https://github.com/samirkumardas/pcm-player/blob/master/pcm-player.js), and the example at [AudioContext.getChannelData](https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer/getChannelData). – bain Feb 22 '22 at 11:12