mixer.cpp 20.8 KB
Newer Older
Stephan Kulow's avatar
Stephan Kulow committed
1
2
3
4
/*
 *              KMix -- KDE's full featured mini mixer
 *
 *
5
6
 * Copyright (C) 1996-2004 Christian Esken - esken@kde.org
 *                    2002 Helio Chissini de Castro - helio@conectiva.com.br
Stephan Kulow's avatar
Stephan Kulow committed
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * 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
Dirk Mueller's avatar
Dirk Mueller committed
20
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Stephan Kulow's avatar
Stephan Kulow committed
21
22
 */

23
#include "core/mixer.h"
24

25
#include <klocalizedstring.h>
Stephan Kulow's avatar
Stephan Kulow committed
26
#include <kconfig.h>
Stephan Kulow's avatar
Stephan Kulow committed
27

28
29
#include "backends/mixer_backend.h"
#include "backends/kmix-backends.cpp"
30
#include "core/ControlManager.h"
Christian Esken's avatar
Christian Esken committed
31
#include "core/GlobalConfig.h"
32
#include "core/volume.h"
Stephan Kulow's avatar
Stephan Kulow committed
33

34
/**
35
 * Some general design hints. Hierarchy is Mixer->MixDevice->Volume
36
37
 */

Christian Esken's avatar
Christian Esken committed
38
QList<Mixer *> Mixer::s_mixers;
39
40
MasterControl Mixer::_globalMasterCurrent;
MasterControl Mixer::_globalMasterPreferred;
41
bool Mixer::m_beepOnVolumeChange = false;
42

43
int Mixer::numDrivers()
44
45
46
{
    MixerFactory *factory = g_mixerFactories;
    int num = 0;
47
    while( factory->getMixer!=0 )
48
    {
49
50
51
52
53
54
55
        num++;
        factory++;
    }

    return num;
}

56
57
58
/*
 * Returns a reference of the current mixer list.
 */
Christian Esken's avatar
Christian Esken committed
59
QList<Mixer *>& Mixer::mixers()
60
{
61
    return s_mixers;
62
63
}

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
 * Returns whether there is at least one dynamic mixer active.
 * @returns true, if at least one dynamic mixer is active
 */
bool Mixer::dynamicBackendsPresent()
{
  foreach ( Mixer* mixer, Mixer::mixers() )
  {
    if ( mixer->isDynamic() )
      return true;
  }
  return false;
}

bool Mixer::pulseaudioPresent()
{
  foreach ( Mixer* mixer, Mixer::mixers() )
  {
    if ( mixer->getDriverName() == "PulseAudio" )
      return true;
  }
  return false;
}


89
90
91
92
Mixer::Mixer(const QString &ref_driverName, int device)
    : m_balance(0),
      _mixerBackend(nullptr),
      m_dynamic(false)
Stephan Kulow's avatar
Stephan Kulow committed
93
{
94
95
96
97
98
99
100
101
102
103
104
105
106
107
    _mixerBackend = 0;
    int driverCount = numDrivers();
    for (int driver=0; driver<driverCount; driver++ ) {
        QString driverName = Mixer::driverName(driver);
        if ( driverName == ref_driverName ) {
            // driver found => retrieve Mixer factory for that driver
            getMixerFunc *f = g_mixerFactories[driver].getMixer;
            if( f!=0 ) {
                _mixerBackend = f( this, device );
                readSetFromHWforceUpdate();  // enforce an initial update on first readSetFromHW()
            }
            break;
        }
    }
108
109
}

110
111


112
Mixer::~Mixer() {
113
114
   // Close the mixer. This might also free memory, depending on the called backend method
   close();
115
   _mixerBackend->deleteLater();
Dirk Mueller's avatar
Dirk Mueller committed
116
}
Stephan Kulow's avatar
Stephan Kulow committed
117

118

119
120
121
122
/*
 * Find a Mixer. If there is no mixer with the given id, 0 is returned
 */
Mixer* Mixer::findMixer( const QString& mixer_id)
123
{
124
125
126
127
128
129
130
131
132
133
134
    Mixer *mixer = 0;
    int mixerCount = Mixer::mixers().count();
    for ( int i=0; i<mixerCount; ++i)
    {
        if ( ((Mixer::mixers())[i])->id() == mixer_id )
        {
            mixer = (Mixer::mixers())[i];
            break;
        }
    }
    return mixer;
135
136
137
}


