summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ClickStream.cpp3
-rw-r--r--ClickStream.h57
-rw-r--r--MIDI.cpp2
-rw-r--r--MIDI.h169
-rw-r--r--Makefile17
-rw-r--r--PCM.cpp1
-rw-r--r--PCM.h134
-rw-r--r--alsa.cpp410
-rw-r--r--config.cpp4
-rw-r--r--config.h11
-rw-r--r--debian/control1
-rw-r--r--main.cpp78
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"
+
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 <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;
+};
+
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 <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;
+}
+