Skip to content

Commit

Permalink
Merge branch 'master' into YamahaRefaceCP
Browse files Browse the repository at this point in the history
# Conflicts:
#	adaptations/Yamaha-YC-Series.py
  • Loading branch information
christofmuc committed Nov 2, 2024
2 parents 709dbf5 + 80e7285 commit 43c1d49
Show file tree
Hide file tree
Showing 15 changed files with 1,191 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ build
build20231003
build_python38/
build_new_python/
build2024.3
build3.x
2 changes: 1 addition & 1 deletion MidiKraft
1 change: 1 addition & 0 deletions The-Orm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ set(SOURCES
MacroConfig.cpp MacroConfig.h
MainComponent.h MainComponent.cpp
Main.cpp
MidiLogPanel.cpp MidiLogPanel.h
OrmLookAndFeel.cpp OrmLookAndFeel.h
PatchButtonPanel.cpp PatchButtonPanel.h
PatchDiff.cpp PatchDiff.h
Expand Down
2 changes: 1 addition & 1 deletion The-Orm/CreateListDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ void CreateListDialog::notifyResult()
else if (static_cast<int>(fillMode_.getValue()) == 2) {
fillParameters.fillMode = TListFillMode::Random;
}
fillParameters.number = patchNumber_.getValue();
fillParameters.number = (size_t) (patchNumber_.getValue().operator int());
callback_(list_, fillParameters);
}

Expand Down
2 changes: 1 addition & 1 deletion The-Orm/CreateListDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class CreateListDialog : public Component, private TextButton::Listener {
};
struct TFillParameters {
TListFillMode fillMode;
int number;
size_t number;
};
typedef std::function<void(std::shared_ptr<midikraft::PatchList> result)> TCallback;
typedef std::function<void(std::shared_ptr<midikraft::PatchList> result, TFillParameters fillParameters)> TCallbackWithFill;
Expand Down
2 changes: 1 addition & 1 deletion The-Orm/MainComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ MainComponent::MainComponent(bool makeYourOwnSize) :

// Install our MidiLogger
midikraft::MidiController::instance()->setMidiLogFunction([this](const MidiMessage& message, const String& source, bool isOut) {
midiLogView_.addMessageToList(message, source, isOut);
midiLogView_.log().addMessageToList(message, source, isOut);
});

// Do a quickconfigure
Expand Down
4 changes: 2 additions & 2 deletions The-Orm/MainComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <juce_gui_basics/juce_gui_basics.h>

#include "LogView.h"
#include "MidiLogView.h"
#include "MidiLogPanel.h"
#include "PatchButtonGrid.h"
#include "InsetBox.h"
#include "DebounceTimer.h"
Expand Down Expand Up @@ -108,7 +108,7 @@ class MainComponent : public Component, private ChangeListener
std::unique_ptr<PatchView> patchView_;
std::unique_ptr<KeyboardMacroView> keyboardView_;
std::unique_ptr<SplitteredComponent> splitter_;
MidiLogView midiLogView_;
MidiLogPanel midiLogView_;
knobkraft::AdaptationView adaptationView_;
InsetBox midiLogArea_;
std::unique_ptr<SettingsView> settingsView_;
Expand Down
91 changes: 91 additions & 0 deletions The-Orm/MidiLogPanel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "MidiLogPanel.h"

#include "LayoutConstants.h"

#include "Sysex.h"
#include "MidiController.h"

#include "spdlog/spdlog.h"

#include <regex>

MidiLogPanel::MidiLogPanel() {
addAndMakeVisible(midiLogView_);
sysexEntryLabel_.setText("Sysex entry", dontSendNotification);
addAndMakeVisible(sysexEntryLabel_);

sysexEntry_.onReturnKey = [this]() { send(); };
sysexEntry_.setTextToShowWhenEmpty("enter sysex message to send, e.g. f0 43 22 01 00 00 f7, press return to send.", Colours::grey);
addAndMakeVisible(sysexEntry_);

sendSysex_.setButtonText("Send");
sendSysex_.onClick = [this]() {
send();
};
addAndMakeVisible(sendSysex_);
}


std::optional<std::vector<uint8>> parseSysexString(const juce::String& input)
{
std::vector<uint8> sysexData;
auto tokens = juce::StringArray::fromTokens(input, " ", "");

std::regex hexRegex("^[0-9A-Fa-f]{1,2}$"); // Matches valid 1 or 2 digit hex numbers
for (auto& token : tokens)
{
if (token.startsWithIgnoreCase("0x"))
token = token.substring(2); // Remove "0x" if present

if (!std::regex_match(token.toStdString(), hexRegex))
{
spdlog::error("Error: Invalid hexadecimal token: {}", token.toStdString());
return std::nullopt; // Invalid hex, return empty optional
}

sysexData.push_back(static_cast<uint8>(token.getHexValue32()));
}

return sysexData; // Return parsed data if no errors
}