138
139
140
141
142
143
144
145
146
147
148
149
/**
 * Set the final ID of this Mixer.
 * <br>Warning: This method is VERY fragile, because it is requires information that we have very late,
 * especially the _cardInstance. We only know the _cardInstance, when we know the ID of the _mixerBackend->getId().
 * OTOH, the Mixer backend needs the _cardInstance during construction of its MixDevice instances.
 *
 * This means, we need the _cardInstance during construction of the Mixer, but we only know it after its constructed.
 * Actually its a design error. The _cardInstance MUST be set and managed by the backend.
 *
 * The current solution works but is very hacky - cardInstance is a parameter of openIfValid().
 *
 */
150
151
152
153
154
155
156
157
void Mixer::recreateId()
{
    /* As we use "::" and ":" as separators, the parts %1,%2 and %3 may not
     * contain it.
     * %1, the driver name is from the KMix backends, it does not contain colons.
     * %2, the mixer name, is typically coming from an OS driver. It could contain colons.
     * %3, the mixer number, is a number: it does not contain colons.
     */
Christian Esken's avatar
Christian Esken committed
158
    QString mixerName = _mixerBackend->getId();
159
    mixerName.replace(':','_');
160
    QString primaryKeyOfMixer = QString("%1::%2:%3")
161
            .arg(getDriverName(), mixerName)
162
            .arg(getCardInstance());
163
    // The following 3 replaces are for not messing up the config file
164
165
166
167
    primaryKeyOfMixer.replace(']','_');
    primaryKeyOfMixer.replace('[','_'); // not strictly necessary, but lets play safe
    primaryKeyOfMixer.replace(' ','_');
    primaryKeyOfMixer.replace('=','_');
168
    _id = primaryKeyOfMixer;
169
//	qCDebug(KMIX_LOG) << "Early _id=" << _id;
170
171
}

172
173
const QString Mixer::dbusPath()
{
174
	// _id needs to be fixed from the very beginning, as the MixDevice construction uses MixDevice::dbusPath().
175
	// So once the first MixDevice is created, this must return the correct value
176
177
	if (_id.isEmpty())
	{
178
179
180
181
182
183
184
		bool wasRegistered = _mixerBackend->_cardRegistered;
		// Bug 308014: Actually this a shortcut (you could also call it a hack). It would likely better if registerCard()
		//             would create the Id, but it requires cooperation from ALL backends. Also Mixer->getId() would need to
		//             proxy that to the backend.
		// So for now we lazily create the MixerId here, while creating the first MixDevice for that card.
		recreateId();
		if (! wasRegistered)
185
186
187
188
		{
			// Bug 308014: By checking _cardRegistered, we can be sure that everything is fine, including the fact that
			// the cardId (aka "card instance") is set. If _cardRegistered would be false, we will create potentially
			// wrong/duplicated DBUS Paths here.
189
			qCWarning(KMIX_LOG) << "Mixer id was empty when creating DBUS path. Emergency code created the id=" <<_id;
190
		}
191
	}
192

193
194
195
196
197
198
	// mixerName may contain arbitrary characters, so replace all that are not allowed to be be part of a DBUS path
	QString cardPath = _id;
	cardPath.replace(QRegExp("[^a-zA-Z0-9_]"), "_");
	cardPath.replace(QLatin1String("//"), QLatin1String("/"));

    return QString("/Mixers/" + cardPath);
199
}
200

201
void Mixer::volumeSave(KConfig *config) const
Stephan Kulow's avatar
Stephan Kulow committed
202
{
203
    //    qCDebug(KMIX_LOG) << "Mixer::volumeSave()";
Christian Esken's avatar
Christian Esken committed
204
    _mixerBackend->readSetFromHW();
Christian Esken's avatar
Christian Esken committed
205
    QString grp("Mixer");
206
    grp.append(id());
207
    _mixerBackend->m_mixDevices.write( config, grp );
208
209
210
211

    // This might not be the standard application config object
    // => Better be safe and call sync().
    config->sync();
Stephan Kulow's avatar
Stephan Kulow committed
212
213
}

