DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

SDL2Bridge.hpp (9112B)


      1 /*
      2  * SDL Bridge for DPF
      3  * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #ifndef SDL_BRIDGE_HPP_INCLUDED
     18 #define SDL_BRIDGE_HPP_INCLUDED
     19 
     20 #include "NativeBridge.hpp"
     21 #include "../../extra/ScopedDenormalDisable.hpp"
     22 
     23 #include <SDL.h>
     24 
     25 #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
     26 # define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
     27 #endif
     28 
     29 #ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME
     30 # define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME"
     31 #endif
     32 
     33 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0
     34 # error SDL without audio does not make sense
     35 #endif
     36 
     37 struct SDL2Bridge : NativeBridge {
     38    #if DISTRHO_PLUGIN_NUM_INPUTS > 0
     39     SDL_AudioDeviceID captureDeviceId;
     40    #endif
     41    #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
     42     SDL_AudioDeviceID playbackDeviceId;
     43    #endif
     44 
     45     SDL2Bridge()
     46         : NativeBridge()
     47        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
     48         , captureDeviceId(0)
     49        #endif
     50        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
     51         , playbackDeviceId(0)
     52        #endif
     53     {}
     54 
     55     bool open(const char* const clientName) override
     56     {
     57         SDL_InitSubSystem(SDL_INIT_AUDIO);
     58 
     59         SDL_AudioSpec requested;
     60         std::memset(&requested, 0, sizeof(requested));
     61         requested.format = AUDIO_F32SYS;
     62         requested.freq = 48000;
     63         requested.samples = 512;
     64         requested.userdata = this;
     65 
     66         SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName);
     67         // SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "1");
     68 
     69        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
     70         SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure");
     71         requested.channels = DISTRHO_PLUGIN_NUM_INPUTS_2;
     72         requested.callback = AudioInputCallback;
     73 
     74         SDL_AudioSpec receivedCapture;
     75         captureDeviceId = SDL_OpenAudioDevice(nullptr, 1, &requested, &receivedCapture,
     76                                               SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
     77         if (captureDeviceId == 0)
     78         {
     79             d_stderr2("Failed to open SDL capture device, error was: %s", SDL_GetError());
     80            #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
     81             return false;
     82            #endif
     83         }
     84         else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS_2)
     85         {
     86             SDL_CloseAudioDevice(captureDeviceId);
     87             captureDeviceId = 0;
     88             d_stderr2("Invalid or missing audio input channels");
     89             return false;
     90         }
     91        #endif
     92 
     93        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
     94         SDL_AudioSpec receivedPlayback;
     95         SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback");
     96         requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS_2;
     97         requested.callback = AudioOutputCallback;
     98 
     99         playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback,
    100                                                SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
    101         if (playbackDeviceId == 0)
    102         {
    103             d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError());
    104             return false;
    105         }
    106 
    107         if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS_2)
    108         {
    109             SDL_CloseAudioDevice(playbackDeviceId);
    110             playbackDeviceId = 0;
    111             d_stderr2("Invalid or missing audio output channels");
    112             return false;
    113         }
    114        #endif
    115 
    116        #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    117         // if using both input and output, make sure they match
    118         if (receivedCapture.samples != receivedPlayback.samples && captureDeviceId != 0)
    119         {
    120             SDL_CloseAudioDevice(captureDeviceId);
    121             SDL_CloseAudioDevice(playbackDeviceId);
    122             captureDeviceId = playbackDeviceId = 0;
    123             d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedPlayback.samples);
    124             return false;
    125         }
    126         if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0)
    127         {
    128             SDL_CloseAudioDevice(captureDeviceId);
    129             SDL_CloseAudioDevice(playbackDeviceId);
    130             captureDeviceId = playbackDeviceId = 0;
    131             d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedPlayback.freq);
    132             return false;
    133         }
    134        #endif
    135 
    136        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    137         if (captureDeviceId != 0)
    138         {
    139             bufferSize = receivedCapture.samples;
    140             sampleRate = receivedCapture.freq;
    141         }
    142        #endif
    143        #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    144         else
    145        #endif
    146        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    147         {
    148             bufferSize = receivedPlayback.samples;
    149             sampleRate = receivedPlayback.freq;
    150         }
    151        #endif
    152 
    153         allocBuffers(true, false);
    154         return true;
    155     }
    156 
    157     bool close() override
    158     {
    159        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    160         if (captureDeviceId != 0)
    161         {
    162             SDL_CloseAudioDevice(captureDeviceId);
    163             captureDeviceId = 0;
    164         }
    165        #endif
    166        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    167         DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
    168         SDL_CloseAudioDevice(playbackDeviceId);
    169         playbackDeviceId = 0;
    170        #endif
    171 
    172         freeBuffers();
    173         return true;
    174     }
    175 
    176     bool activate() override
    177     {
    178        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    179         if (captureDeviceId != 0)
    180             SDL_PauseAudioDevice(captureDeviceId, 0);
    181        #endif
    182        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    183         DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
    184         SDL_PauseAudioDevice(playbackDeviceId, 0);
    185        #endif
    186         return true;
    187     }
    188 
    189     bool deactivate() override
    190     {
    191        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    192         if (captureDeviceId != 0)
    193             SDL_PauseAudioDevice(captureDeviceId, 1);
    194        #endif
    195        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    196         DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
    197         SDL_PauseAudioDevice(playbackDeviceId, 1);
    198        #endif
    199         return true;
    200     }
    201 
    202    #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    203     static void AudioInputCallback(void* const userData, uchar* const stream, const int len)
    204     {
    205         NativeBridge* const self = static_cast<NativeBridge*>(userData);
    206 
    207         // safety checks
    208         DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,);
    209         DISTRHO_SAFE_ASSERT_RETURN(len > 0,);
    210 
    211         if (self->jackProcessCallback == nullptr)
    212             return;
    213 
    214         const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS_2);
    215         DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);
    216 
    217         const float* const fstream = (const float*)stream;
    218 
    219         for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i)
    220         {
    221             for (uint j=0; j<numFrames; ++j)
    222                 self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS_2 + i];
    223         }
    224 
    225        #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
    226         // if there are no outputs, run process callback now
    227         const ScopedDenormalDisable sdd;
    228         self->jackProcessCallback(numFrames, self->jackProcessArg);
    229        #endif
    230     }
    231    #endif
    232 
    233    #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    234     static void AudioOutputCallback(void* const userData, uchar* const stream, const int len)
    235     {
    236         NativeBridge* const self = static_cast<NativeBridge*>(userData);
    237 
    238         // safety checks
    239         DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,);
    240         DISTRHO_SAFE_ASSERT_RETURN(len > 0,);
    241 
    242         if (self->jackProcessCallback == nullptr)
    243         {
    244             std::memset(stream, 0, len);
    245             return;
    246         }
    247 
    248         const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS_2);
    249         DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);
    250 
    251         const ScopedDenormalDisable sdd;
    252         self->jackProcessCallback(numFrames, self->jackProcessArg);
    253 
    254         float* const fstream = (float*)stream;
    255 
    256         for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
    257         {
    258             for (uint j=0; j < numFrames; ++j)
    259                 fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS_2 + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j];
    260         }
    261     }
    262    #endif
    263 };
    264 
    265 #endif // SDL_BRIDGE_HPP_INCLUDED