Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yishengjiang99 opened this issue Feb 11, 2021 · 8 comments
Open

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

yishengjiang99 opened this issue Feb 11, 2021 · 8 comments

Comments

@yishengjiang99
Copy link

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
Copy link
Owner

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
Copy link
Owner

@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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Author

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
Copy link
Author

let me be your apprentice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants