00001 #include <cstdio>
00002 #include <cstdlib>
00003 #include <fcntl.h>
00004 #include <sys/time.h>
00005 #include <unistd.h>
00006 #include <time.h>
00007
00008 #include <sys/ioctl.h>
00009 #include <cerrno>
00010 #include <cstring>
00011
00012 #include <iostream>
00013 #include <qdatetime.h>
00014 #include "config.h"
00015
00016 #ifdef HAVE_SYS_SOUNDCARD_H
00017 #include <sys/soundcard.h>
00018 #elif HAVE_SOUNDCARD_H
00019 #include <soundcard.h>
00020 #endif
00021
00022 using namespace std;
00023
00024 #include "mythcontext.h"
00025 #include "audiooutputoss.h"
00026 #include "util.h"
00027
00028 AudioOutputOSS::AudioOutputOSS(
00029 QString laudio_main_device, QString laudio_passthru_device,
00030 int laudio_bits, int laudio_channels,
00031 int laudio_samplerate, AudioOutputSource lsource,
00032 bool lset_initial_vol, bool laudio_passthru) :
00033 AudioOutputBase(laudio_main_device, laudio_passthru_device,
00034 laudio_bits, laudio_channels,
00035 laudio_samplerate, lsource,
00036 lset_initial_vol, laudio_passthru),
00037 audiofd(-1), numbadioctls(0),
00038 mixerfd(-1), control(SOUND_MIXER_VOLUME)
00039 {
00040
00041 Reconfigure(laudio_bits, laudio_channels,
00042 laudio_samplerate, laudio_passthru);
00043 }
00044
00045 AudioOutputOSS::~AudioOutputOSS()
00046 {
00047 KillAudio();
00048 }
00049
00050 bool AudioOutputOSS::OpenDevice()
00051 {
00052 numbadioctls = 0;
00053
00054 MythTimer timer;
00055 timer.start();
00056
00057 VERBOSE(VB_GENERAL, QString("Opening OSS audio device '%1'.")
00058 .arg(audio_main_device));
00059
00060 while (timer.elapsed() < 2000 && audiofd == -1)
00061 {
00062 audiofd = open(audio_main_device.ascii(), O_WRONLY | O_NONBLOCK);
00063 if (audiofd < 0 && errno != EAGAIN && errno != EINTR)
00064 {
00065 if (errno == EBUSY)
00066 {
00067 Error(QString("WARNING: something is currently"
00068 " using: %1, retrying.").arg(audio_main_device));
00069 return false;
00070 }
00071 VERBOSE(VB_IMPORTANT, QString("Error opening audio device (%1), the"
00072 " error was: %2").arg(audio_main_device).arg(strerror(errno)));
00073 perror(audio_main_device.ascii());
00074 }
00075 if (audiofd < 0)
00076 usleep(50);
00077 }
00078
00079 if (audiofd == -1)
00080 {
00081 Error(QString("Error opening audio device (%1), the error was: %2")
00082 .arg(audio_main_device).arg(strerror(errno)));
00083 return false;
00084 }
00085
00086 fcntl(audiofd, F_SETFL, fcntl(audiofd, F_GETFL) & ~O_NONBLOCK);
00087
00088 SetFragSize();
00089
00090 bool err = false;
00091 int format;
00092
00093 switch (audio_bits)
00094 {
00095 case 8:
00096 format = AFMT_S8;
00097 break;
00098 case 16:
00099 #ifdef WORDS_BIGENDIAN
00100 format = AFMT_S16_BE;
00101 #else
00102 format = AFMT_S16_LE;
00103 #endif
00104 break;
00105 default: Error(QString("AudioOutputOSS() - Illegal bitsize of %1")
00106 .arg(audio_bits));
00107 }
00108
00109 #if defined(AFMT_AC3) && defined(SNDCTL_DSP_GETFMTS)
00110 if (audio_passthru)
00111 {
00112 int format_support;
00113 if (!ioctl(audiofd, SNDCTL_DSP_GETFMTS, &format_support) &&
00114 (format_support & AFMT_AC3))
00115 {
00116 format = AFMT_AC3;
00117 }
00118 }
00119 #endif
00120
00121 if (audio_channels > 2)
00122 {
00123 if (ioctl(audiofd, SNDCTL_DSP_SAMPLESIZE, &audio_bits) < 0 ||
00124 ioctl(audiofd, SNDCTL_DSP_CHANNELS, &audio_channels) < 0 ||
00125 ioctl(audiofd, SNDCTL_DSP_SPEED, &audio_samplerate) < 0 ||
00126 ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
00127 err = true;
00128 }
00129 else
00130 {
00131 int stereo = audio_channels - 1;
00132 if (ioctl(audiofd, SNDCTL_DSP_SAMPLESIZE, &audio_bits) < 0 ||
00133 ioctl(audiofd, SNDCTL_DSP_STEREO, &stereo) < 0 ||
00134 ioctl(audiofd, SNDCTL_DSP_SPEED, &audio_samplerate) < 0 ||
00135 ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
00136 err = true;
00137 }
00138
00139 if (err)
00140 {
00141 Error(QString("Unable to set audio device (%1) to %2 kHz / %3 bits"
00142 " / %4 channels").arg(audio_main_device).arg(audio_samplerate)
00143 .arg(audio_bits).arg(audio_channels));
00144 close(audiofd);
00145 audiofd = -1;
00146 return false;
00147 }
00148
00149 audio_buf_info info;
00150 ioctl(audiofd, SNDCTL_DSP_GETOSPACE, &info);
00151 fragment_size = info.fragsize;
00152
00153 audio_buffer_unused = info.bytes - (fragment_size * 4);
00154 soundcard_buffer_size = info.bytes;
00155
00156 int caps;
00157
00158 if (ioctl(audiofd, SNDCTL_DSP_GETCAPS, &caps) == 0)
00159 {
00160 if (!(caps & DSP_CAP_REALTIME))
00161 {
00162 VERBOSE(VB_IMPORTANT, "The audio device cannot report buffer state"
00163 " accurately! audio/video sync will be bad, continuing...");
00164 }
00165 } else {
00166 VERBOSE(VB_IMPORTANT, QString("Unable to get audio card capabilities,"
00167 " the error was: %1").arg(strerror(errno)));
00168 }
00169
00170
00171 if (internal_vol)
00172 VolumeInit();
00173
00174
00175 return true;
00176 }
00177
00182 void AudioOutputOSS::SetFragSize()
00183 {
00184
00185
00186
00187 const int video_frame_rate = 30;
00188 const int bits_per_byte = 8;
00189
00190
00191 int fbytes = (audio_bits * audio_channels * audio_samplerate) /
00192 (bits_per_byte * video_frame_rate);
00193
00194
00195
00196 int count = 0;
00197 while ( fbytes >> 1 )
00198 {
00199 fbytes >>= 1;
00200 count++;
00201 }
00202
00203 if (count > 4)
00204 {
00205
00206 int frag = 0x7fff0000 + count;
00207 ioctl(audiofd, SNDCTL_DSP_SETFRAGMENT, &frag);
00208
00209 }
00210 }
00211
00212 void AudioOutputOSS::CloseDevice()
00213 {
00214 if (audiofd != -1)
00215 close(audiofd);
00216
00217 audiofd = -1;
00218
00219 VolumeCleanup();
00220 }
00221
00222
00223 void AudioOutputOSS::WriteAudio(unsigned char *aubuf, int size)
00224 {
00225 if (audiofd < 0)
00226 return;
00227
00228 unsigned char *tmpbuf;
00229 int written = 0, lw = 0;
00230
00231 tmpbuf = aubuf;
00232
00233 while ((written < size) &&
00234 ((lw = write(audiofd, tmpbuf, size - written)) > 0))
00235 {
00236 written += lw;
00237 tmpbuf += lw;
00238 }
00239
00240 if (lw < 0)
00241 {
00242 Error(QString("Error writing to audio device (%1), unable to"
00243 " continue. The error was: %2").arg(audio_main_device)
00244 .arg(strerror(errno)));
00245 close(audiofd);
00246 audiofd = -1;
00247 return;
00248 }
00249 }
00250
00251
00252 inline int AudioOutputOSS::getBufferedOnSoundcard(void)
00253 {
00254 int soundcard_buffer=0;
00255
00256 #ifdef SNDCTL_DSP_GETODELAY
00257 ioctl(audiofd, SNDCTL_DSP_GETODELAY, &soundcard_buffer);
00258 #endif
00259 return soundcard_buffer;
00260 }
00261
00262
00263 inline int AudioOutputOSS::getSpaceOnSoundcard(void)
00264 {
00265 audio_buf_info info;
00266 int space = 0;
00267
00268 ioctl(audiofd, SNDCTL_DSP_GETOSPACE, &info);
00269 space = info.bytes - audio_buffer_unused;
00270
00271 if (space < 0)
00272 {
00273 numbadioctls++;
00274 if (numbadioctls > 2 || space < -5000)
00275 {
00276 VERBOSE(VB_IMPORTANT, "Your soundcard is not reporting free space"
00277 " correctly. Falling back to old method...");
00278 audio_buffer_unused = 0;
00279 space = info.bytes;
00280 }
00281 }
00282 else
00283 numbadioctls = 0;
00284
00285 return space;
00286 }
00287
00288 void AudioOutputOSS::VolumeInit()
00289 {
00290 mixerfd = -1;
00291 int volume = 0;
00292
00293 QString device = gContext->GetSetting("MixerDevice", "/dev/mixer");
00294 mixerfd = open(device.ascii(), O_RDONLY);
00295
00296 QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
00297
00298 if (controlLabel == "Master")
00299 {
00300 control = SOUND_MIXER_VOLUME;
00301 }
00302 else
00303 {
00304 control = SOUND_MIXER_PCM;
00305 }
00306
00307 if (mixerfd < 0)
00308 {
00309 cerr << "Unable to open mixer: '" << device << "'\n";
00310 return;
00311 }
00312
00313 if (set_initial_vol)
00314 {
00315 int tmpVol;
00316 volume = gContext->GetNumSetting("MasterMixerVolume", 80);
00317 tmpVol = (volume << 8) + volume;
00318 int ret = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &tmpVol);
00319 if (ret < 0) {
00320 VERBOSE(VB_IMPORTANT, QString("Error Setting initial Master Volume"));
00321 perror("Setting master volume: ");
00322 }
00323
00324 volume = gContext->GetNumSetting("PCMMixerVolume", 80);
00325 tmpVol = (volume << 8) + volume;
00326 ret = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &tmpVol);
00327 if (ret < 0) {
00328 VERBOSE(VB_IMPORTANT, QString("Error setting initial PCM Volume"));
00329 perror("Setting PCM volume: ");
00330 }
00331 }
00332 }
00333
00334 void AudioOutputOSS::VolumeCleanup()
00335 {
00336 if (mixerfd >= 0)
00337 {
00338 close(mixerfd);
00339 mixerfd = -1;
00340 }
00341 }
00342
00343 int AudioOutputOSS::GetVolumeChannel(int channel)
00344 {
00345 int volume=0;
00346 int tmpVol=0;
00347
00348 if (mixerfd <= 0)
00349 return 100;
00350
00351 int ret = ioctl(mixerfd, MIXER_READ(control), &tmpVol);
00352 if (ret < 0) {
00353 VERBOSE(VB_IMPORTANT, QString("Error reading volume for channel %1")
00354 .arg(channel));
00355 perror("Reading PCM volume: ");
00356 return 0;
00357 }
00358
00359 if (channel == 0) {
00360 volume = tmpVol & 0xff;
00361 } else if (channel == 1) {
00362 volume = (tmpVol >> 8) & 0xff;
00363 } else {
00364 VERBOSE(VB_IMPORTANT, QString("Invalid channel. Only stereo volume supported"));
00365 }
00366
00367 return volume;
00368 }
00369
00370 void AudioOutputOSS::SetVolumeChannel(int channel, int volume)
00371 {
00372 if (channel > 1) {
00373
00374 VERBOSE(VB_IMPORTANT, QString("Error setting channel: %1. Only stereo volume supported")
00375 .arg(channel));
00376 return;
00377 }
00378
00379 if (volume > 100)
00380 volume = 100;
00381 if (volume < 0)
00382 volume = 0;
00383
00384 if (mixerfd >= 0)
00385 {
00386 int tmpVol = 0;
00387 if (channel == 0)
00388 tmpVol = (GetVolumeChannel(1) << 8) + volume;
00389 else
00390 tmpVol = (volume << 8) + GetVolumeChannel(0);
00391
00392 int ret = ioctl(mixerfd, MIXER_WRITE(control), &tmpVol);
00393 if (ret < 0)
00394 {
00395 VERBOSE(VB_IMPORTANT, QString("Error setting volume on channel: %1").arg(channel));
00396 perror("Setting volume: ");
00397 }
00398 }
00399 }
00400