The officially official Devuan Forum!

You are not logged in.

#1 Today 19:56:08

igorzwx
Member
Registered: 2024-05-06
Posts: 531  

MPlayer with double precision

MPlayer with double precision

Prerequisites

sudo apt-get install build-essential dpkg-dev devscripts quilt fakeroot debhelper pkg-config yasm

Build-deps

sudo apt-get install x11proto-core-dev ladspa-sdk liba52-dev libaa1-dev libasound2-dev libass-dev libaudio-dev libavcodec-dev libavformat-dev libavutil-dev libbluray-dev libbs2b-dev libcaca-dev libcdio-cdda-dev libcdio-paranoia-dev libdca-dev libdv-dev libdvdnav-dev libdvdread-dev libenca-dev libfaad-dev libfontconfig-dev libfreetype-dev libfribidi-dev libgif-dev libgl1-mesa-dev libgsm1-dev libgtk2.0-dev libjack-dev libjpeg-dev liblirc-dev liblzo2-dev libmad0-dev libmng-dev libmp3lame-dev libmpeg2-4-dev libmpg123-dev libopenal-dev libpostproc-dev librtmp-dev libsmbclient-dev libspeex-dev libswscale-dev libtheora-dev libtwolame-dev libvdpau-dev libvorbisidec-dev libx11-dev libx264-dev libxext-dev libxinerama-dev libxss-dev libxt-dev libxv-dev libxvidcore-dev libxvmc-dev libxxf86dga-dev libxxf86vm-dev zlib1g-dev docbook-xml docbook-xsl xsltproc
$ tree BUILD7
BUILD7
└── PATCHES_mplayer
    ├── 0001-Disable-PulseAudio.patch
    ├── 0002-debian-disable-SDL-fix-default-ao-fallback.patch
    ├── 0003-ALSA-fixes-64-bit-float-audio-support.patch
    ├── 0004-Remove-libaf-resample-format-filters-redundant-with-.patch
    ├── 0005-audio-preserve-native-decoder-output-through-the-pla.patch
    └── 0006-ao_oss-exclusive-mode-OSS4-output-with-runtime-loade.patch
cd BUILD7
apt-get source mplayer
cd mplayer-*/
export QUILT_PATCHES=debian/patches
quilt push -a
quilt import ../PATCHES_mplayer/000*.patch
quilt push -a
dch -v 2:1.99+svn38408-1 "Local rebuild: float64 audio + ao_alsa fixes + OSS4 backend"
dpkg-buildpackage -b -us -uc
mkdir -p ../dist && mv -v ../*.deb ../*.changes ../*.buildinfo ../dist 
sudo dpkg -i ../dist/mplayer_1.99+svn38408-1_amd64.deb

WARNING: 0001-*.patch and 0002-*.patch modify debian/control and debian/rules. Fine for local .deb builds; if you plan to build a source package or upload, apply the debian/ changes directly instead.
Example: How to Create a Debian Package Release Bundle
_https://dev1galaxy.org/viewtopic.php?pid=60629#p60629

$ cat ~/.mplayer/config
# Write your default config options here!

# ao=oss
oss_target_rate=192000
oss_target_bitdepth=32

ao=alsa    
# default: downmix to stereo (if needed) in ALSA backend
alsa_ch=2
# alsa_ch=6

PATCHES for MPlayer

$ cat PATCHES_mplayer/0001*
From feb5cdd9795caa996799fc914084d1445f6472ed Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Sun, 18 Oct 2026 13:50:50 +0200
Subject: [PATCH 1/6] Disable PulseAudio

---
 debian/control |  1 -
 debian/rules   | 15 ++++-----------
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/debian/control b/debian/control
index d877f5d..dcecb01 100644
--- a/debian/control
+++ b/debian/control
@@ -51,7 +51,6 @@ Build-Depends:
  libmpg123-dev,
  libopenal-dev,
  libpostproc-dev,
- libpulse-dev,
  librtmp-dev,
  libsdl1.2-dev,
  libsmbclient-dev [!hurd-any],
diff --git a/debian/rules b/debian/rules
index d939185..91fbc11 100755
--- a/debian/rules
+++ b/debian/rules
@@ -24,7 +24,8 @@ CONFIGURE_FLAGS = \
 	--disable-libmpeg2-internal \
 	--disable-ffmpeg_a \
 	--disable-esd \
-	--extra-cflags="${CFLAGS}" \
+	--disable-pulse \
+	--extra-cflags="${CFLAGS} -I/usr/lib/oss/include" \
 	--extra-ldflags="${LDFLAGS}" \
 	$(archconf)
 
@@ -60,18 +61,10 @@ ifeq ($(DEB_HOST_ARCH),alpha)
   archconf += --target=alpha_ev5-linux
 endif
 
+sound_backend := alsa,oss
+
 ifeq ($(DEB_HOST_ARCH_OS),linux)
     CONFIGURE_FLAGS += --enable-joystick --enable-radio --enable-radio-capture
-    sound_backend := alsa
-else
-    sound_backend := oss
-endif
-
-# It seems x32 has some alsa issues. Add oss as fallback.
-# see http://lists.alioth.debian.org/pipermail/pkg-multimedia-maintainers/2015-July/046049.html
-# The default for all could be "ao=pulse,alsa,oss,sdl:aalib" to avoid this.
-ifeq ($(DEB_HOST_ARCH),x32)
-    sound_backend := alsa,oss
 endif
 
 %:
-- 
2.39.5
$ cat PATCHES_mplayer/0002*
From 802654d408271012fafe524e142f64773a091a16 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Sun, 18 Oct 2026 21:42:14 +0200
Subject: [PATCH 2/6] debian: disable SDL, fix default ao fallback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- control: drop libsdl1.2-dev (its dependency libsdl1.2debian
  links libpulse, dragging libpulse0 back in transitively)
- rules: add --disable-sdl to CONFIGURE_FLAGS
- rules: unconditional sound_backend := alsa,oss, — trailing comma
  preserves fallback to jack/openal and other compiled drivers
- rules: fix sed to match the literal 'ao=' line in mplayer.conf
  (@SOUND_BACKEND@ placeholder never existed in this tree)
---
 debian/control | 1 -
 debian/rules   | 6 +++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/debian/control b/debian/control
index dcecb01..c9af403 100644
--- a/debian/control
+++ b/debian/control
@@ -52,7 +52,6 @@ Build-Depends:
  libopenal-dev,
  libpostproc-dev,
  librtmp-dev,
- libsdl1.2-dev,
  libsmbclient-dev [!hurd-any],
  libspeex-dev,
  libswscale-dev,
diff --git a/debian/rules b/debian/rules
index 91fbc11..fef9bed 100755
--- a/debian/rules
+++ b/debian/rules
@@ -24,7 +24,7 @@ CONFIGURE_FLAGS = \
 	--disable-libmpeg2-internal \
 	--disable-ffmpeg_a \
 	--disable-esd \
-	--disable-pulse \
+	--disable-pulse --disable-sdl \
 	--extra-cflags="${CFLAGS} -I/usr/lib/oss/include" \
 	--extra-ldflags="${LDFLAGS}" \
 	$(archconf)
@@ -61,7 +61,7 @@ ifeq ($(DEB_HOST_ARCH),alpha)
   archconf += --target=alpha_ev5-linux
 endif
 
-sound_backend := alsa,oss
+sound_backend := alsa,oss,
 
 ifeq ($(DEB_HOST_ARCH_OS),linux)
     CONFIGURE_FLAGS += --enable-joystick --enable-radio --enable-radio-capture
@@ -110,5 +110,5 @@ override_dh_compress-arch:
 
 override_dh_install-arch:
 	dh_install
-	sed -e "s/@SOUND_BACKEND@/$(sound_backend)/" -i \
+	sed -e "s/^ao=.*/ao=$(sound_backend)/" -i \
 	                  $(CURDIR)/debian/mplayer/etc/mplayer/mplayer.conf
-- 
2.39.5
$ cat PATCHES_mplayer/0003*
From 19369bf44a3879f0fe89612c6c9b9c83373a211b Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Sun, 18 Oct 2026 22:18:31 +0200
Subject: [PATCH 3/6] ALSA fixes + 64-bit float audio support

ALSA backend (ao_alsa.c):
- fix playback stalls: drop outburst truncation in play(), rewrite
  write loop to handle partial snd_pcm_writei() returns
- route rate conversion to the configured ALSA plugin (fftrate via
  defaults.pcm.rate_converter) instead of the in-tree resample filter
- reduce default buffer to 160 ms / 4 periods
- map AF_FORMAT_DOUBLE_{LE,BE} to SND_PCM_FORMAT_FLOAT64_*

64-bit float audio path:
- af_format.h: add AF_FORMAT_DOUBLE_{LE,BE,NE}; format.c: af_fmt2bits 8-bps;
  mpbswap.h: bswap_64
- af_format: accept bps=8 in check_bps(), endian swap for double
- ad_pcm: dispatch AF_FORMAT_DOUBLE_* by samplesize (float64 WAV was
  mislabeled as float32 and played as noise)
- ad_ffmpeg: accept AV_SAMPLE_FMT_DBL/DBLP in init() sample_fmt switch
- fmt-conversion: DBL/DBLP sample format mapping
- codecs.conf: enable fl64/46lf fourccs for MOV
- af_* filters (resample, volume, equalizer, pan, bs2b, center, comp,
  extrastereo, gate, karaoke, volnorm): handle AF_FORMAT_DOUBLE_NE
- demux_lavf: related fix

Tested: float64 WAV (192 kHz) and DSF/DXD play bit-perfect through
ALSA; 'AO: [alsa] ... doublele (8 bytes per sample)'.
---
 etc/codecs.conf              |   4 +-
 fmt-conversion.c             |   1 +
 libaf/af_bs2b.c              |   5 +
 libaf/af_center.c            |  34 +-
 libaf/af_comp.c              |  63 ++--
 libaf/af_equalizer.c         | 129 ++++---
 libaf/af_extrastereo.c       |  35 +-
 libaf/af_format.c            |  18 +-
 libaf/af_format.h            |   7 +
 libaf/af_gate.c              |  44 ++-
 libaf/af_karaoke.c           |  66 ++--
 libaf/af_pan.c               |  56 ++-
 libaf/af_resample.c          |  34 +-
 libaf/af_resample_template.c |   3 +
 libaf/af_volnorm.c           |  83 ++++-
 libaf/af_volume.c            |  34 +-
 libaf/format.c               |   8 +-
 libao2/ao_alsa.c             | 635 +++++++++++++++++++----------------
 libmpcodecs/ad_ffmpeg.c      |   1 +
 libmpcodecs/ad_pcm.c         |  22 +-
 libmpdemux/demux_lavf.c      |  12 +
 mpbswap.h                    |   1 +
 22 files changed, 849 insertions(+), 446 deletions(-)

diff --git a/etc/codecs.conf b/etc/codecs.conf
index b411047..536bc50 100644
--- a/etc/codecs.conf
+++ b/etc/codecs.conf
@@ -5535,8 +5535,8 @@ audiocodec pcm
   fourcc sowt ; (MOV files)
   fourcc fl32 ; (MOV files)
   fourcc 23lf ; (MOV files), internal MPlayer FourCC
-;  fourcc fl64 ; (MOV files)
-;  fourcc 46lf ; (MOV files), internal MPlayer FourCC
+  fourcc fl64 ; (MOV files)
+  fourcc 46lf ; (MOV files), internal MPlayer FourCC
   fourcc NONE ; (MOV files from Kodak CX6320)
   fourcc in24 ; (MOV files)
   fourcc 42ni ; (MOV files), internal MPlayer FourCC
diff --git a/fmt-conversion.c b/fmt-conversion.c
index a12c03e..39f786d 100644
--- a/fmt-conversion.c
+++ b/fmt-conversion.c
@@ -177,6 +177,7 @@ static const struct {
     {AF_FORMAT_S16_NE, AV_SAMPLE_FMT_S16},
     {AF_FORMAT_S32_NE, AV_SAMPLE_FMT_S32},
     {AF_FORMAT_FLOAT_NE, AV_SAMPLE_FMT_FLT},
+    {AF_FORMAT_DOUBLE_NE, AV_SAMPLE_FMT_DBL},   /* NEW: 64-bit float */
     {AF_FORMAT_UNKNOWN, AV_SAMPLE_FMT_NONE}
 };
 
diff --git a/libaf/af_bs2b.c b/libaf/af_bs2b.c
index 9a4734d..f7c57f9 100644
--- a/libaf/af_bs2b.c
+++ b/libaf/af_bs2b.c
@@ -161,6 +161,11 @@ static int control(struct af_instance_s *af, int cmd, void *arg)
             case AF_FORMAT_U8:
                 af->play = play_u8;
                 break;
+            case AF_FORMAT_DOUBLE_NE:   /* NEW: libbs2b has no native 64-bit path */  
+                af->play = play_f;  
+                af->data->format = AF_FORMAT_FLOAT_NE;  
+                af->data->bps = 4;  
+                break;
             default:
                 af->play = play_f;
                 af->data->format = AF_FORMAT_FLOAT_NE;
diff --git a/libaf/af_center.c b/libaf/af_center.c
index a709e42..faee57b 100644
--- a/libaf/af_center.c
+++ b/libaf/af_center.c
@@ -51,8 +51,14 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
 
     af->data->rate   = ((af_data_t*)arg)->rate;
     af->data->nch    = FFMAX(s->ch+1,((af_data_t*)arg)->nch);
-    af->data->format = AF_FORMAT_FLOAT_NE;
-    af->data->bps    = 4;
+    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+      af->data->format = AF_FORMAT_DOUBLE_NE;   /* NEW: 64-bit float */
+      af->data->bps    = 8;
+    }
+    else{
+      af->data->format = AF_FORMAT_FLOAT_NE;
+      af->data->bps    = 4;
+    }
 
     return af_test_output(af,(af_data_t*)arg);
   }
@@ -87,18 +93,22 @@ static void uninit(struct af_instance_s* af)
 // Filter data through filter
 static af_data_t* play(struct af_instance_s* af, af_data_t* data)
 {
-  af_data_t*    c   = data;	 // Current working data
-  af_center_t*  s   = af->setup; // Setup for this instance
-  float*   	a   = c->audio;	 // Audio data
-  int		len = c->len/4;	 // Number of samples in current audio block
-  int		nch = c->nch;	 // Number of channels
-  int		ch  = s->ch;	 // Channel in which to insert the center audio
+  af_data_t*    c   = data;
+  af_center_t*  s   = af->setup;
+  int		nch = c->nch;
+  int		ch  = s->ch;
   register int  i;
 
-  // Run filter
-  for(i=0;i<len;i+=nch){
-    // Average left and right
-    a[i+ch] = (a[i]/2) + (a[i+1]/2);
+  if (af->data->format == AF_FORMAT_DOUBLE_NE) {
+    double* a = c->audio;
+    int len = c->len/8;
+    for(i=0;i<len;i+=nch)
+      a[i+ch] = (a[i]/2) + (a[i+1]/2);
+  } else {
+    float* a = c->audio;
+    int len = c->len/4;
+    for(i=0;i<len;i+=nch)
+      a[i+ch] = (a[i]/2) + (a[i+1]/2);
   }
 
   return c;
diff --git a/libaf/af_comp.c b/libaf/af_comp.c
index 21725b9..eeaef89 100644
--- a/libaf/af_comp.c
+++ b/libaf/af_comp.c
@@ -54,11 +54,14 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
 
     af->data->rate   = ((af_data_t*)arg)->rate;
     af->data->nch    = ((af_data_t*)arg)->nch;
-    af->data->format = AF_FORMAT_FLOAT_NE;
-    af->data->bps    = 4;
+    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+      af->data->format = AF_FORMAT_DOUBLE_NE;   /* NEW: 64-bit float */
+      af->data->bps    = 8;
+    } else {
+      af->data->format = AF_FORMAT_FLOAT_NE;
+      af->data->bps    = 4;
+    }
 
-    // Time constant set to 0.1s
-    //    s->alpha = (1.0/0.2)/(2.0*M_PI*(float)((af_data_t*)arg)->rate);
     return af_test_output(af,(af_data_t*)arg);
   case AF_CONTROL_COMMAND_LINE:{
 /*     float v=-10.0; */
@@ -115,30 +118,38 @@ static void uninit(struct af_instance_s* af)
 // Filter data through filter
 static af_data_t* play(struct af_instance_s* af, af_data_t* data)
 {
-  af_data_t*    c   = data;			// Current working data
-  af_comp_t*  	s   = (af_comp_t*)af->setup; 	// Setup for this instance
-  float*   	a   = (float*)c->audio;		// Audio data
-  int       	len = c->len/4;			// Number of samples
-  int           ch  = 0;			// Channel counter
-  register int	nch = c->nch;			// Number of channels
+  af_data_t*    c   = data;
+  af_comp_t*  	s   = (af_comp_t*)af->setup;
+  int           ch  = 0;
+  register int	nch = c->nch;
   register int  i   = 0;
 
-  // Compress/expand
-  for(ch = 0; ch < nch ; ch++){
-    if(s->enable[ch]){
-      float	t   = 1.0 - s->time[ch];
-      for(i=ch;i<len;i+=nch){
-	register float x 	= a[i];
-	register float pow 	= x*x;
-	s->pow[ch] = t*s->pow[ch] +
-	  pow*s->time[ch]; // LP filter
-	if(pow < s->pow[ch]){
-	  ;
-	}
-	else{
-	  ;
-	}
-	a[i] = x;
+  if(af->data->format == AF_FORMAT_DOUBLE_NE){
+    double* a   = (double*)c->audio;
+    int     len = c->len/8;
+    for(ch = 0; ch < nch ; ch++){
+      if(s->enable[ch]){
+        float t = 1.0 - s->time[ch];
+        for(i=ch;i<len;i+=nch){
+          register double x   = a[i];
+          register double pow = x*x;
+          s->pow[ch] = t*s->pow[ch] + pow*s->time[ch];
+          a[i] = x;
+        }
+      }
+    }
+  } else {
+    float*  a   = (float*)c->audio;
+    int     len = c->len/4;
+    for(ch = 0; ch < nch ; ch++){
+      if(s->enable[ch]){
+        float t = 1.0 - s->time[ch];
+        for(i=ch;i<len;i+=nch){
+          register float x   = a[i];
+          register float pow = x*x;
+          s->pow[ch] = t*s->pow[ch] + pow*s->time[ch];
+          a[i] = x;
+        }
       }
     }
   }
diff --git a/libaf/af_equalizer.c b/libaf/af_equalizer.c
index 8a8f272..5deda67 100644
--- a/libaf/af_equalizer.c
+++ b/libaf/af_equalizer.c
@@ -59,26 +59,42 @@
 #define G_MAX	+12.0
 #define G_MIN	-12.0
 
-// Data for specific instances of this filter
 typedef struct af_equalizer_s
 {
-  float   a[KM][L];        	// A weights
-  float   b[KM][L];	     	// B weights
-  float   wq[AF_NCH][KM][L];  	// Circular buffer for W data
-  float   g[AF_NCH][KM];      	// Gain factor for each channel and band
-  int     K; 		   	// Number of used eq bands
-  int     channels;        	// Number of channels
-  float   gain_factor;     // applied at output to avoid clipping
+  float   a[KM][L];            // A weights (float path)
+  float   b[KM][L];            // B weights (float path)
+  float   wq[AF_NCH][KM][L];   // Circular buffer (float path)
+  float   g[AF_NCH][KM];       // Gain factor (float path)
+
+  double  da[KM][L];           // A weights (double path)
+  double  db[KM][L];           // B weights (double path)
+  double  dwq[AF_NCH][KM][L];  // Circular buffer (double path)
+  double  dg[AF_NCH][KM];      // Gain factor (double path)
+
+  int     K;
+  int     channels;
+  float   gain_factor;
+  double  dgain_factor;
 } af_equalizer_t;
 
+static af_data_t* play_float(struct af_instance_s* af, af_data_t* data);
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data);
+
 // 2nd order Band-pass Filter design
 static void bp2(float* a, float* b, float fc, float q){
   double th= 2.0 * M_PI * fc;
   double C = (1.0 - tan(th*q/2.0))/(1.0 + tan(th*q/2.0));
-
   a[0] = (1.0 + C) * cos(th);
   a[1] = -1 * C;
+  b[0] = (1.0 - C)/2.0;
+  b[1] = -1.0050;
+}
 
+static void bp2_double(double* a, double* b, double fc, double q){
+  double th= 2.0 * M_PI * fc;
+  double C = (1.0 - tan(th*q/2.0))/(1.0 + tan(th*q/2.0));
+  a[0] = (1.0 + C) * cos(th);
+  a[1] = -1 * C;
   b[0] = (1.0 - C)/2.0;
   b[1] = -1.0050;
 }
@@ -100,41 +116,41 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
 
     af->data->rate   = ((af_data_t*)arg)->rate;
     af->data->nch    = ((af_data_t*)arg)->nch;
-    af->data->format = AF_FORMAT_FLOAT_NE;
-    af->data->bps    = 4;
-
-    // Calculate number of active filters
-    s->K=KM;
-    while(F[s->K-1] > (float)af->data->rate/2.2)
-      s->K--;
-
-    if(s->K != KM)
-      mp_msg(MSGT_AFILTER, MSGL_INFO, "[equalizer] Limiting the number of filters to"
-	     " %i due to low sample rate.\n",s->K);
 
-    // Generate filter taps
-    for(k=0;k<s->K;k++)
-      bp2(s->a[k],s->b[k],F[k]/((float)af->data->rate),Q);
+    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+      af->data->format = AF_FORMAT_DOUBLE_NE;
+      af->data->bps    = 8;
+      af->play = play_double;
 
-    // Calculate how much this plugin adds to the overall time delay
-    af->delay = 2 * af->data->nch * af->data->bps;
+      s->K=KM;
+      while(F[s->K-1] > (double)af->data->rate/2.2) s->K--;
+      for(k=0;k<s->K;k++)
+        bp2_double(s->da[k],s->db[k],F[k]/((double)af->data->rate),Q);
 
-    // Calculate gain factor to prevent clipping at output
-    for(k=0;k<AF_NCH;k++)
-    {
+      s->dgain_factor=0.0;
+      for(k=0;k<AF_NCH;k++)
         for(i=0;i<KM;i++)
-        {
-            if(s->gain_factor < s->g[k][i]) s->gain_factor=s->g[k][i];
-        }
-    }
+          if(s->dgain_factor < s->dg[k][i]) s->dgain_factor=s->dg[k][i];
+      s->dgain_factor=log10(s->dgain_factor + 1.0) * 20.0;
+      s->dgain_factor = (s->dgain_factor > 0.0) ? 0.1+(s->dgain_factor/12.0) : 1;  
+      af->delay = 2 * af->data->nch * af->data->bps;
+    }else{
+      af->data->format = AF_FORMAT_FLOAT_NE;
+      af->data->bps    = 4;
+      af->play = play_float;
 
-    s->gain_factor=log10(s->gain_factor + 1.0) * 20.0;
+      s->K=KM;
+      while(F[s->K-1] > (float)af->data->rate/2.2) s->K--;
+      for(k=0;k<s->K;k++)
+        bp2(s->a[k],s->b[k],F[k]/((float)af->data->rate),Q);
 
-    if(s->gain_factor > 0.0)
-    {
-        s->gain_factor=0.1+(s->gain_factor/12.0);
-    }else{
-        s->gain_factor=1;
+      s->gain_factor=0.0;
+      for(k=0;k<AF_NCH;k++)
+        for(i=0;i<KM;i++)
+          if(s->gain_factor < s->g[k][i]) s->gain_factor=s->g[k][i];
+      s->gain_factor=log10(s->gain_factor + 1.0) * 20.0;
+      s->gain_factor = (s->gain_factor > 0.0) ? 0.1+(s->gain_factor/12.0) : 1;
+      af->delay = 2 * af->data->nch * af->data->bps;
     }
 
     return af_test_output(af,arg);
@@ -188,7 +204,7 @@ static void uninit(struct af_instance_s* af)
 }
 
 // Filter data through filter
-static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+static af_data_t* play_float(struct af_instance_s* af, af_data_t* data)
 {
   af_data_t*       c 	= data;			    	// Current working data
   af_equalizer_t*  s 	= (af_equalizer_t*)af->setup; 	// Setup
@@ -226,12 +242,43 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
   return c;
 }
 
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data)
+{
+  af_equalizer_t* s = (af_equalizer_t*)af->setup;
+  af_data_t*      c = data;
+  int             nch = c->nch;
+  int             ci;
+
+  for(ci = 0; ci < nch; ci++){
+    double* in  = ((double*)c->audio)+ci;
+    double* out = ((double*)c->audio)+ci;
+    double* end = in + c->len/8;
+    double* g   = s->dg[ci];
+
+    while(in < end){
+      register int    k  = 0;
+      register double yt = *in;
+      in += nch;
+
+      for(;k<s->K;k++){
+        register double* wq = s->dwq[ci][k];
+        register double  w = yt*s->db[k][0] + wq[0]*s->da[k][0] + wq[1]*s->da[k][1];
+        yt += (w + wq[1]*s->db[k][1])*g[k];
+        wq[1] = wq[0];
+        wq[0] = w;
+      }
+      *out = yt*s->dgain_factor;
+      out += nch;
+    }
+  }
+  return c;
+}
+
 // Allocate memory and set function pointers
 static int af_open(af_instance_t* af){
   af->control=control;
   af->uninit=uninit;
-  af->play=play;
-  af->mul=1;
+  af->mul=1;                     /* af->play removed — set inside AF_CONTROL_REINIT */
   af->data=calloc(1,sizeof(af_data_t));
   af->setup=calloc(1,sizeof(af_equalizer_t));
   if(af->data == NULL || af->setup == NULL)
diff --git a/libaf/af_extrastereo.c b/libaf/af_extrastereo.c
index 1277753..88a09a9 100644
--- a/libaf/af_extrastereo.c
+++ b/libaf/af_extrastereo.c
@@ -37,6 +37,7 @@ typedef struct af_extrastereo_s
 
 static af_data_t* play_s16(struct af_instance_s* af, af_data_t* data);
 static af_data_t* play_float(struct af_instance_s* af, af_data_t* data);
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data);
 
 // Initialization and runtime control
 static int control(struct af_instance_s* af, int cmd, void* arg)
@@ -50,18 +51,24 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
 
     af->data->rate   = ((af_data_t*)arg)->rate;
     af->data->nch    = 2;
-    if (((af_data_t*)arg)->format == AF_FORMAT_FLOAT_NE)
+    if (((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE)
+    {
+	af->data->format = AF_FORMAT_DOUBLE_NE;
+	af->data->bps = 8;
+	af->play = play_double;
+    }
+    else if (((af_data_t*)arg)->format == AF_FORMAT_FLOAT_NE)
     {
 	af->data->format = AF_FORMAT_FLOAT_NE;
 	af->data->bps = 4;
 	af->play = play_float;
-    }// else
+    }
+    else
     {
 	af->data->format = AF_FORMAT_S16_NE;
 	af->data->bps = 2;
 	af->play = play_s16;
     }
-
     return af_test_output(af,(af_data_t*)arg);
   }
   case AF_CONTROL_COMMAND_LINE:{
@@ -132,6 +139,28 @@ static af_data_t* play_float(struct af_instance_s* af, af_data_t* data)
   return data;
 }
 
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data)  
+{  
+  af_extrastereo_t *s = af->setup;  
+  register int i = 0;  
+  double *a = (double*)data->audio;	// Audio data  
+  int len = data->len/8;		// Number of samples  
+  double avg, l, r;  
+  
+  for (i = 0; i < len; i+=2)  
+  {  
+    avg = (a[i] + a[i + 1]) / 2;  
+  
+    l = avg + (s->mul * (a[i] - avg));  
+    r = avg + (s->mul * (a[i + 1] - avg));  
+  
+    a[i] = af_softclip(l);  
+    a[i + 1] = af_softclip(r);  
+  }  
+  
+  return data;  
+}
+
 // Allocate memory and set function pointers
 static int af_open(af_instance_t* af){
   af->control=control;
diff --git a/libaf/af_format.c b/libaf/af_format.c
index 46b6e1d..e75d243 100644
--- a/libaf/af_format.c
+++ b/libaf/af_format.c
@@ -65,9 +65,9 @@ static af_data_t* play_s16_float(struct af_instance_s* af, af_data_t* data);
 // Sanity check for bytes per sample
 static int check_bps(int bps)
 {
-  if(bps != 4 && bps != 3 && bps != 2 && bps != 1){
+  if(bps != 8 && bps != 4 && bps != 3 && bps != 2 && bps != 1){
     mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] The number of bytes per sample"
-	   " must be 1, 2, 3 or 4. Current value is %i \n",bps);
+	   " must be 1, 2, 3, 4 or 8. Current value is %i \n",bps);
     return AF_ERROR;
   }
   return AF_OK;
@@ -397,6 +397,12 @@ static void endian(const void* in, void* out, int len, int bps)
       }
       break;
     }
