Index: mythplugins/mythmusic/mythmusic/playbackbox.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.cpp	(revision 24896)
+++ mythplugins/mythmusic/mythmusic/playbackbox.cpp	(working copy)
@@ -98,12 +98,16 @@
 
     // Possibly (user-defined) control the volume
 
-    volume_control = false;
-    volume_display_timer = new QTimer(this);
-    if (gCoreContext->GetNumSetting("MythControlsVolume", 0))
+    volume_control = VolumeControlManager::GetControl(gCoreContext->GetSetting("MixerDevice"));
+    if (volume_control)
     {
-        volume_control = true;
+        connect(volume_control.data(), SIGNAL(changedVolume(int)),
+                this, SLOT(VolumeChanged(int)));
+        connect(volume_control.data(), SIGNAL(changedMute(bool)),
+                this, SLOT(MuteChanged(bool)));
     }
+
+    volume_display_timer = new QTimer(this);
     volume_display_timer->setSingleShot(true);
     volume_display_timer->start(2000);
     connect(volume_display_timer, SIGNAL(timeout()),
@@ -1178,9 +1182,9 @@
     if (volume_control && gPlayer->getOutput())
     {
         if (up_or_down)
-            gPlayer->getOutput()->AdjustCurrentVolume(2);
+            gPlayer->incVolume();
         else
-            gPlayer->getOutput()->AdjustCurrentVolume(-2);
+            gPlayer->decVolume();
         showVolume(true);
     }
 }
@@ -1201,7 +1205,7 @@
 {
     if (volume_control && gPlayer->getOutput())
     {
-        gPlayer->getOutput()->ToggleMute();
+        gPlayer->toggleMute();
         showVolume(true);
     }
 }
@@ -1243,7 +1247,7 @@
         {
             if (on_or_off)
             {
-                volume_status->SetUsed(gPlayer->getOutput()->GetCurrentVolume());
+                volume_status->SetUsed(gPlayer->getVolume());
                 volume_status->SetOrder(0);
                 volume_status->refresh();
                 volume_display_timer->setSingleShot(true);
@@ -2454,3 +2458,13 @@
 
     return time_string;
 }
+
+void PlaybackBoxMusic::VolumeChanged(int)
+{
+     showVolume(true);
+}
+
+void PlaybackBoxMusic::MuteChanged(bool)
+{
+     showVolume(true);
+}
Index: mythplugins/mythmusic/mythmusic/musicplayer.h
===================================================================
--- mythplugins/mythmusic/mythmusic/musicplayer.h	(revision 24896)
+++ mythplugins/mythmusic/mythmusic/musicplayer.h	(working copy)
@@ -3,6 +3,7 @@
 
 #include <mythdialogs.h>
 #include <audiooutput.h>
+#include <libmyth/volumecontrolmanager.h>
 #include <mythobservable.h>
 
 #include "metadata.h"
@@ -55,9 +56,8 @@
 
     void setCDDevice(const QString &dev) { m_CDdevice = dev; }
 
-    void      toggleMute(void);
-    MuteState getMuteState(void) const;
-    bool      isMuted(void) const { return getMuteState() == kMuteAll; }
+    void toggleMute(void);
+    bool isMuted(void) const;
 
     void setVolume(int volume);
     void incVolume(void);
@@ -176,6 +176,8 @@
     AudioOutput *m_output;
     Decoder     *m_decoder;
 
+    QSharedPointer<VolumeControl> volume_control; ///< Volume control interface
+
     QSet<QObject*>  m_visualisers;
 
     QString      m_CDdevice;
Index: mythplugins/mythmusic/mythmusic/musicplayer.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/musicplayer.cpp	(revision 24896)
+++ mythplugins/mythmusic/mythmusic/musicplayer.cpp	(working copy)
@@ -88,6 +88,8 @@
 
     m_autoShowPlayer = (gCoreContext->GetNumSetting("MusicAutoShowPlayer", 1) > 0);
 
+    volume_control = VolumeControlManager::GetControl(gCoreContext->GetSetting("MixerDevice"));
+
     gCoreContext->addListener(this);
 }
 
@@ -375,7 +377,7 @@
     // TODO: Error checking that device is opened correctly!
     m_output = AudioOutput::OpenAudio(
                    adevice, pdevice, FORMAT_S16, 2, 0, 44100,
-                   AUDIOOUTPUT_MUSIC, true, false,
+                   AUDIOOUTPUT_MUSIC, false,
                    gCoreContext->GetNumSetting("MusicDefaultUpmix", 0) + 1);
     m_output->setBufferSize(256 * 1024);
 
@@ -934,52 +936,52 @@
 
 void MusicPlayer::incVolume()
 {
-    if (getOutput())
+    if (volume_control)
     {
-        getOutput()->AdjustCurrentVolume(2);
+        volume_control->increaseVolume();
         sendVolumeChangedEvent();
     }
 }
 
 void MusicPlayer::decVolume()
 {
-    if (getOutput())
+    if (volume_control)
     {
-        getOutput()->AdjustCurrentVolume(-2);
+        volume_control->decreaseVolume();
         sendVolumeChangedEvent();
     }
 }
 
 void MusicPlayer::setVolume(int volume)
 {
-    if (getOutput())
+    if (volume_control)
     {
-        getOutput()->SetCurrentVolume(volume);
+        volume_control->setVolume(volume);
         sendVolumeChangedEvent();
     }
 }
 
 uint MusicPlayer::getVolume(void) const
 {
-    if (m_output)
-        return m_output->GetCurrentVolume();
+    if (volume_control)
+        return volume_control->volume();
     return 0;
 }
 
 void MusicPlayer::toggleMute(void)
 {
-    if (m_output)
+    if (volume_control)
     {
-        m_output->ToggleMute();
+        volume_control->setMute(!volume_control->mute());
         sendVolumeChangedEvent();
     }
 }
 
-MuteState MusicPlayer::getMuteState(void) const
+bool MusicPlayer::isMuted(void) const
 {
-    if (m_output)
-        return m_output->GetMuteState();
-    return kMuteAll;
+    if (volume_control)
+        return volume_control->mute();
+    return true;
 }
 
 void MusicPlayer::toMap(QHash<QString, QString> &map)
