Index: configure
===================================================================
--- configure	(revision 15853)
+++ configure	(working copy)
@@ -752,6 +752,7 @@
     libfaac
     libfaad
     libfaadbin
+    libfftw3
     libgsm
     libmp3lame
     libnut
@@ -2886,6 +2887,7 @@
 echo "libfaac enabled           ${libfaac-no}"
 echo "libfaad enabled           ${libfaad-no}"
 echo "libfaad dlopened          ${libfaadbin-no}"
+echo "libfftw3 support          ${liba52-no}"
 echo "libgsm enabled            ${libgsm-no}"
 echo "libmp3lame enabled        ${libmp3lame-no}"
 echo "libnut enabled            ${libnut-no}"
Index: libs/libs.pro
===================================================================
--- libs/libs.pro	(revision 15853)
+++ libs/libs.pro	(working copy)
@@ -6,6 +6,7 @@
 # Directories
 SUBDIRS += libavutil libavcodec libavformat libmythsamplerate 
 SUBDIRS += libmythsoundtouch libmythmpeg2 libmythdvdnav
+SUBDIRS += libmythfreesurround
 
 mingw : SUBDIRS += libmyth libmythupnp libmythui
 !mingw: SUBDIRS += libmythupnp libmythui libmyth
Index: libs/libmyth/libmyth.pro
===================================================================
--- libs/libmyth/libmyth.pro	(revision 15853)
+++ libs/libmyth/libmyth.pro	(working copy)
@@ -11,6 +11,7 @@
 
 # Input
 HEADERS += audiooutput.h audiooutputbase.h audiooutputnull.h
+HEADERS += audiooutputdigitalencoder.h
 HEADERS += backendselect.h dbsettings.h dialogbox.h
 HEADERS += DisplayRes.h DisplayResScreen.h exitcodes.h
 HEADERS += generictree.h httpcomms.h langsettings.h lcddevice.h
@@ -25,8 +26,10 @@
 HEADERS += volumebase.h volumecontrol.h virtualkeyboard.h visual.h xmlparse.h
 HEADERS += mythhdd.h mythcdrom.h storagegroup.h
 HEADERS += compat.h
+HEADERS += audiooutputdigitalencoder.h
 
 SOURCES += audiooutput.cpp audiooutputbase.cpp audiooutputnull.cpp
+SOURCES += audiooutputdigitalencoder.cpp
 SOURCES += backendselect.cpp dbsettings.cpp dialogbox.cpp
 SOURCES += DisplayRes.cpp DisplayResScreen.cpp
 SOURCES += generictree.cpp httpcomms.cpp langsettings.cpp lcddevice.cpp
@@ -41,17 +44,24 @@
 SOURCES += volumebase.cpp volumecontrol.cpp virtualkeyboard.cpp xmlparse.cpp
 SOURCES += mythhdd.cpp mythcdrom.cpp storagegroup.cpp
 
-INCLUDEPATH += ../libmythsamplerate ../libmythsoundtouch ../.. ../ ./
+INCLUDEPATH += ../libmythsamplerate ../libmythsoundtouch ../libmythfreesurround
+INCLUDEPATH += ../libavcodec ../libavutil
+INCLUDEPATH += ../.. ../ ./
 DEPENDPATH += ../libmythsamplerate ../libmythsoundtouch ../ ../libmythui
-DEPENDPATH += ../libmythupnp
+DEPENDPATH += ../libmythupnp ../libmythfreesurround ../libavcodec ../libavutil
 
-LIBS += -L../libmythsamplerate -lmythsamplerate-$${LIBVERSION}
-LIBS += -L../libmythsoundtouch -lmythsoundtouch-$${LIBVERSION}
-LIBS += -L../libmythui         -lmythui-$${LIBVERSION}
-LIBS += -L../libmythupnp       -lmythupnp-$${LIBVERSION}
 
+LIBS += -L../libmythsamplerate   -lmythsamplerate-$${LIBVERSION}
+LIBS += -L../libmythsoundtouch   -lmythsoundtouch-$${LIBVERSION}
+LIBS += -L../libmythui           -lmythui-$${LIBVERSION}
+LIBS += -L../libmythupnp         -lmythupnp-$${LIBVERSION}
+LIBS += -L../libmythfreesurround -lmythfreesurround-$${LIBVERSION}
+LIBS += -L../libavcodec          -lmythavcodec-$${LIBVERSION}
+LIBS += -L../libavutil           -lmythavutil-$${LIBVERSION}
+
 TARGETDEPS += ../libmythsamplerate/libmythsamplerate-$${MYTH_LIB_EXT}
 TARGETDEPS += ../libmythsoundtouch/libmythsoundtouch-$${MYTH_LIB_EXT}
+TARGETDEPS += ../libmythfreesurround/libmythfreesurround-$${MYTH_LIB_EXT}
 
 # Install headers so that plugins can compile independently
 inc.path = $${PREFIX}/include/mythtv/
@@ -221,3 +231,11 @@
 use_hidesyms {
     QMAKE_CXXFLAGS += -fvisibility=hidden
 }
+
+contains( CONFIG_LIBA52, yes ) {
+    LIBS += -la52
+}
+
+contains( CONFIG_LIBFFTW3, yes ) {
+    LIBS += -lfftw3f
+}
Index: libs/libmyth/audiooutput.h
===================================================================
--- libs/libmyth/audiooutput.h	(revision 15853)
+++ libs/libmyth/audiooutput.h	(working copy)
@@ -31,10 +31,14 @@
     virtual ~AudioOutput() { };
 
     // reconfigure sound out for new params
-    virtual void Reconfigure(int audio_bits, int audio_channels,
-                             int audio_samplerate, bool audio_passthru) = 0;
+    virtual void Reconfigure(int audio_bits, 
+                             int audio_channels, 
+                             int audio_samplerate,
+                             bool audio_passthru,
+                             void* audio_codec = NULL) = 0;
     
     virtual void SetStretchFactor(float factor);
+    virtual float GetStretchFactor(void) { return 1.0f; }
 
     // do AddSamples calls block?
     virtual void SetBlocking(bool blocking) = 0;
@@ -76,6 +80,7 @@
         lastError = msg;
         VERBOSE(VB_IMPORTANT, "AudioOutput Error: " + lastError);
     }
+    void ClearError(void) { lastError = QString::null; }
 
     void Warn(QString msg)
     {
Index: libs/libmyth/audiooutputdx.h
===================================================================
--- libs/libmyth/audiooutputdx.h	(revision 15853)
+++ libs/libmyth/audiooutputdx.h	(working copy)
@@ -35,8 +35,11 @@
     /// END HACK HACK HACK HACK
 	
     virtual void Reset(void);
-    virtual void Reconfigure(int audio_bits,       int audio_channels,
-                             int audio_samplerate, int audio_passthru);
+    virtual void Reconfigure(int audio_bits, 
+                             int audio_channels, 
+                             int audio_samplerate,
+                             bool audio_passthru,
+                             AudioCodecMode aom = AUDIOCODECMODE_NORMAL);
     virtual void SetBlocking(bool blocking);
 
     virtual bool AddSamples(char *buffer, int samples, long long timecode);
Index: libs/libmyth/audiooutputdx.cpp
===================================================================
--- libs/libmyth/audiooutputdx.cpp	(revision 15853)
+++ libs/libmyth/audiooutputdx.cpp	(working copy)
@@ -130,8 +130,11 @@
     // FIXME: kedl: not sure what else could be required here?
 }
 
-void AudioOutputDX::Reconfigure(int audio_bits, int audio_channels,
-                                int audio_samplerate, int audio_passthru)
+void AudioOutputDX::Reconfigure(int audio_bits, 
+                                int audio_channels, 
+                                int audio_samplerate,
+                                int audio_passthru,
+                                AudioCodecMode laom)
 {
     if (dsbuffer)
         DestroyDSBuffer();
Index: libs/libmyth/audiooutputbase.h
===================================================================
--- libs/libmyth/audiooutputbase.h	(revision 15853)
+++ libs/libmyth/audiooutputbase.h	(working copy)
@@ -16,13 +16,23 @@
 // MythTV headers
 #include "audiooutput.h"
 #include "samplerate.h"
-#include "SoundTouch.h"
 
-#define AUDBUFSIZE 768000
+namespace soundtouch {
+class SoundTouch;
+};
+class FreeSurround;
+class AudioOutputDigitalEncoder;
+struct AVCodecContext;
+
 #define AUDIO_SRC_IN_SIZE   16384
 #define AUDIO_SRC_OUT_SIZE (16384*6)
 #define AUDIO_TMP_BUF_SIZE (16384*6)
 
+//#define AUDBUFSIZE 768000
+//divisible by 12,10,8,6,4,2 and around 1024000
+//#define AUDBUFSIZE 1024080
+#define AUDBUFSIZE 1536000
+
 class AudioOutputBase : public AudioOutput
 {
  public:
@@ -35,8 +45,11 @@
     virtual ~AudioOutputBase();
 
     // reconfigure sound out for new params
-    virtual void Reconfigure(int audio_bits, int audio_channels,
-                             int audio_samplerate, bool audio_passthru);
+    virtual void Reconfigure(int audio_bits, 
+                             int audio_channels, 
+                             int audio_samplerate,
+                             bool audio_passthru,
+                             void* audio_codec = NULL);
     
     // do AddSamples calls block?
     virtual void SetBlocking(bool blocking);
@@ -45,6 +58,7 @@
     virtual void SetEffDsp(int dsprate);
 
     virtual void SetStretchFactor(float factor);
+    virtual float GetStretchFactor(void);
 
     virtual void Reset(void);
 
@@ -127,6 +141,7 @@
     bool audio_passthru;
 
     float audio_stretchfactor;
+    AVCodecContext *audio_codec;
     AudioOutputSource source;
 
     bool killaudio;
@@ -135,6 +150,8 @@
     bool set_initial_vol;
     bool buffer_output_data_for_use; //  used by AudioOutputNULL
     
+    int configured_audio_channels;
+
  private:
     // resampler
     bool need_resampler;
@@ -145,8 +162,15 @@
     short tmp_buff[AUDIO_TMP_BUF_SIZE];
 
     // timestretch
-    soundtouch::SoundTouch * pSoundStretch;
+    soundtouch::SoundTouch    *pSoundStretch;
+    AudioOutputDigitalEncoder *encoder;
+    FreeSurround              *upmixer;
 
+    int source_audio_channels;
+    int source_audio_bytes_per_sample;
+    bool needs_upmix;
+    int surround_mode;
+
     bool blocking; // do AddSamples calls block?
 
     int lastaudiolen;
@@ -164,15 +188,15 @@
 
     pthread_mutex_t avsync_lock; /* must hold avsync_lock to read or write
                                     'audiotime' and 'audiotime_updated' */
-    int audiotime; // timecode of audio leaving the soundcard (same units as
-                   //                                          timecodes) ...
+    /// timecode of audio leaving the soundcard (same units as timecodes)
+    long long audiotime;
     struct timeval audiotime_updated; // ... which was last updated at this time
 
     /* Audio circular buffer */
     unsigned char audiobuffer[AUDBUFSIZE];  /* buffer */
     int raud, waud;     /* read and write positions */
-    int audbuf_timecode;    /* timecode of audio most recently placed into
-                   buffer */
+    /// timecode of audio most recently placed into buffer
+    long long audbuf_timecode;
 
     int numlowbuffer;
 
Index: libs/libmyth/audiooutputbase.cpp
===================================================================
--- libs/libmyth/audiooutputbase.cpp	(revision 15853)
+++ libs/libmyth/audiooutputbase.cpp	(working copy)
@@ -15,6 +15,9 @@
 
 // MythTV headers
 #include "audiooutputbase.h"
+#include "audiooutputdigitalencoder.h"
+#include "SoundTouch.h"
+#include "freesurround.h"
 #include "compat.h"
 
 #define LOC QString("AO: ")
@@ -36,6 +39,7 @@
     audio_passthru_device(QDeepCopy<QString>(laudio_passthru_device)),
     audio_passthru(false),      audio_stretchfactor(1.0f),
 
+    audio_codec(NULL),
     source(lsource),            killaudio(false),
 
     pauseaudio(false),          audio_actually_paused(false),
@@ -47,8 +51,16 @@
 
     src_ctx(NULL),
 
-    pSoundStretch(NULL),        blocking(false),
+    pSoundStretch(NULL),        
+    encoder(NULL),
+    upmixer(NULL),
+    source_audio_channels(-1),
+    source_audio_bytes_per_sample(0),
+    needs_upmix(false),
+    surround_mode(FreeSurround::SurroundModePassive),
 
+    blocking(false),
+
     lastaudiolen(0),            samples_buffered(0),
 
     audio_thread_exists(false),
@@ -71,6 +83,7 @@
     memset(tmp_buff,           0, sizeof(short) * AUDIO_TMP_BUF_SIZE);
     memset(&audiotime_updated, 0, sizeof(audiotime_updated));
     memset(audiobuffer,        0, sizeof(char)  * AUDBUFSIZE);
+    configured_audio_channels = gContext->GetNumSetting("MaxChannels", 2);
 
     // You need to call Reconfigure from your concrete class.
     // Reconfigure(laudio_bits,       laudio_channels,
@@ -111,9 +124,41 @@
             VERBOSE(VB_GENERAL, LOC + QString("Using time stretch %1")
                                         .arg(audio_stretchfactor));
             pSoundStretch = new soundtouch::SoundTouch();
-            pSoundStretch->setSampleRate(audio_samplerate);
-            pSoundStretch->setChannels(audio_channels);
+            if (audio_codec)
+            {
+                if (!encoder)
+                {
+                    VERBOSE(VB_AUDIO, LOC +
+                            QString("Creating Encoder for codec %1 origfs %2")
+                            .arg(audio_codec->codec_id)
+                            .arg(audio_codec->frame_size));
 
+                    encoder = new AudioOutputDigitalEncoder();
+                    if (!encoder->Init(audio_codec->codec_id,
+                                audio_codec->bit_rate,
+                                audio_codec->sample_rate,
+                                audio_codec->channels
+                                ))
+                    {
+                        // eeks
+                        delete encoder;
+                        encoder = NULL;
+                        VERBOSE(VB_AUDIO, LOC +
+                                QString("Failed to Create Encoder"));
+                    }
+                }
+            }
+            if (encoder)
+            {
+                pSoundStretch->setSampleRate(audio_codec->sample_rate);
+                pSoundStretch->setChannels(audio_codec->channels);
+            }
+            else
+            {
+                pSoundStretch->setSampleRate(audio_samplerate);
+                pSoundStretch->setChannels(audio_channels);
+            }
+
             pSoundStretch->setTempo(audio_stretchfactor);
             pSoundStretch->setSetting(SETTING_SEQUENCE_MS, 35);
 
@@ -134,14 +179,69 @@
     pthread_mutex_unlock(&audio_buflock);
 }
 
+float AudioOutputBase::GetStretchFactor()
+{
+    return audio_stretchfactor;
+}
+
 void AudioOutputBase::Reconfigure(int laudio_bits, int laudio_channels, 
-                                 int laudio_samplerate, bool laudio_passthru)
+                                 int laudio_samplerate, bool laudio_passthru,
+                                 void* laudio_codec)
 {
-    if (laudio_bits == audio_bits && laudio_channels == audio_channels &&
-        laudio_samplerate == audio_samplerate &&
-        laudio_passthru == audio_passthru && !need_resampler)
+    int codec_id = CODEC_ID_NONE;
+    int lcodec_id = CODEC_ID_NONE;
+    int lcchannels = 0;
+    int cchannels = 0;
+    int lsource_audio_channels = laudio_channels;
+    bool lneeds_upmix = false;
+
+    if (laudio_codec)
+    {
+        lcodec_id = ((AVCodecContext*)laudio_codec)->codec_id;
+        laudio_bits = 16;
+        laudio_channels = 2;
+        lsource_audio_channels = laudio_channels;
+        laudio_samplerate = 48000;
+        lcchannels = ((AVCodecContext*)laudio_codec)->channels;
+    }
+
+    if (audio_codec)
+    {
+        codec_id = audio_codec->codec_id;
+        cchannels = ((AVCodecContext*)audio_codec)->channels;
+    }
+
+    if ((configured_audio_channels == 6) && 
+        !(laudio_codec || audio_codec))
+    {
+        laudio_channels = configured_audio_channels;
+        lneeds_upmix = true;
+        VERBOSE(VB_AUDIO,LOC + "Needs upmix");
+    }
+
+    ClearError();
+    bool general_deps = (laudio_bits == audio_bits && 
+        laudio_channels == audio_channels &&
+        laudio_samplerate == audio_samplerate && !need_resampler &&
+        laudio_passthru == audio_passthru &&
+        lneeds_upmix == needs_upmix &&
+        lcodec_id == codec_id && lcchannels == cchannels);
+    bool upmix_deps =
+        (lsource_audio_channels == source_audio_channels);
+    if (general_deps && upmix_deps)
+    {
+        VERBOSE(VB_AUDIO,LOC + "no change exiting");
         return;
+    }
 
+    if (general_deps && !upmix_deps && lneeds_upmix && upmixer)
+    {
+        upmixer->flush();
+        source_audio_channels = lsource_audio_channels;
+        VERBOSE(VB_AUDIO,LOC + QString("source channels changed to %1").arg(source_audio_channels));
+        return;
+    }
+
     KillAudio();
     
     pthread_mutex_lock(&audio_buflock);
@@ -151,10 +251,15 @@
     waud = raud = 0;
     audio_actually_paused = false;
     
+    bool redo_stretch = (pSoundStretch && audio_channels != laudio_channels);
     audio_channels = laudio_channels;
+    source_audio_channels = lsource_audio_channels;
     audio_bits = laudio_bits;
     audio_samplerate = laudio_samplerate;
+    audio_codec = (AVCodecContext*)laudio_codec;
     audio_passthru = laudio_passthru;
+    needs_upmix = lneeds_upmix;
+
     if (audio_bits != 8 && audio_bits != 16)
     {
         pthread_mutex_unlock(&avsync_lock);
@@ -162,7 +267,9 @@
         Error("AudioOutput only supports 8 or 16bit audio.");
         return;
     }
+
     audio_bytes_per_sample = audio_channels * audio_bits / 8;
+    source_audio_bytes_per_sample = source_audio_channels * audio_bits / 8;
     
     need_resampler = false;
     killaudio = false;
@@ -172,12 +279,19 @@
     
     numlowbuffer = 0;
 
+    VERBOSE(VB_GENERAL, QString("Opening audio device '%1'. ch %2(%3) sr %4")
+            .arg(audio_main_device).arg(audio_channels)
+            .arg(source_audio_channels).arg(audio_samplerate));
+ 
     // Actually do the device specific open call
     if (!OpenDevice())
     {
         VERBOSE(VB_AUDIO, LOC_ERR + "Aborting reconfigure");
         pthread_mutex_unlock(&avsync_lock);
         pthread_mutex_unlock(&audio_buflock);
+        if (GetError().isEmpty())
+            Error("Aborting reconfigure");
+        VERBOSE(VB_AUDIO, "Aborting reconfigure");
         return;
     }
 
@@ -200,6 +314,7 @@
     current_seconds = -1;
     source_bitrate = -1;
 
+    // NOTE: this won't do anything as above samplerate vars are set equal
     // Check if we need the resampler
     if (audio_samplerate != laudio_samplerate)
     {
@@ -222,16 +337,79 @@
         need_resampler = true;
     }
 
+    if (needs_upmix)
+    {
+        VERBOSE(VB_AUDIO, LOC + QString("create upmixer"));
+        if (configured_audio_channels == 6)
+        {
+            surround_mode = gContext->GetNumSetting("AudioUpmixType", 2);
+        }
+
+        upmixer = new FreeSurround(
+            audio_samplerate, 
+            source == AUDIOOUTPUT_VIDEO, 
+            (FreeSurround::SurroundMode)surround_mode);
+
+        VERBOSE(VB_AUDIO, LOC +
+                QString("create upmixer done with surround mode %1")
+                .arg(surround_mode));
+    }
+
     VERBOSE(VB_AUDIO, LOC + QString("Audio Stretch Factor: %1")
             .arg(audio_stretchfactor));
+    VERBOSE(VB_AUDIO, QString("Audio Codec Used: %1")
+            .arg((audio_codec) ?
+                 codec_id_string(audio_codec->codec_id) : "not set"));
 
-    SetStretchFactorLocked(audio_stretchfactor);
-    if (pSoundStretch)
+    if (redo_stretch)
     {
-        pSoundStretch->setSampleRate(audio_samplerate);
-        pSoundStretch->setChannels(audio_channels);
+        float laudio_stretchfactor = audio_stretchfactor;
+        delete pSoundStretch;
+        pSoundStretch = NULL;
+        audio_stretchfactor = 0.0f;
+        SetStretchFactorLocked(laudio_stretchfactor);
     }
+    else
+    {
+        SetStretchFactorLocked(audio_stretchfactor);
+        if (pSoundStretch)
+        {
+            // if its passthru then we need to reencode
+            if (audio_codec)
+            {
+                if (!encoder)
+                {
+                    VERBOSE(VB_AUDIO, LOC +
+                            QString("Creating Encoder for codec %1")
+                            .arg(audio_codec->codec_id));
 
+                    encoder = new AudioOutputDigitalEncoder();
+                    if (!encoder->Init(audio_codec->codec_id,
+                                audio_codec->bit_rate,
+                                audio_codec->sample_rate,
+                                audio_codec->channels
+                                ))
+                    {
+                        // eeks
+                        delete encoder;
+                        encoder = NULL;
+                        VERBOSE(VB_AUDIO, LOC + "Failed to Create Encoder");
+                    }
+                }
+            }
+            if (encoder)
+            {
+                pSoundStretch->setSampleRate(audio_codec->sample_rate);
+                pSoundStretch->setChannels(audio_codec->channels);
+            }
+            else
+            {
+                pSoundStretch->setSampleRate(audio_samplerate);
+                pSoundStretch->setChannels(audio_channels);
+            }
+        }
+    }
+
     // Setup visualisations, zero the visualisations buffers
     prepareVisuals();
 
@@ -290,6 +468,19 @@
         pSoundStretch = NULL;
     }
 
