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 aTuple
specifying inlet types (puredata.SIGNAL
orpuredata.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 aTuple
specifying outlet types (puredata.SIGNAL
orpuredata.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.logpost
with 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
: WriteTuple
of numbers in thepd
array.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