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
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
16 changes: 15 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:
if not self.closed:
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,10 @@ 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')
self._temp_action_ptr[0] = action
ret = self._action_q.write(self._temp_action_ptr)
if ret != 1:
Expand Down
Loading