1 /*
2 * KMix -- KDE's full featured mini mixer
3 *
4 *
5 * Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
6 * 2000 Brian Hanson <bhanson@hotmail.com>
7 *
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.
12 *
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.
17 *
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.
21 */
22
23 #include "mixer_sun.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <sys/file.h>
29 #include <sys/audioio.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33
34 #include "core/mixer.h"
35 #include <sys/soundcard.h>
36 #include <QTimer>
37 #include <qplatformdefs.h>
38 //======================================================================
39 // CONSTANT/ENUM DEFINITIONS
40 //======================================================================
41
42 //
43 // Mixer Device Numbers
44 //
45 // Note: We can't just use the Sun port #defines because :
46 // 1) Some logical devices don't correspond to ports (master&recmon)
47 // 2) The play and record port definitions reuse the same values
48 //
49 enum MixerDevs
50 {
51 MIXERDEV_MASTER_VOLUME,
52 MIXERDEV_INTERNAL_SPEAKER,
53 MIXERDEV_HEADPHONE,
54 MIXERDEV_LINE_OUT,
55 MIXERDEV_RECORD_MONITOR,
56 MIXERDEV_MICROPHONE,
57 MIXERDEV_LINE_IN,
58 MIXERDEV_CD,
59 // Insert new devices before this marker
60 MIXERDEV_END_MARKER
61 };
62 const int numDevs = MIXERDEV_END_MARKER;
63
64 //
65 // Device name strings
66 //
67 const char* MixerDevNames[] =
68 {
69 I18N_NOOP("Master Volume"),
70 I18N_NOOP("Internal Speaker"),
71 I18N_NOOP("Headphone"),
72 I18N_NOOP("Line Out"),
73 I18N_NOOP("Record Monitor"),
74 I18N_NOOP("Microphone"),
75 I18N_NOOP("Line In"),
76 I18N_NOOP("CD")
77 };
78
79 //
80 // Channel types (this specifies which icon to display)
81 //
82 const MixDevice::ChannelType MixerChannelTypes[] =
83 {
84 MixDevice::VOLUME, // MASTER_VOLUME
85 MixDevice::AUDIO, // INTERNAL_SPEAKER
86 MixDevice::EXTERNAL, // HEADPHONE (we really need an icon for this)
87 MixDevice::EXTERNAL, // LINE_OUT
88 MixDevice::RECMONITOR, // RECORD_MONITOR
89 MixDevice::MICROPHONE, // MICROPHONE
90 MixDevice::EXTERNAL, // LINE_IN
91 MixDevice::CD // CD
92 };
93
94 //
95 // Mapping from device numbers to Sun port mask values
96 //
97 const uint_t MixerSunPortMasks[] =
98 {
99 0, // MASTER_VOLUME - no associated port
100 AUDIO_SPEAKER,
101 AUDIO_HEADPHONE,
102 AUDIO_LINE_OUT,
103 0, // RECORD_MONITOR - no associated port
104 AUDIO_MICROPHONE,
105 AUDIO_LINE_IN,
106 AUDIO_CD
107 };
108
109
110 //======================================================================
111 // FUNCTION/METHOD DEFINITIONS
112 //======================================================================
113
114
115 //======================================================================
116 // FUNCTION : SUN_getMixer
117 // DESCRIPTION : Creates and returns a new mixer object.
118 //======================================================================
SUN_getMixer(Mixer * mixer,int devnum)119 Mixer_Backend* SUN_getMixer( Mixer *mixer, int devnum )
120 {
121 Mixer_Backend *l_mixer;
122 l_mixer = new Mixer_SUN( mixer, devnum );
123 return l_mixer;
124 }
125
126
127 //======================================================================
128 // FUNCTION : Mixer::Mixer
129 // DESCRIPTION : Class constructor.
130 //======================================================================
Mixer_SUN(Mixer * mixer,int devnum)131 Mixer_SUN::Mixer_SUN(Mixer *mixer, int devnum) : Mixer_Backend(mixer, devnum)
132 {
133 if ( devnum == -1 )
134 m_devnum = 0;
135 }
136
137 //======================================================================
138 // FUNCTION : Mixer::Mixer
139 // DESCRIPTION : Class destructor.
140 //======================================================================
~Mixer_SUN()141 Mixer_SUN::~Mixer_SUN()
142 {
143 close();
144 }
145
146 //======================================================================
147 // FUNCTION : Mixer::open
148 // DESCRIPTION : Initialize the mixer and open the hardware driver.
149 //======================================================================
open()150 int Mixer_SUN::open()
151 {
152 //
153 // We don't support multiple devices
154 //
155 if ( m_devnum !=0 )
156 return Mixer::ERR_OPEN;
157
158 //
159 // Open the mixer hardware driver
160 //
161 QString audiodev(qgetenv("AUDIODEV"));
162 if(audiodev.isNull())
163 audiodev = "/dev/audio";
164 audiodev += "ctl";
165 _udi = audiodev; // use device name as UDI. Doesn't matter as we only use it for hotplugging/unplugging.
166 if ( ( fd = QT_OPEN( audiodev.toLatin1().data(), O_RDWR ) ) < 0 )
167 {
168 if ( errno == EACCES )
169 return Mixer::ERR_PERM;
170 else
171 return Mixer::ERR_OPEN;
172 }
173 else
174 {
175 //
176 // Mixer is open. Now define all of the mix devices.
177 //
178 int devmask, recmask, i_recsrc, stereodevs;
179 // Mixer is open. Now define properties
180 if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
181 return Mixer::ERR_READ;
182 if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
183 return Mixer::ERR_READ;
184 if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
185 return Mixer::ERR_READ;
186 if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1)
187 return Mixer::ERR_READ;
188
189 for ( int idx = 0; idx < numDevs; idx++ )
190 {
191 Volume::ChannelMask chnmask = Volume::MLEFT;
192 if ( stereodevs & ( 1 << idx ) ) chnmask = (Volume::ChannelMask)(chnmask|Volume::MRIGHT);
193
194 Volume playbackVol( 100, 1, true, false );
195 QString id;
196 id.setNum(idx);
197 MixDevice* md = new MixDevice( _mixer, id,
198 QString(MixerDevNames[idx]), MixerChannelTypes[idx]);
199 md->addPlaybackVolume(playbackVol);
200 // Tutorial: Howto add a simple capture switch
201 if ( recmask & ( 1 << idx ) ) {
202 // can be captured => add capture volume, with no capture volume
203 chnmask = Volume::MNONE;
204 Volume captureVol( 100, 1, true, true );
205 md->addCaptureVolume(captureVol);
206 }
207 m_mixDevices.append( md->addToPool() );
208 }
209
210 registerCard("SUN Audio Mixer");
211 m_isOpen = true;
212
213 return 0;
214 }
215 }
216
217 //======================================================================
218 // FUNCTION : Mixer::close
219 // DESCRIPTION : Close the hardware driver.
220 //======================================================================
close()221 int Mixer_SUN::close()
222 {
223 _pollingTimer->stop();
224 m_isOpen = false;
225 int l_i_ret = ::close( fd );
226 closeCommon();
227 return l_i_ret;
228 }
229
230 //======================================================================
231 // FUNCTION : Mixer::errorText
232 // DESCRIPTION : Convert an error code enum to a text string.
233 //======================================================================
errorText(int mixer_error)234 QString Mixer_SUN::errorText( int mixer_error )
235 {
236 QString errmsg;
237 switch (mixer_error)
238 {
239 case Mixer::ERR_PERM:
240 errmsg = i18n(
241 "kmix: You do not have permission to access the mixer device.\n"
242 "Ask your system administrator to fix /dev/audioctl to allow access."
243 );
244 break;
245 default:
246 errmsg = Mixer_Backend::errorText( mixer_error );
247 }
248 return errmsg;
249 }
250
251
252 //======================================================================
253 // FUNCTION : Mixer::readVolumeFromHW
254 // DESCRIPTION : Read the audio information from the driver.
255 //======================================================================
readVolumeFromHW(const QString & id,shared_ptr<MixDevice> md)256 int Mixer_SUN::readVolumeFromHW( const QString& id, shared_ptr<MixDevice> md )
257 {
258 audio_info_t audioinfo;
259 int devnum = id2num(id);
260 uint_t devMask = MixerSunPortMasks[devnum];
261
262 Volume& volume = md->playbackVolume();
263 //
264 // Read the current audio information from the driver
265 //
266 if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
267 {
268 return( Mixer::ERR_READ );
269 }
270 else
271 {
272 //
273 // Extract the appropriate fields based on the requested device
274 //
275 switch ( devnum )
276 {
277 case MIXERDEV_MASTER_VOLUME :
278 //volume.setSwitchActivated( audioinfo.output_muted );
279 GainBalanceToVolume( audioinfo.play.gain,
280 audioinfo.play.balance,
281 volume );
282 break;
283
284 case MIXERDEV_RECORD_MONITOR :
285 md->setMuted(false);
286 volume.setAllVolumes( audioinfo.monitor_gain );
287 break;
288
289 case MIXERDEV_INTERNAL_SPEAKER :
290 case MIXERDEV_HEADPHONE :
291 case MIXERDEV_LINE_OUT :
292 md->setMuted( (audioinfo.play.port & devMask) ? false : true );
293 GainBalanceToVolume( audioinfo.play.gain,
294 audioinfo.play.balance,
295 volume );
296 break;
297
298 case MIXERDEV_MICROPHONE :
299 case MIXERDEV_LINE_IN :
300 case MIXERDEV_CD :
301 md->setMuted( (audioinfo.record.port & devMask) ? false : true );
302 GainBalanceToVolume( audioinfo.record.gain,
303 audioinfo.record.balance,
304 volume );
305 break;
306
307 default :
308 return Mixer::ERR_READ;
309 }
310 return 0;
311 }
312 }
313
314 //======================================================================
315 // FUNCTION : Mixer::writeVolumeToHW
316 // DESCRIPTION : Write the specified audio settings to the hardware.
317 //======================================================================
writeVolumeToHW(const QString & id,shared_ptr<MixDevice> md)318 int Mixer_SUN::writeVolumeToHW( const QString& id, shared_ptr<MixDevice> md )
319 {
320 uint_t gain;
321 uchar_t balance;
322 uchar_t mute;
323
324 Volume& volume = md->playbackVolume();
325 int devnum = id2num(id);
326 //
327 // Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses
328 //
329 VolumeToGainBalance( volume, gain, balance );
330 mute = md->isMuted() ? 1 : 0;
331
332 //
333 // Read the current audio settings from the hardware
334 //
335 audio_info_t audioinfo;
336 if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
337 {
338 return( Mixer::ERR_READ );
339 }
340
341 //
342 // Now, based on the devnum that we are writing to, update the appropriate
343 // volume field and twiddle the appropriate bitmask to enable/mute the
344 // device as necessary.
345 //
346 switch ( devnum )
347 {
348 case MIXERDEV_MASTER_VOLUME :
349 audioinfo.play.gain = gain;
350 audioinfo.play.balance = balance;
351 audioinfo.output_muted = mute;
352 break;
353
354 case MIXERDEV_RECORD_MONITOR :
355 audioinfo.monitor_gain = gain;
356 // no mute or balance for record monitor
357 break;
358
359 case MIXERDEV_INTERNAL_SPEAKER :
360 case MIXERDEV_HEADPHONE :
361 case MIXERDEV_LINE_OUT :
362 audioinfo.play.gain = gain;
363 audioinfo.play.balance = balance;
364 if ( mute )
365 audioinfo.play.port &= ~MixerSunPortMasks[devnum];
366 else
367 audioinfo.play.port |= MixerSunPortMasks[devnum];
368 break;
369
370 case MIXERDEV_MICROPHONE :
371 case MIXERDEV_LINE_IN :
372 case MIXERDEV_CD :
373 audioinfo.record.gain = gain;
374 audioinfo.record.balance = balance;
375 if ( mute )
376 audioinfo.record.port &= ~MixerSunPortMasks[devnum];
377 else
378 audioinfo.record.port |= MixerSunPortMasks[devnum];
379 break;
380
381 default :
382 return Mixer::ERR_READ;
383 }
384
385 //
386 // Now that we've updated the audioinfo struct, write it back to the hardware
387 //
388 if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 )
389 {
390 return( Mixer::ERR_WRITE );
391 }
392 else
393 {
394 return 0;
395 }
396 }
397
398
399 //======================================================================
400 // FUNCTION : Mixer::isRecsrcHW
401 // DESCRIPTION : Returns true if the specified device is a record source.
402 //======================================================================
403
404 // isRecsrcHW() is not supported any longer. You must set the state in the MixDevice in readVolumeFromHW() or writeVolumeFromHW() appropriately
405
406 //bool Mixer_SUN::isRecsrcHW( isRecsrcHW(const QString& id )
407 //{
408 // int devnum = id2num(id);
409 // switch ( devnum )
410 // {
411 // case MIXERDEV_MICROPHONE :
412 // case MIXERDEV_LINE_IN :
413 // case MIXERDEV_CD :
414 // return true;
415 //
416 // default :
417 // return false;
418 // }
419 //}
420
421 //======================================================================
422 // FUNCTION : Mixer::VolumeToGainBalance
423 // DESCRIPTION : Converts a Volume(left vol + right vol) into the
424 // Gain/Balance values used by Sun.
425 //======================================================================
VolumeToGainBalance(Volume & volume,uint_t & gain,uchar_t & balance)426 void Mixer_SUN::VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance )
427 {
428 if ( ( volume.count() == 1 ) ||
429 ( volume.getVolume(Volume::LEFT) == volume.getVolume(Volume::RIGHT) ) )
430 {
431 gain = volume.getVolume(Volume::LEFT);
432 balance = AUDIO_MID_BALANCE;
433 }
434 else
435 {
436 if ( volume.getVolume(Volume::LEFT) > volume.getVolume(Volume::RIGHT) )
437 {
438 gain = volume.getVolume(Volume::LEFT);
439 balance = AUDIO_LEFT_BALANCE +
440 ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) *
441 volume.getVolume(Volume::RIGHT) / volume.getVolume(Volume::LEFT);
442 }
443 else
444 {
445 gain = volume.getVolume(Volume::RIGHT);
446 balance = AUDIO_RIGHT_BALANCE -
447 ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) *
448 volume.getVolume(Volume::LEFT) / volume.getVolume(Volume::RIGHT);
449 }
450 }
451 }
452
453 //======================================================================
454 // FUNCTION : Mixer::GainBalanceToVolume
455 // DESCRIPTION : Converts Gain/Balance returned by Sun driver to the
456 // Volume(left vol + right vol) format used by kmix.
457 //======================================================================
GainBalanceToVolume(uint_t & gain,uchar_t & balance,Volume & volume)458 void Mixer_SUN::GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume )
459 {
460 if ( volume.count() == 1 )
461 {
462 volume.setVolume( Volume::LEFT, gain );
463 }
464 else
465 {
466 if ( balance <= AUDIO_MID_BALANCE )
467 {
468 volume.setVolume( Volume::LEFT, gain );
469 volume.setVolume( Volume::RIGHT, gain *
470 ( balance - AUDIO_LEFT_BALANCE ) /
471 ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) );
472 }
473 else
474 {
475 volume.setVolume( Volume::RIGHT, gain );
476 volume.setVolume( Volume::LEFT, gain *
477 ( AUDIO_RIGHT_BALANCE - balance ) /
478 ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) );
479 }
480 }
481 }
482
id2num(const QString & id)483 int Mixer_SUN::id2num(const QString& id)
484 {
485 return id.toInt();
486 }
487
SUN_getDriverName()488 QString SUN_getDriverName() {
489 return "SUNAudio";
490 }
491
getDriverName()492 QString Mixer_SUN::getDriverName()
493 {
494 return "SUNAudio";
495 }
496
497