Open yishengjiang99 opened 3 years ago
I am not sure what you mean by "race condition"?
Transferable Streams provides a means to transfer ReadableStream
or WritableStream
using postMessage(stream, [stream])
.
The code fetches multiple URL's and pipes each body
, a ReadableStream
, to a single WritableStream
, where the data is read in a user-defined method of AudioWorkletProcessor
.
The purpose of the design is to implement an "infinite" (provided an idealized computer; or "circular buffer", e.g., re-writing indexes of a fixed-length SharedArrayBuffer
instead of growing the buffer) input stream, using URL's or other audio input in form of a ReadableStream
or WritableStream
as a single input stream to AudioWorklet
.
@yishengjiang99
does this rely on some sort of race condition
The anonymous async function is used for the putpose of starting the async generator while not await
ing the result so that readable
is transfered to AudioWorkletProcessor
on next line and data within the stream is available; the stream is intended to be processing ahead of AudioWorkletProcessor.process()
.
const { readable, writable } = new TransformStream(); // create TransformStream
(async _ => { // do not await result of immediately invoked anonymous async function
for await (const _ of (async function* stream() { use for...await and immediately invoked anonymous async generator function
while (urls.length) { // given a list of URL's or other resources, e.g., while(true) {// stream audio}
yield (await fetch(urls.shift(), {cache: 'no-store'})).body.pipeTo(writable, { // fetch URL, do not cache, pipe body to writable
preventClose: !!urls.length, // prevent closing writable
});
}
})());
})();
port.postMessage(
{
readable, // post readable to Worker, Worklet, Window, etc.
},
[readable] // transfer readable to Worker, Worklet, Window, etc.
);
When I initially tried to upload the single WAV file GitHub did not allow. I created several files from the single file (WAV header can be included or excluded), thus the multiple fetch()
requests at https://guest271314.github.io/AudioWorkletStream/ for a single track. With PCM input the same function is used to create Float32Array
whether one file or N files.
Re
race condition
There are cases where process()
can outpace input, or TypedArray
processing throws errors https://bugs.chromium.org/p/chromium/issues/detail?id=1055728#c41 or when cache is enabled or disbled a different result can be output.
process()
can be called between 344 and 384 times per second. We want audio data ready to be read from the stream before porcess()
runs for the first second. An async generator and iterator that yields a ReadableStream
piped to a single WritableStream
that audio data is read from outpaces process()
when testing here. Have you encountered cases where the network requests to readable stream do not outpace process()
calls?
I used this code in a couple projects for chaining together fetch requests with some range headers. Works really well.. in one case I saw the writable being locked.. took me a while to get that the (async ()=>{ (await fetch()).body.pipeTo(writable) })() part..
This is one script that audioworklet reads a sequential queue of readablstreams generated by this method of shared transferable stream: https://github.com/yishengjiang99/ssr-bach/blob/master/js/src/proc2.ts server generally out paces audioworklet processing.
let me be your apprentice.
does this rely on some sort of race condition where the readable stream is neither locked or unlocked for the worker to transfer it to the worklet node?