Index: configure
===================================================================
--- configure	(revision 22850)
+++ configure	(working copy)
@@ -595,6 +595,13 @@
     eval "$var=\"\$$var $*\""
 }
 
+prepend(){
+    var=$1
+    shift
+    flags_saved && eval "SAVE_$var=\"$* \$SAVE_$var\""
+    eval "$var=\"$* \$$var\""
+}
+
 add_cflags(){
     append CFLAGS "$@"
 }
@@ -3325,6 +3332,31 @@
     enable  audio_alsa ||
     disable audio_alsa
 
+# OSS probe
+if ! disabled audio_oss ; then
+    if test -f /usr/"${libdir_name}"/oss/include/sys/soundcard.h &&
+       check_cpp_condition /usr/"${libdir_name}"/oss/include/sys/soundcard.h "SOUND_VERSION >= 0x040000"; then
+        disable soundcard_h
+        enable sys_soundcard_h
+        prepend CONFIG_INCLUDEPATH "/usr/${libdir_name}/oss/include"
+    elif test -f /usr/local/"${libdir_name}"/oss/include/sys/soundcard.h &&
+       check_cpp_condition /usr/local/"${libdir_name}"/oss/include/sys/soundcard.h "SOUND_VERSION >= 0x040000"; then
+        disable soundcard_h
+        enable sys_soundcard_h
+        prepend CONFIG_INCLUDEPATH "/usr/local/${libdir_name}/oss/include"
+    else
+        if enabled soundcard_h ; then
+            check_cpp_condition soundcard.h "SOUND_VERSION >= 0x040000" &&
+            enable  audio_oss ||
+            disable audio_oss
+        elif enabled sys_soundcard_h ; then
+            check_cpp_condition sys/soundcard.h "SOUND_VERSION >= 0x040000" &&
+            enable  audio_oss ||
+            disable audio_oss
+        fi
+    fi
+fi
+
 # JACK probe
 ! disabled audio_jack &&
     check_lib jack/jack.h jack_client_new $audio_jack_libs &&
@@ -4022,6 +4054,7 @@
 if enabled audio_oss; then
     append CCONFIG "using_oss"
 fi
+
 if enabled audio_alsa; then
     append CCONFIG "using_alsa"
   echo "CONFIG_AUDIO_ALSA_LIBS=$audio_alsa_libs" >> $MYTH_CONFIG_MAK
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp	(revision 22850)
+++ libs/libmythtv/NuppelVideoPlayer.cpp	(working copy)
@@ -142,7 +142,7 @@
     kDisplayNUVTeletextCaptions,
 };
 
-NuppelVideoPlayer::NuppelVideoPlayer(bool muted)
+NuppelVideoPlayer::NuppelVideoPlayer()
     : decoder(NULL),                decoder_change_lock(QMutex::Recursive),
       videoOutput(NULL),            player_ctx(NULL),
       no_hardware_decoders(false),
@@ -210,7 +210,6 @@
       audio_channels(2),            audio_codec(0),
       audio_bits(-1),               audio_samplerate(44100),
       audio_stretchfactor(1.0f),    audio_lock(QMutex::Recursive),
-      audio_muted_on_creation(muted),
       // Picture-in-Picture stuff
       pip_active(false),            pip_visible(true),
       // Preview window support
@@ -403,70 +402,6 @@
     audio_samplerate      = (int)samplerate;
 }
 
-uint NuppelVideoPlayer::GetVolume(void)
-{
-    QMutexLocker lock(&audio_lock);
-    if (audioOutput)
-        return audioOutput->GetCurrentVolume();
-    return 0;
-}
-
-bool NuppelVideoPlayer::SetMuted(bool mute)
-{
-    QMutexLocker lock(&audio_lock);
-    bool is_muted = IsMuted();
-
-    if (audioOutput && !is_muted && mute &&
-        (kMuteAll == SetMuteState(kMuteAll)))
-    {
-        VERBOSE(VB_AUDIO, "muting sound " <<IsMuted());
-        return true;
-    }
-    else if (audioOutput && is_muted && !mute &&
-             (kMuteOff == SetMuteState(kMuteOff)))
-    {
-        VERBOSE(VB_AUDIO, "unmuting sound "<<IsMuted());
-        return true;
-    }
-
-    VERBOSE(VB_AUDIO, "not changing sound mute state "<<IsMuted());
-
-    return false;
-}
-
-MuteState NuppelVideoPlayer::SetMuteState(MuteState mstate)
-{
-    QMutexLocker lock(&audio_lock);
-    if (audioOutput)
-        return audioOutput->SetMuteState(mstate);
-    return kMuteAll;
-}
-
-MuteState NuppelVideoPlayer::IncrMuteState(void)
-{
-    QMutexLocker lock(&audio_lock);
-    MuteState mstate = kMuteAll;
-    if (audioOutput)
-        mstate = SetMuteState(VolumeBase::NextMuteState(GetMuteState()));
-    return mstate;
-}
-
-MuteState NuppelVideoPlayer::GetMuteState(void)
-{
-    QMutexLocker lock(&audio_lock);
-    if (audioOutput)
-        return audioOutput->GetMuteState();
-    return kMuteAll;
-}
-
-uint NuppelVideoPlayer::AdjustVolume(int change)
-{
-    QMutexLocker lock(&audio_lock);
-    if (audioOutput)
-        audioOutput->AdjustCurrentVolume(change);
-    return GetVolume();
-}
-
 void NuppelVideoPlayer::PauseDecoder(void)
 {
     decoder_lock.lock();
@@ -902,13 +837,12 @@
 
     if (!audioOutput && !using_null_videoout && player_ctx->IsAudioNeeded())
     {
-        bool setVolume = gContext->GetNumSetting("MythControlsVolume", 1);
         audioOutput = AudioOutput::OpenAudio(audio_main_device,
                                              audio_passthru_device,
                                              audio_bits, audio_channels,
                                              audio_codec, audio_samplerate,
                                              AUDIOOUTPUT_VIDEO,
-                                             setVolume, audio_passthru);
+                                             audio_passthru);
         if (!audioOutput)
             errMsg = QObject::tr("Unable to create AudioOutput.");
         else
@@ -930,11 +864,6 @@
             VERBOSE(VB_IMPORTANT, LOC + "Enabling Audio");
             no_audio_out = false;
         }
-        if (audio_muted_on_creation)
-        {
-            SetMuteState(kMuteAll);
-            audio_muted_on_creation = false;
-        }
     }
 
     if (audioOutput)
Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 22850)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -464,7 +464,6 @@
       audioSamples(NULL),           audioSamplesResampled(NULL),
       reformat_ctx(NULL),           audio_src_fmt(SAMPLE_FMT_NONE),
       allow_ac3_passthru(false),    allow_dts_passthru(false),
-      internal_vol(false),
       disable_passthru(false),      max_channels(2),
       last_ac3_channels(0),	    dummy_frame(NULL),
       // DVD
@@ -487,7 +486,6 @@
     max_channels = (uint) gContext->GetNumSetting("MaxChannels", 2);
     allow_ac3_passthru = (max_channels > 2) ? gContext->GetNumSetting("AC3PassThru", false) : false;
     allow_dts_passthru = (max_channels > 2) ? gContext->GetNumSetting("DTSPassThru", false) : false;
-    internal_vol = gContext->GetNumSetting("MythControlsVolume", 0);
 
     audioIn.sample_size = -32; // force SetupAudioStream to run once
     itv = GetNVP()->GetInteractiveTV();
@@ -4370,10 +4368,9 @@
 
     if (ctx->codec_id == CODEC_ID_AC3)
         passthru = allow_ac3_passthru && 
-                   ctx->channels >= (int)max_channels &&
-                   !internal_vol;
+                   ctx->channels >= (int)max_channels;
     else if (ctx->codec_id == CODEC_ID_DTS)
-        passthru = allow_dts_passthru && !internal_vol;
+        passthru = allow_dts_passthru;
 
     passthru &= !transcoding && !disable_passthru;
     // Don't know any cards that support spdif clocked at < 44100
Index: libs/libmythtv/tv_play.h
===================================================================
--- libs/libmythtv/tv_play.h	(revision 22850)
+++ libs/libmythtv/tv_play.h	(working copy)
@@ -33,7 +33,7 @@
 #include "programlist.h"
 #include "channelutil.h"
 #include "videoouttypes.h"
-#include "volumebase.h"
+#include "volumecontrolmanager.h"
 #include "inputinfo.h"
 #include "channelgroup.h"
 
@@ -296,6 +296,9 @@
     void AddUDPNotifyEvent(const QString &name, const UDPNotifyOSDSet*);
     void ClearUDPNotifyEvents(void);
 
+    void VolumeChanged(int volume);
+    void MuteChanged(bool mute);
+
   protected:
     void TreeMenuEntered(OSDListTreeItemEnteredEvent *e);
     void TreeMenuSelected(OSDListTreeItemSelectedEvent *e);
@@ -305,9 +308,6 @@
     virtual void run(void);
     void TVEventThreadChecks(void);
 
-    void SetMuteTimer(PlayerContext*, int timeout);
-    bool MuteChannelChange(PlayerContext *ctx);
-
     bool eventFilter(QObject *o, QEvent *e);
     static QStringList lastProgramStringList;
     static EMBEDRETURNPROGRAM RunPlaybackBoxPtr;
@@ -497,8 +497,7 @@
 
     vector<long long> TeardownAllNVPs(PlayerContext*);
     void RestartAllNVPs(PlayerContext *lctx,
-                        const vector<long long> &pos,
-                        MuteState mctx_mute);
+                        const vector<long long> &pos);
     void RestartMainNVP(PlayerContext *mctx);
 
     void PxPToggleView(  PlayerContext *actx, bool wantPBP);
@@ -658,6 +657,8 @@
     /// Picture attribute to modify (on arrow left or right)
     PictureAttribute  adjustingPictureAttribute;
 
+    QSharedPointer<VolumeControl> volumeControl; ///< Volume Control interface
+
     // Ask Allow state
     AskAllowType                 askAllowType;
     QMap<QString,AskProgramInfo> askAllowPrograms;
@@ -815,7 +816,6 @@
     TimerContextMap      stateChangeTimerId;
     TimerContextMap      signalMonitorTimerId;
     TimerContextMap      tvchainUpdateTimerId;
-    TimerContextMap      unmuteTimerId;
 
     /// Condition to signal that the Event thread is up and running
     QWaitCondition mainLoopCond;
@@ -836,8 +836,6 @@
 
     ///< Timeout for entry modes in msec
     static const uint kInputModeTimeout;
-    /// Channel changing mute timeout in msec
-    static const uint kMuteTimeout;
     /// Timeout for updating LCD info in msec
     static const uint kLCDTimeout;
     /// Timeout for browse mode exit in msec
Index: libs/libmythtv/playercontext.cpp
===================================================================
--- libs/libmythtv/playercontext.cpp	(revision 22850)
+++ libs/libmythtv/playercontext.cpp	(working copy)
@@ -430,8 +430,7 @@
 
 bool PlayerContext::CreateNVP(TV *tv, QWidget *widget,
                               TVState desiredState,
-                              WId embedwinid, const QRect *embedbounds,
-                              bool muted)
+                              WId embedwinid, const QRect *embedbounds)
 {
     int exact_seeking = gContext->GetNumSetting("ExactSeeking", 0);
 
@@ -442,7 +441,7 @@
         return false;
     }
 
-    NuppelVideoPlayer *_nvp = new NuppelVideoPlayer(muted);
+    NuppelVideoPlayer *_nvp = new NuppelVideoPlayer();
 
     if (nohardwaredecoders)
         _nvp->DisableHardwareDecoders();
@@ -492,8 +491,6 @@
                 VERBOSE(VB_IMPORTANT, LOC_ERR + errMsg);
         }
     }
-    else if (pipState == kPBPRight)
-        nvp->SetMuted(true);
 
     int maxWait = -1;
     //if (isPIP())
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h	(revision 22850)
+++ libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -4,7 +4,6 @@
 #include <sys/time.h>
 
 #include "playercontext.h"
-#include "volumebase.h"
 #include "RingBuffer.h"
 #include "osd.h"
 #include "jitterometer.h"
@@ -104,7 +103,7 @@
     friend class PlayerContext;
 
   public:
-    NuppelVideoPlayer(bool muted = false);
+    NuppelVideoPlayer();
    ~NuppelVideoPlayer();
 
     // Initialization
@@ -127,10 +126,6 @@
     void SetAudioInfo(const QString &main, const QString &passthru, uint rate);
     void SetAudioParams(int bits, int channels, int codec, int samplerate, bool passthru);
     void SetEffDsp(int dsprate);
-    uint AdjustVolume(int change);
-    bool SetMuted(bool mute);
-    MuteState SetMuteState(MuteState);
-    MuteState IncrMuteState(void);
     void SetAudioCodec(void *ac);
 
     // Sets
@@ -187,11 +182,9 @@
     float   GetVideoAspect(void) const        { return video_aspect; }
     float   GetFrameRate(void) const          { return video_frame_rate; }
 
-    uint    GetVolume(void);
     int     GetSecondsBehind(void) const;
     AspectOverrideMode GetAspectOverride(void) const;
     AdjustFillMode     GetAdjustFill(void) const;
-    MuteState          GetMuteState(void);
     CommSkipMode       GetAutoCommercialSkip(void) const;
 
     int     GetFFRewSkip(void) const          { return ffrew_skip; }
@@ -227,7 +220,6 @@
     bool    HasAudioOut(void) const           { return !no_audio_out; }
     bool    IsPIPActive(void) const           { return pip_active; }
     bool    IsPIPVisible(void) const          { return pip_visible; }
-    bool    IsMuted(void)              { return GetMuteState() == kMuteAll; }
     bool    UsingNullVideo(void) const { return using_null_videoout; }
     bool    HasTVChainNext(void) const;
 
@@ -719,7 +711,6 @@
     float    audio_stretchfactor;
     bool     audio_passthru;
     QMutex   audio_lock;
-    bool     audio_muted_on_creation;
 
     // Picture-in-Picture
     mutable QMutex pip_players_lock;
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 22850)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -83,7 +83,6 @@
 
 
 const uint TV::kInputModeTimeout             = 5000;
-const uint TV::kMuteTimeout                  = 800;
 const uint TV::kLCDTimeout                   = 1000;
 const uint TV::kBrowseTimeout                = 30000;
 const uint TV::kKeyRepeatTimeout             = 300;
@@ -894,6 +893,15 @@
     player.push_back(new PlayerContext("player"));
     playerActive = 0;
     playerLock.unlock();
+
+    volumeControl = VolumeControlManager::GetControl(gContext->GetSetting("MixerDevice"));
+    if (volumeControl)
+    {
+        connect(volumeControl.data(), SIGNAL(changedVolume(int)),
+                this, SLOT(VolumeChanged(int)), Qt::QueuedConnection);
+        connect(volumeControl.data(), SIGNAL(changedMute(bool)),
+                this, SLOT(MuteChanged(bool)), Qt::QueuedConnection);
+    }
 }
 
 /** \fn TV::Init(bool)
@@ -2787,33 +2795,6 @@
     if (handled)
         return;
 
-    // Check unmute..
-    ctx = NULL;
-    {
-        QMutexLocker locker(&timerIdLock);
-        TimerContextMap::iterator it = unmuteTimerId.find(timer_id);
-        if (it != unmuteTimerId.end())
-        {
-            KillTimer(timer_id);
-            ctx = *it;
-            unmuteTimerId.erase(it);
-        }
-    }
-
-    if (ctx)
-    {
-        PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
-        if (find_player_index(ctx) >= 0)
-        {
-            ctx->LockDeleteNVP(__FILE__, __LINE__);
-            if (ctx->nvp && ctx->nvp->IsMuted())
-                ctx->nvp->SetMuted(false);
-            ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-        }
-        ReturnPlayerLock(mctx);
-        handled = true;
-    }
-
     if (handled)
         return;
 
@@ -5375,12 +5356,6 @@
         }
     }
 
-    MuteState mctx_mute = kMuteOff;
-    mctx->LockDeleteNVP(__FILE__, __LINE__);
-    if (mctx->nvp)
-        mctx_mute = mctx->nvp->GetMuteState();
-    mctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
     vector<long long> pos = TeardownAllNVPs(mctx);
 
     if (wantPBP)
@@ -5398,7 +5373,7 @@
         }
     }
 
-    RestartAllNVPs(mctx, pos, mctx_mute);
+    RestartAllNVPs(mctx, pos);
 
     VERBOSE(VB_PLAYBACK, LOC +
             QString("PxPToggleType() converting from %1 to %2 -- end")
@@ -5542,8 +5517,7 @@
 * \brief Recreate Main and PIP windows. Could be either PIP or PBP views.
 */
 void TV::RestartAllNVPs(PlayerContext *lctx,
-                        const vector<long long> &pos,
-                        MuteState mctx_mute)
+                        const vector<long long> &pos)
 {
     QString loc = LOC + QString("RestartAllNVPs(): ");
 
@@ -5590,7 +5564,6 @@
             pipctx->LockDeleteNVP(__FILE__, __LINE__);
             if (pipctx->nvp)
             {
-                pipctx->nvp->SetMuted(true);
                 pipctx->nvp->JumpToFrame(pos[i]);
             }
             pipctx->UnlockDeleteNVP(__FILE__, __LINE__);
@@ -5602,13 +5575,6 @@
             ForceNextStateNone(pipctx);
         }
     }
-
-    // If old main player had a kMuteAll | kMuteOff setting,
-    // apply old main player's mute setting to new main player.
-    mctx->LockDeleteNVP(__FILE__, __LINE__);
-    if (mctx->nvp && ((kMuteAll == mctx_mute) || (kMuteOff == mctx_mute)))
-        mctx->nvp->SetMuteState(mctx_mute);
-    mctx->UnlockDeleteNVP(__FILE__, __LINE__);
 }
 
 void TV::PxPSwap(PlayerContext *mctx, PlayerContext *pipctx)
