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 --- PCM.h | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 PCM.h (limited to '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; +}; + -- cgit v1.2.3