MythTV  master
audioinputalsa.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007 Anand K. Mistry
3  * Copyright (C) 2007 Daniel Kristjansson
4  * Copyright (C) 2009 Alan Calvert
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21 
23 #include "audioinputalsa.h"
24 
25 #define LOC QString("AudioInALSA: ")
26 #define LOC_DEV QString("AudioInALSA(%1): ").arg(m_alsaDevice.constData())
27 
28 bool AudioInputALSA::Open(uint sample_bits, uint sample_rate, uint channels)
29 {
30  if (m_alsaDevice.isEmpty())
31  {
32  LOG(VB_GENERAL, LOG_ERR, LOC + QString("invalid alsa device name, %1")
33  .arg(m_alsaDevice.constData()));
34  return false;
35  }
36  (void)AlsaBad(snd_config_update_free_global(), "failed to update snd config");
37  m_audioSampleRate = sample_rate;
38  m_audioChannels = channels;
39  m_audioSampleBits = sample_bits;
40  if (AlsaBad(snd_pcm_open(&m_pcmHandle, m_alsaDevice.constData(),
41  SND_PCM_STREAM_CAPTURE, 0), // blocking mode
42  "pcm open failed"))
43  {
44  m_pcmHandle = nullptr;
45  return false;
46  }
47  if (!(PrepHwParams() && PrepSwParams()))
48  {
49  (void)snd_pcm_close(m_pcmHandle);
50  m_pcmHandle = nullptr;
51  return false;
52  }
53  LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "pcm open");
54  return true;
55 }
56 
58 {
59  if (m_pcmHandle != nullptr)
60  {
61  Stop();
62  (void)AlsaBad(snd_pcm_close(m_pcmHandle), "Close close failed");
63  }
64  m_pcmHandle = nullptr;
65 }
66 
68 {
69  bool stopped = false;
70  if (m_pcmHandle != nullptr &&
71  !AlsaBad(snd_pcm_drop(m_pcmHandle), "Stop drop failed"))
72  {
73  stopped = true;
74  LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "capture stopped");
75  }
76  return stopped;
77 }
78 
79 int AudioInputALSA::GetSamples(void* buf, uint nbytes)
80 {
81  if (!m_pcmHandle)
82  return 0;
83  int bytes_read = 0;
84  int pcm_state = snd_pcm_state(m_pcmHandle);
85  switch (pcm_state)
86  {
87  case SND_PCM_STATE_XRUN:
88  case SND_PCM_STATE_SUSPENDED:
89  {
90  bool recov = Stop() && Start();
91  LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "xrun recovery " +
92  (recov ? "good" : "not good"));
93  if (!recov)
94  break;
95  }
96  [[clang::fallthrough]];
97  case SND_PCM_STATE_PREPARED:
98  if (AlsaBad(snd_pcm_start(m_pcmHandle), "pcm start failed"))
99  break;
100  [[clang::fallthrough]];
101  case SND_PCM_STATE_RUNNING:
102  bytes_read = PcmRead(buf, nbytes);
103  break;
104  default:
105  LOG(VB_AUDIO, LOG_ERR, LOC_DEV +
106  QString("weird pcm state through GetSamples, %1")
107  .arg(pcm_state));
108  break;
109  }
110  return bytes_read;
111 }
112 
114 {
115  int bytes_avail = 0;
116  if (m_pcmHandle != nullptr)
117  {
118  snd_pcm_sframes_t frames_avail = 0;
119  int pcm_state = snd_pcm_state(m_pcmHandle);
120  switch (pcm_state)
121  {
122  case SND_PCM_STATE_PREPARED:
123  case SND_PCM_STATE_RUNNING:
124  if (!AlsaBad((frames_avail = snd_pcm_avail_update(m_pcmHandle)),
125  "GetNumReadyBytes, available update failed"))
126  bytes_avail = snd_pcm_frames_to_bytes(m_pcmHandle,
127  frames_avail);
128  }
129  }
130  return bytes_avail;
131 }
132 
134 {
135  snd_pcm_hw_params_t* hwparams = nullptr;
136  snd_pcm_hw_params_alloca(&hwparams);
137  if (AlsaBad(snd_pcm_hw_params_any(m_pcmHandle, hwparams),
138  "failed to init hw params"))
139  return false;
140  snd_pcm_access_t axs = SND_PCM_ACCESS_RW_INTERLEAVED; //always?
141  if (AlsaBad(snd_pcm_hw_params_set_access(m_pcmHandle, hwparams, axs),
142  "failed to set interleaved rw io"))
143  return false;
144  snd_pcm_format_t format =
145  (m_audioSampleBits > 8) ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8;
146  if (AlsaBad(snd_pcm_hw_params_set_format(m_pcmHandle, hwparams, format),
147  QString("failed to set sample format %1")
148  .arg(snd_pcm_format_description(format))))
149  return false;
150  if (VERBOSE_LEVEL_CHECK(VB_AUDIO, LOG_DEBUG))
151  {
152  uint min_chans = 0;
153  uint max_chans = 0;
154  if(AlsaBad(snd_pcm_hw_params_get_channels_min(hwparams, &min_chans),
155  QString("unable to get min channel count")))
156  min_chans = 0;
157  if(AlsaBad(snd_pcm_hw_params_get_channels_max(hwparams, &max_chans),
158  QString("unable to get max channel count")))
159  max_chans = 0;
160  LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV +
161  QString("min channels %1, max channels %2, myth requests %3")
162  .arg(min_chans).arg(max_chans).arg(m_audioChannels));
163  }
164  if (AlsaBad(snd_pcm_hw_params_set_channels(m_pcmHandle, hwparams,
165  m_audioChannels), QString("failed to set channels to %1")
166  .arg(m_audioChannels)))
167  {
168  return false;
169  }
170  if (AlsaBad(snd_pcm_hw_params_set_rate(m_pcmHandle, hwparams,
171  m_audioSampleRate, 0), QString("failed to set sample rate %1")
172  .arg(m_audioSampleRate)))
173  {
174  uint rate_num = 0;
175  uint rate_den = 0;
176  if (!AlsaBad(snd_pcm_hw_params_get_rate_numden(hwparams, &rate_num,
177  &rate_den), "snd_pcm_hw_params_get_rate_numden failed"))
178  {
179  if (m_audioSampleRate != (int)(rate_num / rate_den))
180  {
181  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
182  QString("device reports sample rate as %1")
183  .arg(rate_num / rate_den));
184  }
185  }
186  return false;
187  }
188  uint buffer_time = 64000; // 64 msec
189  uint period_time = buffer_time / 4;
190  if (AlsaBad(snd_pcm_hw_params_set_period_time_near(m_pcmHandle, hwparams, &period_time, nullptr),
191  "failed to set period time"))
192  return false;
193  if (AlsaBad(snd_pcm_hw_params_set_buffer_time_near(m_pcmHandle, hwparams, &buffer_time, nullptr),
194  "failed to set buffer time"))
195  return false;
196  if (AlsaBad(snd_pcm_hw_params_get_period_size(hwparams, &m_periodSize, nullptr),
197  "failed to get period size"))
198  return false;
199 
200  if (AlsaBad(snd_pcm_hw_params (m_pcmHandle, hwparams),
201  "failed to set hwparams"))
202  return false;
203 
204  m_mythBlockBytes = snd_pcm_frames_to_bytes(m_pcmHandle, m_periodSize);
205  LOG(VB_AUDIO, LOG_INFO, LOC_DEV +
206  QString("channels %1, sample rate %2, buffer_time %3 msec, period "
207  "size %4").arg(m_audioChannels)
208  .arg(m_audioSampleRate).arg(buffer_time / 1000.0, -1, 'f', 1)
209  .arg(m_periodSize));
210  LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV + QString("myth block size %1")
211  .arg(m_mythBlockBytes));
212  return true;
213 }
214 
216 {
217  snd_pcm_sw_params_t* swparams = nullptr;
218  snd_pcm_sw_params_alloca(&swparams);
219  snd_pcm_uframes_t boundary = 0;
220  if (AlsaBad(snd_pcm_sw_params_current(m_pcmHandle, swparams),
221  "failed to get swparams"))
222  return false;
223  if (AlsaBad(snd_pcm_sw_params_get_boundary(swparams, &boundary),
224  "failed to get boundary"))
225  return false;
226  // explicit start, not auto start
227  if (AlsaBad(snd_pcm_sw_params_set_start_threshold(m_pcmHandle, swparams,
228  boundary), "failed to set start threshold"))
229  return false;
230  if (AlsaBad(snd_pcm_sw_params_set_stop_threshold(m_pcmHandle, swparams,
231  boundary), "failed to set stop threshold"))
232  return false;
233  if (AlsaBad(snd_pcm_sw_params(m_pcmHandle, swparams),
234  "failed to set software parameters"))
235  return false;
236 
237  return true;
238 }
239 
240 int AudioInputALSA::PcmRead(void* buf, uint nbytes)
241 {
242  auto* bufptr = (unsigned char*)buf;
243  snd_pcm_uframes_t to_read = snd_pcm_bytes_to_frames(m_pcmHandle, nbytes);
244  snd_pcm_uframes_t nframes = to_read;
245  snd_pcm_sframes_t nread = 0;
246  snd_pcm_sframes_t avail = 0;
247  int retries = 0;
248  while (nframes > 0 && retries < 3)
249  {
250  if (AlsaBad((avail = snd_pcm_avail_update(m_pcmHandle)),
251  "available update failed"))
252  {
253  if (!Recovery(avail))
254  {
255  ++retries;
256  continue;
257  }
258  }
259  if ((nread = snd_pcm_readi(m_pcmHandle, bufptr, nframes)) < 0)
260  {
261  switch (nread)
262  {
263  case -EAGAIN:
264  break;
265  case -EBADFD:
266  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
267  QString("in a state unfit to read (%1): %2")
268  .arg(nread).arg(snd_strerror(nread)));
269  break;
270  case -EINTR:
271  case -EPIPE:
272 #if ESTRPIPE != EPIPE
273  case -ESTRPIPE:
274 #endif
275  Recovery(nread);
276  break;
277  default:
278  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
279  QString("weird return from snd_pcm_readi: %1")
280  .arg(snd_strerror(nread)));
281  break;
282  }
283  }
284  else
285  {
286  nframes -= nread;
287  bufptr += snd_pcm_frames_to_bytes(m_pcmHandle, nread);
288  }
289  ++retries;
290  }
291  if (nframes > 0)
292  {
293  LOG(VB_AUDIO, LOG_ERR, LOC_DEV +
294  QString("short pcm read, %1 of %2 frames, retries %3")
295  .arg(to_read - nframes).arg(to_read).arg(retries));
296  }
297  return snd_pcm_frames_to_bytes(m_pcmHandle, to_read - nframes);
298 }
299 
301 {
302  if (err > 0)
303  err = -err;
304  bool isgood = false;
305  bool suspense = false;
306  switch (err)
307  {
308  case -EINTR:
309  isgood = true; // nothin' to see here
310  break;
311 #if ESTRPIPE != EPIPE
312  case -ESTRPIPE:
313  suspense = true;
314  [[clang::fallthrough]];
315 #endif
316  case -EPIPE:
317  {
318  int ret = snd_pcm_prepare(m_pcmHandle);
319  if (ret < 0)
320  {
321  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
322  QString("failed to recover from %1. %2")
323  .arg(suspense ? "suspend" : "underrun",
324  snd_strerror(ret)));
325  return false;
326  }
327  isgood = true;
328  break;
329  }
330  default:
331  break;
332  }
333  return isgood;
334 }
335 
336 bool AudioInputALSA::AlsaBad(int op_result, const QString& errmsg)
337 { // (op_result < 0) => return true
338  bool bad = (op_result < 0);
339  if (bad)
340  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
341  errmsg + ": " + snd_strerror(op_result));
342  return bad;
343 }
344 
345 /* vim: set expandtab tabstop=4 shiftwidth=4: */
audioinputalsa.h
AudioInputALSA::m_periodSize
snd_pcm_uframes_t m_periodSize
Definition: audioinputalsa.h:64
AudioInputALSA::PrepSwParams
bool PrepSwParams(void)
Definition: audioinputalsa.cpp:215
AudioInput::m_audioChannels
int m_audioChannels
Definition: audioinput.h:51
LOC
#define LOC
Definition: audioinputalsa.cpp:25
AudioInputALSA::Recovery
bool Recovery(int err)
Definition: audioinputalsa.cpp:300
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
mythlogging.h
AudioInputALSA::m_mythBlockBytes
int m_mythBlockBytes
Definition: audioinputalsa.h:65
AudioInputALSA::m_pcmHandle
snd_pcm_t * m_pcmHandle
Definition: audioinputalsa.h:63
AudioInputALSA::PcmRead
int PcmRead(void *buf, uint nbytes)
Definition: audioinputalsa.cpp:240
AudioInputALSA::GetSamples
int GetSamples(void *buf, uint nbytes) override
Definition: audioinputalsa.cpp:79
AudioInputALSA::GetNumReadyBytes
int GetNumReadyBytes(void) override
Definition: audioinputalsa.cpp:113
AudioInputALSA::Stop
bool Stop(void) override
Definition: audioinputalsa.cpp:67
AudioInput::m_audioSampleRate
int m_audioSampleRate
Definition: audioinput.h:53
AudioInput::m_audioSampleBits
int m_audioSampleBits
Definition: audioinput.h:52
uint
unsigned int uint
Definition: compat.h:79
AudioInputALSA::PrepHwParams
bool PrepHwParams(void)
Definition: audioinputalsa.cpp:133
VERBOSE_LEVEL_CHECK
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:14
AudioInputALSA::AlsaBad
bool AlsaBad(int op_result, const QString &errmsg)
Definition: audioinputalsa.cpp:336
AudioInputALSA::Close
void Close(void) override
Definition: audioinputalsa.cpp:57
LOC_DEV
#define LOC_DEV
Definition: audioinputalsa.cpp:26
AudioInputALSA::Start
bool Start(void) override
Definition: audioinputalsa.h:46
AudioInputALSA::m_alsaDevice
QByteArray m_alsaDevice
Definition: audioinputalsa.h:62
AudioInputALSA::Open
bool Open(uint sample_bits, uint sample_rate, uint channels) override
Definition: audioinputalsa.cpp:28