@@ -5636,7 +5602,6 @@
         return;
     }
 
-    MuteState mctx_mute = mctx->nvp->GetMuteState();
     mctx->deleteNVPLock.unlock();
     pipctx->deleteNVPLock.unlock();
 
@@ -5650,7 +5615,7 @@
     playerActive = (ctx_index == playerActive) ?
         0 : ((ctx_index == 0) ? ctx_index : playerActive);
 
-    RestartAllNVPs(mctx, pos, mctx_mute);
+    RestartAllNVPs(mctx, pos);
 
     SetActive(mctx, playerActive, false);
 
@@ -5671,17 +5636,10 @@
         mctx->deleteNVPLock.unlock();
         return;
     }
-
-    MuteState mctx_mute = mctx->nvp->GetMuteState();
-
-    // HACK - FIXME
-    // workaround muted audio when NVP is re-created
-    mctx_mute = kMuteOff;
-    // FIXME - end
     mctx->deleteNVPLock.unlock();
 
     vector<long long> pos = TeardownAllNVPs(mctx);
-    RestartAllNVPs(mctx, pos, mctx_mute);
+    RestartAllNVPs(mctx, pos);
     SetActive(mctx, playerActive, false);
 
     VERBOSE(VB_PLAYBACK, LOC + "Restart main player -- end");
@@ -5806,8 +5764,6 @@
 
     VERBOSE(VB_PLAYBACK, LOC + "DoNVPSeek() -- begin");
 
-    bool muted = false;
-
     ctx->LockDeleteNVP(__FILE__, __LINE__);
     if (!ctx->nvp)
     {
@@ -5815,9 +5771,6 @@
         return false;
     }
 
-    if (ctx == GetPlayer(ctx, 0))
-        muted = MuteChannelChange(ctx);
-
     bool res = false;
 
     if (LONG_LONG_MIN != audiosyncBaseline)
@@ -5838,9 +5791,6 @@
     }
     ctx->UnlockDeleteNVP(__FILE__, __LINE__);
 
-    if (muted)
-        SetMuteTimer(ctx, kMuteTimeout);
-
     VERBOSE(VB_PLAYBACK, LOC + "DoNVPSeek() -- end");
 
     return res;
@@ -6121,10 +6071,6 @@
     if (StateIsLiveTV(GetState(ctx)))
         return;
 
-    ctx->LockDeleteNVP(__FILE__, __LINE__);
-    bool muted = MuteChannelChange(ctx);
-    ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
     struct StatusPosInfo posInfo;
     ctx->CalcNVPSliderPosition(posInfo);
 
@@ -6143,9 +6089,6 @@
     if (ctx->nvp)
         ctx->nvp->SkipCommercials(direction);
     ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
-    if (muted)
-        SetMuteTimer(ctx, kMuteTimeout);
 }
 
 void TV::SwitchSource(uint source_direction)
@@ -6315,12 +6258,6 @@
         if (mctx != ctx)
             PIPRemovePlayer(mctx, ctx);
 
-        bool muted = false;
-        ctx->LockDeleteNVP(__FILE__, __LINE__);
-        if (ctx->nvp && ctx->nvp->IsMuted())
-            muted = true;
-        ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
         // pause the decoder first, so we're not reading too close to the end.
         ctx->buffer->IgnoreLiveEOF(true);
         ctx->buffer->StopReads();
@@ -6372,7 +6309,7 @@
 
             if (ctx->CreateNVP(
                     this, gContext->GetMainWindow(), ctx->GetState(),
-                    mctx->embedWinID, &mctx->embedBounds, muted))
+                    mctx->embedWinID, &mctx->embedBounds))
             {
                 ScheduleStateChange(ctx);
                 ok = true;
@@ -6729,8 +6666,6 @@
 
 void TV::ChangeChannel(PlayerContext *ctx, int direction)
 {
-    bool muted = false;
-
     if ((browse_changrp || (direction == CHANNEL_DIRECTION_FAVORITE)) &&
         (channel_group_id > -1))
     {
@@ -6761,8 +6696,6 @@
 
     QString oldinputname = ctx->recorder->GetInput();
 
-    muted = MuteChannelChange(ctx);
-
     if (ctx->paused)
     {
         OSD *osd = GetOSDLock(ctx);
@@ -6790,9 +6723,6 @@
     ctx->recorder->ChangeChannel(direction);
     ClearInputQueues(ctx, false);
 
-    if (muted)
-        SetMuteTimer(ctx, kMuteTimeout * 2);
-
     UnpauseLiveTV(ctx);
 
     if (oldinputname != ctx->recorder->GetInput())
@@ -6809,7 +6739,6 @@
 
     QString channum = chan;
     QStringList reclist;
-    bool muted = false;
 
     QString oldinputname = ctx->recorder->GetInput();
 
@@ -6879,8 +6808,6 @@
     if (getit || !ctx->recorder || !ctx->recorder->CheckChannel(channum))
         return;
 
-    muted = MuteChannelChange(ctx);
-
     OSD *osd = GetOSDLock(ctx);
     if (osd && ctx->paused)
     {
@@ -6906,9 +6833,6 @@
 
     ctx->recorder->SetChannel(channum);
 
-    if (muted)
-        SetMuteTimer(ctx, kMuteTimeout * 2);
-
     UnpauseLiveTV(ctx);
 
     if (oldinputname != ctx->recorder->GetInput())
@@ -8020,25 +7944,13 @@
 
 void TV::ChangeVolume(PlayerContext *ctx, bool up)
 {
-    ctx->LockDeleteNVP(__FILE__, __LINE__);
-    if (!ctx->nvp)
+    if (volumeControl)
     {
-        ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-        return;
+        if (up)
+            volumeControl->increaseVolume();
+        else
+            volumeControl->decreaseVolume();
     }
-    uint curvol = ctx->nvp->AdjustVolume((up) ? +2 : -2);
-    ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
-    QString text = tr("Volume %1 %").arg(curvol);
-
-    OSD *osd = GetOSDLock(ctx);
-    if (osd && !browsemode)
-    {
-        osd->ShowStatus(curvol * 10, true, tr("Adjust Volume"), text, 5,
-                        kOSDFunctionalType_PictureAdjust);
-        SetUpdateOSDPosition(false);
-    }
-    ReturnOSDLock(ctx, osd);
 }
 
 void TV::ToggleTimeStretch(PlayerContext *ctx)
@@ -8187,40 +8099,8 @@
 
 void TV::ToggleMute(PlayerContext *ctx)
 {
-    ctx->LockDeleteNVP(__FILE__, __LINE__);
-    if (!ctx->nvp || !ctx->nvp->HasAudioOut())
-    {
-        ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-        return;
-    }
-
-    MuteState mute_status;
-
-    if (!MuteIndividualChannels)
-    {
-        ctx->nvp->SetMuted(!ctx->nvp->IsMuted());
-        mute_status = (ctx->nvp->IsMuted()) ? kMuteAll : kMuteOff;
-    }
-    else
-    {
-        mute_status = ctx->nvp->IncrMuteState();
-    }
-    ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
-    QString text;
-
-    switch (mute_status)
-    {
-        case kMuteOff:   text = tr("Mute Off"); break;
-        case kMuteAll:   text = tr("Mute On"); break;
-        case kMuteLeft:  text = tr("Left Channel Muted"); break;
-        case kMuteRight: text = tr("Right Channel Muted"); break;
-    }
-
-    OSD *osd = GetOSDLock(ctx);
-    if (osd && !browsemode)
-        osd->SetSettingsText(text, 5);
-    ReturnOSDLock(ctx, osd);
+    if (volumeControl)
+        volumeControl->setMute(!volumeControl->mute());
 }
 
 void TV::ToggleSleepTimer(const PlayerContext *ctx)
@@ -8380,30 +8260,6 @@
     ReturnOSDLock(ctx, osd);
 }
 
-void TV::SetMuteTimer(PlayerContext *ctx, int timeout)
-{
-    // message to set the timer will be posted to the main UI thread
-    // where it will be caught in eventFilter and processed as CustomEvent
-    // this will properly set the mute timer
-    // otherwise it never fires on Win32
-    QString message = QString("UNMUTE %1 %2").arg((long long)ctx).arg(timeout);
-    qApp->postEvent(gContext->GetMainWindow(), new MythEvent(message));
-}
-
-bool TV::MuteChannelChange(PlayerContext *ctx)
-{
-    if (!ctx)
-        return false;
-
-    bool muted = false;
-    ctx->LockDeleteNVP(__FILE__, __LINE__);
-    if (ctx->nvp && !ctx->nvp->IsMuted())
-        muted = ctx->nvp->SetMuted(true);
-    ctx->UnlockDeleteNVP(__FILE__, __LINE__);
-
-    return muted;
-}
-
 void TV::customEvent(QEvent *e)
 {
     if ((MythEvent::Type)(e->type()) == kOSDClosedEventType)
@@ -8679,25 +8535,6 @@
         }
     }
 
-    if (message.left(6) == "UNMUTE")
-    {
-        if (tokens.size() >= 3)
-        {
-            long long target = tokens[1].toLongLong();
-            PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
-            for (uint i = 0; i < player.size(); i++)
-            {
-                PlayerContext *ctx = GetPlayer(mctx, i);
-                if (((long long)ctx) == target)
-                {
-                    QMutexLocker locker(&timerIdLock);
-                    unmuteTimerId[StartTimer(tokens[2].toUInt(), __LINE__)] = ctx;
-                }
-            }
-            ReturnPlayerLock(mctx);
-        }
-    }
-
     if (message.left(9) == "START_EPG")
     {
         int editType = tokens[1].toInt();
@@ -9171,9 +9008,9 @@
         {
             value = ctx->nvp->getVideoOutput()->GetPictureAttribute(attr);
         }
-        else if (ctx->nvp->HasAudioOut())
+        else if (ctx->nvp->HasAudioOut() && volumeControl)
         {
-            value = ctx->nvp->GetVolume();
+            value = volumeControl->volume();
             title = tr("Adjust Volume");
         }
     }
@@ -11677,6 +11514,28 @@
     ReturnPlayerLock(mctx);
 }
 
+void TV::VolumeChanged(int volume)
+{
+    OSD *osd = GetOSDL(__FILE__, __LINE__);
+    if (osd && !browsemode)
+    {
+        osd->ShowStatus(volume * 10, true, tr("Adjust Volume"),
+                        tr("Volume %1 %").arg(volume), 5,
+                        kOSDFunctionalType_PictureAdjust);
+        SetUpdateOSDPosition(false);
+    }
+    ReturnOSDLock(osd);
+}
+
+void TV::MuteChanged(bool mute)
+{
+    OSD *osd = GetOSDL(__FILE__, __LINE__);
+    if (osd && !browsemode)
+        osd->SetSettingsText(mute ? tr("Mute On") : tr("Mute Off"), 5);
+    ReturnOSDLock(osd);
+}
+
+
 OSD *TV::GetOSDL(const char *file, int location)
 {
     PlayerContext *actx = GetPlayerReadLock(-1, file, location);
Index: libs/libmythtv/playercontext.h
===================================================================
--- libs/libmythtv/playercontext.h	(revision 22850)
+++ libs/libmythtv/playercontext.h	(working copy)
@@ -48,8 +48,7 @@
     // Actions
     bool CreateNVP(TV *tv, QWidget *widget,
                    TVState desiredState,
-                   WId embedwinid, const QRect *embedBounds,
-                   bool muted = false);
+                   WId embedwinid, const QRect *embedBounds);
     void TeardownPlayer(void);
     bool StartDecoderThread(int maxWait = -1);
     bool StartOSD(TV *tv);
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h	(revision 22850)
+++ libs/libmythtv/avformatdecoder.h	(working copy)
@@ -263,7 +263,6 @@
 
     bool              allow_ac3_passthru;
     bool              allow_dts_passthru;
-    bool              internal_vol;
     bool              disable_passthru;
     uint              max_channels;
     uint              last_ac3_channels;
Index: libs/libmyth/audiooutputwin.cpp
===================================================================
--- libs/libmyth/audiooutputwin.cpp	(revision 22850)
+++ libs/libmyth/audiooutputwin.cpp	(working copy)
@@ -280,48 +280,3 @@
 {
     return m_nPkts * fragment_size;
 }
-
-int AudioOutputWin::GetVolumeChannel(int channel) const
-{
-    DWORD dwVolume = 0xffffffff;
-    int Volume = 100;
-    if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
-    {
-        Volume = (channel == 0) ?
-            (LOWORD(dwVolume) / (0xffff / 100)) :
-            (HIWORD(dwVolume) / (0xffff / 100));
-    }
-
-    VERBOSE(VB_AUDIO, "GetVolume(" << channel << ") "
-            << Volume << "(" << dwVolume << ")");
-
-    return Volume;
-}
-
-void AudioOutputWin::SetVolumeChannel(int channel, int volume)
-{
-    if (channel > 1)
-    {
-        Error(QString("Error setting channel: %1. "
-                      "Only stereo volume supported").arg(channel));
-        return;
-    }
-
-    DWORD dwVolume = 0xffffffff;
-    if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
-    {
-        if (channel == 0)
-            dwVolume = dwVolume & 0xffff0000 | volume * (0xffff / 100);
-        else
-            dwVolume = dwVolume & 0xffff | ((volume * (0xffff / 100)) << 16);
-    }
-    else
-    {
-        dwVolume = volume * (0xffff / 100);
-        dwVolume |= (dwVolume << 16);
-    }
-
-    VERBOSE(VB_AUDIO, QString("SetVolume(%1) %2(%3)")
-            .arg(channel).arg(volume).arg(dwVolume));
-    waveOutSetVolume((HWAVEOUT)WAVE_MAPPER, dwVolume);
-}
Index: libs/libmyth/volumecontrolmanager.cpp
===================================================================
--- libs/libmyth/volumecontrolmanager.cpp	(revision 0)
+++ libs/libmyth/volumecontrolmanager.cpp	(revision 0)
@@ -0,0 +1,98 @@
+#include "volumecontrolmanager.h"
+
+#include "mythverbose.h"
+#ifdef USE_ALSA
+#include "volumecontrolalsa.h"
+#endif
+#ifdef USE_COREAUDIO
+#include "volumecontrolcoreaudio.h"
+#endif
+#ifdef USING_OSS
+#include "volumecontroloss.h"
+#endif
+#ifdef USING_MINGW
+#include "volumecontrolendpoint.h"
+#endif
+#include "volumecontrolsoftware.h"
+
+
+QHash<QString, QString> VolumeControlManager::Enumerate()
+{
+    QHash<QString, QString> result;
+
+#ifdef USE_ALSA
+    result.unite(VolumeControlALSA::Enumerate());
+#endif
+#ifdef USE_COREAUDIO
+    result.unite(VolumeControlCoreAudio::Enumerate());
+#endif
+#ifdef USING_OSS
+    result.unite(VolumeControlOSS::Enumerate());
+#endif
+#ifdef USING_MINGW
+    result.unite(VolumeControlEndpoint::Enumerate());
+#endif
+    result.unite(VolumeControlSoftware::Enumerate());
+
+    return result;
+}
+
+QSharedPointer<VolumeControl> VolumeControlManager::GetControl(QString device)
+{
+    QSharedPointer<VolumeControl> result;
+    QString api = device.section(':', 0, 0);
+    device = device.remove(0, api.size() + 1);
+
+    if (api == "ALSA")
+    {
+#ifdef USE_ALSA
+        result = QSharedPointer<VolumeControl>(new VolumeControlALSA(device));
+#else
+        VERBOSE(VB_IMPORTANT,
+                QString("Volume control device is set to an ALSA device but "
+                        "ALSA support is not compiled in!"));
+#endif
+    }
+    else if (api == "CoreAudio")
+    {
+#ifdef USE_COREAUDIO
+        result = QSharedPointer<VolumeControl>(new VolumeControlCoreAudio(device));
+#else
+        VERBOSE(VB_IMPORTANT,
+                QString("Volume control device is set to an Core Audio device "
+                        "but Core Audio support is not compiled in!"));
+#endif
+    }
+    else if (api == "OSS" || api.isEmpty())
+    {
+#ifdef USING_OSS
+        result = QSharedPointer<VolumeControl>(new VolumeControlOSS(device));
+#else
+        VERBOSE(VB_IMPORTANT,
+                QString("Volume control device is set to an OSS device but OSS "
+                        "support is not compiled in!"));
+#endif
+    }
+    else if (api == "Endpoint" || api.isEmpty())
+    {
+#ifdef USING_MINGW
+        result = QSharedPointer<VolumeControl>(new VolumeControlEndpoint(device));
+#else
+        VERBOSE(VB_IMPORTANT,
+                QString("Volume control device is set to an Endpoint device "
+                        "but End Point support is not compiled in!"));
+#endif
+    }
+    else if (api == "Software" || api.isEmpty())
+    {
+        result = QSharedPointer<VolumeControl>(new VolumeControlSoftware(device));
+    }
+    else
+    {
+        VERBOSE(VB_IMPORTANT,
+               QString("Volume control device is set to an unknown API \"%1\"")
+               .arg(api));
+    }
+
+    return result;
+}
Index: libs/libmyth/volumecontrolalsa.cpp
===================================================================
--- libs/libmyth/volumecontrolalsa.cpp	(revision 0)
+++ libs/libmyth/volumecontrolalsa.cpp	(revision 0)
@@ -0,0 +1,358 @@
+#include "volumecontrolalsa.h"
+
+#include "mythverbose.h"
+#include <algorithm>
+
+QHash<QString, QString> VolumeControlALSA::Enumerate()
+{
+    QHash<QString, QString> result;
+
+    result.insert("ALSA:default", "Default ALSA device");
+
+    int card = -1;
+    while (snd_card_next(&card) >= 0 && card >= 0)
+    {
+        snd_ctl_t* handle;
+        if (!snd_ctl_open(&handle,
+                          QString("hw:%1").arg(card).toAscii().constData(), 0))
+        {
+            snd_ctl_card_info_t *info;
+            snd_ctl_card_info_alloca(&info);
+
+            if (!snd_ctl_card_info(handle, info))
+            {
+                result.insert(QString("ALSA:hw:%1")
+                              .arg(snd_ctl_card_info_get_id(info)),
+                              snd_ctl_card_info_get_name(info));
+            }
+            snd_ctl_close(handle);
+        }
+    }
+
+    return result;
+}
+
+VolumeControlALSA::VolumeControlALSA(QString device) :
+    m_MixerHandle(NULL),
+    m_MixerElement(NULL),
+    m_Range(1)
+{
+    if (snd_mixer_open(&m_MixerHandle, 0) < 0)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlALSA::VolumeControlALSA() - ERROR: "
+                        "opening mixer"));
+        return;
+    }
+
+    if (snd_mixer_attach(m_MixerHandle, device.toAscii().constData()) < 0)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlALSA::VolumeControlALSA() - ERROR: "
+                        "attaching mixer \"%1\"")
+                .arg(device));
+        return;
+    }
+
+    if (snd_mixer_selem_register(m_MixerHandle, NULL, NULL) < 0)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlALSA::VolumeControlALSA() - ERROR: "
+                        "registering mixer"));
+        return;
+    }
+
+    if (snd_mixer_load(m_MixerHandle) < 0)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlALSA::VolumeControlALSA() - ERROR: "
+                        "loading mixer"));
+        return;
+    }
+
+    for (m_MixerElement = snd_mixer_first_elem(m_MixerHandle);
+         m_MixerElement;
+         m_MixerElement = snd_mixer_elem_next(m_MixerElement))
+    {
+        if (!snd_mixer_selem_is_active(m_MixerElement))
+            continue;
+
+        if (!snd_mixer_selem_has_playback_volume(m_MixerElement))
+           continue;
+
+        VERBOSE(VB_AUDIO,
+                QString("VolumeControlALSA::VolumeControlALSA() - "
+                        "Using \"%1\"")
+                .arg(snd_mixer_selem_get_name(m_MixerElement)));
+
+        break;
+    }
+
+    if (!m_MixerElement)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlALSA::VolumeControlALSA() - ERROR: "
+                        "unable to find volume control"));
+        return;
+    }
+
+    long min, max;
+    snd_mixer_selem_get_playback_volume_range(m_MixerElement, &min, &max);
+    m_Range = std::min(std::max(1l, max - min), 100l);
+
+    snd_mixer_selem_set_playback_volume_range(m_MixerElement, 0, 100);
+
+    snd_mixer_elem_set_callback_private(m_MixerElement, this);
+    snd_mixer_elem_set_callback(m_MixerElement, &event);
+
+    if (!pipe(m_Exit))
+        start();
+}
+
+VolumeControlALSA::~VolumeControlALSA()
+{
+    if (isRunning())
+    {
+        write(m_Exit[1], "", 1);
+
+        wait();
+
+        close(m_Exit[0]);
+        close(m_Exit[1]);
+    }
+
+    if (m_MixerHandle)
+        snd_mixer_close(m_MixerHandle);
+}
+
+int VolumeControlALSA::volume() const
+{
+    long volume = 0;
+    if (m_MixerElement)
+    {
+        if (snd_mixer_selem_has_playback_switch(m_MixerElement) || !mute())
+            snd_mixer_selem_get_playback_volume(m_MixerElement,
+                                                SND_MIXER_SCHN_MONO,
+                                                &volume);
+        else
+            volume = m_Volume;
+    }
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::volume() = %1")
+            .arg(volume));
+
+    return volume;
+}
+
+void VolumeControlALSA::setVolume(int volume)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::setVolume(%1)")
+            .arg(volume));
+
+    volume = std::min(std::max(0, volume), 100);
+
+    if (mute() && volume > VolumeControlALSA::volume())
+        setMute(false);
+
+    if (m_MixerElement)
+        if (snd_mixer_selem_has_playback_switch(m_MixerElement) || !mute())
+            snd_mixer_selem_set_playback_volume_all(m_MixerElement,
+                                                    volume);
+
+    if (m_Volume.fetchAndStoreRelaxed(volume) != volume)
+        emit changedVolume(volume);
+}
+
+void VolumeControlALSA::increaseVolume()
+{
+    setVolume((volume() * m_Range + 100) / m_Range);
+}
+
+void VolumeControlALSA::decreaseVolume()
+{
+    setVolume((volume() * m_Range - 100) / m_Range);
+}
+
+bool VolumeControlALSA::mute() const
+{
+    union {
+        int _switch;
+        long int _volume;
+    } playback;
+    
+    playback._switch = true;
+
+    if (m_MixerElement)
+    {
+        if (snd_mixer_selem_has_playback_switch(m_MixerElement))
+            snd_mixer_selem_get_playback_switch(m_MixerElement,
+                                                SND_MIXER_SCHN_MONO,
+                                                &playback._switch);
+        else
+            snd_mixer_selem_get_playback_volume(m_MixerElement,
+                                                SND_MIXER_SCHN_MONO,
+                                                &playback._volume);
+    }
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::mute() = %1")
+            .arg(!playback._switch ? "mute" : "unmute"));
+
+    return !playback._switch;
+}
+
+void VolumeControlALSA::setMute(bool mute)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::setMute(%1)")
+            .arg(mute ? "mute" : "unmute"));
+
+    if (m_MixerElement)
+    {
+        if (snd_mixer_selem_has_playback_switch(m_MixerElement))
+            snd_mixer_selem_set_playback_switch_all(m_MixerElement,
+                                                    !mute);
+        else
+            snd_mixer_selem_set_playback_volume_all(m_MixerElement,
+                                                    mute ? 0 : static_cast<int>(m_Volume));
+    }
+
+    if (m_Mute.fetchAndStoreRelaxed(mute) != mute)
+        emit changedMute(mute);
+}
+
+// static
+int VolumeControlALSA::event(snd_mixer_elem_t* elem, unsigned int mask)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::event(%1)")
+            .arg(mask));
+
+    VolumeControlALSA* that =
+        static_cast<VolumeControlALSA*>(snd_mixer_elem_get_callback_private(elem));
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+    {
+        that->m_MixerElement = NULL;
+        return 0;
+    }
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE)
+    {
+        int volume = that->volume();
+        if (that->m_Volume.fetchAndStoreRelaxed(volume) != volume)
+            emit that->changedVolume(volume);
+
+        bool mute = that->mute();
+        if (that->m_Mute.fetchAndStoreRelaxed(mute) != mute)
+            emit that->changedMute(mute);
+    }
+
+    return 0;
+}
+
+void VolumeControlALSA::run()
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::run() - begin"));
+
+    m_Volume = volume();
+    m_Mute = mute();
+
+    int poll_descriptors_count = -1;
+    struct pollfd* poll_descriptors = NULL;
+
+    for(;;)
+    {
+        int poll_descriptors_count_new =
+            snd_mixer_poll_descriptors_count(m_MixerHandle);
+
+        if (poll_descriptors_count_new < 0)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("VolumeControlALSA::run() - ERROR: "
+                            "snd_mixer_poll_descriptors_count"));
+            break;
+        }
+
+        if (poll_descriptors_count != poll_descriptors_count_new)
+        {
+            delete(poll_descriptors);
+
+            poll_descriptors_count = poll_descriptors_count_new;
+            poll_descriptors = new struct pollfd[poll_descriptors_count + 1];
+
+            if (!poll_descriptors)
+            {
+                VERBOSE(VB_IMPORTANT,
+                        QString("VolumeControlALSA::run() - ERROR: "
+                                "malloc"));
+                break;
+            }
+        }
+
+        bzero(poll_descriptors,
+              poll_descriptors_count * sizeof (struct pollfd));
+
+        poll_descriptors->fd = m_Exit[0];
+        poll_descriptors->events = POLLIN;
+
+        if (snd_mixer_poll_descriptors(m_MixerHandle,
+                                       poll_descriptors + 1,
+                                       poll_descriptors_count) !=
+                                       poll_descriptors_count)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("VolumeControlALSA::run() - ERROR: "
+                            "snd_mixer_poll_descriptors"));
+            break;
+        }
+
+        if (poll(poll_descriptors, poll_descriptors_count + 1, -1) < 0)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("VolumeControlALSA::run() - ERROR: "
+                            "poll %1")
+                    .arg(errno));
+            break;
+        }
+
+        if (poll_descriptors->revents & POLLIN)
+            break;
+
+        unsigned short revents;
+        if (snd_mixer_poll_descriptors_revents(m_MixerHandle,
+                                               poll_descriptors + 1,
+                                               poll_descriptors_count,
+                                               &revents) < 0)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("VolumeControlALSA::run() - ERROR: "
+                            "snd_mixer_poll_descriptors_revents"));
+            break;
+        }
+
+        if (revents & (POLLNVAL | POLLERR))
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("VolumeControlALSA::run() - ERROR: "
+                            "snd_mixer_poll_descriptors_revents"));
+            break;
+        }
+
+        if (snd_mixer_handle_events(m_MixerHandle) < 0)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("VolumeControlALSA::run() - ERROR: "
+                            "snd_mixer_handle_events"));
+            break;
+        }
+    }
+
+    delete(poll_descriptors);
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlALSA::run() - end"));
+}
Index: libs/libmyth/audiooutputbase.h
===================================================================
--- libs/libmyth/audiooutputbase.h	(revision 22850)
+++ libs/libmyth/audiooutputbase.h	(working copy)
@@ -18,6 +18,7 @@
 #include "audiooutput.h"
 #include "samplerate.h"
 #include "mythverbose.h"