+    if (encoder)
+    {
+        delete encoder;
+        encoder = NULL;
+    }
+
+    if (upmixer)
+    {
+        delete upmixer;
+        upmixer = NULL;
+    }
+    needs_upmix = false;
+
     CloseDevice();
 
     killAudioLock.unlock();
@@ -303,6 +494,7 @@
 
 void AudioOutputBase::Pause(bool paused)
 {
+    VERBOSE(VB_AUDIO, LOC + QString("Pause %0").arg(paused));
     pauseaudio = paused;
     audio_actually_paused = false;
 }
@@ -385,7 +577,7 @@
        The reason is that computing 'audiotime' requires acquiring the audio 
        lock, which the video thread should not do. So, we call 'SetAudioTime()'
        from the audio thread, and then call this from the video thread. */
-    int ret;
+    long long ret;
     struct timeval now;
 
     if (audiotime == 0)
@@ -397,12 +589,23 @@
 
     ret = (now.tv_sec - audiotime_updated.tv_sec) * 1000;
     ret += (now.tv_usec - audiotime_updated.tv_usec) / 1000;
-    ret = (int)(ret * audio_stretchfactor);
+    ret = (long long)(ret * audio_stretchfactor);
 
+#if 1
+    VERBOSE(VB_AUDIO|VB_TIMESTAMP, 
+            QString("GetAudiotime now=%1.%2, set=%3.%4, ret=%5, audt=%6 sf=%7")
+            .arg(now.tv_sec).arg(now.tv_usec)
+            .arg(audiotime_updated.tv_sec).arg(audiotime_updated.tv_usec)
+            .arg(ret)
+            .arg(audiotime)
+            .arg(audio_stretchfactor)
+           );
+#endif
+
     ret += audiotime;
 
     pthread_mutex_unlock(&avsync_lock);
-    return ret;
+    return (int)ret;
 }
 
 void AudioOutputBase::SetAudiotime(void)
@@ -439,15 +642,38 @@
     // include algorithmic latencies
     if (pSoundStretch)
     {
+        // add the effect of any unused but processed samples,
+        // AC3 reencode does this
+        totalbuffer += (int)(pSoundStretch->numSamples() *
+                             audio_bytes_per_sample);
         // add the effect of unprocessed samples in time stretch algo
         totalbuffer += (int)((pSoundStretch->numUnprocessedSamples() *
                               audio_bytes_per_sample) / audio_stretchfactor);
     }
-               
+
+    if (upmixer && needs_upmix)
+    {
+        totalbuffer += upmixer->sampleLatency() * audio_bytes_per_sample;
+    }
+
     audiotime = audbuf_timecode - (int)(totalbuffer * 100000.0 /
                                    (audio_bytes_per_sample * effdspstretched));
  
     gettimeofday(&audiotime_updated, NULL);
+#if 1
+    VERBOSE(VB_AUDIO|VB_TIMESTAMP, 
+            QString("SetAudiotime set=%1.%2, audt=%3 atc=%4 "
+                    "tb=%5 sb=%6 eds=%7 abps=%8 sf=%9")
+            .arg(audiotime_updated.tv_sec).arg(audiotime_updated.tv_usec)
+            .arg(audiotime)
+            .arg(audbuf_timecode)
+            .arg(totalbuffer)
+            .arg(soundcard_buffer)
+            .arg(effdspstretched)
+            .arg(audio_bytes_per_sample)
+            .arg(audio_stretchfactor)
+           );
+#endif
 
     pthread_mutex_unlock(&avsync_lock);
     pthread_mutex_unlock(&audio_buflock);
@@ -458,13 +684,18 @@
 {
     // NOTE: This function is not threadsafe
     int afree = audiofree(true);
-    int abps = audio_bytes_per_sample;
+    int abps = (encoder) ?
+        encoder->audio_bytes_per_sample : audio_bytes_per_sample;
     int len = samples * abps;
 
     // Check we have enough space to write the data
     if (need_resampler && src_ctx)
         len = (int)ceilf(float(len) * src_data.src_ratio);
 
+    // include samples in upmix buffer that may be flushed
+    if (needs_upmix && upmixer)
+        len += upmixer->numUnprocessedSamples() * abps;
+
     if (pSoundStretch)
         len += (pSoundStretch->numUnprocessedSamples() +
                 (int)(pSoundStretch->numSamples()/audio_stretchfactor))*abps;
@@ -520,13 +751,18 @@
     // NOTE: This function is not threadsafe
 
     int afree = audiofree(true);
-    int abps = audio_bytes_per_sample;
+    int abps = (encoder) ?
+        encoder->audio_bytes_per_sample : audio_bytes_per_sample;
     int len = samples * abps;
 
     // Check we have enough space to write the data
     if (need_resampler && src_ctx)
         len = (int)ceilf(float(len) * src_data.src_ratio);
 
+    // include samples in upmix buffer that may be flushed
+    if (needs_upmix && upmixer)
+        len += upmixer->numUnprocessedSamples() * abps;
+ 
     if (pSoundStretch)
     {
         len += (pSoundStretch->numUnprocessedSamples() +
@@ -575,14 +811,16 @@
 
 int AudioOutputBase::WaitForFreeSpace(int samples)
 {
-    int len = samples * audio_bytes_per_sample;
+    int abps = (encoder) ?
+        encoder->audio_bytes_per_sample : audio_bytes_per_sample;
+    int len = samples * abps;
     int afree = audiofree(false);
 
     while (len > afree)
     {
         if (blocking)
         {
-            VERBOSE(VB_AUDIO, LOC + "Waiting for free space " +
+            VERBOSE(VB_AUDIO|VB_TIMESTAMP, LOC + "Waiting for free space " +
                     QString("(need %1, available %2)").arg(len).arg(afree));
 
             // wait for more space
@@ -591,10 +829,11 @@
         }
         else
         {
-            VERBOSE(VB_IMPORTANT, LOC_ERR +
-                    "Audio buffer overflow, audio data lost!");
-            samples = afree / audio_bytes_per_sample;
-            len = samples * audio_bytes_per_sample;
+            VERBOSE(VB_IMPORTANT, LOC_ERR + 
+                    QString("Audio buffer overflow, %1 audio samples lost!")
+                    .arg(samples - (afree / abps)));
+            samples = afree / abps;
+            len = samples * abps;
             if (src_ctx) 
             {
                 int error = src_reset(src_ctx);
@@ -619,114 +858,244 @@
     
     int afree = audiofree(false);
 
-    VERBOSE(VB_AUDIO|VB_TIMESTAMP,
-            LOC + QString("_AddSamples bytes=%1, used=%2, free=%3, timecode=%4")
-            .arg(samples * audio_bytes_per_sample)
-            .arg(AUDBUFSIZE-afree).arg(afree).arg((long)timecode));
+    int abps = (encoder) ?
+        encoder->audio_bytes_per_sample : audio_bytes_per_sample;
+
+    VERBOSE(VB_AUDIO|VB_TIMESTAMP, 
+            LOC + QString("_AddSamples samples=%1 bytes=%2, used=%3, "
+                          "free=%4, timecode=%5 needsupmix %6")
+            .arg(samples)
+            .arg(samples * abps)
+            .arg(AUDBUFSIZE-afree).arg(afree).arg(timecode)
+            .arg(needs_upmix));
     
-    len = WaitForFreeSpace(samples);
-
-    if (interleaved) 
+    if (upmixer && needs_upmix)
     {
-        char *mybuf = (char*)buffer;
-        int bdiff = AUDBUFSIZE - org_waud;
-        if (bdiff < len)
+        int out_samples = 0;
+        int step = (interleaved)?source_audio_channels:1;
+        len = WaitForFreeSpace(samples);    // test
+        for (int itemp = 0; itemp < samples; )
         {
-            memcpy(audiobuffer + org_waud, mybuf, bdiff);
-            memcpy(audiobuffer, mybuf + bdiff, len - bdiff);
+            // just in case it does a processing cycle, release the lock
+            // to allow the output loop to do output
+            pthread_mutex_unlock(&audio_buflock);
+            if (audio_bytes == 2)
+            {
+                itemp += upmixer->putSamples(
+                    (short*)buffer + itemp * step,
+                    samples - itemp,
+                    source_audio_channels,
+                    (interleaved) ? 0 : samples);
+            }
+            else
+            {
+                itemp += upmixer->putSamples(
+                    (char*)buffer + itemp * step,
+                    samples - itemp,
+                    source_audio_channels,
+                    (interleaved) ? 0 : samples);
+            }
+            pthread_mutex_lock(&audio_buflock);
+
+            int copy_samples = upmixer->numSamples();
+            if (copy_samples)
+            {
+                int copy_len = copy_samples * abps;
+                out_samples += copy_samples;
+                if (out_samples > samples)
+                    len = WaitForFreeSpace(out_samples);
+                int bdiff = AUDBUFSIZE - org_waud;
+                if (bdiff < copy_len) 
+                {
+                    int bdiff_samples = bdiff/abps;
+                    upmixer->receiveSamples(
+                        (short*)(audiobuffer + org_waud), bdiff_samples);
+                    upmixer->receiveSamples(
+                        (short*)(audiobuffer), (copy_samples - bdiff_samples));
+                }
+                else
+                {
+                    upmixer->receiveSamples(
+                        (short*)(audiobuffer + org_waud), copy_samples);
+                }
+                org_waud = (org_waud + copy_len) % AUDBUFSIZE;
+            }
         }
-        else
-            memcpy(audiobuffer + org_waud, mybuf, len);
- 
-        org_waud = (org_waud + len) % AUDBUFSIZE;
-    } 
-    else 
+
+        if (samples > 0)
+            len = WaitForFreeSpace(out_samples);
+
+        samples = out_samples;
+    }
+    else
     {
-        char **mybuf = (char**)buffer;
-        for (int itemp = 0; itemp < samples * audio_bytes; itemp += audio_bytes)
+        len = WaitForFreeSpace(samples);
+
+        if (interleaved) 
         {
-            for (int chan = 0; chan < audio_channels; chan++)
+            char *mybuf = (char*)buffer;
+            int bdiff = AUDBUFSIZE - org_waud;
+            if (bdiff < len)
             {
-                audiobuffer[org_waud++] = mybuf[chan][itemp];
-                if (audio_bits == 16)
-                    audiobuffer[org_waud++] = mybuf[chan][itemp+1];
+                memcpy(audiobuffer + org_waud, mybuf, bdiff);
+                memcpy(audiobuffer, mybuf + bdiff, len - bdiff);
+            }
+            else
+            {
+                memcpy(audiobuffer + org_waud, mybuf, len);
+            }
+     
+            org_waud = (org_waud + len) % AUDBUFSIZE;
+        } 
+        else 
+        {
+            char **mybuf = (char**)buffer;
+            for (int itemp = 0; itemp < samples * audio_bytes;
+                 itemp += audio_bytes)
+            {
+                for (int chan = 0; chan < audio_channels; chan++)
+                {
+                    audiobuffer[org_waud++] = mybuf[chan][itemp];
+                    if (audio_bits == 16)
+                        audiobuffer[org_waud++] = mybuf[chan][itemp+1];
 
-                if (org_waud >= AUDBUFSIZE)
-                    org_waud -= AUDBUFSIZE;
+                    if (org_waud >= AUDBUFSIZE)
+                        org_waud -= AUDBUFSIZE;
+                }
             }
         }
     }
 
-    if (pSoundStretch)
+    if (samples > 0)
     {
-        // does not change the timecode, only the number of samples
-        // back to orig pos
-        org_waud = waud;
-        int bdiff = AUDBUFSIZE - org_waud;
-        int nSamplesToEnd = bdiff/audio_bytes_per_sample;
-        if (bdiff < len)
+        if (pSoundStretch)
         {
-            pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)(audiobuffer + 
-                                      org_waud), nSamplesToEnd);
-            pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)audiobuffer,
-                                      (len - bdiff) / audio_bytes_per_sample);
-        }
-        else
-        {
-            pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)(audiobuffer + 
-                                      org_waud), len / audio_bytes_per_sample);
-        }
 
-        int newLen = 0;
-        int nSamples;
-        len = WaitForFreeSpace(pSoundStretch->numSamples() * 
-                               audio_bytes_per_sample);
-        do 
-        {
-            int samplesToGet = len/audio_bytes_per_sample;
-            if (samplesToGet > nSamplesToEnd)
+            // does not change the timecode, only the number of samples
+            // back to orig pos
+            org_waud = waud;
+            int bdiff = AUDBUFSIZE - org_waud;
+            int nSamplesToEnd = bdiff/abps;
+            if (bdiff < len)
             {
-                samplesToGet = nSamplesToEnd;    
+                pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)
+                                          (audiobuffer + 
+                                           org_waud), nSamplesToEnd);
+                pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)audiobuffer,
+                                          (len - bdiff) / abps);
             }
