2
* KMix -- KDE's full featured mini mixer
5
* Copyright (C) 1996-2004 Christian Esken - esken@kde.org
6
* 2002 Helio Chissini de Castro - helio@conectiva.com.br
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Library General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Library General Public License for more details.
18
* You should have received a copy of the GNU Library General Public
19
* License along with this program; if not, write to the Free
20
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29
#include "core/mixer.h"
30
#include "backends/mixer_backend.h"
31
#include "backends/kmix-backends.cpp"
32
#include "core/volume.h"
33
#include "kmixadaptor.h"
36
* Some general design hints. Hierachy is Mixer->MixDevice->Volume
39
QList<Mixer *> Mixer::s_mixers;
40
QString Mixer::_globalMasterCard;
41
QString Mixer::_globalMasterCardDevice;
44
int Mixer::numDrivers()
46
MixerFactory *factory = g_mixerFactories;
48
while( factory->getMixer!=0 )
58
* Returns a reference of the current mixer list.
60
QList<Mixer *>& Mixer::mixers()
65
Mixer::Mixer( QString& ref_driverName, int device )
66
: m_balance(0), _mixerBackend(0L), m_dynamic(false)
68
(void)new KMixAdaptor(this);
71
int driverCount = numDrivers();
72
for (int driver=0; driver<driverCount; driver++ ) {
73
QString driverName = Mixer::driverName(driver);
74
if ( driverName == ref_driverName ) {
75
// driver found => retrieve Mixer factory for that driver
76
getMixerFunc *f = g_mixerFactories[driver].getMixer;
78
_mixerBackend = f( this, device );
79
readSetFromHWforceUpdate(); // enforce an initial update on first readSetFromHW()
89
// Close the mixer. This might also free memory, depending on the called backend method
90
if ( ! m_dbusName.isEmpty() ) {
91
kDebug(67100) << "Auto-unregistering DBUS object " << m_dbusName;
92
//QDBusConnection::sessionBus().unregisterObject(m_dbusName);
100
* Find a Mixer. If there is no mixer with the given id, 0 is returned
102
Mixer* Mixer::findMixer( const QString& mixer_id)
105
int mixerCount = Mixer::mixers().count();
106
for ( int i=0; i<mixerCount; ++i)
108
if ( ((Mixer::mixers())[i])->id() == mixer_id )
110
mixer = (Mixer::mixers())[i];
119
* Set the card instance. Usually this will be 1, but if there is
120
* more than one card with the same name install, then you need
123
void Mixer::setCardInstance(int cardInstance)
125
_cardInstance = cardInstance;
129
void Mixer::recreateId()
131
/* As we use "::" and ":" as separators, the parts %1,%2 and %3 may not
133
* %1, the driver name is from the KMix backends, it does not contain colons.
134
* %2, the mixer name, is typically coming from an OS driver. It could contain colons.
135
* %3, the mixer number, is a number: it does not contain colons.
137
QString mixerName = getBaseName();
138
mixerName.replace(":","_");
139
QString primaryKeyOfMixer = QString("%1::%2:%3")
140
.arg(getDriverName())
143
// The following 3 replaces are for not messing up the config file
144
primaryKeyOfMixer.replace("]","_");
145
primaryKeyOfMixer.replace("[","_"); // not strictly necessary, but lets play safe
146
primaryKeyOfMixer.replace(" ","_");
147
primaryKeyOfMixer.replace("=","_");
148
_id = primaryKeyOfMixer;
154
void Mixer::volumeSave( KConfig *config )
156
// kDebug(67100) << "Mixer::volumeSave()";
157
_mixerBackend->readSetFromHW();
158
QString grp("Mixer");
160
_mixerBackend->m_mixDevices.write( config, grp );
163
void Mixer::volumeLoad( KConfig *config )
165
QString grp("Mixer");
167
if ( ! config->hasGroup(grp) ) {
168
// no such group. Volumes (of this mixer) were never saved beforehand.
169
// Thus don't restore anything (also see Bug #69320 for understanding the real reason)
170
return; // make sure to bail out immediately
173
// else restore the volumes
174
_mixerBackend->m_mixDevices.read( config, grp );
177
//QListIterator<MixDevice*> it( _mixerBackend->m_mixDevices );
178
for(int i=0; i<_mixerBackend->m_mixDevices.count() ; i++ )
180
MixDevice *md = _mixerBackend->m_mixDevices[i];
181
_mixerBackend->setRecsrcHW( md->id(), md->isRecSource() );
182
_mixerBackend->writeVolumeToHW( md->id(), md );
183
if ( md->isEnum() ) _mixerBackend->setEnumIdHW( md->id(), md->enumId() );
190
* Also, starts the polling timer, for polling the Volumes from the Mixer.
192
* @return 0, if OK. An Mixer::ERR_ error code otherwise
194
bool Mixer::openIfValid() {
195
bool ok = _mixerBackend->openIfValid();
197
recreateId(); // Fallback call. Actually recreateId() is supposed to be called later again, via setCardInstance()
198
MixDevice* recommendedMaster = _mixerBackend->recommendedMaster();
199
if ( recommendedMaster != 0 ) {
200
QString recommendedMasterStr = recommendedMaster->id();
201
setLocalMasterMD( recommendedMasterStr );
202
kDebug() << "Mixer::open() detected master: " << recommendedMaster->id();
206
kError(67100) << "Mixer::open() no master detected." << endl;
207
QString noMaster = "---no-master-detected---";
208
setLocalMasterMD(noMaster); // no master
210
connect( _mixerBackend, SIGNAL(controlChanged()), SLOT(controlChangedForwarder()) );
211
connect( _mixerBackend, SIGNAL(controlsReconfigured(const QString&)), SLOT(controlsReconfiguredForwarder(const QString&)) );
213
m_dbusName = "/Mixer" + QString::number(_mixerBackend->m_devnum);
214
kDebug() << "Registering DBUS object " << m_dbusName;
215
bool regResult = QDBusConnection::sessionBus().registerObject(m_dbusName, this);
216
kDebug() << "Registering DBUS object " << m_dbusName << " returns " << regResult;
222
void Mixer::controlChangedForwarder()
224
emit controlChanged();
227
void Mixer::controlsReconfiguredForwarder( const QString& mixer_ID )
229
emit controlsReconfigured(mixer_ID);
234
* Also, stops the polling timer.
240
return _mixerBackend->close();
244
/* ------- WRAPPER METHODS. START ------------------------------ */
245
unsigned int Mixer::size() const
247
return _mixerBackend->m_mixDevices.count();
250
MixDevice* Mixer::operator[](int num)
252
MixDevice* md = _mixerBackend->m_mixDevices.at( num );
257
MixSet Mixer::getMixSet()
259
return _mixerBackend->m_mixDevices;
264
* Returns the driver name, that handles this Mixer.
266
QString Mixer::getDriverName()
268
QString driverName = _mixerBackend->getDriverName();
269
// kDebug(67100) << "Mixer::getDriverName() = " << driverName << "\n";
273
bool Mixer::isOpen() const {
274
if ( _mixerBackend == 0 )
277
return _mixerBackend->isOpen();
280
void Mixer::readSetFromHWforceUpdate() const {
281
_mixerBackend->readSetFromHWforceUpdate();
284
/// Returns translated WhatsThis messages for a control.Translates from
285
QString Mixer::translateKernelToWhatsthis(const QString &kernelName)
287
return _mixerBackend->translateKernelToWhatsthis(kernelName);
290
/* ------- WRAPPER METHODS. END -------------------------------- */
295
void Mixer::setBalance(int balance)
297
if( balance == m_balance ) {
298
// balance unchanged => return
304
MixDevice* master = getLocalMasterMD();
306
// no master device available => return
310
Volume& volP = master->playbackVolume();
311
setBalanceInternal(volP);
312
Volume& volC = master->captureVolume();
313
setBalanceInternal(volC);
315
_mixerBackend->writeVolumeToHW( master->id(), master );
316
emit newBalance( volP );
319
void Mixer::setBalanceInternal(Volume& vol)
321
//_mixerBackend->readVolumeFromHW( master->id(), master );
323
int left = vol[ Volume::LEFT ];
324
int right = vol[ Volume::RIGHT ];
325
int refvol = left > right ? left : right;
326
if( m_balance < 0 ) // balance left
328
vol.setVolume( Volume::LEFT, refvol);
329
vol.setVolume( Volume::RIGHT, (m_balance * refvol) / 100 + refvol );
333
vol.setVolume( Volume::LEFT, -(m_balance * refvol) / 100 + refvol );
334
vol.setVolume( Volume::RIGHT, refvol);
338
// should return a name suitable for a human user to read (on a label, ...)
339
QString Mixer::readableName()
341
if ( _mixerBackend->m_mixerName.endsWith(":0"))
342
return _mixerBackend->m_mixerName.left(_mixerBackend->m_mixerName.length() - 2);
344
return _mixerBackend->m_mixerName;
348
QString Mixer::getBaseName()
350
return _mixerBackend->m_mixerName;
354
* Queries the Driver Factory for a driver.
355
* @par driver Index number. 0 <= driver < numDrivers()
357
QString Mixer::driverName( int driver )
359
getDriverNameFunc *f = g_mixerFactories[driver].getDriverName;
366
/* obsoleted by setInstance()
367
void Mixer::setID(QString& ref_id)
378
QString& Mixer::udi(){
379
return _mixerBackend->udi();
381
void Mixer::setGlobalMaster(QString& ref_card, QString& ref_control)
383
// The value is taken over without checking on existence. This allows the User to define
384
// a MasterCard that is not always available (e.g. it is an USB hotplugging device).
385
// Also you can set the master at any time you like, e.g. after reading the KMix configuration file
386
// and before actually constructing the Mixer instances (hint: this mehtod is static!).
387
_globalMasterCard = ref_card;
388
_globalMasterCardDevice = ref_control;
389
kDebug() << "Mixer::setGlobalMaster() card=" <<ref_card<< " control=" << ref_control;
392
Mixer* Mixer::getGlobalMasterMixerNoFalback()
395
if(Mixer::mixers().count() == 0)
398
for (int i=0; i< Mixer::mixers().count(); ++i )
400
Mixer* mixerTmp = Mixer::mixers()[i];
401
if ( mixerTmp != 0 && mixerTmp->id() == _globalMasterCard ) {
402
//kDebug() << "Mixer::masterCard() found " << _globalMasterCard;
410
Mixer* Mixer::getGlobalMasterMixer()
412
Mixer *mixer = getGlobalMasterMixerNoFalback();
413
if ( mixer == 0 && Mixer::mixers().count() > 0 ) {
415
mixer = Mixer::mixers()[0];
416
_globalMasterCard = mixer->id();
417
kDebug() << "Mixer::masterCard() fallback to " << _globalMasterCard;
419
//kDebug() << "Mixer::masterCard() returns " << mixer->id();
423
MixDevice* Mixer::getGlobalMasterMD()
425
return getGlobalMasterMD(true);
428
MixDevice* Mixer::getGlobalMasterMD(bool fallbackAllowed)
432
if ( fallbackAllowed)
433
mixer = Mixer::getGlobalMasterMixer();
435
mixer = Mixer::getGlobalMasterMixerNoFalback();
437
for(int i=0; i < mixer->_mixerBackend->m_mixDevices.count() ; i++ )
439
md = mixer->_mixerBackend->m_mixDevices[i];
440
if ( md->id() == _globalMasterCardDevice ) {
441
//kDebug() << "Mixer::masterCardDevice() found " << _globalMasterCardDevice;
447
kDebug() << "Mixer::masterCardDevice() returns 0 (no globalMaster)";
454
MixDevice* Mixer::getLocalMasterMD()
456
return find( _masterDevicePK );
459
void Mixer::setLocalMasterMD(QString &devPK)
461
_masterDevicePK = devPK;
467
Used internally by KMix and as DBUS method
469
void Mixer::setRecordSource( const QString& mixdeviceID, bool on )
471
_mixerBackend->setRecsrcHW( mixdeviceID, on );
478
MixDevice* Mixer::find(const QString& mixdeviceID)
481
for(int i=0; i<_mixerBackend->m_mixDevices.count() ; i++ )
483
md = _mixerBackend->m_mixDevices[i];
484
if( mixdeviceID == md->id() ) {
492
MixDevice* Mixer::getMixdeviceById( const QString& mixdeviceID )
495
int num = _mixerBackend->id2num(mixdeviceID);
496
if ( num!=-1 && num < (int)size() ) {
503
// Used also by the setMasterVolume() method.
504
void Mixer::setVolume( const QString& mixdeviceID, int percentage )
506
MixDevice *md = getMixdeviceById( mixdeviceID );
509
Volume& volP = md->playbackVolume();
510
Volume& volC = md->captureVolume();
512
// @todo The next call doesn't handle negative volumes correctly.
513
volP.setAllVolumes( (percentage*volP.maxVolume())/100 );
514
volC.setAllVolumes( (percentage*volC.maxVolume())/100 );
515
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
519
Call this if you have a *reference* to a Volume object and have modified that locally.
520
Pass the MixDevice associated to that Volume to this method for writing back
521
the changed value to the mixer.
522
Hint: Why do we do it this way?
523
- It is fast (no copying of Volume objects required)
524
- It is easy to understand ( read - modify - commit )
526
void Mixer::commitVolumeChange( MixDevice* md ) {
527
_mixerBackend->writeVolumeToHW(md->id(), md );
528
if (md->isEnum()) _mixerBackend->setEnumIdHW(md->id(), md->enumId() );
529
if ( md->captureVolume().hasSwitch() ) {
530
// Make sure to re-read the hardware, because seting capture might have failed.
531
// This is due to exclusive capture groups.
532
// If we wouldn't do this, KMix might show a Capture Switch disabled, but
533
// in reality the capture switch is still on.
535
// We also cannot rely on a notification from the driver (SocketNotifier), because
536
// nothing has changed, and so there s nothing to notify.
537
_mixerBackend->readSetFromHWforceUpdate();
538
_mixerBackend->readSetFromHW();
543
void Mixer::setMasterVolume( int percentage )
545
MixDevice *master = getLocalMasterMD();
547
setVolume( master->id(), percentage );
552
int Mixer::volume( const QString& mixdeviceID )
554
MixDevice *md= getMixdeviceById( mixdeviceID );
557
Volume vol=md->playbackVolume(); // @todo Is hardcoded to PlaybackVolume
558
// @todo This will not work, if minVolume != 0 !!!
559
// e.g.: minVolume=5 or minVolume=-10
560
// The solution is to check two cases:
561
// volume < 0 => use minVolume for volumeRange
562
// volume > 0 => use maxVolume for volumeRange
563
// If chosen volumeRange==0 => return 0
564
// As this is potentially used often (Sliders, ...), it
565
// should be implemented in the Volume class.
567
// For now we go with "maxVolume()", like in the rest of KMix.
568
long volumeRange = vol.maxVolume(); // -vol.minVolume() ;
569
if ( volumeRange == 0 )
575
return ( vol.getVolume( Volume::LEFT )*100) / volumeRange ;
579
// @dcop , especially for use in KMilo
580
void Mixer::setAbsoluteVolume( const QString& mixdeviceID, long absoluteVolume ) {
581
MixDevice *md= getMixdeviceById( mixdeviceID );
584
Volume& volP=md->playbackVolume();
585
Volume& volC=md->captureVolume();
586
volP.setAllVolumes( absoluteVolume );
587
volC.setAllVolumes( absoluteVolume );
588
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
591
// @dcop , especially for use in KMilo
592
long Mixer::absoluteVolume( const QString& mixdeviceID )
594
MixDevice *md = getMixdeviceById( mixdeviceID );
597
Volume& volP=md->playbackVolume(); // @todo Is hardcoded to PlaybackVolume
598
long avgVolume=volP.getAvgVolume((Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT));
602
// @dcop , especially for use in KMilo
603
long Mixer::absoluteVolumeMax( const QString& mixdeviceID )
605
MixDevice *md= getMixdeviceById( mixdeviceID );
608
Volume vol=md->playbackVolume(); // @todo Is hardcoded to PlaybackVolume
609
long maxVolume=vol.maxVolume();
613
// @dcop , especially for use in KMilo
614
long Mixer::absoluteVolumeMin( const QString& mixdeviceID )
616
MixDevice *md= getMixdeviceById( mixdeviceID );
619
Volume vol=md->playbackVolume(); // @todo Is hardcoded to PlaybackVolume
620
long minVolume=vol.minVolume();
625
int Mixer::masterVolume()
628
MixDevice *master = getLocalMasterMD();
630
vol = volume( master->id() );
636
QString Mixer::masterDeviceIndex()
638
MixDevice *master = getLocalMasterMD();
639
return master ? master->id() : QString();
644
void Mixer::increaseVolume( const QString& mixdeviceID )
646
MixDevice *md= getMixdeviceById( mixdeviceID );
648
Volume& volP=md->playbackVolume();
649
if ( volP.hasVolume() ) {
650
double step = (volP.maxVolume()-volP.minVolume()+1) / 20;
651
if ( step < 1 ) step = 1;
652
volP.changeAllVolumes(step);
655
Volume& volC=md->captureVolume();
656
if ( volC.hasVolume() ) {
657
double step = (volC.maxVolume()-volC.minVolume()+1) / 20;
658
if ( step < 1 ) step = 1;
659
volC.changeAllVolumes(step);
662
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
665
/* see comment at the end of decreaseVolume()
666
int vol=volume(mixdeviceID);
667
setVolume(mixdeviceID, vol+5);
672
void Mixer::decreaseVolume( const QString& mixdeviceID )
674
MixDevice *md= getMixdeviceById( mixdeviceID );
676
Volume& volP=md->playbackVolume();
677
if ( volP.hasVolume() ) {
678
double step = (volP.maxVolume()-volP.minVolume()+1) / 20;
679
if ( step < 1 ) step = 1;
680
volP.changeAllVolumes(-step);
683
Volume& volC=md->captureVolume();
684
if ( volC.hasVolume() ) {
685
double step = (volC.maxVolume()-volC.minVolume()+1) / 20;
686
if ( step < 1 ) step = 1;
687
volC.changeAllVolumes(-step);
691
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
693
/************************************************************
694
It is important, not to implement this method like this:
695
int vol=volume(mixdeviceID);
696
setVolume(mixdeviceID, vol-5);
697
It creates too big rounding errors. If you don't beleive me, then
698
do a decreaseVolume() and increaseVolume() with "vol.maxVolume() == 31".
699
***********************************************************/
703
void Mixer::setMute( const QString& mixdeviceID, bool on )
705
MixDevice *md= getMixdeviceById( mixdeviceID );
710
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
714
void Mixer::toggleMute( const QString& mixdeviceID )
716
MixDevice *md= getMixdeviceById( mixdeviceID );
719
md->setMuted( ! md->isMuted() );
720
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
724
bool Mixer::mute( const QString& mixdeviceID )
726
MixDevice *md= getMixdeviceById( mixdeviceID );
727
if (!md) return true;
729
return md->isMuted();
732
bool Mixer::isRecordSource( const QString& mixdeviceID )
734
MixDevice *md= getMixdeviceById( mixdeviceID );
735
if (!md) return false;
737
return md->isRecSource();
740
/// @DCOP WHAT DOES THIS METHOD?!?!?
741
bool Mixer::isAvailableDevice( const QString& mixdeviceID )
743
return getMixdeviceById( mixdeviceID );
746
void Mixer::setDynamic ( bool dynamic )
751
bool Mixer::isDynamic()
756
bool Mixer::moveStream( const QString id, const QString& destId )
758
// We should really check that id is within our md's....
759
return _mixerBackend->moveStream( id, destId );