|
|
|
@@ -23,7 +23,12 @@ |
|
|
|
#include <SDL.h> |
|
|
|
|
|
|
|
struct SDLBridge { |
|
|
|
SDL_AudioDeviceID deviceId = 0; |
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
SDL_AudioDeviceID captureDeviceId = 0; |
|
|
|
#endif |
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
SDL_AudioDeviceID playbackDeviceId = 0; |
|
|
|
#endif |
|
|
|
|
|
|
|
// SDL information |
|
|
|
uint bufferSize = 0; |
|
|
|
@@ -61,37 +66,97 @@ struct SDLBridge { |
|
|
|
|
|
|
|
bool open(const char* const clientName) |
|
|
|
{ |
|
|
|
SDL_AudioSpec requested, received; |
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
SDL_InitSubSystem(SDL_INIT_AUDIO); |
|
|
|
|
|
|
|
SDL_AudioSpec requested; |
|
|
|
std::memset(&requested, 0, sizeof(requested)); |
|
|
|
requested.format = AUDIO_F32SYS; |
|
|
|
requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS; |
|
|
|
requested.freq = 48000; |
|
|
|
requested.samples = 512; |
|
|
|
requested.callback = SDLCallback; |
|
|
|
requested.userdata = this; |
|
|
|
|
|
|
|
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName); |
|
|
|
// SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, ); |
|
|
|
SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2"); |
|
|
|
#endif |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure"); |
|
|
|
requested.channels = DISTRHO_PLUGIN_NUM_INPUTS; |
|
|
|
requested.callback = AudioInputCallback; |
|
|
|
|
|
|
|
/* |
|
|
|
deviceId = SDL_OpenAudioDevice("PulseAudio", 0, |
|
|
|
&requested, &received, |
|
|
|
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); |
|
|
|
*/ |
|
|
|
deviceId = SDL_OpenAudio(&requested, &received) == 0 ? 1 : 0; |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); |
|
|
|
SDL_AudioSpec receivedCapture; |
|
|
|
captureDeviceId = SDL_OpenAudioDevice(nullptr, 1, &requested, &receivedCapture, |
|
|
|
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); |
|
|
|
if (captureDeviceId == 0) |
|
|
|
{ |
|
|
|
d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS) |
|
|
|
{ |
|
|
|
SDL_CloseAudioDevice(captureDeviceId); |
|
|
|
captureDeviceId = 0; |
|
|
|
d_stderr2("Invalid or missing audio input channels"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
SDL_AudioSpec receivedPlayback; |
|
|
|
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback"); |
|
|
|
requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS; |
|
|
|
requested.callback = AudioOutputCallback; |
|
|
|
|
|
|
|
if (received.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) |
|
|
|
playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback, |
|
|
|
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); |
|
|
|
if (playbackDeviceId == 0) |
|
|
|
{ |
|
|
|
SDL_CloseAudioDevice(deviceId); |
|
|
|
deviceId = 0; |
|
|
|
d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) |
|
|
|
{ |
|
|
|
SDL_CloseAudioDevice(playbackDeviceId); |
|
|
|
playbackDeviceId = 0; |
|
|
|
d_stderr2("Invalid or missing audio output channels"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
// if using both input and output, make sure they match |
|
|
|
if (receivedCapture.samples != receivedPlayback.samples) |
|
|
|
{ |
|
|
|
SDL_CloseAudioDevice(captureDeviceId); |
|
|
|
SDL_CloseAudioDevice(playbackDeviceId); |
|
|
|
captureDeviceId = playbackDeviceId = 0; |
|
|
|
d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedCapture.samples); |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (receivedCapture.freq != receivedPlayback.freq) |
|
|
|
{ |
|
|
|
SDL_CloseAudioDevice(captureDeviceId); |
|
|
|
SDL_CloseAudioDevice(playbackDeviceId); |
|
|
|
captureDeviceId = playbackDeviceId = 0; |
|
|
|
d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedCapture.freq); |
|
|
|
return false; |
|
|
|
} |
|
|
|
bufferSize = receivedCapture.samples; |
|
|
|
sampleRate = receivedCapture.freq; |
|
|
|
#elif DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
bufferSize = receivedCapture.samples; |
|
|
|
sampleRate = receivedCapture.freq; |
|
|
|
#elif DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
bufferSize = receivedPlayback.samples; |
|
|
|
sampleRate = receivedPlayback.freq; |
|
|
|
#else |
|
|
|
d_stderr2("SDL without audio, unsupported for now"); |
|
|
|
return false; |
|
|
|
#endif |
|
|
|
|
|
|
|
bufferSize = received.samples; |
|
|
|
sampleRate = received.freq; |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; |
|
|
|
@@ -108,11 +173,16 @@ struct SDLBridge { |
|
|
|
|
|
|
|
bool close() |
|
|
|
{ |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); |
|
|
|
|
|
|
|
// SDL_CloseAudioDevice(deviceId); |
|
|
|
SDL_CloseAudio(); |
|
|
|
deviceId = 0; |
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); |
|
|
|
SDL_CloseAudioDevice(captureDeviceId); |
|
|
|
captureDeviceId = 0; |
|
|
|
#endif |
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); |
|
|
|
SDL_CloseAudioDevice(playbackDeviceId); |
|
|
|
playbackDeviceId = 0; |
|
|
|
#endif |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
delete[] audioBufferStorage; |
|
|
|
@@ -124,17 +194,27 @@ struct SDLBridge { |
|
|
|
|
|
|
|
bool activate() |
|
|
|
{ |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); |
|
|
|
|
|
|
|
SDL_PauseAudioDevice(deviceId, 0); |
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); |
|
|
|
SDL_PauseAudioDevice(captureDeviceId, 0); |
|
|
|
#endif |
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); |
|
|
|
SDL_PauseAudioDevice(playbackDeviceId, 0); |
|
|
|
#endif |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool deactivate() |
|
|
|
{ |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); |
|
|
|
|
|
|
|
SDL_PauseAudioDevice(deviceId, 1); |
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); |
|
|
|
SDL_PauseAudioDevice(captureDeviceId, 1); |
|
|
|
#endif |
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); |
|
|
|
SDL_PauseAudioDevice(playbackDeviceId, 1); |
|
|
|
#endif |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
@@ -184,7 +264,8 @@ struct SDLBridge { |
|
|
|
return nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
static void SDLCallback(void* const userData, uchar* const stream, const int len) |
|
|
|
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 |
|
|
|
static void AudioInputCallback(void* const userData, uchar* const stream, const int len) |
|
|
|
{ |
|
|
|
SDLBridge* const self = (SDLBridge*)userData; |
|
|
|
|
|
|
|
@@ -192,29 +273,54 @@ struct SDLBridge { |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(len > 0,); |
|
|
|
|
|
|
|
float* const fstream = (float*)stream; |
|
|
|
const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS); |
|
|
|
DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); |
|
|
|
|
|
|
|
const float* const fstream = (const float*)stream; |
|
|
|
|
|
|
|
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) |
|
|
|
{ |
|
|
|
for (uint j=0; j<numFrames; ++j) |
|
|
|
self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i]; |
|
|
|
} |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 |
|
|
|
// if there are no outputs, run process callback now |
|
|
|
if (self->jackProcessCallback != nullptr) |
|
|
|
self->jackProcessCallback(numFrames, self->jackProcessArg); |
|
|
|
#endif |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
static void AudioOutputCallback(void* const userData, uchar* const stream, const int len) |
|
|
|
{ |
|
|
|
SDLBridge* const self = (SDLBridge*)userData; |
|
|
|
|
|
|
|
// safety checks |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(len > 0,); |
|
|
|
|
|
|
|
// #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 |
|
|
|
if (self->jackProcessCallback == nullptr) |
|
|
|
// #endif |
|
|
|
{ |
|
|
|
std::memset(fstream, 0, len); |
|
|
|
std::memset(stream, 0, len); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 |
|
|
|
const uint numFrames = static_cast<uint>(static_cast<uint>(len) / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); |
|
|
|
const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); |
|
|
|
DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); |
|
|
|
|
|
|
|
float* const fstream = (float*)stream; |
|
|
|
|
|
|
|
self->jackProcessCallback(numFrames, self->jackProcessArg); |
|
|
|
|
|
|
|
for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) |
|
|
|
{ |
|
|
|
for (uint j=0; j < numFrames; ++j) |
|
|
|
fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS+i][j]; |
|
|
|
fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j]; |
|
|
|
} |
|
|
|
// #endif |
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
}; |
|
|
|
|
|
|
|
#endif |