mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 10:52:53 +00:00
382 lines
9.3 KiB
C++
382 lines
9.3 KiB
C++
/*
|
|
* KMix -- KDE's full featured mini mixer
|
|
*
|
|
*
|
|
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
|
|
*
|
|
* 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 "core/volume.h"
|
|
|
|
// for operator<<()
|
|
#include <iostream>
|
|
|
|
#include <kdebug.h>
|
|
#include <klocalizedstring.h>
|
|
#include <qmath.h>
|
|
|
|
float Volume::VOLUME_STEP_DIVISOR = 20;
|
|
float Volume::VOLUME_PAGESTEP_DIVISOR = 10;
|
|
|
|
|
|
int Volume::_channelMaskEnum[9] =
|
|
{ MLEFT, MRIGHT, MCENTER,
|
|
MWOOFER,
|
|
MSURROUNDLEFT, MSURROUNDRIGHT,
|
|
MREARSIDELEFT, MREARSIDERIGHT,
|
|
MREARCENTER
|
|
};
|
|
|
|
QString Volume::ChannelNameReadable[9] =
|
|
{
|
|
// "Left", "Right",
|
|
// "Center", "Subwoofer",
|
|
// "Surround Left", "Surround Right",
|
|
// "Side Left", "Side Right",
|
|
// "Rear Center"
|
|
|
|
i18nc("Channel name", "Left"), i18nc("Channel name", "Right"),
|
|
i18nc("Channel name", "Center"), i18nc("Channel name", "Subwoofer"),
|
|
i18nc("Channel name", "Surround Left"), i18nc("Channel name", "Surround Right"),
|
|
i18nc("Channel name", "Side Left"), i18nc("Channel name", "Side Right"),
|
|
i18nc("Channel name", "Rear Center")
|
|
|
|
};
|
|
|
|
char Volume::ChannelNameForPersistence[9][30] = {
|
|
"volumeL", "volumeR",
|
|
"volumeCenter", "volumeWoofer",
|
|
"volumeSurroundL", "volumeSurroundR",
|
|
"volumeSideL", "volumeSideR",
|
|
"volumeRearCenter"
|
|
};
|
|
|
|
// Forbidden/private. Only here because if there is no CaptureVolume we need the values initialized
|
|
// And also QMap requires it.
|
|
Volume::Volume()
|
|
{
|
|
_minVolume = 0;
|
|
_maxVolume = 0;
|
|
_hasSwitch = false;
|
|
_switchActivated = false;
|
|
_switchType = None;
|
|
_isCapture = false;
|
|
_chmask = MNONE;
|
|
}
|
|
|
|
/**
|
|
* Do not use. Only implicitely required for QMap.
|
|
*
|
|
* @deprecated Do not use
|
|
*/
|
|
VolumeChannel::VolumeChannel()
|
|
{
|
|
volume = 0;
|
|
chid = Volume::NOCHANNEL;
|
|
}
|
|
|
|
VolumeChannel::VolumeChannel(Volume::ChannelID chid)
|
|
{
|
|
volume = 0;
|
|
this->chid = chid;
|
|
}
|
|
|
|
Volume::Volume(long maxVolume, long minVolume, bool hasSwitch, bool isCapture )
|
|
{
|
|
init((ChannelMask)0, maxVolume, minVolume, hasSwitch, isCapture );
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
*/
|
|
void Volume::addVolumeChannels(ChannelMask chmask)
|
|
{
|
|
for ( Volume::ChannelID chid=Volume::CHIDMIN; chid<= Volume::CHIDMAX; )
|
|
{
|
|
if ( chmask & Volume::_channelMaskEnum[chid] )
|
|
{
|
|
addVolumeChannel(VolumeChannel(chid));
|
|
}
|
|
chid = (Volume::ChannelID)( 1 + (int)chid); // ugly
|
|
} // for all channels
|
|
}
|
|
|
|
void Volume::addVolumeChannel(VolumeChannel vc)
|
|
{
|
|
_volumesL.insert(vc.chid, vc);
|
|
// Add the correpsonnding "muted version" of the chnnel.
|
|
// VolumeChannel* zeroChannel = new VolumeChannel(vc.chid);
|
|
// zeroChannel->volume = 0;
|
|
// _volumesMuted.insert(zeroChannel->chid, *zeroChannel); // TODO remove _volumesMuted
|
|
}
|
|
|
|
|
|
|
|
void Volume::init( ChannelMask chmask, long maxVolume, long minVolume, bool hasSwitch, bool isCapture)
|
|
{
|
|
_chmask = chmask;
|
|
_maxVolume = maxVolume;
|
|
_minVolume = minVolume;
|
|
_hasSwitch = hasSwitch;
|
|
_isCapture = isCapture;
|
|
//_muted = false;
|
|
// Presume that the switch is active. This will always work:
|
|
// a) Physical switches will be updated after start from the hardware.
|
|
// b) Emulated virtual/switches will not receive updates from the hardware, so they shouldn't disable the channels.
|
|
_switchActivated = true;
|
|
}
|
|
|
|
QMap<Volume::ChannelID, VolumeChannel> Volume::getVolumesWhenActive() const
|
|
{
|
|
return _volumesL;
|
|
}
|
|
|
|
QMap<Volume::ChannelID, VolumeChannel> Volume::getVolumes() const
|
|
{
|
|
return _volumesL;
|
|
}
|
|
|
|
/**
|
|
* Returns the absolute change to do one "step" for this volume. This is similar to a page step in a slider,
|
|
* namely a fixed percentage of the range.
|
|
* One step is the percentage given by 100/VOLUME_STEP_DIVISOR. The
|
|
* default VOLUME_STEP_DIVISOR is 20, so default change is 5% of the volumeSpan().
|
|
*
|
|
* This method guarantees a minimum absolute change of 1, zero is never returned.
|
|
*
|
|
* It is NOT verified, that such a volume change would actually be possible. You might hit the upper or lower bounds
|
|
* of the volume range.
|
|
*
|
|
*
|
|
* @param decrease true, if you want a volume step that decreases the volume by one page step
|
|
* @return The volume step. It will be negative if you have used decrease==true
|
|
*
|
|
*/
|
|
long Volume::volumeStep(bool decrease)
|
|
{
|
|
long inc = volumeSpan() / Volume::VOLUME_STEP_DIVISOR;
|
|
if ( inc == 0 ) inc = 1;
|
|
if ( decrease ) inc *= -1;
|
|
return inc;
|
|
}
|
|
|
|
|
|
// @ compatibility
|
|
void Volume::setAllVolumes(long vol)
|
|
{
|
|
long int finalVol = volrange(vol);
|
|
QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.begin();
|
|
while (it != _volumesL.end())
|
|
{
|
|
it.value().volume = finalVol;
|
|
//it.value().unmutedVolume= finalVol;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void Volume::changeAllVolumes( long step )
|
|
{
|
|
QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.begin();
|
|
while (it != _volumesL.end())
|
|
{
|
|
long int finalVol = volrange(it.value().volume + step);
|
|
it.value().volume = finalVol;
|
|
// it.value().unmutedVolume= finalVol;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the volume for the given Channel
|
|
* @ compatibility
|
|
*/
|
|
void Volume::setVolume( ChannelID chid, long vol)
|
|
{
|
|
QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.find(chid);
|
|
if ( it != _volumesL.end())
|
|
{
|
|
it.value().volume = vol;
|
|
//it.value().unmutedVolume = vol;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy the volume elements contained in v to this Volume object.
|
|
*/
|
|
// void Volume::setVolume(const Volume &v)
|
|
// {
|
|
// foreach (VolumeChannel vc, _volumesL )
|
|
// {
|
|
// ChannelID chid = vc.chid;
|
|
// v.getVolumes()[chid].volume = vc.volume;
|
|
// //v.getVolumes()[chid].unmutedVolume = vc.volume;
|
|
// }
|
|
// }
|
|
|
|
void Volume::setSwitch( bool active )
|
|
{
|
|
_switchActivated = active;
|
|
|
|
// if ( isCapture() )
|
|
// return;
|
|
//
|
|
// for playback volumes we will not only do the switch, but also set the volume to 0
|
|
// QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.begin();
|
|
// if ( active )
|
|
// {
|
|
// while (it != _volumesL.end())
|
|
// {
|
|
// VolumeChannel& vc = it.value();
|
|
// vc.volume = vc.unmutedVolume;
|
|
// ++it;
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// while (it != _volumesL.end())
|
|
// {
|
|
// VolumeChannel& vc = it.value();
|
|
// vc.unmutedVolume = vc.volume;
|
|
// vc.volume = 0;
|
|
// ++it;
|
|
// }
|
|
// }
|
|
}
|
|
|
|
long Volume::maxVolume() {
|
|
return _maxVolume;
|
|
}
|
|
|
|
long Volume::minVolume() {
|
|
return _minVolume;
|
|
}
|
|
|
|
long Volume::volumeSpan() {
|
|
return _maxVolume - _minVolume + 1;
|
|
}
|
|
|
|
/**
|
|
* Returns the volume of the given channel.
|
|
*/
|
|
long Volume::getVolume(ChannelID chid)
|
|
{
|
|
return _volumesL.value(chid).volume;
|
|
}
|
|
|
|
/**
|
|
* Returns the volume of the given channel. If this Volume is inactive (switched off), 0 is returned.
|
|
*/
|
|
long Volume::getVolumeForGUI(ChannelID chid)
|
|
{
|
|
if (! isSwitchActivated() )
|
|
return 0;
|
|
|
|
return _volumesL.value(chid).volume;
|
|
}
|
|
|
|
qreal Volume::getAvgVolume(ChannelMask chmask)
|
|
{
|
|
int avgVolumeCounter = 0;
|
|
long long sumOfActiveVolumes = 0;
|
|
foreach (VolumeChannel vc, _volumesL )
|
|
{
|
|
if (Volume::_channelMaskEnum[vc.chid] & chmask )
|
|
{
|
|
sumOfActiveVolumes += vc.volume;
|
|
++avgVolumeCounter;
|
|
}
|
|
}
|
|
if (avgVolumeCounter != 0) {
|
|
qreal sumOfActiveVolumesQreal = sumOfActiveVolumes;
|
|
sumOfActiveVolumesQreal /= avgVolumeCounter;
|
|
return sumOfActiveVolumesQreal;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Volume::getAvgVolumePercent(ChannelMask chmask)
|
|
{
|
|
qreal volume = getAvgVolume(chmask);
|
|
// min=-100, max=200 => volSpan = 301
|
|
// volume = -50 => volShiftedToZero = -50+min = 50
|
|
qreal volSpan = volumeSpan();
|
|
qreal volShiftedToZero = volume - _minVolume;
|
|
qreal percentReal = ( volSpan == 0 ) ? 0 : ( 100 * volShiftedToZero ) / ( volSpan - 1);
|
|
int percent = qRound(percentReal);
|
|
//kDebug() << "volSpan=" << volSpan << ", volume=" << volume << ", volShiftedToPositive=" << volShiftedToZero << ", percent=" << percent;
|
|
|
|
return percent;
|
|
}
|
|
|
|
int Volume::count() {
|
|
return getVolumes().count();
|
|
}
|
|
|
|
/**
|
|
* returns a "sane" volume level. This means, it is a volume level inside the
|
|
* valid bounds
|
|
*/
|
|
long Volume::volrange( long vol )
|
|
{
|
|
if ( vol < _minVolume ) {
|
|
return _minVolume;
|
|
}
|
|
else if ( vol < _maxVolume ) {
|
|
return vol;
|
|
}
|
|
else {
|
|
return _maxVolume;
|
|
}
|
|
}
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Volume& vol) {
|
|
os << "(";
|
|
|
|
bool first = true;
|
|
foreach ( const VolumeChannel vc, vol.getVolumes() )
|
|
{
|
|
if ( !first ) os << ",";
|
|
else first = false;
|
|
os << vc.volume;
|
|
} // all channels
|
|
os << ")";
|
|
|
|
os << " [" << vol._minVolume << "-" << vol._maxVolume;
|
|
if ( vol._switchActivated ) { os << " : switch active ]"; } else { os << " : switch inactive ]"; }
|
|
|
|
return os;
|
|
}
|
|
|
|
QDebug operator<<(QDebug os, const Volume& vol) {
|
|
os << "(";
|
|
bool first = true;
|
|
foreach ( VolumeChannel vc, vol.getVolumes() )
|
|
{
|
|
if ( !first ) os << ",";
|
|
else first = false;
|
|
os << vc.volume;
|
|
} // all channels
|
|
os << ")";
|
|
|
|
os << " [" << vol._minVolume << "-" << vol._maxVolume;
|
|
if ( vol._switchActivated ) { os << " : switch active ]"; } else { os << " : switch inactive ]"; }
|
|
|
|
return os;
|
|
}
|