Sync
This commit is contained in:
@@ -57,6 +57,17 @@
|
|||||||
} \
|
} \
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
#define JSON_GETTER_SETTER_REF(name) \
|
||||||
|
const decltype(name##_) &name() const { return name##_; } \
|
||||||
|
decltype(name##_) &name() { return name##_; } \
|
||||||
|
void name(const decltype(name##_) &value) { name##_ = value; }
|
||||||
|
|
||||||
|
#define JSON_GETTER_SETTER(name) \
|
||||||
|
decltype(name##_) name() const { return name##_; } \
|
||||||
|
void name(decltype(name##_) value) { name##_ = value; }
|
||||||
|
|
||||||
|
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
{
|
{
|
||||||
class JsonException: public std::runtime_error {
|
class JsonException: public std::runtime_error {
|
||||||
|
|||||||
@@ -562,8 +562,8 @@ void json_reader::skip_array()
|
|||||||
|
|
||||||
void json_reader::skip_object()
|
void json_reader::skip_object()
|
||||||
{
|
{
|
||||||
int c;
|
|
||||||
consume('{');
|
consume('{');
|
||||||
|
int c;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
c = peek();
|
c = peek();
|
||||||
@@ -571,11 +571,11 @@ void json_reader::skip_object()
|
|||||||
if (c == '}')
|
if (c == '}')
|
||||||
{
|
{
|
||||||
c = get();
|
c = get();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
skip_string(); // name.
|
skip_string(); // name.
|
||||||
consume(':');
|
consume(':');
|
||||||
skip_object();
|
skip_property();
|
||||||
if (peek() == ',')
|
if (peek() == ',')
|
||||||
{
|
{
|
||||||
consume(',');
|
consume(',');
|
||||||
|
|||||||
+242
-95
@@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
#include "PiPedalCommon.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "Finally.hpp"
|
#include "Finally.hpp"
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
#include "CrashGuard.hpp"
|
#include "CrashGuard.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
#include "CpuUse.hpp"
|
#include "CpuUse.hpp"
|
||||||
|
|
||||||
@@ -328,11 +330,34 @@ namespace pipedal
|
|||||||
bool inputSwapped = false;
|
bool inputSwapped = false;
|
||||||
bool outputSwapped = false;
|
bool outputSwapped = false;
|
||||||
|
|
||||||
std::vector<float *> activeCaptureBuffers;
|
std::vector<std::vector<float>> allocatedBuffers;
|
||||||
std::vector<float *> activePlaybackBuffers;
|
|
||||||
|
|
||||||
std::vector<float *> captureBuffers;
|
std::set<int64_t> usedOutputChannels;
|
||||||
std::vector<float *> playbackBuffers;
|
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<float *> deviceCaptureBuffers;
|
||||||
|
std::vector<float *> devicePlaybackBuffers;
|
||||||
|
|
||||||
std::vector<uint8_t> rawCaptureBuffer;
|
std::vector<uint8_t> rawCaptureBuffer;
|
||||||
std::vector<uint8_t> rawPlaybackBuffer;
|
std::vector<uint8_t> rawPlaybackBuffer;
|
||||||
@@ -742,7 +767,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = getCaptureBuffer<int32_t>(rawCaptureBuffer);
|
int32_t *p = getCaptureBuffer<int32_t>(rawCaptureBuffer);
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
{
|
{
|
||||||
@@ -760,7 +785,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
float *p = getCaptureBuffer<float>(rawCaptureBuffer);
|
float *p = getCaptureBuffer<float>(rawCaptureBuffer);
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
{
|
{
|
||||||
@@ -776,7 +801,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int16_t *p = getCaptureBuffer<int16_t>(rawCaptureBuffer);
|
int16_t *p = getCaptureBuffer<int16_t>(rawCaptureBuffer);
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr double scale = 1.0f / (std::numeric_limits<int16_t>::max() + 1L);
|
constexpr double scale = 1.0f / (std::numeric_limits<int16_t>::max() + 1L);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -792,7 +817,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int16_t *p = getCaptureBuffer<int16_t>(rawCaptureBuffer);
|
int16_t *p = getCaptureBuffer<int16_t>(rawCaptureBuffer);
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (std::numeric_limits<int16_t>::max() + 1L);
|
constexpr float scale = 1.0f / (std::numeric_limits<int16_t>::max() + 1L);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -809,7 +834,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = getCaptureBuffer<int32_t>(rawCaptureBuffer);
|
int32_t *p = getCaptureBuffer<int32_t>(rawCaptureBuffer);
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1L);
|
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1L);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -825,7 +850,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
uint8_t *p = getCaptureBuffer<uint8_t>(rawCaptureBuffer);
|
uint8_t *p = getCaptureBuffer<uint8_t>(rawCaptureBuffer);
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1LL);
|
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1LL);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -842,7 +867,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
uint8_t *p = (uint8_t *)rawCaptureBuffer.data();
|
uint8_t *p = (uint8_t *)rawCaptureBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1LL);
|
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1LL);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -859,7 +884,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (0x00FFFFFFL + 1L);
|
constexpr float scale = 1.0f / (0x00FFFFFFL + 1L);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -875,7 +900,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (0x00FFFFFFL + 1L);
|
constexpr float scale = 1.0f / (0x00FFFFFFL + 1L);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -891,7 +916,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
int32_t *p = (int32_t *)rawCaptureBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->captureBuffers;
|
std::vector<float *> &buffers = this->deviceCaptureBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->captureChannels;
|
||||||
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1L);
|
constexpr float scale = 1.0f / (std::numeric_limits<int32_t>::max() + 1L);
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -907,7 +932,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int16_t *p = (int16_t *)rawPlaybackBuffer.data();
|
int16_t *p = (int16_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr float scale = std::numeric_limits<int16_t>::max();
|
constexpr float scale = std::numeric_limits<int16_t>::max();
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -927,7 +952,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int16_t *p = (int16_t *)rawPlaybackBuffer.data();
|
int16_t *p = (int16_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr float scale = std::numeric_limits<int16_t>::max();
|
constexpr float scale = std::numeric_limits<int16_t>::max();
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -947,7 +972,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -969,7 +994,7 @@ namespace pipedal
|
|||||||
|
|
||||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr double scale = 0x00FFFFFF;
|
constexpr double scale = 0x00FFFFFF;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -991,7 +1016,7 @@ namespace pipedal
|
|||||||
|
|
||||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr double scale = 0x00FFFFFF;
|
constexpr double scale = 0x00FFFFFF;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -1011,7 +1036,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
int32_t *p = (int32_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -1031,7 +1056,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
uint8_t *p = (uint8_t *)rawPlaybackBuffer.data();
|
uint8_t *p = (uint8_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -1056,7 +1081,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
uint8_t *p = (uint8_t *)rawPlaybackBuffer.data();
|
uint8_t *p = (uint8_t *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
constexpr double scale = std::numeric_limits<int32_t>::max();
|
constexpr double scale = std::numeric_limits<int32_t>::max();
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
@@ -1082,7 +1107,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
float *p = (float *)rawPlaybackBuffer.data();
|
float *p = (float *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
{
|
{
|
||||||
@@ -1097,7 +1122,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
float *p = (float *)rawPlaybackBuffer.data();
|
float *p = (float *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->devicePlaybackBuffers;
|
||||||
int channels = this->playbackChannels;
|
int channels = this->playbackChannels;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
{
|
{
|
||||||
@@ -1127,9 +1152,9 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JackChannelSelection channelSelection;
|
ChannelSelection channelSelection;
|
||||||
bool open = false;
|
bool open = false;
|
||||||
virtual void Open(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||||
{
|
{
|
||||||
terminateAudio_ = false;
|
terminateAudio_ = false;
|
||||||
if (open)
|
if (open)
|
||||||
@@ -1249,7 +1274,7 @@ namespace pipedal
|
|||||||
rawCaptureBuffer.resize(captureFrameSize * bufferSize * 2);
|
rawCaptureBuffer.resize(captureFrameSize * bufferSize * 2);
|
||||||
memset(rawCaptureBuffer.data(), 0, rawCaptureBuffer.size());
|
memset(rawCaptureBuffer.data(), 0, rawCaptureBuffer.size());
|
||||||
|
|
||||||
AllocateBuffers(captureBuffers, captureChannels);
|
AllocateBuffers(deviceCaptureBuffers, captureChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string GetConfigurationDescription()
|
virtual std::string GetConfigurationDescription()
|
||||||
@@ -1260,8 +1285,15 @@ namespace pipedal
|
|||||||
<< ", " << GetAlsaFormatDescription(this->captureFormat)
|
<< ", " << GetAlsaFormatDescription(this->captureFormat)
|
||||||
<< ", " << this->sampleRate
|
<< ", " << this->sampleRate
|
||||||
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
||||||
<< ", in: " << this->InputBufferCount() << "/" << this->captureChannels
|
<< ", " << "device in: " << this->DeviceInputBufferCount()
|
||||||
<< ", out: " << this->OutputBufferCount() << "/" << this->playbackChannels);
|
<< ", " << "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;
|
return result;
|
||||||
}
|
}
|
||||||
void PreparePlaybackFunctions(snd_pcm_format_t playbackFormat)
|
void PreparePlaybackFunctions(snd_pcm_format_t playbackFormat)
|
||||||
@@ -1321,10 +1353,10 @@ namespace pipedal
|
|||||||
rawPlaybackBuffer.resize(playbackFrameSize * bufferSize);
|
rawPlaybackBuffer.resize(playbackFrameSize * bufferSize);
|
||||||
memset(rawPlaybackBuffer.data(), 0, 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};
|
std::lock_guard lock{restartMutex};
|
||||||
|
|
||||||
@@ -1717,7 +1749,7 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void AudioThread()
|
PIPEDAL_NON_INLINE void AudioThread()
|
||||||
{
|
{
|
||||||
SetThreadName("alsaDriver");
|
SetThreadName("alsaDriver");
|
||||||
|
|
||||||
@@ -1797,6 +1829,19 @@ namespace pipedal
|
|||||||
|
|
||||||
cpuUse.AddSample(ProfileCategory::Execute);
|
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);
|
(this->*copyOutputFn)(framesRead);
|
||||||
cpuUse.AddSample(ProfileCategory::Driver);
|
cpuUse.AddSample(ProfileCategory::Driver);
|
||||||
// process.
|
// process.
|
||||||
@@ -1824,9 +1869,9 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
this->driverHost->OnAlsaDriverStopped();
|
this->driverHost->OnAlsaDriverStopped();
|
||||||
// zero out input buffers.
|
// 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)
|
for (size_t j = 0; j < this->bufferSize; ++j)
|
||||||
{
|
{
|
||||||
pBuffer[j] = 0;
|
pBuffer[j] = 0;
|
||||||
@@ -1836,7 +1881,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
while (!terminateAudio())
|
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);
|
this->driverHost->OnProcess(this->bufferSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1849,63 +1894,129 @@ namespace pipedal
|
|||||||
|
|
||||||
bool alsaActive = false;
|
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);
|
PIPEDAL_NON_INLINE void AllocateInputChannels(
|
||||||
if (v < 0)
|
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;
|
bool activated = false;
|
||||||
virtual void Activate()
|
PIPEDAL_NON_INLINE virtual void Activate()
|
||||||
{
|
{
|
||||||
if (activated)
|
if (activated)
|
||||||
{
|
{
|
||||||
throw PiPedalStateException("Already activated.");
|
throw PiPedalStateException("Already activated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
activated = true;
|
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);
|
deviceCaptureBuffers[i] = AllocateAudioBuffer();
|
||||||
if (sourceIndex >= captureBuffers.size())
|
}
|
||||||
{
|
devicePlaybackBuffers.resize(playbackChannels);
|
||||||
Lv2Log::error(SS("Invalid audio input port: " << x));
|
for (size_t i = 0; i < playbackChannels; ++i)
|
||||||
}
|
{
|
||||||
else
|
devicePlaybackBuffers[i] = AllocateAudioBuffer();
|
||||||
{
|
|
||||||
this->activeCaptureBuffers[ix++] = this->captureBuffers[sourceIndex];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->activePlaybackBuffers.resize(channelSelection.GetOutputAudioPorts().size());
|
AllocateInputChannels(
|
||||||
|
channelSelection.mainInputChannels(),
|
||||||
ix = 0;
|
this->mainCaptureBuffers);
|
||||||
for (auto &x : channelSelection.GetOutputAudioPorts())
|
AllocateOutputChannels(
|
||||||
{
|
channelSelection.mainOutputChannels(),
|
||||||
int sourceIndex = IndexFromPortName(x);
|
this->mainPlaybackBuffers
|
||||||
if (sourceIndex >= playbackBuffers.size())
|
);
|
||||||
{
|
AllocateInputChannels(
|
||||||
Lv2Log::error(SS("Invalid audio output port: " << x));
|
channelSelection.auxInputChannels(),
|
||||||
}
|
this->auxCaptureBuffers);
|
||||||
else
|
AllocateOutputChannels(
|
||||||
{
|
channelSelection.auxOutputChannels(),
|
||||||
this->activePlaybackBuffers[ix++] = this->playbackBuffers[sourceIndex];
|
this->auxPlaybackBuffers
|
||||||
}
|
);
|
||||||
}
|
AllocateInputChannels(
|
||||||
|
channelSelection.sendInputChannels(),
|
||||||
|
this->sendCaptureBuffers);
|
||||||
|
AllocateOutputChannels(
|
||||||
|
channelSelection.sendOutputChannels(),
|
||||||
|
this->sendPlaybackBuffers
|
||||||
|
);
|
||||||
|
|
||||||
audioThread = std::make_unique<std::jthread>([this]()
|
audioThread = std::make_unique<std::jthread>([this]()
|
||||||
{ AudioThread(); });
|
{ AudioThread(); });
|
||||||
@@ -1941,10 +2052,43 @@ namespace pipedal
|
|||||||
this->alsaSequencer = alsaSequencer;
|
this->alsaSequencer = alsaSequencer;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual size_t InputBufferCount() const { return activeCaptureBuffers.size(); }
|
virtual size_t DeviceInputBufferCount() const override {
|
||||||
virtual float *GetInputBuffer(size_t channel) 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
|
virtual size_t GetMidiInputEventCount() override
|
||||||
@@ -1956,27 +2100,30 @@ namespace pipedal
|
|||||||
return this->midiEvents.data();
|
return this->midiEvents.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual size_t OutputBufferCount() const { return activePlaybackBuffers.size(); }
|
virtual size_t MainOutputBufferCount() const { return mainPlaybackBuffers.size(); }
|
||||||
virtual float *GetOutputBuffer(size_t channel) override
|
virtual float *GetMainOutputBuffer(size_t channel) override
|
||||||
{
|
{
|
||||||
return activePlaybackBuffers[channel];
|
return mainPlaybackBuffers[channel];
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeBuffers(std::vector<float *> &buffer)
|
float* AllocateAudioBuffer() {
|
||||||
{
|
std::vector<float> buffer;
|
||||||
for (size_t i = 0; i < buffer.size(); ++i)
|
buffer.resize(this->bufferSize);
|
||||||
{
|
float*pBuffer = buffer.data();
|
||||||
delete[] buffer[i];
|
allocatedBuffers.push_back(std::move(buffer));
|
||||||
buffer[i] = 0;
|
return pBuffer;
|
||||||
}
|
|
||||||
buffer.clear();
|
|
||||||
}
|
}
|
||||||
void DeleteBuffers()
|
void DeleteBuffers()
|
||||||
{
|
{
|
||||||
activeCaptureBuffers.clear();
|
mainCaptureBuffers.clear();
|
||||||
activePlaybackBuffers.clear();
|
mainPlaybackBuffers.clear();
|
||||||
FreeBuffers(this->playbackBuffers);
|
auxCaptureBuffers.clear();
|
||||||
FreeBuffers(this->captureBuffers);
|
auxPlaybackBuffers.clear();
|
||||||
|
sendCaptureBuffers.clear();
|
||||||
|
sendPlaybackBuffers.clear();
|
||||||
|
zeroInputBuffer = nullptr;
|
||||||
|
discardOutputBuffer = nullptr;
|
||||||
|
allocatedBuffers.clear();
|
||||||
}
|
}
|
||||||
virtual void Close()
|
virtual void Close()
|
||||||
{
|
{
|
||||||
@@ -2241,7 +2388,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
value += 1.0f * (c) / (128.0 * 256.0 * 256.0);
|
value += 1.0f * (c) / (128.0 * 256.0 * 256.0);
|
||||||
}
|
}
|
||||||
this->playbackBuffers[c][i] = value;
|
this->devicePlaybackBuffers[c][i] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2257,7 +2404,7 @@ namespace pipedal
|
|||||||
for (size_t c = 0; c < captureChannels; ++c)
|
for (size_t c = 0; c < captureChannels; ++c)
|
||||||
{
|
{
|
||||||
float error =
|
float error =
|
||||||
this->captureBuffers[c][i] - this->playbackBuffers[c][i];
|
this->deviceCaptureBuffers[c][i] - this->devicePlaybackBuffers[c][i];
|
||||||
|
|
||||||
assert(std::abs(error) < 4e-5);
|
assert(std::abs(error) < 4e-5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,16 +218,16 @@ public:
|
|||||||
if (testType == TestType::NullTest) return;
|
if (testType == TestType::NullTest) return;
|
||||||
|
|
||||||
|
|
||||||
size_t inputs = audioDriver->InputBufferCount();
|
size_t inputs = audioDriver->MainInputBufferCount();
|
||||||
size_t outputs = audioDriver->OutputBufferCount();
|
size_t outputs = audioDriver->MainOutputBufferCount();
|
||||||
|
|
||||||
for (size_t i = 0; i < inputs; ++i)
|
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)
|
for (size_t i = 0; i < outputs; ++i)
|
||||||
{
|
{
|
||||||
outputBuffers[i] = audioDriver->GetOutputBuffer(i);
|
outputBuffers[i] = audioDriver->GetMainOutputBuffer(i);
|
||||||
}
|
}
|
||||||
if (this->testType == TestType::Oscillator)
|
if (this->testType == TestType::Oscillator)
|
||||||
{
|
{
|
||||||
|
|||||||
+19
-5
@@ -35,6 +35,8 @@ namespace pipedal {
|
|||||||
|
|
||||||
using ProcessCallback = std::function<void (size_t)>;
|
using ProcessCallback = std::function<void (size_t)>;
|
||||||
|
|
||||||
|
class ChannelSelection;
|
||||||
|
|
||||||
|
|
||||||
class AudioDriverHost {
|
class AudioDriverHost {
|
||||||
public:
|
public:
|
||||||
@@ -58,14 +60,26 @@ namespace pipedal {
|
|||||||
virtual size_t GetMidiInputEventCount() = 0;
|
virtual size_t GetMidiInputEventCount() = 0;
|
||||||
virtual MidiEvent*GetMidiEvents() = 0;
|
virtual MidiEvent*GetMidiEvents() = 0;
|
||||||
|
|
||||||
virtual size_t InputBufferCount() const = 0;
|
virtual size_t DeviceInputBufferCount() const = 0;
|
||||||
virtual float*GetInputBuffer(size_t channel) = 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 size_t AuxInputBufferCount() const = 0;
|
||||||
virtual float*GetOutputBuffer(size_t channe) = 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 SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) = 0;
|
||||||
virtual void Activate() = 0;
|
virtual void Activate() = 0;
|
||||||
virtual void Deactivate() = 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
|
// 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.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "PiPedalCommon.hpp"
|
||||||
#include "AudioHost.hpp"
|
#include "AudioHost.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include <lv2/atom/atom.h>
|
#include <lv2/atom/atom.h>
|
||||||
@@ -519,12 +520,17 @@ private:
|
|||||||
SystemMidiBinding rebootMidiBinding;
|
SystemMidiBinding rebootMidiBinding;
|
||||||
SystemMidiBinding shutdownMidiBinding;
|
SystemMidiBinding shutdownMidiBinding;
|
||||||
|
|
||||||
JackChannelSelection channelSelection;
|
ChannelSelection channelSelection;
|
||||||
std::atomic<bool> active = false;
|
std::atomic<bool> active = false;
|
||||||
std::atomic<bool> audioStopped = false;
|
std::atomic<bool> audioStopped = false;
|
||||||
std::atomic<bool> isDummyAudioDriver = false;
|
std::atomic<bool> isDummyAudioDriver = false;
|
||||||
|
|
||||||
std::shared_ptr<Lv2Pedalboard> currentPedalboard;
|
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.
|
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
||||||
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
||||||
|
|
||||||
@@ -620,9 +626,9 @@ private:
|
|||||||
|
|
||||||
void ZeroOutputBuffers(size_t nframes)
|
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)
|
if (out)
|
||||||
{
|
{
|
||||||
ZeroBuffer(out, nframes);
|
ZeroBuffer(out, nframes);
|
||||||
@@ -1173,9 +1179,9 @@ private:
|
|||||||
float *inputBuffers[4];
|
float *inputBuffers[4];
|
||||||
float *outputBuffers[4];
|
float *outputBuffers[4];
|
||||||
bool buffersValid = true;
|
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)
|
if (input == nullptr)
|
||||||
{
|
{
|
||||||
buffersValid = false;
|
buffersValid = false;
|
||||||
@@ -1183,11 +1189,11 @@ private:
|
|||||||
}
|
}
|
||||||
inputBuffers[i] = input;
|
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)
|
if (output == nullptr)
|
||||||
{
|
{
|
||||||
buffersValid = false;
|
buffersValid = false;
|
||||||
@@ -1195,7 +1201,7 @@ private:
|
|||||||
}
|
}
|
||||||
outputBuffers[i] = output;
|
outputBuffers[i] = output;
|
||||||
}
|
}
|
||||||
outputBuffers[audioDriver->OutputBufferCount()] = nullptr;
|
outputBuffers[audioDriver->MainOutputBufferCount()] = nullptr;
|
||||||
|
|
||||||
if (buffersValid)
|
if (buffersValid)
|
||||||
{
|
{
|
||||||
@@ -1670,7 +1676,7 @@ public:
|
|||||||
return result;
|
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);
|
std::lock_guard guard(mutex);
|
||||||
@@ -1692,11 +1698,6 @@ public:
|
|||||||
this->audioDriver = std::unique_ptr<AudioDriver>(CreateAlsaDriver(this));
|
this->audioDriver = std::unique_ptr<AudioDriver>(CreateAlsaDriver(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channelSelection.GetInputAudioPorts().size() == 0 || channelSelection.GetOutputAudioPorts().size() == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->currentSample = 0;
|
this->currentSample = 0;
|
||||||
this->underruns = 0;
|
this->underruns = 0;
|
||||||
|
|
||||||
@@ -1707,7 +1708,7 @@ public:
|
|||||||
this->realtimeReader.Reset();
|
this->realtimeReader.Reset();
|
||||||
this->realtimeWriter.Reset();
|
this->realtimeWriter.Reset();
|
||||||
|
|
||||||
this->channelSelection = channelSelection;
|
this->channelSelection = channelSelection_;
|
||||||
|
|
||||||
StartReaderThread();
|
StartReaderThread();
|
||||||
|
|
||||||
@@ -1795,6 +1796,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual void SetPluginPreset(uint64_t instanceId, const std::vector<ControlValue> &values)
|
virtual void SetPluginPreset(uint64_t instanceId, const std::vector<ControlValue> &values)
|
||||||
{
|
{
|
||||||
std::lock_guard guard(mutex);
|
std::lock_guard guard(mutex);
|
||||||
@@ -1896,6 +1898,49 @@ public:
|
|||||||
pendingSnapshots.clear();
|
pendingSnapshots.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PIPEDAL_NON_INLINE RealtimePedalboardItemIndex GetRealtimeItemIndex(int64_t instanceId)
|
||||||
|
{
|
||||||
|
Pedalboard::InstanceType instanceType = Pedalboard::GetInstanceTypeFromInstanceId(instanceId);
|
||||||
|
int64_t index = -1;
|
||||||
|
switch (instanceType)
|
||||||
|
{
|
||||||
|
case Pedalboard::InstanceType::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::InstanceType::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::InstanceType::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)
|
virtual void SetVuSubscriptions(const std::vector<int64_t> &instanceIds)
|
||||||
{
|
{
|
||||||
std::lock_guard guard(mutex);
|
std::lock_guard guard(mutex);
|
||||||
@@ -1917,7 +1962,7 @@ public:
|
|||||||
auto effect = this->currentPedalboard->GetEffect(instanceId);
|
auto effect = this->currentPedalboard->GetEffect(instanceId);
|
||||||
if (effect)
|
if (effect)
|
||||||
{
|
{
|
||||||
int index = this->currentPedalboard->GetIndexOfInstanceId(instanceIds[i]);
|
RealtimePedalboardItemIndex index = this->GetRealtimeItemIndex(instanceIds[i]);
|
||||||
vuConfig->enabledIndexes.push_back(index);
|
vuConfig->enabledIndexes.push_back(index);
|
||||||
VuUpdate v;
|
VuUpdate v;
|
||||||
v.instanceId_ = instanceId;
|
v.instanceId_ = instanceId;
|
||||||
@@ -1929,20 +1974,41 @@ public:
|
|||||||
vuConfig->vuUpdateWorkingData.push_back(v);
|
vuConfig->vuUpdateWorkingData.push_back(v);
|
||||||
vuConfig->vuUpdateResponseData.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;
|
VuUpdate v;
|
||||||
vuConfig->enabledIndexes.push_back(index);
|
vuConfig->enabledIndexes.push_back(index);
|
||||||
|
|
||||||
v.instanceId_ = instanceId;
|
v.instanceId_ = instanceId;
|
||||||
if (instanceId == Pedalboard::INPUT_VOLUME_ID)
|
size_t nChannels = 0;
|
||||||
|
|
||||||
|
switch (instanceId)
|
||||||
{
|
{
|
||||||
v.isStereoInput_ = v.isStereoOutput_ = this->pHost->GetNumberOfInputAudioChannels() > 1;
|
case Pedalboard::START_CONTROL_ID:
|
||||||
}
|
nChannels = this->pHost->GetChannelSelection().mainInputChannels().size();
|
||||||
else
|
break;
|
||||||
{
|
case Pedalboard::END_CONTROL_ID:
|
||||||
v.isStereoInput_ = v.isStereoOutput_ = this->pHost->GetNumberOfOutputAudioChannels() > 1;
|
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->vuUpdateWorkingData.push_back(v);
|
||||||
vuConfig->vuUpdateResponseData.push_back(v);
|
vuConfig->vuUpdateResponseData.push_back(v);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -32,6 +32,7 @@
|
|||||||
#include "Promise.hpp"
|
#include "Promise.hpp"
|
||||||
#include "json_variant.hpp"
|
#include "json_variant.hpp"
|
||||||
#include "RealtimeMidiEventType.hpp"
|
#include "RealtimeMidiEventType.hpp"
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
{
|
{
|
||||||
@@ -227,7 +228,7 @@ namespace pipedal
|
|||||||
|
|
||||||
virtual std::string AtomToJson(const LV2_Atom *atom) = 0;
|
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 Close() = 0;
|
||||||
|
|
||||||
virtual void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration) = 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.
|
// zero length? We can never have a zero-length bank.
|
||||||
// Add a default preset and make it the selected preset.
|
// Add a default preset and make it the selected preset.
|
||||||
Pedalboard pedalboard = Pedalboard::MakeDefault();
|
Pedalboard pedalboard = Pedalboard::MakeDefault(Pedalboard::InstanceType::MainPedalboard);
|
||||||
this->addPreset(pedalboard);
|
this->addPreset(pedalboard);
|
||||||
newSelection = presets_[0]->instanceId();
|
newSelection = presets_[0]->instanceId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set (PIPEDAL_SOURCES
|
set (PIPEDAL_SOURCES
|
||||||
|
ChannelRouterSettings.cpp ChannelRouterSettings.hpp
|
||||||
CrashGuard.cpp CrashGuard.hpp
|
CrashGuard.cpp CrashGuard.hpp
|
||||||
ModTemplateGenerator.cpp ModTemplateGenerator.hpp
|
ModTemplateGenerator.cpp ModTemplateGenerator.hpp
|
||||||
WebServerMod.cpp WebServerMod.hpp
|
WebServerMod.cpp WebServerMod.hpp
|
||||||
|
|||||||
@@ -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::InstanceType::MainInsert)
|
||||||
|
, auxInserts_(Pedalboard::InstanceType::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,107 @@
|
|||||||
|
// 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_; }
|
||||||
|
|
||||||
|
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
|
* 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
|
* 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
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
#include "PiPedalCommon.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
#include "ss.hpp"
|
#include "ss.hpp"
|
||||||
#include "SchedulerPriority.hpp"
|
#include "SchedulerPriority.hpp"
|
||||||
#include "CrashGuard.hpp"
|
#include "CrashGuard.hpp"
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
#include "CpuUse.hpp"
|
#include "CpuUse.hpp"
|
||||||
|
|
||||||
@@ -85,14 +87,19 @@ namespace pipedal
|
|||||||
uint32_t captureFrameSize = 0;
|
uint32_t captureFrameSize = 0;
|
||||||
|
|
||||||
|
|
||||||
std::vector<float *> activeCaptureBuffers;
|
std::vector<std::vector<float>> allocatedBuffers;
|
||||||
std::vector<float *> activePlaybackBuffers;
|
std::vector<float *> mainCaptureBuffers;
|
||||||
|
std::vector<float *> mainPlaybackBuffers;
|
||||||
|
|
||||||
std::vector<float *> captureBuffers;
|
std::vector<float *> auxCaptureBuffers;
|
||||||
std::vector<float *> playbackBuffers;
|
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;
|
AudioDriverHost *driverHost = nullptr;
|
||||||
uint32_t channels = 2;
|
uint32_t channels = 2;
|
||||||
@@ -145,7 +152,7 @@ namespace pipedal
|
|||||||
|
|
||||||
std::atomic<bool> terminateAudio_ = false;
|
std::atomic<bool> terminateAudio_ = false;
|
||||||
|
|
||||||
void terminateAudio(bool terminate)
|
PIPEDAL_NON_INLINE void terminateAudio(bool terminate)
|
||||||
{
|
{
|
||||||
this->terminateAudio_ = terminate;
|
this->terminateAudio_ = terminate;
|
||||||
}
|
}
|
||||||
@@ -161,32 +168,28 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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);
|
buffers.resize(n);
|
||||||
for (size_t i = 0; i < n; ++i)
|
for (size_t i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
buffers[i] = new float[this->bufferSize];
|
buffers[i] = AllocateAudioBuffer();
|
||||||
for (size_t j = 0; j < this->bufferSize; ++j)
|
|
||||||
{
|
|
||||||
buffers[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual size_t GetMidiInputEventCount() override
|
PIPEDAL_NON_INLINE virtual size_t GetMidiInputEventCount() override
|
||||||
{
|
{
|
||||||
return midiEventCount;
|
return midiEventCount;
|
||||||
}
|
}
|
||||||
virtual MidiEvent *GetMidiEvents() override
|
PIPEDAL_NON_INLINE virtual MidiEvent *GetMidiEvents() override
|
||||||
{
|
{
|
||||||
return this->midiEvents.data();
|
return this->midiEvents.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
JackChannelSelection channelSelection;
|
ChannelSelection channelSelection;
|
||||||
bool open = false;
|
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;
|
terminateAudio_ = false;
|
||||||
if (open)
|
if (open)
|
||||||
@@ -208,7 +211,7 @@ namespace pipedal
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
PIPEDAL_NON_INLINE virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
||||||
{
|
{
|
||||||
this->alsaSequencer = alsaSequencer;
|
this->alsaSequencer = alsaSequencer;
|
||||||
}
|
}
|
||||||
@@ -220,23 +223,28 @@ namespace pipedal
|
|||||||
<< ", " << "Native float"
|
<< ", " << "Native float"
|
||||||
<< ", " << this->sampleRate
|
<< ", " << this->sampleRate
|
||||||
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
||||||
<< ", in: " << this->InputBufferCount() << "/" << this->captureChannels
|
<< ", " << "device in: " << this->DeviceInputBufferCount()
|
||||||
<< ", out: " << this->OutputBufferCount() << "/" << this->playbackChannels);
|
<< ", " << "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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
void OpenAudio(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
||||||
this->bufferSize = jackServerSettings.GetBufferSize();
|
this->bufferSize = jackServerSettings.GetBufferSize();
|
||||||
AllocateBuffers(captureBuffers, channels);
|
|
||||||
AllocateBuffers(playbackBuffers, channels);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::jthread *audioThread;
|
std::unique_ptr<std::jthread> audioThread;
|
||||||
bool audioRunning;
|
bool audioRunning;
|
||||||
|
|
||||||
bool block = false;
|
bool block = false;
|
||||||
@@ -303,7 +311,7 @@ namespace pipedal
|
|||||||
this->driverHost->OnProcess(framesRead);
|
this->driverHost->OnProcess(framesRead);
|
||||||
|
|
||||||
/// no attempt at realtime. Just as long as we run occasionally.
|
/// 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;
|
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;
|
bool activated = false;
|
||||||
virtual void Activate()
|
virtual void Activate()
|
||||||
@@ -344,41 +336,17 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
activated = true;
|
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;
|
audioThread = std::make_unique<std::jthread>([this]()
|
||||||
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(); });
|
{ AudioThread(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +361,7 @@ namespace pipedal
|
|||||||
if (audioThread)
|
if (audioThread)
|
||||||
{
|
{
|
||||||
this->audioThread->join();
|
this->audioThread->join();
|
||||||
this->audioThread = 0;
|
this->audioThread = nullptr;
|
||||||
}
|
}
|
||||||
Lv2Log::debug("Audio thread joined.");
|
Lv2Log::debug("Audio thread joined.");
|
||||||
}
|
}
|
||||||
@@ -402,46 +370,64 @@ namespace pipedal
|
|||||||
public:
|
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 size_t MainInputBufferCount() const override { return mainCaptureBuffers.size(); }
|
||||||
virtual float *GetInputBuffer(size_t channel)
|
virtual float *GetMainInputBuffer(size_t channel) override
|
||||||
{
|
{
|
||||||
return activeCaptureBuffers[channel];
|
return mainCaptureBuffers[channel];
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual size_t OutputBufferCount() const { return activePlaybackBuffers.size(); }
|
virtual size_t MainOutputBufferCount() const { return mainPlaybackBuffers.size(); }
|
||||||
virtual float *GetOutputBuffer(size_t channel)
|
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)
|
return auxCaptureBuffers[channel];
|
||||||
{
|
|
||||||
// delete[] buffer[i];
|
|
||||||
buffer[i] = 0;
|
|
||||||
}
|
|
||||||
buffer.clear();
|
|
||||||
}
|
}
|
||||||
void DeleteBuffers()
|
|
||||||
|
virtual size_t AuxOutputBufferCount() const override { return auxPlaybackBuffers.size(); }
|
||||||
|
virtual float *GetAuxOutputBuffer(size_t channel) override
|
||||||
{
|
{
|
||||||
activeCaptureBuffers.clear();
|
return auxPlaybackBuffers[channel];
|
||||||
activePlaybackBuffers.clear();
|
|
||||||
FreeBuffers(this->playbackBuffers);
|
|
||||||
FreeBuffers(this->captureBuffers);
|
|
||||||
if (rawCaptureBuffer)
|
|
||||||
{
|
|
||||||
delete[] rawCaptureBuffer;
|
|
||||||
rawCaptureBuffer = nullptr;
|
|
||||||
}
|
|
||||||
if (rawPlaybackBuffer)
|
|
||||||
{
|
|
||||||
delete[] rawPlaybackBuffer;
|
|
||||||
rawPlaybackBuffer = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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)
|
if (!open)
|
||||||
{
|
{
|
||||||
@@ -453,18 +439,18 @@ namespace pipedal
|
|||||||
DeleteBuffers();
|
DeleteBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual float CpuUse()
|
PIPEDAL_NON_INLINE virtual float CpuUse()
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual float CpuOverhead()
|
PIPEDAL_NON_INLINE virtual float CpuOverhead()
|
||||||
{
|
{
|
||||||
return 0.1;
|
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);
|
return new DummyDriverImpl(driverHost,deviceName);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -26,6 +26,7 @@ namespace pipedal {
|
|||||||
class Lv2PluginInfo;
|
class Lv2PluginInfo;
|
||||||
class IEffect;
|
class IEffect;
|
||||||
class HostWorkerThread;
|
class HostWorkerThread;
|
||||||
|
class ChannelSelection;
|
||||||
|
|
||||||
class IHost
|
class IHost
|
||||||
{
|
{
|
||||||
@@ -43,13 +44,12 @@ namespace pipedal {
|
|||||||
virtual size_t GetAtomBufferSize() const = 0;
|
virtual size_t GetAtomBufferSize() const = 0;
|
||||||
|
|
||||||
virtual bool HasMidiInputChannel() 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 std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
|
||||||
|
|
||||||
virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0;
|
virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0;
|
||||||
|
|
||||||
virtual std::string GetPluginStoragePath() const = 0;
|
virtual std::string GetPluginStoragePath() const = 0;
|
||||||
|
virtual const ChannelSelection&GetChannelSelection() const = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -124,5 +124,4 @@ namespace pipedal
|
|||||||
DECLARE_JSON_MAP(JackChannelSelection);
|
DECLARE_JSON_MAP(JackChannelSelection);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace.
|
} // namespace.
|
||||||
@@ -574,7 +574,7 @@ void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samp
|
|||||||
{
|
{
|
||||||
int index = vuConfiguration->enabledIndexes[i];
|
int index = vuConfiguration->enabledIndexes[i];
|
||||||
VuUpdate *pUpdate = &vuConfiguration->vuUpdateWorkingData[i];
|
VuUpdate *pUpdate = &vuConfiguration->vuUpdateWorkingData[i];
|
||||||
if (index == Pedalboard::INPUT_VOLUME_ID)
|
if (index == Pedalboard::START_CONTROL_ID)
|
||||||
{
|
{
|
||||||
if (this->pedalboardInputBuffers.size() > 1)
|
if (this->pedalboardInputBuffers.size() > 1)
|
||||||
{
|
{
|
||||||
@@ -588,7 +588,7 @@ void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samp
|
|||||||
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]), samples); // after input volume applied.
|
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)
|
if (this->pedalboardOutputBuffers.size() > 1)
|
||||||
{
|
{
|
||||||
|
|||||||
+17
-1
@@ -210,7 +210,7 @@ PedalboardItem Pedalboard::MakeSplit()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Pedalboard Pedalboard::MakeDefault()
|
Pedalboard Pedalboard::MakeDefault(InstanceType instanceType)
|
||||||
{
|
{
|
||||||
// copy insanity. but it happens so rarely.
|
// copy insanity. but it happens so rarely.
|
||||||
Pedalboard result;
|
Pedalboard result;
|
||||||
@@ -510,6 +510,22 @@ Snapshot Pedalboard::MakeSnapshotFromCurrentSettings(const Pedalboard &previousP
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pedalboard::Pedalboard(InstanceType instanceType = InstanceType::MainPedalboard)
|
||||||
|
{
|
||||||
|
switch (instanceType)
|
||||||
|
{
|
||||||
|
case InstanceType::MainPedalboard:
|
||||||
|
nextInstanceId_ = 0;
|
||||||
|
break;
|
||||||
|
case InstanceType::MainInsert:
|
||||||
|
nextInstanceId_ = MAIN_INSERT_INSTANCE_BASE;;
|
||||||
|
break;
|
||||||
|
case InstanceType::AuxInsert:
|
||||||
|
nextInstanceId_ = AUX_INSERT_INSTANCE_BASE;;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
JSON_MAP_BEGIN(ControlValue)
|
JSON_MAP_BEGIN(ControlValue)
|
||||||
JSON_MAP_REFERENCE(ControlValue,key)
|
JSON_MAP_REFERENCE(ControlValue,key)
|
||||||
|
|||||||
+44
-3
@@ -230,11 +230,52 @@ class Pedalboard {
|
|||||||
|
|
||||||
int64_t selectedPlugin_ = -1;
|
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:
|
public:
|
||||||
|
|
||||||
|
enum class InstanceType {
|
||||||
|
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 InstanceType GetInstanceTypeFromInstanceId(int64_t instanceId) {
|
||||||
|
if (instanceId == START_CONTROL_ID) return InstanceType::MainPedalboard;
|
||||||
|
if (instanceId == END_CONTROL_ID) return InstanceType::MainPedalboard;
|
||||||
|
|
||||||
|
if (instanceId >= MAIN_INSERT_INSTANCE_BASE) {
|
||||||
|
return InstanceType::MainInsert;
|
||||||
|
} else if (instanceId >= AUX_INSERT_INSTANCE_BASE) {
|
||||||
|
return InstanceType::AuxInsert;
|
||||||
|
} else {
|
||||||
|
return InstanceType::MainPedalboard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstanceType GetInstanceType() const {
|
||||||
|
return GetInstanceTypeFromInstanceId(this->nextInstanceId_); // instanceId is irrelevant here.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Pedalboard(InstanceType instanceType = InstanceType::MainPedalboard);
|
||||||
|
|
||||||
// deep copy, breaking shared pointers.
|
// deep copy, breaking shared pointers.
|
||||||
Pedalboard DeepCopy();
|
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 SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
|
||||||
bool SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string&iconColor);
|
bool SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string&iconColor);
|
||||||
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
||||||
@@ -266,7 +307,7 @@ public:
|
|||||||
PedalboardItem MakeSplit();
|
PedalboardItem MakeSplit();
|
||||||
|
|
||||||
|
|
||||||
static Pedalboard MakeDefault();
|
static Pedalboard MakeDefault(Pedalboard::InstanceType instanceType);
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef GETTER_SETTER_REF
|
#undef GETTER_SETTER_REF
|
||||||
|
|||||||
@@ -364,16 +364,16 @@ public:
|
|||||||
virtual void OnProcess(size_t nFrames)
|
virtual void OnProcess(size_t nFrames)
|
||||||
{
|
{
|
||||||
|
|
||||||
size_t inputs = audioDriver->InputBufferCount();
|
size_t inputs = audioDriver->MainInputBufferCount();
|
||||||
size_t outputs = audioDriver->OutputBufferCount();
|
size_t outputs = audioDriver->MainOutputBufferCount();
|
||||||
|
|
||||||
for (size_t i = 0; i < inputs; ++i)
|
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)
|
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)
|
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
|
||||||
+29
-23
@@ -81,7 +81,7 @@ PiPedalModel::PiPedalModel()
|
|||||||
this->updater = Updater::Create();
|
this->updater = Updater::Create();
|
||||||
this->updater->Start();
|
this->updater->Start();
|
||||||
this->currentUpdateStatus = updater->GetCurrentStatus();
|
this->currentUpdateStatus = updater->GetCurrentStatus();
|
||||||
this->pedalboard = Pedalboard::MakeDefault();
|
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::InstanceType::MainPedalboard);
|
||||||
#if JACK_HOST
|
#if JACK_HOST
|
||||||
this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag.
|
this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag.
|
||||||
this->jackServerSettings.ReadJackDaemonConfiguration();
|
this->jackServerSettings.ReadJackDaemonConfiguration();
|
||||||
@@ -216,6 +216,7 @@ void PiPedalModel::Init(const PiPedalConfiguration &configuration)
|
|||||||
storage.Initialize();
|
storage.Initialize();
|
||||||
pluginHost.SetPluginStoragePath(storage.GetPluginUploadDirectory());
|
pluginHost.SetPluginStoragePath(storage.GetPluginUploadDirectory());
|
||||||
|
|
||||||
|
this->channelRouterSettings = storage.GetChannelRouterSettings();
|
||||||
this->systemMidiBindings = storage.GetSystemMidiBindings();
|
this->systemMidiBindings = storage.GetSystemMidiBindings();
|
||||||
|
|
||||||
#if JACK_HOST
|
#if JACK_HOST
|
||||||
@@ -298,7 +299,7 @@ void PiPedalModel::Load()
|
|||||||
if (CrashGuard::HasCrashed())
|
if (CrashGuard::HasCrashed())
|
||||||
{
|
{
|
||||||
// ignore the current preset, and load a blank pedalboard in order to avoid a potential plugin crash.
|
// 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::InstanceType::MainPedalboard);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1422,12 +1423,8 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
|
|||||||
throw std::runtime_error("Audio configuration not valid.");
|
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->audioHost->Open(jackServerSettings, channelSelection);
|
||||||
|
|
||||||
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
|
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||||
@@ -1528,21 +1525,6 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
|
|||||||
return result;
|
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::FireChannelSelectionChanged(int64_t clientId)
|
||||||
{
|
{
|
||||||
@@ -1671,7 +1653,7 @@ void PiPedalModel::UpdateRealtimeVuSubscriptions()
|
|||||||
for (int i = 0; i < activeVuSubscriptions.size(); ++i)
|
for (int i = 0; i < activeVuSubscriptions.size(); ++i)
|
||||||
{
|
{
|
||||||
auto instanceId = activeVuSubscriptions[i].instanceid;
|
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);
|
addedInstances.insert(activeVuSubscriptions[i].instanceid);
|
||||||
}
|
}
|
||||||
@@ -3330,3 +3312,27 @@ int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vecto
|
|||||||
return lastAdded;
|
return lastAdded;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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->FireChannelSelectionChanged(clientId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include "Promise.hpp"
|
#include "Promise.hpp"
|
||||||
#include "AtomConverter.hpp"
|
#include "AtomConverter.hpp"
|
||||||
#include "FileEntry.hpp"
|
#include "FileEntry.hpp"
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
@@ -176,6 +177,8 @@ namespace pipedal
|
|||||||
AtomConverter atomConverter; // must be AFTER pluginHost!
|
AtomConverter atomConverter; // must be AFTER pluginHost!
|
||||||
|
|
||||||
Pedalboard pedalboard;
|
Pedalboard pedalboard;
|
||||||
|
ChannelRouterSettings::ptr channelRouterSettings;
|
||||||
|
|
||||||
bool previousPedalboardLoaded = false;
|
bool previousPedalboardLoaded = false;
|
||||||
Pedalboard previousPedalboard;
|
Pedalboard previousPedalboard;
|
||||||
Storage storage;
|
Storage storage;
|
||||||
@@ -397,7 +400,6 @@ namespace pipedal
|
|||||||
|
|
||||||
JackConfiguration GetJackConfiguration();
|
JackConfiguration GetJackConfiguration();
|
||||||
|
|
||||||
void SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection);
|
|
||||||
JackChannelSelection GetJackChannelSelection();
|
JackChannelSelection GetJackChannelSelection();
|
||||||
|
|
||||||
void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration);
|
void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration);
|
||||||
@@ -497,6 +499,9 @@ namespace pipedal
|
|||||||
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
|
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
|
||||||
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||||
int64_t CopyPresetsToBank(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.
|
} // namespace pipedal.
|
||||||
@@ -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
|
// 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
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
@@ -1422,12 +1422,6 @@ public:
|
|||||||
this->model.AddNotificationSubscription(shared_from_this());
|
this->model.AddNotificationSubscription(shared_from_this());
|
||||||
Reply(replyTo, "ehlo", clientId);
|
Reply(replyTo, "ehlo", clientId);
|
||||||
}
|
}
|
||||||
else if (message == "setJackSettings")
|
|
||||||
{
|
|
||||||
JackChannelSelection jackSettings;
|
|
||||||
pReader->read(&jackSettings);
|
|
||||||
this->model.SetJackChannelSelection(this->clientId, jackSettings);
|
|
||||||
}
|
|
||||||
else if (message == "setShowStatusMonitor")
|
else if (message == "setShowStatusMonitor")
|
||||||
{
|
{
|
||||||
bool showStatusMonitor;
|
bool showStatusMonitor;
|
||||||
@@ -1847,6 +1841,14 @@ public:
|
|||||||
pReader->read(&args);
|
pReader->read(&args);
|
||||||
auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_);
|
auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_);
|
||||||
this->Reply(replyTo,"copyPresetsToBank",result);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-3
@@ -34,6 +34,7 @@
|
|||||||
#include "lv2/urid/urid.h"
|
#include "lv2/urid/urid.h"
|
||||||
#include "lv2/ui/ui.h"
|
#include "lv2/ui/ui.h"
|
||||||
#include "lv2/core/lv2.h"
|
#include "lv2/core/lv2.h"
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
// #include "lv2.h"
|
// #include "lv2.h"
|
||||||
#include "lv2/atom/atom.h"
|
#include "lv2/atom/atom.h"
|
||||||
@@ -315,15 +316,15 @@ PluginHost::PluginHost()
|
|||||||
this->urids = new Urids(mapFeature);
|
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();
|
this->sampleRate = configuration.sampleRate();
|
||||||
if (configuration.isValid())
|
if (configuration.isValid())
|
||||||
{
|
{
|
||||||
this->numberOfAudioInputChannels = settings.GetInputAudioPorts().size();
|
|
||||||
this->numberOfAudioOutputChannels = settings.GetOutputAudioPorts().size();
|
|
||||||
this->maxBufferSize = configuration.blockLength();
|
this->maxBufferSize = configuration.blockLength();
|
||||||
}
|
}
|
||||||
|
this->channelSelection = channelSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginHost::~PluginHost()
|
PluginHost::~PluginHost()
|
||||||
|
|||||||
+12
-10
@@ -43,6 +43,7 @@
|
|||||||
#include "PiPedalUI.hpp"
|
#include "PiPedalUI.hpp"
|
||||||
#include "MapPathFeature.hpp"
|
#include "MapPathFeature.hpp"
|
||||||
#include "ModGui.hpp"
|
#include "ModGui.hpp"
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
{
|
{
|
||||||
@@ -54,6 +55,7 @@ namespace pipedal
|
|||||||
class PluginHost;
|
class PluginHost;
|
||||||
class JackConfiguration;
|
class JackConfiguration;
|
||||||
class JackChannelSelection;
|
class JackChannelSelection;
|
||||||
|
class ChannelRouterSettings;
|
||||||
|
|
||||||
#ifndef LV2_PROPERTY_GETSET
|
#ifndef LV2_PROPERTY_GETSET
|
||||||
#define LV2_PROPERTY_GETSET(name) \
|
#define LV2_PROPERTY_GETSET(name) \
|
||||||
@@ -893,8 +895,8 @@ namespace pipedal
|
|||||||
size_t maxBufferSize = 1024;
|
size_t maxBufferSize = 1024;
|
||||||
size_t maxAtomBufferSize = 16 * 1024;
|
size_t maxAtomBufferSize = 16 * 1024;
|
||||||
bool hasMidiInputChannel;
|
bool hasMidiInputChannel;
|
||||||
int numberOfAudioInputChannels = 1;
|
ChannelSelection channelSelection;
|
||||||
int numberOfAudioOutputChannels = 1;
|
|
||||||
double sampleRate = 48000;
|
double sampleRate = 48000;
|
||||||
|
|
||||||
std::string vst3CachePath;
|
std::string vst3CachePath;
|
||||||
@@ -955,13 +957,12 @@ namespace pipedal
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// IHost implementation.
|
// IHost implementation.
|
||||||
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
|
virtual void SetMaxAudioBufferSize(size_t size) override { maxBufferSize = size; }
|
||||||
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
|
virtual size_t GetMaxAudioBufferSize() const override { return maxBufferSize; }
|
||||||
virtual size_t GetAtomBufferSize() const { return maxAtomBufferSize; }
|
virtual size_t GetAtomBufferSize() const override { return maxAtomBufferSize; }
|
||||||
virtual bool HasMidiInputChannel() const { return hasMidiInputChannel; }
|
virtual bool HasMidiInputChannel() const override { return hasMidiInputChannel; }
|
||||||
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
|
virtual LV2_Feature *const *GetLv2Features() const override { return (LV2_Feature *const *)&(this->lv2Features[0]); }
|
||||||
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
|
virtual const ChannelSelection &GetChannelSelection() const override { return this->channelSelection; }
|
||||||
virtual LV2_Feature *const *GetLv2Features() const { return (LV2_Feature *const *)&(this->lv2Features[0]); }
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
|
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
|
||||||
@@ -1027,7 +1028,8 @@ namespace pipedal
|
|||||||
Urids *urids = nullptr;
|
Urids *urids = nullptr;
|
||||||
ModGuiUris *mod_gui_uris = 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;
|
std::shared_ptr<Lv2PluginClass> GetPluginClass(const std::string &uri) const;
|
||||||
bool is_a(const std::string &class_, const std::string &target_class);
|
bool is_a(const std::string &class_, const std::string &target_class);
|
||||||
|
|||||||
@@ -96,6 +96,24 @@ namespace pipedal
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RealtimePedalboardItemIndex {
|
||||||
|
RealtimePedalboardItemIndex()
|
||||||
|
: instanceType(Pedalboard::InstanceType::MainPedalboard), index(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
RealtimePedalboardItemIndex(
|
||||||
|
Pedalboard::InstanceType instanceType,
|
||||||
|
int index)
|
||||||
|
: instanceType(instanceType), index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
RealtimePedalboardItemIndex(const RealtimePedalboardItemIndex& other) = default;
|
||||||
|
RealtimePedalboardItemIndex& operator=(const RealtimePedalboardItemIndex& other) = default;
|
||||||
|
|
||||||
|
Pedalboard::InstanceType instanceType = Pedalboard::InstanceType::MainPedalboard;
|
||||||
|
int index = -1;
|
||||||
|
};
|
||||||
|
|
||||||
struct RealtimeMidiEventRequest
|
struct RealtimeMidiEventRequest
|
||||||
{
|
{
|
||||||
RealtimeMidiEventType eventType;
|
RealtimeMidiEventType eventType;
|
||||||
@@ -159,7 +177,7 @@ namespace pipedal
|
|||||||
return &vuUpdateResponseData;
|
return &vuUpdateResponseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> enabledIndexes;
|
std::vector<RealtimePedalboardItemIndex> enabledIndexes;
|
||||||
std::vector<VuUpdate> vuUpdateWorkingData;
|
std::vector<VuUpdate> vuUpdateWorkingData;
|
||||||
std::vector<VuUpdate> vuUpdateResponseData;
|
std::vector<VuUpdate> vuUpdateResponseData;
|
||||||
|
|
||||||
|
|||||||
+183
-46
@@ -196,11 +196,11 @@ void Storage::SetDataRoot(const std::filesystem::path &path)
|
|||||||
this->dataRoot__audio_uploads = dataRoot / "audio_uploads";
|
this->dataRoot__audio_uploads = dataRoot / "audio_uploads";
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::filesystem::path &Storage::GetConfigRoot()
|
const std::filesystem::path &Storage::GetConfigRoot() const
|
||||||
{
|
{
|
||||||
return this->configRoot;
|
return this->configRoot;
|
||||||
}
|
}
|
||||||
const std::filesystem::path &Storage::GetDataRoot()
|
const std::filesystem::path &Storage::GetDataRoot() const
|
||||||
{
|
{
|
||||||
return this->dataRoot;
|
return this->dataRoot;
|
||||||
}
|
}
|
||||||
@@ -249,7 +249,6 @@ static void removeFileNoThrow(const std::filesystem::path &path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Storage::UpgradeFactoryPresets()
|
void Storage::UpgradeFactoryPresets()
|
||||||
{
|
{
|
||||||
auto presetsDirectory = this->GetPresetsDirectory();
|
auto presetsDirectory = this->GetPresetsDirectory();
|
||||||
@@ -387,8 +386,10 @@ void Storage::Initialize()
|
|||||||
|
|
||||||
LoadWifiConfigSettings();
|
LoadWifiConfigSettings();
|
||||||
LoadWifiDirectConfigSettings();
|
LoadWifiDirectConfigSettings();
|
||||||
|
this->channelRouterSettings = LoadChannelRouterSettings();
|
||||||
LoadUserSettings();
|
LoadUserSettings();
|
||||||
UpgradeFactoryPresets();
|
UpgradeFactoryPresets();
|
||||||
|
UpgradeChannelRouterSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::LoadBank(int64_t instanceId)
|
void Storage::LoadBank(int64_t instanceId)
|
||||||
@@ -687,40 +688,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.
|
// remove (digit*) from the end of the string.
|
||||||
// Find the last '(' character
|
// Find the last '(' character
|
||||||
size_t pos = name.find_last_of('(');
|
size_t pos = name.find_last_of('(');
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if everything between '(' and ')' is digits
|
// Check if everything between '(' and ')' is digits
|
||||||
size_t len = name.length()-1;
|
size_t len = name.length() - 1;
|
||||||
if (name[len] != ')')
|
if (name[len] != ')')
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
bool allDigits = true;
|
bool allDigits = true;
|
||||||
for (size_t i = pos + 1; i < len - 1; ++i) {
|
for (size_t i = pos + 1; i < len - 1; ++i)
|
||||||
if (name[i] < '0' || name[i] > '9') {
|
{
|
||||||
|
if (name[i] < '0' || name[i] > '9')
|
||||||
|
{
|
||||||
allDigits = false;
|
allDigits = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allDigits) {
|
if (!allDigits)
|
||||||
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing spaces before the '('
|
// Remove trailing spaces before the '('
|
||||||
while (pos > 0 && name[pos - 1] == ' ') {
|
while (pos > 0 && name[pos - 1] == ' ')
|
||||||
|
{
|
||||||
--pos;
|
--pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
return name.substr(0, 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))
|
if (!existingNames.contains(name))
|
||||||
{
|
{
|
||||||
@@ -729,9 +736,11 @@ static std::string makeUniqueName(const std::string &name, const std::set<std::s
|
|||||||
std::string baseName = stripNumericSuffix(name);
|
std::string baseName = stripNumericSuffix(name);
|
||||||
|
|
||||||
size_t i = 2;
|
size_t i = 2;
|
||||||
while (true) {
|
while (true)
|
||||||
|
{
|
||||||
std::string newName = SS(baseName << " (" << i << ")");
|
std::string newName = SS(baseName << " (" << i << ")");
|
||||||
if (!existingNames.contains(newName)) {
|
if (!existingNames.contains(newName))
|
||||||
|
{
|
||||||
return newName;
|
return newName;
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
@@ -739,23 +748,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)
|
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.");
|
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);
|
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
|
||||||
|
|
||||||
std::set<std::string> existingNames;
|
std::set<std::string> existingNames;
|
||||||
|
|
||||||
for (auto&preset: this->currentBank.presets()) {
|
for (auto &preset : this->currentBank.presets())
|
||||||
|
{
|
||||||
existingNames.insert(preset->preset().name());
|
existingNames.insert(preset->preset().name());
|
||||||
}
|
}
|
||||||
BankFile bankFile;
|
BankFile bankFile;
|
||||||
LoadBankFile(indexEntry.name(),&bankFile);
|
LoadBankFile(indexEntry.name(), &bankFile);
|
||||||
int64_t lastPresetId = -1;
|
int64_t lastPresetId = -1;
|
||||||
for (auto &presetEntry: bankFile.presets()) {
|
for (auto &presetEntry : bankFile.presets())
|
||||||
if (presetsSet.contains(presetEntry->instanceId())) {
|
{
|
||||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(),existingNames);
|
if (presetsSet.contains(presetEntry->instanceId()))
|
||||||
|
{
|
||||||
|
std::string uniqueName = makeUniqueName(presetEntry->preset().name(), existingNames);
|
||||||
existingNames.insert(uniqueName);
|
existingNames.insert(uniqueName);
|
||||||
Pedalboard t = presetEntry->preset();
|
Pedalboard t = presetEntry->preset();
|
||||||
t.name(uniqueName);
|
t.name(uniqueName);
|
||||||
@@ -767,31 +780,35 @@ int64_t Storage::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector
|
|||||||
}
|
}
|
||||||
int64_t Storage::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
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.");
|
throw std::runtime_error("Can't copy to self.");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
|
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
|
||||||
BankFile bankFile;
|
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;
|
std::set<std::string> existingNames;
|
||||||
|
|
||||||
for (auto&preset: bankFile.presets()) {
|
for (auto &preset : bankFile.presets())
|
||||||
|
{
|
||||||
existingNames.insert(preset->preset().name());
|
existingNames.insert(preset->preset().name());
|
||||||
}
|
}
|
||||||
for (auto &presetEntry: this->currentBank.presets()) {
|
for (auto &presetEntry : this->currentBank.presets())
|
||||||
if (presetsSet.contains(presetEntry->instanceId())) {
|
{
|
||||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(),existingNames);
|
if (presetsSet.contains(presetEntry->instanceId()))
|
||||||
|
{
|
||||||
|
std::string uniqueName = makeUniqueName(presetEntry->preset().name(), existingNames);
|
||||||
existingNames.insert(uniqueName);
|
existingNames.insert(uniqueName);
|
||||||
Pedalboard t = presetEntry->preset();
|
Pedalboard t = presetEntry->preset();
|
||||||
t.name(uniqueName);
|
t.name(uniqueName);
|
||||||
bankFile.addPreset(t);
|
bankFile.addPreset(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SaveBankFile(indexEntry.name(),bankFile);
|
SaveBankFile(indexEntry.name(), bankFile);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,17 +818,15 @@ std::vector<PresetIndexEntry> Storage::RequestBankPresets(int64_t bankInstanceId
|
|||||||
|
|
||||||
std::vector<PresetIndexEntry> result;
|
std::vector<PresetIndexEntry> result;
|
||||||
BankFile bankFile;
|
BankFile bankFile;
|
||||||
LoadBankFile(indexEntry.name(),&bankFile);
|
LoadBankFile(indexEntry.name(), &bankFile);
|
||||||
for (auto &preset: bankFile.presets()) {
|
for (auto &preset : bankFile.presets())
|
||||||
|
{
|
||||||
result.push_back(
|
result.push_back(
|
||||||
PresetIndexEntry(preset->instanceId(),preset->preset().name())
|
PresetIndexEntry(preset->instanceId(), preset->preset().name()));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Storage::SetPresetIndex(const PresetIndex &presets)
|
void Storage::SetPresetIndex(const PresetIndex &presets)
|
||||||
{
|
{
|
||||||
// painful because we must move unique_ptrs.
|
// painful because we must move unique_ptrs.
|
||||||
@@ -885,7 +900,8 @@ Pedalboard Storage::GetPreset(int64_t instanceId) const
|
|||||||
int64_t Storage::DeletePresets(const std::vector<int64_t> &presetInstanceIds)
|
int64_t Storage::DeletePresets(const std::vector<int64_t> &presetInstanceIds)
|
||||||
{
|
{
|
||||||
int64_t newSelection = currentBank.selectedPreset();
|
int64_t newSelection = currentBank.selectedPreset();
|
||||||
for (auto presetId: presetInstanceIds) {
|
for (auto presetId : presetInstanceIds)
|
||||||
|
{
|
||||||
newSelection = currentBank.deletePreset(presetId);
|
newSelection = currentBank.deletePreset(presetId);
|
||||||
}
|
}
|
||||||
SaveCurrentBank();
|
SaveCurrentBank();
|
||||||
@@ -977,7 +993,7 @@ int64_t Storage::CreateNewPreset()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
newPedalboard.name(name);
|
newPedalboard.name(name);
|
||||||
auto t = this->currentBank.addPreset(newPedalboard, -1);
|
auto t = this->currentBank.addPreset(newPedalboard, -1);
|
||||||
SaveCurrentBank();
|
SaveCurrentBank();
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@@ -997,7 +1013,7 @@ int64_t Storage::CopyPreset(int64_t fromId, int64_t toId)
|
|||||||
{
|
{
|
||||||
auto &toItem = this->currentBank.getItem(toId);
|
auto &toItem = this->currentBank.getItem(toId);
|
||||||
toItem.preset(fromItem.preset());
|
toItem.preset(fromItem.preset());
|
||||||
result = toId;
|
result = toId;
|
||||||
}
|
}
|
||||||
SaveCurrentBank();
|
SaveCurrentBank();
|
||||||
return result;
|
return result;
|
||||||
@@ -2075,21 +2091,21 @@ static bool ensureNoDotDot(const std::filesystem::path &path)
|
|||||||
return true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if (strcasecmp(fileName.c_str(),"license.md") == 0)
|
if (strcasecmp(fileName.c_str(), "license.md") == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (strcasecmp(fileName.c_str(),"readme.txt") == 0)
|
if (strcasecmp(fileName.c_str(), "readme.txt") == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (strcasecmp(fileName.c_str(),"readme.md") == 0)
|
if (strcasecmp(fileName.c_str(), "readme.md") == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3021,11 +3037,13 @@ std::string Storage::GetTone3000Auth() const
|
|||||||
|
|
||||||
std::filesystem::path Storage::FromAbstractPathString(const std::string &stringPath)
|
std::filesystem::path Storage::FromAbstractPathString(const std::string &stringPath)
|
||||||
{
|
{
|
||||||
if (stringPath.empty()) {
|
if (stringPath.empty())
|
||||||
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
fs::path path { stringPath};
|
fs::path path{stringPath};
|
||||||
if (path.is_absolute()) {
|
if (path.is_absolute())
|
||||||
|
{
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
return GetPluginUploadDirectory() / path;
|
return GetPluginUploadDirectory() / path;
|
||||||
@@ -3048,6 +3066,125 @@ std::string Storage::FromAbstractPathJson(const std::string &pathJson)
|
|||||||
return v.to_string();
|
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);
|
||||||
|
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_BEGIN(UserSettings)
|
||||||
JSON_MAP_REFERENCE(UserSettings, governor)
|
JSON_MAP_REFERENCE(UserSettings, governor)
|
||||||
|
|||||||
+18
-2
@@ -32,6 +32,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include "FilePropertyDirectoryTree.hpp"
|
#include "FilePropertyDirectoryTree.hpp"
|
||||||
#include "AlsaSequencer.hpp"
|
#include "AlsaSequencer.hpp"
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace pipedal {
|
namespace pipedal {
|
||||||
@@ -117,6 +119,15 @@ private:
|
|||||||
WifiDirectConfigSettings wifiDirectConfigSettings;
|
WifiDirectConfigSettings wifiDirectConfigSettings;
|
||||||
|
|
||||||
UserSettings userSettings;
|
UserSettings userSettings;
|
||||||
|
|
||||||
|
ChannelRouterSettings::ptr channelRouterSettings;
|
||||||
|
ChannelSelection channelSelection;
|
||||||
|
|
||||||
|
void UpgradeChannelRouterSettings();
|
||||||
|
std::filesystem::path GetChannelRouterSettingsPath() const;
|
||||||
|
void SaveChannelRouterSettings(ChannelRouterSettings::ptr settings);
|
||||||
|
ChannelRouterSettings::ptr LoadChannelRouterSettings();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Storage();
|
Storage();
|
||||||
void Initialize();
|
void Initialize();
|
||||||
@@ -124,8 +135,8 @@ public:
|
|||||||
|
|
||||||
void SetDataRoot(const std::filesystem::path& path);
|
void SetDataRoot(const std::filesystem::path& path);
|
||||||
void SetConfigRoot(const std::filesystem::path& path);
|
void SetConfigRoot(const std::filesystem::path& path);
|
||||||
const std::filesystem::path&GetConfigRoot();
|
const std::filesystem::path&GetConfigRoot() const;
|
||||||
const std::filesystem::path&GetDataRoot();
|
const std::filesystem::path&GetDataRoot() const;
|
||||||
|
|
||||||
const std::filesystem::path &GetPluginUploadDirectory() const;
|
const std::filesystem::path &GetPluginUploadDirectory() const;
|
||||||
|
|
||||||
@@ -206,6 +217,11 @@ public:
|
|||||||
void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration);
|
void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration);
|
||||||
AlsaSequencerConfiguration GetAlsaSequencerConfiguration() const;
|
AlsaSequencerConfiguration GetAlsaSequencerConfiguration() const;
|
||||||
|
|
||||||
|
void SetChannelRouterSettings(ChannelRouterSettings::ptr settings);
|
||||||
|
ChannelRouterSettings::ptr GetChannelRouterSettings();
|
||||||
|
|
||||||
|
const ChannelSelection& GetChannelSelection() const;
|
||||||
|
|
||||||
|
|
||||||
//std::string MapPropertyFileName(Lv2PluginInfo*pluginInfo, const std::string&path);
|
//std::string MapPropertyFileName(Lv2PluginInfo*pluginInfo, const std::string&path);
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
|
|
||||||
import { ControlValue } from "../pipedal/Pedalboard.tsx";
|
|
||||||
|
|
||||||
|
|
||||||
export class ChannelMixerStrip {
|
|
||||||
inputVu: number = -100.0;
|
|
||||||
inputVolume: number = 0;
|
|
||||||
outputVu: number = -100.0;
|
|
||||||
outputVolume: number = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class ChannelMixerSettings {
|
|
||||||
configured: boolean = false;
|
|
||||||
|
|
||||||
guitarInputChannels: number[] = [1,1];
|
|
||||||
|
|
||||||
guitarOutputChannels: number[] = [0,1];
|
|
||||||
|
|
||||||
guitarMixerSettings: ChannelMixerStrip = new ChannelMixerStrip();
|
|
||||||
|
|
||||||
|
|
||||||
auxInputChannels: number[] = [0,0];
|
|
||||||
auxOutputChannels: number[] = [-1,-1];
|
|
||||||
|
|
||||||
auxMixerSettings: ChannelMixerStrip = new ChannelMixerStrip();
|
|
||||||
|
|
||||||
controls: ControlValue[] = [];
|
|
||||||
// Inserts...
|
|
||||||
|
|
||||||
getControlValue(symbol: string): number | null {
|
|
||||||
for (let control of this.controls) {
|
|
||||||
if (control.key === symbol) {
|
|
||||||
return control.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
setControlValue(symbol: string, value: number): boolean {
|
|
||||||
for (let control of this.controls) {
|
|
||||||
if (control.key === symbol) {
|
|
||||||
if (control.value === value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
control.value = value;
|
|
||||||
this.controls = this.controls.slice(); // trigger observers.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let newValue = new ControlValue(symbol,value);
|
|
||||||
this.controls.push(newValue);
|
|
||||||
this.controls = this.controls.slice(); // trigger observers.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,524 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
|
||||||
import VuMeter from './VuMeter';
|
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
|
||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|
||||||
import IconButtonEx from './IconButtonEx';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import useWindowSize from "./UseWindowSize";
|
|
||||||
import DialIcon from './svg/fx_dial.svg?react';
|
|
||||||
import Select from '@mui/material/Select';
|
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
|
||||||
import ChannelMixerSettingsHelpDialog from './ChannelMixerSettingsHelpDialog';
|
|
||||||
import PluginControl from './PluginControl';
|
|
||||||
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import ChannelMixerSettings from './ChannelMixerSettings';
|
|
||||||
import { makeChanelMixerUiPlugin, CHANNEL_MIXER_INSTANCE_ID } from './ChannelMixerUiControl';
|
|
||||||
import { ControlValue } from './Pedalboard';
|
|
||||||
|
|
||||||
import DialogEx from './DialogEx';
|
|
||||||
|
|
||||||
import { PiPedalModelFactory } from './PiPedalModel';
|
|
||||||
|
|
||||||
export interface ChannelMixerSettingsDialogProps {
|
|
||||||
open: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let channelMixerUiPlugin = makeChanelMixerUiPlugin();
|
|
||||||
|
|
||||||
enum RouteType {
|
|
||||||
Main,
|
|
||||||
Aux
|
|
||||||
}
|
|
||||||
let cellSectionHead: React.CSSProperties = {
|
|
||||||
border: "0px",
|
|
||||||
paddingTop: 4,
|
|
||||||
paddingBottom: 4,
|
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 12,
|
|
||||||
margin: "0px",
|
|
||||||
textAlign: "left",
|
|
||||||
verticalAlign: "middle"
|
|
||||||
};
|
|
||||||
|
|
||||||
let cellPortraitControlStrip: React.CSSProperties = {
|
|
||||||
border: "0px",
|
|
||||||
paddingTop: 12,
|
|
||||||
paddingBottom: 12,
|
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 0,
|
|
||||||
margin: "0px",
|
|
||||||
textAlign: "left",
|
|
||||||
verticalAlign: "middle"
|
|
||||||
};
|
|
||||||
let cellLeft: React.CSSProperties = {
|
|
||||||
border: "0px",
|
|
||||||
paddingTop: 4,
|
|
||||||
paddingBottom: 4,
|
|
||||||
paddingLeft: 4,
|
|
||||||
paddingRight: 4,
|
|
||||||
margin: "0px",
|
|
||||||
textAlign: "left",
|
|
||||||
verticalAlign: "middle"
|
|
||||||
};
|
|
||||||
let cellLeftIndent: React.CSSProperties = {
|
|
||||||
border: "0px",
|
|
||||||
paddingTop: 4,
|
|
||||||
paddingBottom: 4,
|
|
||||||
paddingLeft: 12,
|
|
||||||
paddingRight: 4,
|
|
||||||
margin: "0px",
|
|
||||||
textAlign: "right",
|
|
||||||
verticalAlign: "middle"
|
|
||||||
};
|
|
||||||
let cellLeftH: React.CSSProperties = {
|
|
||||||
border: "0px",
|
|
||||||
paddingTop: 4,
|
|
||||||
paddingBottom: 4,
|
|
||||||
paddingLeft: 4,
|
|
||||||
paddingRight: 4,
|
|
||||||
margin: "0px",
|
|
||||||
textAlign: "left",
|
|
||||||
verticalAlign: "middle"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function MakeChannelMenu(channelCount: number): React.ReactElement[] {
|
|
||||||
let items: React.ReactElement[] = [(<MenuItem key={-1} value={-1}>None</MenuItem>)]
|
|
||||||
if (channelCount === 0) {
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
if (channelCount === 2) {
|
|
||||||
items.push((<MenuItem key={0} value={0}>Left</MenuItem>));
|
|
||||||
items.push((<MenuItem key={1} value={1}>Right</MenuItem>));
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < channelCount; ++i) {
|
|
||||||
items.push((<MenuItem key={i} value={i}>Ch {i + 1}</MenuItem>));
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function ChannelMixerSettingsDialog(props: ChannelMixerSettingsDialogProps) {
|
|
||||||
//const classes = useStyles();
|
|
||||||
const { open, onClose } = props;
|
|
||||||
let model = PiPedalModelFactory.getInstance();
|
|
||||||
let config = model.jackConfiguration.get();
|
|
||||||
|
|
||||||
|
|
||||||
const [settings, setSettings] = useState<ChannelMixerSettings>(model.channelMixerSettings.get());
|
|
||||||
const [controlValues,setControlValues] = useState<ControlValue[]>(model.channelMixerControlValues.get());
|
|
||||||
const [showHelp, setShowHelp] = useState<boolean>(false);
|
|
||||||
|
|
||||||
if (settings) {
|
|
||||||
}
|
|
||||||
if (controlValues) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (open) {
|
|
||||||
let handleSettingsChanged = () => {
|
|
||||||
setSettings(model.channelMixerSettings.get());
|
|
||||||
};
|
|
||||||
model.channelMixerSettings.addOnChangedHandler(handleSettingsChanged);
|
|
||||||
let handleControlsChanged = () => {
|
|
||||||
setControlValues(model.channelMixerControlValues.get());
|
|
||||||
};
|
|
||||||
model.channelMixerControlValues.addOnChangedHandler(handleControlsChanged);
|
|
||||||
return () => {
|
|
||||||
model.channelMixerSettings.removeOnChangedHandler(handleSettingsChanged);
|
|
||||||
model.channelMixerControlValues.removeOnChangedHandler(handleControlsChanged);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return () => { };
|
|
||||||
}
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const handleClose = (): void => {
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
let [windowSize] = useWindowSize();
|
|
||||||
let landscape = windowSize.width > windowSize.height;
|
|
||||||
let fullScreen = windowSize.width < 450 || windowSize.height < 600;
|
|
||||||
|
|
||||||
let Dial = (symbol: string) => {
|
|
||||||
let instanceId = CHANNEL_MIXER_INSTANCE_ID;
|
|
||||||
let control = channelMixerUiPlugin.getControl(symbol);
|
|
||||||
let value = settings.getControlValue(symbol);
|
|
||||||
if (value === null) {
|
|
||||||
value = control?.default_value ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (<PluginControl
|
|
||||||
instanceId={instanceId}
|
|
||||||
uiControl={control}
|
|
||||||
onPreviewChange={(value: number) => {
|
|
||||||
model.previewPedalboardValue(instanceId, symbol, value);
|
|
||||||
}}
|
|
||||||
onChange={(value: number) => {
|
|
||||||
model.setPedalboardControl(instanceId, symbol, value);
|
|
||||||
}}
|
|
||||||
requestIMEEdit={() => { }}
|
|
||||||
value={value}
|
|
||||||
/>);
|
|
||||||
|
|
||||||
return <DialIcon width={32} height={32} style={{ verticalAlign: "middle", fill: "white" }} />;
|
|
||||||
}
|
|
||||||
let ChannelSelect = (routeType: RouteType, channelIndex: number, input: boolean) => {
|
|
||||||
let channelCount = input ? config.inputAudioPorts.length : config.outputAudioPorts.length;
|
|
||||||
|
|
||||||
let value: number;
|
|
||||||
|
|
||||||
switch (routeType) {
|
|
||||||
case RouteType.Main:
|
|
||||||
value = input ? settings.guitarInputChannels[channelIndex] : settings.guitarOutputChannels[channelIndex];
|
|
||||||
break;
|
|
||||||
case RouteType.Aux:
|
|
||||||
value = input ? settings.auxInputChannels[channelIndex] : settings.auxOutputChannels[channelIndex];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (value >= channelCount) {
|
|
||||||
value = -1;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Select variant="standard" style={{ width: 80 }} value={value}
|
|
||||||
onChange={(event) => {
|
|
||||||
let newValue = event.target.value as number;
|
|
||||||
let newSettings = Object.assign(new ChannelMixerSettings(), settings);
|
|
||||||
switch (routeType) {
|
|
||||||
case RouteType.Main:
|
|
||||||
if (input) {
|
|
||||||
newSettings.guitarInputChannels[channelIndex] = newValue;
|
|
||||||
} else {
|
|
||||||
newSettings.guitarOutputChannels[channelIndex] = newValue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RouteType.Aux:
|
|
||||||
if (input) {
|
|
||||||
newSettings.auxInputChannels[channelIndex] = newValue;
|
|
||||||
} else {
|
|
||||||
newSettings.auxOutputChannels[channelIndex] = newValue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
model.channelMixerSettings.set(newSettings);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{MakeChannelMenu(channelCount)}
|
|
||||||
</Select>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let Vu = () => {
|
|
||||||
return (
|
|
||||||
<div style={{ }} >
|
|
||||||
<VuMeter instanceId={CHANNEL_MIXER_INSTANCE_ID} display={"input"} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let LandscapeView = () => {
|
|
||||||
return (
|
|
||||||
<div style={{ width: "100%", height: "100%", flexGrow: 1, fontSize: "14px" }} >
|
|
||||||
<table style={{ borderCollapse: "collapse", width: "100%" }}>
|
|
||||||
<colgroup>
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th style={cellLeftH}><Typography variant="caption" >In</Typography></th>
|
|
||||||
<th></th>
|
|
||||||
<th></th>
|
|
||||||
<th></th>
|
|
||||||
<th></th>
|
|
||||||
<th></th>
|
|
||||||
<th style={cellLeftH}><Typography variant="caption" >Out</Typography></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{/* Main */}
|
|
||||||
<tr>
|
|
||||||
<td rowSpan={1} style={cellLeft}>
|
|
||||||
<Typography variant="body2" color="textSecondary">Main</Typography>
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Main, 0, true)}
|
|
||||||
</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Dial("mainIn")}</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>
|
|
||||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
|
||||||
Inserts
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Dial("mainOut")}</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Main, 0, false)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Main, 1, true))}
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Main, 1, false))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{/* Spacer */}
|
|
||||||
<tr><td colSpan={9} style={{ height: 16 }}></td></tr>
|
|
||||||
|
|
||||||
{/* Aux */}
|
|
||||||
<tr>
|
|
||||||
<td rowSpan={1} style={cellLeft}>
|
|
||||||
<Typography variant="body2" color="textSecondary">Aux</Typography>
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Aux, 0, true))}
|
|
||||||
</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Dial("auxIn")}</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>
|
|
||||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
|
||||||
Inserts
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Dial("auxOut")}</td>
|
|
||||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Aux, 0, false))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Aux, 1, true))}
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Aux, 1, false))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let PortraitView = () => {
|
|
||||||
return (
|
|
||||||
<div style={{ width: "100%", height: "100%", flexGrow: 1, fontSize: "14px" }} >
|
|
||||||
<table style={{ borderCollapse: "collapse", width: "100%" }}>
|
|
||||||
<colgroup>
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "auto" }} />
|
|
||||||
<col style={{ width: "100%" }} />
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
|
||||||
{/* Main */}
|
|
||||||
<tr>
|
|
||||||
<td colSpan={4} style={cellSectionHead}>
|
|
||||||
<Typography variant="body2" color="textSecondary" >Main</Typography>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style={cellLeftIndent}>
|
|
||||||
<Typography variant="body2" color="textSecondary" >In</Typography>
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Main, 0, true)}
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Main, 1, true)}
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td colSpan={3}>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={cellPortraitControlStrip}>{Dial("mainIn")}</td>
|
|
||||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
|
||||||
<td style={cellPortraitControlStrip}>
|
|
||||||
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
|
||||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
|
||||||
Inserts
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td style={cellPortraitControlStrip}>{Dial("mainOut")}</td>
|
|
||||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style={cellLeftIndent}>
|
|
||||||
<Typography variant="body2" color="textSecondary" >Out</Typography>
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Main, 0, false)}
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Main, 1, false)}
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{/* Spacer */}
|
|
||||||
<tr><td colSpan={4} style={{ height: 16 }}></td></tr>
|
|
||||||
|
|
||||||
{/* Aux */}
|
|
||||||
<tr>
|
|
||||||
<td colSpan={4} style={cellSectionHead}>
|
|
||||||
<Typography variant="body2" color="textSecondary" >Aux</Typography>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style={cellLeftIndent}>
|
|
||||||
<Typography variant="body2" color="textSecondary" >In</Typography>
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Aux, 0, true))}
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{(ChannelSelect(RouteType.Aux, 1, true))}
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td colSpan={3}>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={cellPortraitControlStrip}>{Dial("auxIn")}</td>
|
|
||||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
|
||||||
<td style={cellPortraitControlStrip}>
|
|
||||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
|
||||||
Inserts
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td style={cellPortraitControlStrip}>{Dial("auxOut")}</td>
|
|
||||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style={cellLeftIndent}>
|
|
||||||
<Typography variant="body2" color="textSecondary" >Out</Typography>
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Aux, 0, false)}
|
|
||||||
</td>
|
|
||||||
<td style={cellLeft}>
|
|
||||||
{ChannelSelect(RouteType.Aux, 1, false)}
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DialogEx tag="channelMixerSettings" onClose={handleClose} aria-labelledby="select-channel_mixer_settings"
|
|
||||||
open={open}
|
|
||||||
fullWidth maxWidth="sm"
|
|
||||||
onEnterKey={handleClose}
|
|
||||||
fullScreen={fullScreen}
|
|
||||||
style={{ maxWidth: 1200 }}
|
|
||||||
>
|
|
||||||
<DialogTitle id="select-channel_mixer_settings" style={{ paddingTop: 0, paddingBottom: 0, marginBottom: 8 }}>
|
|
||||||
<Toolbar style={{ padding: 0, margin: 0 }}>
|
|
||||||
<IconButtonEx
|
|
||||||
tooltip="Close"
|
|
||||||
edge="start"
|
|
||||||
color="inherit"
|
|
||||||
aria-label="cancel"
|
|
||||||
style={{ opacity: 0.6 }}
|
|
||||||
onClick={() => { onClose(); }}
|
|
||||||
>
|
|
||||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
|
||||||
</IconButtonEx>
|
|
||||||
|
|
||||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
|
||||||
Channel Routing
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<IconButtonEx tooltip="Help"
|
|
||||||
edge="end"
|
|
||||||
aria-label="help"
|
|
||||||
onClick={() => { setShowHelp(true); }}
|
|
||||||
>
|
|
||||||
<HelpOutlineIcon style={{ width: 24, height: 24, opacity: 0.6 }} />
|
|
||||||
</IconButtonEx>
|
|
||||||
|
|
||||||
|
|
||||||
</Toolbar>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
{landscape ?
|
|
||||||
LandscapeView()
|
|
||||||
:
|
|
||||||
PortraitView()
|
|
||||||
}
|
|
||||||
</DialogContent>
|
|
||||||
{showHelp && (
|
|
||||||
<ChannelMixerSettingsHelpDialog open={showHelp} onClose={() => setShowHelp(false)} />
|
|
||||||
)}
|
|
||||||
</DialogEx>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChannelMixerSettingsDialog;
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default class ChannelRouterSettings {
|
||||||
|
configured: boolean = false;
|
||||||
|
channelRouterPresetId: number = -1;
|
||||||
|
|
||||||
|
mainInputChannels: number[] = [1,1];
|
||||||
|
mainOutputChannels: number[] = [0,1];
|
||||||
|
mainInserts: Pedalboard = new Pedalboard();
|
||||||
|
|
||||||
|
auxInputChannels: number[] = [0,0];
|
||||||
|
auxOutputChannels: number[] = [-1,-1];
|
||||||
|
auxInserts: Pedalboard = new Pedalboard();
|
||||||
|
|
||||||
|
sendInputChannels: number[] = [-1,-1];
|
||||||
|
sendOutputChannels: number[] = [-1,-1];
|
||||||
|
|
||||||
|
controlValues: ControlValue[] = [];
|
||||||
|
// Inserts...
|
||||||
|
|
||||||
|
deserialize(obj: any): ChannelRouterSettings {
|
||||||
|
this.configured = obj.configured;
|
||||||
|
this.channelRouterPresetId = obj.channelRouterPresetId ? obj.channelRouterPresetId : -1;
|
||||||
|
this.mainInputChannels = obj.mainInputChannels.slice();
|
||||||
|
this.mainOutputChannels = obj.mainOutputChannels.slice();
|
||||||
|
this.mainInserts = new Pedalboard().deserialize(obj.mainInserts);
|
||||||
|
this.auxInputChannels = obj.auxInputChannels.slice();
|
||||||
|
this.auxOutputChannels = obj.auxOutputChannels.slice();
|
||||||
|
this.auxInserts = new Pedalboard().deserialize(obj.auxInserts);
|
||||||
|
this.sendInputChannels = obj.sendInputChannels.slice();
|
||||||
|
this.sendOutputChannels = obj.sendOutputChannels.slice();
|
||||||
|
this.controlValues = ControlValue.deserializeArray(obj.controlValues);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
getControlValue(symbol: string): number | null {
|
||||||
|
for (let control of this.controlValues) {
|
||||||
|
if (control.key === symbol) {
|
||||||
|
return control.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
setControlValue(symbol: string, value: number): boolean {
|
||||||
|
for (let control of this.controlValues) {
|
||||||
|
if (control.key === symbol) {
|
||||||
|
if (control.value === value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
control.value = value;
|
||||||
|
this.controlValues = this.controlValues.slice(); // trigger observers.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let newValue = new ControlValue(symbol,value);
|
||||||
|
this.controlValues.push(newValue);
|
||||||
|
this.controlValues = this.controlValues.slice(); // trigger observers.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,853 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
|
import IconButtonEx from './IconButtonEx';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import useWindowSize from "./UseWindowSize";
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import ChannelRouterSettingsHelpDialog from './ChannelRouterSettingsHelpDialog';
|
||||||
|
import SpeakerIcon from '@mui/icons-material/Speaker';
|
||||||
|
import MicIcon from '@mui/icons-material/Mic';
|
||||||
|
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import ChannelRouterSettings from './ChannelRouterSettings';
|
||||||
|
import { Pedalboard, ControlValue } from './Pedalboard';
|
||||||
|
|
||||||
|
import DialogEx from './DialogEx';
|
||||||
|
|
||||||
|
import { PiPedalModelFactory } from './PiPedalModel';
|
||||||
|
|
||||||
|
export interface ChannelRouterSettingsDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RouteType {
|
||||||
|
Main,
|
||||||
|
Aux,
|
||||||
|
Send
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChannelRouterSettingsPreset {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
settings: ChannelRouterSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
let LEFT_LEFT_PRESET_ID = -2;
|
||||||
|
let RIGHT_STEREO_PRESET_ID = -3;
|
||||||
|
|
||||||
|
let channelRouterPresets: ChannelRouterSettingsPreset[] = [
|
||||||
|
{
|
||||||
|
id: -2, name: "Left -> Left", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -2,
|
||||||
|
mainInputChannels: [0, 0],
|
||||||
|
mainOutputChannels: [0, 0],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [-1, -1],
|
||||||
|
auxOutputChannels: [-1, -1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [-1, -1],
|
||||||
|
sendOutputChannels: [-1, -1],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -3, name: "Left -> Stereo", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -3,
|
||||||
|
mainInputChannels: [0, 0],
|
||||||
|
mainOutputChannels: [0, 1],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [-1, -1],
|
||||||
|
auxOutputChannels: [-1, -1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [-1, -1],
|
||||||
|
sendOutputChannels: [-1, -1],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -4, name: "Right -> Stereo", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -4,
|
||||||
|
mainInputChannels: [1, 1],
|
||||||
|
mainOutputChannels: [0, 1],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [-1, -1],
|
||||||
|
auxOutputChannels: [-1, -1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [-1, -1],
|
||||||
|
sendOutputChannels: [-1, -1],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -5, name: "Right -> Left + re-amp", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -5,
|
||||||
|
mainInputChannels: [1, 1],
|
||||||
|
mainOutputChannels: [0, 0],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [1, 1],
|
||||||
|
auxOutputChannels: [1, 1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [-1, - 1],
|
||||||
|
sendOutputChannels: [-1, -1],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -6, name: "Left -> Left + send", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -6,
|
||||||
|
mainInputChannels: [0, 0],
|
||||||
|
mainOutputChannels: [0, 0],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [-1, -1],
|
||||||
|
auxOutputChannels: [-1, - 1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [1, 1],
|
||||||
|
sendOutputChannels: [1, 1],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -7, name: "Right -> Right + send", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -7,
|
||||||
|
mainInputChannels: [1, 1],
|
||||||
|
mainOutputChannels: [1, 1],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [-1, -1],
|
||||||
|
auxOutputChannels: [-1, - 1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [0, 0],
|
||||||
|
sendOutputChannels: [0, 0],
|
||||||
|
controlValues: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -8, name: "Left -> Stereo + re-amp", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -8,
|
||||||
|
mainInputChannels: [0, 0],
|
||||||
|
mainOutputChannels: [0, 1],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [0, 0],
|
||||||
|
auxOutputChannels: [2, 2],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [-1, -1],
|
||||||
|
sendOutputChannels: [-1, -1],
|
||||||
|
controlValues: [],
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: -9, name: "Left -> Stereo + stereo send", settings: new ChannelRouterSettings().deserialize({
|
||||||
|
configured: true,
|
||||||
|
channelRouterPresetId: -9,
|
||||||
|
mainInputChannels: [0, 0],
|
||||||
|
mainOutputChannels: [0, 1],
|
||||||
|
mainInserts: new Pedalboard(),
|
||||||
|
auxInputChannels: [-1, -1],
|
||||||
|
auxOutputChannels: [-1, - 1],
|
||||||
|
auxInserts: new Pedalboard(),
|
||||||
|
sendInputChannels: [2, 3],
|
||||||
|
sendOutputChannels: [2, 3],
|
||||||
|
controlValues: [],
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let cellPortraitInOutDiv: React.CSSProperties = {
|
||||||
|
width: 80, display: "flex", columnGap: 4, flexDirection: "row", alignItems: "center", justifyContent: "right",
|
||||||
|
};
|
||||||
|
let cellPortraitSectionHead: React.CSSProperties = {
|
||||||
|
border: "0px",
|
||||||
|
paddingTop: 4,
|
||||||
|
paddingBottom: 4,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 12,
|
||||||
|
width: 50,
|
||||||
|
margin: "0px",
|
||||||
|
textAlign: "left",
|
||||||
|
verticalAlign: "middle",
|
||||||
|
};
|
||||||
|
|
||||||
|
let cellPortraitControlStrip: React.CSSProperties = {
|
||||||
|
border: "0px",
|
||||||
|
paddingTop: 12,
|
||||||
|
paddingBottom: 12,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
margin: "0px",
|
||||||
|
textAlign: "left",
|
||||||
|
verticalAlign: "middle"
|
||||||
|
};
|
||||||
|
let cellLeft: React.CSSProperties = {
|
||||||
|
border: "0px",
|
||||||
|
paddingTop: 4,
|
||||||
|
paddingBottom: 4,
|
||||||
|
paddingLeft: 4,
|
||||||
|
paddingRight: 12,
|
||||||
|
margin: "0px",
|
||||||
|
textAlign: "left",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
verticalAlign: "middle"
|
||||||
|
};
|
||||||
|
let cellLandscapeTitle: React.CSSProperties = {
|
||||||
|
border: "0px",
|
||||||
|
paddingTop: 4,
|
||||||
|
paddingBottom: 4,
|
||||||
|
paddingLeft: 4,
|
||||||
|
paddingRight: 20,
|
||||||
|
margin: "0px",
|
||||||
|
textAlign: "left",
|
||||||
|
verticalAlign: "middle"
|
||||||
|
};
|
||||||
|
let cellPortraitInOut: React.CSSProperties = {
|
||||||
|
border: "0px",
|
||||||
|
paddingTop: 4,
|
||||||
|
paddingBottom: 4,
|
||||||
|
paddingLeft: 12,
|
||||||
|
paddingRight: 12,
|
||||||
|
margin: "0px",
|
||||||
|
width: 80,
|
||||||
|
textAlign: "right",
|
||||||
|
verticalAlign: "middle"
|
||||||
|
};
|
||||||
|
|
||||||
|
function MakeChannelMenu(channelCount: number): React.ReactElement[] {
|
||||||
|
let items: React.ReactElement[] = [(<MenuItem key={-1} value={-1}>None</MenuItem>)]
|
||||||
|
if (channelCount === 0) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
if (channelCount === 2) {
|
||||||
|
items.push((<MenuItem key={0} value={0}>Left</MenuItem>));
|
||||||
|
items.push((<MenuItem key={1} value={1}>Right</MenuItem>));
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < channelCount; ++i) {
|
||||||
|
items.push((<MenuItem key={i} value={i}>Ch {i + 1}</MenuItem>));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||||
|
//const classes = useStyles();
|
||||||
|
const { open, onClose } = props;
|
||||||
|
let model = PiPedalModelFactory.getInstance();
|
||||||
|
let config = model.jackConfiguration.get();
|
||||||
|
|
||||||
|
|
||||||
|
const [settings, setSettings] = useState<ChannelRouterSettings>(model.channelRouterSettings.get());
|
||||||
|
const [controlValues, setControlValues] = useState<ControlValue[]>(model.channelRouterControlValues.get());
|
||||||
|
const [showHelp, setShowHelp] = useState<boolean>(false);
|
||||||
|
|
||||||
|
if (settings) {
|
||||||
|
}
|
||||||
|
if (controlValues) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
let handleSettingsChanged = () => {
|
||||||
|
setSettings(model.channelRouterSettings.get());
|
||||||
|
};
|
||||||
|
model.channelRouterSettings.addOnChangedHandler(handleSettingsChanged);
|
||||||
|
let handleControlsChanged = () => {
|
||||||
|
setControlValues(model.channelRouterControlValues.get());
|
||||||
|
};
|
||||||
|
model.channelRouterControlValues.addOnChangedHandler(handleControlsChanged);
|
||||||
|
return () => {
|
||||||
|
model.channelRouterSettings.removeOnChangedHandler(handleSettingsChanged);
|
||||||
|
model.channelRouterControlValues.removeOnChangedHandler(handleControlsChanged);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return () => { };
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
const handleClose = (): void => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
let [windowSize] = useWindowSize();
|
||||||
|
let landscape = windowSize.width > windowSize.height;
|
||||||
|
let fullScreen = windowSize.width < 450 || windowSize.height < 600;
|
||||||
|
|
||||||
|
let ChannelSelect = (routeType: RouteType, channelIndex: number, input: boolean, icon: boolean = true) => {
|
||||||
|
let channelCount = input ? config.inputAudioPorts.length : config.outputAudioPorts.length;
|
||||||
|
|
||||||
|
let value: number;
|
||||||
|
|
||||||
|
switch (routeType) {
|
||||||
|
case RouteType.Main:
|
||||||
|
value = input ? settings.mainInputChannels[channelIndex] : settings.mainOutputChannels[channelIndex];
|
||||||
|
break;
|
||||||
|
case RouteType.Aux:
|
||||||
|
value = input ? settings.auxInputChannels[channelIndex] : settings.auxOutputChannels[channelIndex];
|
||||||
|
break;
|
||||||
|
case RouteType.Send:
|
||||||
|
value = input ? settings.sendInputChannels[channelIndex] : settings.sendOutputChannels[channelIndex];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value >= channelCount) {
|
||||||
|
value = -1;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select variant="standard" style={{ width: 90 }} value={value}
|
||||||
|
startAdornment={
|
||||||
|
icon ? (
|
||||||
|
input ? (<MicIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />) :
|
||||||
|
(<SpeakerIcon style={{ opacity: 0.6, width: 16, height: 16, marginRight: 4 }} />)
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
onChange={(event) => {
|
||||||
|
let newValue = event.target.value as number;
|
||||||
|
let newSettings = Object.assign(new ChannelRouterSettings(), settings);
|
||||||
|
switch (routeType) {
|
||||||
|
case RouteType.Main:
|
||||||
|
if (input) {
|
||||||
|
newSettings.mainInputChannels[channelIndex] = newValue;
|
||||||
|
} else {
|
||||||
|
newSettings.mainOutputChannels[channelIndex] = newValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RouteType.Aux:
|
||||||
|
if (input) {
|
||||||
|
newSettings.auxInputChannels[channelIndex] = newValue;
|
||||||
|
} else {
|
||||||
|
newSettings.auxOutputChannels[channelIndex] = newValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RouteType.Send:
|
||||||
|
if (input) {
|
||||||
|
newSettings.sendInputChannels[channelIndex] = newValue;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
newSettings.sendOutputChannels[channelIndex] = newValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newSettings.channelRouterPresetId = -1; // custom.
|
||||||
|
newSettings.configured = true;
|
||||||
|
model.setChannelRouterSettings(newSettings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{MakeChannelMenu(channelCount)}
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let Vu = () => {
|
||||||
|
return (
|
||||||
|
<div style={{}} >
|
||||||
|
<div style={{ width: 8, height: 48, background: "black" }}></div>
|
||||||
|
{/* <VuMeter instanceId={Pedalboard.CHANNEL_ROUTER_MAIN_INSERT_ID} display={"input"} /> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ApplyPresetId = (presetId: number | string | null | undefined) => {
|
||||||
|
if (typeof presetId !== 'number') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let preset of channelRouterPresets) {
|
||||||
|
if (preset.id === presetId) {
|
||||||
|
model.setChannelRouterSettings(preset.settings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let CanUsePreset = (preset: ChannelRouterSettingsPreset): boolean => {
|
||||||
|
let inputChannels = config.inputAudioPorts.length;
|
||||||
|
let outputChannels = config.outputAudioPorts.length;
|
||||||
|
return (
|
||||||
|
(preset.settings.mainInputChannels.every((ch) => ch < inputChannels || ch === -1)) &&
|
||||||
|
(preset.settings.mainOutputChannels.every((ch) => ch < outputChannels || ch === -1)) &&
|
||||||
|
(preset.settings.auxInputChannels.every((ch) => ch < inputChannels || ch === -1)) &&
|
||||||
|
(preset.settings.auxOutputChannels.every((ch) => ch < outputChannels || ch === -1)) &&
|
||||||
|
(preset.settings.sendInputChannels.every((ch) => ch < inputChannels || ch === -1)) &&
|
||||||
|
(preset.settings.sendOutputChannels.every((ch) => ch < outputChannels || ch === -1))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let GeneratePresetMenuItems = (): React.ReactElement[] => {
|
||||||
|
let items: React.ReactElement[] = [];
|
||||||
|
|
||||||
|
for (let preset of channelRouterPresets) {
|
||||||
|
if (CanUsePreset(preset))
|
||||||
|
items.push(
|
||||||
|
<MenuItem key={preset.id} value={preset.id}>
|
||||||
|
{preset.name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (settings.channelRouterPresetId === -1) {
|
||||||
|
items.push(
|
||||||
|
(<MenuItem key={-1} value={-1} disabled={true}>
|
||||||
|
<span style={{ opacity: 0.6 }}>(Custom)</span></MenuItem>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
let PresetSelect = (width: number | string | undefined) => {
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", flexDirection: "row", marginTop: 2, alignItems: "center" }}>
|
||||||
|
<Select variant="standard" style={{ width: width }} value={settings.channelRouterPresetId}
|
||||||
|
onChange={(event) => {
|
||||||
|
ApplyPresetId(event.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{GeneratePresetMenuItems()}
|
||||||
|
</Select>
|
||||||
|
<IconButtonEx tooltip="Presets" aria-label="more-presets">
|
||||||
|
<MoreVertIcon style={{ opacity: 0.6 }} onClick={() => { }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let LandscapeView = () => {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
||||||
|
width: "100%", height: "100%", flexGrow: 1, fontSize: "14px"
|
||||||
|
}} >
|
||||||
|
<table style={{ borderCollapse: "collapse" }}>
|
||||||
|
<colgroup>
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
{/* Main */}
|
||||||
|
<tr>
|
||||||
|
<td rowSpan={1} style={cellLandscapeTitle}>
|
||||||
|
<Typography variant="body2" color="textSecondary">Main</Typography>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Main, 0, true)}
|
||||||
|
</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>
|
||||||
|
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||||
|
Inserts
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Main, 0, false)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Main, 1, true))}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Main, 1, false))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Spacer ---------------- */}
|
||||||
|
<tr><td colSpan={9} style={{ height: 16 }}></td></tr>
|
||||||
|
|
||||||
|
{/* Aux ----------------------*/}
|
||||||
|
<tr>
|
||||||
|
<td rowSpan={1} style={cellLandscapeTitle}>
|
||||||
|
<Typography variant="body2" color="textSecondary">Aux</Typography>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Aux, 0, true))}
|
||||||
|
</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>
|
||||||
|
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||||
|
Inserts
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Aux, 0, false))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Aux, 1, true))}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Aux, 1, false))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Spacer ---------------- */}
|
||||||
|
<tr><td colSpan={9} style={{ height: 16 }}></td></tr>
|
||||||
|
|
||||||
|
{/* Send ---------------- */}
|
||||||
|
<tr>
|
||||||
|
<td rowSpan={1} style={cellLandscapeTitle}>
|
||||||
|
<Typography variant="body2" color="textSecondary">Send</Typography>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Send, 0, false))}
|
||||||
|
</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>
|
||||||
|
{/* No button */}
|
||||||
|
</td>
|
||||||
|
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Send, 0, true))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Send, 1, false))}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Send, 1, true))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let PortraitView = () => {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
display: "flex", flexFlow: "row", alignItems: "center", justifyContent: "center",
|
||||||
|
width: "100%", flexGrow: 0, fontSize: "14px"
|
||||||
|
}} >
|
||||||
|
<table style={{ borderCollapse: "collapse" }}>
|
||||||
|
<colgroup>
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
<col style={{ width: "auto" }} />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
{/* Main */}
|
||||||
|
<tr>
|
||||||
|
<td colSpan={1} style={cellPortraitSectionHead}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Main</Typography>
|
||||||
|
</td>
|
||||||
|
<td colSpan={4}>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitInOut}>
|
||||||
|
<div style={cellPortraitInOutDiv}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >In</Typography>
|
||||||
|
<MicIcon style={{ verticalAlign: "middle", width: 16, height: 16, opacity: 0.6 }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Main, 0, true, false)}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Main, 1, true, false)}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colSpan={4}>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
|
<td style={cellPortraitControlStrip}>
|
||||||
|
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
||||||
|
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||||
|
Inserts
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitInOut}>
|
||||||
|
<div style={cellPortraitInOutDiv}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Out</Typography>
|
||||||
|
<SpeakerIcon style={{ verticalAlign: "middle", width: 16, height: 16, opacity: 0.6 }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style={cellLeft}>
|
||||||
|
|
||||||
|
{ChannelSelect(RouteType.Main, 0, false, false)}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Main, 1, false, false)}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Spacer ---------------- */}
|
||||||
|
<tr><td colSpan={5} style={{ height: 16 }}></td></tr>
|
||||||
|
{/* Aux */}
|
||||||
|
<tr>
|
||||||
|
<td colSpan={1} style={cellPortraitSectionHead}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Aux</Typography>
|
||||||
|
</td>
|
||||||
|
<td colSpan={4}></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitInOut}>
|
||||||
|
<div style={cellPortraitInOutDiv}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >In</Typography>
|
||||||
|
<MicIcon style={{ verticalAlign: "middle", width: 16, height: 16, opacity: 0.6 }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Aux, 0, true, false))}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Aux, 1, true, false))}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colSpan={4}>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
|
<td style={cellPortraitControlStrip}>
|
||||||
|
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
||||||
|
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||||
|
Inserts
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitInOut}>
|
||||||
|
<div style={cellPortraitInOutDiv}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Out</Typography>
|
||||||
|
<SpeakerIcon style={{ verticalAlign: "middle", width: 16, height: 16, opacity: 0.6 }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Aux, 0, false, false)}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Aux, 1, false, false)}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Spacer ---------------- */}
|
||||||
|
<tr><td colSpan={5} style={{ height: 16 }}></td></tr>
|
||||||
|
|
||||||
|
{/* Send */}
|
||||||
|
<tr>
|
||||||
|
<td colSpan={1} style={cellPortraitSectionHead}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Send</Typography>
|
||||||
|
</td>
|
||||||
|
<td colSpan={4}></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitInOut}>
|
||||||
|
<div style={cellPortraitInOutDiv}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Send</Typography>
|
||||||
|
<SpeakerIcon style={{ verticalAlign: "middle", width: 16, height: 16, opacity: 0.6 }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Send, 0, false, false))}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{(ChannelSelect(RouteType.Send, 1, false, false))}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colSpan={4}>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
|
<td style={cellPortraitControlStrip}>
|
||||||
|
<div style={{ marginLeft: 8, marginRight: 8, visibility: "hidden" }}>
|
||||||
|
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||||
|
Inserts
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style={cellPortraitInOut}>
|
||||||
|
<div style={cellPortraitInOutDiv}>
|
||||||
|
<Typography variant="body2" color="textSecondary" >Return</Typography>
|
||||||
|
<MicIcon style={{ verticalAlign: "middle", width: 16, height: 16, opacity: 0.6 }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Send, 0, true, false)}
|
||||||
|
</td>
|
||||||
|
<td style={cellLeft}>
|
||||||
|
{ChannelSelect(RouteType.Send, 1, false, false)}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogEx tag="channelRouterSettings"
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="select-channel_mixer_settings"
|
||||||
|
open={open}
|
||||||
|
fullWidth
|
||||||
|
maxWidth={landscape ? "sm" : "xs"}
|
||||||
|
onEnterKey={handleClose}
|
||||||
|
fullScreen={fullScreen}
|
||||||
|
>
|
||||||
|
<DialogTitle id="select-channel_mixer_settings" style={{ paddingTop: 0, paddingBottom: 0, marginBottom: 0 }}>
|
||||||
|
<div style={{
|
||||||
|
display: "flex", flexFlow: "row", flexWrap: "nowrap", alignItems: "center",
|
||||||
|
marginTop: 8
|
||||||
|
|
||||||
|
}}>
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="Close"
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="cancel"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={() => { onClose(); }}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<Typography noWrap component="div" sx={{}}>
|
||||||
|
Channel Routing
|
||||||
|
</Typography>
|
||||||
|
{
|
||||||
|
landscape ? (
|
||||||
|
<>
|
||||||
|
<div style={{ flexGrow: 1 }}> </div>
|
||||||
|
<div style={{ flexShrink: 1, maxWidth: 440, display: "relative", marginLeft: 16, marginRight: 16 }}>
|
||||||
|
{PresetSelect(200)}
|
||||||
|
</div>
|
||||||
|
<div style={{ flexGrow: 1 }}> </div>
|
||||||
|
</>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
<div style={{ flexGrow: 1 }}> </div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<IconButtonEx tooltip="Help"
|
||||||
|
edge="end"
|
||||||
|
aria-label="help"
|
||||||
|
onClick={() => { setShowHelp(true); }}
|
||||||
|
>
|
||||||
|
<HelpOutlineIcon style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</DialogTitle>
|
||||||
|
{!landscape && (
|
||||||
|
<>
|
||||||
|
<div style={{ marginLeft: 16 + 32 + 8, marginBottom: 8 }}>
|
||||||
|
{PresetSelect("60%")}
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
{landscape ?
|
||||||
|
LandscapeView()
|
||||||
|
:
|
||||||
|
PortraitView()
|
||||||
|
}
|
||||||
|
</DialogContent>
|
||||||
|
{
|
||||||
|
showHelp && (
|
||||||
|
<ChannelRouterSettingsHelpDialog open={showHelp} onClose={() => setShowHelp(false)} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</DialogEx >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChannelRouterSettingsDialog;
|
||||||
+14
-7
@@ -18,33 +18,30 @@
|
|||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import DialogEx from './DialogEx';
|
import DialogEx from './DialogEx';
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
import Divider from '@mui/material/Divider';
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
import IconButtonEx from './IconButtonEx';
|
import IconButtonEx from './IconButtonEx';
|
||||||
|
|
||||||
|
|
||||||
export interface ChannelMixerSettingsHelpDialogProps {
|
export interface ChannelRouterSettingsHelpDialogProps {
|
||||||
open: boolean,
|
open: boolean,
|
||||||
onClose: () => void,
|
onClose: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ChannelMixerSettingsHelpDialogState {
|
export interface ChannelRouterSettingsHelpDialogState {
|
||||||
fullScreen: boolean;
|
fullScreen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ChannelMixerSettingsHelpDialog extends ResizeResponsiveComponent<ChannelMixerSettingsHelpDialogProps, ChannelMixerSettingsHelpDialogState> {
|
export default class ChannelRouterSettingsHelpDialog extends ResizeResponsiveComponent<ChannelRouterSettingsHelpDialogProps, ChannelRouterSettingsHelpDialogState> {
|
||||||
|
|
||||||
refText: React.RefObject<HTMLInputElement | null>;
|
refText: React.RefObject<HTMLInputElement | null>;
|
||||||
|
|
||||||
constructor(props: ChannelMixerSettingsHelpDialogProps) {
|
constructor(props: ChannelRouterSettingsHelpDialogProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
fullScreen: false
|
fullScreen: false
|
||||||
@@ -137,6 +134,16 @@ export default class ChannelMixerSettingsHelpDialog extends ResizeResponsiveComp
|
|||||||
channels to both the Main and Aux routes. If you have a 2x4 or 4x4 audio interface with two guitar input channels
|
channels to both the Main and Aux routes. If you have a 2x4 or 4x4 audio interface with two guitar input channels
|
||||||
you can also pass through stereo dry signals by configuring Main and Aux input and output channels appropriately.
|
you can also pass through stereo dry signals by configuring Main and Aux input and output channels appropriately.
|
||||||
</p>
|
</p>
|
||||||
|
<p>The Send route, in conjunction with the TooB Send plugin allows you to send and return audio signals to an external physical device
|
||||||
|
from within a PiPedal preset. This allows you to integrate external hardware effects into your PiPedal presets.
|
||||||
|
Connect a cable from the audio interface output <i>Send</i> channel(s) selected here to the input of your
|
||||||
|
hardware effect, and a cable from the output of your hardware effect to the audio interface's <i>Return</i> channel
|
||||||
|
assigned here. Then insert the TooB Send plugin into your PiPedal preset. The input of the
|
||||||
|
TooB Send plugin will be sent to the external hardware effect, and the output the external hardware
|
||||||
|
effect will be returned to PiPedal at the output of the TooB Send plugin. PiPedal currently supports
|
||||||
|
only one Send plugin instance per preset; and the Send and Return channels may not be shared with the Main
|
||||||
|
or Aux routes.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
+2
-3
@@ -19,13 +19,12 @@
|
|||||||
import Units from './Units';
|
import Units from './Units';
|
||||||
import {UiPlugin, UiControl, PluginType, ControlType, ScalePoint} from './Lv2Plugin';
|
import {UiPlugin, UiControl, PluginType, ControlType, ScalePoint} from './Lv2Plugin';
|
||||||
|
|
||||||
export const ChannelMixerUiUri = "uri://two-play/pipedal/ppChannelMixer";
|
export const ChannelRouterUiUri = "uri://two-play/pipedal/ppChannelRouter";
|
||||||
export const CHANNEL_MIXER_INSTANCE_ID = -4;
|
|
||||||
|
|
||||||
export function makeChanelMixerUiPlugin(): UiPlugin {
|
export function makeChanelMixerUiPlugin(): UiPlugin {
|
||||||
|
|
||||||
return new UiPlugin().deserialize({
|
return new UiPlugin().deserialize({
|
||||||
uri: ChannelMixerUiUri,
|
uri: ChannelRouterUiUri,
|
||||||
name: "Channel Mixer",
|
name: "Channel Mixer",
|
||||||
brand: "",
|
brand: "",
|
||||||
label: "",
|
label: "",
|
||||||
@@ -357,14 +357,36 @@ export class PedalboardSplitItem extends PedalboardItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let TWO_POWER_52 = 4503599627370496; // Maximum Javascript integer value.
|
||||||
|
let TWO_POWER_48 = 281474976710656; // Number of possible instance IDs for Main Pedalboards.
|
||||||
|
let MAX_INSTANCE_ID = TWO_POWER_48;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export enum InstanceType {
|
||||||
|
MainPedalboard,
|
||||||
|
MainInsert,
|
||||||
|
AuxInsert
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Pedalboard implements Deserializable<Pedalboard> {
|
export class Pedalboard implements Deserializable<Pedalboard> {
|
||||||
|
|
||||||
|
static readonly CHANNEL_ROUTER_CONTROLS_INSTANCE_ID = -4; // Reserved Instance ID for Router Main Inserts.
|
||||||
|
static readonly CHANNEL_ROUTER_MAIN_INSERT_ID = -5; // Reserved Instance ID for Router Main Inserts.
|
||||||
|
static readonly CHANNEL_ROUTER_AUX_INSERT_ID = -6; // Reserved Instance ID for Router Aux inserts.
|
||||||
|
|
||||||
|
static readonly MAIN_INSERT_INSTANCE_BASE = TWO_POWER_52-2*MAX_INSTANCE_ID;
|
||||||
|
static readonly AUX_INSERT_INSTANCE_BASE = TWO_POWER_52-1*MAX_INSTANCE_ID;
|
||||||
|
|
||||||
static readonly START_CONTROL = -2; // synthetic PedalboardItem for input volume.
|
static readonly START_CONTROL = -2; // synthetic PedalboardItem for input volume.
|
||||||
static readonly END_CONTROL = -3; // synthetic PedalboardItem for output volume.
|
static readonly END_CONTROL = -3; // synthetic PedalboardItem for output volume.
|
||||||
static readonly CHANNEL_MIXER_INSTANCE_ID = -4; // Synthetic instance ID for the channel routing mixer.
|
static readonly MAIN_INSERT_START_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
||||||
|
static readonly MAIN_INSERT_END_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||||
|
static readonly AUX_INSERT_START_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
||||||
|
static readonly AUX_INSERT_END_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||||
|
|
||||||
|
|
||||||
static readonly START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
|
static readonly START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
|
||||||
static readonly END_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#End";
|
static readonly END_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#End";
|
||||||
|
|
||||||
@@ -373,7 +395,7 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
|||||||
this.input_volume_db = input.input_volume_db;
|
this.input_volume_db = input.input_volume_db;
|
||||||
this.output_volume_db = input.output_volume_db;
|
this.output_volume_db = input.output_volume_db;
|
||||||
this.items = PedalboardItem.deserializeArray(input.items);
|
this.items = PedalboardItem.deserializeArray(input.items);
|
||||||
this.nextInstanceId = input.nextInstanceId ?? -1;
|
this.nextInstanceId = input.nextInstanceId ?? 0;
|
||||||
this.snapshots = input.snapshots ? Snapshot.deserializeArray(input.snapshots): [];
|
this.snapshots = input.snapshots ? Snapshot.deserializeArray(input.snapshots): [];
|
||||||
this.selectedSnapshot = input.selectedSnapshot;
|
this.selectedSnapshot = input.selectedSnapshot;
|
||||||
this.pathProperties = input.pathProperties;
|
this.pathProperties = input.pathProperties;
|
||||||
@@ -381,6 +403,25 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getInstanceTypeFromInstanceId(instanceId: number): InstanceType {
|
||||||
|
if (instanceId >= Pedalboard.MAIN_INSERT_INSTANCE_BASE) {
|
||||||
|
return InstanceType.MainInsert;
|
||||||
|
}
|
||||||
|
if (instanceId >= Pedalboard.AUX_INSERT_INSTANCE_BASE) {
|
||||||
|
return InstanceType.AuxInsert;
|
||||||
|
}
|
||||||
|
return InstanceType.MainPedalboard;
|
||||||
|
}
|
||||||
|
getInstanceType(): InstanceType {
|
||||||
|
if (this.nextInstanceId >= Pedalboard.MAIN_INSERT_INSTANCE_BASE) {
|
||||||
|
return InstanceType.MainInsert;
|
||||||
|
}
|
||||||
|
if (this.nextInstanceId >= Pedalboard.AUX_INSERT_INSTANCE_BASE) {
|
||||||
|
return InstanceType.AuxInsert;
|
||||||
|
}
|
||||||
|
return InstanceType.MainPedalboard;
|
||||||
|
}
|
||||||
|
|
||||||
clone(): Pedalboard {
|
clone(): Pedalboard {
|
||||||
return new Pedalboard().deserialize(this);
|
return new Pedalboard().deserialize(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import AudioFileMetadata from './AudioFileMetadata';
|
|||||||
import { pathFileName } from './FileUtils';
|
import { pathFileName } from './FileUtils';
|
||||||
import { AlsaSequencerConfiguration, AlsaSequencerPortSelection } from './AlsaSequencer';
|
import { AlsaSequencerConfiguration, AlsaSequencerPortSelection } from './AlsaSequencer';
|
||||||
import { getDefaultModGuiPreference } from './ModGuiHost';
|
import { getDefaultModGuiPreference } from './ModGuiHost';
|
||||||
import ChannelMixerSettings from './ChannelMixerSettings';
|
import ChannelRouterSettings from './ChannelRouterSettings';
|
||||||
|
|
||||||
export enum State {
|
export enum State {
|
||||||
Loading,
|
Loading,
|
||||||
@@ -530,13 +530,9 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
jackServerSettings: ObservableProperty<JackServerSettings>
|
jackServerSettings: ObservableProperty<JackServerSettings>
|
||||||
= new ObservableProperty<JackServerSettings>(new JackServerSettings());
|
= new ObservableProperty<JackServerSettings>(new JackServerSettings());
|
||||||
|
|
||||||
channelMixerControlValues: ObservableProperty<ControlValue[]> = new ObservableProperty<ControlValue[]>([]);
|
channelRouterControlValues: ObservableProperty<ControlValue[]> = new ObservableProperty<ControlValue[]>([]);
|
||||||
channelMixerSettings: ObservableProperty<ChannelMixerSettings> = new ObservableProperty<ChannelMixerSettings>(new ChannelMixerSettings());
|
channelRouterSettings: ObservableProperty<ChannelRouterSettings> = new ObservableProperty<ChannelRouterSettings>(new ChannelRouterSettings());
|
||||||
|
|
||||||
setChannelMixerSettings(settings: ChannelMixerSettings) {
|
|
||||||
this.channelMixerSettings.set(settings);
|
|
||||||
this.channelMixerControlValues.set(settings.controls);
|
|
||||||
}
|
|
||||||
wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings());
|
wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings());
|
||||||
wifiDirectConfigSettings: ObservableProperty<WifiDirectConfigSettings> = new ObservableProperty<WifiDirectConfigSettings>(new WifiDirectConfigSettings());
|
wifiDirectConfigSettings: ObservableProperty<WifiDirectConfigSettings> = new ObservableProperty<WifiDirectConfigSettings>(new WifiDirectConfigSettings());
|
||||||
governorSettings: ObservableProperty<GovernorSettings> = new ObservableProperty<GovernorSettings>(new GovernorSettings());
|
governorSettings: ObservableProperty<GovernorSettings> = new ObservableProperty<GovernorSettings>(new GovernorSettings());
|
||||||
@@ -1255,6 +1251,11 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
await this.getWebSocket().request<any>("getJackServerSettings")
|
await this.getWebSocket().request<any>("getJackServerSettings")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
this.channelRouterSettings.set(
|
||||||
|
new ChannelRouterSettings().deserialize(
|
||||||
|
await this.getWebSocket().request<any>("getChannelRouterSettings")
|
||||||
|
)
|
||||||
|
)
|
||||||
this.jackConfiguration.set(new JackConfiguration().deserialize(
|
this.jackConfiguration.set(new JackConfiguration().deserialize(
|
||||||
await this.getWebSocket().request<JackConfiguration>("getJackConfiguration")
|
await this.getWebSocket().request<JackConfiguration>("getJackConfiguration")
|
||||||
));
|
));
|
||||||
@@ -1651,15 +1652,15 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
|
|
||||||
private lastControlMessageWasSentbyMe = false;
|
private lastControlMessageWasSentbyMe = false;
|
||||||
|
|
||||||
private _setChannelMixerControlValue(key: string, value: number, notifyServer: boolean) : void {
|
private _setChannelRouterControlValue(key: string, value: number, notifyServer: boolean) : void {
|
||||||
let channelMixerSettings = this.channelMixerSettings.get();
|
let channelRouterSettings = this.channelRouterSettings.get();
|
||||||
let changed = channelMixerSettings.setControlValue(key, value);
|
let changed = channelRouterSettings.setControlValue(key, value);
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
this.channelMixerControlValues.set(this.channelMixerSettings.get().controls);
|
this.channelRouterControlValues.set(this.channelRouterSettings.get().controlValues);
|
||||||
if (notifyServer) {
|
if (notifyServer) {
|
||||||
this.lastControlMessageWasSentbyMe = true;
|
this.lastControlMessageWasSentbyMe = true;
|
||||||
this._setServerControl("setControl", Pedalboard.CHANNEL_MIXER_INSTANCE_ID, key, value);
|
this._setServerControl("setControl", Pedalboard.CHANNEL_ROUTER_CONTROLS_INSTANCE_ID, key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1667,8 +1668,8 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
let pedalboard = this.pedalboard.get();
|
let pedalboard = this.pedalboard.get();
|
||||||
if (pedalboard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
if (pedalboard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
||||||
|
|
||||||
if (instanceId == Pedalboard.CHANNEL_MIXER_INSTANCE_ID) {
|
if (instanceId == Pedalboard.CHANNEL_ROUTER_CONTROLS_INSTANCE_ID) {
|
||||||
this._setChannelMixerControlValue(key,value,notifyServer);
|
this._setChannelRouterControlValue(key,value,notifyServer);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
let changed: boolean;
|
let changed: boolean;
|
||||||
@@ -2316,12 +2317,6 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setJackSettings(jackSettings: JackChannelSelection): void {
|
|
||||||
this.expectDisconnect(ReconnectReason.LoadingSettings);
|
|
||||||
this.webSocket?.send("setJackSettings", jackSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
monitorPortSubscriptions: MonitorPortHandleImpl[] = [];
|
monitorPortSubscriptions: MonitorPortHandleImpl[] = [];
|
||||||
|
|
||||||
monitorPort(instanceId: number, key: string, updateRateSeconds: number, onUpdated: (value: number) => void): MonitorPortHandle {
|
monitorPort(instanceId: number, key: string, updateRateSeconds: number, onUpdated: (value: number) => void): MonitorPortHandle {
|
||||||
@@ -3310,7 +3305,6 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
if (this.webSocket) {
|
if (this.webSocket) {
|
||||||
this.webSocket.send("setFavorites", newFavorites);
|
this.webSocket.send("setFavorites", newFavorites);
|
||||||
}
|
}
|
||||||
// stub: update server.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3680,6 +3674,15 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
}
|
}
|
||||||
this.removeInvalidSidechains(pedalboard);
|
this.removeInvalidSidechains(pedalboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setChannelRouterSettings(settings: ChannelRouterSettings) {
|
||||||
|
this.channelRouterSettings.set(settings);
|
||||||
|
this.channelRouterControlValues.set(settings.controlValues);
|
||||||
|
if (this.webSocket) {
|
||||||
|
this.webSocket.send("setChannelRouterSettings", settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let instance: PiPedalModel | undefined = undefined;
|
let instance: PiPedalModel | undefined = undefined;
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
import React, { SyntheticEvent, Component } from 'react';
|
import React, { SyntheticEvent, Component } from 'react';
|
||||||
import Switch from "@mui/material/Switch";
|
import Switch from "@mui/material/Switch";
|
||||||
import ChannelMixerSettings from './ChannelMixerSettings';
|
import ChannelRouterSettings from './ChannelRouterSettings';
|
||||||
import ChannelMixerSettingsDialog from './ChannelMixerSettingsDialog';
|
import ChannelRouterSettingsDialog from './ChannelRouterSettingsDialog';
|
||||||
import OkCancelDialog from './OkCancelDialog';
|
import OkCancelDialog from './OkCancelDialog';
|
||||||
import RadioSelectDialog from './RadioSelectDialog';
|
import RadioSelectDialog from './RadioSelectDialog';
|
||||||
import IconButtonEx from './IconButtonEx';
|
import IconButtonEx from './IconButtonEx';
|
||||||
@@ -78,7 +78,7 @@ interface SettingsDialogState {
|
|||||||
showStatusMonitorDialog: boolean;
|
showStatusMonitorDialog: boolean;
|
||||||
jackConfiguration: JackConfiguration;
|
jackConfiguration: JackConfiguration;
|
||||||
jackSettings: JackChannelSelection | null;
|
jackSettings: JackChannelSelection | null;
|
||||||
channelMixerSettings: ChannelMixerSettings | null;
|
channelRouterSettings: ChannelRouterSettings | null;
|
||||||
jackServerSettings: JackServerSettings;
|
jackServerSettings: JackServerSettings;
|
||||||
alsaSequencerConfiguration: AlsaSequencerConfiguration;
|
alsaSequencerConfiguration: AlsaSequencerConfiguration;
|
||||||
keepScreenOn: boolean;
|
keepScreenOn: boolean;
|
||||||
@@ -101,7 +101,7 @@ interface SettingsDialogState {
|
|||||||
showMidiSelectDialog: boolean;
|
showMidiSelectDialog: boolean;
|
||||||
showThemeSelectDialog: boolean;
|
showThemeSelectDialog: boolean;
|
||||||
showJackServerSettingsDialog: boolean;
|
showJackServerSettingsDialog: boolean;
|
||||||
showChannelMixerSettingsDialog: boolean;
|
showChannelRouterSettingsDialog: boolean;
|
||||||
shuttingDown: boolean;
|
shuttingDown: boolean;
|
||||||
restarting: boolean;
|
restarting: boolean;
|
||||||
isAndroidHosted: boolean;
|
isAndroidHosted: boolean;
|
||||||
@@ -194,7 +194,7 @@ const SettingsDialog = withStyles(
|
|||||||
showStatusMonitorDialog: false,
|
showStatusMonitorDialog: false,
|
||||||
|
|
||||||
jackServerSettings: this.model.jackServerSettings.get(),
|
jackServerSettings: this.model.jackServerSettings.get(),
|
||||||
channelMixerSettings: this.model.channelMixerSettings.get(),
|
channelRouterSettings: this.model.channelRouterSettings.get(),
|
||||||
jackConfiguration: this.model.jackConfiguration.get(),
|
jackConfiguration: this.model.jackConfiguration.get(),
|
||||||
jackStatus: undefined,
|
jackStatus: undefined,
|
||||||
jackSettings: this.model.jackSettings.get(),
|
jackSettings: this.model.jackSettings.get(),
|
||||||
@@ -215,7 +215,7 @@ const SettingsDialog = withStyles(
|
|||||||
showMidiSelectDialog: false,
|
showMidiSelectDialog: false,
|
||||||
showThemeSelectDialog: false,
|
showThemeSelectDialog: false,
|
||||||
showJackServerSettingsDialog: false,
|
showJackServerSettingsDialog: false,
|
||||||
showChannelMixerSettingsDialog: false,
|
showChannelRouterSettingsDialog: false,
|
||||||
shuttingDown: false,
|
shuttingDown: false,
|
||||||
restarting: false,
|
restarting: false,
|
||||||
showShutdownOkDialog: false,
|
showShutdownOkDialog: false,
|
||||||
@@ -228,7 +228,7 @@ const SettingsDialog = withStyles(
|
|||||||
this.handleAlsaSequencerConfigurationChanged = this.handleAlsaSequencerConfigurationChanged.bind(this);
|
this.handleAlsaSequencerConfigurationChanged = this.handleAlsaSequencerConfigurationChanged.bind(this);
|
||||||
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
|
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
|
||||||
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
|
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
|
||||||
this.handleChannelMixerSettingsChanged = this.handleChannelMixerSettingsChanged.bind(this);
|
this.handleChannelRouterSettingsChanged = this.handleChannelRouterSettingsChanged.bind(this);
|
||||||
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
|
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
|
||||||
this.handleWifiDirectConfigSettingsChanged = this.handleWifiDirectConfigSettingsChanged.bind(this);
|
this.handleWifiDirectConfigSettingsChanged = this.handleWifiDirectConfigSettingsChanged.bind(this);
|
||||||
this.handleGovernorSettingsChanged = this.handleGovernorSettingsChanged.bind(this);
|
this.handleGovernorSettingsChanged = this.handleGovernorSettingsChanged.bind(this);
|
||||||
@@ -330,10 +330,10 @@ const SettingsDialog = withStyles(
|
|||||||
continueDisabled: !this.model.jackServerSettings.get().valid
|
continueDisabled: !this.model.jackServerSettings.get().valid
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
handleChannelMixerSettingsChanged(): void {
|
handleChannelRouterSettingsChanged(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
channelMixerSettings: this.model.channelMixerSettings.get(),
|
channelRouterSettings: this.model.channelRouterSettings.get(),
|
||||||
continueDisabled: !this.model.channelMixerSettings.get().configured
|
continueDisabled: !this.model.channelRouterSettings.get().configured
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +378,7 @@ const SettingsDialog = withStyles(
|
|||||||
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
|
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
|
||||||
this.model.alsaSequencerConfiguration.addOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
this.model.alsaSequencerConfiguration.addOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
||||||
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
|
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||||
this.model.channelMixerSettings.addOnChangedHandler(this.handleChannelMixerSettingsChanged);
|
this.model.channelRouterSettings.addOnChangedHandler(this.handleChannelRouterSettingsChanged);
|
||||||
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||||
this.model.wifiDirectConfigSettings.addOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
this.model.wifiDirectConfigSettings.addOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
||||||
this.model.governorSettings.addOnChangedHandler(this.handleGovernorSettingsChanged);
|
this.model.governorSettings.addOnChangedHandler(this.handleGovernorSettingsChanged);
|
||||||
@@ -401,7 +401,7 @@ const SettingsDialog = withStyles(
|
|||||||
this.handleJackSettingsChanged();
|
this.handleJackSettingsChanged();
|
||||||
this.handleShowStatusMonitorChanged();
|
this.handleShowStatusMonitorChanged();
|
||||||
this.handleJackServerSettingsChanged();
|
this.handleJackServerSettingsChanged();
|
||||||
this.handleChannelMixerSettingsChanged();
|
this.handleChannelRouterSettingsChanged();
|
||||||
this.handleWifiConfigSettingsChanged();
|
this.handleWifiConfigSettingsChanged();
|
||||||
this.handleWifiDirectConfigSettingsChanged();
|
this.handleWifiDirectConfigSettingsChanged();
|
||||||
this.setState({ hasWifiDevice: this.model.hasWifiDevice.get() });
|
this.setState({ hasWifiDevice: this.model.hasWifiDevice.get() });
|
||||||
@@ -420,7 +420,7 @@ const SettingsDialog = withStyles(
|
|||||||
this.model.alsaSequencerConfiguration.removeOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
this.model.alsaSequencerConfiguration.removeOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
||||||
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
|
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
|
||||||
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
|
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||||
this.model.channelMixerSettings.removeOnChangedHandler(this.handleChannelMixerSettingsChanged);
|
this.model.channelRouterSettings.removeOnChangedHandler(this.handleChannelRouterSettingsChanged);
|
||||||
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||||
this.model.wifiDirectConfigSettings.removeOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
this.model.wifiDirectConfigSettings.removeOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
||||||
this.model.governorSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged);
|
this.model.governorSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged);
|
||||||
@@ -720,10 +720,10 @@ const SettingsDialog = withStyles(
|
|||||||
<Typography display="block" variant="caption" noWrap color="textSecondary">{this.state.jackServerSettings.getSummaryText()}</Typography>
|
<Typography display="block" variant="caption" noWrap color="textSecondary">{this.state.jackServerSettings.getSummaryText()}</Typography>
|
||||||
</div>
|
</div>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
{this.state.showChannelMixerSettingsDialog && (
|
{this.state.showChannelRouterSettingsDialog && (
|
||||||
<ChannelMixerSettingsDialog
|
<ChannelRouterSettingsDialog
|
||||||
open={this.state.showChannelMixerSettingsDialog}
|
open={this.state.showChannelRouterSettingsDialog}
|
||||||
onClose={() => this.setState({ showChannelMixerSettingsDialog: false })}
|
onClose={() => this.setState({ showChannelRouterSettingsDialog: false })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{this.state.showJackServerSettingsDialog && (
|
{this.state.showJackServerSettingsDialog && (
|
||||||
@@ -744,7 +744,7 @@ const SettingsDialog = withStyles(
|
|||||||
<ButtonBase className={classes.setting}
|
<ButtonBase className={classes.setting}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showChannelMixerSettingsDialog: true
|
showChannelRouterSettingsDialog: true
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
disabled={!hasAudioConfig}
|
disabled={!hasAudioConfig}
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ const ToobParametricEqView =
|
|||||||
])}
|
])}
|
||||||
{panel("", [
|
{panel("", [
|
||||||
controls.gain
|
controls.gain
|
||||||
])}
|
])}
|
||||||
{divider()}
|
{divider()}
|
||||||
{panel("Low",
|
{panel("Low",
|
||||||
[controls.low_level,
|
[controls.low_level,
|
||||||
|
|||||||
Reference in New Issue
Block a user