diff options
author | Roland Reichwein <mail@reichwein.it> | 2025-01-04 10:25:25 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2025-01-04 10:25:25 +0100 |
commit | aaafcea7e26791acbf5b9612e3fb396edcdfcc8f (patch) | |
tree | 63131edf7b2eafe404a01e7ae01e2680d5f1b1e8 | |
parent | 9351c6d9812523b0b8027f2b2e0de54c27004bc2 (diff) |
Fill UI
-rw-r--r-- | MIDI.h | 88 | ||||
-rw-r--r-- | UI.cpp | 67 | ||||
-rw-r--r-- | UI.h | 12 | ||||
-rw-r--r-- | config.cpp | 25 | ||||
-rw-r--r-- | config.h | 12 | ||||
-rw-r--r-- | main.cpp | 3 |
6 files changed, 155 insertions, 52 deletions
@@ -107,60 +107,62 @@ public: std::cerr << "snd_seq_event_input(): -EAGAIN" << std::endl; } + return ev; + } + + uint64_t timestamp_from_event(const snd_seq_event_t *ev) + { + return ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick; + } + + // returns if click starts + void process(snd_seq_event_t *ev) + { + uint64_t timestamp = timestamp_from_event(ev); // original timestamp + // TODO: fix timestamp to be set automatically struct timespec t; if (TIME_UTC != timespec_get(&t, TIME_UTC)) { - std::cerr << "Error: timespec_get()" << std::endl; + log_cout << "Error: timespec_get()" << std::endl; } else { ev->time.time.tv_sec = t.tv_sec; ev->time.time.tv_nsec = t.tv_nsec; } - return ev; - } - - uint64_t timestamp_from_event(const snd_seq_event_t *ev) - { - return ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick; - } - // 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)) { - uint64_t timestamp = timestamp_from_event(ev); - const char *type = (ev->type == SND_SEQ_EVENT_NOTEON) ? "on " : "off"; - debug_cout << fmt::format("[{}] Note {}: {:2x} vel({:2x}), ch {:2x}\n", timestamp, - type, - ev->data.note.note, - ev->data.note.velocity, - ev->data.control.channel); - if (ev->type == SND_SEQ_EVENT_NOTEON) { - signal_note(ev->data.control.channel, ev->data.note.note, timestamp); - } - } - else if (ev->type == SND_SEQ_EVENT_CONTROLLER) - { - debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", timestamp_from_event(ev), - ev->data.control.param, - ev->data.control.value); - } - else if (ev->type == SND_SEQ_EVENT_SENSING) - { - signal_active_sensing(); - debug_cout << fmt::format("[{}] Active Sensing\n", timestamp_from_event(ev)) << std::endl; - } - else if (ev->type == SND_SEQ_EVENT_CLOCK) - { - signal_clock(); - debug_cout << fmt::format("[{}] Clock\n", timestamp_from_event(ev)) << std::endl; - } - else - { - log_cout << fmt::format("[{}] Unknown MIDI event: {}\n", timestamp_from_event(ev), ev->type) << std::endl; - } + 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", timestamp, + type, + ev->data.note.note, + ev->data.note.velocity, + ev->data.control.channel); + if (ev->type == SND_SEQ_EVENT_NOTEON) { + signal_note(ev->data.control.channel, ev->data.note.note, timestamp); + } + } + else if (ev->type == SND_SEQ_EVENT_CONTROLLER) + { + debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", timestamp_from_event(ev), + ev->data.control.param, + ev->data.control.value); + } + else if (ev->type == SND_SEQ_EVENT_SENSING) + { + signal_active_sensing(); + debug_cout << fmt::format("[{}] Active Sensing\n", timestamp_from_event(ev)) << std::endl; + } + else if (ev->type == SND_SEQ_EVENT_CLOCK) + { + signal_clock(); + debug_cout << fmt::format("[{}] Clock\n", timestamp_from_event(ev)) << std::endl; + } + else + { + log_cout << fmt::format("[{}] Unknown MIDI event: {}\n", timestamp_from_event(ev), ev->type) << std::endl; + } } void flush() @@ -8,11 +8,24 @@ #include <iostream> #include <string> +#include <fmt/color.h> #include <fmt/format.h> +const int midi_monitor_max_size = 3; + using namespace std::chrono_literals; -UI::UI(): m_main_loops{}, m_main_loops_checkpoint{}, m_main_loops_timestamp{}, m_active_sensing_timestamp{} +static std::vector<std::string> mode_names{{ + "NoteClick", "Clock", "Internal" +}}; + +UI::UI(Config& config): + m_config(config), + m_main_loops{}, + m_main_loops_checkpoint{}, + m_main_loops_timestamp{}, + m_active_sensing_timestamp{}, + m_midi_timestamp{} { } @@ -34,6 +47,10 @@ void UI::draw() { std::vector<int> cpuloads = get_cpu_loads(); int main_loops_per_second = get_main_loops_per_second(); + int mode = m_config.get_mode(); + int channel = m_config.get_midi_channel(); + int note = m_config.get_midi_note(); + int bpm = m_config.get_bpm(); bool active_sensing_detected = (clock_type::now() - m_active_sensing_timestamp) < 2s; @@ -41,8 +58,18 @@ void UI::draw() std::cout << "\x1B[2J\x1B[H"; //std::cout << std::endl; - std::cout << "- -- BPM +" << std::endl; - std::cout << "Mode: Click __/__ (Clock Internal)" << std::endl; + std::cout << fmt::format("- {:3} BPM +", bpm) << std::endl; + std::cout << "Mode: "; + for (int i = 0; i < mode_names.size(); ++i) { + if (i == mode) { + std::cout << fmt::format(fg(fmt::color::crimson) | fmt::emphasis::bold, " {}", mode_names[i]); + } else { + std::cout << fmt::format(" {}", mode_names[i]); + } + } + std::cout << std::endl; + std::cout << fmt::format("MIDI Note: {}/{}", channel, note); + std::cout << std::endl; std::cout << std::endl; std::cout << "Status:" << std::endl; @@ -53,12 +80,29 @@ void UI::draw() int max = *std::max_element(cpuloads.begin(), cpuloads.end()); std::cout << fmt::format(", max. {:2}%", max) << std::endl; - std::cout << " Notes/Channels: -- -- -- ... (Choose)" << std::endl; - std::cout << " MIDI Timestamp: ------" << std::endl; + std::cout << " MIDI Activity: "; + if (m_midi_monitor.empty()) { + std::cout << "--"; + } else { + int count{}; + for (const auto& i: m_midi_monitor) { + if (count == 0) { + std::cout << fmt::format(fg(fmt::color::crimson) | fmt::emphasis::bold, " {}/{}", i.first, i.second); + } else { + std::cout << fmt::format(" {}/{}", i.first, i.second); + } + ++count; + } + if (count >= midi_monitor_max_size) { + std::cout << " ..."; + } + } + std::cout << std::endl; + std::cout << fmt::format(" MIDI Timestamp: {}", m_midi_timestamp) << std::endl; std::cout << fmt::format(" Active sensing: {}", active_sensing_detected) << std::endl; std::cout << " Clock: ____ BPM" << std::endl; std::cout << " Click: ____ BPM" << std::endl; - std::cout << " Internal: ____ BPM" << std::endl; + std::cout << fmt::format(" Internal: {:3} BPM", bpm) << std::endl; std::cout << fmt::format(" Main loops/s: {}", main_loops_per_second) << std::endl; @@ -76,3 +120,14 @@ void UI::slot_active_sensing() { m_active_sensing_timestamp = clock_type::now(); } + +void UI::slot_midi_note(int channel, int note, uint64_t timestamp) +{ + m_midi_timestamp = timestamp; + + m_midi_monitor.emplace_front(channel, note); + + while (m_midi_monitor.size() > midi_monitor_max_size) { + m_midi_monitor.pop_back(); + } +} @@ -1,6 +1,10 @@ #pragma once +#include "config.h" + #include <chrono> +#include <deque> +#include <utility> #include <boost/signals2.hpp> @@ -9,7 +13,7 @@ class UI public: using clock_type = std::chrono::high_resolution_clock; - UI(); + UI(Config& config); void draw(); @@ -22,8 +26,10 @@ public: // slots void count_main_loops(); void slot_active_sensing(); + void slot_midi_note(int channel, int note, uint64_t timestamp); private: + Config& m_config; int get_main_loops_per_second(); @@ -32,4 +38,8 @@ private: std::chrono::time_point<clock_type> m_main_loops_timestamp; std::chrono::time_point<clock_type> m_active_sensing_timestamp; + + uint64_t m_midi_timestamp; + std::deque<std::pair<int,int>> m_midi_monitor; }; + @@ -30,6 +30,7 @@ void Config::recover() m_midi_channel = CLICK_CHANNEL; m_midi_note = CLICK_NOTE; m_bpm = default_bpm; + m_mode = default_mode; std::string config = Reichwein::File::getFile(config_filename); @@ -38,12 +39,19 @@ void Config::recover() for (const auto& i: lines) { if (i.starts_with("midi_channel=")) { m_midi_channel = stoul(i.substr(13)); + signal_channel(m_midi_channel); } if (i.starts_with("midi_note=")) { m_midi_note = stoul(i.substr(10)); + signal_note(m_midi_note); } if (i.starts_with("bpm=")) { m_bpm = stoul(i.substr(4)); + signal_bpm(m_bpm); + } + if (i.starts_with("mode=")) { + m_mode = stoul(i.substr(5)); + signal_mode(m_mode); } } } catch (const std::exception& ex) { @@ -55,7 +63,8 @@ void Config::persist() { 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); + fmt::format("bpm={}\n", m_bpm) + + fmt::format("mode={}\n", m_mode); Reichwein::File::setFile(config_filename, config); } @@ -68,6 +77,7 @@ int Config::get_midi_channel() void Config::set_midi_channel(int channel) { m_midi_channel = channel; + signal_channel(channel); } int Config::get_midi_note() @@ -78,6 +88,7 @@ int Config::get_midi_note() void Config::set_midi_note(int note) { m_midi_note = note; + signal_note(note); } int Config::get_bpm() @@ -88,5 +99,17 @@ int Config::get_bpm() void Config::set_bpm(int bpm) { m_bpm = bpm; + signal_bpm(bpm); +} + +int Config::get_mode() +{ + return m_mode; +} + +void Config::set_mode(int mode) +{ + m_mode = mode; + signal_mode(mode); } @@ -1,5 +1,6 @@ #pragma once +#include <boost/signals2.hpp> #include <alsa/asoundlib.h> // Defaults @@ -12,6 +13,7 @@ const int log_lines = 10; const int default_bpm = 120; const int pcm_latency_us = 100000; const int click_latency_frames = 10000; +const int default_mode = 0; // 0 = note, 1 = clock, 2 = internal class Config { @@ -19,6 +21,12 @@ public: Config(); ~Config(); + // signals + boost::signals2::signal<void(int)> signal_mode; + boost::signals2::signal<void(int)> signal_channel; + boost::signals2::signal<void(int)> signal_note; + boost::signals2::signal<void(int)> signal_bpm; + int get_midi_channel(); void set_midi_channel(int channel); @@ -28,6 +36,9 @@ public: int get_bpm(); void set_bpm(int bpm); + int get_mode(); + void set_mode(int mode); + void recover(); void persist(); @@ -35,4 +46,5 @@ private: int m_midi_channel; int m_midi_note; int m_bpm; + int m_mode; }; @@ -59,7 +59,7 @@ int main(void) MIDI midi; PCM pcm; - UI ui; + UI ui(config); pcm.write(); @@ -81,6 +81,7 @@ int main(void) timer_500ms.elapsed.connect([&](){ui.draw();}); signal_count_loops.connect([&](){ui.count_main_loops();}); timer_10min.elapsed.connect([&](){config.persist();}); + midi.signal_note.connect([&](int channel, int note, uint64_t timestamp){ui.slot_midi_note(channel, note, timestamp);}); midi.flush(); |