[mythtv] [PATCH] volume control for OSS and ALSA (Part 2)
David George
david at thegeorges.us
Tue Oct 26 14:46:05 UTC 2004
Here is part two (threaded to make it easier to find).
--
David
-------------- next part --------------
#ifndef VOLUMECONTROLBASE_H_
#define VOLUMECONTROLBASE_H_
#include <qstring.h>
#include "volumecontrol.h"
using namespace std;
class VolumeControlBase : public VolumeControl
{
public:
VolumeControlBase(bool setstartingvolume = true);
virtual ~VolumeControlBase();
virtual void AdjustCurrentVolume(int change);
virtual void SetMute(bool on);
virtual void ToggleMute(void);
virtual bool GetMute(void) { return mute; }
virtual kMuteState IterateMutedChannels(void);
protected:
// You need to implement the following functions
virtual void OpenMixer(bool setstartingvolume) = 0;
virtual void CloseMixer(void) = 0;
virtual void SetCurrentVolume(QString control, int value, bool save) = 0;
QString mixer_control; // e.g. "PCM"
int internal_volume;
bool mute;
kMuteState current_mute_state;
float volume_range_multiplier;
long playback_vol_min, playback_vol_max;
};
#endif
-------------- next part --------------
#include <cstdio>
#include <cstdlib>
using namespace std;
#include "mythcontext.h"
#include "volumecontrolbase.h"
VolumeControlBase::VolumeControlBase(bool setstartingvolume)
{
mute = false;
current_mute_state = MUTE_OFF;
// call OpenMixer from the concrete class
}
VolumeControlBase::~VolumeControlBase()
{
// call CloseMixer from the concrete class
}
void VolumeControlBase::OpenMixer(bool setstartingvolume)
{
// Override me
}
void VolumeControlBase::CloseMixer()
{
// Override me
}
void VolumeControlBase::AdjustCurrentVolume(int change)
{
if (change < 0 && (change * -1) < volume_range_multiplier)
change = (int)(volume_range_multiplier * -1 - 0.5);
else if (change > 0 && change < volume_range_multiplier)
change = (int)(volume_range_multiplier + 0.5);
VERBOSE(VB_AUDIO, QString("AdjustCurrentVolume %1 (mute=%2, internal=%3)")
.arg(change).arg(mute).arg(internal_volume));
int newvol = GetCurrentVolume() + change;
SetCurrentVolume(mixer_control, newvol, true);
}
void VolumeControlBase::SetMute(bool on)
{
VERBOSE(VB_AUDIO, QString("SetMute %1").arg(on));
int actual_volume;
if (on) {
actual_volume = 0;
} else {
actual_volume = internal_volume;
}
mute = on;
SetCurrentVolume(mixer_control, actual_volume, false);
}
void VolumeControlBase::ToggleMute(void)
{
SetMute(!mute);
}
kMuteState VolumeControlBase::IterateMutedChannels(void)
{
// current_mute_state is initialized to "MUTE_OFF". If individual muting
// is enabled, each call to SetMute will advance to the next state:
// MUTE_OFF -> MUTE_LEFT -> MUTE_RIGHT -> MUTE_BOTH -> MUTE_OFF
switch (current_mute_state)
{
case MUTE_OFF:
current_mute_state = MUTE_LEFT;
break;
case MUTE_LEFT:
current_mute_state = MUTE_RIGHT;
break;
case MUTE_RIGHT:
current_mute_state = MUTE_BOTH;
break;
case MUTE_BOTH:
current_mute_state = MUTE_OFF;
break;
}
SetCurrentVolume(mixer_control, internal_volume, false);
return (current_mute_state);
}
-------------- next part --------------
#ifndef VOLUMECONTROLOSS_H_
#define VOLUMECONTROLOSS_H_
#include <qstring.h>
#include "volumecontrolbase.h"
using namespace std;
class VolumeControlOSS : public VolumeControlBase
{
public:
VolumeControlOSS(bool setstartingvolume = true);
virtual ~VolumeControlOSS();
virtual int GetCurrentVolume(void);
virtual void SetCurrentVolume(QString control, int value, bool save);
protected:
virtual void OpenMixer(bool setstartingvolume);
virtual void CloseMixer(void);
virtual void SetupMixer(void);
private:
int mixerfd;
};
#endif
-------------- next part --------------
#include "volumecontroloss.h"
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <cstdio>
#include <unistd.h>
#include <iostream>
using namespace std;
#include "mythcontext.h"
VolumeControlOSS::VolumeControlOSS(bool setstartingvolume)
: VolumeControlBase(setstartingvolume)
{
mixerfd = -1;
volume_range_multiplier = 0.0;
playback_vol_min = 0;
playback_vol_max = 100;
OpenMixer(setstartingvolume);
}
VolumeControlOSS::~VolumeControlOSS()
{
if (mixerfd > -1)
close(mixerfd);
}
void VolumeControlOSS::OpenMixer(bool setstartingvolume)
{
int volume;
mixer_control = gContext->GetSetting("MixerControl", "PCM");
SetupMixer();
if (mixerfd > -1 && setstartingvolume)
{
volume = gContext->GetNumSetting("MasterMixerVolume", 80);
SetCurrentVolume("Master", volume, false);
volume = gContext->GetNumSetting("PCMMixerVolume", 80);
SetCurrentVolume("PCM", volume, false);
}
internal_volume = GetCurrentVolume();
}
void VolumeControlOSS::CloseMixer(void)
{
close(mixerfd);
mixerfd = -1;
}
void VolumeControlOSS::SetupMixer(void)
{
QString device = gContext->GetSetting("MixerDevice", "/dev/mixer");
VERBOSE(VB_AUDIO, QString("Opening mixer %1").arg(device));
mixerfd = open(device.ascii(), O_RDONLY);
if (mixerfd < 0)
VERBOSE(VB_IMPORTANT,
QString("VolumeControlOSS: mixer open (%1) err %2")
.arg(device).arg(errno));
}
int VolumeControlOSS::GetCurrentVolume(void)
{
if (mixerfd < 0 || mute)
return internal_volume;
int realvol, osscontrol;
if (mixer_control == "Master")
osscontrol = SOUND_MIXER_VOLUME;
else
osscontrol = SOUND_MIXER_PCM;
int ret = ioctl(mixerfd, MIXER_READ(osscontrol), &realvol);
if (ret < 0)
{
perror("Reading PCM volume: ");
}
internal_volume = realvol & 0xff; // just use the left channel
VERBOSE(VB_AUDIO, QString("current %1 volume is %2")
.arg(mixer_control).arg(internal_volume));
return internal_volume;
}
void VolumeControlOSS::SetCurrentVolume(QString control, int value, bool save)
{
int osscontrol, volume = value;
if (volume < 0)
volume = 0;
if (volume > 100)
volume = 100;
VERBOSE(VB_AUDIO, QString("Setting %1 volume to %2")
.arg(control).arg(volume));
if (mixerfd > -1 && !mute)
{
int realvol = (volume << 8) + volume;
if (control == "Master")
osscontrol = SOUND_MIXER_VOLUME;
else
osscontrol = SOUND_MIXER_PCM;
int ret = ioctl(mixerfd, MIXER_WRITE(osscontrol), &realvol);
if (ret < 0)
perror("Setting volume: ");
}
if (!mute)
internal_volume = volume;
VERBOSE(VB_AUDIO, QString("new volume is %1 (mute=%2, internal=%3)")
.arg(volume).arg(mute).arg(internal_volume));
if (save)
{
QString controlLabel = mixer_control + "MixerVolume";
gContext->SaveSetting(controlLabel, volume);
}
}
-------------- next part --------------
#ifndef VOLUMECONTROLALSA_H_
#define VOLUMECONTROLALSA_H_
#include <qstring.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include "volumecontrolbase.h"
using namespace std;
class VolumeControlALSA : public VolumeControlBase
{
public:
VolumeControlALSA(bool setstartingvolume = true);
virtual ~VolumeControlALSA();
virtual int GetCurrentVolume(void);
virtual void SetCurrentVolume(QString control, int value, bool save);
protected:
virtual void OpenMixer(bool setstartingvolume);
virtual void CloseMixer(void);
virtual void SetupMixer(void);
private:
inline void GetVolumeRange(void);
snd_mixer_t *mixer_handle;
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
};
#endif
-------------- next part --------------
#include <cstdio>
#include <cstdlib>
using namespace std;
#include "mythcontext.h"
#include "volumecontrolalsa.h"
VolumeControlALSA::VolumeControlALSA(bool setstartingvolume)
: VolumeControlBase(setstartingvolume)
{
mixer_handle = NULL;
OpenMixer(setstartingvolume);
}
VolumeControlALSA::~VolumeControlALSA()
{
if (mixer_handle != NULL)
snd_mixer_close(mixer_handle);
}
void VolumeControlALSA::OpenMixer(bool setstartingvolume)
{
int volume;
mixer_control = gContext->GetSetting("MixerControl", "PCM");
SetupMixer();
if (mixer_handle != NULL && setstartingvolume)
{
volume = gContext->GetNumSetting("MasterMixerVolume", 80);
SetCurrentVolume("Master", volume, false);
volume = gContext->GetNumSetting("PCMMixerVolume", 80);
SetCurrentVolume("PCM", volume, false);
}
internal_volume = GetCurrentVolume();
}
void VolumeControlALSA::CloseMixer(void)
{
snd_mixer_close(mixer_handle);
mixer_handle = NULL;
}
void VolumeControlALSA::SetupMixer(void)
{
int err;
QString device = gContext->GetSetting("MixerDevice", "default");
if (mixer_handle != NULL)
snd_mixer_close(mixer_handle);
VERBOSE(VB_AUDIO, QString("Opening mixer %1").arg(device));
if ((err = snd_mixer_open(&mixer_handle, 0)) < 0)
{
Error(QString("mixer open err %1: %2")
.arg(err).arg(snd_strerror(err)));
mixer_handle = NULL;
return;
}
if ((err = snd_mixer_attach(mixer_handle, device.ascii())) < 0)
{
Error(QString("mixer attach err %1: %2")
.arg(err).arg(snd_strerror(err)));
snd_mixer_close(mixer_handle);
mixer_handle = NULL;
return;
}
if ((err = snd_mixer_selem_register(mixer_handle, NULL, NULL)) < 0)
{
Error(QString("mixer register err %1: %2")
.arg(err).arg(snd_strerror(err)));
snd_mixer_close(mixer_handle);
mixer_handle = NULL;
return;
}
if ((err = snd_mixer_load(mixer_handle)) < 0)
{
Error(QString("mixer load err %1: %2")
.arg(err).arg(snd_strerror(err)));
snd_mixer_close(mixer_handle);
mixer_handle = NULL;
return;
}
}
int VolumeControlALSA::GetCurrentVolume(void)
{
if (mixer_handle == NULL || mute)
return internal_volume;
long actual_volume, volume;
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, mixer_control.ascii());
if ((elem = snd_mixer_find_selem(mixer_handle, sid)) == NULL)
{
Error(QString("mixer unable to find control %1").arg(mixer_control));
snd_mixer_close(mixer_handle);
mixer_handle = NULL;
return 0;
}
GetVolumeRange();
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT,
&actual_volume);
volume = (int)((actual_volume - playback_vol_min) *
volume_range_multiplier);
internal_volume = volume;
VERBOSE(VB_AUDIO, QString("current %1 volume is %2 (internal=%3)")
.arg(mixer_control).arg(volume).arg(internal_volume));
return internal_volume;
}
void VolumeControlALSA::SetCurrentVolume(QString control, int value, bool save)
{
int err, set_vol;
int volume = value;
if (volume < 0)
volume = 0;
if (volume > 100)
volume = 100;
VERBOSE(VB_AUDIO, QString("Setting %1 volume to %2")
.arg(control).arg(volume));
if (mixer_handle != NULL && !mute)
{
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, control.ascii());
if ((elem = snd_mixer_find_selem(mixer_handle, sid)) == NULL)
{
Error(QString("mixer unable to find control %1").arg(control));
return;
}
GetVolumeRange();
if (current_mute_state == MUTE_LEFT ||
current_mute_state == MUTE_BOTH)
set_vol = 0;
else
set_vol = (int)(volume / volume_range_multiplier +
playback_vol_min + 0.5);
if ((err = snd_mixer_selem_set_playback_volume(elem,
SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0)
{
Error(QString("mixer set left channel err %1: %2")
.arg(err).arg(snd_strerror(err)));
return;
}
else
{
VERBOSE(VB_AUDIO, QString("left vol set to %1").arg(set_vol));
}
if (current_mute_state == MUTE_RIGHT ||
current_mute_state == MUTE_BOTH)
set_vol = 0;
else
set_vol = (int)(volume / volume_range_multiplier +
playback_vol_min + 0.5);
if ((err = snd_mixer_selem_set_playback_volume(elem,
SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0)
{
Error(QString("mixer set right channel err %1: %2")
.arg(err).arg(snd_strerror(err)));
return;
}
else
{
VERBOSE(VB_AUDIO, QString("right vol set to %1").arg(set_vol));
}
}
if (!mute)
internal_volume = volume;
VERBOSE(VB_AUDIO, QString("new volume is %1 (mute=%2, internal=%3)")
.arg(volume).arg(mute).arg(internal_volume));
if (save)
{
QString controlLabel = mixer_control + "MixerVolume";
gContext->SaveSetting(controlLabel, volume);
}
}
inline void VolumeControlALSA::GetVolumeRange(void)
{
snd_mixer_selem_get_playback_volume_range(elem, &playback_vol_min,
&playback_vol_max);
volume_range_multiplier = (100.0 / (float)(playback_vol_max -
playback_vol_min));
VERBOSE(VB_AUDIO, QString("Volume range is %1 to %2, mult=%3")
.arg(playback_vol_min).arg(playback_vol_max)
.arg(volume_range_multiplier));
}
More information about the mythtv-dev
mailing list