+    case(8):{
+      for(i=0;i<len;i++){
+	((uint64_t*)out)[i]=bswap_64(((uint64_t*)in)[i]);
+      }
+      break;
+    }
   }
 }
 
@@ -542,6 +548,10 @@ static void float2int(const float* in, void* out, int len, int bps)
 
     }
     break;
+  case(8):
+    for(i=0;i<len;i++)
+      ((double*)out)[i] = (double)in[i];
+    break;
   }
 }
 
@@ -565,5 +575,9 @@ static void int2float(const void* in, float* out, int len, int bps)
     for(i=0;i<len;i++)
       out[i]=(1.0f/2147483648.0f)*((int32_t*)in)[i];
     break;
+  case(8):
+    for(i=0;i<len;i++)
+      out[i]=(float)((double*)in)[i];
+    break;
   }
 }
diff --git a/libaf/af_format.h b/libaf/af_format.h
index 50be146..ddbbbb2 100644
--- a/libaf/af_format.h
+++ b/libaf/af_format.h
@@ -53,6 +53,7 @@
 #define AF_FORMAT_32BIT		(3<<3)
 #define AF_FORMAT_40BIT		(4<<3)
 #define AF_FORMAT_48BIT		(5<<3)
+#define AF_FORMAT_64BIT		(7<<3)   /* NEW: for 64-bit double float */
 #define AF_FORMAT_BITS_MASK	(7<<3)
 
 // Special flags referring to non pcm data
@@ -84,6 +85,10 @@
 #define AF_FORMAT_FLOAT_LE	(AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE)
 #define AF_FORMAT_FLOAT_BE	(AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE)
 
+/* NEW: 64-bit IEEE double float */
+#define AF_FORMAT_DOUBLE_LE	(AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE)
+#define AF_FORMAT_DOUBLE_BE	(AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_BE)
+
 #define AF_FORMAT_AC3_LE	(AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE)
 #define AF_FORMAT_AC3_BE	(AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE)
 
@@ -98,6 +103,7 @@
 #define AF_FORMAT_U32_NE AF_FORMAT_U32_BE
 #define AF_FORMAT_S32_NE AF_FORMAT_S32_BE
 #define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_BE
+#define AF_FORMAT_DOUBLE_NE AF_FORMAT_DOUBLE_BE
 #define AF_FORMAT_AC3_NE AF_FORMAT_AC3_BE
 #define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_BE
 #else
@@ -108,6 +114,7 @@
 #define AF_FORMAT_U32_NE AF_FORMAT_U32_LE
 #define AF_FORMAT_S32_NE AF_FORMAT_S32_LE
 #define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_LE
+#define AF_FORMAT_DOUBLE_NE AF_FORMAT_DOUBLE_LE
 #define AF_FORMAT_AC3_NE AF_FORMAT_AC3_LE
 #define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_LE
 #endif
diff --git a/libaf/af_gate.c b/libaf/af_gate.c
index 12024cc..7e6e241 100644
--- a/libaf/af_gate.c
+++ b/libaf/af_gate.c
@@ -40,6 +40,9 @@ typedef struct af_gate_s
   float	range[AF_NCH];		// Range level [dB]
 }af_gate_t;
 
+static af_data_t* play_float(struct af_instance_s* af, af_data_t* data);
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data);
+
 // Initialization and runtime control
 static int control(struct af_instance_s* af, int cmd, void* arg)
 {
@@ -51,11 +54,15 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
 
     af->data->rate   = ((af_data_t*)arg)->rate;
     af->data->nch    = ((af_data_t*)arg)->nch;
-    af->data->format = AF_FORMAT_FLOAT_NE;
-    af->data->bps    = 4;
-
-    // Time constant set to 0.1s
-    //    s->alpha = (1.0/0.2)/(2.0*M_PI*(float)((af_data_t*)arg)->rate);
+    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+      af->data->format = AF_FORMAT_DOUBLE_NE;   /* NEW: 64-bit float */
+      af->data->bps    = 8;
+      af->play = play_double;
+    }else{
+      af->data->format = AF_FORMAT_FLOAT_NE;
+      af->data->bps    = 4;
+      af->play = play_float;
+    }
     return af_test_output(af,(af_data_t*)arg);
   case AF_CONTROL_COMMAND_LINE:{
 /*     float v=-10.0; */
@@ -107,7 +114,7 @@ static void uninit(struct af_instance_s* af)
 }
 
 // Filter data through filter
-static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+static af_data_t* play_float(struct af_instance_s* af, af_data_t* data)
 {
   af_data_t*    c   = data;			// Current working data
   af_gate_t*  	s   = (af_gate_t*)af->setup; 	// Setup for this instance
@@ -140,11 +147,34 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
   return c;
 }
 
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data)
+{
+  af_data_t*    c   = data;
+  af_gate_t*    s   = (af_gate_t*)af->setup;
+  double*       a   = (double*)c->audio;
+  int           len = c->len/8;
+  int           ch  = 0;
+  register int  nch = c->nch;
+  register int  i   = 0;
+
+  for(ch = 0; ch < nch ; ch++){
+    if(s->enable[ch]){
+      double t = 1.0 - s->time[ch];
+      for(i=ch;i<len;i+=nch){
+        register double x   = a[i];
+        register double pow = x*x;
+        s->pow[ch] = t*s->pow[ch] + pow*s->time[ch];
+        a[i] = x;
+      }
+    }
+  }
+  return c;
+}
+
 // Allocate memory and set function pointers
 static int af_open(af_instance_t* af){
   af->control=control;
   af->uninit=uninit;
-  af->play=play;
   af->mul=1;
   af->data=calloc(1,sizeof(af_data_t));
   af->setup=calloc(1,sizeof(af_gate_t));
diff --git a/libaf/af_karaoke.c b/libaf/af_karaoke.c
index a80f879..f445bee 100644
--- a/libaf/af_karaoke.c
+++ b/libaf/af_karaoke.c
@@ -29,16 +29,26 @@
 
 // Data for specific instances of this filter
 
+static af_data_t* play_float(struct af_instance_s* af, af_data_t* data);
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data);
+
 // Initialization and runtime control
 static int control(struct af_instance_s* af, int cmd, void* arg)
 {
 	switch(cmd){
-		case AF_CONTROL_REINIT:
-		af->data->rate	= ((af_data_t*)arg)->rate;
-		af->data->nch	= ((af_data_t*)arg)->nch;
-		af->data->format= AF_FORMAT_FLOAT_NE;
-		af->data->bps	= 4;
-		return af_test_output(af,(af_data_t*)arg);
+	case AF_CONTROL_REINIT:
+	    af->data->rate   = ((af_data_t*)arg)->rate;
+	    af->data->nch    = ((af_data_t*)arg)->nch;
+	    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+		  af->data->format = AF_FORMAT_DOUBLE_NE;   /* NEW: 64-bit float */
+		  af->data->bps    = 8;
+		  af->play = play_double;
+	    } else {
+		  af->data->format = AF_FORMAT_FLOAT_NE;
+		  af->data->bps    = 4;
+		  af->play = play_float;
+	    }
+	    return af_test_output(af,(af_data_t*)arg);
 	}
 	return AF_UNKNOWN;
 }
@@ -50,34 +60,42 @@ static void uninit(struct af_instance_s* af)
 }
 
 // Filter data through filter
-static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+static af_data_t* play_float(struct af_instance_s* af, af_data_t* data)
 {
-	af_data_t*	c	= data;		 // Current working data
-	float*		a	= c->audio;	 // Audio data
-	int			len	= c->len/4;	 // Number of samples in current audio block
-	int			nch	= c->nch;	 // Number of channels
-	register int  i;
+    af_data_t*  c   = data;
+    float*      a   = c->audio;
+    int         len = c->len/4;
+    int         nch = c->nch;
+    register int i;
 
-	/*
-		FIXME1 add a low band pass filter to avoid suppressing
-		centered bass/drums
-		FIXME2 better calculated* attenuation factor
-	*/
+    for(i=0;i<len;i+=nch)
+    {
+        a[i] = (a[i] - a[i+1]) * 0.7;
+        a[i+1]=a[i];
+    }
+    return c;
+}
 
-	for(i=0;i<len;i+=nch)
-	{
-		a[i] = (a[i] - a[i+1]) * 0.7;
-		a[i+1]=a[i];
-	}
+static af_data_t* play_double(struct af_instance_s* af, af_data_t* data)
+{
+    af_data_t*  c   = data;
+    double*     a   = c->audio;
+    int         len = c->len/8;
+    int         nch = c->nch;
+    register int i;
 
-	return c;
+    for(i=0;i<len;i+=nch)
+    {
+        a[i] = (a[i] - a[i+1]) * 0.7;
+        a[i+1]=a[i];
+    }
+    return c;
 }
 
 // Allocate memory and set function pointers
 static int af_open(af_instance_t* af){
 	af->control	= control;
 	af->uninit	= uninit;
-	af->play	= play;
 	af->mul		= 1;
 	af->data	= calloc(1,sizeof(af_data_t));
 
diff --git a/libaf/af_pan.c b/libaf/af_pan.c
index f6fc3ec..9ddde69 100644
--- a/libaf/af_pan.c
+++ b/libaf/af_pan.c
@@ -47,8 +47,14 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
     if(!arg) return AF_ERROR;
 
     af->data->rate   = ((af_data_t*)arg)->rate;
-    af->data->format = AF_FORMAT_FLOAT_NE;
-    af->data->bps    = 4;
+    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+      af->data->format = AF_FORMAT_DOUBLE_NE;   /* NEW: 64-bit float */
+      af->data->bps    = 8;
+    }
+    else{
+      af->data->format = AF_FORMAT_FLOAT_NE;
+      af->data->bps    = 4;
+    }
     af->data->nch    = s->nch ? s->nch: ((af_data_t*)arg)->nch;
     af->mul          = (double)af->data->nch / ((af_data_t*)arg)->nch;
 
@@ -155,9 +161,6 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
   af_data_t*    c    = data;		// Current working data
   af_data_t*	l    = af->data;	// Local data
   af_pan_t*  	s    = af->setup; 	// Setup for this instance
-  float*   	in   = c->audio;	// Input audio data
-  float*   	out  = NULL;		// Output audio data
-  float*	end  = in+c->len/4; 	// End of loop
   int		nchi = c->nch;		// Number of input channels
   int		ncho = l->nch;		// Number of output channels
   register int  j,k;
@@ -165,19 +168,38 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
   if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
     return NULL;
 
-  out = l->audio;
-  // Execute panning
-  // FIXME: Too slow
-  while(in < end){
-    for(j=0;j<ncho;j++){
-      register float  x   = 0.0;
-      register float* tin = in;
-      for(k=0;k<nchi;k++)
-	x += tin[k] * s->level[j][k];
-      out[j] = x;
+  if(af->data->format == AF_FORMAT_DOUBLE_NE){    /* NEW: 64-bit float */
+    double*   in   = c->audio;
+    double*   out  = l->audio;
+    double*   end  = in+c->len/8;
+    while(in < end){
+      for(j=0;j<ncho;j++){
+        register double  x   = 0.0;
+        register double* tin = in;
+        for(k=0;k<nchi;k++)
+          x += tin[k] * s->level[j][k];
+        out[j] = x;
+      }
+      out+= ncho;
+      in+= nchi;
+    }
+  }
+  else{
+    float*   	in   = c->audio;
+    float*   	out  = NULL;
+    float*	end  = in+c->len/4;
+    out = l->audio;
+    while(in < end){
+      for(j=0;j<ncho;j++){
+        register float  x   = 0.0;
+        register float* tin = in;
+        for(k=0;k<nchi;k++)
+          x += tin[k] * s->level[j][k];
+        out[j] = x;
+      }
+      out+= ncho;
+      in+= nchi;
     }
-    out+= ncho;
-    in+= nchi;
   }
 
   // Set output data
diff --git a/libaf/af_resample.c b/libaf/af_resample.c
index f7318eb..1226e8e 100644
--- a/libaf/af_resample.c
+++ b/libaf/af_resample.c
@@ -47,10 +47,11 @@
 #include "af_resample_template.c"
 
 // Filtering types
-#define RSMP_LIN   	(0<<0)	// Linear interpolation
-#define RSMP_INT   	(1<<0)  // 16 bit integer
-#define RSMP_FLOAT	(2<<0)	// 32 bit floating point
-#define RSMP_MASK	(3<<0)
+#define RSMP_LIN       (0<<0)  // Linear interpolation
+#define RSMP_INT       (1<<0)  // 16 bit integer
+#define RSMP_FLOAT     (2<<0)  // 32 bit floating point
+#define RSMP_DOUBLE    (3<<0)  /* NEW: 64 bit floating point */
+#define RSMP_MASK      (7<<0)  /* widened to fit RSMP_DOUBLE */
 
 // Defines for sloppy or exact resampling
 #define FREQ_SLOPPY 	(0<<2)
@@ -145,7 +146,13 @@ static int set_types(struct af_instance_s* af, af_data_t* data)
   else{
     /* If the input format is float or if float is explicitly selected
        use float, otherwise use int */
-    if((data->format == (AF_FORMAT_FLOAT_NE)) ||
+    if(data->format == (AF_FORMAT_DOUBLE_NE) ||
+       ((s->setup & RSMP_MASK) == RSMP_DOUBLE)){
+      s->setup = (s->setup & ~RSMP_MASK) | RSMP_DOUBLE;
+      af->data->format = AF_FORMAT_DOUBLE_NE;
+      af->data->bps    = 8;
+    }
+    else if((data->format == (AF_FORMAT_FLOAT_NE)) ||
        ((s->setup & RSMP_MASK) == RSMP_FLOAT)){
       s->setup = (s->setup & ~RSMP_MASK) | RSMP_FLOAT;
       af->data->format = AF_FORMAT_FLOAT_NE;
@@ -255,6 +262,8 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
 	    float t=(float)s->up*32767.0*(*wt);
 	    ((int16_t*)s->w)[i*L+j] = (int16_t)((t>=0.0)?(t+0.5):(t-0.5));
 	  }
+	  else if((s->setup & RSMP_MASK) == RSMP_DOUBLE)
+	    ((double*)s->w)[i*L+j] = (double)s->up*(*wt);
 	  else
 	    ((float*)s->w)[i*L+j] = (float)s->up*(*wt);
 	  wt++;
@@ -355,6 +364,21 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
     else{
 #     define DN
 #     include "af_resample_template.c"
+#     undef DN
+    }
+    break;
+  case(RSMP_DOUBLE):
+# undef FORMAT_I
+# undef FORMAT_F
+# define FORMAT_D 1
+    if(s->up>s->dn){
+#     define UP
+#     include "af_resample_template.c"
+#     undef UP
+    }
+    else{
+#     define DN
+#     include "af_resample_template.c"
 #     undef DN
     }
     break;
diff --git a/libaf/af_resample_template.c b/libaf/af_resample_template.c
index 4d4c592..e79f38d 100644
--- a/libaf/af_resample_template.c
+++ b/libaf/af_resample_template.c
@@ -46,6 +46,9 @@
 #if defined(FORMAT_I)
 #define SHIFT >>16
 #define FORMAT int16_t
+#elif defined(FORMAT_D)
+#define SHIFT
+#define FORMAT double
 #else
 #define SHIFT
 #define FORMAT float
diff --git a/libaf/af_volnorm.c b/libaf/af_volnorm.c
index 94c610b..244d7c8 100644
--- a/libaf/af_volnorm.c
+++ b/libaf/af_volnorm.c
@@ -53,6 +53,7 @@
 // FIXME: should be relative to the level of the samples
 #define SIL_S16 (SHRT_MAX * 0.01)
 #define SIL_FLOAT (0.01) // FIXME
+#define SIL_DOUBLE (0.01) // FIXME  /* NEW: 64-bit float */
 
 // smooth must be in ]0.0, 1.0[
 #define SMOOTH_MUL 0.06
@@ -76,6 +77,7 @@ typedef struct af_volume_s
     // "Ideal" level
     float mid_s16;
     float mid_float;
+    float mid_double;   /* NEW: 64-bit float */
 }af_volnorm_t;
 
 // Initialization and runtime control
@@ -94,6 +96,9 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
     if(((af_data_t*)arg)->format == (AF_FORMAT_S16_NE)){
       af->data->format = AF_FORMAT_S16_NE;
       af->data->bps    = 2;
+    }else if(((af_data_t*)arg)->format == (AF_FORMAT_DOUBLE_NE)){
+      af->data->format = AF_FORMAT_DOUBLE_NE;   /* NEW: 64-bit float */
+      af->data->bps    = 8;
     }else{
       af->data->format = AF_FORMAT_FLOAT_NE;
       af->data->bps    = 4;
@@ -108,6 +113,7 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
     s->method = i-1;
     s->mid_s16 = ((float)SHRT_MAX) * target;
     s->mid_float = target;
+    s->mid_double = target;   /* NEW */
     return AF_OK;
   }
   }
@@ -200,6 +206,74 @@ static void method1_float(af_volnorm_t *s, af_data_t *c)
   s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg;
 }
 
+static void method1_double(af_volnorm_t *s, af_data_t *c)
+{
+  register int i = 0;
+  double *data = (double*)c->audio;
+  int len = c->len/8;
+  float curavg = 0.0, newavg, neededmul, tmp;
+
+  for (i = 0; i < len; i++)
+  {
+    tmp = data[i];
+    curavg += tmp * tmp;
+  }
+  curavg = sqrt(curavg / (float) len);
+
+  if (curavg > SIL_DOUBLE)
+  {
+    neededmul = s->mid_double / (curavg * s->mul);
+    s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
+    s->mul = av_clipf(s->mul, MUL_MIN, MUL_MAX);
+  }
+
+  for (i = 0; i < len; i++)
+    data[i] *= s->mul;
+
+  newavg = s->mul * curavg;
+  s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg;
+}
+
+static void method2_double(af_volnorm_t *s, af_data_t *c)
+{
+  register int i = 0;
+  double *data = (double*)c->audio;
+  int len = c->len/8;
+  float curavg = 0.0, newavg, avg = 0.0, tmp;
+  int totallen = 0;
+
+  for (i = 0; i < len; i++)
+  {
+    tmp = data[i];
+    curavg += tmp * tmp;
+  }
+  curavg = sqrt(curavg / (float) len);
+
+  for (i = 0; i < NSAMPLES; i++)
+  {
+    avg += s->mem[i].avg * (float)s->mem[i].len;
+    totallen += s->mem[i].len;
+  }
+
+  if (totallen > MIN_SAMPLE_SIZE)
+  {
+    avg /= (float)totallen;
+    if (avg >= SIL_DOUBLE)
+    {
+	s->mul = s->mid_double / avg;
+	s->mul = av_clipf(s->mul, MUL_MIN, MUL_MAX);
+    }
+  }
+
+  for (i = 0; i < len; i++)
+    data[i] *= s->mul;
+
+  newavg = s->mul * curavg;
+  s->mem[s->idx].len = len;
+  s->mem[s->idx].avg = newavg;
+  s->idx = (s->idx + 1) % NSAMPLES;
+}
+
 static void method2_int16(af_volnorm_t *s, af_data_t *c)
 {
   register int i = 0;
@@ -296,7 +370,6 @@ static void method2_float(af_volnorm_t *s, af_data_t *c)
   s->idx = (s->idx + 1) % NSAMPLES;
 }
 
-// Filter data through filter
 static af_data_t* play(struct af_instance_s* af, af_data_t* data)
 {
   af_volnorm_t *s = af->setup;
@@ -308,6 +381,13 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
     else
 	method1_int16(s, data);
   }
+  else if(af->data->format == (AF_FORMAT_DOUBLE_NE))   /* NEW */
+  {
+    if (s->method)
+	method2_double(s, data);
+    else
+	method1_double(s, data);
+  }
   else if(af->data->format == (AF_FORMAT_FLOAT_NE))
   {
     if (s->method)
@@ -335,6 +415,7 @@ static int af_open(af_instance_t* af){
   ((af_volnorm_t*)af->setup)->idx = 0;
   ((af_volnorm_t*)af->setup)->mid_s16 = ((float)SHRT_MAX) * DEFAULT_TARGET;
   ((af_volnorm_t*)af->setup)->mid_float = DEFAULT_TARGET;
+  ((af_volnorm_t*)af->setup)->mid_double = DEFAULT_TARGET;   /* NEW */
   for (i = 0; i < NSAMPLES; i++)
   {
      ((af_volnorm_t*)af->setup)->mem[i].len = 0;
diff --git a/libaf/af_volume.c b/libaf/af_volume.c
index e79aa25..6c5a6f2 100644
--- a/libaf/af_volume.c
+++ b/libaf/af_volume.c
@@ -62,7 +62,11 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
     af->data->rate   = ((af_data_t*)arg)->rate;
     af->data->nch    = ((af_data_t*)arg)->nch;
 
-    if(s->fast && (((af_data_t*)arg)->format != (AF_FORMAT_FLOAT_NE))){
+    if(((af_data_t*)arg)->format == AF_FORMAT_DOUBLE_NE){
+      af->data->format = AF_FORMAT_DOUBLE_NE;      /* NEW: 64-bit float */
+      af->data->bps    = 8;
+    }
+    else if(s->fast && (((af_data_t*)arg)->format != (AF_FORMAT_FLOAT_NE))){
       af->data->format = AF_FORMAT_S16_NE;
       af->data->bps    = 2;
     }
@@ -122,6 +126,21 @@ static av_always_inline void s16_inner_loop(int16_t *data, int len, int offset,
   }
 }
 
+static av_always_inline void double_inner_loop(double *data, int len, int offset, int step, float level, int softclip)
+{
+  int i;
+  for (i = offset; i < len; i += step)
+  {
+    register double x = data[i];
+    x *= level;
+    if (softclip)
+      x = af_softclip(x);
+    else
+      x = av_clipf(x,-1.0,1.0);
+    data[i] = x;
+  }
+}
+
 static av_always_inline void float_inner_loop(float *data, int len, int offset, int step, float level, int softclip)
 {
   int i;
@@ -185,6 +204,19 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
     else for (ch = 0; ch < nch; ch++)
       s16_inner_loop(a, len, ch, nch, s->level[ch]);
   }
+  // 64-bit double float
+  else if(af->data->format == (AF_FORMAT_DOUBLE_NE)){    /* NEW */
+    double*    a   = (double*)c->audio;
+    int        len = c->len/8;
+    for (i = 0; !s->fast && i < len; i++)
+      s->max = FFMAX(s->max, a[i] * a[i]);
+    if (same_vol && s->soft)
+      double_inner_loop(a, len, 0, 1, s->level[0], 1);
+    else if (same_vol)
+      double_inner_loop(a, len, 0, 1, s->level[0], 0);
+    else for (ch = 0; ch < nch; ch++)
+      double_inner_loop(a, len, ch, nch, s->level[ch], s->soft);
+  }
   // Machine is fast and data is floating point
   else if(af->data->format == (AF_FORMAT_FLOAT_NE)){
     float*   	a   	= (float*)c->audio;	// Audio data
diff --git a/libaf/format.c b/libaf/format.c
index a57d471..51a20b1 100644
--- a/libaf/format.c
+++ b/libaf/format.c
@@ -61,8 +61,9 @@ int af_str2fmt(const char* str)
   }
 
   // Scan for int/float
-  if(strstr(str,"float") || strstr(str,"FLOAT")){
-    format |= AF_FORMAT_F; return format;
+  if(strstr(str,"float") || strstr(str,"FLOAT") ||  
+     strstr(str,"double") || strstr(str,"DOUBLE")){  
+      format |= AF_FORMAT_F; return format;  
   }
   else
     format |= AF_FORMAT_I;
@@ -195,6 +196,9 @@ static struct {
     { "floatle", AF_FORMAT_FLOAT_LE },
     { "floatbe", AF_FORMAT_FLOAT_BE },
     { "floatne", AF_FORMAT_FLOAT_NE },
+    { "doublele", AF_FORMAT_DOUBLE_LE },
+    { "doublebe", AF_FORMAT_DOUBLE_BE },
+    { "doublene", AF_FORMAT_DOUBLE_NE },
 
     { NULL, 0 }
 };
diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c
index 095837f..fff6d34 100644
--- a/libao2/ao_alsa.c
+++ b/libao2/ao_alsa.c
@@ -69,7 +69,7 @@ static int prepause_space;
 #define ALSA_DEVICE_SIZE 256
 
 static void alsa_error_handler(const char *file, int line, const char *function,
-			       int err, const char *format, ...)
+                               int err, const char *format, ...)
 {
   char tmp[0xc00];
   va_list va;
@@ -80,10 +80,10 @@ static void alsa_error_handler(const char *file, int line, const char *function,
 
   if (err)
     mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n",
-	   file, line, function, tmp, snd_strerror(err));
+           file, line, function, tmp, snd_strerror(err));
   else
     mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n",
-	   file, line, function, tmp);
+           file, line, function, tmp);
 }
 
 /* to set/get/query special features/parameters */
@@ -111,23 +111,23 @@ static int control(int cmd, void *arg)
       float f_multi;
 
       if(AF_FORMAT_IS_IEC61937(ao_data.format))
-	return CONTROL_TRUE;
+        return CONTROL_TRUE;
 
       if(mixer_channel) {
-	 char *test_mix_index;
-
-	 mix_name = strdup(mixer_channel);
-	 if ((test_mix_index = strchr(mix_name, ','))){
-		*test_mix_index = 0;
-		test_mix_index++;
-		mix_index = strtol(test_mix_index, &test_mix_index, 0);
-
-		if (*test_mix_index){
-		  mp_msg(MSGT_AO,MSGL_ERR,
-		    MSGTR_AO_ALSA_InvalidMixerIndexDefaultingToZero);
-		  mix_index = 0 ;
-		}
-	 }
+         char *test_mix_index;
+
+         mix_name = strdup(mixer_channel);
+         if ((test_mix_index = strchr(mix_name, ','))){
+                *test_mix_index = 0;
+                test_mix_index++;
+                mix_index = strtol(test_mix_index, &test_mix_index, 0);
+
+                if (*test_mix_index){
+                  mp_msg(MSGT_AO,MSGL_ERR,
+                    MSGTR_AO_ALSA_InvalidMixerIndexDefaultingToZero);
+                  mix_index = 0 ;
+                }
+         }
       }
       if(mixer_device) card = mixer_device;
 
@@ -139,32 +139,32 @@ static int control(int cmd, void *arg)
       snd_mixer_selem_id_set_name(sid, mix_name);
 
       if (mixer_channel) {
-	free(mix_name);
-	mix_name = NULL;
+        free(mix_name);
+        mix_name = NULL;
       }
 
       if ((err = snd_mixer_open(&handle, 0)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerOpenError, snd_strerror(err));
-	return CONTROL_ERROR;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerOpenError, snd_strerror(err));
+        return CONTROL_ERROR;
       }
 
       if ((err = snd_mixer_attach(handle, card)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerAttachError,
-	       card, snd_strerror(err));
-	snd_mixer_close(handle);
-	return CONTROL_ERROR;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerAttachError,
+               card, snd_strerror(err));
+        snd_mixer_close(handle);
+        return CONTROL_ERROR;
       }
 
       if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerRegisterError, snd_strerror(err));
