Skip to content

Commit 61b8c2a

Browse files
committed
Remove CFFI instance, add cdef() and init()
1 parent bcb85a6 commit 61b8c2a

File tree

7 files changed

+155
-99
lines changed

7 files changed

+155
-99
lines changed

.gitmodules

Lines changed: 0 additions & 3 deletions
This file was deleted.

MANIFEST.in

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,2 @@
11
include LICENSE
22
include *.rst
3-
recursive-include doc *.rst *.py
4-
include portaudio/LICENSE.txt
5-
include portaudio/index.html
6-
include portaudio/src/common/pa_ringbuffer.h
7-
include portaudio/src/common/pa_memorybarrier.h

README.rst

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ of PortAudio_, therefore most Python wrappers don't include it, either.
77
This module can be compiled and used on any Python version where CFFI_ is
88
available.
99

10-
The class `pa_ringbuffer.RingBuffer` is designed to be used together with the
11-
sounddevice_ module for non-blocking transfer of data from the main Python
12-
program to an audio callback function which is implemented in C or some other
13-
compiled language.
10+
This module is designed to be used together with the sounddevice_ module
11+
(it might work with other modules, too) for non-blocking transfer of data from
12+
the main Python program to an audio callback function which is implemented in C
13+
or some other compiled language.
1414

1515
.. _PortAudio: http://portaudio.com/
1616
.. _sounddevice: http://python-sounddevice.readthedocs.io/
@@ -19,10 +19,81 @@ compiled language.
1919
Installation
2020
------------
2121

22-
::
22+
Use this if you want to install it locally in a development environment::
2323

2424
python3 setup.py develop --user
2525

2626
or ::
2727

2828
python3 -m pip install -e . --user
29+
30+
Usage
31+
-----
32+
33+
This module is not meant to be used on its own, it is only useful in cooperation
34+
with another Python module using CFFI.
35+
36+
You can get the Python code from PyPI (Note: not yet available!),
37+
for example in your ``setup.py`` file:
38+
39+
.. code:: python
40+
41+
from setuptools import setup
42+
43+
setup(
44+
name=...,
45+
version=...,
46+
author=...,
47+
...,
48+
setup_requires=['CFFI', 'pa_ringbuffer'],
49+
install_requires=['pa_ringbuffer'],
50+
...,
51+
)
52+
53+
Alternatively, you can just copy the file ``src/pa_ringbuffer.py`` to your own
54+
source directory and import it from there.
55+
56+
You can build your own CFFI module like described in
57+
http://cffi.readthedocs.io/en/latest/cdef.html, just adding a few more bits:
58+
59+
.. code:: python
60+
61+
from cffi import FFI
62+
import pa_ringbuffer
63+
64+
ffibuilder = FFI()
65+
ffibuilder.cdef(pa_ringbuffer.cdef())
66+
ffibuilder.cdef("""
67+
68+
/* my own declarations */
69+
70+
""")
71+
ffibuilder.set_source(
72+
'_mycffimodule',
73+
'/* my implementation */',
74+
sources=['portaudio/src/common/pa_ringbuffer.c'],
75+
)
76+
77+
if __name__ == '__main__':
78+
ffibuilder.compile(verbose=True)
79+
80+
Note that the following files must be available to the compiler:
81+
82+
* https://app.assembla.com/spaces/portaudio/git/source/master/src/common/pa_ringbuffer.c
83+
* https://app.assembla.com/spaces/portaudio/git/source/master/src/common/pa_ringbuffer.h
84+
* https://app.assembla.com/spaces/portaudio/git/source/master/src/common/pa_memorybarrier.h
85+
86+
For your own C code, you might need some definitions from the main PortAudio
87+
header:
88+
89+
* https://app.assembla.com/spaces/portaudio/git/source/master/include/portaudio.h
90+
91+
Once you have compiled your extension module (with the help of CFFI), you can
92+
use something like this to get access to the ``RingBuffer`` class:
93+
94+
.. code:: python
95+
96+
import pa_ringbuffer
97+
from _mycffimodule import ffi, lib
98+
99+
RingBuffer = pa_ringbuffer.init(ffi, lib)

portaudio

Lines changed: 0 additions & 1 deletion
This file was deleted.

