root / gr-audio-alsa / src / audio_alsa_source.cc @ ad13c00a
History | View | Annotate | Download (15.1 kB)
| 1 | 5d69a524 | jcorgan | /* -*- c++ -*- */
|
|---|---|---|---|
| 2 | 5d69a524 | jcorgan | /*
|
| 3 | 5d69a524 | jcorgan | * Copyright 2004,2006 Free Software Foundation, Inc. |
| 4 | 5d69a524 | jcorgan | * |
| 5 | 5d69a524 | jcorgan | * This file is part of GNU Radio |
| 6 | 5d69a524 | jcorgan | * |
| 7 | 5d69a524 | jcorgan | * GNU Radio is free software; you can redistribute it and/or modify |
| 8 | 5d69a524 | jcorgan | * it under the terms of the GNU General Public License as published by |
| 9 | 937b719d | eb | * the Free Software Foundation; either version 3, or (at your option) |
| 10 | 5d69a524 | jcorgan | * any later version. |
| 11 | 5d69a524 | jcorgan | * |
| 12 | 5d69a524 | jcorgan | * GNU Radio is distributed in the hope that it will be useful, |
| 13 | 5d69a524 | jcorgan | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | 5d69a524 | jcorgan | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | 5d69a524 | jcorgan | * GNU General Public License for more details. |
| 16 | 5d69a524 | jcorgan | * |
| 17 | 5d69a524 | jcorgan | * You should have received a copy of the GNU General Public License |
| 18 | 5d69a524 | jcorgan | * along with GNU Radio; see the file COPYING. If not, write to |
| 19 | 86f5c924 | eb | * the Free Software Foundation, Inc., 51 Franklin Street, |
| 20 | 86f5c924 | eb | * Boston, MA 02110-1301, USA. |
| 21 | 5d69a524 | jcorgan | */ |
| 22 | 5d69a524 | jcorgan | |
| 23 | 5d69a524 | jcorgan | #ifdef HAVE_CONFIG_H
|
| 24 | 5d69a524 | jcorgan | #include "config.h" |
| 25 | 5d69a524 | jcorgan | #endif
|
| 26 | 5d69a524 | jcorgan | |
| 27 | 5d69a524 | jcorgan | #include <audio_alsa_source.h> |
| 28 | 5d69a524 | jcorgan | #include <gr_io_signature.h> |
| 29 | 5d69a524 | jcorgan | #include <gr_prefs.h> |
| 30 | 5d69a524 | jcorgan | #include <stdio.h> |
| 31 | 5d69a524 | jcorgan | #include <iostream> |
| 32 | 5d69a524 | jcorgan | #include <stdexcept> |
| 33 | 5d69a524 | jcorgan | #include <gri_alsa.h> |
| 34 | 5d69a524 | jcorgan | |
| 35 | 5d69a524 | jcorgan | |
| 36 | 5d69a524 | jcorgan | static bool CHATTY_DEBUG = false; |
| 37 | 5d69a524 | jcorgan | |
| 38 | 5d69a524 | jcorgan | static snd_pcm_format_t acceptable_formats[] = {
|
| 39 | 5d69a524 | jcorgan | // these are in our preferred order...
|
| 40 | 5d69a524 | jcorgan | SND_PCM_FORMAT_S32, |
| 41 | 5d69a524 | jcorgan | SND_PCM_FORMAT_S16 |
| 42 | 5d69a524 | jcorgan | }; |
| 43 | 5d69a524 | jcorgan | |
| 44 | 5d69a524 | jcorgan | #define NELEMS(x) (sizeof(x)/sizeof(x[0])) |
| 45 | 5d69a524 | jcorgan | |
| 46 | 5d69a524 | jcorgan | |
| 47 | 5d69a524 | jcorgan | static std::string |
| 48 | 5d69a524 | jcorgan | default_device_name () |
| 49 | 5d69a524 | jcorgan | {
|
| 50 | 5d69a524 | jcorgan | return gr_prefs::singleton()->get_string("audio_alsa", "default_input_device", "hw:0,0"); |
| 51 | 5d69a524 | jcorgan | } |
| 52 | 5d69a524 | jcorgan | |
| 53 | 5d69a524 | jcorgan | static double |
| 54 | 5d69a524 | jcorgan | default_period_time () |
| 55 | 5d69a524 | jcorgan | {
|
| 56 | 5d69a524 | jcorgan | return std::max(0.001, gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010)); |
| 57 | 5d69a524 | jcorgan | } |
| 58 | 5d69a524 | jcorgan | |
| 59 | 5d69a524 | jcorgan | static int |
| 60 | 5d69a524 | jcorgan | default_nperiods () |
| 61 | 5d69a524 | jcorgan | {
|
| 62 | 5d69a524 | jcorgan | return std::max(2L, gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4)); |
| 63 | 5d69a524 | jcorgan | } |
| 64 | 5d69a524 | jcorgan | |
| 65 | 5d69a524 | jcorgan | // ----------------------------------------------------------------
|
| 66 | 5d69a524 | jcorgan | |
| 67 | 5d69a524 | jcorgan | audio_alsa_source_sptr |
| 68 | 5d69a524 | jcorgan | audio_alsa_make_source (int sampling_rate, const std::string dev, |
| 69 | 5d69a524 | jcorgan | bool ok_to_block)
|
| 70 | 5d69a524 | jcorgan | {
|
| 71 | 5d69a524 | jcorgan | return audio_alsa_source_sptr (new audio_alsa_source (sampling_rate, dev, |
| 72 | 5d69a524 | jcorgan | ok_to_block)); |
| 73 | 5d69a524 | jcorgan | } |
| 74 | 5d69a524 | jcorgan | |
| 75 | 5d69a524 | jcorgan | audio_alsa_source::audio_alsa_source (int sampling_rate,
|
| 76 | 5d69a524 | jcorgan | const std::string device_name, |
| 77 | 5d69a524 | jcorgan | bool ok_to_block)
|
| 78 | 5d69a524 | jcorgan | : gr_sync_block ("audio_alsa_source",
|
| 79 | 5d69a524 | jcorgan | gr_make_io_signature (0, 0, 0), |
| 80 | 5d69a524 | jcorgan | gr_make_io_signature (0, 0, 0)), |
| 81 | 5d69a524 | jcorgan | d_sampling_rate (sampling_rate), |
| 82 | 5d69a524 | jcorgan | d_device_name (device_name.empty() ? default_device_name() : device_name), |
| 83 | 5d69a524 | jcorgan | d_pcm_handle (0),
|
| 84 | 5d69a524 | jcorgan | d_hw_params ((snd_pcm_hw_params_t *)(new char[snd_pcm_hw_params_sizeof()])), |
| 85 | 5d69a524 | jcorgan | d_sw_params ((snd_pcm_sw_params_t *)(new char[snd_pcm_sw_params_sizeof()])), |
| 86 | 5d69a524 | jcorgan | d_nperiods (default_nperiods()), |
| 87 | 5d69a524 | jcorgan | d_period_time_us ((unsigned int) (default_period_time() * 1e6)), |
| 88 | 5d69a524 | jcorgan | d_period_size (0),
|
| 89 | 5d69a524 | jcorgan | d_buffer_size_bytes (0), d_buffer (0), |
| 90 | 5d69a524 | jcorgan | d_worker (0), d_hw_nchan (0), |
| 91 | 5d69a524 | jcorgan | d_special_case_stereo_to_mono (false),
|
| 92 | 5d69a524 | jcorgan | d_noverruns (0), d_nsuspends (0) |
| 93 | 5d69a524 | jcorgan | {
|
| 94 | 5d69a524 | jcorgan | |
| 95 | 5d69a524 | jcorgan | CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false); |
| 96 | 5d69a524 | jcorgan | |
| 97 | 5d69a524 | jcorgan | int error;
|
| 98 | 5d69a524 | jcorgan | int dir;
|
| 99 | 5d69a524 | jcorgan | |
| 100 | 5d69a524 | jcorgan | // open the device for capture
|
| 101 | 5d69a524 | jcorgan | error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str (), |
| 102 | 5d69a524 | jcorgan | SND_PCM_STREAM_CAPTURE, 0);
|
| 103 | 5d69a524 | jcorgan | if (error < 0){ |
| 104 | 5d69a524 | jcorgan | fprintf (stderr, "audio_alsa_source[%s]: %s\n",
|
| 105 | 5d69a524 | jcorgan | d_device_name.c_str(), snd_strerror(error)); |
| 106 | 5d69a524 | jcorgan | throw std::runtime_error ("audio_alsa_source"); |
| 107 | 5d69a524 | jcorgan | } |
| 108 | 5d69a524 | jcorgan | |
| 109 | 5d69a524 | jcorgan | // Fill params with a full configuration space for a PCM.
|
| 110 | 5d69a524 | jcorgan | error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); |
| 111 | 5d69a524 | jcorgan | if (error < 0) |
| 112 | 5d69a524 | jcorgan | bail ("broken configuration for playback", error);
|
| 113 | 5d69a524 | jcorgan | |
| 114 | 5d69a524 | jcorgan | if (CHATTY_DEBUG)
|
| 115 | 5d69a524 | jcorgan | gri_alsa_dump_hw_params (d_pcm_handle, d_hw_params, stdout); |
| 116 | 5d69a524 | jcorgan | |
| 117 | 5d69a524 | jcorgan | // now that we know how many channels the h/w can handle, set output signature
|
| 118 | 5d69a524 | jcorgan | unsigned int umax_chan; |
| 119 | 5d69a524 | jcorgan | unsigned int umin_chan; |
| 120 | 5d69a524 | jcorgan | snd_pcm_hw_params_get_channels_min (d_hw_params, &umin_chan); |
| 121 | 5d69a524 | jcorgan | snd_pcm_hw_params_get_channels_max (d_hw_params, &umax_chan); |
| 122 | 5d69a524 | jcorgan | int min_chan = std::min (umin_chan, 1000U); |
| 123 | 5d69a524 | jcorgan | int max_chan = std::min (umax_chan, 1000U); |
| 124 | 5d69a524 | jcorgan | |
| 125 | 5d69a524 | jcorgan | // As a special case, if the hw's min_chan is two, we'll accept
|
| 126 | 5d69a524 | jcorgan | // a single output and handle the demux ourselves.
|
| 127 | 5d69a524 | jcorgan | |
| 128 | 5d69a524 | jcorgan | if (min_chan == 2){ |
| 129 | 5d69a524 | jcorgan | min_chan = 1;
|
| 130 | 5d69a524 | jcorgan | d_special_case_stereo_to_mono = true;
|
| 131 | 5d69a524 | jcorgan | } |
| 132 | 5d69a524 | jcorgan | |
| 133 | 5d69a524 | jcorgan | set_output_signature (gr_make_io_signature (min_chan, max_chan, |
| 134 | 5d69a524 | jcorgan | sizeof (float))); |
| 135 | 5d69a524 | jcorgan | |
| 136 | 5d69a524 | jcorgan | // fill in portions of the d_hw_params that we know now...
|
| 137 | 5d69a524 | jcorgan | |
| 138 | 5d69a524 | jcorgan | // Specify the access methods we implement
|
| 139 | 5d69a524 | jcorgan | // For now, we only handle RW_INTERLEAVED...
|
| 140 | 5d69a524 | jcorgan | snd_pcm_access_mask_t *access_mask; |
| 141 | 34af4364 | jcorgan | snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning
|
| 142 | 34af4364 | jcorgan | snd_pcm_access_mask_alloca (access_mask_ptr); |
| 143 | 5d69a524 | jcorgan | snd_pcm_access_mask_none (access_mask); |
| 144 | 5d69a524 | jcorgan | snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); |
| 145 | 5d69a524 | jcorgan | // snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
|
| 146 | 5d69a524 | jcorgan | |
| 147 | 5d69a524 | jcorgan | if ((error = snd_pcm_hw_params_set_access_mask (d_pcm_handle,
|
| 148 | 5d69a524 | jcorgan | d_hw_params, access_mask)) < 0)
|
| 149 | 5d69a524 | jcorgan | bail ("failed to set access mask", error);
|
| 150 | 5d69a524 | jcorgan | |
| 151 | 5d69a524 | jcorgan | |
| 152 | 5d69a524 | jcorgan | // set sample format
|
| 153 | 5d69a524 | jcorgan | if (!gri_alsa_pick_acceptable_format (d_pcm_handle, d_hw_params,
|
| 154 | 5d69a524 | jcorgan | acceptable_formats, |
| 155 | 5d69a524 | jcorgan | NELEMS (acceptable_formats), |
| 156 | 5d69a524 | jcorgan | &d_format, |
| 157 | 5d69a524 | jcorgan | "audio_alsa_source",
|
| 158 | 5d69a524 | jcorgan | CHATTY_DEBUG)) |
| 159 | 5d69a524 | jcorgan | throw std::runtime_error ("audio_alsa_source"); |
| 160 | 5d69a524 | jcorgan | |
| 161 | 5d69a524 | jcorgan | |
| 162 | 5d69a524 | jcorgan | // sampling rate
|
| 163 | 5d69a524 | jcorgan | unsigned int orig_sampling_rate = d_sampling_rate; |
| 164 | 5d69a524 | jcorgan | if ((error = snd_pcm_hw_params_set_rate_near (d_pcm_handle, d_hw_params,
|
| 165 | 5d69a524 | jcorgan | &d_sampling_rate, 0)) < 0) |
| 166 | 5d69a524 | jcorgan | bail ("failed to set rate near", error);
|
| 167 | 5d69a524 | jcorgan | |
| 168 | 5d69a524 | jcorgan | if (orig_sampling_rate != d_sampling_rate){
|
| 169 | 5d69a524 | jcorgan | fprintf (stderr, "audio_alsa_source[%s]: unable to support sampling rate %d\n",
|
| 170 | 5d69a524 | jcorgan | snd_pcm_name (d_pcm_handle), orig_sampling_rate); |
| 171 | 5d69a524 | jcorgan | fprintf (stderr, " card requested %d instead.\n", d_sampling_rate);
|
| 172 | 5d69a524 | jcorgan | } |
| 173 | 5d69a524 | jcorgan | |
| 174 | 5d69a524 | jcorgan | /*
|
| 175 | 5d69a524 | jcorgan | * ALSA transfers data in units of "periods". |
| 176 | 5d69a524 | jcorgan | * We indirectly determine the underlying buffersize by specifying |
| 177 | 5d69a524 | jcorgan | * the number of periods we want (typically 4) and the length of each |
| 178 | 5d69a524 | jcorgan | * period in units of time (typically 1ms). |
| 179 | 5d69a524 | jcorgan | */ |
| 180 | 5d69a524 | jcorgan | unsigned int min_nperiods, max_nperiods; |
| 181 | 5d69a524 | jcorgan | snd_pcm_hw_params_get_periods_min (d_hw_params, &min_nperiods, &dir); |
| 182 | 5d69a524 | jcorgan | snd_pcm_hw_params_get_periods_max (d_hw_params, &max_nperiods, &dir); |
| 183 | 5d69a524 | jcorgan | //fprintf (stderr, "alsa_source: min_nperiods = %d, max_nperiods = %d\n",
|
| 184 | 5d69a524 | jcorgan | // min_nperiods, max_nperiods);
|
| 185 | 5d69a524 | jcorgan | |
| 186 | 5d69a524 | jcorgan | |
| 187 | 5d69a524 | jcorgan | unsigned int orig_nperiods = d_nperiods; |
| 188 | 5d69a524 | jcorgan | d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods); |
| 189 | 5d69a524 | jcorgan | |
| 190 | 5d69a524 | jcorgan | // adjust period time so that total buffering remains more-or-less constant
|
| 191 | 5d69a524 | jcorgan | d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; |
| 192 | 5d69a524 | jcorgan | |
| 193 | 5d69a524 | jcorgan | error = snd_pcm_hw_params_set_periods (d_pcm_handle, d_hw_params, |
| 194 | 5d69a524 | jcorgan | d_nperiods, 0);
|
| 195 | 5d69a524 | jcorgan | if (error < 0) |
| 196 | 5d69a524 | jcorgan | bail ("set_periods failed", error);
|
| 197 | 5d69a524 | jcorgan | |
| 198 | 5d69a524 | jcorgan | dir = 0;
|
| 199 | 5d69a524 | jcorgan | error = snd_pcm_hw_params_set_period_time_near (d_pcm_handle, d_hw_params, |
| 200 | 5d69a524 | jcorgan | &d_period_time_us, &dir); |
| 201 | 5d69a524 | jcorgan | if (error < 0) |
| 202 | 5d69a524 | jcorgan | bail ("set_period_time_near failed", error);
|
| 203 | 5d69a524 | jcorgan | |
| 204 | 5d69a524 | jcorgan | dir = 0;
|
| 205 | 5d69a524 | jcorgan | error = snd_pcm_hw_params_get_period_size (d_hw_params, |
| 206 | 5d69a524 | jcorgan | &d_period_size, &dir); |
| 207 | 5d69a524 | jcorgan | if (error < 0) |
| 208 | 5d69a524 | jcorgan | bail ("get_period_size failed", error);
|
| 209 | 5d69a524 | jcorgan | |
| 210 | 5d69a524 | jcorgan | set_output_multiple (d_period_size); |
| 211 | 5d69a524 | jcorgan | } |
| 212 | 5d69a524 | jcorgan | |
| 213 | 5d69a524 | jcorgan | bool
|
| 214 | 5d69a524 | jcorgan | audio_alsa_source::check_topology (int ninputs, int noutputs) |
| 215 | 5d69a524 | jcorgan | {
|
| 216 | 5d69a524 | jcorgan | // noutputs is how many channels the user has connected.
|
| 217 | 5d69a524 | jcorgan | // Now we can finish up setting up the hw params...
|
| 218 | 5d69a524 | jcorgan | |
| 219 | 5d69a524 | jcorgan | unsigned int nchan = noutputs; |
| 220 | 5d69a524 | jcorgan | int err;
|
| 221 | 5d69a524 | jcorgan | |
| 222 | 5d69a524 | jcorgan | // FIXME check_topology may be called more than once.
|
| 223 | 5d69a524 | jcorgan | // Ensure that the pcm is in a state where we can still mess with the hw_params
|
| 224 | 5d69a524 | jcorgan | |
| 225 | 5d69a524 | jcorgan | bool special_case = nchan == 1 && d_special_case_stereo_to_mono; |
| 226 | 5d69a524 | jcorgan | if (special_case)
|
| 227 | 5d69a524 | jcorgan | nchan = 2;
|
| 228 | 5d69a524 | jcorgan | |
| 229 | 5d69a524 | jcorgan | d_hw_nchan = nchan; |
| 230 | 5d69a524 | jcorgan | err = snd_pcm_hw_params_set_channels (d_pcm_handle, d_hw_params, d_hw_nchan); |
| 231 | 5d69a524 | jcorgan | if (err < 0){ |
| 232 | 5d69a524 | jcorgan | output_error_msg ("set_channels failed", err);
|
| 233 | 5d69a524 | jcorgan | return false; |
| 234 | 5d69a524 | jcorgan | } |
| 235 | 5d69a524 | jcorgan | |
| 236 | 5d69a524 | jcorgan | // set the parameters into the driver...
|
| 237 | 5d69a524 | jcorgan | err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); |
| 238 | 5d69a524 | jcorgan | if (err < 0){ |
| 239 | 5d69a524 | jcorgan | output_error_msg ("snd_pcm_hw_params failed", err);
|
| 240 | 5d69a524 | jcorgan | return false; |
| 241 | 5d69a524 | jcorgan | } |
| 242 | 5d69a524 | jcorgan | |
| 243 | 5d69a524 | jcorgan | d_buffer_size_bytes = |
| 244 | 5d69a524 | jcorgan | d_period_size * d_hw_nchan * snd_pcm_format_size (d_format, 1);
|
| 245 | 5d69a524 | jcorgan | |
| 246 | 5d69a524 | jcorgan | d_buffer = new char [d_buffer_size_bytes]; |
| 247 | 5d69a524 | jcorgan | |
| 248 | 5d69a524 | jcorgan | if (CHATTY_DEBUG)
|
| 249 | 5d69a524 | jcorgan | fprintf (stdout, "audio_alsa_source[%s]: sample resolution = %d bits\n",
|
| 250 | 5d69a524 | jcorgan | snd_pcm_name (d_pcm_handle), |
| 251 | 5d69a524 | jcorgan | snd_pcm_hw_params_get_sbits (d_hw_params)); |
| 252 | 5d69a524 | jcorgan | |
| 253 | 5d69a524 | jcorgan | switch (d_format){
|
| 254 | 5d69a524 | jcorgan | case SND_PCM_FORMAT_S16:
|
| 255 | 5d69a524 | jcorgan | if (special_case)
|
| 256 | 5d69a524 | jcorgan | d_worker = &audio_alsa_source::work_s16_2x1; |
| 257 | 5d69a524 | jcorgan | else
|
| 258 | 5d69a524 | jcorgan | d_worker = &audio_alsa_source::work_s16; |
| 259 | 5d69a524 | jcorgan | break;
|
| 260 | 5d69a524 | jcorgan | |
| 261 | 5d69a524 | jcorgan | case SND_PCM_FORMAT_S32:
|
| 262 | 5d69a524 | jcorgan | if (special_case)
|
| 263 | 5d69a524 | jcorgan | d_worker = &audio_alsa_source::work_s32_2x1; |
| 264 | 5d69a524 | jcorgan | else
|
| 265 | 5d69a524 | jcorgan | d_worker = &audio_alsa_source::work_s32; |
| 266 | 5d69a524 | jcorgan | break;
|
| 267 | 5d69a524 | jcorgan | |
| 268 | 5d69a524 | jcorgan | default:
|
| 269 | 5d69a524 | jcorgan | assert (0);
|
| 270 | 5d69a524 | jcorgan | } |
| 271 | 5d69a524 | jcorgan | |
| 272 | 5d69a524 | jcorgan | return true; |
| 273 | 5d69a524 | jcorgan | } |
| 274 | 5d69a524 | jcorgan | |
| 275 | 5d69a524 | jcorgan | audio_alsa_source::~audio_alsa_source () |
| 276 | 5d69a524 | jcorgan | {
|
| 277 | 5d69a524 | jcorgan | if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING)
|
| 278 | 5d69a524 | jcorgan | snd_pcm_drop (d_pcm_handle); |
| 279 | 5d69a524 | jcorgan | |
| 280 | 5d69a524 | jcorgan | snd_pcm_close(d_pcm_handle); |
| 281 | 5d69a524 | jcorgan | delete [] ((char *) d_hw_params); |
| 282 | 5d69a524 | jcorgan | delete [] ((char *) d_sw_params); |
| 283 | 5d69a524 | jcorgan | delete [] d_buffer;
|
| 284 | 5d69a524 | jcorgan | } |
| 285 | 5d69a524 | jcorgan | |
| 286 | 5d69a524 | jcorgan | int
|
| 287 | 5d69a524 | jcorgan | audio_alsa_source::work (int noutput_items,
|
| 288 | 5d69a524 | jcorgan | gr_vector_const_void_star &input_items, |
| 289 | 5d69a524 | jcorgan | gr_vector_void_star &output_items) |
| 290 | 5d69a524 | jcorgan | {
|
| 291 | 5d69a524 | jcorgan | assert ((noutput_items % d_period_size) == 0);
|
| 292 | 5d69a524 | jcorgan | assert (noutput_items != 0);
|
| 293 | 5d69a524 | jcorgan | |
| 294 | 5d69a524 | jcorgan | // this is a call through a pointer to a method...
|
| 295 | 5d69a524 | jcorgan | return (this->*d_worker)(noutput_items, input_items, output_items); |
| 296 | 5d69a524 | jcorgan | } |
| 297 | 5d69a524 | jcorgan | |
| 298 | 5d69a524 | jcorgan | /*
|
| 299 | 5d69a524 | jcorgan | * Work function that deals with float to S16 conversion |
| 300 | 5d69a524 | jcorgan | */ |
| 301 | 5d69a524 | jcorgan | int
|
| 302 | 5d69a524 | jcorgan | audio_alsa_source::work_s16 (int noutput_items,
|
| 303 | 5d69a524 | jcorgan | gr_vector_const_void_star &input_items, |
| 304 | 5d69a524 | jcorgan | gr_vector_void_star &output_items) |
| 305 | 5d69a524 | jcorgan | {
|
| 306 | 5d69a524 | jcorgan | typedef gr_int16 sample_t; // the type of samples we're creating |
| 307 | 5d69a524 | jcorgan | static const int NBITS = 16; // # of bits in a sample |
| 308 | 5d69a524 | jcorgan | |
| 309 | 5d69a524 | jcorgan | unsigned int nchan = output_items.size (); |
| 310 | 5d69a524 | jcorgan | float **out = (float **) &output_items[0]; |
| 311 | 5d69a524 | jcorgan | sample_t *buf = (sample_t *) d_buffer; |
| 312 | 5d69a524 | jcorgan | int bi;
|
| 313 | 5d69a524 | jcorgan | |
| 314 | 5d69a524 | jcorgan | unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
| 315 | 5d69a524 | jcorgan | assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
| 316 | 5d69a524 | jcorgan | |
| 317 | 5d69a524 | jcorgan | // To minimize latency, return at most a single period's worth of samples.
|
| 318 | 5d69a524 | jcorgan | // [We could also read the first one in a blocking mode and subsequent
|
| 319 | 5d69a524 | jcorgan | // ones in non-blocking mode, but we'll leave that for later (or never).]
|
| 320 | 5d69a524 | jcorgan | |
| 321 | 5d69a524 | jcorgan | if (!read_buffer (buf, d_period_size, sizeof_frame))
|
| 322 | 5d69a524 | jcorgan | return -1; // No fixing this problem. Say we're done. |
| 323 | 5d69a524 | jcorgan | |
| 324 | 5d69a524 | jcorgan | // process one period of data
|
| 325 | 5d69a524 | jcorgan | bi = 0;
|
| 326 | 5d69a524 | jcorgan | for (unsigned int i = 0; i < d_period_size; i++){ |
| 327 | 5d69a524 | jcorgan | for (unsigned int chan = 0; chan < nchan; chan++){ |
| 328 | 5d69a524 | jcorgan | out[chan][i] = (float) buf[bi++] * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
| 329 | 5d69a524 | jcorgan | } |
| 330 | 5d69a524 | jcorgan | } |
| 331 | 5d69a524 | jcorgan | |
| 332 | 5d69a524 | jcorgan | return d_period_size;
|
| 333 | 5d69a524 | jcorgan | } |
| 334 | 5d69a524 | jcorgan | |
| 335 | 5d69a524 | jcorgan | /*
|
| 336 | 5d69a524 | jcorgan | * Work function that deals with float to S16 conversion |
| 337 | 5d69a524 | jcorgan | * and stereo to mono kludge... |
| 338 | 5d69a524 | jcorgan | */ |
| 339 | 5d69a524 | jcorgan | int
|
| 340 | 5d69a524 | jcorgan | audio_alsa_source::work_s16_2x1 (int noutput_items,
|
| 341 | 5d69a524 | jcorgan | gr_vector_const_void_star &input_items, |
| 342 | 5d69a524 | jcorgan | gr_vector_void_star &output_items) |
| 343 | 5d69a524 | jcorgan | {
|
| 344 | 5d69a524 | jcorgan | typedef gr_int16 sample_t; // the type of samples we're creating |
| 345 | 5d69a524 | jcorgan | static const int NBITS = 16; // # of bits in a sample |
| 346 | 5d69a524 | jcorgan | |
| 347 | 5d69a524 | jcorgan | unsigned int nchan = output_items.size (); |
| 348 | 5d69a524 | jcorgan | float **out = (float **) &output_items[0]; |
| 349 | 5d69a524 | jcorgan | sample_t *buf = (sample_t *) d_buffer; |
| 350 | 5d69a524 | jcorgan | int bi;
|
| 351 | 5d69a524 | jcorgan | |
| 352 | 5d69a524 | jcorgan | assert (nchan == 1);
|
| 353 | 5d69a524 | jcorgan | |
| 354 | 5d69a524 | jcorgan | unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
| 355 | 5d69a524 | jcorgan | assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
| 356 | 5d69a524 | jcorgan | |
| 357 | 5d69a524 | jcorgan | // To minimize latency, return at most a single period's worth of samples.
|
| 358 | 5d69a524 | jcorgan | // [We could also read the first one in a blocking mode and subsequent
|
| 359 | 5d69a524 | jcorgan | // ones in non-blocking mode, but we'll leave that for later (or never).]
|
| 360 | 5d69a524 | jcorgan | |
| 361 | 5d69a524 | jcorgan | if (!read_buffer (buf, d_period_size, sizeof_frame))
|
| 362 | 5d69a524 | jcorgan | return -1; // No fixing this problem. Say we're done. |
| 363 | 5d69a524 | jcorgan | |
| 364 | 5d69a524 | jcorgan | // process one period of data
|
| 365 | 5d69a524 | jcorgan | bi = 0;
|
| 366 | 5d69a524 | jcorgan | for (unsigned int i = 0; i < d_period_size; i++){ |
| 367 | 5d69a524 | jcorgan | int t = (buf[bi] + buf[bi+1]) / 2; |
| 368 | 5d69a524 | jcorgan | bi += 2;
|
| 369 | 5d69a524 | jcorgan | out[0][i] = (float) t * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
| 370 | 5d69a524 | jcorgan | } |
| 371 | 5d69a524 | jcorgan | |
| 372 | 5d69a524 | jcorgan | return d_period_size;
|
| 373 | 5d69a524 | jcorgan | } |
| 374 | 5d69a524 | jcorgan | |
| 375 | 5d69a524 | jcorgan | /*
|
| 376 | 5d69a524 | jcorgan | * Work function that deals with float to S32 conversion |
| 377 | 5d69a524 | jcorgan | */ |
| 378 | 5d69a524 | jcorgan | int
|
| 379 | 5d69a524 | jcorgan | audio_alsa_source::work_s32 (int noutput_items,
|
| 380 | 5d69a524 | jcorgan | gr_vector_const_void_star &input_items, |
| 381 | 5d69a524 | jcorgan | gr_vector_void_star &output_items) |
| 382 | 5d69a524 | jcorgan | {
|
| 383 | 5d69a524 | jcorgan | typedef gr_int32 sample_t; // the type of samples we're creating |
| 384 | 5d69a524 | jcorgan | static const int NBITS = 32; // # of bits in a sample |
| 385 | 5d69a524 | jcorgan | |
| 386 | 5d69a524 | jcorgan | unsigned int nchan = output_items.size (); |
| 387 | 5d69a524 | jcorgan | float **out = (float **) &output_items[0]; |
| 388 | 5d69a524 | jcorgan | sample_t *buf = (sample_t *) d_buffer; |
| 389 | 5d69a524 | jcorgan | int bi;
|
| 390 | 5d69a524 | jcorgan | |
| 391 | 5d69a524 | jcorgan | unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
| 392 | 5d69a524 | jcorgan | assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
| 393 | 5d69a524 | jcorgan | |
| 394 | 5d69a524 | jcorgan | // To minimize latency, return at most a single period's worth of samples.
|
| 395 | 5d69a524 | jcorgan | // [We could also read the first one in a blocking mode and subsequent
|
| 396 | 5d69a524 | jcorgan | // ones in non-blocking mode, but we'll leave that for later (or never).]
|
| 397 | 5d69a524 | jcorgan | |
| 398 | 5d69a524 | jcorgan | if (!read_buffer (buf, d_period_size, sizeof_frame))
|
| 399 | 5d69a524 | jcorgan | return -1; // No fixing this problem. Say we're done. |
| 400 | 5d69a524 | jcorgan | |
| 401 | 5d69a524 | jcorgan | // process one period of data
|
| 402 | 5d69a524 | jcorgan | bi = 0;
|
| 403 | 5d69a524 | jcorgan | for (unsigned int i = 0; i < d_period_size; i++){ |
| 404 | 5d69a524 | jcorgan | for (unsigned int chan = 0; chan < nchan; chan++){ |
| 405 | 5d69a524 | jcorgan | out[chan][i] = (float) buf[bi++] * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
| 406 | 5d69a524 | jcorgan | } |
| 407 | 5d69a524 | jcorgan | } |
| 408 | 5d69a524 | jcorgan | |
| 409 | 5d69a524 | jcorgan | return d_period_size;
|
| 410 | 5d69a524 | jcorgan | } |
| 411 | 5d69a524 | jcorgan | |
| 412 | 5d69a524 | jcorgan | /*
|
| 413 | 5d69a524 | jcorgan | * Work function that deals with float to S32 conversion |
| 414 | 5d69a524 | jcorgan | * and stereo to mono kludge... |
| 415 | 5d69a524 | jcorgan | */ |
| 416 | 5d69a524 | jcorgan | int
|
| 417 | 5d69a524 | jcorgan | audio_alsa_source::work_s32_2x1 (int noutput_items,
|
| 418 | 5d69a524 | jcorgan | gr_vector_const_void_star &input_items, |
| 419 | 5d69a524 | jcorgan | gr_vector_void_star &output_items) |
| 420 | 5d69a524 | jcorgan | {
|
| 421 | 5d69a524 | jcorgan | typedef gr_int32 sample_t; // the type of samples we're creating |
| 422 | 5d69a524 | jcorgan | static const int NBITS = 32; // # of bits in a sample |
| 423 | 5d69a524 | jcorgan | |
| 424 | 5d69a524 | jcorgan | unsigned int nchan = output_items.size (); |
| 425 | 5d69a524 | jcorgan | float **out = (float **) &output_items[0]; |
| 426 | 5d69a524 | jcorgan | sample_t *buf = (sample_t *) d_buffer; |
| 427 | 5d69a524 | jcorgan | int bi;
|
| 428 | 5d69a524 | jcorgan | |
| 429 | 5d69a524 | jcorgan | assert (nchan == 1);
|
| 430 | 5d69a524 | jcorgan | |
| 431 | 5d69a524 | jcorgan | unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); |
| 432 | 5d69a524 | jcorgan | assert (d_buffer_size_bytes == d_period_size * sizeof_frame); |
| 433 | 5d69a524 | jcorgan | |
| 434 | 5d69a524 | jcorgan | // To minimize latency, return at most a single period's worth of samples.
|
| 435 | 5d69a524 | jcorgan | // [We could also read the first one in a blocking mode and subsequent
|
| 436 | 5d69a524 | jcorgan | // ones in non-blocking mode, but we'll leave that for later (or never).]
|
| 437 | 5d69a524 | jcorgan | |
| 438 | 5d69a524 | jcorgan | if (!read_buffer (buf, d_period_size, sizeof_frame))
|
| 439 | 5d69a524 | jcorgan | return -1; // No fixing this problem. Say we're done. |
| 440 | 5d69a524 | jcorgan | |
| 441 | 5d69a524 | jcorgan | // process one period of data
|
| 442 | 5d69a524 | jcorgan | bi = 0;
|
| 443 | 5d69a524 | jcorgan | for (unsigned int i = 0; i < d_period_size; i++){ |
| 444 | 5d69a524 | jcorgan | int t = (buf[bi] + buf[bi+1]) / 2; |
| 445 | 5d69a524 | jcorgan | bi += 2;
|
| 446 | 5d69a524 | jcorgan | out[0][i] = (float) t * (1.0 / (float) ((1L << (NBITS-1)) - 1)); |
| 447 | 5d69a524 | jcorgan | } |
| 448 | 5d69a524 | jcorgan | |
| 449 | 5d69a524 | jcorgan | return d_period_size;
|
| 450 | 5d69a524 | jcorgan | } |
| 451 | 5d69a524 | jcorgan | |
| 452 | 5d69a524 | jcorgan | bool
|
| 453 | 5d69a524 | jcorgan | audio_alsa_source::read_buffer (void *vbuffer, unsigned nframes, unsigned sizeof_frame) |
| 454 | 5d69a524 | jcorgan | {
|
| 455 | 5d69a524 | jcorgan | unsigned char *buffer = (unsigned char *) vbuffer; |
| 456 | 5d69a524 | jcorgan | |
| 457 | 5d69a524 | jcorgan | while (nframes > 0){ |
| 458 | 5d69a524 | jcorgan | int r = snd_pcm_readi (d_pcm_handle, buffer, nframes);
|
| 459 | 5d69a524 | jcorgan | if (r == -EAGAIN)
|
| 460 | 5d69a524 | jcorgan | continue; // try again |
| 461 | 5d69a524 | jcorgan | |
| 462 | 5d69a524 | jcorgan | else if (r == -EPIPE){ // overrun |
| 463 | 5d69a524 | jcorgan | d_noverruns++; |
| 464 | 5d69a524 | jcorgan | fputs ("aO", stderr);
|
| 465 | 5d69a524 | jcorgan | if ((r = snd_pcm_prepare (d_pcm_handle)) < 0){ |
| 466 | 5d69a524 | jcorgan | output_error_msg ("snd_pcm_prepare failed. Can't recover from overrun", r);
|
| 467 | 5d69a524 | jcorgan | return false; |
| 468 | 5d69a524 | jcorgan | } |
| 469 | 5d69a524 | jcorgan | continue; // try again |
| 470 | 5d69a524 | jcorgan | } |
| 471 | 5d69a524 | jcorgan | |
| 472 | 5d69a524 | jcorgan | else if (r == -ESTRPIPE){ // h/w is suspended (whatever that means) |
| 473 | 5d69a524 | jcorgan | // This is apparently related to power management
|
| 474 | 5d69a524 | jcorgan | d_nsuspends++; |
| 475 | 5d69a524 | jcorgan | if ((r = snd_pcm_resume (d_pcm_handle)) < 0){ |
| 476 | 5d69a524 | jcorgan | output_error_msg ("failed to resume from suspend", r);
|
| 477 | 5d69a524 | jcorgan | return false; |
| 478 | 5d69a524 | jcorgan | } |
| 479 | 5d69a524 | jcorgan | continue; // try again |
| 480 | 5d69a524 | jcorgan | } |
| 481 | 5d69a524 | jcorgan | |
| 482 | 5d69a524 | jcorgan | else if (r < 0){ |
| 483 | 5d69a524 | jcorgan | output_error_msg ("snd_pcm_readi failed", r);
|
| 484 | 5d69a524 | jcorgan | return false; |
| 485 | 5d69a524 | jcorgan | } |
| 486 | 5d69a524 | jcorgan | |
| 487 | 5d69a524 | jcorgan | nframes -= r; |
| 488 | 5d69a524 | jcorgan | buffer += r * sizeof_frame; |
| 489 | 5d69a524 | jcorgan | } |
| 490 | 5d69a524 | jcorgan | |
| 491 | 5d69a524 | jcorgan | return true; |
| 492 | 5d69a524 | jcorgan | } |
| 493 | 5d69a524 | jcorgan | |
| 494 | 5d69a524 | jcorgan | |
| 495 | 5d69a524 | jcorgan | void
|
| 496 | 5d69a524 | jcorgan | audio_alsa_source::output_error_msg (const char *msg, int err) |
| 497 | 5d69a524 | jcorgan | {
|
| 498 | 5d69a524 | jcorgan | fprintf (stderr, "audio_alsa_source[%s]: %s: %s\n",
|
| 499 | 5d69a524 | jcorgan | snd_pcm_name (d_pcm_handle), msg, snd_strerror (err)); |
| 500 | 5d69a524 | jcorgan | } |
| 501 | 5d69a524 | jcorgan | |
| 502 | 5d69a524 | jcorgan | void
|
| 503 | 5d69a524 | jcorgan | audio_alsa_source::bail (const char *msg, int err) throw (std::runtime_error) |
| 504 | 5d69a524 | jcorgan | {
|
| 505 | 5d69a524 | jcorgan | output_error_msg (msg, err); |
| 506 | 5d69a524 | jcorgan | throw std::runtime_error ("audio_alsa_source"); |
| 507 | 5d69a524 | jcorgan | } |