Compare commits

..

33 Commits

Author SHA1 Message Date
Lars Immisch a52176f53d Fix for test.pypi.org 2025-11-18 20:50:34 +00:00
Lars Immisch 53612191d9 Mini-change to test doc generation 2025-11-18 18:41:35 +00:00
Lars Immisch eaa0e33999 Update docs to see if workflow gets triggered 2025-11-18 18:18:30 +00:00
Lars Immisch 1cd978f15f Prepare doc generation test 2025-11-18 18:18:03 +00:00
Matteo Bernardini daf38e38cf chore: use makefile for reference on building sdist and wheels 2025-09-03 13:18:03 +08:00
Matteo Bernardini 04d6f8c9d7 fix: drop py2 trove classifier 2025-09-03 12:59:57 +08:00
Matteo Bernardini f0d9d2a7a8 ci: deploy on tags only 2025-09-03 12:55:59 +08:00
Matteo Bernardini 3889f27891 ci: fix permissions 2025-09-03 12:06:49 +08:00
Matteo Bernardini e5f400866b ci: install ALSA headers 2025-09-03 12:03:45 +08:00
Matteo Bernardini 06a81377a3 ci: install uv 2025-09-03 12:00:42 +08:00
Matteo Bernardini 5e29c0dfe5 ci: automatically build docs 2025-09-03 11:58:13 +08:00
Matteo Bernardini c89faf517b chore: split dependency groups 2025-09-03 11:45:29 +08:00
Matteo Bernardini 112885e97a ci: remove unsupported setting 2025-09-03 11:30:11 +08:00
Matteo Bernardini 06e67cc533 ci: auto-detect package manager to use 2025-09-03 11:27:46 +08:00
Matteo Bernardini 1dfbc37135 ci: use legacy license definition to allow building on py3.9 2025-09-03 11:04:17 +08:00
Matteo Bernardini a3db924109 ci: enforce setuptools version 2025-09-03 10:37:56 +08:00
Matteo Bernardini 68203e9187 ci: ensure alsa headers are installed 2025-09-02 22:31:48 +08:00
Matteo Bernardini 8c693f4843 chore: stub workflow for automatic docs deployment 2025-08-24 22:04:40 +08:00
Matteo Bernardini b4e2ea4fd5 chore: top-level makefile for common dev tasks 2025-08-24 22:03:53 +08:00
Matteo Bernardini 4c75488b47 ci: workflow to automate releases on tags 2025-08-24 21:56:56 +08:00
Matteo Bernardini 39846bfb3e chore: make pyright happy on tests 2025-08-06 18:06:43 +08:00
Matteo Bernardini 0aa35bc379 fix: PCM constructor stubs not coherent with runtime 2025-08-06 18:04:28 +08:00
Matteo Bernardini fb17a4e9af fix(docs): add sphinx dep, fix version retrieval 2025-08-06 17:27:00 +08:00
Matteo Bernardini a9cb7d8437 fix: stub consistency to runtime 2025-08-06 17:17:57 +08:00
Matteo Bernardini b3730bc713 chore(deps): add mypy (for stubtest utility) 2025-08-06 17:14:31 +08:00
Matteo Bernardini 2b090f05fa fix: use Final for enum constants 2025-08-06 17:12:53 +08:00
Matteo Bernardini 177d9e147d fix: missing self in type stubs 2025-08-06 17:03:43 +08:00
Matteo Bernardini be7e203fdb chore(deps): add pyright for type-checking 2025-08-06 17:03:32 +08:00
Matteo Bernardini 1670c6b1a3 fix: add ALSAAudioError to type stubs 2025-08-06 15:45:28 +08:00
Matteo Bernardini 2e0f952475 fix: include doc/ and examples/ in sdist 2025-08-06 15:33:35 +08:00
Matteo Bernardini aea0fc62e8 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-08-06 13:05:52 +08:00
Matteo Bernardini 807c36d133 chore: move unit tests to conventional directory 2025-08-06 13:01:10 +08:00
Matteo Bernardini d86f379554 chore: move examples to dedicated directory 2025-08-06 12:59:57 +08:00
6 changed files with 108 additions and 204 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- main - mttbernardini/chore/modern-packaging
concurrency: concurrency:
group: "pages" group: "pages"
+5 -1
View File
@@ -42,7 +42,8 @@ jobs:
upload_pypi: upload_pypi:
needs: [build_wheels, build_sdist] needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: pypi # TODO: set environment back to pypi after testing
environment: testpypi
permissions: permissions:
id-token: write id-token: write
steps: steps:
@@ -53,3 +54,6 @@ jobs:
path: dist path: dist
merge-multiple: true merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1 - uses: pypa/gh-action-pypi-publish@release/v1
# TODO: delete below once workflow is proved functional
with:
repository-url: https://test.pypi.org/legacy/
+3
View File
@@ -807,6 +807,9 @@ Play back the recording with::
$ python playbacktest.py <filename> $ python playbacktest.py <filename>
playbacktest.py accepts the commandline option
*-d <device>*.
mixertest.py mixertest.py
~~~~~~~~~~~~ ~~~~~~~~~~~~
+1 -1
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 and need the ALSA headers for compilation. Verify that you have
/usr/lib/libasound.so and /usr/include/alsa (or similar paths) before building. /usr/lib/libasound.so and /usr/include/alsa (or similar paths) before building.
*On Debian (and probably Ubuntu), install libasound2-dev.* *On Debian/Ubuntu, install libasound2-dev.*
Naturally you also need to use a kernel with proper ALSA support. This is the 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 default in Linux kernel 2.6 and later. If you are using kernel version 2.4 you
+95 -143
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- mode: python; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; python-indent: 4 -*- # -*- mode: python; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
import sys import sys
import select import select
@@ -7,21 +7,12 @@ import logging
import re import re
import struct import struct
import subprocess import subprocess
import errno
from enum import Enum, auto
from datetime import datetime, timedelta from datetime import datetime, timedelta
from alsaaudio import (PCM, pcms, PCM_PLAYBACK, PCM_CAPTURE, PCM_NONBLOCK, Mixer, from alsaaudio import (PCM, pcms, PCM_PLAYBACK, PCM_CAPTURE, PCM_NONBLOCK, Mixer,
PCM_STATE_OPEN, PCM_STATE_SETUP, PCM_STATE_PREPARED, PCM_STATE_RUNNING, PCM_STATE_XRUN, PCM_STATE_DRAINING, PCM_STATE_OPEN, PCM_STATE_SETUP, PCM_STATE_PREPARED, PCM_STATE_RUNNING, PCM_STATE_XRUN, PCM_STATE_DRAINING,
PCM_STATE_PAUSED, PCM_STATE_SUSPENDED, ALSAAudioError) PCM_STATE_PAUSED, PCM_STATE_SUSPENDED, ALSAAudioError)
from argparse import ArgumentParser from argparse import ArgumentParser
# wake up at least after *idle_timer_period* seconds
idle_timer_period = 0.25
# grace period for opening the device in seconds
open_grace_period = 0.5
# close the device after *idle_close_timeout* seconds silence
idle_close_timeout = 2.0
poll_names = { poll_names = {
select.POLLIN: 'POLLIN', select.POLLIN: 'POLLIN',
select.POLLPRI: 'POLLPRI', select.POLLPRI: 'POLLPRI',
@@ -43,10 +34,6 @@ state_names = {
PCM_STATE_SUSPENDED: 'PCM_STATE_SUSPENDED' PCM_STATE_SUSPENDED: 'PCM_STATE_SUSPENDED'
} }
type NamedProcess = tuple[str, subprocess.Popen]
cmd_process : NamedProcess = None
def poll_desc(mask): def poll_desc(mask):
return '|'.join([poll_names[bit] for bit, name in poll_names.items() if mask & bit]) return '|'.join([poll_names[bit] for bit, name in poll_names.items() if mask & bit])
@@ -70,19 +57,14 @@ class PollDescriptor(object):
return cls(name, fd, mask) return cls(name, fd, mask)
class LoopbackState(Enum):
LISTENING = auto()
PLAYING = auto()
DEVICE_BUSY = auto()
class Loopback(object): class Loopback(object):
'''Loopback state and event handling''' '''Loopback state and event handling'''
def __init__(self, capture, playback_args, volume_handler, run_after_stop=None, run_before_start=None): def __init__(self, capture, playback_args, volume_handler, run_after_stop=None, run_before_start=None):
self.playback_args = playback_args self.playback_args = playback_args
self.playback = None self.playback = None
self.volume_handler = volume_handler self.volume_handler = volume_handler
self.capture_started = None
self.last_capture_event = None self.last_capture_event = None
self.capture = capture self.capture = capture
@@ -95,10 +77,13 @@ class Loopback(object):
self.run_before_start = None self.run_before_start = None
if run_before_start: if run_before_start:
self.run_before_start = run_before_start.split(' ') self.run_before_start = run_before_start.split(' ')
self.run_after_stop_did_run = False
self.state = None self.waitBeforeOpen = False
self.last_state_change = None self.queue = []
self.silence_start = None
self.period_size = 0
self.silent_periods = 0
@staticmethod @staticmethod
def compute_energy(data): def compute_energy(data):
@@ -112,84 +97,43 @@ class Loopback(object):
@staticmethod @staticmethod
def run_command(cmd): def run_command(cmd):
if cmd: if cmd:
global cmd_process rc = subprocess.run(cmd)
cmd_process = (cmd, subprocess.Popen(cmd)) if rc.returncode:
logging.warning(f'run {cmd}, return code {rc.returncode}')
@staticmethod else:
def check_command_idle_handler(): logging.info(f'run {cmd}, return code {rc.returncode}')
# an idle handler to watch the process created above
if cmd_process:
rc = cmd_process[1].poll()
if rc:
if rc.returncode:
logging.warning(f'run {cmd_process[0]}, return code {rc.returncode}')
else:
logging.info(f'run {cmd_process[0]}, return code {rc.returncode}')
cmd_process = None
def register(self, reactor): def register(self, reactor):
reactor.register_idle_handler(self.check_command_idle_handler) reactor.register_timeout_handler(self.timeout_handler)
reactor.register_idle_handler(self.idle_handler)
reactor.register(self.capture_pd, self) reactor.register(self.capture_pd, self)
def start(self): def start(self):
assert self.state == None, "start must only be called once"
# start reading data # start reading data
size, _ = self.capture.read() size, data = self.capture.read()
if size: if size:
logging.warning(f'initial data discarded ({size} bytes)') self.queue.append(data)
self.state = LoopbackState.LISTENING def timeout_handler(self):
if self.playback and self.capture_started:
def set_state(self, new_state: LoopbackState) -> LoopbackState: if self.last_capture_event:
'''Implement the Loopback state as a state machine''' if datetime.now() - self.last_capture_event > timedelta(seconds=2):
logging.info('timeout - closing playback device')
if self.state == new_state: self.playback.close()
return self.state self.playback = None
self.capture_started = None
logging.info(f'{self.state} -> {new_state}')
self.last_state_change = datetime.now()
if new_state == LoopbackState.LISTENING:
if self.state == LoopbackState.PLAYING:
self.playback.close()
self.playback = None
self.last_capture_event = None
if self.volume_handler:
self.volume_handler.stop()
self.run_command(self.run_after_stop)
elif self.state == LoopbackState.DEVICE_BUSY:
pass
elif new_state == LoopbackState.PLAYING:
if self.state == LoopbackState.LISTENING:
try:
self.run_command(self.run_before_start)
self.playback = PCM(**self.playback_args)
period_size = self.playback.info()['period_size']
logging.info(f'opened playback device with period_size {period_size}')
if self.volume_handler: if self.volume_handler:
self.volume_handler.start() self.volume_handler.stop()
except ALSAAudioError as e: self.run_command(self.run_after_stop)
logging.warning('opening PCM playback device failed: %s', e)
self.state = LoopbackState.DEVICE_BUSY
return self.state
elif self.state == LoopbackState.DEVICE_BUSY:
# Only try to reopen the device after the grace period
if datetime.now() - self.last_state_change > timedelta(seconds=open_grace_period):
return self.set_state(LoopbackState.PLAYING)
elif new_state == LoopbackState.DEVICE_BUSY:
logging.error(f'{new_state} is internal and cannot be set directly')
self.state = new_state
return self.state
def idle_handler(self):
if self.state == LoopbackState.PLAYING:
if datetime.now() - self.last_capture_event > timedelta(seconds=idle_close_timeout):
logging.info('timeout - closing playback device')
self.set_state(LoopbackState.LISTENING)
return return
self.waitBeforeOpen = False
if not self.run_after_stop_did_run and not self.playback:
if self.volume_handler:
self.volume_handler.stop()
self.run_command(self.run_after_stop)
self.run_after_stop_did_run = True
def pop(self): def pop(self):
if len(self.queue): if len(self.queue):
return self.queue.pop() return self.queue.pop()
@@ -197,15 +141,6 @@ class Loopback(object):
return None return None
def handle_capture_event(self, eventmask, name): def handle_capture_event(self, eventmask, name):
if eventmask & select.POLLERR == select.POLLERR:
# This is typically an underrun caused by the external command being run synchronously
# (on the same thread)
state = self.capture.state()
if state == PCM_STATE_XRUN:
self.capture.drop()
logging.warning(f'POLLERR for capture device: {state_names[state]}')
'''called when data is available for reading''' '''called when data is available for reading'''
self.last_capture_event = datetime.now() self.last_capture_event = datetime.now()
size, data = self.capture.read() size, data = self.capture.read()
@@ -213,37 +148,65 @@ class Loopback(object):
logging.warning(f'capture event but no data') logging.warning(f'capture event but no data')
return False return False
# the usecase is a USB capture device where we get perfect silence when it's idle
# compute the energy and go back to LISTENING if nothing is captured
energy = self.compute_energy(data) energy = self.compute_energy(data)
logging.debug(f'energy: {energy}')
# the usecase is a USB capture device where we get perfect silence when it's idle
if energy == 0: if energy == 0:
if self.silence_start is None: self.silent_periods = self.silent_periods + 1
self.silence_start = datetime.now()
# turn off playback after idle_close_timeout when there was only silence # turn off playback after two seconds of silence
if datetime.now() - self.silence_start > timedelta(seconds=idle_close_timeout): # 2 channels * 2 seconds * 2 bytes per sample
logging.debug('silence') fps = self.playback_args['rate'] * 8 // (self.playback_args['periodsize'] * self.playback_args['periods'])
self.set_state(LoopbackState.LISTENING)
return False logging.debug(f'{self.silent_periods} of {fps} silent periods: {self.playback}')
if self.silent_periods > fps and self.playback:
logging.info(f'closing playback due to silence')
self.playback.close()
self.playback = None
if self.volume_handler:
self.volume_handler.stop()
self.run_command(self.run_after_stop)
self.run_after_stop_did_run = True
if not self.playback:
return
else: else:
self.silence_start = None self.silent_periods = 0
loop_state = self.set_state(LoopbackState.PLAYING) if not self.playback:
if loop_state != LoopbackState.PLAYING: if self.waitBeforeOpen:
logging.warning(f'setting state PLAYING failed: {str(loop_state)}') return False
try:
if self.volume_handler:
self.volume_handler.start()
self.run_command(self.run_before_start)
self.playback = PCM(**self.playback_args)
self.period_size = self.playback.info()['period_size']
logging.info(f'opened playback device with period_size {self.period_size}')
except ALSAAudioError as e:
logging.info('opening PCM playback device failed: %s', e)
self.waitBeforeOpen = True
return False
self.capture_started = datetime.now()
logging.info(f'{self.playback} capture started: {self.capture_started}')
self.queue.append(data)
if len(self.queue) <= 2:
logging.info(f'buffering: {len(self.queue)}')
return False return False
if data: try:
space = self.playback.avail() data = self.pop()
if space > 0: if data:
space = self.playback.avail()
written = self.playback.write(data) written = self.playback.write(data)
if written == -errno.EPIPE: logging.debug(f'wrote {written} bytes while space was {space}')
logging.warning('playback underrun') except ALSAAudioError:
self.playback.write(data) logging.error('underrun', exc_info=1)
silence = ''
if energy == 0:
silence = '(silence)'
logging.debug(f'wrote {written} bytes while space was {space} {silence}')
return True return True
@@ -276,13 +239,11 @@ class VolumeForwarder(object):
def start(self): def start(self):
self.active = True self.active = True
if self.volume: if self.volume:
logging.info(f'start volume is {self.volume}')
self.volume = playback_control.setvolume(self.volume) self.volume = playback_control.setvolume(self.volume)
def stop(self): def stop(self):
self.active = False self.active = False
self.volume = self.playback_control.getvolume(pcmtype=PCM_CAPTURE)[0] self.volume = self.playback_control.getvolume(pcmtype=PCM_CAPTURE)[0]
logging.info(f'stop volume is {self.volume}')
def __call__(self, fd, eventmask, name): def __call__(self, fd, eventmask, name):
if not self.active: if not self.active:
@@ -302,7 +263,7 @@ class Reactor(object):
def __init__(self): def __init__(self):
self.poll = select.poll() self.poll = select.poll()
self.descriptors = {} self.descriptors = {}
self.idle_handlers = set() self.timeout_handlers = set()
def register(self, polldescriptor, callable): def register(self, polldescriptor, callable):
logging.debug(f'registered {polldescriptor.name}: {poll_desc(polldescriptor.mask)}') logging.debug(f'registered {polldescriptor.name}: {poll_desc(polldescriptor.mask)}')
@@ -313,17 +274,17 @@ class Reactor(object):
self.poll.unregister(polldescriptor.fd) self.poll.unregister(polldescriptor.fd)
del self.descriptors[polldescriptor.fd] del self.descriptors[polldescriptor.fd]
def register_idle_handler(self, callable): def register_timeout_handler(self, callable):
self.idle_handlers.add(callable) self.timeout_handlers.add(callable)
def unregister_idle_handler(self, callable): def unregister_timeout_handler(self, callable):
self.idle_handlers.remove(callable) self.timeout_handlers.remove(callable)
def run(self): def run(self):
last_timeout_ev = datetime.now() last_timeout_ev = datetime.now()
while True: while True:
# poll for a bit, then send a timeout to registered handlers # poll for a bit, then send a timeout to registered handlers
events = self.poll.poll(idle_timer_period) events = self.poll.poll(0.25)
for fd, ev in events: for fd, ev in events:
polldescriptor, handler = self.descriptors[fd] polldescriptor, handler = self.descriptors[fd]
@@ -332,8 +293,8 @@ class Reactor(object):
handler(fd, ev, polldescriptor.name) handler(fd, ev, polldescriptor.name)
if datetime.now() - last_timeout_ev > timedelta(seconds=idle_timer_period): if datetime.now() - last_timeout_ev > timedelta(seconds=0.25):
for t in self.idle_handlers: for t in self.timeout_handlers:
t() t()
last_timeout_ev = datetime.now() last_timeout_ev = datetime.now()
@@ -383,16 +344,6 @@ if __name__ == '__main__':
'periods': args.periods 'periods': args.periods
} }
capture_args = {
'type': PCM_CAPTURE,
'mode': PCM_NONBLOCK,
'device': args.input,
'rate': args.rate,
'channels': args.channels,
'periodsize': args.periodsize,
'periods': args.periods
}
reactor = Reactor() reactor = Reactor()
# If args.input_mixer and args.output_mixer are set, forward the capture volume to the playback volume. # If args.input_mixer and args.output_mixer are set, forward the capture volume to the playback volume.
@@ -427,7 +378,8 @@ if __name__ == '__main__':
sys.exit(1) sys.exit(1)
if input_mixer_card is None: if input_mixer_card is None:
capture = PCM(**capture_args) capture = PCM(type=PCM_CAPTURE, mode=PCM_NONBLOCK, device=args.input, rate=args.rate,
channels=args.channels, periodsize=args.periodsize, periods=args.periods)
input_mixer_card = capture.info()['card_no'] input_mixer_card = capture.info()['card_no']
if output_mixer_card is None: if output_mixer_card is None:
+3 -58
View File
@@ -106,9 +106,6 @@ typedef struct {
snd_pcm_t *handle; snd_pcm_t *handle;
// Debug logging
int state;
// Configurable parameters // Configurable parameters
unsigned int channels; unsigned int channels;
unsigned int rate; unsigned int rate;
@@ -363,40 +360,6 @@ alsapcm_list(PyObject *self, PyObject *args, PyObject *kwds)
return result; return result;
} }
const char* state_name(int state) {
switch (state) {
case SND_PCM_STATE_OPEN:
return "SND_PCM_STATE_OPEN";
case SND_PCM_STATE_SETUP:
return "SND_PCM_STATE_SETUP";
case SND_PCM_STATE_PREPARED:
return "SND_PCM_STATE_PREPARED";
case SND_PCM_STATE_RUNNING:
return "SND_PCM_STATE_RUNNING";
case SND_PCM_STATE_XRUN:
return "SND_PCM_STATE_XRUN";
case SND_PCM_STATE_DISCONNECTED:
return "SND_PCM_STATE_DISCONNECTED";
case SND_PCM_STATE_DRAINING:
return "SND_PCM_STATE_DRAINING";
case SND_PCM_STATE_PAUSED:
return "SND_PCM_STATE_PAUSED";
case SND_PCM_STATE_SUSPENDED:
return "SND_PCM_STATE_SUSPENDED";
default:
return "invalid PCM state";
}
}
void print_state(alsapcm_t *self)
{
int state = snd_pcm_state(self->handle);
if (state != self->state) {
printf("[%s %s] %s->%s\n", self->cardname, self->pcmtype == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", state_name(self->state), state_name(state));
self->state = state;
}
}
static int alsapcm_setup(alsapcm_t *self) static int alsapcm_setup(alsapcm_t *self)
{ {
int res,dir; int res,dir;
@@ -442,8 +405,6 @@ static int alsapcm_setup(alsapcm_t *self)
self->framesize = self->channels * snd_pcm_format_physical_width(self->format)/8; self->framesize = self->channels * snd_pcm_format_physical_width(self->format)/8;
self->state = snd_pcm_state(self->handle);
return res; return res;
} }
@@ -1386,6 +1347,7 @@ static PyObject *
alsapcm_read(alsapcm_t *self, PyObject *args) alsapcm_read(alsapcm_t *self, PyObject *args)
{ {
snd_pcm_state_t state; snd_pcm_state_t state;
int res;
int size = self->framesize * self->periodsize; int size = self->framesize * self->periodsize;
int sizeout = 0; int sizeout = 0;
PyObject *buffer_obj, *tuple_obj, *res_obj; PyObject *buffer_obj, *tuple_obj, *res_obj;
@@ -1418,19 +1380,11 @@ alsapcm_read(alsapcm_t *self, PyObject *args)
buffer = PyBytes_AS_STRING(buffer_obj); buffer = PyBytes_AS_STRING(buffer_obj);
#endif #endif
int res = 0;
print_state(self);
// After drop() and drain(), we need to prepare the stream again. // After drop() and drain(), we need to prepare the stream again.
// Note that fresh streams are already prepared by snd_pcm_hw_params(). // Note that fresh streams are already prepared by snd_pcm_hw_params().
state = snd_pcm_state(self->handle); state = snd_pcm_state(self->handle);
if (state == SND_PCM_STATE_SETUP) { if ((state != SND_PCM_STATE_SETUP) ||
res = snd_pcm_prepare(self->handle); !(res = snd_pcm_prepare(self->handle))) {
printf("[%s] called snd_pcm_prepare: %d\n", self->cardname, res);
}
if (res == 0) {
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
res = snd_pcm_readi(self->handle, buffer, self->periodsize); res = snd_pcm_readi(self->handle, buffer, self->periodsize);
@@ -1534,8 +1488,6 @@ static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args)
return NULL; return NULL;
} }
print_state(self);
int res; int res;
// After drop() and drain(), we need to prepare the stream again. // After drop() and drain(), we need to prepare the stream again.
// Note that fresh streams are already prepared by snd_pcm_hw_params(). // Note that fresh streams are already prepared by snd_pcm_hw_params().
@@ -1625,9 +1577,6 @@ static PyObject *alsapcm_pause(alsapcm_t *self, PyObject *args)
return NULL; return NULL;
} }
print_state(self);
return PyLong_FromLong(res); return PyLong_FromLong(res);
} }
@@ -1650,8 +1599,6 @@ static PyObject *alsapcm_drop(alsapcm_t *self)
return NULL; return NULL;
} }
print_state(self);
return PyLong_FromLong(res); return PyLong_FromLong(res);
} }
@@ -1676,8 +1623,6 @@ static PyObject *alsapcm_drain(alsapcm_t *self)
return NULL; return NULL;
} }
print_state(self);
return PyLong_FromLong(res); return PyLong_FromLong(res);
} }