diff --git a/mythtv/libs/libmyth/audiooutputalsa.cpp b/mythtv/libs/libmyth/audiooutputalsa.cpp
index 96e06cb..1f5fde3 100644
--- a/mythtv/libs/libmyth/audiooutputalsa.cpp
+++ b/mythtv/libs/libmyth/audiooutputalsa.cpp
@@ -698,6 +698,9 @@ void AudioOutputALSA::SetupMixer(void)
     if (mixer_handle != NULL)
         CloseMixer();
 
+    if (alsadevice.lower() == "software")
+        return;
+
     VERBOSE(VB_AUDIO, QString("Opening mixer %1").arg(device));
 
     // TODO: This is opening card 0. Fix for case of multiple soundcards
diff --git a/mythtv/libs/libmyth/audiooutputbase.cpp b/mythtv/libs/libmyth/audiooutputbase.cpp
index ad58b26..62751e5 100644
--- a/mythtv/libs/libmyth/audiooutputbase.cpp
+++ b/mythtv/libs/libmyth/audiooutputbase.cpp
@@ -61,6 +61,7 @@ AudioOutputBase::AudioOutputBase(
     needs_upmix(false),
     surround_mode(FreeSurround::SurroundModePassive),
     old_audio_stretchfactor(1.0),
+    volume(80),
 
     blocking(false),
 
@@ -270,6 +271,14 @@ void AudioOutputBase::Reconfigure(int laudio_bits, int laudio_channels,
     if(audio_passthru || audio_enc)
         // AC-3 output - soundcard expects a 2ch 48k stream
 	audio_channels = 2;
+
+    if (internal_vol && audio_enc && audio_reenc) 
+    {
+        VERBOSE(VB_AUDIO, LOC + "Using software vol control for AC-3");
+        SWVolume(true);
+    }
+    else
+        SWVolume(false);
     
     audio_bytes_per_sample = audio_channels * audio_bits / 8;
     source_audio_bytes_per_sample = source_audio_channels * audio_bits / 8;
@@ -314,7 +323,11 @@ void AudioOutputBase::Reconfigure(int laudio_bits, int laudio_channels,
         VERBOSE(VB_AUDIO, "Aborting reconfigure");
         return;
     }
-
+    
+    // Only used for software volume
+    if (set_initial_vol && internal_vol) 
+        volume = gContext->GetNumSetting("PCMMixerVolume", 100);
+    
     SyncVolume();
     
     VERBOSE(VB_AUDIO, LOC + QString("Audio fragment size: %1")
@@ -625,6 +638,64 @@ void AudioOutputBase::SetAudiotime(void)
     pthread_mutex_unlock(&audio_buflock);
 }
 
+void AudioOutputBase::SetSWVolume(int new_volume, bool save)
+{
+    volume = new_volume;
+    if (save)
+    {
+        QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
+        controlLabel += "MixerVolume";
+        gContext->SaveSetting(controlLabel, volume);    
+    }
+}
+
+int AudioOutputBase::GetSWVolume() 
+{
+    return volume;
+}
+
+template <class AudioDataType>
+void AudioOutputBase::_AdjustVolume(AudioDataType *buffer, int len, bool music) 
+{
+    float g = volume / 100.0;
+
+    // Should probably be logarithmic - this'll do
+    g *= g;
+    
+    // Add gain to AC-3 - try to ~ match PCM volume
+    if (audio_enc && audio_reenc)
+        g *= 1.8;
+
+    // Music is relatively loud - ditto
+    else if (music)
+        g *= 0.4;
+
+    if (g == 1.0)
+        return;
+
+    for (int i = 0; i < (int)(len / sizeof(AudioDataType)); i++)
+    {
+        float s = static_cast<float>(buffer[i]) * g /
+                  static_cast<float>(numeric_limits<AudioDataType>::max());
+        if (s >= 1.0)
+            buffer[i] = numeric_limits<AudioDataType>::max();
+        else if (s <= -1.0)
+            buffer[i] = numeric_limits<AudioDataType>::min();
+        else
+            buffer[i] = static_cast<AudioDataType>
+                        (s * numeric_limits<AudioDataType>::max());
+    }
+}
+
+void AudioOutputBase::AdjustVolume(void *buffer, int len, bool music) {
+
+    if (audio_bits == 8)
+        _AdjustVolume<char>((char *)buffer, len, music);
+    else if (audio_bits == 16)
+        _AdjustVolume<short>((short *)buffer, len, music);
+
+}
+
 bool AudioOutputBase::AddSamples(char *buffers[], int samples, 
                                  long long timecode)
 {
@@ -970,6 +1041,20 @@ void AudioOutputBase::_AddSamples(void *buffer, bool interleaved, int samples,
         
     }
 
+    if (internal_vol && SWVolume()) 
+    {
+        int bdiff = AUDBUFSIZE - waud;
+        bool music = (timecode < 1);
+
+        if (bdiff < len)
+        {
+            AdjustVolume(audiobuffer + waud, bdiff, music);
+            AdjustVolume(audiobuffer, len - bdiff, music);
+        }
+        else
+            AdjustVolume(audiobuffer + waud, len, music);
+    }
+
     // Encode to AC-3? 
     if (encoder) 
     {
diff --git a/mythtv/libs/libmyth/audiooutputbase.h b/mythtv/libs/libmyth/audiooutputbase.h
index 334c530..b6ebe99 100644
--- a/mythtv/libs/libmyth/audiooutputbase.h
+++ b/mythtv/libs/libmyth/audiooutputbase.h
@@ -62,6 +62,9 @@ class AudioOutputBase : public AudioOutput
 
     virtual void Reset(void);
 
+    void SetSWVolume(int new_volume, bool save);
+    int GetSWVolume(void);
+
     // timecode is in milliseconds.
     virtual bool AddSamples(char *buffer, int samples, long long timecode);
     virtual bool AddSamples(char *buffers[], int samples, long long timecode);
@@ -116,7 +119,7 @@ class AudioOutputBase : public AudioOutput
     int audiofree(bool use_lock); // number of free bytes in audio buffer
 
     void UpdateVolume(void);
-    
+
     void SetStretchFactorLocked(float factor);
 
     int GetBaseAudioTime()                    const { return audiotime;       }
@@ -156,6 +159,11 @@ class AudioOutputBase : public AudioOutput
     int src_quality;
 
  private:
+    // software volume
+    template <class AudioDataType> 
+    void _AdjustVolume(AudioDataType *buffer, int len, bool music);
+    void AdjustVolume(void *buffer, int len, bool music);
+    
     // resampler
     bool need_resampler;
     SRC_STATE *src_ctx;
@@ -176,6 +184,7 @@ class AudioOutputBase : public AudioOutput
     int surround_mode;
     bool allow_ac3_passthru;
     float old_audio_stretchfactor;
+    int volume;
 
     bool blocking; // do AddSamples calls block?
 
diff --git a/mythtv/libs/libmyth/audiooutputoss.cpp b/mythtv/libs/libmyth/audiooutputoss.cpp
index 3436579..f0cd5e8 100644
--- a/mythtv/libs/libmyth/audiooutputoss.cpp
+++ b/mythtv/libs/libmyth/audiooutputoss.cpp
@@ -291,6 +291,8 @@ void AudioOutputOSS::VolumeInit()
     int volume = 0;
 
     QString device = gContext->GetSetting("MixerDevice", "/dev/mixer");
+    if (device.lower() == "software")
+        return;
     mixerfd = open(device.ascii(), O_RDONLY);
 
     QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
diff --git a/mythtv/libs/libmyth/volumebase.cpp b/mythtv/libs/libmyth/volumebase.cpp
index 6823bbc..2431ff2 100644
--- a/mythtv/libs/libmyth/volumebase.cpp
+++ b/mythtv/libs/libmyth/volumebase.cpp
@@ -10,8 +10,23 @@ VolumeBase::VolumeBase()
     volume = 80;
     current_mute_state=MUTE_OFF;
     internal_vol = false;
+    swvol = swvol_setting = 
+        (gContext->GetSetting("MixerDevice", "default").lower() == "software");
 };
 
+bool VolumeBase::SWVolume(void)
+{
+    return swvol;
+}
+
+void VolumeBase::SWVolume(bool set)
+{
+    if (swvol_setting)
+        return;
+    swvol = set;
+}
+
+
 int VolumeBase::GetCurrentVolume(void)
 {
     return volume;
@@ -88,9 +103,17 @@ kMuteState VolumeBase::IterateMutedChannels(void)
 void VolumeBase::UpdateVolume(void)
 {
     int new_volume = volume;
+    bool save = true;
     if (current_mute_state == MUTE_BOTH)
     {
         new_volume = 0;
+        save = false;
+    }
+
+    if (swvol) 
+    {
+        SetSWVolume(new_volume, save);
+        return;
     }
     
     // TODO: Avoid assumption that there are 2 channels!
@@ -114,6 +137,9 @@ void VolumeBase::UpdateVolume(void)
 void VolumeBase::SyncVolume(void)
 {
     // Read the volume from the audio driver and setup our internal state to match
-    volume = GetVolumeChannel(0);
+    if (swvol) 
+        volume = GetSWVolume();
+    else
+        volume = GetVolumeChannel(0);
 }
 
diff --git a/mythtv/libs/libmyth/volumebase.h b/mythtv/libs/libmyth/volumebase.h
index f41a5d7..0601568 100644
--- a/mythtv/libs/libmyth/volumebase.h
+++ b/mythtv/libs/libmyth/volumebase.h
@@ -20,6 +20,8 @@ class MPUBLIC VolumeBase
     VolumeBase();    
     virtual ~VolumeBase() {};
 
+    void SWVolume(bool set);
+    bool SWVolume(void);
     virtual int GetCurrentVolume(void);
     virtual void SetCurrentVolume(int value);
     virtual void AdjustCurrentVolume(int change);
@@ -32,6 +34,8 @@ class MPUBLIC VolumeBase
 
     virtual int GetVolumeChannel(int channel) = 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);
@@ -42,6 +46,8 @@ class MPUBLIC VolumeBase
     
     int volume;
     kMuteState current_mute_state;
+    bool swvol;
+    bool swvol_setting;
 
 };
 
diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
index 9ffb7a0..d5221cd 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.cpp
+++ b/mythtv/libs/libmythtv/avformatdecoder.cpp
@@ -410,6 +410,7 @@ AvFormatDecoder::AvFormatDecoder(NuppelVideoPlayer *parent,
       // Audio
       audioSamples(new short int[AVCODEC_MAX_AUDIO_FRAME_SIZE]),
       allow_ac3_passthru(false),    allow_dts_passthru(false),
+      internal_vol(false),
       disable_passthru(false),      max_channels(2),
       last_ac3_channels(0),	    dummy_frame(NULL),
       // DVD
@@ -428,6 +429,7 @@ AvFormatDecoder::AvFormatDecoder(NuppelVideoPlayer *parent,
 
     allow_ac3_passthru = gContext->GetNumSetting("AC3PassThru", false);
     allow_dts_passthru = gContext->GetNumSetting("DTSPassThru", false);
+    internal_vol = gContext->GetNumSetting("MythControlsVolume", 0);
     max_channels = (uint) gContext->GetNumSetting("MaxChannels", 2);
 
     audioIn.sample_size = -32; // force SetupAudioStream to run once
@@ -3958,9 +3960,10 @@ bool AvFormatDecoder::DoPassThrough(const AVCodecContext *ctx)
 
     if (ctx->codec_id == CODEC_ID_AC3)
         passthru = allow_ac3_passthru && 
-                   ctx->channels >= (int)max_channels;
+                   ctx->channels >= (int)max_channels &&
+                   !internal_vol;
     else if (ctx->codec_id == CODEC_ID_DTS)
-        passthru = allow_dts_passthru;
+        passthru = allow_dts_passthru && !internal_vol;
     
     passthru &= !transcoding && !disable_passthru;
     // Don't know any cards that support spdif clocked at < 44100
diff --git a/mythtv/libs/libmythtv/avformatdecoder.h b/mythtv/libs/libmythtv/avformatdecoder.h
index f008136..482ffe2 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.h
+++ b/mythtv/libs/libmythtv/avformatdecoder.h
@@ -263,6 +263,7 @@ class AvFormatDecoder : public DecoderBase
     short int        *audioSamples;
     bool              allow_ac3_passthru;
     bool              allow_dts_passthru;
+    bool              internal_vol;
     bool              disable_passthru;
     uint              max_channels;
     uint              last_ac3_channels;
diff --git a/mythtv/programs/mythtranscode/transcode.cpp b/mythtv/programs/mythtranscode/transcode.cpp
index c6adf51..cd58eba 100644
--- a/mythtv/programs/mythtranscode/transcode.cpp
+++ b/mythtv/programs/mythtranscode/transcode.cpp
@@ -221,6 +221,17 @@ class AudioReencodeBuffer : public AudioOutput
         // 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; }
