Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
mfep committed May 4, 2024
1 parent e473193 commit 4d18d79
Show file tree
Hide file tree
Showing 12 changed files with 576 additions and 51 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ set(sources
src/DisconnectedMidiOutNode.cpp
src/KeyboardShortcutAggregator.cpp
src/Licenses.cpp
src/LogNode.cpp
src/MidiChannelNode.cpp
src/MidiInNode.cpp
src/MidiOutNode.cpp
Expand Down
117 changes: 117 additions & 0 deletions src/LogNode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include "LogNode.hpp"

#include "NodeSerializer.hpp"

#include "midi/MessageView.hpp"

#include "imgui.h"
#include "imnodes.h"
#include "spdlog/spdlog.h"

#include <format>

mc::LogNode::MessageDisplay::MessageDisplay(const duration_t& alive_time)
{
m_alive_until = std::chrono::system_clock::now() + alive_time;
}

mc::LogNode::NoteMessageDisplay::NoteMessageDisplay(
int channel, int note, int velocity, bool note_on, MessageDisplay::duration_t alive_time)
: MessageDisplay(alive_time), m_channel(channel), m_note(note), m_velocity(velocity),
m_note_on(note_on)
{
}

bool mc::LogNode::NoteMessageDisplay::alive() const
{
return m_note_on || std::chrono::system_clock::now() <= m_alive_until;
}

void mc::LogNode::NoteMessageDisplay::render() const
{
const char* on_or_off = m_note_on ? "on" : "off";
ImGui::Text(
"Note %s channel: %i note: %i velocity: %i", on_or_off, m_channel, m_note, m_velocity);
}

std::string_view mc::LogNode::LogMidiNode::name()
{
return "Log Node";
}

mc::LogNode::LogNode()
{
m_log_midi_node.add_observer(this);
}

mc::LogNode::~LogNode()
{
m_log_midi_node.remove_observer(this);
}

void mc::LogNode::accept_serializer(nlohmann::json& j, const NodeSerializer& serializer) const
{
serializer.serialize_node(j, *this);
}

mc::midi::Node* mc::LogNode::get_midi_node()
{
return &m_log_midi_node;
}

void mc::LogNode::render_internal()
{
ImNodes::BeginNodeTitleBar();
ImGui::TextUnformatted("Inspector");
ImNodes::EndNodeTitleBar();
ImNodes::BeginInputAttribute(in_id());
m_input_indicator.render();
ImGui::SameLine();
ImGui::TextUnformatted("MIDI in");
ImNodes::EndInputAttribute();

for (const auto& note_display : m_note_displays)
{
note_display.render();
}

std::erase_if(m_note_displays, [](const auto& note_display) {
return !note_display.alive();
});
}

void mc::LogNode::message_received(std::span<const unsigned char> message_bytes)
{
m_input_indicator.trigger();
midi::MessageView message(message_bytes);
midi::tag_overloads channel_message_visitor{
[this]<class T>(midi::NoteMessageViewTag, T note_message_view) {
const int channel = note_message_view.get_channel();
const int note = note_message_view.get_note();
const bool is_note_on =
std::is_same_v<midi::message_view_tag_t<T>, midi::NoteOnMessageViewTag>;
NoteMessageDisplay new_note_message_display(
channel, note, note_message_view.get_velocity(), is_note_on, default_duration);
auto existing_it = std::find_if(
m_note_displays.begin(), m_note_displays.end(), [&](const auto& display) {
return display.get_channel() == channel && display.get_note() == note;
});

if (existing_it == m_note_displays.end())
{
m_note_displays.emplace_back(new_note_message_display);
}
else
{
*existing_it = new_note_message_display;
}
},
[]<class Tag>(
Tag, auto) -> std::enable_if_t<!std::is_base_of_v<midi::NoteMessageViewTag, Tag>> {}};
midi::tag_overloads message_visitor{
[&](midi::ChannelMessageViewTag, auto channel_message_view) {
std::visit(channel_message_visitor, channel_message_view.parse());
},
[](auto, auto) {}};
std::visit(message_visitor, message.parse());
}
81 changes: 81 additions & 0 deletions src/LogNode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

#include "ActivityIndicator.hpp"
#include "Node.hpp"
#include "midi/MidiGraph.hpp"

#include <chrono>
#include <cstdint>
#include <deque>
#include <span>
#include <string_view>

namespace mc
{

class LogNode final : public Node, private midi::GraphObserver
{
private:
class LogMidiNode final : public midi::Node
{
public:
std::string_view name() override;
};

class MessageDisplay
{
public:
using duration_t = std::chrono::duration<int>;

explicit MessageDisplay(const duration_t& alive_time);
virtual ~MessageDisplay() = default;

protected:
std::chrono::time_point<std::chrono::system_clock> m_alive_until;
};

class NoteMessageDisplay final : public MessageDisplay
{
public:
NoteMessageDisplay(int channel,
int note,
int velocity,
bool note_on,
MessageDisplay::duration_t alive_time);
bool alive() const;
void render() const;

int get_channel() const { return m_channel; }
int get_note() const { return m_note; }

private:
int m_channel;
int m_note;
int m_velocity;
bool m_note_on;
};

public:
LogNode();
~LogNode();

void accept_serializer(nlohmann::json& j, const NodeSerializer& serializer) const override;

protected:
midi::Node* get_midi_node() override;
void render_internal() override;

private:
void message_received(std::span<const unsigned char> message_bytes) override;

static constexpr inline MessageDisplay::duration_t default_duration = std::chrono::seconds(2);

LogMidiNode m_log_midi_node;
ActivityIndicator m_input_indicator;

std::deque<NoteMessageDisplay> m_note_displays;

friend class NodeSerializer;
};

} // namespace mc
65 changes: 43 additions & 22 deletions src/NodeEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "imnodes.h"
#include "nlohmann/json.hpp"

#include "LogNode.hpp"
#include "MidiChannelNode.hpp"
#include "MidiInNode.hpp"
#include "MidiOutNode.hpp"
Expand Down Expand Up @@ -143,21 +144,37 @@ NodeEditor NodeEditor::from_json(NodeFactory& node_factory,
std::shared_ptr<Node> NodeEditor::renderContextMenu(bool show_outputting_nodes,
bool show_inputting_nodes)
{
constexpr ImGuiTreeNodeFlags leaf_flags =
ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
auto render_contents = [this](auto& infos) -> std::shared_ptr<Node> {
auto render_add_node = [this](auto node_builder,
const std::string& name) -> std::shared_ptr<Node> {
constexpr ImGuiTreeNodeFlags leaf_flags =
ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
ImGui::TreeNodeEx(name.c_str(), leaf_flags);
if (ImGui::IsItemClicked())
{
std::shared_ptr<Node> node = node_builder();
m_nodes.push_back(node);

ImNodes::SetNodeScreenSpacePos(node->id(), ImGui::GetMousePosOnOpeningCurrentPopup());
ImGui::CloseCurrentPopup();
return node;
}
else
{
return nullptr;
}
};

auto render_midi_inout_list = [this, render_add_node](auto& infos) -> std::shared_ptr<Node> {
for (const auto& info : infos)
{
const std::string port_name = m_port_name_display->get_port_name(info);
ImGui::TreeNodeEx(port_name.c_str(), leaf_flags);
if (ImGui::IsItemClicked())
if (auto node = render_add_node(
[this, &info] {
return m_node_factory->build_midi_node(info);
},
port_name);
node != nullptr)
{
std::shared_ptr<Node> node = m_node_factory->build_midi_node(info);
m_nodes.push_back(node);

ImNodes::SetNodeScreenSpacePos(node->id(),
ImGui::GetMousePosOnOpeningCurrentPopup());
ImGui::CloseCurrentPopup();
return node;
}
}
Expand All @@ -178,19 +195,23 @@ std::shared_ptr<Node> NodeEditor::renderContextMenu(bool show_outputting_nodes,
}
if (show_outputting_nodes || show_inputting_nodes)
{
ImGui::TreeNodeEx("Channel map", leaf_flags);
if (ImGui::IsItemClicked())
{
node = m_node_factory->build_midi_channel_node();
ImNodes::SetNodeScreenSpacePos(node->id(),
ImGui::GetMousePosOnOpeningCurrentPopup());
m_nodes.push_back(node);
ImGui::CloseCurrentPopup();
}
node = render_add_node(
[this] {
return m_node_factory->build_midi_channel_node();
},
"Channel map");
}
if (show_outputting_nodes || show_inputting_nodes)
{
node = render_add_node(
[this] {
return m_node_factory->build_log_node();
},
"Inspector");
}
if (show_outputting_nodes && ImGui::TreeNode("MIDI inputs"))
{
auto tmp_node = render_contents(m_input_infos);
auto tmp_node = render_midi_inout_list(m_input_infos);
if (tmp_node) // new node was created
{
node = tmp_node;
Expand All @@ -199,7 +220,7 @@ std::shared_ptr<Node> NodeEditor::renderContextMenu(bool show_outputting_nodes,
}
if (show_inputting_nodes && ImGui::TreeNode("MIDI outputs"))
{
auto tmp_node = render_contents(m_output_infos);
auto tmp_node = render_midi_inout_list(m_output_infos);
if (tmp_node) // new node was created
{
node = tmp_node;
Expand Down
6 changes: 6 additions & 0 deletions src/NodeFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "DisconnectedMidiInNode.hpp"
#include "DisconnectedMidiOutNode.hpp"
#include "LogNode.hpp"
#include "MidiChannelNode.hpp"
#include "MidiInNode.hpp"
#include "MidiOutNode.hpp"
Expand Down Expand Up @@ -90,6 +91,11 @@ std::shared_ptr<MidiChannelNode> NodeFactory::build_midi_channel_node()
});
}

std::shared_ptr<LogNode> NodeFactory::build_log_node()
{
return std::make_shared<LogNode>();
}

bool NodeFactory::is_node_instantiated(const midi::InputInfo& input_info)
{
return nullptr != get_cached_midi_node(m_input_nodes, input_info);
Expand Down
11 changes: 9 additions & 2 deletions src/NodeFactory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class OutputNode;

class DisconnectedMidiInNode;
class DisconnectedMidiOutNode;
class LogNode;
class MidiChannelNode;
class MidiInNode;
class MidiOutNode;
Expand All @@ -27,6 +28,11 @@ class NodeFactory final
{
public:
NodeFactory(const ThemeControl& theme_control, const PortNameDisplay& port_name_display);
NodeFactory(const NodeFactory&) = delete;
NodeFactory(NodeFactory&&) = delete;

NodeFactory& operator=(const NodeFactory&) = delete;
NodeFactory& operator=(NodeFactory&&) = delete;

std::shared_ptr<MidiInNode> build_midi_node(const midi::InputInfo& input_info);
std::shared_ptr<MidiOutNode> build_midi_node(const midi::OutputInfo& output_info);
Expand All @@ -35,13 +41,14 @@ class NodeFactory final
std::shared_ptr<DisconnectedMidiOutNode> build_disconnected_midi_out_node(
const std::string& output_name);
std::shared_ptr<MidiChannelNode> build_midi_channel_node();
std::shared_ptr<LogNode> build_log_node();

bool is_node_instantiated(const midi::InputInfo& input_info);
bool is_node_instantiated(const midi::OutputInfo& output_info);

private:
const ThemeControl* m_theme_control;
const PortNameDisplay* m_port_name_display;
const ThemeControl* m_theme_control;
const PortNameDisplay* m_port_name_display;
std::map<std::size_t, std::weak_ptr<midi::InputNode>> m_input_nodes;
std::map<std::size_t, std::weak_ptr<midi::OutputNode>> m_output_nodes;
};
Expand Down
12 changes: 12 additions & 0 deletions src/NodeSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "DisconnectedMidiInNode.hpp"
#include "DisconnectedMidiOutNode.hpp"
#include "LogNode.hpp"
#include "MidiChannelNode.hpp"
#include "MidiInNode.hpp"
#include "MidiOutNode.hpp"
Expand Down Expand Up @@ -71,6 +72,13 @@ void NodeSerializer::serialize_node(json& j, const DisconnectedMidiOutNode& node
};
}

void NodeSerializer::serialize_node(nlohmann::json& j, const LogNode& /*node*/) const
{
j = json{
{"type", "log"},
};
}

void NodeSerializer::serialize_node(json& j, const MidiChannelNode& node) const
{
j = json{
Expand Down Expand Up @@ -116,6 +124,10 @@ std::shared_ptr<Node> NodeSerializer::deserialize_node(const json& j) const
j.at("channels").get<midi::ChannelMap::data_t>();
node = channel_node;
}
else if (node_type == "log")
{
node = m_node_factory->build_log_node();
}
else
{
throw std::logic_error("Unexpected node type");
Expand Down
Loading

0 comments on commit 4d18d79

Please sign in to comment.