+#include "volumecontrolmanager.h"
 
 namespace soundtouch {
 class SoundTouch;
@@ -47,9 +48,6 @@
 
     virtual void Reset(void);
 
-    void SetSWVolume(int new_volume, bool save);
-    int GetSWVolume(void);
-
     // timecode is in milliseconds.
     virtual bool AddSamples(char *buffer, int samples, long long timecode);
     virtual bool AddSamples(char *buffers[], int samples, long long timecode);
@@ -113,8 +111,6 @@
     int audiolen(bool use_lock); // number of valid bytes in audio buffer
     int audiofree(bool use_lock); // number of free bytes in audio buffer
 
-    void UpdateVolume(void);
-
     void SetStretchFactorLocked(float factor);
 
     int GetBaseAudioTime()                    const { return audiotime;       }
@@ -148,7 +144,6 @@
     bool killaudio;
 
     bool pauseaudio, audio_actually_paused, was_paused;
-    bool set_initial_vol;
     bool buffer_output_data_for_use; //  used by AudioOutputNULL
 
     int configured_audio_channels;
@@ -181,7 +176,7 @@
     int surround_mode;
     bool allow_ac3_passthru;
     float old_audio_stretchfactor;
-    int volume;
+    QSharedPointer<VolumeControl> volume_control; ///< Volume Control interface
 
     bool blocking; // do AddSamples calls block?
 
Index: libs/libmyth/audiooutputalsa.cpp
===================================================================
--- libs/libmyth/audiooutputalsa.cpp	(revision 22850)
+++ libs/libmyth/audiooutputalsa.cpp	(working copy)
@@ -21,9 +21,7 @@
 AudioOutputALSA::AudioOutputALSA(const AudioSettings &settings) :
     AudioOutputBase(settings),
     pcm_handle(NULL),
-    numbadioctls(0),
-    mixer_handle(NULL),
-    mixer_control(QString::null)
+    numbadioctls(0)
 {
     // Set everything up
     Reconfigure(settings);
@@ -249,16 +247,12 @@
     // it really is
     audio_buffer_unused = soundcard_buffer_size - (fragment_size * 4);
 
-    if (internal_vol)
-        OpenMixer(set_initial_vol);
-    
     // Device opened successfully
     return true;
 }
 
 void AudioOutputALSA::CloseDevice()
 {
-    CloseMixer();
     if (pcm_handle != NULL)
     {
         snd_pcm_close(pcm_handle);
@@ -617,235 +611,3 @@
 
     return 0;
 }
-
-
-int AudioOutputALSA::GetVolumeChannel(int channel) const
-{
-    long actual_volume;
-
-    if (mixer_handle == NULL)
-        return 100;
-
-    QByteArray mix_ctl = mixer_control.toAscii();
-    snd_mixer_selem_id_t *sid;
-    snd_mixer_selem_id_alloca(&sid);
-    snd_mixer_selem_id_set_index(sid, 0);
-    snd_mixer_selem_id_set_name(sid, mix_ctl.constData());
-
-    snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer_handle, sid);
-    if (!elem)
-    {
-        VERBOSE(VB_IMPORTANT, QString("Mixer unable to find control %1")
-                .arg(mixer_control));
-        return 100;
-    }
-
-    snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
-    if (!snd_mixer_selem_has_playback_channel(elem, chan))
-    {
-        snd_mixer_selem_id_set_index(sid, channel);
-        if ((elem = snd_mixer_find_selem(mixer_handle, sid)) == NULL)
-        {
-            VERBOSE(VB_IMPORTANT, QString("Mixer unable to find control %1 %2")
-                    .arg(mixer_control).arg(channel));
-            return 100;
-        }
-    }
-
-    ALSAVolumeInfo vinfo = GetVolumeRange(elem);
-
-    snd_mixer_selem_get_playback_volume(
-        elem, (snd_mixer_selem_channel_id_t)channel, &actual_volume);
-
-    return vinfo.ToMythRange(actual_volume);
-}
-
-void AudioOutputALSA::SetVolumeChannel(int channel, int volume)
-{
-    SetCurrentVolume(mixer_control, channel, volume);
-}
-
-void AudioOutputALSA::SetCurrentVolume(QString control, int channel, int volume)
-{
-    VERBOSE(VB_AUDIO, QString("Setting %1 volume to %2")
-            .arg(control).arg(volume));
-
-    if (!mixer_handle)
-        return; // no mixer, nothing to do
-
-    QByteArray ctl = control.toAscii();
-    snd_mixer_selem_id_t *sid;
-    snd_mixer_selem_id_alloca(&sid);
-    snd_mixer_selem_id_set_index(sid, 0);
-    snd_mixer_selem_id_set_name(sid, ctl.constData());
-
-    snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer_handle, sid);
-    if (!elem)
-    {
-        VERBOSE(VB_IMPORTANT, QString("Mixer unable to find control %1")
-                .arg(control));
-        return;
-    }
-
-    snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
-    if (!snd_mixer_selem_has_playback_channel(elem, chan))
-    {
-        snd_mixer_selem_id_set_index(sid, channel);
-        if ((elem = snd_mixer_find_selem(mixer_handle, sid)) == NULL)
-        {
-            VERBOSE(VB_IMPORTANT,
-                    QString("mixer unable to find control %1 %2")
-                    .arg(control).arg(channel));
-            return;
-        }
-    }
-
-    ALSAVolumeInfo vinfo = GetVolumeRange(elem);
-
-    long set_vol = vinfo.ToALSARange(volume);
-
-    int err = snd_mixer_selem_set_playback_volume(elem, chan, set_vol);
-    if (err < 0)
-    {
-        VERBOSE(VB_IMPORTANT, QString("mixer set channel %1 err %2: %3")
-                .arg(channel).arg(err).arg(snd_strerror(err)));
-    }
-    else
-    {
-        VERBOSE(VB_AUDIO, QString("channel %1 vol set to %2")
-                .arg(channel).arg(set_vol));
-    }
-
-    if (snd_mixer_selem_has_playback_switch(elem))
-    {
-        int unmute = (0 != set_vol);
-        if (snd_mixer_selem_has_playback_switch_joined(elem))
-        {
-            // Only mute if all the channels should be muted.
-            for (int i = 0; i < audio_channels; i++)
-            {
-                if (0 != GetVolumeChannel(i))
-                    unmute = 1;
-            }
-        }
-
-        err = snd_mixer_selem_set_playback_switch(elem, chan, unmute);
-        if (err < 0)
-        {
-            VERBOSE(VB_IMPORTANT, LOC_ERR +
-                    QString("Mixer set playback switch %1 err %2: %3")
-                    .arg(channel).arg(err).arg(snd_strerror(err)));
-        }
-        else
-        {
-            VERBOSE(VB_AUDIO, LOC +
-                    QString("channel %1 playback switch set to %2")
-                    .arg(channel).arg(unmute));
-        }
-    }
-}
-
-void AudioOutputALSA::OpenMixer(bool setstartingvolume)
-{
-    int volume;
-
-    mixer_control = gContext->GetSetting("MixerControl", "PCM");
-
-    SetupMixer();
-
-    if (mixer_handle != NULL && setstartingvolume)
-    {
-        volume = gContext->GetNumSetting("MasterMixerVolume", 80);
-        SetCurrentVolume("Master", 0, volume);
-        SetCurrentVolume("Master", 1, volume);
-
-        volume = gContext->GetNumSetting("PCMMixerVolume", 80);
-        SetCurrentVolume("PCM", 0, volume);
-        SetCurrentVolume("PCM", 1, volume);
-    }
-}
-
-void AudioOutputALSA::CloseMixer(void)
-{
-    if (mixer_handle != NULL)
-        snd_mixer_close(mixer_handle);
-    mixer_handle = NULL;
-}
-
-void AudioOutputALSA::SetupMixer(void)
-{
-    int err;
-
-    QString alsadevice = gContext->GetSetting("MixerDevice", "default");
-    QString device = alsadevice.remove(QString("ALSA:"));
-
-    if (mixer_handle != NULL)
-        CloseMixer();
-
-    if (alsadevice.toLower() == "software")
-        return;
-
-    VERBOSE(VB_AUDIO, QString("Opening mixer %1").arg(device));
-
-    // TODO: This is opening card 0. Fix for case of multiple soundcards
-    if ((err = snd_mixer_open(&mixer_handle, 0)) < 0)
-    {
-        Warn(QString("Mixer device open error %1: %2")
-             .arg(err).arg(snd_strerror(err)));
-        mixer_handle = NULL;
-        return;
-    }
-
-    QByteArray dev = device.toAscii();
-    if ((err = snd_mixer_attach(mixer_handle, dev.constData())) < 0)
-    {
-        Warn(QString("Mixer attach error %1: %2"
-                     "\n\t\t\tCheck Mixer Name in Setup: '%3'")
-             .arg(err).arg(snd_strerror(err)).arg(device));
-        CloseMixer();
-        return;
-    }
-
-    if ((err = snd_mixer_selem_register(mixer_handle, NULL, NULL)) < 0)
-    {
-        Warn(QString("Mixer register error %1: %2")
-             .arg(err).arg(snd_strerror(err)));
-        CloseMixer();
-        return;
-    }
-
-    if ((err = snd_mixer_load(mixer_handle)) < 0)
-    {
-        Warn(QString("Mixer load error %1: %2")
-             .arg(err).arg(snd_strerror(err)));
-        CloseMixer();
-        return;
-    }
-}
-
-ALSAVolumeInfo AudioOutputALSA::GetVolumeRange(snd_mixer_elem_t *elem) const
-{
-    long volume_min, volume_max;
-
-    int err = snd_mixer_selem_get_playback_volume_range(
-        elem, &volume_min, &volume_max);
-
-    if (err < 0)
-    {
-        static bool first_time = true;
-        if (first_time)
-        {
-            VERBOSE(VB_IMPORTANT,
-                    "snd_mixer_selem_get_playback_volume_range()" + ENO);
-            first_time = false;
-        }
-    }
-
-    ALSAVolumeInfo vinfo(volume_min, volume_max);
-
-    VERBOSE(VB_AUDIO, QString("Volume range is %1 to %2, mult=%3")
-            .arg(vinfo.volume_min).arg(vinfo.volume_max)
-            .arg(vinfo.range_multiplier));
-
-    return vinfo;
-}
Index: libs/libmyth/volumebase.cpp
===================================================================
--- libs/libmyth/volumebase.cpp	(revision 22850)
+++ libs/libmyth/volumebase.cpp	(working copy)
@@ -1,135 +0,0 @@
-#include <cstdio>
-#include <cstdlib>
-
-#include <algorithm>
-using namespace std;
-
-#include <QString>
-
-#include "volumebase.h"
-#include "mythcontext.h"
-
-VolumeBase::VolumeBase() :
-    internal_vol(false), volume(80), 
-    current_mute_state(kMuteOff)
-{
-    swvol = swvol_setting =
-        (gContext->GetSetting("MixerDevice", "default").toLower() == "software");
-}
-
-bool VolumeBase::SWVolume(void)
-{
-    return swvol;
-}
-
-void VolumeBase::SWVolume(bool set)
-{
-    if (swvol_setting)
-        return;
-    swvol = set;
-}
-
-uint VolumeBase::GetCurrentVolume(void) const
-{
-    return volume;
-}
-
-void VolumeBase::SetCurrentVolume(int value)
-{
-    volume = max(min(value, 100), 0);
-    UpdateVolume();
-
-    QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
-    controlLabel += "MixerVolume";
-    gContext->SaveSetting(controlLabel, volume);    
-}
-
-void VolumeBase::AdjustCurrentVolume(int change)
-{
-    SetCurrentVolume(volume + change);
-}
-
-MuteState VolumeBase::SetMuteState(MuteState mstate)
-{
-    current_mute_state = mstate;
-    UpdateVolume();
-    return current_mute_state;
-}
-
-void VolumeBase::ToggleMute(void)
-{
-    bool is_muted = GetMuteState() == kMuteAll;
-    SetMuteState((is_muted) ? kMuteOff : kMuteAll);
-}
-
-MuteState VolumeBase::GetMuteState(void) const
-{
-    return current_mute_state;
-}
-
-MuteState VolumeBase::NextMuteState(MuteState cur)
-{
-    MuteState next = cur;
-
-    switch (cur)
-    {
-       case kMuteOff:
-           next = kMuteLeft;
-           break;
-       case kMuteLeft:
-           next = kMuteRight;
-           break;
-       case kMuteRight:
-           next = kMuteAll;
-           break;
-       case kMuteAll:
-           next = kMuteOff;
-           break;
-    }
-
-    return (next);
-}
-
-void VolumeBase::UpdateVolume(void)
-{
-    int new_volume = volume;
-    bool save = true;
-    if (current_mute_state == kMuteAll)
-    {
-        new_volume = 0;
-        save = false;
-    }
-
-    if (swvol)
-    {
-        SetSWVolume(new_volume, save);
-        return;
-    }
-    
-    // TODO: Avoid assumption that there are 2 channels!
-    for (int i = 0; i < 2; i++)
-    {
-        SetVolumeChannel(i, new_volume);
-    }
-    
-    // Individual channel muting is handled in GetAudioData,
-    // this code demonstrates the old method.
-    // if (current_mute_state == kMuteLeft)
-    // {
-    //     SetVolumeChannel(0, 0);
-    // }
-    // else if (current_mute_state == kMuteRight)
-    // {
-    //     SetVolumeChannel(1, 0);
-    // }
-}
-
-void VolumeBase::SyncVolume(void)
-{
-    // Read the volume from the audio driver and setup our internal state to match
-    if (swvol)
-        volume = GetSWVolume();
-    else
-        volume = GetVolumeChannel(0);
-}
-
Index: libs/libmyth/volumecontrolcoreaudio.h
===================================================================
--- libs/libmyth/volumecontrolcoreaudio.h	(revision 0)
+++ libs/libmyth/volumecontrolcoreaudio.h	(revision 0)
@@ -0,0 +1,40 @@
+#ifndef VOLUMECONTROLCOREAUDIO
+#define VOLUMECONTROLCOREAUDIO
+
+#include "volumecontrol.h"
+
+#include <QHash>
+#include <QAtomicInt>
+#include <CoreAudio/CoreAudio.h>
+
+
+class VolumeControlCoreAudio : public VolumeControl
+{
+public:
+    static QHash<QString, QString> Enumerate();
+
+public:
+    VolumeControlCoreAudio(QString device);
+    virtual ~VolumeControlCoreAudio();
+
+public:
+    int  volume() const;
+    void setVolume(int volume);
+    void increaseVolume();
+    void decreaseVolume();
+
+    bool mute() const;
+    void setMute(bool mute);
+
+protected:
+    static OSStatus event(AudioDeviceID deviceId, UInt32 channel,
+                          Boolean input, AudioDevicePropertyID property,
+                          void* context);
+
+private:
+    AudioDeviceID     m_DeviceId;       //< Core Audio device ID
+    QAtomicInt        m_Volume;         //< Volume state cache
+    QAtomicInt        m_Mute;           //< Mute state cache
+};
+
+#endif // VOLUMECONTROLCOREAUDIO
Index: libs/libmyth/volumecontrolsoftware.h
===================================================================
--- libs/libmyth/volumecontrolsoftware.h	(revision 0)
+++ libs/libmyth/volumecontrolsoftware.h	(revision 0)
@@ -0,0 +1,33 @@
+#ifndef VOLUMECONTROLSOFTWARE
+#define VOLUMECONTROLSOFTWARE
+
+#include "volumecontrol.h"
+
+#include <QHash>
+#include <QAtomicInt>
+
+
+class VolumeControlSoftware : public VolumeControl
+{
+public:
+    static QHash<QString, QString> Enumerate();
+
+public:
+    VolumeControlSoftware(QString device);
+    virtual ~VolumeControlSoftware();
+
+public:
+    int  volume() const;
+    void setVolume(int volume);
+    void increaseVolume();
+    void decreaseVolume();
+
+    bool mute() const;
+    void setMute(bool mute);
+
+private:
+    static QAtomicInt m_Volume;         //< Volume state cache
+    static QAtomicInt m_Mute;           //< Mute state cache
+};
+
+#endif // VOLUMECONTROLSOFTWARE
Index: libs/libmyth/volumecontrolcoreaudio.cpp
===================================================================
--- libs/libmyth/volumecontrolcoreaudio.cpp	(revision 0)
+++ libs/libmyth/volumecontrolcoreaudio.cpp	(revision 0)
@@ -0,0 +1,270 @@
+#include "volumecontrolcoreaudio.h"
+
+#include <CoreServices/CoreServices.h>
+#include <CoreAudio/CoreAudio.h>
+
+#include "mythverbose.h"
+#include <algorithm>
+
+QHash<QString, QString> VolumeControlCoreAudio::Enumerate()
+{
+    QHash<QString, QString> result;
+
+    result.insert("CoreAudio:default", "Default Core Audio device");
+
+    UInt32 size;
+    AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+                                 &size, NULL);	
+    UInt32 count = size / sizeof(AudioDeviceID);
+
+    AudioDeviceID* devices = new AudioDeviceID[count];
+    AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+                             &size, devices);
+
+    for (unsigned int index = 0; index < count; ++index)
+    {
+        bool output = false;
+
+        AudioDeviceGetPropertyInfo(devices[index], 0, 0,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &size, NULL);
+        if (!size)
+            continue;
+
+        AudioBufferList* buffers = static_cast<AudioBufferList*>(malloc(size));
+        AudioDeviceGetProperty(devices[index], 0, 0,
+                               kAudioDevicePropertyStreamConfiguration,
+                               &size, buffers);
+        for (UInt32 buffer = 0; buffer < buffers->mNumberBuffers; ++buffer)
+        {
+            if (buffers->mBuffers[buffer].mNumberChannels)
+            {
+                output = true;
+                break;
+            }
+        }
+
+        free(buffers);
+
+        if (!output)
+            continue;
+
+        QString uid;
+        CFStringRef uidcf;
+        size = sizeof(CFStringRef);
+        AudioDeviceGetProperty(devices[index], 0, 0,
+                               kAudioDevicePropertyDeviceUID,
+                               &size, &uidcf);
+        if (uidcf)
+        {
+            char buffer[256];
+            CFStringGetCString(uidcf, buffer, 256,
+                               CFStringGetFastestEncoding(uidcf));
+            uid = QString(buffer);
+            CFRelease(uidcf);
+        }
+
+        AudioDeviceGetPropertyInfo(devices[index], 0, 0,
+                                   kAudioDevicePropertyDeviceName,
+                                   &size, NULL);
+        if (!size)
+            continue;
+
+        char* name = static_cast<char*>(malloc(size));
+        AudioDeviceGetProperty(devices[index], 0, 0,
+                               kAudioDevicePropertyDeviceName,
+                               &size, name);
+
+        result.insert(QString("CoreAudio:") + uid, name);
+
+        free(name);
+    }
+
+    delete devices;
+
+    return result;
+}
+
+VolumeControlCoreAudio::VolumeControlCoreAudio(QString device) :
+    m_DeviceId(kAudioDeviceUnknown)
+{
+    if (device == QString("default"))
+    {
+        UInt32 size = sizeof(AudioDeviceID);
+        AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+                                 &size, &m_DeviceId);	    
+    }
+    else
+    {
+        CFStringRef cfString =
+            CFStringCreateWithCharacters(kCFAllocatorDefault,
+                reinterpret_cast<const UniChar*>(device.unicode()),
+                device.length());
+
+        AudioValueTranslation translateUidToId = {
+            mInputData:      &cfString,
+            mInputDataSize:  sizeof(CFStringRef),
+            mOutputData:     &m_DeviceId,
+            mOutputDataSize: sizeof(AudioDeviceID) 
+        };
+
+        UInt32 translateUidToIdSize = sizeof(AudioValueTranslation);
+        AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID,
+                                 &translateUidToIdSize,
+                                 &translateUidToId);
+        CFRelease(cfString);
+    }
+
+    AudioDeviceAddPropertyListener(m_DeviceId, 1, false,
+                                   kAudioDevicePropertyVolumeScalar,
+                                   &event,
+                                   this);
+
+    AudioDeviceAddPropertyListener(m_DeviceId, 1, false,
+                                   kAudioDevicePropertyVolumeDecibels,
+                                   &event,
+                                   this);
+
+    AudioDeviceAddPropertyListener(m_DeviceId, 0, false,
+                                   kAudioDevicePropertyMute,
+                                   &event,
+                                   this);
+
+    m_Volume = volume();
+    m_Mute = mute();
+}
+
+VolumeControlCoreAudio::~VolumeControlCoreAudio()
+{
+    AudioDeviceRemovePropertyListener(m_DeviceId, 1, false,
+                                      kAudioDevicePropertyVolumeScalar,
+                                      &event);
+
+    AudioDeviceRemovePropertyListener(m_DeviceId, 1, false,
+                                      kAudioDevicePropertyVolumeDecibels,
+                                      &event);
+
+    AudioDeviceRemovePropertyListener(m_DeviceId, 0, false,
+                                      kAudioDevicePropertyMute,
+                                      &event);
+}
+
+int VolumeControlCoreAudio::volume() const
+{
+    Float32 volume = 0;
+
+    if (!mute())
+    {
+        UInt32 size = sizeof(Float32);
+        AudioDeviceGetProperty(m_DeviceId, 1, false,
+                               kAudioDevicePropertyVolumeScalar,
+                               &size, &volume);
+    }
+    else
+        volume = static_cast<Float32>(m_Volume) / 100.0f;
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlCoreAudio::volume() = %1")
+            .arg(static_cast<int>(100.0f * volume)));
+
+    return static_cast<int>(100.0f * volume);
+}
+
+void VolumeControlCoreAudio::setVolume(int volume)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlCoreAudio::setVolume(%1)")
+            .arg(volume));
+
+    volume = std::min(std::max(0, volume), 100);
+
+    if (mute() && volume > VolumeControlCoreAudio::volume())
+        setMute(false);
+
+    if (!mute())
+    {
+        Float32 value = static_cast<Float32>(volume) / 100.0f;
+        AudioDeviceSetProperty(m_DeviceId, NULL, 1, false,
+                               kAudioDevicePropertyVolumeScalar,
+                               sizeof(UInt32), &value);
+    }
+
+    if (m_Volume.fetchAndStoreRelaxed(volume) != volume)
+        emit changedVolume(volume);
+}
+
+void VolumeControlCoreAudio::increaseVolume()
+{
+    setVolume(volume() + 10);
+}
+
+void VolumeControlCoreAudio::decreaseVolume()
+{
+    setVolume(volume() - 10);
+}
+
+bool VolumeControlCoreAudio::mute() const
+{
+    UInt32 mute = false;
+
+    UInt32 size = sizeof(UInt32);
+    AudioDeviceGetProperty(m_DeviceId, 0, false,
+                           kAudioDevicePropertyMute, &size, &mute);
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlCoreAudio::mute() = %1")
+            .arg(mute ? "mute" : "unmute"));
+
+    return mute;
+}
+
+void VolumeControlCoreAudio::setMute(bool mute)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlCoreAudio::setMute(%1)")
+            .arg(mute ? "mute" : "unmute"));
+
+    if (VolumeControlCoreAudio::mute())
+    {
+        Float32 value = static_cast<Float32>(m_Volume) / 100.0f;
+        AudioDeviceSetProperty(m_DeviceId, NULL, 1, false,
+                               kAudioDevicePropertyVolumeScalar,
+                               sizeof(UInt32), &value);    
+    }
+
+    UInt32 value = mute;
+    AudioDeviceSetProperty(m_DeviceId, NULL, 0, false,
+                           kAudioDevicePropertyMute, sizeof(UInt32), &value);
+
+    if (m_Mute.fetchAndStoreRelaxed(mute) != mute)
+        emit changedMute(mute);
+}
+
+// static
+OSStatus VolumeControlCoreAudio::event(AudioDeviceID deviceId, UInt32 channel,
+                                       Boolean input,
+                                       AudioDevicePropertyID property,
+                                       void* context)
+{
+    VolumeControlCoreAudio* that =
+        static_cast<VolumeControlCoreAudio*>(context);
+
+    if (deviceId == that->m_DeviceId && !input)
+        switch(property)
+       	{
+        case kAudioDevicePropertyVolumeScalar:
+        case kAudioDevicePropertyVolumeDecibels:
+            int volume = that->volume();
+            if (that->m_Volume.fetchAndStoreRelaxed(volume) != volume)
+                emit that->changedVolume(volume);
+            break; 
+
+        case kAudioDevicePropertyMute:
+            bool mute = that->mute();
+            if (that->m_Mute.fetchAndStoreRelaxed(mute) != mute)
+                emit that->changedMute(mute);
+            break; 
+        }
+
+    return noErr;
+}
Index: libs/libmyth/volumebase.h
===================================================================
--- libs/libmyth/volumebase.h	(revision 22850)
+++ libs/libmyth/volumebase.h	(working copy)
@@ -1,52 +0,0 @@
-#ifndef __VOLUME_BASE__
-#define __VOLUME_BASE__
-
-#include "mythexp.h"
-
-typedef enum {
-    kMuteOff = 0,
-    kMuteLeft,
-    kMuteRight,
-    kMuteAll,
-} MuteState;
-
-class MPUBLIC VolumeBase
-{
-  public:
-    VolumeBase();    
-    virtual ~VolumeBase() {};
-
-    void SWVolume(bool set);
-    bool SWVolume(void);
-    virtual uint GetCurrentVolume(void) const;
-    virtual void SetCurrentVolume(int value);
-    virtual void AdjustCurrentVolume(int change);
-    virtual void ToggleMute(void);
-
-    virtual MuteState GetMuteState(void) const;
-    virtual MuteState SetMuteState(MuteState);
-
-    static MuteState NextMuteState(MuteState);
-
-  protected:
-
-    virtual int GetVolumeChannel(int channel) const = 0; // Returns 0-100
-    virtual void SetVolumeChannel(int channel, int volume) = 0; // range 0-100 for vol
-    virtual void SetSWVolume(int new_volume, bool save) = 0;
-    virtual int GetSWVolume(void) = 0;
-
-    void UpdateVolume(void);
-    void SyncVolume(void);
-
-    bool internal_vol;
-
- private:
-    
-    int volume;
-    MuteState current_mute_state;
-    bool swvol;
-    bool swvol_setting;
-
-};
-
-#endif // __VOLUME_BASE__
Index: libs/libmyth/volumecontrol.h
===================================================================
--- libs/libmyth/volumecontrol.h	(revision 0)
+++ libs/libmyth/volumecontrol.h	(revision 0)
@@ -0,0 +1,25 @@
+#ifndef VOLUMECONTROL
+#define VOLUMECONTROL
+
+#include <QObject>
+#include <QString>
+
+class VolumeControl : public QObject
+{
+    Q_OBJECT
+
+public:
+    virtual int  volume() const = 0;
+    virtual void setVolume(int volume) = 0;
+    virtual void increaseVolume() = 0;
+    virtual void decreaseVolume() = 0;
+
+    virtual bool mute() const = 0;
+    virtual void setMute(bool mute) = 0;
+
+signals:
+    void changedVolume(int volume);
+    void changedMute(bool mute);
+};
+
+#endif // VOLUMECONTROL
Index: libs/libmyth/volumecontrolsoftware.cpp
===================================================================
--- libs/libmyth/volumecontrolsoftware.cpp	(revision 0)
+++ libs/libmyth/volumecontrolsoftware.cpp	(revision 0)
@@ -0,0 +1,89 @@
+#include "volumecontrolsoftware.h"
+
+#include "mythcontext.h"
+#include "mythverbose.h"
+#include <algorithm>
+
+QHash<QString, QString> VolumeControlSoftware::Enumerate()
+{
+    QHash<QString, QString> result;
+
+    result.insert("Software:", "Software Volume Processing");
+
+    return result;
+}
+
+VolumeControlSoftware::VolumeControlSoftware(QString device)
+{
+    int volume = gContext->GetNumSetting("SoftwareVolume", 80);
+    m_Volume = std::min(std::max(0, volume), 100);
+    m_Mute = gContext->GetNumSetting("SoftwareMute", false) ? true : false;
+}
+
+VolumeControlSoftware::~VolumeControlSoftware()
+{
+
+}
+
+int VolumeControlSoftware::volume() const
+{
+    int volume = m_Volume;
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlSoftware::volume() = %1")
+            .arg(volume));
+
+    return volume;
+}
+
+void VolumeControlSoftware::setVolume(int volume)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlSoftware::setVolume(%1)")
+            .arg(volume));
+
+    volume = std::min(std::max(0, volume), 100);
+
+    if (m_Volume.fetchAndStoreRelaxed(volume) != volume)
+    {
+        gContext->SaveSetting("SoftwareVolume", volume);
+        emit changedVolume(volume);
+    }
+}
+
+void VolumeControlSoftware::increaseVolume()
+{
+    setVolume(volume() + 1);
+}
+
+void VolumeControlSoftware::decreaseVolume()
+{
+    setVolume(volume() - 1);
+}
+
+bool VolumeControlSoftware::mute() const
+{
+    bool mute = m_Mute ? true : false;
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlSoftware::mute() = %1")
+            .arg(mute ? "mute" : "unmute"));
+
+    return mute;
+}
+
+void VolumeControlSoftware::setMute(bool mute)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlSoftware::setMute(%1)")
+            .arg(mute ? "mute" : "unmute"));
+
+    if (m_Mute.fetchAndStoreRelaxed(mute) != mute)
+    {
+        gContext->SaveSetting("SoftwareMute", mute);
+        emit changedMute(mute);
+    }
+}
+
+QAtomicInt VolumeControlSoftware::m_Volume;
+QAtomicInt VolumeControlSoftware::m_Mute;
Index: libs/libmyth/audiooutputpulse.h
===================================================================
--- libs/libmyth/audiooutputpulse.h	(revision 22850)
+++ libs/libmyth/audiooutputpulse.h	(working copy)
@@ -30,8 +30,6 @@
     AudioOutputPulseAudio(const AudioSettings &settings);
    ~AudioOutputPulseAudio();
 