-	snd_mixer_close(handle);
-	return CONTROL_ERROR;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerRegisterError, snd_strerror(err));
+        snd_mixer_close(handle);
+        return CONTROL_ERROR;
       }
       err = snd_mixer_load(handle);
       if (err < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerLoadError, snd_strerror(err));
-	snd_mixer_close(handle);
-	return CONTROL_ERROR;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerLoadError, snd_strerror(err));
+        snd_mixer_close(handle);
+        return CONTROL_ERROR;
       }
 
       elem = snd_mixer_find_selem(handle, sid);
@@ -175,15 +175,15 @@ static int control(int cmd, void *arg)
             mp_msg(MSGT_AO, MSGL_V, "[AO_ALSA] Auto-selected element %s\n", snd_mixer_selem_get_name(elem));
       }
       if (!elem) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToFindSimpleControl,
-	       snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToFindSimpleControl,
+               snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
         mp_msg(MSGT_AO, MSGL_INFO, "[AO_ALSA] Available elements for device:\n");
         for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem))
             mp_msg(MSGT_AO, MSGL_INFO, "[AO_ALSA]  %s\n", snd_mixer_selem_get_name(elem));
         mp_msg(MSGT_AO, MSGL_INFO, "\n");
-	snd_mixer_close(handle);
-	return CONTROL_ERROR;
-	}
+        snd_mixer_close(handle);
+        return CONTROL_ERROR;
+        }
 
       if (!snd_mixer_selem_has_playback_volume(elem) &&
           !snd_mixer_selem_has_playback_volume_joined(elem))
@@ -217,46 +217,46 @@ static int control(int cmd, void *arg)
 
       if (cmd == AOCONTROL_SET_VOLUME) {
 
-	set_vol = vol->left / f_multi + pmin + 0.5;
-
-	//setting channels
-	if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) {
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSettingLeftChannel,
-		 snd_strerror(err));
-	  snd_mixer_close(handle);
-	  return CONTROL_ERROR;
-	}
-	mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol);
-
-	set_vol = vol->right / f_multi + pmin + 0.5;
-
-	if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) {
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSettingRightChannel,
-		 snd_strerror(err));
-	  snd_mixer_close(handle);
-	  return CONTROL_ERROR;
-	}
-	mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n",
-	       set_vol, pmin, pmax, f_multi);
-
-	if (snd_mixer_selem_has_playback_switch(elem)) {
-	  int lmute = (vol->left == 0.0);
-	  int rmute = (vol->right == 0.0);
-	  if (snd_mixer_selem_has_playback_switch_joined(elem)) {
-	    lmute = rmute = lmute && rmute;
-	  } else {
-	    snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !rmute);
-	  }
-	  snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !lmute);
-	}
+        set_vol = vol->left / f_multi + pmin + 0.5;
+
+        //setting channels
+        if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSettingLeftChannel,
+                 snd_strerror(err));
+          snd_mixer_close(handle);
+          return CONTROL_ERROR;
+        }
+        mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol);
+
+        set_vol = vol->right / f_multi + pmin + 0.5;
+
+        if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSettingRightChannel,
+                 snd_strerror(err));
+          snd_mixer_close(handle);
+          return CONTROL_ERROR;
+        }
+        mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n",
+               set_vol, pmin, pmax, f_multi);
+
+        if (snd_mixer_selem_has_playback_switch(elem)) {
+          int lmute = (vol->left == 0.0);
+          int rmute = (vol->right == 0.0);
+          if (snd_mixer_selem_has_playback_switch_joined(elem)) {
+            lmute = rmute = lmute && rmute;
+          } else {
+            snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !rmute);
+          }
+          snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !lmute);
+        }
       }
       else {
-	snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol);
-	vol->left = (get_vol - pmin) * f_multi;
-	snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol);
-	vol->right = (get_vol - pmin) * f_multi;
+        snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol);
+        vol->left = (get_vol - pmin) * f_multi;
+        snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol);
+        vol->right = (get_vol - pmin) * f_multi;
 
-	mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right);
+        mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right);
       }
       snd_mixer_close(handle);
       return CONTROL_OK;
@@ -307,25 +307,25 @@ static int try_open_device(const char *device, int open_mode, int try_ac3)
       strcat(ac3_device, ":AES0=6");
     } else {
       do
-	++args;
+        ++args;
       while (isspace(*args));
       if (*args == '\0') {
-	/* ":" but no parameters */
-	strcat(ac3_device, "AES0=6");
+        /* ":" but no parameters */
+        strcat(ac3_device, "AES0=6");
       } else if (*args != '{') {
-	/* a simple list of parameters: add it at the end of the list */
-	strcat(ac3_device, ",AES0=6");
+        /* a simple list of parameters: add it at the end of the list */
+        strcat(ac3_device, ",AES0=6");
       } else {
-	/* parameters in config syntax: add it inside the { } block */
-	do
-	  --len;
-	while (len > 0 && isspace(ac3_device[len]));
-	if (ac3_device[len] == '}')
-	  strcpy(ac3_device + len, " AES0=6}");
+        /* parameters in config syntax: add it inside the { } block */
+        do
+          --len;
+        while (len > 0 && isspace(ac3_device[len]));
+        if (ac3_device[len] == '}')
+          strcpy(ac3_device + len, " AES0=6}");
       }
     }
     err = snd_pcm_open(&alsa_handler, ac3_device, SND_PCM_STREAM_PLAYBACK,
-		       open_mode);
+                       open_mode);
     free(ac3_device);
     if (err >= 0)
       return err;
@@ -339,8 +339,8 @@ static int try_open_device(const char *device, int open_mode, int try_ac3)
 */
 static int init(int rate_hz, int channels, int format, int flags)
 {
-    unsigned int alsa_buffer_time = 500000; /* 0.5 s */
-    unsigned int alsa_fragcount = 16;
+    unsigned int alsa_buffer_time = 160000; /* Settings for fftrate, latency 0.16 s */
+    unsigned int alsa_fragcount = 4;        /* Settings for fftrate, latency 0.16 s */
     int err;
     int block;
     strarg_t device;
@@ -358,7 +358,7 @@ static int init(int rate_hz, int channels, int format, int flags)
     memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1);
 
     mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %x\n", rate_hz,
-	channels, format);
+        channels, format);
     alsa_handler = NULL;
 #if SND_LIB_VERSION >= 0x010005
     mp_msg(MSGT_AO,MSGL_V,"alsa-init: using ALSA %s\n", snd_asoundlib_version());
@@ -375,67 +375,73 @@ static int init(int rate_hz, int channels, int format, int flags)
     switch (format)
       {
       case AF_FORMAT_S8:
-	alsa_format = SND_PCM_FORMAT_S8;
-	break;
+        alsa_format = SND_PCM_FORMAT_S8;
+        break;
       case AF_FORMAT_U8:
-	alsa_format = SND_PCM_FORMAT_U8;
-	break;
+        alsa_format = SND_PCM_FORMAT_U8;
+        break;
       case AF_FORMAT_U16_LE:
-	alsa_format = SND_PCM_FORMAT_U16_LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_U16_LE;
+        break;
       case AF_FORMAT_U16_BE:
-	alsa_format = SND_PCM_FORMAT_U16_BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_U16_BE;
+        break;
       case AF_FORMAT_AC3_LE:
       case AF_FORMAT_S16_LE:
       case AF_FORMAT_IEC61937_LE:
-	alsa_format = SND_PCM_FORMAT_S16_LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_S16_LE;
+        break;
       case AF_FORMAT_AC3_BE:
       case AF_FORMAT_S16_BE:
       case AF_FORMAT_IEC61937_BE:
-	alsa_format = SND_PCM_FORMAT_S16_BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_S16_BE;
+        break;
       case AF_FORMAT_U32_LE:
-	alsa_format = SND_PCM_FORMAT_U32_LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_U32_LE;
+        break;
       case AF_FORMAT_U32_BE:
-	alsa_format = SND_PCM_FORMAT_U32_BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_U32_BE;
+        break;
       case AF_FORMAT_S32_LE:
-	alsa_format = SND_PCM_FORMAT_S32_LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_S32_LE;
+        break;
       case AF_FORMAT_S32_BE:
-	alsa_format = SND_PCM_FORMAT_S32_BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_S32_BE;
+        break;
       case AF_FORMAT_U24_LE:
-	alsa_format = SND_PCM_FORMAT_U24_3LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_U24_3LE;
+        break;
       case AF_FORMAT_U24_BE:
-	alsa_format = SND_PCM_FORMAT_U24_3BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_U24_3BE;
+        break;
       case AF_FORMAT_S24_LE:
-	alsa_format = SND_PCM_FORMAT_S24_3LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_S24_3LE;
+        break;
       case AF_FORMAT_S24_BE:
-	alsa_format = SND_PCM_FORMAT_S24_3BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_S24_3BE;
+        break;
       case AF_FORMAT_FLOAT_LE:
-	alsa_format = SND_PCM_FORMAT_FLOAT_LE;
-	break;
+        alsa_format = SND_PCM_FORMAT_FLOAT_LE;
+        break;
       case AF_FORMAT_FLOAT_BE:
-	alsa_format = SND_PCM_FORMAT_FLOAT_BE;
-	break;
+        alsa_format = SND_PCM_FORMAT_FLOAT_BE;
+        break;
+      case AF_FORMAT_DOUBLE_LE:            /* NEW: 64-bit float */
+        alsa_format = SND_PCM_FORMAT_FLOAT64_LE;
+        break;
+      case AF_FORMAT_DOUBLE_BE:            /* NEW: 64-bit float */
+        alsa_format = SND_PCM_FORMAT_FLOAT64_BE;
+        break;
       case AF_FORMAT_MU_LAW:
-	alsa_format = SND_PCM_FORMAT_MU_LAW;
-	break;
+        alsa_format = SND_PCM_FORMAT_MU_LAW;
+        break;
       case AF_FORMAT_A_LAW:
-	alsa_format = SND_PCM_FORMAT_A_LAW;
-	break;
+        alsa_format = SND_PCM_FORMAT_A_LAW;
+        break;
 
       default:
-	alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1
-	break;
+        alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1
+        break;
       }
 
     //subdevice parsing
@@ -448,44 +454,47 @@ static int init(int rate_hz, int channels, int format, int flags)
      * 'iec958'
      */
     if (AF_FORMAT_IS_IEC61937(format)) {
-	device.str = "iec958";
-	mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels);
+        device.str = "iec958";
+        mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels);
     }
   else
         /* in any case for multichannel playback we should select
          * appropriate device
          */
         switch (channels) {
-	case 1:
-	case 2:
-	  device.str = "default";
-	  mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n");
-	  break;
-	case 4:
-	  if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
-	    // hack - use the converter plugin
-	    device.str = "plug:surround40";
-	  else
-	    device.str = "surround40";
-	  mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n");
-	  break;
-	case 6:
-	  if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
-	    device.str = "plug:surround51";
-	  else
-	    device.str = "surround51";
-	  mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n");
-	  break;
-	case 8:
-	  if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
-	    device.str = "plug:surround71";
-	  else
-	    device.str = "surround71";
-	  mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n");
-	  break;
-	default:
-	  device.str = "default";
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ChannelsNotSupported,channels);
+        case 1:
+        case 2:
+          device.str = "default";
+          mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n");
+          break;
+        case 4:
+          if (alsa_format == SND_PCM_FORMAT_FLOAT_LE ||
+              alsa_format == SND_PCM_FORMAT_FLOAT64_LE)
+            // hack - use the converter plugin
+            device.str = "plug:surround40";
+          else
+            device.str = "surround40";
+          mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n");
+          break;
+        case 6:
+          if (alsa_format == SND_PCM_FORMAT_FLOAT_LE ||
+              alsa_format == SND_PCM_FORMAT_FLOAT64_LE)
+            device.str = "plug:surround51";
+          else
+            device.str = "surround51";
+          mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n");
+          break;
+        case 8:
+          if (alsa_format == SND_PCM_FORMAT_FLOAT_LE ||
+              alsa_format == SND_PCM_FORMAT_FLOAT64_LE)
+            device.str = "plug:surround71";
+          else
+            device.str = "surround71";
+          mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n");
+          break;
+        default:
+          device.str = "default";
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ChannelsNotSupported,channels);
         }
     device.len = strlen(device.str);
     if (subopt_parse(ao_subdevice, subopts) != 0) {
@@ -502,23 +511,25 @@ static int init(int rate_hz, int channels, int format, int flags)
       //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
       mp_msg(MSGT_AO,MSGL_V,"alsa-init: opening device in %sblocking mode\n", block ? "" : "non-");
       if ((err = try_open_device(alsa_device, open_mode, isac3)) < 0)
-	{
-	  if (err != -EBUSY && !block) {
-	    mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_OpenInNonblockModeFailed);
-	    if ((err = try_open_device(alsa_device, 0, isac3)) < 0) {
-	      mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PlaybackOpenError, snd_strerror(err));
-	      return 0;
-	    }
-	  } else {
-	    mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PlaybackOpenError, snd_strerror(err));
-	    return 0;
-	  }
-	}
-
-      if ((err = snd_pcm_nonblock(alsa_handler, 0)) < 0) {
-         mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSetBlockMode, snd_strerror(err));
-      } else {
-	mp_msg(MSGT_AO,MSGL_V,"alsa-init: device reopened in blocking mode\n");
+        {
+          if (err != -EBUSY && !block) {
+            mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_OpenInNonblockModeFailed);
+            if ((err = try_open_device(alsa_device, 0, isac3)) < 0) {
+              mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PlaybackOpenError, snd_strerror(err));
+              return 0;
+            }
+          } else {
+            mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PlaybackOpenError, snd_strerror(err));
+            return 0;
+          }
+        }
+
+      /* keep the mode we opened with; 'block=1' (default) is unchanged */
+      if (block) {
+        if ((err = snd_pcm_nonblock(alsa_handler, 0)) < 0)
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSetBlockMode, snd_strerror(err));
+        else
+          mp_msg(MSGT_AO,MSGL_V,"alsa-init: device reopened in blocking mode\n");
       }
 
       snd_pcm_hw_params_alloca(&alsa_hwparams);
@@ -526,27 +537,27 @@ static int init(int rate_hz, int channels, int format, int flags)
 
       // setting hw-parameters
       if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0)
-	{
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetInitialParameters,
-		 snd_strerror(err));
-	  return 0;
-	}
+        {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetInitialParameters,
+                 snd_strerror(err));
+          return 0;
+        }
 
       err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,
-					 SND_PCM_ACCESS_RW_INTERLEAVED);
+                                         SND_PCM_ACCESS_RW_INTERLEAVED);
       if (err < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetAccessType,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetAccessType,
+               snd_strerror(err));
+        return 0;
       }
 
       /* workaround for nonsupported formats
-	 sets default format to S16_LE if the given formats aren't supported */
+         sets default format to S16_LE if the given formats aren't supported */
       if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams,
                                              alsa_format)) < 0)
       {
          mp_msg(MSGT_AO,MSGL_INFO,
-		MSGTR_AO_ALSA_FormatNotSupportedByHardware, af_fmt2str_short(format));
+                MSGTR_AO_ALSA_FormatNotSupportedByHardware, af_fmt2str_short(format));
          alsa_format = SND_PCM_FORMAT_S16_LE;
          if (AF_FORMAT_IS_AC3(ao_data.format))
            ao_data.format = AF_FORMAT_AC3_LE;
@@ -557,141 +568,141 @@ static int init(int rate_hz, int channels, int format, int flags)
       }
 
       if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
-					      alsa_format)) < 0)
-	{
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetFormat,
-		 snd_strerror(err));
-	  return 0;
-	}
+                                              alsa_format)) < 0)
+        {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetFormat,
+                 snd_strerror(err));
+          return 0;
+        }
 
       if ((err = snd_pcm_hw_params_set_channels_near(alsa_handler, alsa_hwparams,
-						     &ao_data.channels)) < 0)
-	{
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetChannels,
-		 snd_strerror(err));
-	  return 0;
-	}
+                                                     &ao_data.channels)) < 0)
+        {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetChannels,
+                 snd_strerror(err));
+          return 0;
+        }
 
+/*     DISABLE workaround with RESAMPLER (comment out) */
       /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
          prefer our own resampler, since that allows users to choose the resampler,
          even per file if desired */
-#if SND_LIB_VERSION >= 0x010009
-      if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams,
-						     0)) < 0)
-	{
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToDisableResampling,
-		 snd_strerror(err));
-	  return 0;
-	}
-#endif
+/*#if SND_LIB_VERSION >= 0x010009*/
+/*      if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams,*/
+/*                                                   0)) < 0)*/
+/*      {*/
+/*        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToDisableResampling,*/
+/*               snd_strerror(err));*/
+/*        return 0;*/
+/*      }*/
+/*#endif*/
 
       if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams,
