Skip to content

Commit 1aec2d6

Browse files
committed
Simplify deriving from _StreamBase
1 parent 53bf244 commit 1aec2d6

1 file changed

Lines changed: 90 additions & 85 deletions

File tree

sounddevice.py

Lines changed: 90 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -841,14 +841,15 @@ def get_portaudio_version():
841841

842842

843843
class _StreamBase(object):
844-
"""Base class for Raw{Input,Output}Stream."""
844+
"""Direct or indirect base class for all stream classes."""
845845

846-
def __init__(self, kind, samplerate=None, blocksize=None, device=None,
846+
def __init__(self, kind=None, samplerate=None, blocksize=None, device=None,
847847
channels=None, dtype=None, latency=None, extra_settings=None,
848848
callback=None, finished_callback=None, clip_off=None,
849849
dither_off=None, never_drop_input=None,
850850
prime_output_buffers_using_stream_callback=None,
851-
userdata=None):
851+
userdata=None, wrap_callback=None):
852+
assert wrap_callback in ('array', 'buffer', None)
852853
if blocksize is None:
853854
blocksize = default.blocksize
854855
if clip_off is None:
@@ -871,7 +872,7 @@ def __init__(self, kind, samplerate=None, blocksize=None, device=None,
871872
if prime_output_buffers_using_stream_callback:
872873
stream_flags |= _lib.paPrimeOutputBuffersUsingStreamCallback
873874

874-
if kind == 'duplex':
875+
if kind is None:
875876
idevice, odevice = _split(device)
876877
ichannels, ochannels = _split(channels)
877878
idtype, odtype = _split(dtype)
@@ -898,30 +899,86 @@ def __init__(self, kind, samplerate=None, blocksize=None, device=None,
898899
extra_settings, samplerate)
899900
self._device = parameters.device
900901
self._channels = parameters.channelCount
901-
902902
if kind == 'input':
903903
iparameters = parameters
904904
oparameters = _ffi.NULL
905905
elif kind == 'output':
906906
iparameters = _ffi.NULL
907907
oparameters = parameters
908908

909+
ffi_callback = _ffi.callback('PaStreamCallback', error=_lib.paAbort)
910+
909911
if callback is None:
910-
callback = _ffi.NULL
911-
elif isinstance(callback, _ffi.CData):
912-
# Use cast() to allow CData from different FFI instance:
913-
callback = _ffi.cast('PaStreamCallback*', callback)
912+
callback_ptr = _ffi.NULL
913+
elif kind == 'input' and wrap_callback == 'buffer':
914+
915+
@ffi_callback
916+
def callback_ptr(iptr, optr, frames, time, status, _):
917+
data = _buffer(iptr, frames, self._channels, self._samplesize)
918+
return _wrap_callback(callback, data, frames, time, status)
919+
920+
elif kind == 'input' and wrap_callback == 'array':
921+
922+
@ffi_callback
923+
def callback_ptr(iptr, optr, frames, time, status, _):
924+
data = _array(
925+
_buffer(iptr, frames, self._channels, self._samplesize),
926+
self._channels, self._dtype)
927+
return _wrap_callback(callback, data, frames, time, status)
928+
929+
elif kind == 'output' and wrap_callback == 'buffer':
930+
931+
@ffi_callback
932+
def callback_ptr(iptr, optr, frames, time, status, _):
933+
data = _buffer(optr, frames, self._channels, self._samplesize)
934+
return _wrap_callback(callback, data, frames, time, status)
935+
936+
elif kind == 'output' and wrap_callback == 'array':
937+
938+
@ffi_callback
939+
def callback_ptr(iptr, optr, frames, time, status, _):
940+
data = _array(
941+
_buffer(optr, frames, self._channels, self._samplesize),
942+
self._channels, self._dtype)
943+
return _wrap_callback(callback, data, frames, time, status)
944+
945+
elif kind is None and wrap_callback == 'buffer':
946+
947+
@ffi_callback
948+
def callback_ptr(iptr, optr, frames, time, status, _):
949+
ichannels, ochannels = self._channels
950+
isize, osize = self._samplesize
951+
idata = _buffer(iptr, frames, ichannels, isize)
952+
odata = _buffer(optr, frames, ochannels, osize)
953+
return _wrap_callback(
954+
callback, idata, odata, frames, time, status)
955+
956+
elif kind is None and wrap_callback == 'array':
957+
958+
@ffi_callback
959+
def callback_ptr(iptr, optr, frames, time, status, _):
960+
ichannels, ochannels = self._channels
961+
idtype, odtype = self._dtype
962+
isize, osize = self._samplesize
963+
idata = _array(_buffer(iptr, frames, ichannels, isize),
964+
ichannels, idtype)
965+
odata = _array(_buffer(optr, frames, ochannels, osize),
966+
ochannels, odtype)
967+
return _wrap_callback(
968+
callback, idata, odata, frames, time, status)
969+
914970
else:
915-
callback = _ffi.callback(
916-
'PaStreamCallback', callback, error=_lib.paAbort)
917-
# CFFI callback object is kept alive during stream lifetime:
918-
self._callback = callback
971+
# Use cast() to allow CData from different FFI instance:
972+
callback_ptr = _ffi.cast('PaStreamCallback*', callback)
973+
974+
# CFFI callback object must be kept alive during stream lifetime:
975+
self._callback = callback_ptr
919976
if userdata is None:
920977
userdata = _ffi.NULL
921978
self._ptr = _ffi.new('PaStream**')
922979
_check(_lib.Pa_OpenStream(self._ptr, iparameters, oparameters,
923980
samplerate, blocksize, stream_flags,
924-
callback, userdata),
981+
callback_ptr, userdata),
925982
'Error opening {0}'.format(self.__class__.__name__))
926983