-    int GetVolumeChannel(int channel) const;
-    void SetVolumeChannel(int channel, int volume);
     void Pause(bool paused);
     void Reset(void);
     void Drain(void);
Index: libs/libmyth/volumecontroloss.h
===================================================================
--- libs/libmyth/volumecontroloss.h	(revision 0)
+++ libs/libmyth/volumecontroloss.h	(revision 0)
@@ -0,0 +1,47 @@
+#ifndef VOLUMECONTROLOSS
+#define VOLUMECONTROLOSS
+
+#include "volumecontrol.h"
+
+#include <QHash>
+#include <QThread>
+#include <QAtomicInt>
+
+
+class VolumeControlOSS : public VolumeControl, protected QThread
+{
+public:
+    static QHash<QString, QString> Enumerate();
+
+public:
+    VolumeControlOSS(QString device);
+    virtual ~VolumeControlOSS();
+
+public:
+    int  volume() const;
+    void setVolume(int volume);
+    void increaseVolume();
+    void decreaseVolume();
+
+    bool mute() const;
+    void setMute(bool mute);
+
+protected:
+    virtual void run();
+
+private:
+    int               m_Mixer;              //< OSS mixer file descriptor
+    int               m_MixerDev;           //< OSS mixer device ID
+    int               m_VolumeCtrl;         //< OSS control ID for volume
+    int               m_VolumeMax;          //< OSS maximum for volume
+    int               m_VolumeType;         //< OSS control type for volume
+    int               m_VolumeTimestamp;    //< OSS timestamp for volume
+    int               m_MuteCtrl;           //< OSS control ID for mute
+    int               m_MuteTimestamp;      //< OSS timestamp for mute
+    int               m_Range;              //< Range of volume control
+    volatile bool     m_Exit;               //< Flag to notify thread to exit
+    QAtomicInt        m_Volume;             //< Volume state cache
+    QAtomicInt        m_Mute;               //< Mute state cache
+};
+
+#endif // VOLUMECONTROLOSS
Index: libs/libmyth/volumecontrolalsa.h
===================================================================
--- libs/libmyth/volumecontrolalsa.h	(revision 0)
+++ libs/libmyth/volumecontrolalsa.h	(revision 0)
@@ -0,0 +1,44 @@
+#ifndef VOLUMECONTROLALSA
+#define VOLUMECONTROLALSA
+
+#include "volumecontrol.h"
+
+#include <QHash>
+#include <QThread>
+#include <QAtomicInt>
+#include <alsa/asoundlib.h>
+
+
+class VolumeControlALSA : public VolumeControl, protected QThread
+{
+public:
+    static QHash<QString, QString> Enumerate();
+
+public:
+    VolumeControlALSA(QString device);
+    virtual ~VolumeControlALSA();
+
+public:
+    int  volume() const;
+    void setVolume(int volume);
+    void increaseVolume();
+    void decreaseVolume();
+
+    bool mute() const;
+    void setMute(bool mute);
+
+protected:
+    virtual void run();
+
+    static int event(snd_mixer_elem_t* elem, unsigned int mask);
+
+private:
+    snd_mixer_t*      m_MixerHandle;    //< ALSA mixer handle
+    snd_mixer_elem_t* m_MixerElement;   //< ALSA mixer element handle
+    int               m_Range;          //< Range of mixer element
+    int               m_Exit[2];        //< Pipe file descriptors
+    QAtomicInt        m_Volume;         //< Volume state cache
+    QAtomicInt        m_Mute;           //< Mute state cache
+};
+
+#endif // VOLUMECONTROLALSA
Index: libs/libmyth/audiooutputpulse.cpp
===================================================================
--- libs/libmyth/audiooutputpulse.cpp	(revision 22850)
+++ libs/libmyth/audiooutputpulse.cpp	(working copy)
@@ -268,43 +268,6 @@
     return writable;
 }
 
