MythTV  master
audiooutputoss.cpp
Go to the documentation of this file.
1 #include <cerrno>
2 #include <cstdio>
3 #include <cstdlib>
4 #include <cstring>
5 #include <ctime>
6 #include <fcntl.h>
7 #include <iostream>
8 #include <sys/ioctl.h>
9 #include <sys/time.h>
10 #include <unistd.h>
11 
12 #include "config.h"
13 
14 using namespace std;
15 
16 #define LOC QString("AOOSS: ")
17 
18 #include "mythcorecontext.h"
19 #include "audiooutputoss.h"
20 #include "mythtimer.h"
21 #include "mythdate.h"
22 
24  AudioOutputBase(settings)
25 {
26  // Set everything up
27  InitSettings(settings);
28  if (settings.m_init)
29  Reconfigure(settings);
30 }
31 
33 {
34  KillAudio();
35 }
36 
38 {
39  AudioOutputSettings *settings = new AudioOutputSettings();
40 
41  QByteArray device = m_main_device.toLatin1();
42  m_audiofd = open(device.constData(), O_WRONLY | O_NONBLOCK);
43 
44  int formats = 0;
45 
46  if (m_audiofd < 0)
47  {
48  VBERRENO(QString("Error opening audio device (%1)").arg(m_main_device));
49  delete settings;
50  return nullptr;
51  }
52 
53  while (int rate = settings->GetNextRate())
54  {
55  int rate2 = rate;
56  if(ioctl(m_audiofd, SNDCTL_DSP_SPEED, &rate2) >= 0
57  && rate2 == rate)
58  settings->AddSupportedRate(rate);
59  }
60 
61  if(ioctl(m_audiofd, SNDCTL_DSP_GETFMTS, &formats) < 0)
62  VBERRENO("Error retrieving formats");
63  else
64  {
65  int ofmt;
66 
67  while (AudioFormat fmt = settings->GetNextFormat())
68  {
69  switch (fmt)
70  {
71  case FORMAT_U8: ofmt = AFMT_U8; break;
72  case FORMAT_S16: ofmt = AFMT_S16_NE; break;
73  default: continue;
74  }
75 
76  if (formats & ofmt)
77  settings->AddSupportedFormat(fmt);
78  }
79  }
80 
81 #if defined(AFMT_AC3)
82  // Check if drivers supports AC3
83  settings->setPassthrough(((formats & AFMT_AC3) != 0) - 1);
84 #endif
85 
86  for (int i = 1; i <= 2; i++)
87  {
88  int channel = i;
89 
90  if (ioctl(m_audiofd, SNDCTL_DSP_CHANNELS, &channel) >= 0 &&
91  channel == i)
92  {
93  settings->AddSupportedChannels(i);
94  }
95  }
96 
98  m_audiofd = -1;
99 
100  return settings;
101 }
102 
104 {
105  m_numbadioctls = 0;
106 
107  MythTimer timer;
108  timer.start();
109 
110  VBAUDIO(QString("Opening OSS audio device '%1'.").arg(m_main_device));
111 
112  while (timer.elapsed() < 2000 && m_audiofd == -1)
113  {
114  QByteArray device = m_main_device.toLatin1();
115  m_audiofd = open(device.constData(), O_WRONLY);
116  if (m_audiofd < 0 && errno != EAGAIN && errno != EINTR)
117  {
118  if (errno == EBUSY)
119  {
120  VBWARN(QString("Something is currently using: %1.")
121  .arg(m_main_device));
122  return false;
123  }
124  VBERRENO(QString("Error opening audio device (%1)")
125  .arg(m_main_device));
126  }
127  if (m_audiofd < 0)
128  usleep(50);
129  }
130 
131  if (m_audiofd == -1)
132  {
133  Error(QObject::tr("Error opening audio device (%1)").arg(m_main_device));
134  VBERRENO(QString("Error opening audio device (%1)").arg(m_main_device));
135  return false;
136  }
137 
138  if (fcntl(m_audiofd, F_SETFL, fcntl(m_audiofd, F_GETFL) & ~O_NONBLOCK) == -1)
139  {
140  VBERRENO(QString("Error removing the O_NONBLOCK flag from audio device FD (%1)").arg(m_main_device));
141  }
142 
143  bool err = false;
144  int format;
145 
146  switch (m_output_format)
147  {
148  case FORMAT_U8: format = AFMT_U8; break;
149  case FORMAT_S16: format = AFMT_S16_NE; break;
150  default:
151  VBERROR(QString("Unknown sample format: %1").arg(m_output_format));
152  close(m_audiofd);
153  m_audiofd = -1;
154  return false;
155  }
156 
157 #if defined(AFMT_AC3) && defined(SNDCTL_DSP_GETFMTS)
158  if (m_passthru)
159  {
160  int format_support = 0;
161  if (!ioctl(m_audiofd, SNDCTL_DSP_GETFMTS, &format_support) &&
162  (format_support & AFMT_AC3))
163  {
164  format = AFMT_AC3;
165  }
166  }
167 #endif
168 
169  if (m_channels > 2)
170  {
171  if (ioctl(m_audiofd, SNDCTL_DSP_CHANNELS, &m_channels) < 0 ||
172  ioctl(m_audiofd, SNDCTL_DSP_SPEED, &m_samplerate) < 0 ||
173  ioctl(m_audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
174  err = true;
175  }
176  else
177  {
178  int stereo = m_channels - 1;
179  if (ioctl(m_audiofd, SNDCTL_DSP_STEREO, &stereo) < 0 ||
180  ioctl(m_audiofd, SNDCTL_DSP_SPEED, &m_samplerate) < 0 ||
181  ioctl(m_audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
182  err = true;
183  }
184 
185  if (err)
186  {
187  VBERRENO(QString("Unable to set audio device (%1) to %2 kHz, %3 bits, "
188  "%4 channels")
189  .arg(m_main_device).arg(m_samplerate)
191  .arg(m_channels));
192 
193  close(m_audiofd);
194  m_audiofd = -1;
195  return false;
196  }
197 
198  audio_buf_info info;
199  if (ioctl(m_audiofd, SNDCTL_DSP_GETOSPACE, &info) < 0)
200  VBERRENO("Error retrieving card buffer size");
201  // align by frame size
202  m_fragment_size = info.fragsize - (info.fragsize % m_output_bytes_per_frame);
203 
204  m_soundcard_buffer_size = info.bytes;
205 
206  int caps;
207 
208  if (ioctl(m_audiofd, SNDCTL_DSP_GETCAPS, &caps) == 0)
209  {
210  if (!(caps & DSP_CAP_REALTIME))
211  VBWARN("The audio device cannot report buffer state "
212  "accurately! audio/video sync will be bad, continuing...");
213  }
214  else
215  VBERRENO("Unable to get audio card capabilities");
216 
217  // Setup volume control
218  if (internal_vol)
219  VolumeInit();
220 
221  // Device opened successfully
222  return true;
223 }
224 
226 {
227  if (m_audiofd != -1)
228  close(m_audiofd);
229 
230  m_audiofd = -1;
231 
232  VolumeCleanup();
233 }
234 
235 
236 void AudioOutputOSS::WriteAudio(uchar *aubuf, int size)
237 {
238  if (m_audiofd < 0)
239  return;
240 
241  uchar *tmpbuf;
242  int written = 0, lw = 0;
243 
244  tmpbuf = aubuf;
245 
246  while ((written < size) &&
247  ((lw = write(m_audiofd, tmpbuf, size - written)) > 0))
248  {
249  written += lw;
250  tmpbuf += lw;
251  }
252 
253  if (lw < 0)
254  {
255  VBERRENO(QString("Error writing to audio device (%1)")
256  .arg(m_main_device));
257  return;
258  }
259 }
260 
261 
263 {
264  int soundcard_buffer=0;
265 //GREG This is needs to be fixed for sure!
266 #ifdef SNDCTL_DSP_GETODELAY
267  if(ioctl(m_audiofd, SNDCTL_DSP_GETODELAY, &soundcard_buffer) < 0) // bytes
268  VBERRNOCONST("Error retrieving buffering delay");
269 #endif
270  return soundcard_buffer;
271 }
272 
274 {
275  m_mixerfd = -1;
276 
277  QString device = gCoreContext->GetSetting("MixerDevice", "/dev/mixer");
278  if (device.toLower() == "software")
279  return;
280 
281  QByteArray dev = device.toLatin1();
282  m_mixerfd = open(dev.constData(), O_RDONLY);
283 
284  QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM");
285 
286  if (controlLabel == "Master")
287  m_control = SOUND_MIXER_VOLUME;
288  else
289  m_control = SOUND_MIXER_PCM;
290 
291  if (m_mixerfd < 0)
292  {
293  VBERROR(QString("Unable to open mixer: '%1'").arg(device));
294  return;
295  }
296 
297  if (m_set_initial_vol)
298  {
299  int tmpVol;
300  int volume = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
301  tmpVol = (volume << 8) + volume;
302  int ret = ioctl(m_mixerfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &tmpVol);
303  if (ret < 0)
304  VBERROR(QString("Error Setting initial Master Volume") + ENO);
305 
306  volume = gCoreContext->GetNumSetting("PCMMixerVolume", 80);
307  tmpVol = (volume << 8) + volume;
308  ret = ioctl(m_mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &tmpVol);
309  if (ret < 0)
310  VBERROR(QString("Error setting initial PCM Volume") + ENO);
311  }
312 }
313 
315 {
316  if (m_mixerfd >= 0)
317  {
318  close(m_mixerfd);
319  m_mixerfd = -1;
320  }
321 }
322 
323 int AudioOutputOSS::GetVolumeChannel(int channel) const
324 {
325  int volume=0;
326  int tmpVol=0;
327 
328  if (m_mixerfd <= 0)
329  return 100;
330 
331  int ret = ioctl(m_mixerfd, MIXER_READ(m_control), &tmpVol);
332  if (ret < 0)
333  {
334  VBERROR(QString("Error reading volume for channel %1").arg(channel));
335  return 0;
336  }
337 
338  if (channel == 0)
339  volume = tmpVol & 0xff; // left
340  else if (channel == 1)
341  volume = (tmpVol >> 8) & 0xff; // right
342  else
343  VBERROR("Invalid channel. Only stereo volume supported");
344 
345  return volume;
346 }
347 
348 void AudioOutputOSS::SetVolumeChannel(int channel, int volume)
349 {
350  if (channel > 1)
351  {
352  // Don't support more than two channels!
353  VBERROR(QString("Error setting channel %1. Only 2 ch volume supported")
354  .arg(channel));
355  return;
356  }
357 
358  if (volume > 100)
359  volume = 100;
360  if (volume < 0)
361  volume = 0;
362 
363  if (m_mixerfd >= 0)
364  {
365  int tmpVol = 0;
366  if (channel == 0)
367  tmpVol = (GetVolumeChannel(1) << 8) + volume;
368  else
369  tmpVol = (volume << 8) + GetVolumeChannel(0);
370 
371  int ret = ioctl(m_mixerfd, MIXER_WRITE(m_control), &tmpVol);
372  if (ret < 0)
373  VBERROR(QString("Error setting volume on channel %1").arg(channel));
374  }
375 }
AudioOutputOSS(const AudioSettings &settings)
def write(text, progress=True)
Definition: mythburn.py:279
void InitSettings(const AudioSettings &settings)
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
void Error(const QString &msg)
void VolumeCleanup(void)
#define O_NONBLOCK
Definition: mythmedia.cpp:25
void VolumeInit(void)
void setPassthrough(int val)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static int FormatToBits(AudioFormat format)
const char * formats[8]
Definition: vbilut.cpp:190
#define VBERROR(str)
#define VBERRNOCONST(str)
#define close
Definition: compat.h:16
#define VBWARN(str)
QString GetSetting(const QString &key, const QString &defaultval="")
void SetVolumeChannel(int channel, int volume) override
bool OpenDevice(void) override
AudioFormat m_output_format
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
bool internal_vol
Definition: volumebase.h:41
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
int GetNumSetting(const QString &key, int defaultval=0)
void AddSupportedRate(int rate)
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
AudioOutputSettings * GetOutputSettings(bool digital) override
void CloseDevice(void) override
void AddSupportedFormat(AudioFormat format)
int GetVolumeChannel(int channel) const override
~AudioOutputOSS() override
static void usleep(unsigned long time)
Definition: mthread.cpp:348
void AddSupportedChannels(int channels)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:80
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
#define VBERRENO(str)
#define VBAUDIO(str)
void WriteAudio(unsigned char *aubuf, int size) override