summaryrefslogtreecommitdiffstats
path: root/kmix/mixer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmix/mixer.cpp')
-rw-r--r--kmix/mixer.cpp753
1 files changed, 753 insertions, 0 deletions
diff --git a/kmix/mixer.cpp b/kmix/mixer.cpp
new file mode 100644
index 00000000..316625e0
--- /dev/null
+++ b/kmix/mixer.cpp
@@ -0,0 +1,753 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken - esken@kde.org
+ * 2002 Helio Chissini de Castro - helio@conectiva.com.br
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <dcopobject.h>
+
+#include "mixer.h"
+#include "mixer_backend.h"
+#include "kmix-platforms.cpp"
+#include "volume.h"
+
+//#define MIXER_MASTER_DEBUG
+
+#ifdef MIXER_MASTER_DEBUG
+#warning MIXER_MASTER_DEBUG is enabled. DO NOT SHIP KMIX LIKE THIS !!!
+#endif
+
+/**
+ * Some general design hints. Hierachy is Mixer->MixDevice->Volume
+ */
+
+// !! Warning: Don't commit with "KMIX_DCOP_OBJID_TEST" #define'd (cesken)
+#undef KMIX_DCOP_OBJID_TEST
+int Mixer::_dcopID = 0;
+
+QPtrList<Mixer> Mixer::s_mixers;
+QString Mixer::_masterCard;
+QString Mixer::_masterCardDevice;
+
+int Mixer::numDrivers()
+{
+ MixerFactory *factory = g_mixerFactories;
+ int num = 0;
+ while( factory->getMixer!=0 )
+ {
+ num++;
+ factory++;
+ }
+
+ return num;
+}
+
+/*
+ * Returns a reference of the current mixer list.
+ */
+QPtrList<Mixer>& Mixer::mixers()
+{
+ return s_mixers;
+}
+
+
+Mixer::Mixer( int driver, int device ) : DCOPObject( "Mixer" )
+{
+ _pollingTimer = 0;
+
+ _mixerBackend = 0;
+ getMixerFunc *f = g_mixerFactories[driver].getMixer;
+ if( f!=0 ) {
+ _mixerBackend = f( device );
+ }
+
+ readSetFromHWforceUpdate(); // enforce an initial update on first readSetFromHW()
+
+ m_balance = 0;
+ m_profiles.setAutoDelete( true );
+
+ _pollingTimer = new QTimer(); // will be started on open() and stopped on close()
+ connect( _pollingTimer, SIGNAL(timeout()), this, SLOT(readSetFromHW()));
+
+ QCString objid;
+#ifndef KMIX_DCOP_OBJID_TEST
+ objid.setNum(_mixerBackend->m_devnum);
+#else
+// should use a nice name like the Unique-Card-ID instead !!
+ objid.setNum(Mixer::_dcopID);
+ Mixer::_dcopID ++;
+#endif
+ objid.prepend("Mixer");
+ DCOPObject::setObjId( objid );
+
+}
+
+Mixer::~Mixer() {
+ // Close the mixer. This might also free memory, depending on the called backend method
+ close();
+ delete _pollingTimer;
+}
+
+void Mixer::volumeSave( KConfig *config )
+{
+ // kdDebug(67100) << "Mixer::volumeSave()" << endl;
+ readSetFromHW();
+ QString grp("Mixer");
+ grp.append(mixerName());
+ _mixerBackend->m_mixDevices.write( config, grp );
+}
+
+void Mixer::volumeLoad( KConfig *config )
+{
+ QString grp("Mixer");
+ grp.append(mixerName());
+ if ( ! config->hasGroup(grp) ) {
+ // no such group. Volumes (of this mixer) were never saved beforehand.
+ // Thus don't restore anything (also see Bug #69320 for understanding the real reason)
+ return; // make sure to bail out immediately
+ }
+
+ // else restore the volumes
+ _mixerBackend->m_mixDevices.read( config, grp );
+
+ // set new settings
+ QPtrListIterator<MixDevice> it( _mixerBackend->m_mixDevices );
+ for(MixDevice *md=it.toFirst(); md!=0; md=++it )
+ {
+ // kdDebug(67100) << "Mixer::volumeLoad() writeVolumeToHW(" << md->num() << ", "<< md->getVolume() << ")" << endl;
+ // !! @todo Restore record source
+ //setRecordSource( md->num(), md->isRecSource() );
+ _mixerBackend->setRecsrcHW( md->num(), md->isRecSource() );
+ _mixerBackend->writeVolumeToHW( md->num(), md->getVolume() );
+ if ( md->isEnum() ) _mixerBackend->setEnumIdHW( md->num(), md->enumId() );
+ }
+}
+
+
+/**
+ * Opens the mixer.
+ * Also, starts the polling timer, for polling the Volumes from the Mixer.
+ *
+ * @return 0, if OK. An Mixer::ERR_ error code otherwise
+ */
+int Mixer::open()
+{
+ int err = _mixerBackend->open();
+ // A better ID is now calculated in mixertoolbox.cpp, and set via setID(),
+ // but we want a somhow usable fallback just in case.
+ _id = mixerName();
+
+ if( err == ERR_INCOMPATIBLESET ) // !!! When does this happen ?!?
+ {
+ // Clear the mixdevices list
+ _mixerBackend->m_mixDevices.clear();
+ // try again with fresh set
+ err = _mixerBackend->open();
+ }
+
+ MixDevice* recommendedMaster = _mixerBackend->recommendedMaster();
+ if ( recommendedMaster != 0 ) {
+ setMasterDevice(recommendedMaster->getPK() );
+ }
+ else {
+ kdError(67100) << "Mixer::open() no master detected." << endl;
+ QString noMaster = "---no-master-detected---";
+ setMasterDevice(noMaster); // no master
+ }
+ /*
+ // --------- Copy the hardware values to the MixDevice -------------------
+ MixSet &mset = _mixerBackend->m_mixDevices;
+ if( !mset.isEmpty() ) {
+ // Copy the initial mix set
+ // kdDebug(67100) << "Mixer::setupMixer() copy Set" << endl;
+ MixDevice* md;
+ for( md = mset.first(); md != 0; md = mset.next() )
+ {
+ MixDevice* mdCopy = _mixerBackend->m_mixDevices.first();
+ while( mdCopy!=0 && mdCopy->num() != md->num() ) {
+ mdCopy = _mixerBackend->m_mixDevices.next();
+ }
+ if ( mdCopy != 0 ) {
+ // The "mdCopy != 0" was not checked before, but its safer to do so
+ setRecordSource( md->num(), md->isRecSource() );
+ Volume &vol = mdCopy->getVolume();
+ vol.setVolume( md->getVolume() );
+ mdCopy->setMuted( md->isMuted() );
+
+ // !! might need writeVolumeToHW( mdCopy->num(), mdCopy->getVolume() );
+ }
+ }
+ }
+ */
+ if ( _mixerBackend->needsPolling() ) {
+ _pollingTimer->start(50);
+ }
+ else {
+ _mixerBackend->prepareSignalling(this);
+ // poll once to give the GUI a chance to rebuild it's info
+ QTimer::singleShot( 50, this, SLOT( readSetFromHW() ) );
+ }
+ return err;
+}
+
+
+/**
+ * Closes the mixer.
+ * Also, stops the polling timer.
+ *
+ * @return 0 (always)
+ */
+int Mixer::close()
+{
+ _pollingTimer->stop();
+ return _mixerBackend->close();
+}
+
+
+/* ------- WRAPPER METHODS. START ------------------------------ */
+unsigned int Mixer::size() const
+{
+ return _mixerBackend->m_mixDevices.count();
+}
+
+MixDevice* Mixer::operator[](int num)
+{
+ MixDevice* md = _mixerBackend->m_mixDevices.at( num );
+ Q_ASSERT( md );
+ return md;
+}
+
+MixSet Mixer::getMixSet()
+{
+ return _mixerBackend->m_mixDevices;
+}
+
+bool Mixer::isValid() {
+ return _mixerBackend->isValid();
+}
+
+bool Mixer::isOpen() const {
+ if ( _mixerBackend == 0 )
+ return false;
+ else
+ return _mixerBackend->isOpen();
+}
+
+/* ------- WRAPPER METHODS. END -------------------------------- */
+
+/**
+ * After calling this, readSetFromHW() will do a complete update. This will
+ * trigger emitting the appropriate signals like newVolumeLevels().
+ *
+ * This method is useful, if you need to get a "refresh signal" - used at:
+ * 1) Start of KMix - so that we can be sure an initial signal is emitted
+ * 2) When reconstructing any MixerWidget (e.g. DockIcon after applying preferences)
+ */
+void Mixer::readSetFromHWforceUpdate() const {
+ _readSetFromHWforceUpdate = true;
+}
+
+/**
+ You can call this to retrieve the freshest information from the mixer HW.
+ This method is also called regulary by the mixer timer.
+*/
+void Mixer::readSetFromHW()
+{
+ if ( ! _mixerBackend->isOpen() ) {
+ // bail out immediately, if the mixer is not open.
+ // This can happen currently only, if the user executes the DCOP close() call.
+ return;
+ }
+ bool updated = _mixerBackend->prepareUpdateFromHW();
+ if ( (! updated) && (! _readSetFromHWforceUpdate) ) {
+ // Some drivers (ALSA) are smart. We don't need to run the following
+ // time-consuming update loop if there was no change
+ return;
+ }
+ _readSetFromHWforceUpdate = false;
+ MixDevice* md;
+ for( md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() )
+ {
+ Volume& vol = md->getVolume();
+ _mixerBackend->readVolumeFromHW( md->num(), vol );
+ md->setRecSource( _mixerBackend->isRecsrcHW( md->num() ) );
+ if (md->isEnum() ) {
+ md->setEnumId( _mixerBackend->enumIdHW(md->num()) );
+ }
+ }
+ // Trivial implementation. Without looking at the devices
+ // kdDebug(67100) << "Mixer::readSetFromHW(): emit newVolumeLevels()" << endl;
+ emit newVolumeLevels();
+ emit newRecsrc(); // cheap, but works
+}
+
+
+void Mixer::setBalance(int balance)
+{
+ // !! BAD, because balance only works on the master device. If you have not Master, the slider is a NOP
+ if( balance == m_balance ) {
+ // balance unchanged => return
+ return;
+ }
+
+ m_balance = balance;
+
+ MixDevice* master = masterDevice();
+ if ( master == 0 ) {
+ // no master device available => return
+ return;
+ }
+
+ Volume& vol = master->getVolume();
+ _mixerBackend->readVolumeFromHW( master->num(), vol );
+
+ int left = vol[ Volume::LEFT ];
+ int right = vol[ Volume::RIGHT ];
+ int refvol = left > right ? left : right;
+ if( balance < 0 ) // balance left
+ {
+ vol.setVolume( Volume::LEFT, refvol);
+ vol.setVolume( Volume::RIGHT, (balance * refvol) / 100 + refvol );
+ }
+ else
+ {
+ vol.setVolume( Volume::LEFT, -(balance * refvol) / 100 + refvol );
+ vol.setVolume( Volume::RIGHT, refvol);
+ }
+
+ _mixerBackend->writeVolumeToHW( master->num(), vol );
+
+ emit newBalance( vol );
+}
+
+QString Mixer::mixerName()
+{
+ return _mixerBackend->m_mixerName;
+}
+
+QString Mixer::driverName( int driver )
+{
+ getDriverNameFunc *f = g_mixerFactories[driver].getDriverName;
+ if( f!=0 )
+ return f();
+ else
+ return "unknown";
+}
+
+void Mixer::setID(QString& ref_id)
+{
+ _id = ref_id;
+}
+
+
+QString& Mixer::id()
+{
+ return _id;
+}
+
+void Mixer::setMasterCard(QString& ref_id)
+{
+ // The value is taken over without checking on existance. This allows the User to define
+ // a MasterCard that is not always available (e.g. it is an USB hotplugging device).
+ // Also you can set the master at any time you like, e.g. after reading the KMix configuration file
+ // and before actually constructing the Mixer instances (hint: this mehtod is static!).
+ _masterCard = ref_id;
+}
+
+Mixer* Mixer::masterCard()
+{
+ Mixer *mixer = 0;
+ kdDebug(67100) << "Mixer::masterCard() searching for id=" << _masterCard << "\n";
+ for (mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next())
+ {
+ if ( mixer->id() == _masterCard ) {
+#ifdef MIXER_MASTER_DEBUG
+ kdDebug(67100) << "Mixer::masterCard() found id=" << mixer->id() << "\n";
+#endif
+ break;
+ }
+ }
+#ifdef MIXER_MASTER_DEBUG
+ if ( mixer == 0) kdDebug(67100) << "Mixer::masterCard() found no Mixer* mixer \n";
+#endif
+ return mixer;
+}
+
+void Mixer::setMasterCardDevice(QString& ref_id)
+{
+ // The value is taken over without checking on existance. This allows the User to define
+ // a MasterCard that is not always available (e.g. it is an USB hotplugging device).
+ // Also you can set the master at any time you like, e.g. after reading the KMix configuration file
+ // and before actually constructing the Mixer instances (hint: this mehtod is static!).
+ _masterCardDevice = ref_id;
+#ifdef MIXER_MASTER_DEBUG
+ kdDebug(67100) << "Mixer::setMasterCardDevice(\"" << ref_id << "\")\n";
+#endif
+}
+
+MixDevice* Mixer::masterCardDevice()
+{
+ MixDevice* md = 0;
+ Mixer *mixer = masterCard();
+ if ( mixer != 0 ) {
+ for( md = mixer->_mixerBackend->m_mixDevices.first(); md != 0; md = mixer->_mixerBackend->m_mixDevices.next() ) {
+
+
+ if ( md->getPK() == _masterCardDevice )
+ {
+#ifdef MIXER_MASTER_DEBUG
+ kdDebug(67100) << "Mixer::masterCardDevice() getPK()="
+ << md->getPK() << " , _masterCardDevice="
+ << _masterCardDevice << "\n";
+#endif
+ break;
+ }
+ }
+ }
+
+#ifdef MIXER_MASTER_DEBUG
+ if ( md == 0) kdDebug(67100) << "Mixer::masterCardDevice() found no MixDevice* md" "\n";
+#endif
+
+ return md;
+}
+
+
+
+
+/**
+ Used internally by the Mixer class and as DCOP method
+*/
+void Mixer::setRecordSource( int devnum, bool on )
+{
+ if( !_mixerBackend->setRecsrcHW( devnum, on ) ) // others have to be updated
+ {
+ for( MixDevice* md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) {
+ bool isRecsrc = _mixerBackend->isRecsrcHW( md->num() );
+// kdDebug(67100) << "Mixer::setRecordSource(): isRecsrcHW(" << md->num() << ") =" << isRecsrc << endl;
+ md->setRecSource( isRecsrc );
+ }
+ // emitting is done after read
+ //emit newRecsrc(); // like "emit newVolumeLevels()", but for record source
+ }
+ else {
+ // just the actual mixdevice
+ for( MixDevice* md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) {
+ if( md->num() == devnum ) {
+ bool isRecsrc = _mixerBackend->isRecsrcHW( md->num() );
+ md->setRecSource( isRecsrc );
+ }
+ }
+ // emitting is done after read
+ //emit newRecsrc(); // like "emit newVolumeLevels()", but for record source
+ }
+
+}
+
+
+MixDevice* Mixer::masterDevice()
+{
+ return find( _masterDevicePK );
+}
+
+void Mixer::setMasterDevice(QString &devPK)
+{
+ _masterDevicePK = devPK;
+}
+
+
+MixDevice* Mixer::find(QString& devPK)
+{
+ MixDevice* md = 0;
+ for( md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) {
+ if( devPK == md->getPK() ) {
+ break;
+ }
+ }
+ return md;
+}
+
+
+MixDevice *Mixer::mixDeviceByType( int deviceidx )
+{
+ unsigned int i=0;
+ while (i<size() && (*this)[i]->num()!=deviceidx) i++;
+ if (i==size()) return 0;
+
+ return (*this)[i];
+}
+
+// @dcop
+// Used also by the setMasterVolume() method.
+void Mixer::setVolume( int deviceidx, int percentage )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ Volume vol=mixdev->getVolume();
+ // @todo The next call doesn't handle negative volumes correctly.
+ vol.setAllVolumes( (percentage*vol.maxVolume())/100 );
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+}
+
+/**
+ Call this if you have a *reference* to a Volume object and have modified that locally.
+ Pass the MixDevice associated to that Volume to this method for writing back
+ the changed value to the mixer.
+ Hint: Why do we do it this way?
+ - It is fast (no copying of Volume objects required)
+ - It is easy to understand ( read - modify - commit )
+*/
+void Mixer::commitVolumeChange( MixDevice* md ) {
+ _mixerBackend->writeVolumeToHW(md->num(), md->getVolume() );
+ _mixerBackend->setEnumIdHW(md->num(), md->enumId() );
+}
+
+// @dcop only
+void Mixer::setMasterVolume( int percentage )
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ setVolume( master->num(), percentage );
+ }
+}
+
+// @dcop
+int Mixer::volume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ // @todo This will not work, if minVolume != 0 !!!
+ // e.g.: minVolume=5 or minVolume=-10
+ // The solution is to check two cases:
+ // volume < 0 => use minVolume for volumeRange
+ // volume > 0 => use maxVolume for volumeRange
+ // If chosen volumeRange==0 => return 0
+ // As this is potentially used often (Sliders, ...), it
+ // should beimplemented in the Volume class.
+
+ // For now we go with "maxVolume()", like in the rest of KMix.
+ long volumeRange = vol.maxVolume(); // -vol.minVolume() ;
+ if ( volumeRange == 0 )
+ {
+ return 0;
+ }
+ else
+ {
+ return ( vol.getVolume( Volume::LEFT )*100) / volumeRange ;
+ }
+}
+
+// @dcop , especially for use in KMilo
+void Mixer::setAbsoluteVolume( int deviceidx, long absoluteVolume ) {
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ Volume vol=mixdev->getVolume();
+ vol.setAllVolumes( absoluteVolume );
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+}
+
+// @dcop , especially for use in KMilo
+long Mixer::absoluteVolume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ long avgVolume=vol.getAvgVolume((Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT));
+ return avgVolume;
+}
+
+// @dcop , especially for use in KMilo
+long Mixer::absoluteVolumeMax( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ long maxVolume=vol.maxVolume();
+ return maxVolume;
+}
+
+// @dcop , especially for use in KMilo
+long Mixer::absoluteVolumeMin( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ long minVolume=vol.minVolume();
+ return minVolume;
+}
+
+// @dcop
+int Mixer::masterVolume()
+{
+ int vol = 0;
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ vol = volume( master->num() );
+ }
+ return vol;
+}
+
+// @dcop
+void Mixer::increaseVolume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (mixdev != 0) {
+ Volume vol=mixdev->getVolume();
+ double fivePercent = (vol.maxVolume()-vol.minVolume()+1) / 20;
+ for (unsigned int i=Volume::CHIDMIN; i <= Volume::CHIDMAX; i++) {
+ int volToChange = vol.getVolume((Volume::ChannelID)i);
+ if ( fivePercent < 1 ) fivePercent = 1;
+ volToChange += (int)fivePercent;
+ vol.setVolume((Volume::ChannelID)i, volToChange);
+ }
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+ }
+
+ /* see comment at the end of decreaseVolume()
+ int vol=volume(deviceidx);
+ setVolume(deviceidx, vol+5);
+ */
+}
+
+// @dcop
+void Mixer::decreaseVolume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (mixdev != 0) {
+ Volume vol=mixdev->getVolume();
+ double fivePercent = (vol.maxVolume()-vol.minVolume()+1) / 20;
+ for (unsigned int i=Volume::CHIDMIN; i <= Volume::CHIDMAX; i++) {
+ int volToChange = vol.getVolume((Volume::ChannelID)i);
+ //std::cout << "Mixer::decreaseVolume(): before: volToChange " <<i<< "=" <<volToChange << std::endl;
+ if ( fivePercent < 1 ) fivePercent = 1;
+ volToChange -= (int)fivePercent;
+ //std::cout << "Mixer::decreaseVolume(): after: volToChange " <<i<< "=" <<volToChange << std::endl;
+ vol.setVolume((Volume::ChannelID)i, volToChange);
+ //int volChanged = vol.getVolume((Volume::ChannelID)i);
+ //std::cout << "Mixer::decreaseVolume(): check: volChanged " <<i<< "=" <<volChanged << std::endl;
+ } // for
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+ }
+
+ /************************************************************
+ It is important, not to implement this method like this:
+ int vol=volume(deviceidx);
+ setVolume(deviceidx, vol-5);
+ It creates too big rounding errors. If you don't beleive me, then
+ do a decreaseVolume() and increaseVolume() with "vol.maxVolume() == 31".
+ ***********************************************************/
+}
+
+// @dcop
+void Mixer::setMute( int deviceidx, bool on )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ mixdev->setMuted( on );
+
+ _mixerBackend->writeVolumeToHW(deviceidx, mixdev->getVolume() );
+}
+
+// @dcop only
+void Mixer::setMasterMute( bool on )
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ setMute( master->num(), on );
+ }
+}
+
+
+// @dcop
+void Mixer::toggleMute( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ bool previousState= mixdev->isMuted();
+
+ mixdev->setMuted( !previousState );
+
+ _mixerBackend->writeVolumeToHW(deviceidx, mixdev->getVolume() );
+}
+
+// @dcop only
+void Mixer::toggleMasterMute()
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ toggleMute( master->num() );
+ }
+}
+
+
+// @dcop
+bool Mixer::mute( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return true;
+
+ return mixdev->isMuted();
+}
+
+// @dcop only
+bool Mixer::masterMute()
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ return mute( master->num() );
+ }
+ return true;
+}
+
+// @dcop only
+int Mixer::masterDeviceIndex()
+{
+ return masterDevice()->num();
+}
+
+bool Mixer::isRecordSource( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return false;
+
+ return mixdev->isRecSource();
+}
+
+/// @DCOP WHAT DOES THIS METHOD?!?!?
+bool Mixer::isAvailableDevice( int deviceidx )
+{
+ return mixDeviceByType( deviceidx );
+}
+
+#include "mixer.moc"