214
void Mixer::volumeLoad(KConfig *config)
Stephan Kulow's avatar
Stephan Kulow committed
215
{
Christian Esken's avatar
Christian Esken committed
216
   QString grp("Mixer");
217
   grp.append(id());
218
219
220
221
222
223
224
   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
225
226
227
228
229
   if ( ! _mixerBackend->m_mixDevices.read( config, grp ) ) {
      // Some mixer backends don't support reading the volume into config
      // files, so bail out early if that's the case.
      return;
   }
230

Stephan Kulow's avatar
Stephan Kulow committed
231
   // set new settings
Christian Esken's avatar
Christian Esken committed
232
   for(int i=0; i<_mixerBackend->m_mixDevices.count() ; i++ )
Stephan Kulow's avatar
Stephan Kulow committed
233
   {
234
235
236
237
	   shared_ptr<MixDevice> md = _mixerBackend->m_mixDevices[i];
	   if ( md.get() == 0 )
		   continue;

Christian Esken's avatar
Christian Esken committed
238
       _mixerBackend->writeVolumeToHW( md->id(), md );
239
240
       if ( md->isEnum() )
    	   _mixerBackend->setEnumIdHW( md->id(), md->enumId() );
Stephan Kulow's avatar
Stephan Kulow committed
241
242
243
   }
}

244
245

/**
246
 * Opens the mixer.
247
248
 * Also, starts the polling timer, for polling the Volumes from the Mixer.
 *
249
 * @return true, if Mixer could be opened.
250
 */
251
bool Mixer::openIfValid()
252
{
253
254
255
256
257
258
    if (_mixerBackend==nullptr)
    {
        // If we did not instantiate a suitable backend, then the mixer is invalid.
        qCWarning(KMIX_LOG) << "no mixer backend";
        return false;
    }
259

Christian Esken's avatar
Christian Esken committed
260
    bool ok = _mixerBackend->openIfValid();
261
262
263
264
265
    if (!ok) return (false);

    recreateId();
    shared_ptr<MixDevice> recommendedMaster = _mixerBackend->recommendedMaster();
    if (recommendedMaster.get()!=nullptr)
266
    {
267
268
269
270
271
272
273
274
275
276
        QString recommendedMasterStr = recommendedMaster->id();
        setLocalMasterMD( recommendedMasterStr );
        qCDebug(KMIX_LOG) << "Detected master" << recommendedMaster->id();
    }
    else
    {
        if (!m_dynamic) qCCritical(KMIX_LOG) << "No master detected and not dynamic";
        else qCDebug(KMIX_LOG) << "No master detected but dynamic";
        QString noMaster = "---no-master-detected---";
        setLocalMasterMD(noMaster); // no master
Christian Esken's avatar
Christian Esken committed
277
    }
278

279
280
    new DBusMixerWrapper(this, dbusPath());
    return (true);
Stephan Kulow's avatar
Stephan Kulow committed
281
282
}

283

284
/**
285
 * Closes the mixer.
286
 */
287
void Mixer::close()
Stephan Kulow's avatar
Stephan Kulow committed
288
{
289
	if ( _mixerBackend != 0)
290
		_mixerBackend->closeCommon();
Stephan Kulow's avatar
Stephan Kulow committed
291
292
}

293
294

/* ------- WRAPPER METHODS. START ------------------------------ */
Stephan Kulow's avatar
Stephan Kulow committed
295
296
unsigned int Mixer::size() const
{
297
  return _mixerBackend->m_mixDevices.count();
Stephan Kulow's avatar
Stephan Kulow committed
298
299
300
}


301
MixSet &Mixer::getMixSet() const
302
{
303
    return (_mixerBackend->m_mixDevices);
304
305
}

306

307
308
309
/**
 * Returns the driver name, that handles this Mixer.
 */
310
QString Mixer::getDriverName() const
311
312
{
  QString driverName = _mixerBackend->getDriverName();
313
//  qCDebug(KMIX_LOG) << "Mixer::getDriverName() = " << driverName << "\n";
314
315
316
  return driverName;
}