+            else
+            {
+                pSoundStretch->putSamples((soundtouch::SAMPLETYPE*)
+                                          (audiobuffer + org_waud),
+                                          len / abps);
+            }
 
-            nSamples = pSoundStretch->receiveSamples((soundtouch::SAMPLETYPE*)
-                                      (audiobuffer + org_waud), samplesToGet);
-            if (nSamples == nSamplesToEnd)
+            if (encoder)
             {
-                org_waud = 0;
-                nSamplesToEnd = AUDBUFSIZE/audio_bytes_per_sample;
+                // pull out a packet's worth and reencode it until we
+                // don't have enough for any more packets
+                soundtouch::SAMPLETYPE *temp_buff = 
+                    (soundtouch::SAMPLETYPE*)encoder->GetFrameBuffer();
+                size_t frameSize = encoder->FrameSize()/abps;
+
+                VERBOSE(VB_AUDIO|VB_TIMESTAMP,
+                        QString("_AddSamples Enc sfs=%1 bfs=%2 sss=%3")
+                        .arg(frameSize)
+                        .arg(encoder->FrameSize())
+                        .arg(pSoundStretch->numSamples()));
+
+                // process the same number of samples as it creates
+                // a full encoded buffer just like before
+                while (pSoundStretch->numSamples() >= frameSize)
+                {
+                    int got = pSoundStretch->receiveSamples(
+                        temp_buff, frameSize);
+                    int amount = encoder->Encode(temp_buff);
+
+                    VERBOSE(VB_AUDIO|VB_TIMESTAMP, 
+                            QString("_AddSamples Enc bytes=%1 got=%2 left=%3")
+                            .arg(amount)
+                            .arg(got)
+                            .arg(pSoundStretch->numSamples()));
+
+                    if (!amount)
+                        continue;
+
+                    //len = WaitForFreeSpace(amount);
+                    char *ob = encoder->GetOutBuff();
+                    if (amount >= bdiff)
+                    {
+                        memcpy(audiobuffer + org_waud, ob, bdiff);
+                        ob += bdiff;
+                        amount -= bdiff;
+                        org_waud = 0;
+                    }
+                    if (amount > 0)
+                        memcpy(audiobuffer + org_waud, ob, amount);
+
+                    bdiff = AUDBUFSIZE - amount;
+                    org_waud += amount;
+                }
             }
             else
             {
-                org_waud += nSamples * audio_bytes_per_sample;
-                nSamplesToEnd -= nSamples;
+                int newLen = 0;
+                int nSamples;
+                len = WaitForFreeSpace(pSoundStretch->numSamples() * 
+                                       audio_bytes_per_sample);
+                do 
+                {
+                    int samplesToGet = len/audio_bytes_per_sample;
+                    if (samplesToGet > nSamplesToEnd)
+                    {
+                        samplesToGet = nSamplesToEnd;    
+                    }
+
+                    nSamples = pSoundStretch->receiveSamples(
+                        (soundtouch::SAMPLETYPE*)
+                        (audiobuffer + org_waud), samplesToGet);
+                    if (nSamples == nSamplesToEnd)
+                    {
+                        org_waud = 0;
+                        nSamplesToEnd = AUDBUFSIZE/audio_bytes_per_sample;
+                    }
+                    else
+                    {
+                        org_waud += nSamples * audio_bytes_per_sample;
+                        nSamplesToEnd -= nSamples;
+                    }
+
+                    newLen += nSamples * audio_bytes_per_sample;
+                    len -= nSamples * audio_bytes_per_sample;
+                } while (nSamples > 0);
             }
+        }
 
-            newLen += nSamples * audio_bytes_per_sample;
-            len -= nSamples * audio_bytes_per_sample;
-        } while (nSamples > 0);
-    }
+        waud = org_waud;
+        lastaudiolen = audiolen(false);
 
-    waud = org_waud;
-    lastaudiolen = audiolen(false);
+        if (timecode < 0)
+        {
+            // mythmusic doesn't give timestamps..
+            timecode = (int)((samples_buffered * 100000.0) / effdsp);
+        }
+        
+        samples_buffered += samples;
+        
+        /* we want the time at the end -- but the file format stores
+           time at the start of the chunk. */
+        // even with timestretch, timecode is still calculated from original
+        // sample count
+        audbuf_timecode = timecode + (int)((samples * 100000.0) / effdsp);
 
-    samples_buffered += samples;
-    
-    if (timecode < 0)
-    {
-        // mythmusic doesn't give timestamps..
-        timecode = (int)((samples_buffered * 100000.0) / effdsp);
+        if (interleaved)
+        {
+            dispatchVisual((unsigned char *)buffer, len, timecode,
+                           source_audio_channels, audio_bits);
+        }
     }
-    
-    /* we want the time at the end -- but the file format stores
-       time at the start of the chunk. */
-    // even with timestretch, timecode is still calculated from original
-    // sample count
-    audbuf_timecode = timecode + (int)((samples * 100000.0) / effdsp);
 
-    if (interleaved)
-        dispatchVisual((unsigned char *)buffer, len, timecode, audio_channels, audio_bits);
-
     pthread_mutex_unlock(&audio_buflock);
 }
 
@@ -739,7 +1108,7 @@
 
     if (source_bitrate == -1)
     {
-        source_bitrate = audio_samplerate * audio_channels * audio_bits;
+        source_bitrate = audio_samplerate * source_audio_channels * audio_bits;
     }
 
     if (ct / 1000 != current_seconds) 
@@ -747,7 +1116,7 @@
         current_seconds = ct / 1000;
         OutputEvent e(current_seconds, ct,
                       source_bitrate, audio_samplerate, audio_bits, 
-                      audio_channels);
+                      source_audio_channels);
         dispatch(e);
     }
 }
@@ -785,9 +1154,12 @@
 
             space_on_soundcard = getSpaceOnSoundcard();
 