void MidiLogPanel::send() {
auto result = parseSysexString(sysexEntry_.getText().trim());
if (result.has_value()) {
std::vector<uint8> entry = *result;
if (entry.size() > 0) {
if (entry[0] != 0xf0) {
std::vector<uint8> fixed({0xf0});
std::copy(entry.cbegin(), entry.cend(), std::back_inserter(fixed));
entry = fixed;
}
if (entry.back() != 0xf7) {
entry.push_back(0xf7);
}
sendToMidiOuts(Sysex::vectorToMessages(entry));
}
}
else
{
spdlog::error("Parsing error, can't format sysex message to send");
}
}

void MidiLogPanel::sendToMidiOuts(std::vector<MidiMessage> const& messages) {
for (auto output : midikraft::MidiController::instance()->currentOutputs(false)) {
auto midiOut = midikraft::MidiController::instance()->getMidiOutput(output);
midiOut->sendBlockOfMessagesFullSpeed(messages);
}
}

void MidiLogPanel::resized()
{
auto area = getLocalBounds();

auto sysexSendRow = area.removeFromBottom(LAYOUT_LINE_SPACING);
sysexEntryLabel_.setBounds(sysexSendRow.removeFromLeft(LAYOUT_BUTTON_WIDTH).withTrimmedLeft(LAYOUT_INSET_SMALL));
sendSysex_.setBounds(sysexSendRow.removeFromRight(LAYOUT_BUTTON_WIDTH + LAYOUT_INSET_NORMAL).withTrimmedLeft(LAYOUT_INSET_NORMAL));
sysexEntry_.setBounds(sysexSendRow);

midiLogView_.setBounds(area);
}
25 changes: 25 additions & 0 deletions The-Orm/MidiLogPanel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include "JuceHeader.h"

#include "MidiLogView.h"


class MidiLogPanel : public Component {
public:
MidiLogPanel();
virtual ~MidiLogPanel() override = default;

virtual void resized() override;

MidiLogView& log() { return midiLogView_; }

private:
void send();
void sendToMidiOuts(std::vector<MidiMessage> const& messages);

Label sysexEntryLabel_;
TextEditor sysexEntry_;
TextButton sendSysex_;
MidiLogView midiLogView_;
};
7 changes: 5 additions & 2 deletions The-Orm/PatchButtonPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,15 @@ void PatchButtonPanel::setPatchLoader(TPageLoader pageGetter)
pageLoader_ = pageGetter;
}

void PatchButtonPanel::setTotalCount(int totalCount)
void PatchButtonPanel::setTotalCount(int totalCount, bool resetToPageOne /* = true */)
{
pageBase_ = pageNumber_ = 0;
totalSize_ = totalCount;
numPages_ = totalCount / pageSize_;
if (totalCount % pageSize_ != 0) numPages_++;
if (resetToPageOne || pageBase_ >= totalCount) {
// Either we want to jump back to page 1, or the current page is no longer possible as the totalCount is smaller than the current's page first element index
pageBase_ = pageNumber_ = 0;
}
}

void PatchButtonPanel::changeGridSize(int newWidth, int newHeight) {
Expand Down
2 changes: 1 addition & 1 deletion The-Orm/PatchButtonPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class PatchButtonPanel : public Component,
virtual ~PatchButtonPanel() override;

void setPatchLoader(TPageLoader pageGetter);
void setTotalCount(int totalCount);
void setTotalCount(int totalCount, bool resetToPageOne = true);
void changeGridSize(int newWidth, int newHeight);
void setPatches(std::vector<midikraft::PatchHolder> const& patches, int autoSelectTarget = -1);

Expand Down
75 changes: 59 additions & 16 deletions The-Orm/PatchListTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ PatchListTree::PatchListTree(midikraft::PatchDatabase& db, std::vector<midikraft
std::vector<TreeViewItem*> result;
auto userLists = db_.allPatchLists();
userLists = sortLists<midikraft::ListInfo>(userLists, [](const midikraft::ListInfo& info) { return info.name; });
userLists_.clear();
for (auto const& list : userLists) {
result.push_back(newTreeViewItemForPatchList(list));
}
Expand Down Expand Up @@ -236,19 +235,6 @@ void PatchListTree::refreshAllUserLists(std::function<void()> onFinished)
});
}

void PatchListTree::refreshUserList(std::string list_id, std::function<void()> onFinished)
{
if (userLists_.find(list_id) != userLists_.end()) {
MessageManager::callAsync([node = userLists_[list_id], onFinished]() {
node->regenerate();
onFinished();
});
}
else {
jassertfalse;
}
}