Index: mythplugins/mythmusic/mythmusic/playbackbox.h
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.h	(revision 24896)
+++ mythplugins/mythmusic/mythmusic/playbackbox.h	(working copy)
@@ -8,7 +8,7 @@
 // mythtv
 #include <mythwidgets.h>
 #include <dialogbox.h>
-#include <audiooutput.h>
+#include <libmyth/volumecontrolmanager.h>
 
 // mythmusic
 #include "mainvisual.h"
@@ -101,6 +101,10 @@
     bool getInsertPLOptions(InsertPLOption &insertOption,
                             PlayPLOption &playOption, bool &bRemoveDups);
 
+  protected slots:
+    void VolumeChanged(int volume);
+    void MuteChanged(bool mute);
+
   signals:
 
     void dummy();   // debugging
@@ -172,7 +176,7 @@
     bool show_album_art;
     bool show_whole_tree;
     bool keyboard_accelerators;
-    bool volume_control;
+    QSharedPointer<VolumeControl> volume_control; ///< Volume control interface
 
     QString exit_action;
 
Index: mythtv/configure
===================================================================
--- mythtv/configure	(revision 24896)
+++ mythtv/configure	(working copy)
@@ -637,6 +637,13 @@
     eval "$var=\"\$$var $*\""
 }
 
+prepend(){
+    var=$1
+    shift
+    flags_saved && eval "SAVE_$var=\"$* \$SAVE_$var\""
+    eval "$var=\"$* \$$var\""
+}
+
 append_uniq(){
     log append_uniq "$@"
     var=$1
@@ -3640,6 +3647,30 @@
     { { ! enabled audio_alsa || die "ERROR: Alsa >= 1.0.16 required"; } &&
     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 &&
Index: mythtv/libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- mythtv/libs/libmythtv/NuppelVideoPlayer.cpp	(revision 24896)
+++ mythtv/libs/libmythtv/NuppelVideoPlayer.cpp	(working copy)
@@ -103,7 +103,7 @@
     kDisplayNUVTeletextCaptions,
 };
 
-NuppelVideoPlayer::NuppelVideoPlayer(bool muted)
+NuppelVideoPlayer::NuppelVideoPlayer()
     : decoder(NULL),                decoder_change_lock(QMutex::Recursive),
       videoOutput(NULL),            player_ctx(NULL),
       no_hardware_decoders(false),
@@ -173,7 +173,6 @@
       audio_channels(2),            audio_codec(0),
       audio_format(FORMAT_NONE),    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
@@ -373,70 +372,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();
@@ -881,13 +816,12 @@
 
     if (!audioOutput && !using_null_videoout && player_ctx->IsAudioNeeded())
     {
-        bool setVolume = gCoreContext->GetNumSetting("MythControlsVolume", 1);
         audioOutput = AudioOutput::OpenAudio(audio_main_device,
                                              audio_passthru_device,
                                              audio_format, audio_channels,
                                              audio_codec, audio_samplerate,
                                              AUDIOOUTPUT_VIDEO,
-                                             setVolume, audio_passthru);
+                                             audio_passthru);
         if (!audioOutput)
             errMsg = QObject::tr("Unable to create AudioOutput.");
         else
@@ -909,11 +843,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: mythtv/libs/libmythtv/avformatdecoder.cpp
===================================================================
--- mythtv/libs/libmythtv/avformatdecoder.cpp	(revision 24896)
+++ mythtv/libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -496,7 +496,6 @@
       // Audio
       audioSamples(NULL),
       allow_ac3_passthru(false),    allow_dts_passthru(false),
-      internal_vol(false),
       disable_passthru(false),      max_channels(2),
       last_ac3_channels(0),         last_framesRead(0),
       dummy_frame(NULL),
@@ -529,7 +528,6 @@
     if (stmp.startsWith("JACK:"))
         allow_dts_passthru = allow_ac3_passthru = false;
 
-    internal_vol = gCoreContext->GetNumSetting("MythControlsVolume", 0);
 
     audioIn.sample_size = -32; // force SetupAudioStream to run once
     itv = GetNVP()->GetInteractiveTV();
@@ -4668,10 +4666,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: mythtv/libs/libmythtv/tv_play.h
===================================================================
--- mythtv/libs/libmythtv/tv_play.h	(revision 24896)
+++ mythtv/libs/libmythtv/tv_play.h	(working copy)
@@ -33,7 +33,7 @@
 #include "programinfo.h"
 #include "channelutil.h"
 #include "videoouttypes.h"
-#include "volumebase.h"
+#include "volumecontrolmanager.h"
 #include "inputinfo.h"
 #include "channelgroup.h"
 
@@ -300,6 +300,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);
@@ -508,8 +511,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);
@@ -668,6 +670,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;
Index: mythtv/libs/libmythtv/playercontext.cpp
===================================================================
--- mythtv/libs/libmythtv/playercontext.cpp	(revision 24896)
+++ mythtv/libs/libmythtv/playercontext.cpp	(working copy)
@@ -432,8 +432,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 = gCoreContext->GetNumSetting("ExactSeeking", 0);
 
@@ -444,7 +443,7 @@
         return false;
     }
 
-    NuppelVideoPlayer *_nvp = new NuppelVideoPlayer(muted);
+    NuppelVideoPlayer *_nvp = new NuppelVideoPlayer();
 
     if (nohardwaredecoders)
         _nvp->DisableHardwareDecoders();
@@ -498,8 +497,6 @@
                 VERBOSE(VB_IMPORTANT, LOC_ERR + errMsg);
         }
     }
-    else if (pipState == kPBPRight)
-        nvp->SetMuted(true);
 
     int maxWait = -1;
     //if (isPIP())
Index: mythtv/libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- mythtv/libs/libmythtv/NuppelVideoPlayer.h	(revision 24896)
+++ mythtv/libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -4,7 +4,6 @@
 #include <sys/time.h>
 
 #include "playercontext.h"
-#include "volumebase.h"
 #include "audiooutputsettings.h"
 #include "RingBuffer.h"
 #include "osd.h"
@@ -90,7 +89,7 @@
     friend class PlayerContext;
 
   public:
-    NuppelVideoPlayer(bool muted = false);
+    NuppelVideoPlayer();
    ~NuppelVideoPlayer();
 
     // Initialization