-						 &ao_data.samplerate, NULL)) < 0)
+                 &ao_data.samplerate, NULL)) < 0)
         {
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetSamplerate2,
-		 snd_strerror(err));
-	  return 0;
+            mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetSamplerate2,
+                 snd_strerror(err));
+            return 0;
         }
 
       bytes_per_sample = af_fmt2bits(ao_data.format) / 8;
       bytes_per_sample *= ao_data.channels;
       ao_data.bps = ao_data.samplerate * bytes_per_sample;
 
-	if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams,
-							  &alsa_buffer_time, NULL)) < 0)
-	  {
-	    mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetBufferTimeNear,
-		   snd_strerror(err));
-	    return 0;
-	  }
-
-	if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams,
-						      &alsa_fragcount, NULL)) < 0) {
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetPeriods,
-		 snd_strerror(err));
-	  return 0;
-	}
+      if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams,
+                &alsa_buffer_time, NULL)) < 0)
+      {
+           mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetBufferTimeNear,
+                snd_strerror(err));
+           return 0;
+      }
+
+        if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams,
+                                                      &alsa_fragcount, NULL)) < 0) {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetPeriods,
+                 snd_strerror(err));
+          return 0;
+        }
 
       /* finally install hardware parameters */
       if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
-	{
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetHwParameters,
-		 snd_strerror(err));
-	  return 0;
-	}
+        {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetHwParameters,
+                 snd_strerror(err));
+          return 0;
+        }
       // end setting hw-params

       // gets buffersize for control
       if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0)
-	{
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBufferSize, snd_strerror(err));
-	  return 0;
-	}
+        {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBufferSize, snd_strerror(err));
+          return 0;
+        }
       else {
-	ao_data.buffersize = bufsize * bytes_per_sample;
-	  mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize);
+        ao_data.buffersize = bufsize * bytes_per_sample;
+          mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize);
       }
 
       if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetPeriodSize, snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetPeriodSize, snd_strerror(err));
+        return 0;
       } else {
-	mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size);
+        mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size);
       }
       ao_data.outburst = chunk_size * bytes_per_sample;
 
       /* setting software parameters */
       if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetSwParameters,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetSwParameters,
+               snd_strerror(err));
+        return 0;
       }
 #if SND_LIB_VERSION >= 0x000901
       if ((err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBoundary,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBoundary,
+               snd_strerror(err));
+        return 0;
       }
 #else
       boundary = 0x7fffffff;
 #endif
       /* start playing when one period has been written */
       if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, chunk_size)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetStartThreshold,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetStartThreshold,
+               snd_strerror(err));
+        return 0;
       }
       /* disable underrun reporting */
       if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, boundary)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetStopThreshold,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetStopThreshold,
+               snd_strerror(err));
+        return 0;
       }
 #if SND_LIB_VERSION >= 0x000901
       /* play silence when there is an underrun */
       if ((err = snd_pcm_sw_params_set_silence_size(alsa_handler, alsa_swparams, boundary)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetSilenceSize,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetSilenceSize,
+               snd_strerror(err));
+        return 0;
       }
 #endif
       if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetSwParameters,
-	       snd_strerror(err));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetSwParameters,
+               snd_strerror(err));
+        return 0;
       }
       /* end setting sw-params */
 
       mp_msg(MSGT_AO,MSGL_V,"alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n",
-	     ao_data.samplerate, ao_data.channels, (int)bytes_per_sample, ao_data.buffersize,
-	     snd_pcm_format_description(alsa_format));
+             ao_data.samplerate, ao_data.channels, (int)bytes_per_sample, ao_data.buffersize,
+             snd_pcm_format_description(alsa_format));
 
     } // end switch alsa_handler (spdif)
     alsa_can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
     return 1;
-} // end init
-
+}
 
 /* close audio device */
 static void uninit(int immed)
@@ -705,8 +716,8 @@ static void uninit(int immed)
 
     if ((err = snd_pcm_close(alsa_handler)) < 0)
       {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmCloseError, snd_strerror(err));
-	return;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmCloseError, snd_strerror(err));
+        return;
       }
     else {
       alsa_handler = NULL;
@@ -754,11 +765,11 @@ static void audio_resume(void)
             mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmResumeError, snd_strerror(err));
             return;
         }
-          mp_msg(MSGT_AO,MSGL_V,"alsa-resume: resume supported by hardware\n");
+            mp_msg(MSGT_AO,MSGL_V,"alsa-resume: resume supported by hardware\n");
     } else {
         if ((err = snd_pcm_prepare(alsa_handler)) < 0)
         {
-           mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(err));
+            mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(err));
             return;
         }
         mp_ao_resume_refill(&audio_out_alsa, prepause_space);
@@ -772,13 +783,13 @@ static void reset(void)
 
     if ((err = snd_pcm_drop(alsa_handler)) < 0)
     {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(err));
-	return;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(err));
+        return;
     }
     if ((err = snd_pcm_prepare(alsa_handler)) < 0)
     {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(err));
-	return;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(err));
+        return;
     }
     return;
 }
@@ -794,11 +805,13 @@ static int play(void* data, int len, int flags)
 {
   int num_frames;
   snd_pcm_sframes_t res = 0;
-  if (!(flags & AOPLAY_FINAL_CHUNK))
-      len = len / ao_data.outburst * ao_data.outburst;
-  num_frames = len / bytes_per_sample;
+  snd_pcm_sframes_t written = 0;          /* frames written so far */
+  int waits = 0;                          /* bounded -EAGAIN waits */
+  const int max_waits = 100;              /* 100 * 10ms = 1s max */
 
-  //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len);
+/*  if (!(flags & AOPLAY_FINAL_CHUNK))*/
+/*      len = len / ao_data.outburst * ao_data.outburst;*/
+  num_frames = len / bytes_per_sample;
 
   if (!alsa_handler) {
     mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_DeviceConfigurationError);
@@ -808,30 +821,56 @@ static int play(void* data, int len, int flags)
   if (num_frames == 0)
     return 0;
 
-  do {
+  while (num_frames > 0) {
     res = snd_pcm_writei(alsa_handler, data, num_frames);
 
-      if (res == -EINTR) {
-	/* nothing to do */
-	res = 0;
-      }
-      else if (res == -ESTRPIPE) {	/* suspend */
-	mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_PcmInSuspendModeTryingResume);
-	while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
-	  sleep(1);
-      }
-      if (res < 0) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_WriteError, snd_strerror(res));
-	mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_TryingToResetSoundcard);
-	if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
-	  mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(res));
-	  break;
-	}
-	res = 0;
-      }
-  } while (res == 0);
+    if (res > 0) {
+      /* partial write: advance and continue */
+      written    += res;
+      data        = (char*)data + res * bytes_per_sample;
+      num_frames -= res;
+      waits       = 0;
+      continue;
+    }
+
+    if (res == -EINTR) {
+      continue;                          /* interrupted: retry, nothing written */
+    }
+    else if (res == -EAGAIN) {
+      /* buffer full (only in real nonblocking mode):
+         sleep on the PCM fd instead of spinning */
+      if (++waits > max_waits)
+        break;                           /* give up, return partial progress */
+      if ((res = snd_pcm_wait(alsa_handler, 10)) < 0)
+        res = -errno;                    /* fall through to error handling */
+      else
+        continue;                        /* space may be free: retry write */
+    }
+    else if (res == -ESTRPIPE) {         /* suspended */
+      mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_PcmInSuspendModeTryingResume);
+      while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
+        sleep(1);
+      if (res < 0)
+        res = snd_pcm_prepare(alsa_handler);  /* resume failed: re-prepare */
+      if (res < 0)
+        break;
+      continue;
+    }
 
-  return res < 0 ? 0 : res * bytes_per_sample;
+    if (res == 0)
+      continue;                          /* nothing written: retry */
+
+    /* any other error: reset the device and retry the REMAINING frames */
+    mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_WriteError, snd_strerror(res));
+    mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_TryingToResetSoundcard);
+    if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
+      mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PcmPrepareError, snd_strerror(res));
+      break;
+    }
+    /* fall through: retry with updated num_frames/data */
+  }
+
+  return written * bytes_per_sample;
 }
 
 /* how many byes are free in the buffer */
@@ -844,13 +883,13 @@ static int get_space(void)
 
     if ((ret = snd_pcm_status(alsa_handler, status)) < 0)
     {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_CannotGetPcmStatus, snd_strerror(ret));
-	return 0;
+        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_CannotGetPcmStatus, snd_strerror(ret));
+        return 0;
     }
 
     ret = snd_pcm_status_get_avail(status) * bytes_per_sample;
     if (ret > ao_data.buffersize)  // Buffer underrun?
-	ret = ao_data.buffersize;
+        ret = ao_data.buffersize;
     return ret;
 }
 
diff --git a/libmpcodecs/ad_ffmpeg.c b/libmpcodecs/ad_ffmpeg.c
index daa9c43..2e2f432 100644
--- a/libmpcodecs/ad_ffmpeg.c
+++ b/libmpcodecs/ad_ffmpeg.c
@@ -184,6 +184,7 @@ static int init(sh_audio_t *sh_audio)
       case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P:
       case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P:
       case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP:
+      case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBLP:   /* NEW: 64-bit float */
           break;
       default:
           return 0;
diff --git a/libmpcodecs/ad_pcm.c b/libmpcodecs/ad_pcm.c
index 3162874..42a4f3d 100644
--- a/libmpcodecs/ad_pcm.c
+++ b/libmpcodecs/ad_pcm.c
@@ -57,7 +57,19 @@ static int init(sh_audio_t * sh_audio)
         }
         break;
     case 0x3:                  // IEEE float
-        sh_audio->sample_format = AF_FORMAT_FLOAT_LE;
+        switch (sh_audio->samplesize) {
+        case 4:
+            sh_audio->sample_format = AF_FORMAT_FLOAT_LE;
+            break;
+        case 8:
+            sh_audio->sample_format = AF_FORMAT_DOUBLE_LE;
+            break;
+        default:
+            mp_msg(MSGT_DECAUDIO, MSGL_WARN,
+                   "Uncompressed PCM: unsupported IEEE float sample size %d\n",
+                   sh_audio->samplesize);
+            return 0;
+        }
         break;
     case 0x6:  sh_audio->sample_format = AF_FORMAT_A_LAW;      break;
     case 0x7:  sh_audio->sample_format = AF_FORMAT_MU_LAW;     break;
@@ -86,14 +98,14 @@ static int init(sh_audio_t * sh_audio)
         sh_audio->sample_format = AF_FORMAT_FLOAT_LE;
         sh_audio->samplesize = 4;
         break;
-/*    case 0x34366c66: // 'fl64', bigendian float64
-       sh_audio->sample_format=AF_FORMAT_FLOAT_BE;
+    case 0x34366c66: // 'fl64', bigendian float64
+       sh_audio->sample_format=AF_FORMAT_DOUBLE_BE;
        sh_audio->samplesize=8;
        break;
     case 0x666c3634: // '46lf', little endian float64, MPlayer internal fourCC
-       sh_audio->sample_format=AF_FORMAT_FLOAT_LE;
+       sh_audio->sample_format=AF_FORMAT_DOUBLE_LE;
        sh_audio->samplesize=8;
-       break;*/
+       break;
     case 0x34326e69: // 'in24', bigendian int24
         sh_audio->sample_format = AF_FORMAT_S24_BE;
         sh_audio->samplesize = 3;
diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c
index 9d17e1b..049fcd9 100644
--- a/libmpdemux/demux_lavf.c
+++ b/libmpdemux/demux_lavf.c
@@ -379,6 +379,18 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) {
         case AVMEDIA_TYPE_VIDEO:{
             sh_video_t* sh_video;
             BITMAPINFOHEADER *bih;
+
+            /* Attached pictures (cover art) are single-packet image
+             * streams, not video. Do not expose them as -vid, otherwise
+             * audio files trigger the whole video pipeline (decoder,
+             * [scale] filter, VO) and spew PTS errors. */
+            if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
+                st->discard = AVDISCARD_ALL;
+                mp_msg(MSGT_DEMUX, MSGL_V,
+                       "[lavf] stream %d: attached picture ignored\n", i);
+                break;
+            }
+
             sh_video=new_sh_video_vid(demuxer, i, priv->video_streams);
             if(!sh_video) break;
             stream_type = "video";
diff --git a/mpbswap.h b/mpbswap.h
index 28f7337..9871e62 100644
--- a/mpbswap.h
+++ b/mpbswap.h
@@ -30,5 +30,6 @@
 #define le2me_64(v) av_le2ne64(v)
 #define be2me_16(v) av_be2ne16(v)
 #define be2me_32(v) av_be2ne32(v)
+#define bswap_64(v) av_bswap64(v)   /* NEW: needed for 64-bit double endian swap */
 
 #endif /* MPLAYER_MPBSWAP_H */
-- 
2.39.5
$ cat PATCHES_mplayer/0004*
From a44482a93da3e96219740bac82cf2a7e8c041dae Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Mon, 26 Oct 2026 19:28:59 +0100
Subject: [PATCH 4/6] Remove libaf resample/format filters: redundant with
 ALSA/fftrate conversion chain

---
 Makefile   |  3 --
 libaf/af.c | 88 +++++-------------------------------------------------
 2 files changed, 7 insertions(+), 84 deletions(-)

diff --git a/Makefile b/Makefile
index 94ef503..7de4362 100644
--- a/Makefile
+++ b/Makefile
@@ -53,7 +53,6 @@ SRCS_COMMON-$(FASTMEMCPY)            += libvo/aclib.c
 SRCS_COMMON-$(FFMPEG)                += av_helpers.c                    \
                                         av_opts.c                       \
                                         libaf/af_lavcac3enc.c           \
-                                        libaf/af_lavcresample.c         \
                                         libmpcodecs/ad_ffmpeg.c         \
                                         libmpcodecs/ad_spdif.c          \
                                         libmpcodecs/vd_ffmpeg.c         \
@@ -274,12 +273,10 @@ SRCS_COMMON = asxparser.c                       \
               libaf/af_dummy.c                  \
               libaf/af_equalizer.c              \
               libaf/af_extrastereo.c            \
-              libaf/af_format.c                 \
               libaf/af_gate.c                   \
               libaf/af_hrtf.c                   \
               libaf/af_karaoke.c                \
               libaf/af_pan.c                    \
-              libaf/af_resample.c               \
               libaf/af_scaletempo.c             \
               libaf/af_sinesuppress.c           \
               libaf/af_stats.c                  \
diff --git a/libaf/af.c b/libaf/af.c
index 3a4d9e2..fd190bf 100644
--- a/libaf/af.c
+++ b/libaf/af.c
@@ -32,8 +32,6 @@
 extern const af_info_t af_info_dummy;
 extern const af_info_t af_info_delay;
 extern const af_info_t af_info_channels;
-extern const af_info_t af_info_format;
-extern const af_info_t af_info_resample;
 extern const af_info_t af_info_volume;
 extern const af_info_t af_info_equalizer;
 extern const af_info_t af_info_gate;
@@ -45,7 +43,6 @@ extern const af_info_t af_info_export;
 extern const af_info_t af_info_volnorm;
 extern const af_info_t af_info_extrastereo;
 extern const af_info_t af_info_lavcac3enc;
-extern const af_info_t af_info_lavcresample;
 extern const af_info_t af_info_sweep;
 extern const af_info_t af_info_hrtf;
 extern const af_info_t af_info_ladspa;
@@ -60,8 +57,6 @@ static const af_info_t * const filter_list[] = {
    &af_info_dummy,
    &af_info_delay,
    &af_info_channels,
-   &af_info_format,
-   &af_info_resample,
    &af_info_volume,
    &af_info_equalizer,
    &af_info_gate,
@@ -76,7 +71,6 @@ static const af_info_t * const filter_list[] = {
    &af_info_extrastereo,
 #ifdef CONFIG_FFMPEG
    &af_info_lavcac3enc,
-   &af_info_lavcresample,
 #endif
    &af_info_sweep,
    &af_info_hrtf,
@@ -296,24 +290,7 @@ int af_reinit(af_stream_t* s, af_instance_t* af)
 	  if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
 	    return rv;
 	}
-	// Insert format filter
-	if((af->prev?af->prev->data->format:s->input.format) != in.format){
-	  // Create format filter
-	  if(NULL == (new = af_prepend(s,af,"format")))
-	    return AF_ERROR;
-	  // Set output bits per sample
-	  in.format |= af_bits2fmt(in.bps*8);
-	  if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_FMT,&in.format)))
-	    return rv;
-	  // Initialize format filter
-	  if(!new->prev)
-	    memcpy(&in,&(s->input),sizeof(af_data_t));
-	  else
-	    memcpy(&in,new->prev->data,sizeof(af_data_t));
-	  if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
-	    return rv;
-	}
-	if(!new){ // Should _never_ happen
+	if(!new){ // No channels filter needed either -> formats must already match
 	  mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
 		 "This error should never occur, please send a bug report.\n");
 	  return AF_ERROR;
@@ -376,20 +353,9 @@ static int fixup_output_format(af_stream_t* s)
 	return AF_ERROR;
     }
 
-    // Check output format fix if not OK
-    if(s->output.format != AF_FORMAT_UNKNOWN &&
-		s->last->data->format != s->output.format){
-      if(strcmp(s->last->info->name,"format"))
-	af = af_append(s,s->last,"format");
-      else
-	af = s->last;
-      // Init the new filter
-      s->output.format |= af_bits2fmt(s->output.bps*8);
-      if(!af || (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT,&(s->output.format))))
-	return AF_ERROR;
-      if(AF_OK != af_reinit(s,af))
-	return AF_ERROR;
-    }
+    // Format conversion filter removed: output format must already match,
+    // the downstream AO (e.g. ALSA) or fftrate plugin is now responsible
+    // for any needed conversion.
 
     // Re init again just in case
     if(AF_OK != af_reinit(s,s->first))
@@ -482,49 +448,9 @@ int af_init(af_stream_t* s)
   // Check output format
   if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
     af_instance_t* af = NULL; // New filter
-    // Check output frequency if not OK fix with resample
-    if(s->output.rate && s->last->data->rate!=s->output.rate){
-      // try to find a filter that can change samplrate
-      af = af_control_any_rev(s, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
-               &(s->output.rate));
-      if (!af) {
-        const char *resampler = "resample";
-#ifdef CONFIG_FFMPEG
-        if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW)
-          resampler = "lavcresample";
-#endif
-	if((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW){
-	  if(!strcmp(s->first->info->name,"format"))
-	    af = af_append(s,s->first,resampler);
-	  else
-	    af = af_prepend(s,s->first,resampler);
-	}
-	else{
-	  if(!strcmp(s->last->info->name,"format"))
-	    af = af_prepend(s,s->last,resampler);
-	  else
-	    af = af_append(s,s->last,resampler);
-	}
-      // Init the new filter
-      if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
-				      &(s->output.rate))))
-	return -1;
-      // Use lin int if the user wants fast
-      if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_FAST) {
-        char args[32];
-	sprintf(args, "%d", s->output.rate);
-#ifdef CONFIG_FFMPEG
-	if (strcmp(resampler, "lavcresample") == 0)
-	  strcat(args, ":1");
-	else
-#endif
-	strcat(args, ":0:0");
-	af->control(af, AF_CONTROL_COMMAND_LINE, args);
-      }
-      }
-      if(AF_OK != af_reinit(s,af))
-      	return -1;
-    }
+
+    // Resampling filters removed: sample-rate conversion is now expected
+    // to be handled by the AO backend (ALSA / fftrate plugin), not libaf.
     if (AF_OK != fixup_output_format(s)) {
       // Something is stuffed audio out will not work
       mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to setup filter system can not"
-- 
2.39.5
$ cat PATCHES_mplayer/0005*
From 520a7433c82d7d0a3d4cc35c3770bbf2ffa053e9 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Wed, 11 Nov 2026 22:03:44 +0100
Subject: [PATCH 5/6] audio: preserve native decoder output through the
 playback chain

Decoder output was previously constrained to audio_output_channels
(default 2) and s16-oriented formats, forcing libavcodec to downmix
multichannel streams and discard sample precision before the audio
filter chain or output driver could see the original signal. The
filter chain additionally inserted a fast linear resampler on any
rate mismatch, and the ALSA driver permitted the rate plugin to
resample a second time.

This change removes those lossy stages:

- libmpcodecs/ad_ffmpeg.c: pass through the codec's native channel
  count, sample rate and sample format (including floatle and
  double-precision float) to sh_audio instead of requesting a
  downmixed s16 stream.

- libaf/af.c, etc/codecs.conf: add AF_FORMAT_DOUBLE_{LE,BE,NE}
  support so 64-bit float samples propagate through the filter
  chain without truncation; enable 64-bit float PCM decoding.

- mplayer.c, access_mpcontext.h: add mpctx_get_current() so AO
  drivers can read the actual decoder output spec from
  mpctx->sh_audio. The init() rate/channels/format arguments carry
  -1 "AO chooses" wildcards and cannot be used for this.

- cfg-mplayer.h, mplayer.c: new option alsa_ch (default 2) selects
  the ALSA device channel count independently of the source.

- libao2/ao_alsa.c: select the PCM device from the configured
  device channel count rather than the source channel count, fixing
  plug:surround51 being opened for stereo output; perform
  downmix/reorder to ALSA channel order in the driver when
  src_channels != dev_channels; report source units in ao_data so
  the filter chain does not insert a second downmix; map
  AF_FORMAT_DOUBLE_* to SND_PCM_FORMAT_FLOAT64_*

- configure: detect fftrate for the OSS driver's optional
  double-precision converter (dlopen'd at runtime, no link-time or
  header dependency).
---
 access_mpcontext.h      |   1 +
 cfg-mplayer.h           |   5 +
 configure               |  25 ++++
 etc/codecs.conf         |   2 +-
 libaf/af.c              | 132 ++++----------------
 libao2/ao_alsa.c        | 265 ++++++++++++++++++++++++++++++++++------
 libmpcodecs/ad_ffmpeg.c |  18 +--
 mplayer.c               |  35 +++---
 8 files changed, 306 insertions(+), 177 deletions(-)

diff --git a/access_mpcontext.h b/access_mpcontext.h
index 3c8c6fc..7a9f0c3 100644
--- a/access_mpcontext.h
+++ b/access_mpcontext.h
@@ -29,5 +29,6 @@ void mpctx_get_global_sub_info(struct MPContext *mpctx, int *size, int *pos);
 int mpctx_get_osd_function(struct MPContext *mpctx);
 void *mpctx_get_stream(struct MPContext *mpctx);
 void *mpctx_get_afilter(struct MPContext *mpctx);
+struct MPContext *mpctx_get_current(void);
 
 #endif /* MPLAYER_ACCESS_MPCONTEXT_H */
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index cf82ed0..2049403 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -91,6 +91,11 @@ const m_option_t mplayer_opts[]={
     {"master", "Option -master has been removed, use -af volume instead.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL},
     // override audio buffer size (used only by -ao oss, anyway obsolete...)
     {"abs", &ao_data.buffersize, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL},
+    // -ao alsa: device channel count (backend downmixes/reorders; default 2 = stereo)  
+    {"alsa_ch", &ao_alsa_channels, CONF_TYPE_INT, CONF_RANGE, 0, 8, NULL},
+    /* -ao oss (OSS4) fftrate target: device rate and sample width */
+    {"oss_target_rate",     &ao_oss_target_rate,     CONF_TYPE_INT, CONF_RANGE, 8000, 768000, NULL},
+    {"oss_target_bitdepth", &ao_oss_target_bitdepth, CONF_TYPE_INT, CONF_RANGE, 16, 32,     NULL},
 
     // -ao pcm options:
     {"aofile", "-aofile has been removed. Use -ao pcm:file=<filename> instead.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL},
diff --git a/configure b/configure
index 6649c86..dd21b1e 100755
--- a/configure
+++ b/configure
@@ -526,6 +526,7 @@ Video output:
 
 Audio output:
   --disable-alsa         disable ALSA audio output [autodetect]
+  --enable-fftrate       enable fftrate converter in OSS output (dlopen) [autodetect]
   --disable-ossaudio     disable OSS audio output [autodetect]
   --disable-arts         disable aRts audio output [autodetect]
   --disable-esd          disable esd audio output [autodetect]
@@ -732,6 +733,7 @@ _iconv=auto
 _langinfo=auto
 _rtc=auto
 _ossaudio=auto
+_fftrate=auto
 _arts=auto
 _esd=auto
 _pulse=auto
@@ -1121,6 +1123,8 @@ for ac_option do
   --disable-libdv)      _libdv=no       ;;
   --enable-ossaudio)    _ossaudio=yes   ;;
   --disable-ossaudio)   _ossaudio=no    ;;
+  --enable-fftrate)     _fftrate=yes    ;;
+  --disable-fftrate)    _fftrate=no     ;;
   --enable-arts)        _arts=yes       ;;
   --disable-arts)       _arts=no        ;;
   --enable-esd)         _esd=yes        ;;