-            if (space_on_soundcard != last_space_on_soundcard) {
-                VERBOSE(VB_AUDIO, LOC + QString("%1 bytes free on soundcard")
+            if (space_on_soundcard != last_space_on_soundcard)
+            {
+                VERBOSE(VB_AUDIO|VB_TIMESTAMP,
+                        LOC + QString("%1 bytes free on soundcard")
                         .arg(space_on_soundcard));
+
                 last_space_on_soundcard = space_on_soundcard;
             }
 
@@ -799,7 +1171,7 @@
                     WriteAudio(zeros, fragment_size);
                 } else {
                     // this should never happen now -dag
-                    VERBOSE(VB_AUDIO, LOC +
+                    VERBOSE(VB_AUDIO|VB_TIMESTAMP, LOC + 
                             QString("waiting for space on soundcard "
                                     "to write zeros: have %1 need %2")
                             .arg(space_on_soundcard).arg(fragment_size));
@@ -835,12 +1207,13 @@
         if (fragment_size > audiolen(true))
         {
             if (audiolen(true) > 0)  // only log if we're sending some audio
-                VERBOSE(VB_AUDIO, LOC +
+                VERBOSE(VB_AUDIO|VB_TIMESTAMP, LOC +
                         QString("audio waiting for buffer to fill: "
                                 "have %1 want %2")
                         .arg(audiolen(true)).arg(fragment_size));
 
-            VERBOSE(VB_AUDIO, LOC + "Broadcasting free space avail");
+            //VERBOSE(VB_AUDIO|VB_TIMESTAMP,
+            //LOC + "Broadcasting free space avail");
             pthread_mutex_lock(&audio_buflock);
             pthread_cond_broadcast(&audio_bufsig);
             pthread_mutex_unlock(&audio_buflock);
@@ -854,7 +1227,7 @@
         if (fragment_size > space_on_soundcard)
         {
             if (space_on_soundcard != last_space_on_soundcard) {
-                VERBOSE(VB_AUDIO, LOC +
+                VERBOSE(VB_AUDIO|VB_TIMESTAMP, LOC +
                         QString("audio waiting for space on soundcard: "
                                 "have %1 need %2")
                         .arg(space_on_soundcard).arg(fragment_size));
@@ -916,7 +1289,7 @@
 
         /* update raud */
         raud = (raud + fragment_size) % AUDBUFSIZE;
-        VERBOSE(VB_AUDIO, LOC + "Broadcasting free space avail");
+        //VERBOSE(VB_AUDIO|VB_TIMESTAMP, LOC + "Broadcasting free space avail");
         pthread_cond_broadcast(&audio_bufsig);
 
         written_size = fragment_size;
Index: libs/libmyth/audiooutputalsa.cpp
===================================================================
--- libs/libmyth/audiooutputalsa.cpp	(revision 15853)
+++ libs/libmyth/audiooutputalsa.cpp	(working copy)
@@ -89,8 +89,9 @@
     }
     else
     {
-        fragment_size = 6144; // nicely divisible by 2,4,6,8 channels @ 16-bits
-        buffer_time = 500000;  // .5 seconds
+        fragment_size =
+            (audio_bits * audio_channels * audio_samplerate) / (8*30);
+        buffer_time = 100000;
         period_time = buffer_time / 4;  // 4 interrupts per buffer
     }
 
@@ -162,7 +163,8 @@
     
     tmpbuf = aubuf;
 
-    VERBOSE(VB_AUDIO, QString("WriteAudio: Preparing %1 bytes (%2 frames)")
+    VERBOSE(VB_AUDIO|VB_TIMESTAMP,
+            QString("WriteAudio: Preparing %1 bytes (%2 frames)")
             .arg(size).arg(frames));
     
     while (frames > 0) 
Index: libs/libmyth/audiooutputdigitalencoder.cpp
===================================================================
--- libs/libmyth/audiooutputdigitalencoder.cpp	(revision 15853)
+++ libs/libmyth/audiooutputdigitalencoder.cpp	(working copy)
@@ -209,6 +209,50 @@
 extern "C" int ac3_sync(const uint8_t *buf, int *channels, int *sample_rate,
                         int *bit_rate, int *samples);
 
+// from http://www.ebu.ch/CMSimages/en/tec_AES-EBU_eg_tcm6-11890.pdf
+// http://en.wikipedia.org/wiki/S/PDIF
+typedef struct {
+    // byte 0
+    unsigned professional_consumer:1;
+    unsigned non_data:1;
+    // 4 - no emphasis
+    // 6 - 50/15us
+    // 7 - CCITT J17
+    unsigned audio_signal_emphasis:3;
+    unsigned SSFL:1;
+    // 0
+    // 1 - 48k
+    // 2 - 44.1k
+    // 3 - 32k
+    unsigned sample_frequency:2;    
+    // byte 1
+    // 0
+    // 1 - 2 ch
+    // 2 - mono
+    // 3 - prim/sec
+    // 4 - stereo
+    unsigned channel_mode:4;
+    // 0
+    // 1 - 192 bit block
+    // 2 - AES18
+    // 3 - user def
+    unsigned user_bit_management:4;
+    // byte 2
+    // 1 - audio data
+    // 2 - co-ordn
+    unsigned auxiliary_bits:3;
+    // 4 - 16 bits
+    // 5-7 - redither to 16 bits
+    unsigned source_word_length:3;
+    unsigned reserved:2;
+    // byte 3
+    unsigned multi_channel_function_description:8;
+    // byte 4
+    unsigned digital_audio_reference_signal:2;
+    unsigned reserved2:6;
+
+} AESHeader;
+
 static int encode_frame(
         bool dts, 
         unsigned char *data,
@@ -239,6 +283,7 @@
 #ifdef ENABLE_AC3_DECODER
         enc_len = ac3_sync(
             data + 8, &flags, &sample_rate, &bit_rate, (int*)&block_len);
+        block_len *= 2 * 2;
 #else
         enc_len = a52_syncinfo(data + 8, &flags, &sample_rate, &bit_rate);
         block_len = MAX_AC3_FRAME_SIZE;
@@ -263,6 +308,7 @@
 
     // the following values come from libmpcodecs/ad_hwac3.c in mplayer.
     // they form a valid IEC958 AC3 header.
+
     data[0] = 0x72;
     data[1] = 0xF8;
     data[2] = 0x1F;
@@ -272,6 +318,10 @@
     {
         switch(nr_samples)
         {
+            case 256:
+                data[4] = 0x0A;      /* DTS-? (256-sample bursts) */
+                break;
+
             case 512:
                 data[4] = 0x0B;      /* DTS-1 (512-sample bursts) */
                 break;
@@ -284,6 +334,10 @@
                 data[4] = 0x0D;      /* DTS-3 (2048-sample bursts) */
                 break;
 
+            case 4096:
+                data[4] = 0x0E;      /* DTS-? (4096-sample bursts) */
+                break;
+
             default:
                 VERBOSE(VB_IMPORTANT, LOC +
                         QString("DTS: %1-sample bursts not supported")
Index: libs/libmythfreesurround/el_processor.cpp
===================================================================
--- libs/libmythfreesurround/el_processor.cpp	(revision 15853)
+++ libs/libmythfreesurround/el_processor.cpp	(working copy)
@@ -1,540 +1,649 @@
-/*
-Copyright (C) 2007 Christian Kothe
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-
-#include "el_processor.h"
-#include <complex>
-#include <cmath>
-#include <vector>
-#include "fftw3.h"
-
-#define FILTERED_LFE
-
-#pragma comment (lib,"libfftw3f-3.lib")
-
-typedef std::complex<float> cfloat;
-
-const float PI = 3.141592654;
-const float epsilon = 0.000001;
-//const float center_level = 0.5*sqrt(0.5);   // gain of the center channel
-const float center_level = sqrt(0.5);   // gain of the center channel
-//const float center_level = 0.5;   // gain of the center channel
-
-// should be .6-.7
-// but with centerlevel 2x what its supposed to be, we halve 0.68
-// to keep center from clipping
-const float window_gain = 0.34;     
-
-// private implementation of the surround decoder
-class decoder_impl {
-public:
-    // create an instance of the decoder
-    //  blocksize is fixed over the lifetime of this object for performance reasons
-    decoder_impl(unsigned blocksize=8192): N(blocksize), halfN(blocksize/2) {
-        // create FFTW buffers
-        lt = (float*)fftwf_malloc(sizeof(float)*N);
-        rt = (float*)fftwf_malloc(sizeof(float)*N);
-        dst = (float*)fftwf_malloc(sizeof(float)*N);
-        dftL = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*N);
-        dftR = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*N);
-        src = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*N);
-        loadL = fftwf_plan_dft_r2c_1d(N, lt, dftL,FFTW_MEASURE);
-        loadR = fftwf_plan_dft_r2c_1d(N, rt, dftR,FFTW_MEASURE);
-        store = fftwf_plan_dft_c2r_1d(N, src, dst,FFTW_MEASURE);    
-        // resize our own buffers
-        frontR.resize(N);
-        frontL.resize(N);
-        avg.resize(N);
-        surR.resize(N);
-        surL.resize(N);
-#ifdef FILTERED_LFE
-        trueavg.resize(N);
-#endif
-        xfs.resize(N);
-        yfs.resize(N);
-        inbuf[0].resize(N);
-        inbuf[1].resize(N);
-        for (unsigned c=0;c<6;c++) {
-            outbuf[c].resize(N);
-            filter[c].resize(N);
-        }
-        // DC component of filters is always 0
-        for (unsigned c=0;c<5;c++)
-        {
-            filter[c][0] = 0.0;
-            filter[c][1] = 0.0;
-            filter[c][halfN] = 0.0;
-        }
-        sample_rate(48000);
-        // generate the window function (square root of hann, b/c it is applied before and after the transform)
-        wnd.resize(N);
-        // dft normalization included in the window for zero cost scaling
-        // also add a gain factor of *2 due to processing gain in algo (see center_level)
-        for (unsigned k=0;k<N;k++)
-            //wnd[k] = sqrt(0.5*(1-cos(2*PI*k/N))/N);
-            wnd[k] = sqrt(window_gain*0.5*(1-cos(2*PI*k/N))/N);
-        current_buf = 0;
-        // set the default coefficients
-        surround_coefficients(0.8165,0.5774);
-        phase_mode(0);
-        separation(1,1);
-        steering_mode(1);
-    }
-
-    // destructor
-    ~decoder_impl() {
-        // clean up the FFTW stuff
-        fftwf_destroy_plan(store);
-        fftwf_destroy_plan(loadR);
-        fftwf_destroy_plan(loadL);
-        fftwf_free(src); 
-        fftwf_free(dftR);
-        fftwf_free(dftL);
-        fftwf_free(dst);
-        fftwf_free(rt);
-        fftwf_free(lt);
-    }
-
-    float ** getInputBuffers()
-    {
-        inbufs[0] = &inbuf[0][current_buf*halfN];
-        inbufs[1] = &inbuf[1][current_buf*halfN];
-        return inbufs;
-    }
-
-    float ** getOutputBuffers()
-    {
-        outbufs[0] = &outbuf[0][current_buf*halfN];
-        outbufs[1] = &outbuf[1][current_buf*halfN];
-        outbufs[2] = &outbuf[2][current_buf*halfN];
-        outbufs[3] = &outbuf[3][current_buf*halfN];
-        outbufs[4] = &outbuf[4][current_buf*halfN];
-        outbufs[5] = &outbuf[5][current_buf*halfN];
-        return outbufs;
-    }
-
-    // decode a chunk of stereo sound, has to contain exactly blocksize samples
-    //  center_width [0..1] distributes the center information towards the front left/right channels, 1=full distribution, 0=no distribution
-    //  dimension [0..1] moves the soundfield backwards, 0=front, 1=side
-    //  adaption_rate [0..1] determines how fast the steering gets adapted, 1=instantaneous, 0.1 = very slow adaption
-    void decode(float center_width, float dimension, float adaption_rate) {
-        // process first part
-        int index;
-        index = current_buf*halfN;
-        float *in_second[2] = {&inbuf[0][index],&inbuf[1][index]};
-        current_buf ^= 1;
-        index = current_buf*halfN;
-        float *in_first[2] = {&inbuf[0][index],&inbuf[1][index]};
-        add_output(in_first,in_second,center_width,dimension,adaption_rate,true);
-        // shift last half of input buffer to the beginning
-    }
-    
-    // flush the internal buffers
-    void flush() {
-        for (unsigned k=0;k<N;k++) {
-            for (unsigned c=0;c<6;c++)
-                outbuf[c][k] = 0;
-            inbuf[0][k] = 0;
-            inbuf[1][k] = 0;
-        }
-    }
-
-    // set lfe filter params
-    void sample_rate(unsigned int srate) {
-        // lfe filter is just straight through band limited
-        unsigned int cutoff = (250*N)/srate;
-        for (unsigned f=0;f<=halfN;f++) {           
-            if ((f>=2) && (f<cutoff))
-                filter[5][f] = 1.0;
-            else
-                filter[5][f] = 0.0;
-        }
-    }
-
-    // set the assumed surround mixing coefficients
-    void surround_coefficients(float a, float b) {
-        master_gain = 1.0;
-        // calc the simple coefficients
-        surround_high = a;
-        surround_low = b;
-        surround_balance = (a-b)/(a+b);
-        surround_level = 1/(a+b);
-        // calc the linear coefficients
-        cfloat i(0,1), u((a+b)*i), v((b-a)*i), n(0.25,0),o(1,0);
-        A = (v-o)*n; B = (o-u)*n; C = (-o-v)*n; D = (o+u)*n;
-        E = (o+v)*n; F = (o+u)*n; G = (o-v)*n;  H = (o-u)*n;
-    }
-
-    // set the phase shifting mode
-    void phase_mode(unsigned mode) {
-        const float modes[4][2] = {{0,0},{0,PI},{PI,0},{-PI/2,PI/2}};
-        phase_offsetL = modes[mode][0];
-        phase_offsetR = modes[mode][1];
-    }
-
-    // what steering mode should be chosen
-    void steering_mode(bool mode) { linear_steering = mode; }
-
-    // set front & rear separation controls
-    void separation(float front, float rear) {
-        front_separation = front;
-        rear_separation = rear;
-    }
-
-private:
-    // polar <-> cartesian coodinates conversion
-    static inline float amplitude(const float cf[2]) { return sqrt(cf[0]*cf[0] + cf[1]*cf[1]); }
-    static inline float phase(const float cf[2]) { return atan2(cf[1],cf[0]); }
-    static inline cfloat polar(float a, float p) { return cfloat(a*cos(p),a*sin(p)); }
-    static inline float sqr(float x) { return x*x; }
-    // the dreaded min/max
-    static inline float min(float a, float b) { return a<b?a:b; }
-    static inline float max(float a, float b) { return a>b?a:b; }
-    static inline float clamp(float x) { return max(-1,min(1,x)); }
-
-    // handle the output buffering for overlapped calls of block_decode
-    void add_output(float *input1[2], float *input2[2], float center_width, float dimension, float adaption_rate, bool result=false) {
-        // add the windowed data to the last 1/2 of the output buffer
-        float *out[6] = {&outbuf[0][0],&outbuf[1][0],&outbuf[2][0],&outbuf[3][0],&outbuf[4][0],&outbuf[5][0]};
-        block_decode(input1,input2,out,center_width,dimension,adaption_rate);
-    }
-
-    // CORE FUNCTION: decode a block of data
-    void block_decode(float *input1[2], float *input2[2], float *output[6], float center_width, float dimension, float adaption_rate) {
-        // 1. scale the input by the window function; this serves a dual purpose:
-        // - first it improves the FFT resolution b/c boundary discontinuities (and their frequencies) get removed
-        // - second it allows for smooth blending of varying filters between the blocks
-        {
-            float* pWnd = &wnd[0];
-            float* pLt = &lt[0];
-            float* pRt = &rt[0];
-            float* pIn0 = input1[0];
-            float* pIn1 = input1[1];
-            for (unsigned k=0;k<halfN;k++) {
-                *pLt++ = *pIn0++ * *pWnd;
-                *pRt++ = *pIn1++ * *pWnd++;
-            }
-            pIn0 = input2[0];
-            pIn1 = input2[1];
-            //for (unsigned k=0,k1=halfN;k<halfN;k++,k1++) {
-            for (unsigned k=0;k<halfN;k++) {
-                *pLt++ = *pIn0++ * *pWnd;
-                *pRt++ = *pIn1++ * *pWnd++;
-            }
-        }
-
-        // ... and tranform it into the frequency domain
-        fftwf_execute(loadL);
-        fftwf_execute(loadR);
-
-        // 2. compare amplitude and phase of each DFT bin and produce the X/Y coordinates in the sound field
-        //    but dont do DC or N/2 component
-        for (unsigned f=2;f<halfN;f++) {           
-            // get left/right amplitudes/phases
-            float ampL = amplitude(dftL[f]), ampR = amplitude(dftR[f]);
-            float phaseL = phase(dftL[f]), phaseR = phase(dftR[f]);
-//          if (ampL+ampR < epsilon)
-//              continue;       
-
-            // calculate the amplitude/phase difference
-            float ampDiff = clamp((ampL+ampR < epsilon) ? 0 : (ampR-ampL) / (ampR+ampL));
-            float phaseDiff = phaseL - phaseR;
-            if (phaseDiff < -PI) phaseDiff += 2*PI;
-            if (phaseDiff > PI) phaseDiff -= 2*PI;
-            phaseDiff = abs(phaseDiff);
-
-            if (linear_steering) {
-/*              cfloat w = polar(sqrt(ampL*ampL+ampR*ampR), (phaseL+phaseR)/2);
-                cfloat lt = cfloat(dftL[f][0],dftL[f][1])/w, rt = cfloat(dftR[f][0],dftR[f][1])/w;              */
-//              xfs[f] = -(C*(rt-H) - B*E + F*A + G*(D-lt)) / (G*A - C*E).real();
-//              yfs[f] = (rt - (xfs[f]*E+H))/(F+xfs[f]*G);
-
-                /*
-                Problem: 
-                This assumes that the values are interpolated linearly between the cardinal points.
-                But this way we have no chance of knowing the average volume...
-                - Can we solve that computing everything under the assumption of normalized volume?
-                  No. Seemingly not.
-                - Maybe we should add w explitcitly into the equation and see if we can solve it...
-                */
-
-
-                //cfloat lt(0.5,0),rt(0.5,0);
-                //cfloat x(0,0), y(1,0);
-                /*cfloat p = (C*(rt-H) - B*E + F*A + G*(D-lt)) / (G*A - C*E);
-                cfloat q = B*(rt+H) + F*(D-lt) / (G*A - C*E);
-                cfloat s = sqrt(p*p/4.0f - q);
-                cfloat x = -p;
-                cfloat x1 = -p/2.0f + s;
-                cfloat x2 = -p/2.0f - s;
-                float x = 0;
-                if (x1.real() >= -1 && x1.real() <= 1)
-                    x = x1.real();
-                else if (x2.real() >= -1 && x2.real() <= 1)
-                    x = x2.real();*/
-
-                //cfloat yp = (rt - (x*E+H))/(F+x*G);
-                //cfloat xp = (lt - (y*B+D))/(A+y*C);
-
-                /*xfs[f] = x;
-                yfs[f] = y.real();*/
-
-                // --- this is the fancy new linear mode ---
-
-                // get sound field x/y position
-                yfs[f] = get_yfs(ampDiff,phaseDiff);
-                xfs[f] = get_xfs(ampDiff,yfs[f]);
-
-                // add dimension control
-                yfs[f] = clamp(yfs[f] - dimension);
-
-                // add crossfeed control
-                xfs[f] = clamp(xfs[f] * (front_separation*(1+yfs[f])/2 + rear_separation*(1-yfs[f])/2));
-
-                // 3. generate frequency filters for each output channel
-                float left = (1-xfs[f])/2, right = (1+xfs[f])/2;
-                float front = (1+yfs[f])/2, back = (1-yfs[f])/2;
-                float volume[5] = {
-                    front * (left * center_width + max(0,-xfs[f]) * (1-center_width)),  // left
-                    front * center_level*((1-abs(xfs[f])) * (1-center_width)),          // center
-                    front * (right * center_width + max(0, xfs[f]) * (1-center_width)), // right
-                    back * surround_level * left,                                       // left surround
-                    back * surround_level * right                                       // right surround
-                };
-
-                // adapt the prior filter
-                for (unsigned c=0;c<5;c++)
-                    filter[c][f] = (1-adaption_rate)*filter[c][f] + adaption_rate*volume[c]/*/N*/;
-
-            } else {
-                // --- this is the old & simple steering mode ---
-
-                // calculate the amplitude/phase difference
-                float ampDiff = clamp((ampL+ampR < epsilon) ? 0 : (ampR-ampL) / (ampR+ampL));
-                float phaseDiff = phaseL - phaseR;
-                if (phaseDiff < -PI) phaseDiff += 2*PI;
-                if (phaseDiff > PI) phaseDiff -= 2*PI;
-                phaseDiff = abs(phaseDiff);
-
-                // determine sound field x-position
-                xfs[f] = ampDiff;
-
-                // determine preliminary sound field y-position from phase difference
-                yfs[f] = 1 - (phaseDiff/PI)*2;
-
-                if (abs(xfs[f]) > surround_balance) {
-                    // blend linearly between the surrounds and the fronts if the balance exceeds the surround encoding balance
-                    // this is necessary because the sound field is trapezoidal and will be stretched behind the listener
-                    float frontness = (abs(xfs[f]) - surround_balance)/(1-surround_balance);
-                    yfs[f]  = (1-frontness) * yfs[f] + frontness * 1; 
-                }
-
-                // add dimension control
-                yfs[f] = clamp(yfs[f] - dimension);
-
-                // add crossfeed control
-                xfs[f] = clamp(xfs[f] * (front_separation*(1+yfs[f])/2 + rear_separation*(1-yfs[f])/2));
-
-                // 3. generate frequency filters for each output channel, according to the signal position
-                // the sum of all channel volumes must be 1.0
-                float left = (1-xfs[f])/2, right = (1+xfs[f])/2;
-                float front = (1+yfs[f])/2, back = (1-yfs[f])/2;
-                float volume[5] = {
-                    front * (left * center_width + max(0,-xfs[f]) * (1-center_width)),      // left
-                    front * center_level*((1-abs(xfs[f])) * (1-center_width)),              // center
-                    front * (right * center_width + max(0, xfs[f]) * (1-center_width)),     // right
-                    back * surround_level*max(0,min(1,((1-(xfs[f]/surround_balance))/2))),  // left surround
-                    back * surround_level*max(0,min(1,((1+(xfs[f]/surround_balance))/2)))   // right surround
-                };
-
-                // adapt the prior filter
-                for (unsigned c=0;c<5;c++)
-                    filter[c][f] = (1-adaption_rate)*filter[c][f] + adaption_rate*volume[c]/*/N*/;
-            }
-
-            // ... and build the signal which we want to position
-            frontL[f] = polar(ampL+ampR,phaseL);
-            frontR[f] = polar(ampL+ampR,phaseR);
-            avg[f] = frontL[f] + frontR[f];
-            surL[f] = polar(ampL+ampR,phaseL+phase_offsetL);
-            surR[f] = polar(ampL+ampR,phaseR+phase_offsetR);
-#ifdef FILTERED_LFE
-            trueavg[f] = cfloat(dftL[f][0] + dftR[f][0], dftL[f][1] + dftR[f][1]);
-#endif
-        }
-
-        // 4. distribute the unfiltered reference signals over the channels
-        apply_filter(&frontL[0],&filter[0][0],&output[0][0]);   // front left
-        apply_filter(&avg[0], &filter[1][0],&output[1][0]);     // front center
-        apply_filter(&frontR[0],&filter[2][0],&output[2][0]);   // front right
-        apply_filter(&surL[0],&filter[3][0],&output[3][0]);     // surround left
-        apply_filter(&surR[0],&filter[4][0],&output[4][0]);     // surround right
-#ifdef FILTERED_LFE
-        apply_filter(&trueavg[0],&filter[5][0],&output[5][0]);  // lfe
-#else
-        float* out5 = &output[5][(current_buf)*halfN];
-        float* in2l = &input2[0][0];
-        float* in2r = &input2[1][0];
-        //for (unsigned k=0,k2=N/4;k<halfN;k++,k2++) {
-        for (unsigned k=0;k<halfN;k++) {
-            *out5++ = *in2l++ + *in2r++;
-        }
-#endif
-    }
-
-#define FASTER_CALC
-    // map from amplitude difference and phase difference to yfs
-    inline double get_yfs(double ampDiff, double phaseDiff) {
-        double x = 1-(((1-sqr(ampDiff))*phaseDiff)/PI*2);
-#ifdef FASTER_CALC
-        double tanX = tan(x);
-        return 0.16468622925824683 + 0.5009268347818189*x - 0.06462757726992101*x*x
-            + 0.09170680403453149*x*x*x + 0.2617754892323973*tanX - 0.04180413533856156*sqr(tanX);
-#else
-        return 0.16468622925824683 + 0.5009268347818189*x - 0.06462757726992101*x*x
-            + 0.09170680403453149*x*x*x + 0.2617754892323973*tan(x) - 0.04180413533856156*sqr(tan(x));
-#endif
-    }
-
-    // map from amplitude difference and yfs to xfs
-    inline double get_xfs(double ampDiff, double yfs) {
-        double x=ampDiff,y=yfs;
-#ifdef FASTER_CALC
-        double tanX = tan(x);
-        double tanY = tan(y);
-        double asinX = asin(x);
-        double sinX = sin(x);
-        double sinY = sin(y);
-        double x3 = x*x*x;
-        double y2 = y*y;
-        double y3 = y*y2;
-        return 2.464833559224702*x - 423.52131153259404*x*y + 
-            67.8557858606918*x3*y + 788.2429425544392*x*y2 - 
-            79.97650354902909*x3*y2 - 513.8966153850349*x*y3 + 
-            35.68117670186306*x3*y3 + 13867.406173420834*y*asinX - 
-            2075.8237075786396*y2*asinX - 908.2722068360281*y3*asinX - 
-            12934.654772878019*asinX*sinY - 13216.736529661162*y*tanX + 
-            1288.6463247741938*y2*tanX + 1384.372969378453*y3*tanX + 
-            12699.231471126128*sinY*tanX + 95.37131275594336*sinX*tanY - 
-            91.21223198407546*tanX*tanY;
-#else
-        return 2.464833559224702*x - 423.52131153259404*x*y + 
-            67.8557858606918*x*x*x*y + 788.2429425544392*x*y*y - 
-            79.97650354902909*x*x*x*y*y - 513.8966153850349*x*y*y*y + 
-            35.68117670186306*x*x*x*y*y*y + 13867.406173420834*y*asin(x) - 
-            2075.8237075786396*y*y*asin(x) - 908.2722068360281*y*y*y*asin(x) - 
-            12934.654772878019*asin(x)*sin(y) - 13216.736529661162*y*tan(x) + 
-            1288.6463247741938*y*y*tan(x) + 1384.372969378453*y*y*y*tan(x) + 
-            12699.231471126128*sin(y)*tan(x) + 95.37131275594336*sin(x)*tan(y) - 
-            91.21223198407546*tan(x)*tan(y);
-#endif
-    }
-
-    // filter the complex source signal and add it to target
-    void apply_filter(cfloat *signal, float *flt, float *target) {
-        // filter the signal
-        for (unsigned f=0;f<=halfN;f++) {       
-            src[f][0] = signal[f].real() * flt[f];
-            src[f][1] = signal[f].imag() * flt[f];
-        }
-        // transform into time domain
-        fftwf_execute(store);
-
-        float* pT1   = &target[current_buf*halfN];
-        float* pWnd1 = &wnd[0];
-        float* pDst1 = &dst[0];
-        float* pT2   = &target[(current_buf^1)*halfN];
-        float* pWnd2 = &wnd[halfN];
-        float* pDst2 = &dst[halfN];
-        // add the result to target, windowed
-        for (unsigned int k=0;k<halfN;k++)
-        {
-            // 1st part is overlap add
-            *pT1++ += *pWnd1++ * *pDst1++;
-            // 2nd part is set as has no history
-            *pT2++ = *pWnd2++ * *pDst2++;
-        }
-    }
-
-    unsigned int N;                    // the block size
-    unsigned int halfN;                // half block size precalculated
-    // FFTW data structures
-    float *lt,*rt,*dst;                // left total, right total (source arrays), destination array
-    fftwf_complex *dftL,*dftR,*src;    // intermediate arrays (FFTs of lt & rt, processing source)
-    fftwf_plan loadL,loadR,store;      // plans for loading the data into the intermediate format and back
-    // buffers
-    std::vector<cfloat> frontL,frontR,avg,surL,surR; // the signal (phase-corrected) in the frequency domain
-#ifdef FILTERED_LFE
-    std::vector<cfloat> trueavg;       // for lfe generation
-#endif
-    std::vector<float> xfs,yfs;        // the feature space positions for each frequency bin
-    std::vector<float> wnd;            // the window function, precalculated
-    std::vector<float> filter[6];      // a frequency filter for each output channel
-    std::vector<float> inbuf[2];       // the sliding input buffers
-    std::vector<float> outbuf[6];      // the sliding output buffers
-    // coefficients
-    float surround_high,surround_low;  // high and low surround mixing coefficient (e.g. 0.8165/0.5774)
-    float surround_balance;            // the xfs balance that follows from the coeffs
-    float surround_level;              // gain for the surround channels (follows from the coeffs
-    float master_gain;                 // gain for all channels
-    float phase_offsetL, phase_offsetR;// phase shifts to be applied to the rear channels
-    float front_separation;            // front stereo separation
-    float rear_separation;             // rear stereo separation
-    bool linear_steering;              // whether the steering should be linear or not
-    cfloat A,B,C,D,E,F,G,H;            // coefficients for the linear steering
-    int current_buf;                   // specifies which buffer is 2nd half of input sliding buffer
-    float * inbufs[2];                 // for passing back to driver
-    float * outbufs[6];                // for passing back to driver
-
-    friend class fsurround_decoder;
-};
-
-
-// implementation of the shell class
-
-fsurround_decoder::fsurround_decoder(unsigned blocksize): impl(new decoder_impl(blocksize)) { }
-
-fsurround_decoder::~fsurround_decoder() { delete impl; }
-
-void fsurround_decoder::decode(float center_width, float dimension, float adaption_rate) {
-    impl->decode(center_width,dimension,adaption_rate);
-}
-
-void fsurround_decoder::flush() { impl->flush(); }
-
-void fsurround_decoder::surround_coefficients(float a, float b) { impl->surround_coefficients(a,b); }
-
-void fsurround_decoder::phase_mode(unsigned mode) { impl->phase_mode(mode); }
-
-void fsurround_decoder::steering_mode(bool mode) { impl->steering_mode(mode); }
-
-void fsurround_decoder::separation(float front, float rear) { impl->separation(front,rear); }
-
-float ** fsurround_decoder::getInputBuffers()
-{
-    return impl->getInputBuffers();
-}
-
-float ** fsurround_decoder::getOutputBuffers()
-{
-    return impl->getOutputBuffers();
-}
-
-void fsurround_decoder::sample_rate(unsigned int samplerate)
-{
-    impl->sample_rate(samplerate);
-}
+/*
+Copyright (C) 2007 Christian Kothe
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+//#define USE_FFTW3
+
+#include "el_processor.h"
+#include <complex>
+#include <cmath>
+#include <vector>
+#ifdef USE_FFTW3
+#include "fftw3.h"
+#else
+extern "C" {
+#include "dsputil.h"
+};
+typedef FFTSample FFTComplexArray[2];
+#endif
+
+
+#ifdef USE_FFTW3
+#pragma comment (lib,"libfftw3f-3.lib")
+#endif
+
+typedef std::complex<float> cfloat;
+
+const float PI = 3.141592654;
+const float epsilon = 0.000001;
+//const float center_level = 0.5*sqrt(0.5);   // gain of the center channel
+//const float center_level = sqrt(0.5);   // gain of the center channel
+const float center_level = 1.0;   // gain of the center channel
+//const float center_level = 0.5;   // gain of the center channel
+
+// should be .6-.7
+// but with centerlevel 2x what its supposed to be, we halve 0.68
+// to keep center from clipping
+//const float window_gain = 0.34;     
+//const float window_gain = 0.68;     
+const float window_gain = 0.95;     // to prive a bit of margin
+
+// private implementation of the surround decoder
+class decoder_impl {
+public:
+    // create an instance of the decoder
+    //  blocksize is fixed over the lifetime of this object for performance reasons
+    decoder_impl(unsigned blocksize=8192): N(blocksize), halfN(blocksize/2) {
+#ifdef USE_FFTW3
+        // create FFTW buffers
+        lt = (float*)fftwf_malloc(sizeof(float)*N);
+        rt = (float*)fftwf_malloc(sizeof(float)*N);
+        dst = (float*)fftwf_malloc(sizeof(float)*N);
+        dftL = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*N);
+        dftR = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*N);
+        src = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*N);
+        loadL = fftwf_plan_dft_r2c_1d(N, lt, dftL,FFTW_MEASURE);
+        loadR = fftwf_plan_dft_r2c_1d(N, rt, dftR,FFTW_MEASURE);
+        store = fftwf_plan_dft_c2r_1d(N, src, dst,FFTW_MEASURE);    
+#else
+        // create lavc fft buffers
+        lt = (float*)av_malloc(sizeof(FFTSample)*N);
+        rt = (float*)av_malloc(sizeof(FFTSample)*N);
+        dftL = (FFTComplexArray*)av_malloc(sizeof(FFTComplex)*N*2);
+        dftR = (FFTComplexArray*)av_malloc(sizeof(FFTComplex)*N*2);
+        src = (FFTComplexArray*)av_malloc(sizeof(FFTComplex)*N*2);
+        fftContextForward = (FFTContext*)av_malloc(sizeof(FFTContext));
+        memset(fftContextForward, 0, sizeof(FFTContext));
+        fftContextReverse = (FFTContext*)av_malloc(sizeof(FFTContext));
+        memset(fftContextReverse, 0, sizeof(FFTContext));
+        ff_fft_init(fftContextForward, 13, 0);
+        ff_fft_init(fftContextReverse, 13, 1);
+#endif
+        // resize our own buffers
+        frontR.resize(N);
+        frontL.resize(N);
+        avg.resize(N);
+        surR.resize(N);
+        surL.resize(N);
+        trueavg.resize(N);
+        xfs.resize(N);
+        yfs.resize(N);
+        inbuf[0].resize(N);
+        inbuf[1].resize(N);
+        for (unsigned c=0;c<6;c++) {
+            outbuf[c].resize(N);
+            filter[c].resize(N);
+        }
+        // DC component of filters is always 0
+        for (unsigned c=0;c<5;c++)
+        {
+            filter[c][0] = 0.0;
+            filter[c][1] = 0.0;
+            filter[c][halfN] = 0.0;
+        }
+        sample_rate(48000);
+        // generate the window function (square root of hann, b/c it is applied before and after the transform)
+        wnd.resize(N);
+        // dft normalization included in the window for zero cost scaling
+        // also add a gain factor of *2 due to processing gain in algo (see center_level)
+        surround_gain(1.0);
+        current_buf = 0;
+        // set the default coefficients
+        surround_coefficients(0.8165,0.5774);
+        phase_mode(0);
+        separation(1,1);
+        steering_mode(1);
+    }
+
+    // destructor
+    ~decoder_impl() {
+#ifdef USE_FFTW3
+        // clean up the FFTW stuff
+        fftwf_destroy_plan(store);
+        fftwf_destroy_plan(loadR);
+        fftwf_destroy_plan(loadL);
+        fftwf_free(src); 
+        fftwf_free(dftR);
+        fftwf_free(dftL);
+        fftwf_free(dst);
+        fftwf_free(rt);
+        fftwf_free(lt);
+#else
+        ff_fft_end(fftContextForward);
+        ff_fft_end(fftContextReverse);
+        av_free(src); 
+        av_free(dftR);
+        av_free(dftL);
+        av_free(rt);
+        av_free(lt);
+        av_free(fftContextForward); 
+        av_free(fftContextReverse); 
+#endif
+    }
+
+    float ** getInputBuffers()
+    {
+        inbufs[0] = &inbuf[0][current_buf*halfN];
+        inbufs[1] = &inbuf[1][current_buf*halfN];
+        return inbufs;
+    }
+
+    float ** getOutputBuffers()
+    {
+        outbufs[0] = &outbuf[0][current_buf*halfN];
+        outbufs[1] = &outbuf[1][current_buf*halfN];
+        outbufs[2] = &outbuf[2][current_buf*halfN];
+        outbufs[3] = &outbuf[3][current_buf*halfN];
+        outbufs[4] = &outbuf[4][current_buf*halfN];
+        outbufs[5] = &outbuf[5][current_buf*halfN];
+        return outbufs;
+    }
+
+    // decode a chunk of stereo sound, has to contain exactly blocksize samples
+    //  center_width [0..1] distributes the center information towards the front left/right channels, 1=full distribution, 0=no distribution
+    //  dimension [0..1] moves the soundfield backwards, 0=front, 1=side
+    //  adaption_rate [0..1] determines how fast the steering gets adapted, 1=instantaneous, 0.1 = very slow adaption
+    void decode(float center_width, float dimension, float adaption_rate) {
+        // process first part
+        int index;
+        index = current_buf*halfN;
+        float *in_second[2] = {&inbuf[0][index],&inbuf[1][index]};
+        current_buf ^= 1;
+        index = current_buf*halfN;
+        float *in_first[2] = {&inbuf[0][index],&inbuf[1][index]};
+        add_output(in_first,in_second,center_width,dimension,adaption_rate,true);
+        // shift last half of input buffer to the beginning
+    }
+    
+    // flush the internal buffers
+    void flush() {
+        for (unsigned k=0;k<N;k++) {
+            for (unsigned c=0;c<6;c++)
+                outbuf[c][k] = 0;
+            inbuf[0][k] = 0;
+            inbuf[1][k] = 0;
+        }
+    }
+
+    // set lfe filter params
+    void sample_rate(unsigned int srate) {
+        // lfe filter is just straight through band limited
+        unsigned int cutoff = (250*N)/srate;
+        for (unsigned f=0;f<=halfN;f++) {           
+            if ((f>=2) && (f<cutoff))
+                filter[5][f] = 1.0;
+            else
+                filter[5][f] = 0.0;
+        }
+    }
+
+    // set the assumed surround mixing coefficients
+    void surround_coefficients(float a, float b) {
+        // calc the simple coefficients
+        surround_high = a;
+        surround_low = b;
+        surround_balance = (a-b)/(a+b);
+        surround_level = 1/(a+b);
+        // calc the linear coefficients
+        cfloat i(0,1), u((a+b)*i), v((b-a)*i), n(0.25,0),o(1,0);
+        A = (v-o)*n; B = (o-u)*n; C = (-o-v)*n; D = (o+u)*n;
+        E = (o+v)*n; F = (o+u)*n; G = (o-v)*n;  H = (o-u)*n;
+    }
+
+    void surround_gain(float gain) {
+        master_gain = gain * window_gain * 0.5 * 0.25;
+        for (unsigned k=0;k<N;k++)
+            wnd[k] = sqrt(master_gain*(1-cos(2*PI*k/N))/N);
+    }
+
+    // set the phase shifting mode
+    void phase_mode(unsigned mode) {
+        const float modes[4][2] = {{0,0},{0,PI},{PI,0},{-PI/2,PI/2}};
+        phase_offsetL = modes[mode][0];
+        phase_offsetR = modes[mode][1];
+    }
+
+    // what steering mode should be chosen
+    void steering_mode(bool mode) { linear_steering = mode; }
+
+    // set front & rear separation controls
+    void separation(float front, float rear) {
+        front_separation = front;
+        rear_separation = rear;
+    }
+
+private:
+    // polar <-> cartesian coodinates conversion
+    static inline float amplitude(const float cf[2]) { return sqrt(cf[0]*cf[0] + cf[1]*cf[1]); }
+    static inline float phase(const float cf[2]) { return atan2(cf[1],cf[0]); }
+    static inline cfloat polar(float a, float p) { return cfloat(a*cos(p),a*sin(p)); }
+    static inline float sqr(float x) { return x*x; }
+    // the dreaded min/max
+    static inline float min(float a, float b) { return a<b?a:b; }
+    static inline float max(float a, float b) { return a>b?a:b; }
+    static inline float clamp(float x) { return max(-1,min(1,x)); }
+
+    // handle the output buffering for overlapped calls of block_decode
+    void add_output(float *input1[2], float *input2[2], float center_width, float dimension, float adaption_rate, bool result=false) {
+        // add the windowed data to the last 1/2 of the output buffer
+        float *out[6] = {&outbuf[0][0],&outbuf[1][0],&outbuf[2][0],&outbuf[3][0],&outbuf[4][0],&outbuf[5][0]};
+        block_decode(input1,input2,out,center_width,dimension,adaption_rate);
+    }
+
+    // CORE FUNCTION: decode a block of data
+    void block_decode(float *input1[2], float *input2[2], float *output[6], float center_width, float dimension, float adaption_rate) {
+        // 1. scale the input by the window function; this serves a dual purpose:
+        // - first it improves the FFT resolution b/c boundary discontinuities (and their frequencies) get removed
+        // - second it allows for smooth blending of varying filters between the blocks
+        {
+            float* pWnd = &wnd[0];
+            float* pLt = &lt[0];
+            float* pRt = &rt[0];
+            float* pIn0 = input1[0];
+            float* pIn1 = input1[1];
+            for (unsigned k=0;k<halfN;k++) {
+                *pLt++ = *pIn0++ * *pWnd;
+                *pRt++ = *pIn1++ * *pWnd++;
+            }
+            pIn0 = input2[0];
+            pIn1 = input2[1];
+            for (unsigned k=0;k<halfN;k++) {
+                *pLt++ = *pIn0++ * *pWnd;
+                *pRt++ = *pIn1++ * *pWnd++;
+            }
+        }
+
+#ifdef USE_FFTW3
+        // ... and tranform it into the frequency domain
+        fftwf_execute(loadL);
+        fftwf_execute(loadR);
+#else
+        ff_fft_permuteRC(fftContextForward, lt, (FFTComplex*)&dftL[0]);
+        ff_fft_permuteRC(fftContextForward, rt, (FFTComplex*)&dftR[0]);
+        ff_fft_calc(fftContextForward, (FFTComplex*)&dftL[0]);
+        ff_fft_calc(fftContextForward, (FFTComplex*)&dftR[0]);
+#endif
+
+        // 2. compare amplitude and phase of each DFT bin and produce the X/Y coordinates in the sound field
+        //    but dont do DC or N/2 component
+        for (unsigned f=2;f<halfN;f++) {           
+            // get left/right amplitudes/phases
+            float ampL = amplitude(dftL[f]), ampR = amplitude(dftR[f]);
+            float phaseL = phase(dftL[f]), phaseR = phase(dftR[f]);
+//          if (ampL+ampR < epsilon)
+//              continue;       
+
+            // calculate the amplitude/phase difference
+            float ampDiff = clamp((ampL+ampR < epsilon) ? 0 : (ampR-ampL) / (ampR+ampL));
+            float phaseDiff = phaseL - phaseR;
+            if (phaseDiff < -PI) phaseDiff += 2*PI;
+            if (phaseDiff > PI) phaseDiff -= 2*PI;
+            phaseDiff = abs(phaseDiff);
+
+            if (linear_steering) {
+/*              cfloat w = polar(sqrt(ampL*ampL+ampR*ampR), (phaseL+phaseR)/2);
+                cfloat lt = cfloat(dftL[f][0],dftL[f][1])/w, rt = cfloat(dftR[f][0],dftR[f][1])/w;              */
+//              xfs[f] = -(C*(rt-H) - B*E + F*A + G*(D-lt)) / (G*A - C*E).real();
+//              yfs[f] = (rt - (xfs[f]*E+H))/(F+xfs[f]*G);
+
+                /*
+                Problem: 
+                This assumes that the values are interpolated linearly between the cardinal points.
+                But this way we have no chance of knowing the average volume...
+                - Can we solve that computing everything under the assumption of normalized volume?
+                  No. Seemingly not.
+                - Maybe we should add w explitcitly into the equation and see if we can solve it...
+                */
+
+
+                //cfloat lt(0.5,0),rt(0.5,0);
+                //cfloat x(0,0), y(1,0);
+                /*cfloat p = (C*(rt-H) - B*E + F*A + G*(D-lt)) / (G*A - C*E);
+                cfloat q = B*(rt+H) + F*(D-lt) / (G*A - C*E);
+                cfloat s = sqrt(p*p/4.0f - q);
+                cfloat x = -p;
+                cfloat x1 = -p/2.0f + s;
+                cfloat x2 = -p/2.0f - s;
+                float x = 0;
+                if (x1.real() >= -1 && x1.real() <= 1)
+                    x = x1.real();
+                else if (x2.real() >= -1 && x2.real() <= 1)
+                    x = x2.real();*/
+
+                //cfloat yp = (rt - (x*E+H))/(F+x*G);
+                //cfloat xp = (lt - (y*B+D))/(A+y*C);
+
+                /*xfs[f] = x;
+                yfs[f] = y.real();*/
+
+                // --- this is the fancy new linear mode ---
+
+                // get sound field x/y position
+                yfs[f] = get_yfs(ampDiff,phaseDiff);
+                xfs[f] = get_xfs(ampDiff,yfs[f]);
+
+                // add dimension control
+                yfs[f] = clamp(yfs[f] - dimension);
+
+                // add crossfeed control
+                xfs[f] = clamp(xfs[f] * (front_separation*(1+yfs[f])/2 + rear_separation*(1-yfs[f])/2));
+
+                // 3. generate frequency filters for each output channel
+                float left = (1-xfs[f])/2, right = (1+xfs[f])/2;
+                float front = (1+yfs[f])/2, back = (1-yfs[f])/2;
+                float volume[5] = {
+                    front * (left * center_width + max(0,-xfs[f]) * (1-center_width)),  // left
+                    front * center_level*((1-abs(xfs[f])) * (1-center_width)),          // center
+                    front * (right * center_width + max(0, xfs[f]) * (1-center_width)), // right
+                    back * surround_level * left,                                       // left surround
+                    back * surround_level * right                                       // right surround
+                };
+
+                // adapt the prior filter
+                for (unsigned c=0;c<5;c++)
+                    filter[c][f] = (1-adaption_rate)*filter[c][f] + adaption_rate*volume[c];
+
+            } else {
+                // --- this is the old & simple steering mode ---
+
+                // calculate the amplitude/phase difference
+                float ampDiff = clamp((ampL+ampR < epsilon) ? 0 : (ampR-ampL) / (ampR+ampL));
+                float phaseDiff = phaseL - phaseR;
+                if (phaseDiff < -PI) phaseDiff += 2*PI;
+                if (phaseDiff > PI) phaseDiff -= 2*PI;
+                phaseDiff = abs(phaseDiff);
+
+                // determine sound field x-position
+                xfs[f] = ampDiff;
+
+                // determine preliminary sound field y-position from phase difference
+                yfs[f] = 1 - (phaseDiff/PI)*2;
+
+                if (abs(xfs[f]) > surround_balance) {
+                    // blend linearly between the surrounds and the fronts if the balance exceeds the surround encoding balance
+                    // this is necessary because the sound field is trapezoidal and will be stretched behind the listener
+                    float frontness = (abs(xfs[f]) - surround_balance)/(1-surround_balance);
+                    yfs[f]  = (1-frontness) * yfs[f] + frontness * 1; 
+                }
+
+                // add dimension control
+                yfs[f] = clamp(yfs[f] - dimension);
+
+                // add crossfeed control
+                xfs[f] = clamp(xfs[f] * (front_separation*(1+yfs[f])/2 + rear_separation*(1-yfs[f])/2));
+
+                // 3. generate frequency filters for each output channel, according to the signal position
+                // the sum of all channel volumes must be 1.0
+                float left = (1-xfs[f])/2, right = (1+xfs[f])/2;
+                float front = (1+yfs[f])/2, back = (1-yfs[f])/2;
+                float volume[5] = {
+                    front * (left * center_width + max(0,-xfs[f]) * (1-center_width)),      // left
+                    front * center_level*((1-abs(xfs[f])) * (1-center_width)),              // center
+                    front * (right * center_width + max(0, xfs[f]) * (1-center_width)),     // right
+                    back * surround_level*max(0,min(1,((1-(xfs[f]/surround_balance))/2))),  // left surround
+                    back * surround_level*max(0,min(1,((1+(xfs[f]/surround_balance))/2)))   // right surround
+                };
+
+                // adapt the prior filter
+                for (unsigned c=0;c<5;c++)
+                    filter[c][f] = (1-adaption_rate)*filter[c][f] + adaption_rate*volume[c];
+            }
+
+            // ... and build the signal which we want to position
+            frontL[f] = polar(ampL+ampR,phaseL);
+            frontR[f] = polar(ampL+ampR,phaseR);
+            avg[f] = frontL[f] + frontR[f];
+            surL[f] = polar(ampL+ampR,phaseL+phase_offsetL);
+            surR[f] = polar(ampL+ampR,phaseR+phase_offsetR);
+            trueavg[f] = cfloat(dftL[f][0] + dftR[f][0], dftL[f][1] + dftR[f][1]);
+        }
+
+        // 4. distribute the unfiltered reference signals over the channels
+        apply_filter(&frontL[0],&filter[0][0],&output[0][0]);   // front left
+        apply_filter(&avg[0], &filter[1][0],&output[1][0]);     // front center
+        apply_filter(&frontR[0],&filter[2][0],&output[2][0]);   // front right
+        apply_filter(&surL[0],&filter[3][0],&output[3][0]);     // surround left
+        apply_filter(&surR[0],&filter[4][0],&output[4][0]);     // surround right
+        apply_filter(&trueavg[0],&filter[5][0],&output[5][0]);  // lfe
+    }
+
+#define FASTER_CALC
+    // map from amplitude difference and phase difference to yfs
+    inline double get_yfs(double ampDiff, double phaseDiff) {
+        double x = 1-(((1-sqr(ampDiff))*phaseDiff)/PI*2);
+#ifdef FASTER_CALC
+        double tanX = tan(x);
+        return 0.16468622925824683 + 0.5009268347818189*x - 0.06462757726992101*x*x
+            + 0.09170680403453149*x*x*x + 0.2617754892323973*tanX - 0.04180413533856156*sqr(tanX);
+#else
+        return 0.16468622925824683 + 0.5009268347818189*x - 0.06462757726992101*x*x
+            + 0.09170680403453149*x*x*x + 0.2617754892323973*tan(x) - 0.04180413533856156*sqr(tan(x));
+#endif
+    }
+
+    // map from amplitude difference and yfs to xfs
+    inline double get_xfs(double ampDiff, double yfs) {
+        double x=ampDiff,y=yfs;
+#ifdef FASTER_CALC
+        double tanX = tan(x);
+        double tanY = tan(y);
+        double asinX = asin(x);
+        double sinX = sin(x);
+        double sinY = sin(y);
+        double x3 = x*x*x;
+        double y2 = y*y;
+        double y3 = y*y2;
+        return 2.464833559224702*x - 423.52131153259404*x*y + 
+            67.8557858606918*x3*y + 788.2429425544392*x*y2 - 
+            79.97650354902909*x3*y2 - 513.8966153850349*x*y3 + 
+            35.68117670186306*x3*y3 + 13867.406173420834*y*asinX - 
+            2075.8237075786396*y2*asinX - 908.2722068360281*y3*asinX - 
+            12934.654772878019*asinX*sinY - 13216.736529661162*y*tanX + 
+            1288.6463247741938*y2*tanX + 1384.372969378453*y3*tanX + 
+            12699.231471126128*sinY*tanX + 95.37131275594336*sinX*tanY - 
+            91.21223198407546*tanX*tanY;
+#else
+        return 2.464833559224702*x - 423.52131153259404*x*y + 
+            67.8557858606918*x*x*x*y + 788.2429425544392*x*y*y - 
+            79.97650354902909*x*x*x*y*y - 513.8966153850349*x*y*y*y + 
+            35.68117670186306*x*x*x*y*y*y + 13867.406173420834*y*asin(x) - 
+            2075.8237075786396*y*y*asin(x) - 908.2722068360281*y*y*y*asin(x) - 
+            12934.654772878019*asin(x)*sin(y) - 13216.736529661162*y*tan(x) + 
+            1288.6463247741938*y*y*tan(x) + 1384.372969378453*y*y*y*tan(x) + 
+            12699.231471126128*sin(y)*tan(x) + 95.37131275594336*sin(x)*tan(y) - 
+            91.21223198407546*tan(x)*tan(y);
+#endif
+    }
+
+    // filter the complex source signal and add it to target
+    void apply_filter(cfloat *signal, float *flt, float *target) {
+        // filter the signal
+        unsigned f;
+        for (f=0;f<=halfN;f++) {
+            src[f][0] = signal[f].real() * flt[f];
+            src[f][1] = signal[f].imag() * flt[f];
+        }
+#ifdef USE_FFTW3
+        // transform into time domain
+        fftwf_execute(store);
+
+        float* pT1   = &target[current_buf*halfN];
+        float* pWnd1 = &wnd[0];
+        float* pDst1 = &dst[0];
+        float* pT2   = &target[(current_buf^1)*halfN];
+        float* pWnd2 = &wnd[halfN];
+        float* pDst2 = &dst[halfN];
+        // add the result to target, windowed
+        for (unsigned int k=0;k<halfN;k++)
+        {
+            // 1st part is overlap add
+            *pT1++ += *pWnd1++ * *pDst1++;
+            // 2nd part is set as has no history
+            *pT2++  = *pWnd2++ * *pDst2++;
+        }
+#else
+        // enforce odd symmetry
+        for (f=1;f<halfN;f++) {
+            src[N-f][0] = src[f][0];
+            src[N-f][1] = -src[f][1];   // complex conjugate
+        }
+        ff_fft_permute(fftContextReverse, (FFTComplex*)&src[0]);
+        ff_fft_calc(fftContextReverse, (FFTComplex*)&src[0]);
+
+        float* pT1   = &target[current_buf*halfN];
+        float* pWnd1 = &wnd[0];
+        float* pDst1 = &src[0][0];
+        float* pT2   = &target[(current_buf^1)*halfN];
+        float* pWnd2 = &wnd[halfN];
+        float* pDst2 = &src[halfN][0];
+        // add the result to target, windowed
+        for (unsigned int k=0;k<halfN;k++)
+        {
+            // 1st part is overlap add
+            *pT1++ += *pWnd1++ * *pDst1; pDst1 += 2;
+            // 2nd part is set as has no history
+            *pT2++  = *pWnd2++ * *pDst2; pDst2 += 2;
+        }
+#endif
+    }
+
+#ifndef USE_FFTW3
+    /**
+     *  * Do the permutation needed BEFORE calling ff_fft_calc()
+     *  special for freesurround that also copies
+     *   */
+    void ff_fft_permuteRC(FFTContext *s, FFTSample *r, FFTComplex *z)
+    {
+        int j, k, np;
+        const uint16_t *revtab = s->revtab;
+
+        /* reverse */
+        np = 1 << s->nbits;
+        for(j=0;j<np;j++) {
+            k = revtab[j];
+            z[k].re = r[j];
+            z[k].im = 0.0;
+        }
+    }
+
+    /**
+     *  * Do the permutation needed BEFORE calling ff_fft_calc()
+     *  special for freesurround that also copies and 
+     *  discards im component as it should be 0
+     *   */
+    void ff_fft_permuteCR(FFTContext *s, FFTComplex *z, FFTSample *r)
+    {
+        int j, k, np;
+        FFTComplex tmp;
+        const uint16_t *revtab = s->revtab;
+
+        /* reverse */
+        np = 1 << s->nbits;
+        for(j=0;j<np;j++) {
+            k = revtab[j];
+            if (k < j) {
+                r[k] = z[j].re;
+                r[j] = z[k].re;
+            }
+        }
+    }
+#endif
+
+    unsigned int N;                    // the block size
+    unsigned int halfN;                // half block size precalculated
+#ifdef USE_FFTW3
+    // FFTW data structures
+    float *lt,*rt,*dst;                // left total, right total (source arrays), destination array
+    fftwf_complex *dftL,*dftR,*src;    // intermediate arrays (FFTs of lt & rt, processing source)
+    fftwf_plan loadL,loadR,store;      // plans for loading the data into the intermediate format and back
+#else
+    FFTContext *fftContextForward, *fftContextReverse; 
+    FFTSample *lt,*rt;                 // left total, right total (source arrays), destination array
+    FFTComplexArray *dftL,*dftR,*src;  // intermediate arrays (FFTs of lt & rt, processing source)
+#endif
+    // buffers
+    std::vector<cfloat> frontL,frontR,avg,surL,surR; // the signal (phase-corrected) in the frequency domain
+    std::vector<cfloat> trueavg;       // for lfe generation
+    std::vector<float> xfs,yfs;        // the feature space positions for each frequency bin
+    std::vector<float> wnd;            // the window function, precalculated
+    std::vector<float> filter[6];      // a frequency filter for each output channel
+    std::vector<float> inbuf[2];       // the sliding input buffers
+    std::vector<float> outbuf[6];      // the sliding output buffers
+    // coefficients
+    float surround_high,surround_low;  // high and low surround mixing coefficient (e.g. 0.8165/0.5774)
+    float surround_balance;            // the xfs balance that follows from the coeffs
+    float surround_level;              // gain for the surround channels (follows from the coeffs
+    float master_gain;                 // gain for all channels
+    float phase_offsetL, phase_offsetR;// phase shifts to be applied to the rear channels
+    float front_separation;            // front stereo separation
+    float rear_separation;             // rear stereo separation
+    bool linear_steering;              // whether the steering should be linear or not
+    cfloat A,B,C,D,E,F,G,H;            // coefficients for the linear steering
+    int current_buf;                   // specifies which buffer is 2nd half of input sliding buffer
+    float * inbufs[2];                 // for passing back to driver
+    float * outbufs[6];                // for passing back to driver
+
+    friend class fsurround_decoder;
+};
+
+
+// implementation of the shell class
+
+fsurround_decoder::fsurround_decoder(unsigned blocksize): impl(new decoder_impl(blocksize)) { }
+
+fsurround_decoder::~fsurround_decoder() { delete impl; }
+
+void fsurround_decoder::decode(float center_width, float dimension, float adaption_rate) {
+    impl->decode(center_width,dimension,adaption_rate);
+}
+
+void fsurround_decoder::flush() { impl->flush(); }
+
+void fsurround_decoder::surround_coefficients(float a, float b) { impl->surround_coefficients(a,b); }
+
+void fsurround_decoder::gain(float gain) { impl->surround_gain(gain); }
+
+void fsurround_decoder::phase_mode(unsigned mode) { impl->phase_mode(mode); }
+
+void fsurround_decoder::steering_mode(bool mode) { impl->steering_mode(mode); }
+
+void fsurround_decoder::separation(float front, float rear) { impl->separation(front,rear); }
+
+float ** fsurround_decoder::getInputBuffers()
+{
+    return impl->getInputBuffers();
+}
+
+float ** fsurround_decoder::getOutputBuffers()
+{
+    return impl->getOutputBuffers();
+}
+
+void fsurround_decoder::sample_rate(unsigned int samplerate)
+{
+    impl->sample_rate(samplerate);
+}
Index: libs/libmythfreesurround/freesurround.cpp
===================================================================
--- libs/libmythfreesurround/freesurround.cpp	(revision 15853)
+++ libs/libmythfreesurround/freesurround.cpp	(working copy)
@@ -192,10 +192,14 @@
     {
         params.phasemode = 1;
         params.center_width = 0;
+        params.gain = 1.0;
     }
     else
     {
-        params.center_width = 50;
+        params.center_width = 70;
+        // for 50, gain should be about 1.9, c/lr about 2.7
+        // for 70, gain should be about 3.1, c/lr about 1.5
+        params.gain = 3.1;
     }
     switch (surround_mode)
     {
@@ -231,6 +235,7 @@
         decoder->phase_mode(params.phasemode);
         decoder->surround_coefficients(params.coeff_a, params.coeff_b);				
         decoder->separation(params.front_sep/100.0,params.rear_sep/100.0);
+        decoder->gain(params.gain);
     }
 }
 
@@ -244,7 +249,8 @@
     phasemode(0),
     steering(1),
     front_sep(100),
-    rear_sep(100) 
+    rear_sep(100), 
+    gain(1.0)
 {
 }
 
@@ -267,50 +273,6 @@
     VERBOSE(QString("FreeSurround::~FreeSurround done"));
 }
 
-void get_peak_i(short* data, int count, int* maxv, int* minv)
-{
-    int _maxv = *data++;
-    int _minv = _maxv;
-    for(int i=1;i<count;i++)
-    {
-        int v = *data++;
-        if (v > _maxv) _maxv = v;
-        if (v < _minv) _minv = v;
-    }
-    *maxv = _maxv;
-    *minv = _minv;
-}
-
-void get_peak_i2(short* data, int count, int* maxv, int* minv)
-{
-    int _maxv = *data;
-    data += 2;
-    int _minv = _maxv;
-    for(int i=1;i<count;i++)
-    {
-        int v = *data;
-        if (v > _maxv) _maxv = v;
-        if (v < _minv) _minv = v;
-        data  += 2;
-    }
-    *maxv = _maxv;
-    *minv = _minv;
-}
-
-void get_peak(float* data, int count, int* maxv, int* minv)
-{
-    int _maxv = lrintf(*data++);
-    int _minv = _maxv;
-    for(int i=1;i<count;i++)
-    {
-        int v = lrintf(*data++);
-        if (v > _maxv) _maxv = v;
-        if (v < _minv) _minv = v;
-    }
-    *maxv = _maxv;
-    *minv = _minv;
-}
-
 uint FreeSurround::putSamples(short* samples, uint numSamples, uint numChannels, int step)
 {
     int i;
Index: libs/libmythfreesurround/el_processor.h
===================================================================
--- libs/libmythfreesurround/el_processor.h	(revision 15853)
+++ libs/libmythfreesurround/el_processor.h	(working copy)
@@ -1,74 +1,77 @@
-/*
-Copyright (C) 2007 Christian Kothe
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-
-#ifndef EL_PROCESSOR_H
-#define EL_PROCESSOR_H
-
-// the Free Surround decoder
-class fsurround_decoder {
-public:
-	// create an instance of the decoder
-	//  blocksize is fixed over the lifetime of this object for performance reasons
-	fsurround_decoder(unsigned blocksize=8192);
-	// destructor
-	~fsurround_decoder();
-	
-    float ** getInputBuffers();
-    float ** getOutputBuffers();
-
-	// decode a chunk of stereo sound, has to contain exactly blocksize samples
-	//  center_width [0..1] distributes the center information towards the front left/right channels, 1=full distribution, 0=no distribution
-	//  dimension [0..1] moves the soundfield backwards, 0=front, 1=side
-	//  adaption_rate [0..1] determines how fast the steering gets adapted, 1=instantaneous, 0.1 = very slow adaption
-	//void decode(float *input[2], float *output[6], float center_width=1, float dimension=0, float adaption_rate=1);	
-	void decode(float center_width=1, float dimension=0, float adaption_rate=1);	
-	
-	// flush the internal buffers
-	void flush();
-
-	// --- advanced configuration ---
-
-	// override the surround coefficients
-	//  a is the coefficient of left rear in left total, b is the coefficient of left rear in right total; the same is true for right.
-	void surround_coefficients(float a, float b);
-
-	// set the phase shifting mode for decoding
-	// 0 = (+0°,+0°)   - music mode
-	// 1 = (+0°,+180°) - PowerDVD compatibility
-	// 2 = (+180°,+0°) - BeSweet compatibility
-	// 3 = (-90°,+90°) - This seems to work. I just don't know why.
-	void phase_mode(unsigned mode);
-
-	// override the steering mode
-	//  false = simple non-linear steering (old)
-	//  true  = advanced linear steering (new)
-	void steering_mode(bool mode);
-
-	// set front/rear stereo separation
-	//  1.0 is default, 0.0 is mono
-	void separation(float front,float rear);
-
-    // set samplerate for lfe filter
-    void sample_rate(unsigned int samplerate);
-
-private:
-	class decoder_impl *impl; // private implementation (details hidden)
-};
-
-
-#endif
+/*
+Copyright (C) 2007 Christian Kothe
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef EL_PROCESSOR_H
+#define EL_PROCESSOR_H
+
+// the Free Surround decoder
+class fsurround_decoder {
+public:
+	// create an instance of the decoder
+	//  blocksize is fixed over the lifetime of this object for performance reasons
+	fsurround_decoder(unsigned blocksize=8192);
+	// destructor
+	~fsurround_decoder();
+	
+    float ** getInputBuffers();
+    float ** getOutputBuffers();
+
+	// decode a chunk of stereo sound, has to contain exactly blocksize samples
+	//  center_width [0..1] distributes the center information towards the front left/right channels, 1=full distribution, 0=no distribution
+	//  dimension [0..1] moves the soundfield backwards, 0=front, 1=side
+	//  adaption_rate [0..1] determines how fast the steering gets adapted, 1=instantaneous, 0.1 = very slow adaption
+	//void decode(float *input[2], float *output[6], float center_width=1, float dimension=0, float adaption_rate=1);	
+	void decode(float center_width=1, float dimension=0, float adaption_rate=1);	
+	
+	// flush the internal buffers
+	void flush();
+
+	// --- advanced configuration ---
+
+	// override the surround coefficients
+	//  a is the coefficient of left rear in left total, b is the coefficient of left rear in right total; the same is true for right.
+	void surround_coefficients(float a, float b);
+
+    // override for master surround gain
+	void gain(float gain);
+
+	// set the phase shifting mode for decoding
+	// 0 = (+0°,+0°)   - music mode
+	// 1 = (+0°,+180°) - PowerDVD compatibility
+	// 2 = (+180°,+0°) - BeSweet compatibility
+	// 3 = (-90°,+90°) - This seems to work. I just don't know why.
+	void phase_mode(unsigned mode);
+
+	// override the steering mode
+	//  false = simple non-linear steering (old)
+	//  true  = advanced linear steering (new)
+	void steering_mode(bool mode);
+
+	// set front/rear stereo separation
+	//  1.0 is default, 0.0 is mono
+	void separation(float front,float rear);
+
+    // set samplerate for lfe filter
+    void sample_rate(unsigned int samplerate);
+
+private:
+	class decoder_impl *impl; // private implementation (details hidden)
+};
+
+
+#endif
Index: libs/libmythfreesurround/freesurround.h
===================================================================
--- libs/libmythfreesurround/freesurround.h	(revision 15853)
+++ libs/libmythfreesurround/freesurround.h	(working copy)
@@ -66,6 +66,7 @@
         int32_t phasemode;			// phase shifting mode
         int32_t steering;			// steering mode (0=simple, 1=linear)
         int32_t front_sep, rear_sep;// front/rear stereo separation
+        float gain;                 // total gain
 
         // (default) constructor
         fsurround_params(int32_t center_width=100, int32_t dimension=0);
Index: libs/libmythfreesurround/libmythfreesurround.pro
===================================================================
--- libs/libmythfreesurround/libmythfreesurround.pro	(revision 15853)
+++ libs/libmythfreesurround/libmythfreesurround.pro	(working copy)
@@ -19,7 +19,17 @@
 SOURCES += el_processor.cpp
 SOURCES += freesurround.cpp
 
-#required until its rewritten to use avcodec fft lib
-#LIBS += -lfftw3
-LIBS += -lfftw3f
-
+contains( CONFIG_LIBFFTW3, yes ) {
+    #required until its rewritten to use avcodec fft lib
+    LIBS += -lfftw3f
+    DEFINES += USE_FFTW3
+    # while Im testing
+    DEPENDPATH += ../libavcodec
+    LIBS += -L../libavcodec -lavcodec
+    INCLUDEPATH += ../../libs/libavutil
+    DEFINES -= USE_FFTW3
+} else {
+    DEPENDPATH += ../libavcodec
+    LIBS += -L../libavcodec -lavcodec
+    INCLUDEPATH += ../../libs/libavutil
+}
Index: programs/mythfrontend/globalsettings.cpp
===================================================================
--- programs/mythfrontend/globalsettings.cpp	(revision 15853)
+++ programs/mythfrontend/globalsettings.cpp	(working copy)
@@ -56,7 +56,12 @@
     }
 #endif
 #ifdef USING_ALSA
-    gc->addSelection("ALSA:default", "ALSA:default");
+    gc->addSelection("ALSA:default",       "ALSA:default");
+    gc->addSelection("ALSA:surround51",    "ALSA:surround51");
+    gc->addSelection("ALSA:analog",        "ALSA:analog");
+    gc->addSelection("ALSA:digital",       "ALSA:digital");
+    gc->addSelection("ALSA:mixed-analog",  "ALSA:mixed-analog");
+    gc->addSelection("ALSA:mixed-digital", "ALSA:mixed-digital");
 #endif
 #ifdef USING_ARTS
     gc->addSelection("ARTS:", "ARTS:");
@@ -78,6 +83,33 @@
     return gc;
 }
 
+static HostComboBox *MaxAudioChannels()
+{
+    HostComboBox *gc = new HostComboBox("MaxChannels",false);
+    gc->setLabel(QObject::tr("Max Audio Channels"));
+    gc->addSelection(QObject::tr("Stereo"), "2", true); // default
+    gc->addSelection(QObject::tr("5.1"), "6");
+    gc->setHelpText(
+            QObject::tr(
+                "Set the maximum number of audio channels to be decoded. "
+                "This is for multi-channel/surround audio playback."));
+    return gc;
+}
+
+static HostComboBox *AudioUpmixType()
+{
+    HostComboBox *gc = new HostComboBox("AudioUpmixType",false);
+    gc->setLabel(QObject::tr("Upmix"));
+    gc->addSelection(QObject::tr("Passive"), "0");
+    gc->addSelection(QObject::tr("Active Simple"), "1");
+    gc->addSelection(QObject::tr("Active Linear"), "2", true); // default
+    gc->setHelpText(
+            QObject::tr(
+                "Set the audio upmix type for 2ch to 6ch conversion. "
+                "This is for multi-channel/surround audio playback."));
+    return gc;
+}
+
 static HostComboBox *PassThroughOutputDevice()
 {
     HostComboBox *gc = new HostComboBox("PassThruOutputDevice", true);
@@ -3255,6 +3297,12 @@
          vgrp0->addChild(AC3PassThrough());
          vgrp0->addChild(DTSPassThrough());
 
+         HorizontalConfigurationGroup *agrp =
+             new HorizontalConfigurationGroup(false, false, true, true);
+         agrp->addChild(MaxAudioChannels());
+         agrp->addChild(AudioUpmixType());
+         addChild(agrp);
+
          VerticalConfigurationGroup *vgrp1 =
              new VerticalConfigurationGroup(false, false, true, true);
          vgrp1->addChild(AggressiveBuffer());
Index: programs/mythtranscode/transcode.cpp
===================================================================
--- programs/mythtranscode/transcode.cpp	(revision 15853)
+++ programs/mythtranscode/transcode.cpp	(working copy)
@@ -55,13 +55,18 @@
 
     // reconfigure sound out for new params
     virtual void Reconfigure(int audio_bits, int audio_channels,
-                             int audio_samplerate, bool audio_passthru)
+                             int audio_samplerate, bool audio_passthru,
+                             void *audio_codec = NULL)
     {
+        ClearError();
         (void)audio_samplerate;
         (void)audio_passthru;
+        (void)audio_codec;
         bits = audio_bits;
         channels = audio_channels;
         bytes_per_sample = bits * channels / 8;
+        if ((uint)audio_channels > 2)
+            Error(QString("Invalid channel count %1").arg(channels));
     }
 
     // dsprate is in 100 * samples/second
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h	(revision 15853)
+++ libs/libmythtv/avformatdecoder.h	(working copy)
@@ -261,6 +261,8 @@
     bool              allow_ac3_passthru;
     bool              allow_dts_passthru;
     bool              disable_passthru;
+    uint              max_channels;
+
     VideoFrame       *dummy_frame;
 
     AudioInfo         audioIn;
Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 15853)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -51,9 +51,6 @@
 
 #define MAX_AC3_FRAME_SIZE 6144
 
-/** Set to zero to allow any number of AC3 channels. */
-#define MAX_OUTPUT_CHANNELS 2
-
 static int cc608_parity(uint8_t byte);
 static int cc608_good_parity(const int *parity_table, uint16_t data);
 static void cc608_build_parity_table(int *parity_table);
@@ -400,7 +397,8 @@
       // Audio
       audioSamples(new short int[AVCODEC_MAX_AUDIO_FRAME_SIZE]),
       allow_ac3_passthru(false),    allow_dts_passthru(false),
-      disable_passthru(false),      dummy_frame(NULL),
+      disable_passthru(false),      max_channels(2),
+      dummy_frame(NULL),
       // DVD
       lastdvdtitle(-1), lastcellstart(0),
       dvdmenupktseen(false), indvdstill(false),
@@ -417,6 +415,7 @@
 
     allow_ac3_passthru = gContext->GetNumSetting("AC3PassThru", false);
     allow_dts_passthru = gContext->GetNumSetting("DTSPassThru", false);
+    max_channels = (uint) gContext->GetNumSetting("MaxChannels", 2);
 
     audioIn.sample_size = -32; // force SetupAudioStream to run once
     itv = GetNVP()->GetInteractiveTV();
@@ -1596,7 +1595,13 @@
                             <<") already open, leaving it alone.");
                 }
                 //assert(enc->codec_id);
+                VERBOSE(VB_GENERAL, LOC + QString("codec %1 has %2 channels")
+                        .arg(codec_id_string(enc->codec_id))
+                        .arg(enc->channels));
 
+#if 0
+                // HACK MULTICHANNEL DTS passthru disabled for multichannel,
+                // dont know how to handle this
                 // HACK BEGIN REALLY UGLY HACK FOR DTS PASSTHRU
                 if (enc->codec_id == CODEC_ID_DTS)
                 {
@@ -1605,6 +1610,7 @@
                     // enc->bit_rate = what??;
                 }
                 // HACK END REALLY UGLY HACK FOR DTS PASSTHRU
+#endif
 
                 bitrate += enc->bit_rate;
                 break;
@@ -3303,15 +3309,37 @@
                         reselectAudioTrack = true;
                     }                            
 