@@ -113,10 +112,6 @@
     void SetAudioInfo(const QString &main, const QString &passthru, uint rate);
     void SetAudioParams(AudioFormat format, 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
@@ -173,11 +168,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; }
@@ -212,7 +205,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;
 
Index: mythtv/libs/libmythtv/tv_play.cpp
===================================================================
--- mythtv/libs/libmythtv/tv_play.cpp	(revision 24896)
+++ mythtv/libs/libmythtv/tv_play.cpp	(working copy)
@@ -1024,6 +1024,16 @@
     player.push_back(new PlayerContext(kPlayerInUseID));
     playerActive = 0;
     playerLock.unlock();
+
+    volumeControl = VolumeControlManager::GetControl(gCoreContext->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);
+    }
+
     VERBOSE(VB_PLAYBACK, LOC + "ctor -- end");
 }
 
@@ -5435,12 +5445,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)
@@ -5458,7 +5462,7 @@
         }
     }
 
-    RestartAllNVPs(mctx, pos, mctx_mute);
+    RestartAllNVPs(mctx, pos);
 
     VERBOSE(VB_PLAYBACK, LOC +
             QString("PxPToggleType() converting from %1 to %2 -- end")
@@ -5602,8 +5606,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(): ");
 
@@ -5650,7 +5653,6 @@
             pipctx->LockDeleteNVP(__FILE__, __LINE__);
             if (pipctx->nvp)
             {
-                pipctx->nvp->SetMuted(true);
                 pipctx->nvp->JumpToFrame(pos[i]);
             }
             pipctx->UnlockDeleteNVP(__FILE__, __LINE__);
@@ -5662,13 +5664,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)
@@ -5696,7 +5691,6 @@
         return;
     }
 
-    MuteState mctx_mute = mctx->nvp->GetMuteState();
     mctx->deleteNVPLock.unlock();
     pipctx->deleteNVPLock.unlock();
 
@@ -5710,7 +5704,7 @@
     playerActive = (ctx_index == playerActive) ?
         0 : ((ctx_index == 0) ? ctx_index : playerActive);
 
-    RestartAllNVPs(mctx, pos, mctx_mute);
+    RestartAllNVPs(mctx, pos);
 
     SetActive(mctx, playerActive, false);
 
@@ -5731,17 +5725,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");
@@ -6475,12 +6462,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();
@@ -6532,7 +6513,7 @@
 
             if (ctx->CreateNVP(
                     this, GetMythMainWindow(), ctx->GetState(),
-                    mctx->embedWinID, &mctx->embedBounds, muted))
+                    mctx->embedWinID, &mctx->embedBounds))
             {
                 ScheduleStateChange(ctx);
                 ok = true;
@@ -8171,25 +8152,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)
@@ -8338,40 +8307,8 @@
 
 void TV::ToggleMute(PlayerContext *ctx, const bool muteIndividualChannels)
 {
-    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)
@@ -9309,9 +9246,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");
         }
     }
@@ -11912,6 +11849,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: mythtv/libs/libmythtv/playercontext.h
===================================================================
--- mythtv/libs/libmythtv/playercontext.h	(revision 24896)
+++ mythtv/libs/libmythtv/playercontext.h	(working copy)
@@ -47,8 +47,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: mythtv/libs/libmythtv/avformatdecoder.h
===================================================================
--- mythtv/libs/libmythtv/avformatdecoder.h	(revision 24896)
+++ mythtv/libs/libmythtv/avformatdecoder.h	(working copy)
@@ -300,7 +300,6 @@
     short int        *audioSamples;
     bool              allow_ac3_passthru;
     bool              allow_dts_passthru;
-    bool              internal_vol;
     bool              disable_passthru;
     uint              max_channels;
     uint              last_ac3_channels;
Index: mythtv/libs/libmyth/audiooutputwin.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputwin.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputwin.cpp	(working copy)
@@ -274,44 +274,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)
-        VBERROR("Windows volume only supports stereo!");
-
-    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);
-    }
-
-    VBAUDIO(QString("SetVolume(%1) %2(%3)")
-            .arg(channel).arg(volume).arg(dwVolume));
-
-    waveOutSetVolume((HWAVEOUT)WAVE_MAPPER, dwVolume);
-}
Index: mythtv/libs/libmyth/volumecontrolmanager.cpp
===================================================================
--- mythtv/libs/libmyth/volumecontrolmanager.cpp	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/volumecontrolalsa.cpp
===================================================================
--- mythtv/libs/libmyth/volumecontrolalsa.cpp	(revision 0)
+++ mythtv/libs/libmyth/volumecontrolalsa.cpp	(revision 0)
@@ -0,0 +1,359 @@
+#include "volumecontrolalsa.h"
+
+#include "mythverbose.h"
+#include <algorithm>
+
+// static
+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: mythtv/libs/libmyth/audiooutputbase.h
===================================================================
--- mythtv/libs/libmyth/audiooutputbase.h	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputbase.h	(working copy)
@@ -19,6 +19,7 @@
 #include "audiooutputsettings.h"
 #include "samplerate.h"
 #include "mythverbose.h"
+#include "volumecontrolmanager.h"
 
 #define VBAUDIO(str)   VERBOSE(VB_AUDIO, LOC + str)
 #define VBAUDIOTS(str) VERBOSE(VB_AUDIO+VB_TIMESTAMP, LOC + str)
@@ -53,9 +54,6 @@
 
     virtual void Reset(void);
 
-    void SetSWVolume(int new_volume, bool save);
-    int GetSWVolume(void);
-
     // timecode is in milliseconds.
     virtual bool AddSamples(void *buffer, int frames, long long timecode);
 
@@ -143,7 +141,6 @@
     bool killaudio;
 
     bool pauseaudio, actually_paused, was_paused, unpause_when_ready;
-    bool set_initial_vol;
     bool buffer_output_data_for_use; //  used by AudioOutputNULL
 
     int configured_channels;
@@ -168,7 +165,7 @@
     int surround_mode;
     bool allow_ac3_passthru;
     float old_stretchfactor;
-    int volume;
+    QSharedPointer<VolumeControl> volume_control; ///< Volume Control interface
     QString volumeControl;
 
     bool processing;
