diff options
-rw-r--r-- | ClickStream.cpp | 3 | ||||
-rw-r--r-- | ClickStream.h | 57 | ||||
-rw-r--r-- | MIDI.cpp | 2 | ||||
-rw-r--r-- | MIDI.h | 169 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | PCM.cpp | 1 | ||||
-rw-r--r-- | PCM.h | 134 | ||||
-rw-r--r-- | alsa.cpp | 410 | ||||
-rw-r--r-- | config.cpp | 4 | ||||
-rw-r--r-- | config.h | 11 | ||||
-rw-r--r-- | debian/control | 1 | ||||
-rw-r--r-- | main.cpp | 78 |
12 files changed, 473 insertions, 414 deletions
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 <libreichwein/file.h> + +#include <cstring> +#include <string> +#include <vector> + +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<uint16_t> 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" + @@ -0,0 +1,169 @@ +#pragma once + +#include "config.h" + +#include <boost/signals2.hpp> + +#include <iostream> + +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<void()> 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; +}; + @@ -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 @@ -0,0 +1 @@ +#include "PCM.h" @@ -0,0 +1,134 @@ +#pragma once + +#include "ClickStream.h" + +#include "config.h" + +#include <alsa/asoundlib.h> + +#include <iostream> +#include <string> + +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 <libreichwein/file.h> - -#include <alsa/asoundlib.h> - -#include <cstdint> -#include <cmath> -#include <iostream> -#include <limits> -#include <exception> -#include <stdexcept> - -#include <stdio.h> -#include <sys/types.h> -#include <sys/time.h> -#include <time.h> - -#include <boost/signals2.hpp> - -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<uint16_t> 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<void()> 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 <alsa/asoundlib.h> + +// 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 <cstdint> +#include <cmath> +#include <iostream> +#include <limits> +#include <exception> +#include <stdexcept> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> + +#include <boost/signals2.hpp> + +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; +} + |