2
* KMix -- KDE's full featured mini mixer
4
* Copyright (C) 1996-2000 Christian Esken
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.
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.
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.
22
#include "mixer_oss.h"
23
#include "core/mixer.h"
24
#include "core/kmixdevicemanager.h"
29
#include <sys/ioctl.h>
30
#include <sys/types.h>
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>
37
#include <soundcard.h>
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.
52
The #define below is only there for internal reasons.
53
In other words: Don't play around with this value
55
#define MAX_MIXDEVS 32
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") };
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 };
83
Mixer_Backend* OSS_getMixer( Mixer* mixer, int device )
85
Mixer_Backend *l_mixer;
86
l_mixer = new Mixer_OSS( mixer, device );
90
Mixer_OSS::Mixer_OSS( Mixer* mixer, int device) : Mixer_Backend(mixer, device)
92
if( device == -1 ) m_devnum = 0;
95
Mixer_OSS::~Mixer_OSS()
100
int Mixer_OSS::open()
102
QString finalDeviceName;
103
finalDeviceName = deviceName( m_devnum );
104
if ((m_fd= ::open( finalDeviceName.toAscii().data(), O_RDWR)) < 0)
106
if ( errno == EACCES )
107
return Mixer::ERR_PERM;
109
finalDeviceName = deviceNameDevfs( m_devnum );
110
if ((m_fd= ::open( finalDeviceName.toAscii().data(), O_RDWR)) < 0)
112
if ( errno == EACCES )
113
return Mixer::ERR_PERM;
115
return Mixer::ERR_OPEN;
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;
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;
139
while( devmask && idx < MAX_MIXDEVS )
141
if( devmask & ( 1 << idx ) ) // device active?
143
Volume::ChannelMask chnmask = Volume::MLEFT;
144
if ( stereodevs & ( 1 << idx ) ) chnmask = (Volume::ChannelMask)(chnmask|Volume::MRIGHT);
146
Volume playbackVol( chnmask, 100, 1, true, false );
154
i18n(MixerDevNames[idx]),
155
MixerChannelTypes[idx]);
156
md->addPlaybackVolume(playbackVol);
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);
166
m_mixDevices.append( md );
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)
175
m_mixerName = l_mix_info.name;
180
m_mixerName = "OSS Audio Mixer";
186
int Mixer_OSS::close()
188
_pollingTimer->stop();
190
int l_i_ret = ::close(m_fd);
191
m_mixDevices.clear();
196
QString Mixer_OSS::deviceName(int devnum)
200
return QString("/dev/mixer");
204
QString devname("/dev/mixer");
205
devname += ('0'+devnum);
210
QString Mixer_OSS::deviceNameDevfs(int devnum)
214
return QString("/dev/sound/mixer");
218
QString devname("/dev/sound/mixer");
219
devname += ('0'+devnum);
224
QString Mixer_OSS::errorText(int mixer_error)
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.");
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.");
241
l_s_errmsg = Mixer_Backend::errorText(mixer_error);
247
bool Mixer_OSS::isRecsrcHW( const QString& id )
249
int devnum = id2num(id);
250
bool isRecsrc = false;
252
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
253
errormsg(Mixer::ERR_READ);
255
// test if device bit is set in record bit mask
256
isRecsrc = ( (recsrcMask & ( 1<<devnum)) != 0 );
263
void Mixer_OSS::setRecsrcHW( const QString& id, bool on )
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);
270
oldrecsrc = i_recsrc = on ?
271
(i_recsrc | (1 << devnum )) :
272
(i_recsrc & ~(1 << devnum ));
274
// Change status of record source(s)
275
if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1)
277
errormsg (Mixer::ERR_WRITE);
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.
288
In any case this patch is a superb workaround for a shortcoming of the OSS v3 API.
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)
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);
303
// Re-read status of record source(s). Just in case the hardware/driver has
304
// some limitaton (like exclusive switches)
306
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
307
errormsg(Mixer::ERR_READ);
310
for(int i=0; i< m_mixDevices.count() ; i++ )
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
321
int Mixer_OSS::readVolumeFromHW( const QString& id, MixDevice* md )
326
Volume& vol = md->playbackVolume();
327
int devnum = id2num(id);
329
if ( vol.hasVolume() ) {
331
if (ioctl(m_fd, MIXER_READ( devnum ), &volume) == -1)
333
/* Oops, can't read mixer */
334
ret = Mixer::ERR_READ;
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 );
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);
354
// --- RECORD SWITCH ---
355
//Volume& captureVol = md->captureVolume();
357
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
358
ret = Mixer::ERR_READ;
360
// test if device bit is set in record bit mask
361
bool isRecsrc = ( (recsrcMask & ( 1<<devnum)) != 0 );
362
md->setRecSource(isRecsrc);
370
int Mixer_OSS::writeVolumeToHW( const QString& id, MixDevice *md)
373
int devnum = id2num(id);
375
Volume& vol = md->playbackVolume();
380
if ( vol.count() > 1 )
381
volume = (vol[ Volume::LEFT ]) + ((vol[ Volume::RIGHT ])<<8);
383
volume = vol[ Volume::LEFT ];
386
if (ioctl(m_fd, MIXER_WRITE( devnum ), &volume) == -1)
387
return Mixer::ERR_WRITE;
392
QString OSS_getDriverName() {
396
QString Mixer_OSS::getDriverName() {