diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 7a097aa7..cde605a9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,7 +21,7 @@ jobs: run: sudo apt update - name: Install dependencies - run: sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libairspy-dev libiio-dev libad9361-dev portaudio19-dev libhackrf-dev librtlsdr-dev + run: sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libairspy-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory diff --git a/CMakeLists.txt b/CMakeLists.txt index ad5ade50..3dec14b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,7 @@ option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Depedencies: libsd option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON) option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" OFF) option(OPT_BUILD_RTL_SDR_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON) -option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: portaudio)" ON) -option(OPT_BUILD_RTAUDIO_SINK "Build RtAudio Sink Module (Depedencies: rtaudio)" OFF) - +option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: rtaudio)" ON) # Core of SDR++ add_subdirectory("core") @@ -64,10 +62,6 @@ if (OPT_BUILD_AUDIO_SINK) add_subdirectory("audio_sink") endif (OPT_BUILD_AUDIO_SINK) -if (OPT_BUILD_RTAUDIO_SINK) -add_subdirectory("rtaudio_sink") -endif (OPT_BUILD_RTAUDIO_SINK) - if (MSVC) set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc") else() diff --git a/audio_sink/CMakeLists.txt b/audio_sink/CMakeLists.txt index 2dc49670..fafc67e5 100644 --- a/audio_sink/CMakeLists.txt +++ b/audio_sink/CMakeLists.txt @@ -16,18 +16,23 @@ target_link_libraries(audio_sink PRIVATE sdrpp_core) set_target_properties(audio_sink PROPERTIES PREFIX "") if (MSVC) - find_package(portaudio CONFIG REQUIRED) - target_link_libraries(sdrpp_core PUBLIC portaudio) + # Lib path + target_link_directories(sdrpp_core PUBLIC "C:/Program Files (x86)/RtAudio/lib") + + # Misc headers + target_include_directories(sdrpp_core PUBLIC "C:/Program Files (x86)/RtAudio/include/rtaudio") + + target_link_libraries(sdrpp_core PUBLIC rtaudio) else (MSVC) find_package(PkgConfig) - pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0) + pkg_check_modules(RTAUDIO REQUIRED rtaudio) - target_include_directories(sdrpp_core PUBLIC ${PORTAUDIO_INCLUDE_DIRS}) + target_include_directories(sdrpp_core PUBLIC ${RTAUDIO_INCLUDE_DIRS}) - target_link_directories(sdrpp_core PUBLIC ${PORTAUDIO_LIBRARY_DIRS}) + target_link_directories(sdrpp_core PUBLIC ${RTAUDIO_LIBRARY_DIRS}) - target_link_libraries(sdrpp_core PUBLIC ${PORTAUDIO_LIBRARIES}) + target_link_libraries(sdrpp_core PUBLIC ${RTAUDIO_LIBRARIES}) endif (MSVC) diff --git a/audio_sink/src/main.cpp b/audio_sink/src/main.cpp index fd40272b..7e6b6b1f 100644 --- a/audio_sink/src/main.cpp +++ b/audio_sink/src/main.cpp @@ -3,10 +3,12 @@ #include #include #include -#include #include #include #include +#include +#include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -18,85 +20,46 @@ SDRPP_MOD_INFO { /* Max instances */ 1 }; +ConfigManager config; + class AudioSink : SinkManager::Sink { public: - struct AudioDevice_t { - std::string name; - int index; - int channels; - int srId; - std::vector sampleRates; - std::string txtSampleRates; - }; - AudioSink(SinkManager::Stream* stream, std::string streamName) { _stream = stream; _streamName = streamName; s2m.init(_stream->sinkOut); - monoRB.init(&s2m.out); - stereoRB.init(_stream->sinkOut); + monoPacker.init(&s2m.out, 512); + stereoPacker.init(_stream->sinkOut, 512); - // monoPacker.init(&s2m.out, 240); - // stereoPacker.init(_stream->sinkOut, 240); + bool created = false; + std::string device = ""; + config.aquire(); + if (!config.conf.contains(_streamName)) { + created = true; + config.conf[_streamName]["device"] = ""; + config.conf[_streamName]["devices"] = json({}); + } + device = config.conf[_streamName]["device"]; + config.release(created); - // Initialize PortAudio - devCount = Pa_GetDeviceCount(); - devId = Pa_GetDefaultOutputDevice(); - const PaDeviceInfo *deviceInfo; - PaStreamParameters outputParams; - outputParams.sampleFormat = paFloat32; - outputParams.hostApiSpecificStreamInfo = NULL; - - // Gather hardware info - for(int i = 0; i < devCount; i++) { - deviceInfo = Pa_GetDeviceInfo(i); - if (deviceInfo->maxOutputChannels < 1) { - continue; - } - AudioDevice_t dev; - dev.name = deviceInfo->name; - dev.index = i; - dev.channels = std::min(deviceInfo->maxOutputChannels, 2); - dev.sampleRates.clear(); - dev.txtSampleRates = ""; - for (int j = 0; j < 6; j++) { - outputParams.channelCount = dev.channels; - outputParams.device = dev.index; - outputParams.suggestedLatency = deviceInfo->defaultLowOutputLatency; - PaError err = Pa_IsFormatSupported(NULL, &outputParams, POSSIBLE_SAMP_RATE[j]); - if (err != paFormatIsSupported) { - continue; - } - dev.sampleRates.push_back(POSSIBLE_SAMP_RATE[j]); - dev.txtSampleRates += std::to_string((int)POSSIBLE_SAMP_RATE[j]); - dev.txtSampleRates += '\0'; - } - if (dev.sampleRates.size() == 0) { - continue; - } - if (i == devId) { - devListId = devices.size(); - defaultDev = devListId; - _stream->setSampleRate(dev.sampleRates[0]); - } - dev.srId = 0; - - AudioDevice_t* _dev = new AudioDevice_t; - *_dev = dev; - devices.push_back(_dev); - - deviceNames.push_back(deviceInfo->name); - txtDevList += deviceInfo->name; + int count = audio.getDeviceCount(); + RtAudio::DeviceInfo info; + for (int i = 0; i < count; i++) { + info = audio.getDeviceInfo(i); + if (!info.probed) { continue; } + if (info.outputChannels == 0) { continue; } + if (info.isDefaultOutput) { defaultDevId = devList.size(); } + devList.push_back(info); + deviceIds.push_back(i); + txtDevList += info.name; txtDevList += '\0'; } - // Load config from file + selectByName(device); } ~AudioSink() { - for (auto const& dev : devices) { - delete dev; - } + } void start() { @@ -114,162 +77,155 @@ public: doStop(); running = false; } + + void selectFirst() { + selectById(defaultDevId); + } + + void selectByName(std::string name) { + for (int i = 0; i < devList.size(); i++) { + if (devList[i].name == name) { + selectById(i); + return; + } + } + selectFirst(); + } + + void selectById(int id) { + devId = id; + bool created = false; + config.aquire(); + if (!config.conf[_streamName]["devices"].contains(devList[id].name)) { + created = true; + config.conf[_streamName]["devices"][devList[id].name] = devList[id].preferredSampleRate; + } + sampleRate = config.conf[_streamName]["devices"][devList[id].name]; + config.release(created); + + sampleRates = devList[id].sampleRates; + sampleRatesTxt = ""; + char buf[256]; + bool found = false; + unsigned int defaultId = 0; + unsigned int defaultSr = devList[id].preferredSampleRate; + for (int i = 0; i < sampleRates.size(); i++) { + if (sampleRates[i] == sampleRate) { + found = true; + srId = i; + } + if (sampleRates[i] == defaultSr) { + defaultId = i; + } + sprintf(buf, "%d", sampleRates[i]); + sampleRatesTxt += buf; + sampleRatesTxt += '\0'; + } + if (!found) { + sampleRate = defaultSr; + srId = defaultId; + } + + _stream->setSampleRate(sampleRate); + + if (running) { doStop(); } + if (running) { doStart(); } + } void menuHandler() { float menuWidth = ImGui::GetContentRegionAvailWidth(); ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(("##_audio_sink_dev_"+_streamName).c_str(), &devListId, txtDevList.c_str())) { - // TODO: Load SR from config - if (running) { - doStop(); - doStart(); - } - // TODO: Save to config + if (ImGui::Combo(("##_audio_sink_dev_"+_streamName).c_str(), &devId, txtDevList.c_str())) { + selectById(devId); + config.aquire(); + config.conf[_streamName]["device"] = devList[devId].name; + config.release(true); } - AudioDevice_t* dev = devices[devListId]; - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(("##_audio_sink_sr_"+_streamName).c_str(), &dev->srId, dev->txtSampleRates.c_str())) { - _stream->setSampleRate(dev->sampleRates[dev->srId]); + if (ImGui::Combo(("##_audio_sink_sr_"+_streamName).c_str(), &srId, sampleRatesTxt.c_str())) { + sampleRate = sampleRates[srId]; + _stream->setSampleRate(sampleRate); if (running) { doStop(); doStart(); } - // TODO: Save to config + config.aquire(); + config.conf[_streamName]["devices"][devList[devId].name] = sampleRate; + config.release(true); } } private: void doStart() { - const PaDeviceInfo *deviceInfo; - AudioDevice_t* dev = devices[devListId]; - PaStreamParameters outputParams; - deviceInfo = Pa_GetDeviceInfo(dev->index); - outputParams.channelCount = 2; - outputParams.sampleFormat = paFloat32; - outputParams.hostApiSpecificStreamInfo = NULL; - outputParams.device = dev->index; - outputParams.suggestedLatency = Pa_GetDeviceInfo(outputParams.device)->defaultLowOutputLatency; - PaError err; + RtAudio::StreamParameters parameters; + parameters.deviceId = deviceIds[devId]; + parameters.nChannels = 2; + unsigned int bufferFrames = sampleRate / 60; + RtAudio::StreamOptions opts; + opts.flags = RTAUDIO_MINIMIZE_LATENCY; - float sampleRate = dev->sampleRates[dev->srId]; - int bufferSize = sampleRate / 60.0f; + stereoPacker.setSampleCount(bufferFrames); - if (dev->channels == 2) { - stereoRB.data.setMaxLatency(bufferSize * 2); - stereoRB.start(); - // stereoPacker.setSampleCount(bufferSize); - // stereoPacker.start(); - err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, paFramesPerBufferUnspecified, 0, _stereo_cb, this); - //err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, bufferSize, 0, _stereo_cb, this); + try { + audio.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &callback, this, &opts); + audio.startStream(); + stereoPacker.start(); } - else { - monoRB.data.setMaxLatency(bufferSize * 2); - monoRB.start(); - // stereoPacker.setSampleCount(bufferSize); - // monoPacker.start(); - err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, paFramesPerBufferUnspecified, 0, _mono_cb, this); - //err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, bufferSize, 0, _mono_cb, this); - } - - if (err != 0) { - spdlog::error("Error while opening audio stream: ({0}) => {1}", err, Pa_GetErrorText(err)); + catch ( RtAudioError& e ) { + spdlog::error("Could not open audio device"); return; } - err = Pa_StartStream(stream); - if (err != 0) { - spdlog::error("Error while starting audio stream: ({0}) => {1}", err, Pa_GetErrorText(err)); - return; - } - spdlog::info("Audio device open."); - running = true; + spdlog::info("RtAudio stream open"); } void doStop() { s2m.stop(); - monoRB.stop(); - stereoRB.stop(); - // monoPacker.stop(); - // stereoPacker.stop(); - monoRB.data.stopReader(); - stereoRB.data.stopReader(); - // monoPacker.out.stopReader(); - // stereoPacker.out.stopReader(); - Pa_StopStream(stream); - Pa_CloseStream(stream); - monoRB.data.clearReadStop(); - stereoRB.data.clearReadStop(); - // monoPacker.out.clearReadStop(); - // stereoPacker.out.clearWriteStop(); + monoPacker.stop(); + stereoPacker.stop(); + monoPacker.out.stopReader(); + stereoPacker.out.stopReader(); + audio.stopStream(); + audio.closeStream(); + monoPacker.out.clearReadStop(); + stereoPacker.out.clearReadStop(); } - static int _mono_cb(const void *input, void *output, unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + static int callback( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *userData) { AudioSink* _this = (AudioSink*)userData; - _this->monoRB.data.read((float*)output, frameCount); + int count = _this->stereoPacker.out.read(); + if (count < 0) { return 0; } + memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t)); + _this->stereoPacker.out.flush(); return 0; } - - static int _stereo_cb(const void *input, void *output, unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { - AudioSink* _this = (AudioSink*)userData; - _this->stereoRB.data.read((dsp::stereo_t*)output, frameCount); - return 0; - } - - // static int _mono_cb(const void *input, void *output, unsigned long frameCount, - // const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { - // AudioSink* _this = (AudioSink*)userData; - // if (_this->monoPacker.out.read() < 0) { return 0; } - // memcpy((float*)output, _this->monoPacker.out.readBuf, frameCount * sizeof(float)); - // _this->monoPacker.out.flush(); - // return 0; - // } - - // static int _stereo_cb(const void *input, void *output, unsigned long frameCount, - // const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { - // AudioSink* _this = (AudioSink*)userData; - // if (_this->stereoPacker.out.read() < 0) { spdlog::warn("CB killed"); return 0; } - // memcpy((dsp::stereo_t*)output, _this->stereoPacker.out.readBuf, frameCount * sizeof(dsp::stereo_t)); - // _this->stereoPacker.out.flush(); - // return 0; - // } - SinkManager::Stream* _stream; dsp::StereoToMono s2m; - dsp::RingBufferSink monoRB; - dsp::RingBufferSink stereoRB; - - // dsp::Packer monoPacker; - // dsp::Packer stereoPacker; + dsp::Packer monoPacker; + dsp::Packer stereoPacker; std::string _streamName; - PaStream *stream; int srId = 0; int devCount; int devId = 0; - int devListId = 0; - int defaultDev = 0; bool running = false; - const double POSSIBLE_SAMP_RATE[6] = { - 48000.0f, - 44100.0f, - 24000.0f, - 22050.0f, - 12000.0f, - 11025.0f - }; + unsigned int defaultDevId = 0; - std::vector devices; - std::vector deviceNames; + std::vector devList; + std::vector deviceIds; std::string txtDevList; + std::vector sampleRates; + std::string sampleRatesTxt; + unsigned int sampleRate = 48000; + + RtAudio audio; + }; class AudioSinkModule : public ModuleManager::Instance { @@ -279,13 +235,11 @@ public: provider.create = create_sink; provider.ctx = this; - Pa_Initialize(); - sigpath::sinkManager.registerSinkProvider("Audio", provider); } ~AudioSinkModule() { - Pa_Terminate(); + } void enable() { @@ -312,8 +266,10 @@ private: }; MOD_EXPORT void _INIT_() { - // Nothing here - // TODO: Do instancing here (in source modules as well) to prevent multiple loads + json def = json({}); + config.setPath(options::opts.root + "/audio_sink_config.json"); + config.load(def); + config.enableAutoSave(); } MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { @@ -322,7 +278,8 @@ MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { } MOD_EXPORT void _DELETE_INSTANCE_() { - + config.disableAutoSave(); + config.save(); } MOD_EXPORT void _END_() { diff --git a/readme.md b/readme.md index 7cd83b31..19d25ea0 100644 --- a/readme.md +++ b/readme.md @@ -35,7 +35,7 @@ Download the latest release from [the Releases page](https://github.com/Alexandr Then, run: ```sh -sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev portaudio19-dev libhackrf-dev +sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev sudo dpkg -i sdrpp_debian_amd64.deb ``` @@ -52,10 +52,10 @@ TODO * cmake * vcpkg * PothosSDR (This will install libraires for most SDRs) +* rtaudio After this, install the following depencies using vcpkg: * fftw3 -* portaudio * glfw * glew @@ -143,7 +143,7 @@ Next install dependencies based on the modules you wish to build: * soapy_source: SoapySDR + drivers for each SDRs (see SoapySDR docs) * airspyhf_source: libairspyhf * plutosdr_source: libiio, libad9361 -* audio_sink: portaudio +* audio_sink: librtaudio-dev Note: make sure you're using GCC 8 or later as older versions do not have `std::filesystem` built-in. diff --git a/rtaudio_sink/CMakeLists.txt b/rtaudio_sink/CMakeLists.txt deleted file mode 100644 index a7a489db..00000000 --- a/rtaudio_sink/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.13) -project(rtaudio_sink) - -if (MSVC) - set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc") -else() - set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive") -endif (MSVC) - -file(GLOB SRC "src/*.cpp") - -include_directories("src/") - -add_library(rtaudio_sink SHARED ${SRC}) -target_link_libraries(rtaudio_sink PRIVATE sdrpp_core) -set_target_properties(rtaudio_sink PROPERTIES PREFIX "") - -if (MSVC) - # Lib path - target_link_directories(sdrpp_core PUBLIC "C:/Program Files (x86)/RtAudio/lib") - - # Misc headers - target_include_directories(sdrpp_core PUBLIC "C:/Program Files (x86)/RtAudio/include/rtaudio") - - target_link_libraries(sdrpp_core PUBLIC rtaudio) -else (MSVC) - find_package(PkgConfig) - - pkg_check_modules(RTAUDIO REQUIRED rtaudio) - - target_include_directories(sdrpp_core PUBLIC ${RTAUDIO_INCLUDE_DIRS}) - - target_link_directories(sdrpp_core PUBLIC ${RTAUDIO_LIBRARY_DIRS}) - - target_link_libraries(sdrpp_core PUBLIC ${RTAUDIO_LIBRARIES}) - -endif (MSVC) - -# Install directives -install(TARGETS rtaudio_sink DESTINATION lib/sdrpp/plugins) \ No newline at end of file diff --git a/rtaudio_sink/src/main.cpp b/rtaudio_sink/src/main.cpp deleted file mode 100644 index 9f3bf3ee..00000000 --- a/rtaudio_sink/src/main.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CONCAT(a, b) ((std::string(a) + b).c_str()) - -SDRPP_MOD_INFO { - /* Name: */ "rtaudio_sink", - /* Description: */ "RtAudio sink module for SDR++", - /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 0, - /* Max instances */ 1 -}; - -ConfigManager config; - -class AudioSink : SinkManager::Sink { -public: - AudioSink(SinkManager::Stream* stream, std::string streamName) { - _stream = stream; - _streamName = streamName; - s2m.init(_stream->sinkOut); - monoPacker.init(&s2m.out, 512); - stereoPacker.init(_stream->sinkOut, 512); - - bool created = false; - std::string device = ""; - config.aquire(); - if (!config.conf.contains(_streamName)) { - created = true; - config.conf[_streamName]["device"] = ""; - config.conf[_streamName]["devices"] = json({}); - } - device = config.conf[_streamName]["device"]; - config.release(created); - - int count = audio.getDeviceCount(); - RtAudio::DeviceInfo info; - for (int i = 0; i < count; i++) { - info = audio.getDeviceInfo(i); - if (!info.probed) { continue; } - if (info.outputChannels == 0) { continue; } - if (info.isDefaultOutput) { defaultDevId = devList.size(); } - devList.push_back(info); - deviceIds.push_back(i); - txtDevList += info.name; - txtDevList += '\0'; - } - - selectByName(device); - } - - ~AudioSink() { - - } - - void start() { - if (running) { - return; - } - doStart(); - running = true; - } - - void stop() { - if (!running) { - return; - } - doStop(); - running = false; - } - - void selectFirst() { - selectById(defaultDevId); - } - - void selectByName(std::string name) { - for (int i = 0; i < devList.size(); i++) { - if (devList[i].name == name) { - selectById(i); - return; - } - } - selectFirst(); - } - - void selectById(int id) { - devId = id; - bool created = false; - config.aquire(); - if (!config.conf[_streamName]["devices"].contains(devList[id].name)) { - created = true; - config.conf[_streamName]["devices"][devList[id].name] = devList[id].preferredSampleRate; - } - sampleRate = config.conf[_streamName]["devices"][devList[id].name]; - config.release(created); - - sampleRates = devList[id].sampleRates; - sampleRatesTxt = ""; - char buf[256]; - bool found = false; - unsigned int defaultId = 0; - unsigned int defaultSr = devList[id].preferredSampleRate; - for (int i = 0; i < sampleRates.size(); i++) { - if (sampleRates[i] == sampleRate) { - found = true; - srId = i; - } - if (sampleRates[i] == defaultSr) { - defaultId = i; - } - sprintf(buf, "%d", sampleRates[i]); - sampleRatesTxt += buf; - sampleRatesTxt += '\0'; - } - if (!found) { - sampleRate = defaultSr; - srId = defaultId; - } - - _stream->setSampleRate(sampleRate); - - if (running) { doStop(); } - if (running) { doStart(); } - } - - void menuHandler() { - float menuWidth = ImGui::GetContentRegionAvailWidth(); - - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(("##_rtaudio_sink_dev_"+_streamName).c_str(), &devId, txtDevList.c_str())) { - selectById(devId); - config.aquire(); - config.conf[_streamName]["device"] = devList[devId].name; - config.release(true); - } - - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(("##_rtaudio_sink_sr_"+_streamName).c_str(), &srId, sampleRatesTxt.c_str())) { - sampleRate = sampleRates[srId]; - _stream->setSampleRate(sampleRate); - if (running) { - doStop(); - doStart(); - } - config.aquire(); - config.conf[_streamName]["devices"][devList[devId].name] = sampleRate; - config.release(true); - } - } - -private: - void doStart() { - RtAudio::StreamParameters parameters; - parameters.deviceId = deviceIds[devId]; - parameters.nChannels = 2; - unsigned int bufferFrames = sampleRate / 60; - RtAudio::StreamOptions opts; - opts.flags = RTAUDIO_MINIMIZE_LATENCY; - - stereoPacker.setSampleCount(bufferFrames); - - try { - audio.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &callback, this, &opts); - audio.startStream(); - stereoPacker.start(); - } - catch ( RtAudioError& e ) { - spdlog::error("Could not open audio device"); - return; - } - - spdlog::info("RtAudio stream open"); - } - - void doStop() { - s2m.stop(); - monoPacker.stop(); - stereoPacker.stop(); - monoPacker.out.stopReader(); - stereoPacker.out.stopReader(); - audio.stopStream(); - audio.closeStream(); - monoPacker.out.clearReadStop(); - stereoPacker.out.clearReadStop(); - } - - static int callback( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *userData) { - AudioSink* _this = (AudioSink*)userData; - int count = _this->stereoPacker.out.read(); - if (count < 0) { return 0; } - memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t)); - _this->stereoPacker.out.flush(); - return 0; - } - - SinkManager::Stream* _stream; - dsp::StereoToMono s2m; - dsp::Packer monoPacker; - dsp::Packer stereoPacker; - - std::string _streamName; - - int srId = 0; - int devCount; - int devId = 0; - bool running = false; - - unsigned int defaultDevId = 0; - - std::vector devList; - std::vector deviceIds; - std::string txtDevList; - - std::vector sampleRates; - std::string sampleRatesTxt; - unsigned int sampleRate = 48000; - - RtAudio audio; - -}; - -class AudioSinkModule : public ModuleManager::Instance { -public: - AudioSinkModule(std::string name) { - this->name = name; - provider.create = create_sink; - provider.ctx = this; - - sigpath::sinkManager.registerSinkProvider("RtAudio", provider); - } - - ~AudioSinkModule() { - - } - - void enable() { - enabled = true; - } - - void disable() { - enabled = false; - } - - bool isEnabled() { - return enabled; - } - -private: - static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) { - return (SinkManager::Sink*)(new AudioSink(stream, streamName)); - } - - std::string name; - bool enabled = true; - SinkManager::SinkProvider provider; - -}; - -MOD_EXPORT void _INIT_() { - json def = json({}); - config.setPath(options::opts.root + "/audio_sink_config.json"); - config.load(def); - config.enableAutoSave(); -} - -MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { - AudioSinkModule* instance = new AudioSinkModule(name); - return instance; -} - -MOD_EXPORT void _DELETE_INSTANCE_() { - config.disableAutoSave(); - config.save(); -} - -MOD_EXPORT void _END_() { - -} \ No newline at end of file