317
318
319
320
321
322
bool Mixer::isOpen() const {
    if ( _mixerBackend == 0 )
        return false;
    else
        return _mixerBackend->isOpen();
}
323

324
void Mixer::readSetFromHWforceUpdate() const {
Christian Esken's avatar
Christian Esken committed
325
   _mixerBackend->readSetFromHWforceUpdate();
326
327
}

328
  /// Returns translated WhatsThis messages for a control.Translates from 
329
QString Mixer::translateKernelToWhatsthis(const QString &kernelName) const
330
331
332
333
{
   return _mixerBackend->translateKernelToWhatsthis(kernelName);
}

Christian Esken's avatar
Christian Esken committed
334
335
/* ------- WRAPPER METHODS. END -------------------------------- */

336
337
338
int Mixer::balance() const {
    return m_balance;
}
339

Stephan Kulow's avatar
Stephan Kulow committed
340
341
void Mixer::setBalance(int balance)
{
342
   if( balance == m_balance ) {
343
344
      // balance unchanged => return
      return;
345
   }
Stephan Kulow's avatar
Stephan Kulow committed
346

347
   m_balance = balance;
Stephan Kulow's avatar
Stephan Kulow committed
348

349
350
351
   shared_ptr<MixDevice> master = getLocalMasterMD();
   if ( master.get() == 0 )
   {
352
353
      // no master device available => return
      return;
354
   }
Stephan Kulow's avatar
Stephan Kulow committed
355

356
357
358
359
   Volume& volP = master->playbackVolume();
   setBalanceInternal(volP);
   Volume& volC = master->captureVolume();
   setBalanceInternal(volC);
Stephan Kulow's avatar
Stephan Kulow committed
360

361
362
363
   _mixerBackend->writeVolumeToHW( master->id(), master );
   emit newBalance( volP );
}
Stephan Kulow's avatar
Stephan Kulow committed
364

365
366
367
void Mixer::setBalanceInternal(Volume& vol)
{
   //_mixerBackend->readVolumeFromHW( master->id(), master );
Stephan Kulow's avatar
Stephan Kulow committed
368

369
370
   int left = vol.getVolume(Volume::LEFT);
   int right = vol.getVolume( Volume::RIGHT );
371
372
373
374
375
376
377
378
379
380
381
   int refvol = left > right ? left : right;
   if( m_balance < 0 ) // balance left
   {
      vol.setVolume( Volume::LEFT,  refvol);
      vol.setVolume( Volume::RIGHT, (m_balance * refvol) / 100 + refvol );
   }
   else
   {
      vol.setVolume( Volume::LEFT, -(m_balance * refvol) / 100 + refvol );
      vol.setVolume( Volume::RIGHT,  refvol);
   }
Stephan Kulow's avatar
Stephan Kulow committed
382
383
}

Christian Esken's avatar
Christian Esken committed
384
385
386
/**
 * Returns a name suitable for a human user to read (on a label, ...)
 */
387

388
QString Mixer::readableName(bool ampersandQuoted) const
389
{
Christian Esken's avatar
Christian Esken committed
390
	QString finalName = _mixerBackend->getName();
391
392
393
	if (ampersandQuoted)
		finalName.replace('&', "&&");

Christian Esken's avatar
Christian Esken committed
394
395
396
	if ( getCardInstance() > 1)
		finalName = finalName.append(" %1").arg(getCardInstance());

397
//	qCDebug(KMIX_LOG) << "name=" << _mixerBackend->getName() << "instance=" <<  getCardInstance() << ", finalName" << finalName;
Christian Esken's avatar
Christian Esken committed
398
	return finalName;
399
400
401
}


402
QString Mixer::getBaseName() const
403
{
Christian Esken's avatar
Christian Esken committed
404
  return _mixerBackend->getName();
405
}
406
407
408
409
410

/**
 * Queries the Driver Factory for a driver.
 * @par driver Index number. 0 <= driver < numDrivers()
 */
411
412
413
414
415
416
417
418
419
QString Mixer::driverName( int driver )
{
    getDriverNameFunc *f = g_mixerFactories[driver].getDriverName;
    if( f!=0 )
        return f();
    else
        return "unknown";
}