@@ -6138,6 +6142,26 @@ else
 fi
 echores "$_ossaudio"
 
+echocheck "fftrate converter"
+if test "$_fftrate" = auto ; then
+  _fftrate=no
+  if test "$_ossaudio" = yes ; then
+    echo '#include <mmsys.h>' > $TMPC
+    echo '#include <convert/convertlib.h>' >> $TMPC
+    echo 'int main(void) { return 0; }' >> $TMPC
+    cc_check -I/usr/include/fftrate && _fftrate=yes
+  fi
+fi
+if test "$_fftrate" = yes ; then
+  def_fftrate='#define CONFIG_FFTRATE 1'
+  extra_cflags="$extra_cflags -I/usr/include/fftrate"
+  statement_check dlfcn.h 'dlopen(0,0);dlsym(0,0)' -ldl &&
+    extra_ldflags="$extra_ldflags -ldl"
+else
+  def_fftrate='#undef CONFIG_FFTRATE'
+fi
+echores "$_fftrate"
+
 
 echocheck "aRts"
 if test "$_arts" = auto ; then
@@ -9329,6 +9353,7 @@ $def_openal_h
 $def_ossaudio
 $def_ossaudio_devdsp
 $def_ossaudio_devmixer
+$def_fftrate
 $def_pulse
 $def_sgiaudio
 $def_sndio
diff --git a/etc/codecs.conf b/etc/codecs.conf
index 536bc50..872685e 100644
--- a/etc/codecs.conf
+++ b/etc/codecs.conf
@@ -5979,7 +5979,7 @@ audiocodec ffdsdlsbf
 audiocodec ffdsdlsbfp
   info "FFmpeg DSD (Direct Stream Digital), lsbit first, planar"
   comment "in DSF files"
-  status  working
+  status  buggy   ; low-quality decimation; use offline DSD->PCM conversion
   fourcc DSFl ; MPlayer internal
   driver ffmpeg
   dll dsd_lsbf_planar
diff --git a/libaf/af.c b/libaf/af.c
index fd190bf..defda09 100644
--- a/libaf/af.c
+++ b/libaf/af.c
@@ -254,7 +254,7 @@ int af_reinit(af_stream_t* s, af_instance_t* af)
 
     // Check if there are any filters left in the list
     if(!af)
-      return af_append(s,s->first,"dummy") ? AF_ERROR : AF_UNKNOWN;
+      return s->first ? AF_OK : AF_ERROR; // unreachable with dummy anchor
 
     // Check if this is the first filter
     if(!af->prev)
@@ -271,37 +271,13 @@ int af_reinit(af_stream_t* s, af_instance_t* af)
 	af = af->next;
       break;
     case AF_FALSE:{ // Configuration filter is needed
-      // Do auto insertion only if force is not specified
-      if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
-	af_instance_t* new = NULL;
-	// Insert channels filter
-	if((af->prev?af->prev->data->nch:s->input.nch) != in.nch){
-	  // Create channels filter
-	  if(NULL == (new = af_prepend(s,af,"channels")))
-	    return AF_ERROR;
-	  // Set number of output channels
-	  if(AF_OK != (rv = new->control(new,AF_CONTROL_CHANNELS,&in.nch)))
-	    return rv;
-	  // Initialize channels filter
-	  if(!new->prev)
-	    memcpy(&in,&(s->input),sizeof(af_data_t));
-	  else
-	    memcpy(&in,new->prev->data,sizeof(af_data_t));
-	  if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
-	    return rv;
-	}
-	if(!new){ // No channels filter needed either -> formats must already match
-	  mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
-		 "This error should never occur, please send a bug report.\n");
-	  return AF_ERROR;
-	}
-	af=new->next;
-      }
-      else {
-        mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Automatic filter insertion disabled "
-               "but formats do not match. Giving up.\n");
-        return AF_ERROR;
-      }
+      // No format correction inside MPlayer — conversion is fftrate's job.
+      // Skip the filter that refuses the input format; PCM passes through.
+      // Propagate the real upstream format into its af->data so downstream
+      // filters (and fixup_output_format) see the truth, not stale values.
+      if(af->data)
+        memcpy(af->data,&in,sizeof(af_data_t));
+      af = af->next;
       break;
     }
     case AF_DETACH:{ // Filter is redundant and wants to be unloaded
@@ -338,61 +314,15 @@ void af_uninit(af_stream_t* s)
  */
 static int fixup_output_format(af_stream_t* s)
 {
-    af_instance_t* af = NULL;
-    // Check number of output channels fix if not OK
-    // If needed always inserted last -> easy to screw up other filters
-    if(s->output.nch && s->last->data->nch!=s->output.nch){
-      if(!strcmp(s->last->info->name,"format"))
-	af = af_prepend(s,s->last,"channels");
-      else
-	af = af_append(s,s->last,"channels");
-      // Init the new filter
-      if(!af || (AF_OK != af->control(af,AF_CONTROL_CHANNELS,&(s->output.nch))))
-	return AF_ERROR;
-      if(AF_OK != af_reinit(s,af))
-	return AF_ERROR;
-    }
-
-    // Format conversion filter removed: output format must already match,
-    // the downstream AO (e.g. ALSA) or fftrate plugin is now responsible
-    // for any needed conversion.
-
-    // Re init again just in case
-    if(AF_OK != af_reinit(s,s->first))
-      return AF_ERROR;
-
-    if (s->output.format == AF_FORMAT_UNKNOWN)
-	s->output.format = s->last->data->format;
-    if (!s->output.nch) s->output.nch = s->last->data->nch;
-    if (!s->output.rate) s->output.rate = s->last->data->rate;
-    if((s->last->data->format != s->output.format) ||
-       (s->last->data->nch    != s->output.nch)    ||
-       (s->last->data->rate   != s->output.rate))  {
-      return AF_ERROR;
-    }
+    if(!s->last || !s->last->data)
+      return AF_ERROR;   // genuinely empty chain — real failure, report it
+    // Whatever the chain produces IS the output — no correction, no error.
+    s->output.format = s->last->data->format;
+    s->output.nch    = s->last->data->nch;
+    s->output.rate   = s->last->data->rate;
     return AF_OK;
 }
 
-/**
- * Automatic downmix to stereo in case the codec does not implement it.
- */
-static void af_downmix(af_stream_t* s)
-{
-    static const char * const downmix_strs[AF_NCH + 1] = {
-        /*                FL       FR       RL       RR          FC          LF         AL      AR */
-        [3] = "pan=2:" "0.6:0:" "0:0.6:"                     "0.4:0.4",
-        [4] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0:"  "0:0.4",
-        [5] = "pan=2:" "0.5:0:" "0:0.5:" "0.2:0:"  "0:0.2:"  "0.3:0.3",
-        [6] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:"  "0:0.2:"  "0.3:0.3:"   "0.1:0.1",
-        [7] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:"  "0:0.2:"  "0.3:0.3:"              "0.1:0:" "0:0.1",
-        [8] = "pan=2:" "0.4:0:" "0:0.4:" "0.15:0:" "0:0.15:" "0.25:0.25:" "0.1:0.1:" "0.1:0:" "0:0.1",
-    };
-    const char *af_pan_str = downmix_strs[s->input.nch];
-
-    if (af_pan_str)
-        af_append(s, s->first, af_pan_str);
-}
-
 /* Initialize the stream "s". This function creates a new filter list
    if necessary according to the values set in input and output. Input
    and output should contain the format of the current movie and the
@@ -419,9 +349,12 @@ int af_init(af_stream_t* s)
 
   // Check if this is the first call
   if(!s->first){
-    // Append a downmix pan filter at the beginning of the chain if needed
-    if (s->input.nch != audio_output_channels && audio_output_channels == 2)
-      af_downmix(s);
+    // Always anchor the chain with a pass-through dummy filter, so
+    // s->first/s->last are never NULL even if every real filter is
+    // skipped (AF_FALSE) or detached. The anchor must be FIRST so it
+    // adopts s->input's format and reports it downstream.
+    if(!af_append(s,s->last,"dummy"))
+      return -1;
     // Add all filters in the list (if there are any)
     if (s->cfg.list) {
       while(s->cfg.list[i]){
@@ -431,34 +364,16 @@ int af_init(af_stream_t* s)
     }
   }
 
-  // If we do not have any filters otherwise
-  // add dummy to make automatic format conversion work
-  if (!s->first && !af_append(s, s->first, "dummy"))
-    return -1;
-
   // Init filters
   if(AF_OK != af_reinit(s,s->first))
     return -1;
 
-  // make sure the chain is not empty and valid (e.g. because of AF_DETACH)
+  // Safety net: if somehow every filter detached, re-anchor.
   if (!s->first)
     if (!af_append(s,s->first,"dummy") || AF_OK != af_reinit(s,s->first))
       return -1;
 
-  // Check output format
-  if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
-    af_instance_t* af = NULL; // New filter
-
-    // Resampling filters removed: sample-rate conversion is now expected
-    // to be handled by the AO backend (ALSA / fftrate plugin), not libaf.
-    if (AF_OK != fixup_output_format(s)) {
-      // Something is stuffed audio out will not work
-      mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to setup filter system can not"
-	     " meet sound-card demands, please send a bug report. \n");
-      af_uninit(s);
-      return -1;
-    }
-  }
+  fixup_output_format(s); // always AF_OK now; just records chain output format
   return 0;
 }
 
@@ -482,8 +397,7 @@ af_instance_t* af_add(af_stream_t* s, char* name){
     return NULL;
 
   // Reinitalize the filter list
-  if(AF_OK != af_reinit(s, s->first) ||
-     AF_OK != fixup_output_format(s)){
+  if(AF_OK != af_reinit(s, s->first)){
     // remove auto-inserted filters
     af_instance_t *to_remove = first_is_format ? s->first->next : s->first;
     while (to_remove != new)
diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c
index fff6d34..c5fc4f8 100644
--- a/libao2/ao_alsa.c
+++ b/libao2/ao_alsa.c
@@ -45,6 +45,8 @@
 #include "audio_out.h"
 #include "audio_out_internal.h"
 #include "libaf/af_format.h"
+#include "mp_core.h"
+#include "access_mpcontext.h"
 
 static const ao_info_t info =
 {
@@ -63,6 +65,118 @@ static snd_pcm_sw_params_t *alsa_swparams;
 
 static size_t bytes_per_sample;
 
+extern int ao_alsa_channels;        /* mplayer.c; alsa_ch option (0 = auto) */
+
+static int   src_channels = 0;      /* channels delivered by the decoder    */
+static int   dev_channels = 0;      /* channels the ALSA device runs        */
+static int   src_frame_bytes = 0;   /* bytes per input frame                */
+static int   sample_bytes  = 0;     /* bytes per single sample              */
+static void *mixbuf = NULL;         /* scratch: downmixed/reordered frames  */
+static int   mixbuf_frames = 0;
+
+/* FFmpeg canonical (MS/WAV) -> ALSA channel order permutation.
+ * in[] index -> position in the output frame; -1 = drop (only used
+ * implicitly by the downmix table, never here). */
+#define MIX_MAX_CH 8
+static const int perm_alsa[MIX_MAX_CH + 1][MIX_MAX_CH] = {
+  {0},                                     /* 0: unused            */
+  {0},                                     /* 1: FC                */
+  {0,1},                                   /* 2: FL,FR             */
+  {0,1,2},                                 /* 3: FL,FR,FC          */
+  {0,1,2,3},                               /* 4: FL,FR,BL,BR (same)*/
+  {0,1,3,4,2},                             /* 5: MS FL,FR,FC,BL,BR -> ALSA FL,FR,BL,BR,FC */
+  {0,1,4,5,2,3},                           /* 6: MS FL,FR,FC,LFE,BL,BR -> ALSA FL,FR,BL,BR,FC,LFE */
+  {0,1,4,5,2,3,6},                         /* 7: +BC (ALSA has no 7ch map; keep tail) */
+  {0,1,4,5,2,3,6,7},                       /* 8: MS FL,FR,FC,LFE,BL,BR,SL,SR -> ALSA FL,FR,BL,BR,FC,LFE,SL,SR */
+};
+
+/* N->2 downmix gains, input order = FFmpeg canonical (MS/WAV).
+ * Positions: FL FR FC LFE BL BR SL SR (indices per channel count).
+ * Center/LFE and surrounds at -3dB, matching fftrate semantics. */
+#define DM_C 0.7071
+static const double dm2[MIX_MAX_CH + 1][2][MIX_MAX_CH] = {
+  {{0}},
+  {{0}},
+  {{0}},
+  /* 3ch: FL FR FC */
+  {{1,0,DM_C},{0,1,DM_C}},
+  /* 4ch: FL FR BL BR */
+  {{1,0,DM_C,0},{0,1,0,DM_C}},
+  /* 5ch: FL FR FC BL BR */
+  {{1,0,DM_C,DM_C,0},{0,1,DM_C,0,DM_C}},
+  /* 6ch: FL FR FC LFE BL BR */
+  {{1,0,DM_C,DM_C,DM_C,0},{0,1,DM_C,DM_C,0,DM_C}},
+  /* 7ch: FL FR FC LFE BL BR BC */
+  {{1,0,DM_C,DM_C,DM_C,0,DM_C},{0,1,DM_C,DM_C,0,DM_C,DM_C}},
+  /* 8ch: FL FR FC LFE BL BR SL SR */
+  {{1,0,DM_C,DM_C,DM_C,0,DM_C,0},{0,1,DM_C,DM_C,0,DM_C,0,DM_C}},
+};
+
+static double mix_get(const char *p, int fmt)
+{
+  switch (fmt) {
+  case AF_FORMAT_FLOAT_LE:   return *(const float*)p;
+  case AF_FORMAT_DOUBLE_LE:  return *(const double*)p;
+  case AF_FORMAT_S16_LE:     return *(const int16_t*)p / 32768.0;
+  case AF_FORMAT_S32_LE:     return *(const int32_t*)p / 2147483648.0;
+  case AF_FORMAT_S24_LE: {   /* packed 3-byte LE */
+      int32_t v = (p[2] << 24 | (unsigned char)p[1] << 16 | (unsigned char)p[0] << 8) >> 8;
+      return v / 8388608.0; }
+  case AF_FORMAT_U8:         return (*(const unsigned char*)p - 128) / 128.0;
+  case AF_FORMAT_S8:         return *(const signed char*)p / 128.0;
+  default:                   return 0.0;
+  }
+}
+
+static void mix_put(char *p, int fmt, double v)
+{
+  if (v >  1.0) v =  1.0;
+  if (v < -1.0) v = -1.0;
+  switch (fmt) {
+  case AF_FORMAT_FLOAT_LE:   *(float*)p = (float)v; break;
+  case AF_FORMAT_DOUBLE_LE:  *(double*)p = v; break;
+  case AF_FORMAT_S16_LE:     *(int16_t*)p = (int16_t)(v * 32767.0); break;
+  case AF_FORMAT_S32_LE:     *(int32_t*)p = (int32_t)(v * 2147483647.0); break;
+  case AF_FORMAT_S24_LE: {   int32_t s = (int32_t)(v * 8388607.0);
+      p[0] = s; p[1] = s >> 8; p[2] = s >> 16; break; }
+  case AF_FORMAT_U8:         *(unsigned char*)p = (unsigned char)(v * 127.0 + 128); break;
+  case AF_FORMAT_S8:         *(signed char*)p = (signed char)(v * 127.0); break;
+  default: break;
+  }
+}
+
+/* Downmix src_channels -> dev_channels(==2) in place into mixbuf,
+ * or permute same-count frames MS -> ALSA order. Returns output frames. */
+static int mix_convert(const char *in, int frames, char *out)
+{
+  int f, i;
+  if (dev_channels == 2 && src_channels > 2 && src_channels <= MIX_MAX_CH) {
+    for (f = 0; f < frames; f++) {
+      double l = 0.0, r = 0.0;
+      const char *ip = in + f * src_frame_bytes;
+      for (i = 0; i < src_channels; i++) {
+        double s = mix_get(ip + i * sample_bytes, ao_data.format);
+        l += s * dm2[src_channels][0][i];
+        r += s * dm2[src_channels][1][i];
+      }
+      mix_put(out + f * bytes_per_sample,               ao_data.format, l);
+      mix_put(out + f * bytes_per_sample + sample_bytes, ao_data.format, r);
+    }
+  } else if (dev_channels == src_channels && src_channels > 2
+             && src_channels <= MIX_MAX_CH) {
+    /* same count, just reorder MS -> ALSA */
+    for (f = 0; f < frames; f++) {
+      const char *ip = in + f * src_frame_bytes;
+      char *op = out + f * bytes_per_sample;
+      for (i = 0; i < src_channels; i++)
+        mix_put(op + i * sample_bytes, ao_data.format,
+                mix_get(ip + perm_alsa[src_channels][i] * sample_bytes,
+                        ao_data.format));
+    }
+  }
+  return frames;
+}
+
 static int alsa_can_pause;
 static int prepause_space;
 
@@ -368,10 +482,42 @@ static int init(int rate_hz, int channels, int format, int flags)
 
     snd_lib_error_set_handler(alsa_error_handler);
 
+    /* Detect the true INPUT spec from the decoder stream; the
+     * (rate_hz,channels,format) args are mplayer's negotiated request,
+     * which can be the -1 "AO chooses" wildcard. */
+    {
+      struct MPContext *ctx = mpctx_get_current();
+      const sh_audio_t *sh_audio = ctx ? ctx->sh_audio : NULL;
+      if (!sh_audio) {
+        mp_msg(MSGT_AO,MSGL_ERR,"[AO_ALSA] no decoder stream (sh_audio); cannot detect input format\n");
+        return 0;
+      }
+      format   = sh_audio->sample_format;
+      rate_hz  = sh_audio->samplerate;
+      channels = sh_audio->channels;
+      fprintf(stderr, "[AO_ALSA] input: %d Hz, %d ch, %d-bit ('%s')\n",
+              rate_hz, channels, af_fmt2bits(format),
+              af_fmt2str_short(format));
+      fflush(stderr);
+    }
+
     ao_data.samplerate = rate_hz;
     ao_data.format = format;
     ao_data.channels = channels;
 
+    /* device channel count: alsa_ch option; default stereo for >2ch
+     * sources (backend downmixes), else follow the source. */
+    src_channels = channels;
+    dev_channels = ao_alsa_channels
+                   ? ao_alsa_channels
+                   : (src_channels > 2 ? 2 : src_channels);
+    if (dev_channels > src_channels)
+      dev_channels = src_channels;      /* never upmix */
+    ao_data.channels = src_channels;    /* what play() receives; device
+                                         * channels are dev_channels */
+    mp_msg(MSGT_AO,MSGL_V,"[AO_ALSA] channels: %d -> %d\n",
+           src_channels, dev_channels);
+
     switch (format)
       {
       case AF_FORMAT_S8:
@@ -440,10 +586,37 @@ static int init(int rate_hz, int channels, int format, int flags)
         break;
 
       default:
-        alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1
-        break;
+	mp_msg(MSGT_AO,MSGL_ERR,"[AO_ALSA] unmapped input format: %s (0x%x) — cannot select ALSA format\n",
+	       af_fmt2str_short(format), format);
+	return 0;
       }
 
+    /* mix_get()/mix_put() only implement little-endian sample I/O;  
+     * a BE source on the downmix/reorder path would decode as zeros.  
+     * Fail loudly instead of playing silence. */  
+    if (dev_channels > 2 && (format & AF_FORMAT_END_MASK) == AF_FORMAT_BE) {  
+        mp_msg(MSGT_AO,MSGL_ERR,  
+               "[AO_ALSA] big-endian input format %s with %dch downmix "  
+               "is not supported (alsa_ch=%d); set alsa_ch=0 to pass "  
+               "channels through unconverted\n",  
+               af_fmt2str_short(format), channels, dev_channels);  
+        return 0;  
+    }
+
+    /* mix_convert() implements only N->2 downmix (N <= MIX_MAX_CH) and
+     * same-count MS->ALSA reorder. Any other dev!=src combination would
+     * hit the passthrough in play() and write wrong-layout frames to the
+     * device — reject it here instead. */
+    if (dev_channels != src_channels &&
+        !(dev_channels == 2 && src_channels > 2 && src_channels <= MIX_MAX_CH)) {
+        mp_msg(MSGT_AO,MSGL_ERR,
+               "[AO_ALSA] cannot convert %dch -> %dch (alsa_ch=%d); "
+               "supported: downmix to stereo (alsa_ch=2) or same-count "
+               "reorder (alsa_ch=%d)\n",
+               src_channels, dev_channels, ao_alsa_channels, src_channels);
+        return 0;
+    }
+
     //subdevice parsing
     // set defaults
     block = 1;
@@ -461,7 +634,7 @@ static int init(int rate_hz, int channels, int format, int flags)
         /* in any case for multichannel playback we should select
          * appropriate device
          */
-        switch (channels) {
+        switch (dev_channels) {
         case 1:
         case 2:
           device.str = "default";
@@ -551,20 +724,12 @@ static int init(int rate_hz, int channels, int format, int flags)
         return 0;
       }
 
-      /* workaround for nonsupported formats
-         sets default format to S16_LE if the given formats aren't supported */
       if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams,
                                              alsa_format)) < 0)
       {
-         mp_msg(MSGT_AO,MSGL_INFO,
-                MSGTR_AO_ALSA_FormatNotSupportedByHardware, af_fmt2str_short(format));
-         alsa_format = SND_PCM_FORMAT_S16_LE;
-         if (AF_FORMAT_IS_AC3(ao_data.format))
-           ao_data.format = AF_FORMAT_AC3_LE;
-         else if (AF_FORMAT_IS_IEC61937(ao_data.format))
-           ao_data.format = AF_FORMAT_IEC61937_LE;
-         else
-         ao_data.format = AF_FORMAT_S16_LE;
+         mp_msg(MSGT_AO,MSGL_ERR,"[AO_ALSA] format %s rejected by device %s\n",
+         snd_pcm_format_description(alsa_format), alsa_device);
+         return 0;
       }
 
       if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
@@ -575,27 +740,21 @@ static int init(int rate_hz, int channels, int format, int flags)
           return 0;
         }
 
+      {
+      unsigned int dev_ch = dev_channels;
       if ((err = snd_pcm_hw_params_set_channels_near(alsa_handler, alsa_hwparams,
-                                                     &ao_data.channels)) < 0)
+                                                     &dev_ch)) < 0)
         {
           mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetChannels,
                  snd_strerror(err));
           return 0;
         }
-
-/*     DISABLE workaround with RESAMPLER (comment out) */
-      /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
-         prefer our own resampler, since that allows users to choose the resampler,
-         even per file if desired */
-/*#if SND_LIB_VERSION >= 0x010009*/
-/*      if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams,*/
-/*                                                   0)) < 0)*/
-/*      {*/
-/*        mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToDisableResampling,*/
-/*               snd_strerror(err));*/
-/*        return 0;*/
-/*      }*/
-/*#endif*/
+      if ((int)dev_ch != dev_channels) {
+          mp_msg(MSGT_AO,MSGL_WARN,"[AO_ALSA] device granted %d ch (requested %d)\n",
+                 dev_ch, dev_channels);
+          dev_channels = dev_ch;
+      }
+      }
 
       if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams,
                  &ao_data.samplerate, NULL)) < 0)
@@ -606,8 +765,12 @@ static int init(int rate_hz, int channels, int format, int flags)
         }
 
       bytes_per_sample = af_fmt2bits(ao_data.format) / 8;