-int AudioOutputPulseAudio::GetVolumeChannel(int channel) const
-{
-    return (float)volume_control.values[channel]
-        / (float)PA_VOLUME_NORM * 100.0f;
-}
-
-void AudioOutputPulseAudio::SetVolumeChannel(int channel, int volume)
-{
-    QString fn_log_tag = "SetVolumeChannel, ";
-    if (channel < 0 || channel > PULSE_MAX_CHANNELS || volume < 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + fn_log_tag +
-                QString("bad volume params, channel %1, volume %2")
-                .arg(channel).arg(volume));
-        return;
-    }
-    volume_control.values[channel] =
-        (float)volume / 100.0f * (float)PA_VOLUME_NORM;
-    volume = min(100, volume);
-    volume = max(0, volume);
-    uint32_t sink_index = pa_stream_get_device_index(pstream);
-    pa_threaded_mainloop_lock(mainloop);
-    pa_operation *op =
-        pa_context_set_sink_volume_by_index(pcontext, sink_index,
-                                            &volume_control,
-                                            OpCompletionCallback, this);
-    pa_threaded_mainloop_unlock(mainloop);
-    if (op)
-        pa_operation_unref(op);
-    else
-        VERBOSE(VB_IMPORTANT, LOC_ERR + fn_log_tag +
-                QString("set sink volume operation failed, sink: %1, "
-                        "error: %2 ")
-                .arg(sink_index)
-                .arg(pa_strerror(pa_context_errno(pcontext))));
-}
-
 void AudioOutputPulseAudio::Pause(bool paused)
 {
     pa_operation *op;
@@ -545,14 +508,7 @@
     pa_stream_set_overflow_callback(pstream, BufferFlowCallback, (char*)"over");
     pa_stream_set_underflow_callback(pstream, BufferFlowCallback,
                                      (char*)"under");
-    if (set_initial_vol)
-    {
-        int volume = gContext->GetNumSetting("MasterMixerVolume", 80);
-        pa_cvolume_set(&volume_control, audio_channels,
-                       (float)volume * (float)PA_VOLUME_NORM / 100.0f);
-    }
-    else
-        pa_cvolume_reset(&volume_control, audio_channels);
+    pa_cvolume_reset(&volume_control, audio_channels);
 
     // set myth sizes and pa buffer metrics
     fragment_size = (float)sample_rate * 0.020f * // 20msec
Index: libs/libmyth/volumecontrolendpoint.cpp
===================================================================
--- libs/libmyth/volumecontrolendpoint.cpp	(revision 0)
+++ libs/libmyth/volumecontrolendpoint.cpp	(revision 0)
@@ -0,0 +1,347 @@
+#include "volumecontrolendpoint.h"
+
+#include "mythverbose.h"
+#include <algorithm>
+#include <math.h>
+
+typedef struct _BYTE_BLOB
+    {
+    unsigned long clSize;
+    byte abData[ 1 ];
+    } 	BYTE_BLOB;
+
+typedef struct _tagpropertykey
+    {
+    GUID fmtid;
+    DWORD pid;
+    } 	PROPERTYKEY;
+
+#define REFPROPVARIANT const PROPVARIANT &
+
+inline void PropVariantInit ( PROPVARIANT * pvar )
+{
+    memset ( pvar, 0, sizeof(PROPVARIANT) );
+}
+WINOLEAPI PropVariantClear ( PROPVARIANT * pvar );
+
+EXTERN_C const CLSID CLSID_MMDeviceEnumerator = {0xBCDE0395,0xE52F,0x467C,{0x8E,0x3D,0xC4,0x57,0x92,0x91,0x69,0x2E}};
+EXTERN_C const IID IID_IMMDeviceEnumerator = {0xA95664D2,0x9614,0x4F35,{0xA7,0x46,0xDE,0x8D,0xB6,0x36,0x17,0xE6}};
+EXTERN_C const IID IID_IAudioEndpointVolume = {0x5CDF2C82,0x841E,0x4546,{0x97,0x22,0x0C,0xF7,0x40,0x78,0x22,0x9A}};
+EXTERN_C const IID IID_IAudioEndpointVolumeCallback = {0x657804FA,0xD6AD,0x4496,{0x8A,0x60,0x35,0x27,0x52,0xAF,0x4F,0x89}};
+EXTERN_C const IID IID_IUnknown = {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}};
+#define INITGUID
+#include <Unknwn.h>
+#include <mmdeviceapi.h>
+#include <FunctionDiscoveryKeys_devpkey.h>
+
+
+QHash<QString, QString> VolumeControlEndpoint::Enumerate()
+{
+    QHash<QString, QString> result;
+
+    HRESULT hr = S_OK;
+    IMMDeviceEnumerator* pEnumerator = NULL;
+    IMMDeviceCollection* pCollection = NULL;
+
+    CoInitialize(NULL);
+
+    // Get enumerator for audio endpoint devices.
+    hr = CoCreateInstance(CLSID_MMDeviceEnumerator,
+                          NULL, CLSCTX_INPROC_SERVER,
+                          IID_IMMDeviceEnumerator,
+                          (void**)&pEnumerator);
+    if (!SUCCEEDED(hr) || !pEnumerator)
+    {
+        CoUninitialize();
+        return result;
+    }
+
+    result.insert("Endpoint:default", "Default End Point device");
+
+    hr = pEnumerator->EnumAudioEndpoints(eRender,
+                                         DEVICE_STATE_ACTIVE,
+                                         &pCollection);
+    if (!SUCCEEDED(hr) || !pCollection)
+    {
+        pEnumerator->Release();
+        CoUninitialize();
+        return result;
+    }
+
+    UINT devices = 0;
+    hr = pCollection->GetCount(&devices);
+    if (!SUCCEEDED(hr))
+    {
+        pCollection->Release();
+        pEnumerator->Release();
+        CoUninitialize();
+        return result;
+    }
+
+    for (UINT device = 0; device < devices; ++device)
+    {
+        IMMDevice* pDevice = NULL;
+        hr = pCollection->Item(device, &pDevice);
+        if (!SUCCEEDED(hr) || !pDevice)
+            continue;
+
+        LPWSTR pwszID = NULL;
+        hr = pDevice->GetId(&pwszID);
+        if (!SUCCEEDED(hr))
+        {
+            pDevice->Release();
+            continue;
+        }
+        QString uid(reinterpret_cast<const QChar*>(pwszID), wcslen(pwszID));
+        CoTaskMemFree(pwszID);
+
+        IPropertyStore *pProps = NULL;
+        hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
+        if (!SUCCEEDED(hr) || !pProps)
+        {
+            pDevice->Release();
+            continue;
+        }
+
+        PROPVARIANT varName;
+        PropVariantInit(&varName);
+
+        hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName);
+        if (!SUCCEEDED(hr))
+        {
+            PropVariantClear(&varName);
+            pProps->Release();
+            pDevice->Release();
+            continue;
+        }
+        QString name(reinterpret_cast<const QChar*>(varName.pwszVal),
+                     wcslen(varName.pwszVal));
+        PropVariantClear(&varName);
+
+        pProps->Release();
+        pDevice->Release();
+
+        result.insert(QString("Endpoint:") + uid, name);
+    }
+
+    pCollection->Release();
+    pEnumerator->Release();
+
+    CoUninitialize();
+
+    return result;
+}
+
+VolumeControlEndpoint::VolumeControlEndpoint(QString device) :
+    m_EndpointVolume(NULL)
+{
+    HRESULT hr = S_OK;
+    IMMDeviceEnumerator* pEnumerator = NULL;
+    IMMDevice* pDevice = NULL;
+
+    CoInitialize(NULL);
+
+    // Get enumerator for audio endpoint devices.
+    hr = CoCreateInstance(CLSID_MMDeviceEnumerator,
+                          NULL, CLSCTX_INPROC_SERVER,
+                          IID_IMMDeviceEnumerator,
+                          (void**)&pEnumerator);
+    if (!SUCCEEDED(hr) || !pEnumerator)
+        return;
+
+    if (device == QString("default"))
+        hr = pEnumerator->GetDefaultAudioEndpoint(eRender,
+                                                  eMultimedia,
+                                                  &pDevice);
+    else
+        hr = pEnumerator->GetDevice((LPCWSTR)device.constData(), &pDevice);
+
+    if (!SUCCEEDED(hr) || !pDevice)
+    {
+        pEnumerator->Release();
+        return;
+    }
+
+    hr = pDevice->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL,
+                           NULL, (void**)&m_EndpointVolume);
+    if (!SUCCEEDED(hr) || !m_EndpointVolume)
+    {
+        pDevice->Release();
+        pEnumerator->Release();
+        return;
+    }
+
+    pDevice->Release();
+    pEnumerator->Release();
+
+    hr = m_EndpointVolume->RegisterControlChangeNotify(this);
+    if (!SUCCEEDED(hr))
+        return;
+
+    UINT step, count;
+    hr = m_EndpointVolume->GetVolumeStepInfo(&step, &count);
+    if (!SUCCEEDED(hr))
+        return;
+
+    m_Range = std::min(std::max(1U, count - 1), 100U);
+
+    m_Volume = volume();
+    m_Mute = mute();
+}
+
+VolumeControlEndpoint::~VolumeControlEndpoint()
+{
+    if (m_EndpointVolume)
+    {
+        m_EndpointVolume->UnregisterControlChangeNotify(this);
+        m_EndpointVolume->Release();
+    }
+
+    CoUninitialize();
+}
+
+int VolumeControlEndpoint::volume() const
+{
+    int volume = 0;
+
+    if (!mute())
+    {
+		if (m_EndpointVolume)
+        {
+            float level;
+            m_EndpointVolume->GetMasterVolumeLevelScalar(&level);
+            volume = static_cast<int>(round(level * 100.0f));
+		}
+    }
+    else
+        volume = m_Volume;
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlEndpoint::volume() = %1")
+            .arg(volume));
+
+    return volume;
+}
+
+void VolumeControlEndpoint::setVolume(int volume)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlEndpoint::setVolume(%1)")
+            .arg(volume));
+
+    volume = std::min(std::max(0, volume), 100);
+
+    if (mute() && volume > VolumeControlEndpoint::volume())
+        setMute(false);
+
+    if (!mute() && m_EndpointVolume)
+        m_EndpointVolume->SetMasterVolumeLevelScalar(
+            round(static_cast<float>(volume * m_Range) / 100.0f) /
+                  static_cast<float>(m_Range), NULL);
+
+    if (m_Volume.fetchAndStoreRelaxed(volume) != volume)
+        emit changedVolume(volume);
+}
+
+void VolumeControlEndpoint::increaseVolume()
+{
+    int volume = VolumeControlEndpoint::volume();
+    float level = (round(static_cast<float>(volume * m_Range) / 100.0f) + 1.0f)
+                  / static_cast<float>(m_Range);
+	volume = static_cast<int>(round(level * 100.0f));
+    setVolume(volume);
+}
+
+void VolumeControlEndpoint::decreaseVolume()
+{
+    int volume = VolumeControlEndpoint::volume();
+    float level = (round(static_cast<float>(volume * m_Range) / 100.0f) - 1.0f)
+                  / static_cast<float>(m_Range);
+	volume = static_cast<int>(round(level * 100.0f));
+    setVolume(volume);
+}
+
+bool VolumeControlEndpoint::mute() const
+{
+    BOOL mute = FALSE;
+
+    if (m_EndpointVolume)
+        m_EndpointVolume->GetMute(&mute);
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlEndpoint::mute() = %1")
+            .arg(mute ? "mute" : "unmute"));
+
+    return mute;
+}
+
+void VolumeControlEndpoint::setMute(bool mute)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlEndpoint::setMute(%1)")
+            .arg(mute ? "mute" : "unmute"));
+
+    if (m_EndpointVolume)
+    {
+        if (VolumeControlEndpoint::mute())
+            m_EndpointVolume->SetMasterVolumeLevelScalar(
+                static_cast<float>(m_Volume) / 100.0f, NULL);
+
+        m_EndpointVolume->SetMute(mute, NULL);
+    }
+
+    if (m_Mute.fetchAndStoreRelaxed(mute) != mute)
+        emit changedMute(mute);
+}
+
+ULONG STDMETHODCALLTYPE VolumeControlEndpoint::AddRef()
+{
+    return 1;
+}
+
+ULONG STDMETHODCALLTYPE VolumeControlEndpoint::Release()
+{
+    return 0;
+}
+
+HRESULT STDMETHODCALLTYPE
+VolumeControlEndpoint::QueryInterface(REFIID riid, VOID **ppvInterface)
+{
+    if (IID_IUnknown == riid)
+    {
+        AddRef();
+        *ppvInterface = (IUnknown*)this;
+    }
+    else if (IID_IAudioEndpointVolumeCallback == riid)
+    {
+        AddRef();
+        *ppvInterface = (IAudioEndpointVolumeCallback*)this;
+    }
+    else
+    {
+        *ppvInterface = NULL;
+        return E_NOINTERFACE;
+    }
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+VolumeControlEndpoint::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify)
+{
+    HRESULT hr = E_INVALIDARG;
+
+    if (pNotify)
+    {
+        int volume = VolumeControlEndpoint::volume();
+        if (m_Volume.fetchAndStoreRelaxed(volume) != volume)
+            emit changedVolume(volume);
+
+        bool mute = VolumeControlEndpoint::mute();
+        if (m_Mute.fetchAndStoreRelaxed(mute) != mute)
+            emit changedMute(mute);
+
+        hr = S_OK;
+    }
+
+    return hr;
+}
Index: libs/libmyth/audiooutputjack.h
===================================================================
--- libs/libmyth/audiooutputjack.h	(revision 22850)
+++ libs/libmyth/audiooutputjack.h	(working copy)
@@ -11,10 +11,6 @@
     AudioOutputJACK(const AudioSettings &settings);
     virtual ~AudioOutputJACK();
    
