diff options
Diffstat (limited to 'PCM.h')
-rw-r--r-- | PCM.h | 134 |
1 files changed, 134 insertions, 0 deletions
@@ -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; +}; + |