-      bytes_per_sample *= ao_data.channels;
-      ao_data.bps = ao_data.samplerate * bytes_per_sample;
+      sample_bytes    = bytes_per_sample;
+      src_frame_bytes = sample_bytes * src_channels;
+      bytes_per_sample *= dev_channels;
+      /* bps must be in SOURCE bytes/sec: mplayer credits delay as
+       * playsize/ao_data.bps on the source-unit buffers it hands play() */
+      ao_data.bps = ao_data.samplerate * src_frame_bytes;
 
       if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams,
                 &alsa_buffer_time, NULL)) < 0)
@@ -641,7 +804,7 @@ static int init(int rate_hz, int channels, int format, int flags)
           return 0;
         }
       else {
-        ao_data.buffersize = bufsize * bytes_per_sample;
+        ao_data.buffersize = bufsize * src_frame_bytes;  /* source units */
           mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize);
       }
 
@@ -652,6 +815,9 @@ static int init(int rate_hz, int channels, int format, int flags)
         mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size);
       }
       ao_data.outburst = chunk_size * bytes_per_sample;
+      /* mplayer hands play() source-unit buffers; express outburst in
+       * source bytes so get_space() (source units) compares correctly */
+      ao_data.outburst = chunk_size * src_frame_bytes;
 
       /* setting software parameters */
       if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) {
@@ -728,6 +894,7 @@ static void uninit(int immed)
     mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_NoHandlerDefined);
   }
   snd_config_update_free_global();
+  free(mixbuf); mixbuf = NULL; mixbuf_frames = 0;
 }
 
 static void audio_pause(void)
@@ -811,7 +978,30 @@ static int play(void* data, int len, int flags)
 
 /*  if (!(flags & AOPLAY_FINAL_CHUNK))*/
 /*      len = len / ao_data.outburst * ao_data.outburst;*/
-  num_frames = len / bytes_per_sample;
+  num_frames = len / src_frame_bytes;   /* len arrives in SOURCE frame units */
+
+  if (src_channels != dev_channels || dev_channels > 2) {
+      int convertible = (dev_channels == 2 && src_channels > 2)
+                     || (dev_channels == src_channels && src_channels > 2);
+      if (src_channels > MIX_MAX_CH)
+          convertible = 0;
+      if (!convertible) {
+          mp_msg(MSGT_AO,MSGL_WARN,"[AO_ALSA] no conversion for %d->%d ch; passing through\n",
+                 src_channels, dev_channels);
+      } else {
+      if (num_frames > mixbuf_frames) {
+          free(mixbuf);
+          mixbuf = malloc((size_t)num_frames * bytes_per_sample);
+          mixbuf_frames = mixbuf ? num_frames : 0;
+      }
+      if (!mixbuf) {
+          mp_msg(MSGT_AO,MSGL_ERR,"[AO_ALSA] downmix buffer alloc failed\n");
+          return 0;
+      }
+      num_frames = mix_convert(data, num_frames, mixbuf);
+      data = mixbuf;
+      }
+  }
 
   if (!alsa_handler) {
     mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_DeviceConfigurationError);
@@ -870,7 +1060,7 @@ static int play(void* data, int len, int flags)
     /* fall through: retry with updated num_frames/data */
   }
 
-  return written * bytes_per_sample;
+  return written * src_frame_bytes;   /* consumed units = source frames */
 }
 
 /* how many byes are free in the buffer */
@@ -887,8 +1077,11 @@ static int get_space(void)
         return 0;
     }
 
-    ret = snd_pcm_status_get_avail(status) * bytes_per_sample;
-    if (ret > ao_data.buffersize)  // Buffer underrun?
+    /* report free space in SOURCE bytes — play() consumes src_frame_bytes
+     * per frame; ao_data.buffersize/outburst stay in device bytes for
+     * fill_audio_out_buffers()'s threshold comparison */
+    ret = snd_pcm_status_get_avail(status) * src_frame_bytes;
+    if (ret > ao_data.buffersize)
         ret = ao_data.buffersize;
     return ret;
 }
diff --git a/libmpcodecs/ad_ffmpeg.c b/libmpcodecs/ad_ffmpeg.c
index 2e2f432..5c05c49 100644
--- a/libmpcodecs/ad_ffmpeg.c
+++ b/libmpcodecs/ad_ffmpeg.c
@@ -28,7 +28,6 @@
 #include "ad_internal.h"
 #include "dec_audio.h"
 #include "av_helpers.h"
-#include "libaf/reorder_ch.h"
 #include "fmt-conversion.h"
 
 static const ad_info_t info =
@@ -100,8 +99,6 @@ static int init(sh_audio_t *sh_audio)
     int x;
     AVCodecContext *lavc_context;
     AVCodec *lavc_codec;
-    AVDictionary *opts = NULL;
-    char tmpstr[50];
 
     mp_msg(MSGT_DECAUDIO,MSGL_V,"FFmpeg's libavcodec audio codec\n");
     init_avcodec();
@@ -116,8 +113,6 @@ static int init(sh_audio_t *sh_audio)
     sh_audio->context=lavc_context;
     lavc_context->opaque = av_mallocz(sizeof(struct adctx));
 
-    snprintf(tmpstr, sizeof(tmpstr), "%f", drc_level);
-    av_dict_set(&opts, "drc_scale", tmpstr, 0);
     lavc_context->sample_rate = sh_audio->samplerate;
     lavc_context->bit_rate = sh_audio->i_bps * 8;
     if(sh_audio->wf){
@@ -128,7 +123,6 @@ static int init(sh_audio_t *sh_audio)
 	lavc_context->bits_per_coded_sample = sh_audio->wf->wBitsPerSample;
     }
     lavc_context->channel_layout = sh_audio->channel_layout;
-    lavc_context->request_channel_layout = av_get_default_channel_layout(audio_output_channels);
     lavc_context->codec_tag = sh_audio->format; //FOURCC
     lavc_context->codec_id = lavc_codec->id; // not sure if required, imho not --A'rpi
 
@@ -150,11 +144,10 @@ static int init(sh_audio_t *sh_audio)
     }
 
     /* open it */
-    if (avcodec_open2(lavc_context, lavc_codec, &opts) < 0) {
+    if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
         mp_msg(MSGT_DECAUDIO,MSGL_ERR, MSGTR_CantOpenCodec);
         return 0;
     }
-    av_dict_free(&opts);
    mp_msg(MSGT_DECAUDIO,MSGL_V,"INFO: libavcodec \"%s\" init OK!\n", lavc_codec->name);
 
 //   printf("\nFOURCC: 0x%X\n",sh_audio->format);
@@ -372,15 +365,6 @@ static int decode_audio(sh_audio_t *sh_audio,unsigned char *buf,int minlen,int m
         if (len2 < 0)
             return len2;
 	if(len2>0){
-	  if (((AVCodecContext *)sh_audio->context)->channels >= 5) {
-            int samplesize = av_get_bytes_per_sample(((AVCodecContext *)
-                                    sh_audio->context)->sample_fmt);
-            reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
-                                AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
-                                ((AVCodecContext *)sh_audio->context)->channels,
-                                len2 / samplesize, samplesize);
-	  }
-	  //len=len2;break;
 	  if(len<0) len=len2; else len+=len2;
 	  buf+=len2;
 	  maxlen -= len2;
diff --git a/mplayer.c b/mplayer.c
index 644aeb5..eb6a0b7 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -144,6 +144,11 @@ double start_pts   = MP_NOPTS_VALUE;
 char *heartbeat_cmd;
 float heartbeat_interval = 30.0;
 static int max_framesize;
+int ao_alsa_channels = 2;          /* 0 = follow source; N = force device chans
+                                    * (backend downmixes/reorders to ALSA order) */
+int ao_oss_target_rate     = 192000;  /* OSS4 device sample rate */
+int ao_oss_target_bitdepth = 32;      /* OSS4 device bit depth : 16 or 32   */
+
 
 int noconsolecontrols;
 //**************************************************************************//
@@ -383,6 +388,11 @@ void *mpctx_get_afilter(MPContext *mpctx)
     return mpctx->sh_audio ? mpctx->sh_audio->afilter : NULL;
 }
 
