Creating Objects
As shown in the Hello World section, to create a new py4pd object you must use the subclass puredata.NewObject, define the object’s name, and save it in a folder using the pattern <object_name>.pd_py. To enable object creation, you must always import the puredata module, which is only available when the script .pd_py is loaded via py4pd.
NewObject Class
All PureData Python objects are created as subclasses from the base class puredata.NewObject.
import puredata as pd
class pymetro(pd.NewObject):
name: str = "pymetro" # object name, must be exactly the same as the file name without extension (pymetro.pd_py)
def __init__(self, args):
# Object initializer
pass
Object Attributes
From the class initializer (__init__), you need to define some object attributes. Like self.inlets, self.outlets and options attributes (like clocks, receivers, etc).
-
self.inlets: Can be aninteger(number of inlets) or aTuplespecifying inlet types (puredata.SIGNALorpuredata.DATA), where each element in the tuple defines the type of the inlet at the corresponding index. -
self.outlets: Can be aninteger(number of outlets) or aTuplespecifying outlet types (puredata.SIGNALorpuredata.DATA), where each element in the tuple defines the type of the outlet at the corresponding index.
pd.NewObject Methods
self.logpost: Post things on Pd console,self,logpost(0, "This is a fatal error")self.logpost(1, "This is an error"),self.logpost(2, "This normal a log"),self.logpost(3, "This is a debug").self.error: Print error, same asself.logpostwith error level 1.self.out: Output data to the object.self.out(0, pd.FLOAT, 1),self.out(0, pd.SYMBOL, "hello").self.out(0, pd.PYOBJECT, [[1,2,3][4,5,6]]).self.tabwrite: WriteTupleof numbers in thepdarray.self.tabread: Read table frompd, returns a tuple.self.reload: Reload the object.
Clocks
class pymetro(pd.NewObject):
name: str = "pymetro"
def __init__(self, args):
self.inlets = 2
self.outlets = 1
self.metro = pd.new_clock(self, self.tick)
pd.new_clock
Clock can be created using the pd.new_clock method, which returns a puredata.Clock object. new_clock accepts the self of the class and a function as an argument, which will be executed when the clock ticks. In the above example, self.metro will have the methods:
self.metro.delay: Set a delay in milliseconds to execute function (in this case,self.tick).
Receivers
class pyreceiver(pd.NewObject): name: str = "pyreceiver"
def __init__(self, args):
self.inlets = 2
self.outlets = 1
self.receiver = pd.new_receiver(self, "pyreceiver", self.received)
self.receiver.unbind()= This make the object not receive messages from the symbolpyreceiver.self.receiver.bind()= This make the object receive messages from the symbolpyreceiver.
Extra puredata Methods
pd.post: Post a message to Pd without being possible to detect the object (for example, for credits in objects), or warnings when some package is not installed.pd.hasgui: Returns if Pd has a GUI interface.pd.get_sample_rate: Return sample rate of Pd.
Complet Examples
Here some examples of objects:
Tradicional Objects
import puredata as pd
class pymetro(pd.NewObject):
name: str = "pymetro"
def __init__(self, args):
self.inlets = 2
self.outlets = 1
self.toggle = False
if len(args) > 0:
self.time = float(args[0])
else:
self.time = 1000
self.metro = pd.new_clock(self, self.tick)
self.args = args
def in_2_float(self, f: float):
self.time = f
def in_1_float(self, f: float):
if f:
self.toggle = True
self.tick()
else:
self.metro.unset()
self.toggle = False
def in_1_reload(self, args: list):
self.reload()
def tick(self):
if self.toggle:
self.metro.delay(self.time)
self.out(0, pd.SYMBOL, "test238")
Python Data Types on Pd
One of the great things that made me start py4pd was that I missed some data types. With py4pd, you can use any datatype supported by Python. This includes:
- Numeric types:
int,float,complex - Sequence types:
list,tuple,range - Text type:
str - Set types:
set,frozenset - Mapping type:
dict - Boolean type:
bool - Binary types:
bytes,bytearray,memoryview
This flexibility allows you to integrate Python data structures directly into your Pd patches, making data manipulation and processing much easier and more powerful.
To use it, you must convert the Pd data to Python using py.2py or use the pd.PYOBJECT type when using self.out method.
Here’s a completed section including your example with a concise explanation:
Audio Objects
With py4pd, you can create custom audio objects (tilde objects, like osc~) entirely in Python. This allows you to define signal processing logic directly in Python, while Pd handles the audio routing.
Example:
import puredata as pd
import math
class pytest_tilde(pd.NewObject):
name: str = "pytest~"
def __init__(self, args):
self.inlets = pd.SIGNAL
self.outlets = pd.SIGNAL
self.phase = 0
def perform(self, input):
# this is executed in each block of audio
blocksize = self.blocksize
samplerate = self.samplerate
out_buffer = []
for i in range(blocksize):
phase_increment = 2 * math.pi * input[0][i] / samplerate
sample = math.sin(self.phase)
out_buffer.append(sample)
self.phase += phase_increment
if self.phase > 2 * math.pi:
self.phase -= 2 * math.pi
return tuple(out_buffer)
def dsp(self, sr, blocksize, inchans):
# this is executed when you turn on the audio
self.samplerate = sr
self.blocksize = blocksize
self.inchans = inchans
return True