Skip to content
Draft
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
2 changes: 1 addition & 1 deletion cmake/OssiaDeps.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ endif()
if(OSSIA_ENABLE_SDL)
include(deps/sdl)

if(NOT TARGET ossia::sdl2)
if(NOT TARGET ossia::sdl3)
set(OSSIA_ENABLE_SDL FALSE CACHE "" INTERNAL FORCE)
set(OSSIA_PROTOCOL_JOYSTICK FALSE CACHE "" INTERNAL FORCE)
endif()
Expand Down
5 changes: 2 additions & 3 deletions cmake/OssiaOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ option(OSSIA_PROTOCOL_HTTP "Enable HTTP protocol" ON) # Requires Qt
option(OSSIA_PROTOCOL_WEBSOCKETS "Enable WebSockets protocol" OFF) # Requires Qt
option(OSSIA_PROTOCOL_SERIAL "Enable Serial port protocol" OFF) # Requires Qt
option(OSSIA_PROTOCOL_PHIDGETS "Enable Phidgets protocol" OFF) # Requires Phidgets library
option(OSSIA_PROTOCOL_JOYSTICK "Enable Joystick protocol" ON) # Requires SDL2 library
option(OSSIA_PROTOCOL_JOYSTICK "Enable Joystick protocol" ON) # Requires SDL3 library
option(OSSIA_PROTOCOL_WIIMOTE "Enable Wiimote Protocol" ON) #use wiiuse
option(OSSIA_PROTOCOL_ARTNET "Enable artnet protocol" ON)
option(OSSIA_PROTOCOL_LIBMAPPER "Enable libmapper protocol" OFF) #use external libmapper
Expand Down Expand Up @@ -111,8 +111,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${PROJECT_SOURCE_DIR}/CMake;${PROJEC
set(OSSIA_SUBMODULE_AUTOUPDATE ON CACHE BOOL "Auto update submodule")

set(CMAKE_PREFIX_PATH
"${OSSIA_SDK}/SDL2"
"${OSSIA_SDK}/SDL2/cmake"
"${OSSIA_SDK}/SDL3"
"${OSSIA_SDK}/portaudio/lib/cmake"
"${CMAKE_PREFIX_PATH}"
)
Expand Down
36 changes: 18 additions & 18 deletions cmake/deps/sdl.cmake
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
set(SDL_BUILDING_LIBRARY TRUE)
if(CMAKE_SYSTEM_NAME MATCHES Emscripten)
add_library(ossia_sdl2 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl2 ALIAS ossia_sdl2)
add_library(ossia_sdl3 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl3 ALIAS ossia_sdl3)

target_compile_options(ossia_sdl2 INTERFACE "SHELL:-s USE_SDL=2")
target_link_options(ossia_sdl2 INTERFACE "SHELL:-s USE_SDL=2")
target_compile_options(ossia_sdl3 INTERFACE "SHELL:-s USE_SDL=3")
target_link_options(ossia_sdl3 INTERFACE "SHELL:-s USE_SDL=3")
return()
else()
find_package(SDL2 CONFIG GLOBAL)
find_package(SDL3 CONFIG GLOBAL)
endif()

if(TARGET SDL2::SDL2)
add_library(ossia_sdl2 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl2 ALIAS ossia_sdl2)
if(TARGET SDL3::SDL3)
add_library(ossia_sdl3 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl3 ALIAS ossia_sdl3)

target_link_libraries(ossia_sdl2 INTERFACE SDL2::SDL2)
elseif(TARGET SDL2::SDL2-static)
add_library(ossia_sdl2 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl2 ALIAS ossia_sdl2)
target_link_libraries(ossia_sdl3 INTERFACE SDL3::SDL3)
elseif(TARGET SDL3::SDL3-static)
add_library(ossia_sdl3 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl3 ALIAS ossia_sdl3)

target_link_libraries(ossia_sdl2 INTERFACE SDL2::SDL2-static)
elseif(SDL2_LIBRARIES AND SDL2_INCLUDE_DIRS)
add_library(ossia_sdl2 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl2 ALIAS ossia_sdl2)
target_link_libraries(ossia_sdl3 INTERFACE SDL3::SDL3-static)
elseif(SDL3_LIBRARIES AND SDL3_INCLUDE_DIRS)
add_library(ossia_sdl3 INTERFACE IMPORTED GLOBAL)
add_library(ossia::sdl3 ALIAS ossia_sdl3)

target_include_directories(ossia_sdl2 PUBLIC "${SDL2_INCLUDE_DIRS}")
target_link_libraries(ossia_sdl2 INTERFACE "${SDL2_LIBRARIES}")
target_include_directories(ossia_sdl3 PUBLIC "${SDL3_INCLUDE_DIRS}")
target_link_libraries(ossia_sdl3 INTERFACE "${SDL3_LIBRARIES}")
endif()
102 changes: 60 additions & 42 deletions src/ossia/audio/sdl_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
#include <ossia/detail/config.hpp>

#if defined(OSSIA_ENABLE_SDL)
#if __has_include(<SDL2/SDL_audio.h>)
#include <SDL2/SDL_config.h>
#if !defined(SDL_AUDIO_DISABLED)
#if __has_include(<SDL3/SDL_audio.h>)
#include <ossia/audio/audio_engine.hpp>
#include <ossia/detail/thread.hpp>

#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <SDL3/SDL.h>
#include <SDL3/SDL_audio.h>

#include <algorithm>
#include <string>
#include <vector>

#define OSSIA_AUDIO_SDL 1

Expand All @@ -24,50 +26,57 @@ class sdl_protocol final : public audio_engine
sdl_protocol(int rate, int bs)
{
SDL_Init(SDL_INIT_AUDIO);
m_desired.freq = rate;
m_desired.format = AUDIO_F32SYS;
m_desired.channels = outputs;
m_desired.samples = bs;
m_desired.callback = SDLCallback;
m_desired.userdata = this;

m_deviceId = SDL_OpenAudioDevice(nullptr, 0, &m_desired, &m_obtained, 0);
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, std::to_string(bs).c_str());

m_spec.freq = rate;
m_spec.format = SDL_AUDIO_F32;
m_spec.channels = outputs;

m_stream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &m_spec, SDLCallback, this);