927984
# dereference PaStream** --> PaStream*
@@ -1207,16 +1264,8 @@ def __init__(self, samplerate=None, blocksize=None,
12071264
RawStream, Stream
12081265
12091266
"""
1210-
1211-
def callback_wrapper(iptr, optr, frames, time, status, _):
1212-
data = _buffer(iptr, frames, self._channels, self._samplesize)
1213-
return _wrap_callback(callback, data, frames, time, status)
1214-
1215-
_StreamBase.__init__(
1216-
self, 'input', samplerate, blocksize, device, channels, dtype,
1217-
latency, extra_settings, callback and callback_wrapper,
1218-
finished_callback, clip_off, dither_off, never_drop_input,
1219-
prime_output_buffers_using_stream_callback)
1267+
_StreamBase.__init__(self, kind='input', wrap_callback='buffer',
1268+
**_remove_self(locals()))
12201269

12211270
@property
12221271
def read_available(self):
@@ -1299,16 +1348,8 @@ def __init__(self, samplerate=None, blocksize=None,
12991348
RawStream, Stream
13001349
13011350
"""
1302-
1303-
def callback_wrapper(iptr, optr, frames, time, status, _):
1304-
data = _buffer(optr, frames, self._channels, self._samplesize)
1305-
return _wrap_callback(callback, data, frames, time, status)
1306-
1307-
_StreamBase.__init__(
1308-
self, 'output', samplerate, blocksize, device, channels, dtype,
1309-
latency, extra_settings, callback and callback_wrapper,
1310-
finished_callback, clip_off, dither_off, never_drop_input,
1311-
prime_output_buffers_using_stream_callback)
1351+
_StreamBase.__init__(self, kind='output', wrap_callback='buffer',
1352+
**_remove_self(locals()))
13121353

13131354
@property
13141355
def write_available(self):
@@ -1415,19 +1456,8 @@ def __init__(self, samplerate=None, blocksize=None,
14151456
RawInputStream, RawOutputStream, Stream
14161457
14171458
"""
1418-
1419-
def callback_wrapper(iptr, optr, frames, time, status, _):
1420-
ichannels, ochannels = self._channels
1421-
isize, osize = self._samplesize
1422-
idata = _buffer(iptr, frames, ichannels, isize)
1423-
odata = _buffer(optr, frames, ochannels, osize)
1424-
return _wrap_callback(callback, idata, odata, frames, time, status)
1425-
1426-
_StreamBase.__init__(
1427-
self, 'duplex', samplerate, blocksize, device, channels, dtype,
1428-
latency, extra_settings, callback and callback_wrapper,
1429-
finished_callback, clip_off, dither_off, never_drop_input,
1430-
prime_output_buffers_using_stream_callback)
1459+
_StreamBase.__init__(self, kind=None, wrap_callback='buffer',
1460+
**_remove_self(locals()))
14311461

14321462

14331463
class InputStream(RawInputStream):
@@ -1463,17 +1493,8 @@ def __init__(self, samplerate=None, blocksize=None,
14631493
Stream, RawInputStream
14641494
14651495
"""
1466-
1467-
def callback_wrapper(iptr, optr, frames, time, status, _):
1468-
buffer = _buffer(iptr, frames, self._channels, self._samplesize)
1469-
data = _array(buffer, self._channels, self._dtype)
1470-
return _wrap_callback(callback, data, frames, time, status)
1471-
1472-
_StreamBase.__init__(
1473-
self, 'input', samplerate, blocksize, device, channels, dtype,
1474-
latency, extra_settings, callback and callback_wrapper,
1475-
finished_callback, clip_off, dither_off, never_drop_input,
1476-
prime_output_buffers_using_stream_callback)
1496+
_StreamBase.__init__(self, kind='input', wrap_callback='array',
1497+
**_remove_self(locals()))
14771498

14781499
def read(self, frames):
14791500
"""Read samples from the stream into a NumPy array.
@@ -1545,17 +1566,8 @@ def __init__(self, samplerate=None, blocksize=None,
15451566
Stream, RawOutputStream
15461567
15471568
"""
1548-
1549-
def callback_wrapper(iptr, optr, frames, time, status, _):
1550-
buffer = _buffer(optr, frames, self._channels, self._samplesize)
1551-
data = _array(buffer, self._channels, self._dtype)
1552-
return _wrap_callback(callback, data, frames, time, status)
1553-
1554-
_StreamBase.__init__(
1555-
self, 'output', samplerate, blocksize, device, channels, dtype,
1556-
latency, extra_settings, callback and callback_wrapper,
1557-
finished_callback, clip_off, dither_off, never_drop_input,
1558-
prime_output_buffers_using_stream_callback)
1569+
_StreamBase.__init__(self, kind='output', wrap_callback='array',
1570+
**_remove_self(locals()))
15591571

15601572
def write(self, data):
15611573
"""Write samples to the stream.
@@ -1842,22 +1854,8 @@ def __init__(self, samplerate=None, blocksize=None,
18421854
See `default.prime_output_buffers_using_stream_callback`.
18431855
18441856
"""
1845-
1846-
def callback_wrapper(iptr, optr, frames, time, status, _):
1847-
ichannels, ochannels = self._channels
1848-
idtype, odtype = self._dtype
1849-
isize, osize = self._samplesize
1850-
ibuffer = _buffer(iptr, frames, ichannels, isize)
1851-
obuffer = _buffer(optr, frames, ochannels, osize)
1852-
idata = _array(ibuffer, ichannels, idtype)
1853-
odata = _array(obuffer, ochannels, odtype)
1854-
return _wrap_callback(callback, idata, odata, frames, time, status)
1855-
1856-
_StreamBase.__init__(
1857-
self, 'duplex', samplerate, blocksize, device, channels, dtype,
1858-
latency, extra_settings, callback and callback_wrapper,
1859-
finished_callback, clip_off, dither_off, never_drop_input,
1860-
prime_output_buffers_using_stream_callback)
1857+
_StreamBase.__init__(self, kind=None, wrap_callback='array',
1858+
**_remove_self(locals()))
18611859

18621860

18631861
class DeviceList(tuple):
@@ -2496,6 +2494,13 @@ def wait(self):
24962494
return self.status if self.status else None
24972495

24982496

2497+
def _remove_self(d):
2498+
"""Return a copy of d without the 'self' entry."""
2499+
d = d.copy()
2500+
del d['self']
2501+
return d
2502+
2503+
24992504
def _check_mapping(mapping, channels):
25002505
"""Check mapping, obtain channels."""
25012506
import numpy as np

0 commit comments

Comments
 (0)