Ticket #5342: alt_alsamixercontrol-v1.patch

File alt_alsamixercontrol-v1.patch, 15.6 KB (added by Alan Calvert <cal@…>, 11 years ago)

@18258

  • libs/libmyth/audiooutputalsa.h

    old new  
    99
    1010using namespace std;
    1111
    12 class ALSAVolumeInfo
    13 {
    14   public:
    15     ALSAVolumeInfo(long  playback_vol_min,
    16                    long  playback_vol_max) :
    17         range_multiplier(1.0f),
    18         volume_min(playback_vol_min), volume_max(playback_vol_max)
    19     {
    20         float range = (float) (volume_max - volume_min);
    21         if (range > 0.0f)
    22             range_multiplier = 100.0f / range;
    23         range_multiplier_inv = 1.0f / range_multiplier;
    24     }
    25 
    26     int ToMythRange(long alsa_volume)
    27     {
    28         long toz = alsa_volume - volume_min;
    29         int val = (int) (toz * range_multiplier);
    30         val = (val < 0)   ? 0   : val;
    31         val = (val > 100) ? 100 : val;
    32         return val;
    33     }
    34 
    35     long ToALSARange(int myth_volume)
    36     {
    37         float tos = myth_volume * range_multiplier_inv;
    38         long val = (long) (tos + volume_min + 0.5);
    39         val = (val < volume_min) ? volume_min : val;
    40         val = (val > volume_max) ? volume_max : val;
    41         return val;
    42     }
    43 
    44     float range_multiplier;
    45     float range_multiplier_inv;
    46     long  volume_min;
    47     long  volume_max;
    48 };
    49 
    5012class AudioOutputALSA : public AudioOutputBase
    5113{
    5214  public:
     
    7234                             unsigned int rate, unsigned int buffer_time,
    7335                             unsigned int period_time);
    7436
    75 
    7637    // Volume related
    77     void SetCurrentVolume(QString control, int channel, int volume);
    78     void OpenMixer(bool setstartingvolume);
    79     void CloseMixer(void);
    80     void SetupMixer(void);
    81     ALSAVolumeInfo GetVolumeRange(snd_mixer_elem_t *elem) const;
     38    bool OpenMixer(void);
    8239
    8340  private:
    8441    snd_pcm_t   *pcm_handle;
    8542    int          numbadioctls;
    8643    QMutex       killAudioLock;
    87     snd_mixer_t *mixer_handle;
    88     QString      mixer_control; // e.g. "PCM"
    8944    snd_pcm_sframes_t (*pcm_write_func)(snd_pcm_t*, const void*,
    9045                                        snd_pcm_uframes_t);
     46    struct {
     47        QByteArray         device;
     48        QByteArray         control;
     49        snd_mixer_t*       handle;
     50        snd_mixer_elem_t*  elem;
     51        long               volmin;
     52        long               volmax;
     53        long               volrange;
     54    } mixer;
     55
    9156};
    9257
    9358#endif
  • libs/libmyth/audiooutputalsa.cpp

    old new  
    2121AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
    2222    AudioOutputBase(settings),
    2323    pcm_handle(NULL),             numbadioctls(0),
    24     killAudioLock(false),         mixer_handle(NULL),
    25     mixer_control(QString::null)
     24    killAudioLock(false)
    2625{
     26    mixer.handle = NULL;
     27    mixer.elem = NULL;
     28
    2729    // Set everything up
    2830    Reconfigure(settings);
    2931}
     
    127129    // it really is
    128130    audio_buffer_unused = soundcard_buffer_size - (fragment_size * 4);
    129131
    130     if (internal_vol)
    131         OpenMixer(set_initial_vol);
    132    
     132    if (internal_vol && !OpenMixer())
     133    {
     134        CloseDevice();
     135        return false;
     136    }
     137
    133138    // Device opened successfully
    134139    return true;
    135140}
    136141
    137142void AudioOutputALSA::CloseDevice()
    138143{
    139     CloseMixer();
     144    if (mixer.handle != NULL)
     145        snd_mixer_close(mixer.handle);
     146    mixer.handle = NULL;
    140147    if (pcm_handle != NULL)
    141148    {
    142149        snd_pcm_close(pcm_handle);
     
    475482    return 0;
    476483}
    477484
    478 
    479485int AudioOutputALSA::GetVolumeChannel(int channel) const
    480486{
    481     long actual_volume;
    482 
    483     if (mixer_handle == NULL)
    484         return 100;
    485 
    486     QByteArray mix_ctl = mixer_control.toAscii();
    487     snd_mixer_selem_id_t *sid;
    488     snd_mixer_selem_id_alloca(&sid);
    489     snd_mixer_selem_id_set_index(sid, 0);
    490     snd_mixer_selem_id_set_name(sid, mix_ctl.constData());
    491 
    492     snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer_handle, sid);
    493     if (!elem)
    494     {
    495         VERBOSE(VB_IMPORTANT, QString("Mixer unable to find control %1")
    496                 .arg(mixer_control));
    497         return 100;
    498     }
     487    if (!mixer.elem)
     488        return 0;
    499489
    500     snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
    501     if (!snd_mixer_selem_has_playback_channel(elem, chan))
     490    int retvol = 0;
     491    long mixervol;
     492    int chk;
     493    if ((chk = snd_mixer_selem_get_playback_volume(mixer.elem,
     494                 (snd_mixer_selem_channel_id_t)channel, &mixervol)) < 0)
     495        VERBOSE(VB_IMPORTANT, LOC_ERR
     496                + QString("failed to get channel %1 volume, mixer %2/%3: %4")
     497                          .arg(channel).arg(mixer.device.constData())
     498                          .arg(mixer.control.constData())
     499                          .arg(snd_strerror(chk)));
     500    else
    502501    {
    503         snd_mixer_selem_id_set_index(sid, channel);
    504         if ((elem = snd_mixer_find_selem(mixer_handle, sid)) == NULL)
    505         {
    506             VERBOSE(VB_IMPORTANT, QString("Mixer unable to find control %1 %2")
    507                     .arg(mixer_control).arg(channel));
    508             return 100;
    509         }
     502        retvol = (mixer.volrange != 0L) ? (mixervol - mixer.volmin) * 100.0f
     503                                          / mixer.volrange + .5f
     504                                        : 0;
     505        retvol = max(retvol, 0);
     506        retvol = min(retvol, 100);
     507        VERBOSE(VB_AUDIO+VB_EXTRA, LOC + QString("get volume channel %1: %2")
     508                                                 .arg(channel).arg(retvol));
    510509    }
    511 
    512     ALSAVolumeInfo vinfo = GetVolumeRange(elem);
    513 
    514     snd_mixer_selem_get_playback_volume(
    515         elem, (snd_mixer_selem_channel_id_t)channel, &actual_volume);
    516 
    517     return vinfo.ToMythRange(actual_volume);
     510    return retvol;
    518511}
    519512
    520513void AudioOutputALSA::SetVolumeChannel(int channel, int volume)
    521514{
    522     SetCurrentVolume(mixer_control, channel, volume);
    523 }
    524 
    525 void AudioOutputALSA::SetCurrentVolume(QString control, int channel, int volume)
    526 {
    527     VERBOSE(VB_AUDIO, QString("Setting %1 volume to %2")
    528             .arg(control).arg(volume));
    529 
    530     if (!mixer_handle)
    531         return; // no mixer, nothing to do
    532 
    533     QByteArray ctl = control.toAscii();
    534     snd_mixer_selem_id_t *sid;
    535     snd_mixer_selem_id_alloca(&sid);
    536     snd_mixer_selem_id_set_index(sid, 0);
    537     snd_mixer_selem_id_set_name(sid, ctl.constData());
    538 
    539     snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer_handle, sid);
    540     if (!elem)
    541     {
    542         VERBOSE(VB_IMPORTANT, QString("Mixer unable to find control %1")
    543                 .arg(control));
     515    if (!(internal_vol && mixer.elem))
    544516        return;
    545     }
    546 
    547     snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
    548     if (!snd_mixer_selem_has_playback_channel(elem, chan))
    549     {
    550         snd_mixer_selem_id_set_index(sid, channel);
    551         if ((elem = snd_mixer_find_selem(mixer_handle, sid)) == NULL)
    552         {
    553             VERBOSE(VB_IMPORTANT,
    554                     QString("mixer unable to find control %1 %2")
    555                     .arg(control).arg(channel));
    556             return;
    557         }
    558     }
    559 
    560     ALSAVolumeInfo vinfo = GetVolumeRange(elem);
    561 
    562     long set_vol = vinfo.ToALSARange(volume);
    563517
    564     int err = snd_mixer_selem_set_playback_volume(elem, chan, set_vol);
    565     if (err < 0)
    566     {
    567         VERBOSE(VB_IMPORTANT, QString("mixer set channel %1 err %2: %3")
    568                 .arg(channel).arg(err).arg(snd_strerror(err)));
    569     }
     518    long mixervol = volume * mixer.volrange / 100.0f - mixer.volmin + 0.5f;
     519    mixervol = max(mixervol, mixer.volmin);
     520    mixervol = min(mixervol, mixer.volmax);
     521    int chk;
     522    if ((chk = snd_mixer_selem_set_playback_volume(mixer.elem,
     523                 (snd_mixer_selem_channel_id_t)channel, mixervol)) < 0)
     524        VERBOSE(VB_IMPORTANT, LOC_ERR
     525                + QString("failed to set channel %1 volume").arg(channel));
    570526    else
    571     {
    572         VERBOSE(VB_AUDIO, QString("channel %1 vol set to %2")
    573                 .arg(channel).arg(set_vol));
    574     }
    575 
    576     if (snd_mixer_selem_has_playback_switch(elem))
    577     {
    578         int unmute = (0 != set_vol);
    579         if (snd_mixer_selem_has_playback_switch_joined(elem))
    580         {
    581             // Only mute if all the channels should be muted.
    582             for (int i = 0; i < audio_channels; i++)
    583             {
    584                 if (0 != GetVolumeChannel(i))
    585                     unmute = 1;
    586             }
    587         }
    588 
    589         err = snd_mixer_selem_set_playback_switch(elem, chan, unmute);
    590         if (err < 0)
    591         {
    592             VERBOSE(VB_IMPORTANT, LOC_ERR +
    593                     QString("Mixer set playback switch %1 err %2: %3")
    594                     .arg(channel).arg(err).arg(snd_strerror(err)));
    595         }
    596         else
    597         {
    598             VERBOSE(VB_AUDIO, LOC +
    599                     QString("channel %1 playback switch set to %2")
    600                     .arg(channel).arg(unmute));
    601         }
    602     }
     527        VERBOSE(VB_AUDIO+VB_EXTRA, LOC
     528                + QString("channel %1 volume set %2 => %3")
     529                          .arg(channel).arg(volume).arg(mixervol));
    603530}
    604531
    605 void AudioOutputALSA::OpenMixer(bool setstartingvolume)
     532bool AudioOutputALSA::OpenMixer(void)
    606533{
    607     int volume;
    608 
    609     mixer_control = gContext->GetSetting("MixerControl", "PCM");
    610 
    611     SetupMixer();
    612 
    613     if (mixer_handle != NULL && setstartingvolume)
     534    if (!pcm_handle)
    614535    {
    615         volume = gContext->GetNumSetting("MasterMixerVolume", 80);
    616         SetCurrentVolume("Master", 0, volume);
    617         SetCurrentVolume("Master", 1, volume);
    618 
    619         volume = gContext->GetNumSetting("PCMMixerVolume", 80);
    620         SetCurrentVolume("PCM", 0, volume);
    621         SetCurrentVolume("PCM", 1, volume);
     536        VERBOSE(VB_IMPORTANT, LOC_ERR + "mixer setup without a pcm??");
     537        return false;
     538    }
     539    mixer.device = gContext->GetSetting("MixerDevice", "default");
     540    if (mixer.device.startsWith("ALSA:"))
     541        mixer.device.remove(0, 5);
     542    mixer.control = gContext->GetSetting("MixerControl", "PCM");
     543    QString mixer_device_tag = QString("mixer device %1")
     544                                       .arg(mixer.device.constData());
     545    int chk;
     546    if ((chk = snd_mixer_open(&mixer.handle, 0)) < 0)
     547    {
     548        VERBOSE(VB_IMPORTANT, LOC_ERR
     549                + QString("failed to open %1: %2")
     550                          .arg(mixer_device_tag).arg(snd_strerror(chk)));
     551        return false;
    622552    }
    623 }
    624 
    625 void AudioOutputALSA::CloseMixer(void)
    626 {
    627     if (mixer_handle != NULL)
    628         snd_mixer_close(mixer_handle);
    629     mixer_handle = NULL;
    630 }
    631 
    632 void AudioOutputALSA::SetupMixer(void)
    633 {
    634     int err;
    635 
    636     QString alsadevice = gContext->GetSetting("MixerDevice", "default");
    637     QString device = alsadevice.remove(QString("ALSA:"));
    638 
    639     if (mixer_handle != NULL)
    640         CloseMixer();
    641553
    642     VERBOSE(VB_AUDIO, QString("Opening mixer %1").arg(device));
     554    struct snd_mixer_selem_regopt regopts =
     555        {1, SND_MIXER_SABSTRACT_NONE, mixer.device.constData(), NULL, NULL};
    643556
    644     // TODO: This is opening card 0. Fix for case of multiple soundcards
    645     if ((err = snd_mixer_open(&mixer_handle, 0)) < 0)
     557    if ((chk = snd_mixer_selem_register(mixer.handle, &regopts, NULL)) < 0)
    646558    {
    647         Warn(QString("Mixer device open error %1: %2")
    648              .arg(err).arg(snd_strerror(err)));
    649         mixer_handle = NULL;
    650         return;
     559        snd_mixer_close(mixer.handle);
     560        mixer.handle = NULL;
     561        VERBOSE(VB_IMPORTANT, LOC_ERR
     562                + QString("failed to register %1: %2")
     563                          .arg(mixer_device_tag).arg(snd_strerror(chk)));
     564        return false;
    651565    }
    652566
    653     QByteArray dev = device.toAscii();
    654     if ((err = snd_mixer_attach(mixer_handle, dev.constData())) < 0)
     567    if ((chk = snd_mixer_load(mixer.handle)) < 0)
    655568    {
    656         Warn(QString("Mixer attach error %1: %2"
    657                      "\n\t\t\tCheck Mixer Name in Setup: '%3'")
    658              .arg(err).arg(snd_strerror(err)).arg(device));
    659         CloseMixer();
    660         return;
     569        snd_mixer_close(mixer.handle);
     570        mixer.handle = NULL;
     571        VERBOSE(VB_IMPORTANT, LOC_ERR
     572                + QString("failed to load %1: %2")
     573                          .arg(mixer_device_tag).arg(snd_strerror(chk)));
     574        return false;
    661575    }
    662576
    663     if ((err = snd_mixer_selem_register(mixer_handle, NULL, NULL)) < 0)
    664     {
    665         Warn(QString("Mixer register error %1: %2")
    666              .arg(err).arg(snd_strerror(err)));
    667         CloseMixer();
    668         return;
     577    mixer.elem = NULL;
     578    unsigned int elcount = snd_mixer_get_count(mixer.handle);
     579    snd_mixer_elem_t* elx = snd_mixer_first_elem(mixer.handle);
     580    for (unsigned int ctr = 0; elx != NULL && ctr < elcount; ++ctr)
     581    {
     582        if (!strcmp(mixer.control.constData(), snd_mixer_selem_get_name(elx))
     583            && !snd_mixer_selem_is_enumerated(elx)
     584            && snd_mixer_selem_has_playback_volume(elx)
     585            && snd_mixer_selem_is_active(elx))
     586        {
     587            mixer.elem = elx;
     588            VERBOSE(VB_AUDIO+VB_EXTRA, LOC + mixer_device_tag
     589                    + QString("found playback control %1 on %2 :-)")
     590                              .arg(mixer.control.constData())
     591                              .arg(mixer_device_tag));
     592            break;
     593        }
     594        elx = snd_mixer_elem_next(elx);
     595    }
     596    if (!mixer.elem)
     597    {
     598        snd_mixer_close(mixer.handle);
     599        mixer.handle = NULL;
     600        VERBOSE(VB_IMPORTANT, LOC_ERR
     601                + QString("no playback control %1 found on %2")
     602                          .arg(mixer.control.constData()).arg(mixer_device_tag));
     603        return false;
    669604    }
    670 
    671     if ((err = snd_mixer_load(mixer_handle)) < 0)
     605    if ((snd_mixer_selem_get_playback_volume_range(mixer.elem, &mixer.volmin,
     606                                                   &mixer.volmax) < 0))
    672607    {
    673         Warn(QString("Mixer load error %1: %2")
    674              .arg(err).arg(snd_strerror(err)));
    675         CloseMixer();
    676         return;
     608        snd_mixer_close(mixer.handle);
     609        mixer.handle = NULL;
     610        VERBOSE(VB_IMPORTANT, LOC_ERR + mixer_device_tag
     611                + QString("failed to get volume range on %1/%2")
     612                          .arg(mixer_device_tag).arg(mixer.control.constData()));
     613        return false;
    677614    }
    678 }
    679 
    680 ALSAVolumeInfo AudioOutputALSA::GetVolumeRange(snd_mixer_elem_t *elem) const
    681 {
    682     long volume_min, volume_max;
    683 
    684     int err = snd_mixer_selem_get_playback_volume_range(
    685         elem, &volume_min, &volume_max);
    686 
    687     if (err < 0)
    688     {
    689         static bool first_time = true;
    690         if (first_time)
    691         {
    692             VERBOSE(VB_IMPORTANT,
    693                     "snd_mixer_selem_get_playback_volume_range()" + ENO);
    694             first_time = false;
    695         }
     615    mixer.volrange = mixer.volmax - mixer.volmin;
     616    VERBOSE(VB_AUDIO+VB_EXTRA, LOC
     617            + QString("mixer volume range on %1/%2 - min %3, max %4, range %5")
     618                      .arg(mixer_device_tag).arg(mixer.control.constData())
     619                      .arg(mixer.volmin).arg(mixer.volmax).arg(mixer.volrange));
     620
     621    VERBOSE(VB_AUDIO, LOC + QString("%1/%2 set up successfully")
     622                                    .arg(mixer_device_tag)
     623                                    .arg(mixer.control.constData()));
     624    if (set_initial_vol)
     625    {
     626        int initial_vol;
     627        if ( mixer.control == "PCM")
     628            initial_vol = gContext->GetNumSetting("PCMMixerVolume", 80);
     629        else
     630            initial_vol = gContext->GetNumSetting("MasterMixerVolume", 80);
     631        for (int ch = 0; ch < audio_channels; ++ch)
     632            SetVolumeChannel(ch, initial_vol);
    696633    }
    697634
    698     ALSAVolumeInfo vinfo(volume_min, volume_max);
    699 
    700     VERBOSE(VB_AUDIO, QString("Volume range is %1 to %2, mult=%3")
    701             .arg(vinfo.volume_min).arg(vinfo.volume_max)
    702             .arg(vinfo.range_multiplier));
    703 
    704     return vinfo;
    705 }
     635    return true;
     636}
     637 No newline at end of file