summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Click.cpp2
-rw-r--r--Click.h62
-rw-r--r--MIDI.h39
-rw-r--r--Makefile3
-rw-r--r--config.cpp78
-rw-r--r--config.h23
-rw-r--r--main.cpp16
7 files changed, 203 insertions, 20 deletions
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 <boost/signals2.hpp>
+
+#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<void()> 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 <boost/signals2.hpp>
@@ -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<void()> signal_click;
+ boost::signals2::signal<void(int, int)> signal_note;
+ boost::signals2::signal<void()> signal_active_sensing;
+ boost::signals2::signal<void()> 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 <libreichwein/file.h>
+#include <libreichwein/stringhelper.h>
+
+#include <fmt/format.h>
+
+#include <string>
+
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<std::string> 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 <alsa/asoundlib.h>
-// 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 <chrono>
#include <cmath>
#include <cstdint>
+#include <exception>
#include <iostream>
#include <limits>
-#include <exception>
+#include <memory>
#include <stdexcept>
#include <stdio.h>
@@ -36,6 +38,15 @@ int main(void)
log_cout.activate();
log_cout.log_lines(log_lines);
+ Config config;
+
+ std::shared_ptr<ClockClick> clock_click = std::make_shared<ClockClick>();
+ std::shared_ptr<NoteClick> note_click = std::make_shared<NoteClick>(config);
+ std::shared_ptr<InternalClick> internal_click = std::make_shared<InternalClick>(config);
+
+ // Active Mode
+ std::shared_ptr<Click> click = note_click;
+
MIDI midi;
ClickStream stream;
PCM pcm{stream};
@@ -50,7 +61,8 @@ int main(void)
boost::signals2::signal<void()> 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();});