Index: mythtv/libs/libmyth/audiooutputalsa.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputalsa.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputalsa.cpp	(working copy)
@@ -1,3 +1,5 @@
+#include "audiooutputalsa.h"
+
 #include <cstdio>
 #include <cstdlib>
 #include <sys/time.h>
@@ -8,7 +10,6 @@
 
 #include <QFile>
 #include "mythcorecontext.h"
-#include "audiooutputalsa.h"
 
 #define LOC QString("ALSA: ")
 #define LOC_WARN QString("ALSA, Warning: ")
@@ -40,9 +41,6 @@
     m_device(-1),
     m_subdevice(-1)
 {
-    m_mixer.handle = NULL;
-    m_mixer.elem = NULL;
-
     // Set everything up
     Reconfigure(settings);
 }
@@ -397,21 +395,12 @@
         return OpenDevice();
     }
 
-    if (internal_vol && !OpenMixer())
-    {
-        CloseDevice();
-        return false;
-    }
-
     // Device opened successfully
     return true;
 }
 
 void AudioOutputALSA::CloseDevice()
 {
-    if (m_mixer.handle)
-        snd_mixer_close(m_mixer.handle);
-    m_mixer.handle = NULL;
     if (pcm_handle)
     {
         snd_pcm_close(pcm_handle);
@@ -660,165 +649,6 @@
     return 0;
 }
 
-int AudioOutputALSA::GetVolumeChannel(int channel) const
-{
-    int retvol = 0;
-
-    if (!m_mixer.elem)
-        return retvol;
-
-    snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
-    if (!snd_mixer_selem_has_playback_channel(m_mixer.elem, chan))
-        return retvol;
-
-    long mixervol;
-    int chk;
-    if ((chk = snd_mixer_selem_get_playback_volume(m_mixer.elem,
-                                                   chan,
-                                                   &mixervol)) < 0)
-    {
-        VBERROR(QString("failed to get channel %1 volume, mixer %2/%3: %4")
-                .arg(channel).arg(m_mixer.device)
-                .arg(m_mixer.control)
-                .arg(snd_strerror(chk)));
-    }
-    else
-    {
-        retvol = (m_mixer.volrange != 0L) ? (mixervol - m_mixer.volmin) *
-                                            100.0f / m_mixer.volrange + 0.5f
-                                            : 0;
-        retvol = max(retvol, 0);
-        retvol = min(retvol, 100);
-        VBAUDIO(QString("get volume channel %1: %2")
-                .arg(channel).arg(retvol));
-    }
-    return retvol;
-}
-
-void AudioOutputALSA::SetVolumeChannel(int channel, int volume)
-{
-    if (!(internal_vol && m_mixer.elem))
-        return;
-
-    long mixervol = volume * m_mixer.volrange / 100.0f - m_mixer.volmin + 0.5f;
-    mixervol = max(mixervol, m_mixer.volmin);
-    mixervol = min(mixervol, m_mixer.volmax);
-
-    snd_mixer_selem_channel_id_t chan = (snd_mixer_selem_channel_id_t) channel;
-    if (snd_mixer_selem_set_playback_volume(m_mixer.elem, chan, mixervol) < 0)
-        VBERROR(QString("failed to set channel %1 volume").arg(channel));
-    else
-        VBAUDIO(QString("channel %1 volume set %2 => %3")
-                .arg(channel).arg(volume).arg(mixervol));
-}
-
-bool AudioOutputALSA::OpenMixer(void)
-{
-    if (!pcm_handle)
-    {
-        VBERROR("mixer setup without a pcm");
-        return false;
-    }
-    m_mixer.device = gCoreContext->GetSetting("MixerDevice", "default");
-    m_mixer.device = m_mixer.device.remove(QString("ALSA:"));
-    if (m_mixer.device.toLower() == "software")
-        return true;
-
-    m_mixer.control = gCoreContext->GetSetting("MixerControl", "PCM");
-
-    QString mixer_device_tag = QString("mixer device %1").arg(m_mixer.device);
-
-    int chk;
-    if ((chk = snd_mixer_open(&m_mixer.handle, 0)) < 0)
-    {
-        VBERROR(QString("failed to open mixer device %1: %2")
-                .arg(mixer_device_tag).arg(snd_strerror(chk)));
-        return false;
-    }
-
-    QByteArray dev_ba = m_mixer.device.toAscii();
-    struct snd_mixer_selem_regopt regopts =
-        {1, SND_MIXER_SABSTRACT_NONE, dev_ba.constData(), NULL, NULL};
-
-    if ((chk = snd_mixer_selem_register(m_mixer.handle, &regopts, NULL)) < 0)
-    {
-        snd_mixer_close(m_mixer.handle);
-        m_mixer.handle = NULL;
-        VBERROR(QString("failed to register %1: %2")
-                .arg(mixer_device_tag).arg(snd_strerror(chk)));
-        return false;
-    }
-
-    if ((chk = snd_mixer_load(m_mixer.handle)) < 0)
-    {
-        snd_mixer_close(m_mixer.handle);
-        m_mixer.handle = NULL;
-        VBERROR(QString("failed to load %1: %2")
-                .arg(mixer_device_tag).arg(snd_strerror(chk)));
-        return false;
-    }
-
-    m_mixer.elem = NULL;
-    uint elcount = snd_mixer_get_count(m_mixer.handle);
-    snd_mixer_elem_t* elx = snd_mixer_first_elem(m_mixer.handle);
-
-    for (uint ctr = 0; elx != NULL && ctr < elcount; ctr++)
-    {
-        QString tmp = QString(snd_mixer_selem_get_name(elx));
-        if (m_mixer.control == tmp &&
-            !snd_mixer_selem_is_enumerated(elx) &&
-            snd_mixer_selem_has_playback_volume(elx) &&
-            snd_mixer_selem_is_active(elx))
-        {
-            m_mixer.elem = elx;
-            VBAUDIO(QString("found playback control %1 on %2")
-                    .arg(m_mixer.control)
-                    .arg(mixer_device_tag));
-            break;
-        }
-        elx = snd_mixer_elem_next(elx);
-    }
-    if (!m_mixer.elem)
-    {
-        snd_mixer_close(m_mixer.handle);
-        m_mixer.handle = NULL;
-        VBERROR(QString("no playback control %1 found on %2")
-                .arg(m_mixer.control).arg(mixer_device_tag));
-        return false;
-    }
-    if ((snd_mixer_selem_get_playback_volume_range(m_mixer.elem,
-                                                   &m_mixer.volmin,
-                                                   &m_mixer.volmax) < 0))
-    {
-        snd_mixer_close(m_mixer.handle);
-        m_mixer.handle = NULL;
-        VBERROR(QString("failed to get volume range on %1/%2")
-                .arg(mixer_device_tag).arg(m_mixer.control));
-        return false;
-    }
-
-    m_mixer.volrange = m_mixer.volmax - m_mixer.volmin;
-    VBAUDIO(QString("mixer volume range on %1/%2 - min %3, max %4, range %5")
-            .arg(mixer_device_tag).arg(m_mixer.control)
-            .arg(m_mixer.volmin).arg(m_mixer.volmax).arg(m_mixer.volrange));
-    VBAUDIO(QString("%1/%2 set up successfully")
-            .arg(mixer_device_tag)
-            .arg(m_mixer.control));
-
-    if (set_initial_vol)
-    {
-        int initial_vol;
-        if (m_mixer.control == "PCM")
-            initial_vol = gCoreContext->GetNumSetting("PCMMixerVolume", 80);
-        else
-            initial_vol = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
-        for (int ch = 0; ch < channels; ++ch)
-            SetVolumeChannel(ch, initial_vol);
-    }
-
-    return true;
-}
-
 QMap<QString, QString> *AudioOutputALSA::GetALSADevices(const char *type)
 {
     QMap<QString, QString> *alsadevs = new QMap<QString, QString>();
Index: mythtv/libs/libmyth/volumebase.cpp
===================================================================
--- mythtv/libs/libmyth/volumebase.cpp	(revision 24896)
+++ mythtv/libs/libmyth/volumebase.cpp	(working copy)
@@ -1,138 +0,0 @@
-#include <cstdio>
-#include <cstdlib>
-
-#include <algorithm>
-using namespace std;
-
-#include <QString>
-
-#include "volumebase.h"
-#include "mythcorecontext.h"
-
-VolumeBase::VolumeBase() :
-    internal_vol(false), volume(80), 
-    current_mute_state(kMuteOff)
-{
-    swvol = swvol_setting =
-        (gCoreContext->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 = gCoreContext->GetSetting("MixerControl", "PCM");
-    controlLabel += "MixerVolume";
-    gCoreContext->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;
-    }
-    
-    for (int i = 0; i < channels; 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);
-}
-
-void VolumeBase::SetChannels(int new_channels)
-{
-    channels = new_channels;
-}
Index: mythtv/libs/libmyth/volumecontrolcoreaudio.h
===================================================================
--- mythtv/libs/libmyth/volumecontrolcoreaudio.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/volumecontrolsoftware.h
===================================================================
--- mythtv/libs/libmyth/volumecontrolsoftware.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/volumecontrolcoreaudio.cpp
===================================================================
--- mythtv/libs/libmyth/volumecontrolcoreaudio.cpp	(revision 0)
+++ mythtv/libs/libmyth/volumecontrolcoreaudio.cpp	(revision 0)
@@ -0,0 +1,271 @@
+#include "volumecontrolcoreaudio.h"
+
+#include <CoreServices/CoreServices.h>
+#include <CoreAudio/CoreAudio.h>
+
+#include "mythverbose.h"
+#include <algorithm>
+
+// static
+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: mythtv/libs/libmyth/volumebase.h
===================================================================
--- mythtv/libs/libmyth/volumebase.h	(revision 24896)
+++ mythtv/libs/libmyth/volumebase.h	(working copy)
@@ -1,53 +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);
-    void SetChannels(int new_channels);
-    bool internal_vol;
-
- private:
-
-    int volume;
-    MuteState current_mute_state;
-    bool swvol;
-    bool swvol_setting;
-    int channels;
-
-};
-
-#endif // __VOLUME_BASE__
Index: mythtv/libs/libmyth/volumecontrol.h
===================================================================
--- mythtv/libs/libmyth/volumecontrol.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/volumecontrolsoftware.cpp
===================================================================
--- mythtv/libs/libmyth/volumecontrolsoftware.cpp	(revision 0)
+++ mythtv/libs/libmyth/volumecontrolsoftware.cpp	(revision 0)
@@ -0,0 +1,90 @@
+#include "volumecontrolsoftware.h"
+
+#include "mythcorecontext.h"
+#include "mythverbose.h"
+#include <algorithm>
+
+// static
+QHash<QString, QString> VolumeControlSoftware::Enumerate()
+{
+    QHash<QString, QString> result;
+
+    result.insert("Software:", "Software Volume Processing");
+
+    return result;
+}
+
+VolumeControlSoftware::VolumeControlSoftware(QString device)
+{
+    int volume = gCoreContext->GetNumSetting("SoftwareVolume", 80);
+    m_Volume = std::min(std::max(0, volume), 100);
+    m_Mute = gCoreContext->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)
+    {
+        gCoreContext->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)
+    {
+        gCoreContext->SaveSetting("SoftwareMute", mute);
+        emit changedMute(mute);
+    }
+}
+
+QAtomicInt VolumeControlSoftware::m_Volume;
+QAtomicInt VolumeControlSoftware::m_Mute;
Index: mythtv/libs/libmyth/audiooutputpulse.h
===================================================================
--- mythtv/libs/libmyth/audiooutputpulse.h	(revision 24896)
+++ mythtv/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 Drain(void);
 
   protected:
