OTHER: implement volume/balance handling in own class

This commit is contained in:
Thomas Frauendorfer 2010-01-22 06:13:08 +01:00
parent 75bf5909ef
commit 19a877ddc6
7 changed files with 299 additions and 154 deletions

View file

@ -0,0 +1,199 @@
/**
* This file is a part of Promoe, an XMMS2 Client.
*
* Copyright (C) 2005-2010 XMMS2 Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*/
#include "volumehandler.h"
#include "xclient.h"
#include <QStyle>
#include <xplayback.h>
struct volumes {
int left;
int right;
};
// The balance calculation still need some tweaking, as modifying volume
// also changes balance (towards 0). So changing volume multiple times causes
// balance to be reset to center
static int
calcBalance (int left, int right)
{
if (left == right)
return 0;
if (left == 0)
return MAX_STEREO_BALANCE;
if (right == 0)
return -MAX_STEREO_BALANCE;
double tmp = static_cast<double>(right-left) / static_cast<double>(qMax(left, right));
tmp *= static_cast<double>(MAX_STEREO_BALANCE);
tmp += 0.5;
return static_cast<int>(tmp);
}
static struct volumes
calcVolumes (int volume, int balance)
{
struct volumes vols = {volume, volume};
if (balance == 0) {
return vols;
}
if (balance >= MAX_STEREO_BALANCE) {
vols.left = 0;
return vols;
}
if (balance <= -MAX_STEREO_BALANCE) {
vols.right = 0;
return vols;
}
int dec = (static_cast<double>(volume) / static_cast<double>(MAX_STEREO_BALANCE) * static_cast<double>(qAbs(balance))) + 0.5;
if (balance > 0) {
vols.left -= dec;
} else {
vols.right -= dec;
}
return vols;
}
VolumeHandler::VolumeHandler(const XClient *client) : QObject(),
m_client (client)
{
m_balance = 0;
m_volume = 0;
keys_left << "left";
keys_right << "right";
connect (m_client->xplayback (), SIGNAL(volumeChanged(VolumeMap)),
this, SLOT(serverVolumeChanged (VolumeMap)));
}
void VolumeHandler::serverVolumeChanged (VolumeMap volumes)
{
if (m_volumes.size () <= 1 && volumes.size () > 1) {
emit mono(false);
}
if (m_volumes.size () > 1 && volumes.size () <= 1) {
emit mono(true);
}
m_volumes = volumes;
int _balance = 0;
int _volume = 0;
if (volumes.isEmpty()) {
channel_left_mono = QString ();
channel_right = QString ();
} else if (isMono ()) {
channel_left_mono = m_volumes.keys ().first ();
channel_right = QString ();
_volume = m_volumes.value (channel_left_mono);
} else {
channel_left_mono = channel_right = QString ();
QStringList keys = m_volumes.keys ();
foreach (QString lkey, keys) {
if (keys_left.contains (lkey)) {
channel_left_mono = lkey;
break;
}
}
foreach (QString rkey, keys) {
if (keys_right.contains (rkey)) {
channel_right = rkey;
break;
}
}
if (channel_left_mono.isEmpty () || channel_right.isEmpty ()) {
qWarning () << "Could not parse left and right volume from: "
<< keys;
} else {
int lvalue = m_volumes.value(channel_left_mono);
int rvalue = m_volumes.value(channel_right);
_volume = qMax (lvalue, rvalue);
_balance = calcBalance (lvalue, rvalue);
}
}
if (_volume != m_volume) {
m_volume = _volume;
emit volume(_volume);
}
if (_balance != m_balance) {
m_balance = _balance;
emit balance(m_balance);
}
}
void VolumeHandler::setVolume (QString key, int volume)
{
if (key.isEmpty ())
return;
if (!m_volumes.contains (key))
return;
if (m_volumes.value (key) == volume)
return;
m_client->xplayback ()->setVolume (key, volume);
}
void VolumeHandler::setVolume (int _volume, int _balance)
{
if (m_volumes.isEmpty ())
return;
if (isMono ())
_balance = 0;
if (_volume == m_volume && _balance == m_balance)
return;
const struct volumes vols = calcVolumes (_volume, _balance);
setVolume (channel_left_mono, vols.left);
setVolume (channel_right, vols.right);
}
// calculate volumes for left and right and set them in the server
void VolumeHandler::setVolume (int volume)
{
setVolume (volume, m_balance);
}
// calculate volumes for left and right and set them in the server
void VolumeHandler::setBalance (int balance)
{
setVolume (m_volume, balance);
}
#include "volumehandler.moc"

View file