+struct MPContext *mpctx_get_current(void)  
+{  
+    return mpctx;  
+}
+
 static int is_valid_metadata_type(metadata_t type)
 {
     switch (type) {
@@ -1336,7 +1346,9 @@ static int build_afilter_chain(sh_audio_t *sh_audio, ao_data_t *ao_data)
         mpctx->mixer.afilter = NULL;
         return 0;
     }
-    if (af_control_any_rev(sh_audio->afilter,
+
+    if (sh_audio->afilter &&
+        af_control_any_rev(sh_audio->afilter,
                            AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET,
                            &playback_speed)) {
         new_srate = sh_audio->samplerate;
@@ -1661,20 +1673,15 @@ void reinit_audio_chain(void)
     }
 
     if (!(initialized_flags & INITIALIZED_AO)) {
-        current_module     = "af_preinit";
-        ao_data.samplerate = force_srate;
-        ao_data.channels   = 0;
-        ao_data.format     = audio_output_format;
-        // first init to detect best values
-        if (!init_audio_filters(mpctx->sh_audio,  // preliminary init
-                                // input:
-                                mpctx->sh_audio->samplerate,
-                                // output:
-                                &ao_data.samplerate, &ao_data.channels, &ao_data.format)) {
-            mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_AudioFilterChainPreinitError);
-            exit_player(EXIT_ERROR);
-        }
         current_module   = "ao2_init";
+         // Offer the decoder's native PCM format; the AO (ALSA plug / fftrate)
+        // negotiates and converts — MPlayer does not.
+        ao_data.samplerate = force_srate ? force_srate
+                                         : mpctx->sh_audio->samplerate;
+        ao_data.channels   = audio_output_channels ? audio_output_channels
+                                                   : mpctx->sh_audio->channels;
+        ao_data.format     = audio_output_format ? audio_output_format
+                                                 : mpctx->sh_audio->sample_format;
         mpctx->audio_out = init_best_audio_out(audio_driver_list,
                                                0, // plugin flag
                                                ao_data.samplerate,
-- 
2.39.5
$ cat PATCHES_mplayer/0006*
From 5c2fa06c83eb0203cc3637605961b80916461865 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Wed, 11 Nov 2026 22:48:37 +0100
Subject: [PATCH 6/6] ao_oss: exclusive-mode OSS4 output with runtime-loaded
 fftrate converter

Rework the OSS driver to feed the decoder's native output to the
hardware, replacing kernel/vmix conversion with libfftrate's
double-precision engine.

- Exclusive access: open the dsp with O_EXCL and disable kernel-side
  conversion via SNDCTL_DSP_COOKEDMODE, bypassing vmix. OSS4-only
  ioctls (COOKEDMODE, GETCAPS/PCM_CAP_VIRTUAL, GETODELAY) are guarded
  by #ifdef so the driver still builds on plain OSS headers.

- fftrate via dlopen/dlsym: rate conversion, N->2 downmix and format
  conversion run in libconvert in 64-bit float. No fftrate headers or
  link-time dependency (local WAVEFORMATEX ABI when CONFIG_FFTRATE is
  unset); falls back to the direct write path if the library is
  absent.

- A drain thread converts staged input and paces the device with
  blocking writes, hiding the engine's packet latency behind
  pre-buffered output.

- Input spec from mpctx->sh_audio via mpctx_get_current() instead of
  the negotiated init() args, so multichannel float streams reach the
  converter unmodified.

- New options oss_target_rate and oss_target_bitdepth (16/32) select
  the device rate and sample width.

- Accounting: ao_data.outburst and get_space() report input-staging
  bytes on the fftrate path, matching the units
  fill_audio_out_buffers() compares against; get_delay() sums staged
  input and device-buffered output. Fixes spurious "Audio device got
  stuck" warnings.
---
 libao2/ao_oss.c | 1029 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 917 insertions(+), 112 deletions(-)

diff --git a/libao2/ao_oss.c b/libao2/ao_oss.c
index 4951ff4..c5596f7 100644
--- a/libao2/ao_oss.c
+++ b/libao2/ao_oss.c
@@ -29,6 +29,9 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <dlfcn.h>
 
 #include "config.h"
 #include "libavutil/avstring.h"
@@ -36,6 +39,12 @@
 #include "mixer.h"
 #include "help_mp.h"
 
+#ifdef CONFIG_FFTRATE
+#include "convert/convertlib.h"
+#include "mmsys.h"
+#endif
+#include <pthread.h>
+
 #ifdef HAVE_SYS_SOUNDCARD_H
 #include <sys/soundcard.h>
 #endif
@@ -44,6 +53,9 @@
 
 #include "audio_out.h"
 #include "audio_out_internal.h"
+#include "access_mpcontext.h"
+#include "mp_core.h"   /* MPContext fields */ 
+#include "libmpdemux/stheader.h"    /* sh_audio_t */
 
 static const ao_info_t info =
 {
@@ -61,8 +73,8 @@ static int format2oss(int format)
 {
     switch(format)
     {
-    case AF_FORMAT_U8: return AFMT_U8;
-    case AF_FORMAT_S8: return AFMT_S8;
+    case AF_FORMAT_U8:     return AFMT_U8;
+    case AF_FORMAT_S8:     return AFMT_S8;
     case AF_FORMAT_U16_LE: return AFMT_U16_LE;
     case AF_FORMAT_U16_BE: return AFMT_U16_BE;
     case AF_FORMAT_S16_LE: return AFMT_S16_LE;
@@ -70,12 +82,6 @@ static int format2oss(int format)
 #ifdef AFMT_S24_PACKED
     case AF_FORMAT_S24_LE: return AFMT_S24_PACKED;
 #endif
-#ifdef AFMT_U32_LE
-    case AF_FORMAT_U32_LE: return AFMT_U32_LE;
-#endif
-#ifdef AFMT_U32_BE
-    case AF_FORMAT_U32_BE: return AFMT_U32_BE;
-#endif
 #ifdef AFMT_S32_LE
     case AF_FORMAT_S32_LE: return AFMT_S32_LE;
 #endif
@@ -86,60 +92,15 @@ static int format2oss(int format)
     case AF_FORMAT_FLOAT_NE: return AFMT_FLOAT;
 #endif
     // SPECIALS
-    case AF_FORMAT_MU_LAW: return AFMT_MU_LAW;
-    case AF_FORMAT_A_LAW: return AFMT_A_LAW;
+    case AF_FORMAT_MU_LAW:    return AFMT_MU_LAW;
+    case AF_FORMAT_A_LAW:     return AFMT_A_LAW;
     case AF_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
-#ifdef AFMT_MPEG
-    case AF_FORMAT_MPEG2: return AFMT_MPEG;
-#endif
-#ifdef AFMT_AC3
-    case AF_FORMAT_AC3_NE: return AFMT_AC3;
-#endif
-    }
-    mp_msg(MSGT_AO, MSGL_V, "OSS: Unknown/not supported internal format: %s\n", af_fmt2str_short(format));
-    return -1;
-}
-
-static int oss2format(int format)
-{
-    switch(format)
-    {
-    case AFMT_U8: return AF_FORMAT_U8;
-    case AFMT_S8: return AF_FORMAT_S8;
-    case AFMT_U16_LE: return AF_FORMAT_U16_LE;
-    case AFMT_U16_BE: return AF_FORMAT_U16_BE;
-    case AFMT_S16_LE: return AF_FORMAT_S16_LE;
-    case AFMT_S16_BE: return AF_FORMAT_S16_BE;
-#ifdef AFMT_S24_PACKED
-    case AFMT_S24_PACKED: return AF_FORMAT_S24_LE;
-#endif
-#ifdef AFMT_U32_LE
-    case AFMT_U32_LE: return AF_FORMAT_U32_LE;
-#endif
-#ifdef AFMT_U32_BE
-    case AFMT_U32_BE: return AF_FORMAT_U32_BE;
-#endif
-#ifdef AFMT_S32_LE
-    case AFMT_S32_LE: return AF_FORMAT_S32_LE;
-#endif
-#ifdef AFMT_S32_BE
-    case AFMT_S32_BE: return AF_FORMAT_S32_BE;
-#endif
-#ifdef AFMT_FLOAT
-    case AFMT_FLOAT: return AF_FORMAT_FLOAT_NE;
-#endif
-    // SPECIALS
-    case AFMT_MU_LAW: return AF_FORMAT_MU_LAW;
-    case AFMT_A_LAW: return AF_FORMAT_A_LAW;
-    case AFMT_IMA_ADPCM: return AF_FORMAT_IMA_ADPCM;
-#ifdef AFMT_MPEG
-    case AFMT_MPEG: return AF_FORMAT_MPEG2;
-#endif
 #ifdef AFMT_AC3
-    case AFMT_AC3: return AF_FORMAT_AC3_NE;
+    case AF_FORMAT_AC3_NE:    return AFMT_AC3;
 #endif
     }
-    mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_AO_OSS_UnknownUnsupportedFormat, format);
+    mp_msg(MSGT_AO, MSGL_V, "OSS: Unknown/not supported internal format: %s (0x%x)\n",
+           af_fmt2str_short(format), format);
     return -1;
 }
 
@@ -148,9 +109,600 @@ static audio_buf_info zz;
 static int audio_fd=-1;
 static int prepause_space;
 
+#define OSS_DMA_BUFSIZE (64*1024)   /* OSS4 enforced dma_buffsize=64 (osscore.conf) */
+
 static const char *oss_mixer_device = PATH_DEV_MIXER;
 static int oss_mixer_channel = SOUND_MIXER_PCM;
 
+/*====================================================================
+ * fftrate integration (optional, runtime-loaded)
+ *
+ * Uses installed headers when CONFIG_FFTRATE is set (cubeb style);
+ * otherwise a minimal local ABI -- no fftrate headers needed.
+ * Constants taken from src/lib/mmsys.h and src/lib/convert.h.
+ *==================================================================*/
+#ifndef CONFIG_FFTRATE
+#pragma pack(push, 2)          /* WAVEFORMATEX is packed at 2 */
+typedef struct {
+    uint16_t wFormatTag;
+    uint16_t nChannels;
+    uint32_t nSamplesPerSec;
+    uint32_t nAvgBytesPerSec;
+    uint16_t nBlockAlign;
+    uint16_t wBitsPerSample;
+    uint16_t cbSize;
+} fftrate_wfmt_t;
+#pragma pack(pop)
+
+#define FFTRATE_WFMT_PCM        0x0001
+#define FFTRATE_WFMT_FLOAT      0x0003
+#define FFTRATE_TRANSFORM_FFT   1        /* CONV_TRANSFORM_FFT  */
+#define FFTRATE_WINDOW_VORBIS   2        /* CONV_WINDOW_VORBIS  */
+
+typedef void *fftrate_hconvert_t;
+#else
+#define fftrate_wfmt_t      WAVEFORMATEX
+#define fftrate_hconvert_t  HCONVERT
+#define FFTRATE_WFMT_PCM      WAVE_FORMAT_PCM
+#define FFTRATE_WFMT_FLOAT    WAVE_FORMAT_IEEE_FLOAT
+#define FFTRATE_TRANSFORM_FFT CONV_TRANSFORM_FFT
+#define FFTRATE_WINDOW_VORBIS CONV_WINDOW_VORBIS
+#endif
+
+/* Cubeb-style lazy loading (its oss-fft backend, works):
+ * libconvert.so is underlinked -- it references parse_arg/str_*
+ * (libcmdline) and math helpers (libmathex) without DT_NEEDED.
+ * RTLD_GLOBAL exports each lib's symbols so the next one resolves.
+ * libriffio provides mmsys_format_pcm_fill/_free, used by
+ * pcm_rate_init (plugin.c) to build the WAVEFORMATEX. */
+static fftrate_hconvert_t (*p_convert_open)(const fftrate_wfmt_t *,
+                                            const fftrate_wfmt_t *,
+                                            const char *, ...);
+static fftrate_hconvert_t (*p_convert_close)(fftrate_hconvert_t);
+static int                  (*p_convert_reset)(fftrate_hconvert_t);
+static int                  (*p_convert_processing)(fftrate_hconvert_t,
+                                                    const unsigned char *,
+                                                    uint32_t *,
+                                                    unsigned char *,
+                                                    uint32_t *);
+static fftrate_wfmt_t      *(*p_mmsys_format_pcm_fill)(uint16_t, int,
+                                                     int, int, uint32_t);
+static fftrate_wfmt_t      *(*p_mmsys_format_free)(fftrate_wfmt_t *);
+
+#define FFTRATE_LIB_COUNT 4
+
+static int fftrate_load(void)
+{
+    static int lib_count = -1;          /* -1 = not tried yet */
+    static const char *const sonames[FFTRATE_LIB_COUNT] = {
+        "libcmdline.so",                /* first: parse_arg, fgetst, str_* */
+        "libmathex.so",
+        "libconvert.so",
+        "libriffio.so",                 /* last: mmsys_format_*            */
+    };
+    void *hconv, *hriff;
+    int i;
+
+    if (lib_count == FFTRATE_LIB_COUNT)
+        return 1;
+    if (lib_count >= 0)
+        return 0;                       /* already tried and failed */
+
+    lib_count = 0;
+    for (i = 0; i < FFTRATE_LIB_COUNT; i++) {
+        if (!dlopen(sonames[i], RTLD_LAZY | RTLD_GLOBAL)) {
+            mp_msg(MSGT_AO, MSGL_V,
+                   "audio_setup: fftrate: dlopen(%s) failed: %s\n",
+                   sonames[i], dlerror());
+            return 0;
+        }
+        lib_count++;
+    }
+
+    hconv = dlopen("libconvert.so", RTLD_LAZY | RTLD_GLOBAL); /* cached */
+    hriff = dlopen("libriffio.so",  RTLD_LAZY | RTLD_GLOBAL);
+    if (!hconv || !hriff) {
+        lib_count = 0;
+        return 0;
+    }
+
+    p_convert_open          = dlsym(hconv, "convert_open");
+    p_convert_close         = dlsym(hconv, "convert_close");
+    p_convert_reset         = dlsym(hconv, "convert_reset");
+    p_convert_processing    = dlsym(hconv, "convert_processing");
+    p_mmsys_format_pcm_fill = dlsym(hriff, "mmsys_format_pcm_fill");
+    p_mmsys_format_free     = dlsym(hriff, "mmsys_format_free");
+
+    if (!p_convert_open || !p_convert_close || !p_convert_reset ||
+        !p_convert_processing ||
+        !p_mmsys_format_pcm_fill || !p_mmsys_format_free) {
+        mp_msg(MSGT_AO, MSGL_V,
+               "audio_setup: fftrate: dlsym failed: %s\n", dlerror());
+        lib_count = 0;
+        return 0;
+    }
+    return 1;
+}
+
+/* converter instance state */
+static fftrate_hconvert_t fftrate_conv   = NULL;
+static unsigned char *fftrate_in_buf     = NULL;   /* staging, input  units */
+static pthread_t      fftrate_thread;
+static pthread_mutex_t fftrate_lock = PTHREAD_MUTEX_INITIALIZER;
+static volatile int   fftrate_quit;    /* set by uninit, cleared at thread start */
+static volatile int   fftrate_pause;   /* set by audio_pause, cleared by resume */
+static volatile int   fftrate_started; /* thread is alive */
+static int            fftrate_thread_created; /* pthread_create succeeded; joinable */
+static int            fftrate_drain_chunk; /* dev bytes per blocking write */
+static unsigned char *fftrate_out_buf    = NULL;   /* staging, output units */
+static uint32_t       fftrate_in_size    = 0;
+static uint32_t       fftrate_in_rem     = 0;      /* un-consumed input bytes */
+static uint32_t       fftrate_out_size   = 0;
+static uint32_t       fftrate_out_rem    = 0;      /* un-written output bytes */
+static int            in_rate            = 0;      /* source rate (double side) */
+static int            dev_rate           = 0;      /* OSS device rate         */
+static int            dev_format         = 0;      /* negotiated OSS format   */
+static int            dev_channels       = 0;      /* OSS device channels     */
+static int            in_frame_bytes     = 0;      /* ch * sizeof(src sample) */
+static int            dev_frame_bytes    = 0;      /* ch * sizeof(dev sample) */
+static int            in_outburst        = 0;      /* play() slice, INPUT bytes */
+static int            dev_bps            = 0;      /* device bytes/sec        */
+static int            src_format_saved   = 0;   /* input AF_FORMAT for re-setup */
+static int            src_rate_saved     = 0;   /* input rate for re-setup      */
+
+#define FFTRATE_OUT_CHANNELS 2                  /* device is stereo; engine's
+                                                 * transform_matrix_make() does N->2 */
+
+/* mplayer.conf: oss_target_rate / oss_target_bitdepth */
+extern int ao_oss_target_rate;
+extern int ao_oss_target_bitdepth;
+
+/* bit depth -> (libaf AF_FORMAT, OSS AFMT_*); 0 on unsupported */
+static int oss_target_formats(int *af_fmt, int *oss_fmt)
+{
+    switch (ao_oss_target_bitdepth) {
+    case 16: *af_fmt = AF_FORMAT_S16_NE; *oss_fmt = AFMT_S16_LE; return 1;
+#ifdef AFMT_S32_LE
+    case 32: *af_fmt = AF_FORMAT_S32_NE; *oss_fmt = AFMT_S32_LE; return 1;
+#endif
+    }
+    return 0;
+}
+
+#define FFTRATE_FRAG_SHIFT  14                  /* fragment = 2^14 = 16384 B  */
+#define FFTRATE_NUM_FRAGS   4                  /* 4x16KB = 64KB = dma_buffsize OSS4 default ~43ms */
+
+/* ---- debug instrumentation ------------------------------------ */
+static int dbg_on  = -1;     /* FFTRATE_DEBUG=1  : counters          */
+static int dbg_hex = -1;     /* FFTRATE_DEBUG_DUMP=1: first-bytes hex */
+static int dbg_on_flag(void)
+{
+    if (dbg_on < 0)
+        dbg_on = getenv("FFTRATE_DEBUG") ? 1 : 0;
+    return dbg_on;
+}
+static int dbg_hex_flag(void)
+{
+    if (dbg_hex < 0)
+        dbg_hex = getenv("FFTRATE_DEBUG_DUMP") ? 1 : 0;
+    return dbg_hex;
+}
+static unsigned dbg_calls   = 0;   /* play() calls        */
+static unsigned dbg_cconv   = 0;   /* convert_processing calls */
+static unsigned dbg_under   = 0;   /* calls producing 0 out bytes */
+static unsigned dbg_dwfull  = 0;   /* drain blocked: device full */
+static unsigned dbg_shortw  = 0;   /* write() < requested        */
+static uint64_t dbg_in_tot  = 0, dbg_out_tot = 0, dbg_w_tot = 0;
+static int      dbg_dumped  = 0;   /* hex dump done once  */
+static void dbg_dump(const char *tag, const unsigned char *p, int n)
+{
+    int i;
+    fprintf(stderr, "[FFTRATE] %s:", tag);
+    for (i = 0; i < n; i++)
+        fprintf(stderr, " %02x", p[i]);
+    fprintf(stderr, "\n");
+}
+static void dbg_summary(void)
+{
+    fprintf(stderr,
+        "[FFTRATE] calls=%u conv=%u in=%llu out=%llu written=%llu "
+        "under=%u devfull=%u shortw=%u in_rem=%u out_rem=%u\n",
+        dbg_calls, dbg_cconv,
+        (unsigned long long)dbg_in_tot,
+        (unsigned long long)dbg_out_tot,
+        (unsigned long long)dbg_w_tot,
+        dbg_under, dbg_dwfull, dbg_shortw,
+        fftrate_in_rem, fftrate_out_rem);
+}
+
+/* map an AF_FORMAT_* to (tag, bits); returns 0 if unsupported by us */
+static int fftrate_afmap(int af, uint16_t *tag, int *bits)
+{
+    switch (af) {
+    case AF_FORMAT_U8:       *tag = FFTRATE_WFMT_PCM;   *bits = 8;  return 1;
+    case AF_FORMAT_S16_LE:
+    case AF_FORMAT_S16_BE:   *tag = FFTRATE_WFMT_PCM;   *bits = 16; return 1;
+    case AF_FORMAT_S24_LE:
+    case AF_FORMAT_S24_BE:   *tag = FFTRATE_WFMT_PCM;   *bits = 24; return 1;
+    case AF_FORMAT_S32_LE:
+    case AF_FORMAT_S32_BE:   *tag = FFTRATE_WFMT_PCM;   *bits = 32; return 1;
+    case AF_FORMAT_FLOAT_NE: *tag = FFTRATE_WFMT_FLOAT; *bits = 32; return 1;
+    case AF_FORMAT_DOUBLE_NE:*tag = FFTRATE_WFMT_FLOAT; *bits = 64; return 1;
+    }
+    return 0;
+}
+
+/* FFmpeg canonical (MS/WAV) channel masks by channel count.
+ * fftrate's mmsys_make_chmask() guesses different layouts for 3/7/8ch
+ * (3ch -> FL,FR,LFE "2.1"), so the mask must be declared explicitly;
+ * order is already SPORDER_MS == FFmpeg canonical, no reorder needed. */
+#define FFTRATE_SPK_FL   0x001   /* SPEAKER_FRONT_LEFT           */
+#define FFTRATE_SPK_FR   0x002   /* SPEAKER_FRONT_RIGHT          */
+#define FFTRATE_SPK_FC   0x004   /* SPEAKER_FRONT_CENTER         */
+#define FFTRATE_SPK_LFE  0x008   /* SPEAKER_LOW_FREQUENCY        */
+#define FFTRATE_SPK_BL   0x010   /* SPEAKER_BACK_LEFT            */
+#define FFTRATE_SPK_BR   0x020   /* SPEAKER_BACK_RIGHT           */
+#define FFTRATE_SPK_FLC  0x040   /* SPEAKER_FRONT_LEFT_OF_CENTER */
+#define FFTRATE_SPK_FRC  0x080   /* SPEAKER_FRONT_RIGHT_OF_CENTER*/
+#define FFTRATE_SPK_BC   0x100   /* SPEAKER_BACK_CENTER          */
+#define FFTRATE_SPK_SL   0x200   /* SPEAKER_SIDE_LEFT            */
+#define FFTRATE_SPK_SR   0x400   /* SPEAKER_SIDE_RIGHT           */
+
+static uint32_t fftrate_ffmpeg_chmask(int channels)
+{
+    switch (channels) {
+    case 1: return FFTRATE_SPK_FC;
+    case 2: return FFTRATE_SPK_FL | FFTRATE_SPK_FR;
+    case 3: return FFTRATE_SPK_FL | FFTRATE_SPK_FR | FFTRATE_SPK_FC;
+    case 4: return FFTRATE_SPK_FL | FFTRATE_SPK_FR |
+                  FFTRATE_SPK_BL | FFTRATE_SPK_BR;
+    case 5: return FFTRATE_SPK_FL | FFTRATE_SPK_FR | FFTRATE_SPK_FC |
+                  FFTRATE_SPK_BL | FFTRATE_SPK_BR;
+    case 6: return FFTRATE_SPK_FL | FFTRATE_SPK_FR | FFTRATE_SPK_FC |
+                  FFTRATE_SPK_LFE | FFTRATE_SPK_BL | FFTRATE_SPK_BR;
+    case 7: return FFTRATE_SPK_FL | FFTRATE_SPK_FR | FFTRATE_SPK_FC |
+                  FFTRATE_SPK_LFE | FFTRATE_SPK_BL | FFTRATE_SPK_BR |
+                  FFTRATE_SPK_BC;
+    case 8: return FFTRATE_SPK_FL | FFTRATE_SPK_FR | FFTRATE_SPK_FC |
+                  FFTRATE_SPK_LFE | FFTRATE_SPK_BL | FFTRATE_SPK_BR |
+                  FFTRATE_SPK_SL | FFTRATE_SPK_SR;
+    }
+    return 0;   /* engine falls back to mmsys_make_chmask() */
+}
+
+/* Drain thread: the KAI callback analog. Consumes input staging
+ * through convert_processing and pushes converted bytes to /dev/dsp
+ * with a blocking write() -- the kernel's own pacing does the
+ * throttling, exactly like kai_audio_callback's pull. */
+static void *fftrate_drain(void *arg)
+{
+    fftrate_started = 1;
+    while (!fftrate_quit) {
+        int w, written;
+
+        if (fftrate_pause) {
+            usleep(1000);
+            continue;
+        }
+
+        /* consume staged input through the converter */
+        pthread_mutex_lock(&fftrate_lock);
+        if (fftrate_conv && fftrate_in_rem) {
+            uint32_t in_size  = fftrate_in_rem;
+            uint32_t out_size = fftrate_out_size - fftrate_out_rem;
+            if (out_size) {
+                p_convert_processing(fftrate_conv,
+                                     fftrate_in_buf, &in_size,
+                                     fftrate_out_buf + fftrate_out_rem,
+                                     &out_size);
+                fftrate_in_rem -= in_size;
+                if (fftrate_in_rem && in_size)
+                    memmove(fftrate_in_buf,
+                            fftrate_in_buf + in_size,
+                            fftrate_in_rem);
+                fftrate_out_rem += out_size;
+            }
+        }
+
+        /* take output for a blocking write, unlock while writing */
+        w = (int)fftrate_out_rem;
+        if (w > fftrate_drain_chunk)
+            w = fftrate_drain_chunk;       /* one 16KB fragment, not all staging */
+        w -= w % dev_frame_bytes;
+        pthread_mutex_unlock(&fftrate_lock);
+
+        if (w > 0) {
+            written = write(audio_fd, fftrate_out_buf, w);
+            pthread_mutex_lock(&fftrate_lock);
+            if (written > 0) {
+                dbg_w_tot += written;
+                fftrate_out_rem -= written;
+                if (fftrate_out_rem)
+                    memmove(fftrate_out_buf,
+                            fftrate_out_buf + written,
+                            fftrate_out_rem);
+            }
+            pthread_mutex_unlock(&fftrate_lock);
+            if (written <= 0 && !fftrate_quit)
+                usleep(1000);
+            continue;
+        }
+
+        /* nothing staged: wait briefly, play() refills */
+        usleep(500);
+    }
+    fftrate_started = 0;
+    return NULL;
+}
+
+static int fftrate_thread_start(void)
+{
+    fftrate_drain_chunk = ao_data.outburst;   /* still device bytes here */
+    fftrate_quit   = 0;
+    fftrate_pause  = 0;
+    fftrate_started = 0;
+    if (pthread_create(&fftrate_thread, NULL, fftrate_drain, NULL) != 0) {
+        mp_msg(MSGT_AO, MSGL_WARN,
+               "audio_setup: fftrate drain thread failed, direct path\n");
+        return 0;
+    }
+    fftrate_thread_created = 1;
+    return 1;
+}
+
+static void fftrate_thread_stop(void)
+{
+    /* push remaining staged output to the kernel before quitting --
+     * one bounded wait, then quit as before */
+    int guard = 200;                     /* ~200ms max */
+    while (guard-- > 0) {
+        int rem;
+        pthread_mutex_lock(&fftrate_lock);
+        rem = (int)fftrate_out_rem;
+        pthread_mutex_unlock(&fftrate_lock);
+        if (!rem || !fftrate_thread_created)
+            break;
+        usleep(1000);
+    }
+    fftrate_quit = 1;
+    if (fftrate_thread_created) {
+        pthread_join(fftrate_thread, NULL);   /* waits out a blocked write() */
+        fftrate_thread_created = 0;
+    }
+    fftrate_started = 0;
+}
+
+/* Drop ALL converter state: handle, SRC delay line, staged bytes.
+ * Required between tracks: statics otherwise leak old-format samples
+ * into the first output block of the next init() -> click/burst. */
+static void fftrate_teardown(void)
+{
+    fftrate_thread_stop();          /* joins drain thread; idempotent */
+
+    if (fftrate_conv) {
+        fftrate_conv = p_convert_close(fftrate_conv);
+        if (fftrate_conv) {          /* close didn't NULL it */
+            p_convert_close(fftrate_conv);
+            fftrate_conv = NULL;
+        }
+    }
+
+    fftrate_in_rem  = 0;
+    fftrate_out_rem = 0;
+}
+
+static void fftrate_free(void)
+{
+    fftrate_thread_stop();
+    if (fftrate_conv && dbg_on_flag())
+        dbg_summary();
+    if (fftrate_conv && p_convert_close)
+        fftrate_conv = p_convert_close(fftrate_conv);
+    fftrate_conv = NULL;
+    free(fftrate_in_buf);  fftrate_in_buf  = NULL;
+    free(fftrate_out_buf); fftrate_out_buf = NULL;
+    fftrate_in_rem = fftrate_out_rem = 0;
+}
+
+/*
+ * Prime the converter with silence: feed zeroed input until the engine
+ * emits its first packet, and KEEP that output as staged backlog.
+ *
+ * Why (cubeb oss backend, proven working): resampling_process()
+ * produces nothing until p_sample_in->rem >= packet_len and
+ * p_sample_out->rem == 0 (convertlib.c), so without priming the first
+ * play() calls write nothing to the OSS ring while input packets
+ * accumulate -- with a 64KB ring draining in ~43ms at s32le/192k and
+ * play() called once per video frame, that gap is the periodic
+ * play_underrun = the "horrible noise".
+ *
+ * Cubeb discards the primed output; we keep it: it is silence
+ * (fade-in of zeros), and draining it on the first play() call tops
+ * up the kernel buffer ahead of real audio. Same trick as plugin.c's
+ * !inst->started fill block.
+ */
+static void fftrate_prime(void)
+{
+    uint32_t feed = fftrate_in_size;      /* silence budget, input bytes */
+    int guard = 64;                       /* convert loop bound */
+
+    if (!fftrate_conv)
+        return;
+
+    /* feed zeroed input in whole frames until the engine emits output */
+    while (feed && !fftrate_out_rem && guard--) {
+        uint32_t in_size = fftrate_in_rem;
+        uint32_t out_size;
+
+        /* stage a chunk of silence into the (already zeroed) in_buf */
+        {
+            uint32_t room = fftrate_in_size - fftrate_in_rem;
+            uint32_t n = feed < room ? feed : room;
+            n -= n % (uint32_t)in_frame_bytes;
+            if (!n)
+                break;
+            fftrate_in_rem += n;
+            feed -= n;
+        }
+
+        in_size  = fftrate_in_rem;
+        out_size = fftrate_out_size - fftrate_out_rem;
+        p_convert_processing(fftrate_conv,
+                             fftrate_in_buf, &in_size,
+                             fftrate_out_buf + fftrate_out_rem,
+                             &out_size);
+        fftrate_in_rem -= in_size;
+        if (fftrate_in_rem && in_size)
+            memmove(fftrate_in_buf, fftrate_in_buf + in_size,
+                    fftrate_in_rem);
+        fftrate_out_rem += out_size;
+    }
+
+    /* drain any input still staged -- engine holds it internally now */
+    fftrate_in_rem = 0;
+
+    if (dbg_on_flag())
+        fprintf(stderr, "[FFTRATE] primed: out_rem=%u\n",
+                fftrate_out_rem);
+}
+
+/*
+ * Set up the converter for src_format@src_rate -> ao_data.format@dev_rate.
+ * Called after the OSS device format/rate have been negotiated.
+ * Returns 1 on success (fftrate active), 0 -> caller uses direct path.
+ */
+static int fftrate_setup(int src_format, int src_rate, int channels)
+{
+    fftrate_wfmt_t *in_fmt = NULL, *out_fmt = NULL;
+    uint16_t in_tag, out_tag;
+    int in_bits, out_bits;
+
+    fftrate_free();
+
+    if (!fftrate_load())
+        return 0;
+
+    if (src_format == dev_format && src_rate == dev_rate &&
+        channels <= FFTRATE_OUT_CHANNELS)
+        return 0;                       /* nothing to convert */
+
+    if (!fftrate_afmap(src_format, &in_tag, &in_bits) ||
+        !fftrate_afmap(dev_format, &out_tag, &out_bits)) {
+        mp_msg(MSGT_AO, MSGL_ERR,
+               "audio_setup: fftrate: unmapped format %s(0x%x) -> %s(0x%x)\n",
+               af_fmt2str_short(src_format), src_format,
+               af_fmt2str_short(dev_format), dev_format);
+        return 0;                       /* unsupported format pair */
+    }
+
+    in_fmt  = p_mmsys_format_pcm_fill(in_tag,  channels, src_rate,
+                                      in_bits, fftrate_ffmpeg_chmask(channels));
+    out_fmt = p_mmsys_format_pcm_fill(out_tag, FFTRATE_OUT_CHANNELS, dev_rate,
+                                      out_bits,
+                                      fftrate_ffmpeg_chmask(FFTRATE_OUT_CHANNELS));
+    if (!in_fmt || !out_fmt) {
+        mp_msg(MSGT_AO, MSGL_ERR,
+               "audio_setup: fftrate: mmsys_format_pcm_fill failed\n");
+        if (in_fmt)  p_mmsys_format_free(in_fmt);
+        if (out_fmt) p_mmsys_format_free(out_fmt);
+        return 0;
+    }
+
+    /* "tws": transform/window/sync selectors; sync off, like plugin.c */
+    fftrate_conv = p_convert_open(in_fmt, out_fmt, "tws",
+                                  FFTRATE_TRANSFORM_FFT,
+                                  FFTRATE_WINDOW_VORBIS, 0);
+    p_mmsys_format_free(in_fmt);
+    p_mmsys_format_free(out_fmt);
+    if (!fftrate_conv) {
+        mp_msg(MSGT_AO, MSGL_ERR,
+               "audio_setup: fftrate: convert_open %s %dHz/%dch -> %s %dHz/%dch failed\n",
+               af_fmt2str_short(src_format), src_rate, channels,
+               af_fmt2str_short(dev_format), dev_rate, FFTRATE_OUT_CHANNELS);
+        return 0;
+    }
+
+    /* Fresh handle: clear resampler history + skip state before any
+     * convert_processing -- plugin.c (pcm_rate_reset) and foo_dsp_fftrate
+     * both do this right after convert_open; without it the engine's
+     * delay lines emit a transient on the first packet = startup click. */
+    p_convert_reset(fftrate_conv);
+
+    in_rate         = src_rate;
+    in_frame_bytes  = channels * (in_bits  / 8);
+    dev_frame_bytes = FFTRATE_OUT_CHANNELS * (out_bits / 8);
+
+    /* Staging must hold several engine packets. For awkward rate
+     * ratios (48000->44100 = 160/147) resampling_init() doubles the
+     * reduced rates until they clear the transform tail and the
+     * 64 ms delay, so packet_len can reach tens of thousands of
+     * frames -- 8*outburst then starves the device between bursts.
+     * 1 MB covers ~5.7 s of 48k stereo floatle input and ~1.4 s of
+     * 192k stereo s32le output -- several packets for any ratio. */
+    fftrate_in_size  = 1024 * 1024;
+    fftrate_out_size = 1024 * 1024;
+
+    fftrate_in_buf   = malloc(fftrate_in_size);
+    fftrate_out_buf  = malloc(fftrate_out_size);
+    if (!fftrate_in_buf || !fftrate_out_buf) {
+        fftrate_free();
+        return 0;
+    }
+
+    memset(fftrate_in_buf,  0, fftrate_in_size);   /* silence, not heap garbage */
+    memset(fftrate_out_buf, 0, fftrate_out_size);
+    dbg_calls = dbg_cconv = dbg_under = dbg_dwfull = dbg_shortw = 0;
+    dbg_in_tot = dbg_out_tot = dbg_w_tot = 0;
+    dbg_dumped = 0;
+    if (dbg_on_flag())
+        fprintf(stderr,
+            "[FFTRATE] setup in_size=%u out_size=%u in_frame=%d "
+            "dev_frame=%d in_bps=%d dev_bps=%d\n",
+            fftrate_in_size, fftrate_out_size,
+            in_frame_bytes, dev_frame_bytes,
+            in_rate * in_frame_bytes, dev_frame_bytes * dev_rate);
+
+    mp_msg(MSGT_AO, MSGL_V,
+           "audio_setup: fftrate %s %d Hz -> %s %d Hz (FFT/Vorbis)\n",
+           af_fmt2str_short(src_format), src_rate,
+           af_fmt2str_short(dev_format), dev_rate);
+    /* Log modeled on plugin.c data_setup()/pcm_rate_init() output. */
+    fprintf(stderr,
+        "Input:  %d Hz, %d ch, '%s' (0x%x): dummy = 0, period = %d\n",
+        src_rate, channels,
+        af_fmt2str_short(src_format), src_format,
+        fftrate_in_size / in_frame_bytes);
+    fprintf(stderr,
+        "Output: %d Hz, %d ch, '%s' (0x%x): dummy = 0, period = %d\n",
+        dev_rate, FFTRATE_OUT_CHANNELS,
+        af_fmt2str_short(dev_format), dev_format,
+        fftrate_out_size / dev_frame_bytes);
+    fprintf(stderr,
+        "Rates:  %d --> %d (T: FFT, W: Vorbis)\n",
+        src_rate, dev_rate);
+    return 1;
+}
+
+static int fftrate_active(void)
+{
+    return fftrate_conv != NULL;
+}
+
+static int fftrate_active_or_saved(void)  
+{  
+    return fftrate_active() || src_format_saved != 0;  
+}
+
+static int src_channels_saved  = 0;   /* input channel count for re-setup */
+
+/* input bytes that can be produced from `n` output device bytes */
+static int out_to_in_bytes(int64_t out_bytes)
+{
+    int64_t r = out_bytes * (int64_t)in_rate * in_frame_bytes
+              / ((int64_t)dev_rate * dev_frame_bytes);
+    return (int)(r > INT32_MAX ? INT32_MAX : r);
+}
+
 // to set/get/query special features/parameters
 static int control(int cmd,void *arg){
     switch(cmd){
@@ -218,7 +770,41 @@ static int control(int cmd,void *arg){
 static int init(int rate,int channels,int format,int flags){
   char *mixer_channels [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
   int oss_format;
+  int use_fft;
   char *mdev = mixer_device, *mchan = mixer_channel;
+  struct MPContext *ctx;
+  const sh_audio_t *sh_audio;
+  int src_format, src_rate, src_channels;
+  int tgt_afmt = 0, tgt_ossfmt = 0;
+
+  /* Detect the true INPUT spec from the decoder stream; the
+   * (rate,channels,format) args are mplayer's negotiated request,
+   * which can be the -1 "AO chooses" wildcard. */
+  ctx = mpctx_get_current();
+  
+    /* Stale converter from a previous track must not survive into
+   * fftrate_setup(): its delay line would replay old samples. */
+  if (fftrate_conv)
+      fftrate_teardown();
+  
+  sh_audio = ctx ? ctx->sh_audio : NULL;
+  fprintf(stderr, "[OSS-FFTRATE] input: %d Hz, %d ch, %d-bit ('%s')\n",
+          sh_audio ? sh_audio->samplerate   : rate,  
+          sh_audio ? sh_audio->channels     : channels,  
+          sh_audio ? af_fmt2bits(sh_audio->sample_format) : 0,  
+          sh_audio ? af_fmt2str_short(sh_audio->sample_format) : "??");
+  src_format   = sh_audio ? sh_audio->sample_format
+               : (format == -1 ? AF_FORMAT_S32_NE : format);
+  src_rate     = sh_audio ? sh_audio->samplerate  : rate;
+  src_channels = sh_audio ? sh_audio->channels    : channels;
+
+  src_format_saved   = src_format;
+  src_rate_saved     = src_rate;
+  src_channels_saved = src_channels;
+
+  fprintf(stderr, "[OSS-FFTRATE] input:  %d Hz, %d ch, '%s' (0x%x)\n",
+          src_rate, src_channels,
+          af_fmt2str_short(src_format), src_format);
 
   mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz  %d chans  %s\n",rate,channels,
     af_fmt2str_short(format));
@@ -277,15 +863,34 @@ static int init(int rate,int channels,int format,int flags){
   mp_msg(MSGT_AO,MSGL_V,"audio_setup: using '%s' mixer device\n", mixer_channels[oss_mixer_channel]);
 
 #ifdef __linux__
-  audio_fd=open(dsp, O_WRONLY | O_NONBLOCK);
+  audio_fd=open(dsp, O_WRONLY | O_NONBLOCK | O_EXCL);
 #else
-  audio_fd=open(dsp, O_WRONLY);
+  audio_fd=open(dsp, O_WRONLY | O_EXCL);
 #endif
   if(audio_fd<0){
     mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_OSS_CantOpenDev, dsp, strerror(errno));
     return 0;
   }
 
+  /* Exclusive mode: O_EXCL bypasses vmix redirection; COOKEDMODE=0
+   * disables format/rate conversion. Must be first ioctl after open. */
+#ifdef SNDCTL_DSP_COOKEDMODE
+  {
+    int tmp = 0;
+    ioctl(audio_fd, SNDCTL_DSP_COOKEDMODE, &tmp);  /* errors ignored */
+  }
+#endif
+
+  /* Verify we really got the hardware engine, not a vmix client */
+#if defined(SNDCTL_DSP_GETCAPS) && defined(PCM_CAP_VIRTUAL)
+  {
+    int caps = 0;
+    if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == 0 &&
+        !(caps & PCM_CAP_VIRTUAL))
+      printf("Exclusive mode is enabled\n");
+  }
+#endif
+
 #ifdef __linux__
   /* Remove the non-blocking flag */
   if(fcntl(audio_fd, F_SETFL, 0) < 0) {
@@ -298,69 +903,94 @@ static int init(int rate,int channels,int format,int flags){
   fcntl(audio_fd, F_SETFD, FD_CLOEXEC);
 #endif
 
+  /* Evaluate dlopen once: lib_count is cached, so every later test in
+   * this init() sees a fixed value */
+  use_fft = fftrate_load();
+
+  if (use_fft && !oss_target_formats(&tgt_afmt, &tgt_ossfmt)) {
+      mp_msg(MSGT_AO,MSGL_ERR,
+             "audio_setup: oss_target_bitdepth=%d unsupported (16 or 32)\n",
+             ao_oss_target_bitdepth);
+      return 0;
+  }
+
   if(AF_FORMAT_IS_AC3(format)) {
     ao_data.samplerate=rate;
     if (ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate) == -1)
       mp_msg(MSGT_AO,MSGL_WARN, "OSS: Failed setting AC3 sample-rate %i %s\n", rate, strerror(errno));
   }
 
-ac3_retry:
   if (AF_FORMAT_IS_AC3(format))
     format = AF_FORMAT_AC3_NE;
-  ao_data.format=format;
-  oss_format=format2oss(format);
+  oss_format = AF_FORMAT_IS_AC3(format) ? format2oss(format)
+             : use_fft ? tgt_ossfmt
+             : format2oss(format);
   if (oss_format == -1) {
-#if HAVE_BIGENDIAN
-    oss_format=AFMT_S16_BE;
-#else
-    oss_format=AFMT_S16_LE;
-#endif
-    format=AF_FORMAT_S16_NE;
+    mp_msg(MSGT_AO,MSGL_ERR,
+           "audio_setup: %s: no OSS format for %s(0x%x)\n",
+           dsp, af_fmt2str_short(format), format);
+    return 0;
   }
   if( ioctl(audio_fd, SNDCTL_DSP_SETFMT, &oss_format)<0 ||
-      oss_format != format2oss(format)) {
-    mp_msg(MSGT_AO,MSGL_WARN, MSGTR_AO_OSS_CantSet, dsp,
-            af_fmt2str_short(format), af_fmt2str_short(AF_FORMAT_S16_NE) );
-    format=AF_FORMAT_S16_NE;
-    goto ac3_retry;
+      oss_format != (use_fft ? tgt_ossfmt
+                                             : format2oss(format))) {
+    mp_msg(MSGT_AO,MSGL_ERR,
+           "audio_setup: %s: SNDCTL_DSP_SETFMT %s(0x%x) failed%s\n",
+           dsp, af_fmt2str_short(format), format,
+           errno ? "" : "");
+    return 0;
   }
-#if 0
-  if(oss_format!=format2oss(format))
-	mp_msg(MSGT_AO,MSGL_WARN,"WARNING! Your soundcard does NOT support %s sample format! Broken audio or bad playback speed are possible! Try with '-af format'\n",audio_out_format_name(format));
-#endif
 
-  ao_data.format = oss2format(oss_format);
-  if (ao_data.format == -1) return 0;
+  /* The device-side libaf format: the fftrate target when converting,
+   * else the negotiated stream format. fftrate_setup() and reset()
+   * both consume dev_format -- leaving it 0 makes the converter see
+   * "unmapped ... -> ??(0x0)" and the direct path writes the source
+   * format into a device configured for the target = noise. */
+  dev_format = use_fft ? tgt_afmt : format;
 
+  ao_data.samplerate = use_fft ? ao_oss_target_rate : rate;
+  ao_data.format = src_format;
   mp_msg(MSGT_AO,MSGL_V,"audio_setup: sample format: %s (requested: %s)\n",
-    af_fmt2str_short(ao_data.format), af_fmt2str_short(format));
-
+    af_fmt2str_short(dev_format), af_fmt2str_short(format));
+  /* device format already forced to s32 above when fftrate loaded */
   ao_data.channels = channels;
   if(!AF_FORMAT_IS_AC3(format)) {
+
+    /* With fftrate loaded the device always runs stereo -- the engine's
+     * channel matrix does N->2; ao_data.channels keeps the SOURCE count
+     * for byte accounting (bps, in_frame_bytes). */
+    dev_channels = use_fft ? FFTRATE_OUT_CHANNELS : channels;
     // We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
-    if (ao_data.channels > 2) {
-      if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels) == -1 ||
-	   ao_data.channels != channels ) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_OSS_CantSetChans, channels);
-	return 0;
+    if (dev_channels > 2) {
+      if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &dev_channels) == -1 ||
+      dev_channels != (use_fft ? FFTRATE_OUT_CHANNELS : channels) ) {
+          mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_OSS_CantSetChans, dev_channels);
+          return 0;
       }
     }
     else {
-      int c = ao_data.channels-1;
+      int c = dev_channels-1;
       if (ioctl (audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
-	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_OSS_CantSetChans, ao_data.channels);
+	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_OSS_CantSetChans, dev_channels);
 	return 0;
       }
-      ao_data.channels=c+1;
+      dev_channels=c+1;
     }
-    mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels, channels);
+    mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", dev_channels, channels);
+
     // set rate
-    ao_data.samplerate=rate;
+    /* With fftrate loaded, always request the fixed target rate -- the
+     * converter resamples src_rate -> dev_rate deterministically.
+     * The kernel clamps if the device can't do ao_oss_target_rate;
+     * dev_rate below records whatever was actually granted. */
+    ao_data.samplerate = use_fft ? ao_oss_target_rate : rate;
     if (ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate) == -1)
       mp_msg(MSGT_AO,MSGL_WARN, "OSS: Failed setting sample-rate %i %s\n", rate, strerror(errno));
     mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d Hz samplerate (requested: %d)\n",ao_data.samplerate,rate);
   }
 
