You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
14 KiB
474 lines
14 KiB
/*
|
|
* KMix -- KDE's full featured mini mixer
|
|
*
|
|
*
|
|
* Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
|
|
* 2000 Brian Hanson <bhanson@hotmail.com>
|
|
*
|
|
* 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
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/file.h>
|
|
#include <sys/audioio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "mixer_sun.h"
|
|
|
|
|
|
//======================================================================
|
|
// CONSTANT/ENUM DEFINITIONS
|
|
//======================================================================
|
|
|
|
//
|
|
// Mixer Device Numbers
|
|
//
|
|
// Note: We can't just use the Sun port #defines because :
|
|
// 1) Some logical devices don't correspond to ports (master&recmon)
|
|
// 2) The play and record port definitions reuse the same values
|
|
//
|
|
enum MixerDevs
|
|
{
|
|
MIXERDEV_MASTER_VOLUME,
|
|
MIXERDEV_INTERNAL_SPEAKER,
|
|
MIXERDEV_HEADPHONE,
|
|
MIXERDEV_LINE_OUT,
|
|
MIXERDEV_RECORD_MONITOR,
|
|
MIXERDEV_MICROPHONE,
|
|
MIXERDEV_LINE_IN,
|
|
MIXERDEV_CD,
|
|
// Insert new devices before this marker
|
|
MIXERDEV_END_MARKER
|
|
};
|
|
const int numDevs = MIXERDEV_END_MARKER;
|
|
|
|
//
|
|
// Device name strings
|
|
//
|
|
const char* MixerDevNames[] =
|
|
{
|
|
I18N_NOOP("Master Volume"),
|
|
I18N_NOOP("Internal Speaker"),
|
|
I18N_NOOP("Headphone"),
|
|
I18N_NOOP("Line Out"),
|
|
I18N_NOOP("Record Monitor"),
|
|
I18N_NOOP("Microphone"),
|
|
I18N_NOOP("Line In"),
|
|
I18N_NOOP("CD")
|
|
};
|
|
|
|
//
|
|
// Channel types (this specifies which icon to display)
|
|
//
|
|
const MixDevice::ChannelType MixerChannelTypes[] =
|
|
{
|
|
MixDevice::VOLUME, // MASTER_VOLUME
|
|
MixDevice::AUDIO, // INTERNAL_SPEAKER
|
|
MixDevice::EXTERNAL, // HEADPHONE (we really need an icon for this)
|
|
MixDevice::EXTERNAL, // LINE_OUT
|
|
MixDevice::RECMONITOR, // RECORD_MONITOR
|
|
MixDevice::MICROPHONE, // MICROPHONE
|
|
MixDevice::EXTERNAL, // LINE_IN
|
|
MixDevice::CD // CD
|
|
};
|
|
|
|
//
|
|
// Mapping from device numbers to Sun port mask values
|
|
//
|
|
const uint_t MixerSunPortMasks[] =
|
|
{
|
|
0, // MASTER_VOLUME - no associated port
|
|
AUDIO_SPEAKER,
|
|
AUDIO_HEADPHONE,
|
|
AUDIO_LINE_OUT,
|
|
0, // RECORD_MONITOR - no associated port
|
|
AUDIO_MICROPHONE,
|
|
AUDIO_LINE_IN,
|
|
AUDIO_CD
|
|
};
|
|
|
|
|
|
//======================================================================
|
|
// FUNCTION/TQT_METHOD DEFINITIONS
|
|
//======================================================================
|
|
|
|
|
|
//======================================================================
|
|
// FUNCTION : SUN_getMixer
|
|
// DESCRIPTION : Creates and returns a new mixer object.
|
|
//======================================================================
|
|
Mixer_Backend* SUN_getMixer( int devnum )
|
|
{
|
|
Mixer_Backend *l_mixer;
|
|
l_mixer = new Mixer_SUN( devnum );
|
|
return l_mixer;
|
|
}
|
|
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::Mixer
|
|
// DESCRIPTION : Class constructor.
|
|
//======================================================================
|
|
Mixer_SUN::Mixer_SUN(int devnum) : Mixer_Backend(devnum)
|
|
{
|
|
if ( devnum == -1 )
|
|
m_devnum = 0;
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::Mixer
|
|
// DESCRIPTION : Class destructor.
|
|
//======================================================================
|
|
Mixer_SUN::~Mixer_SUN()
|
|
{
|
|
close();
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::open
|
|
// DESCRIPTION : Initialize the mixer and open the hardware driver.
|
|
//======================================================================
|
|
int Mixer_SUN::open()
|
|
{
|
|
//
|
|
// We don't support multiple devices
|
|
//
|
|
if ( m_devnum !=0 )
|
|
return Mixer::ERR_OPEN;
|
|
|
|
//
|
|
// Open the mixer hardware driver
|
|
//
|
|
TQCString audiodev(getenv("AUDIODEV"));
|
|
if(audiodev.isNull())
|
|
audiodev = "/dev/audio";
|
|
audiodev += "ctl";
|
|
if ( ( fd = ::open( audiodev.data(), O_RDWR ) ) < 0 )
|
|
{
|
|
if ( errno == EACCES )
|
|
return Mixer::ERR_PERM;
|
|
else
|
|
return Mixer::ERR_OPEN;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Mixer is open. Now define all of the mix devices.
|
|
//
|
|
|
|
if( m_mixDevices.isEmpty() )
|
|
{
|
|
for ( int idx = 0; idx < numDevs; idx++ )
|
|
{
|
|
Volume vol( 2, AUDIO_MAX_GAIN );
|
|
readVolumeFromHW( idx, vol );
|
|
MixDevice* md = new MixDevice( idx, vol, false, true,
|
|
TQString(MixerDevNames[idx]), MixerChannelTypes[idx]);
|
|
md->setRecSource( isRecsrcHW( idx ) );
|
|
m_mixDevices.append( md );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( unsigned int idx = 0; idx < m_mixDevices.count(); idx++ )
|
|
{
|
|
MixDevice* md = m_mixDevices.at( idx );
|
|
if( !md )
|
|
return Mixer::ERR_INCOMPATIBLESET;
|
|
writeVolumeToHW( idx, md->getVolume() );
|
|
}
|
|
}
|
|
|
|
m_mixerName = "SUN Audio Mixer";
|
|
m_isOpen = true;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::close
|
|
// DESCRIPTION : Close the hardware driver.
|
|
//======================================================================
|
|
int Mixer_SUN::close()
|
|
{
|
|
m_isOpen = false;
|
|
int l_i_ret = ::close( fd );
|
|
m_mixDevices.clear();
|
|
return l_i_ret;
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::errorText
|
|
// DESCRIPTION : Convert an error code enum to a text string.
|
|
//======================================================================
|
|
TQString Mixer_SUN::errorText( int mixer_error )
|
|
{
|
|
TQString errmsg;
|
|
switch (mixer_error)
|
|
{
|
|
case Mixer::ERR_PERM:
|
|
errmsg = i18n(
|
|
"kmix: You do not have permission to access the mixer device.\n"
|
|
"Ask your system administrator to fix /dev/audioctl to allow access."
|
|
);
|
|
break;
|
|
default:
|
|
errmsg = Mixer_Backend::errorText( mixer_error );
|
|
}
|
|
return errmsg;
|
|
}
|
|
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::readVolumeFrmoHW
|
|
// DESCRIPTION : Read the audio information from the driver.
|
|
//======================================================================
|
|
int Mixer_SUN::readVolumeFromHW( int devnum, Volume& volume )
|
|
{
|
|
audio_info_t audioinfo;
|
|
uint_t devMask = MixerSunPortMasks[devnum];
|
|
|
|
//
|
|
// Read the current audio information from the driver
|
|
//
|
|
if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
|
|
{
|
|
return( Mixer::ERR_READ );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Extract the appropriate fields based on the requested device
|
|
//
|
|
switch ( devnum )
|
|
{
|
|
case MIXERDEV_MASTER_VOLUME :
|
|
volume.setMuted( audioinfo.output_muted );
|
|
GainBalanceToVolume( audioinfo.play.gain,
|
|
audioinfo.play.balance,
|
|
volume );
|
|
break;
|
|
|
|
case MIXERDEV_RECORD_MONITOR :
|
|
volume.setMuted(FALSE);
|
|
volume.setAllVolumes( audioinfo.monitor_gain );
|
|
break;
|
|
|
|
case MIXERDEV_INTERNAL_SPEAKER :
|
|
case MIXERDEV_HEADPHONE :
|
|
case MIXERDEV_LINE_OUT :
|
|
volume.setMuted( (audioinfo.play.port & devMask) ? FALSE : TRUE );
|
|
GainBalanceToVolume( audioinfo.play.gain,
|
|
audioinfo.play.balance,
|
|
volume );
|
|
break;
|
|
|
|
case MIXERDEV_MICROPHONE :
|
|
case MIXERDEV_LINE_IN :
|
|
case MIXERDEV_CD :
|
|
volume.setMuted( (audioinfo.record.port & devMask) ? FALSE : TRUE );
|
|
GainBalanceToVolume( audioinfo.record.gain,
|
|
audioinfo.record.balance,
|
|
volume );
|
|
break;
|
|
|
|
default :
|
|
return Mixer::ERR_NODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::writeVolumeToHW
|
|
// DESCRIPTION : Write the specified audio settings to the hardware.
|
|
//======================================================================
|
|
int Mixer_SUN::writeVolumeToHW( int devnum, Volume &volume )
|
|
{
|
|
uint_t gain;
|
|
uchar_t balance;
|
|
uchar_t mute;
|
|
|
|
//
|
|
// Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses
|
|
//
|
|
VolumeToGainBalance( volume, gain, balance );
|
|
mute = volume.isMuted() ? 1 : 0;
|
|
|
|
//
|
|
// Read the current audio settings from the hardware
|
|
//
|
|
audio_info_t audioinfo;
|
|
if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
|
|
{
|
|
return( Mixer::ERR_READ );
|
|
}
|
|
|
|
//
|
|
// Now, based on the devnum that we are writing to, update the appropriate
|
|
// volume field and twiddle the appropriate bitmask to enable/mute the
|
|
// device as necessary.
|
|
//
|
|
switch ( devnum )
|
|
{
|
|
case MIXERDEV_MASTER_VOLUME :
|
|
audioinfo.play.gain = gain;
|
|
audioinfo.play.balance = balance;
|
|
audioinfo.output_muted = mute;
|
|
break;
|
|
|
|
case MIXERDEV_RECORD_MONITOR :
|
|
audioinfo.monitor_gain = gain;
|
|
// no mute or balance for record monitor
|
|
break;
|
|
|
|
case MIXERDEV_INTERNAL_SPEAKER :
|
|
case MIXERDEV_HEADPHONE :
|
|
case MIXERDEV_LINE_OUT :
|
|
audioinfo.play.gain = gain;
|
|
audioinfo.play.balance = balance;
|
|
if ( mute )
|
|
audioinfo.play.port &= ~MixerSunPortMasks[devnum];
|
|
else
|
|
audioinfo.play.port |= MixerSunPortMasks[devnum];
|
|
break;
|
|
|
|
case MIXERDEV_MICROPHONE :
|
|
case MIXERDEV_LINE_IN :
|
|
case MIXERDEV_CD :
|
|
audioinfo.record.gain = gain;
|
|
audioinfo.record.balance = balance;
|
|
if ( mute )
|
|
audioinfo.record.port &= ~MixerSunPortMasks[devnum];
|
|
else
|
|
audioinfo.record.port |= MixerSunPortMasks[devnum];
|
|
break;
|
|
|
|
default :
|
|
return Mixer::ERR_NODEV;
|
|
}
|
|
|
|
//
|
|
// Now that we've updated the audioinfo struct, write it back to the hardware
|
|
//
|
|
if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 )
|
|
{
|
|
return( Mixer::ERR_WRITE );
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::setRecsrcHW
|
|
// DESCRIPTION :
|
|
//======================================================================
|
|
bool Mixer_SUN::setRecsrcHW( int /* devnum */, bool /* on */ )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::isRecsrcHW
|
|
// DESCRIPTION : Returns true if the specified device is a record source.
|
|
//======================================================================
|
|
bool Mixer_SUN::isRecsrcHW( int devnum )
|
|
{
|
|
switch ( devnum )
|
|
{
|
|
case MIXERDEV_MICROPHONE :
|
|
case MIXERDEV_LINE_IN :
|
|
case MIXERDEV_CD :
|
|
return TRUE;
|
|
|
|
default :
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::VolumeToGainBalance
|
|
// DESCRIPTION : Converts a Volume(left vol + right vol) into the
|
|
// Gain/Balance values used by Sun.
|
|
//======================================================================
|
|
void Mixer_SUN::VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance )
|
|
{
|
|
if ( ( volume.count() == 1 ) ||
|
|
( volume[Volume::LEFT] == volume[Volume::RIGHT] ) )
|
|
{
|
|
gain = volume[Volume::LEFT];
|
|
balance = AUDIO_MID_BALANCE;
|
|
}
|
|
else
|
|
{
|
|
if ( volume[Volume::LEFT] > volume[Volume::RIGHT] )
|
|
{
|
|
gain = volume[Volume::LEFT];
|
|
balance = AUDIO_LEFT_BALANCE +
|
|
( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) *
|
|
volume[Volume::RIGHT] / volume[Volume::LEFT];
|
|
}
|
|
else
|
|
{
|
|
gain = volume[Volume::RIGHT];
|
|
balance = AUDIO_RIGHT_BALANCE -
|
|
( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) *
|
|
volume[Volume::LEFT] / volume[Volume::RIGHT];
|
|
}
|
|
}
|
|
}
|
|
|
|
//======================================================================
|
|
// FUNCTION : Mixer::GainBalanceToVolume
|
|
// DESCRIPTION : Converts Gain/Balance returned by Sun driver to the
|
|
// Volume(left vol + right vol) format used by kmix.
|
|
//======================================================================
|
|
void Mixer_SUN::GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume )
|
|
{
|
|
if ( volume.count() == 1 )
|
|
{
|
|
volume.setVolume( Volume::LEFT, gain );
|
|
}
|
|
else
|
|
{
|
|
if ( balance <= AUDIO_MID_BALANCE )
|
|
{
|
|
volume.setVolume( Volume::LEFT, gain );
|
|
volume.setVolume( Volume::RIGHT, gain *
|
|
( balance - AUDIO_LEFT_BALANCE ) /
|
|
( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) );
|
|
}
|
|
else
|
|
{
|
|
volume.setVolume( Volume::RIGHT, gain );
|
|
volume.setVolume( Volume::LEFT, gain *
|
|
( AUDIO_RIGHT_BALANCE - balance ) /
|
|
( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
TQString SUN_getDriverName() {
|
|
return "SUNAudio";
|
|
}
|
|
|