Skip to content

Commit

Permalink
Add PA_WEBAUDIO_ASYNCIFY and experimental synchronous mode
Browse files Browse the repository at this point in the history
This synchronous mode uses `emscripten_thread_sleep` instead of
`emscripten_sleep` and is intended to run from a separate thread where
blocking is allowed.

In its current state, this seems to depend on

    WebAudio/web-audio-api#2423

The alternative would be to proxy audio context-related calls to the
main thread.
  • Loading branch information
fwcd committed Mar 11, 2024
1 parent 6375db9 commit 831c636
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 3 deletions.
11 changes: 9 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,12 +412,19 @@ elseif(UNIX)
target_compile_definitions(PortAudio PUBLIC PA_USE_WEBAUDIO=1)
target_compile_options(PortAudio PUBLIC -pthread)
target_link_options(PortAudio PUBLIC
-sASYNCIFY
-sASYNCIFY_IMPORTS=emscripten_asm_const_int
-sAUDIO_WORKLET
-sWASM_WORKERS
-sEXPORTED_RUNTIME_METHODS=emscriptenGetAudioObject
)

option(PA_WEBAUDIO_ASYNCIFY "Build with -sASYNCIFY and use async operations. Recommended when intending to call PortAudio from the main thread to avoid blocking." ON)
if(PA_WEBAUDIO_ASYNCIFY)
target_compile_definitions(PortAudio PUBLIC PA_WEBAUDIO_ASYNCIFY=1)
target_link_options(PortAudio PUBLIC
-sASYNCIFY
-sASYNCIFY_IMPORTS=emscripten_asm_const_int
)
endif()
set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_WEBAUDIO=1")
endif()

Expand Down
3 changes: 3 additions & 0 deletions src/hostapi/webaudio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ emcmake cmake -B build -DPA_BUILD_EXAMPLES=ON
cmake --build build
```

> [!TIP]
> By default PortAudio will be built with `-sASYNCIFY`. This makes it safe to be called from the main thread, but also introduces overhead and potentially other issues when the application already uses other asynchronous operations. PortAudio can also be built without Asyncify (i.e. using synchronous/blocking operations) by setting `-DPA_WEBAUDIO_ASYNCIFY=OFF`, in that case it is recommended to call PortAudio from a worker thread to avoid blocking the main thread (which may result in locking up the UI or in worse cases deadlock the application, e.g. if PortAudio waits for user interaction, as it does during `Pa_StartStream`). This can be done automatically using `-sPROXY_TO_PTHREAD`.
> [!TIP]
> For debug logging, set `-DPA_ENABLE_DEBUG_OUTPUT=ON`
Expand Down
14 changes: 13 additions & 1 deletion src/hostapi/webaudio/pa_webaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -718,12 +718,24 @@ static EM_BOOL WebAudioHostProcessingLoop( int numInputs, const AudioSampleFrame

void WebAudioSuspendContextSync( EMSCRIPTEN_WEBAUDIO_T context )
{
#ifdef PA_WEBAUDIO_ASYNCIFY
EM_ASM({
const context = emscriptenGetAudioObject($0);
Asyncify.handleAsync(async () => {
await context.suspend();
});
}, context);
#else
EM_ASM({
const context = emscriptenGetAudioObject($0);
context.suspend();
}, context);

while ( emscripten_audio_context_state( context ) != AUDIO_CONTEXT_STATE_SUSPENDED )
{
Pa_Sleep ( 100 );
}
#endif
}


Expand Down Expand Up @@ -772,7 +784,7 @@ static PaError StartStream( PaStream *s )
while ( !IsStreamActive(stream) )
{
PA_DEBUG(("Audio context is not running yet, waiting for user interaction...\n"));
emscripten_sleep( 500 );
Pa_Sleep( 500 );
emscripten_resume_audio_context_sync( stream->context );
}

Expand Down
5 changes: 5 additions & 0 deletions src/os/unix/pa_unix_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/threading.h>
#endif

#if defined(__APPLE__) && !defined(HAVE_MACH_ABSOLUTE_TIME)
Expand Down Expand Up @@ -116,7 +117,11 @@ int PaUtil_CountCurrentlyAllocatedBlocks( void )
void Pa_Sleep( long msec )
{
#ifdef __EMSCRIPTEN__
#ifdef PA_WEBAUDIO_ASYNCIFY
emscripten_sleep(msec);
#else
emscripten_thread_sleep(msec);
#endif
#elif defined(HAVE_NANOSLEEP)
struct timespec req = {0}, rem = {0};
PaTime time = msec / 1.e3;
Expand Down

0 comments on commit 831c636

Please sign in to comment.