-    // Volume control
-    virtual int GetVolumeChannel(int channel) const; // Returns 0-100
-    virtual void SetVolumeChannel(int channel, int volume); // range 0-100 for vol
- 
   protected:
 
     // You need to implement the following functions
@@ -27,8 +23,6 @@
 
   private:
 
-    void VolumeInit(void);
-
     int audioid;
 
 };
Index: libs/libmyth/audiosettings.cpp
===================================================================
--- libs/libmyth/audiosettings.cpp	(revision 22850)
+++ libs/libmyth/audiosettings.cpp	(working copy)
@@ -15,7 +15,6 @@
     channels(-1),
     codec(0),
     samplerate(-1),
-    set_initial_vol(false),
     use_passthru(false),
     source(AUDIOOUTPUT_UNKNOWN),
     upmixer(0)
@@ -29,7 +28,6 @@
     channels(other.channels),
     codec(other.codec),
     samplerate(other.samplerate),
-    set_initial_vol(other.set_initial_vol),
     use_passthru(other.use_passthru),
     source(other.source),
     upmixer(other.upmixer)
@@ -44,7 +42,6 @@
     int audio_codec,
     int audio_samplerate,
     AudioOutputSource audio_source,
-    bool audio_set_initial_vol,
     bool audio_use_passthru,
     int upmixer_startup) :
     main_device(audio_main_device),
@@ -53,7 +50,6 @@
     channels(audio_channels),
     codec(audio_codec),
     samplerate(audio_samplerate),
-    set_initial_vol(audio_set_initial_vol),
     use_passthru(audio_use_passthru),
     source(audio_source),
     upmixer(upmixer_startup)
@@ -73,7 +69,6 @@
     channels(audio_channels),
     codec(audio_codec),
     samplerate(audio_samplerate),
-    set_initial_vol(false),
     use_passthru(audio_use_passthru),
     source(AUDIOOUTPUT_UNKNOWN),
     upmixer(upmixer_startup)
Index: libs/libmyth/audiooutputca.cpp
===================================================================
--- libs/libmyth/audiooutputca.cpp	(revision 22850)
+++ libs/libmyth/audiooutputca.cpp	(working copy)
@@ -244,13 +244,6 @@
         return false;
     }
 
-    if (internal_vol && set_initial_vol)
-    {
-        QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
-        controlLabel += "MixerVolume";
-        SetCurrentVolume(gContext->GetNumSetting(controlLabel, 80));
-    }
-
     return true;
 }
 
@@ -399,28 +392,6 @@
     return noErr;
 }
 
-int AudioOutputCA::GetVolumeChannel(int channel) const
-{
-    // FIXME: this only returns global volume
-    (void)channel;
-    Float32 volume;
-
-    if (!AudioUnitGetParameter(d->mOutputUnit,
-                               kHALOutputParam_Volume,
-                               kAudioUnitScope_Global, 0, &volume))
-        return (int)lroundf(volume * 100.0);
-
-    return 0;    // error case
-}
-
-void AudioOutputCA::SetVolumeChannel(int channel, int volume)
-{
-    // FIXME: this only sets global volume
-    (void)channel;
-     AudioUnitSetParameter(d->mOutputUnit, kHALOutputParam_Volume,
-                           kAudioUnitScope_Global, 0, (volume * 0.01), 0);
-}
-
 // IOProc style callback for SPDIF audio output
 static OSStatus RenderCallbackSPDIF(AudioDeviceID        inDevice,
                                     const AudioTimeStamp *inNow, 
Index: libs/libmyth/audiooutputwin.h
===================================================================
--- libs/libmyth/audiooutputwin.h	(revision 22850)
+++ libs/libmyth/audiooutputwin.h	(working copy)
@@ -13,10 +13,6 @@
     AudioOutputWin(const AudioSettings &settings);
     virtual ~AudioOutputWin();
 
-    // Volume control
-    virtual int  GetVolumeChannel(int channel) const;
-    virtual void SetVolumeChannel(int channel, int volume);
-
   protected:
     virtual bool OpenDevice(void);
     virtual void CloseDevice(void);
Index: libs/libmyth/audiooutputjack.cpp
===================================================================
--- libs/libmyth/audiooutputjack.cpp	(revision 22850)
+++ libs/libmyth/audiooutputjack.cpp	(working copy)
@@ -148,10 +148,6 @@
     audio_buffer_unused = JACK_GetBytesFreeSpace(audioid);
     JACK_SetPosition(audioid, BYTES, 0);
 
-    // Setup volume control
-    if (internal_vol)
-        VolumeInit();
-
     // Device opened successfully
     return true; 
 }
@@ -206,30 +202,3 @@
 
     return space;
 }
-
-void AudioOutputJACK::VolumeInit(void)
-{
-    int volume = 100;
-    if (set_initial_vol)
-        volume = gContext->GetNumSetting("MasterMixerVolume", 80);
-
-    JACK_SetAllVolume(audioid, volume);
-}
-
-int AudioOutputJACK::GetVolumeChannel(int channel) const
-{
-    unsigned int vol = 0;
-    
-    if (!internal_vol)
-        return 100;
-
-    JACK_GetVolumeForChannel(audioid, channel, &vol);
-    return vol;
-}
-
-void AudioOutputJACK::SetVolumeChannel(int channel, int volume)
-{
-    if (internal_vol)
-        JACK_SetVolumeForChannel(audioid, channel, volume);
-}
-
Index: libs/libmyth/audiooutputoss.h
===================================================================
--- libs/libmyth/audiooutputoss.h	(revision 22850)
+++ libs/libmyth/audiooutputoss.h	(working copy)
@@ -12,10 +12,6 @@
     AudioOutputOSS(const AudioSettings &settings);
     virtual ~AudioOutputOSS();
 
-    // Volume control
-    virtual int GetVolumeChannel(int channel) const;
-    virtual void SetVolumeChannel(int channel, int volume);
-
   protected:
     // You need to implement the following functions
     virtual bool OpenDevice(void);
@@ -26,17 +22,10 @@
     vector<int> GetSupportedRates(void);
 
   private:
-    void VolumeInit(void);
-    void VolumeCleanup(void);
-    
     void SetFragSize(void);
     
     int audiofd;
     mutable int numbadioctls;
-
-    // Volume related
-    int mixerfd;
-    int control;
 };
 
 #endif
Index: libs/libmyth/audiooutputbase.cpp
===================================================================
--- libs/libmyth/audiooutputbase.cpp	(revision 22850)
+++ libs/libmyth/audiooutputbase.cpp	(working copy)
@@ -37,7 +37,6 @@
     pauseaudio(false),          audio_actually_paused(false),
     was_paused(false),
 
-    set_initial_vol(settings.set_initial_vol),
     buffer_output_data_for_use(false),
 
     // private
@@ -54,7 +53,6 @@
     needs_upmix(false),
     surround_mode(FreeSurround::SurroundModePassive),
     old_audio_stretchfactor(1.0),
-    volume(80),
 
     blocking(false),
 
@@ -109,6 +107,9 @@
 
     allow_ac3_passthru = (orig_config_channels > 2) ? gContext->GetNumSetting("AC3PassThru", false) : false;
 
+    if (gContext->GetSetting("MixerDevice") == "Software:")
+        volume_control = VolumeControlManager::GetControl("Software:");
+    
     // You need to call Reconfigure from your concrete class.
     // Reconfigure(laudio_bits,       laudio_channels,
     //             laudio_samplerate, laudio_passthru);
@@ -278,7 +279,6 @@
     killaudio = false;
     pauseaudio = false;
     was_paused = true;
-    internal_vol = gContext->GetNumSetting("MythControlsVolume", 0);
 
     numlowbuffer = 0;
     
@@ -358,18 +358,6 @@
         return;
     }
 
-    // Only used for software volume
-    if (set_initial_vol && internal_vol) 
-        volume = gContext->GetNumSetting("PCMMixerVolume", 80);
-    {
-        QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
-        controlLabel += "MixerVolume";
-        volume = gContext->GetNumSetting(controlLabel, 80);
-    }
-
-    SyncVolume();
-    VolumeBase::UpdateVolume();
-
     VERBOSE(VB_AUDIO, LOC + QString("Audio fragment size: %1")
             .arg(fragment_size));
 
@@ -663,22 +651,6 @@
      return audbuf_timecode - GetAudiotime();
 }
 
-void AudioOutputBase::SetSWVolume(int new_volume, bool save)
-{
-    volume = new_volume;
-    if (save)
-    {
-        QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
-        controlLabel += "MixerVolume";
-        gContext->SaveSetting(controlLabel, volume);
-    }
-}
-
-int AudioOutputBase::GetSWVolume()
-{
-    return volume;
-}
-
 void AudioOutputBase::AdjustVolume(void *buffer, int len, bool music)
 {
     if (audio_bits == 8)
@@ -690,6 +662,8 @@
 template <class AudioDataType>
 void AudioOutputBase::_AdjustVolume(AudioDataType *buffer, int len, bool music)
 {
+    int volume = volume_control->mute() ? 0 : volume_control->volume();
+  
     float g = volume / 100.0;
 
     // Should probably be exponential - this'll do
@@ -1098,7 +1072,7 @@
         }
     }
 
-    if (internal_vol && SWVolume())
+    if (volume_control)
     {
         int bdiff = kAudioRingBufferSize - waud;
         bool music = (timecode < 1);
@@ -1359,18 +1333,19 @@
     audio_buflock.unlock();
 
     // Mute individual channels through mono->stereo duplication
-    MuteState mute_state = GetMuteState();
+//    MuteState mute_state = GetMuteState();
     if (written_size &&
-        audio_channels > 1 &&
-        (mute_state == kMuteLeft || mute_state == kMuteRight))
+        audio_channels > 1 //&&
+//        (mute_state == kMuteLeft || mute_state == kMuteRight)
+       )
     {
         int offset_src = 0;
         int offset_dst = 0;
  
-        if (mute_state == kMuteLeft)
-            offset_src = audio_bits / 8;    // copy channel 1 to channel 0
-        else if (mute_state == kMuteRight)
-            offset_dst = audio_bits / 8;    // copy channel 0 to channel 1
+//        if (mute_state == kMuteLeft)
+//            offset_src = audio_bits / 8;    // copy channel 1 to channel 0
+//        else if (mute_state == kMuteRight)
+//            offset_dst = audio_bits / 8;    // copy channel 0 to channel 1
      
         for (int i = 0; i < written_size; i += audio_bytes_per_sample)
         {
Index: libs/libmyth/audiooutput.cpp
===================================================================
--- libs/libmyth/audiooutput.cpp	(revision 22850)
+++ libs/libmyth/audiooutput.cpp	(working copy)
@@ -34,13 +34,13 @@
     int audio_bits, int audio_channels, 
     int audio_codec, int audio_samplerate,
     AudioOutputSource source,
-    bool set_initial_vol, bool audio_passthru,
+    bool audio_passthru,
     int upmixer_startup)
 {
     AudioSettings settings(
         main_device, passthru_device, audio_bits,
         audio_channels, audio_codec, audio_samplerate, source,
-        set_initial_vol, audio_passthru, upmixer_startup);
+        audio_passthru, upmixer_startup);
 
     settings.FixPassThrough();
 
Index: libs/libmyth/libmyth.pro
===================================================================
--- libs/libmyth/libmyth.pro	(revision 22850)
+++ libs/libmyth/libmyth.pro	(working copy)
@@ -23,7 +23,8 @@
 HEADERS += output.h 
 HEADERS += settings.h 
 HEADERS += uilistbtntype.h uitypes.h util.h mythuifilebrowser.h
-HEADERS += volumebase.h visual.h xmlparse.h
+HEADERS += volumecontrol.h volumecontrolmanager.h volumecontrolsoftware.h
+HEADERS += visual.h xmlparse.h
 HEADERS += mythhdd.h mythcdrom.h storagegroup.h dbutil.h
 HEADERS += mythcommandlineparser.h mythterminal.h
 HEADERS += mythhttppool.h mythhttphandler.h
@@ -45,7 +46,8 @@
 SOURCES += output.cpp 
 SOURCES += settings.cpp
 SOURCES += uilistbtntype.cpp uitypes.cpp util.cpp mythuifilebrowser.cpp
-SOURCES += volumebase.cpp xmlparse.cpp
+SOURCES += volumecontrolmanager.cpp volumecontrolsoftware.cpp
+SOURCES += xmlparse.cpp
 SOURCES += mythhdd.cpp mythcdrom.cpp storagegroup.cpp dbutil.cpp
 SOURCES += mythcommandlineparser.cpp mythterminal.cpp
 SOURCES += mythhttppool.cpp mythhttphandler.cpp
@@ -83,12 +85,12 @@
 # Install headers so that plugins can compile independently
 inc.path = $${PREFIX}/include/mythtv/
 inc.files  = dialogbox.h mythcontext.h
-inc.files += mythwidgets.h remotefile.h oldsettings.h volumecontrol.h
+inc.files += mythwidgets.h remotefile.h oldsettings.h
 inc.files += settings.h uitypes.h xmlparse.h mythplugin.h mythdialogs.h
 inc.files += audiooutput.h audiosettings.h util.h
 inc.files += inetcomms.h mythmedia.h mythwizard.h schemawizard.h dbutil.h
 inc.files += uilistbtntype.h generictree.h managedlist.h mythmediamonitor.h
-inc.files += visual.h volumebase.h output.h langsettings.h
+inc.files += visual.h volumecontrol.h volumecontrolmanager.h output.h langsettings.h
 inc.files += mythexp.h mythpluginapi.h storagegroup.h
 inc.files += mythconfigdialogs.h mythconfiggroups.h
 inc.files += mythterminal.h mythdeque.h mythuifilebrowser.h
@@ -103,8 +105,8 @@
 
 using_oss {
     DEFINES += USING_OSS
-    SOURCES += audiooutputoss.cpp
-    HEADERS += audiooutputoss.h
+    SOURCES += audiooutputoss.cpp volumecontroloss.cpp
+    HEADERS += audiooutputoss.h volumecontroloss.h
     LIBS += $$OSS_LIBS
 }
 
@@ -132,14 +134,17 @@
 
 mingw {
     DEFINES += USING_MINGW
-    SOURCES += mediamonitor-windows.cpp audiooutputwin.cpp audiooutputdx.cpp
-    HEADERS += mediamonitor-windows.h   audiooutputwin.h   audiooutputdx.h
-    LIBS += -lpthread -lwinmm -lws2_32
+    SOURCES += mediamonitor-windows.cpp
+    HEADERS += mediamonitor-windows.h 
+    SOURCES += audiooutputwin.cpp audiooutputdx.cpp volumecontrolendpoint.cpp
+    HEADERS += audiooutputwin.h   audiooutputdx.h   volumecontrolendpoint.h
+    LIBS += -lpthread -lwinmm -lws2_32 -lole32
 }
 
 macx {
-    HEADERS += audiooutputca.h
-    SOURCES += audiooutputca.cpp
+    DEFINES += USE_COREAUDIO
+    HEADERS += audiooutputca.h volumecontrolcoreaudio.h
+    SOURCES += audiooutputca.cpp volumecontrolcoreaudio.cpp
     HEADERS += mythcdrom-darwin.h
     SOURCES += mythcdrom-darwin.cpp
 
@@ -179,8 +184,8 @@
 
 using_alsa {
     DEFINES += USE_ALSA
-    HEADERS += audiooutputalsa.h
-    SOURCES += audiooutputalsa.cpp
+    HEADERS += audiooutputalsa.h volumecontrolalsa.h
+    SOURCES += audiooutputalsa.cpp volumecontrolalsa.cpp
     LIBS += $$ALSA_LIBS
 }
 
Index: libs/libmyth/audiosettings.h
===================================================================
--- libs/libmyth/audiosettings.h	(revision 22850)
+++ libs/libmyth/audiosettings.h	(working copy)
@@ -32,7 +32,6 @@
         int               audio_codec,
         int               audio_samplerate,
         AudioOutputSource audio_source,
-        bool              audio_set_initial_vol,
         bool              audio_use_passthru,
         int               upmixer_startup = 0);
 
@@ -58,7 +57,6 @@
     int     channels;
     int     codec;
     int     samplerate;
-    bool    set_initial_vol;
     bool    use_passthru;
     AudioOutputSource source;
     int     upmixer;
Index: libs/libmyth/audiooutputca.h
===================================================================
--- libs/libmyth/audiooutputca.h	(revision 22850)
+++ libs/libmyth/audiooutputca.h	(working copy)
@@ -25,10 +25,6 @@
     bool RenderAudio(unsigned char *aubuf, int size,
                      unsigned long long timestamp);
 
-    // Volume control
-    virtual int  GetVolumeChannel(int channel) const;
-    virtual void SetVolumeChannel(int channel, int volume);
-
     void Debug(QString msg)
     {   VERBOSE(VB_AUDIO,     "AudioOutputCA::" + msg);   }
 
@@ -37,6 +33,7 @@
 
     void Warn(QString msg)
     {   VERBOSE(VB_IMPORTANT, "AudioOutputCA Warning: " + msg);   }
+    bool internal_vol;
 
 protected:
 
Index: libs/libmyth/audiooutputoss.cpp
===================================================================
--- libs/libmyth/audiooutputoss.cpp	(revision 22850)
+++ libs/libmyth/audiooutputoss.cpp	(working copy)
@@ -30,8 +30,7 @@
 
 AudioOutputOSS::AudioOutputOSS(const AudioSettings &settings) :
     AudioOutputBase(settings),
-    audiofd(-1), numbadioctls(0),
-    mixerfd(-1), control(SOUND_MIXER_VOLUME)
+    audiofd(-1), numbadioctls(0)
 {
     // Set everything up
     Reconfigure(settings);
@@ -194,10 +193,6 @@
                 " the error was: %1").arg(strerror(errno)));
     }
 