setup.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,14 @@
1212
name='pa-ringbuffer',
1313
version=__version__,
1414
package_dir={'': 'src'},
15-
py_modules=['pa_ringbuffer', '_pa_ringbuffer_build'],
16-
setup_requires=['CFFI>=1.4.0'],
17-
cffi_modules=['src/_pa_ringbuffer_build.py:ffibuilder'],
18-
install_requires=['CFFI>=1'], # for _cffi_backend
15+
py_modules=['pa_ringbuffer'],
1916
author='Matthias Geier',
2017
author_email='[email protected]',
2118
description="Python wrapper for PortAudio's ring buffer",
2219
long_description=open('README.rst').read(),
2320
license='MIT',
2421
keywords='sound audio PortAudio ringbuffer lock-free'.split(),
25-
#url='http://python-pa-ringbuffer.readthedocs.io/',
22+
#url='',
2623
platforms='any',
2724
classifiers=[
2825
'License :: OSI Approved :: MIT License',

src/_pa_ringbuffer_build.py

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/pa_ringbuffer.py

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,54 @@
22

33
__version__ = '0.0.0'
44

5-
import _cffi_backend
6-
from _pa_ringbuffer import ffi as _ffi, lib as _lib
75

8-
9-
class RingBuffer(object):
6+
def cdef():
7+
"""Return C declarations needed by CFFI."""
8+
import platform
9+
10+
if platform.system() == 'Darwin':
11+
ring_buffer_size_t = 'int32_t'
12+
else:
13+
ring_buffer_size_t = 'long'
14+
15+
return """
16+
/* Declarations from PortAudio's pa_ringbuffer.h: */
17+
typedef %(ring_buffer_size_t)s ring_buffer_size_t;
18+
typedef struct PaUtilRingBuffer
19+
{
20+
ring_buffer_size_t bufferSize;
21+
volatile ring_buffer_size_t writeIndex;
22+
volatile ring_buffer_size_t readIndex;
23+
ring_buffer_size_t bigMask;
24+
ring_buffer_size_t smallMask;
25+
ring_buffer_size_t elementSizeBytes;
26+
char* buffer;
27+
} PaUtilRingBuffer;
28+
ring_buffer_size_t PaUtil_InitializeRingBuffer(PaUtilRingBuffer* rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void* dataPtr);
29+
void PaUtil_FlushRingBuffer(PaUtilRingBuffer* rbuf);
30+
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable(const PaUtilRingBuffer* rbuf);
31+
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable(const PaUtilRingBuffer* rbuf);
32+
ring_buffer_size_t PaUtil_WriteRingBuffer(PaUtilRingBuffer* rbuf, const void* data, ring_buffer_size_t elementCount);
33+
ring_buffer_size_t PaUtil_ReadRingBuffer(PaUtilRingBuffer* rbuf, void* data, ring_buffer_size_t elementCount);
34+
ring_buffer_size_t PaUtil_GetRingBufferWriteRegions(PaUtilRingBuffer* rbuf, ring_buffer_size_t elementCount, void** dataPtr1, ring_buffer_size_t* sizePtr1, void** dataPtr2, ring_buffer_size_t* sizePtr2);
35+
ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex(PaUtilRingBuffer* rbuf, ring_buffer_size_t elementCount);
36+
ring_buffer_size_t PaUtil_GetRingBufferReadRegions(PaUtilRingBuffer* rbuf, ring_buffer_size_t elementCount, void** dataPtr1, ring_buffer_size_t* sizePtr1, void** dataPtr2, ring_buffer_size_t* sizePtr2);
37+
ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex(PaUtilRingBuffer* rbuf, ring_buffer_size_t elementCount);
38+
/* End of pa_ringbuffer.h declarations. */
39+
""" % locals()
40+
41+
42+
def init(ffi, lib):
43+
"""Return RingBuffer class using the given CFFI instance."""
44+
45+
class RingBuffer(_RingBufferBase):
46+
_ffi = ffi
47+
_lib = lib
48+
49+
return RingBuffer
50+
51+
52+
class _RingBufferBase(object):
1053
"""Wrapper for PortAudio's ring buffer.
1154
1255
See __init__().
@@ -24,9 +67,9 @@ def __init__(self, elementsize, size):
2467
The number of elements in the buffer (must be a power of 2).
2568
2669
"""
27-
self._ptr = _ffi.new('PaUtilRingBuffer*')
28-
self._data = _ffi.new('unsigned char[]', size * elementsize)
29-
res = _lib.PaUtil_InitializeRingBuffer(
70+
self._ptr = self._ffi.new('PaUtilRingBuffer*')
71+
self._data = self._ffi.new('unsigned char[]', size * elementsize)
72+
res = self._lib.PaUtil_InitializeRingBuffer(
3073
self._ptr, elementsize, size, self._data)
3174
if res != 0:
3275
assert res == -1
@@ -40,17 +83,17 @@ def flush(self):
4083
Should only be called when buffer is NOT being read or written.
4184
4285
"""
43-
_lib.PaUtil_FlushRingBuffer(self._ptr)
86+
self._lib.PaUtil_FlushRingBuffer(self._ptr)
4487

4588
@property
4689
def write_available(self):
4790
"""Number of elements available in the ring buffer for writing."""
48-
return _lib.PaUtil_GetRingBufferWriteAvailable(self._ptr)
91+
return self._lib.PaUtil_GetRingBufferWriteAvailable(self._ptr)
4992

5093
@property
5194
def read_available(self):
5295
"""Number of elements available in the ring buffer for reading."""
53-
return _lib.PaUtil_GetRingBufferReadAvailable(self._ptr)
96+
return self._lib.PaUtil_GetRingBufferReadAvailable(self._ptr)
5497

5598
def write(self, data, size=-1):
5699
"""Write data to the ring buffer.
@@ -69,14 +112,15 @@ def write(self, data, size=-1):
69112
70113
"""
71114
try:
72-
data = _ffi.from_buffer(data)
115+
data = self._ffi.from_buffer(data)
73116
except TypeError:
74117
pass # input is not a buffer
75118
if size < 0:
76-
size, rest = divmod(_ffi.sizeof(data), self._ptr.elementSizeBytes)
119+
size, rest = divmod(self._ffi.sizeof(data),
120+
self._ptr.elementSizeBytes)
77121
if rest:
78122
raise ValueError('data size must be multiple of elementsize')
79-
return _lib.PaUtil_WriteRingBuffer(self._ptr, data, size)
123+
return self._lib.PaUtil_WriteRingBuffer(self._ptr, data, size)
80124

81125
def read(self, data, size=-1):
82126
"""Read data from the ring buffer.
@@ -95,14 +139,15 @@ def read(self, data, size=-1):
95139
96140
"""
97141
try:
98-
data = _ffi.from_buffer(data)
142+
data = self._ffi.from_buffer(data)
99143
except TypeError:
100144
pass # input is not a buffer
101145
if size < 0:
102-
size, rest = divmod(_ffi.sizeof(data), self._ptr.elementSizeBytes)
146+
size, rest = divmod(self._ffi.sizeof(data),
147+
self._ptr.elementSizeBytes)
103148
if rest:
104149
raise ValueError('data size must be multiple of elementsize')
105-
return _lib.PaUtil_ReadRingBuffer(self._ptr, data, size)
150+
return self._lib.PaUtil_ReadRingBuffer(self._ptr, data, size)
106151

107152
def get_write_buffers(self, size):
108153
"""Get buffer(s) to which we can write data.
@@ -123,14 +168,14 @@ def get_write_buffers(self, size):
123168
The second buffer.
124169
125170
"""
126-
ptr1 = _ffi.new('void**')
127-
ptr2 = _ffi.new('void**')
128-
size1 = _ffi.new('ring_buffer_size_t*')
129-
size2 = _ffi.new('ring_buffer_size_t*')
130-
return (_lib.PaUtil_GetRingBufferWriteRegions(
171+
ptr1 = self._ffi.new('void**')
172+
ptr2 = self._ffi.new('void**')
173+
size1 = self._ffi.new('ring_buffer_size_t*')
174+
size2 = self._ffi.new('ring_buffer_size_t*')
175+
return (self._lib.PaUtil_GetRingBufferWriteRegions(
131176
self._ptr, size, ptr1, size1, ptr2, size2),
132-
_ffi.buffer(ptr1[0], size1[0] * self.elementsize),
133-
_ffi.buffer(ptr2[0], size2[0] * self.elementsize))
177+
self._ffi.buffer(ptr1[0], size1[0] * self.elementsize),
178+
self._ffi.buffer(ptr2[0], size2[0] * self.elementsize))
134179

135180
def advance_write_index(self, size):
136181
"""Advance the write index to the next location to be written.
@@ -146,7 +191,7 @@ def advance_write_index(self, size):
146191
The new position.
147192
148193
"""
149-
return _lib.PaUtil_AdvanceRingBufferWriteIndex(self._ptr, size)
194+
return self._lib.PaUtil_AdvanceRingBufferWriteIndex(self._ptr, size)
150195

151196
def get_read_buffers(self, size):
152197
"""Get buffer(s) from which we can read data.
@@ -166,14 +211,14 @@ def get_read_buffers(self, size):
166211
The second buffer.
167212
168213
"""
169-
ptr1 = _ffi.new('void**')
170-
ptr2 = _ffi.new('void**')
171-
size1 = _ffi.new('ring_buffer_size_t*')
172-
size2 = _ffi.new('ring_buffer_size_t*')
173-
return (_lib.PaUtil_GetRingBufferReadRegions(
214+
ptr1 = self._ffi.new('void**')
215+
ptr2 = self._ffi.new('void**')
216+
size1 = self._ffi.new('ring_buffer_size_t*')
217+
size2 = self._ffi.new('ring_buffer_size_t*')
218+
return (self._lib.PaUtil_GetRingBufferReadRegions(
174219
self._ptr, size, ptr1, size1, ptr2, size2),
175-
_ffi.buffer(ptr1[0], size1[0] * self.elementsize),
176-
_ffi.buffer(ptr2[0], size2[0] * self.elementsize))
220+
self._ffi.buffer(ptr1[0], size1[0] * self.elementsize),
221+
self._ffi.buffer(ptr2[0], size2[0] * self.elementsize))
177222

178223
def advance_read_index(self, size):
179224
"""Advance the read index to the next location to be read.
@@ -189,7 +234,7 @@ def advance_read_index(self, size):
189234
The new position.
190235
191236
"""
192-
return _lib.PaUtil_AdvanceRingBufferReadIndex(self._ptr, size)
237+
return self._lib.PaUtil_AdvanceRingBufferReadIndex(self._ptr, size)
193238

194239
@property
195240
def elementsize(self):

0 commit comments

Comments
 (0)