420
/* obsoleted by setInstance()
421
422
423
424
void Mixer::setID(QString& ref_id)
{
  _id = ref_id;
}
425
*/
426

427
const QString &Mixer::id() const
428
{
429
  return _id;
430
}
431

432
433
const QString &Mixer::udi() const
{
434
435
    return _mixerBackend->udi();
}
436
437

/**
Yuri Chornoivan's avatar
Yuri Chornoivan committed
438
 * Set the global master, which is shown in the dock area and which is accessible via the
439
440
441
442
443
444
445
446
447
448
449
450
451
 * DBUS masterVolume() method.
 *
 * The parameters are taken over as-is, this means without checking for validity.
 * This allows the User to define a master card 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 method is static!).
 *
 * @param ref_card The card id
 * @param ref_control The control id. The corresponding control must be present in the card.
 * @param preferred Whether this is the preferred master (auto-selected on coldplug and hotplug).
 */
void Mixer::setGlobalMaster(QString ref_card, QString ref_control, bool preferred)
452
{
453
    qCDebug(KMIX_LOG) << "ref_card=" << ref_card << ", ref_control=" << ref_control << ", preferred=" << preferred;
454
455
456
    _globalMasterCurrent.set(ref_card, ref_control);
    if ( preferred )
        _globalMasterPreferred.set(ref_card, ref_control);
457
    qCDebug(KMIX_LOG) << "Mixer::setGlobalMaster() card=" <<ref_card<< " control=" << ref_control;
458
459
}

Christian Esken's avatar
Christian Esken committed
460
Mixer* Mixer::getGlobalMasterMixerNoFalback()
461
{
462
   foreach ( Mixer* mixer, Mixer::mixers())
463
   {
464
465
      if ( mixer != 0 && mixer->id() == _globalMasterCurrent.getCard() )
         return mixer;
466
   }
467
   return 0;
Christian Esken's avatar
Christian Esken committed
468
469
470
471
472
}

Mixer* Mixer::getGlobalMasterMixer()
{
   Mixer *mixer = getGlobalMasterMixerNoFalback();
473
   if ( mixer == 0 && Mixer::mixers().count() > 0 ) {
474
      mixer = Mixer::mixers()[0];       // produce fallback
475
   }
476
   //qCDebug(KMIX_LOG) << "Mixer::masterCard() returns " << mixer->id();
477
   return mixer;
478
479
}

480
481
482
483
484

/**
 * Return the preferred global master.
 * If there is no preferred global master, returns the current master instead.
 */
485
MasterControl& Mixer::getGlobalMasterPreferred(bool fallbackAllowed)
486
{
487
488
489
    static MasterControl result;

    if ( !fallbackAllowed || _globalMasterPreferred.isValid() ) {
490
//        qCDebug(KMIX_LOG) << "Returning preferred master";
491
492
        return _globalMasterPreferred;
    }
493
494
495
496
497

    Mixer* mm = Mixer::getGlobalMasterMixerNoFalback();
    if (mm) {
        result.set(_globalMasterPreferred.getCard(), mm->getRecommendedDeviceId());
        if (!result.getControl().isEmpty())
498
//            qCDebug(KMIX_LOG) << "Returning extended preferred master";
499
            return result;
500
    }
501

502
    qCDebug(KMIX_LOG) << "Returning current master";
503
    return _globalMasterCurrent;
504
505
506
}


507
shared_ptr<MixDevice> Mixer::getGlobalMasterMD()
Christian Esken's avatar
Christian Esken committed
508
{
Dirk Mueller's avatar
Dirk Mueller committed
509
   return getGlobalMasterMD(true);
Christian Esken's avatar
Christian Esken committed
510
511
}

512

