<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="https://dev1galaxy.org/extern.php?action=feed&amp;tid=8292&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Dev1 Galaxy Forum / ALSA with double precision]]></title>
		<link>https://dev1galaxy.org/viewtopic.php?id=8292</link>
		<description><![CDATA[The most recent posts in ALSA with double precision.]]></description>
		<lastBuildDate>Sat, 14 Nov 2026 21:48:40 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[ALSA with double precision]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=66491#p66491</link>
			<description><![CDATA[<h5>ALSA with double precision</h5><p><strong>1. alsa-lib</strong></p><p>Prerequisites</p><div class="codebox"><pre><code>sudo apt-get install build-essential dpkg-dev devscripts quilt fakeroot debhelper
sudo apt-get build-dep alsa-lib </code></pre></div><div class="codebox"><pre><code>$ 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 </code></pre></div><div class="codebox"><pre><code>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 &quot;1.2.50-1&quot; &quot;Local rebuild: enable float64 audio&quot; 
dpkg-buildpackage -b -us -uc
mkdir -p ../dist &amp;&amp; mv -v ../*.deb ../*.changes ../*.buildinfo ../dist 2&gt;/dev/null
sudo dpkg -i ../dist/libasound2_*.deb ../dist/libasound2-data_*.deb ../dist/libasound2-dev_*.deb</code></pre></div><p>PATCHES for alsa-lib</p><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_alsa-lib/0001*
From 98e1f84f04e190dc5d9086aaa61f359267438e8a Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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 &quot;formats must be linear&quot; 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-&gt;stream == SND_PCM_STREAM_PLAYBACK ?  
+			       rate-&gt;in_formats : rate-&gt;out_formats;
 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
 					 &amp;access_mask);
 	if (err &lt; 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 &amp; (1ULL &lt;&lt; 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) &gt; 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) &gt;= 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-&gt;src_conv_idx =
-			snd_pcm_linear_convert_index(rate-&gt;orig_in_format,
-						     rate-&gt;info.in.format);
+		snd_pcm_format_t f1 = rate-&gt;orig_in_format;
+		snd_pcm_format_t f2 = rate-&gt;info.in.format;
+		if (f1 == f2) {
+			rate-&gt;src_lfloat_mode = 0;
+			rate-&gt;src_conv_idx = RATE_COPY;		/* deinterleave only */
+		} else if (snd_pcm_format_linear(f1) &amp;&amp; snd_pcm_format_linear(f2)) {
+			rate-&gt;src_lfloat_mode = 0;
+			rate-&gt;src_conv_idx = snd_pcm_linear_convert_index(f1, f2);
+		} else if (snd_pcm_format_float(f1) &amp;&amp; snd_pcm_format_linear(f2)) {
+			rate-&gt;src_lfloat_mode = 1;	/* float -&gt; int: get32float + put32 */
+			rate-&gt;src_get_idx = snd_pcm_lfloat_get_s32_index(f1);
+			rate-&gt;src_put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, f2);
+		} else if (snd_pcm_format_linear(f1) &amp;&amp; snd_pcm_format_float(f2)) {
+			rate-&gt;src_lfloat_mode = 2;	/* int -&gt; float: get32 + put32float */
+			rate-&gt;src_get_idx = snd_pcm_linear_get_index(f1, SND_PCM_FORMAT_S32);
+			rate-&gt;src_put_idx = snd_pcm_lfloat_put_s32_index(f2);
+		} else {
+			/* float -&gt; float of different width: no single-pass
+			 * converter (lfloat ops go via int32 on one side) */
+			SNDERR(&quot;rate plugin: unsupported src conversion %s -&gt; %s&quot;,
+			       snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+			err = -EINVAL;
+			goto error;
+		}
+		if ((rate-&gt;src_lfloat_mode &amp;&amp; ((int)rate-&gt;src_get_idx &lt; 0 || (int)rate-&gt;src_put_idx &lt; 0)) ||
+		    (!rate-&gt;src_lfloat_mode &amp;&amp; rate-&gt;src_conv_idx != RATE_COPY &amp;&amp;
+		     (int)rate-&gt;src_conv_idx &lt; 0)) {
+			SNDERR(&quot;rate plugin: unsupported src conversion %s -&gt; %s&quot;,
+			       snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+			err = -EINVAL;
+			goto error;
+		}
 		rate-&gt;src_buf = rate_alloc_tmp_buf(rate-&gt;info.in.format,
 						   channels, rate-&gt;info.in.period_size);
 		if (!rate-&gt;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-&gt;dst_conv_idx =
-			snd_pcm_linear_convert_index(rate-&gt;info.out.format,
-						     rate-&gt;orig_out_format);
+		snd_pcm_format_t f1 = rate-&gt;info.out.format;
+		snd_pcm_format_t f2 = rate-&gt;orig_out_format;
+		if (f1 == f2) {
+			rate-&gt;dst_lfloat_mode = 0;
+			rate-&gt;dst_conv_idx = RATE_COPY;
+		} else if (snd_pcm_format_linear(f1) &amp;&amp; snd_pcm_format_linear(f2)) {
+			rate-&gt;dst_lfloat_mode = 0;
+			rate-&gt;dst_conv_idx = snd_pcm_linear_convert_index(f1, f2);
+		} else if (snd_pcm_format_float(f1) &amp;&amp; snd_pcm_format_linear(f2)) {
+			rate-&gt;dst_lfloat_mode = 1;	/* float -&gt; int */
+			rate-&gt;dst_get_idx = snd_pcm_lfloat_get_s32_index(f1);
+			rate-&gt;dst_put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, f2);
+		} else if (snd_pcm_format_linear(f1) &amp;&amp; snd_pcm_format_float(f2)) {
+			rate-&gt;dst_lfloat_mode = 2;	/* int -&gt; float */
+			rate-&gt;dst_get_idx = snd_pcm_linear_get_index(f1, SND_PCM_FORMAT_S32);
+			rate-&gt;dst_put_idx = snd_pcm_lfloat_put_s32_index(f2);
+		} else {
+			SNDERR(&quot;rate plugin: unsupported dst conversion %s -&gt; %s&quot;,
+			       snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+			err = -EINVAL;
+			goto error;
+		}
+		if ((rate-&gt;dst_lfloat_mode &amp;&amp; ((int)rate-&gt;dst_get_idx &lt; 0 || (int)rate-&gt;dst_put_idx &lt; 0)) ||
+		    (!rate-&gt;dst_lfloat_mode &amp;&amp; rate-&gt;dst_conv_idx != RATE_COPY &amp;&amp;
+		     (int)rate-&gt;dst_conv_idx &lt; 0)) {
+			SNDERR(&quot;rate plugin: unsupported dst conversion %s -&gt; %s&quot;,
+			       snd_pcm_format_name(f1), snd_pcm_format_name(f2));
+			err = -EINVAL;
+			goto error;
+		}
 		rate-&gt;dst_buf = rate_alloc_tmp_buf(rate-&gt;info.out.format,
 						   channels, rate-&gt;info.out.period_size);
 		if (!rate-&gt;dst_buf) {
@@ -585,10 +663,28 @@ static void do_convert(const snd_pcm_channel_area_t *dst_areas,
 	}
 
 	if (rate-&gt;src_buf) {
-		snd_pcm_linear_convert(rate-&gt;src_buf, 0,
-				       src_areas, src_offset,
-				       channels, src_frames,
-				       rate-&gt;src_conv_idx);
+		if (rate-&gt;src_lfloat_mode == 1)		/* float -&gt; int */
+			snd_pcm_lfloat_convert_float_integer(rate-&gt;src_buf, 0,
+					      src_areas, src_offset,
+					      channels, src_frames,
+					      rate-&gt;src_put_idx,
+					      rate-&gt;src_get_idx);
+		else if (rate-&gt;src_lfloat_mode == 2)	/* int -&gt; float */
+			snd_pcm_lfloat_convert_integer_float(rate-&gt;src_buf, 0,
+					      src_areas, src_offset,
+					      channels, src_frames,
+					      rate-&gt;src_get_idx,
+					      rate-&gt;src_put_idx);
+		else if (rate-&gt;src_conv_idx == RATE_COPY)
+			snd_pcm_areas_copy(rate-&gt;src_buf, 0,
+					   src_areas, src_offset,
+					   channels, src_frames,
+					   rate-&gt;info.in.format);
+		else
+			snd_pcm_linear_convert(rate-&gt;src_buf, 0,
+					       src_areas, src_offset,
+					       channels, src_frames,
+					       rate-&gt;src_conv_idx);
 		src_areas = rate-&gt;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-&gt;dst_buf)
-		snd_pcm_linear_convert(dst_areas, dst_offset,
-				       rate-&gt;dst_buf, 0,
-				       channels, dst_frames,
-				       rate-&gt;dst_conv_idx);
+	if (rate-&gt;dst_buf) {  
+		if (rate-&gt;dst_lfloat_mode == 1)		/* float -&gt; int */
+			snd_pcm_lfloat_convert_float_integer(dst_areas, dst_offset,
+					      rate-&gt;dst_buf, 0,
+					      channels, dst_frames,
+					      rate-&gt;dst_put_idx,
+					      rate-&gt;dst_get_idx);
+		else if (rate-&gt;dst_lfloat_mode == 2)	/* int -&gt; float */
+			snd_pcm_lfloat_convert_integer_float(dst_areas, dst_offset,
+					      rate-&gt;dst_buf, 0,
+					      channels, dst_frames,
+					      rate-&gt;dst_get_idx,
+					      rate-&gt;dst_put_idx);
+		else if (rate-&gt;dst_conv_idx == RATE_COPY)  
+			snd_pcm_areas_copy(dst_areas, dst_offset,  
+					   rate-&gt;dst_buf, 0,  
+					   channels, dst_frames,  
+					   rate-&gt;orig_out_format);  
+		else  
+			snd_pcm_linear_convert(dst_areas, dst_offset,  
+					       rate-&gt;dst_buf, 0,  
+					       channels, dst_frames,  
+					       rate-&gt;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 &amp;&amp; slave);
 	if (sformat != SND_PCM_FORMAT_UNKNOWN &amp;&amp;
-	    snd_pcm_format_linear(sformat) != 1)
+	    snd_pcm_format_linear(sformat) != 1 &amp;&amp;
+	    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(&quot;No name given for rate converter&quot;);
 			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 &lt; 0)
 		return err;
 	if (sformat != SND_PCM_FORMAT_UNKNOWN &amp;&amp;
-	    snd_pcm_format_linear(sformat) != 1) {
+	    snd_pcm_format_linear(sformat) != 1 &amp;&amp;
+	    snd_pcm_format_float(sformat) != 1) {
 	    	snd_config_delete(sconf);
-		SNDERR(&quot;slave format is not linear&quot;);
+		SNDERR(&quot;slave format is not linear or float&quot;);
 		return -EINVAL;
 	}
 	err = snd_pcm_open_slave(&amp;spcm, root, sconf, stream, mode, conf);
-- 
2.39.5</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_alsa-lib/0002*
From 8a9e12f00ec215eb9801b34da8a852cab5631ea6 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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-&gt;format), forcing float clients through an
integer downconversion before the rate plugin could see them. Relax
the assertions and the clt-&gt;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-&gt;rate == slv-&gt;rate)
 		return 0;
-	assert(snd_pcm_format_linear(slv-&gt;format));
+	assert(snd_pcm_format_linear(slv-&gt;format) ||
+	       snd_pcm_format_float(slv-&gt;format));
 	err = snd_pcm_rate_open(new, NULL, slv-&gt;format, slv-&gt;rate, plug-&gt;rate_converter,
 				plug-&gt;gen.slave, plug-&gt;gen.slave != plug-&gt;req_slave);
 	if (err &lt; 0)
 		return err;
 	slv-&gt;access = clt-&gt;access;
 	slv-&gt;rate = clt-&gt;rate;
-	if (snd_pcm_format_linear(clt-&gt;format))
+	if (snd_pcm_format_linear(clt-&gt;format) ||
+	    snd_pcm_format_float(clt-&gt;format))
 		slv-&gt;format = clt-&gt;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-&gt;rate != slv-&gt;rate &amp;&amp;
 	    clt-&gt;channels &gt; slv-&gt;channels)
 		return 0;
-	assert(snd_pcm_format_linear(slv-&gt;format));
+	assert(snd_pcm_format_linear(slv-&gt;format) ||
+	       snd_pcm_format_float(slv-&gt;format));
 	tt_ssize = slv-&gt;channels;
 	tt_cused = clt-&gt;channels;
 	tt_sused = slv-&gt;channels;
-- 
2.39.5</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_alsa-lib/0003*
From 55a6cf40140048488d05475e5db8f53e68db53d8 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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 &amp;&amp; slave &amp;&amp; ttable);
 	if (sformat != SND_PCM_FORMAT_UNKNOWN &amp;&amp; 
-	    snd_pcm_format_linear(sformat) != 1)
+	    snd_pcm_format_linear(sformat) != 1 &amp;&amp;
+	    snd_pcm_format_float(sformat) != 1)
 		return -EINVAL;
 	route = calloc(1, sizeof(snd_pcm_route_t));
 	if (!route) {
-- 
2.39.5</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_alsa-lib/0004*
From 6d386441fbf81f3fb6b82222e74b22abdb3ff360 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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

&quot;Rate plugin for linear formats&quot; → 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.5</code></pre></div><p><strong>2. alsa-utils</strong></p><p>Build-deps</p><div class="codebox"><pre><code>sudo apt-get install libasound2-dev libatopology-dev libfftw3-dev libncurses-dev pkg-config python3-docutils xmlto</code></pre></div><div class="codebox"><pre><code>$ 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.patch</code></pre></div><div class="codebox"><pre><code>cd 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 &quot;1.2.50-1&quot; &quot;Local rebuild: enable float64 audio&quot; 
dpkg-buildpackage -b -us -uc
mkdir -p ../dist &amp;&amp; mv -v ../*.deb ../*.changes ../*.buildinfo ../dist 2&gt;/dev/null
sudo dpkg -i ../dist/alsa-utils_*.deb </code></pre></div><p><strong>WARNING:</strong> 0001-*.patch modifies files inside debian/ (control, rules,<br />install, init). Fine for local .deb builds; if you plan to build a<br />source package or upload, apply the debian/ changes directly instead.<br />Example: How to Create a Debian Package Release Bundle<br />_https://dev1galaxy.org/viewtopic.php?pid=60629#p60629</p><p>PATCHES for alsa-utils</p><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_alsa-utils/0001*
From 941fb6274bed74f1a51f66b82453fd2cc940cfae Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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 &quot;systemd&quot; 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 -&gt;
  --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 &quot;lib/systemd&quot; pattern. With units no longer
  installed into debian/tmp, dh_install fails with &quot;missing files&quot;
  under dh_missing --fail-missing. Keep &quot;lib/udev&quot;:
  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 &quot;status&quot; action reporting loaded cards
    (/proc/asound/cards), state file presence/timestamp, and
    alsactl daemon pidfile (/run/alsactl.pid)
  * restart/force-reload: pass &quot;$2&quot; so per-card restart works
  * store_levels: drop &quot;sleep 1&quot; (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 (&gt;= 1.2.1),
                libatopology-dev (&gt;= 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=&quot;${ALSACTLHOME}/runtime&quot;
+ALSACTL_PIDFILE=/run/alsactl.pid
 
-[ -d &quot;$ALSACTLRUNTIME&quot; ] || mkdir -p &quot;$ALSACTLRUNTIME&quot;
-
+# 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 &quot;$1&quot; ${2:+&quot;$2&quot;}
-	exit $1
+    log_action_end_msg &quot;$1&quot; ${2:+&quot;$2&quot;}
+    exit &quot;$1&quot;
 }
 
 # $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 &quot;$1&quot; &gt;/dev/null 2&gt;&amp;1
+    command -v &quot;$1&quot; &gt;/dev/null 2&gt;&amp;1
 }
 
 executable amixer || { echo &quot;${MYNAME}: Error: No amixer program available.&quot; &gt;&amp;2 ; exit 1 ; }
@@ -56,102 +53,45 @@ executable amixer || { echo &quot;${MYNAME}: Error: No amixer program available.&quot; &gt;&amp;2
 # $1 &lt;card ID&gt; | &quot;all&quot;
 restore_levels()
 {
-	[ -f /var/lib/alsa/asound.state ] || return 1
-	CARD=&quot;$1&quot;
-	[ &quot;$1&quot; = all ] &amp;&amp; CARD=&quot;&quot;
-	# Assume that if alsactl prints a message on stderr
-	# then it failed somehow.  This works around the fact
-	# that alsactl doesn&#039;t return nonzero status when it
-	# can&#039;t restore settings for the card
-	if MSG=&quot;$(alsactl -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;${ALSACTLRUNTIME}&quot; restore $CARD 2&gt;&amp;1 &gt;/dev/null)&quot; &amp;&amp; [ ! &quot;$MSG&quot; ] ; then
-		return 0
-	else
-		# Retry with the &quot;force&quot; option.  This restores more levels
-		# but it results in much longer error messages.
-		alsactl -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;${ALSACTLRUNTIME}&quot; -F restore $CARD &gt;/dev/null 2&gt;&amp;1
-		log_action_cont_msg &quot;warning: &#039;alsactl -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;${ALSACTLRUNTIME}&quot; restore${CARD:+ $CARD}&#039; failed with error message &#039;$MSG&#039;&quot;
-		return 1
-	fi
+    [ -f /var/lib/alsa/asound.state ] || return 1
+    CARD=&quot;$1&quot;
+    [ &quot;$1&quot; = all ] &amp;&amp; CARD=&quot;&quot;
+    # Assume that if alsactl prints a message on stderr
+    # then it failed somehow.  This works around the fact
+    # that alsactl doesn&#039;t return nonzero status when it
+    # can&#039;t restore settings for the card
+    if MSG=&quot;$(alsactl -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;$ALSACTLRUNTIME&quot; restore $CARD 2&gt;&amp;1 &gt;/dev/null)&quot; &amp;&amp; [ ! &quot;$MSG&quot; ] ; then
+        return 0
+    else
+        log_action_cont_msg &quot;warning: alsactl restore${CARD:+ $CARD} failed: $MSG&quot;
+        return 1
+    fi
 }
 
 # $1 &lt;card ID&gt; | &quot;all&quot;
 store_levels()
 {
-	CARD=&quot;$1&quot;
-	[ &quot;$1&quot; = all ] &amp;&amp; CARD=&quot;&quot;
-	if MSG=&quot;$(alsactl -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;${ALSACTLRUNTIME}&quot; store $CARD 2&gt;&amp;1)&quot; ; then
-		sleep 1
-		return 0
-	else
-		log_action_cont_msg &quot;warning: &#039;alsactl store${CARD:+ $CARD}&#039; -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;${ALSACTLRUNTIME}&quot; failed with error message &#039;$MSG&#039;&quot;
-		return 1
-	fi
-}
-
-
-# $1 &lt;card ID&gt;
-mute_and_zero_levels_on_card()
-{
-	CARDOPT=&quot;-c $1&quot;
-	for CTL in \
-		Master \
-		PCM \
-		Synth \
-		CD \
-		Line \
-		Mic \
-		&quot;PCM,1&quot; \
-		Wave \
-		Music \
-		AC97 \
-		&quot;Master Digital&quot; \
-		DAC \
-		&quot;DAC,0&quot; \
-		&quot;DAC,1&quot; \
-		Headphone \
-		Speaker \
-		Playback
-	do
-		mute_and_zero_level &quot;$CTL&quot;
-	done
-#	for CTL in \
-#		&quot;Audigy Analog/Digital Output Jack&quot; \
-#		&quot;SB Live Analog/Digital Output Jack&quot;
-#	do
-#		switch_control &quot;$CTL&quot; off
-#	done
-	return 0
+    CARD=&quot;$1&quot;
+    [ &quot;$1&quot; = all ] &amp;&amp; CARD=&quot;&quot;
+    if MSG=&quot;$(alsactl -E HOME=&quot;$ALSACTLHOME&quot; -E XDG_RUNTIME_DIR=&quot;$ALSACTLRUNTIME&quot; store $CARD 2&gt;&amp;1)&quot; ; then
+        return 0
+    else
+        log_action_cont_msg &quot;warning: alsactl store${CARD:+ $CARD} failed: $MSG&quot;
+        return 1
+    fi
 }
 
-# $1 &lt;card ID&gt; | &quot;all&quot;
-mute_and_zero_levels()
-{
-	TTZML_RETURNSTATUS=0
-	case &quot;$1&quot; in
-	  all)
-		for CARD in $(echo_card_indices) ; do
-			mute_and_zero_levels_on_card &quot;$CARD&quot; || TTZML_RETURNSTATUS=1
-		done
-		;;
-	  *)
-		mute_and_zero_levels_on_card &quot;$1&quot; || TTZML_RETURNSTATUS=1
-		;;
-	esac
-	return $TTZML_RETURNSTATUS
-}
-
-
 # $1 &lt;card ID&gt; | &quot;all&quot;
 card_OK()
 {
-	[ &quot;$1&quot; ] || bugout
-	if [ &quot;$1&quot; = all ] ; then
-		[ -d /proc/asound ]
-		return $?
-	else
-		[ -d &quot;/proc/asound/card$1&quot; ] || [ -d &quot;/proc/asound/$1&quot; ]
-		return $?
-	fi
+    [ &quot;$1&quot; ] || bugout
+    if [ &quot;$1&quot; = all ] ; then
+        [ -d /proc/asound ]
+        return $?
+    else
+        [ -d &quot;/proc/asound/card$1&quot; ] || [ -d &quot;/proc/asound/$1&quot; ]
+        return $?
+    fi
 }
 
 # If a card identifier is provided in $2 then regard it as an error
@@ -159,52 +99,71 @@ card_OK()
 
 case &quot;$1&quot; in
   start)
-	EXITSTATUS=0
-	TARGET_CARD=&quot;$2&quot;
-	case &quot;$TARGET_CARD&quot; in
-	  &quot;&quot;|all) TARGET_CARD=all ; log_action_begin_msg &quot;Setting up ALSA&quot; ;;
-	  *) log_action_begin_msg &quot;Setting up ALSA card ${TARGET_CARD}&quot; ;;
-	esac
-	card_OK &quot;$TARGET_CARD&quot; || log_action_end_msg_and_exit &quot;$( [ ! &quot;$2&quot; ] ; echo $? ; )&quot; &quot;none loaded&quot;
-	preinit_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
-	if ! restore_levels &quot;$TARGET_CARD&quot; ; then
-		sanify_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
-		restore_levels &quot;$TARGET_CARD&quot; &gt;/dev/null 2&gt;&amp;1 || :
-	fi
-	log_action_end_msg_and_exit &quot;$EXITSTATUS&quot;
-	;;
+    EXITSTATUS=0
+    TARGET_CARD=&quot;$2&quot;
+    case &quot;$TARGET_CARD&quot; in
+      &quot;&quot;|all) TARGET_CARD=all ; log_action_begin_msg &quot;Setting up ALSA&quot; ;;
+      *) log_action_begin_msg &quot;Setting up ALSA card ${TARGET_CARD}&quot; ;;
+    esac
+    card_OK &quot;$TARGET_CARD&quot; || log_action_end_msg_and_exit &quot;$( [ ! &quot;$2&quot; ] ; echo $? ; )&quot; &quot;none loaded&quot;
+    [ -d &quot;$ALSACTLRUNTIME&quot; ] || mkdir -p &quot;$ALSACTLRUNTIME&quot;
+    preinit_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
+    if ! restore_levels &quot;$TARGET_CARD&quot; ; then
+        sanify_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
+        restore_levels &quot;$TARGET_CARD&quot; &gt;/dev/null 2&gt;&amp;1 || :
+    fi
+    log_action_end_msg_and_exit &quot;$EXITSTATUS&quot;
+    ;;
   stop)
-	EXITSTATUS=0
-	TARGET_CARD=&quot;$2&quot;
-	case &quot;$TARGET_CARD&quot; in
-	  &quot;&quot;|all) TARGET_CARD=all ; log_action_begin_msg &quot;Shutting down ALSA&quot; ;;
-	  *) log_action_begin_msg &quot;Shutting down ALSA card ${TARGET_CARD}&quot; ;;
-	esac
-	card_OK &quot;$TARGET_CARD&quot; || log_action_end_msg_and_exit &quot;$( [ ! &quot;$2&quot; ] ; echo $? ; )&quot; &quot;none loaded&quot;
-	store_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
-	#mute_and_zero_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
-	log_action_end_msg_and_exit &quot;$EXITSTATUS&quot;
-	;;
+    EXITSTATUS=0
+    TARGET_CARD=&quot;$2&quot;
+    case &quot;$TARGET_CARD&quot; in
+      &quot;&quot;|all) TARGET_CARD=all ; log_action_begin_msg &quot;Shutting down ALSA&quot; ;;
+      *) log_action_begin_msg &quot;Shutting down ALSA card ${TARGET_CARD}&quot; ;;
+    esac
+    card_OK &quot;$TARGET_CARD&quot; || log_action_end_msg_and_exit &quot;$( [ ! &quot;$2&quot; ] ; echo $? ; )&quot; &quot;none loaded&quot;
+    [ -d &quot;$ALSACTLRUNTIME&quot; ] || mkdir -p &quot;$ALSACTLRUNTIME&quot;
+    store_levels &quot;$TARGET_CARD&quot; || EXITSTATUS=1
+    log_action_end_msg_and_exit &quot;$EXITSTATUS&quot;
+    ;;
   restart|force-reload)
-	EXITSTATUS=0
-	$0 stop || EXITSTATUS=1
-	$0 start || EXITSTATUS=1
-	exit $EXITSTATUS
-	;;
+    EXITSTATUS=0
+    $0 stop &quot;$2&quot; || EXITSTATUS=1
+    $0 start &quot;$2&quot; || EXITSTATUS=1
+    exit &quot;$EXITSTATUS&quot;
+    ;;
   reset)
-	TARGET_CARD=&quot;$2&quot;
-	case &quot;$TARGET_CARD&quot; in
-	  &quot;&quot;|all) TARGET_CARD=all ; log_action_begin_msg &quot;Resetting ALSA&quot; ;;
-	  *) log_action_begin_msg &quot;Resetting ALSA card ${TARGET_CARD}&quot; ;;
-	esac
-	card_OK &quot;$TARGET_CARD&quot; || log_action_end_msg_and_exit &quot;$( [ ! &quot;$2&quot; ] ; echo $? ; )&quot; &quot;none loaded&quot;
-	preinit_levels &quot;$TARGET_CARD&quot;
-	sanify_levels &quot;$TARGET_CARD&quot;
-	log_action_end_msg_and_exit &quot;$?&quot;
-	;;
+    TARGET_CARD=&quot;$2&quot;
+    case &quot;$TARGET_CARD&quot; in
+      &quot;&quot;|all) TARGET_CARD=all ; log_action_begin_msg &quot;Resetting ALSA&quot; ;;
+      *) log_action_begin_msg &quot;Resetting ALSA card ${TARGET_CARD}&quot; ;;
+    esac
+    card_OK &quot;$TARGET_CARD&quot; || log_action_end_msg_and_exit &quot;$( [ ! &quot;$2&quot; ] ; echo $? ; )&quot; &quot;none loaded&quot;
+    [ -d &quot;$ALSACTLRUNTIME&quot; ] || mkdir -p &quot;$ALSACTLRUNTIME&quot;
+    preinit_levels &quot;$TARGET_CARD&quot;
+    sanify_levels &quot;$TARGET_CARD&quot;
+    log_action_end_msg_and_exit &quot;$?&quot;
+    ;;
+  status)
+    if [ -f &quot;$ALSACTL_PIDFILE&quot; ] &amp;&amp; kill -0 &quot;$(cat &quot;$ALSACTL_PIDFILE&quot;)&quot; 2&gt;/dev/null ; then
+        echo &quot;alsactl daemon running (PID $(cat &quot;$ALSACTL_PIDFILE&quot;))&quot;
+    fi
+    if [ -d /proc/asound ] &amp;&amp; [ -n &quot;$(cat /proc/asound/cards 2&gt;/dev/null)&quot; ] ; then
+        echo &quot;ALSA sound cards loaded:&quot;
+        cat /proc/asound/cards
+        if [ -f /var/lib/alsa/asound.state ] ; then
+            echo &quot;State file: /var/lib/alsa/asound.state ($(date -r /var/lib/alsa/asound.state &#039;+%Y-%m-%d %H:%M:%S&#039; 2&gt;/dev/null))&quot;
+        else
+            echo &quot;No saved state file (/var/lib/alsa/asound.state)&quot;
+        fi
+        exit 0
+    else
+        echo &quot;No ALSA sound cards loaded.&quot;
+        exit 3
+    fi
+    ;;
   *)
-	echo &quot;Usage: $MYNAME {start [CARD]|stop [CARD]|restart [CARD]|reset [CARD]}&quot; &gt;&amp;2
-	exit 3
-	;;
+    echo &quot;Usage: $MYNAME {start [CARD]|stop [CARD]|restart [CARD]|reset [CARD]|status}&quot; &gt;&amp;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</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_alsa-utils/0002*
From d545385298e4b993cab8bcb6514b997ab3e2f544 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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
&quot;can&#039;t play WAVE-files with sample 64 bits wide&quot;, 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 -&gt; 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 &quot;Unsupported bit size 64&quot;. 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(_(&quot; can&#039;t play WAVE-files with sample %d bits wide&quot;),
 		      TO_CPU_SHORT(f-&gt;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-- &gt; 0) {
+				if (format_little_endian)
+					fval = *fvalp;
+				else {
+					uint32_t tmp = be32toh(*(uint32_t *)fvalp);
+					memcpy(&amp;fval, &amp;tmp, sizeof(fval));
+				}
+				fval = fabsf(fval);
+				if (fval &gt; 1.0f)
+					fval = 1.0f;
+				val = (signed int)(fval * 0x7fffffff);
+				if (max_peak[c] &lt; val)
+					max_peak[c] = val;
+				fvalp++;
+				if (vumeter == VUMETER_STEREO)
+					c = !c;
+			}
+			break;
+		}
+
 		c = 0;
 		while (samples-- &gt; 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-- &gt; 0) {
+			if (format_little_endian)
+				dval = *valp;
+			else {
+				uint64_t tmp = be64toh(*(uint64_t *)valp);
+				memcpy(&amp;dval, &amp;tmp, sizeof(dval));
+			}
+			dval = fabs(dval);
+			if (dval &gt; 1.0)
+				dval = 1.0;
+			val = (signed int)(dval * 0x7fffffff);
+			if (max_peak[c] &lt; val)
+				max_peak[c] = val;
+			valp++;
+			if (vumeter == VUMETER_STEREO)
+				c = !c;
+		}
+		break;
+	}
 	default:
 		if (run == 0) {
 			fprintf(stderr, _(&quot;Unsupported bit size %d.\n&quot;), (int)bits_per_sample);
@@ -1895,7 +1953,10 @@ static void compute_max_peak(u_char *data, size_t samples)
 		}
 		return;
 	}
-	max = 1 &lt;&lt; (significant_bits_per_sample-1);
+	if (snd_pcm_format_float(hwparams.format))
+		max = 0x7fffffff;
+	else
+		max = 1 &lt;&lt; (significant_bits_per_sample-1);
 	if (max &lt;= 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.5</code></pre></div><p><strong>3. PATCHES for fftrate</strong></p><div class="codebox"><pre><code>$ 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</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_fftrate/0001*
From 8e94e8fc09b7925fc99a6d4b6d7ebde4646a3bb8 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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 &quot;unsupported&quot;.

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-&gt;format_tag	= WAVE_FORMAT_IEEE_FLOAT;
+		data-&gt;sample_size	= 4;
+		data-&gt;dummy			= 0;
+		data-&gt;fill			= 0x00;
+		fmt_name			= &quot;float_le&quot;;
+		break;
+
 	case SND_PCM_FORMAT_FLOAT64_LE:
+		data-&gt;format_tag	= WAVE_FORMAT_IEEE_FLOAT;
+		data-&gt;sample_size	= 8;
+		data-&gt;dummy			= 0;
+		data-&gt;fill			= 0x00;
+		fmt_name			= &quot;float64_le&quot;;
+		break;
+
 	default:
 		fmt_name			= &quot;unsupported&quot;;
 		break;
-- 
2.39.5</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_fftrate/0002*
From 3f4f64d105ca0ed76270051cea5688e5a9369581 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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 &gt;= 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 &lt;stdio.h&gt;
 #include &lt;stdarg.h&gt;
+#include &lt;stddef.h&gt;   /* offsetof */
 
 #include &lt;alsa/asoundlib.h&gt;
 #include &lt;alsa/pcm_rate.h&gt;
@@ -417,15 +418,22 @@ static int pcm_rate_init( void *obj, snd_pcm_rate_info_t *info )
 
 		//--------------------------------------------------------------------------------------------
 
-		if( ( in_rate * info-&gt;out.period_size ) != ( out_rate * info-&gt;in.period_size ) )
 		{
-			double l_in  = log2( (double)in_rate  / (double)info-&gt;in.period_size );
-			double l_out = log2( (double)out_rate / (double)info-&gt;out.period_size );
-			int   fpp   = (int)( 0.5 * ( l_in + l_out ) );
+			uint64_t prod_in  = (uint64_t)in_rate  * info-&gt;out.period_size;
+			uint64_t prod_out = (uint64_t)out_rate * info-&gt;in.period_size;
 
-			fpp = MAX( fpp, 0 );
-			in_rate	 = info-&gt;in.period_size  &lt;&lt; fpp;
-			out_rate = info-&gt;out.period_size &lt;&lt; fpp;
+			fprintf( OUTF, &quot;Period ratio: %.6f\n&quot;, (double)prod_in / (double)prod_out );
+
+			if( prod_in != prod_out )
+				{
+					double l_in  = log2( (double)in_rate  / (double)info-&gt;in.period_size );
+					double l_out = log2( (double)out_rate / (double)info-&gt;out.period_size );
+					int   fpp   = (int)( 0.5 * ( l_in + l_out ) );
+
+					fpp = MAX( fpp, 0 );
+						in_rate	 = (int)( (uint64_t)info-&gt;in.period_size  &lt;&lt; fpp );
+						out_rate = (int)( (uint64_t)info-&gt;out.period_size &lt;&lt; fpp );
+				}
 		}
 
 		//--------------------------------------------------------------------------------------------
@@ -783,6 +791,30 @@ static void pcm_rate_dump( void *obj, snd_output_t *out )
 }
 #endif
 
