|
MythTV
0.26-pre
|
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 }
1.7.6.1