Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 23 additions & 0 deletions src/ossia/audio/audio_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <ossia/audio/audio_engine.hpp>
#include <ossia/audio/dummy_protocol.hpp>
#include <ossia/audio/jack_protocol.hpp>
//#include <ossia/audio/miniaudio_protocol.hpp>
#include <ossia/audio/pipewire_protocol.hpp>
#include <ossia/audio/portaudio_protocol.hpp>
#include <ossia/audio/pulseaudio_protocol.hpp>
Expand Down Expand Up @@ -56,6 +57,13 @@ void audio_engine::sync()
req++;
request.store(req);

#if defined(__EMSCRIPTEN__)
// On WASM, the audio runs in a worklet thread. We cannot spin-wait
// on the main thread as it blocks the browser event loop entirely.
// The tick will pick up the new request on its next callback.
return;
#endif

// The engine has started running, we wait for a couple iterations
// to leave some time for the ticks to be updated
int k = 0;
Expand Down Expand Up @@ -120,7 +128,22 @@ ossia::audio_engine* make_audio_engine(
bs = 1024;
inputs = 0;
outputs = 2;
#if OSSIA_ENABLE_MINIAUDIO
{
static auto ctx = std::make_shared<ossia::miniaudio_context>();
static bool ctx_init = false;
if(!ctx_init)
{
auto cfg = ma_context_config_init();
ma_context_init(nullptr, 0, &cfg, &ctx->context);
ctx_init = true;
}
ma_device_id in_id{}, out_id{};
return new ossia::miniaudio_engine{ctx, name, in_id, out_id, inputs, outputs, rate, bs};
}
#else
return new ossia::sdl_protocol{rate, bs};
#endif
#endif

if(0)
Expand Down
28 changes: 25 additions & 3 deletions src/ossia/audio/miniaudio_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
#define MA_NO_RUNTIME_LINKING 1
#endif
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS 1
#if defined(__EMSCRIPTEN__)
#define MA_ENABLE_WEBAUDIO 1
#define MA_ENABLE_AUDIO_WORKLETS 1
#else
#define MA_ENABLE_COREAUDIO 1
#define MA_ENABLE_ALSA 1
#endif
#define MA_NO_WAV 1
#define MA_NO_FLAC 1
#define MA_NO_MP3 1
Expand All @@ -22,7 +27,7 @@
#include <ossia/audio/audio_engine.hpp>
#include <ossia/detail/thread.hpp>

#include <kfr/base/conversion.hpp>
//#include <kfr/base/conversion.hpp>

#include <miniaudio.h>

Expand Down Expand Up @@ -144,18 +149,18 @@ class miniaudio_engine final : public audio_engine
static void
callback(ma_device* pDevice, void* output, const void* input, ma_uint32 nframes)
{
#if !defined(__EMSCRIPTEN__)
[[maybe_unused]]
static const thread_local auto _
= [] {
ossia::set_thread_name("ossia audio 0");
ossia::set_thread_pinned(thread_type::Audio, 0);
return 0;
}();
#endif

auto& self = *static_cast<miniaudio_engine*>(pDevice->pUserData);
self.tick_start();
if(!self.m_start)
self.m_start = std::chrono::steady_clock::now();

if(self.stop_processing)
{
Expand All @@ -167,33 +172,50 @@ class miniaudio_engine final : public audio_engine
auto ins_data = self.ins_data.data();
for(int i = 0; i < self.effective_inputs; i++)
ins[i] = ins_data + i * nframes;
#if !defined(__EMSCRIPTEN__)
kfr::deinterleave(ins, (float*)input, self.effective_inputs, nframes);
#endif

auto outs = self.outs.data();
auto outs_data = self.outs_data.data();
std::memset(outs_data, 0, sizeof(float) * self.effective_outputs * nframes);
for(int i = 0; i < self.effective_outputs; i++)
outs[i] = outs_data + i * nframes;

#if defined(__EMSCRIPTEN__)
// On WASM audio worklets, std::chrono::steady_clock is unavailable.
// Use the sample count to derive time instead.
self.m_frames_elapsed += nframes;
double nsecs = (double)self.m_frames_elapsed / self.effective_sample_rate;
#else
if(!self.m_start)
self.m_start = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now();
auto nsecs
= std::chrono::duration_cast<std::chrono::nanoseconds>(now - *self.m_start)
.count()
/ 1e9;
#endif

ossia::audio_tick_state ts{(float* const*)ins, outs, self.effective_inputs,
self.effective_outputs, nframes, nsecs};
self.audio_tick(ts);

self.tick_end();

#if !defined(__EMSCRIPTEN__)
kfr::interleave(
(float*)output, (const float**)outs, self.effective_outputs, nframes);
#endif
}

std::shared_ptr<miniaudio_context> m_ctx;
ma_device m_stream;
#if defined(__EMSCRIPTEN__)
uint64_t m_frames_elapsed{};
#else
std::optional<std::chrono::steady_clock::time_point> m_start;
#endif

boost::container::vector<float> ins_data;
boost::container::vector<float*> ins;
Expand Down
7 changes: 7 additions & 0 deletions src/ossia_features.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ if(OSSIA_DATAFLOW)
target_link_libraries(ossia PRIVATE $<BUILD_INTERFACE:ossia::sdl2>)
endif()

# MiniAudio / Web Audio support
if(CMAKE_SYSTEM_NAME MATCHES Emscripten)
target_include_directories(ossia PRIVATE $<BUILD_INTERFACE:${OSSIA_3RDPARTY_FOLDER}/miniaudio>)
target_compile_definitions(ossia PRIVATE MA_ENABLE_AUDIO_WORKLETS MA_ENABLE_WEBAUDIO)
target_link_options(ossia PUBLIC -sAUDIO_WORKLET=1 -sWASM_WORKERS=1)
endif()

if(OSSIA_ENABLE_LIBSAMPLERATE)
target_link_libraries(ossia PRIVATE $<BUILD_INTERFACE:SampleRate::samplerate>)
endif()
Expand Down
Loading