/* * KMix -- KDE's full featured mini mixer * Alsa 0.9x and 1.0 - Based on original alsamixer code * from alsa-project ( www/alsa-project.org ) * * * Copyright (C) 2002 Helio Chissini de Castro * 2004 Christian Esken * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // STD Headers #include #include #include #include #include extern "C" { #include } // KDE Headers #include #include // Local Headers #include "mixer_alsa.h" //#include "mixer.h" #include "volume.h" // #define if you want MUCH debugging output //#define ALSA_SWITCH_DEBUG //#define KMIX_ALSA_VOLUME_DEBUG Mixer_Backend* ALSA_getMixer( int device ) { Mixer_Backend *l_mixer; l_mixer = new Mixer_ALSA( device ); return l_mixer; } Mixer_ALSA::Mixer_ALSA( int device ) : Mixer_Backend( device ) { m_fds = 0; m_sns = 0; _handle = 0; _initialUpdate = true; } Mixer_ALSA::~Mixer_ALSA() { close(); } int Mixer_ALSA::identify( snd_mixer_selem_id_t *sid ) { TQString name = snd_mixer_selem_id_get_name( sid ); if ( name == "Master" ) return MixDevice::VOLUME; if ( name == "Capture" ) return MixDevice::RECMONITOR; if ( name == "Master Mono" ) return MixDevice::VOLUME; if ( name == "PC Speaker" ) return MixDevice::VOLUME; if ( name == "Music" || name == "Synth" || name == "FM" ) return MixDevice::MIDI; if ( name.find( "Headphone", 0, false ) != -1 ) return MixDevice::HEADPHONE; if ( name == "Bass" ) return MixDevice::BASS; if ( name == "Treble" ) return MixDevice::TREBLE; if ( name == "CD" ) return MixDevice::CD; if ( name == "Video" ) return MixDevice::VIDEO; if ( name == "PCM" || name == "Wave" ) return MixDevice::AUDIO; if ( name == "Surround" ) return MixDevice::SURROUND_BACK; if ( name == "Center" ) return MixDevice::SURROUND_CENTERFRONT; if ( name.find( "ac97", 0, false ) != -1 ) return MixDevice::AC97; if ( name.find( "coaxial", 0, false ) != -1 ) return MixDevice::DIGITAL; if ( name.find( "optical", 0, false ) != -1 ) return MixDevice::DIGITAL; if ( name.find( "IEC958", 0, false ) != -1 ) return MixDevice::DIGITAL; if ( name.find( "Mic" ) != -1 ) return MixDevice::MICROPHONE; if ( name.find( "LFE" ) != -1 ) return MixDevice::SURROUND_LFE; if ( name.find( "Monitor" ) != -1 ) return MixDevice::RECMONITOR; if ( name.find( "3D", 0, false ) != -1 ) return MixDevice::SURROUND; // Should be probably some own icon return MixDevice::EXTERNAL; } int Mixer_ALSA::open() { bool virginOpen = m_mixDevices.isEmpty(); bool validDevice = false; bool masterChosen = false; int err; snd_ctl_t *ctl_handle; snd_ctl_card_info_t *hw_info; snd_ctl_card_info_alloca(&hw_info); snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca( &sid ); // Card information if ((unsigned)m_devnum > 31) m_devnum = -1; devName = m_devnum == -1 ? "default" : TQString("hw:%1").arg(m_devnum); TQString probeMessage; if (virginOpen) probeMessage += "Trying ALSA Device '" + devName + "': "; if ( ( err = snd_ctl_open ( &ctl_handle, devName.latin1(), 0 ) ) < 0 ) { kdDebug(67100) << probeMessage << "not found: snd_ctl_open err=" << snd_strerror(err) << endl; //_stateMessage = errorText( Mixer::ERR_NODEV ); return Mixer::ERR_OPEN; } if ( ( err = snd_ctl_card_info ( ctl_handle, hw_info ) ) < 0 ) { kdDebug(67100) << probeMessage << "not found: snd_ctl_card_info err=" << snd_strerror(err) << endl; //_stateMessage = errorText( Mixer::ERR_READ ); snd_ctl_close( ctl_handle ); return Mixer::ERR_READ; } // Device and mixer names const char* mixer_card_name = snd_ctl_card_info_get_name( hw_info ); //mixer_device_name = snd_ctl_card_info_get_mixername( hw_info ); // Copy the name of kmix mixer from card name (mixername is rumoured to be not that good) m_mixerName = mixer_card_name; if (m_devnum == -1) m_devnum = snd_card_get_index(snd_ctl_card_info_get_id(hw_info)); if (m_devnum < 0) m_devnum = -1; snd_ctl_close( ctl_handle ); /* open mixer device */ //kdDebug(67100) << "IN Mixer_ALSA snd_mixer_open()" << endl; if ( ( err = snd_mixer_open ( &_handle, 0 ) ) < 0 ) { kdDebug(67100) << probeMessage << "not found: snd_mixer_open err=" << snd_strerror(err) << endl; //errormsg( Mixer::ERR_NODEV ); _handle = 0; return Mixer::ERR_NODEV; // if we cannot open the mixer, we have no devices } //kdDebug(67100) << "OUT Mixer_ALSA snd_mixer_open()" << endl; if ( ( err = snd_mixer_attach ( _handle, devName.latin1() ) ) < 0 ) { kdDebug(67100) << probeMessage << "not found: snd_mixer_attach err=" << snd_strerror(err) << endl; //errormsg( Mixer::ERR_PERM ); return Mixer::ERR_OPEN; } if ( ( err = snd_mixer_selem_register ( _handle, NULL, NULL ) ) < 0 ) { kdDebug(67100) << probeMessage << "not found: snd_mixer_selem_register err=" << snd_strerror(err) << endl; //errormsg( Mixer::ERR_READ ); return Mixer::ERR_READ; } if ( ( err = snd_mixer_load ( _handle ) ) < 0 ) { kdDebug(67100) << probeMessage << "not found: snd_mixer_load err=" << snd_strerror(err) << endl; //errormsg( Mixer::ERR_READ ); close(); return Mixer::ERR_READ; } kdDebug(67100) << probeMessage << "found" << endl; unsigned int mixerIdx = 0; for ( elem = snd_mixer_first_elem( _handle ); elem; elem = snd_mixer_elem_next( elem ), mixerIdx++ ) { // If element is not active, just skip if ( ! snd_mixer_selem_is_active ( elem ) ) { // ...but we still want to insert a null value into our mixer element // list so that the list indexes match up. mixer_elem_list.append( 0 ); mixer_sid_list.append( 0 ); continue; } sid = (snd_mixer_selem_id_t*)malloc(snd_mixer_selem_id_sizeof()); // I believe *we* must malloc it for ourself snd_mixer_selem_get_id( elem, sid ); bool canRecord = false; bool canMute = false; bool canCapture = false; long maxVolumePlay= 0, minVolumePlay= 0; long maxVolumeRec = 0, minVolumeRec = 0; validDevice = true; snd_mixer_selem_get_playback_volume_range( elem, &minVolumePlay, &maxVolumePlay ); snd_mixer_selem_get_capture_volume_range( elem, &minVolumeRec , &maxVolumeRec ); // New mix device MixDevice::ChannelType ct = (MixDevice::ChannelType)identify( sid ); /* if (!masterChosen && ct==MixDevice::VOLUME) { // Determine a nicer MasterVolume m_masterDevice = mixerIdx; masterChosen = true; } */ if( virginOpen ) { MixDevice::DeviceCategory cc = MixDevice::UNDEFINED; //kdDebug() << "--- Loop: name=" << snd_mixer_selem_id_get_name( sid ) << " , mixerIdx=" << mixerIdx << "------------" << endl; Volume* volPlay = 0, *volCapture = 0; TQPtrList enumList; if ( snd_mixer_selem_is_enumerated(elem) ) { cc = MixDevice::ENUM; volPlay = new Volume(); // Dummy, unused volCapture = new Volume(); mixer_elem_list.append( elem ); mixer_sid_list.append( sid ); // --- get Enum names START --- int numEnumitems = snd_mixer_selem_get_enum_items(elem); if ( numEnumitems > 0 ) { // OK. no error for (int iEnum = 0; iEnumappend(buffer); enumList.append( enumName); } // enumName could be read succesfully } // for all enum items of this device } // no error in reading enum list else { // 0 items or Error code => ignore this entry } // --- get Enum names END --- } // is an enum else { Volume::ChannelMask chn = Volume::MNONE; Volume::ChannelMask chnTmp; if ( snd_mixer_selem_has_playback_volume(elem) ) { //kdDebug(67100) << "has_playback_volume()" << endl; chnTmp = snd_mixer_selem_is_playback_mono ( elem ) ? Volume::MLEFT : (Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT); chn = (Volume::ChannelMask) (chn | chnTmp); cc = MixDevice::SLIDER; volPlay = new Volume( chn, maxVolumePlay, minVolumePlay ); } else { volPlay = new Volume(); } if ( snd_mixer_selem_has_capture_volume(elem) ) { //kdDebug(67100) << "has_capture_volume()" << endl; chnTmp = snd_mixer_selem_is_capture_mono( elem ) ? Volume::MLEFT : (Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT ); chn = (Volume::ChannelMask) (chn | chnTmp); cc = MixDevice::SLIDER; canCapture = true; volCapture = new Volume( chn, maxVolumeRec, minVolumeRec, true ); } else { volCapture = new Volume(); } /* Create Volume object. If there is no volume on this device, * it will be created with maxVolume == 0 && minVolume == 0 */ mixer_elem_list.append( elem ); mixer_sid_list.append( sid ); if ( snd_mixer_selem_has_playback_switch ( elem ) ) { //kdDebug(67100) << "has_playback_switch()" << endl; canMute = true; } if ( snd_mixer_selem_has_capture_switch ( elem ) ) { //kdDebug(67100) << "has_capture_switch()" << endl; canRecord = true; } if ( snd_mixer_selem_has_common_switch ( elem ) ) { //kdDebug(67100) << "has_common_switch()" << endl; canMute = true; canRecord = true; } if ( /*snd_mixer_selem_has_common_switch ( elem ) || */ cc == MixDevice::UNDEFINED ) { // Everything unknown is handled as switch cc = MixDevice::SWITCH; } } // is ordinary mixer element (NOT an enum) MixDevice* md = new MixDevice( mixerIdx, canCapture ? *volCapture : *volPlay, canCapture ? true : canRecord, canMute, snd_mixer_selem_id_get_name( sid ), ct, cc ); m_mixDevices.append( md ); if (!masterChosen && ct==MixDevice::VOLUME) { // Determine a nicer MasterVolume m_recommendedMaster = md; masterChosen = true; } if ( enumList.count() > 0 ) { int maxEnumId= enumList.count(); TQPtrList& enumValuesRef = md->enumValues(); // retrieve a ref for (int i=0; ievents = POLLIN; if ((err = snd_mixer_poll_descriptors(_handle, m_fds, m_count)) < 0) { kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << "\n"; return Mixer::ERR_OPEN; } if (err != m_count) { kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << " m_count=" << m_count << "\n"; return Mixer::ERR_OPEN; } return 0; } void Mixer_ALSA::prepareSignalling( Mixer *mixer ) { assert( !m_sns ); m_sns = new TQSocketNotifier*[m_count]; for ( int i = 0; i < m_count; ++i ) { kdDebug() << "socket " << i << endl; m_sns[i] = new TQSocketNotifier(m_fds[i].fd, TQSocketNotifier::Read); mixer->connect(m_sns[i], TQT_SIGNAL(activated(int)), mixer, TQT_SLOT(readSetFromHW())); } } void Mixer_ALSA::removeSignalling() { if ( m_fds ) free( m_fds ); m_fds = 0; if ( m_sns ) { for ( int i = 0; i < m_count; i++ ) delete m_sns[i]; delete [] m_sns; m_sns = 0; } } int Mixer_ALSA::close() { int ret=0; m_isOpen = false; if ( _handle != 0 ) { //kdDebug(67100) << "IN Mixer_ALSA::close()" << endl; snd_mixer_free ( _handle ); if ( ( ret = snd_mixer_detach ( _handle, devName.latin1() ) ) < 0 ) { kdDebug(67100) << "snd_mixer_detach err=" << snd_strerror(ret) << endl; } int ret2 = 0; if ( ( ret2 = snd_mixer_close ( _handle ) ) < 0 ) { kdDebug(67100) << "snd_mixer_close err=" << snd_strerror(ret2) << endl; if ( ret == 0 ) ret = ret2; // no error before => use current error code } _handle = 0; //kdDebug(67100) << "OUT Mixer_ALSA::close()" << endl; } mixer_elem_list.clear(); mixer_sid_list.clear(); m_mixDevices.clear(); removeSignalling(); return ret; } snd_mixer_elem_t* Mixer_ALSA::getMixerElem(int devnum) { snd_mixer_elem_t* elem = 0; if ( ! m_isOpen ) return elem; // unplugging guard if ( int( mixer_sid_list.count() ) > devnum ) { snd_mixer_selem_id_t * sid = mixer_sid_list[ devnum ]; // The next line (hopefully) only finds selem's, not elem's. elem = snd_mixer_find_selem(_handle, sid); if ( elem == 0 ) { // !! Check, whether the warning should be omitted. Probably // Route controls are non-simple elements. kdDebug(67100) << "Error finding mixer element " << devnum << endl; } } return elem; /* I would have liked to use the following trivial implementation instead of the code above. But it will also return elem's. which are not selem's. As there is no way to check an elem's type (e.g. elem->type == SND_MIXER_ELEM_SIMPLE), callers of getMixerElem() cannot check the type. :-( snd_mixer_elem_t* elem = mixer_elem_list[ devnum ]; return elem; */ } bool Mixer_ALSA::prepareUpdateFromHW() { if ( !m_fds || !m_isOpen ) return false; //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 1\n"; // Poll on fds with 10ms timeout // Hint: alsamixer has an infinite timeout, but we cannot do this because we would block // the X11 event handling (TQt event loop) with this. //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 3\n"; int finished = poll(m_fds, m_count, 10); //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 4\n"; bool updated = false; if (finished > 0) { //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 5\n"; unsigned short revents; if (snd_mixer_poll_descriptors_revents(_handle, m_fds, m_count, &revents) >= 0) { //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 6\n"; if (revents & POLLNVAL) { /* Bug 127294 shows, that we receieve POLLNVAL when the user unplugs an USB soundcard. Lets close the card. */ kdDebug(67100) << "Mixer_ALSA::poll() , Error: poll() returns POLLNVAL\n"; close(); // Card was unplugged (unplug, driver unloaded) return false; } if (revents & POLLERR) { kdDebug(67100) << "Mixer_ALSA::poll() , Error: poll() returns POLLERR\n"; return false; } if (revents & POLLIN) { //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 7\n"; snd_mixer_handle_events(_handle); updated = true; } } } //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() " << updated << endl;; return updated; } bool Mixer_ALSA::isRecsrcHW( int devnum ) { bool isCurrentlyRecSrc = false; snd_mixer_elem_t *elem = getMixerElem( devnum ); if ( !elem ) { return false; } if ( snd_mixer_selem_has_capture_switch( elem ) ) { // Has a on-off switch // Yes, this element can be record source. But the user can switch it off, so lets see if it is switched on. int swLeft; int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &swLeft ); if ( ret != 0 ) { kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 1\n"; } if (snd_mixer_selem_has_capture_switch_joined( elem ) ) { isCurrentlyRecSrc = (swLeft != 0); #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_switch joined: #" << devnum << " >>> " << swLeft << " : " << isCurrentlyRecSrc << endl; #endif } else { int swRight; snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_RIGHT, &swRight ); isCurrentlyRecSrc = ( (swLeft != 0) || (swRight != 0) ); #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_switch non-joined, state " << isCurrentlyRecSrc << endl; #endif } } else { // Has no on-off switch if ( snd_mixer_selem_has_capture_volume( elem ) ) { // Has a volume, but has no OnOffSwitch => We assume that this is a fixed record source (always on). (esken) isCurrentlyRecSrc = true; #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_no_switch, state " << isCurrentlyRecSrc << endl; #endif } } return isCurrentlyRecSrc; } bool Mixer_ALSA::setRecsrcHW( int devnum, bool on ) { int sw = 0; if (on) sw = !sw; snd_mixer_elem_t *elem = getMixerElem( devnum ); if ( !elem ) { return 0; } if (snd_mixer_selem_has_capture_switch_joined( elem ) ) { int before, after; int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &before ); if ( ret != 0 ) { kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 1\n"; } ret = snd_mixer_selem_set_capture_switch_all( elem, sw ); if ( ret != 0 ) { kdDebug(67100) << "snd_mixer_selem_set_capture_switch_all() failed 2: errno=" << ret << "\n"; } ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &after ); if ( ret != 0 ) { kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 3: errno=" << ret << "\n"; } #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "Mixer_ALSA::setRecsrcHW(" << devnum << "," << on << ")joined. Before=" << before << " Set=" << sw << " After=" << after <<"\n"; #endif } else { #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "Mixer_ALSA::setRecsrcHW LEFT\n"; #endif snd_mixer_selem_set_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, sw ); #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "Mixer_ALSA::setRecsrcHW RIGHT\n"; #endif snd_mixer_selem_set_capture_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, sw); } #ifdef ALSA_SWITCH_DEBUG kdDebug(67100) << "EXIT Mixer_ALSA::setRecsrcHW(" << devnum << "," << on << ")\n"; #endif return false; // we should always return false, so that other devnum's get updated // The ALSA polling has been implemented some time ago. So it should be safe to // return "true" here. // The other devnum's Rec-Sources won't get update by KMix code, but ALSA will send // us an event, if neccesary. But OTOH it is possibly better not to trust alsalib fully, // because the old code is working also well (just takes more processing time). // return true; } /** * Sets the ID of the currently selected Enum entry. * Warning: ALSA supports to have different enums selected on each channel * of the SAME snd_mixer_elem_t. KMix does NOT support that and * always sets both channels (0 and 1). */ void Mixer_ALSA::setEnumIdHW(int mixerIdx, unsigned int idx) { //kdDebug(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << ", idx=" << idx << ") 1\n"; snd_mixer_elem_t *elem = getMixerElem( mixerIdx ); if ( elem==0 || ( !snd_mixer_selem_is_enumerated(elem)) ) { return; } //kdDebug(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << ", idx=" << idx << ") 2\n"; int ret = snd_mixer_selem_set_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,idx); if (ret < 0) { kdError(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << "), errno=" << ret << "\n"; } snd_mixer_selem_set_enum_item(elem,SND_MIXER_SCHN_FRONT_RIGHT,idx); // we don't care about possible error codes on channel 1 return; } /** * Return the ID of the currently selected Enum entry. * Warning: ALSA supports to have different enums selected on each channel * of the SAME snd_mixer_elem_t. KMix does NOT support that and * always shows the value of the first channel. */ unsigned int Mixer_ALSA::enumIdHW(int mixerIdx) { snd_mixer_elem_t *elem = getMixerElem( mixerIdx ); if ( elem==0 || ( !snd_mixer_selem_is_enumerated(elem)) ) { return 0; } unsigned int idx = 0; int ret = snd_mixer_selem_get_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,&idx); //kdDebug(67100) << "Mixer_ALSA::enumIdHW(" << mixerIdx << ") idx=" << idx << "\n"; if (ret < 0) { idx = 0; kdError(67100) << "Mixer_ALSA::enumIdHW(" << mixerIdx << "), errno=" << ret << "\n"; } return idx; } int Mixer_ALSA::readVolumeFromHW( int mixerIdx, Volume &volume ) { int elem_sw; long left, right; snd_mixer_elem_t *elem = getMixerElem( mixerIdx ); if ( !elem ) { return 0; } // *** READ PLAYBACK VOLUMES ************* if ( snd_mixer_selem_has_playback_volume( elem ) && !volume.isCapture() ) { int ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_LEFT, &left ); if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_playback_volume,R] failed, errno=" << ret << endl; if ( snd_mixer_selem_is_playback_mono ( elem )) { volume.setVolume( Volume::LEFT , left ); volume.setVolume( Volume::RIGHT, left ); } else { int ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT, &right ); if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_playback_volume,R] failed, errno=" << ret << endl; volume.setVolume( Volume::LEFT , left ); volume.setVolume( Volume::RIGHT, right ); } } else if ( snd_mixer_selem_has_capture_volume ( elem ) && volume.isCapture() ) { int ret = snd_mixer_selem_get_capture_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, &left ); if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [get_capture_volume,L] failed, errno=" << ret << endl; if ( snd_mixer_selem_is_capture_mono ( elem )) { volume.setVolume( Volume::LEFT , left ); volume.setVolume( Volume::RIGHT, left ); } else { int ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT, &right ); if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_capture_volume,R] failed, errno=" << ret << endl; volume.setVolume( Volume::LEFT , left ); volume.setVolume( Volume::RIGHT, right ); } } //kdDebug() << "snd_mixer_selem_has_playback_volume " << mixerIdx << " " << snd_mixer_selem_has_playback_switch( elem ) << endl; if ( snd_mixer_selem_has_playback_switch( elem ) ) { snd_mixer_selem_get_playback_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &elem_sw ); volume.setMuted( elem_sw == 0 ); } return 0; } int Mixer_ALSA::writeVolumeToHW( int devnum, Volume& volume ) { int left, right; snd_mixer_elem_t *elem = getMixerElem( devnum ); if ( !elem ) { return 0; } // --- VOLUME - WE HAVE JUST ONE TYPE OF VOLUME A TIME, // CAPTURE OR PLAYBACK, SO IT"S JUST USE VOLUME ------------ left = volume[ Volume::LEFT ]; right = volume[ Volume::RIGHT ]; if (snd_mixer_selem_has_playback_volume( elem ) && !volume.isCapture() ) { snd_mixer_selem_set_playback_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, left ); if ( ! snd_mixer_selem_is_playback_mono ( elem ) ) { snd_mixer_selem_set_playback_volume ( elem, SND_MIXER_SCHN_FRONT_RIGHT, right ); } } else if ( snd_mixer_selem_has_capture_volume( elem ) && volume.isCapture() ) { snd_mixer_selem_set_capture_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, left ); if ( ! snd_mixer_selem_is_capture_mono ( elem ) ) { snd_mixer_selem_set_capture_volume ( elem, SND_MIXER_SCHN_FRONT_RIGHT, right ); } } if ( snd_mixer_selem_has_playback_switch( elem ) ) { int sw = 0; if (! volume.isMuted()) sw = !sw; snd_mixer_selem_set_playback_switch_all(elem, sw); } return 0; } TQString Mixer_ALSA::errorText( int mixer_error ) { TQString l_s_errmsg; switch ( mixer_error ) { case Mixer::ERR_PERM: l_s_errmsg = i18n("You do not have permission to access the alsa mixer device.\n" \ "Please verify if all alsa devices are properly created."); break; case Mixer::ERR_OPEN: l_s_errmsg = i18n("Alsa mixer cannot be found.\n" \ "Please check that the soundcard is installed and the\n" \ "soundcard driver is loaded.\n" ); break; default: l_s_errmsg = Mixer_Backend::errorText( mixer_error ); } return l_s_errmsg; } TQString ALSA_getDriverName() { return "ALSA"; } ALSA_DevIterator::ALSA_DevIterator() { N = -1; NMax = 31; } void ALSA_DevIterator::next() { #if 0 int rc = snd_card_next(&N); if (rc || (N == -1)) N = NMax + 1; #else if ((snd_card_next(&N) != 0) || (N == -1)) N = NMax + 1; #endif } DevIterator* ALSA_getDevIterator() { return new ALSA_DevIterator(); }