00001 #include <cstdio>
00002 #include <cstdlib>
00003 #include <sys/time.h>
00004 #include <time.h>
00005 #include "config.h"
00006
00007 using namespace std;
00008
00009 #include <QFile>
00010 #include "mythcorecontext.h"
00011 #include "audiooutputalsa.h"
00012
00013 #define LOC QString("ALSA: ")
00014
00015
00016
00017 #undef assert
00018 #define assert(x)
00019
00020 #define CHANNELS_MIN 1
00021 #define CHANNELS_MAX 8
00022
00023 #define OPEN_FLAGS (SND_PCM_NO_AUTO_RESAMPLE|SND_PCM_NO_AUTO_FORMAT| \
00024 SND_PCM_NO_AUTO_CHANNELS)
00025
00026 #define FILTER_FLAGS ~(SND_PCM_NO_AUTO_FORMAT)
00027
00028 #define AERROR(str) VBERROR(str + QString(": %1").arg(snd_strerror(err)))
00029 #define CHECKERR(str) { if (err < 0) { AERROR(str); return err; } }
00030
00031 AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
00032 AudioOutputBase(settings),
00033 pcm_handle(NULL),
00034 pbufsize(-1),
00035 m_card(-1),
00036 m_device(-1),
00037 m_subdevice(-1)
00038 {
00039 m_mixer.handle = NULL;
00040 m_mixer.elem = NULL;
00041
00042
00043 if (passthru_device == "auto")
00044 {
00045 passthru_device = main_device;
00046
00047 int len = passthru_device.length();
00048 int args = passthru_device.indexOf(":");
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058 bool s48k = gCoreContext->GetNumSetting("SPDIFRateOverride", false);
00059 QString iecarg = QString("AES0=6,AES1=0x82,AES2=0x00") +
00060 (s48k ? QString() : QString(",AES3=0x01"));
00061 QString iecarg2 = QString("AES0=6 AES1=0x82 AES2=0x00") +
00062 (s48k ? QString() : QString(" AES3=0x01"));
00063
00064 if (args < 0)
00065 {
00066
00067 passthru_device += ":" + iecarg;
00068 }
00069 else
00070 {
00071 do
00072 ++args;
00073 while (args < passthru_device.length() &&
00074 passthru_device[args].isSpace());
00075 if (args == passthru_device.length())
00076 {
00077
00078 passthru_device += iecarg;
00079 }
00080 else if (passthru_device[args] != '{')
00081 {
00082
00083 passthru_device += "," + iecarg;
00084 }
00085 else
00086 {
00087
00088 do
00089 --len;
00090 while (len > 0 && passthru_device[len].isSpace());
00091 if (passthru_device[len] == '}')
00092 passthru_device =
00093 passthru_device.insert(len, " " + iecarg2);
00094 }
00095 }
00096 }
00097 else if (passthru_device.toLower() == "default")
00098 passthru_device = main_device;
00099 else
00100 m_discretedigital = true;
00101
00102 InitSettings(settings);
00103 if (settings.init)
00104 Reconfigure(settings);
00105 }
00106
00107 AudioOutputALSA::~AudioOutputALSA()
00108 {
00109 KillAudio();
00110 }
00111
00112 int AudioOutputALSA::TryOpenDevice(int open_mode, int try_ac3)
00113 {
00114 QString real_device;
00115 QByteArray dev_ba;
00116 int err = -1;
00117
00118 if (try_ac3)
00119 {
00120 dev_ba = passthru_device.toAscii();
00121 VBAUDIO(QString("OpenDevice %1 for passthrough").arg(passthru_device));
00122 err = snd_pcm_open(&pcm_handle, dev_ba.constData(),
00123 SND_PCM_STREAM_PLAYBACK, open_mode);
00124
00125 m_lastdevice = passthru_device;
00126
00127 if (m_discretedigital)
00128 return err;
00129
00130 if (err < 0)
00131 {
00132 VBAUDIO(QString("Auto setting passthrough failed (%1), defaulting "
00133 "to main device").arg(snd_strerror(err)));
00134 }
00135 }
00136 if (!try_ac3 || err < 0)
00137 {
00138
00139 VBAUDIO(QString("OpenDevice %1").arg(main_device));
00140 dev_ba = main_device.toAscii();
00141 err = snd_pcm_open(&pcm_handle, dev_ba.constData(),
00142 SND_PCM_STREAM_PLAYBACK, open_mode);
00143 m_lastdevice = main_device;
00144 }
00145 return err;
00146 }
00147
00148 int AudioOutputALSA::GetPCMInfo(int &card, int &device, int &subdevice)
00149 {
00150
00151 if (m_card != -1 && m_device != -1 && m_subdevice != -1)
00152 {
00153 card = m_card;
00154 device = m_device;
00155 subdevice = m_subdevice;
00156 return 0;
00157 }
00158
00159 if (!pcm_handle)
00160 return -1;
00161
00162 int err;
00163 snd_pcm_info_t *pcm_info = NULL;
00164 int tcard, tdevice, tsubdevice;
00165
00166 snd_pcm_info_alloca(&pcm_info);
00167
00168 err = snd_pcm_info(pcm_handle, pcm_info);
00169 CHECKERR("snd_pcm_info");
00170
00171 err = snd_pcm_info_get_card(pcm_info);
00172 CHECKERR("snd_pcm_info_get_card");
00173 tcard = err;
00174
00175 err = snd_pcm_info_get_device(pcm_info);
00176 CHECKERR("snd_pcm_info_get_device");
00177 tdevice = err;
00178
00179 err = snd_pcm_info_get_subdevice(pcm_info);
00180 CHECKERR("snd_pcm_info_get_subdevice");
00181 tsubdevice = err;
00182
00183 m_card = card = tcard;
00184 m_device = device = tdevice;
00185 m_subdevice = subdevice = tsubdevice;
00186
00187 return 0;
00188 }
00189
00190 bool AudioOutputALSA::SetPreallocBufferSize(int size)
00191 {
00192 int card, device, subdevice;
00193 bool ret = true;
00194
00195 VBERROR(QString("Setting hardware audio buffer size to %1").arg(size));
00196
00197 if (GetPCMInfo(card, device, subdevice) < 0)
00198 return false;
00199
00200
00201
00202 if (pcm_handle != NULL)
00203 CloseDevice();
00204
00205 QFile pfile(QString("/proc/asound/card%1/pcm%2p/sub%3/prealloc")
00206 .arg(card).arg(device).arg(subdevice));
00207
00208 if (!pfile.open(QIODevice::ReadWrite))
00209 {
00210 VBERROR(QString("Error opening %1: %2. ")
00211 .arg(pfile.fileName()).arg(pfile.errorString()));
00212 VBERROR(QString("Try to manually increase audio buffer with: echo %1 "
00213 "| sudo tee %2").arg(size).arg(pfile.fileName()));
00214 return false;
00215 }
00216
00217 QByteArray content_ba = QString("%1\n").arg(size).toAscii();
00218 if (pfile.write(content_ba.constData()) <= 0)
00219 {
00220 VBERROR(QString("Error writing to %1")
00221 .arg(pfile.fileName()).arg(pfile.errorString()));
00222 ret = false;
00223 }
00224
00225 pfile.close();
00226
00227 return ret;
00228 }
00229
00230 bool AudioOutputALSA::IncPreallocBufferSize(int requested, int buffer_time)
00231 {
00232 int card, device, subdevice;
00233 bool ret = true;
00234 pbufsize = 0;
00235
00236 if (GetPCMInfo(card, device, subdevice) < 0)
00237 return false;
00238
00239 const QString pf = QString("/proc/asound/card%1/pcm%2p/sub%3/prealloc")
00240 .arg(card).arg(device).arg(subdevice);
00241
00242 QFile pfile(pf);
00243 QFile mfile(pf + "_max");
00244
00245 if (!pfile.open(QIODevice::ReadOnly))
00246 {
00247 VBERROR(QString("Error opening %1. Fix reading permissions.").arg(pf));
00248 return false;
00249 }
00250
00251 if (!mfile.open(QIODevice::ReadOnly))
00252 {
00253 VBERROR(QString("Error opening %1").arg(pf + "_max"));
00254 return false;
00255 }
00256
00257 int cur = pfile.readAll().trimmed().toInt();
00258 int max = mfile.readAll().trimmed().toInt();
00259
00260 int size = ((int)(cur * (float)requested / (float)buffer_time)
00261 / 64 + 1) * 64;
00262
00263 VBAUDIO(QString("Hardware audio buffer cur: %1 need: %2 max allowed: %3")
00264 .arg(cur).arg(size).arg(max));
00265
00266 if (cur == max)
00267 {
00268
00269 pfile.close();
00270 mfile.close();
00271 return false;
00272 }
00273 if (size > max || !size)
00274 {
00275 size = max;
00276 ret = false;
00277 }
00278
00279 pfile.close();
00280 mfile.close();
00281
00282 if (SetPreallocBufferSize(size))
00283 pbufsize = cur;
00284 else
00285 ret = false;
00286
00287 return ret;
00288 }
00289
00290 QByteArray *AudioOutputALSA::GetELD(int card, int device, int subdevice)
00291 {
00292 QByteArray *result = NULL;
00293 snd_hctl_t *hctl;
00294 snd_hctl_elem_t *elem;
00295 snd_ctl_elem_info_t *info; snd_ctl_elem_info_alloca(&info);
00296 snd_ctl_elem_id_t *id; snd_ctl_elem_id_alloca(&id);
00297 snd_ctl_elem_value_t *control; snd_ctl_elem_value_alloca(&control);
00298 snd_ctl_elem_type_t type;
00299
00300 unsigned int count;
00301
00302 int err;
00303
00304 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
00305 snd_ctl_elem_id_set_name(id, "ELD");
00306 snd_ctl_elem_id_set_device(id, device);
00307 if ((err = snd_hctl_open(&hctl,
00308 QString("hw:%1").arg(card).toAscii().constData(),
00309 0)) < 0)
00310 {
00311 VBAUDIO(QString("Control %1 open error: %2")
00312 .arg(card)
00313 .arg(snd_strerror(err)));
00314 return NULL;
00315 }
00316 if ((err = snd_hctl_load(hctl)) < 0)
00317 {
00318 VBAUDIO(QString("Control %1 load error: %2")
00319 .arg(card)
00320 .arg(snd_strerror(err)));
00321
00322 return NULL;
00323 }
00324 elem = snd_hctl_find_elem(hctl, id);
00325 if (elem)
00326 {
00327 if ((err = snd_hctl_elem_info(elem, info)) < 0)
00328 {
00329 VBAUDIO(QString("Control %1 snd_hctl_elem_info error: %2")
00330 .arg(card)
00331 .arg(snd_strerror(err)));
00332 snd_hctl_close(hctl);
00333 return NULL;
00334 }
00335 count = snd_ctl_elem_info_get_count(info);
00336 type = snd_ctl_elem_info_get_type(info);
00337 if (!snd_ctl_elem_info_is_readable(info))
00338 {
00339 VBAUDIO(QString("Control %1 element info is not readable")
00340 .arg(card));
00341 snd_hctl_close(hctl);
00342 return NULL;
00343 }
00344 if ((err = snd_hctl_elem_read(elem, control)) < 0)
00345 {
00346 VBAUDIO(QString("Control %1 element read error: %2")
00347 .arg(card)
00348 .arg(snd_strerror(err)));
00349 snd_hctl_close(hctl);
00350 return NULL;
00351 }
00352 if (type != SND_CTL_ELEM_TYPE_BYTES)
00353 {
00354 VBAUDIO(QString("Control %1 element is of the wrong type")
00355 .arg(card));
00356 snd_hctl_close(hctl);
00357 return NULL;
00358 }
00359 result = new QByteArray((char *)snd_ctl_elem_value_get_bytes(control),
00360 count);
00361 }
00362 snd_hctl_close(hctl);
00363 return result;
00364 }
00365
00366 AudioOutputSettings* AudioOutputALSA::GetOutputSettings(bool passthrough)
00367 {
00368 snd_pcm_hw_params_t *params;
00369 snd_pcm_format_t afmt = SND_PCM_FORMAT_UNKNOWN;
00370 AudioFormat fmt;
00371 int rate;
00372 int err;
00373
00374 AudioOutputSettings *settings = new AudioOutputSettings();
00375
00376 if (pcm_handle)
00377 {
00378 snd_pcm_close(pcm_handle);
00379 pcm_handle = NULL;
00380 }
00381
00382 if ((err = TryOpenDevice(OPEN_FLAGS, passthrough)) < 0)
00383 {
00384 AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastdevice));
00385 delete settings;
00386 return NULL;
00387 }
00388
00389 snd_pcm_hw_params_alloca(¶ms);
00390
00391 if ((err = snd_pcm_hw_params_any(pcm_handle, params)) < 0)
00392 {
00393 snd_pcm_close(pcm_handle);
00394 if ((err = TryOpenDevice(OPEN_FLAGS&FILTER_FLAGS, passthrough)) < 0)
00395 {
00396 AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastdevice));
00397 delete settings;
00398 return NULL;
00399 }
00400 if ((err = snd_pcm_hw_params_any(pcm_handle, params)) < 0)
00401 {
00402 AERROR("No playback configurations available");
00403 snd_pcm_close(pcm_handle);
00404 pcm_handle = NULL;
00405 delete settings;
00406 return NULL;
00407 }
00408 Warn("Supported audio format detection will be inacurrate "
00409 "(using plugin?)");
00410 }
00411
00412 while ((rate = settings->GetNextRate()))
00413 if(snd_pcm_hw_params_test_rate(pcm_handle, params, rate, 0) >= 0)
00414 settings->AddSupportedRate(rate);
00415
00416 while ((fmt = settings->GetNextFormat()))
00417 {
00418 switch (fmt)
00419 {
00420 case FORMAT_U8: afmt = SND_PCM_FORMAT_U8; break;
00421 case FORMAT_S16: afmt = SND_PCM_FORMAT_S16; break;
00422 case FORMAT_S24LSB: afmt = SND_PCM_FORMAT_S24; break;
00423 case FORMAT_S24: afmt = SND_PCM_FORMAT_S32; break;
00424 case FORMAT_S32: afmt = SND_PCM_FORMAT_S32; break;
00425 case FORMAT_FLT: afmt = SND_PCM_FORMAT_FLOAT; break;
00426 default: continue;
00427 }
00428 if (snd_pcm_hw_params_test_format(pcm_handle, params, afmt) >= 0)
00429 settings->AddSupportedFormat(fmt);
00430 }
00431
00432 for (uint channels = CHANNELS_MIN; channels <= CHANNELS_MAX; channels++)
00433 if (snd_pcm_hw_params_test_channels(pcm_handle, params, channels) >= 0)
00434 settings->AddSupportedChannels(channels);
00435
00436 int card, device, subdevice;
00437 if (GetPCMInfo(card, device, subdevice) >= 0)
00438 {
00439
00440 QByteArray *eld = GetELD(card, device, subdevice);
00441 if (eld != NULL)
00442 {
00443 VBAUDIO(QString("Successfully retrieved ELD data"));
00444 settings->setELD(eld);
00445 delete eld;
00446 }
00447 }
00448 else
00449 {
00450 VBAUDIO("Can't get card and device number");
00451 }
00452
00453 snd_pcm_close(pcm_handle);
00454 pcm_handle = NULL;
00455
00456
00457
00458 QMap<QString, QString> *alsadevs = GetDevices("pcm");
00459 while(1)
00460 {
00461 QString real_device = ((passthrough && m_discretedigital) ?
00462 passthru_device : main_device);
00463
00464 QString desc = alsadevs->value(real_device);
00465
00466 settings->setPassthrough(1);
00467 if (real_device.contains("digital", Qt::CaseInsensitive) ||
00468 desc.contains("digital", Qt::CaseInsensitive))
00469 break;
00470 if (real_device.contains("iec958", Qt::CaseInsensitive))
00471 break;
00472 if (real_device.contains("spdif", Qt::CaseInsensitive))
00473 break;
00474 if (real_device.contains("hdmi", Qt::CaseInsensitive))
00475 break;
00476
00477 settings->setPassthrough(-1);
00478
00479 if (real_device.contains("pulse", Qt::CaseInsensitive) ||
00480 desc.contains("pulse", Qt::CaseInsensitive))
00481 break;
00482 if (real_device.contains("analog", Qt::CaseInsensitive) ||
00483 desc.contains("analog", Qt::CaseInsensitive))
00484 break;
00485 if (real_device.contains("surround", Qt::CaseInsensitive) ||
00486 desc.contains("surround", Qt::CaseInsensitive))
00487 break;
00488
00489 settings->setPassthrough(0);
00490 break;
00491 }
00492 delete alsadevs;
00493 return settings;
00494 }
00495
00496 bool AudioOutputALSA::OpenDevice()
00497 {
00498 snd_pcm_format_t format;
00499 uint buffer_time, period_time;
00500 int err;
00501
00502 if (pcm_handle != NULL)
00503 CloseDevice();
00504
00505 if ((err = TryOpenDevice(0, passthru || enc)) < 0)
00506 {
00507 AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastdevice));
00508 if (pcm_handle)
00509 CloseDevice();
00510 return false;
00511 }
00512
00513 switch (output_format)
00514 {
00515 case FORMAT_U8: format = SND_PCM_FORMAT_U8; break;
00516 case FORMAT_S16: format = SND_PCM_FORMAT_S16; break;
00517 case FORMAT_S24LSB: format = SND_PCM_FORMAT_S24; break;
00518 case FORMAT_S24: format = SND_PCM_FORMAT_S32; break;
00519 case FORMAT_S32: format = SND_PCM_FORMAT_S32; break;
00520 case FORMAT_FLT: format = SND_PCM_FORMAT_FLOAT; break;
00521 default:
00522 Error(QString("Unknown sample format: %1").arg(output_format));
00523 return false;
00524 }
00525
00526
00527 buffer_time = gCoreContext->GetNumSetting("ALSABufferOverride", 500) * 1000;
00528
00529 period_time = 4;
00530
00531 err = SetParameters(pcm_handle, format, channels, samplerate,
00532 buffer_time, period_time);
00533 if (err < 0)
00534 {
00535 AERROR("Unable to set ALSA parameters");
00536 CloseDevice();
00537 return false;
00538 }
00539 else if (err > 0 && pbufsize < 0)
00540 {
00541
00542
00543 if(!IncPreallocBufferSize(buffer_time, err))
00544 VBERROR("Unable to sufficiently increase ALSA hardware buffer size"
00545 " - underruns are likely");
00546 return OpenDevice();
00547 }
00548
00549 if (internal_vol && !OpenMixer())
00550 VBERROR("Unable to open audio mixer. Volume control disabled");
00551
00552
00553 return true;
00554 }
00555
00556 void AudioOutputALSA::CloseDevice()
00557 {
00558 if (m_mixer.handle)
00559 snd_mixer_close(m_mixer.handle);
00560 m_mixer.handle = NULL;
00561 if (pcm_handle)
00562 {
00563 snd_pcm_close(pcm_handle);
00564 pcm_handle = NULL;
00565 }
00566 }
00567
00568 template <class AudioDataType>
00569 static inline void _ReorderSmpteToAlsa(AudioDataType *buf, uint frames,
00570 uint extrach)
00571 {
00572 AudioDataType tmpC, tmpLFE, *buf2;
00573
00574 for (uint i = 0; i < frames; i++)
00575 {
00576 buf = buf2 = buf + 2;
00577
00578 tmpC = *buf++;
00579 tmpLFE = *buf++;
00580 *buf2++ = *buf++;
00581 *buf2++ = *buf++;
00582 *buf2++ = tmpC;
00583 *buf2++ = tmpLFE;
00584 buf += extrach;
00585 }
00586 }
00587
00588 static inline void ReorderSmpteToAlsa(void *buf, uint frames,
00589 AudioFormat format, uint extrach)
00590 {
00591 switch(AudioOutputSettings::FormatToBits(format))
00592 {
00593 case 8: _ReorderSmpteToAlsa((uchar *)buf, frames, extrach); break;
00594 case 16: _ReorderSmpteToAlsa((short *)buf, frames, extrach); break;
00595 default: _ReorderSmpteToAlsa((int *)buf, frames, extrach); break;
00596 }
00597 }
00598
00599 void AudioOutputALSA::WriteAudio(uchar *aubuf, int size)
00600 {
00601 uchar *tmpbuf = aubuf;
00602 uint frames = size / output_bytes_per_frame;
00603 int err, lw = 0;
00604
00605 if (pcm_handle == NULL)
00606 {
00607 Error("WriteAudio() called with pcm_handle == NULL!");
00608 return;
00609 }
00610
00611
00612 if (!passthru && (channels == 6 || channels == 8))
00613 {
00614 ReorderSmpteToAlsa(aubuf, frames, output_format, channels - 6);
00615 }
00616
00617 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
00618 QString("WriteAudio: Preparing %1 bytes (%2 frames)")
00619 .arg(size).arg(frames));
00620
00621 while (frames > 0)
00622 {
00623 lw = snd_pcm_writei(pcm_handle, tmpbuf, frames);
00624
00625 if (lw >= 0)
00626 {
00627 if ((uint)lw < frames)
00628 VBAUDIO(QString("WriteAudio: short write %1 bytes (ok)")
00629 .arg(lw * output_bytes_per_frame));
00630
00631 frames -= lw;
00632 tmpbuf += lw * output_bytes_per_frame;
00633 continue;
00634 }
00635
00636 err = lw;
00637
00638 switch (err)
00639 {
00640 case -EPIPE:
00641 if (snd_pcm_state(pcm_handle) == SND_PCM_STATE_XRUN)
00642 {
00643 VBAUDIO("WriteAudio: buffer underrun");
00644 if ((err = snd_pcm_prepare(pcm_handle)) < 0)
00645 {
00646 AERROR("WriteAudio: unable to recover from xrun");
00647 return;
00648 }
00649 }
00650 break;
00651
00652 case -ESTRPIPE:
00653 VBAUDIO("WriteAudio: device is suspended");
00654 while ((err = snd_pcm_resume(pcm_handle)) == -EAGAIN)
00655 usleep(200);
00656
00657 if (err < 0)
00658 {
00659 VBERROR("WriteAudio: resume failed");
00660 if ((err = snd_pcm_prepare(pcm_handle)) < 0)
00661 {
00662 AERROR("WriteAudio: unable to recover from suspend");
00663 return;
00664 }
00665 }
00666 break;
00667
00668 case -EBADFD:
00669 Error(
00670 QString("WriteAudio: device is in a bad state (state = %1)")
00671 .arg(snd_pcm_state(pcm_handle)));
00672 return;
00673
00674 default:
00675 AERROR(QString("WriteAudio: Write failed, state: %1, err")
00676 .arg(snd_pcm_state(pcm_handle)));
00677 return;
00678 }
00679 }
00680 }
00681
00682 int AudioOutputALSA::GetBufferedOnSoundcard(void) const
00683 {
00684 if (pcm_handle == NULL)
00685 {
00686 VBERROR("getBufferedOnSoundcard() called with pcm_handle == NULL!");
00687 return 0;
00688 }
00689
00690 snd_pcm_sframes_t delay = 0;
00691
00692
00693
00694 if (snd_pcm_delay(pcm_handle, &delay) < 0)
00695 return 0;
00696
00697 snd_pcm_state_t state = snd_pcm_state(pcm_handle);
00698
00699 if (state == SND_PCM_STATE_RUNNING || state == SND_PCM_STATE_DRAINING)
00700 {
00701 delay *= output_bytes_per_frame;
00702 }
00703 else
00704 {
00705 delay = 0;
00706 }
00707
00708 return delay;
00709 }
00710
00718 int AudioOutputALSA::SetParameters(snd_pcm_t *handle, snd_pcm_format_t format,
00719 uint channels, uint rate, uint buffer_time,
00720 uint period_time)
00721 {
00722 int err;
00723 snd_pcm_hw_params_t *params;
00724 snd_pcm_sw_params_t *swparams;
00725 snd_pcm_uframes_t period_size, period_size_min, period_size_max;
00726 snd_pcm_uframes_t buffer_size, buffer_size_min, buffer_size_max;
00727
00728 VBAUDIO(QString("SetParameters(format=%1, channels=%2, rate=%3, "
00729 "buffer_time=%4, period_time=%5)")
00730 .arg(format).arg(channels).arg(rate).arg(buffer_time)
00731 .arg(period_time));
00732
00733 if (handle == NULL)
00734 {
00735 Error("SetParameters() called with handle == NULL!");
00736 return -1;
00737 }
00738
00739 snd_pcm_hw_params_alloca(¶ms);
00740 snd_pcm_sw_params_alloca(&swparams);
00741
00742
00743 err = snd_pcm_hw_params_any(handle, params);
00744 CHECKERR("No playback configurations available");
00745
00746
00747 err = snd_pcm_hw_params_set_access(handle, params,
00748 SND_PCM_ACCESS_RW_INTERLEAVED);
00749 CHECKERR(QString("Interleaved RW audio not available"));
00750
00751
00752 err = snd_pcm_hw_params_set_format(handle, params, format);
00753 CHECKERR(QString("Sample format %1 not available").arg(format));
00754
00755
00756 err = snd_pcm_hw_params_set_channels(handle, params, channels);
00757 CHECKERR(QString("Channels count %1 not available").arg(channels));
00758
00759
00760 if (src_quality == QUALITY_DISABLED)
00761 {
00762 err = snd_pcm_hw_params_set_rate_resample(handle, params, 1);
00763 CHECKERR(QString("Resampling setup failed").arg(rate));
00764
00765 uint rrate = rate;
00766 err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
00767 CHECKERR(QString("Rate %1Hz not available for playback: %s").arg(rate));
00768
00769 if (rrate != rate)
00770 {
00771 VBERROR(QString("Rate doesn't match (requested %1Hz, got %2Hz)")
00772 .arg(rate).arg(err));
00773 return err;
00774 }
00775 }
00776 else
00777 {
00778 err = snd_pcm_hw_params_set_rate(handle, params, rate, 0);
00779 CHECKERR(QString("Samplerate %1 Hz not available").arg(rate));
00780 }
00781
00782
00783 err = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
00784 err = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
00785 err = snd_pcm_hw_params_get_period_size_min(params, &period_size_min, NULL);
00786 err = snd_pcm_hw_params_get_period_size_max(params, &period_size_max, NULL);
00787 VBAUDIO(QString("Buffer size range from %1 to %2")
00788 .arg(buffer_size_min)
00789 .arg(buffer_size_max));
00790 VBAUDIO(QString("Period size range from %1 to %2")
00791 .arg(period_size_min)
00792 .arg(period_size_max));
00793
00794
00795 uint original_buffer_time = buffer_time;
00796 err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
00797 &buffer_time, NULL);
00798 CHECKERR(QString("Unable to set buffer time %1").arg(buffer_time));
00799
00800
00801
00802 if ((buffer_time * 1.10f < (float)original_buffer_time) && pbufsize < 0)
00803 {
00804 VBAUDIO(QString("Requested %1us got %2 buffer time")
00805 .arg(original_buffer_time).arg(buffer_time));
00806 return buffer_time;
00807 }
00808
00809 VBAUDIO(QString("Buffer time = %1 us").arg(buffer_time));
00810
00811
00812 err = snd_pcm_hw_params_set_periods_near(handle, params,
00813 &period_time, NULL);
00814 CHECKERR(QString("Unable to set period time %1").arg(period_time));
00815 VBAUDIO(QString("Period time = %1 periods").arg(period_time));
00816
00817
00818 err = snd_pcm_hw_params(handle, params);
00819 CHECKERR("Unable to set hw params for playback");
00820
00821 err = snd_pcm_get_params(handle, &buffer_size, &period_size);
00822 CHECKERR("Unable to get PCM params");
00823 VBAUDIO(QString("Buffer size = %1 | Period size = %2")
00824 .arg(buffer_size).arg(period_size));
00825
00826
00827 soundcard_buffer_size = buffer_size * output_bytes_per_frame;
00828 fragment_size = (period_size >> 1) * output_bytes_per_frame;
00829
00830
00831 err = snd_pcm_sw_params_current(handle, swparams);
00832 CHECKERR("Unable to get current swparams");
00833
00834
00835 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period_size);
00836 CHECKERR("Unable to set start threshold");
00837
00838
00839 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
00840 CHECKERR("Unable to set avail min");
00841
00842
00843 err = snd_pcm_sw_params(handle, swparams);
00844 CHECKERR("Unable to set sw params");
00845
00846 err = snd_pcm_prepare(handle);
00847 CHECKERR("Unable to prepare the PCM");
00848
00849 return 0;
00850 }
00851
00852 int AudioOutputALSA::GetVolumeChannel(int channel) const
00853 {
00854 int retvol = 0;
00855
00856 if (!m_mixer.elem)
00857 return retvol;
00858
00859 snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
00860 if (!snd_mixer_selem_has_playback_channel(m_mixer.elem, chan))
00861 return retvol;
00862
00863 long mixervol;
00864 int chk;
00865 if ((chk = snd_mixer_selem_get_playback_volume(m_mixer.elem,
00866 chan,
00867 &mixervol)) < 0)
00868 {
00869 VBERROR(QString("failed to get channel %1 volume, mixer %2/%3: %4")
00870 .arg(channel).arg(m_mixer.device)
00871 .arg(m_mixer.control)
00872 .arg(snd_strerror(chk)));
00873 }
00874 else
00875 {
00876 retvol = (m_mixer.volrange != 0L) ? (mixervol - m_mixer.volmin) *
00877 100.0f / m_mixer.volrange + 0.5f
00878 : 0;
00879 retvol = max(retvol, 0);
00880 retvol = min(retvol, 100);
00881 VBAUDIO(QString("get volume channel %1: %2")
00882 .arg(channel).arg(retvol));
00883 }
00884 return retvol;
00885 }
00886
00887 void AudioOutputALSA::SetVolumeChannel(int channel, int volume)
00888 {
00889 if (!(internal_vol && m_mixer.elem))
00890 return;
00891
00892 long mixervol = volume * m_mixer.volrange / 100.0f - m_mixer.volmin + 0.5f;
00893 mixervol = max(mixervol, m_mixer.volmin);
00894 mixervol = min(mixervol, m_mixer.volmax);
00895
00896 snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
00897 if (snd_mixer_selem_set_playback_volume(m_mixer.elem, chan, mixervol) < 0)
00898 VBERROR(QString("failed to set channel %1 volume").arg(channel));
00899 else
00900 VBAUDIO(QString("channel %1 volume set %2 => %3")
00901 .arg(channel).arg(volume).arg(mixervol));
00902 }
00903
00904 bool AudioOutputALSA::OpenMixer(void)
00905 {
00906 if (!pcm_handle)
00907 {
00908 VBERROR("mixer setup without a pcm");
00909 return false;
00910 }
00911 m_mixer.device = gCoreContext->GetSetting("MixerDevice", "default");
00912 m_mixer.device = m_mixer.device.remove(QString("ALSA:"));
00913 if (m_mixer.device.toLower() == "software")
00914 return true;
00915
00916 m_mixer.control = gCoreContext->GetSetting("MixerControl", "PCM");
00917
00918 QString mixer_device_tag = QString("mixer device %1").arg(m_mixer.device);
00919
00920 int chk;
00921 if ((chk = snd_mixer_open(&m_mixer.handle, 0)) < 0)
00922 {
00923 VBERROR(QString("failed to open mixer device %1: %2")
00924 .arg(mixer_device_tag).arg(snd_strerror(chk)));
00925 return false;
00926 }
00927
00928 QByteArray dev_ba = m_mixer.device.toAscii();
00929 struct snd_mixer_selem_regopt regopts =
00930 {1, SND_MIXER_SABSTRACT_NONE, dev_ba.constData(), NULL, NULL};
00931
00932 if ((chk = snd_mixer_selem_register(m_mixer.handle, ®opts, NULL)) < 0)
00933 {
00934 snd_mixer_close(m_mixer.handle);
00935 m_mixer.handle = NULL;
00936 VBERROR(QString("failed to register %1: %2")
00937 .arg(mixer_device_tag).arg(snd_strerror(chk)));
00938 return false;
00939 }
00940
00941 if ((chk = snd_mixer_load(m_mixer.handle)) < 0)
00942 {
00943 snd_mixer_close(m_mixer.handle);
00944 m_mixer.handle = NULL;
00945 VBERROR(QString("failed to load %1: %2")
00946 .arg(mixer_device_tag).arg(snd_strerror(chk)));
00947 return false;
00948 }
00949
00950 m_mixer.elem = NULL;
00951 uint elcount = snd_mixer_get_count(m_mixer.handle);
00952 snd_mixer_elem_t* elx = snd_mixer_first_elem(m_mixer.handle);
00953
00954 for (uint ctr = 0; elx != NULL && ctr < elcount; ctr++)
00955 {
00956 QString tmp = QString(snd_mixer_selem_get_name(elx));
00957 if (m_mixer.control == tmp &&
00958 !snd_mixer_selem_is_enumerated(elx) &&
00959 snd_mixer_selem_has_playback_volume(elx) &&
00960 snd_mixer_selem_is_active(elx))
00961 {
00962 m_mixer.elem = elx;
00963 VBAUDIO(QString("found playback control %1 on %2")
00964 .arg(m_mixer.control)
00965 .arg(mixer_device_tag));
00966 break;
00967 }
00968 elx = snd_mixer_elem_next(elx);
00969 }
00970 if (!m_mixer.elem)
00971 {
00972 snd_mixer_close(m_mixer.handle);
00973 m_mixer.handle = NULL;
00974 VBERROR(QString("no playback control %1 found on %2")
00975 .arg(m_mixer.control).arg(mixer_device_tag));
00976 return false;
00977 }
00978 if ((snd_mixer_selem_get_playback_volume_range(m_mixer.elem,
00979 &m_mixer.volmin,
00980 &m_mixer.volmax) < 0))
00981 {
00982 snd_mixer_close(m_mixer.handle);
00983 m_mixer.handle = NULL;
00984 VBERROR(QString("failed to get volume range on %1/%2")
00985 .arg(mixer_device_tag).arg(m_mixer.control));
00986 return false;
00987 }
00988
00989 m_mixer.volrange = m_mixer.volmax - m_mixer.volmin;
00990 VBAUDIO(QString("mixer volume range on %1/%2 - min %3, max %4, range %5")
00991 .arg(mixer_device_tag).arg(m_mixer.control)
00992 .arg(m_mixer.volmin).arg(m_mixer.volmax).arg(m_mixer.volrange));
00993 VBAUDIO(QString("%1/%2 set up successfully")
00994 .arg(mixer_device_tag)
00995 .arg(m_mixer.control));
00996
00997 if (set_initial_vol)
00998 {
00999 int initial_vol;
01000 if (m_mixer.control == "PCM")
01001 initial_vol = gCoreContext->GetNumSetting("PCMMixerVolume", 80);
01002 else
01003 initial_vol = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
01004 for (int ch = 0; ch < channels; ++ch)
01005 SetVolumeChannel(ch, initial_vol);
01006 }
01007
01008 return true;
01009 }
01010
01011 QMap<QString, QString> *AudioOutputALSA::GetDevices(const char *type)
01012 {
01013 QMap<QString, QString> *alsadevs = new QMap<QString, QString>();
01014 void **hints, **n;
01015 char *name, *desc;
01016
01017 if (snd_device_name_hint(-1, type, &hints) < 0)
01018 return alsadevs;
01019 n = hints;
01020
01021 while (*n != NULL)
01022 {
01023 name = snd_device_name_get_hint(*n, "NAME");
01024 desc = snd_device_name_get_hint(*n, "DESC");
01025 if (name && desc && strcmp(name, "null"))
01026 alsadevs->insert(name, desc);
01027 if (name)
01028 free(name);
01029 if (desc)
01030 free(desc);
01031 n++;
01032 }
01033 snd_device_name_free_hint(hints);
01034
01035
01036 #if SND_LIB_MAJOR == 1
01037 #if SND_LIB_MINOR == 0
01038 #if SND_LIB_SUBMINOR < 22
01039 snd_config_update_free_global();
01040 #endif
01041 #endif
01042 #endif
01043 return alsadevs;
01044 }