From 6bf4770e950299da92952f2965cccf86a903fc9f Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 3 Jan 2025 20:17:26 +0100 Subject: Added config --- Click.cpp | 2 ++ Click.h | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ MIDI.h | 39 ++++++++++++++++++------------- Makefile | 3 ++- config.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.h | 23 +++++++++++++++++- main.cpp | 16 +++++++++++-- 7 files changed, 203 insertions(+), 20 deletions(-) create mode 100644 Click.cpp create mode 100644 Click.h diff --git a/Click.cpp b/Click.cpp new file mode 100644 index 0000000..4083e0d --- /dev/null +++ b/Click.cpp @@ -0,0 +1,2 @@ +#include "Click.h" + diff --git a/Click.h b/Click.h new file mode 100644 index 0000000..0c2d0c7 --- /dev/null +++ b/Click.h @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include "config.h" + +// Virtual base class +// Abstraction of BPM detection / setting +class Click +{ +public: + Click(){} + virtual ~Click(){} +}; + +// Internally generated +// Configured via: BPM +class InternalClick: public Click +{ +public: + InternalClick(Config& config): m_bpm(config.get_bpm()){} + virtual ~InternalClick(){} + +private: + int m_bpm; +}; + +// Generated from MIDI notes +// Configured via channel and note to listen to +class NoteClick: public Click +{ +public: + NoteClick(Config& config): + m_channel(config.get_midi_channel()), + m_note(config.get_midi_note()) + { + } + + virtual ~NoteClick(){} + + boost::signals2::signal signal_click; + + void receive_note(int channel, int note) + { + if (true || (channel == m_channel && note == m_note)) { + signal_click(); + } + } + +private: + int m_channel; + int m_note; +}; + +// Generated from MIDI Clock +class ClockClick: public Click +{ +public: + ClockClick(){} + virtual ~ClockClick(){} +}; + diff --git a/MIDI.h b/MIDI.h index 0dbc4aa..d71298d 100644 --- a/MIDI.h +++ b/MIDI.h @@ -2,6 +2,7 @@ #include "config.h" #include "debug.h" +#include "log.h" #include @@ -31,7 +32,7 @@ public: throw std::runtime_error("MIDI port couldn't be opened"); } - std::cout << "in_port: " << std::to_string(in_port) << std::endl; + log_cout << "in_port: " << std::to_string(in_port) << std::endl; #if 1 snd_seq_addr_t sender, dest; @@ -79,7 +80,9 @@ public: free(pfd); } - boost::signals2::signal signal_click; + boost::signals2::signal signal_note; + boost::signals2::signal signal_active_sensing; + boost::signals2::signal signal_clock; int fd() { @@ -121,33 +124,37 @@ public: // returns if click starts void process(const snd_seq_event_t *ev) { - if((ev->type == SND_SEQ_EVENT_NOTEON) - ||(ev->type == SND_SEQ_EVENT_NOTEOFF)) { - const char *type = (ev->type == SND_SEQ_EVENT_NOTEON) ? "on " : "off"; - debug_cout << fmt::format("[{}] Note {}: {:2x} vel({:2x}), ch {:2x}\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick, + if ((ev->type == SND_SEQ_EVENT_NOTEON) ||(ev->type == SND_SEQ_EVENT_NOTEOFF)) { + const char *type = (ev->type == SND_SEQ_EVENT_NOTEON) ? "on " : "off"; + debug_cout << fmt::format("[{}] Note {}: {:2x} vel({:2x}), ch {:2x}\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick, type, ev->data.note.note, ev->data.note.velocity, ev->data.control.channel); - if (ev->type == SND_SEQ_EVENT_NOTEON) { - if (true || (ev->data.note.note == CLICK_NOTE && ev->data.control.channel == CLICK_CHANNEL)) { - signal_click(); - } - } + if (ev->type == SND_SEQ_EVENT_NOTEON) { + signal_note(ev->data.control.channel, ev->data.note.note); + } } - else if(ev->type == SND_SEQ_EVENT_CONTROLLER) + else if (ev->type == SND_SEQ_EVENT_CONTROLLER) { - debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick, + debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", + ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick, ev->data.control.param, ev->data.control.value); } - else if(ev->type == SND_SEQ_EVENT_CLOCK) + else if (ev->type == SND_SEQ_EVENT_SENSING) { - debug_cout << fmt::format("[{}] Clock\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick) << std::endl;; + signal_active_sensing(); + debug_cout << fmt::format("[{}] Active Sensing\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick) << std::endl; + } + else if (ev->type == SND_SEQ_EVENT_CLOCK) + { + signal_clock(); + debug_cout << fmt::format("[{}] Clock\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick) << std::endl; } else { - debug_cout << fmt::format("[{}] Unknown: Unhandled Event Received\n", ev->time.tick) << std::endl;; + log_cout << fmt::format("[{}] Unknown MIDI event: {}\n", ev->time.tick, ev->type) << std::endl; } } diff --git a/Makefile b/Makefile index 4c58223..8667576 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,8 @@ SRCS= \ Timer.cpp \ debug.cpp \ cpuload.cpp \ - log.cpp + log.cpp \ + Click.cpp HEADERS=$(SRCS:.cpp=.h) diff --git a/config.cpp b/config.cpp index a2c980f..b6460f2 100644 --- a/config.cpp +++ b/config.cpp @@ -1,4 +1,82 @@ #include "config.h" +#include "log.h" + +#include +#include + +#include + +#include + const char *device = "default"; // playback device +std::string config_filename = "click.cfg"; + +Config::Config() +{ + try { + // presets / defaults + m_midi_channel = CLICK_CHANNEL; + m_midi_note = CLICK_NOTE; + m_bpm = default_bpm; + + std::string config = Reichwein::File::getFile(config_filename); + + std::vector lines = Reichwein::Stringhelper::split(config, "\n"); + + for (const auto& i: lines) { + if (i.starts_with("midi_channel=")) { + m_midi_channel = stoul(i.substr(13)); + } + if (i.starts_with("midi_note=")) { + m_midi_note = stoul(i.substr(10)); + } + if (i.starts_with("bpm=")) { + m_bpm = stoul(i.substr(4)); + } + } + } catch (const std::exception& ex) { + log_cout << "Config not found. Setting config to defaults." << std::endl; + } +} + +Config::~Config() +{ + std::string config = fmt::format("midi_channel={}\n", m_midi_channel) + + fmt::format("midi_note={}\n", m_midi_note) + + fmt::format("bpm={}\n", m_bpm); + + Reichwein::File::setFile(config_filename, config); +} + +int Config::get_midi_channel() +{ + return m_midi_channel; +} + +void Config::set_midi_channel(int channel) +{ + m_midi_channel = channel; +} + +int Config::get_midi_note() +{ + return m_midi_note; +} + +void Config::set_midi_note(int note) +{ + m_midi_note = note; +} + +int Config::get_bpm() +{ + return m_bpm; +} + +void Config::set_bpm(int bpm) +{ + m_bpm = bpm; +} + diff --git a/config.h b/config.h index b1ee6a4..b347113 100644 --- a/config.h +++ b/config.h @@ -2,11 +2,32 @@ #include -// Config +// Defaults static const int CLICK_NOTE = 37; static const int CLICK_CHANNEL = 4; extern const char *device; // playback device const snd_pcm_sframes_t nframes = 1024; // ~1/44th sec buffer size const unsigned int f_sample = 44100; const int log_lines = 10; +const int default_bpm = 120; +class Config +{ +public: + Config(); + ~Config(); + + int get_midi_channel(); + void set_midi_channel(int channel); + + int get_midi_note(); + void set_midi_note(int note); + + int get_bpm(); + void set_bpm(int bpm); + +private: + int m_midi_channel; + int m_midi_note; + int m_bpm; +}; diff --git a/main.cpp b/main.cpp index 8fb4822..ca8ec91 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,4 @@ +#include "Click.h" #include "ClickStream.h" #include "MIDI.h" #include "PCM.h" @@ -9,9 +10,10 @@ #include #include #include +#include #include #include -#include +#include #include #include @@ -36,6 +38,15 @@ int main(void) log_cout.activate(); log_cout.log_lines(log_lines); + Config config; + + std::shared_ptr clock_click = std::make_shared(); + std::shared_ptr note_click = std::make_shared(config); + std::shared_ptr internal_click = std::make_shared(config); + + // Active Mode + std::shared_ptr click = note_click; + MIDI midi; ClickStream stream; PCM pcm{stream}; @@ -50,7 +61,8 @@ int main(void) boost::signals2::signal signal_count_loops; // Signal-Slot Connections: - midi.signal_click.connect([&](){stream.click();}); + midi.signal_note.connect([&](int channel, int note){note_click->receive_note(channel, note);}); + note_click->signal_click.connect([&](){stream.click();}); timer_500ms.elapsed.connect([&](){ui.draw();}); signal_count_loops.connect([&](){ui.count_main_loops();}); -- cgit v1.2.3