Merge branch 'dev_routing' of https://github.com/rerdavies/pipedal into dev_tone3000
This commit is contained in:
+237
-95
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
#include "PiPedalCommon.hpp"
|
||||
#include "util.hpp"
|
||||
#include <cmath>
|
||||
#include "Finally.hpp"
|
||||
@@ -39,6 +40,7 @@
|
||||
#include "CrashGuard.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
#include "CpuUse.hpp"
|
||||
|
||||
@@ -328,11 +330,29 @@ namespace pipedal
|
||||
bool inputSwapped = false;
|
||||
bool outputSwapped = false;
|
||||
|
||||
std::vector<float *> activeCaptureBuffers;
|
||||
std::vector<float *> activePlaybackBuffers;
|
||||
std::vector<std::vector<float>> allocatedBuffers;
|
||||
|
||||
std::vector<float *> captureBuffers;
|
||||
std::vector<float *> playbackBuffers;
|
||||
std::set<int64_t> usedOutputChannels;
|
||||
std::vector<float*> deviceCaptureBuffers;
|
||||
std::vector<float*> devicePlaybackBuffers;
|
||||
float *zeroInputBuffer = nullptr;
|
||||
float *discardOutputBuffer = nullptr;
|
||||
std::vector<float *> mainCaptureBuffers;
|
||||
std::vector<float *> mainPlaybackBuffers;
|
||||
|
||||
std::vector<float *> auxCaptureBuffers;
|
||||
std::vector<float *> auxPlaybackBuffers;
|
||||
|
||||
std::vector<float *> sendCaptureBuffers;
|
||||
std::vector<float *> sendPlaybackBuffers;
|
||||
|
||||
struct OutputBufferMix {
|
||||
size_t outputChannel = 0;
|
||||
std::vector<float*> inputBuffers;
|
||||
float *outputBuffer = nullptr;
|
||||
};
|
||||
|
||||
std::vector<OutputBufferMix> outputBufferMixes;
|
||||
|
||||
std::vector<uint8_t> rawCaptureBuffer;
|
||||
std::vector<uint8_t> rawPlaybackBuffer;
|
||||
@@ -742,7 +762,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = getCaptureBuffer<int32_t>(rawCaptureBuffer);
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
{
|
||||
@@ -760,7 +780,7 @@ namespace pipedal
|
||||
{
|
||||
float *p = getCaptureBuffer<float>(rawCaptureBuffer);
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
{
|
||||
@@ -776,7 +796,7 @@ namespace pipedal
|
||||
{
|
||||
int16_t *p = getCaptureBuffer<int16_t>(rawCaptureBuffer);
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr double scale = 1.0f / (std::numeric_limits<int16_t>::max() + 1L);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -792,7 +812,7 @@ namespace pipedal
|
||||
{
|
||||
int16_t *p = getCaptureBuffer<int16_t>(rawCaptureBuffer);
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (std::numeric_limits<int16_t>::max() + 1L);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -809,7 +829,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = getCaptureBuffer<int32_t>(rawCaptureBuffer);
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1L);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -825,7 +845,7 @@ namespace pipedal
|
||||
{
|
||||
uint8_t *p = getCaptureBuffer<uint8_t>(rawCaptureBuffer);
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1LL);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -842,7 +862,7 @@ namespace pipedal
|
||||
{
|
||||
uint8_t *p = (uint8_t *)rawCaptureBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1LL);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -859,7 +879,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (0x00FFFFFFL + 1L);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -875,7 +895,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (0x00FFFFFFL + 1L);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -891,7 +911,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->captureBuffers;
|
||||
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||
int channels = this->captureChannels;
|
||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1L);
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -907,7 +927,7 @@ namespace pipedal
|
||||
{
|
||||
int16_t *p = (int16_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr float scale = std::numeric_limits<int16_t>::max();
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -927,7 +947,7 @@ namespace pipedal
|
||||
{
|
||||
int16_t *p = (int16_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr float scale = std::numeric_limits<int16_t>::max();
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -947,7 +967,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -969,7 +989,7 @@ namespace pipedal
|
||||
|
||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = 0x00FFFFFF;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -991,7 +1011,7 @@ namespace pipedal
|
||||
|
||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = 0x00FFFFFF;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -1011,7 +1031,7 @@ namespace pipedal
|
||||
{
|
||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -1031,7 +1051,7 @@ namespace pipedal
|
||||
{
|
||||
uint8_t *p = (uint8_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -1056,7 +1076,7 @@ namespace pipedal
|
||||
{
|
||||
uint8_t *p = (uint8_t *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
@@ -1082,7 +1102,7 @@ namespace pipedal
|
||||
{
|
||||
float *p = (float *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
{
|
||||
@@ -1097,7 +1117,7 @@ namespace pipedal
|
||||
{
|
||||
float *p = (float *)rawPlaybackBuffer.data();
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
{
|
||||
@@ -1127,9 +1147,9 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
|
||||
JackChannelSelection channelSelection;
|
||||
ChannelSelection channelSelection;
|
||||
bool open = false;
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||
{
|
||||
terminateAudio_ = false;
|
||||
if (open)
|
||||
@@ -1249,7 +1269,7 @@ namespace pipedal
|
||||
rawCaptureBuffer.resize(captureFrameSize * bufferSize * 2);
|
||||
memset(rawCaptureBuffer.data(), 0, rawCaptureBuffer.size());
|
||||
|
||||
AllocateBuffers(captureBuffers, captureChannels);
|
||||
AllocateBuffers(deviceCaptureBuffers, captureChannels);
|
||||
}
|
||||
|
||||
virtual std::string GetConfigurationDescription()
|
||||
@@ -1260,8 +1280,15 @@ namespace pipedal
|
||||
<< ", " << GetAlsaFormatDescription(this->captureFormat)
|
||||
<< ", " << this->sampleRate
|
||||
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
||||
<< ", in: " << this->InputBufferCount() << "/" << this->captureChannels
|
||||
<< ", out: " << this->OutputBufferCount() << "/" << this->playbackChannels);
|
||||
<< ", " << "device in: " << this->DeviceInputBufferCount()
|
||||
<< ", " << "device out: " << this->DeviceOutputBufferCount()
|
||||
<< ", main in: " << this->MainInputBufferCount()
|
||||
<< ", main out: " << this->MainOutputBufferCount()
|
||||
<< ", aux in: " << this->AuxInputBufferCount()
|
||||
<< ", aux out: " << this->AuxOutputBufferCount()
|
||||
<< ", send in: " << this->SendInputBufferCount()
|
||||
<< ", send out: " << this->SendOutputBufferCount()
|
||||
);
|
||||
return result;
|
||||
}
|
||||
void PreparePlaybackFunctions(snd_pcm_format_t playbackFormat)
|
||||
@@ -1321,10 +1348,10 @@ namespace pipedal
|
||||
rawPlaybackBuffer.resize(playbackFrameSize * bufferSize);
|
||||
memset(rawPlaybackBuffer.data(), 0, playbackFrameSize * bufferSize);
|
||||
|
||||
AllocateBuffers(playbackBuffers, playbackChannels);
|
||||
AllocateBuffers(devicePlaybackBuffers, playbackChannels);
|
||||
}
|
||||
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||
{
|
||||
std::lock_guard lock{restartMutex};
|
||||
|
||||
@@ -1717,7 +1744,7 @@ namespace pipedal
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void AudioThread()
|
||||
PIPEDAL_NON_INLINE void AudioThread()
|
||||
{
|
||||
SetThreadName("alsaDriver");
|
||||
|
||||
@@ -1797,6 +1824,19 @@ namespace pipedal
|
||||
|
||||
cpuUse.AddSample(ProfileCategory::Execute);
|
||||
|
||||
// Perform any neccessary mixing of outputs.
|
||||
for (auto &bufferMix : outputBufferMixes)
|
||||
{
|
||||
float *outputBuffer = bufferMix.outputBuffer;
|
||||
for (auto &mixBuffer : bufferMix.inputBuffers)
|
||||
{
|
||||
for (size_t i = 0; i < framesRead; ++i)
|
||||
{
|
||||
outputBuffer[i] += mixBuffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(this->*copyOutputFn)(framesRead);
|
||||
cpuUse.AddSample(ProfileCategory::Driver);
|
||||
// process.
|
||||
@@ -1824,9 +1864,9 @@ namespace pipedal
|
||||
{
|
||||
this->driverHost->OnAlsaDriverStopped();
|
||||
// zero out input buffers.
|
||||
for (size_t i = 0; i < this->captureBuffers.size(); ++i)
|
||||
for (size_t i = 0; i < this->deviceCaptureBuffers.size(); ++i)
|
||||
{
|
||||
float *pBuffer = captureBuffers[i];
|
||||
float *pBuffer = deviceCaptureBuffers[i];
|
||||
for (size_t j = 0; j < this->bufferSize; ++j)
|
||||
{
|
||||
pBuffer[j] = 0;
|
||||
@@ -1836,7 +1876,7 @@ namespace pipedal
|
||||
{
|
||||
while (!terminateAudio())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
this->driverHost->OnProcess(this->bufferSize);
|
||||
}
|
||||
}
|
||||
@@ -1849,63 +1889,129 @@ namespace pipedal
|
||||
|
||||
bool alsaActive = false;
|
||||
|
||||
static int IndexFromPortName(const std::string &s)
|
||||
{
|
||||
auto pos = s.find_last_of('_');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
throw std::invalid_argument("Bad port name.");
|
||||
}
|
||||
const char *p = s.c_str() + (pos + 1);
|
||||
|
||||
int v = atoi(p);
|
||||
if (v < 0)
|
||||
PIPEDAL_NON_INLINE void AllocateInputChannels(
|
||||
const std::vector<int64_t> &channelSelection,
|
||||
std::vector<float*> &channelBuffers
|
||||
) {
|
||||
size_t nChannels = channelSelection.size();
|
||||
channelBuffers.resize(nChannels);
|
||||
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
throw std::invalid_argument("Bad port name.");
|
||||
int64_t deviceChannel = channelSelection[i];
|
||||
if (deviceChannel == -1 || deviceChannel >= captureChannels)
|
||||
{
|
||||
channelBuffers[i] = zeroInputBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
channelBuffers[i] = deviceCaptureBuffers[deviceChannel];
|
||||
}
|
||||
}
|
||||
}
|
||||
PIPEDAL_NON_INLINE void AddMixBuffer(int64_t outputChannel, float *mixBuffer)
|
||||
{
|
||||
for (auto&bufferMix: outputBufferMixes)
|
||||
{
|
||||
if (bufferMix.outputChannel == outputChannel)
|
||||
{
|
||||
bufferMix.inputBuffers.push_back(mixBuffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
OutputBufferMix newMix;
|
||||
newMix.outputChannel = outputChannel;
|
||||
newMix.outputBuffer = devicePlaybackBuffers[outputChannel];
|
||||
newMix.inputBuffers.push_back(mixBuffer);
|
||||
outputBufferMixes.push_back(std::move(newMix));
|
||||
}
|
||||
PIPEDAL_NON_INLINE void AllocateOutputChannels(
|
||||
const std::vector<int64_t> &channelSelection,
|
||||
std::vector<float*> &channelBuffers
|
||||
) {
|
||||
size_t nChannels = channelSelection.size();
|
||||
|
||||
channelBuffers.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
int64_t deviceChannel = channelSelection[i];
|
||||
if (deviceChannel == -1 || deviceChannel >= playbackChannels)
|
||||
{
|
||||
if (discardOutputBuffer == nullptr)
|
||||
{
|
||||
discardOutputBuffer = AllocateAudioBuffer();
|
||||
}
|
||||
channelBuffers[i] = discardOutputBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!usedOutputChannels.contains(deviceChannel))
|
||||
{
|
||||
usedOutputChannels.insert(deviceChannel);
|
||||
channelBuffers[i] = deviceCaptureBuffers[deviceChannel];
|
||||
|
||||
} else {
|
||||
// Used once already. We need to mix down.
|
||||
float *mixBuffer = AllocateAudioBuffer();
|
||||
channelBuffers[i] = mixBuffer;
|
||||
AddMixBuffer(deviceChannel, mixBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool activated = false;
|
||||
virtual void Activate()
|
||||
PIPEDAL_NON_INLINE virtual void Activate()
|
||||
{
|
||||
if (activated)
|
||||
{
|
||||
throw PiPedalStateException("Already activated.");
|
||||
}
|
||||
|
||||
activated = true;
|
||||
|
||||
this->activeCaptureBuffers.resize(channelSelection.GetInputAudioPorts().size());
|
||||
// Reset previously allocated buffers.
|
||||
usedOutputChannels.clear();
|
||||
allocatedBuffers.resize(0);
|
||||
|
||||
int ix = 0;
|
||||
for (auto &x : channelSelection.GetInputAudioPorts())
|
||||
|
||||
// Allocate device capture buffers.
|
||||
zeroInputBuffer = AllocateAudioBuffer();
|
||||
deviceCaptureBuffers.resize(captureChannels);
|
||||
for (size_t i = 0; i < captureChannels; ++i)
|
||||
{
|
||||
int sourceIndex = IndexFromPortName(x);
|
||||
if (sourceIndex >= captureBuffers.size())
|
||||
{
|
||||
Lv2Log::error(SS("Invalid audio input port: " << x));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->activeCaptureBuffers[ix++] = this->captureBuffers[sourceIndex];
|
||||
}
|
||||
deviceCaptureBuffers[i] = AllocateAudioBuffer();
|
||||
}
|
||||
devicePlaybackBuffers.resize(playbackChannels);
|
||||
for (size_t i = 0; i < playbackChannels; ++i)
|
||||
{
|
||||
devicePlaybackBuffers[i] = AllocateAudioBuffer();
|
||||
}
|
||||
|
||||
this->activePlaybackBuffers.resize(channelSelection.GetOutputAudioPorts().size());
|
||||
|
||||
ix = 0;
|
||||
for (auto &x : channelSelection.GetOutputAudioPorts())
|
||||
{
|
||||
int sourceIndex = IndexFromPortName(x);
|
||||
if (sourceIndex >= playbackBuffers.size())
|
||||
{
|
||||
Lv2Log::error(SS("Invalid audio output port: " << x));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->activePlaybackBuffers[ix++] = this->playbackBuffers[sourceIndex];
|
||||
}
|
||||
}
|
||||
AllocateInputChannels(
|
||||
channelSelection.mainInputChannels(),
|
||||
this->mainCaptureBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.mainOutputChannels(),
|
||||
this->mainPlaybackBuffers
|
||||
);
|
||||
AllocateInputChannels(
|
||||
channelSelection.auxInputChannels(),
|
||||
this->auxCaptureBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.auxOutputChannels(),
|
||||
this->auxPlaybackBuffers
|
||||
);
|
||||
AllocateInputChannels(
|
||||
channelSelection.sendInputChannels(),
|
||||
this->sendCaptureBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.sendOutputChannels(),
|
||||
this->sendPlaybackBuffers
|
||||
);
|
||||
|
||||
audioThread = std::make_unique<std::jthread>([this]()
|
||||
{ AudioThread(); });
|
||||
@@ -1941,10 +2047,43 @@ namespace pipedal
|
||||
this->alsaSequencer = alsaSequencer;
|
||||
}
|
||||
|
||||
virtual size_t InputBufferCount() const { return activeCaptureBuffers.size(); }
|
||||
virtual float *GetInputBuffer(size_t channel) override
|
||||
virtual size_t DeviceInputBufferCount() const override {
|
||||
return deviceCaptureBuffers.size();
|
||||
}
|
||||
virtual size_t DeviceOutputBufferCount() const override {
|
||||
return devicePlaybackBuffers.size();
|
||||
}
|
||||
|
||||
|
||||
virtual size_t MainInputBufferCount() const { return mainCaptureBuffers.size(); }
|
||||
virtual float *GetMainInputBuffer(size_t channel) override
|
||||
{
|
||||
return activeCaptureBuffers[channel];
|
||||
if (channel >= (int64_t)mainCaptureBuffers.size())
|
||||
{
|
||||
throw std::runtime_error("Argument out of range.");
|
||||
}
|
||||
return mainCaptureBuffers[channel];
|
||||
}
|
||||
virtual size_t AuxInputBufferCount() const { return auxCaptureBuffers.size(); }
|
||||
virtual float *GetAuxInputBuffer(size_t channel) override
|
||||
{
|
||||
return auxCaptureBuffers[channel];
|
||||
}
|
||||
virtual size_t AuxOutputBufferCount() const {
|
||||
return auxPlaybackBuffers.size(); }
|
||||
virtual float *GetAuxOutputBuffer(size_t channel) override
|
||||
{
|
||||
return auxPlaybackBuffers[channel];
|
||||
}
|
||||
virtual size_t SendInputBufferCount() const { return sendCaptureBuffers.size(); }
|
||||
virtual float *GetSendInputBuffer(size_t channel) override
|
||||
{
|
||||
return sendCaptureBuffers[channel];
|
||||
}
|
||||
virtual size_t SendOutputBufferCount() const { return sendPlaybackBuffers.size(); }
|
||||
virtual float *GetSendOutputBuffer(size_t channel) override
|
||||
{
|
||||
return sendPlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
virtual size_t GetMidiInputEventCount() override
|
||||
@@ -1956,27 +2095,30 @@ namespace pipedal
|
||||
return this->midiEvents.data();
|
||||
}
|
||||
|
||||
virtual size_t OutputBufferCount() const { return activePlaybackBuffers.size(); }
|
||||
virtual float *GetOutputBuffer(size_t channel) override
|
||||
virtual size_t MainOutputBufferCount() const { return mainPlaybackBuffers.size(); }
|
||||
virtual float *GetMainOutputBuffer(size_t channel) override
|
||||
{
|
||||
return activePlaybackBuffers[channel];
|
||||
return mainPlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
void FreeBuffers(std::vector<float *> &buffer)
|
||||
{
|
||||
for (size_t i = 0; i < buffer.size(); ++i)
|
||||
{
|
||||
delete[] buffer[i];
|
||||
buffer[i] = 0;
|
||||
}
|
||||
buffer.clear();
|
||||
float* AllocateAudioBuffer() {
|
||||
std::vector<float> buffer;
|
||||
buffer.resize(this->bufferSize);
|
||||
float*pBuffer = buffer.data();
|
||||
allocatedBuffers.push_back(std::move(buffer));
|
||||
return pBuffer;
|
||||
}
|
||||
void DeleteBuffers()
|
||||
{
|
||||
activeCaptureBuffers.clear();
|
||||
activePlaybackBuffers.clear();
|
||||
FreeBuffers(this->playbackBuffers);
|
||||
FreeBuffers(this->captureBuffers);
|
||||
mainCaptureBuffers.clear();
|
||||
mainPlaybackBuffers.clear();
|
||||
auxCaptureBuffers.clear();
|
||||
auxPlaybackBuffers.clear();
|
||||
sendCaptureBuffers.clear();
|
||||
sendPlaybackBuffers.clear();
|
||||
zeroInputBuffer = nullptr;
|
||||
discardOutputBuffer = nullptr;
|
||||
allocatedBuffers.clear();
|
||||
}
|
||||
virtual void Close()
|
||||
{
|
||||
@@ -2241,7 +2383,7 @@ namespace pipedal
|
||||
{
|
||||
value += 1.0f * (c) / (128.0 * 256.0 * 256.0);
|
||||
}
|
||||
this->playbackBuffers[c][i] = value;
|
||||
this->devicePlaybackBuffers[c][i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2257,7 +2399,7 @@ namespace pipedal
|
||||
for (size_t c = 0; c < captureChannels; ++c)
|
||||
{
|
||||
float error =
|
||||
this->captureBuffers[c][i] - this->playbackBuffers[c][i];
|
||||
this->deviceCaptureBuffers[c][i] - this->devicePlaybackBuffers[c][i];
|
||||
|
||||
assert(std::abs(error) < 4e-5);
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2025 Robin E. R. Davies
|
||||
* Copyright (c) 2026 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <lilv/lilv.h>
|
||||
#include "AudioDriver.hpp"
|
||||
#include "JackServerSettings.hpp"
|
||||
|
||||
|
||||
+113
-98
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2022 Robin E. R. Davies
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
@@ -27,28 +27,34 @@
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "AlsaDriver.hpp"
|
||||
#include "JackDriver.hpp"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
|
||||
|
||||
class AlsaTester: private AudioDriverHost {
|
||||
class AlsaTester : private AudioDriverHost
|
||||
{
|
||||
public:
|
||||
enum class TestType { Oscillator, LatencyMonitor, NullTest};
|
||||
enum class TestType
|
||||
{
|
||||
Oscillator,
|
||||
LatencyMonitor,
|
||||
NullTest
|
||||
};
|
||||
|
||||
private:
|
||||
AudioDriver *audioDriver = nullptr;
|
||||
TestType testType;
|
||||
|
||||
public:
|
||||
AlsaTester(TestType testType)
|
||||
: testType(testType)
|
||||
: testType(testType)
|
||||
{
|
||||
// audioDriver = CreateAlsaDriver(this);
|
||||
// audioDriver = CreateJackDriver(this);
|
||||
|
||||
}
|
||||
~AlsaTester()
|
||||
{
|
||||
@@ -57,47 +63,47 @@ public:
|
||||
delete[] outputBuffers;
|
||||
}
|
||||
|
||||
bool useJack = false;
|
||||
void initializeChannelSelection(JackConfiguration &jackConfiguration, ChannelSelection &channelSelection)
|
||||
{
|
||||
size_t nInputs = std::max(jackConfiguration.inputAudioPorts().size(),size_t(2));
|
||||
size_t nOutputs = std::max(jackConfiguration.outputAudioPorts().size(),size_t(2));
|
||||
auto &inputSelections = channelSelection.mainInputChannels();
|
||||
auto &outputSelections = channelSelection.mainOutputChannels();
|
||||
inputSelections.resize(nInputs);
|
||||
for (size_t i = 0; i < nInputs; ++i)
|
||||
{
|
||||
inputSelections[i] = i;
|
||||
}
|
||||
outputSelections.resize(nOutputs);
|
||||
for (size_t i = 0; i < nOutputs; ++i)
|
||||
{
|
||||
outputSelections[i] = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Test()
|
||||
{
|
||||
|
||||
AlsaFormatEncodeDecodeTest(this);
|
||||
|
||||
JackServerSettings serverSettings("hw:M2","hw:M2",48000,32,3);
|
||||
JackServerSettings serverSettings("hw:M2", "hw:M2", 48000, 32, 3);
|
||||
|
||||
JackConfiguration jackConfiguration;
|
||||
if (useJack)
|
||||
{
|
||||
jackConfiguration.JackInitialize();
|
||||
} else {
|
||||
jackConfiguration.AlsaInitialize(serverSettings);
|
||||
}
|
||||
jackConfiguration.AlsaInitialize(serverSettings);
|
||||
|
||||
JackChannelSelection channelSelection(
|
||||
jackConfiguration.inputAudioPorts(),
|
||||
jackConfiguration.outputAudioPorts(),
|
||||
jackConfiguration.inputMidiDevices());
|
||||
ChannelSelection channelSelection;
|
||||
|
||||
#if JACK_HOST
|
||||
if (useJack)
|
||||
{
|
||||
audioDriver = CreateJackDriver(this);
|
||||
|
||||
} else {
|
||||
audioDriver = CreateAlsaDriver(this);
|
||||
}
|
||||
#else
|
||||
audioDriver = CreateAlsaDriver(this);
|
||||
#endif
|
||||
|
||||
initializeChannelSelection(jackConfiguration, channelSelection);
|
||||
|
||||
oscillator.Init(440,jackConfiguration.sampleRate());
|
||||
oscillator.Init(440, jackConfiguration.sampleRate());
|
||||
latencyMonitor.Init(jackConfiguration.sampleRate());
|
||||
audioDriver->Open(serverSettings,channelSelection);
|
||||
audioDriver->Open(serverSettings, channelSelection);
|
||||
|
||||
inputBuffers = new float*[channelSelection.GetInputAudioPorts().size()];
|
||||
outputBuffers = new float*[channelSelection.GetOutputAudioPorts().size()];
|
||||
inputBuffers = new float *[channelSelection.mainInputChannels().size()];
|
||||
outputBuffers = new float *[channelSelection.mainOutputChannels().size()];
|
||||
|
||||
audioDriver->Activate();
|
||||
|
||||
@@ -107,7 +113,7 @@ public:
|
||||
if (testType == TestType::LatencyMonitor)
|
||||
{
|
||||
auto latency = this->latencyMonitor.GetLatency();
|
||||
double ms = 1000.0*latency/jackConfiguration.sampleRate();
|
||||
double ms = 1000.0 * latency / jackConfiguration.sampleRate();
|
||||
|
||||
cout << "Latency: " << latency << " samples " << ms << "ms" << " xruns: " << GetXruns() << " Cpu: " << audioDriver->CpuUse() << "%" << endl;
|
||||
}
|
||||
@@ -115,37 +121,40 @@ public:
|
||||
|
||||
audioDriver->Deactivate();
|
||||
audioDriver->Close();
|
||||
|
||||
}
|
||||
|
||||
float**inputBuffers = nullptr;
|
||||
float**outputBuffers = nullptr;
|
||||
float **inputBuffers = nullptr;
|
||||
float **outputBuffers = nullptr;
|
||||
|
||||
class Oscillator {
|
||||
class Oscillator
|
||||
{
|
||||
private:
|
||||
double dx = 0;
|
||||
double x = 0;
|
||||
double dx2 = 0;
|
||||
double x2 = 0;
|
||||
public:
|
||||
|
||||
public:
|
||||
void Init(float frequency, size_t sampleRate)
|
||||
{
|
||||
dx = frequency*3.141592736*2/sampleRate;
|
||||
dx2 = 0.5*3.141592736*2/sampleRate;
|
||||
dx = frequency * 3.141592736 * 2 / sampleRate;
|
||||
dx2 = 0.5 * 3.141592736 * 2 / sampleRate;
|
||||
}
|
||||
|
||||
float Next() {
|
||||
float Next()
|
||||
{
|
||||
float result = (float)std::cos(x);
|
||||
float env = (float)std::cos(x2);
|
||||
x += dx;
|
||||
x2 += dx2;
|
||||
return result*env;
|
||||
return result * env;
|
||||
}
|
||||
};
|
||||
|
||||
class LatencyMonitor {
|
||||
enum class State {
|
||||
class LatencyMonitor
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
Idle,
|
||||
Waiting,
|
||||
};
|
||||
@@ -156,91 +165,99 @@ public:
|
||||
size_t current_latency = 0;
|
||||
size_t latency = 0;
|
||||
std::mutex sync;
|
||||
|
||||
public:
|
||||
void Init(uint64_t sampleRate)
|
||||
{
|
||||
idle_samples = (uint64_t)sampleRate*2;
|
||||
waiting_samples = (uint64_t)sampleRate*2;
|
||||
idle_samples = (uint64_t)sampleRate * 2;
|
||||
waiting_samples = (uint64_t)sampleRate * 2;
|
||||
|
||||
state = State::Idle;
|
||||
t = idle_samples;
|
||||
latency = 0;
|
||||
}
|
||||
|
||||
size_t GetLatency() {
|
||||
std::lock_guard lock { sync};
|
||||
size_t GetLatency()
|
||||
{
|
||||
std::lock_guard lock{sync};
|
||||
return latency;
|
||||
}
|
||||
float Next(float input)
|
||||
{
|
||||
switch(state)
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
case State::Idle:
|
||||
default:
|
||||
case State::Idle:
|
||||
{
|
||||
if (t-- == 0)
|
||||
{
|
||||
if (t-- == 0) {
|
||||
state = State::Waiting;
|
||||
current_latency = 0;
|
||||
}
|
||||
return 0.01;
|
||||
state = State::Waiting;
|
||||
current_latency = 0;
|
||||
}
|
||||
break;
|
||||
case State::Waiting:
|
||||
return 0.01;
|
||||
}
|
||||
break;
|
||||
case State::Waiting:
|
||||
{
|
||||
if (std::abs(input) > 0.1 || current_latency > 500)
|
||||
{
|
||||
if (std::abs(input) > 0.1 || current_latency > 500) {
|
||||
{
|
||||
std::lock_guard lock { sync};
|
||||
latency = current_latency;
|
||||
}
|
||||
state = State::Idle;
|
||||
t = idle_samples;
|
||||
} else {
|
||||
++current_latency;
|
||||
{
|
||||
std::lock_guard lock{sync};
|
||||
latency = current_latency;
|
||||
}
|
||||
return current_latency < 100 ? 0.25 : 0.0;
|
||||
state = State::Idle;
|
||||
t = idle_samples;
|
||||
}
|
||||
break;
|
||||
else
|
||||
{
|
||||
++current_latency;
|
||||
}
|
||||
return current_latency < 100 ? 0.25 : 0.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Oscillator oscillator;
|
||||
LatencyMonitor latencyMonitor;
|
||||
|
||||
virtual void OnAudioTerminated() {
|
||||
|
||||
virtual void OnAudioTerminated()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnAlsaDriverStopped() {
|
||||
virtual void OnAlsaDriverStopped()
|
||||
{
|
||||
}
|
||||
virtual void OnProcess(size_t nFrames) {
|
||||
if (testType == TestType::NullTest) return;
|
||||
virtual void OnProcess(size_t nFrames)
|
||||
{
|
||||
if (testType == TestType::NullTest)
|
||||
return;
|
||||
|
||||
|
||||
size_t inputs = audioDriver->InputBufferCount();
|
||||
size_t outputs = audioDriver->OutputBufferCount();
|
||||
size_t inputs = audioDriver->MainInputBufferCount();
|
||||
size_t outputs = audioDriver->MainOutputBufferCount();
|
||||
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
{
|
||||
inputBuffers[i] = audioDriver->GetInputBuffer(i);
|
||||
inputBuffers[i] = audioDriver->GetMainInputBuffer(i);
|
||||
}
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
{
|
||||
outputBuffers[i] = audioDriver->GetOutputBuffer(i);
|
||||
outputBuffers[i] = audioDriver->GetMainOutputBuffer(i);
|
||||
}
|
||||
if (this->testType == TestType::Oscillator)
|
||||
{
|
||||
for (size_t i = 0; i < nFrames; ++i)
|
||||
{
|
||||
float v = oscillator.Next()*0.25f;
|
||||
float v = oscillator.Next() * 0.25f;
|
||||
for (size_t c = 0; c < outputs; ++c)
|
||||
{
|
||||
outputBuffers[c][i] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < nFrames; ++i)
|
||||
{
|
||||
float v = latencyMonitor.Next(inputBuffers[0][i]);
|
||||
@@ -249,36 +266,34 @@ public:
|
||||
outputBuffers[c][i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
std::mutex sync;
|
||||
uint64_t xruns;
|
||||
|
||||
uint64_t GetXruns() {
|
||||
lock_guard lock { sync};
|
||||
uint64_t GetXruns()
|
||||
{
|
||||
lock_guard lock{sync};
|
||||
return xruns;
|
||||
}
|
||||
virtual void OnUnderrun() {
|
||||
lock_guard lock { sync };
|
||||
virtual void OnUnderrun()
|
||||
{
|
||||
lock_guard lock{sync};
|
||||
++xruns;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("alsa_test", "[alsa_test]")
|
||||
{
|
||||
|
||||
TEST_CASE( "alsa_test", "[alsa_test]" ) {
|
||||
|
||||
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
||||
|
||||
alsaDriver.Test();
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE( "alsa_midi_test", "[alsa_midi_test]" ) {
|
||||
TEST_CASE("alsa_midi_test", "[alsa_midi_test]")
|
||||
{
|
||||
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
||||
|
||||
MidiDecoderTest();
|
||||
}
|
||||
|
||||
|
||||
+19
-5
@@ -35,6 +35,8 @@ namespace pipedal {
|
||||
|
||||
using ProcessCallback = std::function<void (size_t)>;
|
||||
|
||||
class ChannelSelection;
|
||||
|
||||
|
||||
class AudioDriverHost {
|
||||
public:
|
||||
@@ -58,14 +60,26 @@ namespace pipedal {
|
||||
virtual size_t GetMidiInputEventCount() = 0;
|
||||
virtual MidiEvent*GetMidiEvents() = 0;
|
||||
|
||||
virtual size_t InputBufferCount() const = 0;
|
||||
virtual float*GetInputBuffer(size_t channel) = 0;
|
||||
virtual size_t DeviceInputBufferCount() const = 0;
|
||||
virtual size_t DeviceOutputBufferCount() const = 0;
|
||||
virtual size_t MainInputBufferCount() const = 0;
|
||||
virtual float*GetMainInputBuffer(size_t channel) = 0;
|
||||
virtual size_t MainOutputBufferCount() const = 0;
|
||||
virtual float*GetMainOutputBuffer(size_t channe) = 0;
|
||||
|
||||
|
||||
virtual size_t OutputBufferCount() const = 0;
|
||||
virtual float*GetOutputBuffer(size_t channe) = 0;
|
||||
virtual size_t AuxInputBufferCount() const = 0;
|
||||
virtual float*GetAuxInputBuffer(size_t channel) = 0;
|
||||
virtual size_t AuxOutputBufferCount() const = 0;
|
||||
virtual float*GetAuxOutputBuffer(size_t channel) = 0;
|
||||
|
||||
virtual void Open(const JackServerSettings & jackServerSettings,const JackChannelSelection &channelSelection) = 0;
|
||||
virtual size_t SendInputBufferCount() const = 0;
|
||||
virtual float*GetSendInputBuffer(size_t channel) = 0;
|
||||
virtual size_t SendOutputBufferCount() const = 0;
|
||||
virtual float*GetSendOutputBuffer(size_t channel) = 0;
|
||||
|
||||
|
||||
virtual void Open(const JackServerSettings & jackServerSettings,const ChannelSelection &channelSelection) = 0;
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) = 0;
|
||||
virtual void Activate() = 0;
|
||||
virtual void Deactivate() = 0;
|
||||
|
||||
+91
-25
@@ -18,6 +18,7 @@
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "PiPedalCommon.hpp"
|
||||
#include "AudioHost.hpp"
|
||||
#include "util.hpp"
|
||||
#include <lv2/atom/atom.h>
|
||||
@@ -519,12 +520,17 @@ private:
|
||||
SystemMidiBinding rebootMidiBinding;
|
||||
SystemMidiBinding shutdownMidiBinding;
|
||||
|
||||
JackChannelSelection channelSelection;
|
||||
ChannelSelection channelSelection;
|
||||
std::atomic<bool> active = false;
|
||||
std::atomic<bool> audioStopped = false;
|
||||
std::atomic<bool> isDummyAudioDriver = false;
|
||||
|
||||
std::shared_ptr<Lv2Pedalboard> currentPedalboard;
|
||||
std::shared_ptr<Lv2Pedalboard> currentMainInsertPedalboard;
|
||||
std::shared_ptr<Lv2Pedalboard> currentAuxInsertPedalboard;
|
||||
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
||||
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
||||
|
||||
@@ -620,9 +626,9 @@ private:
|
||||
|
||||
void ZeroOutputBuffers(size_t nframes)
|
||||
{
|
||||
for (size_t i = 0; i < audioDriver->OutputBufferCount(); ++i)
|
||||
for (size_t i = 0; i < audioDriver->MainOutputBufferCount(); ++i)
|
||||
{
|
||||
float * out = (float *)audioDriver->GetOutputBuffer(i);
|
||||
float * out = (float *)audioDriver->GetMainOutputBuffer(i);
|
||||
if (out)
|
||||
{
|
||||
ZeroBuffer(out, nframes);
|
||||
@@ -1173,9 +1179,9 @@ private:
|
||||
float *inputBuffers[4];
|
||||
float *outputBuffers[4];
|
||||
bool buffersValid = true;
|
||||
for (int i = 0; i < audioDriver->InputBufferCount(); ++i)
|
||||
for (int i = 0; i < audioDriver->MainInputBufferCount(); ++i)
|
||||
{
|
||||
float *input = (float *)audioDriver->GetInputBuffer(i);
|
||||
float *input = (float *)audioDriver->GetMainInputBuffer(i);
|
||||
if (input == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
@@ -1183,11 +1189,11 @@ private:
|
||||
}
|
||||
inputBuffers[i] = input;
|
||||
}
|
||||
inputBuffers[audioDriver->InputBufferCount()] = nullptr;
|
||||
inputBuffers[audioDriver->MainInputBufferCount()] = nullptr;
|
||||
|
||||
for (int i = 0; i < audioDriver->OutputBufferCount(); ++i)
|
||||
for (int i = 0; i < audioDriver->MainOutputBufferCount(); ++i)
|
||||
{
|
||||
float *output = audioDriver->GetOutputBuffer(i);
|
||||
float *output = audioDriver->GetMainOutputBuffer(i);
|
||||
if (output == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
@@ -1195,7 +1201,7 @@ private:
|
||||
}
|
||||
outputBuffers[i] = output;
|
||||
}
|
||||
outputBuffers[audioDriver->OutputBufferCount()] = nullptr;
|
||||
outputBuffers[audioDriver->MainOutputBufferCount()] = nullptr;
|
||||
|
||||
if (buffersValid)
|
||||
{
|
||||
@@ -1670,7 +1676,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection_)
|
||||
{
|
||||
|
||||
std::lock_guard guard(mutex);
|
||||
@@ -1692,11 +1698,6 @@ public:
|
||||
this->audioDriver = std::unique_ptr<AudioDriver>(CreateAlsaDriver(this));
|
||||
}
|
||||
|
||||
if (channelSelection.GetInputAudioPorts().size() == 0 || channelSelection.GetOutputAudioPorts().size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->currentSample = 0;
|
||||
this->underruns = 0;
|
||||
|
||||
@@ -1707,7 +1708,7 @@ public:
|
||||
this->realtimeReader.Reset();
|
||||
this->realtimeWriter.Reset();
|
||||
|
||||
this->channelSelection = channelSelection;
|
||||
this->channelSelection = channelSelection_;
|
||||
|
||||
StartReaderThread();
|
||||
|
||||
@@ -1795,6 +1796,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void SetPluginPreset(uint64_t instanceId, const std::vector<ControlValue> &values)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
@@ -1896,6 +1898,49 @@ public:
|
||||
pendingSnapshots.clear();
|
||||
}
|
||||
|
||||
PIPEDAL_NON_INLINE RealtimePedalboardItemIndex GetRealtimeItemIndex(int64_t instanceId)
|
||||
{
|
||||
Pedalboard::PedalboardType instanceType = Pedalboard::GetInstanceTypeFromInstanceId(instanceId);
|
||||
int64_t index = -1;
|
||||
switch (instanceType)
|
||||
{
|
||||
case Pedalboard::PedalboardType::MainPedalboard:
|
||||
if (this->currentPedalboard) {
|
||||
if (instanceId == Pedalboard::START_CONTROL_ID) {
|
||||
index = Pedalboard::START_CONTROL_ID;
|
||||
} else if (instanceId == Pedalboard::END_CONTROL_ID) {
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
} else {
|
||||
index = this->currentPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Pedalboard::PedalboardType::MainInsert:
|
||||
if (this->currentMainInsertPedalboard) {
|
||||
if (instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID) {
|
||||
index = Pedalboard::MAIN_INSERT_START_CONTROL_ID;
|
||||
} else if (instanceId == Pedalboard::MAIN_INSERT_END_CONTROL_ID) {
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
} else {
|
||||
index = this->currentMainInsertPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Pedalboard::PedalboardType::AuxInsert:
|
||||
if (this->currentAuxInsertPedalboard) {
|
||||
if (instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID) {
|
||||
index = Pedalboard::START_CONTROL_ID;
|
||||
} else if (instanceId == Pedalboard::AUX_INSERT_END_CONTROL_ID) {
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
} else {
|
||||
index = this->currentAuxInsertPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
RealtimePedalboardItemIndex result { instanceType, index};
|
||||
return result;
|
||||
}
|
||||
virtual void SetVuSubscriptions(const std::vector<int64_t> &instanceIds)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
@@ -1917,7 +1962,7 @@ public:
|
||||
auto effect = this->currentPedalboard->GetEffect(instanceId);
|
||||
if (effect)
|
||||
{
|
||||
int index = this->currentPedalboard->GetIndexOfInstanceId(instanceIds[i]);
|
||||
RealtimePedalboardItemIndex index = this->GetRealtimeItemIndex(instanceIds[i]);
|
||||
vuConfig->enabledIndexes.push_back(index);
|
||||
VuUpdate v;
|
||||
v.instanceId_ = instanceId;
|
||||
@@ -1929,20 +1974,41 @@ public:
|
||||
vuConfig->vuUpdateWorkingData.push_back(v);
|
||||
vuConfig->vuUpdateResponseData.push_back(v);
|
||||
}
|
||||
else if (instanceId == Pedalboard::INPUT_VOLUME_ID || instanceId == Pedalboard::OUTPUT_VOLUME_ID)
|
||||
else if (
|
||||
instanceId == Pedalboard::START_CONTROL_ID || instanceId == Pedalboard::END_CONTROL_ID
|
||||
|| instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID || instanceId == Pedalboard::MAIN_INSERT_END_CONTROL_ID
|
||||
|| instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID || instanceId == Pedalboard::AUX_INSERT_END_CONTROL_ID
|
||||
)
|
||||
{
|
||||
int index = (int)instanceId;
|
||||
auto index = GetRealtimeItemIndex(instanceId);
|
||||
VuUpdate v;
|
||||
vuConfig->enabledIndexes.push_back(index);
|
||||
|
||||
v.instanceId_ = instanceId;
|
||||
if (instanceId == Pedalboard::INPUT_VOLUME_ID)
|
||||
size_t nChannels = 0;
|
||||
|
||||
switch (instanceId)
|
||||
{
|
||||
v.isStereoInput_ = v.isStereoOutput_ = this->pHost->GetNumberOfInputAudioChannels() > 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.isStereoInput_ = v.isStereoOutput_ = this->pHost->GetNumberOfOutputAudioChannels() > 1;
|
||||
case Pedalboard::START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainInputChannels().size();
|
||||
break;
|
||||
case Pedalboard::END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::MAIN_INSERT_START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::MAIN_INSERT_END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::AUX_INSERT_START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().auxInputChannels().size();
|
||||
break;
|
||||
case Pedalboard::AUX_INSERT_END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().auxOutputChannels().size();
|
||||
break;
|
||||
}
|
||||
v.isStereoInput_ = v.isStereoOutput_ = nChannels > 1;
|
||||
vuConfig->vuUpdateWorkingData.push_back(v);
|
||||
vuConfig->vuUpdateResponseData.push_back(v);
|
||||
}
|
||||
|
||||
+2
-1
@@ -32,6 +32,7 @@
|
||||
#include "Promise.hpp"
|
||||
#include "json_variant.hpp"
|
||||
#include "RealtimeMidiEventType.hpp"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
@@ -227,7 +228,7 @@ namespace pipedal
|
||||
|
||||
virtual std::string AtomToJson(const LV2_Atom *atom) = 0;
|
||||
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection) = 0;
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection) = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration) = 0;
|
||||
|
||||
+1
-1
@@ -240,7 +240,7 @@ namespace pipedal
|
||||
{
|
||||
// zero length? We can never have a zero-length bank.
|
||||
// Add a default preset and make it the selected preset.
|
||||
Pedalboard pedalboard = Pedalboard::MakeDefault();
|
||||
Pedalboard pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
this->addPreset(pedalboard);
|
||||
newSelection = presets_[0]->instanceId();
|
||||
}
|
||||
|
||||
+10
-3
@@ -204,6 +204,7 @@ else()
|
||||
endif()
|
||||
|
||||
set (PIPEDAL_SOURCES
|
||||
ChannelRouterSettings.cpp ChannelRouterSettings.hpp
|
||||
Curl.cpp Curl.hpp
|
||||
Tone3000DownloadType.cpp Tone3000DownloadType.hpp
|
||||
Tone3000DownloadProgress.cpp Tone3000DownloadProgress.hpp
|
||||
@@ -358,8 +359,9 @@ target_compile_definitions(libpipedald PUBLIC "_REENTRANT")
|
||||
target_include_directories(libpipedald PUBLIC ${PIPEDAL_INCLUDES})
|
||||
|
||||
target_link_libraries(libpipedald
|
||||
PUBLIC
|
||||
PUBLIC
|
||||
PiPedalCommon
|
||||
PRIVATE
|
||||
SQLiteCpp
|
||||
${PipeWire_LIBRARIES}
|
||||
)
|
||||
@@ -768,6 +770,7 @@ add_executable(pipedal_latency_test
|
||||
|
||||
target_link_libraries(pipedal_latency_test PRIVATE pthread asound PiPedalCommon)
|
||||
|
||||
target_include_directories(pipedal_latency_test PRIVATE ${PIPEDAL_INCLUDES})
|
||||
|
||||
add_executable(pipedal_alsa_info
|
||||
alsaCheck.cpp alsaCheck.hpp
|
||||
@@ -865,10 +868,14 @@ add_custom_target (
|
||||
)
|
||||
|
||||
|
||||
|
||||
install (TARGETS pipedalconfig pipedal_kconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
|
||||
EXPORT pipedalTargets)
|
||||
EXPORT pipedalTargets
|
||||
)
|
||||
|
||||
install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
||||
EXPORT pipedalSbinTargets)
|
||||
EXPORT pipedalSbinTargets
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2026 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
static uint64_t countChannels(const std::vector<int64_t> &channels)
|
||||
{
|
||||
if (channels.size() != 2)
|
||||
{
|
||||
throw std::runtime_error("Invalid Channel Router channel configuration.");
|
||||
}
|
||||
if (channels[0] == -1)
|
||||
{
|
||||
if (channels[1] == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channels[1] == -1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (channels[0] == channels[1])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ChannelRouterSettings::numberOfAudioInputChannels() const
|
||||
{
|
||||
if (!configured_)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return countChannels(mainInputChannels_);
|
||||
}
|
||||
uint64_t ChannelRouterSettings::numberOfAudioOutputChannels() const
|
||||
{
|
||||
if (!configured_)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return countChannels(mainOutputChannels_);
|
||||
}
|
||||
|
||||
ChannelSelection::ChannelSelection(ChannelRouterSettings&settings)
|
||||
: mainInputChannels_(settings.mainInputChannels())
|
||||
, mainOutputChannels_(settings.mainOutputChannels())
|
||||
, auxInputChannels_(settings.auxInputChannels())
|
||||
, auxOutputChannels_(settings.auxOutputChannels())
|
||||
, sendInputChannels_(settings.sendInputChannels())
|
||||
, sendOutputChannels_(settings.sendOutputChannels())
|
||||
{
|
||||
normalizeChannelSelection();
|
||||
}
|
||||
|
||||
static void normalizeInputChannels(std::vector<int64_t>&channels) {
|
||||
if (channels.size() == 2) {
|
||||
if (channels[0] == -1 && channels[1] == -1)
|
||||
{
|
||||
channels.resize(0);
|
||||
} else if (channels[0] == channels[1]) {
|
||||
channels.resize(1);
|
||||
} else if (channels[1] == -1) {
|
||||
channels.resize(1);} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void normalizeOutputChannels(std::vector<int64_t>&channels) {
|
||||
normalizeInputChannels(channels);
|
||||
}
|
||||
|
||||
void ChannelSelection::normalizeChannelSelection() {
|
||||
normalizeInputChannels(mainInputChannels_);
|
||||
normalizeOutputChannels(mainOutputChannels_);
|
||||
normalizeInputChannels(auxInputChannels_);
|
||||
normalizeOutputChannels(auxOutputChannels_);
|
||||
normalizeInputChannels(sendInputChannels_);
|
||||
normalizeOutputChannels(sendOutputChannels_);
|
||||
|
||||
// If either aux inputs or outputs are zero, don't do ANY aux processing.
|
||||
if (auxInputChannels_.size() == 0)
|
||||
{
|
||||
auxOutputChannels_.resize(0);
|
||||
} else if (auxOutputChannels_.size() == 0) {
|
||||
auxInputChannels_.resize(0);
|
||||
}
|
||||
// If either main inputs or outputs are empty, add send/receive dummy buffers. (Lv2 plugins shouldn't have to deal with this)
|
||||
if (mainInputChannels_.size() == 0)
|
||||
{
|
||||
mainInputChannels_.resize(1);
|
||||
mainInputChannels_[0] = -1;
|
||||
}
|
||||
if (mainOutputChannels_.size() == 0) {
|
||||
mainInputChannels_.resize(1);
|
||||
mainOutputChannels_[0] = -1;
|
||||
}
|
||||
// Send buffers are what they are. Let the send plugin deal with it.
|
||||
}
|
||||
|
||||
|
||||
ChannelRouterSettings::ChannelRouterSettings()
|
||||
: mainInserts_(Pedalboard::PedalboardType::MainInsert)
|
||||
, auxInserts_(Pedalboard::PedalboardType::AuxInsert)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(ChannelRouterSettings)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, configured)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, channelRouterPresetId)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainInputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainOutputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, mainInserts)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, auxInputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, auxOutputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, auxInserts)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, sendInputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, sendOutputChannels)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettings, controlValues)
|
||||
JSON_MAP_END()
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2026 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "json.hpp"
|
||||
#include "Pedalboard.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
class ChannelRouterSettings
|
||||
{
|
||||
protected:
|
||||
static constexpr int64_t CHANNEL_ROUTER_MAIN_INSERT_ID = -4; // Reserved Instance ID for Router Main Inserts.
|
||||
static constexpr int64_t CHANNEL_ROUTER_AUX_INSERT_ID = -5; // Reserved Instance ID for Router Aux inserts.
|
||||
|
||||
bool configured_ = false;
|
||||
int64_t channelRouterPresetId_ = -1;
|
||||
|
||||
std::vector<int64_t> mainInputChannels_ = {1, 1};
|
||||
std::vector<int64_t> mainOutputChannels_ = {0, 1};
|
||||
Pedalboard mainInserts_;
|
||||
|
||||
std::vector<int64_t> auxInputChannels_ = {-1, -1};
|
||||
std::vector<int64_t> auxOutputChannels_ = {-1, -1};
|
||||
Pedalboard auxInserts_;
|
||||
|
||||
std::vector<int64_t> sendInputChannels_ = {-1, -1};
|
||||
std::vector<int64_t> sendOutputChannels_ = {-1, -1};
|
||||
|
||||
std::vector<ControlValue> controlValues_;
|
||||
|
||||
public:
|
||||
using self = ChannelRouterSettings;
|
||||
using ptr = std::shared_ptr<self>;
|
||||
|
||||
ChannelRouterSettings();
|
||||
|
||||
uint64_t numberOfAudioInputChannels() const;
|
||||
uint64_t numberOfAudioOutputChannels() const;
|
||||
|
||||
JSON_GETTER_SETTER(configured)
|
||||
JSON_GETTER_SETTER(channelRouterPresetId)
|
||||
JSON_GETTER_SETTER_REF(mainInputChannels)
|
||||
JSON_GETTER_SETTER_REF(mainOutputChannels)
|
||||
JSON_GETTER_SETTER_REF(mainInserts)
|
||||
JSON_GETTER_SETTER_REF(auxInputChannels)
|
||||
JSON_GETTER_SETTER_REF(auxOutputChannels)
|
||||
JSON_GETTER_SETTER_REF(auxInserts)
|
||||
JSON_GETTER_SETTER_REF(sendInputChannels)
|
||||
JSON_GETTER_SETTER_REF(sendOutputChannels)
|
||||
JSON_GETTER_SETTER_REF(controlValues)
|
||||
|
||||
DECLARE_JSON_MAP(ChannelRouterSettings);
|
||||
};
|
||||
|
||||
// just the channel selecttions.
|
||||
class ChannelSelection
|
||||
{
|
||||
public:
|
||||
ChannelSelection() = default;
|
||||
ChannelSelection(ChannelRouterSettings &settings);
|
||||
ChannelSelection(const ChannelSelection &other) = default;
|
||||
ChannelSelection(ChannelSelection &&other) = default;
|
||||
ChannelSelection &operator=(const ChannelSelection &other) = default;
|
||||
ChannelSelection &operator=(ChannelSelection &&other) = default;
|
||||
~ChannelSelection() = default;
|
||||
|
||||
const std::vector<int64_t> &mainInputChannels() const { return mainInputChannels_; }
|
||||
const std::vector<int64_t> &mainOutputChannels() const { return mainOutputChannels_; }
|
||||
const std::vector<int64_t> &auxInputChannels() const { return auxInputChannels_; }
|
||||
const std::vector<int64_t> &auxOutputChannels() const { return auxOutputChannels_; }
|
||||
const std::vector<int64_t> &sendInputChannels() const { return sendInputChannels_; }
|
||||
const std::vector<int64_t> &sendOutputChannels() const { return sendOutputChannels_; }
|
||||
|
||||
std::vector<int64_t> &mainInputChannels() { return mainInputChannels_; }
|
||||
std::vector<int64_t> &mainOutputChannels() { return mainOutputChannels_; }
|
||||
std::vector<int64_t> &auxInputChannels() { return auxInputChannels_; }
|
||||
std::vector<int64_t> &auxOutputChannels() { return auxOutputChannels_; }
|
||||
std::vector<int64_t> &sendInputChannels() { return sendInputChannels_; }
|
||||
std::vector<int64_t> &sendOutputChannels() { return sendOutputChannels_; }
|
||||
|
||||
private:
|
||||
void normalizeChannelSelection();
|
||||
|
||||
std::vector<int64_t> mainInputChannels_;
|
||||
std::vector<int64_t> mainOutputChannels_;
|
||||
std::vector<int64_t> auxInputChannels_;
|
||||
std::vector<int64_t> auxOutputChannels_;
|
||||
std::vector<int64_t> sendInputChannels_;
|
||||
std::vector<int64_t> sendOutputChannels_;
|
||||
};
|
||||
|
||||
}
|
||||
+94
-108
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Robin E. R. Davies
|
||||
* Copyright (c) 2026 Robin E. R. Davies
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
#include "PiPedalCommon.hpp"
|
||||
#include "util.hpp"
|
||||
#include <bit>
|
||||
#include <memory>
|
||||
@@ -39,6 +40,7 @@
|
||||
#include "ss.hpp"
|
||||
#include "SchedulerPriority.hpp"
|
||||
#include "CrashGuard.hpp"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
#include "CpuUse.hpp"
|
||||
|
||||
@@ -85,14 +87,19 @@ namespace pipedal
|
||||
uint32_t captureFrameSize = 0;
|
||||
|
||||
|
||||
std::vector<float *> activeCaptureBuffers;
|
||||
std::vector<float *> activePlaybackBuffers;
|
||||
std::vector<std::vector<float>> allocatedBuffers;
|
||||
std::vector<float *> mainCaptureBuffers;
|
||||
std::vector<float *> mainPlaybackBuffers;
|
||||
|
||||
std::vector<float *> captureBuffers;
|
||||
std::vector<float *> playbackBuffers;
|
||||
std::vector<float *> auxCaptureBuffers;
|
||||
std::vector<float *> auxPlaybackBuffers;
|
||||
|
||||
std::vector<float*> sendCaptureBuffers;
|
||||
std::vector<float*> sendPlaybackBuffers;
|
||||
|
||||
std::vector<float *> deviceCaptureBuffers;
|
||||
std::vector<float *> devicePlaybackBuffers;
|
||||
|
||||
uint8_t *rawCaptureBuffer = nullptr;
|
||||
uint8_t *rawPlaybackBuffer = nullptr;
|
||||
|
||||
AudioDriverHost *driverHost = nullptr;
|
||||
uint32_t channels = 2;
|
||||
@@ -145,7 +152,7 @@ namespace pipedal
|
||||
|
||||
std::atomic<bool> terminateAudio_ = false;
|
||||
|
||||
void terminateAudio(bool terminate)
|
||||
PIPEDAL_NON_INLINE void terminateAudio(bool terminate)
|
||||
{
|
||||
this->terminateAudio_ = terminate;
|
||||
}
|
||||
@@ -161,32 +168,28 @@ namespace pipedal
|
||||
}
|
||||
|
||||
private:
|
||||
void AllocateBuffers(std::vector<float *> &buffers, size_t n)
|
||||
PIPEDAL_NON_INLINE void AllocateBuffers(std::vector<float *> &buffers, size_t n)
|
||||
{
|
||||
buffers.resize(n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buffers[i] = new float[this->bufferSize];
|
||||
for (size_t j = 0; j < this->bufferSize; ++j)
|
||||
{
|
||||
buffers[i][j] = 0;
|
||||
}
|
||||
buffers[i] = AllocateAudioBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t GetMidiInputEventCount() override
|
||||
PIPEDAL_NON_INLINE virtual size_t GetMidiInputEventCount() override
|
||||
{
|
||||
return midiEventCount;
|
||||
}
|
||||
virtual MidiEvent *GetMidiEvents() override
|
||||
PIPEDAL_NON_INLINE virtual MidiEvent *GetMidiEvents() override
|
||||
{
|
||||
return this->midiEvents.data();
|
||||
}
|
||||
|
||||
|
||||
JackChannelSelection channelSelection;
|
||||
ChannelSelection channelSelection;
|
||||
bool open = false;
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
PIPEDAL_NON_INLINE virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||
{
|
||||
terminateAudio_ = false;
|
||||
if (open)
|
||||
@@ -208,7 +211,7 @@ namespace pipedal
|
||||
throw;
|
||||
}
|
||||
}
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
||||
PIPEDAL_NON_INLINE virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
||||
{
|
||||
this->alsaSequencer = alsaSequencer;
|
||||
}
|
||||
@@ -220,23 +223,28 @@ namespace pipedal
|
||||
<< ", " << "Native float"
|
||||
<< ", " << this->sampleRate
|
||||
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
||||
<< ", in: " << this->InputBufferCount() << "/" << this->captureChannels
|
||||
<< ", out: " << this->OutputBufferCount() << "/" << this->playbackChannels);
|
||||
<< ", " << "device in: " << this->DeviceInputBufferCount()
|
||||
<< ", " << "device out: " << this->DeviceOutputBufferCount()
|
||||
<< ", main in: " << this->MainInputBufferCount()
|
||||
<< ", main out: " << this->MainOutputBufferCount()
|
||||
<< ", aux in: " << this->AuxInputBufferCount()
|
||||
<< ", aux out: " << this->AuxOutputBufferCount()
|
||||
<< ", send in: " << this->SendInputBufferCount()
|
||||
<< ", send out: " << this->SendOutputBufferCount()
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
void OpenAudio(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||
{
|
||||
int err;
|
||||
|
||||
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
||||
this->bufferSize = jackServerSettings.GetBufferSize();
|
||||
AllocateBuffers(captureBuffers, channels);
|
||||
AllocateBuffers(playbackBuffers, channels);
|
||||
|
||||
}
|
||||
|
||||
std::jthread *audioThread;
|
||||
std::unique_ptr<std::jthread> audioThread;
|
||||
bool audioRunning;
|
||||
|
||||
bool block = false;
|
||||
@@ -303,7 +311,7 @@ namespace pipedal
|
||||
this->driverHost->OnProcess(framesRead);
|
||||
|
||||
/// no attempt at realtime. Just as long as we run occasionally.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
|
||||
|
||||
}
|
||||
@@ -318,22 +326,6 @@ namespace pipedal
|
||||
|
||||
bool alsaActive = false;
|
||||
|
||||
static int IndexFromPortName(const std::string &s)
|
||||
{
|
||||
auto pos = s.find_last_of('_');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
throw std::invalid_argument("Bad port name.");
|
||||
}
|
||||
const char *p = s.c_str() + (pos + 1);
|
||||
|
||||
int v = atoi(p);
|
||||
if (v < 0)
|
||||
{
|
||||
throw std::invalid_argument("Bad port name.");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
bool activated = false;
|
||||
virtual void Activate()
|
||||
@@ -344,41 +336,17 @@ namespace pipedal
|
||||
}
|
||||
activated = true;
|
||||
|
||||
this->activeCaptureBuffers.resize(channelSelection.GetInputAudioPorts().size());
|
||||
AllocateBuffers(deviceCaptureBuffers, channels);
|
||||
AllocateBuffers(devicePlaybackBuffers, channels);
|
||||
|
||||
playbackBuffers.resize(channels);
|
||||
AllocateBuffers(mainCaptureBuffers, channelSelection.mainInputChannels().size());
|
||||
AllocateBuffers(mainPlaybackBuffers, channelSelection.mainOutputChannels().size());
|
||||
AllocateBuffers(auxCaptureBuffers, channelSelection.auxInputChannels().size());
|
||||
AllocateBuffers(auxPlaybackBuffers, channelSelection.auxOutputChannels().size());
|
||||
AllocateBuffers(sendCaptureBuffers, channelSelection.sendInputChannels().size());
|
||||
AllocateBuffers(sendPlaybackBuffers, channelSelection.sendOutputChannels().size());
|
||||
|
||||
int ix = 0;
|
||||
for (auto &x : channelSelection.GetInputAudioPorts())
|
||||
{
|
||||
int sourceIndex = IndexFromPortName(x);
|
||||
if (sourceIndex >= captureBuffers.size())
|
||||
{
|
||||
Lv2Log::error(SS("Invalid audio input port: " << x));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->activeCaptureBuffers[ix++] = this->captureBuffers[sourceIndex];
|
||||
}
|
||||
}
|
||||
|
||||
this->activePlaybackBuffers.resize(channelSelection.GetOutputAudioPorts().size());
|
||||
|
||||
ix = 0;
|
||||
for (auto &x : channelSelection.GetOutputAudioPorts())
|
||||
{
|
||||
int sourceIndex = IndexFromPortName(x);
|
||||
if (sourceIndex >= playbackBuffers.size())
|
||||
{
|
||||
Lv2Log::error(SS("Invalid audio output port: " << x));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->activePlaybackBuffers[ix++] = this->playbackBuffers[sourceIndex];
|
||||
}
|
||||
}
|
||||
|
||||
audioThread = new std::jthread([this]()
|
||||
audioThread = std::make_unique<std::jthread>([this]()
|
||||
{ AudioThread(); });
|
||||
}
|
||||
|
||||
@@ -393,7 +361,7 @@ namespace pipedal
|
||||
if (audioThread)
|
||||
{
|
||||
this->audioThread->join();
|
||||
this->audioThread = 0;
|
||||
this->audioThread = nullptr;
|
||||
}
|
||||
Lv2Log::debug("Audio thread joined.");
|
||||
}
|
||||
@@ -402,46 +370,64 @@ namespace pipedal
|
||||
public:
|
||||
|
||||
|
||||
virtual size_t DeviceInputBufferCount() const override { return deviceCaptureBuffers.size(); }
|
||||
virtual size_t DeviceOutputBufferCount() const override { return devicePlaybackBuffers.size(); }
|
||||
|
||||
virtual size_t InputBufferCount() const { return activeCaptureBuffers.size(); }
|
||||
virtual float *GetInputBuffer(size_t channel)
|
||||
virtual size_t MainInputBufferCount() const override { return mainCaptureBuffers.size(); }
|
||||
virtual float *GetMainInputBuffer(size_t channel) override
|
||||
{
|
||||
return activeCaptureBuffers[channel];
|
||||
return mainCaptureBuffers[channel];
|
||||
}
|
||||
|
||||
virtual size_t OutputBufferCount() const { return activePlaybackBuffers.size(); }
|
||||
virtual float *GetOutputBuffer(size_t channel)
|
||||
virtual size_t MainOutputBufferCount() const { return mainPlaybackBuffers.size(); }
|
||||
virtual float *GetMainOutputBuffer(size_t channel) override
|
||||
{
|
||||
return activePlaybackBuffers[channel];
|
||||
return mainPlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
void FreeBuffers(std::vector<float *> &buffer)
|
||||
virtual size_t AuxInputBufferCount() const override { return auxCaptureBuffers.size(); }
|
||||
virtual float *GetAuxInputBuffer(size_t channel) override
|
||||
{
|
||||
for (size_t i = 0; i < buffer.size(); ++i)
|
||||
{
|
||||
// delete[] buffer[i];
|
||||
buffer[i] = 0;
|
||||
}
|
||||
buffer.clear();
|
||||
return auxCaptureBuffers[channel];
|
||||
}
|
||||
void DeleteBuffers()
|
||||
|
||||
virtual size_t AuxOutputBufferCount() const override { return auxPlaybackBuffers.size(); }
|
||||
virtual float *GetAuxOutputBuffer(size_t channel) override
|
||||
{
|
||||
activeCaptureBuffers.clear();
|
||||
activePlaybackBuffers.clear();
|
||||
FreeBuffers(this->playbackBuffers);
|
||||
FreeBuffers(this->captureBuffers);
|
||||
if (rawCaptureBuffer)
|
||||
{
|
||||
delete[] rawCaptureBuffer;
|
||||
rawCaptureBuffer = nullptr;
|
||||
}
|
||||
if (rawPlaybackBuffer)
|
||||
{
|
||||
delete[] rawPlaybackBuffer;
|
||||
rawPlaybackBuffer = nullptr;
|
||||
}
|
||||
return auxPlaybackBuffers[channel];
|
||||
}
|
||||
virtual void Close()
|
||||
|
||||
virtual size_t SendInputBufferCount() const override { return sendCaptureBuffers.size(); }
|
||||
virtual float *GetSendInputBuffer(size_t channel) override
|
||||
{
|
||||
return sendCaptureBuffers[channel];
|
||||
}
|
||||
|
||||
virtual size_t SendOutputBufferCount() const override { return sendPlaybackBuffers.size(); }
|
||||
virtual float *GetSendOutputBuffer(size_t channel) override
|
||||
{
|
||||
return sendPlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PIPEDAL_NON_INLINE float*AllocateAudioBuffer() {
|
||||
allocatedBuffers.push_back(std::vector<float>(bufferSize));
|
||||
return allocatedBuffers.back().data();
|
||||
}
|
||||
PIPEDAL_NON_INLINE void DeleteBuffers()
|
||||
{
|
||||
mainCaptureBuffers.clear();
|
||||
mainPlaybackBuffers.clear();
|
||||
auxCaptureBuffers.clear();
|
||||
auxPlaybackBuffers.clear();
|
||||
sendCaptureBuffers.clear();
|
||||
sendPlaybackBuffers.clear();
|
||||
allocatedBuffers.clear();
|
||||
}
|
||||
PIPEDAL_NON_INLINE virtual void Close()
|
||||
{
|
||||
if (!open)
|
||||
{
|
||||
@@ -453,18 +439,18 @@ namespace pipedal
|
||||
DeleteBuffers();
|
||||
}
|
||||
|
||||
virtual float CpuUse()
|
||||
PIPEDAL_NON_INLINE virtual float CpuUse()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual float CpuOverhead()
|
||||
PIPEDAL_NON_INLINE virtual float CpuOverhead()
|
||||
{
|
||||
return 0.1;
|
||||
}
|
||||
};
|
||||
|
||||
AudioDriver *CreateDummyAudioDriver(AudioDriverHost *driverHost,const std::string&deviceName)
|
||||
PIPEDAL_NON_INLINE AudioDriver *CreateDummyAudioDriver(AudioDriverHost *driverHost,const std::string&deviceName)
|
||||
{
|
||||
return new DummyDriverImpl(driverHost,deviceName);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <lilv/lilv.h>
|
||||
#include "AudioDriver.hpp"
|
||||
#include "JackServerSettings.hpp"
|
||||
|
||||
|
||||
+2
-2
@@ -26,6 +26,7 @@ namespace pipedal {
|
||||
class Lv2PluginInfo;
|
||||
class IEffect;
|
||||
class HostWorkerThread;
|
||||
class ChannelSelection;
|
||||
|
||||
class IHost
|
||||
{
|
||||
@@ -43,13 +44,12 @@ namespace pipedal {
|
||||
virtual size_t GetAtomBufferSize() const = 0;
|
||||
|
||||
virtual bool HasMidiInputChannel() const = 0;
|
||||
virtual int GetNumberOfInputAudioChannels() const = 0;
|
||||
virtual int GetNumberOfOutputAudioChannels() const = 0;
|
||||
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
|
||||
|
||||
virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0;
|
||||
|
||||
virtual std::string GetPluginStoragePath() const = 0;
|
||||
virtual const ChannelSelection&GetChannelSelection() const = 0;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -124,5 +124,4 @@ namespace pipedal
|
||||
DECLARE_JSON_MAP(JackChannelSelection);
|
||||
};
|
||||
|
||||
|
||||
} // namespace.
|
||||
+42
-5
@@ -320,6 +320,7 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
|
||||
void Lv2Pedalboard::Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList, ExistingEffectMap *existingEffects)
|
||||
{
|
||||
this->pHost = pHost;
|
||||
this->pedalboardType = pedalboard.GetInstanceType();
|
||||
|
||||
inputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
|
||||
outputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
|
||||
@@ -329,13 +330,15 @@ void Lv2Pedalboard::Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardE
|
||||
inputVolume.SetTarget(pedalboard.input_volume_db());
|
||||
outputVolume.SetTarget(pedalboard.output_volume_db());
|
||||
|
||||
for (int i = 0; i < pHost->GetNumberOfInputAudioChannels(); ++i)
|
||||
size_t nInputs = std::max(GetNumberOfAudioInputChannels(),(size_t)1);
|
||||
|
||||
for (size_t i = 0; i < nInputs; ++i)
|
||||
{
|
||||
this->pedalboardInputBuffers.push_back(bufferPool.AllocateBuffer<float>(pHost->GetMaxAudioBufferSize()));
|
||||
}
|
||||
|
||||
auto outputs = PrepareItems(pedalboard.items(), this->pedalboardInputBuffers, errorList, existingEffects);
|
||||
int nOutputs = pHost->GetNumberOfOutputAudioChannels();
|
||||
size_t nOutputs = GetNumberOfAudioOutputChannels();
|
||||
if (nOutputs == 1)
|
||||
{
|
||||
this->pedalboardOutputBuffers.push_back(outputs[0]);
|
||||
@@ -572,9 +575,14 @@ void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samp
|
||||
{
|
||||
for (size_t i = 0; i < vuConfiguration->enabledIndexes.size(); ++i)
|
||||
{
|
||||
int index = vuConfiguration->enabledIndexes[i];
|
||||
auto& rtIndex = vuConfiguration->enabledIndexes[i];
|
||||
if (rtIndex.instanceType != this->pedalboardType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int index = rtIndex.index;
|
||||
VuUpdate *pUpdate = &vuConfiguration->vuUpdateWorkingData[i];
|
||||
if (index == Pedalboard::INPUT_VOLUME_ID)
|
||||
if (index == Pedalboard::START_CONTROL_ID)
|
||||
{
|
||||
if (this->pedalboardInputBuffers.size() > 1)
|
||||
{
|
||||
@@ -588,7 +596,7 @@ void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samp
|
||||
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]), samples); // after input volume applied.
|
||||
}
|
||||
}
|
||||
else if (index == Pedalboard::OUTPUT_VOLUME_ID)
|
||||
else if (index == Pedalboard::END_CONTROL_ID)
|
||||
{
|
||||
if (this->pedalboardOutputBuffers.size() > 1)
|
||||
{
|
||||
@@ -1016,3 +1024,32 @@ void Lv2Pedalboard::handleTapTempo(uint8_t value, const MidiTimestamp& timestamp
|
||||
mapping.lastTapTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t Lv2Pedalboard::GetNumberOfAudioInputChannels() const {
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case Pedalboard::PedalboardType::MainPedalboard:
|
||||
return pHost->GetChannelSelection().mainInputChannels().size();
|
||||
case Pedalboard::PedalboardType::MainInsert:
|
||||
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
case Pedalboard::PedalboardType::AuxInsert:
|
||||
return pHost->GetChannelSelection().auxInputChannels().size();
|
||||
default:
|
||||
throw std::runtime_error("Invalid argument");
|
||||
}
|
||||
}
|
||||
|
||||
size_t Lv2Pedalboard::GetNumberOfAudioOutputChannels() const {
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case Pedalboard::PedalboardType::MainPedalboard:
|
||||
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
case Pedalboard::PedalboardType::MainInsert:
|
||||
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
case Pedalboard::PedalboardType::AuxInsert:
|
||||
return pHost->GetChannelSelection().auxOutputChannels().size();
|
||||
default:
|
||||
throw std::runtime_error("Invalid argument");
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ namespace pipedal
|
||||
class Lv2Pedalboard
|
||||
{
|
||||
private:
|
||||
Pedalboard::PedalboardType pedalboardType;
|
||||
IHost *pHost = nullptr;
|
||||
size_t currentFrameOffset = 0;
|
||||
DbDezipper inputVolume;
|
||||
@@ -128,6 +129,9 @@ namespace pipedal
|
||||
std::vector<IEffect *> &GetEffects() { return realtimeEffects; }
|
||||
std::vector<std::shared_ptr<IEffect>> &GetSharedEffectList() { return effects; }
|
||||
|
||||
size_t GetNumberOfAudioInputChannels() const;
|
||||
size_t GetNumberOfAudioOutputChannels() const;
|
||||
|
||||
int GetIndexOfInstanceId(uint64_t instanceId)
|
||||
{
|
||||
for (int i = 0; i < this->realtimeEffects.size(); ++i)
|
||||
|
||||
+17
-1
@@ -210,7 +210,7 @@ PedalboardItem Pedalboard::MakeSplit()
|
||||
|
||||
|
||||
|
||||
Pedalboard Pedalboard::MakeDefault()
|
||||
Pedalboard Pedalboard::MakeDefault(PedalboardType instanceType)
|
||||
{
|
||||
// copy insanity. but it happens so rarely.
|
||||
Pedalboard result;
|
||||
@@ -510,6 +510,22 @@ Snapshot Pedalboard::MakeSnapshotFromCurrentSettings(const Pedalboard &previousP
|
||||
}
|
||||
|
||||
|
||||
Pedalboard::Pedalboard(PedalboardType instanceType)
|
||||
{
|
||||
switch (instanceType)
|
||||
{
|
||||
case PedalboardType::MainPedalboard:
|
||||
nextInstanceId_ = 0;
|
||||
break;
|
||||
case PedalboardType::MainInsert:
|
||||
nextInstanceId_ = MAIN_INSERT_INSTANCE_BASE;;
|
||||
break;
|
||||
case PedalboardType::AuxInsert:
|
||||
nextInstanceId_ = AUX_INSERT_INSTANCE_BASE;;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(ControlValue)
|
||||
JSON_MAP_REFERENCE(ControlValue,key)
|
||||
|
||||
+45
-3
@@ -230,11 +230,53 @@ class Pedalboard {
|
||||
|
||||
int64_t selectedPlugin_ = -1;
|
||||
|
||||
|
||||
static constexpr int64_t TWO_POWER_52 = 4503599627370496; // Maximum Javascript integer value.
|
||||
static constexpr int64_t TWO_POWER_48 = 281474976710656; // Number of possible instance IDs for Main Pedalboards.
|
||||
static constexpr int64_t MAX_INSTANCE_ID = TWO_POWER_48;
|
||||
|
||||
public:
|
||||
|
||||
enum class PedalboardType {
|
||||
MainPedalboard,
|
||||
MainInsert,
|
||||
AuxInsert
|
||||
};
|
||||
static constexpr int64_t MAIN_INSERT_INSTANCE_BASE = TWO_POWER_52-2*MAX_INSTANCE_ID;
|
||||
static constexpr int64_t AUX_INSERT_INSTANCE_BASE = TWO_POWER_52-1*MAX_INSTANCE_ID;
|
||||
|
||||
static constexpr int64_t CHANNEL_ROUTER_CONTROLS_INSTANCE_ID = -4; // Reserved Instance ID for Router Controls.
|
||||
|
||||
static constexpr int64_t START_CONTROL_ID = -2; // synthetic PedalboardItem for input volume.
|
||||
static constexpr int64_t END_CONTROL_ID = -3; // synthetic PedalboardItem for output volume.
|
||||
static constexpr int64_t MAIN_INSERT_START_CONTROL_ID = MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
||||
static constexpr int64_t MAIN_INSERT_END_CONTROL_ID = MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||
static constexpr int64_t AUX_INSERT_START_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
||||
static constexpr int64_t AUX_INSERT_END_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||
|
||||
static PedalboardType GetInstanceTypeFromInstanceId(int64_t instanceId) {
|
||||
if (instanceId == START_CONTROL_ID) return PedalboardType::MainPedalboard;
|
||||
if (instanceId == END_CONTROL_ID) return PedalboardType::MainPedalboard;
|
||||
|
||||
if (instanceId >= MAIN_INSERT_INSTANCE_BASE) {
|
||||
return PedalboardType::MainInsert;
|
||||
} else if (instanceId >= AUX_INSERT_INSTANCE_BASE) {
|
||||
return PedalboardType::AuxInsert;
|
||||
} else {
|
||||
return PedalboardType::MainPedalboard;
|
||||
}
|
||||
}
|
||||
PedalboardType GetInstanceType() const {
|
||||
return GetInstanceTypeFromInstanceId(this->nextInstanceId_); // instanceId is irrelevant here.
|
||||
}
|
||||
|
||||
|
||||
|
||||
Pedalboard(PedalboardType instanceType = PedalboardType::MainPedalboard);
|
||||
|
||||
// deep copy, breaking shared pointers.
|
||||
Pedalboard DeepCopy();
|
||||
static constexpr int64_t INPUT_VOLUME_ID = -2; // synthetic PedalboardItem for input volume.
|
||||
static constexpr int64_t OUTPUT_VOLUME_ID = -3; // synthetic PedalboardItem for output volume.
|
||||
|
||||
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
|
||||
bool SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string&iconColor);
|
||||
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
||||
@@ -266,7 +308,7 @@ public:
|
||||
PedalboardItem MakeSplit();
|
||||
|
||||
|
||||
static Pedalboard MakeDefault();
|
||||
static Pedalboard MakeDefault(Pedalboard::PedalboardType instanceType = Pedalboard::PedalboardType::MainPedalboard);
|
||||
};
|
||||
|
||||
#undef GETTER_SETTER_REF
|
||||
|
||||
+22
-14
@@ -34,6 +34,7 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <sched.h>
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -169,19 +170,26 @@ public:
|
||||
delete[] inputBuffers;
|
||||
delete[] outputBuffers;
|
||||
}
|
||||
std::vector<std::string> SelectChannels(const std::vector<std::string> &available, const std::vector<int> &selection)
|
||||
std::vector<int64_t> SelectChannels(const std::vector<std::string> &available, const std::vector<int> &selection)
|
||||
{
|
||||
std::vector<int64_t> result;
|
||||
if (selection.size() == 0)
|
||||
return available;
|
||||
{
|
||||
result.resize(available.size());
|
||||
for (size_t i = 0; i < result.size(); ++i)
|
||||
{
|
||||
result[i] = (int64_t)i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
for (int sel : selection)
|
||||
{
|
||||
if (sel < 0 || sel >= available.size())
|
||||
{
|
||||
throw PiPedalArgumentException(SS("Invalid channel: " + sel));
|
||||
}
|
||||
result.push_back(available[sel]);
|
||||
result.push_back(sel);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -199,22 +207,22 @@ public:
|
||||
auto &availableInputs = jackConfiguration.inputAudioPorts();
|
||||
auto &availableOutputs = jackConfiguration.outputAudioPorts();
|
||||
|
||||
std::vector<std::string> inputAudioPorts, outputAudioPorts;
|
||||
std::vector<int64_t> inputAudioPorts, outputAudioPorts;
|
||||
|
||||
inputAudioPorts = SelectChannels(availableInputs, this->inputChannels);
|
||||
outputAudioPorts = SelectChannels(availableOutputs, this->outputChannels);
|
||||
|
||||
JackChannelSelection channelSelection(
|
||||
inputAudioPorts, outputAudioPorts,
|
||||
std::vector<AlsaMidiDeviceInfo>());
|
||||
ChannelSelection channelSelection;
|
||||
channelSelection.mainInputChannels() = inputAudioPorts;
|
||||
channelSelection.mainOutputChannels() = outputAudioPorts;
|
||||
|
||||
audioDriver = CreateAlsaDriver(this);
|
||||
|
||||
latencyMonitor.Init(jackConfiguration.sampleRate());
|
||||
audioDriver->Open(serverSettings, channelSelection);
|
||||
|
||||
inputBuffers = new float *[channelSelection.GetInputAudioPorts().size()];
|
||||
outputBuffers = new float *[channelSelection.GetOutputAudioPorts().size()];
|
||||
inputBuffers = new float *[channelSelection.mainInputChannels().size()];
|
||||
outputBuffers = new float *[channelSelection.mainOutputChannels().size()];
|
||||
|
||||
audioDriver->Activate();
|
||||
|
||||
@@ -364,16 +372,16 @@ public:
|
||||
virtual void OnProcess(size_t nFrames)
|
||||
{
|
||||
|
||||
size_t inputs = audioDriver->InputBufferCount();
|
||||
size_t outputs = audioDriver->OutputBufferCount();
|
||||
size_t inputs = audioDriver->MainInputBufferCount();
|
||||
size_t outputs = audioDriver->MainOutputBufferCount();
|
||||
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
{
|
||||
inputBuffers[i] = audioDriver->GetInputBuffer(i);
|
||||
inputBuffers[i] = audioDriver->GetMainInputBuffer(i);
|
||||
}
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
{
|
||||
outputBuffers[i] = audioDriver->GetOutputBuffer(i);
|
||||
outputBuffers[i] = audioDriver->GetMainOutputBuffer(i);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nFrames; ++i)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2026 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef PIPEDAL_NON_INLINE
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define PIPEDAL_NON_INLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define PIPEDAL_NON_INLINE __declspec(noinline)
|
||||
#else
|
||||
#define PIPEDAL_NON_INLINE
|
||||
#endif
|
||||
#endif
|
||||
+39
-27
@@ -82,7 +82,7 @@ PiPedalModel::PiPedalModel()
|
||||
this->updater = Updater::Create();
|
||||
this->updater->Start();
|
||||
this->currentUpdateStatus = updater->GetCurrentStatus();
|
||||
this->pedalboard = Pedalboard::MakeDefault();
|
||||
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
#if JACK_HOST
|
||||
this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag.
|
||||
this->jackServerSettings.ReadJackDaemonConfiguration();
|
||||
@@ -228,7 +228,14 @@ void PiPedalModel::Init(const PiPedalConfiguration &configuration)
|
||||
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
||||
#else
|
||||
this->jackServerSettings = storage.GetJackServerSettings();
|
||||
this->jackConfiguration.AlsaInitialize(jackServerSettings);
|
||||
#endif
|
||||
|
||||
this->channelRouterSettings = storage.GetChannelRouterSettings();
|
||||
pluginHost.OnConfigurationChanged(
|
||||
jackConfiguration,
|
||||
storage.GetChannelSelection());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -390,7 +397,7 @@ void PiPedalModel::Load()
|
||||
if (CrashGuard::HasCrashed())
|
||||
{
|
||||
// ignore the current preset, and load a blank pedalboard in order to avoid a potential plugin crash.
|
||||
this->pedalboard = Pedalboard::MakeDefault();
|
||||
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1516,17 +1523,13 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
|
||||
throw std::runtime_error("Audio configuration not valid.");
|
||||
}
|
||||
|
||||
auto channelSelection = this->storage.GetJackChannelSelection(jackConfiguration);
|
||||
const auto & channelSelection = this->storage.GetChannelSelection();
|
||||
|
||||
if (!channelSelection.isValid())
|
||||
{
|
||||
throw std::runtime_error("Audio configuration not valid.");
|
||||
}
|
||||
this->audioHost->Open(jackServerSettings, channelSelection);
|
||||
|
||||
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
|
||||
FireChannelSelectionChanged(-1);
|
||||
FireChannelRouterSettingsChanged(-1);
|
||||
LoadCurrentPedalboard();
|
||||
|
||||
this->UpdateRealtimeVuSubscriptions();
|
||||
@@ -1622,33 +1625,18 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
|
||||
return result;
|
||||
}
|
||||
|
||||
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex); // copy atomically.
|
||||
this->storage.SetJackChannelSelection(channelSelection);
|
||||
|
||||
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||
|
||||
CancelAudioRetry();
|
||||
}
|
||||
|
||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||
|
||||
this->FireChannelSelectionChanged(clientId);
|
||||
}
|
||||
|
||||
void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
|
||||
void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
{
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
JackChannelSelection channelSelection = storage.GetJackChannelSelection(this->jackConfiguration);
|
||||
ChannelRouterSettings::ptr channelRouterSettings = this->channelRouterSettings;
|
||||
|
||||
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||
for (auto &subscriber : t)
|
||||
{
|
||||
subscriber->OnChannelSelectionChanged(clientId, channelSelection);
|
||||
subscriber->OnChannelRouterSettingsChanged(clientId, *channelRouterSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1765,7 +1753,7 @@ void PiPedalModel::UpdateRealtimeVuSubscriptions()
|
||||
for (int i = 0; i < activeVuSubscriptions.size(); ++i)
|
||||
{
|
||||
auto instanceId = activeVuSubscriptions[i].instanceid;
|
||||
if (pedalboard.HasItem(instanceId) || instanceId == Pedalboard::INPUT_VOLUME_ID || instanceId == Pedalboard::OUTPUT_VOLUME_ID)
|
||||
if (pedalboard.HasItem(instanceId) || instanceId == Pedalboard::START_CONTROL_ID || instanceId == Pedalboard::END_CONTROL_ID)
|
||||
{
|
||||
addedInstances.insert(activeVuSubscriptions[i].instanceid);
|
||||
}
|
||||
@@ -3408,6 +3396,30 @@ int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vecto
|
||||
|
||||
}
|
||||
|
||||
|
||||
ChannelRouterSettings::ptr PiPedalModel::GetChannelRouterSettings()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
return this->channelRouterSettings;
|
||||
}
|
||||
|
||||
void PiPedalModel::SetChannelRouterSettings(int64_t clientId,ChannelRouterSettings::ptr &settings)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
this->channelRouterSettings = settings;
|
||||
this->storage.SetChannelRouterSettings(settings);
|
||||
this->pluginHost.OnConfigurationChanged(jackConfiguration, *settings);
|
||||
CancelAudioRetry();
|
||||
}
|
||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||
|
||||
this->FireChannelRouterSettingsChanged(clientId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string PiPedalModel::Tone3000ThumbnailDirectory()
|
||||
{
|
||||
return "/var/pipedal/tone3000_thumbnails";
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "Promise.hpp"
|
||||
#include "AtomConverter.hpp"
|
||||
#include "FileEntry.hpp"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
#include <unordered_map>
|
||||
#include "Tone3000Downloader.hpp"
|
||||
|
||||
@@ -73,7 +74,7 @@ namespace pipedal
|
||||
virtual void OnSnapshotModified(int64_t selectedSnapshot, bool modified) = 0;
|
||||
virtual void OnSelectedSnapshotChanged(int64_t selectedSnapshot) = 0;
|
||||
virtual void OnPluginPresetsChanged(const std::string &pluginUri) = 0;
|
||||
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection &channelSelection) = 0;
|
||||
virtual void OnChannelRouterSettingsChanged(int64_t clientId, const ChannelRouterSettings &channelRouterSettings) = 0;
|
||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates) = 0;
|
||||
virtual void OnBankIndexChanged(const BankIndex &bankIndex) = 0;
|
||||
virtual void OnJackServerSettingsChanged(const JackServerSettings &jackServerSettings) = 0;
|
||||
@@ -188,6 +189,8 @@ namespace pipedal
|
||||
AtomConverter atomConverter; // must be AFTER pluginHost!
|
||||
|
||||
Pedalboard pedalboard;
|
||||
ChannelRouterSettings::ptr channelRouterSettings;
|
||||
|
||||
bool previousPedalboardLoaded = false;
|
||||
Pedalboard previousPedalboard;
|
||||
Storage storage;
|
||||
@@ -215,7 +218,7 @@ namespace pipedal
|
||||
void FirePresetChanged(bool changed);
|
||||
void FirePluginPresetsChanged(const std::string &pluginUri);
|
||||
void FirePedalboardChanged(int64_t clientId, bool reloadAudioThread = true);
|
||||
void FireChannelSelectionChanged(int64_t clientId);
|
||||
void FireChannelRouterSettingsChanged(int64_t clientId);
|
||||
void FireBanksChanged(int64_t clientId);
|
||||
void FireJackConfigurationChanged(const JackConfiguration &jackConfiguration);
|
||||
void FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State);
|
||||
@@ -424,7 +427,6 @@ namespace pipedal
|
||||
|
||||
JackConfiguration GetJackConfiguration();
|
||||
|
||||
void SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection);
|
||||
JackChannelSelection GetJackChannelSelection();
|
||||
|
||||
void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration);
|
||||
@@ -521,6 +523,9 @@ namespace pipedal
|
||||
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
|
||||
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||
int64_t CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||
|
||||
void SetChannelRouterSettings(int64_t clientId, ChannelRouterSettings::ptr &settings);
|
||||
ChannelRouterSettings::ptr GetChannelRouterSettings();
|
||||
};
|
||||
|
||||
} // namespace pipedal.
|
||||
+25
-18
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022-2023 Robin Davies
|
||||
// Copyright (c) 2026 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
@@ -594,17 +594,24 @@ JSON_MAP_REFERENCE(SnapshotModifiedBody, snapshotIndex)
|
||||
JSON_MAP_REFERENCE(SnapshotModifiedBody, modified)
|
||||
JSON_MAP_END()
|
||||
|
||||
class ChannelSelectionChangedBody
|
||||
class ChannelRouterSettingsChangedBody
|
||||
{
|
||||
public:
|
||||
int64_t clientId_ = -1;
|
||||
JackChannelSelection *jackChannelSelection_ = nullptr;
|
||||
ChannelRouterSettingsChangedBody(
|
||||
int64_t clientId,
|
||||
const ChannelRouterSettings &channelRouterSettings
|
||||
): clientId_(clientId), channelRouterSettings_(channelRouterSettings)
|
||||
{
|
||||
|
||||
DECLARE_JSON_MAP(ChannelSelectionChangedBody);
|
||||
}
|
||||
int64_t clientId_ = -1;
|
||||
ChannelRouterSettings channelRouterSettings_;
|
||||
|
||||
DECLARE_JSON_MAP(ChannelRouterSettingsChangedBody);
|
||||
};
|
||||
JSON_MAP_BEGIN(ChannelSelectionChangedBody)
|
||||
JSON_MAP_REFERENCE(ChannelSelectionChangedBody, clientId)
|
||||
JSON_MAP_REFERENCE(ChannelSelectionChangedBody, jackChannelSelection)
|
||||
JSON_MAP_BEGIN(ChannelRouterSettingsChangedBody)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettingsChangedBody, clientId)
|
||||
JSON_MAP_REFERENCE(ChannelRouterSettingsChangedBody, channelRouterSettings)
|
||||
JSON_MAP_END()
|
||||
|
||||
class PresetsChangedBody
|
||||
@@ -1481,12 +1488,6 @@ public:
|
||||
this->model.AddNotificationSubscription(shared_from_this());
|
||||
Reply(replyTo, "ehlo", clientId);
|
||||
}
|
||||
else if (message == "setJackSettings")
|
||||
{
|
||||
JackChannelSelection jackSettings;
|
||||
pReader->read(&jackSettings);
|
||||
this->model.SetJackChannelSelection(this->clientId, jackSettings);
|
||||
}
|
||||
else if (message == "setShowStatusMonitor")
|
||||
{
|
||||
bool showStatusMonitor;
|
||||
@@ -1902,6 +1903,14 @@ public:
|
||||
pReader->read(&args);
|
||||
auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_);
|
||||
this->Reply(replyTo,"copyPresetsToBank",result);
|
||||
} else if (message == "getChannelRouterSettings")
|
||||
{
|
||||
ChannelRouterSettings::ptr result = this->model.GetChannelRouterSettings();
|
||||
this->Reply(replyTo, "getChannelRouterSettings", result);
|
||||
} else if (message == "setChannelRouterSettings") {
|
||||
ChannelRouterSettings::ptr args;
|
||||
pReader->read(&args);
|
||||
this->model.SetChannelRouterSettings(this->clientId, args);
|
||||
}
|
||||
else if (message == "downloadModelsFromTone3000")
|
||||
{
|
||||
@@ -2100,11 +2109,9 @@ private:
|
||||
Send("onShowStatusMonitorChanged", show);
|
||||
}
|
||||
|
||||
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
virtual void OnChannelRouterSettingsChanged(int64_t clientId, const ChannelRouterSettings &channelRouterSettings)
|
||||
{
|
||||
ChannelSelectionChangedBody body;
|
||||
body.clientId_ = clientId;
|
||||
body.jackChannelSelection_ = const_cast<JackChannelSelection *>(&channelSelection);
|
||||
ChannelRouterSettingsChangedBody body(clientId, channelRouterSettings);
|
||||
Send("onChannelSelectionChanged", body);
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -34,6 +34,7 @@
|
||||
#include "lv2/urid/urid.h"
|
||||
#include "lv2/ui/ui.h"
|
||||
#include "lv2/core/lv2.h"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
// #include "lv2.h"
|
||||
#include "lv2/atom/atom.h"
|
||||
@@ -315,15 +316,15 @@ PluginHost::PluginHost()
|
||||
this->urids = new Urids(mapFeature);
|
||||
}
|
||||
|
||||
void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
|
||||
|
||||
void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const ChannelSelection &channelSelection)
|
||||
{
|
||||
this->sampleRate = configuration.sampleRate();
|
||||
if (configuration.isValid())
|
||||
{
|
||||
this->numberOfAudioInputChannels = settings.GetInputAudioPorts().size();
|
||||
this->numberOfAudioOutputChannels = settings.GetOutputAudioPorts().size();
|
||||
this->maxBufferSize = configuration.blockLength();
|
||||
}
|
||||
this->channelSelection = channelSelection;
|
||||
}
|
||||
|
||||
PluginHost::~PluginHost()
|
||||
|
||||
+12
-10
@@ -43,6 +43,7 @@
|
||||
#include "PiPedalUI.hpp"
|
||||
#include "MapPathFeature.hpp"
|
||||
#include "ModGui.hpp"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
@@ -54,6 +55,7 @@ namespace pipedal
|
||||
class PluginHost;
|
||||
class JackConfiguration;
|
||||
class JackChannelSelection;
|
||||
class ChannelRouterSettings;
|
||||
|
||||
#ifndef LV2_PROPERTY_GETSET
|
||||
#define LV2_PROPERTY_GETSET(name) \
|
||||
@@ -893,8 +895,8 @@ namespace pipedal
|
||||
size_t maxBufferSize = 1024;
|
||||
size_t maxAtomBufferSize = 16 * 1024;
|
||||
bool hasMidiInputChannel;
|
||||
int numberOfAudioInputChannels = 1;
|
||||
int numberOfAudioOutputChannels = 1;
|
||||
ChannelSelection channelSelection;
|
||||
|
||||
double sampleRate = 48000;
|
||||
|
||||
std::string vst3CachePath;
|
||||
@@ -955,13 +957,12 @@ namespace pipedal
|
||||
|
||||
private:
|
||||
// IHost implementation.
|
||||
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
|
||||
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
|
||||
virtual size_t GetAtomBufferSize() const { return maxAtomBufferSize; }
|
||||
virtual bool HasMidiInputChannel() const { return hasMidiInputChannel; }
|
||||
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
|
||||
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
|
||||
virtual LV2_Feature *const *GetLv2Features() const { return (LV2_Feature *const *)&(this->lv2Features[0]); }
|
||||
virtual void SetMaxAudioBufferSize(size_t size) override { maxBufferSize = size; }
|
||||
virtual size_t GetMaxAudioBufferSize() const override { return maxBufferSize; }
|
||||
virtual size_t GetAtomBufferSize() const override { return maxAtomBufferSize; }
|
||||
virtual bool HasMidiInputChannel() const override { return hasMidiInputChannel; }
|
||||
virtual LV2_Feature *const *GetLv2Features() const override { return (LV2_Feature *const *)&(this->lv2Features[0]); }
|
||||
virtual const ChannelSelection &GetChannelSelection() const override { return this->channelSelection; }
|
||||
|
||||
public:
|
||||
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
|
||||
@@ -1027,7 +1028,8 @@ namespace pipedal
|
||||
Urids *urids = nullptr;
|
||||
ModGuiUris *mod_gui_uris = nullptr;
|
||||
|
||||
void OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings);
|
||||
void OnConfigurationChanged(const JackConfiguration &configuration, const ChannelSelection &channelSelection);
|
||||
|
||||
|
||||
std::shared_ptr<Lv2PluginClass> GetPluginClass(const std::string &uri) const;
|
||||
bool is_a(const std::string &class_, const std::string &target_class);
|
||||
|
||||
@@ -96,6 +96,24 @@ namespace pipedal
|
||||
|
||||
};
|
||||
|
||||
struct RealtimePedalboardItemIndex {
|
||||
RealtimePedalboardItemIndex()
|
||||
: instanceType(Pedalboard::PedalboardType::MainPedalboard), index(-1)
|
||||
{
|
||||
}
|
||||
RealtimePedalboardItemIndex(
|
||||
Pedalboard::PedalboardType instanceType,
|
||||
int64_t index)
|
||||
: instanceType(instanceType), index(index)
|
||||
{
|
||||
}
|
||||
RealtimePedalboardItemIndex(const RealtimePedalboardItemIndex& other) = default;
|
||||
RealtimePedalboardItemIndex& operator=(const RealtimePedalboardItemIndex& other) = default;
|
||||
|
||||
Pedalboard::PedalboardType instanceType = Pedalboard::PedalboardType::MainPedalboard;
|
||||
int64_t index = -1;
|
||||
};
|
||||
|
||||
struct RealtimeMidiEventRequest
|
||||
{
|
||||
RealtimeMidiEventType eventType;
|
||||
@@ -159,7 +177,7 @@ namespace pipedal
|
||||
return &vuUpdateResponseData;
|
||||
}
|
||||
|
||||
std::vector<int> enabledIndexes;
|
||||
std::vector<RealtimePedalboardItemIndex> enabledIndexes;
|
||||
std::vector<VuUpdate> vuUpdateWorkingData;
|
||||
std::vector<VuUpdate> vuUpdateResponseData;
|
||||
|
||||
|
||||
@@ -19,13 +19,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lilv/lilv.h"
|
||||
#include "lv2/state/state.h"
|
||||
#include <cstddef>
|
||||
#include "json_variant.hpp"
|
||||
#include "MapFeature.hpp"
|
||||
#include "IHost.hpp"
|
||||
#include "lilv/lilv.h"
|
||||
#include <lilv/lilv.h>
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
+187
-49
@@ -197,11 +197,11 @@ void Storage::SetDataRoot(const std::filesystem::path &path)
|
||||
this->dataRoot__audio_uploads = dataRoot / "audio_uploads";
|
||||
}
|
||||
|
||||
const std::filesystem::path &Storage::GetConfigRoot()
|
||||
const std::filesystem::path &Storage::GetConfigRoot() const
|
||||
{
|
||||
return this->configRoot;
|
||||
}
|
||||
const std::filesystem::path &Storage::GetDataRoot()
|
||||
const std::filesystem::path &Storage::GetDataRoot() const
|
||||
{
|
||||
return this->dataRoot;
|
||||
}
|
||||
@@ -250,7 +250,6 @@ static void removeFileNoThrow(const std::filesystem::path &path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Storage::UpgradeFactoryPresets()
|
||||
{
|
||||
auto presetsDirectory = this->GetPresetsDirectory();
|
||||
@@ -378,7 +377,7 @@ void Storage::Initialize()
|
||||
LoadCurrentBank();
|
||||
try
|
||||
{
|
||||
LoadChannelSelection();
|
||||
LoadJackChannelSelection();
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
@@ -387,8 +386,10 @@ void Storage::Initialize()
|
||||
|
||||
LoadWifiConfigSettings();
|
||||
LoadWifiDirectConfigSettings();
|
||||
this->channelRouterSettings = LoadChannelRouterSettings();
|
||||
LoadUserSettings();
|
||||
UpgradeFactoryPresets();
|
||||
UpgradeChannelRouterSettings();
|
||||
}
|
||||
|
||||
void Storage::LoadBank(int64_t instanceId)
|
||||
@@ -471,7 +472,7 @@ void Storage::LoadBankIndex()
|
||||
if (bankIndex.entries().size() == 0)
|
||||
{
|
||||
currentBank.clear();
|
||||
Pedalboard defaultPedalboard = Pedalboard::MakeDefault();
|
||||
Pedalboard defaultPedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
int64_t instanceId = currentBank.addPreset(defaultPedalboard);
|
||||
currentBank.selectedPreset(instanceId);
|
||||
|
||||
@@ -683,40 +684,46 @@ int64_t Storage::SaveCurrentPresetAs(const Pedalboard &pedalboard, int64_t bankI
|
||||
}
|
||||
}
|
||||
|
||||
static std::string stripNumericSuffix(const std::string &name) {
|
||||
static std::string stripNumericSuffix(const std::string &name)
|
||||
{
|
||||
// remove (digit*) from the end of the string.
|
||||
// Find the last '(' character
|
||||
size_t pos = name.find_last_of('(');
|
||||
if (pos == std::string::npos) {
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
// Check if everything between '(' and ')' is digits
|
||||
size_t len = name.length()-1;
|
||||
if (name[len] != ')')
|
||||
size_t len = name.length() - 1;
|
||||
if (name[len] != ')')
|
||||
{
|
||||
return name;
|
||||
}
|
||||
bool allDigits = true;
|
||||
for (size_t i = pos + 1; i < len - 1; ++i) {
|
||||
if (name[i] < '0' || name[i] > '9') {
|
||||
for (size_t i = pos + 1; i < len - 1; ++i)
|
||||
{
|
||||
if (name[i] < '0' || name[i] > '9')
|
||||
{
|
||||
allDigits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allDigits) {
|
||||
if (!allDigits)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
// Remove trailing spaces before the '('
|
||||
while (pos > 0 && name[pos - 1] == ' ') {
|
||||
while (pos > 0 && name[pos - 1] == ' ')
|
||||
{
|
||||
--pos;
|
||||
}
|
||||
|
||||
return name.substr(0, pos);
|
||||
}
|
||||
static std::string makeUniqueName(const std::string &name, const std::set<std::string>&existingNames)
|
||||
static std::string makeUniqueName(const std::string &name, const std::set<std::string> &existingNames)
|
||||
{
|
||||
if (!existingNames.contains(name))
|
||||
{
|
||||
@@ -725,9 +732,11 @@ static std::string makeUniqueName(const std::string &name, const std::set<std::s
|
||||
std::string baseName = stripNumericSuffix(name);
|
||||
|
||||
size_t i = 2;
|
||||
while (true) {
|
||||
while (true)
|
||||
{
|
||||
std::string newName = SS(baseName << " (" << i << ")");
|
||||
if (!existingNames.contains(newName)) {
|
||||
if (!existingNames.contains(newName))
|
||||
{
|
||||
return newName;
|
||||
}
|
||||
++i;
|
||||
@@ -735,23 +744,27 @@ static std::string makeUniqueName(const std::string &name, const std::set<std::s
|
||||
}
|
||||
int64_t Storage::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||
{
|
||||
if (bankIndex.selectedBank() == bankInstanceId) {
|
||||
if (bankIndex.selectedBank() == bankInstanceId)
|
||||
{
|
||||
throw std::runtime_error("Can't import to self.");
|
||||
}
|
||||
std::set<int64_t> presetsSet { presets.begin(), presets.end()};
|
||||
std::set<int64_t> presetsSet{presets.begin(), presets.end()};
|
||||
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
|
||||
|
||||
std::set<std::string> existingNames;
|
||||
|
||||
for (auto&preset: this->currentBank.presets()) {
|
||||
for (auto &preset : this->currentBank.presets())
|
||||
{
|
||||
existingNames.insert(preset->preset().name());
|
||||
}
|
||||
BankFile bankFile;
|
||||
LoadBankFile(indexEntry.name(),&bankFile);
|
||||
LoadBankFile(indexEntry.name(), &bankFile);
|
||||
int64_t lastPresetId = -1;
|
||||
for (auto &presetEntry: bankFile.presets()) {
|
||||
if (presetsSet.contains(presetEntry->instanceId())) {
|
||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(),existingNames);
|
||||
for (auto &presetEntry : bankFile.presets())
|
||||
{
|
||||
if (presetsSet.contains(presetEntry->instanceId()))
|
||||
{
|
||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(), existingNames);
|
||||
existingNames.insert(uniqueName);
|
||||
Pedalboard t = presetEntry->preset();
|
||||
t.name(uniqueName);
|
||||
@@ -763,31 +776,35 @@ int64_t Storage::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector
|
||||
}
|
||||
int64_t Storage::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||
{
|
||||
if (bankIndex.selectedBank() == bankInstanceId) {
|
||||
if (bankIndex.selectedBank() == bankInstanceId)
|
||||
{
|
||||
throw std::runtime_error("Can't copy to self.");
|
||||
}
|
||||
|
||||
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
|
||||
BankFile bankFile;
|
||||
LoadBankFile(indexEntry.name(),&bankFile);
|
||||
LoadBankFile(indexEntry.name(), &bankFile);
|
||||
|
||||
std::set<int64_t> presetsSet { presets.begin(), presets.end()};
|
||||
std::set<int64_t> presetsSet{presets.begin(), presets.end()};
|
||||
|
||||
std::set<std::string> existingNames;
|
||||
|
||||
for (auto&preset: bankFile.presets()) {
|
||||
for (auto &preset : bankFile.presets())
|
||||
{
|
||||
existingNames.insert(preset->preset().name());
|
||||
}
|
||||
for (auto &presetEntry: this->currentBank.presets()) {
|
||||
if (presetsSet.contains(presetEntry->instanceId())) {
|
||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(),existingNames);
|
||||
for (auto &presetEntry : this->currentBank.presets())
|
||||
{
|
||||
if (presetsSet.contains(presetEntry->instanceId()))
|
||||
{
|
||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(), existingNames);
|
||||
existingNames.insert(uniqueName);
|
||||
Pedalboard t = presetEntry->preset();
|
||||
t.name(uniqueName);
|
||||
bankFile.addPreset(t);
|
||||
}
|
||||
}
|
||||
SaveBankFile(indexEntry.name(),bankFile);
|
||||
SaveBankFile(indexEntry.name(), bankFile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -797,17 +814,15 @@ std::vector<PresetIndexEntry> Storage::RequestBankPresets(int64_t bankInstanceId
|
||||
|
||||
std::vector<PresetIndexEntry> result;
|
||||
BankFile bankFile;
|
||||
LoadBankFile(indexEntry.name(),&bankFile);
|
||||
for (auto &preset: bankFile.presets()) {
|
||||
LoadBankFile(indexEntry.name(), &bankFile);
|
||||
for (auto &preset : bankFile.presets())
|
||||
{
|
||||
result.push_back(
|
||||
PresetIndexEntry(preset->instanceId(),preset->preset().name())
|
||||
);
|
||||
PresetIndexEntry(preset->instanceId(), preset->preset().name()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Storage::SetPresetIndex(const PresetIndex &presets)
|
||||
{
|
||||
// painful because we must move unique_ptrs.
|
||||
@@ -881,7 +896,8 @@ Pedalboard Storage::GetPreset(int64_t instanceId) const
|
||||
int64_t Storage::DeletePresets(const std::vector<int64_t> &presetInstanceIds)
|
||||
{
|
||||
int64_t newSelection = currentBank.selectedPreset();
|
||||
for (auto presetId: presetInstanceIds) {
|
||||
for (auto presetId : presetInstanceIds)
|
||||
{
|
||||
newSelection = currentBank.deletePreset(presetId);
|
||||
}
|
||||
SaveCurrentBank();
|
||||
@@ -973,7 +989,7 @@ int64_t Storage::CreateNewPreset()
|
||||
}
|
||||
}
|
||||
newPedalboard.name(name);
|
||||
auto t = this->currentBank.addPreset(newPedalboard, -1);
|
||||
auto t = this->currentBank.addPreset(newPedalboard, -1);
|
||||
SaveCurrentBank();
|
||||
return t;
|
||||
}
|
||||
@@ -993,7 +1009,7 @@ int64_t Storage::CopyPreset(int64_t fromId, int64_t toId)
|
||||
{
|
||||
auto &toItem = this->currentBank.getItem(toId);
|
||||
toItem.preset(fromItem.preset());
|
||||
result = toId;
|
||||
result = toId;
|
||||
}
|
||||
SaveCurrentBank();
|
||||
return result;
|
||||
@@ -1115,7 +1131,7 @@ AlsaSequencerConfiguration Storage::GetAlsaSequencerConfiguration() const
|
||||
return this->alsaSequencerConfiguration;
|
||||
}
|
||||
|
||||
void Storage::LoadChannelSelection()
|
||||
void Storage::LoadJackChannelSelection()
|
||||
{
|
||||
auto fileName = this->GetChannelSelectionFileName();
|
||||
if (std::filesystem::exists(fileName))
|
||||
@@ -2071,21 +2087,21 @@ static bool ensureNoDotDot(const std::filesystem::path &path)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isInfoFile(const std::string&fileName)
|
||||
static bool isInfoFile(const std::string &fileName)
|
||||
{
|
||||
if (strcasecmp(fileName.c_str(),"license.txt") == 0)
|
||||
if (strcasecmp(fileName.c_str(), "license.txt") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp(fileName.c_str(),"license.md") == 0)
|
||||
if (strcasecmp(fileName.c_str(), "license.md") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp(fileName.c_str(),"readme.txt") == 0)
|
||||
if (strcasecmp(fileName.c_str(), "readme.txt") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp(fileName.c_str(),"readme.md") == 0)
|
||||
if (strcasecmp(fileName.c_str(), "readme.md") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2983,11 +2999,13 @@ const PluginPresetIndex &Storage::GetPluginPresetIndex()
|
||||
|
||||
std::filesystem::path Storage::FromAbstractPathString(const std::string &stringPath)
|
||||
{
|
||||
if (stringPath.empty()) {
|
||||
if (stringPath.empty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
fs::path path { stringPath};
|
||||
if (path.is_absolute()) {
|
||||
fs::path path{stringPath};
|
||||
if (path.is_absolute())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
return GetPluginUploadDirectory() / path;
|
||||
@@ -3010,6 +3028,126 @@ std::string Storage::FromAbstractPathJson(const std::string &pathJson)
|
||||
return v.to_string();
|
||||
}
|
||||
|
||||
void Storage::SetChannelRouterSettings(ChannelRouterSettings::ptr settings)
|
||||
{
|
||||
this->channelRouterSettings = settings;
|
||||
this->channelSelection = ChannelSelection(*settings);
|
||||
SaveChannelRouterSettings(settings);
|
||||
}
|
||||
ChannelRouterSettings::ptr Storage::GetChannelRouterSettings()
|
||||
{
|
||||
return channelRouterSettings;
|
||||
}
|
||||
|
||||
std::filesystem::path Storage::GetChannelRouterSettingsPath() const
|
||||
{
|
||||
return GetDataRoot() / "ChannelRouterSettings.json";
|
||||
}
|
||||
|
||||
void Storage::SaveChannelRouterSettings(ChannelRouterSettings::ptr settings)
|
||||
{
|
||||
std::ofstream os(GetChannelRouterSettingsPath());
|
||||
json_writer writer(os);
|
||||
writer.write(settings);
|
||||
}
|
||||
ChannelRouterSettings::ptr Storage::LoadChannelRouterSettings()
|
||||
{
|
||||
auto path = GetChannelRouterSettingsPath();
|
||||
if (!fs::exists(path))
|
||||
{
|
||||
// See UpgradeChannelRouterSettings(), which will upgrade from old settings
|
||||
// or create a default instance.
|
||||
return nullptr; // will be upgraded later.
|
||||
}
|
||||
try {
|
||||
std::ifstream is(path);
|
||||
json_reader reader(is);
|
||||
ChannelRouterSettings::ptr result;
|
||||
reader.read(&result);
|
||||
this->channelSelection = ChannelSelection(*result);
|
||||
return result;
|
||||
} catch (const std::exception &e) {
|
||||
Lv2Log::error("Failed to load Channel Router settings: %s", e.what());
|
||||
return std::make_shared<ChannelRouterSettings>();
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t GetUpgradedPortIndex(const std::string &portName)
|
||||
{
|
||||
auto nPos = portName.find_last_of('_');
|
||||
if (nPos != std::string::npos)
|
||||
{
|
||||
std::string channelStr = portName.substr(nPos + 1);
|
||||
try
|
||||
{
|
||||
int64_t channelIndex = std::stoll(channelStr);
|
||||
return channelIndex;
|
||||
} catch (const std::exception &) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
void Storage::UpgradeChannelRouterSettings()
|
||||
{
|
||||
if (channelRouterSettings == nullptr)
|
||||
{
|
||||
channelRouterSettings = std::make_shared<ChannelRouterSettings>();
|
||||
if (jackChannelSelection.isValid())
|
||||
{
|
||||
channelRouterSettings->configured(true);
|
||||
channelRouterSettings->mainInputChannels().resize(2);
|
||||
const std::vector<std::string>& oldInputs = jackChannelSelection.GetInputAudioPorts();
|
||||
std::vector<int64_t>& newInputs = channelRouterSettings->mainInputChannels();
|
||||
|
||||
if (oldInputs.size() ==1)
|
||||
{
|
||||
int64_t leftIndex = GetUpgradedPortIndex(oldInputs[0]);
|
||||
newInputs.resize(2);
|
||||
newInputs[0] = leftIndex;
|
||||
newInputs[1] = leftIndex;
|
||||
} else if (oldInputs.size() == 2) {
|
||||
newInputs.resize(2);
|
||||
for (size_t i = 0; i < std::min(oldInputs.size(), newInputs.size()); ++i)
|
||||
{
|
||||
int64_t portIndex = GetUpgradedPortIndex(oldInputs[i]);
|
||||
newInputs[i] = portIndex;
|
||||
}
|
||||
}
|
||||
const std::vector<std::string>& oldOutputs = jackChannelSelection.GetOutputAudioPorts();
|
||||
auto &newMainOutputs = channelRouterSettings->mainOutputChannels();
|
||||
if (oldOutputs.size() == 1) {
|
||||
int64_t leftIndex = GetUpgradedPortIndex(oldOutputs[0]);
|
||||
newMainOutputs.resize(2);
|
||||
newMainOutputs[0] = leftIndex;
|
||||
newMainOutputs[1] = leftIndex;
|
||||
} else if (oldOutputs.size() == 2) {
|
||||
newMainOutputs.resize(2);
|
||||
for (size_t i = 0; i < std::min(oldOutputs.size(), newMainOutputs.size()); ++i)
|
||||
{
|
||||
int64_t portIndex = GetUpgradedPortIndex(oldOutputs[i]);
|
||||
newMainOutputs[i] = portIndex;
|
||||
}
|
||||
}
|
||||
|
||||
auto &newAuxInputs = channelRouterSettings->auxInputChannels();
|
||||
newAuxInputs.resize(2);
|
||||
newAuxInputs[0] = -1;
|
||||
newAuxInputs[1] = -1;
|
||||
auto &newAuxOutputs = channelRouterSettings->auxOutputChannels();
|
||||
newAuxOutputs.resize(2);
|
||||
newAuxOutputs[0] = -1;
|
||||
newAuxOutputs[1] = -1;
|
||||
SaveChannelRouterSettings(channelRouterSettings);
|
||||
}
|
||||
channelSelection = ChannelSelection(*channelRouterSettings);
|
||||
}
|
||||
}
|
||||
|
||||
const ChannelSelection& Storage::GetChannelSelection() const {
|
||||
return channelSelection;
|
||||
}
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(UserSettings)
|
||||
JSON_MAP_REFERENCE(UserSettings, governor)
|
||||
|
||||
+19
-3
@@ -32,6 +32,8 @@
|
||||
#include <map>
|
||||
#include "FilePropertyDirectoryTree.hpp"
|
||||
#include "AlsaSequencer.hpp"
|
||||
#include "ChannelRouterSettings.hpp"
|
||||
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
@@ -96,7 +98,7 @@ private:
|
||||
void LoadCurrentBank();
|
||||
void SaveCurrentBank();
|
||||
|
||||
void LoadChannelSelection();
|
||||
void LoadJackChannelSelection();
|
||||
void SaveChannelSelection();
|
||||
|
||||
void LoadAlsaSequencerConfiguration();
|
||||
@@ -115,6 +117,15 @@ private:
|
||||
WifiDirectConfigSettings wifiDirectConfigSettings;
|
||||
|
||||
UserSettings userSettings;
|
||||
|
||||
ChannelRouterSettings::ptr channelRouterSettings;
|
||||
ChannelSelection channelSelection;
|
||||
|
||||
void UpgradeChannelRouterSettings();
|
||||
std::filesystem::path GetChannelRouterSettingsPath() const;
|
||||
void SaveChannelRouterSettings(ChannelRouterSettings::ptr settings);
|
||||
ChannelRouterSettings::ptr LoadChannelRouterSettings();
|
||||
|
||||
public:
|
||||
Storage();
|
||||
void Initialize();
|
||||
@@ -122,8 +133,8 @@ public:
|
||||
|
||||
void SetDataRoot(const std::filesystem::path& path);
|
||||
void SetConfigRoot(const std::filesystem::path& path);
|
||||
const std::filesystem::path&GetConfigRoot();
|
||||
const std::filesystem::path&GetDataRoot();
|
||||
const std::filesystem::path&GetConfigRoot() const;
|
||||
const std::filesystem::path&GetDataRoot() const;
|
||||
|
||||
const std::filesystem::path &GetPluginUploadDirectory() const;
|
||||
|
||||
@@ -204,6 +215,11 @@ public:
|
||||
void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration);
|
||||
AlsaSequencerConfiguration GetAlsaSequencerConfiguration() const;
|
||||
|
||||
void SetChannelRouterSettings(ChannelRouterSettings::ptr settings);
|
||||
ChannelRouterSettings::ptr GetChannelRouterSettings();
|
||||
|
||||
const ChannelSelection& GetChannelSelection() const;
|
||||
|
||||
|
||||
//std::string MapPropertyFileName(Lv2PluginInfo*pluginInfo, const std::string&path);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user