MythTV  0.26-pre
audiooutput.cpp
Go to the documentation of this file.
00001 #include <cstdio>
00002 #include <cstdlib>
00003 
00004 using namespace std;
00005 
00006 // Qt utils: to parse audio list
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         // Check if using ALSA, that the device doesn't contain the word
00112         // "pulse" in its hint
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 /*factor*/)
00215 {
00216 }
00217 
00218 AudioOutputSettings* AudioOutput::GetOutputSettingsCleaned(bool /*digital*/)
00219 {
00220     return new AudioOutputSettings;
00221 }
00222 
00223 AudioOutputSettings* AudioOutput::GetOutputSettingsUsers(bool /*digital*/)
00224 {
00225     return new AudioOutputSettings;
00226 }
00227 
00228 bool AudioOutput::CanPassthrough(int /*samplerate*/,
00229                                  int /*channels*/,
00230                                  int /*codec*/,
00231                                  int /*profile*/) const
00232 {
00233     return false;
00234 }
00235 
00236 // TODO: get rid of this if possible...  need to see what uses GetError() and
00237 //       GetWarning() and why.  These would give more useful logs as macros
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                 // We have an ELD, show actual reported capabilities
00326             capabilities += " (" + aosettings.getELD().codecs_desc() + ")";
00327         }
00328         else 
00329         {
00330                 // build capabilities string, in a similar fashion as reported
00331                 // by ELD
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends