guest271314 / AudioWorkletStream

fetch() => ReadableStream => AudioWorklet
https://guest271314.github.io/AudioWorkletStream/
25 stars 5 forks source link

this code is voodoo magic (question, not bug report) #2

Open yishengjiang99 opened 3 years ago

yishengjiang99 commented 3 years ago

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?

` // https://github.com/whatwg/streams/blob/master/transferable-streams-explainer.md const { readable, writable } = new TransformStream(); (async => { for await (const of (async function* stream() { while (urls.length) { yield (await fetch(urls.shift(), {cache: 'no-store'})).body.pipeTo(writable, { preventClose: !!urls.length, }); } })()); })(); port.postMessage( { readable, }, [readable] );

guest271314 commented 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.

guest271314 commented 3 years ago

@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 awaiting 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().

guest271314 commented 3 years ago
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.
);
guest271314 commented 3 years ago

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?

yishengjiang99 commented 3 years ago

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..

yishengjiang99 commented 3 years ago

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.

yishengjiang99 commented 3 years ago

let me be your apprentice.

guest271314 commented 3 years ago

https://gist.githubusercontent.com/guest271314/1fcb5d90799c48fdf34c68dd7f74a304/raw/c06235ae8ebb1ae20f0edea5e8bdc119f0897d9a/UseItBreakItFileBugsRequestFeaturesTestTestTestTestToThePointItBreaks.txt