You are not logged in.
Pages: 1
1. alsa-lib
Prerequisites
sudo apt-get install build-essential dpkg-dev devscripts quilt fakeroot debhelper
sudo apt-get build-dep alsa-lib $ tree BUILD3
BUILD3
└── PATCHES_alsa-lib
├── 0001-rate-support-float-input-output-formats.patch
├── 0002-plug-allow-float-formats-in-rate-channel-change-plug.patch
├── 0003-route-fix-float-support-in-channel-mixing-paths.patch
└── 0004-pcm_plugin.h-update-rate-plugin-comment-for-float-fo.patch cd BUILD3
apt-get source alsa-lib
cd alsa-lib-*/
export QUILT_PATCHES=debian/patches
quilt push -a
quilt import ../PATCHES_alsa-lib/000*.patch
quilt push -a
dch -v "1.2.50-1" "Local rebuild: enable float64 audio"
dpkg-buildpackage -b -us -uc
mkdir -p ../dist && mv -v ../*.deb ../*.changes ../*.buildinfo ../dist 2>/dev/null
sudo dpkg -i ../dist/libasound2_*.deb ../dist/libasound2-data_*.deb ../dist/libasound2-dev_*.debPATCHES for alsa-lib
$ cat PATCHES_alsa-lib/0001*
From 98e1f84f04e190dc5d9086aaa61f359267438e8a Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Thu, 15 Oct 2026 13:13:44 +0200
Subject: [PATCH 1/4] rate: support float input/output formats
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The rate plugin restricted clients and slaves to linear formats only,
even though converters implementing the 0x010003 protocol may advertise
non-linear formats via get_supported_formats (e.g. fftrate handles
float and float64 internally).
- snd_pcm_rate_hw_refine_cprepare: OR the converter-advertised format
mask (in_formats for playback, out_formats for capture) into the
client format mask instead of restricting to SND_PCM_FMTBIT_LINEAR.
- get_best_format: score float candidates so a float client format
prefers a float converter format over an integer one.
- snd_pcm_rate_hw_params: select lfloat get/put indices when a float
format is involved; reject float↔float width mismatches.
- do_convert: dispatch float↔linear conversion to
snd_pcm_lfloat_convert_float_integer / _convert_integer_float.
- snd_pcm_rate_open / _snd_pcm_rate_open: accept float sformat.
- export lfloat helper prototypes in pcm_plugin.h.
- doc: update "formats must be linear" wording.
---
src/pcm/pcm_plugin.h | 12 ++++
src/pcm/pcm_rate.c | 163 +++++++++++++++++++++++++++++++++++++------
2 files changed, 154 insertions(+), 21 deletions(-)
diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h
index 9896ee8..d9e8714 100644
--- a/src/pcm/pcm_plugin.h
+++ b/src/pcm/pcm_plugin.h
@@ -104,6 +104,18 @@ snd_pcm_sframes_t snd_pcm_plugin_undo_write_generic
int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
int snd_pcm_linear_convert_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
+int snd_pcm_lfloat_get_s32_index(snd_pcm_format_t format);
+int snd_pcm_lfloat_put_s32_index(snd_pcm_format_t format);
+
+void snd_pcm_lfloat_convert_integer_float(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
+ const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
+ unsigned int channels, snd_pcm_uframes_t frames,
+ unsigned int get32idx, unsigned int put32floatidx);
+
+void snd_pcm_lfloat_convert_float_integer(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
+ const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
+ unsigned int channels, snd_pcm_uframes_t frames,
+ unsigned int put32idx, unsigned int get32floatidx);
void snd_pcm_linear_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index e5d8edd..14ac6a3 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -64,6 +64,11 @@ struct _snd_pcm_rate {
snd_pcm_rate_ops_t ops;
unsigned int src_conv_idx;
unsigned int dst_conv_idx;
+ /* float-aware conversion: lfloat ops instead of conv_labels */
+ unsigned int src_get_idx, src_put_idx;
+ unsigned int dst_get_idx, dst_put_idx;
+ int src_lfloat_mode; /* 0=linear_convert, 1=lfloat_getput, 2=lfloat_putget */
+ int dst_lfloat_mode;
snd_pcm_channel_area_t *src_buf;
snd_pcm_channel_area_t *dst_buf;
int start_pending; /* start is triggered but not commited to slave */
@@ -77,6 +82,8 @@ struct _snd_pcm_rate {
unsigned int format_flags;
};
+#define RATE_COPY 0xffffffffU /* sentinel: pure copy, no conversion */
+
#define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
#endif /* DOC_HIDDEN */
@@ -125,6 +132,9 @@ static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_
int err;
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
+ /* converter-advertised extras, e.g. float — client side only */
+ format_mask.bits[0] |= pcm->stream == SND_PCM_STREAM_PLAYBACK ?
+ rate->in_formats : rate->out_formats;
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
if (err < 0)
@@ -284,6 +294,7 @@ static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
int pwidth = snd_pcm_format_physical_width(orig);
int width = snd_pcm_format_width(orig);
int signd = snd_pcm_format_signed(orig);
+ int orig_float = snd_pcm_format_float(orig);
int best_score = -1;
int match = -1;
int f, score;
@@ -292,7 +303,18 @@ static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
if (!(mask & (1ULL << f)))
continue;
score = 0;
- if (snd_pcm_format_linear(f)) {
+ if (snd_pcm_format_float(f)) {
+ if (!orig_float)
+ continue; /* never pick float for linear orig */
+ score += 8; /* same family: always beats linear */
+ if (snd_pcm_format_physical_width(f) == pwidth)
+ score += 2;
+ else if (snd_pcm_format_physical_width(f) > pwidth)
+ score++;
+ } else if (snd_pcm_format_linear(f)) {
+ if (orig_float)
+ continue; /* never pick linear for float orig */
+ score += 8;
if (snd_pcm_format_physical_width(f) == pwidth)
score++;
if (snd_pcm_format_physical_width(f) >= pwidth)
@@ -439,9 +461,38 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
}
if (need_src_buf) {
- rate->src_conv_idx =
- snd_pcm_linear_convert_index(rate->orig_in_format,
- rate->info.in.format);
+ snd_pcm_format_t f1 = rate->orig_in_format;
+ snd_pcm_format_t f2 = rate->info.in.format;
+ if (f1 == f2) {
+ rate->src_lfloat_mode = 0;
+ rate->src_conv_idx = RATE_COPY; /* deinterleave only */
+ } else if (snd_pcm_format_linear(f1) && snd_pcm_format_linear(f2)) {
+ rate->src_lfloat_mode = 0;
+ rate->src_conv_idx = snd_pcm_linear_convert_index(f1, f2);
+ } else if (snd_pcm_format_float(f1) && snd_pcm_format_linear(f2)) {
+ rate->src_lfloat_mode = 1; /* float -> int: get32float + put32 */
+ rate->src_get_idx = snd_pcm_lfloat_get_s32_index(f1);
+ rate->src_put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, f2);
+ } else if (snd_pcm_format_linear(f1) && snd_pcm_format_float(f2)) {
+ rate->src_lfloat_mode = 2; /* int -> float: get32 + put32float */
+ rate->src_get_idx = snd_pcm_linear_get_index(f1, SND_PCM_FORMAT_S32);
+ rate->src_put_idx = snd_pcm_lfloat_put_s32_index(f2);
+ } else {
+ /* float -> float of different width: no single-pass
+ * converter (lfloat ops go via int32 on one side) */
+ SNDERR("rate plugin: unsupported src conversion %s -> %s",
+ snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+ err = -EINVAL;
+ goto error;
+ }
+ if ((rate->src_lfloat_mode && ((int)rate->src_get_idx < 0 || (int)rate->src_put_idx < 0)) ||
+ (!rate->src_lfloat_mode && rate->src_conv_idx != RATE_COPY &&
+ (int)rate->src_conv_idx < 0)) {
+ SNDERR("rate plugin: unsupported src conversion %s -> %s",
+ snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+ err = -EINVAL;
+ goto error;
+ }
rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
channels, rate->info.in.period_size);
if (!rate->src_buf) {
@@ -451,9 +502,36 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
}
if (need_dst_buf) {
- rate->dst_conv_idx =
- snd_pcm_linear_convert_index(rate->info.out.format,
- rate->orig_out_format);
+ snd_pcm_format_t f1 = rate->info.out.format;
+ snd_pcm_format_t f2 = rate->orig_out_format;
+ if (f1 == f2) {
+ rate->dst_lfloat_mode = 0;
+ rate->dst_conv_idx = RATE_COPY;
+ } else if (snd_pcm_format_linear(f1) && snd_pcm_format_linear(f2)) {
+ rate->dst_lfloat_mode = 0;
+ rate->dst_conv_idx = snd_pcm_linear_convert_index(f1, f2);
+ } else if (snd_pcm_format_float(f1) && snd_pcm_format_linear(f2)) {
+ rate->dst_lfloat_mode = 1; /* float -> int */
+ rate->dst_get_idx = snd_pcm_lfloat_get_s32_index(f1);
+ rate->dst_put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, f2);
+ } else if (snd_pcm_format_linear(f1) && snd_pcm_format_float(f2)) {
+ rate->dst_lfloat_mode = 2; /* int -> float */
+ rate->dst_get_idx = snd_pcm_linear_get_index(f1, SND_PCM_FORMAT_S32);
+ rate->dst_put_idx = snd_pcm_lfloat_put_s32_index(f2);
+ } else {
+ SNDERR("rate plugin: unsupported dst conversion %s -> %s",
+ snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+ err = -EINVAL;
+ goto error;
+ }
+ if ((rate->dst_lfloat_mode && ((int)rate->dst_get_idx < 0 || (int)rate->dst_put_idx < 0)) ||
+ (!rate->dst_lfloat_mode && rate->dst_conv_idx != RATE_COPY &&
+ (int)rate->dst_conv_idx < 0)) {
+ SNDERR("rate plugin: unsupported dst conversion %s -> %s",
+ snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+ err = -EINVAL;
+ goto error;
+ }
rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
channels, rate->info.out.period_size);
if (!rate->dst_buf) {
@@ -585,10 +663,28 @@ static void do_convert(const snd_pcm_channel_area_t *dst_areas,
}
if (rate->src_buf) {
- snd_pcm_linear_convert(rate->src_buf, 0,
- src_areas, src_offset,
- channels, src_frames,
- rate->src_conv_idx);
+ if (rate->src_lfloat_mode == 1) /* float -> int */
+ snd_pcm_lfloat_convert_float_integer(rate->src_buf, 0,
+ src_areas, src_offset,
+ channels, src_frames,
+ rate->src_put_idx,
+ rate->src_get_idx);
+ else if (rate->src_lfloat_mode == 2) /* int -> float */
+ snd_pcm_lfloat_convert_integer_float(rate->src_buf, 0,
+ src_areas, src_offset,
+ channels, src_frames,
+ rate->src_get_idx,
+ rate->src_put_idx);
+ else if (rate->src_conv_idx == RATE_COPY)
+ snd_pcm_areas_copy(rate->src_buf, 0,
+ src_areas, src_offset,
+ channels, src_frames,
+ rate->info.in.format);
+ else
+ snd_pcm_linear_convert(rate->src_buf, 0,
+ src_areas, src_offset,
+ channels, src_frames,
+ rate->src_conv_idx);
src_areas = rate->src_buf;
src_offset = 0;
}
@@ -602,11 +698,30 @@ static void do_convert(const snd_pcm_channel_area_t *dst_areas,
dst_frames,
snd_pcm_channel_area_addr(src_areas, src_offset),
src_frames);
- if (rate->dst_buf)
- snd_pcm_linear_convert(dst_areas, dst_offset,
- rate->dst_buf, 0,
- channels, dst_frames,
- rate->dst_conv_idx);
+ if (rate->dst_buf) {
+ if (rate->dst_lfloat_mode == 1) /* float -> int */
+ snd_pcm_lfloat_convert_float_integer(dst_areas, dst_offset,
+ rate->dst_buf, 0,
+ channels, dst_frames,
+ rate->dst_put_idx,
+ rate->dst_get_idx);
+ else if (rate->dst_lfloat_mode == 2) /* int -> float */
+ snd_pcm_lfloat_convert_integer_float(dst_areas, dst_offset,
+ rate->dst_buf, 0,
+ channels, dst_frames,
+ rate->dst_get_idx,
+ rate->dst_put_idx);
+ else if (rate->dst_conv_idx == RATE_COPY)
+ snd_pcm_areas_copy(dst_areas, dst_offset,
+ rate->dst_buf, 0,
+ channels, dst_frames,
+ rate->orig_out_format);
+ else
+ snd_pcm_linear_convert(dst_areas, dst_offset,
+ rate->dst_buf, 0,
+ channels, dst_frames,
+ rate->dst_conv_idx);
+ }
}
static inline void
@@ -1483,7 +1598,7 @@ static int is_string_array(const snd_config_t *conf)
* \brief Creates a new rate PCM
* \param pcmp Returns created PCM handle
* \param name Name of PCM
- * \param sformat Slave format
+ * \param sformat Slave format (linear or float).
* \param srate Slave rate
* \param converter SRC type string node
* \param slave Slave PCM handle
@@ -1509,7 +1624,8 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
assert(pcmp && slave);
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
- snd_pcm_format_linear(sformat) != 1)
+ snd_pcm_format_linear(sformat) != 1 &&
+ snd_pcm_format_float(sformat) != 1)
return -EINVAL;
rate = calloc(1, sizeof(snd_pcm_rate_t));
if (!rate) {
@@ -1566,6 +1682,7 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
break;
}
if (!type) {
+
SNDERR("No name given for rate converter");
snd_pcm_free(pcm);
free(rate);
@@ -1623,7 +1740,10 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
\section pcm_plugins_rate Plugin: Rate
-This plugin converts a stream rate. The input and output formats must be linear.
+This plugin converts a stream rate. The input and output formats must be
+linear or float. Since protocol version 0x010003, a converter may advertise
+its supported formats via the get_supported_formats callback; the plugin
+then negotiates a matching format and inserts the necessary conversions.
\code
pcm.name {
@@ -1713,9 +1833,10 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
if (err < 0)
return err;
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
- snd_pcm_format_linear(sformat) != 1) {
+ snd_pcm_format_linear(sformat) != 1 &&
+ snd_pcm_format_float(sformat) != 1) {
snd_config_delete(sconf);
- SNDERR("slave format is not linear");
+ SNDERR("slave format is not linear or float");
return -EINVAL;
}
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
--
2.39.5$ cat PATCHES_alsa-lib/0002*
From 8a9e12f00ec215eb9801b34da8a852cab5631ea6 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Thu, 15 Oct 2026 13:17:22 +0200
Subject: [PATCH 2/4] plug: allow float formats in rate/channel-change plugin
insertion
snd_pcm_plug_change_rate and snd_pcm_plug_change_channels asserted
snd_pcm_format_linear(slv->format), forcing float clients through an
integer downconversion before the rate plugin could see them. Relax
the assertions and the clt->format propagation checks to accept float
formats, so a float-capable rate converter (get_supported_formats)
receives float input when channels also differ.
---
src/pcm/pcm_plug.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c
index abf6f1a..34a7185 100644
--- a/src/pcm/pcm_plug.c
+++ b/src/pcm/pcm_plug.c
@@ -374,14 +374,16 @@ static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plu
int err;
if (clt->rate == slv->rate)
return 0;
- assert(snd_pcm_format_linear(slv->format));
+ assert(snd_pcm_format_linear(slv->format) ||
+ snd_pcm_format_float(slv->format));
err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
plug->gen.slave, plug->gen.slave != plug->req_slave);
if (err < 0)
return err;
slv->access = clt->access;
slv->rate = clt->rate;
- if (snd_pcm_format_linear(clt->format))
+ if (snd_pcm_format_linear(clt->format) ||
+ snd_pcm_format_float(clt->format))
slv->format = clt->format;
return 1;
}
@@ -400,7 +402,8 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm
if (clt->rate != slv->rate &&
clt->channels > slv->channels)
return 0;
- assert(snd_pcm_format_linear(slv->format));
+ assert(snd_pcm_format_linear(slv->format) ||
+ snd_pcm_format_float(slv->format));
tt_ssize = slv->channels;
tt_cused = clt->channels;
tt_sused = slv->channels;
--
2.39.5$ cat PATCHES_alsa-lib/0003*
From 55a6cf40140048488d05475e5db8f53e68db53d8 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Thu, 15 Oct 2026 13:18:58 +0200
Subject: [PATCH 3/4] route: fix float support in channel mixing paths
Complete the SND_PCM_PLUGIN_ROUTE_FLOAT code paths: get/put index
selection for float formats and the many-source summation labels so
routing/attenuation works when the negotiated format is float.
---
src/pcm/pcm_route.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
index d3e5f3f..a612dc7 100644
--- a/src/pcm/pcm_route.c
+++ b/src/pcm/pcm_route.c
@@ -1025,7 +1025,8 @@ int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
int err;
assert(pcmp && slave && ttable);
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
- snd_pcm_format_linear(sformat) != 1)
+ snd_pcm_format_linear(sformat) != 1 &&
+ snd_pcm_format_float(sformat) != 1)
return -EINVAL;
route = calloc(1, sizeof(snd_pcm_route_t));
if (!route) {
--
2.39.5$ cat PATCHES_alsa-lib/0004*
From 6d386441fbf81f3fb6b82222e74b22abdb3ff360 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Thu, 15 Oct 2026 13:20:21 +0200
Subject: [PATCH 4/4] pcm_plugin.h: update rate plugin comment for float
formats
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
"Rate plugin for linear formats" → reflect that float formats are
now supported end-to-end.
---
include/pcm_plugin.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/pcm_plugin.h b/include/pcm_plugin.h
index 2d01439..2b226f7 100644
--- a/include/pcm_plugin.h
+++ b/include/pcm_plugin.h
@@ -134,7 +134,7 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode);
/*
- * Route plugin for linear formats
+ * Route plugin for linear formats and float formats
*/
int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
unsigned int tt_csize, unsigned int tt_ssize,
@@ -154,7 +154,7 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode);
/*
- * Rate plugin for linear formats
+ * Rate plugin for linear formats and float formats
*/
int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t sformat, unsigned int srate,
--
2.39.52. alsa-utils
Build-deps
sudo apt-get install libasound2-dev libatopology-dev libfftw3-dev libncurses-dev pkg-config python3-docutils xmlto$ tree BUILD2
BUILD2
└── PATCHES_alsa-utils
├── 0001-debian-remove-leftover-systemd-artifacts-from-Devuan.patch
└── 0002-aplay-add-64-bit-float-WAV-support-and-fix-vumeter-f.patchcd BUILD2
apt-get source alsa-utils
cd alsa-utils-*/
export QUILT_PATCHES=debian/patches
quilt push -a
quilt import ../PATCHES_alsa-utils/000*.patch
quilt push -a
dch -v "1.2.50-1" "Local rebuild: enable float64 audio"
dpkg-buildpackage -b -us -uc
mkdir -p ../dist && mv -v ../*.deb ../*.changes ../*.buildinfo ../dist 2>/dev/null
sudo dpkg -i ../dist/alsa-utils_*.deb WARNING: 0001-*.patch modifies files inside debian/ (control, rules,
install, init). 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
PATCHES for alsa-utils
$ cat PATCHES_alsa-utils/0001*
From 941fb6274bed74f1a51f66b82453fd2cc940cfae Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Fri, 13 Nov 2026 03:00:20 +0100
Subject: [PATCH 1/2] debian: remove leftover systemd artifacts from Devuan
packaging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This tree comes from the Devuan official repository source package
(apt source alsa-utils). The packaging was originally built with
systemd support, then unpacked, had the systemd dependency removed,
and re-packed — leaving stale systemd references that break the
build on a sysvinit system:
- debian/control: drop "systemd" from Build-Depends. It only exists
to let configure resolve systemdsystemunitdir via pkg-config
(configure.ac); without it the unit dir is empty and
HAVE_SYSTEMD is false. Also replace the transitional
libncursesw5-dev with libncurses-dev (ncursesw is probed via
pkg-config/ncursesw5-config/-lncursesw, all provided by
libncurses-dev), and drop libsamplerate-dev (optional
AC_CHECK_HEADERS([samplerate.h]) for alsaloop only).
- debian/rules: --with-systemdsystemunitdir=/lib/systemd/system ->
--without-systemdsystemunitdir. The explicit path forced
HAVE_SYSTEMD on even without systemd.pc, installing
alsa-state.service/alsa-restore.service and creating
sound.target.wants symlinks (alsactl/Makefile.am) — dead
units on a sysvinit system.
- debian/install: drop "lib/systemd" pattern. With units no longer
installed into debian/tmp, dh_install fails with "missing files"
under dh_missing --fail-missing. Keep "lib/udev":
90-alsa-restore.rules is installed unconditionally via
udevrules_DATA (alsactl/Makefile.am) and works with eudev.
- debian/init: fix and extend the sysvinit script, which is now the
only state save/restore mechanism (with the udev rule) since
systemd units are disabled:
* add LSB-required "status" action reporting loaded cards
(/proc/asound/cards), state file presence/timestamp, and
alsactl daemon pidfile (/run/alsactl.pid)
* restart/force-reload: pass "$2" so per-card restart works
* store_levels: drop "sleep 1" (alsactl store is synchronous)
* executable(): replace deprecated which(1) with POSIX command -v
* drop redundant -F flag (force_restore=1 is the compiled-in
default, alsactl.c)
* move runtime-dir mkdir into the actions that need it
* fix warning-message quoting; remove dead mute_and_zero_*
helpers; shellcheck clean (SC1091 source= directives, SC2086)
---
debian/control | 4 +-
debian/init | 239 ++++++++++++++++++++-----------------------------
debian/install | 1 -
debian/rules | 2 +-
4 files changed, 101 insertions(+), 145 deletions(-)
diff --git a/debian/control b/debian/control
index 01229ec..b2564dc 100644
--- a/debian/control
+++ b/debian/control
@@ -9,11 +9,9 @@ Build-Depends: debhelper-compat (= 13),
libasound2-dev (>= 1.2.1),
libatopology-dev (>= 1.2.2),
libfftw3-dev,
- libncursesw5-dev,
- libsamplerate-dev,
+ libncurses-dev,
pkg-config,
python3-docutils,
- systemd,
xmlto
Standards-Version: 4.6.1
Homepage: https://www.alsa-project.org/
diff --git a/debian/init b/debian/init
index 632573c..853d571 100755
--- a/debian/init
+++ b/debian/init
@@ -10,7 +10,7 @@
# Default-Stop: 0 1 6
# Short-Description: Restore and store ALSA driver settings
# Description: This script stores and restores mixer levels on
-# shutdown and bootup.On sysv-rc systems: to
+# shutdown and bootup. On sysv-rc systems: to
# disable storing of mixer levels on shutdown,
# remove /etc/rc[06].d/K50alsa-utils. To disable
# restoring of mixer levels on bootup, rename the
@@ -27,28 +27,25 @@ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYNAME=/etc/init.d/alsa-utils
ALSACTLHOME=/run/alsa
ALSACTLRUNTIME="${ALSACTLHOME}/runtime"
+ALSACTL_PIDFILE=/run/alsactl.pid
-[ -d "$ALSACTLRUNTIME" ] || mkdir -p "$ALSACTLRUNTIME"
-
+# shellcheck source=/lib/lsb/init-functions
. /lib/lsb/init-functions
+# shellcheck source=debian/utils.sh
. /usr/share/alsa/utils.sh
# $1 EXITSTATUS
# [$2 MESSAGE]
log_action_end_msg_and_exit()
{
- log_action_end_msg "$1" ${2:+"$2"}
- exit $1
+ log_action_end_msg "$1" ${2:+"$2"}
+ exit "$1"
}
# $1 PROGRAM
executable()
{
- # If which is not available then we must be running before
- # /usr is mounted on a system that has which in /usr/bin/.
- # Conclude that $1 is not executable.
- [ -x /bin/which ] || [ -x /usr/bin/which ] || return 1
- which "$1" >/dev/null 2>&1
+ command -v "$1" >/dev/null 2>&1
}
executable amixer || { echo "${MYNAME}: Error: No amixer program available." >&2 ; exit 1 ; }
@@ -56,102 +53,45 @@ executable amixer || { echo "${MYNAME}: Error: No amixer program available." >&2
# $1 <card ID> | "all"
restore_levels()
{
- [ -f /var/lib/alsa/asound.state ] || return 1
- CARD="$1"
- [ "$1" = all ] && CARD=""
- # Assume that if alsactl prints a message on stderr
- # then it failed somehow. This works around the fact
- # that alsactl doesn't return nonzero status when it
- # can't restore settings for the card
- if MSG="$(alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" restore $CARD 2>&1 >/dev/null)" && [ ! "$MSG" ] ; then
- return 0
- else
- # Retry with the "force" option. This restores more levels
- # but it results in much longer error messages.
- alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" -F restore $CARD >/dev/null 2>&1
- log_action_cont_msg "warning: 'alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" restore${CARD:+ $CARD}' failed with error message '$MSG'"
- return 1
- fi
+ [ -f /var/lib/alsa/asound.state ] || return 1
+ CARD="$1"
+ [ "$1" = all ] && CARD=""
+ # Assume that if alsactl prints a message on stderr
+ # then it failed somehow. This works around the fact
+ # that alsactl doesn't return nonzero status when it
+ # can't restore settings for the card
+ if MSG="$(alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="$ALSACTLRUNTIME" restore $CARD 2>&1 >/dev/null)" && [ ! "$MSG" ] ; then
+ return 0
+ else
+ log_action_cont_msg "warning: alsactl restore${CARD:+ $CARD} failed: $MSG"
+ return 1
+ fi
}
# $1 <card ID> | "all"
store_levels()
{
- CARD="$1"
- [ "$1" = all ] && CARD=""
- if MSG="$(alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" store $CARD 2>&1)" ; then
- sleep 1
- return 0
- else
- log_action_cont_msg "warning: 'alsactl store${CARD:+ $CARD}' -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" failed with error message '$MSG'"
- return 1
- fi
-}
-
-
-# $1 <card ID>
-mute_and_zero_levels_on_card()
-{
- CARDOPT="-c $1"
- for CTL in \
- Master \
- PCM \
- Synth \
- CD \
- Line \
- Mic \
- "PCM,1" \
- Wave \
- Music \
- AC97 \
- "Master Digital" \
- DAC \
- "DAC,0" \
- "DAC,1" \
- Headphone \
- Speaker \
- Playback
- do
- mute_and_zero_level "$CTL"
- done
-# for CTL in \
-# "Audigy Analog/Digital Output Jack" \
-# "SB Live Analog/Digital Output Jack"
-# do
-# switch_control "$CTL" off
-# done
- return 0
+ CARD="$1"
+ [ "$1" = all ] && CARD=""
+ if MSG="$(alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="$ALSACTLRUNTIME" store $CARD 2>&1)" ; then
+ return 0
+ else
+ log_action_cont_msg "warning: alsactl store${CARD:+ $CARD} failed: $MSG"
+ return 1
+ fi
}
-# $1 <card ID> | "all"
-mute_and_zero_levels()
-{
- TTZML_RETURNSTATUS=0
- case "$1" in
- all)
- for CARD in $(echo_card_indices) ; do
- mute_and_zero_levels_on_card "$CARD" || TTZML_RETURNSTATUS=1
- done
- ;;
- *)
- mute_and_zero_levels_on_card "$1" || TTZML_RETURNSTATUS=1
- ;;
- esac
- return $TTZML_RETURNSTATUS
-}
-
-
# $1 <card ID> | "all"
card_OK()
{
- [ "$1" ] || bugout
- if [ "$1" = all ] ; then
- [ -d /proc/asound ]
- return $?
- else
- [ -d "/proc/asound/card$1" ] || [ -d "/proc/asound/$1" ]
- return $?
- fi
+ [ "$1" ] || bugout
+ if [ "$1" = all ] ; then
+ [ -d /proc/asound ]
+ return $?
+ else
+ [ -d "/proc/asound/card$1" ] || [ -d "/proc/asound/$1" ]
+ return $?
+ fi
}
# If a card identifier is provided in $2 then regard it as an error
@@ -159,52 +99,71 @@ card_OK()
case "$1" in
start)
- EXITSTATUS=0
- TARGET_CARD="$2"
- case "$TARGET_CARD" in
- ""|all) TARGET_CARD=all ; log_action_begin_msg "Setting up ALSA" ;;
- *) log_action_begin_msg "Setting up ALSA card ${TARGET_CARD}" ;;
- esac
- card_OK "$TARGET_CARD" || log_action_end_msg_and_exit "$( [ ! "$2" ] ; echo $? ; )" "none loaded"
- preinit_levels "$TARGET_CARD" || EXITSTATUS=1
- if ! restore_levels "$TARGET_CARD" ; then
- sanify_levels "$TARGET_CARD" || EXITSTATUS=1
- restore_levels "$TARGET_CARD" >/dev/null 2>&1 || :
- fi
- log_action_end_msg_and_exit "$EXITSTATUS"
- ;;
+ EXITSTATUS=0
+ TARGET_CARD="$2"
+ case "$TARGET_CARD" in
+ ""|all) TARGET_CARD=all ; log_action_begin_msg "Setting up ALSA" ;;
+ *) log_action_begin_msg "Setting up ALSA card ${TARGET_CARD}" ;;
+ esac
+ card_OK "$TARGET_CARD" || log_action_end_msg_and_exit "$( [ ! "$2" ] ; echo $? ; )" "none loaded"
+ [ -d "$ALSACTLRUNTIME" ] || mkdir -p "$ALSACTLRUNTIME"
+ preinit_levels "$TARGET_CARD" || EXITSTATUS=1
+ if ! restore_levels "$TARGET_CARD" ; then
+ sanify_levels "$TARGET_CARD" || EXITSTATUS=1
+ restore_levels "$TARGET_CARD" >/dev/null 2>&1 || :
+ fi
+ log_action_end_msg_and_exit "$EXITSTATUS"
+ ;;
stop)
- EXITSTATUS=0
- TARGET_CARD="$2"
- case "$TARGET_CARD" in
- ""|all) TARGET_CARD=all ; log_action_begin_msg "Shutting down ALSA" ;;
- *) log_action_begin_msg "Shutting down ALSA card ${TARGET_CARD}" ;;
- esac
- card_OK "$TARGET_CARD" || log_action_end_msg_and_exit "$( [ ! "$2" ] ; echo $? ; )" "none loaded"
- store_levels "$TARGET_CARD" || EXITSTATUS=1
- #mute_and_zero_levels "$TARGET_CARD" || EXITSTATUS=1
- log_action_end_msg_and_exit "$EXITSTATUS"
- ;;
+ EXITSTATUS=0
+ TARGET_CARD="$2"
+ case "$TARGET_CARD" in
+ ""|all) TARGET_CARD=all ; log_action_begin_msg "Shutting down ALSA" ;;
+ *) log_action_begin_msg "Shutting down ALSA card ${TARGET_CARD}" ;;
+ esac
+ card_OK "$TARGET_CARD" || log_action_end_msg_and_exit "$( [ ! "$2" ] ; echo $? ; )" "none loaded"
+ [ -d "$ALSACTLRUNTIME" ] || mkdir -p "$ALSACTLRUNTIME"
+ store_levels "$TARGET_CARD" || EXITSTATUS=1
+ log_action_end_msg_and_exit "$EXITSTATUS"
+ ;;
restart|force-reload)
- EXITSTATUS=0
- $0 stop || EXITSTATUS=1
- $0 start || EXITSTATUS=1
- exit $EXITSTATUS
- ;;
+ EXITSTATUS=0
+ $0 stop "$2" || EXITSTATUS=1
+ $0 start "$2" || EXITSTATUS=1
+ exit "$EXITSTATUS"
+ ;;
reset)
- TARGET_CARD="$2"
- case "$TARGET_CARD" in
- ""|all) TARGET_CARD=all ; log_action_begin_msg "Resetting ALSA" ;;
- *) log_action_begin_msg "Resetting ALSA card ${TARGET_CARD}" ;;
- esac
- card_OK "$TARGET_CARD" || log_action_end_msg_and_exit "$( [ ! "$2" ] ; echo $? ; )" "none loaded"
- preinit_levels "$TARGET_CARD"
- sanify_levels "$TARGET_CARD"
- log_action_end_msg_and_exit "$?"
- ;;
+ TARGET_CARD="$2"
+ case "$TARGET_CARD" in
+ ""|all) TARGET_CARD=all ; log_action_begin_msg "Resetting ALSA" ;;
+ *) log_action_begin_msg "Resetting ALSA card ${TARGET_CARD}" ;;
+ esac
+ card_OK "$TARGET_CARD" || log_action_end_msg_and_exit "$( [ ! "$2" ] ; echo $? ; )" "none loaded"
+ [ -d "$ALSACTLRUNTIME" ] || mkdir -p "$ALSACTLRUNTIME"
+ preinit_levels "$TARGET_CARD"
+ sanify_levels "$TARGET_CARD"
+ log_action_end_msg_and_exit "$?"
+ ;;
+ status)
+ if [ -f "$ALSACTL_PIDFILE" ] && kill -0 "$(cat "$ALSACTL_PIDFILE")" 2>/dev/null ; then
+ echo "alsactl daemon running (PID $(cat "$ALSACTL_PIDFILE"))"
+ fi
+ if [ -d /proc/asound ] && [ -n "$(cat /proc/asound/cards 2>/dev/null)" ] ; then
+ echo "ALSA sound cards loaded:"
+ cat /proc/asound/cards
+ if [ -f /var/lib/alsa/asound.state ] ; then
+ echo "State file: /var/lib/alsa/asound.state ($(date -r /var/lib/alsa/asound.state '+%Y-%m-%d %H:%M:%S' 2>/dev/null))"
+ else
+ echo "No saved state file (/var/lib/alsa/asound.state)"
+ fi
+ exit 0
+ else
+ echo "No ALSA sound cards loaded."
+ exit 3
+ fi
+ ;;
*)
- echo "Usage: $MYNAME {start [CARD]|stop [CARD]|restart [CARD]|reset [CARD]}" >&2
- exit 3
- ;;
+ echo "Usage: $MYNAME {start [CARD]|stop [CARD]|restart [CARD]|reset [CARD]|status}" >&2
+ exit 3
+ ;;
esac
-
diff --git a/debian/install b/debian/install
index 0a64ff7..f76ccae 100644
--- a/debian/install
+++ b/debian/install
@@ -1,5 +1,4 @@
debian/utils.sh /usr/share/alsa
-lib/systemd
lib/udev
usr/bin
usr/lib/*/alsa-topology/*.so
diff --git a/debian/rules b/debian/rules
index f988405..0e509c3 100755
--- a/debian/rules
+++ b/debian/rules
@@ -10,7 +10,7 @@ override_dh_auto_configure:
--with-asound-state-dir=/var/lib/alsa \
--with-alsactl-home-dir=/run/alsa \
--with-alsactl-runtime-dir=/run/alsa/runtime \
- --with-systemdsystemunitdir=/lib/systemd/system \
+ --without-systemdsystemunitdir \
--disable-alsaconf
override_dh_auto_test:
--
2.39.5$ cat PATCHES_alsa-utils/0002*
From d545385298e4b993cab8bcb6514b997ab3e2f544 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Fri, 13 Nov 2026 03:22:47 +0100
Subject: [PATCH 2/2] aplay: add 64-bit float WAV support and fix vumeter for
float formats
test_wavefile() rejected WAVE files with bit_p_spl=64, aborting with
"can't play WAVE-files with sample 64 bits wide", even though
SND_PCM_FORMAT_FLOAT64_LE/BE exist in the ALSA UAPI and are already
handled by axfer (container-riff-wave.c maps WAVE_FORMAT_IEEE_FLOAT
to FLOAT64_LE/BE).
- test_wavefile(): add case 64 mapping WAV_FMT_IEEE_FLOAT to
SND_PCM_FORMAT_FLOAT64_LE/BE, mirroring the existing case 32
float handling.
- begin_wave(): add SND_PCM_FORMAT_FLOAT64_LE -> bits=64 and emit
WAV_FMT_IEEE_FLOAT for FLOAT64_LE, so arecord can write float64
WAV headers symmetrically.
- compute_max_peak(): add case 64 reading double samples via
fabs() scaled to int32 peak range, so -V vumeter works instead
of printing "Unsupported bit size 64". Also fix the pre-existing
case 32 bug where FLOAT_LE samples were interpreted as signed
int bit patterns, producing garbage meter readings; float
formats are now detected via snd_pcm_format_float() and handled
as float/double respectively.
Verified: aplay plays Float64/Float32 WAVs through the plug chain;
aplay -V s renders the stereo meter for both.
---
aplay/aplay.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 67 insertions(+), 2 deletions(-)
diff --git a/aplay/aplay.c b/aplay/aplay.c
index 6ce7191..cc3471b 100644
--- a/aplay/aplay.c
+++ b/aplay/aplay.c
@@ -1136,6 +1136,15 @@ static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
hwparams.format = native_format;
}
break;
+ case 64:
+ if (format == WAV_FMT_IEEE_FLOAT) {
+ if (big_endian)
+ native_format = SND_PCM_FORMAT_FLOAT64_BE;
+ else
+ native_format = SND_PCM_FORMAT_FLOAT64_LE;
+ hwparams.format = native_format;
+ }
+ break;
default:
error(_(" can't play WAVE-files with sample %d bits wide"),
TO_CPU_SHORT(f->bit_p_spl, big_endian));
@@ -1869,6 +1878,31 @@ static void compute_max_peak(u_char *data, size_t samples)
signed int *valp = (signed int *)data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
+ if (snd_pcm_format_float(hwparams.format)) {
+ float *fvalp = (float *)data;
+ float fval;
+
+ c = 0;
+ while (samples-- > 0) {
+ if (format_little_endian)
+ fval = *fvalp;
+ else {
+ uint32_t tmp = be32toh(*(uint32_t *)fvalp);
+ memcpy(&fval, &tmp, sizeof(fval));
+ }
+ fval = fabsf(fval);
+ if (fval > 1.0f)
+ fval = 1.0f;
+ val = (signed int)(fval * 0x7fffffff);
+ if (max_peak[c] < val)
+ max_peak[c] = val;
+ fvalp++;
+ if (vumeter == VUMETER_STEREO)
+ c = !c;
+ }
+ break;
+ }
+
c = 0;
while (samples-- > 0) {
if (format_little_endian)
@@ -1888,6 +1922,30 @@ static void compute_max_peak(u_char *data, size_t samples)
}
break;
}
+ case 64: {
+ double *valp = (double *)data;
+ double dval;
+
+ c = 0;
+ while (samples-- > 0) {
+ if (format_little_endian)
+ dval = *valp;
+ else {
+ uint64_t tmp = be64toh(*(uint64_t *)valp);
+ memcpy(&dval, &tmp, sizeof(dval));
+ }
+ dval = fabs(dval);
+ if (dval > 1.0)
+ dval = 1.0;
+ val = (signed int)(dval * 0x7fffffff);
+ if (max_peak[c] < val)
+ max_peak[c] = val;
+ valp++;
+ if (vumeter == VUMETER_STEREO)
+ c = !c;
+ }
+ break;
+ }
default:
if (run == 0) {
fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
@@ -1895,7 +1953,10 @@ static void compute_max_peak(u_char *data, size_t samples)
}
return;
}
- max = 1 << (significant_bits_per_sample-1);
+ if (snd_pcm_format_float(hwparams.format))
+ max = 0x7fffffff;
+ else
+ max = 1 << (significant_bits_per_sample-1);
if (max <= 0)
max = 0x7fffffff;
@@ -2671,6 +2732,9 @@ static void begin_wave(int fd, size_t cnt)
case SND_PCM_FORMAT_FLOAT_LE:
bits = 32;
break;
+ case SND_PCM_FORMAT_FLOAT64_LE:
+ bits = 64;
+ break;
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_3LE:
bits = 24;
@@ -2687,7 +2751,8 @@ static void begin_wave(int fd, size_t cnt)
cf.type = WAV_FMT;
cf.length = LE_INT(16);
- if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE)
+ if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE ||
+ hwparams.format == SND_PCM_FORMAT_FLOAT64_LE)
f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
else
f.format = LE_SHORT(WAV_FMT_PCM);
--
2.39.53. PATCHES for fftrate
$ ls -1 PATCHES_fftrate
0001-plugin-add-float32-float64-format-support-in-data_se.patch
0002-plugin-advertise-float-input-formats-and-harden-peri.patch
0003-pcm_conv-enlarge-static-I-O-buffers-for-high-ratio-c.patch$ cat PATCHES_fftrate/0001*
From 8e94e8fc09b7925fc99a6d4b6d7ebde4646a3bb8 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Thu, 8 Oct 2026 11:41:33 +0200
Subject: [PATCH 1/3] plugin: add float32/float64 format support in
data_setup()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Map SND_PCM_FORMAT_FLOAT_LE to WAVE_FORMAT_IEEE_FLOAT
(sample_size=4) and SND_PCM_FORMAT_FLOAT64_LE to
WAVE_FORMAT_IEEE_FLOAT (sample_size=8) instead of rejecting
them as "unsupported".
The conversion engine already supports both unit types:
mmsys_format_unit_type() maps IEEE_FLOAT/32-bit to
UNIT_TYPE_FLOAT and IEEE_FLOAT/64-bit to UNIT_TYPE_DOUBLE,
and samples_load()/samples_save() in convertlib.c implement
both paths. The interleave/deinterleave helpers are
byte-oriented and need no changes.
Note: under current alsa-lib, the rate plugin API negotiates
only linear integer formats, so plug demotes float client
output to s32_le/s16_le before the plugin sees it — these
cases are forward-looking and currently unreachable via the
dmix path, but harmless and required if alsa-lib ever offers
float formats to rate plugins (or if the plugin is reused in
a context where they are offered).
---
src/tools/alsa/plugin/plugin.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/tools/alsa/plugin/plugin.c b/src/tools/alsa/plugin/plugin.c
index 400e83b..8c9b92d 100644
--- a/src/tools/alsa/plugin/plugin.c
+++ b/src/tools/alsa/plugin/plugin.c
@@ -207,7 +207,21 @@ static int data_setup( conv_data *data, snd_pcm_rate_side_info_t *info, int n_ch
break;
case SND_PCM_FORMAT_FLOAT_LE:
+ data->format_tag = WAVE_FORMAT_IEEE_FLOAT;
+ data->sample_size = 4;
+ data->dummy = 0;
+ data->fill = 0x00;
+ fmt_name = "float_le";
+ break;
+
case SND_PCM_FORMAT_FLOAT64_LE:
+ data->format_tag = WAVE_FORMAT_IEEE_FLOAT;
+ data->sample_size = 8;
+ data->dummy = 0;
+ data->fill = 0x00;
+ fmt_name = "float64_le";
+ break;
+
default:
fmt_name = "unsupported";
break;
--
2.39.5$ cat PATCHES_fftrate/0002*
From 3f4f64d105ca0ed76270051cea5688e5a9369581 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Wed, 21 Oct 2026 17:28:38 +0200
Subject: [PATCH 2/3] plugin: advertise float input formats and harden
period-alignment math
- Declare SND_PCM_FORMAT_FLOAT_LE/Float64_LE support via
pcm_rate_formats/get_supported_formats in pcm_rate_ops, guarded
by SND_PCM_RATE_PLUGIN_VERSION >= 0x010003 so builds against
older pcm_rate.h still work.
- Handle version 0x010001/0x010002 ops structs in
SND_PCM_RATE_PLUGIN_ENTRY via braced arrcpy_bytes branches.
- Compute period-alignment products and shifted rates in 64-bit
arithmetic (uint64_t) to avoid int32 overflow at high input
rates and large period sizes (e.g. 2822400 Hz, period 677376).
- Log the resulting period ratio in pcm_rate_init.
---
src/tools/alsa/plugin/plugin.c | 67 ++++++++++++++++++++++++++++------
1 file changed, 55 insertions(+), 12 deletions(-)
diff --git a/src/tools/alsa/plugin/plugin.c b/src/tools/alsa/plugin/plugin.c
index 8c9b92d..8a0a519 100644
--- a/src/tools/alsa/plugin/plugin.c
+++ b/src/tools/alsa/plugin/plugin.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdarg.h>
+#include <stddef.h> /* offsetof */
#include <alsa/asoundlib.h>
#include <alsa/pcm_rate.h>
@@ -417,15 +418,22 @@ static int pcm_rate_init( void *obj, snd_pcm_rate_info_t *info )
//--------------------------------------------------------------------------------------------
- if( ( in_rate * info->out.period_size ) != ( out_rate * info->in.period_size ) )
{
- double l_in = log2( (double)in_rate / (double)info->in.period_size );
- double l_out = log2( (double)out_rate / (double)info->out.period_size );
- int fpp = (int)( 0.5 * ( l_in + l_out ) );
+ uint64_t prod_in = (uint64_t)in_rate * info->out.period_size;
+ uint64_t prod_out = (uint64_t)out_rate * info->in.period_size;
- fpp = MAX( fpp, 0 );
- in_rate = info->in.period_size << fpp;
- out_rate = info->out.period_size << fpp;
+ fprintf( OUTF, "Period ratio: %.6f\n", (double)prod_in / (double)prod_out );
+
+ if( prod_in != prod_out )
+ {
+ double l_in = log2( (double)in_rate / (double)info->in.period_size );
+ double l_out = log2( (double)out_rate / (double)info->out.period_size );
+ int fpp = (int)( 0.5 * ( l_in + l_out ) );
+
+ fpp = MAX( fpp, 0 );
+ in_rate = (int)( (uint64_t)info->in.period_size << fpp );
+ out_rate = (int)( (uint64_t)info->out.period_size << fpp );
+ }
}
//--------------------------------------------------------------------------------------------
@@ -783,6 +791,30 @@ static void pcm_rate_dump( void *obj, snd_output_t *out )
}
#endif
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+static int pcm_rate_formats
+(
+ void *obj,
+ uint64_t *in_formats,
+ uint64_t *out_formats,
+ unsigned int *flags
+)
+{
+ dbg_printf( "[pcm_rate_formats]\n" );
+
+ *in_formats = *out_formats =
+ ( 1ULL << SND_PCM_FORMAT_U8 )
+ | ( 1ULL << SND_PCM_FORMAT_S16_LE )
+ | ( 1ULL << SND_PCM_FORMAT_S24_3LE )
+ | ( 1ULL << SND_PCM_FORMAT_S24_LE )
+ | ( 1ULL << SND_PCM_FORMAT_S32_LE )
+ | ( 1ULL << SND_PCM_FORMAT_FLOAT_LE )
+ | ( 1ULL << SND_PCM_FORMAT_FLOAT64_LE );
+
+ *flags = 0;
+ return 0;
+}
+#endif
static const snd_pcm_rate_ops_t pcm_rate_ops =
{
@@ -801,6 +833,10 @@ static const snd_pcm_rate_ops_t pcm_rate_ops =
.get_supported_rates = pcm_rate_rates,
.dump = pcm_rate_dump,
#endif
+
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010003
+ .get_supported_formats = pcm_rate_formats,
+#endif
};
@@ -830,11 +866,18 @@ int SND_PCM_RATE_PLUGIN_ENTRY( fftrate )
arrzero_unit( inst );
*objp = inst;
-#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
- if( version == 0x010001 )
- arrcpy_bytes( ops, &pcm_rate_ops, sizeof(snd_pcm_rate_old_ops_t) )
- else
-#endif
+#if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
+ if( version == 0x010001 )
+ {
+ arrcpy_bytes( ops, &pcm_rate_ops, sizeof(snd_pcm_rate_old_ops_t) );
+ }
+ else
+ if( version == 0x010002 )
+ {
+ arrcpy_bytes( ops, &pcm_rate_ops, sizeof(snd_pcm_rate_v2_ops_t) );
+ }
+ else
+#endif
*ops = pcm_rate_ops;
return 0;
--
2.39.5$ cat PATCHES_fftrate/0003*
From 120e746adc3f5e5ea0ea43fa110cbba6427e21c3 Mon Sep 17 00:00:00 2001
From: igorzwx <igorzwx@devuan.cargo-cult.org>
Date: Thu, 22 Oct 2026 19:26:48 +0200
Subject: [PATCH 3/3] pcm_conv: enlarge static I/O buffers for high-ratio
conversions
BUFFER_SIZE (65636 bytes) was too small for extreme upsampling
ratios (e.g. 352800 -> 2822400/5644800 Hz DSD-equivalent rates
with -T fft), whose Vorbis window size exceeds the buffer,
causing the conversion loop to exit on its first iteration and
silently write a 44-byte (header-only) WAV while still printing
'Ok.'
---
src/apps/pcm_conv/Main.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/apps/pcm_conv/Main.cpp b/src/apps/pcm_conv/Main.cpp
index d7fd865..5eef1bb 100644
--- a/src/apps/pcm_conv/Main.cpp
+++ b/src/apps/pcm_conv/Main.cpp
@@ -36,7 +36,9 @@
// Definition
//-----------------------------------------------------------------------------
#define DEFAULT_OUTPUT "convert.wav"
-#define BUFFER_SIZE 65636L
+#define BUFFER_SIZE 4194304L /* 4 MiB: large enough for high-ratio
+ * FFT windows (e.g. DSD512-rate simulation,
+ * 352800 Hz -> 5644800 Hz, 64-bit float) */
//=============================================================================
--
2.39.5TEST
$ aplay -V s test*
Playing WAVE 'test_f64.wav' : Float 64 bit Little Endian, Rate 192000 Hz, Stereo
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.
+###############48%|49%################+ $ aplay -V s *Float.wav
Playing WAVE '32bitFloat.wav' : Float 32 bit Little Endian, Rate 352800 Hz, Stereo
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.
+ #####36%|49%########## + Test file can be created with pcm_conv (fftrate)
pcm_conv 01*.wav test_f64.wav -b 64f -f 192000 -T fft -v Offline
Pages: 1