-    // Setup volume control
-    if (internal_vol)
-        VolumeInit();
-
     // Device opened successfully
     return true;
 }
@@ -242,8 +237,6 @@
         close(audiofd);
 
     audiofd = -1;
-    
-    VolumeCleanup();
 }
 
 
@@ -310,124 +303,3 @@
 
     return space;
 }
-
-void AudioOutputOSS::VolumeInit()
-{
-    mixerfd = -1;
-    int volume = 0;
-
-    QString device = gContext->GetSetting("MixerDevice", "/dev/mixer");
-    if (device.toLower() == "software")
-        return;
-
-    QByteArray dev = device.toAscii();
-    mixerfd = open(dev.constData(), O_RDONLY);
-
-    QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
-
-    if (controlLabel == "Master")
-    {
-        control = SOUND_MIXER_VOLUME;
-    }
-    else
-    {
-        control = SOUND_MIXER_PCM;
-    }
-
-    if (mixerfd < 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC +
-                QString("Unable to open mixer: '%1'").arg(device));
-        return;
-    }
-
-    if (set_initial_vol)
-    {
-        int tmpVol;
-        volume = gContext->GetNumSetting("MasterMixerVolume", 80);
-        tmpVol = (volume << 8) + volume;
-        int ret = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &tmpVol);
-        if (ret < 0)
-        {
-            VERBOSE(VB_IMPORTANT, LOC_ERR +
-                    QString("Error Setting initial Master Volume") + ENO);
-        }
-
-        volume = gContext->GetNumSetting("PCMMixerVolume", 80);
-        tmpVol = (volume << 8) + volume;
-        ret = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &tmpVol);
-        if (ret < 0)
-        {
-            VERBOSE(VB_IMPORTANT, LOC_ERR +
-                    QString("Error setting initial PCM Volume") + ENO);
-        }
-    }
-}
-
-void AudioOutputOSS::VolumeCleanup()
-{
-    if (mixerfd >= 0)
-    {
-        close(mixerfd);
-        mixerfd = -1;
-    }
-}
-
-int AudioOutputOSS::GetVolumeChannel(int channel) const
-{
-    int volume=0;
-    int tmpVol=0;
-
-    if (mixerfd <= 0)
-        return 100;
-
-    int ret = ioctl(mixerfd, MIXER_READ(control), &tmpVol);
-    if (ret < 0) {
-        VERBOSE(VB_IMPORTANT, QString("Error reading volume for channel %1")
-                          .arg(channel));
-        perror("Reading PCM volume: ");
-        return 0;
-    }
-
-    if (channel == 0) {
-        volume = tmpVol & 0xff; // left
-    } else if (channel == 1) {
-        volume = (tmpVol >> 8) & 0xff; // right
-    } else {
-        VERBOSE(VB_IMPORTANT, QString("Invalid channel. Only stereo volume supported"));
-    }
-
-    return volume;
-}
-
-void AudioOutputOSS::SetVolumeChannel(int channel, int volume)
-{
-    if (channel > 1) {
-        // Don't support more than two channels!
-    VERBOSE(VB_IMPORTANT, QString("Error setting channel: %1.  Only stereo volume supported")
-                                .arg(channel));
-        return;
-    }
-
-    if (volume > 100)
-        volume = 100;
-    if (volume < 0)
-        volume = 0;
-
-    if (mixerfd >= 0)
-    {
-        int tmpVol = 0;
-        if (channel == 0) 
-            tmpVol = (GetVolumeChannel(1) << 8) + volume;
-        else
-            tmpVol = (volume << 8) + GetVolumeChannel(0);
-
-        int ret = ioctl(mixerfd, MIXER_WRITE(control), &tmpVol);
-        if (ret < 0) 
-        {
-            VERBOSE(VB_IMPORTANT, QString("Error setting volume on channel: %1").arg(channel));
-            perror("Setting volume: ");
-        }
-    }
-}
-
Index: libs/libmyth/audiooutputdx.cpp
===================================================================
--- libs/libmyth/audiooutputdx.cpp	(revision 22850)
+++ libs/libmyth/audiooutputdx.cpp	(working copy)
@@ -369,8 +369,6 @@
     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
                     | DSBCAPS_GLOBALFOCUS       /* Allows background playing */
                     | DSBCAPS_LOCHARDWARE;      /* Needed for 5.1 on emu101k */
-    if (!m_UseSPDIF)
-        dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;  /* Allow volume control */
     dsbdesc.dwBufferBytes = soundcard_buffer_size;   /* buffer size */
     dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
 
@@ -517,49 +515,4 @@
     return buffered;
 }
 
-int AudioOutputDX::GetVolumeChannel(int channel) const
-{
-    HRESULT dsresult;
-    long dxVolume = 0;
-    int volume;
-
-    if (m_UseSPDIF)
-        return 100;
-
-    dsresult = IDirectSoundBuffer_GetVolume(m_priv->dsbuffer, &dxVolume);
-    volume = (int)(pow(10,(float)dxVolume/20)*100);
-
-    if (dsresult != DS_OK)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Failed to get volume %1")
-                                                .arg(dxVolume));
-        return volume;
-    }
-
-    VERBOSE(VB_AUDIO, LOC + QString("Got volume %1").arg(volume));
-    return volume;
-}
-
-void AudioOutputDX::SetVolumeChannel(int channel, int volume)
-{
-    HRESULT dsresult;
-    float dbAtten = 20 * log10((float)volume/100);
-    long dxVolume = (volume == 0) ? DSBVOLUME_MIN : (long)(100.0f * dbAtten);
-
-    if (m_UseSPDIF)
-        return;
-
-    // dxVolume is attenuation in 100ths of a decibel
-    dsresult = IDirectSoundBuffer_SetVolume(m_priv->dsbuffer, dxVolume);
-
-    if (dsresult != DS_OK)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Failed to set volume %1")
-                                                .arg(dxVolume));
-        return;
-    }
-    
-    VERBOSE(VB_AUDIO, LOC + QString("Set volume %1").arg(dxVolume));
-}
-
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmyth/audiooutputnull.h
===================================================================
--- libs/libmyth/audiooutputnull.h	(revision 22850)
+++ libs/libmyth/audiooutputnull.h	(working copy)
@@ -27,11 +27,6 @@
 
     virtual void Reset(void);
 
-
-    // Volume control
-    virtual int GetVolumeChannel(int /* channel */) const { return 100; }
-    virtual void SetVolumeChannel(int /* channel */, int /* volume */){return;}
-
     virtual int readOutputData(unsigned char *read_buffer, int max_length);
 
   protected:
Index: libs/libmyth/audiooutput.h
===================================================================
--- libs/libmyth/audiooutput.h	(revision 22850)
+++ libs/libmyth/audiooutput.h	(working copy)
@@ -5,10 +5,9 @@
 
 #include "audiosettings.h"
 #include "mythcontext.h"
-#include "volumebase.h"
 #include "output.h"
 
-class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners
+class MPUBLIC AudioOutput : public OutputListeners
 {
  public:
     // opens one of the concrete subclasses
@@ -18,11 +17,11 @@
         int audio_bits, int audio_channels, 
         int audio_codec, int audio_samplerate,
         AudioOutputSource source,
-        bool set_initial_vol, bool audio_passthru,
+        bool audio_passthru,
         int upmixer_startup = 0);
 
     AudioOutput() :
-        VolumeBase(),             OutputListeners(),
+        OutputListeners(),
         lastError(QString::null), lastWarn(QString::null) {}
 
     virtual ~AudioOutput() { };
Index: libs/libmyth/volumecontrolmanager.h
===================================================================
--- libs/libmyth/volumecontrolmanager.h	(revision 0)
+++ libs/libmyth/volumecontrolmanager.h	(revision 0)
@@ -0,0 +1,19 @@
+#ifndef VOLUMECONTROLMANAGER
+#define VOLUMECONTROLMANAGER
+
+#include "volumecontrol.h"
+
+#include "mythexp.h"
+
+#include <QHash>
+#include <QSharedPointer>
+#include <QString>
+
+class MPUBLIC VolumeControlManager
+{
+public:
+    static QHash<QString, QString> Enumerate();
+    static QSharedPointer<VolumeControl> GetControl(QString device);
+};
+
+#endif // VOLUMECONTROLMANAGER
Index: libs/libmyth/volumecontrolendpoint.h
===================================================================
--- libs/libmyth/volumecontrolendpoint.h	(revision 0)
+++ libs/libmyth/volumecontrolendpoint.h	(revision 0)
@@ -0,0 +1,45 @@
+#ifndef VOLUMECONTROLENDPOINT
+#define VOLUMECONTROLENDPOINT
+
+#include "volumecontrol.h"
+
+#include <QHash>
+#include <QAtomicInt>
+
+#include <rpcsal.h>
+#include <endpointvolume.h>
+
+
+class VolumeControlEndpoint : public VolumeControl,
+                              protected IAudioEndpointVolumeCallback
+{
+public:
+    static QHash<QString, QString> Enumerate();
+
+public:
+    VolumeControlEndpoint(QString device);
+    virtual ~VolumeControlEndpoint();
+
+public:
+    int  volume() const;
+    void setVolume(int volume);
+    void increaseVolume();
+    void decreaseVolume();
+
+    bool mute() const;
+    void setMute(bool mute);
+
+protected:
+    ULONG STDMETHODCALLTYPE AddRef();
+    ULONG STDMETHODCALLTYPE Release();
+    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface);
+    HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify);
+
+private:
+    IAudioEndpointVolume* m_EndpointVolume; //< Endpoint volume interface
+    int                   m_Range;          //< Range of mixer element
+    QAtomicInt            m_Volume;         //< Volume state cache
+    QAtomicInt            m_Mute;           //< Mute state cache
+};
+
+#endif // VOLUMECONTROLWINDOWSENDPOINT
Index: libs/libmyth/audiooutputdx.h
===================================================================
--- libs/libmyth/audiooutputdx.h	(revision 22850)
+++ libs/libmyth/audiooutputdx.h	(working copy)
@@ -13,9 +13,6 @@
     AudioOutputDX(const AudioSettings &settings);
     virtual ~AudioOutputDX();
 
-    virtual int GetVolumeChannel(int channel) const;
-    virtual void SetVolumeChannel(int channel, int volume);
-
   protected:
     virtual bool OpenDevice(void);
     virtual void CloseDevice(void);
Index: libs/libmyth/audiooutputalsa.h
===================================================================
--- libs/libmyth/audiooutputalsa.h	(revision 22850)
+++ libs/libmyth/audiooutputalsa.h	(working copy)
@@ -9,55 +9,12 @@
 
 using namespace std;
 
-class ALSAVolumeInfo
-{
-  public:
-    ALSAVolumeInfo(long  playback_vol_min,
-                   long  playback_vol_max) :
-        range_multiplier(1.0f),
-        volume_min(playback_vol_min), volume_max(playback_vol_max)
-    {
-        float range = (float) (volume_max - volume_min);
-        if (range > 0.0f)
-            range_multiplier = 100.0f / range;
-        range_multiplier_inv = 1.0f / range_multiplier;
-    }
-
-    int ToMythRange(long alsa_volume)
-    {
-        long toz = alsa_volume - volume_min;
-        int val = (int) (toz * range_multiplier);
-        val = (val < 0)   ? 0   : val;
-        val = (val > 100) ? 100 : val;
-        return val;
-    }
-
-    long ToALSARange(int myth_volume)
-    {
-        float tos = myth_volume * range_multiplier_inv;
-        long val = (long) (tos + volume_min + 0.5);
-        val = (val < volume_min) ? volume_min : val;
-        val = (val > volume_max) ? volume_max : val;
-        return val;
-    }
-
-    float range_multiplier;
-    float range_multiplier_inv;
-    long  volume_min;
-    long  volume_max;
-};
-
 class AudioOutputALSA : public AudioOutputBase
 {
   public:
     AudioOutputALSA(const AudioSettings &settings);
     virtual ~AudioOutputALSA();
 
-    // Volume control
-    virtual int GetVolumeChannel(int channel) const; // Returns 0-100
-    virtual void SetVolumeChannel(int channel, int volume); // range 0-100 for vol
-
-    
   protected:
     // You need to implement the following functions
     virtual bool OpenDevice(void);
@@ -77,19 +34,11 @@
     void ReorderSmpteToAlsa6ch(void *buf, int frames);
     template <class AudioDataType>
         void _ReorderSmpteToAlsa6ch(AudioDataType *buf, int frames);
-    // Volume related
-    void SetCurrentVolume(QString control, int channel, int volume);
-    void OpenMixer(bool setstartingvolume);
-    void CloseMixer(void);
-    void SetupMixer(void);
-    ALSAVolumeInfo GetVolumeRange(snd_mixer_elem_t *elem) const;
 
   private:
     snd_pcm_t   *pcm_handle;
     int          numbadioctls;
     QMutex       killAudioLock;
-    snd_mixer_t *mixer_handle;
-    QString      mixer_control; // e.g. "PCM"
     snd_pcm_sframes_t (*pcm_write_func)(snd_pcm_t*, const void*, 
                                         snd_pcm_uframes_t);
 };
