Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ __pycache__
.eggs
/dist/
/wheelhouse/
.DS_Store
2 changes: 1 addition & 1 deletion portaudio

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would normally try to stay on released versions, but if you like to depend on a development version, that's also fine for me.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v19.7.0 is from 2021 and there's been no tag since (!). There are a few years of upstream fixes (e.g. CoreAudio alignment)

Submodule portaudio updated 230 files
5 changes: 4 additions & 1 deletion src/rtmixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ int callback(const void* input, void* output, frame_t frameCount
struct state* state = userData;
CALLBACK_ASSERT(state);

memset(output, 0, sizeof(float) * state->output_channels * frameCount);
if (output) // NULL for input-only streams
{
memset(output, 0, sizeof(float) * state->output_channels * frameCount);
}

get_stats(frameCount, statusFlags, &(state->stats));

Expand Down
28 changes: 27 additions & 1 deletion src/rtmixer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ def __init__(self, kind, qsize=16, **kwargs):
self._actions = {}
self._temp_action_ptr = _ffi.new('struct action**')

def __del__(self):
# The C callback uses memory owned by this object, so the stream
# must be closed before that memory is freed.
try:
self.abort()
self.close()
except Exception:
pass # e.g. __init__() failed or interpreter shutdown

@property
def actions(self):
"""The set of active "actions"."""
Expand Down Expand Up @@ -76,7 +85,8 @@ def cancel(self, action, time=0, allow_belated=True):
requested_time=time,
action=action,
))
self._enqueue(cancel_action)
# The callback dereferences *action*, so it must be kept alive:
self._enqueue(cancel_action, keep_alive=action)
return cancel_action

def fetch_and_reset_stats(self, time=0, allow_belated=True):
Expand Down Expand Up @@ -126,6 +136,11 @@ def _check_channels(self, channels, kind):

def _enqueue(self, action, keep_alive=None):
self._drain_result_q()
# Each action ends up in the result queue exactly once, so limiting
# the number of pending actions guarantees that it never overflows:
if len(self._actions) >= len(self._result_q):
raise RuntimeError(
'Too many pending actions, consider increasing qsize')
self._temp_action_ptr[0] = action
ret = self._action_q.write(self._temp_action_ptr)
if ret != 1:
Expand All @@ -149,6 +164,13 @@ class Mixer(_Base):
except *callback* (a callback function implemented in C is used
internally) and *dtype* (which is always ``'float32'``).

The additional keyword argument *qsize* (default: 16) sets the size
of the queues used to communicate with the callback, which is also
the maximum number of pending actions, i.e. actions that have been
started (or scheduled) but haven't finished yet. If an action is
started while *qsize* actions are pending, a `RuntimeError` is
raised. It must be a power of 2.

Uses default values from `sounddevice.default` (except *dtype*,
which is always ``'float32'``).

Expand Down Expand Up @@ -217,6 +239,8 @@ class Recorder(_Base):
except *callback* (a callback function implemented in C is used
internally) and *dtype* (which is always ``'float32'``).

The additional keyword argument *qsize* is described in `Mixer`.

Uses default values from `sounddevice.default` (except *dtype*,
which is always ``'float32'``).

Expand Down Expand Up @@ -282,6 +306,8 @@ class MixerAndRecorder(Mixer, Recorder):
*callback* (a callback function implemented in C is used internally)
and *dtype* (which is always ``'float32'``).

The additional keyword argument *qsize* is described in `Mixer`.

Uses default values from `sounddevice.default` (except *dtype*,
which is always ``'float32'``).

Expand Down
Loading