diff --git a/src/deluge/gui/menu_item/dx/browse.cpp b/src/deluge/gui/menu_item/dx/browse.cpp
deleted file mode 100644
index 7a5614bec8..0000000000
--- a/src/deluge/gui/menu_item/dx/browse.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright © 2015-2023 Synthstrom Audible Limited
- *
- * This file is part of The Synthstrom Audible Deluge Firmware.
- *
- * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with this program.
- * If not, see .
- */
-
-#include "browse.h"
-#include "gui/ui/browser/dx_browser.h"
-#include "gui/ui/sound_editor.h"
-#include "gui/ui_timer_manager.h"
-
-namespace deluge::gui::menu_item {
-
-DxBrowseMenu dxBrowseMenu{l10n::String::STRING_FOR_DX_BROWSER};
-
-void DxBrowseMenu::beginSession(MenuItem* navigatedBackwardFrom) {
- soundEditor.shouldGoUpOneLevelOnBegin = true;
- // if (getRootUI() == &keyboardScreen && currentUIMode == UI_MODE_AUDITIONING) {
- // keyboardScreen.exitAuditionMode();
- // }
- bool success = openUI(&dxBrowser);
- if (!success) {
- // if (getCurrentUI() == &soundEditor) soundEditor.goUpOneLevel();
- uiTimerManager.unsetTimer(TimerName::SHORTCUT_BLINK);
- }
-}
-
-} // namespace deluge::gui::menu_item
diff --git a/src/deluge/gui/menu_item/dx/browse.h b/src/deluge/gui/menu_item/dx/browse.h
deleted file mode 100644
index d9d28a32ad..0000000000
--- a/src/deluge/gui/menu_item/dx/browse.h
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * Copyright © 2015-2023 Synthstrom Audible Limited
- *
- * This file is part of The Synthstrom Audible Deluge Firmware.
- *
- * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with this program.
- * If not, see .
- */
-
-#pragma once
-
-#include "gui/menu_item/menu_item.h"
-
-namespace deluge::gui::menu_item {
-
-class DxBrowseMenu final : public MenuItem {
-public:
- using MenuItem::MenuItem;
- DxBrowseMenu(l10n::String newName) : MenuItem(newName) {}
- void beginSession(MenuItem* navigatedBackwardFrom) override;
-};
-
-extern DxBrowseMenu dxBrowseMenu;
-} // namespace deluge::gui::menu_item
diff --git a/src/deluge/gui/menu_item/dx/cartridge.cpp b/src/deluge/gui/menu_item/dx/cartridge.cpp
deleted file mode 100644
index 750e2bccc8..0000000000
--- a/src/deluge/gui/menu_item/dx/cartridge.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright © 2015-2023 Synthstrom Audible Limited
- *
- * This file is part of The Synthstrom Audible Deluge Firmware.
- *
- * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with this program.
- * If not, see .
- */
-
-#include "cartridge.h"
-#include "dsp/dx/engine.h"
-#include "gui/ui/browser/dx_browser.h"
-#include "gui/ui/sound_editor.h"
-#include "gui/ui_timer_manager.h"
-#include "hid/display/display.h"
-#include "memory/general_memory_allocator.h"
-#include "model/song/song.h"
-#include "processing/sound/sound.h"
-#include "processing/source.h"
-#include "storage/DX7Cartridge.h"
-#include "util/container/static_vector.hpp"
-
-static bool openFile(const char* path, DX7Cartridge* data) {
- using deluge::l10n::String;
- bool didLoad = false;
- const int minSize = SMALL_SYSEX_SIZE;
-
- FILINFO fno;
- int result = f_stat(path, &fno);
- FSIZE_t fileSize = fno.fsize;
- if (fileSize < minSize) {
- display->displayPopup(get(String::STRING_FOR_DX_ERROR_FILE_TOO_SMALL));
- }
-
- FIL file;
- // Open the file
- result = f_open(&file, path, FA_READ);
- if (result != FR_OK) {
- display->displayPopup(get(String::STRING_FOR_DX_ERROR_READ_ERROR));
- return false;
- }
-
- String error = String::EMPTY_STRING;
- UINT numBytesRead;
- int readsize = std::min((int)fileSize, 8192);
- auto buffer = (uint8_t*)GeneralMemoryAllocator::get().allocLowSpeed(readsize);
- if (!buffer) {
- display->displayPopup(get(String::STRING_FOR_DX_ERROR_READ_ERROR));
- goto close;
- }
- result = f_read(&file, buffer, fileSize, &numBytesRead);
- if (numBytesRead < minSize) {
- display->displayPopup(get(String::STRING_FOR_DX_ERROR_FILE_TOO_SMALL));
- goto free;
- }
-
- error = data->load(buffer, numBytesRead);
- if (error != String::EMPTY_STRING) {
- display->displayPopup(get(error), 3);
- }
- else {
- didLoad = true;
- }
-
-free:
- GeneralMemoryAllocator::get().dealloc(buffer);
-close:
- f_close(&file);
- return didLoad;
-}
-namespace deluge::gui::menu_item {
-
-DxCartridge dxCartridge{l10n::String::STRING_FOR_DX_CARTRIDGE};
-
-void DxCartridge::beginSession(MenuItem* navigatedBackwardFrom) {
- readValueAgain();
-}
-
-void DxCartridge::readValueAgain() {
- if (pd == nullptr) {
- return;
- }
- if (display->haveOLED()) {
- renderUIsForOled();
- }
- else {
- drawValue();
- }
-
- DxPatch* patch = soundEditor.currentSource->ensureDxPatch();
- pd->unpackProgram(patch->params, currentValue);
- soundEditor.currentSound->unassignAllVoices();
- Instrument* instrument = getCurrentInstrument();
- if (instrument->type == OutputType::SYNTH && !instrument->existsOnCard) {
- char name[11];
- pd->getProgramName(currentValue, name);
- if (name[0] != 0) {
- instrument->name.set(name);
- }
- }
-}
-
-void DxCartridge::drawPixelsForOled() {
- if (pd == nullptr) {
- return;
- }
- char names[32][11];
- pd->getProgramNames(names);
-
- static_vector itemNames = {};
- for (int i = 0; i < pd->numPatches(); i++) {
- itemNames.push_back(names[i]);
- }
- drawItemsForOled(itemNames, currentValue - scrollPos, scrollPos);
-}
-
-void DxCartridge::drawValue() {
- char names[32][11];
- pd->getProgramNames(names);
-
- display->setScrollingText(names[currentValue]);
-}
-
-bool DxCartridge::tryLoad(const char* path) {
- if (pd == nullptr) {
- pd = new DX7Cartridge();
- }
- currentValue = 0;
- scrollPos = 0;
-
- return openFile(path, pd);
-}
-
-void DxCartridge::selectEncoderAction(int32_t offset) {
- int32_t newValue = currentValue + offset;
- int32_t numValues = pd->numPatches();
-
- if (display->haveOLED()) {
- if (newValue >= numValues || newValue < 0) {
- return;
- }
- }
- else {
- if (newValue >= numValues) {
- newValue %= numValues;
- }
- else if (newValue < 0) {
- newValue = (newValue % numValues + numValues) % numValues;
- }
- }
-
- currentValue = newValue;
-
- if (display->haveOLED()) {
- if (currentValue < scrollPos) {
- scrollPos = currentValue;
- }
- else if (currentValue >= scrollPos + kOLEDMenuNumOptionsVisible) {
- scrollPos++;
- }
- }
-
- readValueAgain();
-}
-
-MenuItem* DxCartridge::selectButtonPress() {
- soundEditor.exitCompletely();
- return nullptr;
-}
-
-} // namespace deluge::gui::menu_item
diff --git a/src/deluge/gui/ui/browser/dx_browser.cpp b/src/deluge/gui/ui/browser/dx_browser.cpp
deleted file mode 100644
index e135b281d9..0000000000
--- a/src/deluge/gui/ui/browser/dx_browser.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-
-/*
- * Copyright © 2015-2023 Synthstrom Audible Limited
- *
- * This file is part of The Synthstrom Audible Deluge Firmware.
- *
- * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with this program.
- * If not, see .
- */
-
-#include "dx_browser.h"
-#include "definitions_cxx.hpp"
-#include "gui/menu_item/dx/cartridge.h"
-#include "gui/ui/sound_editor.h"
-#include "hid/display/oled.h"
-
-using namespace deluge::gui;
-
-DxSyxBrowser::DxSyxBrowser() {
- fileIcon = deluge::hid::display::OLED::waveIcon;
- title = "DX7 syx files";
- shouldWrapFolderContents = false;
-}
-
-static char const* allowedFileExtensionsSyx[] = {"SYX", NULL};
-bool DxSyxBrowser::opened() {
-
- bool success = Browser::opened();
- if (!success)
- return false;
-
- allowedFileExtensions = allowedFileExtensionsSyx;
-
- allowFoldersSharingNameWithFile = true;
- outputTypeToLoad = OutputType::NONE;
- qwertyVisible = false;
-
- fileIndexSelected = 0;
-
- Error error = StorageManager::initSD();
- if (error != Error::NONE)
- goto sdError;
-
- currentDir.set("DX7");
-
- // TODO: fill in last used name!
- error = arrivedInNewFolder(1, "", "DX7");
- if (error != Error::NONE)
- goto sdError;
-
- return true;
-sdError:
- display->displayError(error);
- return false;
-}
-
-// TODO: this is identical to SampleBrowser, move to parent class?
-Error DxSyxBrowser::getCurrentFilePath(String* path) {
- Error error;
-
- path->set(¤tDir);
- int oldLength = path->getLength();
- if (oldLength) {
- error = path->concatenateAtPos("/", oldLength);
- if (error != Error::NONE) {
-gotError:
- path->clear();
- return error;
- }
- }
-
- FileItem* currentFileItem = getCurrentFileItem();
-
- error = path->concatenate(¤tFileItem->filename);
- if (error != Error::NONE)
- goto gotError;
-
- return Error::NONE;
-}
-
-void DxSyxBrowser::enterKeyPress() {
- FileItem* currentFileItem = getCurrentFileItem();
- if (!currentFileItem) {
- return;
- }
-
- if (currentFileItem->isFolder) {
- // [SIC]
- char const* filenameChars =
- currentFileItem->filename
- .get(); // Extremely weirdly, if we try to just put this inside the parentheses in the next line,
- // it returns an empty string (¬hing). Surely this is a compiler error??
-
- Error error = goIntoFolder(filenameChars);
- if (error != Error::NONE) {
- display->displayError(error);
- close(); // Don't use goBackToSoundEditor() because that would do a left-scroll
- return;
- }
- }
- else {
- // TODO: c.f. slotbrowser, we might just be able to pass a file pointer to the FAT loader
- String path;
- getCurrentFilePath(&path);
- close();
-
- if (!path.isEmpty()) {
- if (menu_item::dxCartridge.tryLoad(path.get())) {
- soundEditor.enterSubmenu(&menu_item::dxCartridge);
- }
- }
-
- // dx7ui.openFile(path.get());
- }
-}
-
-DxSyxBrowser dxBrowser{};
diff --git a/src/deluge/gui/ui/browser/dx_browser.h b/src/deluge/gui/ui/browser/dx_browser.h
deleted file mode 100644
index fedab9be5d..0000000000
--- a/src/deluge/gui/ui/browser/dx_browser.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright © 2015-2023 Synthstrom Audible Limited
- *
- * This file is part of The Synthstrom Audible Deluge Firmware.
- *
- * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with this program.
- * If not, see .
- */
-
-#pragma once
-
-#include "definitions_cxx.hpp"
-#include "gui/ui/browser/browser.h"
-#include "hid/button.h"
-
-class DxSyxBrowser final : public Browser {
-public:
- DxSyxBrowser();
- bool opened();
- void enterKeyPress();
- Error getCurrentFilePath(String* path) override;
- const char* getName() { return "dx_browser"; }
-};
-
-extern DxSyxBrowser dxBrowser;
diff --git a/src/deluge/gui/ui/load/load_dx_cartridge.cpp b/src/deluge/gui/ui/load/load_dx_cartridge.cpp
new file mode 100644
index 0000000000..e41dc672e0
--- /dev/null
+++ b/src/deluge/gui/ui/load/load_dx_cartridge.cpp
@@ -0,0 +1,253 @@
+
+#include "load_dx_cartridge.h"
+#include "dsp/dx/dx7note.h"
+#include "ff.h"
+#include "gui/l10n/l10n.h"
+#include "gui/ui/keyboard/keyboard_screen.h"
+#include "gui/ui/load/load_instrument_preset_ui.h"
+#include "gui/views/automation_view.h"
+#include "gui/views/instrument_clip_view.h"
+#include "hid/display/display.h"
+#include "hid/led/indicator_leds.h"
+#include "hid/led/pad_leds.h"
+#include "memory/general_memory_allocator.h"
+#include "model/song/song.h"
+#include "processing/sound/sound.h"
+#include "processing/sound/sound_instrument.h"
+#include "util/container/static_vector.hpp"
+
+using namespace deluge;
+
+static bool openFile(const char* path, DX7Cartridge* data) {
+ using deluge::l10n::String;
+ bool didLoad = false;
+ const int minSize = SMALL_SYSEX_SIZE;
+
+ FILINFO fno;
+ int result = f_stat(path, &fno);
+ FSIZE_t fileSize = fno.fsize;
+ if (fileSize < minSize) {
+ display->displayPopup(get(String::STRING_FOR_DX_ERROR_FILE_TOO_SMALL));
+ }
+
+ FIL file;
+ // Open the file
+ result = f_open(&file, path, FA_READ);
+ if (result != FR_OK) {
+ display->displayPopup(get(String::STRING_FOR_DX_ERROR_READ_ERROR));
+ return false;
+ }
+
+ String error = String::EMPTY_STRING;
+ UINT numBytesRead;
+ int readsize = std::min((int)fileSize, 8192);
+ auto buffer = (uint8_t*)GeneralMemoryAllocator::get().allocLowSpeed(readsize);
+ if (!buffer) {
+ display->displayPopup(get(String::STRING_FOR_DX_ERROR_READ_ERROR));
+ goto close;
+ }
+ result = f_read(&file, buffer, fileSize, &numBytesRead);
+ if (numBytesRead < minSize) {
+ display->displayPopup(get(String::STRING_FOR_DX_ERROR_FILE_TOO_SMALL));
+ goto free;
+ }
+
+ error = data->load(buffer, numBytesRead);
+ if (error != String::EMPTY_STRING) {
+ display->displayPopup(get(error), 3);
+ }
+ else {
+ didLoad = true;
+ }
+
+free:
+ GeneralMemoryAllocator::get().dealloc(buffer);
+close:
+ f_close(&file);
+ return didLoad;
+}
+
+bool LoadDxCartridgeUI::tryLoad(const char* path) {
+ if (pd == nullptr) {
+ pd = new DX7Cartridge();
+ }
+ currentValue = 0;
+ scrollPos = 0;
+
+ return openFile(path, pd);
+}
+
+void LoadDxCartridgeUI::readValue() {
+ if (pd == nullptr) {
+ return;
+ }
+ if (display->haveOLED()) {
+ renderUIsForOled();
+ }
+ else {
+ drawValue();
+ }
+
+ DxPatch* patch = currentSound->sources[0].ensureDxPatch();
+ pd->unpackProgram(patch->params, currentValue);
+ currentSound->unassignAllVoices();
+ // TODO: redundant with currentSound :p
+ Instrument* instrument = getCurrentInstrument();
+ if (instrument->type == OutputType::SYNTH && !instrument->existsOnCard) {
+ char name[11];
+ pd->getProgramName(currentValue, name);
+ if (name[0] != 0) {
+ instrument->name.set(name);
+ }
+ ((SoundInstrument *)instrument)->syxSlot = currentValue;
+ }
+}
+
+void LoadDxCartridgeUI::selectEncoderAction(int8_t offset) {
+ navigate(offset, !display->haveOLED());
+ if (display->haveOLED()) {
+ if (currentValue < scrollPos) {
+ scrollPos = currentValue;
+ }
+ else if (currentValue >= scrollPos + kOLEDMenuNumOptionsVisible) {
+ scrollPos++;
+ }
+ }
+
+}
+
+void LoadDxCartridgeUI::navigate(int8_t offset, bool wrapAround) {
+ int32_t newValue = currentValue + offset;
+ int32_t numValues = pd->numPatches();
+
+ if (wrapAround) {
+ if (newValue >= numValues) {
+ newValue %= numValues;
+ }
+ else if (newValue < 0) {
+ newValue = (newValue % numValues + numValues) % numValues;
+ }
+ }
+ else {
+ if (newValue >= numValues || newValue < 0) {
+ return;
+ }
+ }
+
+ currentValue = newValue;
+
+ readValue();
+}
+
+ActionResult LoadDxCartridgeUI::buttonAction(deluge::hid::Button b, bool on, bool inCardRoutine) {
+ using namespace deluge::hid::button;
+ if (b == BACK) {
+ if (on && !currentUIMode) {
+ close();
+ // we cannot "stack" them as we like to see through KeyboardScreen
+ // in this UI
+ openUI(&loadInstrumentPresetUI);
+ }
+ } else if (b == LOAD || b == SELECT_ENC) {
+ if (on && !currentUIMode) {
+ close();
+ }
+ } else if (b == KEYBOARD && on) {
+ if (inCardRoutine) {
+ return ActionResult::REMIND_ME_OUTSIDE_CARD_ROUTINE;
+ }
+ // This is a duplicate of SoundEditor, refactor!
+ Clip* clip = getCurrentClip();
+
+ if (getRootUI() == &keyboardScreen) {
+ if (clip->onAutomationClipView) {
+ swapOutRootUILowLevel(&automationView);
+ automationView.openedInBackground();
+ }
+
+ else {
+ swapOutRootUILowLevel(&instrumentClipView);
+ instrumentClipView.openedInBackground();
+ }
+ }
+ else if (getRootUI() == &instrumentClipView) {
+ swapOutRootUILowLevel(&keyboardScreen);
+ keyboardScreen.openedInBackground();
+ }
+ else if (getRootUI() == &automationView) {
+ if (automationView.onMenuView) {
+ clip->onAutomationClipView = false;
+ automationView.onMenuView = false;
+ indicator_leds::setLedState(IndicatorLED::CLIP_VIEW, true);
+ }
+ automationView.resetInterpolationShortcutBlinking();
+ automationView.resetPadSelectionShortcutBlinking();
+ instrumentClipView.resetSelectedNoteRowBlinking();
+ swapOutRootUILowLevel(&keyboardScreen);
+ keyboardScreen.openedInBackground();
+ }
+
+ PadLEDs::reassessGreyout();
+
+ indicator_leds::setLedState(IndicatorLED::KEYBOARD, getRootUI() == &keyboardScreen);
+ }
+ return ActionResult::NOT_DEALT_WITH;
+}
+
+ActionResult LoadDxCartridgeUI::padAction(int32_t x, int32_t y, int32_t on) {
+ if (sdRoutineLock) {
+ return ActionResult::REMIND_ME_OUTSIDE_CARD_ROUTINE;
+ }
+
+ // TODO: very similar to soundEditor, share?
+ RootUI* rootUI = getRootUI();
+ if (rootUI == &keyboardScreen) {
+ keyboardScreen.padAction(x, y, on);
+ return ActionResult::DEALT_WITH;
+ }
+
+ // Audition pads
+ else if (rootUI == &instrumentClipView) {
+ if (x == kDisplayWidth + 1) {
+ instrumentClipView.padAction(x, y, on);
+ return ActionResult::DEALT_WITH;
+ }
+ }
+
+ else if (rootUI == &automationView) {
+ ActionResult result = automationView.padAction(x, y, on);
+ if (result == ActionResult::DEALT_WITH) {
+ return result;
+ }
+ }
+
+ return ActionResult::NOT_DEALT_WITH;
+}
+
+void LoadDxCartridgeUI::renderOLED(deluge::hid::display::oled_canvas::Canvas& canvas) {
+ if (!currentSound->syxPath.isEmpty()) {
+ canvas.drawScreenTitle(currentSound->syxPath.get());
+ }
+
+ if (pd == nullptr) {
+ return;
+ }
+ char names[32][11];
+ pd->getProgramNames(names);
+
+ static_vector itemNames = {};
+ for (int i = 0; i < pd->numPatches(); i++) {
+ itemNames.push_back(names[i]);
+ }
+ MenuItem::drawItemsForOled(itemNames, currentValue - scrollPos, scrollPos);
+}
+
+void LoadDxCartridgeUI::drawValue() {
+ char names[32][11];
+ pd->getProgramNames(names);
+
+ display->setScrollingText(names[currentValue]);
+}
+
+
+LoadDxCartridgeUI loadDxCartridgeUI;
diff --git a/src/deluge/gui/menu_item/dx/cartridge.h b/src/deluge/gui/ui/load/load_dx_cartridge.h
similarity index 56%
rename from src/deluge/gui/menu_item/dx/cartridge.h
rename to src/deluge/gui/ui/load/load_dx_cartridge.h
index 1d3fab9b29..93c6b1284d 100644
--- a/src/deluge/gui/menu_item/dx/cartridge.h
+++ b/src/deluge/gui/ui/load/load_dx_cartridge.h
@@ -1,6 +1,6 @@
/*
- * Copyright © 2015-2023 Synthstrom Audible Limited
+ * Copyright © 2018-2023 Synthstrom Audible Limited
*
* This file is part of The Synthstrom Audible Deluge Firmware.
*
@@ -18,30 +18,35 @@
#pragma once
-#include "gui/menu_item/menu_item.h"
+#include "definitions_cxx.hpp"
+#include "gui/ui/ui.h"
+#include "storage/DX7Cartridge.h"
-class DX7Cartridge;
+class SoundInstrument;
-namespace deluge::gui::menu_item {
+class LoadDxCartridgeUI : public UI {
+ void renderOLED(deluge::hid::display::oled_canvas::Canvas& canvas) override;
+ void drawValue();
+
+ void selectEncoderAction(int8_t offset) override;
+ ActionResult buttonAction(deluge::hid::Button b, bool on, bool inCardRoutine) override;
+ ActionResult padAction(int32_t x, int32_t y, int32_t on) override;
+ UIType getUIType() override { return UIType::BROWSER; }
+ void readValue();
-class DxCartridge final : public MenuItem {
public:
- using MenuItem::MenuItem;
- DxCartridge(l10n::String newName) : MenuItem(newName), pd(nullptr) {}
- void beginSession(MenuItem* navigatedBackwardFrom) override;
- bool tryLoad(const char* path);
- void drawPixelsForOled() override;
- void readValueAgain() final;
- void selectEncoderAction(int32_t offset) final;
- MenuItem* selectButtonPress() final;
- void drawValue();
+ LoadDxCartridgeUI() {};
+
+ bool tryLoad(const char* path);
+ void navigate(int8_t offset, bool wrapAround = true);
// this thing is big. allocate external mem on demand
- DX7Cartridge* pd;
+ DX7Cartridge* pd = nullptr;
int32_t currentValue = 0;
int scrollPos = 0; // Each instance needs to store this separately
+
+ SoundInstrument *currentSound = nullptr;
};
-extern DxCartridge dxCartridge;
-} // namespace deluge::gui::menu_item
+extern LoadDxCartridgeUI loadDxCartridgeUI;
diff --git a/src/deluge/gui/ui/load/load_instrument_preset_ui.cpp b/src/deluge/gui/ui/load/load_instrument_preset_ui.cpp
index 2d3c960515..c93879eda2 100644
--- a/src/deluge/gui/ui/load/load_instrument_preset_ui.cpp
+++ b/src/deluge/gui/ui/load/load_instrument_preset_ui.cpp
@@ -20,6 +20,7 @@
#include "extern.h"
#include "gui/context_menu/load_instrument_preset.h"
#include "gui/ui/keyboard/keyboard_screen.h"
+#include "gui/ui/load/load_dx_cartridge.h"
#include "gui/ui/root_ui.h"
#include "gui/views/arranger_view.h"
#include "gui/views/instrument_clip_view.h"
@@ -38,6 +39,8 @@
#include "model/instrument/midi_instrument.h"
#include "model/song/song.h"
#include "processing/engines/audio_engine.h"
+#include "processing/sound/sound_instrument.h"
+#include "storage/DX7Cartridge.h"
#include "storage/audio/audio_file_manager.h"
#include "storage/file_item.h"
#include "storage/storage_manager.h"
@@ -47,6 +50,13 @@ using namespace deluge;
namespace encoders = deluge::hid::encoders;
using encoders::EncoderName;
+char const* allowedFileExtensionsXMLandSYX[] = {"XML", "Json", "syx", NULL};
+
+static bool isSYXFile(FileItem *item) {
+ size_t len = item->filename.getLength();
+ return(len >= 4 && strcasecmp(item->filename.get()+(len-4), ".syx") == 0);
+}
+
LoadInstrumentPresetUI loadInstrumentPresetUI{};
LoadInstrumentPresetUI::LoadInstrumentPresetUI() {
@@ -62,6 +72,7 @@ bool LoadInstrumentPresetUI::getGreyoutColsAndRows(uint32_t* cols, uint32_t* row
return true;
}
+
bool LoadInstrumentPresetUI::opened() {
if (getRootUI() == &keyboardScreen) {
@@ -130,12 +141,15 @@ Error LoadInstrumentPresetUI::setupForOutputType() {
indicator_leds::setLedState(IndicatorLED::MIDI, false);
indicator_leds::setLedState(IndicatorLED::CV, false);
+ allowedFileExtensions = allowedFileExtensionsXML;
+
if (loadingSynthToKitRow) {
indicator_leds::blinkLed(IndicatorLED::SYNTH);
indicator_leds::blinkLed(IndicatorLED::KIT);
}
else if (outputTypeToLoad == OutputType::SYNTH) {
indicator_leds::blinkLed(IndicatorLED::SYNTH);
+ allowedFileExtensions = allowedFileExtensionsXMLandSYX;
}
else if (outputTypeToLoad == OutputType::MIDI_OUT) {
indicator_leds::blinkLed(IndicatorLED::MIDI);
@@ -289,6 +303,17 @@ void LoadInstrumentPresetUI::currentFileChanged(int32_t movementDirection) {
//}
}
+static Error getRawFilePath(String* path, FileItem *item) {
+ path->set(&Browser::currentDir);
+
+ Error error = path->concatenate("/");
+ if (error != Error::NONE) {
+ return error;
+ }
+
+return path->concatenate(&item->filename);
+}
+
void LoadInstrumentPresetUI::enterKeyPress() {
FileItem* currentFileItem = getCurrentFileItem();
@@ -307,8 +332,46 @@ void LoadInstrumentPresetUI::enterKeyPress() {
return;
}
}
+ else if (isSYXFile(currentFileItem)) {
+ String path;
+ getRawFilePath(&path, currentFileItem);
- else {
+ // TODO: do a quick check this is a yamaha syx file - nice to leave in a slot where other SYX files can be detected as well
+ if (!loadDxCartridgeUI.tryLoad(path.get())) {
+ // display->displayPopup(path.get());
+ return;
+ }
+
+ // TODO: not if we currently is at a freshly loaded DX7 instrument, in case we can just modify it in place
+ ParamManagerForTimeline newParamManager;
+ SoundInstrument* newInstrument = (SoundInstrument *)StorageManager::createNewInstrument(OutputType::SYNTH, &newParamManager);
+ newInstrument->dirPath.set(¤tDir);
+ newInstrument->syxPath.set(¤tFileItem->filename);
+ newInstrument->setupAsBlankSynth(&newParamManager, true);
+ // TODO: oldInstrumentShouldBeReplaced ??????
+ // This is how we feed a ParamManager into the replaceInstrument() function
+ currentSong->backUpParamManager((ModControllableAudio*)newInstrument->toModControllable(), nullptr,
+ &newParamManager, true);
+ currentSong->replaceInstrument(instrumentToReplace, newInstrument);
+ currentInstrument = newInstrument;
+ instrumentToReplace = newInstrument;
+ actionLogger.deleteAllLogs(); // Can't undo past this!
+ display->removeWorkingAnimation();
+ close();
+
+ if (loadDxCartridgeUI.pd->isCartridge()) {
+ loadDxCartridgeUI.currentSound = newInstrument;
+ openUI(&loadDxCartridgeUI);
+ // associating the instrument with a file is tricky, we need to
+ // allocate an "unsaved slot" which can be navigated to..
+ } else {
+ loadDxCartridgeUI.pd->unpackProgram(newInstrument->sources[0].dxPatch->params, 0);
+ currentFileItem->instrument = newInstrument;
+ }
+
+ return;
+
+ } else {
if (currentInstrumentLoadError != Error::NONE) {
if (loadingSynthToKitRow) {
@@ -857,6 +920,11 @@ Error LoadInstrumentPresetUI::performLoad(bool doClone) {
return Error::NONE; // Happens if navigate over a folder's name (Instrument stays the same),
}
+ if (isSYXFile(currentFileItem)) {
+ // TODO: if this is a single-patch .syx file, treat it as a synth patch already
+ return Error::NONE;
+ }
+
// then back onto that neighbouring Instrument - you'd incorrectly get a "USED" error without this line.
// Work out availabilityRequirement. This can't change as presets are navigated through... I don't think?
diff --git a/src/deluge/gui/ui/menus.cpp b/src/deluge/gui/ui/menus.cpp
index c1eef5f800..a36db953b0 100644
--- a/src/deluge/gui/ui/menus.cpp
+++ b/src/deluge/gui/ui/menus.cpp
@@ -50,7 +50,6 @@
#include "gui/menu_item/delay/ping_pong.h"
#include "gui/menu_item/delay/sync.h"
#include "gui/menu_item/drum_name.h"
-#include "gui/menu_item/dx/browse.h"
#include "gui/menu_item/dx/engine_select.h"
#include "gui/menu_item/dx/global_params.h"
#include "gui/menu_item/dx/param.h"
@@ -1142,8 +1141,7 @@ submenu::Modulator modulator0Menu{STRING_FOR_FM_MODULATOR_1, modulatorMenuItems,
submenu::Modulator modulator1Menu{STRING_FOR_FM_MODULATOR_2, modulatorMenuItems, 1};
// Submenu::SubmenuReferringToOneThing if we need to address osc1 and osc2 directly
-std::array