+                    bool do_ac3_passthru = (allow_ac3_passthru && !transcoding &&
+                                            (curstream->codec->codec_id == CODEC_ID_AC3));
+                    bool do_dts_passthru = (allow_dts_passthru && !transcoding &&
+                                            (curstream->codec->codec_id == CODEC_ID_DTS));
+                    bool using_passthru = do_ac3_passthru || do_dts_passthru;
+
                     // detect channels on streams that need
                     // to be decoded before we can know this
+                    bool already_decoded = false;
                     if (!curstream->codec->channels)
                     {
                         QMutexLocker locker(&avcodeclock);
-                        curstream->codec->channels = MAX_OUTPUT_CHANNELS;
+                        VERBOSE(VB_IMPORTANT, LOC +
+                                QString("Setting channels to %1")
+                                .arg(audioOut.channels));
+
+                        if (using_passthru)
+                        {
+                            // for passthru let it select the max number of channels
+                            curstream->codec->channels = 0;
+                            curstream->codec->request_channels = 0;
+                        }
+                        else
+                        {
+                            curstream->codec->channels = audioOut.channels;
+                            curstream->codec->request_channels = audioOut.channels;
+                        }
                         ret = avcodec_decode_audio(
                             curstream->codec, audioSamples,
                             &data_size, ptr, len);
+                        already_decoded = true;
 
                         reselectAudioTrack |= curstream->codec->channels;
                     }
