Skip to content

Commit bbd1d6e

Browse files
committed
Merge branch 'org-master'
2 parents 9f302f5 + eb00732 commit bbd1d6e

7 files changed

Lines changed: 344 additions & 1 deletion

File tree

documentation/source/api/classes/pattern.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Objects in this category
1010
------------------------------
1111

1212
- :py:class:`CallAfter` : Calls a Python function after a given time.
13+
- :py:class:`CallAlways` : Calls a Python function every processing frame.
1314
- :py:class:`Pattern` : Periodically calls a Python function.
1415
- :py:class:`Score` : Calls functions by incrementation of a preformatted name.
1516

@@ -21,6 +22,14 @@ Objects in this category
2122

2223
.. autoclasstoc::
2324

25+
*CallAlways*
26+
-----------------------------------
27+
28+
.. autoclass:: CallAlways
29+
:members:
30+
31+
.. autoclasstoc::
32+
2433
*Pattern*
2534
-----------------------------------
2635

include/pyomodule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ typedef enum
9595
PYO_RUNTIME_TYPE_PATTERN,
9696
PYO_RUNTIME_TYPE_SCORE,
9797
PYO_RUNTIME_TYPE_CALLAFTER,
98+
PYO_RUNTIME_TYPE_CALLALWAYS,
9899
PYO_RUNTIME_TYPE_M_SIN,
99100
PYO_RUNTIME_TYPE_M_COS,
100101
PYO_RUNTIME_TYPE_M_TAN,
@@ -442,6 +443,7 @@ PyTypeObject * PyoCreateOscListReceiverType(PyObject *module);
442443
PyTypeObject * PyoCreatePatternType(PyObject *module);
443444
PyTypeObject * PyoCreateScoreType(PyObject *module);
444445
PyTypeObject * PyoCreateCallAfterType(PyObject *module);
446+
PyTypeObject * PyoCreateCallAlwaysType(PyObject *module);
445447
PyTypeObject * PyoCreateMSinType(PyObject *module);
446448
PyTypeObject * PyoCreateMCosType(PyObject *module);
447449
PyTypeObject * PyoCreateMTanType(PyObject *module);
@@ -773,6 +775,7 @@ typedef Py_ssize_t T_SIZE_T;
773775
#define TYPE_F_FFF "f|fff"
774776
#define TYPE_O_FIFFI "O|fiffi"
775777
#define TYPE_O_F "O|f"
778+
#define TYPE_O_O "O|O"
776779
#define TYPE_O_FO "O|fO"
777780
#define TYPE_O_OIF "O|Oif"
778781
#define TYPE__OF "|Of"
@@ -869,6 +872,7 @@ typedef Py_ssize_t T_SIZE_T;
869872
#define TYPE_F_FFF "d|ddd"
870873
#define TYPE_O_FIFFI "O|diddi"
871874
#define TYPE_O_F "O|d"
875+
#define TYPE_O_O "O|O"
872876
#define TYPE_O_FO "O|dO"
873877
#define TYPE_O_OIF "O|Oid"
874878
#define TYPE__OF "|Od"

