Skip to content

Commit

Permalink
initial version for #344, allowing to send sysex stuff directly to al…
Browse files Browse the repository at this point in the history
…l outputs removing the need to use a tool like Midi-OX for such a simple thing.
  • Loading branch information
christofmuc committed Oct 21, 2024
1 parent c71e1f2 commit 4c79ddf
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 3 deletions.
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/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_;
};

0 comments on commit 4c79ddf

Please sign in to comment.