@@ -3369,11 +3397,15 @@
                         AVCodecContext *ctx = curstream->codec;
 
                         if ((ctx->channels == 0) ||
-                            (ctx->channels > MAX_OUTPUT_CHANNELS))
-                            ctx->channels = MAX_OUTPUT_CHANNELS;
+                            (ctx->channels > audioOut.channels))
+                            ctx->channels = audioOut.channels;
 
-                        ret = avcodec_decode_audio(
-                            ctx, audioSamples, &data_size, ptr, len);
+                        if (!already_decoded)
+                        {
+                            curstream->codec->request_channels = audioOut.channels;
+                            ret = avcodec_decode_audio(
+                                ctx, audioSamples, &data_size, ptr, len);
+                        }
 
                         // When decoding some audio streams the number of
                         // channels, etc isn't known until we try decoding it.
@@ -3808,6 +3840,11 @@
 
 void AvFormatDecoder::SetDisablePassThrough(bool disable)
 {
+    // can only disable never reenable as once
+    // timestretch is on its on for the session
+    if (disable_passthru)
+        return;
+
     if (selectedTrack[kTrackTypeAudio].av_stream_index < 0)
     {
         disable_passthru = disable;
@@ -3840,6 +3877,7 @@
     AVCodecContext *codec_ctx = NULL;
     AudioInfo old_in  = audioIn;
     AudioInfo old_out = audioOut;
+    bool using_passthru = false;
 
     if ((currentTrack[kTrackTypeAudio] >= 0) &&
         (selectedTrack[kTrackTypeAudio].av_stream_index <=
@@ -3851,37 +3889,62 @@
         assert(curstream->codec);
         codec_ctx = curstream->codec;        
         bool do_ac3_passthru = (allow_ac3_passthru && !transcoding &&
-                                !disable_passthru &&
                                 (codec_ctx->codec_id == CODEC_ID_AC3));
         bool do_dts_passthru = (allow_dts_passthru && !transcoding &&
-                                !disable_passthru &&
                                 (codec_ctx->codec_id == CODEC_ID_DTS));
+        using_passthru = do_ac3_passthru || do_dts_passthru;
         info = AudioInfo(codec_ctx->codec_id,
                          codec_ctx->sample_rate, codec_ctx->channels,
-                         do_ac3_passthru || do_dts_passthru);
+                         using_passthru && !disable_passthru);
     }
 
     if (info == audioIn)
         return false; // no change
 
+    QString ptmsg = (using_passthru) ? " using passthru" : "";
     VERBOSE(VB_AUDIO, LOC + "Initializing audio parms from " +
             QString("audio track #%1").arg(currentTrack[kTrackTypeAudio]+1));
 
     audioOut = audioIn = info;
-    if (audioIn.do_passthru)
+    if (using_passthru)
     {
         // A passthru stream looks like a 48KHz 2ch (@ 16bit) to the sound card
-        audioOut.channels    = 2;
-        audioOut.sample_rate = 48000;
-        audioOut.sample_size = 4;
+        AudioInfo digInfo = audioOut;
+        if (!disable_passthru)
+        {
+            digInfo.channels    = 2;
+            digInfo.sample_rate = 48000;
+            digInfo.sample_size = 4;
+        }
+        if (audioOut.channels > (int) max_channels)
+        {
+            audioOut.channels = (int) max_channels;
+            audioOut.sample_size = audioOut.channels * 2;
+            codec_ctx->channels = audioOut.channels;
+        }
+        VERBOSE(VB_AUDIO, LOC + "Audio format changed digital passthrough " +
+                QString("%1\n\t\t\tfrom %2 ; %3\n\t\t\tto   %4 ; %5")
+                .arg(digInfo.toString())
+                .arg(old_in.toString()).arg(old_out.toString())
+                .arg(audioIn.toString()).arg(audioOut.toString()));
+
+        if (digInfo.sample_rate > 0)
+            GetNVP()->SetEffDsp(digInfo.sample_rate * 100);
+
+        GetNVP()->SetAudioParams(digInfo.bps(), digInfo.channels,
+                                 digInfo.sample_rate, audioIn.do_passthru);
+        // allow the audio stuff to reencode
+        GetNVP()->SetAudioCodec(codec_ctx);
+        GetNVP()->ReinitAudio();
+        return true;
     }
     else
     {
-        if (audioOut.channels > MAX_OUTPUT_CHANNELS)
+        if (audioOut.channels > (int) max_channels)
         {
-            audioOut.channels = MAX_OUTPUT_CHANNELS;
+            audioOut.channels = (int) max_channels;
             audioOut.sample_size = audioOut.channels * 2;
-            codec_ctx->channels = MAX_OUTPUT_CHANNELS;
+            codec_ctx->channels = audioOut.channels;
         }
     }
 
@@ -3896,8 +3959,12 @@
     GetNVP()->SetAudioParams(audioOut.bps(), audioOut.channels,
                              audioOut.sample_rate,
                              audioIn.do_passthru);
-    GetNVP()->ReinitAudio();
 
+    // allow the audio stuff to reencode
+    GetNVP()->SetAudioCodec(using_passthru?codec_ctx:NULL);
+    QString errMsg = GetNVP()->ReinitAudio();
+    bool audiook = errMsg.isEmpty();
+
     return true;
 }
 
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h	(revision 15853)
+++ libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -127,6 +127,7 @@
     void SetAudioInfo(const QString &main, const QString &passthru, uint rate);
     void SetAudioParams(int bits, int channels, int samplerate, bool passthru);
     void SetEffDsp(int dsprate);
+    void SetAudioCodec(void *ac);
 
     // Sets
     void SetParentWidget(QWidget *widget)     { parentWidget = widget; }
@@ -684,6 +685,7 @@
     int      audio_bits;
     int      audio_samplerate;
     float    audio_stretchfactor;
+    void    *audio_codec;
     bool     audio_passthru;
 
     // Picture-in-Picture
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp	(revision 15853)
+++ libs/libmythtv/NuppelVideoPlayer.cpp	(working copy)
@@ -207,6 +207,7 @@
       audio_passthru_device(QString::null),
       audio_channels(2),            audio_bits(-1),
       audio_samplerate(44100),      audio_stretchfactor(1.0f),
+      audio_codec(NULL),
       // Picture-in-Picture
       pipplayer(NULL), setpipplayer(NULL), needsetpipplayer(false),
       // Preview window support
@@ -799,7 +800,8 @@
     if (audioOutput)
     {
         audioOutput->Reconfigure(audio_bits, audio_channels,
-                                 audio_samplerate, audio_passthru);
+                                 audio_samplerate, audio_passthru,
+                                 audio_codec);
         errMsg = audioOutput->GetError();
         if (!errMsg.isEmpty())
             audioOutput->SetStretchFactor(audio_stretchfactor);
@@ -3684,6 +3691,11 @@
     audio_passthru = passthru;
 }
 
