<?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;fid=25&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Dev1 Galaxy Forum / Freedom Hacks]]></title>
		<link>https://dev1galaxy.org/index.php</link>
		<description><![CDATA[The most recent topics at Dev1 Galaxy Forum.]]></description>
		<lastBuildDate>Sat, 05 Sep 2026 13:32:57 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[grafana]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=8162&amp;action=new</link>
			<description><![CDATA[<p>I resorted to running Grafana in Docker, which I suppose trades one evil empire for another but I digress.</p>]]></description>
			<author><![CDATA[dummy@example.com (jw4791)]]></author>
			<pubDate>Sat, 05 Sep 2026 13:32:57 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=8162&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Use refracta2usb with netinstall iso]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=8143&amp;action=new</link>
			<description><![CDATA[<p>One more...&#160; &#160;I dd&#039;d a desktop installer iso to the second partition. It finds the cdrom and unlike the netinstall iso it offers to set up wlan0 in addition to the non-functional eth0 on the laptop. A bonus for those without ethernet ports.</p><p>Also, the destkop installer iso does copy correctly to the first (fat32) partition if you use the ISO_1 option, because it unpacks the iso file and no single file is bigger than 4GB. But you still have to mount it manually.</p>]]></description>
			<author><![CDATA[dummy@example.com (fsmithred)]]></author>
			<pubDate>Sun, 16 Aug 2026 01:47:28 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=8143&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[[SOLVED] Tips for people who don't like hidden scrollbars and other GTK3 things]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=8087&amp;action=new</link>
			<description><![CDATA[<p>Thanks! Most helpful!</p>]]></description>
			<author><![CDATA[dummy@example.com (swanson)]]></author>
			<pubDate>Fri, 14 Aug 2026 06:42:28 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=8087&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Bluetooth Audio: How It Works]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7730&amp;action=new</link>
			<description><![CDATA[<h5>Architectural Monty Python</h5><p>The removal of the notorious PulseAudio workaround from the Firefox/Cubeb ALSA backend unveils a new reality. The purified ALSA backend, aligned with BlueALSA, AAC CBR, and modest hardware (the USB-BT540 adapter and Sennheiser HD 450BT headphones), yields superior sound quality compared to the standard ALSA configuration.</p><p>This exemplifies a true dialectical absurdity reminiscent of Monty Python: a massive, complex system (ALSA) failing to solve a problem that a tiny, focused tool (BlueALSA) handles elegantly.</p><p>The reason is clear. Just as the architect in the Monty Python sketch designs a building that fails its primary purpose — a house that does not let you walk through it — the traditional Linux audio stack often prioritizes universal compatibility over functional simplicity.</p><p>BlueALSA possesses its own bugs and workarounds; however, its codebase remains minuscule compared to ALSA. This disparity explains the situation entirely.</p><p>BlueALSA&#039;s entire &quot;quirks surface&quot; is tiny compared to the vast, decades-deep hardware-quirk machinery in the Linux kernel&#039;s ALSA subsystem. Scale differences like that plausibly explain why BlueALSA&#039;s bug surface feels so much smaller and more tractable.</p><p>alsa-lib&#039;s complexity is substantial and driven largely by breadth of hardware support: the per-device quirk table alone in <span class="bbc">src/conf/cards/USB-Audio.conf</span> lists dozens of named devices needing special-cased dmix disabling, surround-channel remapping, and IEC958 device-numbering exceptions. This is on top of the general PCM plugin architecture (format conversion, rate conversion, routing, software volume, etc.) that alsa-lib maintains to support arbitrary hardware capabilities.</p><p>A userspace Bluetooth bridge like BlueALSA, by design, only needs to target a much narrower surface: BlueZ&#039;s Bluetooth audio profiles (A2DP/HFP) on one side, and a generic ALSA PCM I/O device on the other — it doesn&#039;t need to enumerate and special-case hundreds of USB/PCI/HDA sound cards. So the size disparity is architecturally plausible: alsa-lib accumulates workarounds because it&#039;s the common layer for all Linux sound hardware, while BlueALSA&#039;s scope is deliberately narrow.</p><p>Dialectically, absurdity is the hidden truth of normality (Albert Camus). ALSA users are unlikely to notice the comical irony of the situation, yet it is absurd that a minimal userspace daemon (BlueALSA, comprising only a few thousand lines of code) outperforms the kernel-level subsystem and userspace server (ALSA, consisting of millions of lines) in both sound quality and stability for this specific use case. The &quot;correct&quot; enterprise-grade architecture (the full Linux audio stack) proves to be the broken building, while the &quot;hacky&quot; side project (BlueALSA) is the only one that lets you walk through the door.</p><p>Imagine you have two vehicles: a car and a bike, both neglected and rusty. The car is a seized limousine with thousands of grinding parts. Fixing it requires rebuilding the entire transmission. The bike is a lightweight frame with minor rust. Because it has only 50 parts, you can see the friction points instantly. You can file it down and oil the chain in minutes. Once you fix the bike, it rides faster and smoother than the car ever could.</p><p>Dialectically, absurdity is inherent to normality. Attempting to destroy absurdity inevitably destroys normality, strengthens the absurd, and unleashes chaos. Absurdity cannot be defeated.&#160; But you may find a practical solution to it.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Wed, 05 Aug 2026 22:28:16 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7730&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Firefox: Dialectics of Antagonistic Security Bugs]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=8012&amp;action=new</link>
			<description><![CDATA[<h5>New version of Firefox/Cubeb ALSA backend cubeb_alsa.c </h5><div class="codebox"><pre class="vscroll"><code>Remove PulseAudio ALSA plugin workaround from ALSA backend

This change removes the PulseAudio ALSA plugin workaround code that was
added to work around a bug where the PulseAudio server forces higher
latency than requested but the plugin doesn&#039;t update its internal state.

Rationale:
- Users who want PulseAudio should use the dedicated PulseAudio backends
  (pulse or pulse-rust) instead of the ALSA backend with plugin workarounds
- The ALSA backend is intended for direct hardware access for users without
  PulseAudio/PipeWire
- The workaround adds unnecessary complexity for a use case that shouldn&#039;t
  exist

Changes made:
- Removed local_config and is_pa fields from cubeb struct
- Removed init_local_config_with_workaround() function that detected and
  configured the PulseAudio plugin workaround
- Removed PulseAudio workaround detection and testing code from alsa_init()
- Removed local_config cleanup code from alsa_destroy()
- Simplified alsa_locked_pcm_open() to remove the local_config parameter
  and always use standard snd_pcm_open() instead of snd_pcm_open_lconf()
- Updated all callers of alsa_locked_pcm_open() to use the simplified signature
- Kept the thread safety wrapper via cubeb_alsa_mutex which is still required
  for ALSA versions before 1.0.24

Impact:
- Simplifies the ALSA backend code by removing ~90 lines of workaround code
- Eliminates confusion about PulseAudio plugin handling in the ALSA backend
- Improves code readability by removing conditional configuration paths
- Maintains all essential functionality for direct ALSA hardware access
- Sound quality may improve due to removal of workaround latency adjustments

The device enumeration feature remains intact and unaffected by this change.</code></pre></div><div class="codebox"><pre class="vscroll"><code>$ cat firefox/media/libcubeb/src/cubeb_alsa.c
/*
 * 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;
#include &lt;stdbool.h&gt;

#ifdef DISABLE_LIBASOUND_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*cubeb_##x)
#define LIBASOUND_API_VISIT(X)                                                 \
  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)                                                            \
  X(snd_device_name_hint)                                                      \
  X(snd_device_name_get_hint)                                                  \
  X(snd_device_name_free_hint)                                                 \
  X(snd_pcm_hw_params_test_format)                                             \
  X(snd_pcm_hw_params_get_rate_min)                                            \
  X(snd_pcm_hw_params_get_rate_max)                                            \
  X(snd_pcm_hw_params_get_channels_min)                                        \
  X(snd_pcm_hw_params_get_channels_max)                                        \
  X(snd_card_next)                                                             \
  X(snd_ctl_close)                                                             \
  X(snd_ctl_open)                                                              \
  X(snd_ctl_pcm_info)                                                          \
  X(snd_ctl_pcm_next_device)                                                   \
  X(snd_pcm_info)                                                              \
  X(snd_ctl_card_info)                                                         \
  X(snd_pcm_info_set_device)                                                   \
  X(snd_pcm_info_set_stream)                                                   \
  X(snd_pcm_hw_params_get_format_mask)                                         \
  X(snd_pcm_format_mask_test)                                                  \
  X(snd_pcm_info_set_subdevice)

#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;

/* 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;
};

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 int
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
                     snd_pcm_stream_t stream)
{
  int r;

  pthread_mutex_lock(&amp;cubeb_alsa_mutex);
  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 */
  r = alsa_locked_pcm_open(&amp;dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK);  
  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);

#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);

  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;

  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);
}
                                          
// max_channel_count
static int
alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
  snd_ctl_t *handle;
  int card = -1;
  uint32_t max = 0;
  int r;

  printf(&quot;DEBUG: Starting max channel count calculation\n&quot;);

  if (!ctx || !max_channels) {
    printf(&quot;DEBUG: Invalid parameters (ctx=%p, max_channels=%p)\n&quot;, ctx, max_channels);
    return CUBEB_ERROR_INVALID_PARAMETER;
  }

  /* Iterate through all cards */
  while (snd_card_next(&amp;card) &gt;= 0 &amp;&amp; card &gt;= 0) {
    char card_name[32];
    snprintf(card_name, sizeof(card_name), &quot;hw:%d&quot;, card);

    r = snd_ctl_open(&amp;handle, card_name, 0);
    if (r &lt; 0) {
      printf(&quot;DEBUG: Cannot open control for card %d: %s\n&quot;, card, snd_strerror(r));
      continue;
    }

    /* Iterate through PCM devices on this card */
    int dev = -1;
    while (snd_ctl_pcm_next_device(handle, &amp;dev) &gt;= 0 &amp;&amp; dev &gt;= 0) {
      snd_pcm_chmap_query_t **maps;

      /* Query channel maps without opening PCM device */
      maps = snd_pcm_query_chmaps_from_hw(card, dev, 0, SND_PCM_STREAM_PLAYBACK);
      if (maps) {
        snd_pcm_chmap_query_t **p;
        for (p = maps; *p != NULL; p++) {
          unsigned int channels = (*p)-&gt;map.channels;
          printf(&quot;DEBUG: Device hw:%d,%d: channel map type %d, channels = %u\n&quot;, 
                 card, dev, (*p)-&gt;type, channels);
          if (channels &gt; max) {
            max = channels;
            printf(&quot;DEBUG: New maximum: %u\n&quot;, max);
          }
        }
        snd_pcm_free_chmaps(maps);
      } else {
        printf(&quot;DEBUG: No channel maps available for device hw:%d,%d\n&quot;, card, dev);
      }
    }

    snd_ctl_close(handle);
  }

  printf(&quot;DEBUG: Final max channels: %u\n&quot;, max);

  if (max == 0) {
    printf(&quot;DEBUG: No valid channel counts found, using fallback\n&quot;);
    max = 2; /* Fallback to stereo */
  }

  *max_channels = max;
  return CUBEB_OK;
}
// END of max_channel_count 

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;
}

/* Helper Functions for alsa_enumerate_devices */
static bool
is_virtual_device(const char *name)
{
  const char *virtual_prefixes[] = {
    &quot;plug&quot;, &quot;dmix&quot;, &quot;dsnoop&quot;, &quot;default&quot;, &quot;plughw&quot;, &quot;tee&quot;,
    &quot;null&quot;, &quot;sysdefault&quot;, &quot;lavrate&quot;, &quot;samplerate&quot;, &quot;speexrate&quot;,
    &quot;bluealsa&quot;, &quot;oss&quot;, &quot;speex&quot;, &quot;upmix&quot;, &quot;vdownmix&quot;,
    &quot;front&quot;, &quot;surround40&quot;, &quot;surround41&quot;, &quot;surround50&quot;,
    &quot;surround51&quot;, &quot;surround71&quot;, &quot;iec958&quot;,
    NULL
  };

  for (int i = 0; virtual_prefixes[i] != NULL; i++) {
    if (strncmp(name, virtual_prefixes[i], strlen(virtual_prefixes[i])) == 0) {
      // Check if it&#039;s an exact match or followed by colon or other separator
      size_t len = strlen(virtual_prefixes[i]);
      if (name[len] == &#039;\0&#039; || name[len] == &#039;:&#039; || name[len] == &#039;=&#039;) {
        return true;
      }
    }
  }
  return false;
}

static int
add_bluealsa_device(cubeb_device_info **devinfop, int *collection_cnt,
                    cubeb_device_type type)
{
  cubeb_device_info *device = &amp;(*devinfop)[*collection_cnt];

  /* A2DP is output-only (better quality) */
  if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
    device-&gt;device_id = strdup(&quot;bluealsa:PROFILE=a2dp&quot;);
    device-&gt;friendly_name = strdup(&quot;BlueALSA A2DP&quot;);
    device-&gt;group_id = strdup(&quot;bluealsa&quot;);
    device-&gt;vendor_name = strdup(&quot;BlueALSA&quot;);
    device-&gt;format = CUBEB_DEVICE_FMT_S16LE;
    device-&gt;default_format = CUBEB_DEVICE_FMT_S16LE;
    device-&gt;max_channels = 2;
    device-&gt;default_rate = 48000;
    device-&gt;min_rate = 44100;
    device-&gt;max_rate = 48000;
    device-&gt;type = CUBEB_DEVICE_TYPE_OUTPUT;
    device-&gt;state = CUBEB_DEVICE_STATE_ENABLED;
    device-&gt;preferred = CUBEB_DEVICE_PREF_NONE;
    device-&gt;latency_lo = 200;
    device-&gt;latency_hi = 200;
    (*collection_cnt)++;
  }

  /* SCO supports full duplex (both input and output) */
  if (type == CUBEB_DEVICE_TYPE_OUTPUT || type == CUBEB_DEVICE_TYPE_INPUT) {
    device = &amp;(*devinfop)[*collection_cnt];
    device-&gt;device_id = strdup(&quot;bluealsa:PROFILE=sco&quot;);
    device-&gt;friendly_name = strdup(&quot;BlueALSA SCO&quot;);
    device-&gt;group_id = strdup(&quot;bluealsa&quot;);
    device-&gt;vendor_name = strdup(&quot;BlueALSA&quot;);
    device-&gt;format = CUBEB_DEVICE_FMT_S16LE;
    device-&gt;default_format = CUBEB_DEVICE_FMT_S16LE;
    device-&gt;max_channels = 1;
    device-&gt;default_rate = 16000;
    device-&gt;min_rate = 16000;
    device-&gt;max_rate = 16000;
    device-&gt;type = type;
    device-&gt;state = CUBEB_DEVICE_STATE_ENABLED;
    device-&gt;preferred = CUBEB_DEVICE_PREF_NONE;
    device-&gt;latency_lo = 20;
    device-&gt;latency_hi = 30;
    (*collection_cnt)++;
  }

  return CUBEB_OK;
}

