- use pyproject.toml to define project metadata and setuptools-specific configuration, removing the need for a setup.py file
- move sources in conventional src/ directory
- rework stubs so that they're visible downstream. PEP-561 doesn't support top-level .pyi files, so I made a dummy package instead `alsaaudio-stubs`
snd_pcm_hw_params_get_sbits gives the number of significant bits, not
the actual number of bits stored. Change to snd_pcm_format_physical_width.
This fixes a bug where, for example on my hardware:
format = 'S32_LE'
significant bits = 24
physical bits = 32
the program will segfault because the allocated buffer is too small.
while it's usually not actually necessary to generate a stereo signal
(alsa's default plughw device will happily duplicate it for us), we
still do it for demo purposes, just because.
a more realistic demo would actually use numpy, as that's what the
library will most likely be used with, but anyway.
the thread in the background actually needs time to do something
sensible. this is most easily achieved by simply sleeping in the
foreground thread.
i addition to the 440 Hz tone, also play 1 kHz, to demonstrate how
the change() function is used.
there is no need to be pedantic about the period size, especially with
a blocking device. what's more, attempting to set it on an already
playing device would error out, and it would be rather counter-
productive to temporarily stop it.
we *really* should not paper over underruns, as they require attention.
however, the previous attempt (c2a6b6e) caused an exception to be thrown
(see #130), which was a bit excessive, and was consequently reverted
(438e52e).
so instead we make the handling consistent with what we do in read():
return the verbatim -EPIPE in this case. this can be simply ignored, and
the next write will resume the stream, so this is mostly backwards-
compatible (the failing write will be discarded and would need
repeating, but that will just cause a skip after the interruption,
which does not seem particularly relevant).
as a drive-by, again stop using snd_pcm_recover(), as it still just
obfuscates the snd_pcm_prepare() call it does in the end.
the `else` branch of the return value handling cascade got lost in
commit 438e52e, leading to us returning None on success.
rather than restoring the old code exactly, delay the construction
of the final return code object. this is more consistent with
alsapcm_read() and overall nicer.
this came from 438e52e, which tried to partially revert c2a6b6e, but
inserted a chunk that actually belonged to alsapcm_drop(). the latter
does not need to be restored, as we now handle SND_PCM_STATE_SETUP prior
to reading/writing.
my commit c2a6b6e broke it big time; we'd now just paper over overruns.
:}
the previous handling was fundamentally correct, needing only two
adjustments:
- to recover from drop()/drain(), we need to call snd_pcm_prepare() when
the stream state is SND_PCM_STATE_SETUP. notably, we must not do this
when the state is SND_PCM_STATE_XRUN.
- we should error-check the unlikely case that the recovery from an xrun
fails.
that way we now have two snd_pcm_prepare() call sites in read(), which
looks a bit messy, but it's actually correct.
as a drive-by, simplify the return value check of snd_pcm_prepare() -
values higher than zero are impossible.
- update CHANGES.md
- bump version in setup.py
[Revisionist Note] This is the end of the rewritten branch. The original
history can be found in the branch main-pre-rewrite.
* WIP
* Open/close the playback device when idle.
It takes a long time until it's stopped, though.
* open/close logic of playback device
* Fix opening logic, make period size divisible by 6
* Be less verbose in level info
* Extra argument for output mixer card index
Sometimes, this cannot be deduced from the output device
* Better silence detection
* Run run_after_stop when idle on startup
[Revisionist Note] This is a squashed commit formed from commits
f374adb, 3743cf5, and cd44517, still found in the main-pre-rewrite
branch. It incorporates a suggestion from PR #134.
The Reactor now takes a callable, and the loopback and volume forwarder
are now implemented as callable instances, which seemed the most
Pythonic solution.
This needs the patches from (probably)
https://lkml.org/lkml/2021/3/1/419. They are already in the raspberry OS
kernel sources and the setup works on an RPi 4.
This issue can be worked around by calling mixer.handleevents() before
calling mixer.getvolume(), but it makes more sense to handle all events
before returning the volume.
in principle, the state is already available from info(), but that's a
rather heavy function for something one might want to query often.
a practical use case might be checking whether a playback stream is done
draining, for example.