+void NuppelVideoPlayer::SetAudioCodec(void *ac)
+{
+    audio_codec = ac;
+}
+
 void NuppelVideoPlayer::SetEffDsp(int dsprate)
 {
     if (audioOutput)
Index: libs/libavcodec/liba52.c
===================================================================
--- libs/libavcodec/liba52.c	(revision 15853)
+++ libs/libavcodec/liba52.c	(working copy)
@@ -134,6 +134,181 @@
     }
 }
 
+static inline int16_t convert(int32_t i)
+{
+    return av_clip_int16(i - 0x43c00000);
+}
+
+void float2s16_2 (float * _f, int16_t * s16)
+{
+    int i;
+    int32_t * f = (int32_t *) _f;
+
+    for (i = 0; i < 256; i++) {
+	s16[2*i] = convert (f[i]);
+	s16[2*i+1] = convert (f[i+256]);
+    }
+}
+
+void float2s16_4 (float * _f, int16_t * s16)
+{
+    int i;
+    int32_t * f = (int32_t *) _f;
+
+    for (i = 0; i < 256; i++) {
+	s16[4*i] = convert (f[i]);
+	s16[4*i+1] = convert (f[i+256]);
+	s16[4*i+2] = convert (f[i+512]);
+	s16[4*i+3] = convert (f[i+768]);
+    }
+}
+
+void float2s16_5 (float * _f, int16_t * s16)
+{
+    int i;
+    int32_t * f = (int32_t *) _f;
+
+    for (i = 0; i < 256; i++) {
+	s16[5*i] = convert (f[i]);
+	s16[5*i+1] = convert (f[i+256]);
+	s16[5*i+2] = convert (f[i+512]);
+	s16[5*i+3] = convert (f[i+768]);
+	s16[5*i+4] = convert (f[i+1024]);
+    }
+}
+
+#define LIKEAC3DEC 1
+int channels_multi (int flags)
+{
+    if (flags & A52_LFE)
+	return 6;
+    else if (flags & 1)	/* center channel */
+	return 5;
+    else if ((flags & A52_CHANNEL_MASK) == A52_2F2R)
+	return 4;
+    else
+	return 2;
+}
+
+void float2s16_multi (float * _f, int16_t * s16, int flags)
+{
+    int i;
+    int32_t * f = (int32_t *) _f;
+
+    switch (flags) {
+    case A52_MONO:
+	for (i = 0; i < 256; i++) {
+	    s16[5*i] = s16[5*i+1] = s16[5*i+2] = s16[5*i+3] = 0;
+	    s16[5*i+4] = convert (f[i]);
+	}
+	break;
+    case A52_CHANNEL:
+    case A52_STEREO:
+    case A52_DOLBY:
+	float2s16_2 (_f, s16);
+	break;
+    case A52_3F:
+	for (i = 0; i < 256; i++) {
+	    s16[5*i] = convert (f[i]);
+	    s16[5*i+1] = convert (f[i+512]);
+	    s16[5*i+2] = s16[5*i+3] = 0;
+	    s16[5*i+4] = convert (f[i+256]);
+	}
+	break;
+    case A52_2F2R:
+	float2s16_4 (_f, s16);
+	break;
+    case A52_3F2R:
+	float2s16_5 (_f, s16);
+	break;
+    case A52_MONO | A52_LFE:
+	for (i = 0; i < 256; i++) {
+#if LIKEAC3DEC
+	    s16[6*i] = s16[6*i+2] = s16[6*i+3] = s16[6*i+4] = 0;
+	    s16[6*i+1] = convert (f[i+256]);
+	    s16[6*i+5] = convert (f[i]);
+#else
+	    s16[6*i] = s16[6*i+1] = s16[6*i+2] = s16[6*i+3] = 0;
+	    s16[6*i+4] = convert (f[i+256]);
+	    s16[6*i+5] = convert (f[i]);
+#endif
+	}
+	break;
+    case A52_CHANNEL | A52_LFE:
+    case A52_STEREO | A52_LFE:
+    case A52_DOLBY | A52_LFE:
+	for (i = 0; i < 256; i++) {
+#if LIKEAC3DEC
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+2] = convert (f[i+512]);
+	    s16[6*i+1] = s16[6*i+3] = s16[6*i+4] = 0;
+	    s16[6*i+5] = convert (f[i]);
+#else
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+1] = convert (f[i+512]);
+	    s16[6*i+2] = s16[6*i+3] = s16[6*i+4] = 0;
+	    s16[6*i+5] = convert (f[i]);
+#endif
+	}
+	break;
+    case A52_3F | A52_LFE:
+	for (i = 0; i < 256; i++) {
+#if LIKEAC3DEC
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+2] = convert (f[i+768]);
+	    s16[6*i+3] = s16[6*i+4] = 0;
+	    s16[6*i+1] = convert (f[i+512]);
+	    s16[6*i+5] = convert (f[i]);
+#else
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+1] = convert (f[i+768]);
+	    s16[6*i+2] = s16[6*i+3] = 0;
+	    s16[6*i+4] = convert (f[i+512]);
+	    s16[6*i+5] = convert (f[i]);
+#endif
+	}
+	break;
+    case A52_2F2R | A52_LFE:
+	for (i = 0; i < 256; i++) {
+#if LIKEAC3DEC
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+1] = 0;
+	    s16[6*i+2] = convert (f[i+512]);
+	    s16[6*i+3] = convert (f[i+768]);
+	    s16[6*i+4] = convert (f[i+1024]);
+	    s16[6*i+5] = convert (f[i]);
+#else
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+1] = convert (f[i+512]);
+	    s16[6*i+2] = convert (f[i+768]);
+	    s16[6*i+3] = convert (f[i+1024]);
+	    s16[6*i+4] = 0;
+	    s16[6*i+5] = convert (f[i]);
+#endif
+	}
+	break;
+    case A52_3F2R | A52_LFE:
+	for (i = 0; i < 256; i++) {
+#if LIKEAC3DEC
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+1] = convert (f[i+512]);
+	    s16[6*i+2] = convert (f[i+768]);
+	    s16[6*i+3] = convert (f[i+1024]);
+	    s16[6*i+4] = convert (f[i+1280]);
+	    s16[6*i+5] = convert (f[i]);
+#else
+	    s16[6*i] = convert (f[i+256]);
+	    s16[6*i+1] = convert (f[i+768]);
+	    s16[6*i+2] = convert (f[i+1024]);
+	    s16[6*i+3] = convert (f[i+1280]);
+	    s16[6*i+4] = convert (f[i+512]);
+	    s16[6*i+5] = convert (f[i]);
+#endif
+	}
+	break;
+    }
+}
+
 /**** end */
 
 #define HEADER_SIZE 7
