承上文,應用 JACK Client for Python 寫程式,主要重心就在set_process_callback 程序。以及詳實認識輸入、出聲音緩衝區︰
class OwnPort(Port): """A JACK audio port owned by a `Client`. This class is derived from `Port`. `OwnPort` objects can do everything that `Port` objects can, plus a lot more. This class cannot be instantiated directly (see `Port`). New JACK audio/MIDI ports can be created with the :meth:`~Ports.register` method of `Client.inports`, `Client.outports`, `Client.midi_inports` and `Client.midi_outports`. """ def get_buffer(self): """Get buffer for audio data. This returns a buffer holding the memory area associated with the specified port. For an output port, it will be a memory area that can be written to; for an input port, it will be an area containing the data from the port's connection(s), or zero-filled. If there are multiple inbound connections, the data will be mixed appropriately. Caching output ports is DEPRECATED in JACK 2.0, due to some new optimization (like "pipelining"). Port buffers have to be retrieved in each callback for proper functioning. This method shall only be called from within the process callback (see `Client.set_process_callback()`). """ blocksize = self._client.blocksize return _ffi.buffer(_lib.jack_port_get_buffer(self._ptr, blocksize), blocksize * _ffi.sizeof('float')) def get_array(self): """Get audio buffer as NumPy array. Make sure to ``import numpy`` before calling this, otherwise the first call might take a long time. This method shall only be called from within the process callback (see `Client.set_process_callback()`). See Also -------- get_buffer """ import numpy as np return np.frombuffer(self.get_buffer(), dtype=np.float32)
雖說深入了解 CFFI 並非必要︰
cffi 1.11.2
Foreign Function Interface for Python calling C code.
Foreign Function Interface for Python calling C code. Please see the Documentation.
……
Overview
Contents
CFFI can be used in one of four modes: “ABI” versus “API” level, each with “in-line” or “out-of-line” preparation (or compilation).
The ABI mode accesses libraries at the binary level, whereas the faster API mode accesses them with a C compiler. This is described in detail below.
In the in-line mode, everything is set up every time you import your Python code. In the out-of-line mode, you have a separate step of preparation (and possibly C compilation) that produces a module which your main program can then import.
(The examples below assume that you have installed CFFI.)
>>> from cffi import FFI >>> ffi = FFI() >>> ffi.cdef(""" ... int printf(const char *format, ...); // copy-pasted from the man page ... """) >>> C = ffi.dlopen(None) # loads the entire C namespace >>> arg = ffi.new("char[]", "world") # equivalent to C code: char arg[] = "world"; >>> C.printf("hi there, %s.\n", arg) # call printf hi there, world. 17 # this is the return value >>>
然而終將發現它的好處與重要性也。
此處且藉派生內建之 memoryview 功能
class memoryview
(obj)
Create a memoryview
that references obj. obj must support the buffer protocol. Built-in objects that support the buffer protocol include bytes
and bytearray
.
A memoryview
has the notion of an element, which is the atomic memory unit handled by the originating object obj. For many simple types such as bytes
and bytearray
, an element is a single byte, but other types such as array.array
may have bigger elements.
len(view)
is equal to the length of tolist
. If view.ndim = 0
, the length is 1. If view.ndim = 1
, the length is equal to the number of elements in the view. For higher dimensions, the length is equal to the length of the nested list representation of the view. The itemsize
attribute will give you the number of bytes in a single element.
A memoryview
supports slicing and indexing to expose its data. One-dimensional slicing will result in a subview:
>>> v = memoryview(b'abcefg') >>> v[1] 98 >>> v[-1] 103 >>> v[1:4] <memory at 0x7f3ddc9f4350> >>> bytes(v[1:4]) b'bce'
來趟簡短的探索之旅︰
pi@raspberrypi:~ python3 Python 3.5.3 (default, Jan 19 2017, 14:11:04) [GCC 6.3.0 20170124] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import jack >>> client = jack.Client("thru_client") >>> audioVol = 1.0 >>> @client.set_process_callback ... def process(frames): ... assert len(client.inports) == len(client.outports) ... assert frames == client.blocksize ... for i, o in zip(client.inports, client.outports): ... ibuf = memoryview(i.get_buffer()).cast('f') ... obuf = memoryview(o.get_buffer()).cast('f') ... for n in range(len(ibuf)): ... obuf[n] = ibuf[n] * audioVol ... >>> for number in 1, 2: ... client.inports.register('input_{0}'.format(number)) ... client.outports.register('output_{0}'.format(number)) ... jack.OwnPort('thru_client:input_1') jack.OwnPort('thru_client:output_1') jack.OwnPort('thru_client:input_2') jack.OwnPort('thru_client:output_2') >>> client.activate() >>> audioVol = 0.2 >>> audioVol = 1.2 >>>