00001 #include <cstdio>
00002 #include <cstdlib>
00003
00004 using namespace std;
00005
00006
00007 #include <QFile>
00008 #include <QDateTime>
00009 #include <QDir>
00010
00011 #include "mythconfig.h"
00012 #include "audiooutput.h"
00013 #include "mythmiscutil.h"
00014 #include "compat.h"
00015
00016 #include "audiooutputnull.h"
00017 #ifdef USING_MINGW
00018 #include "audiooutputdx.h"
00019 #include "audiooutputwin.h"
00020 #endif
00021 #ifdef USING_OSS
00022 #include "audiooutputoss.h"
00023 #endif
00024 #ifdef USING_ALSA
00025 #include "audiooutputalsa.h"
00026 #endif
00027 #if CONFIG_DARWIN
00028 #include "audiooutputca.h"
00029 #endif
00030 #ifdef USING_JACK
00031 #include "audiooutputjack.h"
00032 #endif
00033 #ifdef USING_PULSEOUTPUT
00034 #include "audiooutputpulse.h"
00035 #endif
00036 #ifdef USING_PULSE
00037 #include "audiopulsehandler.h"
00038 #endif
00039
00040 void AudioOutput::Cleanup(void)
00041 {
00042 #ifdef USING_PULSE
00043 PulseHandler::Suspend(PulseHandler::kPulseCleanup);
00044 #endif
00045 }
00046
00047 AudioOutput *AudioOutput::OpenAudio(
00048 const QString &main_device, const QString &passthru_device,
00049 AudioFormat format, int channels, int codec, int samplerate,
00050 AudioOutputSource source, bool set_initial_vol, bool passthru,
00051 int upmixer_startup, AudioOutputSettings *custom)
00052 {
00053 AudioSettings settings(
00054 main_device, passthru_device, format, channels, codec, samplerate,
00055 source, set_initial_vol, passthru, upmixer_startup, custom);
00056
00057 return OpenAudio(settings);
00058 }
00059
00060 AudioOutput *AudioOutput::OpenAudio(
00061 const QString &main_device, const QString &passthru_device,
00062 bool willsuspendpa)
00063 {
00064 AudioSettings settings(main_device, passthru_device);
00065
00066 return OpenAudio(settings, willsuspendpa);
00067 }
00068
00069 AudioOutput *AudioOutput::OpenAudio(AudioSettings &settings,
00070 bool willsuspendpa)
00071 {
00072 QString &main_device = settings.main_device;
00073 AudioOutput *ret = NULL;
00074
00075 #ifdef USING_PULSE
00076 bool pulsestatus = false;
00077 #else
00078 {
00079 static bool warned = false;
00080 if (!warned && IsPulseAudioRunning())
00081 {
00082 warned = true;
00083 LOG(VB_GENERAL, LOG_WARNING,
00084 "WARNING: ***Pulse Audio is running***");
00085 }
00086 }
00087 #endif
00088
00089 settings.FixPassThrough();
00090
00091 if (main_device.startsWith("PulseAudio:"))
00092 {
00093 #ifdef USING_PULSEOUTPUT
00094 return new AudioOutputPulseAudio(settings);
00095 #else
00096 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to PulseAudio "
00097 "but PulseAudio support is not compiled in!");
00098 return NULL;
00099 #endif
00100 }
00101 else if (main_device.startsWith("NULL"))
00102 {
00103 return new AudioOutputNULL(settings);
00104 }
00105
00106 #ifdef USING_PULSE
00107 if (willsuspendpa)
00108 {
00109 bool ispulse = false;
00110 #ifdef USING_ALSA
00111
00112
00113 if (main_device.startsWith("ALSA:"))
00114 {
00115 QString device_name = main_device;
00116
00117 device_name.remove(0, 5);
00118 QMap<QString, QString> *alsadevs =
00119 AudioOutputALSA::GetDevices("pcm");
00120 if (!alsadevs->empty() && alsadevs->contains(device_name))
00121 {
00122 if (alsadevs->value(device_name).contains("pulse",
00123 Qt::CaseInsensitive))
00124 {
00125 ispulse = true;
00126 }
00127 }
00128 delete alsadevs;
00129 }
00130 #endif
00131 if (main_device.contains("pulse", Qt::CaseInsensitive))
00132 {
00133 ispulse = true;
00134 }
00135 if (!ispulse)
00136 {
00137 pulsestatus = PulseHandler::Suspend(PulseHandler::kPulseSuspend);
00138 }
00139 }
00140 #endif
00141
00142 if (main_device.startsWith("ALSA:"))
00143 {
00144 #ifdef USING_ALSA
00145 settings.TrimDeviceType();
00146 ret = new AudioOutputALSA(settings);
00147 #else
00148 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to an ALSA device "
00149 "but ALSA support is not compiled in!");
00150 #endif
00151 }
00152 else if (main_device.startsWith("JACK:"))
00153 {
00154 #ifdef USING_JACK
00155 settings.TrimDeviceType();
00156 ret = new AudioOutputJACK(settings);
00157 #else
00158 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a JACK device "
00159 "but JACK support is not compiled in!");
00160 #endif
00161 }
00162 else if (main_device.startsWith("DirectX:"))
00163 {
00164 #ifdef USING_MINGW
00165 ret = new AudioOutputDX(settings);
00166 #else
00167 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to DirectX device "
00168 "but DirectX support is not compiled in!");
00169 #endif
00170 }
00171 else if (main_device.startsWith("Windows:"))
00172 {
00173 #ifdef USING_MINGW
00174 ret = new AudioOutputWin(settings);
00175 #else
00176 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a Windows "
00177 "device but Windows support is not compiled "
00178 "in!");
00179 #endif
00180 }
00181 #if defined(USING_OSS)
00182 else
00183 ret = new AudioOutputOSS(settings);
00184 #elif CONFIG_DARWIN
00185 else
00186 ret = new AudioOutputCA(settings);
00187 #endif
00188
00189 if (!ret)
00190 {
00191 LOG(VB_GENERAL, LOG_CRIT, "No useable audio output driver found.");
00192 LOG(VB_GENERAL, LOG_ERR, "Don't disable OSS support unless you're "
00193 "not running on Linux.");
00194 #ifdef USING_PULSE
00195 if (pulsestatus)
00196 PulseHandler::Suspend(PulseHandler::kPulseResume);
00197 #endif
00198 return NULL;
00199 }
00200 #ifdef USING_PULSE
00201 ret->pulsewassuspended = pulsestatus;
00202 #endif
00203 return ret;
00204 }
00205
00206 AudioOutput::~AudioOutput()
00207 {
00208 #ifdef USING_PULSE
00209 if (pulsewassuspended)
00210 PulseHandler::Suspend(PulseHandler::kPulseResume);
00211 #endif
00212 }
00213
00214 void AudioOutput::SetStretchFactor(float )
00215 {
00216 }
00217
00218 AudioOutputSettings* AudioOutput::GetOutputSettingsCleaned(bool )
00219 {
00220 return new AudioOutputSettings;
00221 }
00222
00223 AudioOutputSettings* AudioOutput::GetOutputSettingsUsers(bool )
00224 {
00225 return new AudioOutputSettings;
00226 }
00227
00228 bool AudioOutput::CanPassthrough(int ,
00229 int ,
00230 int ,
00231 int ) const
00232 {
00233 return false;
00234 }
00235
00236
00237
00238 void AudioOutput::Error(const QString &msg)
00239 {
00240 lastError = msg;
00241 lastError.detach();
00242 LOG(VB_GENERAL, LOG_ERR, "AudioOutput Error: " + lastError);
00243 }
00244
00245 void AudioOutput::SilentError(const QString &msg)
00246 {
00247 lastError = msg;
00248 lastError.detach();
00249 }
00250
00251 void AudioOutput::Warn(const QString &msg)
00252 {
00253 lastWarn = msg;
00254 lastWarn.detach();
00255 LOG(VB_GENERAL, LOG_WARNING, "AudioOutput Warning: " + lastWarn);
00256 }
00257
00258 void AudioOutput::ClearError(void)
00259 {
00260 lastError = QString::null;
00261 }
00262
00263 void AudioOutput::ClearWarning(void)
00264 {
00265 lastWarn = QString::null;
00266 }
00267
00268 AudioOutput::AudioDeviceConfig* AudioOutput::GetAudioDeviceConfig(
00269 QString &name, QString &desc, bool willsuspendpa)
00270 {
00271 AudioOutputSettings aosettings;
00272 AudioOutput::AudioDeviceConfig *adc;
00273
00274 AudioOutput *ao = OpenAudio(name, QString::null, willsuspendpa);
00275 aosettings = *(ao->GetOutputSettingsCleaned());
00276 delete ao;
00277
00278 if (aosettings.IsInvalid())
00279 {
00280 if (!willsuspendpa)
00281 return NULL;
00282 else
00283 {
00284 QString msg = QObject::tr("Invalid or unuseable audio device");
00285 return new AudioOutput::AudioDeviceConfig(name, msg);
00286 }
00287 }
00288
00289 QString capabilities = desc;
00290 int max_channels = aosettings.BestSupportedChannelsELD();
00291 if (aosettings.hasELD())
00292 {
00293 if (aosettings.getELD().isValid())
00294 {
00295 capabilities += QObject::tr(" (%1 connected to %2)")
00296 .arg(aosettings.getELD().product_name().simplified())
00297 .arg(aosettings.getELD().connection_name());
00298 }
00299 else
00300 {
00301 capabilities += QObject::tr(" (No connection detected)");
00302 }
00303 }
00304
00305 QString speakers;
00306 switch (max_channels)
00307 {
00308 case 6:
00309 speakers = "5.1";
00310 break;
00311 case 8:
00312 speakers = "7.1";
00313 break;
00314 default:
00315 speakers = "2.0";
00316 break;
00317 }
00318
00319 capabilities += QObject::tr("\nDevice supports up to %1")
00320 .arg(speakers);
00321 if (aosettings.canPassthrough() >= 0)
00322 {
00323 if (aosettings.hasELD() && aosettings.getELD().isValid())
00324 {
00325
00326 capabilities += " (" + aosettings.getELD().codecs_desc() + ")";
00327 }
00328 else
00329 {
00330
00331
00332 int mask = 0;
00333 mask |=
00334 (aosettings.canLPCM() << 0) |
00335 (aosettings.canAC3() << 1) |
00336 (aosettings.canDTS() << 2);
00337 static const char *type_names[] = { "LPCM", "AC3", "DTS" };
00338
00339 if (mask != 0)
00340 {
00341 capabilities += QObject::tr(" (guessing: ");
00342 bool found_one = false;
00343 for (unsigned int i = 0; i < 3; i++)
00344 {
00345 if ((mask & (1 << i)) != 0)
00346 {
00347 if (found_one)
00348 capabilities += ", ";
00349 capabilities += type_names[i];
00350 found_one = true;
00351 }
00352 }
00353 capabilities += QString(")");
00354 }
00355 }
00356 }
00357 LOG(VB_AUDIO, LOG_INFO, QString("Found %1 (%2)")
00358 .arg(name).arg(capabilities));
00359 adc = new AudioOutput::AudioDeviceConfig(name, capabilities);
00360 adc->settings = aosettings;
00361 return adc;
00362 }
00363
00364 #ifdef USING_OSS
00365 static void fillSelectionsFromDir(const QDir &dir,
00366 AudioOutput::ADCVect *list)
00367 {
00368 QFileInfoList il = dir.entryInfoList();
00369 for (QFileInfoList::Iterator it = il.begin();
00370 it != il.end(); ++it )
00371 {
00372 QFileInfo &fi = *it;
00373 QString name = fi.absoluteFilePath();
00374 QString desc = QObject::tr("OSS device");
00375 AudioOutput::AudioDeviceConfig *adc =
00376 AudioOutput::GetAudioDeviceConfig(name, desc);
00377 if (!adc)
00378 continue;
00379 list->append(*adc);
00380 delete adc;
00381 }
00382 }
00383 #endif
00384
00385 AudioOutput::ADCVect* AudioOutput::GetOutputList(void)
00386 {
00387 ADCVect *list = new ADCVect;
00388 AudioDeviceConfig *adc;
00389
00390 #ifdef USING_PULSE
00391 bool pasuspended = PulseHandler::Suspend(PulseHandler::kPulseSuspend);
00392 #endif
00393
00394 #ifdef USING_ALSA
00395 QMap<QString, QString> *alsadevs = AudioOutputALSA::GetDevices("pcm");
00396
00397 if (!alsadevs->empty())
00398 {
00399 for (QMap<QString, QString>::const_iterator i = alsadevs->begin();
00400 i != alsadevs->end(); ++i)
00401 {
00402 QString key = i.key();
00403 QString desc = i.value();
00404 QString devname = QString("ALSA:%1").arg(key);
00405
00406 adc = GetAudioDeviceConfig(devname, desc);
00407 if (!adc)
00408 continue;
00409 list->append(*adc);
00410 delete adc;
00411 }
00412 }
00413 delete alsadevs;
00414 #endif
00415 #ifdef USING_OSS
00416 {
00417 QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
00418 fillSelectionsFromDir(dev, list);
00419 dev.setNameFilters(QStringList("adsp*"));
00420 fillSelectionsFromDir(dev, list);
00421
00422 dev.setPath("/dev/sound");
00423 if (dev.exists())
00424 {
00425 dev.setNameFilters(QStringList("dsp*"));
00426 fillSelectionsFromDir(dev, list);
00427 dev.setNameFilters(QStringList("adsp*"));
00428 fillSelectionsFromDir(dev, list);
00429 }
00430 }
00431 #endif
00432 #ifdef USING_JACK
00433 {
00434 QString name = "JACK:";
00435 QString desc = QObject::tr("Use JACK default sound server.");
00436 adc = GetAudioDeviceConfig(name, desc);
00437 if (adc)
00438 {
00439 list->append(*adc);
00440 delete adc;
00441 }
00442 }
00443 #endif
00444 #if CONFIG_DARWIN
00445
00446 {
00447 QMap<QString, QString> *devs = AudioOutputCA::GetDevices(NULL);
00448 if (!devs->empty())
00449 {
00450 for (QMap<QString, QString>::const_iterator i = devs->begin();
00451 i != devs->end(); ++i)
00452 {
00453 QString key = i.key();
00454 QString desc = i.value();
00455 QString devname = QString("CoreAudio:%1").arg(key);
00456
00457 adc = GetAudioDeviceConfig(devname, desc);
00458 if (!adc)
00459 continue;
00460 list->append(*adc);
00461 delete adc;
00462 }
00463 }
00464 delete devs;
00465 QString name = "CoreAudio:Default Output Device";
00466 QString desc = QObject::tr("CoreAudio default output");
00467 adc = GetAudioDeviceConfig(name, desc);
00468 if (adc)
00469 {
00470 list->append(*adc);
00471 delete adc;
00472 }
00473 }
00474 #endif
00475 #ifdef USING_MINGW
00476 {
00477 QString name = "Windows:";
00478 QString desc = "Windows default output";
00479 adc = GetAudioDeviceConfig(name, desc);
00480 if (adc)
00481 {
00482 list->append(*adc);
00483 delete adc;
00484 }
00485
00486 QMap<int, QString> *dxdevs = AudioOutputDX::GetDXDevices();
00487
00488 if (!dxdevs->empty())
00489 {
00490 for (QMap<int, QString>::const_iterator i = dxdevs->begin();
00491 i != dxdevs->end(); ++i)
00492 {
00493 QString desc = i.value();
00494 QString devname = QString("DirectX:%1").arg(desc);
00495
00496 adc = GetAudioDeviceConfig(devname, desc);
00497 if (!adc)
00498 continue;
00499 list->append(*adc);
00500 delete adc;
00501 }
00502 }
00503 delete dxdevs;
00504 }
00505 #endif
00506
00507 #ifdef USING_PULSE
00508 if (pasuspended)
00509 PulseHandler::Suspend(PulseHandler::kPulseResume);
00510 #endif
00511
00512 #ifdef USING_PULSEOUTPUT
00513 {
00514 QString name = "PulseAudio:default";
00515 QString desc = QObject::tr("PulseAudio default sound server.");
00516 adc = GetAudioDeviceConfig(name, desc);
00517 if (adc)
00518 {
00519 list->append(*adc);
00520 delete adc;
00521 }
00522 }
00523 #endif
00524 QString name = "NULL";
00525 QString desc = "NULL device";
00526 adc = GetAudioDeviceConfig(name, desc);
00527 if (adc)
00528 {
00529 list->append(*adc);
00530 delete adc;
00531 }
00532 return list;
00533 }