+#if SND_PCM_RATE_PLUGIN_VERSION &gt;= 0x010003
+static int pcm_rate_formats
+(
+	void			*obj,
+	uint64_t		*in_formats,
+	uint64_t		*out_formats,
+	unsigned int	*flags
+)
+{
+	dbg_printf( &quot;[pcm_rate_formats]\n&quot; );
+
+	*in_formats = *out_formats =
+		  ( 1ULL &lt;&lt; SND_PCM_FORMAT_U8 )
+		| ( 1ULL &lt;&lt; SND_PCM_FORMAT_S16_LE )
+		| ( 1ULL &lt;&lt; SND_PCM_FORMAT_S24_3LE )
+		| ( 1ULL &lt;&lt; SND_PCM_FORMAT_S24_LE )
+		| ( 1ULL &lt;&lt; SND_PCM_FORMAT_S32_LE )
+		| ( 1ULL &lt;&lt; SND_PCM_FORMAT_FLOAT_LE )
+		| ( 1ULL &lt;&lt; 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 &gt;= 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 &gt;= 0x010002
-	if( version == 0x010001 )
-		arrcpy_bytes( ops, &amp;pcm_rate_ops, sizeof(snd_pcm_rate_old_ops_t) )
-	else
-#endif
+#if SND_PCM_RATE_PLUGIN_VERSION &gt;= 0x010002  
+	if( version == 0x010001 )  
+	{  
+		arrcpy_bytes( ops, &amp;pcm_rate_ops, sizeof(snd_pcm_rate_old_ops_t) );  
+	}  
+	else  
+	if( version == 0x010002 )  
+	{  
+		arrcpy_bytes( ops, &amp;pcm_rate_ops, sizeof(snd_pcm_rate_v2_ops_t) );  
+	}  
+	else  
+#endif  
 		*ops = pcm_rate_ops;
 
 	return 0;
-- 
2.39.5</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat PATCHES_fftrate/0003*
From 120e746adc3f5e5ea0ea43fa110cbba6427e21c3 Mon Sep 17 00:00:00 2001
From: igorzwx &lt;igorzwx@devuan.cargo-cult.org&gt;
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 -&gt; 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
&#039;Ok.&#039;
---
 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		&quot;convert.wav&quot;
-#define BUFFER_SIZE			65636L
+#define BUFFER_SIZE			4194304L	/* 4 MiB: large enough for high-ratio
+						 * FFT windows (e.g. DSD512-rate simulation,
+						 * 352800 Hz -&gt; 5644800 Hz, 64-bit float) */

 //=============================================================================
-- 
2.39.5</code></pre></div><p><strong>TEST</strong></p><div class="codebox"><pre><code>$ aplay -V s test*
Playing WAVE &#039;test_f64.wav&#039; : Float 64 bit Little Endian, Rate 192000 Hz, Stereo
Input:  192000 Hz, 2 ch, &#039;float64_le&#039; (0x10): dummy = 0, period = 7680
Output: 192000 Hz, 2 ch, &#039;s32_le&#039; (0xa): dummy = 0, period = 7680
Period ratio: 1.000000
Rates:  192000 --&gt; 192000 (J: 0.00%, T: None, W: Planar)
Ok.

                   +###############48%|49%################+                  </code></pre></div><div class="codebox"><pre><code>$ aplay -V s *Float.wav
Playing WAVE &#039;32bitFloat.wav&#039; : Float 32 bit Little Endian, Rate 352800 Hz, Stereo
Input:  352800 Hz, 2 ch, &#039;float_le&#039; (0xe): dummy = 0, period = 14112
Output: 192000 Hz, 2 ch, &#039;s32_le&#039; (0xa): dummy = 0, period = 7680
Period ratio: 1.000000
Rates:  352800 --&gt; 192000 (J: 0.00%, T: FFT, W: Vorbis)
Ok.

                       +      #####36%|49%##########      +                  </code></pre></div><p>Test file can be created with pcm_conv (fftrate)</p><div class="codebox"><pre><code>pcm_conv 01*.wav test_f64.wav -b 64f -f 192000 -T fft -v </code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Sat, 14 Nov 2026 21:48:40 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=66491#p66491</guid>
		</item>
	</channel>
</rss>
