~ubuntu-branches/ubuntu/natty/kdemultimedia/natty-proposed

« back to all changes in this revision

Viewing changes to kmix/mixertoolbox.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Debian Qt/KDE Maintainers
  • Date: 2011-05-26 02:41:36 UTC
  • mfrom: (0.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 108.
  • Revision ID: james.westby@ubuntu.com-20110526024136-jjwsigfy402jhupm
Tags: upstream-4.6.3
ImportĀ upstreamĀ versionĀ 4.6.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * KMix -- KDE's full featured mini mixer
3
 
 *
4
 
 *
5
 
 * Copyright (C) 2004 Christian Esken <esken@kde.org>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or
8
 
 * modify it under the terms of the GNU Library General Public
9
 
 * License as published by the Free Software Foundation; either
10
 
 * version 2 of the License, or (at your option) any later version.
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful,
13
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
 * Library General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU Library General Public
18
 
 * License along with this program; if not, write to the Free
19
 
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
 
 */
21
 
 
22
 
 
23
 
#include <QDir>
24
 
#include <QWidget>
25
 
#include <QString>
26
 
 
27
 
//#include <kdebug.h>
28
 
#include <klocale.h>
29
 
#include <kstandarddirs.h>
30
 
 
31
 
#include "guiprofile.h"
32
 
#include "kmixdevicemanager.h"
33
 
#include "mixdevice.h"
34
 
#include "mixer.h"
35
 
 
36
 
#include "mixertoolbox.h"
37
 
 
38
 
 
39
 
MixerToolBox* MixerToolBox::s_instance      = 0;
40
 
QMap<Mixer*,GUIProfile*> MixerToolBox::s_fallbackProfiles;
41
 
QRegExp MixerToolBox::s_ignoreMixerExpression("Modem");
42
 
//KLocale* MixerToolBox::s_whatsthisLocale = 0;
43
 
 
44
 
/***********************************************************************************
45
 
 Attention:
46
 
 This MixerToolBox is linked to the KMix Main Program, the KMix Applet and kmixctrl.
47
 
 As we do not want to link in more than necessary to kmixctrl, you are asked
48
 
 not to put any GUI classes in here.
49
 
 In the case where it is unavoidable, please put them in KMixToolBox.
50
 
 ***********************************************************************************/
51
 
 
52
 
MixerToolBox* MixerToolBox::instance()
53
 
{
54
 
   if ( s_instance == 0 ) {
55
 
      s_instance = new MixerToolBox();
56
 
//      if ( s_ignoreMixerExpression.isEmpty() )
57
 
//          s_ignoreMixerExpression.setPattern("Modem");
58
 
   }
59
 
   return s_instance;
60
 
}
61
 
 
62
 
 
63
 
/**
64
 
 * Scan for Mixers in the System. This is the method that implicitely fills the
65
 
 * list of Mixer's, which is accessible via the static Mixer::mixer() method.
66
 
 *
67
 
 * This is run only once during the initialization phase of KMix. It has the following tasks:
68
 
 * 1) Coldplug scan, to fill the initial mixer list
69
 
* 2) Rember UDI's, to match them when unplugging a device
70
 
* 3) Find out, which Backend to use (plugin events of other Backends are ignored).
71
 
 *
72
 
 * @par multiDriverMode Whether the Mixer scan should try more all backendends.
73
 
 *          'true' means to scan all backends. 'false' means: After scanning the
74
 
 *          current backend the next backend is only scanned if no Mixers were found yet.
75
 
 * @par ref_hwInfoString Here a descripitive text of the scan is returned (Hardware Information)
76
 
 */
77
 
void MixerToolBox::initMixer(bool multiDriverMode, QString& ref_hwInfoString)
78
 
{
79
 
   //kDebug(67100) << "IN MixerToolBox::initMixer()";
80
 
 
81
 
   // Find all mixers and initialize them
82
 
   int drvNum = Mixer::numDrivers();
83
 
 
84
 
   int driverWithMixer = -1;
85
 
   bool multipleDriversActive = false;
86
 
 
87
 
   QString driverInfo = "";
88
 
   QString driverInfoUsed = "";
89
 
 
90
 
   for( int drv1=0; drv1<drvNum; drv1++ )
91
 
   {
92
 
      QString driverName = Mixer::driverName(drv1);
93
 
      if ( driverInfo.length() > 0 ) {
94
 
         driverInfo += " + ";
95
 
      }
96
 
      driverInfo += driverName;
97
 
   }
98
 
   /* Run a loop over all drivers. The loop will terminate after the first driver which
99
 
      has mixers. And here is the reason:
100
 
      - If you run ALSA with ALSA-OSS-Emulation enabled, mixers will show up twice: once
101
 
         as native ALSA mixer, once as OSS mixer (emulated by ALSA). This is bad and WILL
102
 
         confuse users. So it is a design decision that we can compile in multiple drivers
103
 
         but we can run only one driver.
104
 
      - For special usage scenarios, people will still want to run both drivers at the
105
 
         same time. We allow them to hack their Config-File, where they can enable a
106
 
         multi-driver mode.
107
 
      - Another remark: For KMix3.0 or so, we should allow multiple-driver, for allowing
108
 
         addition of special-use drivers, e.g. an Jack-Mixer-Backend, or a CD-Rom volume Backend.
109
 
      */
110
 
   
111
 
   bool autodetectionFinished = false;
112
 
   for( int drv=0; drv<drvNum; drv++ )
113
 
   {
114
 
      QString driverName = Mixer::driverName(drv);
115
 
      kDebug(67100) << "Looking for mixers with the : " << driverName << " driver";
116
 
 
117
 
      if ( autodetectionFinished ) {
118
 
         // inner loop indicates that we are finished => sane exit from outer loop
119
 
         break;
120
 
      }
121
 
   
122
 
      bool drvInfoAppended = false;
123
 
      // The "19" below is just a "silly" number:
124
 
      // (Old: The loop will break as soon as an error is detected - e.g. on 3rd loop when 2 soundcards are installed)
125
 
      // New: We don't try be that clever anymore. We now blindly scan 20 cards, as the clever
126
 
      // approach doesn't work for the one or other user (e.g. hotplugging might create holes in the list of soundcards).
127
 
      int devNumMax = 19;
128
 
      for( int dev=0; dev<=devNumMax; dev++ )
129
 
      {
130
 
         Mixer *mixer = new Mixer( driverName, dev );
131
 
         bool mixerAccepted = possiblyAddMixer(mixer);
132
 
   
133
 
         /* Lets decide if the autoprobing shall end (BTW: In multiDriver mode we scan all devices, so no check is necessary) */
134
 
         if ( ! multiDriverMode ) {
135
 
            // In Single-Driver-mode we only need to check after we reached devNumMax
136
 
            if ( dev == devNumMax && Mixer::mixers().count() != 0 )
137
 
                autodetectionFinished = true; // highest device number of driver and a Mixer => finished
138
 
        }
139
 
      
140
 
         if ( mixerAccepted )
141
 
         {
142
 
            kDebug(67100) << "Success! Found a mixer with the : " << driverName << " driver";
143
 
            // append driverName (used drivers)
144
 
            if ( !drvInfoAppended )
145
 
            {
146
 
               drvInfoAppended = true;
147
 
               if (  Mixer::mixers().count() > 1)
148
 
                  driverInfoUsed += " + ";
149
 
               driverInfoUsed += driverName;
150
 
            }
151
 
 
152
 
            // Check whether there are mixers in different drivers, so that the user can be warned
153
 
            if ( !multipleDriversActive )
154
 
            {
155
 
               if ( driverWithMixer == -1 )
156
 
               {
157
 
                  // Aha, this is the very first detected device
158
 
                  driverWithMixer = drv;
159
 
               }
160
 
               else if ( driverWithMixer != drv )
161
 
               {
162
 
                   // Got him: There are mixers in different drivers
163
 
                   multipleDriversActive = true;
164
 
               }
165
 
            } //  !multipleDriversActive
166
 
         } // mixerAccepted
167
 
      
168
 
      } // loop over sound card devices of current driver
169
 
 
170
 
      if (autodetectionFinished) {
171
 
         break;
172
 
      }
173
 
   } // loop over soundcard drivers
174
 
 
175
 
   
176
 
    // Add a master device (if we haven't defined one yet)
177
 
   if ( Mixer::getGlobalMasterMD(false) == 0 ) {
178
 
      // We have no master card yet. This actually only happens when there was
179
 
      // not one defined in the kmixrc.
180
 
      // So lets just set the first card as master card.
181
 
      if ( Mixer::mixers().count() > 0 ) {
182
 
         QString controlId = Mixer::mixers().first()->getLocalMasterMD()->id();
183
 
         Mixer::setGlobalMaster( Mixer::mixers().first()->id(), controlId);
184
 
      }
185
 
   }
186
 
   else {
187
 
      // setGlobalMaster was already set after reading the configuration.
188
 
      // So we must make the local master consistent
189
 
      MixDevice* md = Mixer::getGlobalMasterMD();
190
 
      QString mdID = md->id();
191
 
      md->mixer()->setLocalMasterMD(mdID);
192
 
   }
193
 
 
194
 
 
195
 
 
196
 
   if ( Mixer::mixers().count() == 0 )
197
 
   {
198
 
      // If there was no mixer found, we assume, that hotplugging will take place
199
 
       // on the preferred driver (this is always the first in the backend list).
200
 
      driverInfoUsed = Mixer::driverName(0);
201
 
   }
202
 
 
203
 
   ref_hwInfoString = i18n("Sound drivers supported:");
204
 
   ref_hwInfoString.append(" ").append( driverInfo ).append(    "\n").append(i18n("Sound drivers used:")) .append(" ").append(driverInfoUsed);
205
 
 
206
 
   if ( multipleDriversActive )
207
 
   {
208
 
      // this will only be possible by hacking the config-file, as it will not be officially supported
209
 
      ref_hwInfoString += "\nExperimental multiple-Driver mode activated";
210
 
      QString allDrivermatch("*");
211
 
      KMixDeviceManager::instance()->setHotpluggingBackends(allDrivermatch);
212
 
   }
213
 
   else {
214
 
       KMixDeviceManager::instance()->setHotpluggingBackends(driverInfoUsed);
215
 
   }
216
 
 
217
 
   kDebug(67100) << ref_hwInfoString << endl << "Total number of detected Mixers: " << Mixer::mixers().count();
218
 
   //kDebug(67100) << "OUT MixerToolBox::initMixer()";
219
 
 
220
 
}
221
 
 
222
 
bool MixerToolBox::possiblyAddMixer(Mixer *mixer)
223
 
{
224
 
    if ( mixer->openIfValid() )
225
 
    {
226
 
        if ( (!s_ignoreMixerExpression.isEmpty()) && mixer->id().contains(s_ignoreMixerExpression) ) {
227
 
            // This Mixer should be ignored (default expression is "Modem").
228
 
            delete mixer;
229
 
            mixer = 0;
230
 
            return false;
231
 
        }
232
 
        Mixer::mixers().append( mixer );
233
 
        // Count mixer nums for every mixer name to identify mixers with equal names.
234
 
        // This is for creating persistent (reusable) primary keys, which can safely
235
 
        // be referenced (especially for config file access, so it is meant to be persistent!).
236
 
        s_mixerNums[mixer->baseName()]++;
237
 
        kDebug(67100) << "mixerNums entry of added mixer is now: " <<  s_mixerNums[mixer->baseName()];
238
 
        // Create a useful PK
239
 
        /* As we use "::" and ":" as separators, the parts %1,%2 and %3 may not
240
 
         * contain it.
241
 
         * %1, the driver name is from the KMix backends, it does not contain colons.
242
 
         * %2, the mixer name, is typically coming from an OS driver. It could contain colons.
243
 
         * %3, the mixer number, is a number: it does not contain colons.
244
 
         */
245
 
        QString mixerName = mixer->baseName();
246
 
        mixerName.replace(":","_");
247
 
        QString primaryKeyOfMixer = QString("%1::%2:%3")
248
 
                .arg(mixer->getDriverName())
249
 
                .arg(mixerName)
250
 
                .arg(s_mixerNums[mixer->baseName()]);
251
 
        // The following 3 replaces are for not messing up the config file
252
 
        primaryKeyOfMixer.replace("]","_");
253
 
        primaryKeyOfMixer.replace("[","_"); // not strictly necessary, but lets play safe
254
 
        primaryKeyOfMixer.replace(" ","_");
255
 
        primaryKeyOfMixer.replace("=","_");
256
 
    
257
 
        mixer->setID(primaryKeyOfMixer);
258
 
        emit mixerAdded(primaryKeyOfMixer);
259
 
        return true;
260
 
    } // valid
261
 
    else
262
 
    {
263
 
        delete mixer;
264
 
        mixer = 0;
265
 
        return false;
266
 
    } // invalid
267
 
}
268
 
 
269
 
/* This allows to set an expression form Mixers that should be ignored.
270
 
  The default is "Modem", because most people don't want to control the modem volume. */
271
 
void MixerToolBox::setMixerIgnoreExpression(QString& ignoreExpr)
272
 
{
273
 
    s_ignoreMixerExpression.setPattern(ignoreExpr);
274
 
}
275
 
 
276
 
QString MixerToolBox::mixerIgnoreExpression()
277
 
{
278
 
     return s_ignoreMixerExpression.pattern( );
279
 
}
280
 
 
281
 
void MixerToolBox::removeMixer(Mixer *par_mixer)
282
 
{
283
 
    for (int i=0; i<Mixer::mixers().count(); ++i) {
284
 
        Mixer *mixer = (Mixer::mixers())[i];
285
 
        if ( mixer == par_mixer ) {
286
 
            s_mixerNums[mixer->baseName()]--;
287
 
            kDebug(67100) << "mixerNums entry of removed mixer is now: " <<  s_mixerNums[mixer->baseName()];
288
 
            Mixer::mixers().removeAt(i);
289
 
            delete mixer;
290
 
        }
291
 
    }
292
 
}
293
 
 
294
 
 
295
 
 
296
 
/*
297
 
 * Clean up and free all resources of all found Mixers, which were found in the initMixer() call
298
 
 */
299
 
void MixerToolBox::deinitMixer()
300
 
{
301
 
   //kDebug(67100) << "IN MixerToolBox::deinitMixer()";
302
 
 
303
 
   int mixerCount = Mixer::mixers().count();
304
 
   for ( int i=0; i<mixerCount; ++i)
305
 
   {
306
 
      Mixer* mixer = (Mixer::mixers())[i];
307
 
      //kDebug(67100) << "MixerToolBox::deinitMixer() Remove Mixer";
308
 
      mixer->close();
309
 
      delete mixer;
310
 
   }
311
 
   Mixer::mixers().clear();
312
 
   // kDebug(67100) << "OUT MixerToolBox::deinitMixer()";
313
 
}
314
 
 
315
 
/*
316
 
 * Find a Mixer. If there is no mixer with the given id, 0 is returned
317
 
 */
318
 
Mixer* MixerToolBox::find( const QString& mixer_id)
319
 
{
320
 
   //kDebug(67100) << "IN MixerToolBox::find()";
321
 
 
322
 
   Mixer* mixer = 0;
323
 
   int mixerCount = Mixer::mixers().count();
324
 
   for ( int i=0; i<mixerCount; ++i)
325
 
   {
326
 
      if ( ((Mixer::mixers())[i])->id() == mixer_id )
327
 
      {
328
 
         mixer = (Mixer::mixers())[i];
329
 
         break;
330
 
      }
331
 
   }
332
 
 
333
 
   return mixer;
334
 
   // kDebug(67100) << "OUT MixerToolBox::find()";
335
 
}
336
 
 
337
 
/*
338
 
KLocale* MixerToolBox::whatsthisControlLocale()
339
 
{
340
 
   if ( s_whatsthisLocale == 0 ) {
341
 
          s_whatsthisLocale = new KLocale("kmix-controls");
342
 
   }
343
 
   return s_whatsthisLocale;
344
 
}
345
 
*/
346
 
 
347
 
/**
348
 
   Returns a GUI Profile
349
 
  */
350
 
GUIProfile* MixerToolBox::selectProfile(Mixer* mixer)
351
 
{
352
 
   /** This is a two-step process *****************************************
353
 
      * (1) Build a list of all files we want to check
354
 
      * (2) Evaluate all files and keep the best
355
 
      ***********************************************************************/
356
 
 
357
 
   GUIProfile* guiprofBest = 0;
358
 
   unsigned long matchValueBest = 0;
359
 
   unsigned long matchValueTemp = 0;
360
 
   
361
 
   QString userProfileDir = KStandardDirs::locateLocal("appdata", "profiles/" );
362
 
 
363
 
   QString mixerNameSpacesToUnderscores = mixer->baseName();
364
 
   mixerNameSpacesToUnderscores.replace(" ","_");
365
 
 
366
 
   // (1) User profile Directory
367
 
   QDir dir(userProfileDir);
368
 
   dir.setFilter(QDir::Files);
369
 
   dir.setNameFilters(QStringList(mixer->getDriverName() + "." + mixerNameSpacesToUnderscores + "*.xml"));
370
 
   QFileInfoList fileList = dir.entryInfoList();
371
 
 
372
 
   QString fileNamePrefix = "profiles/" + mixer->getDriverName() + ".";
373
 
 
374
 
   // (2) Default profile for Soundcard Driver (usually from system Directory)
375
 
   QString fileName = fileNamePrefix + "default.xml";
376
 
   QString fileNameFQ;
377
 
   fileNameFQ = KStandardDirs::locate("appdata", fileName );
378
 
kDebug() << fileName << "; fnfq1=" << fileNameFQ;
379
 
   if (!fileNameFQ.isEmpty())
380
 
       fileList.insert(0, QFileInfo(fileNameFQ));
381
 
 
382
 
   // (3) Soundcard specific profile (usually from system Directory)
383
 
   fileName = fileNamePrefix + mixerNameSpacesToUnderscores + ".xml";
384
 
   fileNameFQ = KStandardDirs::locate("appdata", fileName );
385
 
kDebug() << fileName << "; fnfq2=" << fileNameFQ;
386
 
   if (!fileNameFQ.isEmpty())
387
 
      fileList.insert(0, QFileInfo(fileNameFQ));
388
 
 
389
 
 
390
 
 
391
 
        for (int i = 0; i < fileList.size(); ++i) {
392
 
                QFileInfo fileInfo = fileList.at(i);
393
 
                QString fileNameAbs = fileInfo.absoluteFilePath();
394
 
                QString fileNameRelToProfile = "profiles/" + fileInfo.fileName();
395
 
                kDebug() << i << ": Try user profile " << fileNameAbs;
396
 
                GUIProfile* guiprofTemp = new GUIProfile();
397
 
                if ( guiprofTemp->readProfile(fileNameAbs, fileNameRelToProfile) ) {
398
 
                        matchValueTemp = guiprofTemp->match(mixer);
399
 
                        if ( matchValueTemp < matchValueBest ) {
400
 
                                delete guiprofTemp;
401
 
                                guiprofTemp = 0;
402
 
                                matchValueTemp = 0;
403
 
                        }
404
 
                        else {
405
 
                                guiprofBest = guiprofTemp;
406
 
                                matchValueBest = matchValueTemp;
407
 
                        }
408
 
                }
409
 
        }
410
 
 
411
 
 
412
 
   if ( guiprofBest == 0 ) {
413
 
      // Still no profile found. This should usually not happen. This means one of the following things:
414
 
      // a) The KMix installation is not OK
415
 
      // b) The user has a defective profile in ~/.kde/share/apps/kmix/profiles/
416
 
      // c) It is a Backend that ships no default profile (currently this is only Mixer_SUN and Mixer_PULSE)
417
 
      guiprofBest = fallbackProfile(mixer);
418
 
   }
419
 
//    kDebug(67100) << "New Best    =" << matchValueBest << " pointer=" << guiprofBest << "\n";
420
 
 
421
 
   return guiprofBest;
422
 
}
423
 
 
424
 
 
425
 
GUIProfile* MixerToolBox::fallbackProfile(Mixer *mixer)
426
 
{
427
 
   if ( ! s_fallbackProfiles.contains(mixer) ) {
428
 
      GUIProfile *fallback = new GUIProfile();
429
 
 
430
 
      ProfProduct* prd = new ProfProduct();
431
 
      prd->vendor         = mixer->getDriverName();
432
 
      prd->productName    = mixer->readableName();
433
 
      prd->productRelease = "1.0";
434
 
      fallback->_products.insert(prd);
435
 
 
436
 
      ProfControl* ctl = new ProfControl();
437
 
      ctl->id          = ".*";
438
 
      ctl->regexp      = ".*";   // make sure id matches the regexp
439
 
      ctl->subcontrols = ".*";
440
 
      ctl->show        = "simple";
441
 
      fallback->_controls.push_back(ctl);
442
 
 
443
 
      fallback->_soundcardDriver = mixer->getDriverName();
444
 
      fallback->_soundcardName   = mixer->readableName();
445
 
 
446
 
      fallback->finalizeProfile();
447
 
      s_fallbackProfiles[mixer] = fallback;
448
 
   }
449
 
   return s_fallbackProfiles[mixer];
450
 
}
451
 
 
452
 
#include "mixertoolbox.moc"