more work

This commit is contained in:
AlexandreRouma 2023-07-10 03:48:59 +02:00
parent 2e80882ab5
commit 25bc9f60ed
9 changed files with 259 additions and 114 deletions

View File

@ -1,5 +1,3 @@
set(CMAKE_BUILD_TYPE "Debug") # TODO: Make sure this gets properly disabled when going back to normal builds
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
project(sdrpp) project(sdrpp)

View File

@ -16,6 +16,7 @@ namespace icons {
ImTextureID UNMUTED; ImTextureID UNMUTED;
ImTextureID NORMAL_TUNING; ImTextureID NORMAL_TUNING;
ImTextureID CENTER_TUNING; ImTextureID CENTER_TUNING;
ImTextureID ALIGN_CENTER;
GLuint loadTexture(std::string path) { GLuint loadTexture(std::string path) {
int w, h, n; int w, h, n;
@ -45,6 +46,7 @@ namespace icons {
UNMUTED = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/unmuted.png"); UNMUTED = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/unmuted.png");
NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/normal_tuning.png"); NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/normal_tuning.png");
CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/center_tuning.png"); CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/center_tuning.png");
ALIGN_CENTER = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/align_center.png");
return true; return true;
} }

View File

@ -13,6 +13,7 @@ namespace icons {
extern ImTextureID UNMUTED; extern ImTextureID UNMUTED;
extern ImTextureID NORMAL_TUNING; extern ImTextureID NORMAL_TUNING;
extern ImTextureID CENTER_TUNING; extern ImTextureID CENTER_TUNING;
extern ImTextureID ALIGN_CENTER;
GLuint loadTexture(std::string path); GLuint loadTexture(std::string path);
bool load(std::string resDir); bool load(std::string resDir);

View File

@ -3,20 +3,57 @@
#include <imgui.h> #include <imgui.h>
#include <utils/flog.h> #include <utils/flog.h>
#include <gui/style.h> #include <gui/style.h>
#include <gui/icons.h>
#include <utils/optionlist.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
namespace streamsmenu { namespace streamsmenu {
std::vector<SinkID> sinksToBeRemoved; std::vector<SinkID> sinksToBeRemoved;
void init() { std::recursive_mutex sinkTypesMtx;
OptionList<std::string, std::string> sinkTypes;
std::map<std::string, int> selectedSinkTypeId;
std::map<std::string, int> addSinkTypeId;
void updateSinkTypeList(const std::string& removed = "") {
std::lock_guard<std::recursive_mutex> lck1(sinkTypesMtx);
auto lck2 = sigpath::streamManager.getSinkTypesLock();
const auto& types = sigpath::streamManager.getSinkTypes();
sinkTypes.clear();
for (const auto& type : types) {
if (type == removed) { continue; }
sinkTypes.define(type, type, type);
}
}
void onSinkProviderRegistered(const std::string& type) {
// Update the list
updateSinkTypeList();
// Update the selected ID of each drop down
// TODO
}
void onSinkProviderUnregister(const std::string& type) {
// Update the list
updateSinkTypeList(type);
// Update the selected ID of each drop down
// TODO
}
void init() {
sigpath::streamManager.onSinkProviderRegistered.bind(onSinkProviderRegistered);
sigpath::streamManager.onSinkProviderUnregister.bind(onSinkProviderUnregister);
updateSinkTypeList();
} }
void draw(void* ctx) { void draw(void* ctx) {
float menuWidth = ImGui::GetContentRegionAvail().x; float menuWidth = ImGui::GetContentRegionAvail().x;
auto lck = sigpath::streamManager.getStreamsLock(); auto lck = sigpath::streamManager.getStreamsLock();
auto streams = sigpath::streamManager.getStreams(); const auto& streams = sigpath::streamManager.getStreams();
int count = 0; int count = 0;
int maxCount = streams.size(); int maxCount = streams.size();
@ -26,35 +63,88 @@ namespace streamsmenu {
ImGui::Text("%s", name.c_str()); ImGui::Text("%s", name.c_str());
// Display ever sink // Display ever sink
if (ImGui::BeginTable(CONCAT("sdrpp_streams_tbl_", name), 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { if (ImGui::BeginTable(CONCAT("sdrpp_streams_tbl_", name), 1, ImGuiTableFlags_Borders)) {
auto lck2 = stream->getSinksLock(); auto lck2 = stream->getSinksLock();
auto sinks = stream->getSinks(); auto sinks = stream->getSinks();
for (auto& [id, sink] : sinks) { for (auto& [id, sink] : sinks) {
std::string sid = sink->getStringID();
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(0);
float tableWidth = ImGui::GetContentRegionAvail().x;
ImGui::Spacing();
// Sink type
sink->showMenu(); sink->showMenu();
float vol = sink->getVolume();
bool muted = sink->getMuted();
float pan = sink->getPanning();
bool linked = true;
// Volume slider + mute button float height = ImGui::GetTextLineHeightWithSpacing() + 2;
ImGui::PushID(ImGui::GetID(("sdrpp_streams_center_btn_" + sid).c_str()));
if (ImGui::ImageButton(icons::ALIGN_CENTER, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), 0, ImVec4(0, 0, 0, 0), ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
sink->setPanning(0.0f);
}
ImGui::PopID();
ImGui::SameLine();
ImGui::FillWidth(); ImGui::FillWidth();
if (ImGui::Button(CONCAT("Remove##sdrpp_streams_remove_type_", name))) { if (ImGui::SliderFloat(CONCAT("##sdrpp_streams_pan_", sid), &pan, -1, 1, "")) {
sink->setPanning(pan);
}
if (muted) {
ImGui::PushID(ImGui::GetID(("sdrpp_unmute_btn_" + sid).c_str()));
if (ImGui::ImageButton(icons::MUTED, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), 0, ImVec4(0, 0, 0, 0), ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
sink->setMuted(false);
}
ImGui::PopID();
}
else {
ImGui::PushID(ImGui::GetID(("sdrpp_mute_btn_" + sid).c_str()));
if (ImGui::ImageButton(icons::UNMUTED, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), 0, ImVec4(0, 0, 0, 0), ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
sink->setMuted(true);
}
ImGui::PopID();
}
ImGui::SameLine();
ImGui::FillWidth();
if (ImGui::SliderFloat(CONCAT("##sdrpp_streams_vol_", sid), &vol, 0, 1, "")) {
sink->setVolume(vol);
}
int startCur = ImGui::GetCursorPosX();
if (ImGui::Checkbox(CONCAT("Link volume##sdrpp_streams_vol_", sid), &linked)) {
// TODO
}
ImGui::SameLine();
if (ImGui::Button(CONCAT("Remove##sdrpp_streams_remove_type_", sid), ImVec2(tableWidth - (ImGui::GetCursorPosX() - startCur), 0))) {
sinksToBeRemoved.push_back(id); sinksToBeRemoved.push_back(id);
} }
ImGui::Spacing();
} }
lck2.unlock(); lck2.unlock();
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(0);
float tableWidth = ImGui::GetContentRegionAvail().x;
ImGui::Spacing();
int ssds = 0; int ssds = 0;
ImGui::Combo(CONCAT("##sdrpp_streams_add_type_", name), &ssds, "Test 1\0Test 2\0"); int startCur = ImGui::GetCursorPosX();
{
std::lock_guard<std::recursive_mutex> lck(sinkTypesMtx);
ImGui::Combo(CONCAT("##sdrpp_streams_add_type_", name), &ssds, sinkTypes.txt);
}
ImGui::SameLine(); ImGui::SameLine();
ImGui::FillWidth(); if (ImGui::Button(CONCAT("Add##sdrpp_streams_add_btn_", name), ImVec2(tableWidth - (ImGui::GetCursorPosX() - startCur), 0))) {
if (ImGui::Button(CONCAT("Add##sdrpp_streams_add_btn_", name))) {
stream->addSink("Audio"); stream->addSink("Audio");
} }
ImGui::Spacing();
ImGui::EndTable(); ImGui::EndTable();

View File

@ -1,11 +1,12 @@
#include "stream.h" #include "stream.h"
#include <utils/flog.h> #include <utils/flog.h>
Sink::Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id) : Sink::Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId) :
entry(entry), entry(entry),
stream(stream), stream(stream),
streamName(name), streamName(name),
id(id) id(id),
stringId(stringId)
{} {}
void Sink::showMenu() {} void Sink::showMenu() {}
@ -18,6 +19,12 @@ SinkEntry::SinkEntry(StreamManager* manager, AudioStream* parentStream, const st
this->type = type; this->type = type;
this->inputSamplerate = inputSamplerate; this->inputSamplerate = inputSamplerate;
// Generate string ID
stringId = parentStream->getName();
char buf[16];
sprintf(buf, "%d", (int)id);
stringId += buf;
// Initialize DSP // Initialize DSP
resamp.init(&input, inputSamplerate, inputSamplerate); resamp.init(&input, inputSamplerate, inputSamplerate);
volumeAdjust.init(&resamp.out, 1.0f, false); volumeAdjust.init(&resamp.out, 1.0f, false);
@ -44,7 +51,7 @@ void SinkEntry::setType(const std::string& type) {
auto lck2 = manager->getSinkTypesLock(); auto lck2 = manager->getSinkTypesLock();
// Get the provider or throw error // Get the provider or throw error
auto types = manager->getSinkTypes(); const auto& types = manager->getSinkTypes();
if (std::find(types.begin(), types.end(), type) == types.end()) { if (std::find(types.begin(), types.end(), type) == types.end()) {
this->type.clear(); this->type.clear();
throw SinkEntryCreateException("Invalid sink type"); throw SinkEntryCreateException("Invalid sink type");
@ -53,7 +60,7 @@ void SinkEntry::setType(const std::string& type) {
// Create sink // Create sink
this->type = type; this->type = type;
provider = manager->providers[type]; provider = manager->providers[type];
sink = provider->createSink(this, &volumeAdjust.out, parentStream->getName(), id); sink = provider->createSink(this, &volumeAdjust.out, parentStream->getName(), id, stringId);
} }
SinkID SinkEntry::getID() const { SinkID SinkEntry::getID() const {
@ -111,6 +118,15 @@ void SinkEntry::stopSink() {
sink->stop(); sink->stop();
} }
std::lock_guard<std::recursive_mutex> SinkEntry::getLock() {
return std::lock_guard<std::recursive_mutex>(mtx);
}
void SinkEntry::setSamplerate(double samplerate) {
std::lock_guard<std::recursive_mutex> lck(mtx);
resamp.setOutSamplerate(samplerate);
}
void SinkEntry::startDSP() { void SinkEntry::startDSP() {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
resamp.start(); resamp.start();
@ -136,6 +152,10 @@ void SinkEntry::setInputSamplerate(double samplerate) {
resamp.setInSamplerate(samplerate); resamp.setInSamplerate(samplerate);
} }
std::string SinkEntry::getStringID() {
return stringId;
}
AudioStream::AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) : AudioStream::AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) :
manager(manager), manager(manager),
name(name) name(name)
@ -449,7 +469,7 @@ void StreamManager::unregisterSinkProvider(SinkProvider* provider) {
for (auto& [name, stream] : streams) { for (auto& [name, stream] : streams) {
// Aquire lock on sink list // Aquire lock on sink list
auto sLock = stream->getSinksLock(); auto sLock = stream->getSinksLock();
auto sinks = stream->getSinks(); const auto& sinks = stream->getSinks();
// Find all sinks with the type that is about to be removed // Find all sinks with the type that is about to be removed
std::vector<SinkID> toRemove; std::vector<SinkID> toRemove;

View File

@ -19,7 +19,7 @@ class SinkEntry;
class Sink { class Sink {
public: public:
Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id); Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId);
virtual ~Sink() {} virtual ~Sink() {}
virtual void start() = 0; virtual void start() = 0;
@ -31,6 +31,7 @@ protected:
dsp::stream<dsp::stereo_t>* const stream; dsp::stream<dsp::stereo_t>* const stream;
const std::string streamName; const std::string streamName;
const SinkID id; const SinkID id;
const std::string stringId;
}; };
class SinkProvider { class SinkProvider {
@ -41,7 +42,7 @@ public:
* @param name Name of the audio stream. * @param name Name of the audio stream.
* @param index Index of the sink in the menu. Should be use to keep settings. * @param index Index of the sink in the menu. Should be use to keep settings.
*/ */
virtual std::unique_ptr<Sink> createSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID index) = 0; virtual std::unique_ptr<Sink> createSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId) = 0;
/** /**
* Destroy a sink instance. This function is so that the provide knows at all times how many instances there are. * Destroy a sink instance. This function is so that the provide knows at all times how many instances there are.
@ -62,6 +63,7 @@ class StreamManager;
// TODO: Would be cool to have data and audio sinks instead of just audio. // TODO: Would be cool to have data and audio sinks instead of just audio.
class SinkEntry { class SinkEntry {
friend AudioStream; friend AudioStream;
friend Sink;
public: public:
SinkEntry(StreamManager* manager, AudioStream* parentStream, const std::string& type, SinkID id, double inputSamplerate); SinkEntry(StreamManager* manager, AudioStream* parentStream, const std::string& type, SinkID id, double inputSamplerate);
@ -124,6 +126,12 @@ public:
*/ */
void showMenu(); void showMenu();
/**
* Get the string form ID unique to both the sink and stream. Be used to reference settings.
* @return Unique string ID.
*/
std::string getStringID();
// Emitted when the type of the sink was changed // Emitted when the type of the sink was changed
NewEvent<const std::string&> onTypeChanged; NewEvent<const std::string&> onTypeChanged;
// Emmited when volume of the sink was changed // Emmited when volume of the sink was changed
@ -133,11 +141,17 @@ public:
// Emitted when the panning of the sink was changed // Emitted when the panning of the sink was changed
NewEvent<float> onPanningChanged; NewEvent<float> onPanningChanged;
// TODO: Need to allow the sink to change the entry samplerate and start/stop the DSP
// This will also require allowing it to get a lock on the sink so others don't attempt to mess with it.
std::lock_guard<std::recursive_mutex> getLock();
void startDSP();
void stopDSP();
void setSamplerate(double samplerate);
private: private:
void startSink(); void startSink();
void stopSink(); void stopSink();
void startDSP();
void stopDSP();
void destroy(bool forgetSettings); void destroy(bool forgetSettings);
void setInputSamplerate(double samplerate); void setInputSamplerate(double samplerate);
@ -154,6 +168,8 @@ private:
AudioStream* const parentStream; AudioStream* const parentStream;
StreamManager* const manager; StreamManager* const manager;
std::string stringId;
float volume = 1.0f; float volume = 1.0f;
bool muted = false; bool muted = false;
float panning = 0.0f; float panning = 0.0f;

View File

@ -446,6 +446,9 @@ private:
afChain.disableAllBlocks([=](dsp::stream<dsp::stereo_t>* out){ stream->setInput(out); }); afChain.disableAllBlocks([=](dsp::stream<dsp::stereo_t>* out){ stream->setInput(out); });
} }
// Update audo samplerate
stream->setSamplerate(selectedDemod->getAFSampleRate());
// Start the IF chain // Start the IF chain
ifChain.start(); ifChain.start();

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -2,10 +2,10 @@
#include <module.h> #include <module.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <signal_path/signal_path.h> #include <signal_path/signal_path.h>
#include <signal_path/sink.h>
#include <dsp/buffer/packer.h> #include <dsp/buffer/packer.h>
#include <dsp/convert/stereo_to_mono.h> #include <dsp/convert/stereo_to_mono.h>
#include <utils/flog.h> #include <utils/flog.h>
#include <utils/optionlist.h>
#include <RtAudio.h> #include <RtAudio.h>
#include <config.h> #include <config.h>
#include <core.h> #include <core.h>
@ -22,26 +22,37 @@ SDRPP_MOD_INFO{
ConfigManager config; ConfigManager config;
class AudioSink : SinkManager::Sink { bool operator==(const RtAudio::DeviceInfo& a, const RtAudio::DeviceInfo& b) {
public: return a.name == b.name;
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.acquire();
if (!config.conf.contains(_streamName)) {
created = true;
config.conf[_streamName]["device"] = "";
config.conf[_streamName]["devices"] = json({});
} }
device = config.conf[_streamName]["device"];
config.release(created);
class AudioSink : public Sink {
public:
AudioSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId) :
Sink(entry, stream, name, id, stringId)
{
s2m.init(stream);
monoPacker.init(&s2m.out, 512);
stereoPacker.init(stream, 512);
// Convert config to the new format and get selected device
bool modified = false;
std::string device = "";
// config.acquire();
// if (config.conf.contains(streamName)) {
// if (!config.conf[streamName].is_array()) {
// json tmp = config.conf[streamName];
// config.conf[streamName] = json::array();
// config.conf[streamName][0] = tmp;
// modified = true;
// }
// if (config.conf[streamName].contains((int)id)) {
// device = config.conf[streamName][(int)id]["device"];
// }
// }
// config.release(modified);
// List devices
int count = audio.getDeviceCount(); int count = audio.getDeviceCount();
RtAudio::DeviceInfo info; RtAudio::DeviceInfo info;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -50,15 +61,13 @@ public:
if (!info.probed) { continue; } if (!info.probed) { continue; }
if (info.outputChannels == 0) { continue; } if (info.outputChannels == 0) { continue; }
if (info.isDefaultOutput) { defaultDevId = devList.size(); } if (info.isDefaultOutput) { defaultDevId = devList.size(); }
devList.push_back(info); devList.define(i, info.name, info);
deviceIds.push_back(i);
txtDevList += info.name;
txtDevList += '\0';
} }
catch (std::exception e) { catch (std::exception e) {
flog::error("AudioSinkModule Error getting audio device info: {0}", e.what()); flog::error("AudioSinkModule Error getting audio device info: {0}", e.what());
} }
} }
selectByName(device); selectByName(device);
} }
@ -92,79 +101,104 @@ public:
} }
void selectById(int id) { void selectById(int id) {
// Update ID
devId = id; devId = id;
bool created = false; selectedDevName = devList[id].name;
config.acquire();
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; // List samplerates and select default SR
sampleRatesTxt = "";
char buf[256]; char buf[256];
bool found = false; sampleRates.clear();
unsigned int defaultId = 0; const auto& srList = devList[id].sampleRates;
unsigned int defaultSr = devList[id].preferredSampleRate; unsigned int defaultSr = devList[id].preferredSampleRate;
for (int i = 0; i < sampleRates.size(); i++) { for (auto& sr : srList) {
if (sampleRates[i] == sampleRate) { if (sr == defaultSr) {
found = true; srId = sampleRates.size();
srId = i; sampleRate = sr;
} }
if (sampleRates[i] == defaultSr) { sprintf(buf, "%d", sr);
defaultId = i; sampleRates.define(sr, buf, sr);
}
sprintf(buf, "%d", sampleRates[i]);
sampleRatesTxt += buf;
sampleRatesTxt += '\0';
}
if (!found) {
sampleRate = defaultSr;
srId = defaultId;
} }
_stream->setSampleRate(sampleRate); // // Load config
// config.acquire();
// if (config.conf[streamName][(int)id].contains(selectedDevName)) {
// unsigned int wantedSr = config.conf[streamName][id][selectedDevName];
// if (sampleRates.keyExists(wantedSr)) {
// srId = sampleRates.keyId(wantedSr);
// sampleRate = sampleRates[srId];
// }
// }
// config.release();
// Lock the sink
auto lck = entry->getLock();
// Stop the sink DSP
// TODO: Only if the sink DSP is running, otherwise you risk starting it when it shouldn't
entry->stopDSP();
// Stop the sink
if (running) { doStop(); } if (running) { doStop(); }
// Update stream samplerate
entry->setSamplerate(sampleRate);
// Start the DSP
entry->startDSP();
// Start the sink
if (running) { doStart(); } if (running) { doStart(); }
} }
void menuHandler() { void showMenu() {
float menuWidth = ImGui::GetContentRegionAvail().x; float menuWidth = ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(menuWidth); ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(("##_audio_sink_dev_" + _streamName).c_str(), &devId, txtDevList.c_str())) { if (ImGui::Combo(("##_audio_sink_dev_" + stringId).c_str(), &devId, devList.txt)) {
selectById(devId); selectById(devId);
config.acquire(); // config.acquire();
config.conf[_streamName]["device"] = devList[devId].name; // config.conf[streamName]["device"] = devList[devId].name;
config.release(true); // config.release(true);
} }
ImGui::SetNextItemWidth(menuWidth); ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(("##_audio_sink_sr_" + _streamName).c_str(), &srId, sampleRatesTxt.c_str())) { if (ImGui::Combo(("##_audio_sink_sr_" + stringId).c_str(), &srId, sampleRates.txt)) {
sampleRate = sampleRates[srId]; sampleRate = sampleRates[srId];
_stream->setSampleRate(sampleRate);
if (running) { // Lock the sink
doStop(); auto lck = entry->getLock();
doStart();
} // Stop the sink DSP
config.acquire(); // TODO: Only if the sink DSP is running, otherwise you risk starting it when it shouldn't
config.conf[_streamName]["devices"][devList[devId].name] = sampleRate; entry->stopDSP();
config.release(true);
// Stop the sink
if (running) { doStop(); }
// Update stream samplerate
entry->setSamplerate(sampleRate);
// Start the DSP
entry->startDSP();
// Start the sink
if (running) { doStart(); }
// config.acquire();
// config.conf[streamName]["devices"][devList[devId].name] = sampleRate;
// config.release(true);
} }
} }
private: private:
bool doStart() { bool doStart() {
RtAudio::StreamParameters parameters; RtAudio::StreamParameters parameters;
parameters.deviceId = deviceIds[devId]; parameters.deviceId = devList.key(devId);
parameters.nChannels = 2; parameters.nChannels = 2;
unsigned int bufferFrames = sampleRate / 60; unsigned int bufferFrames = sampleRate / 60;
RtAudio::StreamOptions opts; RtAudio::StreamOptions opts;
opts.flags = RTAUDIO_MINIMIZE_LATENCY; opts.flags = RTAUDIO_MINIMIZE_LATENCY;
opts.streamName = _streamName; opts.streamName = streamName;
try { try {
audio.openStream(&parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &callback, this, &opts); audio.openStream(&parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &callback, this, &opts);
@ -198,57 +232,39 @@ private:
int count = _this->stereoPacker.out.read(); int count = _this->stereoPacker.out.read();
if (count < 0) { return 0; } if (count < 0) { return 0; }
// For debug purposes only...
// if (nBufferFrames != count) { flog::warn("Buffer size mismatch, wanted {0}, was asked for {1}", count, nBufferFrames); }
// for (int i = 0; i < count; i++) {
// if (_this->stereoPacker.out.readBuf[i].l == NAN || _this->stereoPacker.out.readBuf[i].r == NAN) { flog::error("NAN in audio data"); }
// if (_this->stereoPacker.out.readBuf[i].l == INFINITY || _this->stereoPacker.out.readBuf[i].r == INFINITY) { flog::error("INFINITY in audio data"); }
// if (_this->stereoPacker.out.readBuf[i].l == -INFINITY || _this->stereoPacker.out.readBuf[i].r == -INFINITY) { flog::error("-INFINITY in audio data"); }
// }
memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t)); memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t));
_this->stereoPacker.out.flush(); _this->stereoPacker.out.flush();
return 0; return 0;
} }
SinkManager::Stream* _stream;
dsp::convert::StereoToMono s2m; dsp::convert::StereoToMono s2m;
dsp::buffer::Packer<float> monoPacker; dsp::buffer::Packer<float> monoPacker;
dsp::buffer::Packer<dsp::stereo_t> stereoPacker; dsp::buffer::Packer<dsp::stereo_t> stereoPacker;
std::string _streamName;
int srId = 0; int srId = 0;
int devCount;
int devId = 0; int devId = 0;
bool running = false; bool running = false;
std::string selectedDevName;
unsigned int defaultDevId = 0; unsigned int defaultDevId = 0;
std::vector<RtAudio::DeviceInfo> devList; OptionList<unsigned int, RtAudio::DeviceInfo> devList;
std::vector<unsigned int> deviceIds; OptionList<unsigned int, unsigned int> sampleRates;
std::string txtDevList;
std::vector<unsigned int> sampleRates;
std::string sampleRatesTxt;
unsigned int sampleRate = 48000; unsigned int sampleRate = 48000;
RtAudio audio; RtAudio audio;
}; };
class AudioSinkModule : public ModuleManager::Instance { class AudioSinkModule : public ModuleManager::Instance, public SinkProvider {
public: public:
AudioSinkModule(std::string name) { AudioSinkModule(std::string name) {
this->name = name; this->name = name;
provider.create = create_sink; sigpath::streamManager.registerSinkProvider("Audio", this);
provider.ctx = this;
sigpath::sinkManager.registerSinkProvider("Audio", provider);
} }
~AudioSinkModule() { ~AudioSinkModule() {
// Unregister sink, this will automatically stop and delete all instances of the audio sink sigpath::streamManager.unregisterSinkProvider(this);
sigpath::sinkManager.unregisterSinkProvider("Audio");
} }
void postInit() {} void postInit() {}
@ -265,14 +281,13 @@ public:
return enabled; return enabled;
} }
private: std::unique_ptr<Sink> createSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId) {
static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) { return std::make_unique<AudioSink>(entry, stream, name, id, stringId);
return (SinkManager::Sink*)(new AudioSink(stream, streamName));
} }
private:
std::string name; std::string name;
bool enabled = true; bool enabled = true;
SinkManager::SinkProvider provider;
}; };
MOD_EXPORT void _INIT_() { MOD_EXPORT void _INIT_() {