3

I have a app where I play different code-generated sounds. I place these sounds in a AudioBufferSourceNode. I allow the the user to choose what output device to play the sound through, so I use a MediaStreamAudioDestinationNode with its stream used as the source for an Audio Element. This way when the user chooses an audio output to play the sound to, I set the Sink Id of the Audio element to the requested audio output.

So I have AudioBufferSourceNode -> some Audio Graph (gain nodes, etc) -> MediaStreamAudioDestinationNode -> Audio element.

When I Play the first sound, it sound fine. But when I create a new source and connect it to the same MediaStreamAudioDestinationNode, the sound is played with the wrong pitch.

I created a Fiddle that shows the problem.

Is this a bug, or am I doing something wrong?

Asher
  • 1,267
  • 1
  • 12
  • 24

2 Answers2

0

The problem was identified based on the OP Chrome Ticket.

It seems to come from the lack of sync between AudioElement and its source AudioNode (AudioBufferSourceNode, OscillatorNode, etc.) when you pause the source and play it back again.

The solution is to always call AudioElement.pause() and AudioElement.start() alongside your source stop and start.

https://jsfiddle.net/k1r7o0xj/3/

agfline
  • 1
  • 1
-1

It's possible to dynamically change your graph layout by using .connect() and .disconnect(), even when audio is playing or sent through a stream (which could even be streamed over WebRTC). I couldn't find a reference in the spec, so I'm pretty sure this is taken for granted.

For example, if you have two AudioBufferSourceNodes bufferSource1 and bufferSource2, and a MediaStreamAudioDestinationNode streamDestination:

bufferSource1.connect(streamDestination);

//do some other things here, and after some time, switch to bufferSource2:

//(streamDestination doesn't need to be explicitly specified here)
bufferSource1.disconnect(streamDestination); 
bufferSource2.connect(streamDestination);

Example in action.


Edit 1:

Proper implementation:

According to the Editors Draft on the Audio Output API, it is planned/will be possible to choose a custom audio output device for the AudioContext as well (by means of new AudioContext({ sinkId: requestedSinkId });). I couldn't find any info on the progress, and even found a related discussion which the asker apparently read already. According to this and (many) other references, it doesn't seem te be an easy task, but it's planned for WA V1.

Edit: That section has been removed from the API Draft, but you can still find it in an older version.

Current workaround:

I played around with your workaround (using a MediaStreamAudioDestinationNode and Audio object), and it seems to be related to nothing being connected. I modified my example to toggle a single buffer (similar to your example but with an AudioBufferSourceNode), and observed a similar frequency drop. However, when using a GainNode inbetween and setting it's gain.value to either 0 or 1, the frequency drops disappeared (this isn't gonna be the solution if you want to create and connect new AudioBuffers dynamically).

MarijnS95
  • 4,703
  • 3
  • 23
  • 47
  • I created a similar fiddle, but for some reason it doesn't work well in Chrome. See the fiddle at https://jsfiddle.net/ashercoren/98LoLtep/, and a bug I opened for Chrome at https://bugs.chromium.org/p/chromium/issues/detail?id=638823 – Asher Aug 18 '16 at 13:59
  • @Asher that indeed doesn't sound right. I tried all kinds of things, and the only thing that "fixed" it was not using an `Audio` element. Seems related to the `OscillatorNode` though, because this issue doesn't exist in my demo - I'll investigate further. Also, according to [the Audio Output API spec](https://www.w3.org/TR/audio-output/#audiocontext-constructor-argument), you should be able to specify a sink ID to the AudioContext constructor - I couldn't get it to work though on Chrome 51 for Linux. I'll test on 52 when I get home. – MarijnS95 Aug 18 '16 at 15:45
  • There is another way around the bug, when connecting an empty AudioBufferSourceNode to the Gain Node along side the OscillatorNode. I don't know why, nut when doing so the sound is correct in most cases. Regrading the sink ID in the Audio Context, it isn't implemented yet in Chrome, or any other Browser. – Asher Aug 21 '16 at 05:29
  • Also, I edited the question to reflect the issue I'm having with Chrome – Asher Aug 21 '16 at 05:44
  • @Asher: I assume it has to do with saving stream bandwidth (or some other stream-signalling mechanism) when nothing is connected or has it's gain set to 0 somewhere on te line, and doesn't come back up properly when connecting something or setting the gain > 0 (even when the stream isn't actually being sent over the net). But that's pure speculation - I don't know any of the internals. Downvoter: care to explain why? The initial question was answered. The problem as described in a comment seems to be a bug, which doesn't make the answer incorrect. At least explain what should be improved. – MarijnS95 Aug 30 '16 at 21:21