From 4af400141af0c97c4e4bcd47acf78107a17eafbe Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 3 Jan 2025 10:31:38 +0100 Subject: Separated class files --- ClickStream.cpp | 3 + ClickStream.h | 57 ++++++++ MIDI.cpp | 2 + MIDI.h | 169 +++++++++++++++++++++++ Makefile | 17 ++- PCM.cpp | 1 + PCM.h | 134 ++++++++++++++++++ alsa.cpp | 410 -------------------------------------------------------- config.cpp | 4 + config.h | 11 ++ debian/control | 1 + main.cpp | 78 +++++++++++ 12 files changed, 473 insertions(+), 414 deletions(-) create mode 100644 ClickStream.cpp create mode 100644 ClickStream.h create mode 100644 MIDI.cpp create mode 100644 MIDI.h create mode 100644 PCM.cpp create mode 100644 PCM.h delete mode 100644 alsa.cpp create mode 100644 config.cpp create mode 100644 config.h create mode 100644 debian/control create mode 100644 main.cpp diff --git a/ClickStream.cpp b/ClickStream.cpp new file mode 100644 index 0000000..0448af1 --- /dev/null +++ b/ClickStream.cpp @@ -0,0 +1,3 @@ +#include "ClickStream.h" + + diff --git a/ClickStream.h b/ClickStream.h new file mode 100644 index 0000000..c94f8de --- /dev/null +++ b/ClickStream.h @@ -0,0 +1,57 @@ +#pragma once + +#include "config.h" + +#include + +#include +#include +#include + +class ClickStream +{ +public: + ClickStream(): m_phase(1000000) + { + std::string data_s = Reichwein::File::getFile("media/click.s16le"); + m_data.resize(data_s.size() / 2); // src is in bytes + memcpy(m_data.data(), data_s.data(), data_s.size()); + } + + // generate 1 buffer size + void generate() + { + int i; + size_t j = m_phase; + + for (i = 0; i < nframes; i++) { + if (j >= m_data.size()) + { + buffer[i] = 0; + } + else + { + buffer[i] = m_data[j]; + } + j++; + } + + m_phase = j; + } + + void click() + { + m_phase = 0; + } + + int16_t* get_buffer() + { + return buffer; + } + +private: + std::vector m_data; + size_t m_phase; + int16_t buffer[nframes]; +}; + diff --git a/MIDI.cpp b/MIDI.cpp new file mode 100644 index 0000000..1fe49fe --- /dev/null +++ b/MIDI.cpp @@ -0,0 +1,2 @@ +#include "MIDI.h" + diff --git a/MIDI.h b/MIDI.h new file mode 100644 index 0000000..f0175bc --- /dev/null +++ b/MIDI.h @@ -0,0 +1,169 @@ +#pragma once + +#include "config.h" + +#include + +#include + +class MIDI +{ +public: + MIDI() + { + if (0 > snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK)) + { + throw std::runtime_error("MIDI sequencer couldn't be opened"); + } + + if (0 > snd_seq_set_client_name(seq_handle, "Midi Listener")) + { + throw std::runtime_error("MIDI client name couldn't be set"); + } + + if (0 > ((in_port = snd_seq_create_simple_port(seq_handle, "24:0", + SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_APPLICATION)))) + { + throw std::runtime_error("MIDI port couldn't be opened"); + } + + std::cout << "in_port: " << std::to_string(in_port) << std::endl; + +#if 1 + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *subs; + sender.client = 24; + sender.port = 0; + dest.client = snd_seq_client_id(seq_handle); + dest.port = in_port; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + snd_seq_port_subscribe_set_queue(subs, 1); + snd_seq_port_subscribe_set_time_update(subs, 1); + snd_seq_port_subscribe_set_time_real(subs, 1); + // TODO: fix timestamp (currently always 0) + if (0 > snd_seq_subscribe_port(seq_handle, subs)) +#endif + //if (0 > snd_seq_connect_from(seq_handle, in_port, 24, 0)) + { + throw std::runtime_error("MIDI port couldn't be connected"); + } + + npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN); + if (npfd < 0) { + throw std::runtime_error("snd_seq_poll_descriptors_count() failed"); + } + pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); + if (pfd == nullptr) { + throw std::runtime_error("alloca() error for MIDI"); + } + if (0 > snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN)) + { + throw std::runtime_error("snd_seq_poll_descriptors() failure"); + } + + if (npfd != 1) { + std::cout << "Warning: " << std::to_string(npfd) << " poll fds for MIDI" << std::endl; + } + } + + ~MIDI() + { + free(pfd); + } + + boost::signals2::signal signal_click; + + int fd() + { + return pfd->fd; + } + + bool event_ready() + { + int result = snd_seq_event_input_pending(seq_handle, 1); + if (result < 0) { + throw std::runtime_error("snd_seq_event_input_pending() failed"); + } + + return result > 0; + } + + snd_seq_event_t *read(void) + { + snd_seq_event_t *ev = NULL; + if (0 > snd_seq_event_input(seq_handle, &ev)) + { + std::cerr << "snd_seq_event_input(): -EAGAIN" << std::endl; + } + + // 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; + } + else + { + ev->time.time.tv_sec = t.tv_sec; + ev->time.time.tv_nsec = t.tv_nsec; + } + return ev; + } + + // 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"; + printf("[%d] Note %s: %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(); + } + } + } + else if(ev->type == SND_SEQ_EVENT_CONTROLLER) + { + printf("[%d] 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) + { + printf("[%d] Clock\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick); + } + else + { + printf("[%d] Unknown: Unhandled Event Received\n", ev->time.tick); + } + } + + void wait_for_event() + { + int result = poll(pfd, npfd, 10000); + if (result > 0) + { + // event + } + else if (result == 0) { + // timeout + } else { + throw std::runtime_error("Poll unsuccessful"); + } + } + +private: + snd_seq_t *seq_handle; + int in_port; + int npfd; + struct pollfd* pfd; +}; + diff --git a/Makefile b/Makefile index eb05671..47f9a5f 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,22 @@ -default: alsa +TARGET=click + +default: $(TARGET) + +SRCS=main.cpp MIDI.cpp PCM.cpp ClickStream.cpp config.cpp + +OBJS=$(SRCS:.cpp=.o) CXXLIBS=$(shell pkg-config --libs alsa) -lreichwein -lfmt CXX=clang++ -alsa: alsa.cpp - $(CXX) $(CXXFLAGS) -std=c++20 -O2 -g -Wall -o $@ $^ $(CXXLIBS) +$(TARGET): $(OBJS) + $(CXX) $^ -o $@ $(CXXLIBS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -std=c++20 -O2 -g -Wall -o $@ -c $< clean: - rm -f alsa + rm -f $(TARGET) $(OBJS) sound: ffmpeg -i media/click.wav -f s16le media/click.s16le diff --git a/PCM.cpp b/PCM.cpp new file mode 100644 index 0000000..b70652c --- /dev/null +++ b/PCM.cpp @@ -0,0 +1 @@ +#include "PCM.h" diff --git a/PCM.h b/PCM.h new file mode 100644 index 0000000..c9d7f7f --- /dev/null +++ b/PCM.h @@ -0,0 +1,134 @@ +#pragma once + +#include "ClickStream.h" + +#include "config.h" + +#include + +#include +#include + +using namespace std::string_literals; + +class PCM +{ +public: + PCM(ClickStream& stream): m_stream(stream) + { + // non-blocking + if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { + printf("Playback open error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + if ((err = snd_pcm_set_params(handle, + SND_PCM_FORMAT_S16_LE, + SND_PCM_ACCESS_RW_INTERLEAVED, + 1, + f_sample, + 1, + 100000)) < 0) { // latency in us + printf("Playback open error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + + m_stream.generate(); + + npfd = snd_pcm_poll_descriptors_count(handle); + if (npfd < 0) { + throw std::runtime_error("snd_pcm_poll_descriptors_count() failed"); + } + pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); + if (pfd == nullptr) { + throw std::runtime_error("alloca() error for PCM"); + } + if (0 > snd_pcm_poll_descriptors(handle, pfd, npfd)) + { + throw std::runtime_error("snd_pcm_poll_descriptors() failure"); + } + + if (0 > snd_pcm_start(handle)) + { + throw std::runtime_error("PCM could not be started"); + } + + if (npfd != 1) { + std::cout << "Warning: " << std::to_string(npfd) << " poll fds for pcm" << std::endl; + } + } + + ~PCM() + { + // pass the remaining samples, otherwise they're dropped in close + err = snd_pcm_drain(handle); + if (err < 0) + printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); + snd_pcm_close(handle); + + free(pfd); + } + + int fd() + { + return pfd->fd; + } + + // write from buffer to ALSA PCM + void write() + { + snd_pcm_sframes_t written = snd_pcm_writei(handle, m_stream.get_buffer(), nframes); + if (written < 0) { + if (written == -EPIPE) { + std::cout << "Warning: PCM underrun" << std::endl; + } + std::cout << "Recovering." << std::endl; + written = snd_pcm_recover(handle, written, 0); + } + if (written < 0) { + throw std::runtime_error("snd_pcm_writei failed: "s + snd_strerror(written)); + } + + if (written != nframes) { + std::cout << "Warning: written " << std::to_string(written) << " frames instead of "<< std::to_string(nframes) << std::endl; + } + + m_stream.generate(); + } + + bool wait_for_event() + { + int result = poll(pfd, npfd, 10000); + if (result > 0) + { + // event + return true; + } + else if (result == 0) { + // timeout + return false; + } else { + throw std::runtime_error("Poll unsuccessful"); + } + } + + bool write_available() + { + snd_pcm_sframes_t result = snd_pcm_avail(handle); + if (0 > result) { + std::cerr << "Error: snd_pcm_avail()" << std::endl; + //throw std::runtime_error("snd_pcm_avail()"); + } + + return result >= nframes; + } + +private: + int err; + snd_pcm_t *handle; + + int npfd; + struct pollfd* pfd; + + ClickStream& m_stream; +}; + diff --git a/alsa.cpp b/alsa.cpp deleted file mode 100644 index e669593..0000000 --- a/alsa.cpp +++ /dev/null @@ -1,410 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -using namespace std::string_literals; - -// Config -static const int CLICK_NOTE = 37; -static const int CLICK_CHANNEL = 4; -static const char *device = "default"; // playback device -const snd_pcm_sframes_t nframes = 1024; // ~1/44th sec buffer size -int16_t buffer[nframes]; -const unsigned int f_sample = 44100; - -const double pi = std::acos(-1); - -double diff_timespec(const struct timespec *time1, const struct timespec *time0) { - return (time1->tv_sec - time0->tv_sec) - + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; -} - -class ClickStream -{ -public: - ClickStream(): m_phase(1000000) - { - std::string data_s = Reichwein::File::getFile("media/click.s16le"); - m_data.resize(data_s.size() / 2); // src is in bytes - memcpy(m_data.data(), data_s.data(), data_s.size()); - } - - // generate 1 buffer size - void generate() - { - int i; - size_t j = m_phase; - - for (i = 0; i < nframes; i++) { - if (j >= m_data.size()) - { - buffer[i] = 0; - } - else - { - buffer[i] = m_data[j]; - } - j++; - } - - m_phase = j; - } - - void click() - { - m_phase = 0; - } - -private: - std::vector m_data; - size_t m_phase; -}; - -class PCM -{ -public: - PCM(ClickStream& stream): m_stream(stream) - { - // non-blocking - if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { - printf("Playback open error: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); - } - if ((err = snd_pcm_set_params(handle, - SND_PCM_FORMAT_S16_LE, - SND_PCM_ACCESS_RW_INTERLEAVED, - 1, - f_sample, - 1, - 100000)) < 0) { // latency in us - printf("Playback open error: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); - } - - m_stream.generate(); - - npfd = snd_pcm_poll_descriptors_count(handle); - if (npfd < 0) { - throw std::runtime_error("snd_pcm_poll_descriptors_count() failed"); - } - pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); - if (pfd == nullptr) { - throw std::runtime_error("alloca() error for PCM"); - } - if (0 > snd_pcm_poll_descriptors(handle, pfd, npfd)) - { - throw std::runtime_error("snd_pcm_poll_descriptors() failure"); - } - - if (0 > snd_pcm_start(handle)) - { - throw std::runtime_error("PCM could not be started"); - } - - if (npfd != 1) { - std::cout << "Warning: " << std::to_string(npfd) << " poll fds for pcm" << std::endl; - } - } - - ~PCM() - { - // pass the remaining samples, otherwise they're dropped in close - err = snd_pcm_drain(handle); - if (err < 0) - printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); - snd_pcm_close(handle); - - free(pfd); - } - - int fd() - { - return pfd->fd; - } - - // write from buffer to ALSA PCM - void write() - { - snd_pcm_sframes_t written = snd_pcm_writei(handle, &buffer[0], nframes); - if (written < 0) { - if (written == -EPIPE) { - std::cout << "Warning: PCM underrun" << std::endl; - } - std::cout << "Recovering." << std::endl; - written = snd_pcm_recover(handle, written, 0); - } - if (written < 0) { - throw std::runtime_error("snd_pcm_writei failed: "s + snd_strerror(written)); - } - - if (written != nframes) { - std::cout << "Warning: written " << std::to_string(written) << " frames instead of "<< std::to_string(nframes) << std::endl; - } - - m_stream.generate(); - } - - bool wait_for_event() - { - int result = poll(pfd, npfd, 10000); - if (result > 0) - { - // event - return true; - } - else if (result == 0) { - // timeout - return false; - } else { - throw std::runtime_error("Poll unsuccessful"); - } - } - - bool write_available() - { - snd_pcm_sframes_t result = snd_pcm_avail(handle); - if (0 > result) { - std::cerr << "Error: snd_pcm_avail()" << std::endl; - //throw std::runtime_error("snd_pcm_avail()"); - } - - return result >= nframes; - } - -private: - int err; - snd_pcm_t *handle; - - int npfd; - struct pollfd* pfd; - - ClickStream& m_stream; -}; - -class MIDI -{ -public: - MIDI() - { - if (0 > snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK)) - { - throw std::runtime_error("MIDI sequencer couldn't be opened"); - } - - if (0 > snd_seq_set_client_name(seq_handle, "Midi Listener")) - { - throw std::runtime_error("MIDI client name couldn't be set"); - } - - if (0 > ((in_port = snd_seq_create_simple_port(seq_handle, "24:0", - SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_APPLICATION)))) - { - throw std::runtime_error("MIDI port couldn't be opened"); - } - - std::cout << "in_port: " << std::to_string(in_port) << std::endl; - -#if 1 - snd_seq_addr_t sender, dest; - snd_seq_port_subscribe_t *subs; - sender.client = 24; - sender.port = 0; - dest.client = snd_seq_client_id(seq_handle); - dest.port = in_port; - snd_seq_port_subscribe_alloca(&subs); - snd_seq_port_subscribe_set_sender(subs, &sender); - snd_seq_port_subscribe_set_dest(subs, &dest); - snd_seq_port_subscribe_set_queue(subs, 1); - snd_seq_port_subscribe_set_time_update(subs, 1); - snd_seq_port_subscribe_set_time_real(subs, 1); - // TODO: fix timestamp (currently always 0) - if (0 > snd_seq_subscribe_port(seq_handle, subs)) -#endif - //if (0 > snd_seq_connect_from(seq_handle, in_port, 24, 0)) - { - throw std::runtime_error("MIDI port couldn't be connected"); - } - - npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN); - if (npfd < 0) { - throw std::runtime_error("snd_seq_poll_descriptors_count() failed"); - } - pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd)); - if (pfd == nullptr) { - throw std::runtime_error("alloca() error for MIDI"); - } - if (0 > snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN)) - { - throw std::runtime_error("snd_seq_poll_descriptors() failure"); - } - - if (npfd != 1) { - std::cout << "Warning: " << std::to_string(npfd) << " poll fds for MIDI" << std::endl; - } - } - - ~MIDI() - { - free(pfd); - } - - boost::signals2::signal signal_click; - - int fd() - { - return pfd->fd; - } - - bool event_ready() - { - int result = snd_seq_event_input_pending(seq_handle, 1); - if (result < 0) { - throw std::runtime_error("snd_seq_event_input_pending() failed"); - } - - return result > 0; - } - - snd_seq_event_t *read(void) - { - snd_seq_event_t *ev = NULL; - if (0 > snd_seq_event_input(seq_handle, &ev)) - { - std::cerr << "snd_seq_event_input(): -EAGAIN" << std::endl; - } - - // 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; - } - else - { - ev->time.time.tv_sec = t.tv_sec; - ev->time.time.tv_nsec = t.tv_nsec; - } - return ev; - } - - // 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"; - printf("[%d] Note %s: %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(); - } - } - } - else if(ev->type == SND_SEQ_EVENT_CONTROLLER) - { - printf("[%d] 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) - { - printf("[%d] Clock\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick); - } - else - { - printf("[%d] Unknown: Unhandled Event Received\n", ev->time.tick); - } - } - - void wait_for_event() - { - int result = poll(pfd, npfd, 10000); - if (result > 0) - { - // event - } - else if (result == 0) { - // timeout - } else { - throw std::runtime_error("Poll unsuccessful"); - } - } - -private: - snd_seq_t *seq_handle; - int in_port; - int npfd; - struct pollfd* pfd; -}; - -int main(void) -{ - try { - MIDI midi; - ClickStream stream; - PCM pcm{stream}; - - pcm.write(); - - midi.signal_click.connect([&](){stream.click();}); - - while (true) { - //std::cout << "Main loop entered." << std::endl; - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(midi.fd(), &read_set); - - fd_set write_set; - FD_ZERO(&write_set); - FD_SET(pcm.fd(), &write_set); - - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - int result = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout); - if (result < 0) { - throw std::runtime_error("select() failed"); - } else if (result == 0) { - throw std::runtime_error("select() timeout"); - } - - if (midi.event_ready()) - { - //std::cout << "read..." << std::endl; - auto event = midi.read(); - //std::cout << "process..." << std::endl; - midi.process(event); - } - - if (pcm.write_available()) { - //std::cout << "DEBUG: WRITE" << std::endl; - pcm.write(); - } - } - } catch (const std::exception& ex) { - std::cerr << "Error: " << ex.what() << std::endl; - } - - return 0; -} - diff --git a/config.cpp b/config.cpp new file mode 100644 index 0000000..a2c980f --- /dev/null +++ b/config.cpp @@ -0,0 +1,4 @@ +#include "config.h" + +const char *device = "default"; // playback device + diff --git a/config.h b/config.h new file mode 100644 index 0000000..8c6f1ce --- /dev/null +++ b/config.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +// Config +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; + diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..92bb565 --- /dev/null +++ b/debian/control @@ -0,0 +1 @@ +Build-Depends: libreichwein-dev, asound-dev, fmt-dev, boost-dev diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..629b92c --- /dev/null +++ b/main.cpp @@ -0,0 +1,78 @@ +#include "ClickStream.h" +#include "MIDI.h" +#include "PCM.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace std::string_literals; + +double diff_timespec(const struct timespec *time1, const struct timespec *time0) { + return (time1->tv_sec - time0->tv_sec) + + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; +} + +int main(void) +{ + try { + MIDI midi; + ClickStream stream; + PCM pcm{stream}; + + pcm.write(); + + midi.signal_click.connect([&](){stream.click();}); + + while (true) { + //std::cout << "Main loop entered." << std::endl; + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(midi.fd(), &read_set); + + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(pcm.fd(), &write_set); + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + int result = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout); + if (result < 0) { + throw std::runtime_error("select() failed"); + } else if (result == 0) { + throw std::runtime_error("select() timeout"); + } + + if (midi.event_ready()) + { + //std::cout << "read..." << std::endl; + auto event = midi.read(); + //std::cout << "process..." << std::endl; + midi.process(event); + } + + if (pcm.write_available()) { + //std::cout << "DEBUG: WRITE" << std::endl; + pcm.write(); + } + } + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; + } + + return 0; +} + -- cgit v1.2.3