Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
448425f
Add emscripten_sleep implementation of Pa_Sleep
fwcd Mar 10, 2024
01f8f8d
Stub out Web Audio hostapi based on skeleton
fwcd Mar 10, 2024
a8aee40
Create an audio context and enable audio worklets
fwcd Mar 10, 2024
ac876e6
Make sure that PortAudio and consumers are compiled with -pthread
fwcd Mar 10, 2024
0770e36
Add a README.md for building/testing the Web Audio hostapi
fwcd Mar 10, 2024
8da3745
Include pa_debugprint
fwcd Mar 10, 2024
848fbdd
Set up a basic 2-channel default output device
fwcd Mar 10, 2024
da02a65
Add experimental Emscripten/Wasm CI
fwcd Mar 10, 2024
c1a6865
Disable jack feature when targeting emscripten
fwcd Mar 10, 2024
1706a88
Set VCPKG_CHAINLOAD_TOOLCHAIN_FILE in CI
fwcd Mar 10, 2024
be3f169
Disable unavailable -Wno-error=stringop-overflow
fwcd Mar 10, 2024
692d915
Rename to WebAudioHostProcessingLoop
fwcd Mar 10, 2024
83eb5a5
Experimentally set up Wasm Audio Worklet
fwcd Mar 10, 2024
1431da1
Create a separate audio context per stream
fwcd Mar 10, 2024
a02a4e2
Implement IsStreamStopped and IsStreamActive
fwcd Mar 10, 2024
42bdb7c
Update comments
fwcd Mar 10, 2024
d27dbc5
Add missing newlines to debug statements
fwcd Mar 10, 2024
d2639d6
Add some tips
fwcd Mar 10, 2024
33c679a
Await context suspend/resume
fwcd Mar 10, 2024
6e356cd
Wait for user interaction when attempting to resume context
fwcd Mar 10, 2024
c8d1abf
Add note on user interaction
fwcd Mar 10, 2024
edffb12
Handle the buffers in host loop properly
fwcd Mar 10, 2024
deb8a57
Use C wrapper for querying audio context state
fwcd Mar 10, 2024
4057598
Connect node to context destination
fwcd Mar 10, 2024
6d2ff90
Set sample rate during audio context creation
fwcd Mar 10, 2024
a2102c5
Use proper host format (non-interleaved 32-bit float)
fwcd Mar 10, 2024
47d4202
Format code towards the prevalent style
fwcd Mar 10, 2024
6375db9
Add utility function for suspension and suspend initially
fwcd Mar 10, 2024
831c636
Add PA_WEBAUDIO_ASYNCIFY and experimental synchronous mode
fwcd Mar 11, 2024
c80758a
Experimentally build examples with PROXY_TO_PTHREAD
fwcd Mar 11, 2024
3a6a5b7
Add checks to not block the main thread
fwcd Mar 11, 2024
c9015e5
Add missing <emscripten/threading.h> import
fwcd Mar 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ jobs:
-DPA_USE_ASIO=ON
-DPA_USE_JACK=OFF
-DASIO_SDK_ZIP_PATH=asiosdk.zip
- name: Ubuntu Emscripten
os: ubuntu-latest
vcpkg_triplet: wasm32-emscripten
cmake_generator: "Unix Makefiles"
cmake_options:
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE="$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
- name: Windows MSVC
os: windows-latest
vcpkg_triplet: x64-windows
Expand Down Expand Up @@ -72,6 +78,11 @@ jobs:
sudo apt-get update
sudo apt-get install libasound2-dev libpulse-dev libsndio-dev ${{ matrix.dependencies_extras }}
if: matrix.os == 'ubuntu-latest'
- name: Set up Emscripten SDK
uses: mymindstorm/setup-emsdk@v14
with:
version: '3.1.55'
if: startsWith(matrix.vcpkg_triplet, 'wasm32-')
- name: "Set up ASIO SDK cache [Windows/MinGW]"
uses: actions/cache@v2
if: matrix.asio_sdk_cache_path != null
Expand Down
36 changes: 35 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ if(PA_WARNINGS_ARE_ERRORS)
# Do *NOT* add warnings to this list. Instead, fix your code so that it doesn't produce the warning.
# TODO: fix the offending code so that we don't have to exclude specific warnings anymore.
-Wno-error=deprecated-declarations # https://github.com/PortAudio/portaudio/issues/213 https://github.com/PortAudio/portaudio/issues/641
-Wno-error=stringop-overflow
)
if (NOT EMSCRIPTEN)
add_compile_options(-Wno-error=stringop-overflow)
endif()
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
# Don't fail on older clang versions that don't recognize the latest warnings in the list above.
# Note that unrecognized warning options are not a fatal error on GCC, and in fact, GCC will choke on this option. Hence the conditional.
Expand Down Expand Up @@ -399,6 +401,38 @@ elseif(UNIX)
set(PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS} -DPA_USE_SNDIO=1")
set(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE} sndio")
endif()