Index: mythtv/libs/libmyth/volumecontroloss.h
===================================================================
--- mythtv/libs/libmyth/volumecontroloss.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/volumecontrolalsa.h
===================================================================
--- mythtv/libs/libmyth/volumecontrolalsa.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/audiooutputpulse.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputpulse.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputpulse.cpp	(working copy)
@@ -306,44 +306,6 @@
     return (int)latency * samplerate * output_bytes_per_frame / 1000000;
 }
 
-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)
-    {
-        VBERROR(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
-        VBERROR(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::Drain(void)
 {
     AudioOutputBase::Drain();
@@ -487,14 +449,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 = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
-        pa_cvolume_set(&volume_control, channels,
-                       (float)volume * (float)PA_VOLUME_NORM / 100.0f);
-    }
-    else
-        pa_cvolume_reset(&volume_control, channels);
+    pa_cvolume_reset(&volume_control, channels);
 
     fragment_size = (samplerate * 25 * output_bytes_per_frame) / 1000;
 
Index: mythtv/libs/libmyth/volumecontrolendpoint.cpp
===================================================================
--- mythtv/libs/libmyth/volumecontrolendpoint.cpp	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/audiooutputjack.h
===================================================================
--- mythtv/libs/libmyth/audiooutputjack.h	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputjack.h	(working copy)
@@ -18,10 +18,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
-
   protected:
 
     // You need to implement the following functions
@@ -37,8 +33,6 @@
 
   private:
 
-    void VolumeInit(void);
-
     // Our various callback functions
     inline int JackCallback(jack_nframes_t nframes);
     static int _JackCallback(jack_nframes_t nframes, void *arg);
Index: mythtv/libs/libmyth/audiosettings.cpp
===================================================================
--- mythtv/libs/libmyth/audiosettings.cpp	(revision 24896)
+++ mythtv/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               codec,
     int               samplerate,
     AudioOutputSource source,
-    bool              set_initial_vol,
     bool              use_passthru,
     int               upmixer_startup) :
     main_device(main_device),
@@ -53,7 +50,6 @@
     channels(channels),
     codec(codec),
     samplerate(samplerate),
-    set_initial_vol(set_initial_vol),
     use_passthru(use_passthru),
     source(source),
     upmixer(upmixer_startup)
@@ -73,7 +69,6 @@
     channels(channels),
     codec(codec),
     samplerate(samplerate),
-    set_initial_vol(false),
     use_passthru(use_passthru),
     source(AUDIOOUTPUT_UNKNOWN),
     upmixer(upmixer_startup)
Index: mythtv/libs/libmyth/audiooutputca.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputca.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputca.cpp	(working copy)
@@ -304,13 +304,6 @@
         return false;
     }
 
-    if (internal_vol && set_initial_vol)
-    {
-        QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM");
-        controlLabel += "MixerVolume";
-        SetCurrentVolume(gCoreContext->GetNumSetting(controlLabel, 80));
-    }
-
     return true;
 }
 
