diff options
author | Roland Reichwein <mail@reichwein.it> | 2025-01-03 10:31:38 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2025-01-03 10:31:38 +0100 |
commit | 4af400141af0c97c4e4bcd47acf78107a17eafbe (patch) | |
tree | cd919376466679f4177d68cf6c14bdf9a0f80595 /alsa.cpp | |
parent | 7b9e5cb03b530458b7f9df1fee678e2ba3a746da (diff) |
Separated class files
Diffstat (limited to 'alsa.cpp')
-rw-r--r-- | alsa.cpp | 410 |
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; -} - |