+  dev_rate = ao_data.samplerate;
+
   if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)==-1){
       int r=0;
       mp_msg(MSGT_AO,MSGL_WARN,MSGTR_AO_OSS_CantUseGetospace);
@@ -377,6 +1007,9 @@ ac3_retry:
       ao_data.outburst=zz.fragsize;
   }
 
+  if (ao_data.buffersize > OSS_DMA_BUFSIZE)
+      ao_data.buffersize = OSS_DMA_BUFSIZE;
+
   if(ao_data.buffersize==-1){
     // Measuring buffer size:
     void* data;
@@ -400,8 +1033,65 @@ ac3_retry:
 #endif
   }
 
+  /* Try fftrate conversion for formats/rates the device can't do natively.
+   * On failure the device was forced to s32/192k, which no longer matches
+   * the stream: re-negotiate the DEVICE at source params instead of
+   * silently writing unconverted data (that mismatch is the noise). */
+  if (fftrate_setup(src_format, src_rate, src_channels)) {
+      src_format_saved = src_format;
+      src_rate_saved   = src_rate;
+      src_channels_saved = src_channels;
+      /* Prime with silence -- absorbs packet latency/fade-in and leaves
+       * fftrate_out_rem backlog so the first play() tops up the ring. */
+      fftrate_prime();
+      /* spawn the drainer LAST: buffers + primed counters committed,
+       * no lock needed in prime() since the thread doesn't exist yet */
+      if (!fftrate_thread_start()) {
+          fftrate_free();
+          src_format_saved = src_rate_saved = 0;
+          goto fftrate_done;
+      }
+      /* Announce the pipeline in SOURCE units: libaf sees that the AO
+       * accepts the decoder's own format/rate and passes audio through
+       * untouched -- play()/fftrate does the conversion to dev_format.
+       * Without this, ao_data stays in device units and [libaf] hunts
+       * for a converter filter. */
+      ao_data.format     = src_format;
+      ao_data.samplerate = src_rate;
+      ao_data.channels   = src_channels;
+  } else {
+      /* converter unavailable: reopen device params at SOURCE spec so
+       * the direct write() path at least matches what the stream sends */
+      int oss_f = format2oss(src_format);
+      int dch   = src_channels;
+      int drate = src_rate;
+      if (oss_f != -1 &&
+          ioctl(audio_fd, SNDCTL_DSP_SETFMT, &oss_f) != -1 &&
+          oss_f == format2oss(src_format)) {
+          ao_data.format = src_format;   /* direct path: device re-negotiated to SOURCE spec */
+          if (dch > 2)
+              ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &dch);
+          else {
+              int c = dch - 1;
+              if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &c) != -1)
+                  dch = c + 1;
+          }
+          ao_data.channels = dch;
+          dev_channels = dch;
+          ioctl(audio_fd, SNDCTL_DSP_SPEED, &drate);
+          ao_data.samplerate = drate;
+          dev_rate   = drate;
+          dev_format = ao_data.format;
+      }
+      src_format_saved = 0;
+      src_rate_saved   = 0;
+      src_channels_saved = 0;
+  }
+fftrate_done:
+
   ao_data.bps=ao_data.channels;
-  switch (ao_data.format & AF_FORMAT_BITS_MASK) {
+  switch ((fftrate_active() ? src_format : ao_data.format)
+          & AF_FORMAT_BITS_MASK) {
   case AF_FORMAT_8BIT:
     break;
   case AF_FORMAT_16BIT:
@@ -413,28 +1103,52 @@ ac3_retry:
   case AF_FORMAT_32BIT:
     ao_data.bps*=4;
     break;
+  case AF_FORMAT_64BIT:
+    ao_data.bps*=8;
+    break;
   }
 
-  ao_data.outburst-=ao_data.outburst % ao_data.bps; // round down
-  ao_data.bps*=ao_data.samplerate;
+  if (fftrate_active()) {
+      /* outburst is in DEVICE bytes; slice input separately */
+      dev_bps      = dev_frame_bytes * dev_rate;
+      in_outburst  = out_to_in_bytes(ao_data.outburst);
+      if (in_outburst < in_frame_bytes)
+          in_outburst = in_frame_bytes;
+      in_outburst -= in_outburst % in_frame_bytes;
+      /* get_space() reports INPUT staging bytes; the stuck-detector
+       * threshold and sleep_time must be in the same units. */
+      ao_data.outburst = in_outburst;
+  } else {
+      ao_data.outburst -= ao_data.outburst % ao_data.bps;
+      dev_bps = ao_data.bps * ao_data.samplerate;
+  }
+  ao_data.bps *= (fftrate_active() ? in_rate : ao_data.samplerate);
 
     return 1;
 }
 
 // close audio device
 static void uninit(int immed){
+    int had_fftrate;
     if(audio_fd == -1) return;
+    had_fftrate = fftrate_conv || fftrate_thread_created;
+    if (had_fftrate)
+        fftrate_teardown();
 #ifdef SNDCTL_DSP_SYNC
-    // to get the buffer played
+    /* Drain the kernel buffer only on the graceful (!immed) path.
+     * On immed (seek/track switch) the drain thread already flushed
+     * staged output in fftrate_thread_stop(); a second SYNC here replays
+     * up to ~43ms of the OLD track after the switch -> the click/burst. */
     if (!immed)
-	ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL);
+        ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL);
 #endif
 #ifdef SNDCTL_DSP_RESET
     if (immed)
-	ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
+        ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
 #endif
     close(audio_fd);
     audio_fd = -1;
+    fftrate_free();
 }
 
 // stop playing and empty buffers (for seeking/pause)
@@ -442,37 +1156,72 @@ static void reset(void){
   int fail = 0;
   int oss_format;
     uninit(1);
-    audio_fd=open(dsp, O_WRONLY);
+    audio_fd=open(dsp, O_WRONLY | O_EXCL);
     if(audio_fd < 0){
 	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_OSS_CantReopen, strerror(errno));
 	return;
     }
 
+  /* Exclusive mode: O_EXCL bypasses vmix redirection; COOKEDMODE=0
+   * disables format/rate conversion. Must be first ioctl after open. */
+#ifdef SNDCTL_DSP_COOKEDMODE
+  {
+    int tmp = 0;
+    ioctl(audio_fd, SNDCTL_DSP_COOKEDMODE, &tmp);  /* errors ignored */
+  }
+#endif
+
+  /* Verify we really got the hardware engine, not a vmix client */
+#if defined(SNDCTL_DSP_GETCAPS) && defined(PCM_CAP_VIRTUAL)
+  {
+    int caps = 0;
+    if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == 0 &&
+        !(caps & PCM_CAP_VIRTUAL))
+      printf("Exclusive mode is enabled\n");
+  }
+#endif
+
 #if defined(FD_CLOEXEC) && defined(F_SETFD)
   fcntl(audio_fd, F_SETFD, FD_CLOEXEC);
 #endif
 
-  oss_format = format2oss(ao_data.format);
+  oss_format = format2oss(fftrate_active_or_saved() ? dev_format : ao_data.format);
   if(AF_FORMAT_IS_AC3(ao_data.format))
-    fail |= ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate) == -1;
+    fail |= ioctl (audio_fd, SNDCTL_DSP_SPEED, &dev_rate) == -1;
   fail |= ioctl (audio_fd, SNDCTL_DSP_SETFMT, &oss_format) == -1;
   if(!AF_FORMAT_IS_AC3(ao_data.format)) {
-    if (ao_data.channels > 2)
-      fail |= ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels) == -1;
+    if (dev_channels > 2)
+      fail |= ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &dev_channels) == -1;
     else {
-      int c = ao_data.channels-1;
+      int c = dev_channels-1;
       fail |= ioctl (audio_fd, SNDCTL_DSP_STEREO, &c) == -1;
     }
-    fail |= ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate) == -1;
+    fail |= ioctl (audio_fd, SNDCTL_DSP_SPEED, &dev_rate) == -1;
   }
   if (fail)
     mp_msg(MSGT_AO,MSGL_WARN, "OSS: Reset failed\n");
+
+  /* uninit() already freed the handle -- re-open fresh (new converter,
+   * zeroed staging, fresh prime). No convert_reset on a new handle. */
+  if (src_format_saved &&
+      fftrate_setup(src_format_saved, src_rate_saved, src_channels_saved)) {
+      fftrate_prime();
+      fftrate_thread_start();
+  }
+  /* restore the source-unit announcement that init() made */
+  if (fftrate_active() && src_format_saved) {
+      ao_data.format     = src_format_saved;
+      ao_data.samplerate = src_rate_saved;
+      ao_data.channels   = src_channels_saved;
+  }
 }
 
 // stop playing, keep buffers (for pause)
 static void audio_pause(void)
 {
     prepause_space = get_space();
+    fftrate_pause = 1;
+    fftrate_thread_stop();      /* uninit(1) closes audio_fd */
     uninit(1);
 }
 
@@ -488,12 +1237,26 @@ static void audio_resume(void)
 static int get_space(void){
   int playsize=ao_data.outburst;
 
+  /* fftrate path: report free INPUT staging, the only thing play()
+   * can actually accept -- same as KAI's av_fifo_space(m_audioBuf).
+   * No drain modeling: play() drains out staging and consumes input
+   * on every call regardless of what get_space() said, so honest
+   * free space can't deadlock and can't over-report. */
+  if (fftrate_active()) {
+      int free_in;
+      pthread_mutex_lock(&fftrate_lock);
+      free_in = (int)fftrate_in_size - (int)fftrate_in_rem;
+      pthread_mutex_unlock(&fftrate_lock);
+      free_in -= free_in % in_frame_bytes;   /* whole frames only */
+      return free_in;
+  }
+
 #ifdef SNDCTL_DSP_GETOSPACE
   if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)!=-1){
       // calculate exact buffer space:
       playsize = zz.fragments*zz.fragsize;
       if (playsize > MAX_OUTBURST)
-	playsize = (MAX_OUTBURST / zz.fragsize) * zz.fragsize;
+        playsize = (MAX_OUTBURST / zz.fragsize) * zz.fragsize;
       return playsize;
   }
 #endif
@@ -516,12 +1279,40 @@ static int get_space(void){
 // plays 'len' bytes of 'data'
 // it should round it down to outburst*n
 // return: number of bytes played
+
 static int play(void* data,int len,int flags){
+    if (fftrate_active()) {
+        const unsigned char *src = data;
+        int consumed = 0;
+        dbg_calls++;
+
+        /* KAI model: play() only fills input staging; the drain thread
+         * owns convert + device writes. */
+        int n;
+        pthread_mutex_lock(&fftrate_lock);
+        n = (int)fftrate_in_size - (int)fftrate_in_rem;
+        if (n > len) n = len;
+        n -= n % in_frame_bytes;
+        if (n > 0) {
+            memcpy(fftrate_in_buf + fftrate_in_rem, src, n);
+            fftrate_in_rem += n;
+            consumed = n;
+            dbg_in_tot += n;
+        }
+        pthread_mutex_unlock(&fftrate_lock);
+        if (dbg_on_flag() && dbg_calls % 200 == 0)
+            dbg_summary();
+        return consumed;
+    }
+
     if(len==0)
         return len;
-    if(len>ao_data.outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
-        len/=ao_data.outburst;
-        len*=ao_data.outburst;
+    {
+        int slice = ao_data.outburst;
+        if(len>slice || !(flags & AOPLAY_FINAL_CHUNK)) {
+            len/=slice;
+            len*=slice;
+        }
     }
     len=write(audio_fd,data,len);
     return len;
@@ -531,20 +1322,34 @@ static int audio_delay_method=2;
 
 // return: delay in seconds between first and last sample in buffer
 static float get_delay(void){
+  float staged = 0.0f;
+
+  /* With the converter active, pending audio also sits in our staging
+   * buffers (in units of INPUT bytes / OUTPUT bytes respectively).
+   * Count it -- otherwise mplayer's position jumps once the engine
+   * starts emitting, since kernel-buffer delay alone is ~0. */
+  if (fftrate_active()) {
+      pthread_mutex_lock(&fftrate_lock);
+      staged += (float)fftrate_out_rem / (float)dev_bps;
+      staged += (float)fftrate_in_rem /
+                ((float)in_rate * (float)in_frame_bytes);
+      pthread_mutex_unlock(&fftrate_lock);
+  }
+
   /* Calculate how many bytes/second is sent out */
   if(audio_delay_method==2){
 #ifdef SNDCTL_DSP_GETODELAY
       int r=0;
       if(ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &r)!=-1)
-         return ((float)r)/(float)ao_data.bps;
+         return staged + ((float)r)/(float)dev_bps;
 #endif
       audio_delay_method=1; // fallback if not supported
   }
   if(audio_delay_method==1){
       // SNDCTL_DSP_GETOSPACE
       if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)!=-1)
-         return ((float)(ao_data.buffersize-zz.bytes))/(float)ao_data.bps;
+         return staged + ((float)(ao_data.buffersize-zz.bytes))/(float)dev_bps;
       audio_delay_method=0; // fallback if not supported
   }
-  return ((float)ao_data.buffersize)/(float)ao_data.bps;
+  return staged + ((float)ao_data.buffersize)/(float)dev_bps;
 }
-- 
2.39.5

TEST

$ mplayer -ao alsa test*
MPlayer SVN-r38408-12 (C) 2000-2023 MPlayer Team
Playing test_f64.wav.
libavformat version 59.27.100 (external)
Audio only file format detected.
Load subtitles in ./
==========================================================================
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 192000 Hz, 2 ch, doublele, 24576.0 kbit/100.00% (ratio: 3072000->3072000)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
==========================================================================
[AO_ALSA] input: 192000 Hz, 2 ch, 64-bit ('doublele')
Input:  192000 Hz, 2 ch, 'float64_le' (0x10): dummy = 0, period = 7680
Output: 192000 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 7680
Period ratio: 1.000000
Rates:  192000 --> 192000 (J: 0.00%, T: None, W: Planar)
Ok.

AO: [alsa] 192000Hz 2ch doublele (8 bytes per sample)
Video: no video
Starting playback...
A:  44.4 (44.4) of 554.0 (09:14.0)  0.4% 
$ mplayer -ao alsa DXD*
MPlayer SVN-r38408-12 (C) 2000-2023 MPlayer Team
Playing DXD_32bitFloat.wav.
libavformat version 59.27.100 (external)
libavformat file format detected.
[wav @ 0x7fae1388ace0]Discarding ID3 tags because more suitable tags were found.
[lavf] stream 0: audio (pcm_f32le), -aid 0
Load subtitles in ./
==========================================================================
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 352800 Hz, 2 ch, floatle, 22579.2 kbit/100.00% (ratio: 2822400->2822400)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
==========================================================================
[AO_ALSA] input: 352800 Hz, 2 ch, 32-bit ('floatle')
Input:  352800 Hz, 2 ch, 'float_le' (0xe): dummy = 0, period = 14112
Output: 192000 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 7680
Period ratio: 1.000000
Rates:  352800 --> 192000 (J: 0.00%, T: FFT, W: Vorbis)
Ok.

AO: [alsa] 352800Hz 2ch floatle (4 bytes per sample)
Video: no video
Starting playback...
A:  59.1 (59.1) of 275.9 (04:35.9)  0.9% 
$ mplayer -ao oss test*
MPlayer SVN-r38408-12 (C) 2000-2023 MPlayer Team
Playing test_f64.wav.
libavformat version 59.27.100 (external)
Audio only file format detected.
Load subtitles in ./
==========================================================================
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 192000 Hz, 2 ch, doublele, 24576.0 kbit/100.00% (ratio: 3072000->3072000)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
==========================================================================
[OSS-FFTRATE] input: 192000 Hz, 2 ch, 64-bit ('doublele')
[OSS-FFTRATE] input:  192000 Hz, 2 ch, 'doublele' (0x3d)
Exclusive mode is enabled
Input:  192000 Hz, 2 ch, 'doublele' (0x3d): dummy = 0, period = 65536
Output: 192000 Hz, 2 ch, 's32le' (0x19): dummy = 0, period = 131072
Rates:  192000 --> 192000 (T: FFT, W: Vorbis)
AO: [oss] 192000Hz 2ch doublele (8 bytes per sample)
Video: no video
Starting playback...
A:  44.8 (44.8) of 554.0 (09:14.0)  2.6% 
$ mplayer -ao oss DXD*
MPlayer SVN-r38408-12 (C) 2000-2023 MPlayer Team
Playing DXD_32bitFloat.wav.
libavformat version 59.27.100 (external)
libavformat file format detected.
[wav @ 0x7f700e28ace0]Discarding ID3 tags because more suitable tags were found.
[lavf] stream 0: audio (pcm_f32le), -aid 0
Load subtitles in ./
==========================================================================
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 352800 Hz, 2 ch, floatle, 22579.2 kbit/100.00% (ratio: 2822400->2822400)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
==========================================================================
[OSS-FFTRATE] input: 352800 Hz, 2 ch, 32-bit ('floatle')
[OSS-FFTRATE] input:  352800 Hz, 2 ch, 'floatle' (0x1d)
Exclusive mode is enabled
Input:  352800 Hz, 2 ch, 'floatle' (0x1d): dummy = 0, period = 131072
Output: 192000 Hz, 2 ch, 's32le' (0x19): dummy = 0, period = 131072
Rates:  352800 --> 192000 (T: FFT, W: Vorbis)
AO: [oss] 352800Hz 2ch floatle (4 bytes per sample)
Video: no video
Starting playback...
A:  65.6 (01:05.5) of 275.9 (04:35.9)  0.6% 

Offline

Board footer