void PatchListTree::refreshAllImports(std::function<void()> onFinished)
{
MessageManager::callAsync([this, onFinished]() {
Expand All @@ -257,6 +243,65 @@ void PatchListTree::refreshAllImports(std::function<void()> onFinished)
});
}

TreeViewNode* PatchListTree::findNodeForListID(std::string const& list_id) {
// Walk the tree and find the node for the given list id
std::deque<juce::TreeViewItem*> items;
items.push_back(treeView_->getRootItem());
while (!items.empty()) {
TreeViewItem* node = items.front();
items.pop_front();
// Check if this is a node for the list we're looking for
auto treeviewnode = dynamic_cast<TreeViewNode*>(node);
if (treeviewnode && (treeviewnode->id().toStdString() == list_id)) {
return treeviewnode;
}

// Inspect the children
for (int i = 0; i < node->getNumSubItems(); i++) {
items.push_back(node->getSubItem(i));
}
}
return nullptr;
}

void PatchListTree::refreshChildrenOfListId(std::string const& list_id, std::function<void()> onFinished) {
MessageManager::callAsync([this, list_id, onFinished] {
auto node = findNodeForListID(list_id);
if (node != nullptr) {
node->regenerate();
onFinished();
}
else
{
spdlog::error("Program error: Did not find node for list ID {}, failed to refresh tree view", list_id);
};
});
}

void PatchListTree::refreshParentOfListId(std::string const& list_id, std::function<void()> onFinished) {
MessageManager::callAsync([this, list_id, onFinished] {
auto node = findNodeForListID(list_id);
if (node != nullptr) {
// Found, fresh the parent
auto parent = node->getParentItem();
auto parentitem = dynamic_cast<TreeViewNode*>(parent);
if (parentitem) {
parentitem->regenerate();
onFinished();
return;
}
else {
spdlog::error("Program error: Parent has no regenerate capability, failed to refresh tree view");
return;
}
}
else
{
spdlog::error("Program error: Did not find node for list ID {}, failed to refresh tree view", list_id);
};
});
}

void PatchListTree::selectAllIfNothingIsSelected()
{
if (treeView_->getNumSelectedItems() == 0) {
Expand Down Expand Up @@ -524,7 +569,6 @@ TreeViewNode* PatchListTree::newTreeViewItemForImports(std::shared_ptr<midikraft

TreeViewNode* PatchListTree::newTreeViewItemForUserBank(std::shared_ptr<midikraft::Synth> synth, TreeViewNode *parent, midikraft::ListInfo list) {
auto node = new TreeViewNode(list.name, list.id);
userLists_[list.id] = node;
node->onSelected = [this, list, synth](String clicked) {
juce::ignoreUnused(clicked);
UIModel::instance()->multiMode_.setMultiSynthMode(false);
Expand Down Expand Up @@ -568,7 +612,6 @@ TreeViewNode* PatchListTree::newTreeViewItemForUserBank(std::shared_ptr<midikraf

TreeViewNode* PatchListTree::newTreeViewItemForPatchList(midikraft::ListInfo list) {
auto node = new TreeViewNode(list.name, list.id);
userLists_[list.id] = node;
node->onGenerateChildren = [this, list]() {
auto patchList = db_.getPatchList(list, synths_);
std::vector<TreeViewItem*> result;
Expand Down
5 changes: 3 additions & 2 deletions The-Orm/PatchListTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ class PatchListTree : public Component, private ChangeListener {
virtual void resized() override;

void refreshAllUserLists(std::function<void()> onFinished);
void refreshUserList(std::string list_id, std::function<void()> onFinished);
void refreshAllImports(std::function<void()> onFinished);
void refreshChildrenOfListId(std::string const& list_id, std::function<void()> onFinished);
void refreshParentOfListId(std::string const& list_id, std::function<void()> onFinished);

void selectAllIfNothingIsSelected();
void selectItemByPath(std::vector<std::string> const& path);
Expand All @@ -54,6 +55,7 @@ class PatchListTree : public Component, private ChangeListener {
std::string getSelectedSynth() const;
bool isUserListSelected() const;
std::list<std::string> pathOfSelectedItem() const;
TreeViewNode* findNodeForListID(std::string const& list_id);

TreeViewNode* newTreeViewItemForPatch(midikraft::ListInfo list, midikraft::PatchHolder patchHolder, int index);
TreeViewNode* newTreeViewItemForSynthBanks(std::shared_ptr<midikraft::SimpleDiscoverableDevice> synth);
Expand All @@ -72,6 +74,5 @@ class PatchListTree : public Component, private ChangeListener {
std::unique_ptr<TreeView> treeView_;
TreeViewNode* allPatchesItem_;
TreeViewNode* userListsItem_;
std::map<std::string, TreeViewNode*> userLists_;
};

Loading

0 comments on commit 43c1d49

Please sign in to comment.