Revision 6d1d64eb
| b/gr-audio/lib/Makefile.am | ||
|---|---|---|
| 47 | 47 |
######################################################################## |
| 48 | 48 |
if GR_AUDIO_ALSA_SUPPORT |
| 49 | 49 |
|
| 50 |
AM_CPPFLAGS += $(ALSA_CPPFLAGS) |
|
| 50 |
AM_CPPFLAGS += \ |
|
| 51 |
-I$(srcdir)/alsa \ |
|
| 52 |
$(ALSA_CPPFLAGS) |
|
| 51 | 53 |
|
| 52 | 54 |
libgnuradio_audio_la_LIBADD += $(ALSA_LIBS) |
| 53 | 55 |
|
| 54 | 56 |
libgnuradio_audio_la_SOURCES += \ |
| 55 |
gri_alsa.cc \ |
|
| 56 |
audio_alsa_source.cc \ |
|
| 57 |
audio_alsa_sink.cc |
|
| 57 |
alsa/gri_alsa.cc \
|
|
| 58 |
alsa/audio_alsa_source.cc \
|
|
| 59 |
alsa/audio_alsa_sink.cc
|
|
| 58 | 60 |
|
| 59 | 61 |
noinst_HEADERS += \ |
| 60 |
gri_alsa.h \ |
|
| 61 |
audio_alsa_source.h \ |
|
| 62 |
audio_alsa_sink.h |
|
| 62 |
alsa/gri_alsa.h \
|
|
| 63 |
alsa/audio_alsa_source.h \
|
|
| 64 |
alsa/audio_alsa_sink.h
|
|
| 63 | 65 |
|
| 64 |
dist_etc_DATA += gr-audio-alsa.conf |
|
| 66 |
dist_etc_DATA += alsa/gr-audio-alsa.conf
|
|
| 65 | 67 |
|
| 66 | 68 |
endif |
| b/gr-audio/lib/alsa/audio_alsa_sink.cc | ||
|---|---|---|
| 1 |
/* -*- c++ -*- */ |
|
| 2 |
/* |
|
| 3 |
* Copyright 2004-2011 Free Software Foundation, Inc. |
|
| 4 |
* |
|
| 5 |
* This file is part of GNU Radio |
|
| 6 |
* |
|
| 7 |
* GNU Radio is free software; you can redistribute it and/or modify |
|
| 8 |
* it under the terms of the GNU General Public License as published by |
|
| 9 |
* the Free Software Foundation; either version 3, or (at your option) |
|
| 10 |
* any later version. |
|
| 11 |
* |
|
| 12 |
* GNU Radio is distributed in the hope that it will be useful, |
|
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 |
* GNU General Public License for more details. |
|
| 16 |
* |
|
| 17 |
* You should have received a copy of the GNU General Public License |
|
| 18 |
* along with GNU Radio; see the file COPYING. If not, write to |
|
| 19 |
* the Free Software Foundation, Inc., 51 Franklin Street, |
|
| 20 |
* Boston, MA 02110-1301, USA. |
|
| 21 |
*/ |
|
| 22 |
|
|
| 23 |
#ifdef HAVE_CONFIG_H |
|
| 24 |
#include "config.h" |
|
| 25 |
#endif |
|
| 26 |
|
|
| 27 |
#include "gr_audio_registry.h" |
|
| 28 |
#include <audio_alsa_sink.h> |
|
| 29 |
#include <gr_io_signature.h> |
|
| 30 |
#include <gr_prefs.h> |
|
| 31 |
#include <stdio.h> |
|
| 32 |
#include <iostream> |
|
| 33 |
#include <stdexcept> |
|
| 34 |
#include <gri_alsa.h> |
|
| 35 |
|
|
| 36 |
AUDIO_REGISTER_SINK(alsa)( |
|
| 37 |
int sampling_rate, const std::string &device_name, bool ok_to_block |
|
| 38 |
){
|
|
| 39 |
return audio_sink::sptr(new audio_alsa_sink(sampling_rate, device_name, ok_to_block)); |
|
| 40 |
} |
|
| 41 |
|
|
| 42 |
static bool CHATTY_DEBUG = false; |
|
| 43 |
|
|
| 44 |
|
|
| 45 |
static snd_pcm_format_t acceptable_formats[] = {
|
|
| 46 |
// these are in our preferred order... |
|
| 47 |
SND_PCM_FORMAT_S32, |
|
| 48 |
SND_PCM_FORMAT_S16 |
|
| 49 |
}; |
|
| 50 |
|
|
| 51 |
#define NELEMS(x) (sizeof(x)/sizeof(x[0])) |
|
| 52 |
|
|
| 53 |
|
|
| 54 |
static std::string |
|
| 55 |
default_device_name () |
|
| 56 |
{
|
|
| 57 |
return gr_prefs::singleton()->get_string("audio_alsa", "default_output_device", "hw:0,0");
|
|
| 58 |
} |
|
| 59 |
|
|
| 60 |
static double |
|
| 61 |
default_period_time () |
|
| 62 |
{
|
|
| 63 |
return std::max(0.001, gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010));
|
|
| 64 |
} |
|
| 65 |
|
|
| 66 |
static int |
|
| 67 |
default_nperiods () |
|
| 68 |
{
|
|
| 69 |
return std::max(2L, gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4));
|
|
| 70 |
} |
|
| 71 |
|
|
| 72 |
// ---------------------------------------------------------------- |
|
| 73 |
|
|
| 74 |
audio_alsa_sink::audio_alsa_sink (int sampling_rate, |
|
| 75 |
const std::string device_name, |
|
| 76 |
bool ok_to_block) |
|
| 77 |
: audio_sink ("audio_alsa_sink",
|
|
| 78 |
gr_make_io_signature (0, 0, 0), |
|
| 79 |
gr_make_io_signature (0, 0, 0)), |
|
| 80 |
d_sampling_rate (sampling_rate), |
|
| 81 |
d_device_name (device_name.empty() ? default_device_name() : device_name), |
|
| 82 |
d_pcm_handle (0), |
|
| 83 |
d_hw_params ((snd_pcm_hw_params_t *)(new char[snd_pcm_hw_params_sizeof()])), |
|
| 84 |
d_sw_params ((snd_pcm_sw_params_t *)(new char[snd_pcm_sw_params_sizeof()])), |
|
| 85 |
d_nperiods (default_nperiods()), |
|
| 86 |
d_period_time_us ((unsigned int) (default_period_time() * 1e6)), |
|
| 87 |
d_period_size (0), |
|
| 88 |
d_buffer_size_bytes (0), d_buffer (0), |
|
| 89 |
d_worker (0), d_special_case_mono_to_stereo (false), |
|
| 90 |
d_nunderuns (0), d_nsuspends (0), d_ok_to_block(ok_to_block) |
|
| 91 |
{
|
|
| 92 |
CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false);
|
|
| 93 |
|
|
| 94 |
int error; |
|
| 95 |
int dir; |
|
| 96 |
|
|
| 97 |
// open the device for playback |
|
| 98 |
error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str (), |
|
| 99 |
SND_PCM_STREAM_PLAYBACK, 0); |
|
| 100 |
if (ok_to_block == false) |
|
| 101 |
snd_pcm_nonblock(d_pcm_handle, !ok_to_block); |
|
| 102 |
if (error < 0){
|
|
| 103 |
fprintf (stderr, "audio_alsa_sink[%s]: %s\n", |
|
| 104 |
d_device_name.c_str(), snd_strerror(error)); |
|
| 105 |
throw std::runtime_error ("audio_alsa_sink");
|
|
| 106 |
} |
|
| 107 |
|
|
| 108 |
// Fill params with a full configuration space for a PCM. |
|
| 109 |
error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); |
|
| 110 |
if (error < 0) |
|
| 111 |
bail ("broken configuration for playback", error);
|
|
| 112 |
|
|
| 113 |
|
|
| 114 |
if (CHATTY_DEBUG) |
|
| 115 |
gri_alsa_dump_hw_params (d_pcm_handle, d_hw_params, stdout); |
|
| 116 |
|
|
| 117 |
|
|
| 118 |
// now that we know how many channels the h/w can handle, set input signature |
|
| 119 |
unsigned int umin_chan, umax_chan; |
|
| 120 |
snd_pcm_hw_params_get_channels_min (d_hw_params, &umin_chan); |
|
| 121 |
snd_pcm_hw_params_get_channels_max (d_hw_params, &umax_chan); |
|
| 122 |
int min_chan = std::min (umin_chan, 1000U); |
|
| 123 |
int max_chan = std::min (umax_chan, 1000U); |
|
| 124 |
|
|
| 125 |
// As a special case, if the hw's min_chan is two, we'll accept |
|
| 126 |
// a single input and handle the duplication ourselves. |
|
| 127 |
|
|
| 128 |
if (min_chan == 2){
|
|
| 129 |
min_chan = 1; |
|
| 130 |
d_special_case_mono_to_stereo = true; |
|
| 131 |
} |
|
| 132 |
set_input_signature (gr_make_io_signature (min_chan, max_chan, |
|
| 133 |
sizeof (float))); |
|
| 134 |
|
|
| 135 |
// fill in portions of the d_hw_params that we know now... |
|
| 136 |
|
|
| 137 |
// Specify the access methods we implement |
|
| 138 |
// For now, we only handle RW_INTERLEAVED... |
|
| 139 |
snd_pcm_access_mask_t *access_mask; |
|
| 140 |
snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning |
|
| 141 |
snd_pcm_access_mask_alloca (access_mask_ptr); |
|
| 142 |
snd_pcm_access_mask_none (access_mask); |
|
| 143 |
snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); |
|
| 144 |
// snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); |
|
| 145 |
|
|
| 146 |
if ((error = snd_pcm_hw_params_set_access_mask (d_pcm_handle, |
|
| 147 |
d_hw_params, access_mask)) < 0) |
|
| 148 |
bail ("failed to set access mask", error);
|
|
| 149 |
|
|
| 150 |
|
|
| 151 |
// set sample format |
|
| 152 |
if (!gri_alsa_pick_acceptable_format (d_pcm_handle, d_hw_params, |
|
| 153 |
acceptable_formats, |
|
| 154 |
NELEMS (acceptable_formats), |
|
| 155 |
&d_format, |
|
| 156 |
"audio_alsa_sink", |
|
| 157 |
CHATTY_DEBUG)) |
|
| 158 |
throw std::runtime_error ("audio_alsa_sink");
|
|
| 159 |
|
|
| 160 |
|
|
| 161 |
// sampling rate |
|
| 162 |
unsigned int orig_sampling_rate = d_sampling_rate; |
|
| 163 |
if ((error = snd_pcm_hw_params_set_rate_near (d_pcm_handle, d_hw_params, |
|
| 164 |
&d_sampling_rate, 0)) < 0) |
|
| 165 |
bail ("failed to set rate near", error);
|
|
| 166 |
|
|
| 167 |
if (orig_sampling_rate != d_sampling_rate){
|
|
| 168 |
fprintf (stderr, "audio_alsa_sink[%s]: unable to support sampling rate %d\n", |
|
| 169 |
snd_pcm_name (d_pcm_handle), orig_sampling_rate); |
|
| 170 |
fprintf (stderr, " card requested %d instead.\n", d_sampling_rate); |
|
| 171 |
} |
|
| 172 |
|
|
| 173 |
/* |
|
| 174 |
* ALSA transfers data in units of "periods". |
|
| 175 |
* We indirectly determine the underlying buffersize by specifying |
|
| 176 |
* the number of periods we want (typically 4) and the length of each |
|
| 177 |
* period in units of time (typically 1ms). |
|
| 178 |
*/ |
|
| 179 |
unsigned int min_nperiods, max_nperiods; |
|
| 180 |
snd_pcm_hw_params_get_periods_min (d_hw_params, &min_nperiods, &dir); |
|
| 181 |
snd_pcm_hw_params_get_periods_max (d_hw_params, &max_nperiods, &dir); |
|
| 182 |
//fprintf (stderr, "alsa_sink: min_nperiods = %d, max_nperiods = %d\n", |
|
| 183 |
// min_nperiods, max_nperiods); |
|
| 184 |
|
|
| 185 |
unsigned int orig_nperiods = d_nperiods; |
|
| 186 |
d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods); |
|
| 187 |
|
|
| 188 |
// adjust period time so that total buffering remains more-or-less constant |
|
| 189 |
d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; |
|
| 190 |
|
|
| 191 |
error = snd_pcm_hw_params_set_periods (d_pcm_handle, d_hw_params, |
|
| 192 |
d_nperiods, 0); |
|
| 193 |
if (error < 0) |
|
| 194 |
bail ("set_periods failed", error);
|
|
| 195 |
|
|
| 196 |
dir = 0; |
|
| 197 |
error = snd_pcm_hw_params_set_period_time_near (d_pcm_handle, d_hw_params, |
|
| 198 |
&d_period_time_us, &dir); |
|
| 199 |
if (error < 0) |
|
| 200 |
bail ("set_period_time_near failed", error);
|
|
| 201 |
|
|
| 202 |
dir = 0; |
|
| 203 |
error = snd_pcm_hw_params_get_period_size (d_hw_params, |
|
| 204 |
&d_period_size, &dir); |
|
| 205 |
if (error < 0) |
|
| 206 |
bail ("get_period_size failed", error);
|
|
| 207 |
|
|
| 208 |
set_output_multiple (d_period_size); |
|
| 209 |
} |
|
| 210 |
|
|
| 211 |
|
|
| 212 |
bool |
|
| 213 |
audio_alsa_sink::check_topology (int ninputs, int noutputs) |
|
| 214 |
{
|
|
| 215 |
// ninputs is how many channels the user has connected. |
|
| 216 |
// Now we can finish up setting up the hw params... |
|
| 217 |
|
|
| 218 |
int nchan = ninputs; |
|
| 219 |
int err; |
|
| 220 |
|
|
| 221 |
// Check the state of the stream |
|
| 222 |
// Ensure that the pcm is in a state where we can still mess with the hw_params |
|
| 223 |
snd_pcm_state_t state; |
|
| 224 |
state=snd_pcm_state(d_pcm_handle); |
|
| 225 |
if ( state== SND_PCM_STATE_RUNNING) |
|
| 226 |
return true; // If stream is running, don't change any parameters |
|
| 227 |
else if(state == SND_PCM_STATE_XRUN ) |
|
| 228 |
snd_pcm_prepare ( d_pcm_handle ); // Prepare stream on underrun, and we can set parameters; |
|
| 229 |
|
|
| 230 |
bool special_case = nchan == 1 && d_special_case_mono_to_stereo; |
|
| 231 |
if (special_case) |
|
| 232 |
nchan = 2; |
|
| 233 |
|
|
| 234 |
err = snd_pcm_hw_params_set_channels (d_pcm_handle, d_hw_params, nchan); |
|
| 235 |
|
|
| 236 |
if (err < 0){
|
|
| 237 |
output_error_msg ("set_channels failed", err);
|
|
| 238 |
return false; |
|
| 239 |
} |
|
| 240 |
|
|
| 241 |
// set the parameters into the driver... |
|
| 242 |
err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); |
|
| 243 |
if (err < 0){
|
|
| 244 |
output_error_msg ("snd_pcm_hw_params failed", err);
|
|
| 245 |
return false; |
|
| 246 |
} |
|
| 247 |
|
|
| 248 |
// get current s/w params |
|
| 249 |
err = snd_pcm_sw_params_current (d_pcm_handle, d_sw_params); |
|
| 250 |
if (err < 0) |
|
| 251 |
bail ("snd_pcm_sw_params_current", err);
|
|
| 252 |
|
|
| 253 |
// Tell the PCM device to wait to start until we've filled |
|
| 254 |
// it's buffers half way full. This helps avoid audio underruns. |
|
| 255 |
|
|
| 256 |
err = snd_pcm_sw_params_set_start_threshold(d_pcm_handle, |
|
| 257 |
d_sw_params, |
|
| 258 |
d_nperiods * d_period_size / 2); |
|
| 259 |
if (err < 0) |
|
| 260 |
bail ("snd_pcm_sw_params_set_start_threshold", err);
|
|
| 261 |
|
|
| 262 |
// store the s/w params |
|
| 263 |
err = snd_pcm_sw_params (d_pcm_handle, d_sw_params); |
|
| 264 |
if (err < 0) |
|
| 265 |
bail ("snd_pcm_sw_params", err);
|
|
| 266 |
|
|
| 267 |
d_buffer_size_bytes = |
|
| 268 |
d_period_size * nchan * snd_pcm_format_size (d_format, 1); |
|
| 269 |
|
|
| 270 |
d_buffer = new char [d_buffer_size_bytes]; |
|
| 271 |
|
|
| 272 |
if (CHATTY_DEBUG) |
|
| 273 |
fprintf (stdout, "audio_alsa_sink[%s]: sample resolution = %d bits\n", |
|
| 274 |
snd_pcm_name (d_pcm_handle), |
|
| 275 |
snd_pcm_hw_params_get_sbits (d_hw_params)); |
|
| 276 |
|
|
| 277 |
switch (d_format){
|
|
| 278 |
case SND_PCM_FORMAT_S16: |
|
| 279 |
if (special_case) |
|
| 280 |
d_worker = &audio_alsa_sink::work_s16_1x2; |
|
| 281 |
else |
|
| 282 |
d_worker = &audio_alsa_sink::work_s16; |
|
| 283 |
break; |
|
| 284 |
|
|
| 285 |
case SND_PCM_FORMAT_S32: |
|
| 286 |
if (special_case) |
|
| 287 |
d_worker = &audio_alsa_sink::work_s32_1x2; |
|
| 288 |
else |
|
| 289 |
d_worker = &audio_alsa_sink::work_s32; |
|
| 290 |
break; |
|
| 291 |
|
|
| 292 |
default: |
|
| 293 |
assert (0); |
|
| 294 |
} |
|
| 295 |
return true; |
|
| 296 |
} |
|
| 297 |
|
|
| 298 |
audio_alsa_sink::~audio_alsa_sink () |
|
| 299 |
{
|
|
| 300 |
if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING) |
|
| 301 |
snd_pcm_drop (d_pcm_handle); |
|
| 302 |
|
|
| 303 |
snd_pcm_close(d_pcm_handle); |
|
| 304 |
delete [] ((char *) d_hw_params); |
|
| 305 |
delete [] ((char *) d_sw_params); |
|
| 306 |
delete [] d_buffer; |
|
| 307 |
} |
|
| 308 |
|
|
| 309 |
int |
|
| 310 |
audio_alsa_sink::work (int noutput_items, |
|
| 311 |
gr_vector_const_void_star &input_items, |
|
| 312 |
gr_vector_void_star &output_items) |
|
| 313 |
{
|
|
| 314 |
assert ((noutput_items % d_period_size) == 0); |
|
| 315 |
|
|
| 316 |
// this is a call through a pointer to a method... |
|
| 317 |
return (this->*d_worker)(noutput_items, input_items, output_items); |
|
| 318 |
} |
|
| 319 |
|
|
| 320 |
/* |
|
| 321 |
* Work function that deals with float to S16 conversion |
|
| 322 |
*/ |
|
| 323 |
int |
|
| 324 |
audio_alsa_sink::work_s16 (int noutput_items, |
|
| 325 |
gr_vector_const_void_star &input_items, |
|
| 326 |
gr_vector_void_star &output_items) |
|
| 327 |
{
|
|
| 328 |
typedef gr_int16 sample_t; // the type of samples we're creating |
|
| 329 |
static const int NBITS = 16; // # of bits in a sample |
|
| 330 |
|
|
| 331 |
unsigned int nchan = input_items.size (); |
|
| 332 |
const float **in = (const float **) &input_items[0]; |
|
| 333 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 334 |
int bi; |
|
| 335 |
int n; |
|
| 336 |
|
|
| 337 |
unsigned int sizeof_frame = nchan * sizeof (sample_t); |
|
| 338 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 339 |
|
|
| 340 |
for (n = 0; n < noutput_items; n += d_period_size){
|
|
| 341 |
|
|
| 342 |
// process one period of data |
|
| 343 |
bi = 0; |
|
| 344 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 345 |
for (unsigned int chan = 0; chan < nchan; chan++){
|
|
| 346 |
buf[bi++] = (sample_t) (in[chan][i] * (float) ((1L << (NBITS-1)) - 1)); |
|
| 347 |
} |
|
| 348 |
} |
|
| 349 |
|
|
| 350 |
// update src pointers |
|
| 351 |
for (unsigned int chan = 0; chan < nchan; chan++) |
|
| 352 |
in[chan] += d_period_size; |
|
| 353 |
|
|
| 354 |
if (!write_buffer (buf, d_period_size, sizeof_frame)) |
|
| 355 |
return -1; // No fixing this problem. Say we're done. |
|
| 356 |
} |
|
| 357 |
|
|
| 358 |
return n; |
|
| 359 |
} |
|
| 360 |
|
|
| 361 |
|
|
| 362 |
/* |
|
| 363 |
* Work function that deals with float to S32 conversion |
|
| 364 |
*/ |
|
| 365 |
int |
|
| 366 |
audio_alsa_sink::work_s32 (int noutput_items, |
|
| 367 |
gr_vector_const_void_star &input_items, |
|
| 368 |
gr_vector_void_star &output_items) |
|
| 369 |
{
|
|
| 370 |
typedef gr_int32 sample_t; // the type of samples we're creating |
|
| 371 |
static const int NBITS = 32; // # of bits in a sample |
|
| 372 |
|
|
| 373 |
unsigned int nchan = input_items.size (); |
|
| 374 |
const float **in = (const float **) &input_items[0]; |
|
| 375 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 376 |
int bi; |
|
| 377 |
int n; |
|
| 378 |
|
|
| 379 |
unsigned int sizeof_frame = nchan * sizeof (sample_t); |
|
| 380 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 381 |
|
|
| 382 |
for (n = 0; n < noutput_items; n += d_period_size){
|
|
| 383 |
|
|
| 384 |
// process one period of data |
|
| 385 |
bi = 0; |
|
| 386 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 387 |
for (unsigned int chan = 0; chan < nchan; chan++){
|
|
| 388 |
buf[bi++] = (sample_t) (in[chan][i] * (float) ((1L << (NBITS-1)) - 1)); |
|
| 389 |
} |
|
| 390 |
} |
|
| 391 |
|
|
| 392 |
// update src pointers |
|
| 393 |
for (unsigned int chan = 0; chan < nchan; chan++) |
|
| 394 |
in[chan] += d_period_size; |
|
| 395 |
|
|
| 396 |
if (!write_buffer (buf, d_period_size, sizeof_frame)) |
|
| 397 |
return -1; // No fixing this problem. Say we're done. |
|
| 398 |
} |
|
| 399 |
|
|
| 400 |
return n; |
|
| 401 |
} |
|
| 402 |
|
|
| 403 |
/* |
|
| 404 |
* Work function that deals with float to S16 conversion and |
|
| 405 |
* mono to stereo kludge. |
|
| 406 |
*/ |
|
| 407 |
int |
|
| 408 |
audio_alsa_sink::work_s16_1x2 (int noutput_items, |
|
| 409 |
gr_vector_const_void_star &input_items, |
|
| 410 |
gr_vector_void_star &output_items) |
|
| 411 |
{
|
|
| 412 |
typedef gr_int16 sample_t; // the type of samples we're creating |
|
| 413 |
static const int NBITS = 16; // # of bits in a sample |
|
| 414 |
|
|
| 415 |
assert (input_items.size () == 1); |
|
| 416 |
static const unsigned int nchan = 2; |
|
| 417 |
const float **in = (const float **) &input_items[0]; |
|
| 418 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 419 |
int bi; |
|
| 420 |
int n; |
|
| 421 |
|
|
| 422 |
unsigned int sizeof_frame = nchan * sizeof (sample_t); |
|
| 423 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 424 |
|
|
| 425 |
for (n = 0; n < noutput_items; n += d_period_size){
|
|
| 426 |
|
|
| 427 |
// process one period of data |
|
| 428 |
bi = 0; |
|
| 429 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 430 |
sample_t t = (sample_t) (in[0][i] * (float) ((1L << (NBITS-1)) - 1)); |
|
| 431 |
buf[bi++] = t; |
|
| 432 |
buf[bi++] = t; |
|
| 433 |
} |
|
| 434 |
|
|
| 435 |
// update src pointers |
|
| 436 |
in[0] += d_period_size; |
|
| 437 |
|
|
| 438 |
if (!write_buffer (buf, d_period_size, sizeof_frame)) |
|
| 439 |
return -1; // No fixing this problem. Say we're done. |
|
| 440 |
} |
|
| 441 |
|
|
| 442 |
return n; |
|
| 443 |
} |
|
| 444 |
|
|
| 445 |
/* |
|
| 446 |
* Work function that deals with float to S32 conversion and |
|
| 447 |
* mono to stereo kludge. |
|
| 448 |
*/ |
|
| 449 |
int |
|
| 450 |
audio_alsa_sink::work_s32_1x2 (int noutput_items, |
|
| 451 |
gr_vector_const_void_star &input_items, |
|
| 452 |
gr_vector_void_star &output_items) |
|
| 453 |
{
|
|
| 454 |
typedef gr_int32 sample_t; // the type of samples we're creating |
|
| 455 |
static const int NBITS = 32; // # of bits in a sample |
|
| 456 |
|
|
| 457 |
assert (input_items.size () == 1); |
|
| 458 |
static unsigned int nchan = 2; |
|
| 459 |
const float **in = (const float **) &input_items[0]; |
|
| 460 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 461 |
int bi; |
|
| 462 |
int n; |
|
| 463 |
|
|
| 464 |
unsigned int sizeof_frame = nchan * sizeof (sample_t); |
|
| 465 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 466 |
|
|
| 467 |
for (n = 0; n < noutput_items; n += d_period_size){
|
|
| 468 |
|
|
| 469 |
// process one period of data |
|
| 470 |
bi = 0; |
|
| 471 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 472 |
sample_t t = (sample_t) (in[0][i] * (float) ((1L << (NBITS-1)) - 1)); |
|
| 473 |
buf[bi++] = t; |
|
| 474 |
buf[bi++] = t; |
|
| 475 |
} |
|
| 476 |
|
|
| 477 |
// update src pointers |
|
| 478 |
in[0] += d_period_size; |
|
| 479 |
|
|
| 480 |
if (!write_buffer (buf, d_period_size, sizeof_frame)) |
|
| 481 |
return -1; // No fixing this problem. Say we're done. |
|
| 482 |
} |
|
| 483 |
|
|
| 484 |
return n; |
|
| 485 |
} |
|
| 486 |
|
|
| 487 |
bool |
|
| 488 |
audio_alsa_sink::write_buffer (const void *vbuffer, |
|
| 489 |
unsigned nframes, unsigned sizeof_frame) |
|
| 490 |
{
|
|
| 491 |
const unsigned char *buffer = (const unsigned char *) vbuffer; |
|
| 492 |
|
|
| 493 |
while (nframes > 0){
|
|
| 494 |
int r = snd_pcm_writei (d_pcm_handle, buffer, nframes); |
|
| 495 |
if (r == -EAGAIN) |
|
| 496 |
{
|
|
| 497 |
if (d_ok_to_block == true) |
|
| 498 |
continue; // try again |
|
| 499 |
|
|
| 500 |
break; |
|
| 501 |
} |
|
| 502 |
|
|
| 503 |
else if (r == -EPIPE){ // underrun
|
|
| 504 |
d_nunderuns++; |
|
| 505 |
fputs ("aU", stderr);
|
|
| 506 |
if ((r = snd_pcm_prepare (d_pcm_handle)) < 0){
|
|
| 507 |
output_error_msg ("snd_pcm_prepare failed. Can't recover from underrun", r);
|
|
| 508 |
return false; |
|
| 509 |
} |
|
| 510 |
continue; // try again |
|
| 511 |
} |
|
| 512 |
|
|
| 513 |
else if (r == -ESTRPIPE){ // h/w is suspended (whatever that means)
|
|
| 514 |
// This is apparently related to power management |
|
| 515 |
d_nsuspends++; |
|
| 516 |
if ((r = snd_pcm_resume (d_pcm_handle)) < 0){
|
|
| 517 |
output_error_msg ("failed to resume from suspend", r);
|
|
| 518 |
return false; |
|
| 519 |
} |
|
| 520 |
continue; // try again |
|
| 521 |
} |
|
| 522 |
|
|
| 523 |
else if (r < 0){
|
|
| 524 |
output_error_msg ("snd_pcm_writei failed", r);
|
|
| 525 |
return false; |
|
| 526 |
} |
|
| 527 |
|
|
| 528 |
nframes -= r; |
|
| 529 |
buffer += r * sizeof_frame; |
|
| 530 |
} |
|
| 531 |
|
|
| 532 |
return true; |
|
| 533 |
} |
|
| 534 |
|
|
| 535 |
|
|
| 536 |
void |
|
| 537 |
audio_alsa_sink::output_error_msg (const char *msg, int err) |
|
| 538 |
{
|
|
| 539 |
fprintf (stderr, "audio_alsa_sink[%s]: %s: %s\n", |
|
| 540 |
snd_pcm_name (d_pcm_handle), msg, snd_strerror (err)); |
|
| 541 |
} |
|
| 542 |
|
|
| 543 |
void |
|
| 544 |
audio_alsa_sink::bail (const char *msg, int err) throw (std::runtime_error) |
|
| 545 |
{
|
|
| 546 |
output_error_msg (msg, err); |
|
| 547 |
throw std::runtime_error ("audio_alsa_sink");
|
|
| 548 |
} |
|
| b/gr-audio/lib/alsa/audio_alsa_sink.h | ||
|---|---|---|
| 1 |
/* -*- c++ -*- */ |
|
| 2 |
/* |
|
| 3 |
* Copyright 2004-2011 Free Software Foundation, Inc. |
|
| 4 |
* |
|
| 5 |
* This file is part of GNU Radio |
|
| 6 |
* |
|
| 7 |
* GNU Radio is free software; you can redistribute it and/or modify |
|
| 8 |
* it under the terms of the GNU General Public License as published by |
|
| 9 |
* the Free Software Foundation; either version 3, or (at your option) |
|
| 10 |
* any later version. |
|
| 11 |
* |
|
| 12 |
* GNU Radio is distributed in the hope that it will be useful, |
|
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 |
* GNU General Public License for more details. |
|
| 16 |
* |
|
| 17 |
* You should have received a copy of the GNU General Public License |
|
| 18 |
* along with GNU Radio; see the file COPYING. If not, write to |
|
| 19 |
* the Free Software Foundation, Inc., 51 Franklin Street, |
|
| 20 |
* Boston, MA 02110-1301, USA. |
|
| 21 |
*/ |
|
| 22 |
|
|
| 23 |
#ifndef INCLUDED_AUDIO_ALSA_SINK_H |
|
| 24 |
#define INCLUDED_AUDIO_ALSA_SINK_H |
|
| 25 |
|
|
| 26 |
// use new ALSA API |
|
| 27 |
#define ALSA_PCM_NEW_HW_PARAMS_API |
|
| 28 |
#define ALSA_PCM_NEW_SW_PARAMS_API |
|
| 29 |
|
|
| 30 |
#include <gr_audio_sink.h> |
|
| 31 |
#include <string> |
|
| 32 |
#include <alsa/asoundlib.h> |
|
| 33 |
#include <stdexcept> |
|
| 34 |
|
|
| 35 |
/*! |
|
| 36 |
* \brief audio sink using ALSA |
|
| 37 |
* |
|
| 38 |
* The sink has N input streams of floats, where N depends |
|
| 39 |
* on the hardware characteristics of the selected device. |
|
| 40 |
* |
|
| 41 |
* Input samples must be in the range [-1,1]. |
|
| 42 |
*/ |
|
| 43 |
class audio_alsa_sink : public audio_sink {
|
|
| 44 |
// typedef for pointer to class work method |
|
| 45 |
typedef int (audio_alsa_sink::*work_t)(int noutput_items, |
|
| 46 |
gr_vector_const_void_star &input_items, |
|
| 47 |
gr_vector_void_star &output_items); |
|
| 48 |
|
|
| 49 |
unsigned int d_sampling_rate; |
|
| 50 |
std::string d_device_name; |
|
| 51 |
snd_pcm_t *d_pcm_handle; |
|
| 52 |
snd_pcm_hw_params_t *d_hw_params; |
|
| 53 |
snd_pcm_sw_params_t *d_sw_params; |
|
| 54 |
snd_pcm_format_t d_format; |
|
| 55 |
unsigned int d_nperiods; |
|
| 56 |
unsigned int d_period_time_us; // microseconds |
|
| 57 |
snd_pcm_uframes_t d_period_size; // in frames |
|
| 58 |
unsigned int d_buffer_size_bytes; // sizeof of d_buffer |
|
| 59 |
char *d_buffer; |
|
| 60 |
work_t d_worker; // the work method to use |
|
| 61 |
bool d_special_case_mono_to_stereo; |
|
| 62 |
|
|
| 63 |
// random stats |
|
| 64 |
int d_nunderuns; // count of underruns |
|
| 65 |
int d_nsuspends; // count of suspends |
|
| 66 |
bool d_ok_to_block; // defaults to "true", controls blocking/non-block I/O |
|
| 67 |
|
|
| 68 |
void output_error_msg (const char *msg, int err); |
|
| 69 |
void bail (const char *msg, int err) throw (std::runtime_error); |
|
| 70 |
|
|
| 71 |
public: |
|
| 72 |
audio_alsa_sink (int sampling_rate, const std::string device_name, |
|
| 73 |
bool ok_to_block); |
|
| 74 |
|
|
| 75 |
~audio_alsa_sink (); |
|
| 76 |
|
|
| 77 |
bool check_topology (int ninputs, int noutputs); |
|
| 78 |
|
|
| 79 |
int work (int noutput_items, |
|
| 80 |
gr_vector_const_void_star &input_items, |
|
| 81 |
gr_vector_void_star &output_items); |
|
| 82 |
|
|
| 83 |
|
|
| 84 |
protected: |
|
| 85 |
bool write_buffer (const void *buffer, unsigned nframes, unsigned sizeof_frame); |
|
| 86 |
|
|
| 87 |
int work_s16 (int noutput_items, |
|
| 88 |
gr_vector_const_void_star &input_items, |
|
| 89 |
gr_vector_void_star &output_items); |
|
| 90 |
|
|
| 91 |
int work_s16_1x2 (int noutput_items, |
|
| 92 |
gr_vector_const_void_star &input_items, |
|
| 93 |
gr_vector_void_star &output_items); |
|
| 94 |
|
|
| 95 |
int work_s32 (int noutput_items, |
|
| 96 |
gr_vector_const_void_star &input_items, |
|
| 97 |
gr_vector_void_star &output_items); |
|
| 98 |
|
|
| 99 |
int work_s32_1x2 (int noutput_items, |
|
| 100 |
gr_vector_const_void_star &input_items, |
|
| 101 |
gr_vector_void_star &output_items); |
|
| 102 |
}; |
|
| 103 |
|
|
| 104 |
#endif /* INCLUDED_AUDIO_ALSA_SINK_H */ |
|
| b/gr-audio/lib/alsa/audio_alsa_source.cc | ||
|---|---|---|
| 1 |
/* -*- c++ -*- */ |
|
| 2 |
/* |
|
| 3 |
* Copyright 2004-2011 Free Software Foundation, Inc. |
|
| 4 |
* |
|
| 5 |
* This file is part of GNU Radio |
|
| 6 |
* |
|
| 7 |
* GNU Radio is free software; you can redistribute it and/or modify |
|
| 8 |
* it under the terms of the GNU General Public License as published by |
|
| 9 |
* the Free Software Foundation; either version 3, or (at your option) |
|
| 10 |
* any later version. |
|
| 11 |
* |
|
| 12 |
* GNU Radio is distributed in the hope that it will be useful, |
|
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 |
* GNU General Public License for more details. |
|
| 16 |
* |
|
| 17 |
* You should have received a copy of the GNU General Public License |
|
| 18 |
* along with GNU Radio; see the file COPYING. If not, write to |
|
| 19 |
* the Free Software Foundation, Inc., 51 Franklin Street, |
|
| 20 |
* Boston, MA 02110-1301, USA. |
|
| 21 |
*/ |
|
| 22 |
|
|
| 23 |
#ifdef HAVE_CONFIG_H |
|
| 24 |
#include "config.h" |
|
| 25 |
#endif |
|
| 26 |
|
|
| 27 |
#include "gr_audio_registry.h" |
|
| 28 |
#include <audio_alsa_source.h> |
|
| 29 |
#include <gr_io_signature.h> |
|
| 30 |
#include <gr_prefs.h> |
|
| 31 |
#include <stdio.h> |
|
| 32 |
#include <iostream> |
|
| 33 |
#include <stdexcept> |
|
| 34 |
#include <gri_alsa.h> |
|
| 35 |
|
|
| 36 |
AUDIO_REGISTER_SOURCE(alsa)( |
|
| 37 |
int sampling_rate, const std::string &device_name, bool ok_to_block |
|
| 38 |
){
|
|
| 39 |
return audio_source::sptr(new audio_alsa_source(sampling_rate, device_name, ok_to_block)); |
|
| 40 |
} |
|
| 41 |
|
|
| 42 |
static bool CHATTY_DEBUG = false; |
|
| 43 |
|
|
| 44 |
static snd_pcm_format_t acceptable_formats[] = {
|
|
| 45 |
// these are in our preferred order... |
|
| 46 |
SND_PCM_FORMAT_S32, |
|
| 47 |
SND_PCM_FORMAT_S16 |
|
| 48 |
}; |
|
| 49 |
|
|
| 50 |
#define NELEMS(x) (sizeof(x)/sizeof(x[0])) |
|
| 51 |
|
|
| 52 |
|
|
| 53 |
static std::string |
|
| 54 |
default_device_name () |
|
| 55 |
{
|
|
| 56 |
return gr_prefs::singleton()->get_string("audio_alsa", "default_input_device", "hw:0,0");
|
|
| 57 |
} |
|
| 58 |
|
|
| 59 |
static double |
|
| 60 |
default_period_time () |
|
| 61 |
{
|
|
| 62 |
return std::max(0.001, gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010));
|
|
| 63 |
} |
|
| 64 |
|
|
| 65 |
static int |
|
| 66 |
default_nperiods () |
|
| 67 |
{
|
|
| 68 |
return std::max(2L, gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4));
|
|
| 69 |
} |
|
| 70 |
|
|
| 71 |
// ---------------------------------------------------------------- |
|
| 72 |
|
|
| 73 |
audio_alsa_source::audio_alsa_source (int sampling_rate, |
|
| 74 |
const std::string device_name, |
|
| 75 |
bool ok_to_block) |
|
| 76 |
: audio_source ("audio_alsa_source",
|
|
| 77 |
gr_make_io_signature (0, 0, 0), |
|
| 78 |
gr_make_io_signature (0, 0, 0)), |
|
| 79 |
d_sampling_rate (sampling_rate), |
|
| 80 |
d_device_name (device_name.empty() ? default_device_name() : device_name), |
|
| 81 |
d_pcm_handle (0), |
|
| 82 |
d_hw_params ((snd_pcm_hw_params_t *)(new char[snd_pcm_hw_params_sizeof()])), |
|
| 83 |
d_sw_params ((snd_pcm_sw_params_t *)(new char[snd_pcm_sw_params_sizeof()])), |
|
| 84 |
d_nperiods (default_nperiods()), |
|
| 85 |
d_period_time_us ((unsigned int) (default_period_time() * 1e6)), |
|
| 86 |
d_period_size (0), |
|
| 87 |
d_buffer_size_bytes (0), d_buffer (0), |
|
| 88 |
d_worker (0), d_hw_nchan (0), |
|
| 89 |
d_special_case_stereo_to_mono (false), |
|
| 90 |
d_noverruns (0), d_nsuspends (0) |
|
| 91 |
{
|
|
| 92 |
|
|
| 93 |
CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false);
|
|
| 94 |
|
|
| 95 |
int error; |
|
| 96 |
int dir; |
|
| 97 |
|
|
| 98 |
// open the device for capture |
|
| 99 |
error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str (), |
|
| 100 |
SND_PCM_STREAM_CAPTURE, 0); |
|
| 101 |
if (error < 0){
|
|
| 102 |
fprintf (stderr, "audio_alsa_source[%s]: %s\n", |
|
| 103 |
d_device_name.c_str(), snd_strerror(error)); |
|
| 104 |
throw std::runtime_error ("audio_alsa_source");
|
|
| 105 |
} |
|
| 106 |
|
|
| 107 |
// Fill params with a full configuration space for a PCM. |
|
| 108 |
error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); |
|
| 109 |
if (error < 0) |
|
| 110 |
bail ("broken configuration for playback", error);
|
|
| 111 |
|
|
| 112 |
if (CHATTY_DEBUG) |
|
| 113 |
gri_alsa_dump_hw_params (d_pcm_handle, d_hw_params, stdout); |
|
| 114 |
|
|
| 115 |
// now that we know how many channels the h/w can handle, set output signature |
|
| 116 |
unsigned int umax_chan; |
|
| 117 |
unsigned int umin_chan; |
|
| 118 |
snd_pcm_hw_params_get_channels_min (d_hw_params, &umin_chan); |
|
| 119 |
snd_pcm_hw_params_get_channels_max (d_hw_params, &umax_chan); |
|
| 120 |
int min_chan = std::min (umin_chan, 1000U); |
|
| 121 |
int max_chan = std::min (umax_chan, 1000U); |
|
| 122 |
|
|
| 123 |
// As a special case, if the hw's min_chan is two, we'll accept |
|
| 124 |
// a single output and handle the demux ourselves. |
|
| 125 |
|
|
| 126 |
if (min_chan == 2){
|
|
| 127 |
min_chan = 1; |
|
| 128 |
d_special_case_stereo_to_mono = true; |
|
| 129 |
} |
|
| 130 |
|
|
| 131 |
set_output_signature (gr_make_io_signature (min_chan, max_chan, |
|
| 132 |
sizeof (float))); |
|
| 133 |
|
|
| 134 |
// fill in portions of the d_hw_params that we know now... |
|
| 135 |
|
|
| 136 |
// Specify the access methods we implement |
|
| 137 |
// For now, we only handle RW_INTERLEAVED... |
|
| 138 |
snd_pcm_access_mask_t *access_mask; |
|
| 139 |
snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning |
|
| 140 |
snd_pcm_access_mask_alloca (access_mask_ptr); |
|
| 141 |
snd_pcm_access_mask_none (access_mask); |
|
| 142 |
snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); |
|
| 143 |
// snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); |
|
| 144 |
|
|
| 145 |
if ((error = snd_pcm_hw_params_set_access_mask (d_pcm_handle, |
|
| 146 |
d_hw_params, access_mask)) < 0) |
|
| 147 |
bail ("failed to set access mask", error);
|
|
| 148 |
|
|
| 149 |
|
|
| 150 |
// set sample format |
|
| 151 |
if (!gri_alsa_pick_acceptable_format (d_pcm_handle, d_hw_params, |
|
| 152 |
acceptable_formats, |
|
| 153 |
NELEMS (acceptable_formats), |
|
| 154 |
&d_format, |
|
| 155 |
"audio_alsa_source", |
|
| 156 |
CHATTY_DEBUG)) |
|
| 157 |
throw std::runtime_error ("audio_alsa_source");
|
|
| 158 |
|
|
| 159 |
|
|
| 160 |
// sampling rate |
|
| 161 |
unsigned int orig_sampling_rate = d_sampling_rate; |
|
| 162 |
if ((error = snd_pcm_hw_params_set_rate_near (d_pcm_handle, d_hw_params, |
|
| 163 |
&d_sampling_rate, 0)) < 0) |
|
| 164 |
bail ("failed to set rate near", error);
|
|
| 165 |
|
|
| 166 |
if (orig_sampling_rate != d_sampling_rate){
|
|
| 167 |
fprintf (stderr, "audio_alsa_source[%s]: unable to support sampling rate %d\n", |
|
| 168 |
snd_pcm_name (d_pcm_handle), orig_sampling_rate); |
|
| 169 |
fprintf (stderr, " card requested %d instead.\n", d_sampling_rate); |
|
| 170 |
} |
|
| 171 |
|
|
| 172 |
/* |
|
| 173 |
* ALSA transfers data in units of "periods". |
|
| 174 |
* We indirectly determine the underlying buffersize by specifying |
|
| 175 |
* the number of periods we want (typically 4) and the length of each |
|
| 176 |
* period in units of time (typically 1ms). |
|
| 177 |
*/ |
|
| 178 |
unsigned int min_nperiods, max_nperiods; |
|
| 179 |
snd_pcm_hw_params_get_periods_min (d_hw_params, &min_nperiods, &dir); |
|
| 180 |
snd_pcm_hw_params_get_periods_max (d_hw_params, &max_nperiods, &dir); |
|
| 181 |
//fprintf (stderr, "alsa_source: min_nperiods = %d, max_nperiods = %d\n", |
|
| 182 |
// min_nperiods, max_nperiods); |
|
| 183 |
|
|
| 184 |
|
|
| 185 |
unsigned int orig_nperiods = d_nperiods; |
|
| 186 |
d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods); |
|
| 187 |
|
|
| 188 |
// adjust period time so that total buffering remains more-or-less constant |
|
| 189 |
d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; |
|
| 190 |
|
|
| 191 |
error = snd_pcm_hw_params_set_periods (d_pcm_handle, d_hw_params, |
|
| 192 |
d_nperiods, 0); |
|
| 193 |
if (error < 0) |
|
| 194 |
bail ("set_periods failed", error);
|
|
| 195 |
|
|
| 196 |
dir = 0; |
|
| 197 |
error = snd_pcm_hw_params_set_period_time_near (d_pcm_handle, d_hw_params, |
|
| 198 |
&d_period_time_us, &dir); |
|
| 199 |
if (error < 0) |
|
| 200 |
bail ("set_period_time_near failed", error);
|
|
| 201 |
|
|
| 202 |
dir = 0; |
|
| 203 |
error = snd_pcm_hw_params_get_period_size (d_hw_params, |
|
| 204 |
&d_period_size, &dir); |
|
| 205 |
if (error < 0) |
|
| 206 |
bail ("get_period_size failed", error);
|
|
| 207 |
|
|
| 208 |
set_output_multiple (d_period_size); |
|
| 209 |
} |
|
| 210 |
|
|
| 211 |
bool |
|
| 212 |
audio_alsa_source::check_topology (int ninputs, int noutputs) |
|
| 213 |
{
|
|
| 214 |
// noutputs is how many channels the user has connected. |
|
| 215 |
// Now we can finish up setting up the hw params... |
|
| 216 |
|
|
| 217 |
unsigned int nchan = noutputs; |
|
| 218 |
int err; |
|
| 219 |
|
|
| 220 |
// FIXME check_topology may be called more than once. |
|
| 221 |
// Ensure that the pcm is in a state where we can still mess with the hw_params |
|
| 222 |
|
|
| 223 |
bool special_case = nchan == 1 && d_special_case_stereo_to_mono; |
|
| 224 |
if (special_case) |
|
| 225 |
nchan = 2; |
|
| 226 |
|
|
| 227 |
d_hw_nchan = nchan; |
|
| 228 |
err = snd_pcm_hw_params_set_channels (d_pcm_handle, d_hw_params, d_hw_nchan); |
|
| 229 |
if (err < 0){
|
|
| 230 |
output_error_msg ("set_channels failed", err);
|
|
| 231 |
return false; |
|
| 232 |
} |
|
| 233 |
|
|
| 234 |
// set the parameters into the driver... |
|
| 235 |
err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); |
|
| 236 |
if (err < 0){
|
|
| 237 |
output_error_msg ("snd_pcm_hw_params failed", err);
|
|
| 238 |
return false; |
|
| 239 |
} |
|
| 240 |
|
|
| 241 |
d_buffer_size_bytes = |
|
| 242 |
d_period_size * d_hw_nchan * snd_pcm_format_size (d_format, 1); |
|
| 243 |
|
|
| 244 |
d_buffer = new char [d_buffer_size_bytes]; |
|
| 245 |
|
|
| 246 |
if (CHATTY_DEBUG) |
|
| 247 |
fprintf (stdout, "audio_alsa_source[%s]: sample resolution = %d bits\n", |
|
| 248 |
snd_pcm_name (d_pcm_handle), |
|
| 249 |
snd_pcm_hw_params_get_sbits (d_hw_params)); |
|
| 250 |
|
|
| 251 |
switch (d_format){
|
|
| 252 |
case SND_PCM_FORMAT_S16: |
|
| 253 |
if (special_case) |
|
| 254 |
d_worker = &audio_alsa_source::work_s16_2x1; |
|
| 255 |
else |
|
| 256 |
d_worker = &audio_alsa_source::work_s16; |
|
| 257 |
break; |
|
| 258 |
|
|
| 259 |
case SND_PCM_FORMAT_S32: |
|
| 260 |
if (special_case) |
|
| 261 |
d_worker = &audio_alsa_source::work_s32_2x1; |
|
| 262 |
else |
|
| 263 |
d_worker = &audio_alsa_source::work_s32; |
|
| 264 |
break; |
|
| 265 |
|
|
| 266 |
default: |
|
| 267 |
assert (0); |
|
| 268 |
} |
|
| 269 |
|
|
| 270 |
return true; |
|
| 271 |
} |
|
| 272 |
|
|
| 273 |
audio_alsa_source::~audio_alsa_source () |
|
| 274 |
{
|
|
| 275 |
if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING) |
|
| 276 |
snd_pcm_drop (d_pcm_handle); |
|
| 277 |
|
|
| 278 |
snd_pcm_close(d_pcm_handle); |
|
| 279 |
delete [] ((char *) d_hw_params); |
|
| 280 |
delete [] ((char *) d_sw_params); |
|
| 281 |
delete [] d_buffer; |
|
| 282 |
} |
|
| 283 |
|
|
| 284 |
int |
|
| 285 |
audio_alsa_source::work (int noutput_items, |
|
| 286 |
gr_vector_const_void_star &input_items, |
|
| 287 |
gr_vector_void_star &output_items) |
|
| 288 |
{
|
|
| 289 |
assert ((noutput_items % d_period_size) == 0); |
|
| 290 |
assert (noutput_items != 0); |
|
| 291 |
|
|
| 292 |
// this is a call through a pointer to a method... |
|
| 293 |
return (this->*d_worker)(noutput_items, input_items, output_items); |
|
| 294 |
} |
|
| 295 |
|
|
| 296 |
/* |
|
| 297 |
* Work function that deals with float to S16 conversion |
|
| 298 |
*/ |
|
| 299 |
int |
|
| 300 |
audio_alsa_source::work_s16 (int noutput_items, |
|
| 301 |
gr_vector_const_void_star &input_items, |
|
| 302 |
gr_vector_void_star &output_items) |
|
| 303 |
{
|
|
| 304 |
typedef gr_int16 sample_t; // the type of samples we're creating |
|
| 305 |
static const int NBITS = 16; // # of bits in a sample |
|
| 306 |
|
|
| 307 |
unsigned int nchan = output_items.size (); |
|
| 308 |
float **out = (float **) &output_items[0]; |
|
| 309 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 310 |
int bi; |
|
| 311 |
|
|
| 312 |
unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
|
| 313 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 314 |
|
|
| 315 |
// To minimize latency, return at most a single period's worth of samples. |
|
| 316 |
// [We could also read the first one in a blocking mode and subsequent |
|
| 317 |
// ones in non-blocking mode, but we'll leave that for later (or never).] |
|
| 318 |
|
|
| 319 |
if (!read_buffer (buf, d_period_size, sizeof_frame)) |
|
| 320 |
return -1; // No fixing this problem. Say we're done. |
|
| 321 |
|
|
| 322 |
// process one period of data |
|
| 323 |
bi = 0; |
|
| 324 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 325 |
for (unsigned int chan = 0; chan < nchan; chan++){
|
|
| 326 |
out[chan][i] = (float) buf[bi++] * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
|
| 327 |
} |
|
| 328 |
} |
|
| 329 |
|
|
| 330 |
return d_period_size; |
|
| 331 |
} |
|
| 332 |
|
|
| 333 |
/* |
|
| 334 |
* Work function that deals with float to S16 conversion |
|
| 335 |
* and stereo to mono kludge... |
|
| 336 |
*/ |
|
| 337 |
int |
|
| 338 |
audio_alsa_source::work_s16_2x1 (int noutput_items, |
|
| 339 |
gr_vector_const_void_star &input_items, |
|
| 340 |
gr_vector_void_star &output_items) |
|
| 341 |
{
|
|
| 342 |
typedef gr_int16 sample_t; // the type of samples we're creating |
|
| 343 |
static const int NBITS = 16; // # of bits in a sample |
|
| 344 |
|
|
| 345 |
unsigned int nchan = output_items.size (); |
|
| 346 |
float **out = (float **) &output_items[0]; |
|
| 347 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 348 |
int bi; |
|
| 349 |
|
|
| 350 |
assert (nchan == 1); |
|
| 351 |
|
|
| 352 |
unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
|
| 353 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 354 |
|
|
| 355 |
// To minimize latency, return at most a single period's worth of samples. |
|
| 356 |
// [We could also read the first one in a blocking mode and subsequent |
|
| 357 |
// ones in non-blocking mode, but we'll leave that for later (or never).] |
|
| 358 |
|
|
| 359 |
if (!read_buffer (buf, d_period_size, sizeof_frame)) |
|
| 360 |
return -1; // No fixing this problem. Say we're done. |
|
| 361 |
|
|
| 362 |
// process one period of data |
|
| 363 |
bi = 0; |
|
| 364 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 365 |
int t = (buf[bi] + buf[bi+1]) / 2; |
|
| 366 |
bi += 2; |
|
| 367 |
out[0][i] = (float) t * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
|
| 368 |
} |
|
| 369 |
|
|
| 370 |
return d_period_size; |
|
| 371 |
} |
|
| 372 |
|
|
| 373 |
/* |
|
| 374 |
* Work function that deals with float to S32 conversion |
|
| 375 |
*/ |
|
| 376 |
int |
|
| 377 |
audio_alsa_source::work_s32 (int noutput_items, |
|
| 378 |
gr_vector_const_void_star &input_items, |
|
| 379 |
gr_vector_void_star &output_items) |
|
| 380 |
{
|
|
| 381 |
typedef gr_int32 sample_t; // the type of samples we're creating |
|
| 382 |
static const int NBITS = 32; // # of bits in a sample |
|
| 383 |
|
|
| 384 |
unsigned int nchan = output_items.size (); |
|
| 385 |
float **out = (float **) &output_items[0]; |
|
| 386 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 387 |
int bi; |
|
| 388 |
|
|
| 389 |
unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
|
| 390 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 391 |
|
|
| 392 |
// To minimize latency, return at most a single period's worth of samples. |
|
| 393 |
// [We could also read the first one in a blocking mode and subsequent |
|
| 394 |
// ones in non-blocking mode, but we'll leave that for later (or never).] |
|
| 395 |
|
|
| 396 |
if (!read_buffer (buf, d_period_size, sizeof_frame)) |
|
| 397 |
return -1; // No fixing this problem. Say we're done. |
|
| 398 |
|
|
| 399 |
// process one period of data |
|
| 400 |
bi = 0; |
|
| 401 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 402 |
for (unsigned int chan = 0; chan < nchan; chan++){
|
|
| 403 |
out[chan][i] = (float) buf[bi++] * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
|
| 404 |
} |
|
| 405 |
} |
|
| 406 |
|
|
| 407 |
return d_period_size; |
|
| 408 |
} |
|
| 409 |
|
|
| 410 |
/* |
|
| 411 |
* Work function that deals with float to S32 conversion |
|
| 412 |
* and stereo to mono kludge... |
|
| 413 |
*/ |
|
| 414 |
int |
|
| 415 |
audio_alsa_source::work_s32_2x1 (int noutput_items, |
|
| 416 |
gr_vector_const_void_star &input_items, |
|
| 417 |
gr_vector_void_star &output_items) |
|
| 418 |
{
|
|
| 419 |
typedef gr_int32 sample_t; // the type of samples we're creating |
|
| 420 |
static const int NBITS = 32; // # of bits in a sample |
|
| 421 |
|
|
| 422 |
unsigned int nchan = output_items.size (); |
|
| 423 |
float **out = (float **) &output_items[0]; |
|
| 424 |
sample_t *buf = (sample_t *) d_buffer; |
|
| 425 |
int bi; |
|
| 426 |
|
|
| 427 |
assert (nchan == 1); |
|
| 428 |
|
|
| 429 |
unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
|
| 430 |
assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
|
| 431 |
|
|
| 432 |
// To minimize latency, return at most a single period's worth of samples. |
|
| 433 |
// [We could also read the first one in a blocking mode and subsequent |
|
| 434 |
// ones in non-blocking mode, but we'll leave that for later (or never).] |
|
| 435 |
|
|
| 436 |
if (!read_buffer (buf, d_period_size, sizeof_frame)) |
|
| 437 |
return -1; // No fixing this problem. Say we're done. |
|
| 438 |
|
|
| 439 |
// process one period of data |
|
| 440 |
bi = 0; |
|
| 441 |
for (unsigned int i = 0; i < d_period_size; i++){
|
|
| 442 |
int t = (buf[bi] + buf[bi+1]) / 2; |
|
| 443 |
bi += 2; |
|
| 444 |
out[0][i] = (float) t * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
|
| 445 |
} |
|
| 446 |
|
|
| 447 |
return d_period_size; |
|
| 448 |
} |
|
| 449 |
|
|
| 450 |
bool |
|
| 451 |
audio_alsa_source::read_buffer (void *vbuffer, unsigned nframes, unsigned sizeof_frame) |
|
| 452 |
{
|
|
| 453 |
unsigned char *buffer = (unsigned char *) vbuffer; |
|
| 454 |
|
|
| 455 |
while (nframes > 0){
|
|
| 456 |
int r = snd_pcm_readi (d_pcm_handle, buffer, nframes); |
|
| 457 |
if (r == -EAGAIN) |
|
| 458 |
continue; // try again |
|
| 459 |
|
|
| 460 |
else if (r == -EPIPE){ // overrun
|
|
| 461 |
d_noverruns++; |
|
| 462 |
fputs ("aO", stderr);
|
|
| 463 |
if ((r = snd_pcm_prepare (d_pcm_handle)) < 0){
|
|
| 464 |
output_error_msg ("snd_pcm_prepare failed. Can't recover from overrun", r);
|
|
| 465 |
return false; |
|
| 466 |
} |
|
| 467 |
continue; // try again |
|
| 468 |
} |
|
| 469 |
|
|
| 470 |
else if (r == -ESTRPIPE){ // h/w is suspended (whatever that means)
|
|
| 471 |
// This is apparently related to power management |
|
| 472 |
d_nsuspends++; |
|
| 473 |
if ((r = snd_pcm_resume (d_pcm_handle)) < 0){
|
|
| 474 |
output_error_msg ("failed to resume from suspend", r);
|
|
| 475 |
return false; |
|
| 476 |
} |
|
| 477 |
continue; // try again |
|
| 478 |
} |
|
| 479 |
|
|
| 480 |
else if (r < 0){
|
|
| 481 |
output_error_msg ("snd_pcm_readi failed", r);
|
|
| 482 |
return false; |
|
| 483 |
} |
|
| 484 |
|
|
| 485 |
nframes -= r; |
|
| 486 |
buffer += r * sizeof_frame; |
|
| 487 |
} |
|
| 488 |
|
|
| 489 |
return true; |
|
| 490 |
} |
|
| 491 |
|
|
| 492 |
|
|
| 493 |
void |
|
| 494 |
audio_alsa_source::output_error_msg (const char *msg, int err) |
|
| 495 |
{
|
|
| 496 |
fprintf (stderr, "audio_alsa_source[%s]: %s: %s\n", |
|
| 497 |
snd_pcm_name (d_pcm_handle), msg, snd_strerror (err)); |
|
| 498 |
} |
|
| 499 |
|
|
| 500 |
void |
|
| 501 |
audio_alsa_source::bail (const char *msg, int err) throw (std::runtime_error) |
|
| 502 |
{
|
|
| 503 |
output_error_msg (msg, err); |
|
| 504 |
throw std::runtime_error ("audio_alsa_source");
|
|
| 505 |
} |
|
| b/gr-audio/lib/alsa/audio_alsa_source.h | ||
|---|---|---|
| 1 |
/* -*- c++ -*- */ |
|
| 2 |
/* |
|
| 3 |
* Copyright 2004-2011 Free Software Foundation, Inc. |
|
| 4 |
* |
|
| 5 |
* This file is part of GNU Radio |
|
| 6 |
* |
|
| 7 |
* GNU Radio is free software; you can redistribute it and/or modify |
|
| 8 |
* it under the terms of the GNU General Public License as published by |
|
| 9 |
* the Free Software Foundation; either version 3, or (at your option) |
|
| 10 |
* any later version. |
|
| 11 |
* |
|
| 12 |
* GNU Radio is distributed in the hope that it will be useful, |
|
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 |
* GNU General Public License for more details. |
|
| 16 |
* |
|
| 17 |
* You should have received a copy of the GNU General Public License |
|
| 18 |
* along with GNU Radio; see the file COPYING. If not, write to |
|
| 19 |
* the Free Software Foundation, Inc., 51 Franklin Street, |
|
| 20 |
* Boston, MA 02110-1301, USA. |
|
| 21 |
*/ |
|
| 22 |
|
|
| 23 |
#ifndef INCLUDED_AUDIO_ALSA_SOURCE_H |
|
| 24 |
#define INCLUDED_AUDIO_ALSA_SOURCE_H |
|
| 25 |
|
|
| 26 |
// use new ALSA API |
|
| 27 |
#define ALSA_PCM_NEW_HW_PARAMS_API |
|
| 28 |
#define ALSA_PCM_NEW_SW_PARAMS_API |
|
| 29 |
|
|
| 30 |
#include <gr_audio_source.h> |
|
| 31 |
#include <string> |
|
| 32 |
#include <alsa/asoundlib.h> |
|
| 33 |
#include <stdexcept> |
|
| 34 |
|
|
| 35 |
class audio_alsa_source; |
|
| 36 |
typedef boost::shared_ptr<audio_alsa_source> audio_alsa_source_sptr; |
|
| 37 |
|
|
| 38 |
/*! |
|
| 39 |
* \brief audio source using ALSA |
|
| 40 |
* |
|
| 41 |
* The source has between 1 and N input streams of floats, where N is |
|
| 42 |
* depends on the hardware characteristics of the selected device. |
|
| 43 |
* |
|
| 44 |
* Output samples will be in the range [-1,1]. |
|
| 45 |
*/ |
|
| 46 |
class audio_alsa_source : public audio_source {
|
|
| 47 |
// typedef for pointer to class work method |
|
| 48 |
typedef int (audio_alsa_source::*work_t)(int noutput_items, |
|
| 49 |
gr_vector_const_void_star &input_items, |
|
| 50 |
gr_vector_void_star &output_items); |
|
| 51 |
|
|
| 52 |
unsigned int d_sampling_rate; |
|
| 53 |
std::string d_device_name; |
|
| 54 |
snd_pcm_t *d_pcm_handle; |
|
| 55 |
snd_pcm_hw_params_t *d_hw_params; |
|
| 56 |
snd_pcm_sw_params_t *d_sw_params; |
|
| 57 |
snd_pcm_format_t d_format; |
|
| 58 |
unsigned int d_nperiods; |
|
| 59 |
unsigned int d_period_time_us; // microseconds |
|
| 60 |
snd_pcm_uframes_t d_period_size; // in frames |
|
| 61 |
unsigned int d_buffer_size_bytes; // sizeof of d_buffer |
|
| 62 |
char *d_buffer; |
|
| 63 |
work_t d_worker; // the work method to use |
|
| 64 |
unsigned int d_hw_nchan; // # of configured h/w channels |
|
| 65 |
bool d_special_case_stereo_to_mono; |
|
| 66 |
|
|
| 67 |
// random stats |
|
| 68 |
int d_noverruns; // count of overruns |
|
| 69 |
int d_nsuspends; // count of suspends |
|
| 70 |
|
|
| 71 |
void output_error_msg (const char *msg, int err); |
|
| 72 |
void bail (const char *msg, int err) throw (std::runtime_error); |
|
| 73 |
|
|
| 74 |
public: |
|
| 75 |
audio_alsa_source (int sampling_rate, const std::string device_name, |
|
| 76 |
bool ok_to_block); |
|
| 77 |
|
|
| 78 |
~audio_alsa_source (); |
|
| 79 |
|
|
| 80 |
bool check_topology (int ninputs, int noutputs); |
|
| 81 |
|
|
| 82 |
int work (int noutput_items, |
|
| 83 |
gr_vector_const_void_star &input_items, |
|
| 84 |
gr_vector_void_star &output_items); |
|
| 85 |
|
|
| 86 |
protected: |
|
| 87 |
bool read_buffer (void *buffer, unsigned nframes, unsigned sizeof_frame); |
|
| 88 |
|
|
| 89 |
int work_s16 (int noutput_items, |
|
| 90 |
gr_vector_const_void_star &input_items, |
|
| 91 |
gr_vector_void_star &output_items); |
|
| 92 |
|
|
| 93 |
int work_s16_2x1 (int noutput_items, |
|
| 94 |
gr_vector_const_void_star &input_items, |
|
| 95 |
gr_vector_void_star &output_items); |
|
| 96 |
|
|
| 97 |
int work_s32 (int noutput_items, |
|
| 98 |
gr_vector_const_void_star &input_items, |
|
| 99 |
gr_vector_void_star &output_items); |
|
| 100 |
|
|
| 101 |
int work_s32_2x1 (int noutput_items, |
|
| 102 |
gr_vector_const_void_star &input_items, |
|
| 103 |
gr_vector_void_star &output_items); |
|
| 104 |
}; |
|
| 105 |
|
|
| 106 |
#endif /* INCLUDED_AUDIO_ALSA_SOURCE_H */ |
|
| b/gr-audio/lib/alsa/gr-audio-alsa.conf | ||
|---|---|---|
| 1 |
# This file contains system wide configuration data for GNU Radio. |
|
| 2 |
# You may override any setting on a per-user basis by editing |
|
| 3 |
# ~/.gnuradio/config.conf |
|
| 4 |
|
|
| 5 |
[audio_alsa] |
|
| 6 |
|
|
| 7 |
default_input_device = hw:0,0 |
|
| 8 |
default_output_device = hw:0,0 |
|
| 9 |
period_time = 0.010 # in seconds |
|
| 10 |
nperiods = 4 # total buffering = period_time * nperiods |
|
| 11 |
verbose = false |
|
| b/gr-audio/lib/alsa/gri_alsa.cc | ||
|---|---|---|
| 1 |
/* -*- c++ -*- */ |
|
| 2 |
/* |
|
| 3 |
* Copyright 2004 Free Software Foundation, Inc. |
|
| 4 |
* |
|
| 5 |
* This file is part of GNU Radio |
|
| 6 |
* |
|
| 7 |
* GNU Radio is free software; you can redistribute it and/or modify |
|
| 8 |
* it under the terms of the GNU General Public License as published by |
|
| 9 |
* the Free Software Foundation; either version 3, or (at your option) |
|
| 10 |
* any later version. |
|
| 11 |
* |
|
| 12 |
* GNU Radio is distributed in the hope that it will be useful, |
|
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 |
* GNU General Public License for more details. |
|
| 16 |
* |
|
| 17 |
* You should have received a copy of the GNU General Public License |
|
| 18 |
* along with GNU Radio; see the file COPYING. If not, write to |
|
| 19 |
* the Free Software Foundation, Inc., 51 Franklin Street, |
|
| 20 |
* Boston, MA 02110-1301, USA. |
|
| 21 |
*/ |
|
| 22 |
|
|
| 23 |
#ifdef HAVE_CONFIG_H |
|
| 24 |
#include "config.h" |
|
| 25 |
#endif |
|
| 26 |
|
|
| 27 |
#include <gri_alsa.h> |
|
| 28 |
#include <algorithm> |
|
| 29 |
|
|
| 30 |
static snd_pcm_access_t access_types[] = {
|
|
| 31 |
SND_PCM_ACCESS_MMAP_INTERLEAVED, |
|
| 32 |
SND_PCM_ACCESS_MMAP_NONINTERLEAVED, |
|
| 33 |
SND_PCM_ACCESS_MMAP_COMPLEX, |
|
| 34 |
SND_PCM_ACCESS_RW_INTERLEAVED, |
|
| 35 |
SND_PCM_ACCESS_RW_NONINTERLEAVED |
|
| 36 |
}; |
|
| 37 |
|
|
| 38 |
static snd_pcm_format_t format_types[] = {
|
|
| 39 |
// SND_PCM_FORMAT_UNKNOWN, |
|
| 40 |
SND_PCM_FORMAT_S8, |
|
| 41 |
SND_PCM_FORMAT_U8, |
|
| 42 |
SND_PCM_FORMAT_S16_LE, |
|
| 43 |
SND_PCM_FORMAT_S16_BE, |
|
| 44 |
SND_PCM_FORMAT_U16_LE, |
|
| 45 |
SND_PCM_FORMAT_U16_BE, |
|
| 46 |
SND_PCM_FORMAT_S24_LE, |
|
| 47 |
SND_PCM_FORMAT_S24_BE, |
|
| 48 |
SND_PCM_FORMAT_U24_LE, |
|
| 49 |
SND_PCM_FORMAT_U24_BE, |
|
| 50 |
SND_PCM_FORMAT_S32_LE, |
|
| 51 |
SND_PCM_FORMAT_S32_BE, |
|
| 52 |
SND_PCM_FORMAT_U32_LE, |
|
| 53 |
SND_PCM_FORMAT_U32_BE, |
|
| 54 |
SND_PCM_FORMAT_FLOAT_LE, |
|
| 55 |
SND_PCM_FORMAT_FLOAT_BE, |
|
| 56 |
SND_PCM_FORMAT_FLOAT64_LE, |
|
| 57 |
SND_PCM_FORMAT_FLOAT64_BE, |
|
| 58 |
SND_PCM_FORMAT_IEC958_SUBFRAME_LE, |
|
| 59 |
SND_PCM_FORMAT_IEC958_SUBFRAME_BE, |
|
| 60 |
SND_PCM_FORMAT_MU_LAW, |
|
| 61 |
SND_PCM_FORMAT_A_LAW, |
|
| 62 |
SND_PCM_FORMAT_IMA_ADPCM, |
|
| 63 |
SND_PCM_FORMAT_MPEG, |
|
| 64 |
SND_PCM_FORMAT_GSM, |
|
| 65 |
SND_PCM_FORMAT_SPECIAL, |
|
| 66 |
SND_PCM_FORMAT_S24_3LE, |
|
| 67 |
SND_PCM_FORMAT_S24_3BE, |
|
| 68 |
SND_PCM_FORMAT_U24_3LE, |
|
| 69 |
SND_PCM_FORMAT_U24_3BE, |
|
| 70 |
SND_PCM_FORMAT_S20_3LE, |
|
| 71 |
SND_PCM_FORMAT_S20_3BE, |
|
| 72 |
SND_PCM_FORMAT_U20_3LE, |
|
| 73 |
SND_PCM_FORMAT_U20_3BE, |
|
| 74 |
SND_PCM_FORMAT_S18_3LE, |
|
| 75 |
SND_PCM_FORMAT_S18_3BE, |
|
| 76 |
SND_PCM_FORMAT_U18_3LE, |
|
| 77 |
SND_PCM_FORMAT_U18_3BE |
|
| 78 |
}; |
|
| 79 |
|
|
| 80 |
static unsigned int test_rates[] = {
|
|
| 81 |
8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000 |
|
| 82 |
}; |
|
| 83 |
|
|
| 84 |
#define NELEMS(x) (sizeof(x)/sizeof(x[0])) |
|
| 85 |
|
|
| 86 |
void |
|
| 87 |
gri_alsa_dump_hw_params (snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, FILE *fp) |
|
| 88 |
{
|
|
| 89 |
fprintf (fp, "PCM name: %s\n", snd_pcm_name (pcm)); |
|
| 90 |
|
|
| 91 |
fprintf (fp, "Access types:\n"); |
|
| 92 |
for (unsigned i = 0; i < NELEMS (access_types); i++){
|
|
| 93 |
snd_pcm_access_t at = access_types[i]; |
|
| 94 |
fprintf (fp, " %-20s %s\n", |
|
| 95 |
snd_pcm_access_name (at), |
|
| 96 |
snd_pcm_hw_params_test_access (pcm, hwparams, at) == 0 ? "YES" : "NO"); |
|
| 97 |
} |
|
| 98 |
|
|
| 99 |
fprintf (fp, "Formats:\n"); |
|
| 100 |
for (unsigned i = 0; i < NELEMS (format_types); i++){
|
|
| 101 |
snd_pcm_format_t ft = format_types[i]; |
|
| 102 |
if (0) |
|
| 103 |
fprintf (fp, " %-20s %s\n", |
|
| 104 |
snd_pcm_format_name (ft), |
|
| 105 |
snd_pcm_hw_params_test_format (pcm, hwparams, ft) == 0 ? "YES" : "NO"); |
|
| 106 |
else {
|
|
| 107 |
if (snd_pcm_hw_params_test_format (pcm, hwparams, ft) == 0) |
|
| 108 |
fprintf (fp, " %-20s YES\n", snd_pcm_format_name (ft)); |
|
| 109 |
} |
|
| 110 |
} |
|
| 111 |
|
|
| 112 |
fprintf (fp, "Number of channels\n"); |
|
| 113 |
unsigned int min_chan, max_chan; |
|
| 114 |
snd_pcm_hw_params_get_channels_min (hwparams, &min_chan); |
|
| 115 |
snd_pcm_hw_params_get_channels_max (hwparams, &max_chan); |
|
| 116 |
fprintf (fp, " min channels: %d\n", min_chan); |
|
| 117 |
fprintf (fp, " max channels: %d\n", max_chan); |
|
| 118 |
unsigned int chan; |
|
| 119 |
max_chan = std::min (max_chan, 16U); // truncate display... |
|
| 120 |
for (chan = min_chan; chan <= max_chan; chan++){
|
|
| 121 |
fprintf (fp, " %d channels\t%s\n", chan, |
|
| 122 |
snd_pcm_hw_params_test_channels (pcm, hwparams, chan) == 0 ? "YES" : "NO"); |
|
| 123 |
} |
|
| 124 |
|
|
| 125 |
fprintf (fp, "Sample Rates:\n"); |
|
| 126 |
unsigned int min_rate, max_rate; |
|
| 127 |
int min_dir, max_dir; |
|
| 128 |
|
|
| 129 |
snd_pcm_hw_params_get_rate_min (hwparams, &min_rate, &min_dir); |
|
| 130 |
snd_pcm_hw_params_get_rate_max (hwparams, &max_rate, &max_dir); |
|
| 131 |
fprintf (fp, " min rate: %7d (dir = %d)\n", min_rate, min_dir); |
|
| 132 |
fprintf (fp, " max rate: %7d (dir = %d)\n", max_rate, max_dir); |
|
| 133 |
for (unsigned i = 0; i < NELEMS (test_rates); i++){
|
|
| 134 |
unsigned int rate = test_rates[i]; |
|
| 135 |
fprintf (fp, " %6u %s\n", rate, |
|
| 136 |
snd_pcm_hw_params_test_rate (pcm, hwparams, rate, 0) == 0 ? "YES" : "NO"); |
|
| 137 |
} |
|
| 138 |
|
|
| 139 |
fflush (fp); |
|
| 140 |
} |
|
| 141 |
|
|
| 142 |
bool |
|
| 143 |
gri_alsa_pick_acceptable_format (snd_pcm_t *pcm, |
|
| 144 |
snd_pcm_hw_params_t *hwparams, |
|
| 145 |
snd_pcm_format_t acceptable_formats[], |
|
| 146 |
unsigned nacceptable_formats, |
|
| 147 |
snd_pcm_format_t *selected_format, |
|
| 148 |
const char *error_msg_tag, |
|
| 149 |
bool verbose) |
|
| 150 |
{
|
|
Also available in: Unified diff