|
5 | 5 | from _rtmixer import ffi as _ffi, lib as _lib |
6 | 6 |
|
7 | 7 |
|
8 | | -class Mixer(_sd._StreamBase): |
9 | | - """PortAudio stream for realtime mixing.""" |
| 8 | +class _Base(_sd._StreamBase): |
| 9 | + """Base class for Mixer et al.""" |
10 | 10 |
|
11 | | - def __init__(self, channels, **kwargs): |
12 | | - """Create a realtime mixer object. |
13 | | -
|
14 | | - Takes the same keyword arguments as `sounddevice.Stream`, except |
15 | | - *callback* and *dtype*. |
16 | | -
|
17 | | - In contrast to `sounddevice.Stream`, the *channels* argument is |
18 | | - not optional. |
19 | | -
|
20 | | - Uses default values from `sounddevice.default`. |
21 | | -
|
22 | | - """ |
| 11 | + def __init__(self, kind, **kwargs): |
23 | 12 | callback = _ffi.addressof(_lib, 'callback') |
24 | 13 |
|
25 | 14 | # TODO: parameter for ring buffer size |
26 | 15 | self._action_q = RingBuffer(_ffi.sizeof('struct action*'), 512) |
27 | 16 | self._userdata = _ffi.new('struct state*', dict( |
28 | | - input_channels=0, |
29 | | - output_channels=channels, |
30 | 17 | action_q=self._action_q._ptr, |
31 | 18 | )) |
32 | 19 | _sd._StreamBase.__init__( |
33 | | - self, kind='output', wrap_callback=None, channels=channels, |
34 | | - dtype='float32', callback=callback, userdata=self._userdata, |
35 | | - **kwargs) |
36 | | - # Add a few attributes that are only known after opening the stream: |
| 20 | + self, kind=kind, dtype='float32', |
| 21 | + callback=callback, userdata=self._userdata, **kwargs) |
37 | 22 | self._userdata.samplerate = self.samplerate |
38 | 23 |
|
39 | 24 | self._actions = [] |
40 | 25 |
|
| 26 | + def _check_channels(self, channels, kind): |
| 27 | + """Check if number of channels or mapping was given.""" |
| 28 | + assert kind in ('input', 'output') |
| 29 | + try: |
| 30 | + channels, mapping = len(channels), channels |
| 31 | + except TypeError: |
| 32 | + mapping = tuple(range(1, channels + 1)) |
| 33 | + max_channels = _sd._split(self.channels)[kind == 'output'] |
| 34 | + if max(mapping) > max_channels: |
| 35 | + raise ValueError('Channel number too large') |
| 36 | + if min(mapping) < 1: |
| 37 | + raise ValueError('Channel numbers start with 1') |
| 38 | + return channels, mapping |
| 39 | + |
| 40 | + |
| 41 | +class Mixer(_Base): |
| 42 | + """PortAudio output stream for realtime mixing.""" |
| 43 | + |
| 44 | + def __init__(self, **kwargs): |
| 45 | + """Create a realtime mixer object. |
| 46 | +
|
| 47 | + Takes the same keyword arguments as `sounddevice.OutputStream`, |
| 48 | + except *callback* and *dtype*. |
| 49 | +
|
| 50 | + Uses default values from `sounddevice.default`. |
| 51 | +
|
| 52 | + """ |
| 53 | + _Base.__init__(self, kind='output', **kwargs) |
| 54 | + self._userdata.input_channels = 0 |
| 55 | + self._userdata.output_channels = self.channels |
| 56 | + |
41 | 57 | def play_buffer(self, buffer, channels, start=0): |
42 | 58 | """Send a buffer to the callback to be played back. |
43 | 59 |
|
@@ -93,23 +109,9 @@ def play_ringbuffer(self, ringbuffer, channels=None, start=0): |
93 | 109 | # TODO: block until playback has finished (optional)? |
94 | 110 | # TODO: return something that allows stopping playback? |
95 | 111 |
|
96 | | - def _check_channels(self, channels, kind): |
97 | | - """Check if number of channels or mapping was given.""" |
98 | | - assert kind in ('input', 'output') |
99 | | - try: |
100 | | - channels, mapping = len(channels), channels |
101 | | - except TypeError: |
102 | | - mapping = tuple(range(1, channels + 1)) |
103 | | - max_channels = _sd._split(self.channels)[kind == 'output'] |
104 | | - if max(mapping) > max_channels: |
105 | | - raise ValueError('Channel number too large') |
106 | | - if min(mapping) < 1: |
107 | | - raise ValueError('Channel numbers start with 1') |
108 | | - return channels, mapping |
109 | | - |
110 | 112 |
|
111 | | -class Recorder(_sd._StreamBase): |
112 | | - """PortAudio stream for realtime recording.""" |
| 113 | +class Recorder(_Base): |
| 114 | + """PortAudio input stream for realtime recording.""" |
113 | 115 |
|
114 | 116 |
|
115 | 117 | class MixerAndRecorder(Mixer, Recorder): |
|
0 commit comments