diff --git a/mythtv/libs/libmyth/audiooutputalsa.cpp b/mythtv/libs/libmyth/audiooutputalsa.cpp
index 1a66dfe..cce3258 100644
--- a/mythtv/libs/libmyth/audiooutputalsa.cpp
+++ b/mythtv/libs/libmyth/audiooutputalsa.cpp
@@ -699,6 +699,9 @@ void AudioOutputALSA::SetupMixer(void)
     if (mixer_handle != NULL)
         CloseMixer();
 
+    if (alsadevice.toLower() == "software")
+        return;
+
     VERBOSE(VB_AUDIO, QString("Opening mixer %1").arg(device));
 
     // TODO: This is opening card 0. Fix for case of multiple soundcards
diff --git a/mythtv/libs/libmyth/audiooutputbase.cpp b/mythtv/libs/libmyth/audiooutputbase.cpp
index 112f554..e9bdb2f 100644
--- a/mythtv/libs/libmyth/audiooutputbase.cpp
+++ b/mythtv/libs/libmyth/audiooutputbase.cpp
@@ -53,6 +53,7 @@ AudioOutputBase::AudioOutputBase(const AudioSettings &settings) :
     needs_upmix(false),
     surround_mode(FreeSurround::SurroundModePassive),
     old_audio_stretchfactor(1.0),
+    volume(80),
 
     blocking(false),
 
@@ -269,6 +270,14 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
     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;
@@ -309,7 +318,11 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
         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")
@@ -600,6 +613,62 @@ int AudioOutputBase::GetAudioBufferedTime(void)
      return audbuf_timecode - GetAudiotime();
 }
 
+void AudioOutputBase::SetSWVolume(int new_volume)
+{
+    volume = new_volume;
+    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)
 {
@@ -943,6 +1012,20 @@ void AudioOutputBase::_AddSamples(void *buffer, bool interleaved, int samples,
         
     }
 
+    if (internal_vol && SWVolume()) 
+    {
+        int bdiff = kAudioRingBufferSize - 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 9a264a7..20f32ef 100644
--- a/mythtv/libs/libmyth/audiooutputbase.h
+++ b/mythtv/libs/libmyth/audiooutputbase.h
@@ -47,6 +47,9 @@ class AudioOutputBase : public AudioOutput, public QThread
 
     virtual void Reset(void);
 
+    void SetSWVolume(int new_volume);
+    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);
@@ -150,6 +153,11 @@ class AudioOutputBase : public AudioOutput, public QThread
     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;
@@ -166,6 +174,7 @@ class AudioOutputBase : public AudioOutput, public QThread
     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 a18f147..348a60f 100644
--- a/mythtv/libs/libmyth/audiooutputoss.cpp
+++ b/mythtv/libs/libmyth/audiooutputoss.cpp
@@ -287,6 +287,8 @@ void AudioOutputOSS::VolumeInit()
     int volume = 0;
 
     QString device = gContext->GetSetting("MixerDevice", "/dev/mixer");
+    if (device.toLower() == "software")
+        return;
     QByteArray dev = device.toAscii();
     mixerfd = open(dev.constData(), O_RDONLY);
 
diff --git a/mythtv/libs/libmyth/volumebase.cpp b/mythtv/libs/libmyth/volumebase.cpp
index d1f1fb0..be28940 100644
--- a/mythtv/libs/libmyth/volumebase.cpp
+++ b/mythtv/libs/libmyth/volumebase.cpp
@@ -10,6 +10,20 @@ VolumeBase::VolumeBase() :
     internal_vol(false), volume(80), 
     current_mute_state(kMuteOff)
 {
+    swvol = swvol_setting = 
+      (gContext->GetSetting("MixerDevice", "default").toLower() == "software");
+}
+
+bool VolumeBase::SWVolume(void)
+{
+    return swvol;
+}
+
+void VolumeBase::SWVolume(bool set)
+{
+    if (swvol_setting)
+        return;
+    swvol = set;
 }
 
 uint VolumeBase::GetCurrentVolume(void) const
@@ -80,6 +94,12 @@ void VolumeBase::UpdateVolume(void)
     {
         new_volume = 0;
     }
+
+    if (swvol) 
+    {
+        SetSWVolume(new_volume);
+        return;
+    }
     
     // TODO: Avoid assumption that there are 2 channels!
     for (int i = 0; i < 2; i++)
@@ -102,6 +122,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 4257f0b..8c658da 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 uint GetCurrentVolume(void) const;
     virtual void SetCurrentVolume(int value);
     virtual void AdjustCurrentVolume(int change);
@@ -34,6 +36,8 @@ class MPUBLIC VolumeBase
 
     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) = 0;
+    virtual int GetSWVolume(void) = 0;
 
     void UpdateVolume(void);
     void SyncVolume(void);
@@ -44,6 +48,8 @@ class MPUBLIC VolumeBase
     
     int volume;
     MuteState current_mute_state;
+    bool swvol;
+    bool swvol_setting;
 
 };
 
diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
index 50c2919..e963be9 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.cpp
+++ b/mythtv/libs/libmythtv/avformatdecoder.cpp
@@ -427,6 +427,7 @@ AvFormatDecoder::AvFormatDecoder(NuppelVideoPlayer *parent,
       // Audio
       audioSamples(NULL),
       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
@@ -448,6 +449,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
@@ -4129,9 +4131,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 57311b6..fb9cfe7 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.h
+++ b/mythtv/libs/libmythtv/avformatdecoder.h
@@ -269,6 +269,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 f3b7f04..57d7280 100644
--- a/mythtv/programs/mythtranscode/transcode.cpp
+++ b/mythtv/programs/mythtranscode/transcode.cpp
@@ -227,6 +227,17 @@ class AudioReencodeBuffer : public AudioOutput
         // Do nothing
         return false;
     }
+    virtual void SetSWVolume(int new_volume) 
+    {
+        // 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; }
