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

« back to all changes in this revision

Viewing changes to kmix/backends/mixer_oss.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
 *              Copyright (C) 1996-2000 Christian Esken
 
5
 *                        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
#include "mixer_oss.h"
 
23
#include "core/mixer.h"
 
24
#include "core/kmixdevicemanager.h"
 
25
 
 
26
#include <fcntl.h>
 
27
#include <errno.h>
 
28
#include <unistd.h>
 
29
#include <sys/ioctl.h>
 
30
#include <sys/types.h>
 
31
#include <sys/stat.h>
 
32
 
 
33
// Since we're guaranteed an OSS setup here, let's make life easier
 
34
#if !defined(__NetBSD__) && !defined(__OpenBSD__)
 
35
        #include <sys/soundcard.h>
 
36
#else
 
37
        #include <soundcard.h>
 
38
#endif
 
39
 
 
40
 
 
41
#include <klocale.h>
 
42
#include <QTimer>
 
43
/*
 
44
  I am using a fixed MAX_MIXDEVS #define here.
 
45
   People might argue, that I should rather use the SOUND_MIXER_NRDEVICES
 
46
   #define used by OSS. But using this #define is not good, because it is
 
47
   evaluated during compile time. Compiling on one platform and running
 
48
   on another with another version of OSS with a different value of
 
49
   SOUND_MIXER_NRDEVICES is very bad. Because of this, usage of
 
50
   SOUND_MIXER_NRDEVICES should be discouraged.
 
51
 
 
52
   The #define below is only there for internal reasons.
 
53
   In other words: Don't play around with this value
 
54
 */
 
55
#define MAX_MIXDEVS 32
 
56
 
 
57
const char* MixerDevNames[32]={
 
58
    I18N_NOOP("Volume"),   I18N_NOOP("Bass"),       I18N_NOOP("Treble"),
 
59
    I18N_NOOP("Synth"),    I18N_NOOP("Pcm"),        I18N_NOOP("Speaker"),
 
60
    I18N_NOOP("Line"),     I18N_NOOP("Microphone"), I18N_NOOP("CD"),
 
61
    I18N_NOOP("Mix"),      I18N_NOOP("Pcm2"),       I18N_NOOP("RecMon"),
 
62
    I18N_NOOP("IGain"),    I18N_NOOP("OGain"),      I18N_NOOP("Line1"),
 
63
    I18N_NOOP("Line2"),    I18N_NOOP("Line3"),      I18N_NOOP("Digital1"),
 
64
    I18N_NOOP("Digital2"), I18N_NOOP("Digital3"),   I18N_NOOP("PhoneIn"),
 
65
    I18N_NOOP("PhoneOut"), I18N_NOOP("Video"),      I18N_NOOP("Radio"),
 
66
    I18N_NOOP("Monitor"),  I18N_NOOP("3D-depth"),   I18N_NOOP("3D-center"),
 
67
    I18N_NOOP("unknown"),  I18N_NOOP("unknown"),    I18N_NOOP("unknown"),
 
68
    I18N_NOOP("unknown") , I18N_NOOP("unused") };
 
69
 
 
70
const MixDevice::ChannelType MixerChannelTypes[32] = {
 
71
    MixDevice::VOLUME,   MixDevice::BASS,       MixDevice::TREBLE,
 
72
    MixDevice::MIDI,     MixDevice::AUDIO,      MixDevice::SPEAKER,
 
73
    MixDevice::EXTERNAL, MixDevice::MICROPHONE, MixDevice::CD,
 
74
    MixDevice::VOLUME,   MixDevice::AUDIO,      MixDevice::RECMONITOR,
 
75
    MixDevice::VOLUME,   MixDevice::RECMONITOR, MixDevice::EXTERNAL,
 
76
    MixDevice::EXTERNAL, MixDevice::EXTERNAL,   MixDevice::DIGITAL,
 
77
    MixDevice::DIGITAL,  MixDevice::DIGITAL,    MixDevice::EXTERNAL,
 
78
    MixDevice::EXTERNAL, MixDevice::VIDEO,      MixDevice::EXTERNAL,
 
79
    MixDevice::EXTERNAL, MixDevice::VOLUME,     MixDevice::VOLUME,
 
80
    MixDevice::UNKNOWN,  MixDevice::UNKNOWN,    MixDevice::UNKNOWN,
 
81
    MixDevice::UNKNOWN,  MixDevice::UNKNOWN };
 
