summaryrefslogtreecommitdiffhomepage
path: root/alsa.cpp
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2025-01-03 10:31:38 +0100
committerRoland Reichwein <mail@reichwein.it>2025-01-03 10:31:38 +0100
commit4af400141af0c97c4e4bcd47acf78107a17eafbe (patch)
treecd919376466679f4177d68cf6c14bdf9a0f80595 /alsa.cpp
parent7b9e5cb03b530458b7f9df1fee678e2ba3a746da (diff)
Separated class files
Diffstat (limited to 'alsa.cpp')
-rw-r--r--alsa.cpp410
1 files changed, 0 insertions, 410 deletions
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;
-}
-