diff --git a/src/AudioHost.cpp b/src/AudioHost.cpp index 92d618a..84d23f7 100644 --- a/src/AudioHost.cpp +++ b/src/AudioHost.cpp @@ -516,6 +516,8 @@ private: SystemMidiBinding snapshot5MidiBinding; SystemMidiBinding snapshot6MidiBinding; + SystemMidiBinding nextSnapshotMidiBinding; + SystemMidiBinding prevSnapshotMidiBinding; SystemMidiBinding startHotspotMidiBinding; SystemMidiBinding stopHotspotMidiBinding; SystemMidiBinding rebootMidiBinding; @@ -1015,6 +1017,18 @@ private: midiProgramChangePending = true; this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1); } + else if (nextSnapshotMidiBinding.IsTriggered(event)) + { + this->deferredMidiMessageCount = 0; // we can discard previous control changes. + midiProgramChangePending = true; + this->realtimeWriter.OnNextMidiSnapshot(++(this->midiProgramChangeId), 1); + } + else if (prevSnapshotMidiBinding.IsTriggered(event)) + { + this->deferredMidiMessageCount = 0; // we can discard previous control changes. + midiProgramChangePending = true; + this->realtimeWriter.OnNextMidiSnapshot(++(this->midiProgramChangeId), -1); + } else if (shutdownMidiBinding.IsTriggered(event)) { @@ -1737,6 +1751,12 @@ public: hostReader.read(&request); pNotifyCallbacks->OnNotifyNextMidiBank(request); } + else if (command == RingBufferCommand::NextMidiSnapshot) + { + RealtimeNextMidiProgramRequest request; + hostReader.read(&request); + pNotifyCallbacks->OnNotifyNextMidiSnapshot(request); + } else if (command == RingBufferCommand::RealtimeMidiEvent) { @@ -2423,6 +2443,14 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector &bindin { this->snapshot6MidiBinding.SetBinding(*i); } + else if (i->symbol() == "nextSnapshot") + { + this->nextSnapshotMidiBinding.SetBinding(*i); + } + else if (i->symbol() == "prevSnapshot") + { + this->prevSnapshotMidiBinding.SetBinding(*i); + } else { Lv2Log::error(SS("Invalid system midi binding: " << i->symbol())); diff --git a/src/AudioHost.hpp b/src/AudioHost.hpp index bc39c0f..a37dc13 100644 --- a/src/AudioHost.hpp +++ b/src/AudioHost.hpp @@ -177,6 +177,7 @@ namespace pipedal virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) = 0; virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) = 0; virtual void OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request) = 0; + virtual void OnNotifyNextMidiSnapshot(const RealtimeNextMidiProgramRequest &request) = 0; virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) = 0; virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) = 0; virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0; diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 9d02874..e758af0 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -1181,6 +1181,92 @@ void PiPedalModel::NextPreset(Direction direction) LoadPreset(-1, index.presets()[currentPresetIndex].instanceId()); } +void PiPedalModel::NextSnapshot(Direction direction) +{ + std::lock_guard guard{mutex}; + + auto &snapshots = this->pedalboard.snapshots(); + if (snapshots.size() == 0) + { + return; + } + int64_t currentSnapshot = this->pedalboard.selectedSnapshot(); + int64_t nextSnapshot = -1; + + if (direction == Direction::Increase) + { + for (int64_t i = currentSnapshot + 1; i < (int64_t)snapshots.size(); ++i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + if (nextSnapshot == -1) + { + for (int64_t i = 0; i <= currentSnapshot && i < (int64_t)snapshots.size(); ++i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + } + } + else + { + for (int64_t i = currentSnapshot - 1; i >= 0; --i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + if (nextSnapshot == -1) + { + for (int64_t i = (int64_t)snapshots.size() - 1; i > currentSnapshot; --i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + } + } + if (nextSnapshot != -1 && nextSnapshot != currentSnapshot) + { + SetSnapshot(nextSnapshot); + } +} + +void PiPedalModel::OnNotifyNextMidiSnapshot(const RealtimeNextMidiProgramRequest &request) +{ + std::lock_guard guard{mutex}; + try + { + if (request.direction >= 0) + { + NextSnapshot(); + } + else + { + PreviousSnapshot(); + } + } + catch (std::exception &e) + { + Lv2Log::error(e.what()); + } + if (this->audioHost) + { + this->audioHost->AckMidiProgramRequest(request.requestId); + } +} + void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) { std::lock_guard guard{mutex}; diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 2a53848..c8f9d7a 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -273,6 +273,7 @@ namespace pipedal virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) override; virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) override; virtual void OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request) override; + virtual void OnNotifyNextMidiSnapshot(const RealtimeNextMidiProgramRequest &request) override; virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) override; PostHandle networkChangingDelayHandle = 0; @@ -311,6 +312,8 @@ namespace pipedal void PreviousBank() { NextBank(Direction::Decrease); } void NextPreset(Direction direction = Direction::Increase); void PreviousPreset() { NextPreset(Direction::Decrease); } + void NextSnapshot(Direction direction = Direction::Increase); + void PreviousSnapshot() { NextSnapshot(Direction::Decrease); } int64_t DownloadModelsFromTone3000( const std::string &responseuri, diff --git a/src/RingBufferReader.hpp b/src/RingBufferReader.hpp index 9855958..dbbabf8 100644 --- a/src/RingBufferReader.hpp +++ b/src/RingBufferReader.hpp @@ -83,6 +83,7 @@ namespace pipedal NextMidiProgram, NextMidiBank, + NextMidiSnapshot, Lv2StateChanged, MaybeLv2StateChanged, @@ -444,6 +445,11 @@ namespace pipedal RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction}; write(RingBufferCommand::NextMidiBank, msg); } + void OnNextMidiSnapshot(int64_t requestId, int32_t direction) + { + RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction}; + write(RingBufferCommand::NextMidiSnapshot, msg); + } void SetControlValue(int effectIndex, int controlIndex, float value) { diff --git a/src/Storage.cpp b/src/Storage.cpp index e924191..3606fa7 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -2180,6 +2180,9 @@ std::vector Storage::GetSystemMidiBindings() result.push_back(MidiBinding::SystemBinding("snapshot5")); result.push_back(MidiBinding::SystemBinding("snapshot6")); + result.push_back(MidiBinding::SystemBinding("prevSnapshot")); + result.push_back(MidiBinding::SystemBinding("nextSnapshot")); + result.push_back(MidiBinding::SystemBinding("stopHotspot")); result.push_back(MidiBinding::SystemBinding("startHotspot")); result.push_back(MidiBinding::SystemBinding("shutdown")); diff --git a/vite/src/pipedal/PiPedalModel.tsx b/vite/src/pipedal/PiPedalModel.tsx index f5ba352..d356d89 100644 --- a/vite/src/pipedal/PiPedalModel.tsx +++ b/vite/src/pipedal/PiPedalModel.tsx @@ -602,7 +602,9 @@ export class PiPedalModel //implements PiPedalModel ( [ MidiBinding.systemBinding("prevProgram"), - MidiBinding.systemBinding("nextProgram") + MidiBinding.systemBinding("nextProgram"), + MidiBinding.systemBinding("prevSnapshot"), + MidiBinding.systemBinding("nextSnapshot") ] ); zoomedUiControl: ObservableProperty = new ObservableProperty(undefined); diff --git a/vite/src/pipedal/SystemMidiBindingsDialog.tsx b/vite/src/pipedal/SystemMidiBindingsDialog.tsx index d9aef17..b272884 100644 --- a/vite/src/pipedal/SystemMidiBindingsDialog.tsx +++ b/vite/src/pipedal/SystemMidiBindingsDialog.tsx @@ -159,6 +159,11 @@ export const SystemMidiBindingDialog = } else if (item.symbol === "snapshot6") { displayName = "Snapshot 6"; + } + else if (item.symbol === "nextSnapshot") { + displayName = "Next Snapshot"; + } else if (item.symbol === "prevSnapshot") { + displayName = "Previous Snapshot"; } else if (item.symbol === "startHotspot") { displayName = "Enable Hotspot"; } else if (item.symbol === "stopHotspot") {