static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                       cubeb_device_collection * collection)
{
  void **hints = NULL;
  void **hint;
  char *name = NULL;
  char *io = NULL;
  char *desc = NULL;
  cubeb_device_info * devinfop = NULL;
  int collection_cnt = 0;
  int r;

  if (!context)
    return CUBEB_ERROR;

  /* Get device name hints from ALSA */
  r = WRAP(snd_device_name_hint)(-1, &quot;pcm&quot;, &amp;hints);
  if (r &lt; 0) {
    return CUBEB_ERROR;
  }

  /* First pass: count physical devices */
  for (hint = hints; *hint != NULL; hint++) {
    name = WRAP(snd_device_name_get_hint)(*hint, &quot;NAME&quot;);
    if (!name)
      continue;

    /* Skip virtual devices */
    if (is_virtual_device(name)) {
      free(name);
      continue;
    }

    /* Check IO type matches requested type */
    io = WRAP(snd_device_name_get_hint)(*hint, &quot;IO&quot;);
    if (io) {
      if (type == CUBEB_DEVICE_TYPE_INPUT &amp;&amp; strcmp(io, &quot;Input&quot;) != 0) {
        free(name);
        free(io);
        continue;
      }
      if (type == CUBEB_DEVICE_TYPE_OUTPUT &amp;&amp; strcmp(io, &quot;Output&quot;) != 0) {
        free(name);
        free(io);
        continue;
      }
      free(io);
    }

    collection_cnt++;
    free(name);
  }

  /* Allocate device array */
  devinfop = calloc(collection_cnt + 2, sizeof(cubeb_device_info));
  if (!devinfop) {
    WRAP(snd_device_name_free_hint)(hints);
    return CUBEB_ERROR;
  }

  collection_cnt = 0;

  /* Second pass: populate device info */
  for (hint = hints; *hint != NULL; hint++) {
    name = WRAP(snd_device_name_get_hint)(*hint, &quot;NAME&quot;);
    if (!name)
      continue;

    if (is_virtual_device(name)) {
      free(name);
      continue;
    }

    io = WRAP(snd_device_name_get_hint)(*hint, &quot;IO&quot;);
    if (io) {
      if (type == CUBEB_DEVICE_TYPE_INPUT &amp;&amp; strcmp(io, &quot;Input&quot;) != 0) {
        free(name);
        free(io);
        continue;
      }
      if (type == CUBEB_DEVICE_TYPE_OUTPUT &amp;&amp; strcmp(io, &quot;Output&quot;) != 0) {
        free(name);
        free(io);
        continue;
      }
      free(io);
    }

    /* Query device capabilities by opening it temporarily */
    snd_pcm_t *pcm;
    snd_pcm_hw_params_t *hw_params;
    snd_pcm_hw_params_alloca(&amp;hw_params);
    snd_pcm_format_mask_t *format_mask;

    if (WRAP(snd_pcm_open)(&amp;pcm, name, 
                          type == CUBEB_DEVICE_TYPE_INPUT ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
                          SND_PCM_NONBLOCK) &gt;= 0) {
      if (WRAP(snd_pcm_hw_params_any)(pcm, hw_params) &gt;= 0) {
        /* Query actual capabilities */
        unsigned int min_rate, max_rate;
        unsigned int min_channels, max_channels;
        snd_pcm_format_mask_alloca(&amp;format_mask);

        WRAP(snd_pcm_hw_params_get_format_mask)(hw_params, format_mask);
        WRAP(snd_pcm_hw_params_get_rate_min)(hw_params, &amp;min_rate, NULL);
        WRAP(snd_pcm_hw_params_get_rate_max)(hw_params, &amp;max_rate, NULL);
        WRAP(snd_pcm_hw_params_get_channels_min)(hw_params, &amp;min_channels);
        WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, &amp;max_channels);

        /* Determine supported formats */
        cubeb_device_fmt format = CUBEB_DEVICE_FMT_S16NE;
        if (WRAP(snd_pcm_format_mask_test)(format_mask, SND_PCM_FORMAT_S32_LE)) {
          format |= CUBEB_DEVICE_FMT_S32LE;
        }
        if (WRAP(snd_pcm_format_mask_test)(format_mask, SND_PCM_FORMAT_FLOAT)) {
          format |= CUBEB_DEVICE_FMT_F32NE;
        }

        devinfop[collection_cnt].device_id = strdup(name);
        devinfop[collection_cnt].devid = (cubeb_devid)devinfop[collection_cnt].device_id;
        desc = WRAP(snd_device_name_get_hint)(*hint, &quot;DESC&quot;);
        devinfop[collection_cnt].friendly_name = desc ? strdup(desc) : strdup(name);
        if (desc) free(desc);
        devinfop[collection_cnt].group_id = strdup(name);
        devinfop[collection_cnt].vendor_name = NULL;
        devinfop[collection_cnt].type = type;
        devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED;
        devinfop[collection_cnt].preferred = CUBEB_DEVICE_PREF_NONE;
        devinfop[collection_cnt].format = format;
        devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE; /* Default to S16 */
        devinfop[collection_cnt].max_channels = max_channels;
        devinfop[collection_cnt].default_rate = 44100;
        devinfop[collection_cnt].min_rate = min_rate;
        devinfop[collection_cnt].max_rate = max_rate;
        devinfop[collection_cnt].latency_lo = 0;
        devinfop[collection_cnt].latency_hi = 0;

        collection_cnt++;
      }
      WRAP(snd_pcm_close)(pcm);
    }

    free(name);
  }

  WRAP(snd_device_name_free_hint)(hints);

  /* Add BlueALSA devices, if BT headphones are connected */  
  snd_pcm_t *pcm;  
  int err = WRAP(snd_pcm_open)(&amp;pcm, &quot;bluealsa&quot;, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);  
  if (err == 0) {  
      /* BlueALSA is available (BT headphones connected) */  
      WRAP(snd_pcm_close)(pcm);  
      /* Add to device list */  
      add_bluealsa_device(&amp;devinfop, &amp;collection_cnt, type);  
  }  
  /* If err != 0, BlueALSA is not available (no BT headphones) */

  collection-&gt;device = devinfop;
  collection-&gt;count = collection_cnt;

  return CUBEB_OK;
}

