diff options
-rw-r--r-- | gr-audio/lib/alsa/alsa_impl.h | 63 | ||||
-rw-r--r-- | gr-audio/lib/alsa/alsa_sink.cc | 104 | ||||
-rw-r--r-- | gr-audio/lib/alsa/alsa_sink.h | 22 | ||||
-rw-r--r-- | gr-audio/lib/alsa/alsa_source.cc | 101 | ||||
-rw-r--r-- | gr-audio/lib/alsa/alsa_source.h | 23 |
5 files changed, 171 insertions, 142 deletions
diff --git a/gr-audio/lib/alsa/alsa_impl.h b/gr-audio/lib/alsa/alsa_impl.h index 37099ce5ba..7ea0c451ce 100644 --- a/gr-audio/lib/alsa/alsa_impl.h +++ b/gr-audio/lib/alsa/alsa_impl.h @@ -24,5 +24,68 @@ bool gri_alsa_pick_acceptable_format(snd_pcm_t* pcm, const char* error_msg_tag, bool verbose); +namespace gr { +namespace audio { +namespace alsa_internal { +// hw/sw params RAII wrapper. +template <typename T, int Alloc(T**), void Free(T*)> +class param_wrap +{ +public: + param_wrap() + { + const int err = Alloc(&d_param); + if (err) { + throw std::runtime_error("failed to allocate ALSA params. Error code " + + std::to_string(err)); + } + } + param_wrap(const param_wrap&) = delete; + param_wrap& operator=(const param_wrap&) = delete; + T* get() { return d_param; } + ~param_wrap() { Free(d_param); } + +private: + T* d_param = nullptr; +}; +typedef param_wrap<snd_pcm_hw_params_t, snd_pcm_hw_params_malloc, snd_pcm_hw_params_free> + hwparam_wrap; +typedef param_wrap<snd_pcm_sw_params_t, snd_pcm_sw_params_malloc, snd_pcm_sw_params_free> + swparam_wrap; + +class sndpcm_wrap +{ +public: + sndpcm_wrap(snd_pcm_t* in = nullptr) : d_pcm_handle(in) {} + sndpcm_wrap(const sndpcm_wrap&) = delete; + sndpcm_wrap(sndpcm_wrap&&) = delete; + sndpcm_wrap& operator=(const sndpcm_wrap&) = delete; + sndpcm_wrap& operator=(sndpcm_wrap&&) = delete; + ~sndpcm_wrap() { close(); } + void close() + { + if (d_pcm_handle == nullptr) { + return; + } + if (snd_pcm_state(d_pcm_handle) == SND_PCM_STATE_RUNNING) { + snd_pcm_drop(d_pcm_handle); + } + snd_pcm_close(d_pcm_handle); + d_pcm_handle = nullptr; + } + void set(snd_pcm_t* ptr) + { + close(); + d_pcm_handle = ptr; + } + snd_pcm_t* get() noexcept { return d_pcm_handle; } + +private: + snd_pcm_t* d_pcm_handle; +}; + +} // namespace alsa_internal +} // namespace audio +} // namespace gr #endif /* INCLUDED_GRI_ALSA_H */ diff --git a/gr-audio/lib/alsa/alsa_sink.cc b/gr-audio/lib/alsa/alsa_sink.cc index f4e1cefb9e..d37673f215 100644 --- a/gr-audio/lib/alsa/alsa_sink.cc +++ b/gr-audio/lib/alsa/alsa_sink.cc @@ -65,18 +65,9 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t "audio_alsa_sink", io_signature::make(0, 0, 0), io_signature::make(0, 0, 0)), d_sampling_rate(sampling_rate), d_device_name(device_name.empty() ? default_device_name() : device_name), - d_pcm_handle(0), - d_hw_params((snd_pcm_hw_params_t*)(new char[snd_pcm_hw_params_sizeof()])), - d_sw_params((snd_pcm_sw_params_t*)(new char[snd_pcm_sw_params_sizeof()])), d_nperiods(default_nperiods()), d_period_time_us((unsigned int)(default_period_time() * 1e6)), - d_period_size(0), - d_buffer_size_bytes(0), - d_buffer(0), - d_worker(0), d_special_case_mono_to_stereo(false), - d_nunderuns(0), - d_nsuspends(0), d_ok_to_block(ok_to_block) { CHATTY_DEBUG = prefs::singleton()->get_bool("audio_alsa", "verbose", false); @@ -87,14 +78,15 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t // open the device for playback int attempts = 10; while ((error != 0) && (attempts-- > 0)) { - error = snd_pcm_open( - &d_pcm_handle, d_device_name.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + snd_pcm_t* t = nullptr; + error = snd_pcm_open(&t, d_device_name.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + d_pcm_handle.set(t); if (error < 0) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); } } if (ok_to_block == false) - snd_pcm_nonblock(d_pcm_handle, 1); + snd_pcm_nonblock(d_pcm_handle.get(), 1); if (error < 0) { GR_LOG_ERROR(d_logger, boost::format("[%1%]: %2%") % (d_device_name) % @@ -103,17 +95,17 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t } // Fill params with a full configuration space for a PCM. - error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); + error = snd_pcm_hw_params_any(d_pcm_handle.get(), d_hw_params.get()); if (error < 0) bail("broken configuration for playback", error); if (CHATTY_DEBUG) - gri_alsa_dump_hw_params(d_pcm_handle, d_hw_params, stdout); + gri_alsa_dump_hw_params(d_pcm_handle.get(), d_hw_params.get(), stdout); // now that we know how many channels the h/w can handle, set input signature unsigned int umin_chan, umax_chan; - snd_pcm_hw_params_get_channels_min(d_hw_params, &umin_chan); - snd_pcm_hw_params_get_channels_max(d_hw_params, &umax_chan); + snd_pcm_hw_params_get_channels_min(d_hw_params.get(), &umin_chan); + snd_pcm_hw_params_get_channels_max(d_hw_params.get(), &umax_chan); int min_chan = std::min(umin_chan, 1000U); int max_chan = std::min(umax_chan, 1000U); @@ -138,12 +130,12 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t // snd_pcm_access_mask_set(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); if ((error = snd_pcm_hw_params_set_access_mask( - d_pcm_handle, d_hw_params, access_mask)) < 0) + d_pcm_handle.get(), d_hw_params.get(), access_mask)) < 0) bail("failed to set access mask", error); // set sample format - if (!gri_alsa_pick_acceptable_format(d_pcm_handle, - d_hw_params, + if (!gri_alsa_pick_acceptable_format(d_pcm_handle.get(), + d_hw_params.get(), acceptable_formats, NELEMS(acceptable_formats), &d_format, @@ -154,14 +146,14 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t // sampling rate unsigned int orig_sampling_rate = d_sampling_rate; if ((error = snd_pcm_hw_params_set_rate_near( - d_pcm_handle, d_hw_params, &d_sampling_rate, 0)) < 0) + d_pcm_handle.get(), d_hw_params.get(), &d_sampling_rate, 0)) < 0) bail("failed to set rate near", error); if (orig_sampling_rate != d_sampling_rate) { GR_LOG_INFO(d_logger, boost::format("[%1%]: unable to support sampling rate %2%\n\tCard " "requested %3% instead.") % - snd_pcm_name(d_pcm_handle) % orig_sampling_rate % + snd_pcm_name(d_pcm_handle.get()) % orig_sampling_rate % d_sampling_rate); } @@ -172,8 +164,8 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t * period in units of time (typically 1ms). */ unsigned int min_nperiods, max_nperiods; - snd_pcm_hw_params_get_periods_min(d_hw_params, &min_nperiods, &dir); - snd_pcm_hw_params_get_periods_max(d_hw_params, &max_nperiods, &dir); + snd_pcm_hw_params_get_periods_min(d_hw_params.get(), &min_nperiods, &dir); + snd_pcm_hw_params_get_periods_max(d_hw_params.get(), &max_nperiods, &dir); unsigned int orig_nperiods = d_nperiods; d_nperiods = std::min(std::max(min_nperiods, d_nperiods), max_nperiods); @@ -181,18 +173,19 @@ alsa_sink::alsa_sink(int sampling_rate, const std::string device_name, bool ok_t // adjust period time so that total buffering remains more-or-less constant d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; - error = snd_pcm_hw_params_set_periods(d_pcm_handle, d_hw_params, d_nperiods, 0); + error = snd_pcm_hw_params_set_periods( + d_pcm_handle.get(), d_hw_params.get(), d_nperiods, 0); if (error < 0) bail("set_periods failed", error); dir = 0; error = snd_pcm_hw_params_set_period_time_near( - d_pcm_handle, d_hw_params, &d_period_time_us, &dir); + d_pcm_handle.get(), d_hw_params.get(), &d_period_time_us, &dir); if (error < 0) bail("set_period_time_near failed", error); dir = 0; - error = snd_pcm_hw_params_get_period_size(d_hw_params, &d_period_size, &dir); + error = snd_pcm_hw_params_get_period_size(d_hw_params.get(), &d_period_size, &dir); if (error < 0) bail("get_period_size failed", error); @@ -210,18 +203,18 @@ bool alsa_sink::check_topology(int ninputs, int noutputs) // Check the state of the stream // Ensure that the pcm is in a state where we can still mess with the hw_params snd_pcm_state_t state; - state = snd_pcm_state(d_pcm_handle); + state = snd_pcm_state(d_pcm_handle.get()); if (state == SND_PCM_STATE_RUNNING) return true; // If stream is running, don't change any parameters else if (state == SND_PCM_STATE_XRUN) snd_pcm_prepare( - d_pcm_handle); // Prepare stream on underrun, and we can set parameters; + d_pcm_handle.get()); // Prepare stream on underrun, and we can set parameters; bool special_case = nchan == 1 && d_special_case_mono_to_stereo; if (special_case) nchan = 2; - err = snd_pcm_hw_params_set_channels(d_pcm_handle, d_hw_params, nchan); + err = snd_pcm_hw_params_set_channels(d_pcm_handle.get(), d_hw_params.get(), nchan); if (err < 0) { output_error_msg("set_channels failed", err); @@ -229,14 +222,14 @@ bool alsa_sink::check_topology(int ninputs, int noutputs) } // set the parameters into the driver... - err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); + err = snd_pcm_hw_params(d_pcm_handle.get(), d_hw_params.get()); if (err < 0) { output_error_msg("snd_pcm_hw_params failed", err); return false; } // get current s/w params - err = snd_pcm_sw_params_current(d_pcm_handle, d_sw_params); + err = snd_pcm_sw_params_current(d_pcm_handle.get(), d_sw_params.get()); if (err < 0) bail("snd_pcm_sw_params_current", err); @@ -244,24 +237,22 @@ bool alsa_sink::check_topology(int ninputs, int noutputs) // it's buffers half way full. This helps avoid audio underruns. err = snd_pcm_sw_params_set_start_threshold( - d_pcm_handle, d_sw_params, d_nperiods * d_period_size / 2); + d_pcm_handle.get(), d_sw_params.get(), d_nperiods * d_period_size / 2); if (err < 0) bail("snd_pcm_sw_params_set_start_threshold", err); // store the s/w params - err = snd_pcm_sw_params(d_pcm_handle, d_sw_params); + err = snd_pcm_sw_params(d_pcm_handle.get(), d_sw_params.get()); if (err < 0) bail("snd_pcm_sw_params", err); - d_buffer_size_bytes = d_period_size * nchan * snd_pcm_format_size(d_format, 1); - - d_buffer = new char[d_buffer_size_bytes]; + d_buffer.resize(d_period_size * nchan * snd_pcm_format_size(d_format, 1)); if (CHATTY_DEBUG) { GR_LOG_DEBUG(d_debug_logger, boost::format("[%1%]: sample resolution = %2% bits") % - snd_pcm_name(d_pcm_handle) % - snd_pcm_hw_params_get_sbits(d_hw_params)); + snd_pcm_name(d_pcm_handle.get()) % + snd_pcm_hw_params_get_sbits(d_hw_params.get())); } switch (d_format) { @@ -285,16 +276,7 @@ bool alsa_sink::check_topology(int ninputs, int noutputs) return true; } -alsa_sink::~alsa_sink() -{ - if (snd_pcm_state(d_pcm_handle) == SND_PCM_STATE_RUNNING) - snd_pcm_drop(d_pcm_handle); - - snd_pcm_close(d_pcm_handle); - delete[]((char*)d_hw_params); - delete[]((char*)d_sw_params); - delete[] d_buffer; -} +alsa_sink::~alsa_sink() {} int alsa_sink::work(int noutput_items, gr_vector_const_void_star& input_items, @@ -318,12 +300,12 @@ int alsa_sink::work_s16(int noutput_items, unsigned int nchan = input_items.size(); const float** in = (const float**)&input_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; int n; unsigned int sizeof_frame = nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); for (n = 0; n < noutput_items; n += d_period_size) { // process one period of data @@ -357,12 +339,12 @@ int alsa_sink::work_s32(int noutput_items, unsigned int nchan = input_items.size(); const float** in = (const float**)&input_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; int n; unsigned int sizeof_frame = nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); for (n = 0; n < noutput_items; n += d_period_size) { // process one period of data @@ -398,12 +380,12 @@ int alsa_sink::work_s16_1x2(int noutput_items, assert(input_items.size() == 1); static const unsigned int nchan = 2; const float** in = (const float**)&input_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; int n; unsigned int sizeof_frame = nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); for (n = 0; n < noutput_items; n += d_period_size) { // process one period of data @@ -438,12 +420,12 @@ int alsa_sink::work_s32_1x2(int noutput_items, assert(input_items.size() == 1); static unsigned int nchan = 2; const float** in = (const float**)&input_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; int n; unsigned int sizeof_frame = nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); for (n = 0; n < noutput_items; n += d_period_size) { // process one period of data @@ -469,7 +451,7 @@ bool alsa_sink::write_buffer(const void* vbuffer, unsigned nframes, unsigned siz const unsigned char* buffer = (const unsigned char*)vbuffer; while (nframes > 0) { - int r = snd_pcm_writei(d_pcm_handle, buffer, nframes); + int r = snd_pcm_writei(d_pcm_handle.get(), buffer, nframes); if (r == -EAGAIN) { if (d_ok_to_block == true) continue; // try again @@ -479,7 +461,7 @@ bool alsa_sink::write_buffer(const void* vbuffer, unsigned nframes, unsigned siz // we need to have an lvalue, async pitfall! auto future_local = std::async(::fputs, "aU", stderr); - if ((r = snd_pcm_prepare(d_pcm_handle)) < 0) { + if ((r = snd_pcm_prepare(d_pcm_handle.get())) < 0) { output_error_msg("snd_pcm_prepare failed. Can't recover from underrun", r); return false; @@ -490,7 +472,7 @@ bool alsa_sink::write_buffer(const void* vbuffer, unsigned nframes, unsigned siz else if (r == -ESTRPIPE) { // h/w is suspended (whatever that means) // This is apparently related to power management d_nsuspends++; - if ((r = snd_pcm_resume(d_pcm_handle)) < 0) { + if ((r = snd_pcm_resume(d_pcm_handle.get())) < 0) { output_error_msg("failed to resume from suspend", r); return false; } @@ -512,8 +494,8 @@ bool alsa_sink::write_buffer(const void* vbuffer, unsigned nframes, unsigned siz void alsa_sink::output_error_msg(const char* msg, int err) { GR_LOG_ERROR(d_logger, - boost::format("[%1%]: %2%: %3%") % snd_pcm_name(d_pcm_handle) % msg % - snd_strerror(err)); + boost::format("[%1%]: %2%: %3%") % snd_pcm_name(d_pcm_handle.get()) % + msg % snd_strerror(err)); } void alsa_sink::bail(const char* msg, int err) diff --git a/gr-audio/lib/alsa/alsa_sink.h b/gr-audio/lib/alsa/alsa_sink.h index b931112b52..818f0f2273 100644 --- a/gr-audio/lib/alsa/alsa_sink.h +++ b/gr-audio/lib/alsa/alsa_sink.h @@ -15,6 +15,7 @@ #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API +#include "alsa_impl.h" #include <gnuradio/audio/sink.h> #include <alsa/asoundlib.h> #include <stdexcept> @@ -41,22 +42,21 @@ class alsa_sink : public sink unsigned int d_sampling_rate; std::string d_device_name; - snd_pcm_t* d_pcm_handle; - snd_pcm_hw_params_t* d_hw_params; - snd_pcm_sw_params_t* d_sw_params; + alsa_internal::sndpcm_wrap d_pcm_handle; + alsa_internal::hwparam_wrap d_hw_params; + alsa_internal::swparam_wrap d_sw_params; snd_pcm_format_t d_format; unsigned int d_nperiods; - unsigned int d_period_time_us; // microseconds - snd_pcm_uframes_t d_period_size; // in frames - unsigned int d_buffer_size_bytes; // sizeof of d_buffer - char* d_buffer; - work_t d_worker; // the work method to use + unsigned int d_period_time_us; // microseconds + snd_pcm_uframes_t d_period_size = 0; // in frames + std::vector<char> d_buffer; + work_t d_worker = 0; // the work method to use bool d_special_case_mono_to_stereo; // random stats - int d_nunderuns; // count of underruns - int d_nsuspends; // count of suspends - bool d_ok_to_block; // defaults to "true", controls blocking/non-block I/O + int d_nunderuns = 0; // count of underruns + int d_nsuspends = 0; // count of suspends + bool d_ok_to_block; // defaults to "true", controls blocking/non-block I/O void output_error_msg(const char* msg, int err); void bail(const char* msg, int err); diff --git a/gr-audio/lib/alsa/alsa_source.cc b/gr-audio/lib/alsa/alsa_source.cc index 6edf2c4b67..dc7863f39c 100644 --- a/gr-audio/lib/alsa/alsa_source.cc +++ b/gr-audio/lib/alsa/alsa_source.cc @@ -66,19 +66,9 @@ alsa_source::alsa_source(int sampling_rate, "audio_alsa_source", io_signature::make(0, 0, 0), io_signature::make(0, 0, 0)), d_sampling_rate(sampling_rate), d_device_name(device_name.empty() ? default_device_name() : device_name), - d_pcm_handle(0), - d_hw_params((snd_pcm_hw_params_t*)(new char[snd_pcm_hw_params_sizeof()])), - d_sw_params((snd_pcm_sw_params_t*)(new char[snd_pcm_sw_params_sizeof()])), d_nperiods(default_nperiods()), d_period_time_us((unsigned int)(default_period_time() * 1e6)), - d_period_size(0), - d_buffer_size_bytes(0), - d_buffer(0), - d_worker(0), - d_hw_nchan(0), - d_special_case_stereo_to_mono(false), - d_noverruns(0), - d_nsuspends(0) + d_special_case_stereo_to_mono(false) { CHATTY_DEBUG = prefs::singleton()->get_bool("audio_alsa", "verbose", false); @@ -86,7 +76,9 @@ alsa_source::alsa_source(int sampling_rate, int dir; // open the device for capture - error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str(), SND_PCM_STREAM_CAPTURE, 0); + snd_pcm_t* t = nullptr; + error = snd_pcm_open(&t, d_device_name.c_str(), SND_PCM_STREAM_CAPTURE, 0); + d_pcm_handle.set(t); if (error < 0) { GR_LOG_ERROR(d_logger, boost::format("[%1%]: %2%") % (d_device_name) % @@ -95,18 +87,18 @@ alsa_source::alsa_source(int sampling_rate, } // Fill params with a full configuration space for a PCM. - error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); + error = snd_pcm_hw_params_any(d_pcm_handle.get(), d_hw_params.get()); if (error < 0) bail("broken configuration for playback", error); if (CHATTY_DEBUG) - gri_alsa_dump_hw_params(d_pcm_handle, d_hw_params, stdout); + gri_alsa_dump_hw_params(d_pcm_handle.get(), d_hw_params.get(), stdout); // now that we know how many channels the h/w can handle, set output signature unsigned int umax_chan; unsigned int umin_chan; - snd_pcm_hw_params_get_channels_min(d_hw_params, &umin_chan); - snd_pcm_hw_params_get_channels_max(d_hw_params, &umax_chan); + snd_pcm_hw_params_get_channels_min(d_hw_params.get(), &umin_chan); + snd_pcm_hw_params_get_channels_max(d_hw_params.get(), &umax_chan); int min_chan = std::min(umin_chan, 1000U); int max_chan = std::min(umax_chan, 1000U); @@ -132,12 +124,12 @@ alsa_source::alsa_source(int sampling_rate, // snd_pcm_access_mask_set(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); if ((error = snd_pcm_hw_params_set_access_mask( - d_pcm_handle, d_hw_params, access_mask)) < 0) + d_pcm_handle.get(), d_hw_params.get(), access_mask)) < 0) bail("failed to set access mask", error); // set sample format - if (!gri_alsa_pick_acceptable_format(d_pcm_handle, - d_hw_params, + if (!gri_alsa_pick_acceptable_format(d_pcm_handle.get(), + d_hw_params.get(), acceptable_formats, NELEMS(acceptable_formats), &d_format, @@ -148,14 +140,14 @@ alsa_source::alsa_source(int sampling_rate, // sampling rate unsigned int orig_sampling_rate = d_sampling_rate; if ((error = snd_pcm_hw_params_set_rate_near( - d_pcm_handle, d_hw_params, &d_sampling_rate, 0)) < 0) + d_pcm_handle.get(), d_hw_params.get(), &d_sampling_rate, 0)) < 0) bail("failed to set rate near", error); if (orig_sampling_rate != d_sampling_rate) { GR_LOG_INFO(d_logger, boost::format("[%1%]: unable to support sampling rate %2%\n\tCard " "requested %3% instead.") % - snd_pcm_name(d_pcm_handle) % orig_sampling_rate % + snd_pcm_name(d_pcm_handle.get()) % orig_sampling_rate % d_sampling_rate); } @@ -166,8 +158,8 @@ alsa_source::alsa_source(int sampling_rate, * period in units of time (typically 1ms). */ unsigned int min_nperiods, max_nperiods; - snd_pcm_hw_params_get_periods_min(d_hw_params, &min_nperiods, &dir); - snd_pcm_hw_params_get_periods_max(d_hw_params, &max_nperiods, &dir); + snd_pcm_hw_params_get_periods_min(d_hw_params.get(), &min_nperiods, &dir); + snd_pcm_hw_params_get_periods_max(d_hw_params.get(), &max_nperiods, &dir); unsigned int orig_nperiods = d_nperiods; d_nperiods = std::min(std::max(min_nperiods, d_nperiods), max_nperiods); @@ -175,24 +167,27 @@ alsa_source::alsa_source(int sampling_rate, // adjust period time so that total buffering remains more-or-less constant d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; - error = snd_pcm_hw_params_set_periods(d_pcm_handle, d_hw_params, d_nperiods, 0); + error = snd_pcm_hw_params_set_periods( + d_pcm_handle.get(), d_hw_params.get(), d_nperiods, 0); if (error < 0) bail("set_periods failed", error); dir = 0; error = snd_pcm_hw_params_set_period_time_near( - d_pcm_handle, d_hw_params, &d_period_time_us, &dir); + d_pcm_handle.get(), d_hw_params.get(), &d_period_time_us, &dir); if (error < 0) bail("set_period_time_near failed", error); dir = 0; - error = snd_pcm_hw_params_get_period_size(d_hw_params, &d_period_size, &dir); + error = snd_pcm_hw_params_get_period_size(d_hw_params.get(), &d_period_size, &dir); if (error < 0) bail("get_period_size failed", error); set_output_multiple(d_period_size); } +alsa_source::~alsa_source() {} + bool alsa_source::check_topology(int ninputs, int noutputs) { // noutputs is how many channels the user has connected. @@ -204,40 +199,39 @@ bool alsa_source::check_topology(int ninputs, int noutputs) // Check the state of the stream // Ensure that the pcm is in a state where we can still mess with the hw_params snd_pcm_state_t state; - state = snd_pcm_state(d_pcm_handle); + state = snd_pcm_state(d_pcm_handle.get()); if (state == SND_PCM_STATE_RUNNING) return true; // If stream is running, don't change any parameters else if (state == SND_PCM_STATE_XRUN) snd_pcm_prepare( - d_pcm_handle); // Prepare stream on underrun, and we can set parameters; + d_pcm_handle.get()); // Prepare stream on underrun, and we can set parameters; bool special_case = nchan == 1 && d_special_case_stereo_to_mono; if (special_case) nchan = 2; d_hw_nchan = nchan; - err = snd_pcm_hw_params_set_channels(d_pcm_handle, d_hw_params, d_hw_nchan); + err = + snd_pcm_hw_params_set_channels(d_pcm_handle.get(), d_hw_params.get(), d_hw_nchan); if (err < 0) { output_error_msg("set_channels failed", err); return false; } // set the parameters into the driver... - err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); + err = snd_pcm_hw_params(d_pcm_handle.get(), d_hw_params.get()); if (err < 0) { output_error_msg("snd_pcm_hw_params failed", err); return false; } - d_buffer_size_bytes = d_period_size * d_hw_nchan * snd_pcm_format_size(d_format, 1); - - d_buffer = new char[d_buffer_size_bytes]; + d_buffer.resize(d_period_size * d_hw_nchan * snd_pcm_format_size(d_format, 1)); if (CHATTY_DEBUG) { GR_LOG_DEBUG(d_logger, boost::format("[%1%]: sample resolution = %2% bits") % - snd_pcm_name(d_pcm_handle) % - snd_pcm_hw_params_get_sbits(d_hw_params)); + snd_pcm_name(d_pcm_handle.get()) % + snd_pcm_hw_params_get_sbits(d_hw_params.get())); } switch (d_format) { @@ -262,17 +256,6 @@ bool alsa_source::check_topology(int ninputs, int noutputs) return true; } -alsa_source::~alsa_source() -{ - if (snd_pcm_state(d_pcm_handle) == SND_PCM_STATE_RUNNING) - snd_pcm_drop(d_pcm_handle); - - snd_pcm_close(d_pcm_handle); - delete[]((char*)d_hw_params); - delete[]((char*)d_sw_params); - delete[] d_buffer; -} - int alsa_source::work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items) @@ -296,11 +279,11 @@ int alsa_source::work_s16(int noutput_items, unsigned int nchan = output_items.size(); float** out = (float**)&output_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); // To minimize latency, return at most a single period's worth of samples. // [We could also read the first one in a blocking mode and subsequent @@ -332,13 +315,13 @@ int alsa_source::work_s16_2x1(int noutput_items, static const float scale_factor = 1.0 / std::pow(2.0f, 16 - 1); float** out = (float**)&output_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; assert(output_items.size() == 1); unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); // To minimize latency, return at most a single period's worth of samples. // [We could also read the first one in a blocking mode and subsequent @@ -369,11 +352,11 @@ int alsa_source::work_s32(int noutput_items, unsigned int nchan = output_items.size(); float** out = (float**)&output_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); // To minimize latency, return at most a single period's worth of samples. // [We could also read the first one in a blocking mode and subsequent @@ -405,13 +388,13 @@ int alsa_source::work_s32_2x1(int noutput_items, static const float scale_factor = 1.0 / std::pow(2.0f, 32 - 1); float** out = (float**)&output_items[0]; - sample_t* buf = (sample_t*)d_buffer; + sample_t* buf = reinterpret_cast<sample_t*>(d_buffer.data()); int bi; assert(output_items.size() == 1); unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); - assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + assert(d_buffer.size() == d_period_size * sizeof_frame); // To minimize latency, return at most a single period's worth of samples. // [We could also read the first one in a blocking mode and subsequent @@ -436,14 +419,14 @@ bool alsa_source::read_buffer(void* vbuffer, unsigned nframes, unsigned sizeof_f unsigned char* buffer = (unsigned char*)vbuffer; while (nframes > 0) { - int r = snd_pcm_readi(d_pcm_handle, buffer, nframes); + int r = snd_pcm_readi(d_pcm_handle.get(), buffer, nframes); if (r == -EAGAIN) continue; // try again else if (r == -EPIPE) { // overrun d_noverruns++; fputs("aO", stderr); - if ((r = snd_pcm_prepare(d_pcm_handle)) < 0) { + if ((r = snd_pcm_prepare(d_pcm_handle.get())) < 0) { output_error_msg("snd_pcm_prepare failed. Can't recover from overrun", r); return false; } @@ -453,7 +436,7 @@ bool alsa_source::read_buffer(void* vbuffer, unsigned nframes, unsigned sizeof_f else if (r == -ESTRPIPE) { // h/w is suspended (whatever that means) // This is apparently related to power management d_nsuspends++; - if ((r = snd_pcm_resume(d_pcm_handle)) < 0) { + if ((r = snd_pcm_resume(d_pcm_handle.get())) < 0) { output_error_msg("failed to resume from suspend", r); return false; } @@ -475,8 +458,8 @@ bool alsa_source::read_buffer(void* vbuffer, unsigned nframes, unsigned sizeof_f void alsa_source::output_error_msg(const char* msg, int err) { GR_LOG_ERROR(d_logger, - boost::format("[%1%]: %2%: %3%") % snd_pcm_name(d_pcm_handle) % msg % - snd_strerror(err)); + boost::format("[%1%]: %2%: %3%") % snd_pcm_name(d_pcm_handle.get()) % + msg % snd_strerror(err)); } void alsa_source::bail(const char* msg, int err) diff --git a/gr-audio/lib/alsa/alsa_source.h b/gr-audio/lib/alsa/alsa_source.h index 34d1a4f3bf..cfda8e7638 100644 --- a/gr-audio/lib/alsa/alsa_source.h +++ b/gr-audio/lib/alsa/alsa_source.h @@ -15,10 +15,12 @@ #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API +#include "alsa_impl.h" #include <gnuradio/audio/source.h> #include <alsa/asoundlib.h> #include <stdexcept> #include <string> +#include <vector> namespace gr { namespace audio { @@ -44,22 +46,21 @@ class alsa_source : public source unsigned int d_sampling_rate; std::string d_device_name; - snd_pcm_t* d_pcm_handle; - snd_pcm_hw_params_t* d_hw_params; - snd_pcm_sw_params_t* d_sw_params; + alsa_internal::sndpcm_wrap d_pcm_handle; + alsa_internal::hwparam_wrap d_hw_params; snd_pcm_format_t d_format; unsigned int d_nperiods; - unsigned int d_period_time_us; // microseconds - snd_pcm_uframes_t d_period_size; // in frames - unsigned int d_buffer_size_bytes; // sizeof of d_buffer - char* d_buffer; - work_t d_worker; // the work method to use - unsigned int d_hw_nchan; // # of configured h/w channels + unsigned int d_period_time_us; // microseconds + snd_pcm_uframes_t d_period_size = 0; // in frames + unsigned int d_buffer_size_bytes = 0; // sizeof of d_buffer + std::vector<char> d_buffer; + work_t d_worker = 0; // the work method to use + unsigned int d_hw_nchan = 0; // # of configured h/w channels bool d_special_case_stereo_to_mono; // random stats - int d_noverruns; // count of overruns - int d_nsuspends; // count of suspends + int d_noverruns = 0; // count of overruns + int d_nsuspends = 0; // count of suspends void output_error_msg(const char* msg, int err); void bail(const char* msg, int err); |