pyo/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@
347347
"Binaural",
348348
]
349349
),
350-
"pattern": sorted(["Pattern", "Score", "CallAfter"]),
350+
"pattern": sorted(["Pattern", "Score", "CallAfter", "CallAlways"]),
351351
"randoms": sorted(
352352
[
353353
"Randi",
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
04-process-calls.py - Calling python functions every processing frame
3+
4+
This example shows two things:
5+
6+
1. How to share memory from a PyoTableObject to a numpy array with the
7+
`getBuffer()` method of the PyoTableObject. Numpy functions can then
8+
be used to modify the table's content without copying every samples.
9+
10+
2. How to call a python function every processing frame with the
11+
CallAlways object.
12+
13+
.. note::
14+
15+
The numpy module **must** be installed for this example to work.
16+
17+
"""
18+
19+
from pyo import *
20+
import numpy as np
21+
22+
s = Server().boot()
23+
24+
# Get the length of an audio block.
25+
bs = s.getBufferSize()
26+
27+
# Create a table of length `buffer size`.
28+
t = DataTable(size=bs)
29+
30+
# Share the table's memory with a numpy array.
31+
arr = np.asarray(t.getBuffer())
32+
33+
def proc1():
34+
"Fill the array (so the table) with white noise."
35+
arr[:] = np.random.normal(0.0, 0.5, size=bs)
36+
37+
# Fill the table.
38+
call1 = CallAlways(proc1)
39+
40+
# You can do other pyo processes here (read the table, process the signal, feed into another shared table, etc.).
41+
42+
# numpy filter's kernel.
43+
kernel_len = 32
44+
window = np.ones(kernel_len) / kernel_len
45+
46+
def proc2():
47+
"Lowpass filter the array content."
48+
arr[:] = np.convolve(arr, window, mode='same')
49+
50+
# Lowpass filter the table content.
51+
call2 = CallAlways(proc2)
52+
53+
# Read the table in loop.
54+
osc = TableRead(t, freq=t.getRate(), loop=True, mul=0.1).out()
55+
56+
s.gui(locals())

pyo/lib/pattern.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,3 +390,96 @@ def arg(self):
390390
@arg.setter
391391
def arg(self, x):
392392
self.setArg(x)
393+
394+
class CallAlways(PyoObject):
395+
"""
396+
Calls a Python function every processing frame.
397+
398+
:Parent: :py:class:`PyoObject`
399+
400+
:Args:
401+
402+
function: Python callable (function or method)
403+
Python callable executed every buffer size.
404+
arg: any Python object, optional
405+
Argument sent to the called function. Defaults to None.
406+
407+
.. note::
408+
409+
The out() method is bypassed. CallAlways doesn't return signal.
410+
411+
CallAlways has no `mul` and `add` attributes.
412+
413+
If `arg` is None, the function must be defined without argument:
414+
415+
>>> def tocall():
416+
>>> pass # function's body
417+
418+
If `arg` is not None, the function must be defined with one argument:
419+
420+
>>> def tocall(arg):
421+
>>> print(arg)
422+
423+
>>> s = Server().boot()
424+
>>> s.start()
425+
>>> t = DataTable(size=s.getBufferSize())
426+
>>> n = Noise()
427+
>>> f = TableFill(n, t)
428+
>>> def callback(arg):
429+
... # Apply a lowpass filter and a gain on the table content
430+
... t.lowpass(arg)
431+
... t.mul(0.25)
432+
>>> a = CallAlways(callback, 500)
433+
>>> o = TableScan(t).out()
434+
435+
"""
436+
437+
def __init__(self, function, arg=None):
438+
pyoArgsAssert(self, "c", function)
439+
PyoObject.__init__(self)
440+
self._function = getWeakMethodRef(function)
441+
self._arg = arg
442+
function, arg, lmax = convertArgsToLists(function, arg)
443+
self._base_objs = [
444+
CallAlways_base(WeakMethod(wrap(function, i)), wrap(arg, i))
445+
for i in range(lmax)
446+
]
447+
self._init_play()
448+
449+
def out(self, x=0, inc=1, dur=0, delay=0):
450+
return self.play(dur, delay)
451+
452+
def setMul(self, x):
453+
pass
454+
455+
def setAdd(self, x):
456+
pass
457+
458+
def setSub(self, x):
459+
pass
460+
461+
def setDiv(self, x):
462+
pass
463+
464+
def setArg(self, x):
465+
"""
466+
Replace the `arg` attribute.
467+
468+
:Args:
469+
470+
x: Anything
471+
new `arg` attribute.
472+
473+
"""
474+
self._arg = x
475+
x, lmax = convertArgsToLists(x)
476+
[obj.setArg(wrap(x, i)) for i, obj in enumerate(self._base_objs)]
477+
478+
@property
479+
def arg(self):
480+
"""Anything. Callable's argument."""
481+
return self._arg
482+
483+
@arg.setter
484+
def arg(self, x):
485+
self.setArg(x)

src/engine/pyomodule.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,8 @@ pyo_exec(PyObject *m)
25222522
return -1;
25232523
if (module_add_heap_object(m, "CallAfter_base", PyoCreateCallAfterType, PYO_RUNTIME_TYPE_CALLAFTER) < 0)
25242524
return -1;
2525+
if (module_add_heap_object(m, "CallAlways_base", PyoCreateCallAlwaysType, PYO_RUNTIME_TYPE_CALLALWAYS) < 0)
2526+
return -1;
25252527
if (module_add_heap_object(m, "BandSplitter_base", PyoCreateBandSplitterType, PYO_RUNTIME_TYPE_BANDSPLITTER) < 0)
25262528
return -1;
25272529
if (module_add_heap_object(m, "BandSplit_base", PyoCreateBandSplitType, PYO_RUNTIME_TYPE_BANDSPLIT) < 0)

0 commit comments

Comments
 (0)