if(EMSCRIPTEN)
option(PA_USE_WEBAUDIO "Enable support for Web Audio" ON)

if(PA_USE_WEBAUDIO)
target_sources(PortAudio PRIVATE
src/hostapi/webaudio/pa_webaudio.c
)
target_compile_definitions(PortAudio PUBLIC PA_USE_WEBAUDIO=1)
target_compile_options(PortAudio PUBLIC -pthread)
target_link_options(PortAudio PUBLIC
-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()

# This makes it easy to run the examples. The default output format is .js
# and is set in the Emscripten toolchain file, so it cannot be overriden
# from the command-line with -DCMAKE_EXECUTABLE_SUFFIX.
set(CMAKE_EXECUTABLE_SUFFIX ".html")
endif()
endif()
endif()

Expand Down
8 changes: 8 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ macro(add_example appl_name)
if(WIN32)
set_property(TARGET ${appl_name} APPEND_STRING PROPERTY COMPILE_DEFINITIONS _CRT_SECURE_NO_WARNINGS)
endif()
if(EMSCRIPTEN AND NOT PA_WEBAUDIO_ASYNCIFY)
target_compile_options(${appl_name} PUBLIC -pthread)
target_link_options(${appl_name} PUBLIC -pthread -sPROXY_TO_PTHREAD)
endif()
endmacro()

macro(add_example_cpp appl_name)
Expand All @@ -19,6 +23,10 @@ macro(add_example_cpp appl_name)
if(WIN32)
set_property(TARGET ${appl_name} APPEND_STRING PROPERTY COMPILE_DEFINITIONS _CRT_SECURE_NO_WARNINGS)
endif()
if(EMSCRIPTEN AND NOT PA_WEBAUDIO_ASYNCIFY)
target_compile_options(${appl_name} PUBLIC -pthread)
target_link_options(${appl_name} PUBLIC -pthread -sPROXY_TO_PTHREAD)
endif()
endmacro()

add_example(pa_devs)
Expand Down
33 changes: 33 additions & 0 deletions src/hostapi/webaudio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Web Audio API

To build PortAudio for the web, make sure to have [the Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) installed and on your `PATH`, then run from the repository's top-level directory:

```sh
emcmake cmake -B build
cmake --build build
```

To build the examples, use

```sh
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`

You can now run the examples in a local browser using `emrun`, for example

```sh
emrun build/examples/paex_sine.html
```

> [!IMPORTANT]
> Due to browser policies you have to interact with the site at least once (e.g. by clicking anywhere) before audio contexts can be started. `Pa_StartStream` will (from the C/C++ perspective) block until this happens. Under the hood this is handled asynchronously using Asyncify.

> [!TIP]
> You can customize the browser e.g. by setting `--browser=firefox` and also pass arguments to the browser with `--browser-args`
Loading