513
shared_ptr<MixDevice> Mixer::getGlobalMasterMD(bool fallbackAllowed)
514
{
515
	shared_ptr<MixDevice> mdRet;
516
	shared_ptr<MixDevice> firstDevice;
517
518
519
520
521
522
	Mixer *mixer = fallbackAllowed ?
		   Mixer::getGlobalMasterMixer() : Mixer::getGlobalMasterMixerNoFalback();

	if ( mixer == 0 )
		return mdRet;

523
524
525
526
527
528
	if (_globalMasterCurrent.getControl().isEmpty())
	{
		// Default (recommended) control
		return mixer->_mixerBackend->recommendedMaster();
	}

529
530
531
532
	foreach (shared_ptr<MixDevice> md, mixer->_mixerBackend->m_mixDevices )
	{
		if ( md.get() == 0 )
			continue; // invalid
Christian Esken's avatar
Christian Esken committed
533
534

		firstDevice=md;
535
536
537
538
539
540
541
		if ( md->id() == _globalMasterCurrent.getControl() )
		{
			mdRet = md;
			break; // found
		}
	}
	if ( mdRet.get() == 0 )
542
543
544
	{
	  //For some sound cards when using pulseaudio the mixer id is not proper hence returning the first device as master channel device
	  //This solves the bug id:290177 and problems stated in review #105422
545
		qCDebug(KMIX_LOG) << "Mixer::masterCardDevice() returns 0 (no globalMaster), returning the first device";
546
547
		mdRet=firstDevice;
	}
548
549

	return mdRet;
550
551
}

552
553
554
555
556
557
558
559
560
QString Mixer::getRecommendedDeviceId()
{
    if ( _mixerBackend != 0 ) {
        shared_ptr<MixDevice> recommendedMaster = _mixerBackend->recommendedMaster();
        if ( recommendedMaster.get() != 0 )
            return recommendedMaster->id();
    }
    return QString();
}
561

562
shared_ptr<MixDevice> Mixer::getLocalMasterMD() const
563
{
564
565
566
    if (_mixerBackend && _masterDevicePK.isEmpty())
        return _mixerBackend->recommendedMaster();
    return find( _masterDevicePK );
567
568
}

569
570
571
572
void Mixer::setLocalMasterMD(QString &devPK)
{
    _masterDevicePK = devPK;
}
573

Stephan Kulow's avatar
Stephan Kulow committed
574

575
shared_ptr<MixDevice> Mixer::find(const QString& mixdeviceID) const
576
{
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591

	shared_ptr<MixDevice> mdRet;

	foreach (shared_ptr<MixDevice> md, _mixerBackend->m_mixDevices )
	{
		if ( md.get() == 0 )
			continue; // invalid
		if ( md->id() == mixdeviceID )
		{
			mdRet = md;
			break; // found
		}
	}

    return mdRet;
592
}
593

594

595
shared_ptr<MixDevice> Mixer::getMixdeviceById( const QString& mixdeviceID ) const
596
{
597
	qCDebug(KMIX_LOG) << "id=" << mixdeviceID << "md=" << _mixerBackend->m_mixDevices.get(mixdeviceID).get()->id();
598
599
600
601
602
603
604
605
	return _mixerBackend->m_mixDevices.get(mixdeviceID);
//	shared_ptr<MixDevice> md;
//   int num = _mixerBackend->id2num(mixdeviceID);
//   if ( num!=-1 && num < (int)size() )
//   {
//      md = (*this)[num];
//   }
//   return md;
606
607
}

608
609
/**
   Call this if you have a *reference* to a Volume object and have modified that locally.
Christian Esken's avatar
Christian Esken committed
610
   Pass the MixDevice associated to that Volume to this method for writing back
611
   the changed value to the mixer.
Christian Esken's avatar
Christian Esken committed
612
613
614
   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 )
615
*/
Christian Esken's avatar
Christian Esken committed
616
void Mixer::commitVolumeChange(shared_ptr<MixDevice> md)
617
{
Christian Esken's avatar
Christian Esken committed
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
	_mixerBackend->writeVolumeToHW(md->id(), md);
	if (md->isEnum())
	{
		_mixerBackend->setEnumIdHW(md->id(), md->enumId());
	}
	if (md->captureVolume().hasSwitch())
	{
		// Make sure to re-read the hardware, because setting capture might have failed.
		// This is due to exclusive capture groups.
		// If we wouldn't do this, KMix might show a Capture Switch disabled, but
		// in reality the capture switch is still on.
		//
		// We also cannot rely on a notification from the driver (SocketNotifier), because
		// nothing has changed, and so there s nothing to notify.
		_mixerBackend->readSetFromHWforceUpdate();
633
		if (GlobalConfig::instance().data.debugControlManager)
634
			qCDebug(KMIX_LOG)
Christian Esken's avatar
Christian Esken committed
635
636
637
			<< "committing a control with capture volume, that might announce: " << md->id();
		_mixerBackend->readSetFromHW();
	}
638
	if (GlobalConfig::instance().data.debugControlManager)
639
		qCDebug(KMIX_LOG)
Christian Esken's avatar
Christian Esken committed
640
641
642
		<< "committing announces the change of: " << md->id();

	// We announce the change we did, so all other parts of KMix can pick up the change
643
	ControlManager::instance().announce(md->mixer()->id(), ControlManager::Volume,
Christian Esken's avatar
Christian Esken committed
644
		QString("Mixer.commitVolumeChange()"));
645
646
}

