2 # cython: language_level=3
4 """Python bindings for jack_mixer.c and scale.c using Cython."""
6 __all__
= ("Scale", "MidiBehaviour", "Mixer")
10 from _jack_mixer cimport
*
13 cdef void midi_change_callback_func
(void *userdata
) with gil
:
14 """Wrapper for a Python callback function for MIDI input."""
15 channel
= <object> userdata
16 channel
._midi_change_callback
()
19 class MidiBehaviour
(enum
.IntEnum
):
20 """MIDI control behaviour.
24 Received MIDI control messages affect mixer directly.
28 Received MIDI control messages have to match up with current
29 mixer value first (within a small margin), before further
37 """Mixer level scale representation.
39 Wraps `jack_mixer_scale_t` struct.
42 cdef jack_mixer_scale_t _scale
45 self._scale
= scale_create
()
47 def __dealloc__
(self):
49 scale_destroy
(self._scale
)
51 cpdef bool add_threshold
(self, float db
, float scale_value
):
52 """Add scale treshold."""
53 return scale_add_threshold
(self._scale
, db
, scale_value
)
55 cpdef
void remove_thresholds
(self):
56 """Remove scale threshold."""
57 scale_remove_thresholds
(self._scale
)
59 cpdef
void calculate_coefficients
(self):
60 """Calculate scale coefficents."""
61 scale_calculate_coefficients
(self._scale
)
63 cpdef double db_to_scale
(self, double db
):
64 """Return scale value responding to given dB value."""
65 return scale_db_to_scale
(self._scale
, db
)
67 cpdef double scale_to_db
(self, double scale_value
):
68 """Return dB value responding to given scale value."""
69 return scale_scale_to_db
(self._scale
, scale_value
)
73 """Jack Mixer representation.
75 Wraps `jack_mixer_t` struct.
78 cdef jack_mixer_t _mixer
81 def __cinit__
(self, name
, stereo
=True
):
83 self._mixer
= mixer_create
(name
.encode
('utf-8'), stereo
)
85 def __dealloc__
(self):
87 mixer_destroy
(self._mixer
)
90 """Close mixer Jack client and destroy mixer instance.
92 The instance must not be used anymore after calling this
96 mixer_destroy
(self._mixer
)
99 def channels_count
(self):
100 """Number of mixer channels."""
101 return mixer_get_channels_count
(self._mixer
)
104 def client_name
(self):
105 """Jack client name of mixer."""
106 return mixer_get_client_name
(self._mixer
).decode
('utf-8')
109 def last_midi_cc
(self):
110 """Last received MIDI control change message."""
111 return mixer_get_last_midi_cc
(self._mixer
)
114 def last_midi_cc
(self, int channel
):
115 mixer_set_last_midi_cc
(self._mixer
, channel
)
118 def midi_behavior_mode
(self):
119 """MIDI control change behaviour mode.
121 See `MidiBehaviour` enum for more information.
123 return MidiBehaviour
(mixer_get_midi_behavior_mode
(self._mixer
))
125 @midi_behavior_mode
.setter
126 def midi_behavior_mode
(self, mode
):
127 mixer_set_midi_behavior_mode
(self._mixer
,
128 mode
.value
if isinstance(mode
, MidiBehaviour
) else mode
)
130 cpdef add_channel
(self, channel_name
, stereo
=None
):
131 """Add a stereo or mono input channel with given name to the mixer.
133 Returns a `Channel` instance when successfull or `None` if channel
136 cdef jack_mixer_channel_t chan_ptr
138 stereo
= self._stereo
140 chan_ptr
= mixer_add_channel
(self._mixer
, channel_name
.encode
('utf-8'), stereo
)
144 return Channel
.new
(chan_ptr
)
146 cpdef add_output_channel
(self, channel_name
, stereo
=None
, system
=False
):
147 """Add a stereo or mono output channel with given name to the mixer.
149 Returns a `OutputChannel` instance when successfull or `None` if
150 channel creation failed.
152 cdef jack_mixer_output_channel_t chan_ptr
155 stereo
= self._stereo
157 chan_ptr
= mixer_add_output_channel
(self._mixer
, channel_name
.encode
('utf-8'), stereo
,
163 return OutputChannel
.new
(chan_ptr
)
167 """Jack Mixer (input) channel representation.
169 Wraps `jack_mixer_channel_t` struct.
172 cdef jack_mixer_channel_t _channel
173 cdef object _midi_change_callback
176 raise TypeError("Channel instances can only be created via Mixer.add_channel().")
179 cdef Channel new
(jack_mixer_channel_t chan_ptr
):
180 """Create a new Channel instance.
182 A pointer to an initialized `jack_mixer_channel_t` struct must be
185 This should not be called directly but only via `Mixer.add_channel()`.
187 cdef Channel channel
= Channel
.__new__
(Channel
)
188 channel
._channel
= chan_ptr
193 """Absolute peak of channel meter.
195 Set to `None` to reset the absolute peak to -inf.
196 Trying to set it to any other value will raise a `ValueError`.
198 return channel_abspeak_read
(self._channel
)
201 def abspeak
(self, reset
):
202 if reset
is not None
:
203 raise ValueError("abspeak can only be reset (set to None)")
204 channel_abspeak_reset
(self._channel
)
208 """Channel balance property."""
209 return channel_balance_read
(self._channel
)
212 def balance
(self, double bal
):
213 channel_balance_write
(self._channel
, bal
)
217 """Is channel stereo or mono?"""
218 return channel_is_stereo
(self._channel
)
222 """Read channel kmeter.
224 If channel is stereo, return a four-item tupel with
225 ``(peak_left, peak_right, rms_left, rms_right)`` value.
226 If channel is mono, return a tow-item tupel with ``(peak, rms)`` value.
228 cdef double peak_left
, peak_right
, left_rms
, right_rms
230 if channel_is_stereo
(self._channel
):
231 channel_stereo_kmeter_read
(
232 self._channel
, &peak_left
, &peak_right
, &left_rms
, &right_rms
)
233 return (peak_left
, peak_right
, left_rms
, right_rms
)
235 channel_mono_kmeter_read
(self._channel
, &peak_left
, &left_rms
)
236 return (peak_left
, left_rms
)
240 """Read channel meter.
242 If channel is stereo, return a two-item tupel with (left, right) value.
243 If channel is mono, return a tupel with the value as the only item.
245 cdef double left
, right
247 if channel_is_stereo
(self._channel
):
248 channel_stereo_meter_read
(self._channel
, &left
, &right
)
251 channel_mono_meter_read
(self._channel
, &left
)
255 def midi_change_callback
(self):
256 """Function to be called when a channel property is changed via MIDI.
258 The callback function takes no arguments.
260 Assign `None` to remove any existing callback.
262 return self._midi_change_callback
264 @midi_change_callback
.setter
265 def midi_change_callback
(self, callback
):
266 self._midi_change_callback
= callback
268 channel_set_midi_change_callback
(self._channel
, NULL
, NULL
)
270 channel_set_midi_change_callback
(self._channel
,
271 &midi_change_callback_func
,
276 """Channel name property."""
277 return channel_get_name
(self._channel
).decode
('utf-8')
280 def name
(self, newname
):
281 channel_rename
(self._channel
, newname
.encode
('utf-8'))
285 """Channel solo status property."""
286 return channel_is_out_muted
(self._channel
)
289 def out_mute
(self, bool value
):
291 channel_out_mute
(self._channel
)
293 channel_out_unmute
(self._channel
)
297 """Channel solo status property."""
298 return channel_is_soloed
(self._channel
)
301 def solo
(self, bool value
):
303 channel_solo
(self._channel
)
305 channel_unsolo
(self._channel
)
308 def midi_in_got_events
(self):
309 """Did channel receive any MIDI events assigned to one of its controls?
311 Reading this property also resets it to False.
313 return channel_get_midi_in_got_events
(self._channel
)
316 def midi_scale
(self):
317 """MIDI scale used by channel."""
318 raise AttributeError("midi_scale can only be set.")
321 def midi_scale
(self, Scale scale
):
322 channel_set_midi_scale
(self._channel
, scale
._scale
)
326 """Channel volume property."""
327 return channel_volume_read
(self._channel
)
330 def volume
(self, double vol
):
331 channel_volume_write
(self._channel
, vol
)
334 def balance_midi_cc
(self):
335 """MIDI CC assigned to control channel balance."""
336 return channel_get_balance_midi_cc
(self._channel
)
338 @balance_midi_cc
.setter
339 def balance_midi_cc
(self, int cc
):
340 channel_set_balance_midi_cc
(self._channel
, cc
)
343 def mute_midi_cc
(self):
344 """MIDI CC assigned to control channel mute status."""
345 return channel_get_mute_midi_cc
(self._channel
)
348 def mute_midi_cc
(self, int cc
):
349 channel_set_mute_midi_cc
(self._channel
, cc
)
352 def solo_midi_cc
(self):
353 """MIDI CC assigned to control channel solo status."""
354 return channel_get_solo_midi_cc
(self._channel
)
357 def solo_midi_cc
(self, int cc
):
358 channel_set_solo_midi_cc
(self._channel
, cc
)
361 def volume_midi_cc
(self):
362 """MIDI CC assigned to control channel volume."""
363 return channel_get_volume_midi_cc
(self._channel
)
365 @volume_midi_cc
.setter
366 def volume_midi_cc
(self, int cc
):
367 channel_set_volume_midi_cc
(self._channel
, cc
)
369 def autoset_balance_midi_cc
(self):
370 """Auto assign MIDI CC for channel balance."""
371 channel_autoset_balance_midi_cc
(self._channel
)
373 def autoset_mute_midi_cc
(self):
374 """Auto assign MIDI CC for channel mute status."""
375 channel_autoset_mute_midi_cc
(self._channel
)
377 def autoset_solo_midi_cc
(self):
378 """Auto assign MIDI CC for channel solo status."""
379 channel_autoset_solo_midi_cc
(self._channel
)
381 def autoset_volume_midi_cc
(self):
382 """Auto assign MIDI CC for channel volume."""
383 channel_autoset_volume_midi_cc
(self._channel
)
386 """Remove channel."""
387 remove_channel
(self._channel
)
389 def set_midi_cc_balance_picked_up
(self, bool status
):
390 """Set whether balance value is out-of-sync with MIDI control."""
391 channel_set_midi_cc_balance_picked_up
(self._channel
, status
)
393 def set_midi_cc_volume_picked_up
(self, bool status
):
394 """Set whether volume value is out-of-sync with MIDI control."""
395 channel_set_midi_cc_volume_picked_up
(self._channel
, status
)
399 cdef class OutputChannel
(Channel
):
400 """Jack Mixer output channel representation.
402 Wraps `jack_mixer_output_channel_t` struct.
404 Inherits from `Channel` class.
407 cdef jack_mixer_output_channel_t _output_channel
410 raise TypeError("OutputChannel instances can only be created via "
411 "Mixer.add_output_channel().")
414 cdef OutputChannel new
(jack_mixer_output_channel_t chan_ptr
):
415 """Create a new OutputChannel instance.
417 A pointer to an initialzed `jack_mixer_output_channel_t` struct must
420 This should not be called directly but only via
421 `Mixer.add_output_channel()`.
423 cdef OutputChannel channel
= OutputChannel
.__new__
(OutputChannel
)
424 channel
._output_channel
= chan_ptr
425 channel
._channel
= <jack_mixer_channel_t
> chan_ptr
430 return output_channel_is_prefader
(self._output_channel
)
433 def prefader
(self, bool pfl
):
434 output_channel_set_prefader
(self._output_channel
, pfl
)
436 def is_in_prefader
(self, Channel channel
):
437 """Is a channel set as prefader?"""
438 return output_channel_is_in_prefader
(self._output_channel
, channel
._channel
)
440 def set_in_prefader
(self, Channel channel
, bool value
):
441 """Set a channel as prefader."""
442 output_channel_set_in_prefader
(self._output_channel
, channel
._channel
, value
)
444 def is_muted
(self, Channel channel
):
445 """Is a channel set as muted?"""
446 return output_channel_is_muted
(self._output_channel
, channel
._channel
)
448 def set_muted
(self, Channel channel
, bool value
):
449 """Set a channel as muted."""
450 output_channel_set_muted
(self._output_channel
, channel
._channel
, value
)
452 def is_solo
(self, Channel channel
):
453 """Is a channel set as solo?"""
454 return output_channel_is_solo
(self._output_channel
, channel
._channel
)
456 def set_solo
(self, Channel channel
, bool value
):
457 """Set a channel as solo."""
458 output_channel_set_solo
(self._output_channel
, channel
._channel
, value
)
461 """Remove output channel."""
462 remove_output_channel
(self._output_channel
)