Compare commits

..

32 Commits

Author SHA1 Message Date
Pbopbo b3d11582e0 Adds read_sw function to be able to read any size of frames. 2026-04-02 17:27:53 +02:00
Lars Immisch 2a2fa8f742 Update local build instructions wherever setup.py is greppable. 2025-12-10 23:05:14 +01:00
Lars Immisch acc6f152a3 Adressing TODO in release.yml
Initial test with test.pypi.org was successful
2025-12-01 23:26:37 +01:00
Matteo Bernardini 0d6e0e7bb4 chore: use makefile for reference on building sdist and wheels 2025-12-01 23:26:37 +01:00
Matteo Bernardini 4f3c299e53 fix: drop py2 trove classifier 2025-12-01 23:26:37 +01:00
Matteo Bernardini e78c145d9e ci: deploy on tags only 2025-12-01 23:26:37 +01:00
Matteo Bernardini 6acae81487 ci: fix permissions 2025-12-01 23:26:37 +01:00
Matteo Bernardini 5eed8adfe5 ci: install ALSA headers 2025-12-01 23:26:37 +01:00
Matteo Bernardini e3e51cc34d ci: install uv 2025-12-01 23:26:37 +01:00
Matteo Bernardini f6f53ce92a ci: automatically build docs 2025-12-01 23:26:37 +01:00
Matteo Bernardini 38d0882379 chore: split dependency groups 2025-12-01 23:26:37 +01:00
Matteo Bernardini 6822cffcc8 ci: remove unsupported setting 2025-12-01 23:26:37 +01:00
Matteo Bernardini 8c1e370e04 ci: auto-detect package manager to use 2025-12-01 23:26:37 +01:00
Matteo Bernardini 6d17bc92b7 ci: use legacy license definition to allow building on py3.9 2025-12-01 23:26:37 +01:00
Matteo Bernardini 3e41a9eb97 ci: enforce setuptools version 2025-12-01 23:26:37 +01:00
Matteo Bernardini df8886dde1 ci: ensure alsa headers are installed 2025-12-01 23:26:37 +01:00
Matteo Bernardini aac2843f73 chore: stub workflow for automatic docs deployment 2025-12-01 23:26:37 +01:00
Matteo Bernardini 7987986ee4 chore: top-level makefile for common dev tasks 2025-12-01 23:26:37 +01:00
Matteo Bernardini ae93ddc48e ci: workflow to automate releases on tags 2025-12-01 23:26:37 +01:00
Matteo Bernardini a48389b750 chore: make pyright happy on tests 2025-12-01 23:26:37 +01:00
Matteo Bernardini 0b8db5f46d fix: PCM constructor stubs not coherent with runtime 2025-12-01 23:26:37 +01:00
Matteo Bernardini 6b2c4c22db fix(docs): add sphinx dep, fix version retrieval 2025-12-01 23:26:37 +01:00
Matteo Bernardini a7896ee069 fix: stub consistency to runtime 2025-12-01 23:26:37 +01:00
Matteo Bernardini ed83b3e29b chore(deps): add mypy (for stubtest utility) 2025-12-01 23:26:37 +01:00
Matteo Bernardini e96f550862 fix: use Final for enum constants 2025-12-01 23:26:37 +01:00
Matteo Bernardini 40f4647d8c fix: missing self in type stubs 2025-12-01 23:26:37 +01:00
Matteo Bernardini 3a8c53851d chore(deps): add pyright for type-checking 2025-12-01 23:26:37 +01:00
Matteo Bernardini f6770e3a42 fix: add ALSAAudioError to type stubs 2025-12-01 23:26:37 +01:00
Matteo Bernardini 0ac28e9eea fix: include doc/ and examples/ in sdist 2025-12-01 23:26:37 +01:00
Matteo Bernardini fdc5f3782e chore: modernise packaging.
- 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`
2025-12-01 23:26:37 +01:00
Matteo Bernardini 6efef83429 chore: move unit tests to conventional directory 2025-12-01 23:26:37 +01:00
Matteo Bernardini df5c2f4685 chore: move examples to dedicated directory 2025-12-01 23:26:37 +01:00
7 changed files with 123 additions and 40 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ on:
workflow_dispatch:
push:
branches:
- mttbernardini/chore/modern-packaging
- main
concurrency:
group: "pages"
+1 -5
View File
@@ -42,8 +42,7 @@ jobs:
upload_pypi:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
# TODO: set environment back to pypi after testing
environment: testpypi
environment: pypi
permissions:
id-token: write
steps:
@@ -54,6 +53,3 @@ jobs:
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
# TODO: delete below once workflow is proved functional
with:
repository-url: https://test.pypi.org/legacy/
+2 -7
View File
@@ -46,14 +46,9 @@ First, get the sources and change to the source directory:
$ cd pyalsaaudio
```
Then, build:
Then, build and install:
```
$ python setup.py build
```
And install:
```
$ sudo python setup.py install
$ pip install .
```
# Using the API
+8 -18
View File
@@ -1,28 +1,18 @@
# Make a new release
Update the version in setup.py
Create and push a tag naming the version (i.e. 0.11.1):
pyalsa_version = '0.9.0'
git tag 0.11.1
git push origin 0.11.1
Commit and push the update.
Create and push a tag naming the version (i.e. 0.9.0):
git tag 0.9.0
git push origin 0.9.0
Create the package:
python3 setup.py sdist
Upload the package
twine upload dist/*
Don't forget to update the documentation.
This should trigger a build via a github actions and publish pre-built binaries to pypi.org
# Publish the documentation
All commits to main should trigger a rebuild of the documentation.
## Historical background
The documentation is published through the `gh-pages` branch.
To publish the documentation, you need to clone the `gh-pages` branch of this repository into
-3
View File
@@ -807,9 +807,6 @@ Play back the recording with::
$ python playbacktest.py <filename>
playbacktest.py accepts the commandline option
*-d <device>*.
mixertest.py
~~~~~~~~~~~~
+2 -6
View File
@@ -77,7 +77,7 @@ Note: the wrappers link with the alsasound library (from the alsa-lib package)
and need the ALSA headers for compilation. Verify that you have
/usr/lib/libasound.so and /usr/include/alsa (or similar paths) before building.
*On Debian/Ubuntu, install libasound2-dev.*
*On Debian (and probably Ubuntu), install libasound2-dev.*
Naturally you also need to use a kernel with proper ALSA support. This is the
default in Linux kernel 2.6 and later. If you are using kernel version 2.4 you
@@ -86,11 +86,7 @@ ship with ALSA kernels.
To install, execute the following: --- ::
$ python setup.py build
And then as root: --- ::
# python setup.py install
$ pip install .
*******
+109
View File
@@ -1448,6 +1448,114 @@ alsapcm_read(alsapcm_t *self, PyObject *args)
return tuple_obj;
}
static PyObject *
alsapcm_read_sw(alsapcm_t *self, PyObject *args)
{
snd_pcm_state_t state;
int res;
int max_frames_to_read;
int size;
int sizeout = 0;
PyObject *buffer_obj, *tuple_obj, *res_obj;
char *buffer;
if (!PyArg_ParseTuple(args,"i:read_sw", &max_frames_to_read))
return NULL;
if (!self->handle) {
PyErr_SetString(ALSAAudioError, "PCM device is closed");
return NULL;
}
if (self->pcmtype != SND_PCM_STREAM_CAPTURE)
{
PyErr_Format(ALSAAudioError, "Cannot read from playback PCM [%s]",
self->cardname);
return NULL;
}
size = self->framesize * max_frames_to_read;
#if PY_MAJOR_VERSION < 3
buffer_obj = PyString_FromStringAndSize(NULL, size);
if (!buffer_obj)
return NULL;
buffer = PyString_AS_STRING(buffer_obj);
#else
buffer_obj = PyBytes_FromStringAndSize(NULL, size);
if (!buffer_obj)
return NULL;
buffer = PyBytes_AS_STRING(buffer_obj);
#endif
// After drop() and drain(), we need to prepare the stream again.
// Note that fresh streams are already prepared by snd_pcm_hw_params().
state = snd_pcm_state(self->handle);
if ((state != SND_PCM_STATE_SETUP) ||
!(res = snd_pcm_prepare(self->handle))) {
Py_BEGIN_ALLOW_THREADS
res = snd_pcm_readi(self->handle, buffer, max_frames_to_read);
Py_END_ALLOW_THREADS
if (res == -EPIPE) {
// This means buffer overrun, which we need to report.
// However, we recover the stream, so the next PCM.read() will work
// again. If recovery fails (very unlikely), report that instead.
if (!(res = snd_pcm_prepare(self->handle)))
res = -EPIPE;
}
}
if (res != -EPIPE)
{
if (res == -EAGAIN)
{
res = 0;
}
else if (res < 0) {
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
self->cardname);
Py_DECREF(buffer_obj);
return NULL;
}
else
{
sizeout = res * self->framesize;
}
}
if (size != sizeout) {
#if PY_MAJOR_VERSION < 3
/* If the following fails, it will free the object */
if (_PyString_Resize(&buffer_obj, sizeout))
return NULL;
#else
/* If the following fails, it will free the object */
if (_PyBytes_Resize(&buffer_obj, sizeout))
return NULL;
#endif
}
res_obj = PyLong_FromLong(res);
if (!res_obj) {
Py_DECREF(buffer_obj);
return NULL;
}
tuple_obj = PyTuple_New(2);
if (!tuple_obj) {
Py_DECREF(buffer_obj);
Py_DECREF(res_obj);
return NULL;
}
/* Steal reference counts */
PyTuple_SET_ITEM(tuple_obj, 0, res_obj);
PyTuple_SET_ITEM(tuple_obj, 1, buffer_obj);
return tuple_obj;
}
static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args)
{
int datalen;
@@ -1767,6 +1875,7 @@ static PyMethodDef alsapcm_methods[] = {
{"getratebounds", (PyCFunction)alsapcm_getratemaxmin, METH_VARARGS},
{"getrates", (PyCFunction)alsapcm_getrates, METH_VARARGS},
{"read", (PyCFunction)alsapcm_read, METH_VARARGS},
{"read_sw", (PyCFunction)alsapcm_read_sw, METH_VARARGS},
{"write", (PyCFunction)alsapcm_write, METH_VARARGS},
{"avail", (PyCFunction)alsapcm_avail, METH_VARARGS},
{"pause", (PyCFunction)alsapcm_pause, METH_VARARGS},