<?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=7605&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Dev1 Galaxy Forum / The Absurdist Comedy]]></title>
		<link>https://dev1galaxy.org/viewtopic.php?id=7605</link>
		<description><![CDATA[The most recent posts in The Absurdist Comedy.]]></description>
		<lastBuildDate>Tue, 23 Jun 2026 19:28:41 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64381#p64381</link>
			<description><![CDATA[<p><span class="bbc">about:support</span></p><div class="codebox"><pre><code>Name 	Firefox Developer Edition
Version 	154.0a1 	
Update Channel 	aurora
User Agent 	Mozilla/5.0 (X11; Linux x86_64; rv:154.0) Gecko/20100101 Firefox/154.0
Profile Directory 	~/.firefox-dev/qxls80vg.dev-edition-default-1</code></pre></div><p>If User Agent is not set correctly with:</p><div class="codebox"><pre><code>imply_option(&quot;MOZ_APP_UA_NAME&quot;, &quot;Firefox&quot;)</code></pre></div><p>You can set it with override in <span class="bbc">about:config</span></p><div class="codebox"><pre><code>general.useragent.override      Mozilla/5.0 (X11; Linux x86_64; rv:154.0) Gecko/20100101 Firefox/154.0	</code></pre></div><div class="codebox"><pre><code>imply_option(&quot;MOZ_APP_PROFILE&quot;, &quot;firefox-dev&quot;)</code></pre></div><p><span class="bbc">MOZ_APP_PROFILE=firefox-dev</span> means User Profile Directory&#160; &#160; &#160;</p><div class="codebox"><pre><code>$HOME/.firefox-dev</code></pre></div><p>for user settings.</p><p><span class="bbc">firefox/browser/moz.configure</span> is a secret configuration file.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Tue, 23 Jun 2026 19:28:41 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64381#p64381</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64380#p64380</link>
			<description><![CDATA[<p>thanks, really comofrtable, should be nice feature.</p><p>hae however built according to your instructions, but sadly with&#160; patch 1 &amp;&#160; 2 error (<del>not your fault</del>); </p><p>will the resulting &quot;firefox-dev-ed_154.0a1-1_amd64&quot; be blemished forever? are those patches needed?</p>]]></description>
			<author><![CDATA[dummy@example.com (kapqa)]]></author>
			<pubDate>Tue, 23 Jun 2026 18:41:38 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64380#p64380</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64379#p64379</link>
			<description><![CDATA[<div class="quotebox"><blockquote><div><p>does the firefox develper auto-update?</p></div></blockquote></div><p>Yes, it does (by default). You can disable/enable this feature is <span class="bbc">about:config</span>, or disable and lock in a secret config. </p><div class="codebox"><pre><code>$ cat /etc/firefox-dev/defaults/pref/firefox-dev.cfg3-2.js
// Debian system-wide preferences for Firefox Developer Edition
// This file is intended for system administrators to configure default settings.
// Use &#039;pref(&quot;preference.name&quot;, value);&#039; for defaults.
// Use &#039;pref(&quot;preference.name&quot;, value, locked);&#039; to enforce immutable settings.

// Enable Remote Improvements - Nimbus rollouts system
pref(&quot;nimbus.rollouts.enabled&quot;, true);
pref(&quot;services.sync.prefs.sync.nimbus.rollouts.enabled&quot;, false, locked);

// Enable automatic updates  
pref(&quot;app.update.auto&quot;, true);  
pref(&quot;app.update.enabled&quot;, true);
pref(&quot;app.update.background.enabled&quot;, true);

// Disable Remote Improvements - Nimbus rollouts system
// pref(&quot;nimbus.rollouts.enabled&quot;, false, locked);
// pref(&quot;services.sync.prefs.sync.nimbus.rollouts.enabled&quot;, false, locked);

// Disable automatic updates  
// pref(&quot;app.update.auto&quot;, false, locked);  
// pref(&quot;app.update.enabled&quot;, false, locked);
// pref(&quot;app.update.background.enabled&quot;, false, locked); </code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Tue, 23 Jun 2026 18:27:56 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64379#p64379</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64345#p64345</link>
			<description><![CDATA[<p>thanks, i am not needing zoom atm, but might later one and will test.<br />have not understood your patches, but can hopefully adapt to it.</p><p>what does it mean &quot;firefox developer edition is being updated by another instance&quot;?</p><p>does the firefox develper auto-update?</p>]]></description>
			<author><![CDATA[dummy@example.com (kapqa)]]></author>
			<pubDate>Fri, 19 Jun 2026 17:55:22 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64345#p64345</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64344#p64344</link>
			<description><![CDATA[<p>Patch 1 &amp; 2<br />Sourcedir: firefox<br />FILE: firefox/browser/moz.configure</p><div class="codebox"><pre><code> imply_option(&quot;MOZ_DEVTOOLS&quot;, &quot;all&quot;)
 imply_option(&quot;BROWSER_CHROME_URL&quot;, &quot;chrome://browser/content/browser.xhtml&quot;)
+imply_option(&quot;MOZ_APP_UA_NAME&quot;, &quot;Firefox&quot;)
+imply_option(&quot;MOZ_APP_PROFILE&quot;, &quot;firefox-dev&quot;) </code></pre></div><p>You should simply add two lines:</p><div class="codebox"><pre><code> imply_option(&quot;MOZ_APP_UA_NAME&quot;, &quot;Firefox&quot;)
 imply_option(&quot;MOZ_APP_PROFILE&quot;, &quot;firefox-dev&quot;)</code></pre></div><p>after other &quot;imply_option&quot; lines. </p><div class="codebox"><pre><code>$ cat firefox/browser/moz.configure 
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

imply_option(&quot;MOZ_PLACES&quot;, True)
imply_option(&quot;MOZ_SERVICES_HEALTHREPORT&quot;, True)
imply_option(&quot;MOZ_SERVICES_SYNC&quot;, True)
imply_option(&quot;MOZ_DEDICATED_PROFILES&quot;, True)
imply_option(&quot;MOZ_BLOCK_PROFILE_DOWNGRADE&quot;, True)
imply_option(&quot;MOZ_NORMANDY&quot;, True)
imply_option(&quot;MOZ_PROFILE_MIGRATOR&quot;, True)

imply_option(&quot;MOZ_APP_VENDOR&quot;, &quot;Mozilla&quot;)
imply_option(&quot;MOZ_APP_ID&quot;, &quot;{ec8030f7-c20a-464f-9b0e-13a3a9e97384}&quot;)
# Include the DevTools client, not just the server (which is the default)
imply_option(&quot;MOZ_DEVTOOLS&quot;, &quot;all&quot;)
imply_option(&quot;BROWSER_CHROME_URL&quot;, &quot;chrome://browser/content/browser.xhtml&quot;)
imply_option(&quot;MOZ_APP_UA_NAME&quot;, &quot;Firefox&quot;)
imply_option(&quot;MOZ_APP_PROFILE&quot;, &quot;firefox-dev&quot;)

...</code></pre></div><p>It works with Zoom.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Fri, 19 Jun 2026 16:54:19 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64344#p64344</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64339#p64339</link>
			<description><![CDATA[<p>thank you,<br />patch 1 &amp; 2 gave error (with 2.39.5 at line 22 &amp; 23 respectively) , but the build went through ~<br />very pleased!!!<br />you made browsing a lot more pleasant ~ think if ever there be an devuan award, it should go söso<br /> toyou!</p><div class="quotebox"><blockquote><div><p>154.0a1 (2026-06-19) (64-bit)</p></div></blockquote></div><p><a href="https://ibb.co/vC5qJDMK" rel="nofollow">https://ibb.co/vC5qJDMK</a></p>]]></description>
			<author><![CDATA[dummy@example.com (kapqa)]]></author>
			<pubDate>Fri, 19 Jun 2026 14:23:48 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64339#p64339</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64289#p64289</link>
			<description><![CDATA[<p>Why should they accept a patch that has not been tested by ALSA users?</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Sat, 13 Jun 2026 22:17:16 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64289#p64289</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64287#p64287</link>
			<description><![CDATA[<p>I think it would be wonderful if the LibreWolf developers could be persuaded to adopt this.</p>]]></description>
			<author><![CDATA[dummy@example.com (pcalvert)]]></author>
			<pubDate>Sat, 13 Jun 2026 20:52:14 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64287#p64287</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64286#p64286</link>
			<description><![CDATA[<p>Updated the guide on building Firefox with the ALSA backend patch: <br />&quot;Firefox: Dialectics of Antagonistic Security Bugs&quot;<br />_https://dev1galaxy.org/viewtopic.php?id=8012</p><p><strong>NOTE:</strong> The Debian package is created using the <span class="bbc">make_deb.sh</span> script. </p><p>If the build is successful, you can test it with Zoom.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Sat, 13 Jun 2026 19:51:57 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64286#p64286</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64284#p64284</link>
			<description><![CDATA[<p>have been following your instructions, </p><p>that you posted early on how to build firefox;<br />but missed lately the ones for creating a .deb file ~</p><p>so probably the .cubeb thing is not implemented ~ hence , a half-baked solution?</p><p><a href="https://ibb.co/bgVJ6KBt" rel="nofollow">https://ibb.co/bgVJ6KBt</a></p>]]></description>
			<author><![CDATA[dummy@example.com (kapqa)]]></author>
			<pubDate>Sat, 13 Jun 2026 17:52:24 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64284#p64284</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64283#p64283</link>
			<description><![CDATA[<p><strong>Guide on building Firefox: </strong><br />_https://dev1galaxy.org/viewtopic.php?id=8012</p><p>Since applying patches can be confusing, simply replace the existing file with the new one:</p><p>1. Locate the original file in your Firefox source code: <br /><span class="bbc">firefox/media/libcubeb/src/cubeb_alsa.c</span><br />2. Delete or backup the old file. <br />3. Copy the new <span class="bbc">cubeb_alsa.c</span> file (the fully functional version) into the same directory.<br />4. Proceed with the build process as usual.<br />This avoids the need for patch commands entirely. </p><p><strong>Firefox/cubeb ALSA backend</strong><br /><span class="bbc">firefox/media/libcubeb/src/cubeb_alsa.c</span></p><div class="codebox"><pre class="vscroll"><code>/*
 * Copyright © 2011 Mozilla Foundation
 *
 * This program is made available under an ISC-style license.  See the
 * accompanying file LICENSE for details.
 */
#undef NDEBUG
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#if defined(__NetBSD__)
#define _NETBSD_SOURCE /* timersub() */
#endif
#define _XOPEN_SOURCE 500
#include &quot;cubeb-internal.h&quot;
#include &quot;cubeb/cubeb.h&quot;
#include &quot;cubeb_tracing.h&quot;
#include &lt;alsa/asoundlib.h&gt;
#include &lt;assert.h&gt;
#include &lt;dlfcn.h&gt;
#include &lt;limits.h&gt;
#include &lt;poll.h&gt;
#include &lt;pthread.h&gt;
#include &lt;sys/time.h&gt;
#include &lt;unistd.h&gt;

#ifdef DISABLE_LIBASOUND_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*cubeb_##x)
#define LIBASOUND_API_VISIT(X)                                                 \
  X(snd_config)                                                                \
  X(snd_config_add)                                                            \
  X(snd_config_copy)                                                           \
  X(snd_config_delete)                                                         \
  X(snd_config_get_id)                                                         \
  X(snd_config_get_string)                                                     \
  X(snd_config_imake_integer)                                                  \
  X(snd_config_search)                                                         \
  X(snd_config_search_definition)                                              \
  X(snd_lib_error_set_handler)                                                 \
  X(snd_pcm_avail_update)                                                      \
  X(snd_pcm_close)                                                             \
  X(snd_pcm_delay)                                                             \
  X(snd_pcm_drain)                                                             \
  X(snd_pcm_frames_to_bytes)                                                   \
  X(snd_pcm_get_params)                                                        \
  X(snd_pcm_hw_params_any)                                                     \
  X(snd_pcm_hw_params_get_channels_max)                                        \
  X(snd_pcm_hw_params_get_rate)                                                \
  X(snd_pcm_hw_params_set_rate_near)                                           \
  X(snd_pcm_hw_params_sizeof)                                                  \
  X(snd_pcm_nonblock)                                                          \
  X(snd_pcm_open)                                                              \
  X(snd_pcm_open_lconf)                                                        \
  X(snd_pcm_pause)                                                             \
  X(snd_pcm_poll_descriptors)                                                  \
  X(snd_pcm_poll_descriptors_count)                                            \
  X(snd_pcm_poll_descriptors_revents)                                          \
  X(snd_pcm_readi)                                                             \
  X(snd_pcm_recover)                                                           \
  X(snd_pcm_set_params)                                                        \
  X(snd_pcm_start)                                                             \
  X(snd_pcm_state)                                                             \
  X(snd_pcm_writei)

#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
/* snd_pcm_hw_params_alloca is actually a macro */
#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
#endif

#define CUBEB_STREAM_MAX 16
#define CUBEB_WATCHDOG_MS 10000

#define CUBEB_ALSA_PCM_NAME &quot;default&quot;

#define ALSA_PA_PLUGIN &quot;ALSA &lt;-&gt; PulseAudio PCM I/O Plugin&quot;

/* ALSA is not thread-safe.  snd_pcm_t instances are individually protected
   by the owning cubeb_stream&#039;s mutex.  snd_pcm_t creation and destruction
   is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
   so those calls must be wrapped in the following mutex. */
static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
static int cubeb_alsa_error_handler_set = 0;

static struct cubeb_ops const alsa_ops;

struct cubeb {
  struct cubeb_ops const * ops;
  void * libasound;

  pthread_t thread;

  /* Mutex for streams array, must not be held while blocked in poll(2). */
  pthread_mutex_t mutex;

  /* Sparse array of streams managed by this context. */
  cubeb_stream * streams[CUBEB_STREAM_MAX];

  /* fds and nfds are only updated by alsa_run when rebuild is set. */
  struct pollfd * fds;
  nfds_t nfds;
  int rebuild;

  int shutdown;

  /* Control pipe for forcing poll to wake and rebuild fds or recalculate the
   * timeout. */
  int control_fd_read;
  int control_fd_write;

  /* Track number of active streams.  This is limited to CUBEB_STREAM_MAX
     due to resource contraints. */
  unsigned int active_streams;

  /* Local configuration with handle_underrun workaround set for PulseAudio
     ALSA plugin.  Will be NULL if the PA ALSA plugin is not in use or the
     workaround is not required. */
  snd_config_t * local_config;
  int is_pa;
};

enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };

struct cubeb_stream {
  /* Note: Must match cubeb_stream layout in cubeb.c. */
  cubeb * context;
  void * user_ptr;
  /**/
  pthread_mutex_t mutex;
  snd_pcm_t * pcm;
  cubeb_data_callback data_callback;
  cubeb_state_callback state_callback;
  snd_pcm_uframes_t stream_position;
  snd_pcm_uframes_t last_position;
  snd_pcm_uframes_t buffer_size;
  cubeb_stream_params params;

  /* Every member after this comment is protected by the owning context&#039;s
     mutex rather than the stream&#039;s mutex, or is only used on the context&#039;s
     run thread. */
  pthread_cond_t cond; /* Signaled when the stream&#039;s state is changed. */

  enum stream_state state;

  struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
  struct pollfd *
      fds; /* Pointer to this waitable&#039;s pollfds within struct cubeb&#039;s fds. */
  nfds_t nfds;

  struct timeval drain_timeout;

  /* XXX: Horrible hack -- if an active stream has been idle for
     CUBEB_WATCHDOG_MS it will be disabled and the error callback will be
     called.  This works around a bug seen with older versions of ALSA and
     PulseAudio where streams would stop requesting new data despite still
     being logically active and playing. */
  struct timeval last_activity;
  float volume;

  char * buffer;
  snd_pcm_uframes_t bufframes;
  snd_pcm_stream_t stream_type;

  struct cubeb_stream * other_stream;
};

static int
any_revents(struct pollfd * fds, nfds_t nfds)
{
  nfds_t i;

  for (i = 0; i &lt; nfds; ++i) {
    if (fds[i].revents) {
      return 1;
    }
  }

  return 0;
}

static int
cmp_timeval(struct timeval * a, struct timeval * b)
{
  if (a-&gt;tv_sec == b-&gt;tv_sec) {
    if (a-&gt;tv_usec == b-&gt;tv_usec) {
      return 0;
    }
    return a-&gt;tv_usec &gt; b-&gt;tv_usec ? 1 : -1;
  }
  return a-&gt;tv_sec &gt; b-&gt;tv_sec ? 1 : -1;
}

static int
timeval_to_relative_ms(struct timeval * tv)
{
  struct timeval now;
  struct timeval dt;
  long long t;
  int r;

  gettimeofday(&amp;now, NULL);
  r = cmp_timeval(tv, &amp;now);
  if (r &gt;= 0) {
    timersub(tv, &amp;now, &amp;dt);
  } else {
    timersub(&amp;now, tv, &amp;dt);
  }
  t = dt.tv_sec;
  t *= 1000;
  t += (dt.tv_usec + 500) / 1000;

  if (t &gt; INT_MAX) {
    t = INT_MAX;
  } else if (t &lt; INT_MIN) {
    t = INT_MIN;
  }

  return r &gt;= 0 ? t : -t;
}

static int
ms_until(struct timeval * tv)
{
  return timeval_to_relative_ms(tv);
}

static int
ms_since(struct timeval * tv)
{
  return -timeval_to_relative_ms(tv);
}

static void
rebuild(cubeb * ctx)
{
  nfds_t nfds;
  int i;
  nfds_t j;
  cubeb_stream * stm;

  assert(ctx-&gt;rebuild);

  /* Always count context&#039;s control pipe fd. */
  nfds = 1;
  for (i = 0; i &lt; CUBEB_STREAM_MAX; ++i) {
    stm = ctx-&gt;streams[i];
    if (stm) {
      stm-&gt;fds = NULL;
      if (stm-&gt;state == RUNNING) {
        nfds += stm-&gt;nfds;
      }
    }
  }

  free(ctx-&gt;fds);
  ctx-&gt;fds = calloc(nfds, sizeof(struct pollfd));
  assert(ctx-&gt;fds);
  ctx-&gt;nfds = nfds;

  /* Include context&#039;s control pipe fd. */
  ctx-&gt;fds[0].fd = ctx-&gt;control_fd_read;
  ctx-&gt;fds[0].events = POLLIN | POLLERR;

  for (i = 0, j = 1; i &lt; CUBEB_STREAM_MAX; ++i) {
    stm = ctx-&gt;streams[i];
    if (stm &amp;&amp; stm-&gt;state == RUNNING) {
      memcpy(&amp;ctx-&gt;fds[j], stm-&gt;saved_fds, stm-&gt;nfds * sizeof(struct pollfd));
      stm-&gt;fds = &amp;ctx-&gt;fds[j];
      j += stm-&gt;nfds;
    }
  }

  ctx-&gt;rebuild = 0;
}

static void
poll_wake(cubeb * ctx)
{
  if (write(ctx-&gt;control_fd_write, &quot;x&quot;, 1) &lt; 0) {
    /* ignore write error */
  }
}

static void
set_timeout(struct timeval * timeout, unsigned int ms)
{
  gettimeofday(timeout, NULL);
  timeout-&gt;tv_sec += ms / 1000;
  timeout-&gt;tv_usec += (ms % 1000) * 1000;
}

static void
stream_buffer_decrement(cubeb_stream * stm, long count)
{
  if (count &lt; 0 || (snd_pcm_uframes_t)count &gt; stm-&gt;bufframes) {
    count = stm-&gt;bufframes;
  }
  char * bufremains =
      stm-&gt;buffer + WRAP(snd_pcm_frames_to_bytes)(stm-&gt;pcm, count);
  memmove(stm-&gt;buffer, bufremains,
          WRAP(snd_pcm_frames_to_bytes)(stm-&gt;pcm, stm-&gt;bufframes - count));
  stm-&gt;bufframes -= count;
}

static void
alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
{
  cubeb * ctx;
  int r;

  ctx = stm-&gt;context;
  stm-&gt;state = state;
  r = pthread_cond_broadcast(&amp;stm-&gt;cond);
  assert(r == 0);
  ctx-&gt;rebuild = 1;
  poll_wake(ctx);
}

static enum stream_state
alsa_process_stream(cubeb_stream * stm)
{
  unsigned short revents;
  snd_pcm_sframes_t avail;
  int draining;

  draining = 0;

  pthread_mutex_lock(&amp;stm-&gt;mutex);

  /* Call _poll_descriptors_revents() even if we don&#039;t use it
     to let underlying plugins clear null events.  Otherwise poll()
     may wake up again and again, producing unnecessary CPU usage. */
  WRAP(snd_pcm_poll_descriptors_revents)
  (stm-&gt;pcm, stm-&gt;fds, stm-&gt;nfds, &amp;revents);

  avail = WRAP(snd_pcm_avail_update)(stm-&gt;pcm);

  /* Got null event? Bail and wait for another wakeup. */
  if (avail == 0) {
    pthread_mutex_unlock(&amp;stm-&gt;mutex);
    return RUNNING;
  }

  /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
   */
  if ((unsigned int)avail &gt; stm-&gt;buffer_size) {
    avail = stm-&gt;buffer_size;
  }

  /* Capture: Read available frames */
  if (stm-&gt;stream_type == SND_PCM_STREAM_CAPTURE &amp;&amp; avail &gt; 0) {
    snd_pcm_sframes_t got;

    if (avail + stm-&gt;bufframes &gt; stm-&gt;buffer_size) {
      /* Buffer overflow. Skip and overwrite with new data. */
      stm-&gt;bufframes = 0;
      // TODO: should it be marked as DRAINING?
    }

    got = WRAP(snd_pcm_readi)(stm-&gt;pcm, stm-&gt;buffer + stm-&gt;bufframes, avail);

    if (got &lt; 0) {
      avail = got; // the error handler below will recover us
    } else {
      stm-&gt;bufframes += got;
      stm-&gt;stream_position += got;

      gettimeofday(&amp;stm-&gt;last_activity, NULL);
    }
  }

  /* Capture: Pass read frames to callback function */
  if (stm-&gt;stream_type == SND_PCM_STREAM_CAPTURE &amp;&amp; stm-&gt;bufframes &gt; 0 &amp;&amp;
      (!stm-&gt;other_stream ||
       stm-&gt;other_stream-&gt;bufframes &lt; stm-&gt;other_stream-&gt;buffer_size)) {
    snd_pcm_sframes_t wrote = stm-&gt;bufframes;
    struct cubeb_stream * mainstm = stm-&gt;other_stream ? stm-&gt;other_stream : stm;
    void * other_buffer = stm-&gt;other_stream ? stm-&gt;other_stream-&gt;buffer +
                                                  stm-&gt;other_stream-&gt;bufframes
                                            : NULL;

    /* Correct write size to the other stream available space */
    if (stm-&gt;other_stream &amp;&amp;
        wrote &gt; (snd_pcm_sframes_t)(stm-&gt;other_stream-&gt;buffer_size -
                                    stm-&gt;other_stream-&gt;bufframes)) {
      wrote = stm-&gt;other_stream-&gt;buffer_size - stm-&gt;other_stream-&gt;bufframes;
    }

    pthread_mutex_unlock(&amp;stm-&gt;mutex);
    wrote = stm-&gt;data_callback(mainstm, stm-&gt;user_ptr, stm-&gt;buffer,
                               other_buffer, wrote);
    pthread_mutex_lock(&amp;stm-&gt;mutex);

    if (wrote &lt; 0) {
      avail = wrote; // the error handler below will recover us
    } else {
      stream_buffer_decrement(stm, wrote);

      if (stm-&gt;other_stream) {
        stm-&gt;other_stream-&gt;bufframes += wrote;
      }
    }
  }

  /* Playback: Don&#039;t have enough data? Let&#039;s ask for more. */
  if (stm-&gt;stream_type == SND_PCM_STREAM_PLAYBACK &amp;&amp;
      avail &gt; (snd_pcm_sframes_t)stm-&gt;bufframes &amp;&amp;
      (!stm-&gt;other_stream || stm-&gt;other_stream-&gt;bufframes &gt; 0)) {
    long got = avail - stm-&gt;bufframes;
    void * other_buffer = stm-&gt;other_stream ? stm-&gt;other_stream-&gt;buffer : NULL;
    char * buftail =
        stm-&gt;buffer + WRAP(snd_pcm_frames_to_bytes)(stm-&gt;pcm, stm-&gt;bufframes);

    /* Correct read size to the other stream available frames */
    if (stm-&gt;other_stream &amp;&amp;
        got &gt; (snd_pcm_sframes_t)stm-&gt;other_stream-&gt;bufframes) {
      got = stm-&gt;other_stream-&gt;bufframes;
    }

    pthread_mutex_unlock(&amp;stm-&gt;mutex);
    got = stm-&gt;data_callback(stm, stm-&gt;user_ptr, other_buffer, buftail, got);
    pthread_mutex_lock(&amp;stm-&gt;mutex);

    if (got &lt; 0) {
      avail = got; // the error handler below will recover us
    } else {
      stm-&gt;bufframes += got;

      if (stm-&gt;other_stream) {
        stream_buffer_decrement(stm-&gt;other_stream, got);
      }
    }
  }

  /* Playback: Still don&#039;t have enough data? Add some silence. */
  if (stm-&gt;stream_type == SND_PCM_STREAM_PLAYBACK &amp;&amp;
      avail &gt; (snd_pcm_sframes_t)stm-&gt;bufframes) {
    long drain_frames = avail - stm-&gt;bufframes;
    double drain_time = (double)drain_frames / stm-&gt;params.rate;

    char * buftail =
        stm-&gt;buffer + WRAP(snd_pcm_frames_to_bytes)(stm-&gt;pcm, stm-&gt;bufframes);
    memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm-&gt;pcm, drain_frames));
    stm-&gt;bufframes = avail;

    /* Mark as draining, unless we&#039;re waiting for capture */
    if (!stm-&gt;other_stream || stm-&gt;other_stream-&gt;bufframes &gt; 0) {
      set_timeout(&amp;stm-&gt;drain_timeout, drain_time * 1000);

      draining = 1;
    }
  }

  /* Playback: Have enough data and no errors. Let&#039;s write it out. */
  if (stm-&gt;stream_type == SND_PCM_STREAM_PLAYBACK &amp;&amp; avail &gt; 0) {
    snd_pcm_sframes_t wrote;

    if (stm-&gt;params.format == CUBEB_SAMPLE_FLOAT32NE) {
      float * b = (float *)stm-&gt;buffer;
      for (uint32_t i = 0; i &lt; avail * stm-&gt;params.channels; i++) {
        b[i] *= stm-&gt;volume;
      }
    } else {
      short * b = (short *)stm-&gt;buffer;
      for (uint32_t i = 0; i &lt; avail * stm-&gt;params.channels; i++) {
        b[i] *= stm-&gt;volume;
      }
    }

    wrote = WRAP(snd_pcm_writei)(stm-&gt;pcm, stm-&gt;buffer, avail);
    if (wrote &lt; 0) {
      avail = wrote; // the error handler below will recover us
    } else {
      stream_buffer_decrement(stm, wrote);

      stm-&gt;stream_position += wrote;
      gettimeofday(&amp;stm-&gt;last_activity, NULL);
    }
  }

  /* Got some error? Let&#039;s try to recover the stream. */
  if (avail &lt; 0) {
    avail = WRAP(snd_pcm_recover)(stm-&gt;pcm, avail, 0);

    /* Capture pcm must be started after initial setup/recover */
    if (avail &gt;= 0 &amp;&amp; stm-&gt;stream_type == SND_PCM_STREAM_CAPTURE &amp;&amp;
        WRAP(snd_pcm_state)(stm-&gt;pcm) == SND_PCM_STATE_PREPARED) {
      avail = WRAP(snd_pcm_start)(stm-&gt;pcm);
    }
  }

  /* Failed to recover, this stream must be broken. */
  if (avail &lt; 0) {
    pthread_mutex_unlock(&amp;stm-&gt;mutex);
    stm-&gt;state_callback(stm, stm-&gt;user_ptr, CUBEB_STATE_ERROR);
    return ERROR;
  }

  pthread_mutex_unlock(&amp;stm-&gt;mutex);
  return draining ? DRAINING : RUNNING;
}

static int
alsa_run(cubeb * ctx)
{
  int r;
  int timeout;
  int i;
  char dummy;
  cubeb_stream * stm;
  enum stream_state state;

  pthread_mutex_lock(&amp;ctx-&gt;mutex);

  if (ctx-&gt;rebuild) {
    rebuild(ctx);
  }

  /* Wake up at least once per second for the watchdog. */
  timeout = 1000;
  for (i = 0; i &lt; CUBEB_STREAM_MAX; ++i) {
    stm = ctx-&gt;streams[i];
    if (stm &amp;&amp; stm-&gt;state == DRAINING) {
      r = ms_until(&amp;stm-&gt;drain_timeout);
      if (r &gt;= 0 &amp;&amp; timeout &gt; r) {
        timeout = r;
      }
    }
  }

  pthread_mutex_unlock(&amp;ctx-&gt;mutex);
  r = poll(ctx-&gt;fds, ctx-&gt;nfds, timeout);
  pthread_mutex_lock(&amp;ctx-&gt;mutex);

  if (r &gt; 0) {
    if (ctx-&gt;fds[0].revents &amp; POLLIN) {
      if (read(ctx-&gt;control_fd_read, &amp;dummy, 1) &lt; 0) {
        /* ignore read error */
      }

      if (ctx-&gt;shutdown) {
        pthread_mutex_unlock(&amp;ctx-&gt;mutex);
        return -1;
      }
    }

    for (i = 0; i &lt; CUBEB_STREAM_MAX; ++i) {
      stm = ctx-&gt;streams[i];
      /* We can&#039;t use snd_pcm_poll_descriptors_revents here because of
         https://github.com/kinetiknz/cubeb/issues/135. */
      if (stm &amp;&amp; stm-&gt;state == RUNNING &amp;&amp; stm-&gt;fds &amp;&amp;
          any_revents(stm-&gt;fds, stm-&gt;nfds)) {
        alsa_set_stream_state(stm, PROCESSING);
        pthread_mutex_unlock(&amp;ctx-&gt;mutex);
        state = alsa_process_stream(stm);
        pthread_mutex_lock(&amp;ctx-&gt;mutex);
        alsa_set_stream_state(stm, state);
      }
    }
  } else if (r == 0) {
    for (i = 0; i &lt; CUBEB_STREAM_MAX; ++i) {
      stm = ctx-&gt;streams[i];
      if (stm) {
        if (stm-&gt;state == DRAINING &amp;&amp; ms_since(&amp;stm-&gt;drain_timeout) &gt;= 0) {
          alsa_set_stream_state(stm, INACTIVE);
          stm-&gt;state_callback(stm, stm-&gt;user_ptr, CUBEB_STATE_DRAINED);
        } else if (stm-&gt;state == RUNNING &amp;&amp;
                   ms_since(&amp;stm-&gt;last_activity) &gt; CUBEB_WATCHDOG_MS) {
          alsa_set_stream_state(stm, ERROR);
          stm-&gt;state_callback(stm, stm-&gt;user_ptr, CUBEB_STATE_ERROR);
        }
      }
    }
  }

  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  return 0;
}

static void *
alsa_run_thread(void * context)
{
  cubeb * ctx = context;
  int r;

  CUBEB_REGISTER_THREAD(&quot;cubeb rendering thread&quot;);

  do {
    r = alsa_run(ctx);
  } while (r &gt;= 0);

  CUBEB_UNREGISTER_THREAD();

  return NULL;
}

static snd_config_t *
get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
{
  int r;
  snd_config_t * slave_pcm;
  snd_config_t * slave_def;
  snd_config_t * pcm;
  char const * string;
  char node_name[64];

  slave_def = NULL;

  r = WRAP(snd_config_search)(root_pcm, &quot;slave&quot;, &amp;slave_pcm);
  if (r &lt; 0) {
    return NULL;
  }

  r = WRAP(snd_config_get_string)(slave_pcm, &amp;string);
  if (r &gt;= 0) {
    r = WRAP(snd_config_search_definition)(lconf, &quot;pcm_slave&quot;, string,
                                           &amp;slave_def);
    if (r &lt; 0) {
      return NULL;
    }
  }

  do {
    r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, &quot;pcm&quot;, &amp;pcm);
    if (r &lt; 0) {
      break;
    }

    r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &amp;string);
    if (r &lt; 0) {
      break;
    }

    r = snprintf(node_name, sizeof(node_name), &quot;pcm.%s&quot;, string);
    if (r &lt; 0 || r &gt; (int)sizeof(node_name)) {
      break;
    }
    r = WRAP(snd_config_search)(lconf, node_name, &amp;pcm);
    if (r &lt; 0) {
      break;
    }

    return pcm;
  } while (0);

  if (slave_def) {
    WRAP(snd_config_delete)(slave_def);
  }

  return NULL;
}

/* Work around PulseAudio ALSA plugin bug where the PA server forces a
   higher than requested latency, but the plugin does not update its (and
   ALSA&#039;s) internal state to reflect that, leading to an immediate underrun
   situation.  Inspired by WINE&#039;s make_handle_underrun_config.
   Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05
 */
static snd_config_t *
init_local_config_with_workaround(char const * pcm_name)
{
  int r;
  snd_config_t * lconf;
  snd_config_t * pcm_node;
  snd_config_t * node;
  char const * string;
  char node_name[64];

  lconf = NULL;

  if (WRAP(snd_config) == NULL) {
    return NULL;
  }

  r = WRAP(snd_config_copy)(&amp;lconf, WRAP(snd_config));
  if (r &lt; 0) {
    return NULL;
  }

  do {
    r = WRAP(snd_config_search_definition)(lconf, &quot;pcm&quot;, pcm_name, &amp;pcm_node);
    if (r &lt; 0) {
      break;
    }

    r = WRAP(snd_config_get_id)(pcm_node, &amp;string);
    if (r &lt; 0) {
      break;
    }

    r = snprintf(node_name, sizeof(node_name), &quot;pcm.%s&quot;, string);
    if (r &lt; 0 || r &gt; (int)sizeof(node_name)) {
      break;
    }
    r = WRAP(snd_config_search)(lconf, node_name, &amp;pcm_node);
    if (r &lt; 0) {
      break;
    }

    /* If this PCM has a slave, walk the slave configurations until we reach the
     * bottom. */
    while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
      pcm_node = node;
    }

    /* Fetch the PCM node&#039;s type, and bail out if it&#039;s not the PulseAudio
     * plugin. */
    r = WRAP(snd_config_search)(pcm_node, &quot;type&quot;, &amp;node);
    if (r &lt; 0) {
      break;
    }

    r = WRAP(snd_config_get_string)(node, &amp;string);
    if (r &lt; 0) {
      break;
    }

    if (strcmp(string, &quot;pulse&quot;) != 0) {
      break;
    }

    /* Don&#039;t clobber an explicit existing handle_underrun value, set it only
       if it doesn&#039;t already exist. */
    r = WRAP(snd_config_search)(pcm_node, &quot;handle_underrun&quot;, &amp;node);
    if (r != -ENOENT) {
      break;
    }

    /* Disable pcm_pulse&#039;s asynchronous underrun handling. */
    r = WRAP(snd_config_imake_integer)(&amp;node, &quot;handle_underrun&quot;, 0);
    if (r &lt; 0) {
      break;
    }

    r = WRAP(snd_config_add)(pcm_node, node);
    if (r &lt; 0) {
      break;
    }

    return lconf;
  } while (0);

  WRAP(snd_config_delete)(lconf);

  return NULL;
}

static int
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
                     snd_pcm_stream_t stream, snd_config_t * local_config)
{
  int r;

  pthread_mutex_lock(&amp;cubeb_alsa_mutex);
  if (local_config) {
    r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
                                 local_config);
  } else {
    r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
  }
  pthread_mutex_unlock(&amp;cubeb_alsa_mutex);

  return r;
}

static int
alsa_locked_pcm_close(snd_pcm_t * pcm)
{
  int r;

  pthread_mutex_lock(&amp;cubeb_alsa_mutex);
  r = WRAP(snd_pcm_close)(pcm);
  pthread_mutex_unlock(&amp;cubeb_alsa_mutex);

  return r;
}

static int
alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
{
  int i;

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  for (i = 0; i &lt; CUBEB_STREAM_MAX; ++i) {
    if (!ctx-&gt;streams[i]) {
      ctx-&gt;streams[i] = stm;
      break;
    }
  }
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  return i == CUBEB_STREAM_MAX;
}

static void
alsa_unregister_stream(cubeb_stream * stm)
{
  cubeb * ctx;
  int i;

  ctx = stm-&gt;context;

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  for (i = 0; i &lt; CUBEB_STREAM_MAX; ++i) {
    if (ctx-&gt;streams[i] == stm) {
      ctx-&gt;streams[i] = NULL;
      break;
    }
  }
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);
}

static void
silent_error_handler(char const * file, int line, char const * function,
                     int err, char const * fmt, ...)
{
  (void)file;
  (void)line;
  (void)function;
  (void)err;
  (void)fmt;
}

/*static*/ int
alsa_init(cubeb ** context, char const * context_name)
{
  (void)context_name;
  void * libasound = NULL;
  cubeb * ctx;
  int r;
  int i;
  int fd[2];
  pthread_attr_t attr;
  snd_pcm_t * dummy;

  assert(context);
  *context = NULL;

#ifndef DISABLE_LIBASOUND_DLOPEN
  libasound = dlopen(&quot;libasound.so.2&quot;, RTLD_LAZY);
  if (!libasound) {
    libasound = dlopen(&quot;libasound.so&quot;, RTLD_LAZY);
    if (!libasound) {
      return CUBEB_ERROR;
    }
  }

#define LOAD(x)                                                                \
  {                                                                            \
    cubeb_##x = dlsym(libasound, #x);                                          \
    if (!cubeb_##x) {                                                          \
      dlclose(libasound);                                                      \
      return CUBEB_ERROR;                                                      \
    }                                                                          \
  }

  LIBASOUND_API_VISIT(LOAD);
#undef LOAD
#endif

  pthread_mutex_lock(&amp;cubeb_alsa_mutex);
  if (!cubeb_alsa_error_handler_set) {
    WRAP(snd_lib_error_set_handler)(silent_error_handler);
    cubeb_alsa_error_handler_set = 1;
  }
  pthread_mutex_unlock(&amp;cubeb_alsa_mutex);

  ctx = calloc(1, sizeof(*ctx));
  assert(ctx);

  ctx-&gt;ops = &amp;alsa_ops;
  ctx-&gt;libasound = libasound;

  r = pthread_mutex_init(&amp;ctx-&gt;mutex, NULL);
  assert(r == 0);

  r = pipe(fd);
  assert(r == 0);

  for (i = 0; i &lt; 2; ++i) {
    fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
    fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
  }

  ctx-&gt;control_fd_read = fd[0];
  ctx-&gt;control_fd_write = fd[1];

  /* Force an early rebuild when alsa_run is first called to ensure fds and
     nfds have been initialized. */
  ctx-&gt;rebuild = 1;

  r = pthread_attr_init(&amp;attr);
  assert(r == 0);

  r = pthread_attr_setstacksize(&amp;attr, 256 * 1024);
  assert(r == 0);

  r = pthread_create(&amp;ctx-&gt;thread, &amp;attr, alsa_run_thread, ctx);
  assert(r == 0);

  r = pthread_attr_destroy(&amp;attr);
  assert(r == 0);

  /* Open a dummy PCM to force the configuration space to be evaluated so that
     init_local_config_with_workaround can find and modify the default node. */
  r = alsa_locked_pcm_open(&amp;dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
                           NULL);
  if (r &gt;= 0) {
    alsa_locked_pcm_close(dummy);
  }
  ctx-&gt;is_pa = 0;
  pthread_mutex_lock(&amp;cubeb_alsa_mutex);
  ctx-&gt;local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
  pthread_mutex_unlock(&amp;cubeb_alsa_mutex);
  if (ctx-&gt;local_config) {
    ctx-&gt;is_pa = 1;
    r = alsa_locked_pcm_open(&amp;dummy, CUBEB_ALSA_PCM_NAME,
                             SND_PCM_STREAM_PLAYBACK, ctx-&gt;local_config);
    /* If we got a local_config, we found a PA PCM.  If opening a PCM with that
       config fails with EINVAL, the PA PCM is too old for this workaround. */
    if (r == -EINVAL) {
      pthread_mutex_lock(&amp;cubeb_alsa_mutex);
      WRAP(snd_config_delete)(ctx-&gt;local_config);
      pthread_mutex_unlock(&amp;cubeb_alsa_mutex);
      ctx-&gt;local_config = NULL;
    } else if (r &gt;= 0) {
      alsa_locked_pcm_close(dummy);
    }
  }

  *context = ctx;

  return CUBEB_OK;
}

static char const *
alsa_get_backend_id(cubeb * ctx)
{
  (void)ctx;
  return &quot;alsa&quot;;
}

static void
alsa_destroy(cubeb * ctx)
{
  int r;

  assert(ctx);

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  ctx-&gt;shutdown = 1;
  poll_wake(ctx);
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  r = pthread_join(ctx-&gt;thread, NULL);
  assert(r == 0);

  close(ctx-&gt;control_fd_read);
  close(ctx-&gt;control_fd_write);
  pthread_mutex_destroy(&amp;ctx-&gt;mutex);
  free(ctx-&gt;fds);

  if (ctx-&gt;local_config) {
    pthread_mutex_lock(&amp;cubeb_alsa_mutex);
    WRAP(snd_config_delete)(ctx-&gt;local_config);
    pthread_mutex_unlock(&amp;cubeb_alsa_mutex);
  }
#ifndef DISABLE_LIBASOUND_DLOPEN
  if (ctx-&gt;libasound) {
    dlclose(ctx-&gt;libasound);
  }
#endif
  free(ctx);
}

static void
alsa_stream_destroy(cubeb_stream * stm);

static int
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream,
                        char const * stream_name, snd_pcm_stream_t stream_type,
                        cubeb_devid deviceid,
                        cubeb_stream_params * stream_params,
                        unsigned int latency_frames,
                        cubeb_data_callback data_callback,
                        cubeb_state_callback state_callback, void * user_ptr)
{
  (void)stream_name;
  cubeb_stream * stm;
  int r;
  snd_pcm_format_t format;
  snd_pcm_uframes_t period_size;
  int latency_us = 0;
  char const * pcm_name =
      deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;

  assert(ctx &amp;&amp; stream);

  *stream = NULL;

  if (stream_params-&gt;prefs &amp; CUBEB_STREAM_PREF_LOOPBACK) {
    return CUBEB_ERROR_NOT_SUPPORTED;
  }

  switch (stream_params-&gt;format) {
  case CUBEB_SAMPLE_S16LE:
    format = SND_PCM_FORMAT_S16_LE;
    break;
  case CUBEB_SAMPLE_S16BE:
    format = SND_PCM_FORMAT_S16_BE;
    break;
  case CUBEB_SAMPLE_FLOAT32LE:
    format = SND_PCM_FORMAT_FLOAT_LE;
    break;
  case CUBEB_SAMPLE_FLOAT32BE:
    format = SND_PCM_FORMAT_FLOAT_BE;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  if (ctx-&gt;active_streams &gt;= CUBEB_STREAM_MAX) {
    pthread_mutex_unlock(&amp;ctx-&gt;mutex);
    return CUBEB_ERROR;
  }
  ctx-&gt;active_streams += 1;
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  stm = calloc(1, sizeof(*stm));
  assert(stm);

  stm-&gt;context = ctx;
  stm-&gt;data_callback = data_callback;
  stm-&gt;state_callback = state_callback;
  stm-&gt;user_ptr = user_ptr;
  stm-&gt;params = *stream_params;
  stm-&gt;state = INACTIVE;
  stm-&gt;volume = 1.0;
  stm-&gt;buffer = NULL;
  stm-&gt;bufframes = 0;
  stm-&gt;stream_type = stream_type;
  stm-&gt;other_stream = NULL;

  r = pthread_mutex_init(&amp;stm-&gt;mutex, NULL);
  assert(r == 0);

  r = pthread_cond_init(&amp;stm-&gt;cond, NULL);
  assert(r == 0);

  r = alsa_locked_pcm_open(&amp;stm-&gt;pcm, pcm_name, stm-&gt;stream_type,
                           ctx-&gt;local_config);
  if (r &lt; 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_nonblock)(stm-&gt;pcm, 1);
  assert(r == 0);

  latency_us = latency_frames * 1e6 / stm-&gt;params.rate;

  /* Ugly hack: the PA ALSA plugin allows buffer configurations that can&#039;t
     possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
     Only resort to this hack if the handle_underrun workaround failed. */
  if (!ctx-&gt;local_config &amp;&amp; ctx-&gt;is_pa) {
    const int min_latency = 5e5;
    latency_us = latency_us &lt; min_latency ? min_latency : latency_us;
  }

  r = WRAP(snd_pcm_set_params)(stm-&gt;pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                               stm-&gt;params.channels, stm-&gt;params.rate, 1,
                               latency_us);
  if (r &lt; 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  r = WRAP(snd_pcm_get_params)(stm-&gt;pcm, &amp;stm-&gt;buffer_size, &amp;period_size);
  assert(r == 0);

  /* Double internal buffer size to have enough space when waiting for the other
   * side of duplex connection */
  stm-&gt;buffer_size *= 2;
  stm-&gt;buffer =
      calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm-&gt;pcm, stm-&gt;buffer_size));
  assert(stm-&gt;buffer);

  stm-&gt;nfds = WRAP(snd_pcm_poll_descriptors_count)(stm-&gt;pcm);
  assert(stm-&gt;nfds &gt; 0);

  stm-&gt;saved_fds = calloc(stm-&gt;nfds, sizeof(struct pollfd));
  assert(stm-&gt;saved_fds);
  r = WRAP(snd_pcm_poll_descriptors)(stm-&gt;pcm, stm-&gt;saved_fds, stm-&gt;nfds);
  assert((nfds_t)r == stm-&gt;nfds);

  if (alsa_register_stream(ctx, stm) != 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  *stream = stm;

  return CUBEB_OK;
}

static int
alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                 cubeb_devid input_device,
                 cubeb_stream_params * input_stream_params,
                 cubeb_devid output_device,
                 cubeb_stream_params * output_stream_params,
                 unsigned int latency_frames, cubeb_data_callback data_callback,
                 cubeb_state_callback state_callback, void * user_ptr)
{
  int result = CUBEB_OK;
  cubeb_stream *instm = NULL, *outstm = NULL;

  if (result == CUBEB_OK &amp;&amp; input_stream_params) {
    result = alsa_stream_init_single(ctx, &amp;instm, stream_name,
                                     SND_PCM_STREAM_CAPTURE, input_device,
                                     input_stream_params, latency_frames,
                                     data_callback, state_callback, user_ptr);
  }

  if (result == CUBEB_OK &amp;&amp; output_stream_params) {
    result = alsa_stream_init_single(ctx, &amp;outstm, stream_name,
                                     SND_PCM_STREAM_PLAYBACK, output_device,
                                     output_stream_params, latency_frames,
                                     data_callback, state_callback, user_ptr);
  }

  if (result == CUBEB_OK &amp;&amp; input_stream_params &amp;&amp; output_stream_params) {
    instm-&gt;other_stream = outstm;
    outstm-&gt;other_stream = instm;
  }

  if (result != CUBEB_OK &amp;&amp; instm) {
    alsa_stream_destroy(instm);
  }

  *stream = outstm ? outstm : instm;

  return result;
}

static void
alsa_stream_destroy(cubeb_stream * stm)
{
  int r;
  cubeb * ctx;

  assert(stm &amp;&amp; (stm-&gt;state == INACTIVE || stm-&gt;state == ERROR ||
                 stm-&gt;state == DRAINING));

  ctx = stm-&gt;context;

  if (stm-&gt;other_stream) {
    stm-&gt;other_stream-&gt;other_stream = NULL; // to stop infinite recursion
    alsa_stream_destroy(stm-&gt;other_stream);
  }

  pthread_mutex_lock(&amp;stm-&gt;mutex);
  if (stm-&gt;pcm) {
    if (stm-&gt;state == DRAINING) {
      WRAP(snd_pcm_drain)(stm-&gt;pcm);
    }
    alsa_locked_pcm_close(stm-&gt;pcm);
    stm-&gt;pcm = NULL;
  }
  free(stm-&gt;saved_fds);
  pthread_mutex_unlock(&amp;stm-&gt;mutex);
  pthread_mutex_destroy(&amp;stm-&gt;mutex);

  r = pthread_cond_destroy(&amp;stm-&gt;cond);
  assert(r == 0);

  alsa_unregister_stream(stm);

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  assert(ctx-&gt;active_streams &gt;= 1);
  ctx-&gt;active_streams -= 1;
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  free(stm-&gt;buffer);

  free(stm);
}

static int
alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
  int r;
  cubeb_stream * stm;
  snd_pcm_hw_params_t * hw_params;
  cubeb_stream_params params;
  params.rate = 44100;
  params.format = CUBEB_SAMPLE_FLOAT32NE;
  params.channels = 2;

  snd_pcm_hw_params_alloca(&amp;hw_params);

  assert(ctx);

  r = alsa_stream_init(ctx, &amp;stm, &quot;&quot;, NULL, NULL, NULL, &amp;params, 100, NULL,
                       NULL, NULL);
  if (r != CUBEB_OK) {
    return CUBEB_ERROR;
  }

  assert(stm);

  r = WRAP(snd_pcm_hw_params_any)(stm-&gt;pcm, hw_params);
  if (r &lt; 0) {
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
  if (r &lt; 0) {
    return CUBEB_ERROR;
  }

  /* Cap at reasonable maximum to filter driver placeholder values */  
  if (*max_channels &gt; 64) {  
    *max_channels = 64;  
  }  

  alsa_stream_destroy(stm);

  return CUBEB_OK;
}

static int
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
  (void)ctx;
  int r, dir;
  snd_pcm_t * pcm;
  snd_pcm_hw_params_t * hw_params;

  snd_pcm_hw_params_alloca(&amp;hw_params);

  /* get a pcm, disabling resampling, so we get a rate the
   * hardware/dmix/pulse/etc. supports. */
  r = WRAP(snd_pcm_open)(&amp;pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
                         SND_PCM_NO_AUTO_RESAMPLE);
  if (r &lt; 0) {
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
  if (r &lt; 0) {
    WRAP(snd_pcm_close)(pcm);
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &amp;dir);
  if (r &gt;= 0) {
    /* There is a default rate: use it. */
    WRAP(snd_pcm_close)(pcm);
    return CUBEB_OK;
  }

  /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
  *rate = 44100;

  r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
  if (r &lt; 0) {
    WRAP(snd_pcm_close)(pcm);
    return CUBEB_ERROR;
  }

  WRAP(snd_pcm_close)(pcm);

  return CUBEB_OK;
}

static int
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
                     uint32_t * latency_frames)
{
  (void)ctx;
  /* 40ms is found to be an acceptable minimum, even on a super low-end
   * machine. */
  *latency_frames = 40 * params.rate / 1000;

  return CUBEB_OK;
}

static int
alsa_stream_start(cubeb_stream * stm)
{
  cubeb * ctx;

  assert(stm);
  ctx = stm-&gt;context;

  if (stm-&gt;stream_type == SND_PCM_STREAM_PLAYBACK &amp;&amp; stm-&gt;other_stream) {
    int r = alsa_stream_start(stm-&gt;other_stream);
    if (r != CUBEB_OK)
      return r;
  }

  pthread_mutex_lock(&amp;stm-&gt;mutex);
  /* Capture pcm must be started after initial setup/recover */
  if (stm-&gt;stream_type == SND_PCM_STREAM_CAPTURE &amp;&amp;
      WRAP(snd_pcm_state)(stm-&gt;pcm) == SND_PCM_STATE_PREPARED) {
    WRAP(snd_pcm_start)(stm-&gt;pcm);
  }
  WRAP(snd_pcm_pause)(stm-&gt;pcm, 0);
  gettimeofday(&amp;stm-&gt;last_activity, NULL);
  pthread_mutex_unlock(&amp;stm-&gt;mutex);

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  if (stm-&gt;state != INACTIVE) {
    pthread_mutex_unlock(&amp;ctx-&gt;mutex);
    return CUBEB_ERROR;
  }
  alsa_set_stream_state(stm, RUNNING);
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  return CUBEB_OK;
}

static int
alsa_stream_stop(cubeb_stream * stm)
{
  cubeb * ctx;
  int r;

  assert(stm);
  ctx = stm-&gt;context;

  if (stm-&gt;stream_type == SND_PCM_STREAM_PLAYBACK &amp;&amp; stm-&gt;other_stream) {
    int r = alsa_stream_stop(stm-&gt;other_stream);
    if (r != CUBEB_OK)
      return r;
  }

  pthread_mutex_lock(&amp;ctx-&gt;mutex);
  while (stm-&gt;state == PROCESSING) {
    r = pthread_cond_wait(&amp;stm-&gt;cond, &amp;ctx-&gt;mutex);
    assert(r == 0);
  }

  alsa_set_stream_state(stm, INACTIVE);
  pthread_mutex_unlock(&amp;ctx-&gt;mutex);

  pthread_mutex_lock(&amp;stm-&gt;mutex);
  WRAP(snd_pcm_pause)(stm-&gt;pcm, 1);
  pthread_mutex_unlock(&amp;stm-&gt;mutex);

  return CUBEB_OK;
}

static int
alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
  snd_pcm_sframes_t delay;

  assert(stm &amp;&amp; position);

  pthread_mutex_lock(&amp;stm-&gt;mutex);

  delay = -1;
  if (WRAP(snd_pcm_state)(stm-&gt;pcm) != SND_PCM_STATE_RUNNING ||
      WRAP(snd_pcm_delay)(stm-&gt;pcm, &amp;delay) != 0) {
    *position = stm-&gt;last_position;
    pthread_mutex_unlock(&amp;stm-&gt;mutex);
    return CUBEB_OK;
  }

  assert(delay &gt;= 0);

  *position = 0;
  if (stm-&gt;stream_position &gt;= (snd_pcm_uframes_t)delay) {
    *position = stm-&gt;stream_position - delay;
  }

  stm-&gt;last_position = *position;

  pthread_mutex_unlock(&amp;stm-&gt;mutex);
  return CUBEB_OK;
}

static int
alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
  snd_pcm_sframes_t delay;
  /* This function returns the delay in frames until a frame written using
     snd_pcm_writei is sent to the DAC. The DAC delay should be &lt; 1ms anyways.
   */
  if (WRAP(snd_pcm_delay)(stm-&gt;pcm, &amp;delay)) {
    return CUBEB_ERROR;
  }

  *latency = delay;

  return CUBEB_OK;
}

static int
alsa_stream_set_volume(cubeb_stream * stm, float volume)
{
  /* setting the volume using an API call does not seem very stable/supported */
  pthread_mutex_lock(&amp;stm-&gt;mutex);
  stm-&gt;volume = volume;
  pthread_mutex_unlock(&amp;stm-&gt;mutex);

  return CUBEB_OK;
}

static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                       cubeb_device_collection * collection)
{
  cubeb_device_info * device;
  snd_pcm_info_t * pcminfo;
  snd_ctl_t * ctl;
  snd_ctl_card_info_t * cardinfo;
  int card = -1;
  char card_name[32];
  int err;
  int dev;
  snd_pcm_stream_t stream;

  snd_pcm_info_alloca(&amp;pcminfo);
  snd_ctl_card_info_alloca(&amp;cardinfo);

  collection-&gt;count = 0;
  collection-&gt;device = NULL;

  if (snd_card_next(&amp;card) &lt; 0 || card &lt; 0)
    return CUBEB_OK;

  while (card &gt;= 0) {
    sprintf(card_name, &quot;hw:%d&quot;, card);
    err = snd_ctl_open(&amp;ctl, card_name, 0);
    if (err &lt; 0) {
      snd_card_next(&amp;card);
      continue;
    }

    err = snd_ctl_card_info(ctl, cardinfo);
    if (err &lt; 0) {
      snd_ctl_close(ctl);
      snd_card_next(&amp;card);
      continue;
    }

    dev = -1;
    while (1) {
      if (snd_ctl_pcm_next_device(ctl, &amp;dev) &lt; 0)
        break;
      if (dev &lt; 0)
        break;

      for (stream = 0; stream &lt; 2; stream++) {
        if ((type &amp; CUBEB_DEVICE_TYPE_OUTPUT) &amp;&amp; stream == SND_PCM_STREAM_CAPTURE)
          continue;
        if ((type &amp; CUBEB_DEVICE_TYPE_INPUT) &amp;&amp; stream == SND_PCM_STREAM_PLAYBACK)
          continue;

        snd_pcm_info_set_device(pcminfo, dev);
        snd_pcm_info_set_subdevice(pcminfo, 0);
        snd_pcm_info_set_stream(pcminfo, stream);
        err = snd_ctl_pcm_info(ctl, pcminfo);
        if (err &lt; 0)
          continue;

        device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
        if (!device) {
          snd_ctl_close(ctl);
          return CUBEB_ERROR;
        }

        device-&gt;device_id = strdup(snd_pcm_info_get_name(pcminfo));
        device-&gt;friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
        device-&gt;group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
        device-&gt;vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));

        device-&gt;type = (stream == SND_PCM_STREAM_PLAYBACK) ? 
                       CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
        device-&gt;devid = (void *)(intptr_t)(card &lt;&lt; 16 | dev);
        device-&gt;state = CUBEB_DEVICE_STATE_ENABLED;
        device-&gt;preferred = (dev == 0);
        
        device-&gt;max_channels = 8;  
        
        device-&gt;min_rate = 8000;   /* Conservative minimum */ 
        device-&gt;max_rate = 192000; /* Conservative maximum */
        device-&gt;default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
        device-&gt;latency_lo = 0;
        device-&gt;latency_hi = 0;

        collection-&gt;count++;
        collection-&gt;device = (cubeb_device_info *)realloc(collection-&gt;device,
                                                         collection-&gt;count * sizeof(cubeb_device_info));
        if (!collection-&gt;device) {
          free((void *)device-&gt;device_id);  
          free((void *)device-&gt;friendly_name);  
          free((void *)device-&gt;group_id);  
          free((void *)device-&gt;vendor_name);
          free(device);
          snd_ctl_close(ctl);
          return CUBEB_ERROR;
        }
        collection-&gt;device[collection-&gt;count - 1] = *device;
        free(device);
      }
    }
    snd_ctl_close(ctl);
    snd_card_next(&amp;card);
  }

  return CUBEB_OK;
}

static int  
alsa_device_collection_destroy(cubeb * context,  
                               cubeb_device_collection * collection)  
{  
  size_t i;  
  
  (void)context;  
  
  for (i = 0; i &lt; collection-&gt;count; ++i) {  
    free((void *)collection-&gt;device[i].device_id);  
    free((void *)collection-&gt;device[i].friendly_name);  
    free((void *)collection-&gt;device[i].group_id);  
    free((void *)collection-&gt;device[i].vendor_name);  
  }  
  free(collection-&gt;device);  
  return CUBEB_OK;  
}

static struct cubeb_ops const alsa_ops = {
    .init = alsa_init,
    .get_backend_id = alsa_get_backend_id,
    .get_max_channel_count = alsa_get_max_channel_count,
    .get_min_latency = alsa_get_min_latency,
    .get_preferred_sample_rate = alsa_get_preferred_sample_rate,
    .get_supported_input_processing_params = NULL,
    .enumerate_devices = alsa_enumerate_devices,
    .device_collection_destroy = alsa_device_collection_destroy,
    .destroy = alsa_destroy,
    .stream_init = alsa_stream_init,
    .stream_destroy = alsa_stream_destroy,
    .stream_start = alsa_stream_start,
    .stream_stop = alsa_stream_stop,
    .stream_get_position = alsa_stream_get_position,
    .stream_get_latency = alsa_stream_get_latency,
    .stream_get_input_latency = NULL,
    .stream_set_volume = alsa_stream_set_volume,
    .stream_set_name = NULL,
    .stream_get_current_device = NULL,
    .stream_set_input_mute = NULL,
    .stream_set_input_processing_params = NULL,
    .stream_device_destroy = NULL,
    .stream_register_device_changed_callback = NULL,
    .register_device_collection_changed = NULL};</code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Sat, 13 Jun 2026 11:41:09 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64283#p64283</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64279#p64279</link>
			<description><![CDATA[<div class="quotebox"><cite>PedroReina wrote:</cite><blockquote><div><p>Any chance to be accepted upstream?</p></div></blockquote></div><p>Any chance for it to be tested by ALSA users?</p><p>The OSS4 backend is officially unmaintained. The ALSA backend seems to be orphaned. Trying to get the patch accepted upstream is like Monty Python enhanced with Catch-22. A workaround is a dialectical deconstruction of Firefox.</p><p><strong>Summary of the Patch</strong></p><p>The ALSA backend patch for cubeb fixes two critical issues:</p><p><strong>Device enumeration:</strong> Replaces the placeholder implementation that returned a single fictional &quot;default&quot; device with proper ALSA device discovery using <span class="bbc">snd_card_next()</span> and <span class="bbc">snd_ctl_pcm_next_device()</span> to report actual audio devices with their real capabilities</p><p><strong>Max channel count:</strong> Caps the maximum channel count at 64 to sanitize unrealistic values (like 10000) returned by <span class="bbc">snd_pcm_hw_params_get_channels_max()</span> from some drivers</p><p>These fixes ensure that Firefox and web applications like Zoom receive accurate device information instead of incorrect placeholder values that were causing failures.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Fri, 12 Jun 2026 13:24:52 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64279#p64279</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64278#p64278</link>
			<description><![CDATA[<div class="quotebox"><cite>igorzwx wrote:</cite><blockquote><div><p>Patch to fix Firefox ALSA backend</p></div></blockquote></div><p>Any chance to be accepted upstream?</p>]]></description>
			<author><![CDATA[dummy@example.com (PedroReina)]]></author>
			<pubDate>Fri, 12 Jun 2026 08:33:46 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64278#p64278</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=64275#p64275</link>
			<description><![CDATA[<p>Patch to fix Firefox ALSA backend <span class="bbc">src/cubeb_alsa.c</span></p><div class="codebox"><pre class="vscroll"><code>$ cat patches/0002-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch
From 69df295f5cf8d80f594110f9576a7bed153295b5 Mon Sep 17 00:00:00 2001
From: Devuan
Date: Thu, 11 Jun 2026 21:58:59 +0200
Subject: [PATCH 2/3] ALSA backend for Firefox: Fix device enumeration and max
 channel count

- Replace placeholder device enumeration with proper ALSA device
  discovery using snd_card_next() and snd_ctl_pcm_next_device() to
  report actual audio devices with their real capabilities instead of
  a single fictional default device.

- Cap max channel count at 64 to sanitize unrealistic values returned
  by snd_pcm_hw_params_get_channels_max() which can report placeholder
  values like 10000 from some drivers.
---
 src/cubeb_alsa.c | 160 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 114 insertions(+), 46 deletions(-)

diff --git a/src/cubeb_alsa.c b/src/cubeb_alsa.c
index be9faa4..b76ec4f 100644
--- a/src/cubeb_alsa.c
+++ b/src/cubeb_alsa.c
@@ -1223,6 +1223,11 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
     return CUBEB_ERROR;
   }
 
+  /* Cap at reasonable maximum to filter driver placeholder values */  
+  if (*max_channels &gt; 64) {  
+    *max_channels = 64;  
+  }  
+
   alsa_stream_destroy(stm);
 
   return CUBEB_OK;
@@ -1412,61 +1417,124 @@ static int
 alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                        cubeb_device_collection * collection)
 {
-  cubeb_device_info * device = NULL;
+  cubeb_device_info * device;
+  snd_pcm_info_t * pcminfo;
+  snd_ctl_t * ctl;
+  snd_ctl_card_info_t * cardinfo;
+  int card = -1;
+  char card_name[32];
+  int err;
+  int dev;
+  snd_pcm_stream_t stream;
+
+  snd_pcm_info_alloca(&amp;pcminfo);
+  snd_ctl_card_info_alloca(&amp;cardinfo);
+
+  collection-&gt;count = 0;
+  collection-&gt;device = NULL;
+
+  if (snd_card_next(&amp;card) &lt; 0 || card &lt; 0)
+    return CUBEB_OK;
 
-  if (!context)
-    return CUBEB_ERROR;
+  while (card &gt;= 0) {
+    sprintf(card_name, &quot;hw:%d&quot;, card);
+    err = snd_ctl_open(&amp;ctl, card_name, 0);
+    if (err &lt; 0) {
+      snd_card_next(&amp;card);
+      continue;
+    }
 
-  uint32_t rate, max_channels;
-  int r;
+    err = snd_ctl_card_info(ctl, cardinfo);
+    if (err &lt; 0) {
+      snd_ctl_close(ctl);
+      snd_card_next(&amp;card);
+      continue;
+    }
 
-  r = alsa_get_preferred_sample_rate(context, &amp;rate);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR;
-  }
+    dev = -1;
+    while (1) {
+      if (snd_ctl_pcm_next_device(ctl, &amp;dev) &lt; 0)
+        break;
+      if (dev &lt; 0)
+        break;
+
+      for (stream = 0; stream &lt; 2; stream++) {
+        if ((type &amp; CUBEB_DEVICE_TYPE_OUTPUT) &amp;&amp; stream == SND_PCM_STREAM_CAPTURE)
+          continue;
+        if ((type &amp; CUBEB_DEVICE_TYPE_INPUT) &amp;&amp; stream == SND_PCM_STREAM_PLAYBACK)
+          continue;
+
+        snd_pcm_info_set_device(pcminfo, dev);
+        snd_pcm_info_set_subdevice(pcminfo, 0);
+        snd_pcm_info_set_stream(pcminfo, stream);
+        err = snd_ctl_pcm_info(ctl, pcminfo);
+        if (err &lt; 0)
+          continue;
+
+        device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
+        if (!device) {
+          snd_ctl_close(ctl);
+          return CUBEB_ERROR;
+        }
 
-  r = alsa_get_max_channel_count(context, &amp;max_channels);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR;
+        device-&gt;device_id = strdup(snd_pcm_info_get_name(pcminfo));
+        device-&gt;friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
+        device-&gt;group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
+        device-&gt;vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));
+
+        device-&gt;type = (stream == SND_PCM_STREAM_PLAYBACK) ? 
+                       CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
+        device-&gt;devid = (void *)(intptr_t)(card &lt;&lt; 16 | dev);
+        device-&gt;state = CUBEB_DEVICE_STATE_ENABLED;
+        device-&gt;preferred = (dev == 0);
+        
+        device-&gt;max_channels = 8;  
+        
+        device-&gt;min_rate = 8000;   /* Conservative minimum */ 
+        device-&gt;max_rate = 192000; /* Conservative maximum */
+        device-&gt;default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
+        device-&gt;latency_lo = 0;
+        device-&gt;latency_hi = 0;
+
+        collection-&gt;count++;
+        collection-&gt;device = (cubeb_device_info *)realloc(collection-&gt;device,
+                                                         collection-&gt;count * sizeof(cubeb_device_info));
+        if (!collection-&gt;device) {
+          free((void *)device-&gt;device_id);  
+          free((void *)device-&gt;friendly_name);  
+          free((void *)device-&gt;group_id);  
+          free((void *)device-&gt;vendor_name);
+          free(device);
+          snd_ctl_close(ctl);
+          return CUBEB_ERROR;
+        }
+        collection-&gt;device[collection-&gt;count - 1] = *device;
+        free(device);
+      }
+    }
+    snd_ctl_close(ctl);
+    snd_card_next(&amp;card);
   }
 
-  char const * a_name = &quot;default&quot;;
-  device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
-  assert(device);
-  if (!device)
-    return CUBEB_ERROR;
-
-  device-&gt;device_id = a_name;
-  device-&gt;devid = (cubeb_devid)device-&gt;device_id;
-  device-&gt;friendly_name = a_name;
-  device-&gt;group_id = a_name;
-  device-&gt;vendor_name = a_name;
-  device-&gt;type = type;
-  device-&gt;state = CUBEB_DEVICE_STATE_ENABLED;
-  device-&gt;preferred = CUBEB_DEVICE_PREF_ALL;
-  device-&gt;format = CUBEB_DEVICE_FMT_S16NE;
-  device-&gt;default_format = CUBEB_DEVICE_FMT_S16NE;
-  device-&gt;max_channels = max_channels;
-  device-&gt;min_rate = rate;
-  device-&gt;max_rate = rate;
-  device-&gt;default_rate = rate;
-  device-&gt;latency_lo = 0;
-  device-&gt;latency_hi = 0;
-
-  collection-&gt;device = device;
-  collection-&gt;count = 1;
-
   return CUBEB_OK;
 }
 
-static int
-alsa_device_collection_destroy(cubeb * context,
-                               cubeb_device_collection * collection)
-{
-  assert(collection-&gt;count == 1);
-  (void)context;
-  free(collection-&gt;device);
-  return CUBEB_OK;
+static int  
+alsa_device_collection_destroy(cubeb * context,  
+                               cubeb_device_collection * collection)  
+{  
+  size_t i;  
+  
+  (void)context;  
+  
+  for (i = 0; i &lt; collection-&gt;count; ++i) {  
+    free((void *)collection-&gt;device[i].device_id);  
+    free((void *)collection-&gt;device[i].friendly_name);  
+    free((void *)collection-&gt;device[i].group_id);  
+    free((void *)collection-&gt;device[i].vendor_name);  
+  }  
+  free(collection-&gt;device);  
+  return CUBEB_OK;  
 }
 
 static struct cubeb_ops const alsa_ops = {
-- 
2.39.5</code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Thu, 11 Jun 2026 21:16:38 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=64275#p64275</guid>
		</item>
		<item>
			<title><![CDATA[Re: The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?pid=60007#p60007</link>
			<description><![CDATA[<p>To @tux_99<br />You&#039;re absolutely right — ALSA&#039;s documentation is notoriously opaque, and that confusion has fueled decades of misconceptions. The irony? The default configuration has quietly handled full-duplex and software mixing for years. Tools like fftrate are excellent enhancements — they improve resampling quality (defaults.pcm.rate_converter &quot;fftrate&quot;) — but they don’t fix broken audio; they refine an already working system. So while fftrate is a smart tweak for audiophiles, the real issue isn’t the tech — it’s that the documentation never told anyone it already worked.<br />_https://dev1galaxy.org/viewtopic.php?id=7142<br /><span class="bbc">arateconf</span><br />_https://dev1galaxy.org/viewtopic.php?id=6644<br />modified <span class="bbc">dmix</span> + <span class="bbc">dsnoop</span><br />_https://dev1galaxy.org/viewtopic.php?id=7587</p><p>To@Altoid<br />I know recompiling packages isn’t everyone’s idea of fun, but this one’s worth it. Rebuilding libasound2-plugins without PulseAudio isn’t just a technical tweak — it’s a small act of digital self-determination. No more phantom plugins trying to phone home to a server that isn’t there. No more audio routing chaos. Just clean, direct ALSA control.</p><p>And once it’s done, it stays done. No files sneaking back on update. It’s stable, silent, and free.</p><p>The process? A few commands, one .deb, and you’re set for life. Freedom isn’t always easy — but it is simple.<br />_https://dev1galaxy.org/viewtopic.php?id=7523</p><div class="codebox"><pre><code>$ ls -1 /usr/share/alsa/alsa.conf.d
10-rate-lav.conf
10-samplerate.conf
10-speexrate.conf
50-arcam-av-ctl.conf
50-jack.conf
50-oss.conf
60-a52-encoder.conf
60-speex.conf
60-upmix.conf
60-vdownmix.conf
98-usb-stream.conf</code></pre></div><div class="codebox"><pre><code>$ ls -l -1 /etc/alsa/conf.d
total 0
lrwxrwxrwx 1 root root 44 Jul  1  2024 10-rate-lav.conf -&gt; /usr/share/alsa/alsa.conf.d/10-rate-lav.conf
lrwxrwxrwx 1 root root 46 Jul  1  2024 10-samplerate.conf -&gt; /usr/share/alsa/alsa.conf.d/10-samplerate.conf
lrwxrwxrwx 1 root root 45 Jul  1  2024 10-speexrate.conf -&gt; /usr/share/alsa/alsa.conf.d/10-speexrate.conf
lrwxrwxrwx 1 root root 48 Jul  1  2024 50-arcam-av-ctl.conf -&gt; /usr/share/alsa/alsa.conf.d/50-arcam-av-ctl.conf
lrwxrwxrwx 1 root root 40 Jul  1  2024 50-jack.conf -&gt; /usr/share/alsa/alsa.conf.d/50-jack.conf
lrwxrwxrwx 1 root root 39 Jul  1  2024 50-oss.conf -&gt; /usr/share/alsa/alsa.conf.d/50-oss.conf
lrwxrwxrwx 1 root root 47 Jul  1  2024 60-a52-encoder.conf -&gt; /usr/share/alsa/alsa.conf.d/60-a52-encoder.conf
lrwxrwxrwx 1 root root 41 Jul  1  2024 60-speex.conf -&gt; /usr/share/alsa/alsa.conf.d/60-speex.conf
lrwxrwxrwx 1 root root 41 Jul  1  2024 60-upmix.conf -&gt; /usr/share/alsa/alsa.conf.d/60-upmix.conf
lrwxrwxrwx 1 root root 44 Jul  1  2024 60-vdownmix.conf -&gt; /usr/share/alsa/alsa.conf.d/60-vdownmix.conf
lrwxrwxrwx 1 root root 46 Jul  1  2024 98-usb-stream.conf -&gt; /usr/share/alsa/alsa.conf.d/98-usb-stream.conf</code></pre></div><div class="codebox"><pre><code>$ apt-file find 99-pulseaudio-default.conf.example
libasound2-plugins: /etc/alsa/conf.d/99-pulseaudio-default.conf.example</code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Sun, 23 Nov 2025 20:40:31 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?pid=60007#p60007</guid>
		</item>
	</channel>
</rss>
