diff -Naur --exclude=.svn mythtv.ori/libs/libmyth/audiooutputalsa.cpp mythtv/libs/libmyth/audiooutputalsa.cpp
--- mythtv.ori/libs/libmyth/audiooutputalsa.cpp	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/libs/libmyth/audiooutputalsa.cpp	2009-08-31 01:32:54.000000000 +1000
@@ -780,6 +780,9 @@
     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 -Naur --exclude=.svn mythtv.ori/libs/libmyth/audiooutputbase.cpp mythtv/libs/libmyth/audiooutputbase.cpp
--- mythtv.ori/libs/libmyth/audiooutputbase.cpp	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/libs/libmyth/audiooutputbase.cpp	2009-08-31 01:40:53.000000000 +1000
@@ -1,5 +1,6 @@
 // Std C headers
 #include <cmath>
+#include <limits>
 
 // POSIX headers
 #include <unistd.h>
@@ -53,6 +54,7 @@
     needs_upmix(false),
     surround_mode(FreeSurround::SurroundModePassive),
     old_audio_stretchfactor(1.0),
+    volume(80),
 
     blocking(false),
 
@@ -335,6 +337,15 @@
         return;
     }
 
+    // Only used for software volume
+    if (set_initial_vol && internal_vol) 
+        volume = gContext->GetNumSetting("PCMMixerVolume", 80);
+    {
+        QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
+        controlLabel += "MixerVolume";
+        volume = gContext->GetNumSetting(controlLabel, 80);
+    }
+
     SyncVolume();
 
     VERBOSE(VB_AUDIO, LOC + QString("Audio fragment size: %1")
@@ -628,6 +639,63 @@
      return audbuf_timecode - GetAudiotime();
 }
 
+void AudioOutputBase::SetSWVolume(int new_volume, bool save)
+{
+    volume = new_volume;
+    if (save)
+    {
+        QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
+        controlLabel += "MixerVolume";
+        gContext->SaveSetting(controlLabel, volume);
+    }
+}
+
+int AudioOutputBase::GetSWVolume()
+{
+    return volume;
+}
+
+template <class AudioDataType>
+void AudioOutputBase::_AdjustVolume(AudioDataType *buffer, int len, bool music)
+{
+    float g = volume / 100.0;
+
+    // Should probably be exponential - 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)
 {
@@ -969,6 +1037,20 @@
         }
     }
 
+    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 -Naur --exclude=.svn mythtv.ori/libs/libmyth/audiooutputbase.h mythtv/libs/libmyth/audiooutputbase.h
--- mythtv.ori/libs/libmyth/audiooutputbase.h	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/libs/libmyth/audiooutputbase.h	2009-08-31 01:42:06.000000000 +1000
@@ -47,6 +47,9 @@
 
     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);
@@ -153,6 +156,11 @@
     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;
@@ -169,6 +177,7 @@
     int surround_mode;
     bool allow_ac3_passthru;
     float old_audio_stretchfactor;
+    int volume;
 
     bool blocking; // do AddSamples calls block?
 
diff -Naur --exclude=.svn mythtv.ori/libs/libmyth/audiooutputoss.cpp mythtv/libs/libmyth/audiooutputoss.cpp
--- mythtv.ori/libs/libmyth/audiooutputoss.cpp	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/libs/libmyth/audiooutputoss.cpp	2009-08-31 01:42:44.000000000 +1000
@@ -317,6 +317,9 @@
     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 -Naur --exclude=.svn mythtv.ori/libs/libmyth/volumebase.cpp mythtv/libs/libmyth/volumebase.cpp
--- mythtv.ori/libs/libmyth/volumebase.cpp	2009-08-31 01:29:36.000000000 +1000
+++ mythtv/libs/libmyth/volumebase.cpp	2009-08-31 01:45:03.000000000 +1000
@@ -10,6 +10,20 @@
     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
@@ -76,9 +90,17 @@
 void VolumeBase::UpdateVolume(void)
 {
     int new_volume = volume;
+    bool save = true;
     if (current_mute_state == kMuteAll)
     {
         new_volume = 0;
+        save = false;
+    }
+
+    if (swvol)
+    {
+        SetSWVolume(new_volume, save);
+        return;
     }
     
     // TODO: Avoid assumption that there are 2 channels!
@@ -102,6 +124,9 @@
 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 -Naur --exclude=.svn mythtv.ori/libs/libmyth/volumebase.h mythtv/libs/libmyth/volumebase.h
--- mythtv.ori/libs/libmyth/volumebase.h	2009-08-31 01:29:36.000000000 +1000
+++ mythtv/libs/libmyth/volumebase.h	2009-08-31 01:45:34.000000000 +1000
@@ -20,6 +20,8 @@
     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 @@
 
     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);
@@ -44,6 +48,8 @@
     
     int volume;
     MuteState current_mute_state;
+    bool swvol;
+    bool swvol_setting;
 
 };
 
diff -Naur --exclude=.svn mythtv.ori/libs/libmythtv/avformatdecoder.cpp mythtv/libs/libmythtv/avformatdecoder.cpp
--- mythtv.ori/libs/libmythtv/avformatdecoder.cpp	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/libs/libmythtv/avformatdecoder.cpp	2009-08-31 01:49:07.000000000 +1000
@@ -461,6 +461,7 @@
       // 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
@@ -482,6 +483,7 @@
 
     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
@@ -4203,9 +4205,10 @@
 
     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 -Naur --exclude=.svn mythtv.ori/libs/libmythtv/avformatdecoder.h mythtv/libs/libmythtv/avformatdecoder.h
--- mythtv.ori/libs/libmythtv/avformatdecoder.h	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/libs/libmythtv/avformatdecoder.h	2009-08-31 01:49:36.000000000 +1000
@@ -259,6 +259,7 @@
     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 -Naur --exclude=.svn mythtv.ori/programs/mythfrontend/globalsettings.cpp mythtv/programs/mythfrontend/globalsettings.cpp
--- mythtv.ori/programs/mythfrontend/globalsettings.cpp	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/programs/mythfrontend/globalsettings.cpp	2009-08-31 02:02:42.000000000 +1000
@@ -196,6 +196,9 @@
 #ifdef USING_WINAUDIO
     gc->addSelection("Windows:", "Windows:");
 #endif
+#if !(defined(USING_DIRECTX) || defined(USING_WINAUDIO))
+    gc->addSelection("software", "sofware");
+#endif
 
     return gc;
 }
diff -Naur --exclude=.svn mythtv.ori/programs/mythtranscode/transcode.cpp mythtv/programs/mythtranscode/transcode.cpp
--- mythtv.ori/programs/mythtranscode/transcode.cpp	2009-08-31 01:31:31.000000000 +1000
+++ mythtv/programs/mythtranscode/transcode.cpp	2009-08-31 01:50:45.000000000 +1000
@@ -228,6 +228,17 @@
         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; }
