From 127d6bf0a7362dfaec7bc69c95f940f9ef581881 Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Sat, 20 Feb 2021 22:05:13 +0100 Subject: [PATCH] added rtaudio sink --- CMakeLists.txt | 5 + rtaudio_sink/CMakeLists.txt | 40 +++++++ rtaudio_sink/src/main.cpp | 218 ++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 rtaudio_sink/CMakeLists.txt create mode 100644 rtaudio_sink/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 773d4b3d..ad5ade50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: lib 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) # Core of SDR++ add_subdirectory("core") @@ -63,6 +64,10 @@ 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/rtaudio_sink/CMakeLists.txt b/rtaudio_sink/CMakeLists.txt new file mode 100644 index 00000000..a7a489db --- /dev/null +++ b/rtaudio_sink/CMakeLists.txt @@ -0,0 +1,40 @@ +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 new file mode 100644 index 00000000..29903048 --- /dev/null +++ b/rtaudio_sink/src/main.cpp @@ -0,0 +1,218 @@ +#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 +}; + +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); + + stream->setSampleRate(48000); + + RtAudio::StreamParameters parameters; + + 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) { devId = devList.size(); } + devList.push_back(info); + deviceIds.push_back(i); + } + } + + ~AudioSink() { + + } + + void start() { + if (running) { + return; + } + doStart(); + running = true; + } + + void stop() { + if (!running) { + return; + } + doStop(); + running = false; + } + + void menuHandler() { + float menuWidth = ImGui::GetContentRegionAvailWidth(); + + // ImGui::SetNextItemWidth(menuWidth); + // if (ImGui::Combo(("##_rtaudio_sink_dev_"+_streamName).c_str(), &devListId, txtDevList.c_str())) { + // // TODO: Load SR from config + // if (running) { + // doStop(); + // doStart(); + // } + // // TODO: Save to config + // } + + // AudioDevice_t* dev = devices[devListId]; + + // ImGui::SetNextItemWidth(menuWidth); + // if (ImGui::Combo(("##_rtaudio_sink_sr_"+_streamName).c_str(), &dev->srId, dev->txtSampleRates.c_str())) { + // _stream->setSampleRate(dev->sampleRates[dev->srId]); + // if (running) { + // doStop(); + // doStart(); + // } + // // TODO: Save to config + // } + } + +private: + void doStart() { + RtAudio::StreamParameters parameters; + parameters.deviceId = deviceIds[devId]; + parameters.nChannels = 2; + unsigned int sampleRate = 48000; + unsigned int bufferFrames = sampleRate / 200; + 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"); + } + } + + void doStop() { + s2m.stop(); + monoPacker.stop(); + stereoPacker.stop(); + monoPacker.out.stopReader(); + stereoPacker.out.stopReader(); + audio.stopStream(); + audio.closeStream(); + monoPacker.out.clearReadStop(); + stereoPacker.out.clearWriteStop(); + } + + 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; + + const double POSSIBLE_SAMP_RATE[6] = { + 48000.0f, + 44100.0f, + 24000.0f, + 22050.0f, + 12000.0f, + 11025.0f + }; + + + std::vector devList; + std::vector deviceIds; + std::string txtDevList; + + 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_() { + // Nothing here + // TODO: Do instancing here (in source modules as well) to prevent multiple loads +} + +MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { + AudioSinkModule* instance = new AudioSinkModule(name); + return instance; +} + +MOD_EXPORT void _DELETE_INSTANCE_() { + +} + +MOD_EXPORT void _END_() { + +} \ No newline at end of file