@ -0,0 +1,77 @@
/**
* This file is a part of Promoe, an XMMS2 Client.
*
* Copyright (C) 2005-2010 XMMS2 Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*/
#ifndef __VOLUMEHANDLER_H__
#define __VOLUMEHANDLER_H__
#include <QObject>
#include <QMap>
#include <QString>
#include <QStringList>
class XClient;
typedef QMap<QString, int> VolumeMap;
static const int MAX_STEREO_BALANCE = 20;
class VolumeHandler : public QObject
{
Q_OBJECT
public:
VolumeHandler (const XClient *);
VolumeMap getVolumes () const {
return m_volumes;
};
QStringList getVolumeKeys () const {
return m_volumes.keys();
}
bool isMono () const {
return (m_volumes.size() == 1);
}
public slots:
void serverVolumeChanged (VolumeMap);
void setVolume (QString, int);
void setVolume (int volume, int balance);
void setVolume (int);
void setBalance (int);
signals:
void volume (int volume);
void balance (int balance);
void mono (bool);
protected:
const XClient *m_client;
int m_volume;
int m_balance;
QString channel_left_mono;
QString channel_right;
VolumeMap m_volumes;
QStringList keys_left;
QStringList keys_right;
};
#endif

View file

@ -41,6 +41,7 @@ lib_source = """
xclientcache.cpp
xcollection.cpp
xmmsqt4.cpp
volumehandler.cpp
"""

View file