if(m_deviceId < 2)
if(!m_stream)
{
using namespace std::literals;
throw std::runtime_error("SDL: Couldn't open audio: "s + SDL_GetError());
}

this->effective_sample_rate = m_obtained.freq;
this->effective_buffer_size = m_obtained.samples;
this->effective_sample_rate = m_spec.freq;
this->effective_buffer_size = bs;
this->effective_inputs = 0;
this->effective_outputs = m_obtained.channels;
this->effective_outputs = m_spec.channels;

SDL_PauseAudioDevice(m_deviceId, 0);
m_interleaved.resize(std::size_t(bs) * m_spec.channels);
m_planar.resize(std::size_t(bs) * m_spec.channels);
m_channels.resize(m_spec.channels);

SDL_ResumeAudioStreamDevice(m_stream);
m_activated = true;
}

~sdl_protocol() override { stop(); }

bool running() const override
{
return m_activated && SDL_GetAudioDeviceStatus(m_deviceId) == SDL_AUDIO_PLAYING;
return m_activated && !SDL_AudioStreamDevicePaused(m_stream);
}

void stop() override
{
audio_engine::stop();
if(m_activated)
{
SDL_CloseAudioDevice(m_deviceId);
SDL_DestroyAudioStream(m_stream);
m_stream = nullptr;
m_activated = false;
}
SDL_Quit();
}

private:
static void SDLCallback(void* userData, Uint8* data, int bytes)
static void SDLCallback(
void* userData, SDL_AudioStream* stream, int additional_amount, int total_amount)
{
[[maybe_unused]]
static const thread_local auto _
Expand All @@ -77,35 +86,37 @@ class sdl_protocol final : public audio_engine
return 0;
}();

auto& self = *static_cast<sdl_protocol*>(userData);
self.tick_start();
if(!self.m_start)
self.m_start = std::chrono::steady_clock::now();
if(additional_amount <= 0)
return;

auto audio_out = reinterpret_cast<float*>(data);
const int out_chan = self.m_obtained.channels;
const int frames = self.m_obtained.samples;
auto& self = *static_cast<sdl_protocol*>(userData);
const int out_chan = self.m_spec.channels;
const int frames = self.effective_buffer_size;
assert(out_chan > 0);
assert(frames > 0);
assert(frames * out_chan * sizeof(float) == bytes);

if(self.stop_processing)
{
self.tick_clear();
memset(data, 0, bytes);
return;
}
const int block_bytes = int(frames * out_chan * sizeof(float));

while(additional_amount > 0)
{
auto float_data = (float*)alloca(sizeof(float) * frames * out_chan);
memset(float_data, 0, sizeof(sizeof(float) * frames * out_chan));
self.tick_start();
if(!self.m_start)
self.m_start = std::chrono::steady_clock::now();

auto float_output = (float**)alloca(sizeof(float*) * out_chan);
if(self.stop_processing)
{
self.tick_clear();
std::fill(self.m_interleaved.begin(), self.m_interleaved.end(), 0.f);
SDL_PutAudioStreamData(stream, self.m_interleaved.data(), block_bytes);
return;
}

float* const float_data = self.m_planar.data();
float** const float_output = self.m_channels.data();
std::fill_n(float_data, std::size_t(frames) * out_chan, 0.f);

for(int c = 0; c < out_chan; c++)
{
float_output[c] = float_data + c * frames;
}

// if one day there's input... samples[j++] / 32768.;

Expand All @@ -119,17 +130,25 @@ class sdl_protocol final : public audio_engine
out_chan, (uint64_t)frames, nsecs};
self.audio_tick(ts);

float* audio_out = self.m_interleaved.data();
for(int j = 0; j < frames; j++)
for(int c = 0; c < out_chan; c++)
*audio_out++ = float_output[c][j];

SDL_PutAudioStreamData(stream, self.m_interleaved.data(), block_bytes);

self.tick_end();
self.m_total_frames += frames;

additional_amount -= block_bytes;
}
}

SDL_AudioDeviceID m_deviceId{};
SDL_AudioSpec m_desired, m_obtained;
SDL_AudioStream* m_stream{};
SDL_AudioSpec m_spec{};
std::vector<float> m_interleaved;
std::vector<float> m_planar;
std::vector<float*> m_channels;
uint64_t m_total_frames{};
std::optional<std::chrono::steady_clock::time_point> m_start;
bool m_activated{};
Expand All @@ -138,4 +157,3 @@ class sdl_protocol final : public audio_engine

#endif
#endif
#endif
Loading
Loading