#ifndef ALSA_HPP_INCLUDED #define ALSA_HPP_INCLUDED /** * @file Experimental low level C++ API to ALSA. * @licence GNU LGPL 2.1 or later **/ #include #include namespace alsa { namespace util { /** * @short FOR INTERNAL USE ONLY. A utility class similar to * boost::noncopyable, duplicated here in order to avoid * a dependency on the Boost library. **/ class noncopyable { protected: noncopyable() {} ~noncopyable() {} private: noncopyable(noncopyable const&); noncopyable const& operator=(noncopyable const&); }; } /** * @short A minimal RAII wrapper for ALSA PCM. * Automatically converts into snd_pcm_t* as needed, so the ALSA C API * can be used directly with this. **/ class PCM: util::noncopyable { snd_pcm_t* handle; public: PCM(char const* device = "default", snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK, int mode = 0) { if (snd_pcm_open(&handle, device, stream, mode) < 0) throw std::runtime_error(std::string("Cannot open ALSA device ") + device); } ~PCM() { snd_pcm_close(handle); } operator snd_pcm_t*() { return handle; } }; /** * @short A RAII wrapper for ALSA HWParams structure. * Automatically converts into snd_pcm_hw_params_t* as needed, so the * ALSA C API can still be used directly with this. * Note: contents need to be loaded before the struct can be used. **/ class HWParams { snd_pcm_hw_params_t* handle; void init() { if (snd_pcm_hw_params_malloc(&handle) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_malloc failed"); } public: HWParams() { init(); } ~HWParams() { snd_pcm_hw_params_free(handle); } HWParams(HWParams const& orig) { init(); *this = orig; } HWParams& operator=(HWParams const& orig) { if (this != &orig) { snd_pcm_hw_params_copy(handle, orig.handle); } return *this; } operator snd_pcm_hw_params_t*() { return handle; } }; /** * @short A helper object for modifying HWParams of a PCM. * * Normally only temporary objects should be created. A typical use case: * alsa::PCM alsa_pcm; // Create a PCM object * alsa::HWConfig(alsa_pcm) // Create a new config space * .set(SND_PCM_ACCESS_RW_INTERLEAVED) * .set(SND_PCM_FORMAT_S16_LE) * .rate_near(rate) * .channels_near(channels) * .period_time_near(period) * .commit(); // Apply the config to the PCM * Note: in case of failure, an exception is thrown. When this happens, * the commit part never gets executed and thus this entire statement * will become void as if it was never executed in the first place. * * If you need to use the C snd_pcm_hw_params_* functions, you may use a * block like this: * { * alsa::HWConfig hw(alsa_pcm); * hw.foo(); * snd_pcm_hw_params_set_period_size(alsa_pcm, hw, 1024, 0); * hw.bar().commit(); * } **/ class HWConfig: util::noncopyable { HWParams params; PCM& pcm; public: HWConfig(PCM& pcm): pcm(pcm) { any(); } operator HWParams&() { return params; } void commit() { if (snd_pcm_hw_params(pcm, params) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params failed"); } HWConfig& any() { if (snd_pcm_hw_params_any(pcm, params) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_any failed"); return *this; } HWConfig& current() { if (snd_pcm_hw_params_current(pcm, params) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_current failed"); return *this; } HWConfig& set(snd_pcm_access_t access) { if (snd_pcm_hw_params_set_access(pcm, params, access) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_access failed"); return *this; } HWConfig& set(snd_pcm_format_t format) { if (snd_pcm_hw_params_set_format(pcm, params, format) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_format failed"); return *this; } HWConfig& rate(unsigned int rate) { if (snd_pcm_hw_params_set_rate(pcm, params, rate, NULL) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_rate failed"); return *this; } HWConfig& rate_near(unsigned int& rate) { if (snd_pcm_hw_params_set_rate_near(pcm, params, &rate, NULL) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_rate_near failed"); return *this; } HWConfig& channels(unsigned int num) { if (snd_pcm_hw_params_set_channels(pcm, params, num) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_channels failed"); return *this; } HWConfig& channels_near(unsigned int& num) { if (snd_pcm_hw_params_set_channels_near(pcm, params, &num) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_channels_near failed"); return *this; } HWConfig& period_time(unsigned int samples) { if (snd_pcm_hw_params_set_period_time(pcm, params, samples, NULL) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_period_time failed"); return *this; } HWConfig& period_time_near(unsigned int& us) { if (snd_pcm_hw_params_set_period_time_near(pcm, params, &us, NULL) < 0) throw std::runtime_error("ALSA snd_pcm_hw_params_set_period_time_near failed"); return *this; } }; } #endif