82
 
 
83
Mixer_Backend* OSS_getMixer( Mixer* mixer, int device )
 
84
{
 
85
  Mixer_Backend *l_mixer;
 
86
  l_mixer = new Mixer_OSS( mixer, device );
 
87
  return l_mixer;
 
88
}
 
89
 
 
90
Mixer_OSS::Mixer_OSS( Mixer* mixer, int device) : Mixer_Backend(mixer, device)
 
91
{
 
92
  if( device == -1 ) m_devnum = 0;
 
93
}
 
94
 
 
95
Mixer_OSS::~Mixer_OSS()
 
96
{
 
97
  close();
 
98
}
 
99
 
 
100
int Mixer_OSS::open()
 
101
{
 
102
    QString finalDeviceName;
 
103
    finalDeviceName = deviceName( m_devnum );
 
104
    if ((m_fd= ::open( finalDeviceName.toAscii().data(), O_RDWR)) < 0)
 
105
    {
 
106
        if ( errno == EACCES )
 
107
        return Mixer::ERR_PERM;
 
108
        else {
 
109
            finalDeviceName = deviceNameDevfs( m_devnum );
 
110
            if ((m_fd= ::open( finalDeviceName.toAscii().data(), O_RDWR)) < 0)
 
111
            {
 
112
                if ( errno == EACCES )
 
113
                    return Mixer::ERR_PERM;
 
114
                    else
 
115
                return Mixer::ERR_OPEN;
 
116
            }
 
117
        }
 
118
    }
 
119
 
 
120
    _udi = KMixDeviceManager::instance()->getUDI_OSS(finalDeviceName);
 
121
    if ( _udi.isEmpty() ) {
 
122
        QString msg("No UDI found for '");
 
123
        msg += finalDeviceName;
 
124
        msg += "'. Hotplugging not possible";
 
125
        kDebug(67100) << msg;
 
126
    }
 
127
      int devmask, recmask, i_recsrc, stereodevs;
 
128
      // Mixer is open. Now define properties
 
129
      if (ioctl(m_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
 
130
        return Mixer::ERR_READ;
 
131
      if (ioctl(m_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
 
132
        return Mixer::ERR_READ;
 
133
      if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
 
134
        return Mixer::ERR_READ;
 
135
      if (ioctl(m_fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1)
 
136
        return Mixer::ERR_READ;
 
137
 
 
138
          int idx = 0;
 
139
          while( devmask && idx < MAX_MIXDEVS )
 
140
            {
 
141
              if( devmask & ( 1 << idx ) ) // device active?
 
142
                {
 
143
                  Volume::ChannelMask chnmask = Volume::MLEFT;
 
144
                  if ( stereodevs & ( 1 << idx ) ) chnmask = (Volume::ChannelMask)(chnmask|Volume::MRIGHT);
 
145
 
 
146
                  Volume playbackVol( chnmask, 100, 1, true, false );
 
147
 
 
148
                  QString id;
 
149
                  id.setNum(idx);
 
150
                  MixDevice* md =
 
151
                    new MixDevice(
 
152
                                   _mixer,
 
153
                                   id,
 
154
                                   i18n(MixerDevNames[idx]),
 
155
                                   MixerChannelTypes[idx]);
 
156
                  md->addPlaybackVolume(playbackVol);
 
157
 
 
158
                  // Tutorial: Howto add a simple capture switch
 
159
                  if ( recmask & ( 1 << idx ) ) {
 
160
                     // can be captured => add capture volume, with no capture volume
 
161
                     chnmask = Volume::MNONE;
 
162
                     Volume captureVol( chnmask, 100, 1, true, true );
 
163
                     md->addCaptureVolume(captureVol);
 
164
                 }
 
165
 
 
166
                  m_mixDevices.append( md );
 
167
                }
 
168
              idx++;
 
169
            }
 
170
 
 
171
#if defined(SOUND_MIXER_INFO)
 
172
      struct mixer_info l_mix_info;
 
173
      if (ioctl(m_fd, SOUND_MIXER_INFO, &l_mix_info) != -1)
 
174
        {
 
175
          m_mixerName = l_mix_info.name;
 
176
        }
 
177
      else
 
178
#endif
 
179
 
 
180
        m_mixerName = "OSS Audio Mixer";
 
181
 
 
182
      m_isOpen = true;
 
183
      return 0;
 
184
}
 
185
 
 
186
int Mixer_OSS::close()
 
187
{
 
188
    _pollingTimer->stop();
 
189
    m_isOpen = false;
 
190
    int l_i_ret = ::close(m_fd);
 
191
    m_mixDevices.clear();
 
192
    return l_i_ret;
 
193
}
 
194
 
 
195
 
 
196
QString Mixer_OSS::deviceName(int devnum)
 
197
{
 
198
  switch (devnum) {
 
199
  case 0:
 
200
    return QString("/dev/mixer");
 
201
    break;
 
202
 
 
203
  default:
 
204
    QString devname("/dev/mixer");
 
205
    devname += ('0'+devnum);
 
206
    return devname;
 
207
  }
 
208
}
 
209
 
 
210
QString Mixer_OSS::deviceNameDevfs(int devnum)
 
211
{
 
212
  switch (devnum) {
 
213
  case 0:
 
214
    return QString("/dev/sound/mixer");
 
215
    break;
 
216
 
 
217
  default:
 
218
    QString devname("/dev/sound/mixer");
 
219
    devname += ('0'+devnum);
 
220
    return devname;
 
221
  }
 
222
}
 
223
 
 
224
QString Mixer_OSS::errorText(int mixer_error)
 
225
{
 
226
  QString l_s_errmsg;
 
227
  switch (mixer_error)
 
228
    {
 
229
    case Mixer::ERR_PERM:
 
230
      l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \
 
231
                        "Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access.");
 
232
      break;
 
233
    case Mixer::ERR_OPEN:
 
234
      l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
 
235
                        "Please check that the soundcard is installed and the\n" \
 
236
                        "soundcard driver is loaded.\n" \
 
237
                        "On Linux you might need to use 'insmod' to load the driver.\n" \
 
238
                        "Use 'soundon' when using commercial OSS.");
 
239
      break;
 
240
    default:
 
241
      l_s_errmsg = Mixer_Backend::errorText(mixer_error);
 
242
    }
 
243
  return l_s_errmsg;
 
244
}
 
245
 
 
246
/*
 
247
bool Mixer_OSS::isRecsrcHW( const QString& id )
 
248
{
 
249
   int devnum = id2num(id);
 
250
   bool isRecsrc = false;
 
251
   int recsrcMask;
 
252
   if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
 
253
      errormsg(Mixer::ERR_READ);
 
254
   else {
 
255
      // test if device bit is set in record bit mask
 
256
      isRecsrc =  ( (recsrcMask & ( 1<<devnum)) != 0 );
 
257
   }
 
258
   return isRecsrc;
 
259
}
 
260
*/
 
261
 
 
262
 
 
263
void Mixer_OSS::setRecsrcHW( const QString& id, bool on )
 
264
{
 
265
    int i_recsrc, oldrecsrc;
 
266
    int devnum = id2num(id);
 
267
    if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
 
268
        errormsg(Mixer::ERR_READ);
 
269
 
 
270
    oldrecsrc = i_recsrc = on ?
 
271
             (i_recsrc | (1 << devnum )) :
 
272
             (i_recsrc & ~(1 << devnum ));
 
273
 
 
274
    // Change status of record source(s)
 
275
    if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1)
 
276
    {
 
277
        errormsg (Mixer::ERR_WRITE);
 
278
    }
 
279
    /* The following if {} patch was submitted by Tim McCormick <tim@pcbsd.org>. */
 
280
    /*   Comment (cesken): This patch fixes an issue with mutual exclusive recording sources.
 
281
         Actually the kernel soundcard driver *could* "do the right thing" by examining the change
 
282
         (old-recsrc XOR new-recsrc), and knowing which sources are mutual exclusive.
 
283
         The OSS v3 API docs indicate that the behaviour is undefined for this case, and it is not
 
284
         clearly documented how and whether SOUND_MIXER_CAP_EXCL_INPUT is evaluated in the OSS driver.
 
285
         Evaluating that in the application (KMix) could help, but the patch will work independent
 
286
         on whether SOUND_MIXER_CAP_EXCL_INPUT is set or not.
 
287
 
 
288
         In any case this patch is a superb workaround for a shortcoming of the OSS v3 API.
 
289
     */
 
290
    // If the record source is supposed to be on, but wasn't set, explicitly
 
291
    // set the record source. Not all cards support multiple record sources.
 
292
    // As a result, we also need to do the read & write again.
 
293
    if (((i_recsrc & ( 1<<devnum)) == 0) && on)
 
294
    {
 
295
       // Setting the new device failed => Try to enable it *exclusively*
 
296
       oldrecsrc = i_recsrc = 1 << devnum;
 
297
       if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1)
 
298
         errormsg (Mixer::ERR_WRITE);
 
299
       if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
 
300
         errormsg(Mixer::ERR_READ);
 
301
    }
 
302
 
 
303
    // Re-read status of record source(s). Just in case the hardware/driver has
 
304
    // some limitaton (like exclusive switches)
 
305
    int recsrcMask;
 
306
    if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
 
307
        errormsg(Mixer::ERR_READ);
 
308
    else
 
309
    {
 
310
        for(int i=0; i< m_mixDevices.count() ; i++ )
 
311
        {
 
312
            MixDevice *md = m_mixDevices[i];
 
313
            bool isRecsrc =  ( (recsrcMask & ( 1<<devnum)) != 0 );
 
314
            md->setRecSource(isRecsrc);
 
315
        } // for all controls
 
316
    } // reading newrecsrcmask is OK
 
317
    
 
318
}
 
319
 
 
320
 
 
321
int Mixer_OSS::readVolumeFromHW( const QString& id, MixDevice* md )
 
322
{
 
323
    int ret = 0;
 
324
 
 
325
    // --- VOLUME ---
 
326
    Volume& vol = md->playbackVolume();
 
327
    int devnum = id2num(id);
 
328
 
 
329
    if ( vol.hasVolume() ) {
 
330
        int volume;
 
331
         if (ioctl(m_fd, MIXER_READ( devnum ), &volume) == -1)
 
332
        {
 
333
            /* Oops, can't read mixer */
 
334
            ret = Mixer::ERR_READ;
 
335
        }
 
336
        else
 
337
        {
 
338
 
 
339
            int volLeft  = (volume & 0x7f);
 
340
            int volRight = ((volume>>8) & 0x7f);
 
341
            bool isMuted = volLeft==0 && ( vol.count() < 2 || volRight==0 ); // muted is "left and right muted" or "left muted when mono"
 
342
            md->setMuted( isMuted );
 
343
            if ( ! isMuted ) {
 
344
               // Muted is represented in OSS by value 0. We don't want to write the value 0 as a volume,
 
345
               // but instead we onlm mark it muted (see setMuted() above).
 
346
               vol.setVolume( Volume::LEFT, volLeft);
 
347
               if( vol.count() > 1 )
 
348
                  vol.setVolume( Volume::RIGHT, volRight);
 
349
            }
 
350
        }
 
351
    }
 
352
 
 
353
 
 
354
    // --- RECORD SWITCH ---
 
355
    //Volume& captureVol = md->captureVolume();
 
356
    int recsrcMask;
 
357
    if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
 
358
        ret = Mixer::ERR_READ;
 
359
    else {
 
360
        // test if device bit is set in record bit mask
 
361
        bool isRecsrc =  ( (recsrcMask & ( 1<<devnum)) != 0 );
 
362
        md->setRecSource(isRecsrc);
 
363
    }
 
364
 
 
365
    return ret;
 
366
}
 
367
 
 
368
 
 
369
 
 
370
int Mixer_OSS::writeVolumeToHW( const QString& id, MixDevice *md)
 
371
{
 
372
    int volume;
 
373
    int devnum = id2num(id);
 
374
 
 
375
    Volume& vol = md->playbackVolume();
 
376
    if( md->isMuted() )
 
377
       volume = 0;
 
378
    else
 
379
    {
 
380
        if ( vol.count() > 1 )
 
381
            volume = (vol[ Volume::LEFT ]) + ((vol[ Volume::RIGHT ])<<8);
 
382
        else
 
383
            volume = vol[ Volume::LEFT ];
 
384
    }
 
385
    
 
386
    if (ioctl(m_fd, MIXER_WRITE( devnum ), &volume) == -1)
 
387
        return Mixer::ERR_WRITE;
 
388
 
 
389
    return 0;
 
390
}
 
391
 
 
392
QString OSS_getDriverName() {
 
393
   return "OSS";
 
394
}
 
395
 
 
396
QString Mixer_OSS::getDriverName() {
 
397
   return "OSS";
 
398
}
 
399