@ -30,9 +30,6 @@ XPlayback::XPlayback (XClient *client)
{
m_client = client;
m_volume = 0;
m_balance = 0;
connect (client, SIGNAL (gotConnection (XClient *)),
this, SLOT (on_connect (XClient *)));
@ -151,149 +148,25 @@ XPlayback::playback_status (const Xmms::Playback::Status &status)
/*
* Volume
*/
inline int
calcBalance (int left, int right)
{
if (left == right)
return 0;
if (left == 0)
return MAX_BALANCE;
if (right == 0)
return -MAX_BALANCE;
//FIXME: This somehow works, but I'm not happy with it as
// QStyle::sliderValueFromPosition is not intended for this
if (left > right)
return -QStyle::sliderValueFromPosition(0, MAX_BALANCE, right, left, true);
else
return QStyle::sliderValueFromPosition(0, MAX_BALANCE, left, right, true);
}
bool
XPlayback::volume_changed (const Xmms::Dict &volDict)
{
QHash<QString, QVariant> levels = XClient::convert_dict (volDict);
if (levels.size () == 1) {
m_onechannel = true;
newVolume (levels.values ().first ().toInt ());
newBalance (0);
} else {
/*
* I might add a configure option later, to map arbitrary keys to
* left and right
*/
if (!levels.contains ("left") || !levels.contains ("right")) {
qWarning () << "Could not get volume levels, dict contains keys: "
<< levels.keys ();
// disable further updates. Otherwise we would spam the console
return false;
}
int left = levels["left"].toInt ();
int right = levels["right"].toInt ();
newVolume (qMax (right, left));
newBalance (calcBalance (left, right));
VolumeMap volumes;
QStringList keys = levels.keys();
foreach (QString key, keys) {
volumes.insert (key, levels.value (key).toInt());
}
return true;
emit volumeChanged (volumes);
}
void
XPlayback::newVolume (int new_volume)
XPlayback::setVolume (QString key, int volume)
{
// only emit signal if the volume really changed
if (new_volume == m_volume)
return;
m_volume = new_volume;
emit volumeChanged (new_volume);
}
void
XPlayback::newBalance (int new_balance)
{
// only emit signal if balance really changed
if (new_balance == m_balance)
return;
m_balance = new_balance;
emit balanceChanged (new_balance);
}
int
calcVolume (int volume, int balance)
{
balance = qAbs (balance);
if (balance > MAX_BALANCE) {
qWarning () << "Error in calculating balance, value " << balance
<< "is outside valid range";
return 0;
}
if (balance == 0)
return volume;
if (balance == MAX_BALANCE)
return 0;
//FIXME: this somehow works, but I'm not happy with it as
// QStyle::sliderPositionFromValue is not intended for this
return QStyle::sliderPositionFromValue(0, MAX_BALANCE, balance, volume, true);
}
void
XPlayback::setVolume (int new_volume)
{
if (!m_client->isConnected ()) return;
// Don't echo values the server sent us back to it
if (m_volume == new_volume)
return;
//TODO: some error checking
if (m_onechannel) {
m_client->playback ()->volumeSet ("master", new_volume);
} else {
int right, left;
if (m_balance < 0) {
left = new_volume;
right = calcVolume (new_volume, m_balance);
} else {
left = calcVolume (new_volume, m_balance);
right = new_volume;
}
m_client->playback ()->volumeSet ("left", left);
m_client->playback ()->volumeSet ("right", right);
}
m_volume = new_volume;
}
void
XPlayback::setBalance (int new_balance)
{
if (!m_client->isConnected ()) return;
// Don't echo values the server sent back to the server
if ((m_balance == new_balance) || m_onechannel)
return;
if (new_balance < 0) {
if (m_balance > 0) {
m_client->playback ()->volumeSet ("left", m_volume);
}
m_client->playback ()->volumeSet ("right",
calcVolume (m_volume, new_balance));
} else {
if (m_balance < 0) {
m_client->playback ()->volumeSet ("right", m_volume);
}
m_client->playback ()->volumeSet ("left",
calcVolume (m_volume, new_balance));
}
m_balance = new_balance;
m_client->playback ()->volumeSet (key.toStdString(), volume);
}
#include "xplayback.moc"

View file

@ -21,8 +21,9 @@ class XClient;
#include <xmmsclient/xmmsclient++.h>
#include <QObject>
#include <QMap>
static const int MAX_BALANCE = 20;
typedef QMap<QString, int> VolumeMap;
/**
* @class XPlayback
@ -35,9 +36,6 @@ class XPlayback : public QObject {
public:
XPlayback (XClient *);
int getVolume () {return m_volume;}
int getBalance () {return m_balance;}
public slots:
void play ();
void pause ();
@ -52,8 +50,7 @@ class XPlayback : public QObject {
// Helper to directly connect sliders to this class
void seekMs (int milliseconds) {seekMs ((uint) milliseconds);};
void setVolume (int new_volume);
void setBalance (int new_balance);
void setVolume (QString key, int volume);
// callbacks for clientlib
bool playback_status (const Xmms::Playback::Status &status);
@ -63,18 +60,12 @@ class XPlayback : public QObject {
signals:
void playbackStatusChanged (Xmms::Playback::Status status);
void volumeChanged (int volume);
void balanceChanged (int balance);
void volumeChanged (VolumeMap);
private:
XClient *m_client;
Xmms::Playback::Status m_status;
void newVolume (int new_volume);
void newBalance (int new_balance);
int m_volume;
int m_balance;
bool m_onechannel;
};
#endif

View file

@ -17,6 +17,7 @@
#include "xclientcache.h"
#include "xplayback.h"
#include "xconfig.h"
#include "volumehandler.h"
#include "application.h"
#include "maindisplay.h"
@ -44,6 +45,7 @@ MainDisplay::MainDisplay (MainWindow *parent) : SkinDisplay(parent)
{
const XClient *client = App->client ();
m_xconfig = client->xconfig ();
m_volumehandler = new VolumeHandler (client);
Skin* skin = Skin::getInstance ();
connect (skin, SIGNAL (skinChanged (Skin *)),
@ -101,21 +103,21 @@ MainDisplay::MainDisplay (MainWindow *parent) : SkinDisplay(parent)
m_vslider->setSliderOffset (QPoint (0, 1));
m_vslider->resize (skin->getSize (Skin::SLIDER_VOLUMEBAR_BGS));
m_vslider->move (skin->getPos (Skin::SLIDER_VOLUMEBAR_BGS));
connect (client->xplayback (), SIGNAL (volumeChanged (int)),
connect (m_volumehandler, SIGNAL (volume (int)),
m_vslider, SLOT (setValue (int)));
connect (m_vslider, SIGNAL (sliderMoved (int)),
client->xplayback (), SLOT (setVolume (int)));
m_volumehandler, SLOT (setVolume (int)));
m_bslider = new PixmapSlider (this);
m_bslider->setMinimum (-MAX_BALANCE);
m_bslider->setMaximum (MAX_BALANCE);
m_bslider->setMinimum (-MAX_STEREO_BALANCE);
m_bslider->setMaximum (MAX_STEREO_BALANCE);
m_bslider->setSliderOffset (QPoint (0, 1));
m_bslider->resize (skin->getSize (Skin::SLIDER_BALANCEBAR_BGS));
m_bslider->move (skin->getPos (Skin::SLIDER_BALANCEBAR_BGS));
connect (client->xplayback (), SIGNAL (balanceChanged (int)),
connect (m_volumehandler, SIGNAL (balance (int)),
m_bslider, SLOT (setValue (int)));
connect (m_bslider, SIGNAL (sliderMoved (int)),
client->xplayback (), SLOT (setBalance (int)));
m_volumehandler, SLOT (setBalance (int)));
connect (client->cache (), SIGNAL (activeEntryChanged (QVariantHash)),
this, SLOT (setMediainfo (const QVariantHash)));

View file

@ -47,6 +47,7 @@ class MainWindow;
class ClutterBar;
class XConfig;
class XClient;
class VolumeHandler;
class MainDisplay : public SkinDisplay
{
@ -110,6 +111,7 @@ class MainDisplay : public SkinDisplay
XConfig *m_xconfig;
VolumeHandler *m_volumehandler;
};
#endif