@@ -179,6 +354,12 @@
                     s->channels = ac3_channels[s->flags & 7];
                     if (s->flags & A52_LFE)
                         s->channels++;
+                    if (avctx->request_channels > 0)
+                    {
+                        avctx->channels = s->channels;
+                        if (s->channels > avctx->channels)
+                            avctx->channels = avctx->request_channels;
+                    }
                     if (avctx->channels == 0)
                         /* No specific number of channel requested */
                         avctx->channels = s->channels;
@@ -199,14 +380,20 @@
             s->inbuf_ptr += len;
             buf_size -= len;
         } else {
+            int chans;
             flags = s->flags;
             if (avctx->channels == 1)
                 flags = A52_MONO;
-            else if (avctx->channels == 2)
-                flags = A52_STEREO;
+            else if (avctx->channels == 2) {
+                if (s->channels>2)
+                    flags = A52_DOLBY;
+                else
+                    flags = A52_STEREO;
+            }
             else
                 flags |= A52_ADJUST_LEVEL;
             level = 1;
+            chans = channels_multi(flags);
             if (s->a52_frame(s->state, s->inbuf, &flags, &level, 384)) {
             fail:
                 av_log(avctx, AV_LOG_ERROR, "Error decoding frame\n");
@@ -217,7 +404,7 @@
             for (i = 0; i < 6; i++) {
                 if (s->a52_block(s->state))
                     goto fail;
-                float_to_int(s->samples, out_samples + i * 256 * avctx->channels, avctx->channels);
+                float2s16_multi(s->samples, out_samples + i * 256 * chans, flags);
             }
             s->inbuf_ptr = s->inbuf;
             s->frame_size = 0;
Index: libs/libavcodec/ac3dec.c
===================================================================
--- libs/libavcodec/ac3dec.c	(revision 15853)
+++ libs/libavcodec/ac3dec.c	(working copy)
@@ -1132,6 +1132,12 @@
 
     /* channel config */
     ctx->out_channels = ctx->nchans;
+    if (avctx->request_channels > 0)
+    {
+        avctx->channels = ctx->out_channels;
+        if (avctx->channels > avctx->request_channels)
+            avctx->channels = avctx->request_channels;
+    }
     if (avctx->channels == 0) {
         avctx->channels = ctx->out_channels;
     } else if(ctx->out_channels < avctx->channels) {
Index: libs/libavcodec/dca.c
===================================================================
--- libs/libavcodec/dca.c	(revision 15853)
+++ libs/libavcodec/dca.c	(working copy)
@@ -1159,7 +1159,13 @@
     avctx->bit_rate = s->bit_rate;
 
     channels = s->prim_channels + !!s->lfe;
-    avctx->channels = avctx->request_channels;
+    //avctx->channels = avctx->request_channels;
+    if (avctx->request_channels > 0)
+    {
+        avctx->channels = channels;
+        if (avctx->channels > avctx->request_channels)
+            avctx->channels = avctx->request_channels;
+    }
     if(avctx->channels == 0) {
         avctx->channels = channels;
     } else if(channels < avctx->channels) {
