Logo Search packages:      
Sourcecode: qasmixer version File versions  Download package

snd_mixer_ctl_elem.cpp

//
//
// C++ Implementation:
//
// Description:
//
//
// Author: Sebastian Holtermann <sebholt@xwmw.org>, (C) 2010-2011
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include "snd_mixer_ctl_elem.hpp"

#include <iostream>

#include <QCoreApplication>
#include <QEvent>
#include "mixer_events.hpp"


namespace QSnd
{


Snd_Mixer_CTL_Elem::Snd_Mixer_CTL_Elem (
      const Snd_Mixer_CTL_Data & data_n,
      QObject * parent_n,
      snd_hctl_t * hctl_n,
      snd_hctl_elem_t * hctl_elem_n ) :
QObject ( parent_n ),
_data ( data_n ),
_snd_hctl ( hctl_n ),
_values_changed ( false )
{
      snd_ctl_elem_info_malloc ( &_snd_ctl_elem_info );
      snd_ctl_elem_value_malloc  ( &_snd_ctl_elem_value );

      snd_ctl_elem_info_clear ( _snd_ctl_elem_info );
      snd_ctl_elem_value_clear ( _snd_ctl_elem_value );

      set_snd_hctl_elem ( hctl_elem_n );
}


Snd_Mixer_CTL_Elem::~Snd_Mixer_CTL_Elem ( )
{
      snd_ctl_elem_info_free ( _snd_ctl_elem_info );
      snd_ctl_elem_value_free ( _snd_ctl_elem_value );
}


QString
Snd_Mixer_CTL_Elem::enum_item_display_name (
      unsigned int enum_idx_n ) const
{
      return QCoreApplication::translate (
            "ALSA::Enum_Value", enum_item_name ( enum_idx_n ) );
}


void
Snd_Mixer_CTL_Elem::clear ( )
{
      _enum_item_names.clear();
}


void
Snd_Mixer_CTL_Elem::set_snd_hctl (
      snd_hctl_t * hctl_n )
{
      clear();

      _snd_hctl = hctl_n;
}


void
Snd_Mixer_CTL_Elem::set_display_name (
      const QString & name_n )
{
      _display_name = name_n;
}


void
Snd_Mixer_CTL_Elem::set_snd_hctl_elem (
      snd_hctl_elem_t * hctl_elem_n )
{
      clear();

      if ( hctl_elem_n == 0 ) {
            return;
      }

      _snd_hctl_elem = hctl_elem_n;

      if ( snd_hctl_elem() == 0 ) {
            return;
      }

      // Load info
      snd_hctl_elem_info ( snd_hctl_elem(), _snd_ctl_elem_info );

      _display_name = QCoreApplication::translate (
            "ALSA::Elem_Name", snd_hctl_elem_get_name ( snd_hctl_elem() ) );

      update_value_mark();
}


// Boolean type

bool
Snd_Mixer_CTL_Elem::switches_equal ( ) const
{
      bool res ( true );
      const unsigned int num ( count() );
      if ( num > 1 ) {
            long state0 ( switch_state ( 0 ) );
            for ( unsigned int ii=1; ii < num; ++ii ) {
                  if ( switch_state ( ii ) != state0 ) {
                        res = false;
                        break;
                  }
            }
      }
      return res;
}


void
Snd_Mixer_CTL_Elem::set_switch_state (
      unsigned int idx_n,
      bool state_n )
{
      snd_ctl_elem_value_set_boolean ( _snd_ctl_elem_value, idx_n, state_n );
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::set_switch_all (
      bool state_n )
{
      unsigned int num ( count() );
      for ( unsigned int ii=0; ii < num; ++ii ) {
            snd_ctl_elem_value_set_boolean ( _snd_ctl_elem_value, ii, state_n );
      }
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::invert_switch_state (
      unsigned int idx_n )
{
      bool state ( !switch_state ( idx_n ) );
      snd_ctl_elem_value_set_boolean ( _snd_ctl_elem_value, idx_n, state );
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::invert_switch_all ( )
{
      unsigned int num ( count() );
      for ( unsigned int ii=0; ii < num; ++ii ) {
            bool state ( !switch_state ( ii ) );
            snd_ctl_elem_value_set_boolean ( _snd_ctl_elem_value, ii, state );
      }
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::level_switches ( )
{
      const unsigned int num_switches ( count() );

      unsigned int num_on ( 0 );
      unsigned int num_off ( 0 );

      for ( unsigned int ii=0; ii < num_switches; ++ii ) {
            if ( switch_state ( ii ) ) {
                  ++num_on;
            } else {
                  ++num_off;
            }
      }

      set_switch_all ( ( num_on >= num_off ) );
}



// Integer type

bool
Snd_Mixer_CTL_Elem::integers_equal ( ) const
{
      bool res ( true );
      const unsigned int num ( count() );
      if ( num > 1 ) {
            long val0 ( integer_value ( 0 ) );
            for ( unsigned int ii=1; ii < num; ++ii ) {
                  if ( integer_value ( ii ) != val0 ) {
                        res = false;
                        break;
                  }
            }
      }
      return res;
}


void
Snd_Mixer_CTL_Elem::set_integer (
      unsigned int idx_n,
      long value_n )
{
      //std::cout << "Snd_Mixer_CTL_Elem::set_integer " << idx_n << " value " << value_n << "\n";

      snd_ctl_elem_value_set_integer ( _snd_ctl_elem_value, idx_n, value_n );
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::set_integer_all (
      long value_n )
{
      unsigned int num ( count() );
      for ( unsigned int ii=0; ii < num; ++ii ) {
            snd_ctl_elem_value_set_integer ( _snd_ctl_elem_value, ii, value_n );
      }
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::level_integers ( )
{
      const unsigned int num ( count() );

      if (  num < 2 ) {
            return;
      }

      const long num_int ( num );
      long vol_avrg_accu ( 0 );
      long vol_mod_accu ( 0 );

      for ( unsigned int ii=0; ii <  num; ++ii ) {
            const long val ( integer_value ( ii ) );

            long vol_mod;
            if ( ( val >= num_int ) || ( val <= (-num_int ) ) )
            {
                  const long vol_avrg ( val / num_int );
                  vol_avrg_accu += vol_avrg;
                  vol_mod = val;
                  // Subtraction performed in two steps to prevent and overflow
                  vol_mod -= vol_avrg * ( num_int - 1 );
                  vol_mod -= vol_avrg;
            } else {
                  vol_mod = val;
            }

            vol_mod_accu += vol_mod;

            if ( ( vol_mod_accu >= num_int ) ||
                  ( vol_mod_accu <= (-num_int) ) )
            {
                  const long vol_avrg ( vol_mod_accu / num_int );
                  vol_avrg_accu += vol_avrg;
                  vol_mod_accu -= ( vol_avrg * num_int );
            }
      }

      set_integer_all ( vol_avrg_accu );
}


// Decibel

bool
Snd_Mixer_CTL_Elem::has_dB ( ) const
{
      long tmp[2];
      return ( dB_range ( &tmp[0], &tmp[1] ) == 0 );
}


int
00298 Snd_Mixer_CTL_Elem::dB_range (
      long * min_n,
      long * max_n ) const
{
      snd_ctl_elem_id_t * elem_id;
      snd_ctl_elem_id_alloca ( &elem_id );
      snd_ctl_elem_info_get_id ( _snd_ctl_elem_info, elem_id );
      return snd_ctl_get_dB_range (
            snd_hctl_ctl ( _snd_hctl ), elem_id, min_n, max_n );
}


long
Snd_Mixer_CTL_Elem::dB_value (
      unsigned int idx_n ) const
{
      return ask_dB_from_int ( integer_value ( idx_n ) );
}


long
Snd_Mixer_CTL_Elem::ask_dB_from_int (
      long int_value_n ) const
{
      long res ( 0 );

      snd_ctl_elem_id_t * elem_id;
      snd_ctl_elem_id_alloca ( &elem_id );
      snd_ctl_elem_info_get_id ( _snd_ctl_elem_info, elem_id );
      snd_ctl_convert_to_dB (
            snd_hctl_ctl ( _snd_hctl ),   elem_id,
            int_value_n, &res );

      return res;
}


long
Snd_Mixer_CTL_Elem::ask_int_from_dB (
      long db_value_n,
      int round_dir_n ) const
{
      long res ( 0 );

      snd_ctl_elem_id_t * elem_id;
      snd_ctl_elem_id_alloca ( &elem_id );
      snd_ctl_elem_info_get_id ( _snd_ctl_elem_info, elem_id );
      snd_ctl_convert_from_dB (
            snd_hctl_ctl ( _snd_hctl ),   elem_id,
            db_value_n, &res, round_dir_n );

      return res;
}


// Enum type

bool
Snd_Mixer_CTL_Elem::enum_idices_equal ( ) const
{
      bool res ( true );
      const unsigned int num ( count() );
      if ( num > 1 ) {
            unsigned int idx0 ( enum_index ( 0 ) );
            for ( unsigned int ii=1; ii < num; ++ii ) {
                  if ( enum_index ( ii ) != idx0 ) {
                        res = false;
                        break;
                  }
            }
      }
      return res;
}


void
Snd_Mixer_CTL_Elem::set_enum_index (
      unsigned int channel_idx_n,
      unsigned int enum_idx_n )
{
      snd_ctl_elem_value_set_enumerated (
            _snd_ctl_elem_value, channel_idx_n, enum_idx_n );
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::set_enum_index_all (
      unsigned int enum_idx_n )
{
      unsigned int num ( count() );
      for ( unsigned int ii=0; ii < num; ++ii ) {
            snd_ctl_elem_value_set_enumerated (
                  _snd_ctl_elem_value, ii, enum_idx_n );
      }
      value_was_set();
}


void
Snd_Mixer_CTL_Elem::level_enums ( )
{
      unsigned int idx ( 0 );
      if ( count() > 0 ) {
            idx = enum_index ( 0 );
      }
      set_enum_index_all ( idx );
}


// General

bool
Snd_Mixer_CTL_Elem::values_equal ( ) const
{
      bool res ( true );
      if ( is_boolean() ) {
            res = switches_equal();
      } else if ( is_integer() ) {
            res = integers_equal();
      } else if ( is_enumerated() ) {
            res = enum_idices_equal();
      }
      return res;
}


void
Snd_Mixer_CTL_Elem::level_values ( )
{
      if ( is_boolean() ) {
            level_switches();
      } else if ( is_integer() ) {
            level_integers();
      } else if ( is_enumerated() ) {
            level_enums();
      }
}


void
Snd_Mixer_CTL_Elem::value_was_set ( )
{
      snd_hctl_elem_t * hctl_elem ( snd_hctl_elem() );
      if ( hctl_elem != 0 ) {
            int err = snd_hctl_elem_write ( hctl_elem, _snd_ctl_elem_value );
            if ( err >= 0 ) {

                  // Read values back
                  update_value_mark();

                  if ( parent() != 0 ) {
                        QEvent ev_req ( event_type_update_values_request );
                        QCoreApplication::sendEvent ( parent(), &ev_req );
                  }

            } else {
                  print_alsa_error ( "snd_hctl_elem_write", err );
            }
      }
}


void
00462 Snd_Mixer_CTL_Elem::update_value ( )
{
      snd_hctl_elem_t * hctl_elem ( snd_hctl_elem() );
      if ( hctl_elem != 0 ) {
            int err = snd_hctl_elem_read ( hctl_elem, _snd_ctl_elem_value );
            if ( err < 0 ) {
                  print_alsa_error ( "snd_hctl_elem_read", err );
            }
      }
}


void
00475 Snd_Mixer_CTL_Elem::update_value_mark ( )
{
      _values_changed = true;
      update_value();
}


void
Snd_Mixer_CTL_Elem::signalize_changes ( )
{
      if ( _values_changed ) {
            _values_changed = false;
            emit sig_values_changed();
      }
}


void
00493 Snd_Mixer_CTL_Elem::signalize_element_changed ( )
{
      if ( snd_hctl_elem() != 0 ) {
            snd_hctl_elem_info ( snd_hctl_elem(), _snd_ctl_elem_info );
      }
      if ( parent() != 0 ) {
            QEvent ev_req ( event_type_reload_request );
            QCoreApplication::sendEvent ( parent(), &ev_req );
      }
}


// Alsa callbacks

int
Snd_Mixer_CTL_Elem::alsa_callback_hctl_elem (
      snd_hctl_elem_t * elem_n,
      unsigned int mask_n )
{
      int res ( 0 );

      Snd_Mixer_CTL_Elem * smce;
      {
            void * priv ( snd_hctl_elem_get_callback_private ( elem_n ) );
            smce = reinterpret_cast < Snd_Mixer_CTL_Elem * > ( priv );
      }

      if ( smce != 0 ) {
            const unsigned int change_mask (
                  SND_CTL_EVENT_MASK_INFO |
                  SND_CTL_EVENT_MASK_ADD |
                  SND_CTL_EVENT_MASK_TLV );

            if ( ( mask_n == SND_CTL_EVENT_MASK_REMOVE ) ||
                  ( ( mask_n & change_mask ) != 0 ) )
            {
                  smce->signalize_element_changed();
            } else if ( ( mask_n & SND_CTL_EVENT_MASK_VALUE ) != 0 ) {
                  smce->update_value_mark();
            } else {
                  // Unusual mask
                  ::std::cerr << "Snd_Mixer_CTL_Elem::alsa_callback_hctl_elem: ";
                  ::std::cerr << "Unknown mask ( " << mask_n << " )\n";
                  res = -1;
            }
      }

      return res;
}



//
// Snd_Mixer_CTL_Elem_Group
//

void
Snd_Mixer_CTL_Elem_Group::clear ( )
{
      _snd_elems.clear();
}


} // End of namespace

Generated by  Doxygen 1.6.0   Back to index