Protection against invalid Channel Router settings.

This commit is contained in:
Robin E.R. Davies
2026-05-25 21:43:19 -04:00
parent 2004069e07
commit 726b0d268e
5 changed files with 121 additions and 69 deletions
+2
View File
@@ -39,6 +39,8 @@ PiPedal 2.0 can now be installed as a Progressive Web Application (PWA). This al
- Split controls now support double-tap to reset to a default value. (Other controls already support this feature). - Split controls now support double-tap to reset to a default value. (Other controls already support this feature).
- Runtime detection and use of AVX extensions for TooB plugins. Reduces CPU use of TooB NAM by 50% on most x86_64/amd64 computers.
## PiPedal 1.5.99 Beta ## PiPedal 1.5.99 Beta
### Bug Fixes ### Bug Fixes
+58 -41
View File
@@ -1189,7 +1189,7 @@ private:
std::vector<float *> *pOutputBuffers; std::vector<float *> *pOutputBuffers;
pedalboard = this->realtimeActivePedalboard; pedalboard = this->realtimeActivePedalboard;
pInputBuffers = &(audioDriver->MainInputBuffers()); pInputBuffers = &(audioDriver->MainInputBuffers());
pOutputBuffers = &(audioDriver->MainOutputBuffers()); pOutputBuffers = &(audioDriver->MainOutputBuffers());
if (pedalboard == nullptr || pInputBuffers->size() == 0 || pOutputBuffers->size() == 0) if (pedalboard == nullptr || pInputBuffers->size() == 0 || pOutputBuffers->size() == 0)
@@ -1255,60 +1255,79 @@ private:
masterOutputBuffers[outputIx++] = audioDriver->GetDeviceOutputBuffer(nChannel); masterOutputBuffers[outputIx++] = audioDriver->GetDeviceOutputBuffer(nChannel);
} }
} }
inline void AccumulateVu(float*result, size_t nFrames, float*buffer) inline void AccumulateVu(float *result, size_t nFrames, float *buffer)
{ {
float maxVal = *result; float maxVal = *result;
for (size_t i = 0; i < nFrames; ++i) for (size_t i = 0; i < nFrames; ++i)
{ {
float v = std::fabs(buffer[i]); float v = std::fabs(buffer[i]);
if (v > maxVal) if (v > maxVal)
{ {
maxVal = v; maxVal = v;
} }
} }
*result = maxVal; *result = maxVal;
} }
void AccumulateVuInputs(size_t nFrames,VuUpdateX&vuUpdate, const std::vector<int64_t>&channels) void AccumulateVuInputs(size_t nFrames, VuUpdateX &vuUpdate, const std::vector<int64_t> &channels)
{ {
if (channels.size() == 0) return; if (channels.size() == 0)
return;
AccumulateVu(&vuUpdate.inputMaxValueL_,nFrames,this->audioDriver->DeviceInputBuffers()[channels[0]]); if (channels[0] == -1) // Indicates invalid channel router settings.
if (channels.size() == 2) {
AccumulateVu(&vuUpdate.inputMaxValueR_,nFrames,this->audioDriver->DeviceInputBuffers()[channels[1]]);
}
}
void AccumulateVuOutputs(size_t nFrames,VuUpdateX&vuUpdate, const std::vector<int64_t>&channels)
{
if (channels.size() == 0) return;
AccumulateVu(&vuUpdate.outputMaxValueL_,nFrames,this->audioDriver->DeviceOutputBuffers()[channels[0]]);
if (channels.size() >= 2) {
AccumulateVu(&vuUpdate.outputMaxValueR_,nFrames,this->audioDriver->DeviceOutputBuffers()[channels[1]]);
}
}
void ComputeMasterVus(size_t nFrames) {
if (this->realtimeVuBuffers)
{ {
float** audioBuffers; vuUpdate.inputMaxValueL_ = 0;
vuUpdate.inputMaxValueR_ = 0;
return;
}
for (auto& vuUpdate: this->realtimeVuBuffers->vuUpdateWorkingData) AccumulateVu(&vuUpdate.inputMaxValueL_, nFrames, this->audioDriver->DeviceInputBuffers()[channels[0]]);
if (channels.size() == 2)
{
AccumulateVu(&vuUpdate.inputMaxValueR_, nFrames, this->audioDriver->DeviceInputBuffers()[channels[1]]);
}
}
void AccumulateVuOutputs(size_t nFrames, VuUpdateX &vuUpdate, const std::vector<int64_t> &channels)
{
if (channels.size() == 0)
return;
if (channels[0] == -1) // Indicates invalid channel router settings.
{
vuUpdate.outputMaxValueL_ = 0;
vuUpdate.outputMaxValueR_ = 0;
return;
}
AccumulateVu(&vuUpdate.outputMaxValueL_, nFrames, this->audioDriver->DeviceOutputBuffers()[channels[0]]);
if (channels.size() >= 2)
{
AccumulateVu(&vuUpdate.outputMaxValueR_, nFrames, this->audioDriver->DeviceOutputBuffers()[channels[1]]);
}
}
void ComputeMasterVus(size_t nFrames)
{
if (this->realtimeVuBuffers)
{
float **audioBuffers;
for (auto &vuUpdate : this->realtimeVuBuffers->vuUpdateWorkingData)
{ {
if (vuUpdate.instanceId_ < 0) if (vuUpdate.instanceId_ < 0)
{ {
switch (vuUpdate.instanceId_) switch (vuUpdate.instanceId_)
{ {
case Pedalboard::START_CONTROL_ID: case Pedalboard::START_CONTROL_ID:
AccumulateVuInputs(nFrames, vuUpdate, pHost->GetChannelSelection().mainInputChannels()); AccumulateVuInputs(nFrames, vuUpdate, pHost->GetChannelSelection().mainInputChannels());
break; break;
case Pedalboard::END_CONTROL_ID: case Pedalboard::END_CONTROL_ID:
AccumulateVuOutputs(nFrames, vuUpdate, pHost->GetChannelSelection().mainOutputChannels()); AccumulateVuOutputs(nFrames, vuUpdate, pHost->GetChannelSelection().mainOutputChannels());
break; break;
case Pedalboard::AUX_START_CONTROL_ID: case Pedalboard::AUX_START_CONTROL_ID:
AccumulateVuInputs(nFrames, vuUpdate, pHost->GetChannelSelection().auxInputChannels()); AccumulateVuInputs(nFrames, vuUpdate, pHost->GetChannelSelection().auxInputChannels());
break; break;
case Pedalboard::AUX_END_CONTROL_ID: case Pedalboard::AUX_END_CONTROL_ID:
AccumulateVuOutputs(nFrames, vuUpdate, pHost->GetChannelSelection().auxOutputChannels()); AccumulateVuOutputs(nFrames, vuUpdate, pHost->GetChannelSelection().auxOutputChannels());
break; break;
} }
} }
} }
@@ -1905,7 +1924,6 @@ public:
} }
} }
virtual void SetBypass(uint64_t instanceId, bool enabled) virtual void SetBypass(uint64_t instanceId, bool enabled)
{ {
std::lock_guard guard(mutex); std::lock_guard guard(mutex);
@@ -2067,7 +2085,7 @@ public:
for (size_t i = 0; i < instanceIds.size(); ++i) for (size_t i = 0; i < instanceIds.size(); ++i)
{ {
int64_t instanceId = instanceIds[i]; int64_t instanceId = instanceIds[i];
std::shared_ptr<Lv2Pedalboard>& pedalboard = this->currentPedalboard; std::shared_ptr<Lv2Pedalboard> &pedalboard = this->currentPedalboard;
int64_t effectIndex = -1; int64_t effectIndex = -1;
if (pedalboard != nullptr) if (pedalboard != nullptr)
{ {
@@ -2091,8 +2109,7 @@ public:
instanceId == Pedalboard::START_CONTROL_ID || instanceId == Pedalboard::START_CONTROL_ID ||
instanceId == Pedalboard::END_CONTROL_ID || instanceId == Pedalboard::END_CONTROL_ID ||
instanceId == Pedalboard::AUX_START_CONTROL_ID || instanceId == Pedalboard::AUX_START_CONTROL_ID ||
instanceId == Pedalboard::AUX_END_CONTROL_ID instanceId == Pedalboard::AUX_END_CONTROL_ID)
)
{ {
auto index = GetRealtimeItemIndex(instanceId); auto index = GetRealtimeItemIndex(instanceId);
VuUpdateX v; VuUpdateX v;
+58 -26
View File
@@ -17,7 +17,6 @@
// 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 "ChannelRouterSettings.hpp" #include "ChannelRouterSettings.hpp"
#include <stdexcept> #include <stdexcept>
@@ -52,7 +51,7 @@ static uint64_t countChannels(const std::vector<int64_t> &channels)
} }
return 2; return 2;
} }
} }
uint64_t ChannelRouterSettings::numberOfAudioInputChannels() const uint64_t ChannelRouterSettings::numberOfAudioInputChannels() const
{ {
@@ -71,75 +70,110 @@ uint64_t ChannelRouterSettings::numberOfAudioOutputChannels() const
return countChannels(mainOutputChannels_); return countChannels(mainOutputChannels_);
} }
ChannelSelection::ChannelSelection(ChannelRouterSettings&settings) ChannelSelection::ChannelSelection(ChannelRouterSettings &settings)
: mainInputChannels_(settings.mainInputChannels()) : mainInputChannels_(settings.mainInputChannels()), mainOutputChannels_(settings.mainOutputChannels()), auxInputChannels_(settings.auxInputChannels()), auxOutputChannels_(settings.auxOutputChannels())
, mainOutputChannels_(settings.mainOutputChannels())
, auxInputChannels_(settings.auxInputChannels())
, auxOutputChannels_(settings.auxOutputChannels())
{ {
normalizeChannelSelection(); normalizeChannelSelection();
} }
static void normalizeInputChannels(std::vector<int64_t>&channels) { static void normalizeInputChannels(std::vector<int64_t> &channels)
if (channels.size() == 2) { {
if (channels[0] == -1 && channels[1] == -1) if (channels.size() == 2)
{
if (channels[0] == -1 && channels[1] == -1)
{ {
channels.resize(0); channels.resize(0);
} else if (channels[0] == channels[1]) { }
else if (channels[0] == channels[1])
{
channels.resize(1); channels.resize(1);
} else if (channels[1] == -1) { }
channels.resize(1);} { else if (channels[1] == -1)
{
channels.resize(1);
}
{
} }
} }
} }
static void normalizeOutputChannels(std::vector<int64_t>&channels) { static void normalizeOutputChannels(std::vector<int64_t> &channels)
{
normalizeInputChannels(channels); normalizeInputChannels(channels);
} }
void ChannelSelection::normalizeChannelSelection() { void ChannelSelection::normalizeChannelSelection()
{
normalizeInputChannels(mainInputChannels_); normalizeInputChannels(mainInputChannels_);
normalizeOutputChannels(mainOutputChannels_); normalizeOutputChannels(mainOutputChannels_);
normalizeInputChannels(auxInputChannels_); normalizeInputChannels(auxInputChannels_);
normalizeOutputChannels(auxOutputChannels_); normalizeOutputChannels(auxOutputChannels_);
// If either aux inputs or outputs are zero, don't do ANY aux processing. // If either aux inputs or outputs are zero, don't do ANY aux processing.
if (auxInputChannels_.size() == 0) if (auxInputChannels_.size() == 0)
{ {
auxOutputChannels_.resize(0); auxOutputChannels_.resize(0);
} else if (auxOutputChannels_.size() == 0) { }
else if (auxOutputChannels_.size() == 0)
{
auxInputChannels_.resize(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 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) if (mainInputChannels_.size() == 0)
{ {
mainInputChannels_.resize(1); mainInputChannels_.resize(1);
mainInputChannels_[0] = -1; mainInputChannels_[0] = -1;
} }
if (mainOutputChannels_.size() == 0) { if (mainOutputChannels_.size() == 0)
{
mainInputChannels_.resize(1); mainInputChannels_.resize(1);
mainOutputChannels_[0] = -1; mainOutputChannels_[0] = -1;
} }
// Send buffers are what they are. Let the send plugin deal with it. // Send buffers are what they are. Let the send plugin deal with it.
} }
ChannelRouterSettings::ChannelRouterSettings() ChannelRouterSettings::ChannelRouterSettings()
{ {
} }
std::vector<ChannelRouterPresetIndexEntry> ChannelRouterPresetBank::getIndexEntries() std::vector<ChannelRouterPresetIndexEntry> ChannelRouterPresetBank::getIndexEntries()
{ {
std::vector<ChannelRouterPresetIndexEntry> result; std::vector<ChannelRouterPresetIndexEntry> result;
for (auto &entry: entries_) for (auto &entry : entries_)
{ {
result.push_back(ChannelRouterPresetIndexEntry{entry->id(), entry->name()}); result.push_back(ChannelRouterPresetIndexEntry{entry->id(), entry->name()});
} }
return result; return result;
} }
static bool HasIllegalChannel(const std::vector<int64_t> &channels)
{
for (auto c : channels)
{
if (c < 0)
{
return true;
}
}
return false;
}
bool ChannelSelection::IsValid() const
{
if (mainInputChannels_.size() == 0 || mainOutputChannels_.size() == 0)
{
return false;
}
if (HasIllegalChannel(mainInputChannels_) ||
HasIllegalChannel(mainOutputChannels_) ||
HasIllegalChannel(auxInputChannels_) ||
HasIllegalChannel(auxOutputChannels_))
{
return false;
}
return true;
}
JSON_MAP_BEGIN(ChannelRouterSettings) JSON_MAP_BEGIN(ChannelRouterSettings)
JSON_MAP_REFERENCE(ChannelRouterSettings, configured) JSON_MAP_REFERENCE(ChannelRouterSettings, configured)
JSON_MAP_REFERENCE(ChannelRouterSettings, changed) JSON_MAP_REFERENCE(ChannelRouterSettings, changed)
@@ -155,7 +189,6 @@ JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, id)
JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, name) JSON_MAP_REFERENCE(ChannelRouterPresetIndexEntry, name)
JSON_MAP_END(); JSON_MAP_END();
JSON_MAP_BEGIN(ChannelRouterPresetBankEntry) JSON_MAP_BEGIN(ChannelRouterPresetBankEntry)
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, id) JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, id)
JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, name) JSON_MAP_REFERENCE(ChannelRouterPresetBankEntry, name)
@@ -167,4 +200,3 @@ JSON_MAP_REFERENCE(ChannelRouterPresetBank, nextId)
JSON_MAP_REFERENCE(ChannelRouterPresetBank, version) JSON_MAP_REFERENCE(ChannelRouterPresetBank, version)
JSON_MAP_REFERENCE(ChannelRouterPresetBank, entries) JSON_MAP_REFERENCE(ChannelRouterPresetBank, entries)
JSON_MAP_END(); JSON_MAP_END();
+1
View File
@@ -84,6 +84,7 @@ namespace pipedal
std::vector<int64_t> &auxInputChannels() { return auxInputChannels_; } std::vector<int64_t> &auxInputChannels() { return auxInputChannels_; }
std::vector<int64_t> &auxOutputChannels() { return auxOutputChannels_; } std::vector<int64_t> &auxOutputChannels() { return auxOutputChannels_; }
bool IsValid() const;
private: private:
void normalizeChannelSelection(); void normalizeChannelSelection();
+2 -2
View File
@@ -1576,9 +1576,9 @@ void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
throw std::runtime_error("Audio configuration not valid."); throw std::runtime_error("Audio configuration not valid.");
} }
const auto &channelSelection = this->storage.GetChannelSelection(); auto channelSelection = this->storage.GetChannelSelection();
this->audioHost->Open(jackServerSettings, channelSelection); this->audioHost->Open(jackServerSettings, channelSelection);
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection); this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);