diff --git a/source/include/signalflow/node/io/input/abstract.h b/source/include/signalflow/node/io/input/abstract.h index a1c28beb..500a8be4 100644 --- a/source/include/signalflow/node/io/input/abstract.h +++ b/source/include/signalflow/node/io/input/abstract.h @@ -7,16 +7,18 @@ namespace signalflow { + class AudioIn_Abstract : public Node { public: AudioIn_Abstract(); - virtual int init() = 0; - virtual int start() = 0; - virtual int stop() = 0; - virtual int destroy() = 0; + virtual void init() = 0; + virtual void start() = 0; + virtual void stop() = 0; + virtual void destroy() = 0; virtual void process(Buffer &out, int num_samples) = 0; }; + } diff --git a/source/include/signalflow/node/io/input/miniaudio.h b/source/include/signalflow/node/io/input/miniaudio.h index 8ee00902..ec836e05 100644 --- a/source/include/signalflow/node/io/input/miniaudio.h +++ b/source/include/signalflow/node/io/input/miniaudio.h @@ -1,8 +1,5 @@ #pragma once -#define AudioIn AudioIn_MiniAudio - -#include #include #include "abstract.h" @@ -13,15 +10,15 @@ namespace signalflow { -class AudioIn_MiniAudio : public AudioIn_Abstract +class AudioIn : public AudioIn_Abstract { public: - AudioIn_MiniAudio(unsigned int num_channels = 1); - virtual ~AudioIn_MiniAudio() override; - virtual int init() override; - virtual int start() override; - virtual int stop() override; - virtual int destroy() override; + AudioIn(unsigned int num_channels = 1); + virtual ~AudioIn() override; + virtual void init() override; + virtual void start() override; + virtual void stop() override; + virtual void destroy() override; virtual void process(Buffer &out, int num_samples) override; private: diff --git a/source/include/signalflow/node/io/input/soundio.h b/source/include/signalflow/node/io/input/soundio.h deleted file mode 100644 index 90e4e273..00000000 --- a/source/include/signalflow/node/io/input/soundio.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#ifdef HAVE_SOUNDIO - -#define AudioIn AudioIn_SoundIO - -#include -#include - -#include "abstract.h" - -#include "signalflow/core/graph.h" - -namespace signalflow -{ - -class AudioIn_SoundIO : public AudioIn_Abstract -{ -public: - AudioIn_SoundIO(unsigned int num_channels = 1); - virtual ~AudioIn_SoundIO() override; - virtual int init() override; - virtual int start() override; - virtual int stop() override; - virtual int destroy() override; - virtual void process(Buffer &out, int num_samples) override; - - struct SoundIo *soundio; - struct SoundIoDevice *device; - struct SoundIoInStream *instream; - - Buffer *buffer; - int read_pos; - int write_pos; - unsigned int num_channels_requested; -}; - -} - -#endif diff --git a/source/include/signalflow/node/io/output/abstract.h b/source/include/signalflow/node/io/output/abstract.h index a891e4a2..2b4e130c 100644 --- a/source/include/signalflow/node/io/output/abstract.h +++ b/source/include/signalflow/node/io/output/abstract.h @@ -13,10 +13,10 @@ class AudioOut_Abstract : public Node AudioOut_Abstract(); virtual void process(Buffer &out, int num_samples); - virtual int init() = 0; - virtual int start() = 0; - virtual int stop() = 0; - virtual int destroy() = 0; + virtual void init() = 0; + virtual void start() = 0; + virtual void stop() = 0; + virtual void destroy() = 0; virtual void add_input(NodeRef node); virtual void remove_input(NodeRef node); diff --git a/source/include/signalflow/node/io/output/dummy.h b/source/include/signalflow/node/io/output/dummy.h index 73e102c7..7d79f7b9 100644 --- a/source/include/signalflow/node/io/output/dummy.h +++ b/source/include/signalflow/node/io/output/dummy.h @@ -11,10 +11,10 @@ class AudioOut_Dummy : public AudioOut_Abstract public: AudioOut_Dummy(int num_channels = 2, int buffer_size = 256); - virtual int init() { return 0; } - virtual int start() { return 0; } - virtual int stop() { return 0; } - virtual int destroy() { return 0; } + virtual void init() {} + virtual void start() {} + virtual void stop() {} + virtual void destroy() {} }; REGISTER(AudioOut_Dummy, "audioout-dummy") diff --git a/source/include/signalflow/node/io/output/ios.h b/source/include/signalflow/node/io/output/ios.h deleted file mode 100644 index bdc83f01..00000000 --- a/source/include/signalflow/node/io/output/ios.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "signalflow/core/platform.h" - -#if __APPLE__ - -#if TARGET_OS_IPHONE - -#define AudioOut AudioOut_iOS - -#include "abstract.h" - -#include "signalflow/core/graph.h" -#include "signalflow/node/node.h" - -namespace signalflow -{ - -class AudioOut_iOS : public AudioOut_Abstract -{ -public: - AudioOut_iOS(AudioGraph *graph); - - virtual int init() override; - virtual int start() override; - virtual int close() override; -}; - -} // namespace signalflow - -#endif - -#endif diff --git a/source/include/signalflow/node/io/output/miniaudio.h b/source/include/signalflow/node/io/output/miniaudio.h index 990caaa1..e242209c 100644 --- a/source/include/signalflow/node/io/output/miniaudio.h +++ b/source/include/signalflow/node/io/output/miniaudio.h @@ -1,7 +1,5 @@ #pragma once -#define AudioOut AudioOut_MiniAudio - #include #include @@ -14,24 +12,28 @@ namespace signalflow { -class AudioOut_MiniAudio : public AudioOut_Abstract +class AudioOut : public AudioOut_Abstract { public: - AudioOut_MiniAudio(const std::string &backend_name = "", - const std::string &device_name = "", - unsigned int sample_rate = 0, - unsigned int buffer_size = 0); + AudioOut(const std::string &backend_name = "", + const std::string &device_name = "", + unsigned int sample_rate = 0, + unsigned int buffer_size = 0); - virtual int init() override; - virtual int start() override; - virtual int stop() override; - virtual int destroy() override; + virtual void init() override; + virtual void start() override; + virtual void stop() override; + virtual void destroy() override; std::list get_output_device_names(); std::list get_output_backend_names(); int get_default_output_device_index(); private: + /*-------------------------------------------------------------------------------- + * Initialise a new miniaudio context, using the specified backend name if + * present, or the default backend otherwise. + *-------------------------------------------------------------------------------*/ void init_context(ma_context *context); std::string backend_name; @@ -40,6 +42,6 @@ class AudioOut_MiniAudio : public AudioOut_Abstract ma_device device; }; -REGISTER(AudioOut_MiniAudio, "audioout-miniaudio") +REGISTER(AudioOut, "audioout") } // namespace signalflow diff --git a/source/include/signalflow/node/io/output/soundio.h b/source/include/signalflow/node/io/output/soundio.h deleted file mode 100644 index 518cc988..00000000 --- a/source/include/signalflow/node/io/output/soundio.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#ifdef HAVE_SOUNDIO - -#define AudioOut AudioOut_SoundIO - -#include -#include - -#include "abstract.h" - -#include "signalflow/core/graph.h" -#include "signalflow/node/node.h" - -namespace signalflow -{ - -class AudioOut_SoundIO : public AudioOut_Abstract -{ -public: - AudioOut_SoundIO(const std::string &backend_name = "", - const std::string &device_name = "", - unsigned int sample_rate = 0, - unsigned int buffer_size = 0); - - virtual int init() override; - virtual int start() override; - virtual int stop() override; - virtual int destroy() override; - - std::list get_output_device_names(); - std::list get_output_backend_names(); - int get_default_output_device_index(); - - struct SoundIo *soundio; - struct SoundIoDevice *device; - struct SoundIoOutStream *outstream; - -private: - std::string backend_name; - std::string device_name; -}; - -REGISTER(AudioOut_SoundIO, "audioout-soundio") - -} // namespace signalflow - -#endif diff --git a/source/include/signalflow/signalflow.h b/source/include/signalflow/signalflow.h index 8d3ad2a0..64b20819 100644 --- a/source/include/signalflow/signalflow.h +++ b/source/include/signalflow/signalflow.h @@ -69,7 +69,6 @@ #include #include #include -#include #include /*------------------------------------------------------------------------ diff --git a/source/src/core/graph.cpp b/source/src/core/graph.cpp index ca93ec80..3627fb19 100644 --- a/source/src/core/graph.cpp +++ b/source/src/core/graph.cpp @@ -3,7 +3,6 @@ #include "signalflow/core/graph.h" #include "signalflow/node/io/output/abstract.h" #include "signalflow/node/io/output/dummy.h" -#include "signalflow/node/io/output/ios.h" #include "signalflow/node/io/output/miniaudio.h" #include "signalflow/node/node.h" #include "signalflow/node/oscillators/constant.h" @@ -49,7 +48,7 @@ AudioGraph::AudioGraph(AudioGraphConfig *config, std::string output_device, bool this->config = *config; } - if (output_device == "dummy") + if (output_device == "dummy" || this->config.get_output_device_name() == "dummy") { this->output = new AudioOut_Dummy(); } @@ -590,13 +589,13 @@ std::list AudioGraph::get_outputs() std::list AudioGraph::get_output_device_names() { - AudioOut_MiniAudio *output = (AudioOut_MiniAudio *) (this->output.get()); + AudioOut *output = (AudioOut *) (this->output.get()); return output->get_output_device_names(); } std::list AudioGraph::get_output_backend_names() { - AudioOut_MiniAudio *output = (AudioOut_MiniAudio *) (this->output.get()); + AudioOut *output = (AudioOut *) (this->output.get()); return output->get_output_backend_names(); } diff --git a/source/src/node/io/input/abstract.cpp b/source/src/node/io/input/abstract.cpp index 76a266e4..18d34b10 100644 --- a/source/src/node/io/input/abstract.cpp +++ b/source/src/node/io/input/abstract.cpp @@ -2,15 +2,9 @@ namespace signalflow { -AudioIn_Abstract *shared_in = nullptr; AudioIn_Abstract::AudioIn_Abstract() { - if (shared_in) - throw std::runtime_error("Multiple AudioIn nodes are not yet supported."); - - shared_in = this; - this->name = "audioin"; this->set_channels(0, 1); } diff --git a/source/src/node/io/input/miniaudio.cpp b/source/src/node/io/input/miniaudio.cpp index 48036622..47670f71 100644 --- a/source/src/node/io/input/miniaudio.cpp +++ b/source/src/node/io/input/miniaudio.cpp @@ -11,7 +11,8 @@ static bool is_processing = false; namespace signalflow { -extern AudioIn_Abstract *shared_in; + +AudioIn *shared_in; void read_callback(ma_device *pDevice, void *pOutput, @@ -20,7 +21,7 @@ void read_callback(ma_device *pDevice, { is_processing = true; - AudioIn_MiniAudio *input_node = (AudioIn_MiniAudio *) shared_in; + AudioIn *input_node = (AudioIn *) shared_in; if (!input_node) return; @@ -30,7 +31,7 @@ void read_callback(ma_device *pDevice, int num_channels = input_node->get_num_output_channels(); for (unsigned int frame = 0; frame < frameCount; frame++) { - for (unsigned int channel = 0; channel < num_channels; channel++) + for (int channel = 0; channel < num_channels; channel++) { input_node->out[channel][frame] = input_samples[frame * num_channels + channel]; } @@ -39,7 +40,7 @@ void read_callback(ma_device *pDevice, is_processing = false; } -AudioIn_MiniAudio::AudioIn_MiniAudio(unsigned int num_channels) +AudioIn::AudioIn(unsigned int num_channels) : AudioIn_Abstract() { this->name = "audioin-miniaudio"; @@ -47,13 +48,13 @@ AudioIn_MiniAudio::AudioIn_MiniAudio(unsigned int num_channels) this->init(); } -AudioIn_MiniAudio::~AudioIn_MiniAudio() +AudioIn::~AudioIn() { // TODO: call superclass destructor to set shared_in to null this->destroy(); } -int AudioIn_MiniAudio::init() +void AudioIn::init() { ma_device_config config = ma_device_config_init(ma_device_type_capture); config.capture.format = ma_format_f32; @@ -65,7 +66,7 @@ int AudioIn_MiniAudio::init() ma_result rv = ma_device_init(NULL, &config, &device); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error initialising input device"); + throw audio_io_exception("miniaudio: Error initialising input device"); } this->set_channels(0, device.capture.internalChannels); @@ -76,33 +77,27 @@ int AudioIn_MiniAudio::init() << std::endl; this->start(); - - return 0; } -int AudioIn_MiniAudio::start() +void AudioIn::start() { ma_result rv = ma_device_start(&device); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error starting device"); + throw audio_io_exception("miniaudio: Error starting device"); } - - return 0; } -int AudioIn_MiniAudio::stop() +void AudioIn::stop() { ma_result rv = ma_device_stop(&device); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error stopping device"); + throw audio_io_exception("miniaudio: Error stopping device"); } - - return 0; } -int AudioIn_MiniAudio::destroy() +void AudioIn::destroy() { while (is_processing) { @@ -110,11 +105,9 @@ int AudioIn_MiniAudio::destroy() this->stop(); shared_in = nullptr; - - return 0; } -void AudioIn_MiniAudio::process(Buffer &out, int num_samples) +void AudioIn::process(Buffer &out, int num_samples) { } diff --git a/source/src/node/io/output/abstract.cpp b/source/src/node/io/output/abstract.cpp index e766ada9..2862ad44 100644 --- a/source/src/node/io/output/abstract.cpp +++ b/source/src/node/io/output/abstract.cpp @@ -7,7 +7,6 @@ namespace signalflow AudioOut_Abstract::AudioOut_Abstract() { this->name = "audioout"; - // do we need to set num_output channels to allocate the right number of output buffers? this->set_channels(2, 0); this->no_input_upmix = true; this->has_variable_inputs = true; @@ -105,6 +104,11 @@ void AudioOut_Abstract::replace_input(NodeRef node, NodeRef other) void AudioOut_Abstract::set_channels(int num_input_channels, int num_output_channels) { Node::set_channels(num_input_channels, num_output_channels); + + /*-------------------------------------------------------------------------------- + * Typically, Node objects allocate an output buffer per output channel. + * In this unique case, allocate an output buffer per input channel. + *--------------------------------------------------------------------------------*/ this->resize_output_buffers(num_input_channels); } diff --git a/source/src/node/io/output/ios.mm b/source/src/node/io/output/ios.mm deleted file mode 100644 index 9eb2f2a0..00000000 --- a/source/src/node/io/output/ios.mm +++ /dev/null @@ -1,63 +0,0 @@ -#include "signalflow/node/io/output/ios.h" - -#if TARGET_OS_IPHONE - -#include "signalflow/core/graph.h" - -#ifdef __OBJC__ -#include "AudioIOManager.h" -#endif - -#include -#include -#include -#include -#include - - -namespace libsignal -{ - -extern AudioGraph *shared_graph; - -void audio_callback(float **data, int num_channels, int num_frames) -{ - shared_graph->pull_input(num_frames); - - for (int frame = 0; frame < num_frames; frame++) - { - for (int channel = 0; channel < num_channels; channel++) - { - data[channel][frame] = shared_graph->get_output()->out[channel][frame]; - } - } -} - -AudioOut_iOS::AudioOut_iOS(AudioGraph *graph) : AudioOut_Abstract(graph) -{ - this->init(); -} - -int AudioOut_iOS::init() -{ - AudioIOManager *ioManager = [[AudioIOManager alloc] initWithCallback:audio_callback]; - [ioManager start]; - - return 0; -} - -int AudioOut_iOS::start() -{ - return 0; -} - -int AudioOut_iOS::close() -{ - return 0; -} - - -} // namespace libsignal - -#endif /* TARGET_OS_IPHONE */ - diff --git a/source/src/node/io/output/miniaudio.cpp b/source/src/node/io/output/miniaudio.cpp index 0a121471..d4234bfc 100644 --- a/source/src/node/io/output/miniaudio.cpp +++ b/source/src/node/io/output/miniaudio.cpp @@ -20,29 +20,44 @@ namespace signalflow extern AudioGraph *shared_graph; -void data_callback(ma_device *pDevice, - void *pOutput, - const void *pInput, - ma_uint32 frame_count) +std::unordered_map possible_backend_names = { + { "wasapi", ma_backend_wasapi }, + { "dsound", ma_backend_dsound }, + { "ma_backend_winmm", ma_backend_winmm }, + { "coreaudio", ma_backend_coreaudio }, + { "sndio", ma_backend_sndio }, + { "audio4", ma_backend_audio4 }, + { "oss", ma_backend_oss }, + { "pulseaudio", ma_backend_pulseaudio }, + { "alsa", ma_backend_alsa }, + { "jack", ma_backend_jack }, + { "aaudio", ma_backend_aaudio }, + { "opensl", ma_backend_opensl }, + { "webaudio", ma_backend_webaudio }, + { "null", ma_backend_null }, +}; + +void data_callback(ma_device *ma_device_ptr, + void *ma_output_pointer, + const void *ma_input_pointer, + ma_uint32 ma_frame_count) { is_processing = true; - int channel_count = pDevice->playback.channels; + int channel_count = ma_device_ptr->playback.channels; /*-----------------------------------------------------------------------* - * Return if the shared_graph hasn't been initialized yet. - * (The libsoundio Pulse Audio driver calls the write_callback once - * on initialization, so this may happen legitimately.) + * Do nothing if the shared_graph hasn't been initialized yet. *-----------------------------------------------------------------------*/ if (!shared_graph || !shared_graph->get_output()) { return; } - float *output_pointer = (float *) pOutput; + float *output_pointer = (float *) ma_output_pointer; try { - shared_graph->render(frame_count); + shared_graph->render(ma_frame_count); } catch (const std::exception &e) { @@ -51,9 +66,9 @@ void data_callback(ma_device *pDevice, } NodeRef output = shared_graph->get_output(); - for (unsigned int frame = 0; frame < frame_count; frame++) + for (unsigned int frame = 0; frame < ma_frame_count; frame++) { - for (unsigned int channel = 0; channel < channel_count; channel += 1) + for (int channel = 0; channel < channel_count; channel += 1) { output_pointer[channel_count * frame + channel] = output->out[channel][frame]; } @@ -62,63 +77,46 @@ void data_callback(ma_device *pDevice, is_processing = false; } -AudioOut_MiniAudio::AudioOut_MiniAudio(const std::string &backend_name, - const std::string &device_name, - unsigned int sample_rate, - unsigned int buffer_size) +AudioOut::AudioOut(const std::string &backend_name, + const std::string &device_name, + unsigned int sample_rate, + unsigned int buffer_size) : AudioOut_Abstract() { this->backend_name = backend_name; this->device_name = device_name; this->sample_rate = sample_rate; this->buffer_size = buffer_size; - this->name = "audioout-miniaudio"; + this->name = "audioout"; this->init(); } -void AudioOut_MiniAudio::init_context(ma_context *context) +void AudioOut::init_context(ma_context *context) { if (!this->backend_name.empty()) { - std::unordered_map possible_backend_names = { - { "wasapi", ma_backend_wasapi }, - { "dsound", ma_backend_dsound }, - { "ma_backend_winmm", ma_backend_winmm }, - { "coreaudio", ma_backend_coreaudio }, - { "sndio", ma_backend_sndio }, - { "audio4", ma_backend_audio4 }, - { "oss", ma_backend_oss }, - { "pulseaudio", ma_backend_pulseaudio }, - { "alsa", ma_backend_alsa }, - { "jack", ma_backend_jack }, - { "aaudio", ma_backend_aaudio }, - { "opensl", ma_backend_opensl }, - { "webaudio", ma_backend_webaudio }, - { "null", ma_backend_null }, - }; - if (possible_backend_names.find(this->backend_name) == possible_backend_names.end()) { - throw std::runtime_error("miniaudio: Backend name not recognised: " + this->backend_name); + throw audio_io_exception("miniaudio: Backend name not recognised: " + this->backend_name); } ma_backend backend_name = possible_backend_names[this->backend_name]; if (ma_context_init(&backend_name, 1, NULL, context) != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error initialising context"); + throw audio_io_exception("miniaudio: Error initialising context"); } } else { if (ma_context_init(NULL, 0, NULL, context) != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error initialising context"); + throw audio_io_exception("miniaudio: Error initialising context"); } } } -int AudioOut_MiniAudio::init() +void AudioOut::init() { ma_device_config config = ma_device_config_init(ma_device_type_playback); @@ -144,14 +142,14 @@ int AudioOut_MiniAudio::init() { if (selected_device_index != -1) { - throw std::runtime_error("More than one audio device found matching name '" + device_name + "'"); + throw audio_io_exception("More than one audio device found matching name '" + device_name + "'"); } selected_device_index = i; } } if (selected_device_index == -1) { - throw std::runtime_error("No audio device found matching name '" + device_name + "'"); + throw audio_io_exception("No audio device found matching name '" + device_name + "'"); } config.playback.pDeviceID = &playback_devices[selected_device_index].id; @@ -160,13 +158,15 @@ int AudioOut_MiniAudio::init() // Set to ma_format_unknown to use the device's native format. config.playback.format = ma_format_f32; - // Set to 0 to use the device's native channel count / buffer size. + // Set to 0 to use the device's native channel count. config.playback.channels = 0; + + // Set to 0 to use the device's native buffer size. config.periodSizeInFrames = buffer_size; // Note that the underlying connection always uses the device's native sample rate. // Setting values other than zero instantiates miniaudio's internal resampler. - config.sampleRate = sample_rate; + config.sampleRate = this->sample_rate; config.dataCallback = data_callback; // Buffer blocks into a fixed number of frames @@ -175,50 +175,47 @@ int AudioOut_MiniAudio::init() rv = ma_device_init(NULL, &config, &device); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error initialising output device"); + throw audio_io_exception("miniaudio: Error initialising output device"); } - this->sample_rate = device.playback.internalSampleRate; this->set_channels(device.playback.internalChannels, 0); std::string s = device.playback.internalChannels == 1 ? "" : "s"; std::cerr << "[miniaudio] Output device: " << std::string(device.playback.name) << " (" << device.playback.internalSampleRate << "Hz, " << "buffer size " << device.playback.internalPeriodSizeInFrames << " samples, " << device.playback.internalChannels << " channel" << s << ")" << std::endl; - - return 0; } -int AudioOut_MiniAudio::start() +void AudioOut::start() { ma_result rv = ma_device_start(&device); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Error starting output device"); + throw audio_io_exception("miniaudio: Error starting output device"); } this->set_state(SIGNALFLOW_NODE_STATE_ACTIVE); - return 0; } -int AudioOut_MiniAudio::stop() +void AudioOut::stop() { - // TODO + ma_result rv = ma_device_stop(&device); + if (rv != MA_SUCCESS) + { + throw audio_io_exception("miniaudio: Error stopping output device"); + } this->set_state(SIGNALFLOW_NODE_STATE_STOPPED); - return 0; } -int AudioOut_MiniAudio::destroy() +void AudioOut::destroy() { while (is_processing) { } ma_device_uninit(&device); - - return 0; } -std::list AudioOut_MiniAudio::get_output_device_names() +std::list AudioOut::get_output_device_names() { std::list device_names; @@ -237,7 +234,7 @@ std::list AudioOut_MiniAudio::get_output_device_names() &capture_device_count); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Failure querying audio devices"); + throw audio_io_exception("miniaudio: Failure querying audio devices"); } for (unsigned int i = 0; i < playback_device_count; i++) { @@ -247,13 +244,13 @@ std::list AudioOut_MiniAudio::get_output_device_names() return device_names; } -int AudioOut_MiniAudio::get_default_output_device_index() +int AudioOut::get_default_output_device_index() { // TODO: Is this even used? return -1; } -std::list AudioOut_MiniAudio::get_output_backend_names() +std::list AudioOut::get_output_backend_names() { std::list backend_names; ma_backend enabled_backends[MA_BACKEND_COUNT]; @@ -263,14 +260,20 @@ std::list AudioOut_MiniAudio::get_output_backend_names() rv = ma_get_enabled_backends(enabled_backends, MA_BACKEND_COUNT, &enabled_backend_count); if (rv != MA_SUCCESS) { - throw std::runtime_error("miniaudio: Failure querying backend devices"); + throw audio_io_exception("miniaudio: Failure querying backend devices"); } for (unsigned int i = 0; i < enabled_backend_count; i++) { - std::string backend_name = std::string(ma_get_backend_name(enabled_backends[i])); - if (backend_name != "Custom" && backend_name != "Null") + for (auto pair : possible_backend_names) { - backend_names.push_back(backend_name); + if (pair.second == enabled_backends[i]) + { + std::string backend_name = pair.first; + if (backend_name != "null") + { + backend_names.push_back(backend_name); + } + } } } diff --git a/source/src/node/io/output/soundio.cpp b/source/src/node/io/output/soundio.cpp deleted file mode 100644 index bb8383de..00000000 --- a/source/src/node/io/output/soundio.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "signalflow/node/io/output/soundio.h" - -#ifdef HAVE_SOUNDIO - -#include "signalflow/core/graph.h" - -#include -#include -#include -#include -#include -#include - -static bool is_processing = false; - -namespace signalflow -{ - -extern AudioGraph *shared_graph; - -void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) -{ - is_processing = true; - - const struct SoundIoChannelLayout *layout = &outstream->layout; - struct SoundIoChannelArea *areas; - int frame_count = frame_count_max; - int frames_left = frame_count_max; - - /*-----------------------------------------------------------------------* - * Return if the shared_graph hasn't been initialized yet. - * (The libsoundio Pulse Audio driver calls the write_callback once - * on initialization, so this may happen legitimately.) - *-----------------------------------------------------------------------*/ - if (!shared_graph || !shared_graph->get_output()) - { - return; - } - - AudioOut_SoundIO *out_node = (AudioOut_SoundIO *) outstream->userdata; - - /*-----------------------------------------------------------------------* - * On some drivers (eg Linux), we cannot write all samples at once. - * Keep writing as many as we can until we have cleared the buffer. - *-----------------------------------------------------------------------*/ - while (frames_left > 0) - { - int err; - - if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) - { - throw audio_io_exception("libsoundio error on begin write: " + std::string(soundio_strerror(err))); - } - if (out_node->get_state() == SIGNALFLOW_NODE_STATE_ACTIVE) - { - try - { - shared_graph->render(frame_count); - } - catch (const std::exception &e) - { - std::cerr << "Exception in AudioGraph: " << e.what() << std::endl; - exit(1); - } - - NodeRef output = shared_graph->get_output(); - for (int frame = 0; frame < frame_count; frame++) - { - for (int channel = 0; channel < layout->channel_count; channel += 1) - { - if (outstream->format == SoundIoFormatFloat32NE) - { - float *ptr = reinterpret_cast(areas[channel].ptr + areas[channel].step * frame); - *ptr = output->out[channel][frame]; - /*-----------------------------------------------------------------------* - * Hard limiter. - *-----------------------------------------------------------------------*/ - if (*ptr > 1.0) - *ptr = 1.0; - if (*ptr < -1.0) - *ptr = -1.0; - } - else if (outstream->format == SoundIoFormatS16LE) - { - int16_t *ptr = reinterpret_cast(areas[channel].ptr + areas[channel].step * frame); - *ptr = (int16_t)(output->out[channel][frame] * 32768.0f); - } - } - } - } - else - { - for (int frame = 0; frame < frame_count; frame++) - { - for (int channel = 0; channel < layout->channel_count; channel += 1) - { - float *ptr = reinterpret_cast(areas[channel].ptr + areas[channel].step * frame); - *ptr = 0; - } - } - } - - if ((err = soundio_outstream_end_write(outstream))) - { - throw audio_io_exception("libsoundio error on end write: " + std::string(soundio_strerror(err))); - } - - frames_left -= frame_count; - } - - is_processing = false; -} - -int soundio_get_device_by_name(struct SoundIo *soundio, const char *name) -{ - int output_count = soundio_output_device_count(soundio); - for (int i = 0; i < output_count; i++) - { - struct SoundIoDevice *device = soundio_get_output_device(soundio, i); - if (strcmp(device->name, name) == 0) - { - return i; - } - } - std::cerr << "Couldn't find output device " << std::string(name) << std::endl; - - return -1; -} - -AudioOut_SoundIO::AudioOut_SoundIO(const std::string &backend_name, - const std::string &device_name, - unsigned int sample_rate, - unsigned int buffer_size) - : AudioOut_Abstract() -{ - this->backend_name = backend_name; - this->device_name = device_name; - this->sample_rate = sample_rate; - this->buffer_size = buffer_size; - this->name = "audioout-soundio"; - - this->init(); -} - -int AudioOut_SoundIO::init() -{ - int err; - - this->soundio = soundio_create(); - - if (!this->soundio) - throw audio_io_exception("libsoundio error: out of memory"); - - if (!this->backend_name.empty()) - { - // Backend name is specified; connect to the given backend - std::vector possible_backend_names = { - "none", - "jack", - "pulseaudio", - "alsa", - "coreaudio", - "wasapi", - "dummy" - }; - - auto location = std::find(possible_backend_names.begin(), - possible_backend_names.end(), - this->backend_name); - - if (location == possible_backend_names.end()) - { - throw audio_io_exception("libsoundio error: could not find backend name " + this->backend_name); - } - enum SoundIoBackend backend_index = (enum SoundIoBackend)(location - possible_backend_names.begin()); - if ((err = soundio_connect_backend(this->soundio, backend_index))) - throw audio_io_exception("libsoundio error: could not connect (" + std::string(soundio_strerror(err)) + ")"); - } - else - { - if ((err = soundio_connect(this->soundio))) - throw audio_io_exception("libsoundio error: could not connect (" + std::string(soundio_strerror(err)) + ")"); - } - - soundio_flush_events(this->soundio); - - int default_out_device_index = soundio_default_output_device_index(this->soundio); - if (default_out_device_index < 0) - throw device_not_found_exception("No audio devices were found. More information: https://signalflow.dev/troubleshooting/device_not_found_exception/"); - - if (!this->device_name.empty()) - { - int index = soundio_get_device_by_name(this->soundio, this->device_name.c_str()); - if (index == -1) - { - throw device_not_found_exception("Could not find device name: " + this->device_name + ". More information: https://signalflow.dev/troubleshooting/device_not_found_exception/"); - } - this->device = soundio_get_output_device(this->soundio, index); - } - else - { - this->device = soundio_get_output_device(this->soundio, default_out_device_index); - } - - if (!device) - throw audio_io_exception("libsoundio error: out of memory."); - - this->outstream = soundio_outstream_create(device); - if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) - { - this->outstream->format = SoundIoFormatFloat32NE; - } - else if (soundio_device_supports_format(device, SoundIoFormatS16LE)) - { - this->outstream->format = SoundIoFormatS16LE; - } - else - { - /*-----------------------------------------------------------------------* - * SignalFlow currently only supports float32 sample output - *-----------------------------------------------------------------------*/ - throw audio_io_exception("libsoundio error: Output device does not support float32 or int16le samples"); - } - this->outstream->write_callback = write_callback; - if (!this->sample_rate) - { - this->sample_rate = this->device->sample_rate_current; - } - this->outstream->sample_rate = this->sample_rate; - this->outstream->software_latency = (double) this->buffer_size / this->outstream->sample_rate; - this->outstream->userdata = (void *) this; - // With a device with multiple layouts, use the first. - // To check: is this always the layout with the most channels? - this->outstream->layout = device->layouts[0]; - - if ((err = soundio_outstream_open(this->outstream))) - { - throw audio_io_exception("libsoundio error: unable to open device: " + std::string(soundio_strerror(err))); - } - - if (this->outstream->layout_error) - { - /*-------------------------------------------------------------------------------- - * This should not be a fatal error (see example in libsoundio sio_sine.c). - * Should just generate a warning instead. - * Experienced on Raspberry Pi 4 with raspi-audio interface. - *-------------------------------------------------------------------------------*/ - std::cerr << "libsoundio warning: unable to set channel layout: " - << std::string(soundio_strerror(this->outstream->layout_error)) << std::endl; - } - - this->num_output_channels = this->outstream->layout.channel_count; - - // update based on the actual buffer size - this->buffer_size = (int) round(this->outstream->software_latency * this->outstream->sample_rate); - - std::string s = num_output_channels == 1 ? "" : "s"; - - std::cerr << "[soundio] Output device: " << device->name << " (" << sample_rate << "Hz, " - << "buffer size " << buffer_size << " samples, " << num_output_channels << " channel" << s << ")" - << std::endl; - - // do we need to set num_output channels to allocate the right number of output buffers? - this->set_channels(num_output_channels, 0); - - return 0; -} - -int AudioOut_SoundIO::start() -{ - int err; - if ((err = soundio_outstream_start(outstream))) - throw audio_io_exception("libsoundio error: unable to start device: " + std::string(soundio_strerror(err))); - this->set_state(SIGNALFLOW_NODE_STATE_ACTIVE); - - return 0; -} - -int AudioOut_SoundIO::stop() -{ - this->set_state(SIGNALFLOW_NODE_STATE_STOPPED); - return 0; -} - -int AudioOut_SoundIO::destroy() -{ - while (is_processing) - { - } - - soundio_outstream_destroy(this->outstream); - soundio_device_unref(this->device); - soundio_destroy(this->soundio); - - return 0; -} - -std::list AudioOut_SoundIO::get_output_device_names() -{ - int output_count = soundio_output_device_count(this->soundio); - - std::list device_names; - - for (int i = 0; i < output_count; i++) - { - struct SoundIoDevice *device = soundio_get_output_device(soundio, i); - device_names.push_back(std::string(device->name)); - } - - return device_names; -} - -int AudioOut_SoundIO::get_default_output_device_index() -{ - unsigned int default_output = soundio_default_output_device_index(this->soundio); - return default_output; -} - -std::list AudioOut_SoundIO::get_output_backend_names() -{ - std::list backend_names; - std::vector possible_backend_names = { - "none", - "jack", - "pulseaudio", - "alsa", - "coreaudio", - "wasapi", - "dummy" - }; - for (int i = 0; i < soundio_backend_count(this->soundio); i++) - { - int backend_index = soundio_get_backend(this->soundio, i); - std::string backend_name = possible_backend_names[backend_index]; - backend_names.push_back(backend_name); - } - - return backend_names; -} - -} // namespace signalflow - -#endif