WIP Sync
This commit is contained in:
Vendored
-1
@@ -13,7 +13,6 @@
|
|||||||
"/usr/include/x86_64-linux-gnu",
|
"/usr/include/x86_64-linux-gnu",
|
||||||
"/usr/lib"
|
"/usr/lib"
|
||||||
],
|
],
|
||||||
"compilerPath": "/usr/bin/gcc-12",
|
|
||||||
"cStandard": "c17",
|
"cStandard": "c17",
|
||||||
"cppStandard": "c++20",
|
"cppStandard": "c++20",
|
||||||
"intelliSenseMode": "linux-gcc-arm64",
|
"intelliSenseMode": "linux-gcc-arm64",
|
||||||
|
|||||||
+17
-2
@@ -11,6 +11,17 @@ set (DISPLAY_VERSION "PiPedal v1.5.99-Beta")
|
|||||||
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
||||||
set (CMAKE_INSTALL_PREFIX "/usr/")
|
set (CMAKE_INSTALL_PREFIX "/usr/")
|
||||||
|
|
||||||
|
set (LIBZIP_DO_INSTALL OFF CACHE BOOL "Disable libzip install" FORCE)
|
||||||
|
set (SQLITECPP_INSTALL OFF CACHE BOOL "Disable SQLiteCpp install" FORCE)
|
||||||
|
|
||||||
|
set(SUBMODULE_INSTALL OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
# Remove SQLiteCPP install files.
|
||||||
|
set(CPACK_SOURCE_IGNORE_FILES
|
||||||
|
"*.a;sqlite3.h;$(CPACK_SOURCE_IGNORE_FILES)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
set (PIPEDAL_EXCLUDE_TESTS false)
|
set (PIPEDAL_EXCLUDE_TESTS false)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
@@ -96,9 +107,9 @@ set(CPACK_DEBIAN_PACKAGE_SECTION sound)
|
|||||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
|
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
|
||||||
|
|
||||||
# Accept either libzip4 or libzip5 to satisfy libzip 4 linkage (because Ubuntu nonsense)
|
|
||||||
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libicu-dev ,lv2-dev, ffmpeg, authbind, curl, gpg, alsa-base| pipewire, alsa-utils")
|
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libicu-dev ,lv2-dev,iw, ffmpeg, authbind, curl, gpg, alsa-base| pipewire, alsa-utils")
|
||||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
|
||||||
set(CPACK_PACKAGING_INSTALL_PREFIX /usr)
|
set(CPACK_PACKAGING_INSTALL_PREFIX /usr)
|
||||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||||
@@ -118,4 +129,8 @@ set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
|
message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
|||||||
@@ -354,11 +354,6 @@ namespace pipedal
|
|||||||
|
|
||||||
std::vector<OutputBufferMix> outputBufferMixes;
|
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;
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2025 Robin E. R. Davies
|
* Copyright (c) 2026 Robin E. R. Davies
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
* 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
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <lilv/lilv.h>
|
||||||
#include "AudioDriver.hpp"
|
#include "AudioDriver.hpp"
|
||||||
#include "JackServerSettings.hpp"
|
#include "JackServerSettings.hpp"
|
||||||
|
|
||||||
|
|||||||
+109
-94
@@ -1,18 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2022 Robin E. R. Davies
|
* Copyright (c) 2022 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
|
||||||
* the Software without restriction, including without limitation the rights to
|
* the Software without restriction, including without limitation the rights to
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
* 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
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
* so, subject to the following conditions:
|
* so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
* copies or substantial portions of the Software.
|
* copies or substantial portions of the Software.
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
@@ -27,28 +27,34 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "AlsaDriver.hpp"
|
#include "AlsaDriver.hpp"
|
||||||
#include "JackDriver.hpp"
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
class AlsaTester : private AudioDriverHost
|
||||||
class AlsaTester: private AudioDriverHost {
|
{
|
||||||
public:
|
public:
|
||||||
enum class TestType { Oscillator, LatencyMonitor, NullTest};
|
enum class TestType
|
||||||
|
{
|
||||||
|
Oscillator,
|
||||||
|
LatencyMonitor,
|
||||||
|
NullTest
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioDriver *audioDriver = nullptr;
|
AudioDriver *audioDriver = nullptr;
|
||||||
TestType testType;
|
TestType testType;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AlsaTester(TestType testType)
|
AlsaTester(TestType testType)
|
||||||
: testType(testType)
|
: testType(testType)
|
||||||
{
|
{
|
||||||
// audioDriver = CreateAlsaDriver(this);
|
// audioDriver = CreateAlsaDriver(this);
|
||||||
// audioDriver = CreateJackDriver(this);
|
// audioDriver = CreateJackDriver(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
~AlsaTester()
|
~AlsaTester()
|
||||||
{
|
{
|
||||||
@@ -57,47 +63,47 @@ public:
|
|||||||
delete[] outputBuffers;
|
delete[] outputBuffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useJack = false;
|
void initializeChannelSelection(JackConfiguration &jackConfiguration, ChannelSelection &channelSelection)
|
||||||
|
{
|
||||||
|
size_t nInputs = std::max(jackConfiguration.inputAudioPorts().size(),size_t(2));
|
||||||
|
size_t nOutputs = std::max(jackConfiguration.outputAudioPorts().size(),size_t(2));
|
||||||
|
auto &inputSelections = channelSelection.mainInputChannels();
|
||||||
|
auto &outputSelections = channelSelection.mainOutputChannels();
|
||||||
|
inputSelections.resize(nInputs);
|
||||||
|
for (size_t i = 0; i < nInputs; ++i)
|
||||||
|
{
|
||||||
|
inputSelections[i] = i;
|
||||||
|
}
|
||||||
|
outputSelections.resize(nOutputs);
|
||||||
|
for (size_t i = 0; i < nOutputs; ++i)
|
||||||
|
{
|
||||||
|
outputSelections[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Test()
|
void Test()
|
||||||
{
|
{
|
||||||
|
|
||||||
AlsaFormatEncodeDecodeTest(this);
|
AlsaFormatEncodeDecodeTest(this);
|
||||||
|
|
||||||
JackServerSettings serverSettings("hw:M2","hw:M2",48000,32,3);
|
JackServerSettings serverSettings("hw:M2", "hw:M2", 48000, 32, 3);
|
||||||
|
|
||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
if (useJack)
|
jackConfiguration.AlsaInitialize(serverSettings);
|
||||||
{
|
|
||||||
jackConfiguration.JackInitialize();
|
|
||||||
} else {
|
|
||||||
jackConfiguration.AlsaInitialize(serverSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
JackChannelSelection channelSelection(
|
ChannelSelection channelSelection;
|
||||||
jackConfiguration.inputAudioPorts(),
|
|
||||||
jackConfiguration.outputAudioPorts(),
|
|
||||||
jackConfiguration.inputMidiDevices());
|
|
||||||
|
|
||||||
#if JACK_HOST
|
|
||||||
if (useJack)
|
|
||||||
{
|
|
||||||
audioDriver = CreateJackDriver(this);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
audioDriver = CreateAlsaDriver(this);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
audioDriver = CreateAlsaDriver(this);
|
audioDriver = CreateAlsaDriver(this);
|
||||||
#endif
|
|
||||||
|
|
||||||
|
initializeChannelSelection(jackConfiguration, channelSelection);
|
||||||
|
|
||||||
oscillator.Init(440,jackConfiguration.sampleRate());
|
oscillator.Init(440, jackConfiguration.sampleRate());
|
||||||
latencyMonitor.Init(jackConfiguration.sampleRate());
|
latencyMonitor.Init(jackConfiguration.sampleRate());
|
||||||
audioDriver->Open(serverSettings,channelSelection);
|
audioDriver->Open(serverSettings, channelSelection);
|
||||||
|
|
||||||
inputBuffers = new float*[channelSelection.GetInputAudioPorts().size()];
|
inputBuffers = new float *[channelSelection.mainInputChannels().size()];
|
||||||
outputBuffers = new float*[channelSelection.GetOutputAudioPorts().size()];
|
outputBuffers = new float *[channelSelection.mainOutputChannels().size()];
|
||||||
|
|
||||||
audioDriver->Activate();
|
audioDriver->Activate();
|
||||||
|
|
||||||
@@ -107,7 +113,7 @@ public:
|
|||||||
if (testType == TestType::LatencyMonitor)
|
if (testType == TestType::LatencyMonitor)
|
||||||
{
|
{
|
||||||
auto latency = this->latencyMonitor.GetLatency();
|
auto latency = this->latencyMonitor.GetLatency();
|
||||||
double ms = 1000.0*latency/jackConfiguration.sampleRate();
|
double ms = 1000.0 * latency / jackConfiguration.sampleRate();
|
||||||
|
|
||||||
cout << "Latency: " << latency << " samples " << ms << "ms" << " xruns: " << GetXruns() << " Cpu: " << audioDriver->CpuUse() << "%" << endl;
|
cout << "Latency: " << latency << " samples " << ms << "ms" << " xruns: " << GetXruns() << " Cpu: " << audioDriver->CpuUse() << "%" << endl;
|
||||||
}
|
}
|
||||||
@@ -115,37 +121,40 @@ public:
|
|||||||
|
|
||||||
audioDriver->Deactivate();
|
audioDriver->Deactivate();
|
||||||
audioDriver->Close();
|
audioDriver->Close();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float**inputBuffers = nullptr;
|
float **inputBuffers = nullptr;
|
||||||
float**outputBuffers = nullptr;
|
float **outputBuffers = nullptr;
|
||||||
|
|
||||||
class Oscillator {
|
class Oscillator
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
double dx = 0;
|
double dx = 0;
|
||||||
double x = 0;
|
double x = 0;
|
||||||
double dx2 = 0;
|
double dx2 = 0;
|
||||||
double x2 = 0;
|
double x2 = 0;
|
||||||
public:
|
|
||||||
|
|
||||||
|
public:
|
||||||
void Init(float frequency, size_t sampleRate)
|
void Init(float frequency, size_t sampleRate)
|
||||||
{
|
{
|
||||||
dx = frequency*3.141592736*2/sampleRate;
|
dx = frequency * 3.141592736 * 2 / sampleRate;
|
||||||
dx2 = 0.5*3.141592736*2/sampleRate;
|
dx2 = 0.5 * 3.141592736 * 2 / sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Next() {
|
float Next()
|
||||||
|
{
|
||||||
float result = (float)std::cos(x);
|
float result = (float)std::cos(x);
|
||||||
float env = (float)std::cos(x2);
|
float env = (float)std::cos(x2);
|
||||||
x += dx;
|
x += dx;
|
||||||
x2 += dx2;
|
x2 += dx2;
|
||||||
return result*env;
|
return result * env;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class LatencyMonitor {
|
class LatencyMonitor
|
||||||
enum class State {
|
{
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
Idle,
|
Idle,
|
||||||
Waiting,
|
Waiting,
|
||||||
};
|
};
|
||||||
@@ -156,67 +165,74 @@ public:
|
|||||||
size_t current_latency = 0;
|
size_t current_latency = 0;
|
||||||
size_t latency = 0;
|
size_t latency = 0;
|
||||||
std::mutex sync;
|
std::mutex sync;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Init(uint64_t sampleRate)
|
void Init(uint64_t sampleRate)
|
||||||
{
|
{
|
||||||
idle_samples = (uint64_t)sampleRate*2;
|
idle_samples = (uint64_t)sampleRate * 2;
|
||||||
waiting_samples = (uint64_t)sampleRate*2;
|
waiting_samples = (uint64_t)sampleRate * 2;
|
||||||
|
|
||||||
state = State::Idle;
|
state = State::Idle;
|
||||||
t = idle_samples;
|
t = idle_samples;
|
||||||
latency = 0;
|
latency = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetLatency() {
|
size_t GetLatency()
|
||||||
std::lock_guard lock { sync};
|
{
|
||||||
|
std::lock_guard lock{sync};
|
||||||
return latency;
|
return latency;
|
||||||
}
|
}
|
||||||
float Next(float input)
|
float Next(float input)
|
||||||
{
|
{
|
||||||
switch(state)
|
switch (state)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case State::Idle:
|
case State::Idle:
|
||||||
|
{
|
||||||
|
if (t-- == 0)
|
||||||
{
|
{
|
||||||
if (t-- == 0) {
|
state = State::Waiting;
|
||||||
state = State::Waiting;
|
current_latency = 0;
|
||||||
current_latency = 0;
|
|
||||||
}
|
|
||||||
return 0.01;
|
|
||||||
}
|
}
|
||||||
break;
|
return 0.01;
|
||||||
case State::Waiting:
|
}
|
||||||
|
break;
|
||||||
|
case State::Waiting:
|
||||||
|
{
|
||||||
|
if (std::abs(input) > 0.1 || current_latency > 500)
|
||||||
{
|
{
|
||||||
if (std::abs(input) > 0.1 || current_latency > 500) {
|
{
|
||||||
{
|
std::lock_guard lock{sync};
|
||||||
std::lock_guard lock { sync};
|
latency = current_latency;
|
||||||
latency = current_latency;
|
|
||||||
}
|
|
||||||
state = State::Idle;
|
|
||||||
t = idle_samples;
|
|
||||||
} else {
|
|
||||||
++current_latency;
|
|
||||||
}
|
}
|
||||||
return current_latency < 100 ? 0.25 : 0.0;
|
state = State::Idle;
|
||||||
|
t = idle_samples;
|
||||||
}
|
}
|
||||||
break;
|
else
|
||||||
|
{
|
||||||
|
++current_latency;
|
||||||
|
}
|
||||||
|
return current_latency < 100 ? 0.25 : 0.0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Oscillator oscillator;
|
Oscillator oscillator;
|
||||||
LatencyMonitor latencyMonitor;
|
LatencyMonitor latencyMonitor;
|
||||||
|
|
||||||
virtual void OnAudioTerminated() {
|
virtual void OnAudioTerminated()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnAlsaDriverStopped() {
|
virtual void OnAlsaDriverStopped()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
virtual void OnProcess(size_t nFrames) {
|
virtual void OnProcess(size_t nFrames)
|
||||||
if (testType == TestType::NullTest) return;
|
{
|
||||||
|
if (testType == TestType::NullTest)
|
||||||
|
return;
|
||||||
|
|
||||||
size_t inputs = audioDriver->MainInputBufferCount();
|
size_t inputs = audioDriver->MainInputBufferCount();
|
||||||
size_t outputs = audioDriver->MainOutputBufferCount();
|
size_t outputs = audioDriver->MainOutputBufferCount();
|
||||||
@@ -233,14 +249,15 @@ public:
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < nFrames; ++i)
|
for (size_t i = 0; i < nFrames; ++i)
|
||||||
{
|
{
|
||||||
float v = oscillator.Next()*0.25f;
|
float v = oscillator.Next() * 0.25f;
|
||||||
for (size_t c = 0; c < outputs; ++c)
|
for (size_t c = 0; c < outputs; ++c)
|
||||||
{
|
{
|
||||||
outputBuffers[c][i] = v;
|
outputBuffers[c][i] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
for (size_t i = 0; i < nFrames; ++i)
|
for (size_t i = 0; i < nFrames; ++i)
|
||||||
{
|
{
|
||||||
float v = latencyMonitor.Next(inputBuffers[0][i]);
|
float v = latencyMonitor.Next(inputBuffers[0][i]);
|
||||||
@@ -249,36 +266,34 @@ public:
|
|||||||
outputBuffers[c][i] = v;
|
outputBuffers[c][i] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
std::mutex sync;
|
std::mutex sync;
|
||||||
uint64_t xruns;
|
uint64_t xruns;
|
||||||
|
|
||||||
uint64_t GetXruns() {
|
uint64_t GetXruns()
|
||||||
lock_guard lock { sync};
|
{
|
||||||
|
lock_guard lock{sync};
|
||||||
return xruns;
|
return xruns;
|
||||||
}
|
}
|
||||||
virtual void OnUnderrun() {
|
virtual void OnUnderrun()
|
||||||
lock_guard lock { sync };
|
{
|
||||||
|
lock_guard lock{sync};
|
||||||
++xruns;
|
++xruns;
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_CASE("alsa_test", "[alsa_test]")
|
||||||
|
{
|
||||||
|
|
||||||
TEST_CASE( "alsa_test", "[alsa_test]" ) {
|
|
||||||
|
|
||||||
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
||||||
|
|
||||||
alsaDriver.Test();
|
alsaDriver.Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("alsa_midi_test", "[alsa_midi_test]")
|
||||||
TEST_CASE( "alsa_midi_test", "[alsa_midi_test]" ) {
|
{
|
||||||
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
||||||
|
|
||||||
MidiDecoderTest();
|
MidiDecoderTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1900,11 +1900,11 @@ public:
|
|||||||
|
|
||||||
PIPEDAL_NON_INLINE RealtimePedalboardItemIndex GetRealtimeItemIndex(int64_t instanceId)
|
PIPEDAL_NON_INLINE RealtimePedalboardItemIndex GetRealtimeItemIndex(int64_t instanceId)
|
||||||
{
|
{
|
||||||
Pedalboard::InstanceType instanceType = Pedalboard::GetInstanceTypeFromInstanceId(instanceId);
|
Pedalboard::PedalboardType instanceType = Pedalboard::GetInstanceTypeFromInstanceId(instanceId);
|
||||||
int64_t index = -1;
|
int64_t index = -1;
|
||||||
switch (instanceType)
|
switch (instanceType)
|
||||||
{
|
{
|
||||||
case Pedalboard::InstanceType::MainPedalboard:
|
case Pedalboard::PedalboardType::MainPedalboard:
|
||||||
if (this->currentPedalboard) {
|
if (this->currentPedalboard) {
|
||||||
if (instanceId == Pedalboard::START_CONTROL_ID) {
|
if (instanceId == Pedalboard::START_CONTROL_ID) {
|
||||||
index = Pedalboard::START_CONTROL_ID;
|
index = Pedalboard::START_CONTROL_ID;
|
||||||
@@ -1915,7 +1915,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Pedalboard::InstanceType::MainInsert:
|
case Pedalboard::PedalboardType::MainInsert:
|
||||||
if (this->currentMainInsertPedalboard) {
|
if (this->currentMainInsertPedalboard) {
|
||||||
if (instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID) {
|
if (instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID) {
|
||||||
index = Pedalboard::MAIN_INSERT_START_CONTROL_ID;
|
index = Pedalboard::MAIN_INSERT_START_CONTROL_ID;
|
||||||
@@ -1926,7 +1926,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Pedalboard::InstanceType::AuxInsert:
|
case Pedalboard::PedalboardType::AuxInsert:
|
||||||
if (this->currentAuxInsertPedalboard) {
|
if (this->currentAuxInsertPedalboard) {
|
||||||
if (instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID) {
|
if (instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID) {
|
||||||
index = Pedalboard::START_CONTROL_ID;
|
index = Pedalboard::START_CONTROL_ID;
|
||||||
|
|||||||
+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::InstanceType::MainPedalboard);
|
Pedalboard pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||||
this->addPreset(pedalboard);
|
this->addPreset(pedalboard);
|
||||||
newSelection = presets_[0]->instanceId();
|
newSelection = presets_[0]->instanceId();
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -352,8 +352,9 @@ target_compile_definitions(libpipedald PUBLIC "_REENTRANT")
|
|||||||
target_include_directories(libpipedald PUBLIC ${PIPEDAL_INCLUDES})
|
target_include_directories(libpipedald PUBLIC ${PIPEDAL_INCLUDES})
|
||||||
|
|
||||||
target_link_libraries(libpipedald
|
target_link_libraries(libpipedald
|
||||||
PUBLIC
|
PUBLIC
|
||||||
PiPedalCommon
|
PiPedalCommon
|
||||||
|
PRIVATE
|
||||||
SQLiteCpp
|
SQLiteCpp
|
||||||
${PipeWire_LIBRARIES}
|
${PipeWire_LIBRARIES}
|
||||||
)
|
)
|
||||||
@@ -762,6 +763,7 @@ add_executable(pipedal_latency_test
|
|||||||
|
|
||||||
target_link_libraries(pipedal_latency_test PRIVATE pthread asound PiPedalCommon)
|
target_link_libraries(pipedal_latency_test PRIVATE pthread asound PiPedalCommon)
|
||||||
|
|
||||||
|
target_include_directories(pipedal_latency_test PRIVATE ${PIPEDAL_INCLUDES})
|
||||||
|
|
||||||
add_executable(pipedal_alsa_info
|
add_executable(pipedal_alsa_info
|
||||||
alsaCheck.cpp alsaCheck.hpp
|
alsaCheck.cpp alsaCheck.hpp
|
||||||
@@ -859,10 +861,14 @@ add_custom_target (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
install (TARGETS pipedalconfig pipedal_kconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
|
install (TARGETS pipedalconfig pipedal_kconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
|
||||||
EXPORT pipedalTargets)
|
EXPORT pipedalTargets
|
||||||
|
)
|
||||||
|
|
||||||
install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
||||||
EXPORT pipedalSbinTargets)
|
EXPORT pipedalSbinTargets
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -129,8 +129,8 @@ void ChannelSelection::normalizeChannelSelection() {
|
|||||||
|
|
||||||
|
|
||||||
ChannelRouterSettings::ChannelRouterSettings()
|
ChannelRouterSettings::ChannelRouterSettings()
|
||||||
: mainInserts_(Pedalboard::InstanceType::MainInsert)
|
: mainInserts_(Pedalboard::PedalboardType::MainInsert)
|
||||||
, auxInserts_(Pedalboard::InstanceType::AuxInsert)
|
, auxInserts_(Pedalboard::PedalboardType::AuxInsert)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace pipedal
|
|||||||
|
|
||||||
std::vector<ControlValue> controlValues_;
|
std::vector<ControlValue> controlValues_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using self = ChannelRouterSettings;
|
using self = ChannelRouterSettings;
|
||||||
using ptr = std::shared_ptr<self>;
|
using ptr = std::shared_ptr<self>;
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ namespace pipedal
|
|||||||
|
|
||||||
uint64_t numberOfAudioInputChannels() const;
|
uint64_t numberOfAudioInputChannels() const;
|
||||||
uint64_t numberOfAudioOutputChannels() const;
|
uint64_t numberOfAudioOutputChannels() const;
|
||||||
|
|
||||||
JSON_GETTER_SETTER(configured)
|
JSON_GETTER_SETTER(configured)
|
||||||
JSON_GETTER_SETTER(channelRouterPresetId)
|
JSON_GETTER_SETTER(channelRouterPresetId)
|
||||||
JSON_GETTER_SETTER_REF(mainInputChannels)
|
JSON_GETTER_SETTER_REF(mainInputChannels)
|
||||||
@@ -70,28 +70,34 @@ namespace pipedal
|
|||||||
JSON_GETTER_SETTER_REF(sendOutputChannels)
|
JSON_GETTER_SETTER_REF(sendOutputChannels)
|
||||||
JSON_GETTER_SETTER_REF(controlValues)
|
JSON_GETTER_SETTER_REF(controlValues)
|
||||||
|
|
||||||
|
|
||||||
DECLARE_JSON_MAP(ChannelRouterSettings);
|
DECLARE_JSON_MAP(ChannelRouterSettings);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// just the channel selecttions.
|
// just the channel selecttions.
|
||||||
class ChannelSelection {
|
class ChannelSelection
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
ChannelSelection() = default;
|
ChannelSelection() = default;
|
||||||
ChannelSelection(ChannelRouterSettings&settings);
|
ChannelSelection(ChannelRouterSettings &settings);
|
||||||
ChannelSelection(const ChannelSelection&other) = default;
|
ChannelSelection(const ChannelSelection &other) = default;
|
||||||
ChannelSelection(ChannelSelection&&other) = default;
|
ChannelSelection(ChannelSelection &&other) = default;
|
||||||
ChannelSelection& operator=(const ChannelSelection&other) = default;
|
ChannelSelection &operator=(const ChannelSelection &other) = default;
|
||||||
ChannelSelection& operator=(ChannelSelection&&other) = default;
|
ChannelSelection &operator=(ChannelSelection &&other) = default;
|
||||||
~ChannelSelection() = default;
|
~ChannelSelection() = default;
|
||||||
|
|
||||||
const std::vector<int64_t>&mainInputChannels() const { return mainInputChannels_; }
|
const std::vector<int64_t> &mainInputChannels() const { return mainInputChannels_; }
|
||||||
const std::vector<int64_t>&mainOutputChannels() const { return mainOutputChannels_; }
|
const std::vector<int64_t> &mainOutputChannels() const { return mainOutputChannels_; }
|
||||||
const std::vector<int64_t>&auxInputChannels() const { return auxInputChannels_; }
|
const std::vector<int64_t> &auxInputChannels() const { return auxInputChannels_; }
|
||||||
const std::vector<int64_t>&auxOutputChannels() const { return auxOutputChannels_; }
|
const std::vector<int64_t> &auxOutputChannels() const { return auxOutputChannels_; }
|
||||||
const std::vector<int64_t>&sendInputChannels() const { return sendInputChannels_; }
|
const std::vector<int64_t> &sendInputChannels() const { return sendInputChannels_; }
|
||||||
const std::vector<int64_t>&sendOutputChannels() const { return sendOutputChannels_; }
|
const std::vector<int64_t> &sendOutputChannels() const { return sendOutputChannels_; }
|
||||||
|
|
||||||
|
std::vector<int64_t> &mainInputChannels() { return mainInputChannels_; }
|
||||||
|
std::vector<int64_t> &mainOutputChannels() { return mainOutputChannels_; }
|
||||||
|
std::vector<int64_t> &auxInputChannels() { return auxInputChannels_; }
|
||||||
|
std::vector<int64_t> &auxOutputChannels() { return auxOutputChannels_; }
|
||||||
|
std::vector<int64_t> &sendInputChannels() { return sendInputChannels_; }
|
||||||
|
std::vector<int64_t> &sendOutputChannels() { return sendOutputChannels_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void normalizeChannelSelection();
|
void normalizeChannelSelection();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <lilv/lilv.h>
|
||||||
#include "AudioDriver.hpp"
|
#include "AudioDriver.hpp"
|
||||||
#include "JackServerSettings.hpp"
|
#include "JackServerSettings.hpp"
|
||||||
|
|
||||||
|
|||||||
+40
-3
@@ -320,6 +320,7 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
|
|||||||
void Lv2Pedalboard::Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList, ExistingEffectMap *existingEffects)
|
void Lv2Pedalboard::Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList, ExistingEffectMap *existingEffects)
|
||||||
{
|
{
|
||||||
this->pHost = pHost;
|
this->pHost = pHost;
|
||||||
|
this->pedalboardType = pedalboard.GetInstanceType();
|
||||||
|
|
||||||
inputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
|
inputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
|
||||||
outputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
|
outputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
|
||||||
@@ -329,13 +330,15 @@ void Lv2Pedalboard::Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardE
|
|||||||
inputVolume.SetTarget(pedalboard.input_volume_db());
|
inputVolume.SetTarget(pedalboard.input_volume_db());
|
||||||
outputVolume.SetTarget(pedalboard.output_volume_db());
|
outputVolume.SetTarget(pedalboard.output_volume_db());
|
||||||
|
|
||||||
for (int i = 0; i < pHost->GetNumberOfInputAudioChannels(); ++i)
|
size_t nInputs = std::max(GetNumberOfAudioInputChannels(),(size_t)1);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nInputs; ++i)
|
||||||
{
|
{
|
||||||
this->pedalboardInputBuffers.push_back(bufferPool.AllocateBuffer<float>(pHost->GetMaxAudioBufferSize()));
|
this->pedalboardInputBuffers.push_back(bufferPool.AllocateBuffer<float>(pHost->GetMaxAudioBufferSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto outputs = PrepareItems(pedalboard.items(), this->pedalboardInputBuffers, errorList, existingEffects);
|
auto outputs = PrepareItems(pedalboard.items(), this->pedalboardInputBuffers, errorList, existingEffects);
|
||||||
int nOutputs = pHost->GetNumberOfOutputAudioChannels();
|
size_t nOutputs = GetNumberOfAudioOutputChannels();
|
||||||
if (nOutputs == 1)
|
if (nOutputs == 1)
|
||||||
{
|
{
|
||||||
this->pedalboardOutputBuffers.push_back(outputs[0]);
|
this->pedalboardOutputBuffers.push_back(outputs[0]);
|
||||||
@@ -572,7 +575,12 @@ void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samp
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < vuConfiguration->enabledIndexes.size(); ++i)
|
for (size_t i = 0; i < vuConfiguration->enabledIndexes.size(); ++i)
|
||||||
{
|
{
|
||||||
int index = vuConfiguration->enabledIndexes[i];
|
auto& rtIndex = vuConfiguration->enabledIndexes[i];
|
||||||
|
if (rtIndex.instanceType != this->pedalboardType)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int index = rtIndex.index;
|
||||||
VuUpdate *pUpdate = &vuConfiguration->vuUpdateWorkingData[i];
|
VuUpdate *pUpdate = &vuConfiguration->vuUpdateWorkingData[i];
|
||||||
if (index == Pedalboard::START_CONTROL_ID)
|
if (index == Pedalboard::START_CONTROL_ID)
|
||||||
{
|
{
|
||||||
@@ -1016,3 +1024,32 @@ void Lv2Pedalboard::handleTapTempo(uint8_t value, const MidiTimestamp& timestamp
|
|||||||
mapping.lastTapTimestamp = timestamp;
|
mapping.lastTapTimestamp = timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Lv2Pedalboard::GetNumberOfAudioInputChannels() const {
|
||||||
|
switch (pedalboardType)
|
||||||
|
{
|
||||||
|
case Pedalboard::PedalboardType::MainPedalboard:
|
||||||
|
return pHost->GetChannelSelection().mainInputChannels().size();
|
||||||
|
case Pedalboard::PedalboardType::MainInsert:
|
||||||
|
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||||
|
case Pedalboard::PedalboardType::AuxInsert:
|
||||||
|
return pHost->GetChannelSelection().auxInputChannels().size();
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Lv2Pedalboard::GetNumberOfAudioOutputChannels() const {
|
||||||
|
switch (pedalboardType)
|
||||||
|
{
|
||||||
|
case Pedalboard::PedalboardType::MainPedalboard:
|
||||||
|
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||||
|
case Pedalboard::PedalboardType::MainInsert:
|
||||||
|
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||||
|
case Pedalboard::PedalboardType::AuxInsert:
|
||||||
|
return pHost->GetChannelSelection().auxOutputChannels().size();
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,6 +51,7 @@ namespace pipedal
|
|||||||
class Lv2Pedalboard
|
class Lv2Pedalboard
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
Pedalboard::PedalboardType pedalboardType;
|
||||||
IHost *pHost = nullptr;
|
IHost *pHost = nullptr;
|
||||||
size_t currentFrameOffset = 0;
|
size_t currentFrameOffset = 0;
|
||||||
DbDezipper inputVolume;
|
DbDezipper inputVolume;
|
||||||
@@ -128,6 +129,9 @@ namespace pipedal
|
|||||||
std::vector<IEffect *> &GetEffects() { return realtimeEffects; }
|
std::vector<IEffect *> &GetEffects() { return realtimeEffects; }
|
||||||
std::vector<std::shared_ptr<IEffect>> &GetSharedEffectList() { return effects; }
|
std::vector<std::shared_ptr<IEffect>> &GetSharedEffectList() { return effects; }
|
||||||
|
|
||||||
|
size_t GetNumberOfAudioInputChannels() const;
|
||||||
|
size_t GetNumberOfAudioOutputChannels() const;
|
||||||
|
|
||||||
int GetIndexOfInstanceId(uint64_t instanceId)
|
int GetIndexOfInstanceId(uint64_t instanceId)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < this->realtimeEffects.size(); ++i)
|
for (int i = 0; i < this->realtimeEffects.size(); ++i)
|
||||||
|
|||||||
+5
-5
@@ -210,7 +210,7 @@ PedalboardItem Pedalboard::MakeSplit()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Pedalboard Pedalboard::MakeDefault(InstanceType instanceType)
|
Pedalboard Pedalboard::MakeDefault(PedalboardType instanceType)
|
||||||
{
|
{
|
||||||
// copy insanity. but it happens so rarely.
|
// copy insanity. but it happens so rarely.
|
||||||
Pedalboard result;
|
Pedalboard result;
|
||||||
@@ -510,17 +510,17 @@ Snapshot Pedalboard::MakeSnapshotFromCurrentSettings(const Pedalboard &previousP
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pedalboard::Pedalboard(InstanceType instanceType = InstanceType::MainPedalboard)
|
Pedalboard::Pedalboard(PedalboardType instanceType)
|
||||||
{
|
{
|
||||||
switch (instanceType)
|
switch (instanceType)
|
||||||
{
|
{
|
||||||
case InstanceType::MainPedalboard:
|
case PedalboardType::MainPedalboard:
|
||||||
nextInstanceId_ = 0;
|
nextInstanceId_ = 0;
|
||||||
break;
|
break;
|
||||||
case InstanceType::MainInsert:
|
case PedalboardType::MainInsert:
|
||||||
nextInstanceId_ = MAIN_INSERT_INSTANCE_BASE;;
|
nextInstanceId_ = MAIN_INSERT_INSTANCE_BASE;;
|
||||||
break;
|
break;
|
||||||
case InstanceType::AuxInsert:
|
case PedalboardType::AuxInsert:
|
||||||
nextInstanceId_ = AUX_INSERT_INSTANCE_BASE;;
|
nextInstanceId_ = AUX_INSERT_INSTANCE_BASE;;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -230,13 +230,14 @@ 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_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 TWO_POWER_48 = 281474976710656; // Number of possible instance IDs for Main Pedalboards.
|
||||||
static constexpr int64_t MAX_INSTANCE_ID = TWO_POWER_48;
|
static constexpr int64_t MAX_INSTANCE_ID = TWO_POWER_48;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum class InstanceType {
|
enum class PedalboardType {
|
||||||
MainPedalboard,
|
MainPedalboard,
|
||||||
MainInsert,
|
MainInsert,
|
||||||
AuxInsert
|
AuxInsert
|
||||||
@@ -253,25 +254,25 @@ public:
|
|||||||
static constexpr int64_t AUX_INSERT_START_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
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 constexpr int64_t AUX_INSERT_END_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||||
|
|
||||||
static InstanceType GetInstanceTypeFromInstanceId(int64_t instanceId) {
|
static PedalboardType GetInstanceTypeFromInstanceId(int64_t instanceId) {
|
||||||
if (instanceId == START_CONTROL_ID) return InstanceType::MainPedalboard;
|
if (instanceId == START_CONTROL_ID) return PedalboardType::MainPedalboard;
|
||||||
if (instanceId == END_CONTROL_ID) return InstanceType::MainPedalboard;
|
if (instanceId == END_CONTROL_ID) return PedalboardType::MainPedalboard;
|
||||||
|
|
||||||
if (instanceId >= MAIN_INSERT_INSTANCE_BASE) {
|
if (instanceId >= MAIN_INSERT_INSTANCE_BASE) {
|
||||||
return InstanceType::MainInsert;
|
return PedalboardType::MainInsert;
|
||||||
} else if (instanceId >= AUX_INSERT_INSTANCE_BASE) {
|
} else if (instanceId >= AUX_INSERT_INSTANCE_BASE) {
|
||||||
return InstanceType::AuxInsert;
|
return PedalboardType::AuxInsert;
|
||||||
} else {
|
} else {
|
||||||
return InstanceType::MainPedalboard;
|
return PedalboardType::MainPedalboard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstanceType GetInstanceType() const {
|
PedalboardType GetInstanceType() const {
|
||||||
return GetInstanceTypeFromInstanceId(this->nextInstanceId_); // instanceId is irrelevant here.
|
return GetInstanceTypeFromInstanceId(this->nextInstanceId_); // instanceId is irrelevant here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Pedalboard(InstanceType instanceType = InstanceType::MainPedalboard);
|
Pedalboard(PedalboardType instanceType = PedalboardType::MainPedalboard);
|
||||||
|
|
||||||
// deep copy, breaking shared pointers.
|
// deep copy, breaking shared pointers.
|
||||||
Pedalboard DeepCopy();
|
Pedalboard DeepCopy();
|
||||||
@@ -307,7 +308,7 @@ public:
|
|||||||
PedalboardItem MakeSplit();
|
PedalboardItem MakeSplit();
|
||||||
|
|
||||||
|
|
||||||
static Pedalboard MakeDefault(Pedalboard::InstanceType instanceType);
|
static Pedalboard MakeDefault(Pedalboard::PedalboardType instanceType = Pedalboard::PedalboardType::MainPedalboard);
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef GETTER_SETTER_REF
|
#undef GETTER_SETTER_REF
|
||||||
|
|||||||
+18
-10
@@ -34,6 +34,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
#include "ChannelRouterSettings.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -169,19 +170,26 @@ public:
|
|||||||
delete[] inputBuffers;
|
delete[] inputBuffers;
|
||||||
delete[] outputBuffers;
|
delete[] outputBuffers;
|
||||||
}
|
}
|
||||||
std::vector<std::string> SelectChannels(const std::vector<std::string> &available, const std::vector<int> &selection)
|
std::vector<int64_t> SelectChannels(const std::vector<std::string> &available, const std::vector<int> &selection)
|
||||||
{
|
{
|
||||||
|
std::vector<int64_t> result;
|
||||||
if (selection.size() == 0)
|
if (selection.size() == 0)
|
||||||
return available;
|
{
|
||||||
|
result.resize(available.size());
|
||||||
|
for (size_t i = 0; i < result.size(); ++i)
|
||||||
|
{
|
||||||
|
result[i] = (int64_t)i;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> result;
|
|
||||||
for (int sel : selection)
|
for (int sel : selection)
|
||||||
{
|
{
|
||||||
if (sel < 0 || sel >= available.size())
|
if (sel < 0 || sel >= available.size())
|
||||||
{
|
{
|
||||||
throw PiPedalArgumentException(SS("Invalid channel: " + sel));
|
throw PiPedalArgumentException(SS("Invalid channel: " + sel));
|
||||||
}
|
}
|
||||||
result.push_back(available[sel]);
|
result.push_back(sel);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -199,22 +207,22 @@ public:
|
|||||||
auto &availableInputs = jackConfiguration.inputAudioPorts();
|
auto &availableInputs = jackConfiguration.inputAudioPorts();
|
||||||
auto &availableOutputs = jackConfiguration.outputAudioPorts();
|
auto &availableOutputs = jackConfiguration.outputAudioPorts();
|
||||||
|
|
||||||
std::vector<std::string> inputAudioPorts, outputAudioPorts;
|
std::vector<int64_t> inputAudioPorts, outputAudioPorts;
|
||||||
|
|
||||||
inputAudioPorts = SelectChannels(availableInputs, this->inputChannels);
|
inputAudioPorts = SelectChannels(availableInputs, this->inputChannels);
|
||||||
outputAudioPorts = SelectChannels(availableOutputs, this->outputChannels);
|
outputAudioPorts = SelectChannels(availableOutputs, this->outputChannels);
|
||||||
|
|
||||||
JackChannelSelection channelSelection(
|
ChannelSelection channelSelection;
|
||||||
inputAudioPorts, outputAudioPorts,
|
channelSelection.mainInputChannels() = inputAudioPorts;
|
||||||
std::vector<AlsaMidiDeviceInfo>());
|
channelSelection.mainOutputChannels() = outputAudioPorts;
|
||||||
|
|
||||||
audioDriver = CreateAlsaDriver(this);
|
audioDriver = CreateAlsaDriver(this);
|
||||||
|
|
||||||
latencyMonitor.Init(jackConfiguration.sampleRate());
|
latencyMonitor.Init(jackConfiguration.sampleRate());
|
||||||
audioDriver->Open(serverSettings, channelSelection);
|
audioDriver->Open(serverSettings, channelSelection);
|
||||||
|
|
||||||
inputBuffers = new float *[channelSelection.GetInputAudioPorts().size()];
|
inputBuffers = new float *[channelSelection.mainInputChannels().size()];
|
||||||
outputBuffers = new float *[channelSelection.GetOutputAudioPorts().size()];
|
outputBuffers = new float *[channelSelection.mainOutputChannels().size()];
|
||||||
|
|
||||||
audioDriver->Activate();
|
audioDriver->Activate();
|
||||||
|
|
||||||
|
|||||||
+14
-8
@@ -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(Pedalboard::InstanceType::MainPedalboard);
|
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::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,14 +216,20 @@ 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
|
||||||
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
this->jackConfiguration = this->jackConfiguration.JackInitialize();
|
||||||
#else
|
#else
|
||||||
this->jackServerSettings = storage.GetJackServerSettings();
|
this->jackServerSettings = storage.GetJackServerSettings();
|
||||||
|
this->jackConfiguration.AlsaInitialize(jackServerSettings);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
this->channelRouterSettings = storage.GetChannelRouterSettings();
|
||||||
|
pluginHost.OnConfigurationChanged(
|
||||||
|
jackConfiguration,
|
||||||
|
storage.GetChannelSelection());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiPedalModel::LoadLv2PluginInfo()
|
void PiPedalModel::LoadLv2PluginInfo()
|
||||||
@@ -299,7 +305,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(Pedalboard::InstanceType::MainPedalboard);
|
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1429,7 +1435,7 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
|
|||||||
|
|
||||||
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
|
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
|
||||||
|
|
||||||
FireChannelSelectionChanged(-1);
|
FireChannelRouterSettingsChanged(-1);
|
||||||
LoadCurrentPedalboard();
|
LoadCurrentPedalboard();
|
||||||
|
|
||||||
this->UpdateRealtimeVuSubscriptions();
|
this->UpdateRealtimeVuSubscriptions();
|
||||||
@@ -1526,17 +1532,17 @@ std::vector<AlsaSequencerPortSelection> PiPedalModel::GetAlsaSequencerPorts()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
|
void PiPedalModel::FireChannelRouterSettingsChanged(int64_t clientId)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||||
{
|
{
|
||||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||||
JackChannelSelection channelSelection = storage.GetJackChannelSelection(this->jackConfiguration);
|
ChannelRouterSettings::ptr channelRouterSettings = this->channelRouterSettings;
|
||||||
|
|
||||||
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||||
for (auto &subscriber : t)
|
for (auto &subscriber : t)
|
||||||
{
|
{
|
||||||
subscriber->OnChannelSelectionChanged(clientId, channelSelection);
|
subscriber->OnChannelRouterSettingsChanged(clientId, *channelRouterSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3331,7 +3337,7 @@ void PiPedalModel::SetChannelRouterSettings(int64_t clientId,ChannelRouterSettin
|
|||||||
}
|
}
|
||||||
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
|
||||||
|
|
||||||
this->FireChannelSelectionChanged(clientId);
|
this->FireChannelRouterSettingsChanged(clientId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace pipedal
|
|||||||
virtual void OnSnapshotModified(int64_t selectedSnapshot, bool modified) = 0;
|
virtual void OnSnapshotModified(int64_t selectedSnapshot, bool modified) = 0;
|
||||||
virtual void OnSelectedSnapshotChanged(int64_t selectedSnapshot) = 0;
|
virtual void OnSelectedSnapshotChanged(int64_t selectedSnapshot) = 0;
|
||||||
virtual void OnPluginPresetsChanged(const std::string &pluginUri) = 0;
|
virtual void OnPluginPresetsChanged(const std::string &pluginUri) = 0;
|
||||||
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection &channelSelection) = 0;
|
virtual void OnChannelRouterSettingsChanged(int64_t clientId, const ChannelRouterSettings &channelRouterSettings) = 0;
|
||||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates) = 0;
|
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates) = 0;
|
||||||
virtual void OnBankIndexChanged(const BankIndex &bankIndex) = 0;
|
virtual void OnBankIndexChanged(const BankIndex &bankIndex) = 0;
|
||||||
virtual void OnJackServerSettingsChanged(const JackServerSettings &jackServerSettings) = 0;
|
virtual void OnJackServerSettingsChanged(const JackServerSettings &jackServerSettings) = 0;
|
||||||
@@ -206,7 +206,7 @@ namespace pipedal
|
|||||||
void FirePresetChanged(bool changed);
|
void FirePresetChanged(bool changed);
|
||||||
void FirePluginPresetsChanged(const std::string &pluginUri);
|
void FirePluginPresetsChanged(const std::string &pluginUri);
|
||||||
void FirePedalboardChanged(int64_t clientId, bool reloadAudioThread = true);
|
void FirePedalboardChanged(int64_t clientId, bool reloadAudioThread = true);
|
||||||
void FireChannelSelectionChanged(int64_t clientId);
|
void FireChannelRouterSettingsChanged(int64_t clientId);
|
||||||
void FireBanksChanged(int64_t clientId);
|
void FireBanksChanged(int64_t clientId);
|
||||||
void FireJackConfigurationChanged(const JackConfiguration &jackConfiguration);
|
void FireJackConfigurationChanged(const JackConfiguration &jackConfiguration);
|
||||||
void FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State);
|
void FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State);
|
||||||
|
|||||||
+16
-11
@@ -537,17 +537,24 @@ JSON_MAP_REFERENCE(SnapshotModifiedBody, snapshotIndex)
|
|||||||
JSON_MAP_REFERENCE(SnapshotModifiedBody, modified)
|
JSON_MAP_REFERENCE(SnapshotModifiedBody, modified)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
class ChannelSelectionChangedBody
|
class ChannelRouterSettingsChangedBody
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int64_t clientId_ = -1;
|
ChannelRouterSettingsChangedBody(
|
||||||
JackChannelSelection *jackChannelSelection_ = nullptr;
|
int64_t clientId,
|
||||||
|
const ChannelRouterSettings &channelRouterSettings
|
||||||
|
): clientId_(clientId), channelRouterSettings_(channelRouterSettings)
|
||||||
|
{
|
||||||
|
|
||||||
DECLARE_JSON_MAP(ChannelSelectionChangedBody);
|
}
|
||||||
|
int64_t clientId_ = -1;
|
||||||
|
ChannelRouterSettings channelRouterSettings_;
|
||||||
|
|
||||||
|
DECLARE_JSON_MAP(ChannelRouterSettingsChangedBody);
|
||||||
};
|
};
|
||||||
JSON_MAP_BEGIN(ChannelSelectionChangedBody)
|
JSON_MAP_BEGIN(ChannelRouterSettingsChangedBody)
|
||||||
JSON_MAP_REFERENCE(ChannelSelectionChangedBody, clientId)
|
JSON_MAP_REFERENCE(ChannelRouterSettingsChangedBody, clientId)
|
||||||
JSON_MAP_REFERENCE(ChannelSelectionChangedBody, jackChannelSelection)
|
JSON_MAP_REFERENCE(ChannelRouterSettingsChangedBody, channelRouterSettings)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
class PresetsChangedBody
|
class PresetsChangedBody
|
||||||
@@ -2000,11 +2007,9 @@ private:
|
|||||||
Send("onShowStatusMonitorChanged", show);
|
Send("onShowStatusMonitorChanged", show);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection &channelSelection)
|
virtual void OnChannelRouterSettingsChanged(int64_t clientId, const ChannelRouterSettings &channelRouterSettings)
|
||||||
{
|
{
|
||||||
ChannelSelectionChangedBody body;
|
ChannelRouterSettingsChangedBody body(clientId, channelRouterSettings);
|
||||||
body.clientId_ = clientId;
|
|
||||||
body.jackChannelSelection_ = const_cast<JackChannelSelection *>(&channelSelection);
|
|
||||||
Send("onChannelSelectionChanged", body);
|
Send("onChannelSelectionChanged", body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,20 +98,20 @@ namespace pipedal
|
|||||||
|
|
||||||
struct RealtimePedalboardItemIndex {
|
struct RealtimePedalboardItemIndex {
|
||||||
RealtimePedalboardItemIndex()
|
RealtimePedalboardItemIndex()
|
||||||
: instanceType(Pedalboard::InstanceType::MainPedalboard), index(-1)
|
: instanceType(Pedalboard::PedalboardType::MainPedalboard), index(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
RealtimePedalboardItemIndex(
|
RealtimePedalboardItemIndex(
|
||||||
Pedalboard::InstanceType instanceType,
|
Pedalboard::PedalboardType instanceType,
|
||||||
int index)
|
int64_t index)
|
||||||
: instanceType(instanceType), index(index)
|
: instanceType(instanceType), index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
RealtimePedalboardItemIndex(const RealtimePedalboardItemIndex& other) = default;
|
RealtimePedalboardItemIndex(const RealtimePedalboardItemIndex& other) = default;
|
||||||
RealtimePedalboardItemIndex& operator=(const RealtimePedalboardItemIndex& other) = default;
|
RealtimePedalboardItemIndex& operator=(const RealtimePedalboardItemIndex& other) = default;
|
||||||
|
|
||||||
Pedalboard::InstanceType instanceType = Pedalboard::InstanceType::MainPedalboard;
|
Pedalboard::PedalboardType instanceType = Pedalboard::PedalboardType::MainPedalboard;
|
||||||
int index = -1;
|
int64_t index = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RealtimeMidiEventRequest
|
struct RealtimeMidiEventRequest
|
||||||
|
|||||||
@@ -19,13 +19,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "lilv/lilv.h"
|
|
||||||
#include "lv2/state/state.h"
|
#include "lv2/state/state.h"
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include "json_variant.hpp"
|
#include "json_variant.hpp"
|
||||||
#include "MapFeature.hpp"
|
#include "MapFeature.hpp"
|
||||||
#include "IHost.hpp"
|
#include "IHost.hpp"
|
||||||
#include "lilv/lilv.h"
|
#include <lilv/lilv.h>
|
||||||
|
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-3
@@ -377,7 +377,7 @@ void Storage::Initialize()
|
|||||||
LoadTone3000Auth();
|
LoadTone3000Auth();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadChannelSelection();
|
LoadJackChannelSelection();
|
||||||
}
|
}
|
||||||
catch (const std::exception &)
|
catch (const std::exception &)
|
||||||
{
|
{
|
||||||
@@ -476,7 +476,7 @@ void Storage::LoadBankIndex()
|
|||||||
if (bankIndex.entries().size() == 0)
|
if (bankIndex.entries().size() == 0)
|
||||||
{
|
{
|
||||||
currentBank.clear();
|
currentBank.clear();
|
||||||
Pedalboard defaultPedalboard = Pedalboard::MakeDefault();
|
Pedalboard defaultPedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||||
int64_t instanceId = currentBank.addPreset(defaultPedalboard);
|
int64_t instanceId = currentBank.addPreset(defaultPedalboard);
|
||||||
currentBank.selectedPreset(instanceId);
|
currentBank.selectedPreset(instanceId);
|
||||||
|
|
||||||
@@ -1135,7 +1135,7 @@ AlsaSequencerConfiguration Storage::GetAlsaSequencerConfiguration() const
|
|||||||
return this->alsaSequencerConfiguration;
|
return this->alsaSequencerConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::LoadChannelSelection()
|
void Storage::LoadJackChannelSelection()
|
||||||
{
|
{
|
||||||
auto fileName = this->GetChannelSelectionFileName();
|
auto fileName = this->GetChannelSelectionFileName();
|
||||||
if (std::filesystem::exists(fileName))
|
if (std::filesystem::exists(fileName))
|
||||||
@@ -3102,6 +3102,7 @@ ChannelRouterSettings::ptr Storage::LoadChannelRouterSettings()
|
|||||||
json_reader reader(is);
|
json_reader reader(is);
|
||||||
ChannelRouterSettings::ptr result;
|
ChannelRouterSettings::ptr result;
|
||||||
reader.read(&result);
|
reader.read(&result);
|
||||||
|
this->channelSelection = ChannelSelection(*result);
|
||||||
return result;
|
return result;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
Lv2Log::error("Failed to load Channel Router settings: %s", e.what());
|
Lv2Log::error("Failed to load Channel Router settings: %s", e.what());
|
||||||
|
|||||||
+1
-1
@@ -100,7 +100,7 @@ private:
|
|||||||
void LoadCurrentBank();
|
void LoadCurrentBank();
|
||||||
void SaveCurrentBank();
|
void SaveCurrentBank();
|
||||||
|
|
||||||
void LoadChannelSelection();
|
void LoadJackChannelSelection();
|
||||||
void SaveChannelSelection();
|
void SaveChannelSelection();
|
||||||
|
|
||||||
void LoadAlsaSequencerConfiguration();
|
void LoadAlsaSequencerConfiguration();
|
||||||
|
|||||||
@@ -1,23 +1,61 @@
|
|||||||
|
|
||||||
import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
|
import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
|
||||||
|
import JackServerSettings from "./JackServerSettings.tsx";
|
||||||
|
import JackConfiguration from "./Jack.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
function isActiveChannel(channels: number[]): boolean {
|
||||||
|
if (channels.length < 2) return false;
|
||||||
|
return channels[1] >= 0 || channels[0] >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chName(ch: number): string {
|
||||||
|
if (ch === -1) {
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
return "Ch " + (ch + 1);
|
||||||
|
}
|
||||||
|
function channelPairName(channels: number[], maxChannels: number): string {
|
||||||
|
if (channels.length !== 2) {
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
if (channels[0] === -1 && channels[1] === -1) {
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxChannels === 2) {
|
||||||
|
if (channels[0] === 0 && channels[1] === 1) {
|
||||||
|
return "Stereo";
|
||||||
|
}
|
||||||
|
if (channels[0] === 0 && (channels[0] === channels[1] || channels[1] === -1)) {
|
||||||
|
return "Left";
|
||||||
|
}
|
||||||
|
if (channels[1] === 1 && (channels[0] === channels[1])) {
|
||||||
|
return "Right";
|
||||||
|
}
|
||||||
|
if (channels[0] === 1 && channels[1] === 0) {
|
||||||
|
return "Right,Left";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chName(channels[0]) + "," + chName(channels[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class ChannelRouterSettings {
|
export default class ChannelRouterSettings {
|
||||||
configured: boolean = false;
|
configured: boolean = false;
|
||||||
channelRouterPresetId: number = -1;
|
channelRouterPresetId: number = -1;
|
||||||
|
|
||||||
mainInputChannels: number[] = [1,1];
|
mainInputChannels: number[] = [1, 1];
|
||||||
mainOutputChannels: number[] = [0,1];
|
mainOutputChannels: number[] = [0, 1];
|
||||||
mainInserts: Pedalboard = new Pedalboard();
|
mainInserts: Pedalboard = new Pedalboard();
|
||||||
|
|
||||||
auxInputChannels: number[] = [0,0];
|
auxInputChannels: number[] = [0, 0];
|
||||||
auxOutputChannels: number[] = [-1,-1];
|
auxOutputChannels: number[] = [-1, -1];
|
||||||
auxInserts: Pedalboard = new Pedalboard();
|
auxInserts: Pedalboard = new Pedalboard();
|
||||||
|
|
||||||
sendInputChannels: number[] = [-1,-1];
|
sendInputChannels: number[] = [-1, -1];
|
||||||
sendOutputChannels: number[] = [-1,-1];
|
sendOutputChannels: number[] = [-1, -1];
|
||||||
|
|
||||||
controlValues: ControlValue[] = [];
|
controlValues: ControlValue[] = [];
|
||||||
// Inserts...
|
// Inserts...
|
||||||
@@ -55,10 +93,89 @@ export default class ChannelRouterSettings {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let newValue = new ControlValue(symbol,value);
|
let newValue = new ControlValue(symbol, value);
|
||||||
this.controlValues.push(newValue);
|
this.controlValues.push(newValue);
|
||||||
this.controlValues = this.controlValues.slice(); // trigger observers.
|
this.controlValues = this.controlValues.slice(); // trigger observers.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescription(jackConfiguration: JackConfiguration): string {
|
||||||
|
if (!this.configured) {
|
||||||
|
return "Not configured";
|
||||||
|
}
|
||||||
|
if (!jackConfiguration.isValid)
|
||||||
|
{
|
||||||
|
return "Not configured"
|
||||||
|
}
|
||||||
|
if (!this.isValid(jackConfiguration)) {
|
||||||
|
return "Invalid configuration";
|
||||||
|
}
|
||||||
|
let nInputs = jackConfiguration.inputAudioPorts.length;
|
||||||
|
let nOutputs = jackConfiguration.outputAudioPorts.length;
|
||||||
|
let description = channelPairName(this.mainInputChannels, nInputs) + " -> " +
|
||||||
|
channelPairName(this.mainOutputChannels, nOutputs);
|
||||||
|
if (isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels))
|
||||||
|
{
|
||||||
|
description += " " + "+ re-amp: " + channelPairName(this.auxInputChannels, nInputs) + " -> " +
|
||||||
|
channelPairName(this.auxOutputChannels, nOutputs);
|
||||||
|
}
|
||||||
|
if (isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels)) {
|
||||||
|
description += " " + "+ send: " +channelPairName(this.sendOutputChannels, nOutputs)
|
||||||
|
+ " -> " + channelPairName(this.sendInputChannels, nInputs);
|
||||||
|
}
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
canEdit(jackConfiguration: JackConfiguration): boolean {
|
||||||
|
return jackConfiguration.isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid(jackConfiguration: JackConfiguration): boolean {
|
||||||
|
if (!this.configured) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let maxInputChannels = jackConfiguration.inputAudioPorts.length;
|
||||||
|
let maxOutputChannels = jackConfiguration.outputAudioPorts.length;
|
||||||
|
|
||||||
|
let hasInput = false;
|
||||||
|
let hasOutput = false;
|
||||||
|
for (let ch of this.mainInputChannels) {
|
||||||
|
if (ch >= 0) {
|
||||||
|
hasInput = true;
|
||||||
|
if (ch >= maxInputChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasInput) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let ch of this.mainOutputChannels) {
|
||||||
|
if (ch >= 0) {
|
||||||
|
hasOutput = true;
|
||||||
|
if (ch >= maxOutputChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasOutput) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let ch of this.auxInputChannels) {
|
||||||
|
if (ch >= maxInputChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let ch of this.auxOutputChannels) {
|
||||||
|
if (ch >= maxOutputChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let ch of this.sendInputChannels) {
|
||||||
|
if (ch >= maxInputChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,11 @@ import DialogEx from './DialogEx';
|
|||||||
|
|
||||||
import { PiPedalModelFactory } from './PiPedalModel';
|
import { PiPedalModelFactory } from './PiPedalModel';
|
||||||
|
|
||||||
|
|
||||||
|
let debugInputChannels: number | null = 2;
|
||||||
|
let debugOutputChannels: number | null = 4;
|
||||||
|
|
||||||
|
|
||||||
export interface ChannelRouterSettingsDialogProps {
|
export interface ChannelRouterSettingsDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -309,6 +314,12 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
|
|
||||||
let ChannelSelect = (routeType: RouteType, channelIndex: number, input: boolean, icon: boolean = true) => {
|
let ChannelSelect = (routeType: RouteType, channelIndex: number, input: boolean, icon: boolean = true) => {
|
||||||
let channelCount = input ? config.inputAudioPorts.length : config.outputAudioPorts.length;
|
let channelCount = input ? config.inputAudioPorts.length : config.outputAudioPorts.length;
|
||||||
|
if (input && debugInputChannels != null) {
|
||||||
|
channelCount = debugInputChannels;
|
||||||
|
}
|
||||||
|
if (!input && debugOutputChannels != null) {
|
||||||
|
channelCount = debugOutputChannels;
|
||||||
|
}
|
||||||
|
|
||||||
let value: number;
|
let value: number;
|
||||||
|
|
||||||
@@ -336,7 +347,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
}
|
}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
let newValue = event.target.value as number;
|
let newValue = event.target.value as number;
|
||||||
let newSettings = Object.assign(new ChannelRouterSettings(), settings);
|
let newSettings = new ChannelRouterSettings().deserialize(settings);
|
||||||
switch (routeType) {
|
switch (routeType) {
|
||||||
case RouteType.Main:
|
case RouteType.Main:
|
||||||
if (input) {
|
if (input) {
|
||||||
@@ -385,7 +396,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
}
|
}
|
||||||
for (let preset of channelRouterPresets) {
|
for (let preset of channelRouterPresets) {
|
||||||
if (preset.id === presetId) {
|
if (preset.id === presetId) {
|
||||||
model.setChannelRouterSettings(preset.settings);
|
let newPreset = Object.assign(new ChannelRouterSettings(), preset.settings);
|
||||||
|
// mark the presets as inserts.
|
||||||
|
newPreset.mainInserts.nextInstanceId = Pedalboard.MAIN_INSERT_INSTANCE_BASE;
|
||||||
|
newPreset.auxInserts.nextInstanceId = Pedalboard.AUX_INSERT_INSTANCE_BASE;
|
||||||
|
model.setChannelRouterSettings(newPreset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -759,7 +774,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
|||||||
{ChannelSelect(RouteType.Send, 0, true, false)}
|
{ChannelSelect(RouteType.Send, 0, true, false)}
|
||||||
</td>
|
</td>
|
||||||
<td style={cellLeft}>
|
<td style={cellLeft}>
|
||||||
{ChannelSelect(RouteType.Send, 1, false, false)}
|
{ChannelSelect(RouteType.Send, 1, true, false)}
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -381,10 +381,10 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
|||||||
|
|
||||||
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 MAIN_INSERT_START_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
static readonly MAIN_INSERT_START_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + -2 + MAX_INSTANCE_ID;
|
||||||
static readonly MAIN_INSERT_END_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
static readonly MAIN_INSERT_END_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + -3 + MAX_INSTANCE_ID;
|
||||||
static readonly AUX_INSERT_START_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
static readonly AUX_INSERT_START_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + -2 + MAX_INSTANCE_ID
|
||||||
static readonly AUX_INSERT_END_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
static readonly AUX_INSERT_END_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + -3 + MAX_INSTANCE_ID;
|
||||||
|
|
||||||
|
|
||||||
static readonly START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
|
static readonly START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
|
||||||
|
|||||||
@@ -431,9 +431,9 @@ interface MonitorPortOutputBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface ChannelSelectionChangedBody {
|
interface ChannelRouterSettingsChangedBody {
|
||||||
clientId: number;
|
clientId: number;
|
||||||
jackChannelSelection: JackChannelSelection;
|
channelRouterSettings: ChannelRouterSettings;
|
||||||
}
|
}
|
||||||
interface RenamePresetBody {
|
interface RenamePresetBody {
|
||||||
clientId: number;
|
clientId: number;
|
||||||
@@ -730,10 +730,11 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
} else if (message === "onShowStatusMonitorChanged") {
|
} else if (message === "onShowStatusMonitorChanged") {
|
||||||
let value = body as boolean;
|
let value = body as boolean;
|
||||||
this.showStatusMonitor.set(value);
|
this.showStatusMonitor.set(value);
|
||||||
} else if (message === "onChannelSelectionChanged") {
|
} else if (message === "onChannelRouterSettingsChanged") {
|
||||||
let channelSelectionBody = body as ChannelSelectionChangedBody;
|
let channelRouterSettingChangedBody = body as ChannelRouterSettingsChangedBody;
|
||||||
let channelSelection = new JackChannelSelection().deserialize(channelSelectionBody.jackChannelSelection);
|
let channelRouterSettings = new ChannelRouterSettings().deserialize(
|
||||||
this.jackSettings.set(channelSelection);
|
channelRouterSettingChangedBody.channelRouterSettings);
|
||||||
|
this.channelRouterSettings.set(channelRouterSettings);
|
||||||
} else if (message === "onSnapshotModified") {
|
} else if (message === "onSnapshotModified") {
|
||||||
let { snapshotIndex, modified } = (body as { snapshotIndex: number, modified: boolean });
|
let { snapshotIndex, modified } = (body as { snapshotIndex: number, modified: boolean });
|
||||||
let snapshots = this.pedalboard.get().snapshots;
|
let snapshots = this.pedalboard.get().snapshots;
|
||||||
|
|||||||
@@ -519,29 +519,10 @@ const SettingsDialog = withStyles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChannelsDialogResult(channels: string[] | null): void {
|
handleSelectChannelsDialogResult(channels: string[] | null): void {
|
||||||
if (this.state.jackSettings === null) { return; }
|
this.setState({
|
||||||
if (channels) {
|
showInputSelectDialog: false,
|
||||||
let newSelection = this.state.jackSettings.clone();
|
showOutputSelectDialog: false
|
||||||
if (this.state.showInputSelectDialog) {
|
});
|
||||||
newSelection.inputAudioPorts = channels;
|
|
||||||
} else {
|
|
||||||
newSelection.outputAudioPorts = channels;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
jackSettings: newSelection,
|
|
||||||
showInputSelectDialog: false,
|
|
||||||
showOutputSelectDialog: false
|
|
||||||
|
|
||||||
});
|
|
||||||
this.model.setJackSettings(newSelection);
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
showInputSelectDialog: false,
|
|
||||||
showOutputSelectDialog: false
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
midiSummary(): string {
|
midiSummary(): string {
|
||||||
@@ -618,8 +599,10 @@ const SettingsDialog = withStyles(
|
|||||||
let disableShutdown = this.state.shuttingDown || this.state.restarting;
|
let disableShutdown = this.state.shuttingDown || this.state.restarting;
|
||||||
|
|
||||||
let canKeepScreenOn = this.model.canKeepScreenOn;
|
let canKeepScreenOn = this.model.canKeepScreenOn;
|
||||||
let hasAudioConfig = isConfigValid && this.state.jackConfiguration.outputAudioPorts.length >= 1
|
let hasAudioConfig = isConfigValid
|
||||||
&& this.state.jackConfiguration.inputAudioPorts.length >= 1
|
&& this.state.jackConfiguration.inputAudioPorts.length >= 1
|
||||||
|
&& this.state.jackConfiguration.outputAudioPorts.length >= 1;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogEx tag="settings" fullScreen open={this.props.open}
|
<DialogEx tag="settings" fullScreen open={this.props.open}
|
||||||
@@ -753,40 +736,43 @@ const SettingsDialog = withStyles(
|
|||||||
<SelectHoverBackground selected={false} showHover={true} />
|
<SelectHoverBackground selected={false} showHover={true} />
|
||||||
<div style={{ width: "100%" }}>
|
<div style={{ width: "100%" }}>
|
||||||
<Typography display="block" variant="body2" noWrap>Channel Routing</Typography>
|
<Typography display="block" variant="body2" noWrap>Channel Routing</Typography>
|
||||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
<Typography display="block" variant="caption"
|
||||||
this.state.jackSettings == null ? "" :
|
color={
|
||||||
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
|
this.state.channelRouterSettings?.isValid(this.state.jackConfiguration) ?? false
|
||||||
|
? "textSecondary": "error"} noWrap>{
|
||||||
|
this.state.channelRouterSettings?.getDescription(this.state.jackConfiguration)??""}</Typography>
|
||||||
</div>
|
</div>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
|
|
||||||
|
{/* Old Input and Output selection
|
||||||
|
|
||||||
|
<ButtonBase className={classes.setting} onClick={() => this.handleInputSelection()}
|
||||||
|
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
|
||||||
|
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
|
||||||
|
|
||||||
<ButtonBase className={classes.setting} onClick={() => this.handleInputSelection()}
|
>
|
||||||
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
|
<SelectHoverBackground selected={false} showHover={true} />
|
||||||
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
|
<div style={{ width: "100%" }}>
|
||||||
|
<Typography display="block" variant="body2" noWrap>Input channels</Typography>
|
||||||
>
|
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
||||||
<SelectHoverBackground selected={false} showHover={true} />
|
this.state.jackSettings == null ? "" :
|
||||||
<div style={{ width: "100%" }}>
|
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||||
<Typography display="block" variant="body2" noWrap>Input channels</Typography>
|
</div>
|
||||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
</ButtonBase>
|
||||||
this.state.jackSettings == null ? "" :
|
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()}
|
||||||
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
|
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
|
||||||
</div>
|
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
|
||||||
</ButtonBase>
|
>
|
||||||
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()}
|
<SelectHoverBackground selected={false} showHover={true} />
|
||||||
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
|
<div style={{ width: "100%" }}>
|
||||||
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
|
<Typography display="block" variant="body2" noWrap>Output channels</Typography>
|
||||||
>
|
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
||||||
<SelectHoverBackground selected={false} showHover={true} />
|
this.state.jackSettings == null ? "" :
|
||||||
<div style={{ width: "100%" }}>
|
this.state.jackSettings.getAudioOutputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||||
<Typography display="block" variant="body2" noWrap>Output channels</Typography>
|
</div>
|
||||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
</ButtonBase>
|
||||||
this.state.jackSettings == null ? "" :
|
*/}
|
||||||
this.state.jackSettings.getAudioOutputDisplayValue(this.state.jackConfiguration)}</Typography>
|
|
||||||
</div>
|
|
||||||
</ButtonBase>
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<div >
|
<div >
|
||||||
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">MIDI</Typography>
|
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">MIDI</Typography>
|
||||||
|
|||||||
Reference in New Issue
Block a user