You are not logged in.
Pages: 1
Prerequisites
sudo apt-get install build-essential dpkg-dev devscripts quilt fakeroot debhelper pkg-config yasmBuild-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.patchcd 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.debWARNING: 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=6PATCHES 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.5TEST
$ 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
Pages: 1