static int
alsa_device_collection_destroy(cubeb * context,
                               cubeb_device_collection * collection)
{
  size_t i;
  (void)context;

  if (!collection-&gt;device)
    return CUBEB_OK;

  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);
  collection-&gt;device = NULL;
  collection-&gt;count = 0;

  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><p>Sound quality is much better now. <br />Although, of course, semi-deaf, semi-blind, and half-demented ALSA users may not notice any difference.<br />It seems that almost all ALSA backends include some kind of PulseAudio workaround and/or other bugs. That is why, perhaps, ALSA has a reputation for sounding like it’s playing from inside a shoebox.</p><p>Redirecting to PulseAudio from within an ALSA backend is an architectural bug. PulseAudio is a separate userspace sound server and should be handled by its own dedicated backend (e.g., Cubeb&#039;s pulse or pulse-rust backends), not by an ALSA backend that treats the pulse ALSA plugin as if it were an ALSA device.</p><p>PulseAudio has its own IPC, latency model, stream routing, and device enumeration. Trying to handle it through the ALSA API forces the backend to carry workarounds for the PA plugin&#039;s behavior (e.g., the handle_underrun workaround), which only make sense when the plugin is actually in the path.</p><p>The ALSA backend should focus on ALSA devices, and PulseAudio should be handled by a dedicated PulseAudio backend. The pulse virtual card is an external plugin, not a native ALSA device, and conflating the two subsystems leads to unnecessary workarounds and reduced audio quality.</p><p>The PulseAudio ALSA plugin workaround is only meaningful when the <span class="bbc">pulse</span> virtual PCM plugin from <span class="bbc">libasound2-plugins</span> is actually in the path. When PulseAudio, the Pulse plugin, and PipeWire are removed, the workaround can degrade sound quality by forcing a configuration path that adds latency, triggers unnecessary conversions, or selects suboptimal hardware parameters for direct device access.</p><p>The PulseAudio plugin is not part of core ALSA. It is an external virtual sound card provided by <span class="bbc">libasound2-plugins</span> and appears alongside physical cards in the control interface.</p><p>Core ALSA&#039;s built-in plugin list does not include <span class="bbc">pulse</span></p><p>This means the workaround is targeting an optional, userspace-server-specific plugin, not the ALSA hardware path.</p><p>When PulseAudio/PipeWire and the Pulse plugin are gone, the ALSA backend should open ALSA devices directly<br />(e.g., <span class="bbc">dmix</span> with fftrate, or hardware devices, such as <span class="bbc">hw:CARD=PCH,DEV=0</span>). The workaround, however, was built around the assumption that ALSA&#039;s default PCM might resolve through the <span class="bbc">pulse</span> plugin. In a PulseAudio-free system, leftover detection logic or a forced <span class="bbc">local_config</span> can:</p><p>1. Apply the wrong parameter set — the workaround adjusted hardware parameters to compensate for PulseAudio&#039;s latency behavior, which is irrelevant for direct <span class="bbc">hw</span> access.</p><p>2. Trigger the <span class="bbc">plug</span> plugin chain — if the requested parameters do not match the hardware, ALSA&#039;s <span class="bbc">plug</span> plugin inserts format/rate/channel conversions.</p><p>Each conversion in that chain can add quantization, resampling artifacts, or latency.</p><p>3. Open the device through a non-default configuration — <span class="bbc">snd_pcm_open()</span> resolves the name through the global ALSA configuration tree. </p><p>If the workaround injects a <span class="bbc">local_config</span> that overrides the normal tree, it can route a direct hardware request through <span class="bbc">plug</span> → <span class="bbc">pulse</span> or through an unintended slave, even when the user expects raw hardware access.</p><p>Removing the workaround lets <span class="bbc">snd_pcm_open()</span> follow the normal configuration path for the requested device. This is why sound quality is much better: the audio path became shorter and no longer carried PulseAudio-specific latency/buffering compensations.</p><p>The workaround was correct for its original target environment, but it is actively harmful in a clean ALSA-only setup. Removing it is the right thing to do.</p><p>The workaround was necessary for PulseAudio users when dedicated PulseAudio backends were unavailable; however, in a clean ALSA environment, it is detrimental to audio quality.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Tue, 28 Jul 2026 10:06:49 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=8012&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[The Absurdist Comedy]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7605&amp;action=new</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?id=7605&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[The exclusive mode of ALSA]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=8053&amp;action=new</link>
			<description><![CDATA[<h5>The exclusive mode of ALSA</h5><p>With <span class="bbc">plughw:0,0</span> or default, the audio path is:</p><div class="codebox"><pre><code>Application → ALSA Library → Plugin Layer → Kernel PCM Core → Hardware Driver → Hardware </code></pre></div><p>The ALSA path with <span class="bbc">hw:0,0</span> is:</p><div class="codebox"><pre><code>Application → ALSA Library → Kernel PCM Core → Hardware Driver → Hardware </code></pre></div><p>When using <span class="bbc">hw:0,0</span>, the ALSA library bypasses the plugin layer (software format/rate/channel conversion) but still goes through the kernel PCM core for buffer management, period handling, and synchronization before reaching the hardware driver. </p><p>Usually, &quot;exclusive mode&quot; refers to exclusive device access control by one applications (this is what the word exclusive is supposed to mean). Such exclusive device access prevents multiple applications from simultaneously using the same hardware resource. In the Linux kernel&#039;s technical documentation, &quot;exclusive&quot; has a specific meaning: preventing multiple applications from simultaneously using the same hardware resource. </p><p>ALSA permits &quot;exclusive full duplex&quot;: </p><div class="codebox"><pre><code>$ arecord -f cd -D hw:0,0 | aplay -V mono -D hw:0,0  
Recording WAVE &#039;stdin&#039; : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE &#039;stdin&#039; : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
#####################   +                          | 46%

$ arecord -f cd -D hw:1,0 | aplay -V mono -D hw:1,0 
Recording WAVE &#039;stdin&#039; : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE &#039;stdin&#039; : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
##################################################+| MAX

$ arecord -f cd -D hw:PCH,0 | aplay -V mono -D hw:PCH,0 
Recording WAVE &#039;stdin&#039; : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE &#039;stdin&#039; : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
####################                             + | 97% </code></pre></div><p><span class="bbc">arecord</span> opens <span class="bbc">hw:0,0</span> for capture<br /><span class="bbc">aplay</span> opens <span class="bbc">hw:0,0</span> for playback<br />Both processes have the the same device open simultaneously in &quot;exclusive mode&quot;.</p><p>The active meter indicates that the full duplex works and audio data is actively flowing through the pipeline in real-time.</p><p>Thus, two applications, <span class="bbc">arecord</span> and <span class="bbc">aplay</span>, have exclusive access to the same device simultaneously. It works with Intel HDA codecs, USB soundcards, etc.</p><p>OSS4 with software mixer <span class="bbc">vmix</span> disabled does not support &quot;exclusive full duplex&quot; for Intel HDA codecs</p><div class="codebox"><pre><code>$ ./fulldup

Using audio engine 0=HD Audio play front for duplex

/dev/dsp doesn&#039;t support one device based full duplex scheme
Please use the two device scheme.

$ file /dev/dsp
/dev/dsp: symbolic link to /dev/oss/oss_hdaudio0/pcm0 </code></pre></div><p>ALSA is more advanced in this sense. In advanced Monty Python reality, the word exclusive has a flexible meaning. It may mean anything you want.</p>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Sun, 21 Jun 2026 01:45:27 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=8053&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Do ALSA users really need fftrate?]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=8049&amp;action=new</link>
			<description><![CDATA[<p>Bypassing the hardware resampler in Intel HDA codecs with a software real-time fftrate codec to resample everything to 192kHz 32-bit is a solid architectural strategy. It works at the userspace level on top of ALSA and avoids potential quality issues in codec hardware SRC implementations.</p><p><strong>HDA Codec Resampling in the Kernel</strong></p><p>The Linux kernel&#039;s HDA subsystem does have hardware sample rate converter (SRC) capabilities managed by codec drivers. For example, the Creative CA0132 driver documentation shows that HDA codecs have dedicated SRC ports (0x80–0xbf) for sample rate conversion within the codec&#039;s internal routing.</p><p><strong>Kernel-Level Format Handling</strong></p><p>The kernel handles PCM format conversion through the <span class="bbc">snd_hdac_stream_format()</span> function, which converts channel count, sample format, and sample rate into the HDA format value used by the hardware.</p><p><strong>Hardware-Specific Workarounds</strong></p><p>Some codecs have hardware-specific sample rate limitations that require kernel-level workarounds. For instance, the ALC269 driver has a fixup for the Lenovo Ideapad that forces analog I/O to 44.1kHz due to a hardware problem.</p><p><strong>The fftrate project</strong></p><p>The software resampling approach of the fftrate project bypasses these kernel-level hardware resamplers entirely by:<br />1.&#160; Accepting audio at any rate from the application.<br />2.&#160; Resampling to 192kHz 32-bit in userspace.<br />3.&#160; Sending the already-resampled audio to ALSA at a fixed rate.</p><p>This avoids potential quality issues or bugs in the codec&#039;s hardware SRC implementation. The kernel ALSA driver simply sees a constant 192kHz 32-bit stream and passes it through without additional resampling.</p><p><strong>Dialectical conclusion</strong></p><p>There is a belief that fftrate may cure deafness, though it hasn&#039;t been verified by scientific methods. Give it a chance. It may (or may not) work.</p><div class="quotebox"><blockquote><div><p><strong>NOTE:</strong> The claim that fftrate can &quot;cure deafness&quot; is a metaphorical exaggeration common in audiophile circles, not a medical fact. There is no scientific evidence that software resampling can restore hearing loss or cure physiological deafness.</p></div></blockquote></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Thu, 18 Jun 2026 18:58:54 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=8049&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Chasing the Fox (Caccia alla volpe)]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7620&amp;action=new</link>
			<description><![CDATA[<div class="quotebox"><cite>greenjeans wrote:</cite><blockquote><div><p>That would be a real service to the community</p></div></blockquote></div><p>A real service to the community might be to avoid annoying others with your comments and advice. Please do not post in my topics.</p><div class="quotebox"><blockquote><div><p>_https://en.wikipedia.org/wiki/Sndio<br /><strong>sndio</strong> is the software layer of the OpenBSD operating system that manages sound cards and MIDI ports. It provides an optional <strong>sound server</strong> and a documented application programming interface to access either the server or the audio and MIDI hardware in a uniform way.</p></div></blockquote></div><div class="quotebox"><blockquote><div><p>_https://man.openbsd.org/sndiod.8<br /><strong>BUGS</strong><br /><strong><span style="color: green">Resampling is low quality</span></strong>; down-sampling especially should be avoided when recording.</p><p>If -a off is used, sndiod creates sub-devices to expose first and then opens the audio hardware on demand. Technically, this allows sndiod to attempt to use one of the sub-devices it exposes as an audio device, creating a deadlock. There&#039;s nothing to prevent the user from shooting themselves in the foot by creating such a deadlock.</p></div></blockquote></div><p><strong>sndio</strong> might be perfectly suitable for semi-deaf and half-demented users.</p><p>If someone really needs <strong>sndio</strong>, he might consider forking it to integrate the fftrate resampler for higher-quality audio processing<br />_https://github.com/PetrovSE/fftrate</p><div class="quotebox"><blockquote><div><p>_https://man.openbsd.org/sndiod.8<br /><strong>BUGS</strong><br /><strong><span style="color: green">Resampling is low quality</span></strong>; down-sampling especially should be avoided when recording.</p></div></blockquote></div><p>Perhaps <strong>sndio</strong> developers engage in post-modern humor. Linux users are unlikely to notice a Monty Python situation where a &quot;bug&quot; is documented rather than fixed.</p><p>If you don’t get this kind of humor, think of post-truth, post-philosophy, and post-documentation. It’s a post-real world where absurdity is plainly documented — in man pages, wikis, and official notes — without irony. The joke isn’t hidden; it’s right there, labeled &quot;BUG&quot;. The word &quot;bug&quot; has become a post-word with a post-meaning: not a flaw to fix, but a punchline accepted as fact.</p><p>What is special about post-reality is that it can be consumed innocently — just like myth, it appears factual, not constructed. </p><div class="quotebox"><blockquote><div><p>The myth consumer takes the signification for a system of facts: myth is read as a factual system whereas it is but a semiological system.<br />Roland Barthes, <em>Myth Today.</em></p></div></blockquote></div>]]></description>
			<author><![CDATA[dummy@example.com (igorzwx)]]></author>
			<pubDate>Tue, 05 May 2026 14:17:10 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7620&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[system-config-printer-udev: 70-printers.rules]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=4819&amp;action=new</link>
			<description><![CDATA[<p>Bug report and patch posted to <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132179" rel="nofollow">https://bugs.debian.org/cgi-bin/bugrepo … ug=1132179</a>.</p>]]></description>
			<author><![CDATA[dummy@example.com (teom)]]></author>
			<pubDate>Sun, 29 Mar 2026 12:16:56 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=4819&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[How will life be without gvfs?]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7851&amp;action=new</link>
			<description><![CDATA[<p>Hello:</p><div class="quotebox"><cite>tux_99 wrote:</cite><blockquote><div><p>... don&#039;t know what&#039;s the root cause of your issues ...</p></div></blockquote></div><p>Hmm ...<br />I have <em>so</em> many, but the root cause to any of them has eluded me from an early age.</p><p>Most of them can be found in any edition of Jon Winokur&#039;s &#039;The Portable Curmudgeon&#039;.</p><div class="quotebox"><blockquote><div><p>... don&#039;t have any of those files you listed in that dbus folder ...</p></div></blockquote></div><p>That is because I have <span class="bbc">gvfs</span> installed and you do not.</p><p>I had to reinstall it.</p><p>Best,</p><p>A.</p>]]></description>
			<author><![CDATA[dummy@example.com (Altoid)]]></author>
			<pubDate>Tue, 24 Mar 2026 20:09:36 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7851&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Needs for better modularization of installs]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7847&amp;action=new</link>
			<description><![CDATA[<div class="quotebox"><blockquote><div><p>So it does work using extracted squashfs. The errors seem just warnings. It&#039;s there in the overlay. The overlay can be mounted in main system and new initrd copied to where the the relevant bootloader can find it.</p><p>But it doesn&#039;t work using fromiso/findiso (my preferred use case) without user intervention as noted. I don&#039;t see that mentioned in live-boot&#039;s manual.</p></div></blockquote></div><p>I really appreciate that you take the time to delve into this stuff.<br />At present, I don&#039;t have the time myself, but it seems that clarifications and eventual workarounds may be helpful also to the upstream developers.</p>]]></description>
			<author><![CDATA[dummy@example.com (tyder)]]></author>
			<pubDate>Tue, 17 Mar 2026 11:54:14 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7847&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[what about "turnstile" for user services?]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7798&amp;action=new</link>
			<description><![CDATA[<p>A note for anyone holding their breath:<br /> <span class="bbc">turnstile</span> is now available on ceres<br />(debian package version 0.1.11-2)</p>]]></description>
			<author><![CDATA[dummy@example.com (ralph.ronnquist)]]></author>
			<pubDate>Sat, 07 Mar 2026 05:03:18 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7798&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[About installers]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7803&amp;action=new</link>
			<description><![CDATA[<p>I really would like people to put just a little bit of thought into how their software may reflect on them, before publishing it.</p><p>I assume you were obliquely referring to my comment, ralph.ronnquist, so I removed what is probably the part you found most objectionable.</p><p>But I have spent a great deal of my life trying to be polite and diplomatic, and I&#039;m now at the age where I think a lot of the world&#039;s problems are caused by stupid people not being properly told they&#039;re being stupid. To that end, I&#039;ve grown very fond of Linus Torvalds&#039; occasional rants about &quot;brain damage&quot; in software, because that&#039;s what it is, and it needs to be called out. Politeness implies acceptance or at least tolerance, and stupid decisions should be greeted with neither.</p><p>Stupid decisions are stupid.</p>]]></description>
			<author><![CDATA[dummy@example.com (Mercury)]]></author>
			<pubDate>Fri, 20 Feb 2026 23:42:04 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7803&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[UDisks2: Security Considerations]]></title>
			<link>https://dev1galaxy.org/viewtopic.php?id=7807&amp;action=new</link>
			<description><![CDATA[<p>Find some other place for chatting please.</p>]]></description>
			<author><![CDATA[dummy@example.com (ralph.ronnquist)]]></author>
			<pubDate>Sun, 15 Feb 2026 21:57:57 +0000</pubDate>
			<guid>https://dev1galaxy.org/viewtopic.php?id=7807&amp;action=new</guid>
		</item>
	</channel>
</rss>