@@ -416,28 +409,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.0f);
-
-    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.01f), 0);
-}
-
 // IOProc style callback for SPDIF audio output
 static OSStatus RenderCallbackSPDIF(AudioDeviceID        inDevice,
                                     const AudioTimeStamp *inNow,
Index: mythtv/libs/libmyth/audiooutputwin.h
===================================================================
--- mythtv/libs/libmyth/audiooutputwin.h	(revision 24896)
+++ mythtv/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: mythtv/libs/libmyth/audiooutputjack.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputjack.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputjack.cpp	(working copy)
@@ -406,6 +406,8 @@
 
     return 0;
 }
+<<<<<<< .mine
+=======
 
 
 /* Our Jack XRun callback.
@@ -485,42 +487,6 @@
         chan_volumes[i] = volume;
 }
 
-int AudioOutputJACK::GetVolumeChannel(int channel) const
-{
-    unsigned int vol = 0;
-
-    if (!internal_vol)
-        return 100;
-
-    if (channel < JACK_CHANNELS_MAX)
-        vol = chan_volumes[channel];
-
-    return vol;
-}
-
-void AudioOutputJACK::SetVolumeChannel(int channel, int volume)
-{
-    if (internal_vol && (channel < JACK_CHANNELS_MAX))
-    {
-        chan_volumes[channel] = volume;
-        if (channel == 0)
-        {
-            // Left
-            chan_volumes[2] = volume; // left rear
-        }
-        else if (channel == 1)
-        {
-            // Right
-            chan_volumes[3] = volume; // right rear
-        }
-
-        // LFE and Center
-        chan_volumes[4] = chan_volumes[5] =
-                                    (chan_volumes[0] + chan_volumes[1]) / 2;
-    }
-}
-
-
 /* We don't need an audio output thread for Jack
   Everything handled by callbacks here
   Therefore we can loose all the Start/StopOutputThread, WriteAudio, etc
Index: mythtv/libs/libmyth/audiooutputoss.h
===================================================================
--- mythtv/libs/libmyth/audiooutputoss.h	(revision 24896)
+++ mythtv/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);
@@ -25,17 +21,10 @@
     AudioOutputSettings* GetOutputSettings(void);
 
   private:
-    void VolumeInit(void);
-    void VolumeCleanup(void);
-
     void SetFragSize(void);
 
     int audiofd;
     mutable int numbadioctls;
-
-    // Volume related
-    int mixerfd;
-    int control;
 };
 
 #endif
Index: mythtv/libs/libmyth/audiooutputbase.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputbase.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputbase.cpp	(working copy)
@@ -62,7 +62,6 @@
     pauseaudio(false),          actually_paused(false),
     was_paused(false),          unpause_when_ready(false),
 
-    set_initial_vol(settings.set_initial_vol),
     buffer_output_data_for_use(false),
 
     // private
@@ -76,7 +75,7 @@
     source_bytes_per_frame(0),
     needs_upmix(false),         needs_downmix(false),
     surround_mode(QUALITY_LOW), old_stretchfactor(1.0f),
-    volume(80),                 volumeControl(NULL),
+    volumeControl(NULL),
 
     processing(false),
 
@@ -122,6 +121,9 @@
     AudioOutput::AudioSetup(max_channels, allow_ac3_passthru,
                             ignore, allow_multipcm, upmix_default);
 
+    if (gCoreContext->GetSetting("MixerDevice") == "Software:")
+        volume_control = VolumeControlManager::GetControl("Software:");
+    
     if (settings.upmixer == 0)      // Use general settings (video)
         configured_channels = upmix_default ? max_channels : 2;
     else if (settings.upmixer == 1) // music, upmixer off
@@ -325,7 +327,6 @@
 
     killaudio = pauseaudio = false;
     was_paused = true;
-    internal_vol = gCoreContext->GetNumSetting("MythControlsVolume", 0);
 
     // Ask the subclass what we can send to the device
     if (!output_settings)
@@ -405,7 +406,7 @@
 
     // Turn on float conversion?
     if (need_resampler || needs_upmix || needs_downmix ||
-        stretchfactor != 1.0f || (internal_vol && SWVolume()) ||
+        stretchfactor != 1.0f || (volume_control) ||
         (enc && output_format != FORMAT_S16) ||
         !output_settings->IsSupportedFormat(output_format))
     {
@@ -444,19 +445,6 @@
         return;
     }
 
-    // Only used for software volume
-    if (set_initial_vol && internal_vol && SWVolume())
-    {
-        VBAUDIO("Software volume enabled");
-        volumeControl  = gCoreContext->GetSetting("MixerControl", "PCM");
-        volumeControl += "MixerVolume";
-        volume = gCoreContext->GetNumSetting(volumeControl, 80);
-    }
-
-    VolumeBase::SetChannels(channels);
-    VolumeBase::SyncVolume();
-    VolumeBase::UpdateVolume();
-
     VBAUDIO(QString("Audio fragment size: %1").arg(fragment_size));
 
     audbuf_timecode = audiotime = frames_buffered = 0;
@@ -733,24 +721,6 @@
 }
 
 /**
- * Set the volume for software volume control
- */
-void AudioOutputBase::SetSWVolume(int new_volume, bool save)
-{
-    volume = new_volume;
-    if (save && volumeControl != NULL)
-        gCoreContext->SaveSetting(volumeControl, volume);
-}
-
-/**
- * Get the volume for software volume control
- */
-int AudioOutputBase::GetSWVolume()
-{
-    return volume;
-}
-
-/**
  * Check that there's enough space in the audiobuffer to write the provided
  * number of frames
  *
@@ -998,10 +968,11 @@
         org_waud += nFrames * bpf;
     }
 
-    if (internal_vol && SWVolume())
+    if (volume_control)
     {
         org_waud    = waud;
         int num     = len;
+        int volume  = volume_control->volume();
 
         if (bdiff <= num)
         {
@@ -1214,12 +1185,10 @@
     raud += frag_size;
 
     // Mute individual channels through mono->stereo duplication
-    MuteState mute_state = GetMuteState();
-    if (written_size && channels > 1 &&
-        (mute_state == kMuteLeft || mute_state == kMuteRight))
+    bool mute = volume_control ? volume_control->mute() : false;
+    if (written_size && channels > 1 && mute)
     {
-        AudioOutputUtil::MuteChannel(obytes << 3, channels,
-                                     mute_state == kMuteLeft ? 0 : 1,
+        AudioOutputUtil::MuteChannel(obytes << 3, channels, 1,
                                      buffer, written_size);
     }
 
Index: mythtv/libs/libmyth/audiooutput.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutput.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutput.cpp	(working copy)
@@ -31,12 +31,11 @@
 AudioOutput *AudioOutput::OpenAudio(
     const QString &main_device, const QString &passthru_device,
     AudioFormat format, int channels, int codec, int samplerate,
-    AudioOutputSource source, bool set_initial_vol, bool passthru,
-    int upmixer_startup)
+    AudioOutputSource source, bool passthru, int upmixer_startup)
 {
     AudioSettings settings(
         main_device, passthru_device, format, channels, codec, samplerate,
-        source, set_initial_vol, passthru, upmixer_startup);
+        source, passthru, upmixer_startup);
 
     settings.FixPassThrough();
 
Index: mythtv/libs/libmyth/libmyth.pro
===================================================================
--- mythtv/libs/libmyth/libmyth.pro	(revision 24896)
+++ mythtv/libs/libmyth/libmyth.pro	(working copy)
@@ -24,7 +24,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
@@ -51,7 +52,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
@@ -93,12 +95,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 audiooutputsettings.h util.h
 inc.files += inetcomms.h mythmedia.h mythcdrom.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
@@ -117,8 +119,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
 }
 
@@ -146,14 +148,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
 
@@ -193,8 +198,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: mythtv/libs/libmyth/audiosettings.h
===================================================================
--- mythtv/libs/libmyth/audiosettings.h	(revision 24896)
+++ mythtv/libs/libmyth/audiosettings.h	(working copy)
@@ -33,7 +33,6 @@
         int               codec,
         int               samplerate,
         AudioOutputSource source,
-        bool              set_initial_vol,
         bool              use_passthru,
         int               upmixer_startup = 0);
 
@@ -59,7 +58,6 @@
     int               channels;
     int               codec;
     int               samplerate;
-    bool              set_initial_vol;
     bool              use_passthru;
     AudioOutputSource source;
     int               upmixer;
Index: mythtv/libs/libmyth/audiooutputca.h
===================================================================
--- mythtv/libs/libmyth/audiooutputca.h	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputca.h	(working copy)
@@ -26,10 +26,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);   }
 
@@ -38,6 +34,7 @@
 
     void Warn(QString msg)
     {   VERBOSE(VB_IMPORTANT, "AudioOutputCA Warning: " + msg);   }
+    bool internal_vol;
 
 protected:
 
Index: mythtv/libs/libmyth/audiooutputoss.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputoss.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputoss.cpp	(working copy)
@@ -32,8 +32,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);
@@ -202,10 +201,6 @@
     else
         OERROR("Unable to get audio card capabilities");
 
-    // Setup volume control
-    if (internal_vol)
-        VolumeInit();
-
     // Device opened successfully
     return true;
 }
@@ -216,8 +211,6 @@
         close(audiofd);
 
     audiofd = -1;
-
-    VolumeCleanup();
 }
 
 
@@ -257,108 +250,3 @@
     return soundcard_buffer;
 }
 
-void AudioOutputOSS::VolumeInit()
-{
-    mixerfd = -1;
-    int volume = 0;
-
-    QString device = gCoreContext->GetSetting("MixerDevice", "/dev/mixer");
-    if (device.toLower() == "software")
-        return;
-
-    QByteArray dev = device.toAscii();
-    mixerfd = open(dev.constData(), O_RDONLY);
-
-    QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM");
-
-    if (controlLabel == "Master")
-        control = SOUND_MIXER_VOLUME;
-    else
-        control = SOUND_MIXER_PCM;
-
-    if (mixerfd < 0)
-    {
-        VBERROR(QString("Unable to open mixer: '%1'").arg(device));
-        return;
-    }
-
-    if (set_initial_vol)
-    {
-        int tmpVol;
-        volume = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
-        tmpVol = (volume << 8) + volume;
-        int ret = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &tmpVol);
-        if (ret < 0)
-            VBERROR(QString("Error Setting initial Master Volume") + ENO);
-
-        volume = gCoreContext->GetNumSetting("PCMMixerVolume", 80);
-        tmpVol = (volume << 8) + volume;
-        ret = ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &tmpVol);
-        if (ret < 0)
-            VBERROR(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)
-    {
-        VBERROR(QString("Error reading volume for channel %1").arg(channel));
-        return 0;
-    }
-
-    if (channel == 0)
-        volume = tmpVol & 0xff; // left
-    else if (channel == 1)
-        volume = (tmpVol >> 8) & 0xff; // right
-    else
-        VBERROR("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!
-        VBERROR(QString("Error setting channel %1. Only 2 ch 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)
-            VBERROR(QString("Error setting volume on channel %1").arg(channel));
-    }
-}
-
Index: mythtv/libs/libmyth/audiooutputdx.cpp
===================================================================
--- mythtv/libs/libmyth/audiooutputdx.cpp	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputdx.cpp	(working copy)
@@ -476,9 +476,6 @@
                     | 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;
 
@@ -549,47 +546,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)
-    {
-        VBERROR(QString("Failed to get volume %1").arg(dxVolume));
-        return volume;
-    }
-
-    VBAUDIO(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)
-    {
-        VBERROR(QString("Failed to set volume %1").arg(dxVolume));
-        return;
-    }
-
-    VBAUDIO(QString("Set volume %1").arg(dxVolume));
-}
-
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: mythtv/libs/libmyth/audiooutputnull.h
===================================================================
--- mythtv/libs/libmyth/audiooutputnull.h	(revision 24896)
+++ mythtv/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: mythtv/libs/libmyth/audiooutput.h
===================================================================
--- mythtv/libs/libmyth/audiooutput.h	(revision 24896)
+++ mythtv/libs/libmyth/audiooutput.h	(working copy)
@@ -5,10 +5,9 @@
 
 #include "audiosettings.h"
 #include "mythcorecontext.h"
-#include "volumebase.h"
 #include "output.h"
 
-class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners
+class MPUBLIC AudioOutput : public OutputListeners
 {
  public:
     static void AudioSetup(int &max_channels, bool &allow_ac3_passthru,
@@ -19,11 +18,10 @@
     static AudioOutput *OpenAudio(
         const QString &audiodevice, const QString &passthrudevice,
         AudioFormat format, int channels, int codec, int samplerate,
-        AudioOutputSource source, bool set_initial_vol, bool passthru,
-        int upmixer_startup = 0);
+        AudioOutputSource source, bool passthru, int upmixer_startup = 0);
 
     AudioOutput() :
-        VolumeBase(),             OutputListeners(),
+        OutputListeners(),
         lastError(QString::null), lastWarn(QString::null) {}
 
     virtual ~AudioOutput() { };
Index: mythtv/libs/libmyth/volumecontrolmanager.h
===================================================================
--- mythtv/libs/libmyth/volumecontrolmanager.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/volumecontrolendpoint.h
===================================================================
--- mythtv/libs/libmyth/volumecontrolendpoint.h	(revision 0)
+++ mythtv/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: mythtv/libs/libmyth/audiooutputdx.h
===================================================================
--- mythtv/libs/libmyth/audiooutputdx.h	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputdx.h	(working copy)
@@ -14,9 +14,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: mythtv/libs/libmyth/audiooutputalsa.h
===================================================================
--- mythtv/libs/libmyth/audiooutputalsa.h	(revision 24896)
+++ mythtv/libs/libmyth/audiooutputalsa.h	(working copy)
@@ -17,9 +17,6 @@
     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
     static QMap<QString, QString> *GetALSADevices(const char *type);
 
   protected:
@@ -38,8 +35,6 @@
     inline int SetParameters(snd_pcm_t *handle, snd_pcm_format_t format,
                              uint channels, uint rate, uint buffer_time,
                              uint period_time);
-    // Volume related
-    bool OpenMixer(void);
 
   private:
     snd_pcm_t   *pcm_handle;
@@ -49,15 +44,5 @@
     QMutex       killAudioLock;
     snd_pcm_sframes_t (*pcm_write_func)(snd_pcm_t*, const void*,
                                         snd_pcm_uframes_t);
-    struct {
-        QString            device;
-        QString            control;
-        snd_mixer_t*       handle;
-        snd_mixer_elem_t*  elem;
-        long               volmin;
-        long               volmax;
-        long               volrange;
-    } m_mixer;
-
 };
 #endif
Index: mythtv/libs/libmyth/volumecontroloss.cpp
===================================================================
--- mythtv/libs/libmyth/volumecontroloss.cpp	(revision 0)
+++ mythtv/libs/libmyth/volumecontroloss.cpp	(revision 0)
@@ -0,0 +1,389 @@
+#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)
+
+// static
+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: mythtv/programs/mythfrontend/globalsettings.cpp
===================================================================
--- mythtv/programs/mythfrontend/globalsettings.cpp	(revision 24896)
+++ mythtv/programs/mythfrontend/globalsettings.cpp	(working copy)
@@ -32,6 +32,7 @@
 #include "iso639.h"
 #include "playbackbox.h"
 #include "globalsettings.h"
+#include "libmyth/volumecontrolmanager.h"
 #include "recordingprofile.h"
 #include "mythxdisplay.h"
 #include "DisplayRes.h"
@@ -207,88 +208,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 *AC3PassThrough()
 {
     HostCheckBox *gc = new HostCheckBox("AC3PassThru");
@@ -3495,39 +3431,20 @@
     group2->addChild(settings4);
 
     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());
+    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");
@@ -4211,7 +4128,7 @@
 
     addChild(AudioSystemSettingsGroup());
 
-    addChild(new AudioMixerSettingsGroup());
+    addChild(AudioMixerSettingsGroup());
 
     VerticalConfigurationGroup *general =
         new VerticalConfigurationGroup(false, true, false, false);
Index: mythtv/programs/mythtranscode/transcode.cpp
===================================================================
--- mythtv/programs/mythtranscode/transcode.cpp	(revision 24896)
+++ mythtv/programs/mythtranscode/transcode.cpp	(working copy)
@@ -137,67 +137,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; }