647
// @dbus, used also in kmix app
648
void Mixer::increaseVolume( const QString& mixdeviceID )
649
{
Christian Esken's avatar
Christian Esken committed
650
	increaseOrDecreaseVolume(mixdeviceID, false);
651
652
}

653
// @dbus
654
void Mixer::decreaseVolume( const QString& mixdeviceID )
655
{
Christian Esken's avatar
Christian Esken committed
656
657
658
	increaseOrDecreaseVolume(mixdeviceID, true);
}

659
660
661
662
663
664
665
666
/**
 * Increase or decrease all playback and capture channels of the given control.
 * This method is very similar to MDWSlider::increaseOrDecreaseVolume(), but it will
 * NOT auto-unmute.
 *
 * @param mixdeviceID The control name
 * @param decrease true for decrease. false for increase
 */
Christian Esken's avatar
Christian Esken committed
667
668
669
void Mixer::increaseOrDecreaseVolume( const QString& mixdeviceID, bool decrease )
{

670
671
672
	shared_ptr<MixDevice> md= getMixdeviceById( mixdeviceID );
    if (md.get() != 0)
    {
673
        Volume& volP=md->playbackVolume();
674
675
676
        if ( volP.hasVolume() )
        {
           volP.changeAllVolumes(volP.volumeStep(decrease));
677
678
        }
        
Christian Esken's avatar
Christian Esken committed
679
        Volume& volC=md->captureVolume();
680
681
682
        if ( volC.hasVolume() )
        {
           volC.changeAllVolumes(volC.volumeStep(decrease));
683
        }
684

Christian Esken's avatar
Christian Esken committed
685
686
        _mixerBackend->writeVolumeToHW(mixdeviceID, md);
    }
687
   ControlManager::instance().announce(md->mixer()->id(), ControlManager::Volume, QString("Mixer.increaseOrDecreaseVolume()"));
688

689
690
691
692
    /************************************************************
        It is important, not to implement this method like this:
    int vol=volume(mixdeviceID);
    setVolume(mixdeviceID, vol-5);
Christian Esken's avatar
Christian Esken committed
693
        It creates too big rounding errors. If you don't believe me, then
694
695
        do a decreaseVolume() and increaseVolume() with "vol.maxVolume() == 31".
    ***********************************************************/
696
697
}

Christian Esken's avatar
Christian Esken committed
698

699
700
701
702
703
void Mixer::setDynamic ( bool dynamic )
{
    m_dynamic = dynamic;
}

704
bool Mixer::isDynamic() const
705
706
707
708
{
    return m_dynamic;
}

709
710

bool Mixer::moveStream(const QString &id, const QString &destId)
711
712
{
    // We should really check that id is within our md's....
713
    bool ret = _mixerBackend->moveStream(id, destId);
714
    ControlManager::instance().announce(QString(), ControlManager::ControlList, QString("Mixer.moveStream()"));
715
    return (ret);
716
717
}

718
719
720
721
722

QString Mixer::currentStreamDevice(const QString &id) const
{
    return (_mixerBackend->currentStreamDevice(id));
}
723
724
725
726
727
728
729
730


QString Mixer::iconName() const
{
    const shared_ptr<MixDevice> master = getLocalMasterMD();
    if (master!=nullptr) return (master->iconName());
    return ("media-playback-start");			// fallback default icon
}