Index: libs/libmyth/volumecontroloss.cpp
===================================================================
--- libs/libmyth/volumecontroloss.cpp	(revision 0)
+++ libs/libmyth/volumecontroloss.cpp	(revision 0)
@@ -0,0 +1,388 @@
+#include "volumecontroloss.h"
+
+#include "mythverbose.h"
+#include <algorithm>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <QDir>
+
+#include "mythconfig.h"
+#ifdef HAVE_SYS_SOUNDCARD_H
+    #include <sys/soundcard.h>
+#elif HAVE_SOUNDCARD_H
+    #include <soundcard.h>
+#endif
+
+static const unsigned long poll = 250;   //< OSS control poll interval (ms)
+
+QHash<QString, QString> VolumeControlOSS::Enumerate()
+{
+    QHash<QString, QString> result;
+#if 1
+    int mixerfd = open("/dev/mixer", O_RDWR, 0);
+
+    if (mixerfd == -1)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlOSS::Enumerate() - ERROR: "
+                        "opening \"/dev/mixer\""));
+        return result;
+    }
+
+    oss_sysinfo sysinfo;
+    if (ioctl(mixerfd, SNDCTL_SYSINFO, &sysinfo) == -1)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlOSS::Enumerate() - ERROR: "
+                        "obtaining system information"));
+        return result;
+    }
+
+    for (int mixer = 0; mixer < sysinfo.nummixers; ++mixer)
+    {
+        oss_mixerinfo info = {
+            dev: mixer
+        };
+
+        if (ioctl(mixerfd, SNDCTL_MIXERINFO, &info) != -1)
+            result.insert(QString("OSS:%1").arg(info.devnode), info.name);
+    }
+
+    close(mixerfd);
+
+#else
+    QDir devs("/dev", "mixer*", QDir::Name, QDir::System);
+
+    QFileInfoList files = devs.entryInfoList();
+    QFileInfoList::Iterator file;
+    for (file = files.begin(); file != files.end(); ++file)
+    {
+        result.insert("OSS:" + file->absoluteFilePath(),
+                      file->absoluteFilePath());
+    }
+
+    devs.setPath("/dev/sound");
+    files = devs.entryInfoList();
+    for (file = files.begin(); file != files.end(); ++file)
+    {
+        result.insert("OSS:" + file->absoluteFilePath(),
+                      file->absoluteFilePath());
+    }
+#endif
+    return result;
+}
+
+VolumeControlOSS::VolumeControlOSS(QString device) :
+   m_VolumeCtrl(-1), m_MuteCtrl(-1), m_Exit(false)
+{
+    m_Mixer = open(device.toAscii().constData(), O_RDWR, 0);
+
+    if (m_Mixer == -1)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlOSS::VolumeControlOSS() - ERROR: "
+                        "opening \"%1\"").arg(device));
+        return;
+    }
+
+    oss_sysinfo sysinfo;
+    if (ioctl(m_Mixer, SNDCTL_SYSINFO, &sysinfo) == -1)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlOSS::VolumeControlOSS() - ERROR: "
+                        "obtaining system information"));
+        return;
+    }
+
+    for (m_MixerDev = 0; m_MixerDev < sysinfo.nummixers; ++m_MixerDev)
+    {
+        oss_mixerinfo info = {
+            dev: m_MixerDev
+        };
+
+        if (ioctl(m_Mixer, SNDCTL_MIXERINFO, &info) != -1)
+            if (QString(info.devnode) == device)
+                break;
+    }
+
+    int extensions = m_MixerDev;
+    if (ioctl(m_Mixer, SNDCTL_MIX_NREXT, &extensions) == -1)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("VolumeControlOSS::VolumeControlOSS() - ERROR: "
+                        "obtaining number of extensions"));
+        return;
+    }
+
+    for (int control = 0; control < extensions; ++control)
+    {
+        oss_mixext info = {
+            dev:  m_MixerDev,
+            ctrl: control
+        };
+
+        if (ioctl(m_Mixer, SNDCTL_MIX_EXTINFO, &info) == -1)
+            continue;
+
+        switch (info.type)
+        {
+        case MIXT_MONODB:
+        case MIXT_MONOSLIDER:
+        case MIXT_MONOSLIDER16:
+        case MIXT_SLIDER:
+        case MIXT_STEREOSLIDER:
+        case MIXT_STEREOSLIDER16:
+            if (m_VolumeCtrl == -1)
+            {
+                m_VolumeCtrl = info.ctrl;
+                m_VolumeMax = info.maxvalue;
+                m_VolumeType = info.type;
+                m_VolumeTimestamp = info.timestamp;
+            }
+            break;
+
+        case MIXT_ONOFF:
+        case MIXT_MUTE:
+            if (m_MuteCtrl == -1)
+            {
+                m_MuteCtrl = info.ctrl;
+                m_MuteTimestamp = info.timestamp;
+            }
+            break;
+        }
+
+        if (m_VolumeCtrl != -1 && m_MuteCtrl != -1)
+            break;
+    }
+
+    m_Range = std::min(std::max(1, m_VolumeMax), 100);
+
+    if (m_VolumeCtrl != -1)
+        start();
+}
+
+VolumeControlOSS::~VolumeControlOSS()
+{
+    m_Exit = true;
+
+    if (isRunning())
+        wait();
+
+    if (m_Mixer != -1)
+        close(m_Mixer);
+}
+
+int VolumeControlOSS::volume() const
+{
+    int volume = 0;
+
+    if (m_VolumeCtrl != -1)
+    {
+        if (m_MuteCtrl != -1 || !mute())
+        {
+            oss_mixer_value value;
+            memset(&value, 0, sizeof(value));
+
+            value.dev = m_MixerDev;
+            value.ctrl = m_VolumeCtrl;
+            value.timestamp = m_VolumeTimestamp;
+
+            if (ioctl(m_Mixer, SNDCTL_MIX_READ, &value) != -1)
+            {
+                switch (m_VolumeType)
+                {
+                case MIXT_MONODB:
+                case MIXT_MONOSLIDER:
+                    volume = value.value & 0xFF;
+                    break;
+
+                case MIXT_MONOSLIDER16:
+                    volume = value.value & 0xFFFF;
+                    break;
+
+                case MIXT_SLIDER:
+                    volume = value.value & 0x7FFFFFFF;
+                    break;
+
+                case MIXT_STEREOSLIDER:
+                    volume = std::max(value.value & 0xFF,
+                                      (value.value >> 8) & 0xFF);
+                    break;
+
+                case MIXT_STEREOSLIDER16:
+                    volume = std::max(value.value & 0xFFFF,
+                                      (value.value >> 16) & 0xFFFF);
+                    break;
+                }
+
+                volume = ((200 * volume) + m_VolumeMax) / (2 * m_VolumeMax);
+            }
+        }
+        else
+            volume = m_Volume;
+    }
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlOSS::volume() = %1")
+            .arg(volume));
+
+    return volume;
+}
+
+void VolumeControlOSS::setVolume(int volume)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlOSS::setVolume(%1)")
+            .arg(volume));
+
+    volume = std::min(std::max(0, volume), 100);
+
+    if (mute() && volume > VolumeControlOSS::volume())
+        setMute(false);
+
+    if (m_VolumeCtrl != -1 && (m_MuteCtrl != -1 || !mute()))
+    {
+        oss_mixer_value value;
+        memset(&value, 0, sizeof(value));
+
+        value.dev = m_MixerDev;
+        value.ctrl = m_VolumeCtrl;
+        value.timestamp = m_VolumeTimestamp;
+        value.value = (m_VolumeMax * volume) / 100;
+
+        switch (m_VolumeType)
+        {
+        case MIXT_STEREOSLIDER:
+            value.value = value.value | (value.value << 8);
+            break;
+
+        case MIXT_STEREOSLIDER16:
+            value.value = value.value | (value.value << 16);
+            break;
+        }
+
+        ioctl(m_Mixer, SNDCTL_MIX_WRITE, &value);
+    }
+
+    if (m_Volume.fetchAndStoreRelaxed(volume) != volume)
+        emit changedVolume(volume);
+}
+
+void VolumeControlOSS::increaseVolume()
+{
+    setVolume((volume() * m_Range + 100) / m_Range);
+}
+
+void VolumeControlOSS::decreaseVolume()
+{
+    setVolume((volume() * m_Range - 100) / m_Range);
+}
+
+bool VolumeControlOSS::mute() const
+{
+    bool mute = false;
+
+    if (m_VolumeCtrl != -1)
+    {
+        oss_mixer_value value;
+        memset(&value, 0, sizeof(value));
+
+        value.dev = m_MixerDev;
+
+        if (m_MuteCtrl != -1)
+        {
+            value.ctrl = m_MuteCtrl;
+            value.timestamp = m_MuteTimestamp;
+
+            if (ioctl(m_Mixer, SNDCTL_MIX_READ, &value) != -1)
+                mute = value.value;
+        }
+        else
+        {
+            value.ctrl = m_VolumeCtrl;
+            value.timestamp = m_VolumeTimestamp;
+
+            if (ioctl(m_Mixer, SNDCTL_MIX_READ, &value) != -1)
+                mute = !value.value;
+        }
+    }
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlOSS::mute() = %1")
+            .arg(mute ? "mute" : "unmute"));
+
+    return mute;
+}
+
+void VolumeControlOSS::setMute(bool mute)
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlOSS::setMute(%1)")
+            .arg(mute ? "mute" : "unmute"));
+
+    if (m_VolumeCtrl != -1)
+    {
+        oss_mixer_value value;
+        memset(&value, 0, sizeof(value));
+
+        value.dev = m_MixerDev;
+
+        if (m_MuteCtrl != -1)
+        {
+            value.ctrl = m_MuteCtrl;
+            value.timestamp = m_MuteTimestamp;
+            value.value = mute;
+        }
+        else
+        {
+            value.ctrl = m_VolumeCtrl;
+            value.timestamp = m_VolumeTimestamp;
+            value.value = mute ? 0 : (m_VolumeMax * m_Volume) / 100;
+
+            switch (m_VolumeType)
+            {
+            case MIXT_STEREOSLIDER:
+                value.value = value.value | (value.value << 8);
+                break;
+
+            case MIXT_STEREOSLIDER16:
+                value.value = value.value | (value.value << 16);
+                break;
+            }
+        }
+
+        ioctl(m_Mixer, SNDCTL_MIX_WRITE, &value);
+    }
+
+    if (m_Mute.fetchAndStoreRelaxed(mute) != mute)
+        emit changedMute(mute);
+}
+
+void VolumeControlOSS::run()
+{
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlOSS::run() - begin"));
+
+    m_Volume = volume();
+    m_Mute = mute();
+
+    while(!m_Exit)
+    {
+        msleep(poll);
+
+        int pollvolume = volume();
+        if (m_Volume.fetchAndStoreRelaxed(pollvolume) != pollvolume)
+            emit changedVolume(pollvolume);
+
+        bool pollmute = mute();
+        if (m_Mute.fetchAndStoreRelaxed(pollmute) != pollmute)
+            emit changedMute(pollmute);
+    }
+
+    VERBOSE(VB_AUDIO,
+            QString("VolumeControlOSS::run() - end"));
+}
+
Index: programs/mythfrontend/globalsettings.cpp
===================================================================
--- programs/mythfrontend/globalsettings.cpp	(revision 22850)
+++ programs/mythfrontend/globalsettings.cpp	(working copy)
@@ -29,6 +29,7 @@
 #include "iso639.h"
 #include "playbackbox.h"
 #include "globalsettings.h"
+#include "libmyth/volumecontrolmanager.h"
 #include "recordingprofile.h"
 #include "mythxdisplay.h"
 #include "DisplayRes.h"
@@ -180,88 +181,23 @@
     return gc;
 }
 
-static HostCheckBox *MythControlsVolume()
-{
-    HostCheckBox *gc = new HostCheckBox("MythControlsVolume");
-    gc->setLabel(QObject::tr("Use internal volume controls"));
-    gc->setValue(true);
-    gc->setHelpText(QObject::tr("MythTV can control the PCM and master "
-                    "mixer volume.  If you prefer to control the volume externally "
-                    "(like your amplifier) or use an external mixer "
-                    "program, disable this option."));
-    return gc;
-}
-
 static HostComboBox *MixerDevice()
 {
     HostComboBox *gc = new HostComboBox("MixerDevice", true);
     gc->setLabel(QObject::tr("Mixer Device"));
+    gc->addSelection(QObject::tr("Disabled"), QString());
 
-#ifdef USING_OSS
-    QDir dev("/dev", "mixer*", QDir::Name, QDir::System);
-    gc->fillSelectionsFromDir(dev);
+    QHash<QString, QString> controls = VolumeControlManager::Enumerate();
 
-    dev.setPath("/dev/sound");
-    if (dev.exists())
+    for (QHash<QString, QString>::const_iterator control = controls.begin();
+         control != controls.end(); ++control)
     {
-        gc->fillSelectionsFromDir(dev);
+        gc->addSelection(control.value(), control.key());
     }
-#endif
-#ifdef USING_ALSA
-    gc->addSelection("ALSA:default", "ALSA:default");
-#endif
-#ifdef USING_MINGW
-    gc->addSelection("DirectX:", "DirectX:");
-    gc->addSelection("Windows:", "Windows:");
-#endif
-#if !defined(USING_MINGW)
-    gc->addSelection("software", "software");
-    gc->setHelpText(QObject::tr("Setting the mixer device to \"software\" lets MythTV control "
-                                "the volume of all audio at the expense of a slight quality loss."));
-#endif
 
     return gc;
 }
 
-static const char* MixerControlControls[] = { "PCM",
-                                              "Master" };
-
-static HostComboBox *MixerControl()
-{
-    HostComboBox *gc = new HostComboBox("MixerControl", true);
-    gc->setLabel(QObject::tr("Mixer Controls"));
-    for (unsigned int i = 0; i < sizeof(MixerControlControls) / sizeof(char*);
-         ++i)
-    {
-        gc->addSelection(QObject::tr(MixerControlControls[i]),
-                         MixerControlControls[i]);
-    }
-
-    gc->setHelpText(QObject::tr("Changing the volume adjusts the selected mixer."));
-    return gc;
-}
-
-static HostSlider *MixerVolume()
-{
-    HostSlider *gs = new HostSlider("MasterMixerVolume", 0, 100, 1);
-    gs->setLabel(QObject::tr("Master Mixer Volume"));
-    gs->setValue(70);
-    gs->setHelpText(QObject::tr("Initial volume for the Master Mixer.  "
-                    "This affects all sound created by the sound card.  "
-                    "Note: Do not set this too low."));
-    return gs;
-}
-
-static HostSlider *PCMVolume()
-{
-    HostSlider *gs = new HostSlider("PCMMixerVolume", 0, 100, 1);
-    gs->setLabel(QObject::tr("PCM Mixer Volume"));
-    gs->setValue(70);
-    gs->setHelpText(QObject::tr("Initial volume for PCM output.  Using the "
-                    "volume keys in MythTV will adjust this parameter."));
-    return gs;
-}
-
 static HostCheckBox *IndividualMuteControl()
 {
     HostCheckBox *gc = new HostCheckBox("IndividualMuteControl");
@@ -3553,40 +3489,20 @@
     group2->addChild(AggressiveBuffer());
 
     return vcg;
-
 }
 
-class AudioMixerSettingsGroup : public TriggeredConfigurationGroup
+static ConfigurationGroup *AudioMixerSettingsGroup()
 {
-  public:
-    AudioMixerSettingsGroup() :
-        TriggeredConfigurationGroup(false, true, false, false)
-    {
-        setLabel(QObject::tr("Audio Mixer"));
-        setUseLabel(false);
+    ConfigurationGroup *vcg = new VerticalConfigurationGroup(false, true, false, false);
+    
+    vcg->setLabel(QObject::tr("Audio Mixer"));
+    vcg->setUseLabel(false);
 
-        Setting *volumeControl = MythControlsVolume();
-        addChild(volumeControl);
+    vcg->addChild(MixerDevice());
 
-        // Mixer settings
-        ConfigurationGroup *settings =
-            new VerticalConfigurationGroup(false, true, false, false);
-        settings->addChild(MixerDevice());
-        settings->addChild(MixerControl());
-        settings->addChild(MixerVolume());
-        settings->addChild(PCMVolume());
-        settings->addChild(IndividualMuteControl());
+    return vcg;
+}
 
-        ConfigurationGroup *dummy =
-            new VerticalConfigurationGroup(false, true, false, false);
-
-        // Show Mixer config only if internal volume controls enabled
-        setTrigger(volumeControl);
-        addTarget("0", dummy);
-        addTarget("1", settings);
-    }
-};
-
 static HostComboBox *MythLanguage()
 {
     HostComboBox *gc = new HostComboBox("Language");
@@ -4270,7 +4186,7 @@
 
     addChild(AudioSystemSettingsGroup());
 
-    addChild(new AudioMixerSettingsGroup());
+    addChild(AudioMixerSettingsGroup());
 
     VerticalConfigurationGroup *general =
         new VerticalConfigurationGroup(false, true, false, false);
Index: programs/mythtranscode/transcode.cpp
===================================================================
--- programs/mythtranscode/transcode.cpp	(revision 22850)
+++ programs/mythtranscode/transcode.cpp	(working copy)
@@ -166,67 +166,12 @@
         return last_audiotime;
     }
 
-    virtual int GetVolumeChannel(int) const
-    { 
-        // Do nothing
-        return 100;
-    }
-    virtual void SetVolumeChannel(int, int) 
-    {
-        // Do nothing
-    }
-    virtual void SetVolumeAll(int)
-    {
-        // Do nothing
-    }
-    virtual uint GetCurrentVolume(void) const
-    { 
-        // Do nothing
-        return 100;
-    }
-    virtual void SetCurrentVolume(int)
-    {
-        // Do nothing
-    }
-    virtual void AdjustCurrentVolume(int) 
-    {
-        // Do nothing
-    }
-    virtual void SetMute(bool) 
-    {
-        // Do nothing
-    }
-    virtual void ToggleMute(void) 
-    {
-        // Do nothing
-    }
-    virtual MuteState GetMuteState(void) const
-    {
-        // Do nothing
-        return kMuteOff;
-    }
-    virtual MuteState IterateMutedChannels(void)
-    {
-        // Do nothing
-        return kMuteOff;
-    }
     virtual bool ToggleUpmix(void) 
     {
         // Do nothing
         return false;
     }
 
-    virtual void SetSWVolume(int new_volume, bool save)
-    {
-        // Do nothing
-        return;
-    }
-    virtual int GetSWVolume(void)
-    {
-        // Do nothing
-        return 100;
-    }
-
     //  These are pure virtual in AudioOutput, but we don't need them here
     virtual void bufferOutputData(bool){ return; }
     virtual int readOutputData(unsigned char*, int ){ return 0; }
