diff --git a/embedded/juceplugin/PyoClass.cpp b/embedded/juceplugin/PyoClass.cpp
index a27ee884b..b432f51e5 100644
--- a/embedded/juceplugin/PyoClass.cpp
+++ b/embedded/juceplugin/PyoClass.cpp
@@ -18,140 +18,154 @@
* If not, see . *
* *
*************************************************************************/
-#include "PyoClass.h"
-
-Pyo::Pyo() {
- interpreter = nullptr;
-}
+#include "PyoClass.h"
+#include
+
+Pyo::Pyo() {
+ interpreter = nullptr;
+ debug = 0;
+}
Pyo::~Pyo() {
if (interpreter != NULL) {
pyo_end_interpreter(interpreter);
}
}
-
-void Pyo::setup(int _nChannels, int _bufferSize, int _sampleRate) {
- nChannels = _nChannels;
- bufferSize = _bufferSize;
- sampleRate = _sampleRate;
- interpreter = pyo_new_interpreter(sampleRate, bufferSize, nChannels);
- pyoInBuffer = reinterpret_cast(pyo_get_input_buffer_address(interpreter));
- pyoOutBuffer = reinterpret_cast(pyo_get_output_buffer_address(interpreter));
- pyoCallback = reinterpret_cast(pyo_get_embedded_callback_address(interpreter));
- pyoId = pyo_get_server_id(interpreter);
- pyo_set_server_params(interpreter, sampleRate, bufferSize);
-}
+
+void Pyo::setup(int _inChannels, int _outChannels, int _bufferSize, int _sampleRate) {
+ inChannels = _inChannels;
+ outChannels = _outChannels;
+ bufferSize = _bufferSize;
+ sampleRate = _sampleRate;
+
+ interpreter = pyo_new_interpreter(sampleRate, bufferSize, inChannels, outChannels);
+ pyoInBuffer = reinterpret_cast(pyo_get_input_buffer_address(interpreter));
+ pyoOutBuffer = reinterpret_cast(pyo_get_output_buffer_address(interpreter));
+ pyoCallback = reinterpret_cast(pyo_get_embedded_callback_address(interpreter));
+ pyoId = pyo_get_server_id(interpreter);
+ pyo_set_server_params(interpreter, sampleRate, bufferSize);
+}
+
+std::vector Pyo::getStdout() {
+ std::vector out;
+ char *msg;
+ while (pyo_dequeue_stdout(&msg)) {
+ out.emplace_back(msg); // copy into vector
+ free(msg); // free allocated C buffer
+ }
+ return out;
+}
void Pyo::setbpm(double bpm) {
pyo_set_bpm(interpreter, bpm);
}
-
-void Pyo::process(AudioBuffer& buffer) {
- for (int channel = 0; channel < nChannels; ++channel) {
- float *channelData = buffer.getWritePointer(channel);
- for (int sample = 0; sample < bufferSize; sample++) {
- pyoInBuffer[sample*nChannels+channel] = channelData[sample];
- }
- }
- pyoCallback(pyoId);
- for (int channel = 0; channel < nChannels; ++channel) {
- float *channelData = buffer.getWritePointer(channel);
- for (int sample = 0; sample < bufferSize; sample++) {
- channelData[sample] = pyoOutBuffer[sample*nChannels+channel];
- }
- }
-}
-
-int Pyo::loadfile(const char *file, int add) {
- return pyo_exec_file(interpreter, file, pyoMsg, add);
-}
-
-int Pyo::loadfile(const String &file, int add) {
- return pyo_exec_file(interpreter, file.getCharPointer(), pyoMsg, add);
-}
+
+void Pyo::process(juce::AudioBuffer& buffer) {
+ for (int channel = 0; channel < inChannels; ++channel) {
+ float *channelData = buffer.getWritePointer(channel);
+ for (int sample = 0; sample < bufferSize; sample++) {
+ pyoInBuffer[sample*inChannels+channel] = channelData[sample];
+ }
+ }
+ pyoCallback(pyoId);
+ for (int channel = 0; channel < outChannels; ++channel) {
+ float *channelData = buffer.getWritePointer(channel);
+ for (int sample = 0; sample < bufferSize; sample++) {
+ channelData[sample] = pyoOutBuffer[sample*outChannels+channel];
+ }
+ }
+}
+
+int Pyo::loadfile(const char *file, int add) {
+ return pyo_exec_file(interpreter, file, pyoMsg, add, debug);
+}
+
+int Pyo::loadfile(const juce::String &file, int add) {
+ return pyo_exec_file(interpreter, file.getCharPointer(), pyoMsg, add, debug);
+}
int Pyo::exec(const char *_msg) {
strcpy(pyoMsg, _msg);
- return pyo_exec_statement(interpreter, pyoMsg, 0);
+ return pyo_exec_statement(interpreter, pyoMsg, debug);
}
-int Pyo::exec(const String &_msg) {
+int Pyo::exec(const juce::String &_msg) {
strcpy(pyoMsg, _msg.getCharPointer());
- return pyo_exec_statement(interpreter, pyoMsg, 0);
-}
-
-int Pyo::value(const char *name, float value) {
- sprintf(pyoMsg, "%s.value=%f", name, value);
- return pyo_exec_statement(interpreter, pyoMsg, 0);
-}
-
-int Pyo::value(const String &name, float value) {
- const char * _name = name.getCharPointer();
- sprintf(pyoMsg, "%s.value=%f", _name, value);
- return pyo_exec_statement(interpreter, pyoMsg, 0);
-}
-
-int Pyo::value(const char *name, float *value, int len) {
- char fchar[32];
- sprintf(pyoMsg, "%s.value=[", name);
- for (int i=0; i. *
* *
*************************************************************************/
-#ifndef PYOCLASS_H_INCLUDED
-#define PYOCLASS_H_INCLUDED
-
-#include "m_pyo.h"
-#include "../JuceLibraryCode/JuceHeader.h"
-
-typedef int callPtr(int);
-
-class Pyo {
- public:
+#ifndef PYOCLASS_H_INCLUDED
+#define PYOCLASS_H_INCLUDED
+
+#include "m_pyo.h"
+#include "../JuceLibraryCode/JuceHeader.h"
+#include
+#include
+
+typedef int callPtr(int);
+
+class Pyo {
+ public:
Pyo();
/*
** Terminates this object's interpreter.
- */
+ */
~Pyo();
/*
@@ -46,9 +48,9 @@ class Pyo {
** sampleRate : int, sample rate frequency.
**
** All arguments should be equal to the host audio settings.
- */
- void setup(int nChannels, int bufferSize, int sampleRate);
-
+ */
+ void setup(int inChannels, int outChannels, int bufferSize, int sampleRate);
+ std::vector getStdout();
/*
** This function can be used to pass the DAW's bpm value to the
** python interpreter. Changes the value of the BPM variable
@@ -67,8 +69,8 @@ class Pyo {
**
** arguments:
** buffer : AudioBuffer&, Juce's audio buffer object.
- */
- void process(AudioSampleBuffer& buffer);
+ */
+ void process(juce::AudioBuffer& buffer);
/*
** Execute a python script "file" in the objectès thread's interpreter.
@@ -76,7 +78,7 @@ class Pyo {
** reboot or not.
**
** arguments:
- ** file : const char * or const String &,
+ ** file : const char * or const juce::String &,
** filename to execute as a python script. The file is first
** searched in the current working directory. If not found,
** the module will try to open it as an absolute path.
@@ -86,16 +88,16 @@ class Pyo {
**
** returns 0 (no error), 1 (failed to open the file) or 2 (bad code in file).
*/
- int loadfile(const char *file, int add);
- int loadfile(const String &file, int add);
+ int loadfile(const char *file, int add);
+ int loadfile(const juce::String &file, int add);
/*
** Executes any raw valid python statement. With this function, one can
** dynamically creates and manipulates audio objects and algorithms.
**
** arguments:
- ** msg : const char * or const String &
- ** pointer to a string containing the statement to execute.
+ ** msg : const char * or const juce::String &
+ ** pointer to a juce::String containing the statement to execute.
**
** returns 0 (no error) or 1 (bad code in file).
**
@@ -105,14 +107,14 @@ class Pyo {
** pyo.exec("fr = Rossler(pitch=pits, chaos=0.9, mul=250, add=500)")
** pyo.exec("b = SumOsc(freq=fr, ratio=0.499, index=0.4, mul=0.2).out()")
*/
- int exec(const char *msg);
- int exec(const String &msg);
+ int exec(const char *msg);
+ int exec(const juce::String &msg);
/*
** Sends a numerical value to an existing Sig or SigTo object.
**
** arguments:
- ** name : const char * or const String &,
+ ** name : const char * or const juce::String &,
** variable name of the object.
** value : float, value to be assigned.
**
@@ -126,14 +128,14 @@ class Pyo {
**
** pyo.value("freq", 880);
*/
- int value(const char *name, float value);
- int value(const String &name, float value);
+ int value(const char *name, float value);
+ int value(const juce::String &name, float value);
/*
** Sends an array of numerical values to an existing Sig or SigTo object.
**
** arguments:
- ** name : const char * or const String &,
+ ** name : const char * or const juce::String &,
** variable name of the object.
** value : float *, array of floats.
** len : int, number of elements in the array.
@@ -149,14 +151,14 @@ class Pyo {
** float frequencies[4] = {150, 250, 350, 450};
** pyo.value("freq", frequencies, 4);
*/
- int value(const char *name, float *value, int len);
- int value(const String &name, float *value, int len);
+ int value(const char *name, float *value, int len);
+ int value(const juce::String &name, float *value, int len);
/*
** Sends a numerical value to a Pyo object's attribute.
**
** arguments:
- ** name : const char * or const String &,
+ ** name : const char * or const juce::String &,
** object name and attribute separated by a dot.
** value : float, value to be assigned.
**
@@ -170,14 +172,14 @@ class Pyo {
**
** pyo.set("filter.freq", 2000);
*/
- int set(const char *name, float value);
- int set(const String &name, float value);
+ int set(const char *name, float value);
+ int set(const juce::String &name, float value);
/*
** Sends an array of numerical values to a Pyo object's attribute.
**
** arguments:
- ** name : const char * or const String &,
+ ** name : const char * or const juce::String &,
** object name and attribute separated by a dot.
** value : float *, array of floats.
** len : int, number of elements in the array.
@@ -193,8 +195,8 @@ class Pyo {
** float frequencies[4] = {350, 700, 1400, 2800};
** pyo.set("filters.freq", frequencies, 4);
*/
- int set(const char *name, float *value, int len);
- int set(const String &name, float *value, int len);
+ int set(const char *name, float *value, int len);
+ int set(const juce::String &name, float *value, int len);
/*
** Sends a MIDI messges to the Server.
@@ -204,7 +206,7 @@ class Pyo {
** pyo.midi(144, 60, 127) //Send a Note on message on channel 1 for note # 60
** pyo.midi(128, 60, 0) //Send a Note off messge on channel 1 for note # 60
*/
- void midi(int status, int data1, int data2);
+ void midi(int status, int data1, int data2);
/*
** Shutdown and reboot the pyo server while keeping current in/out buffers.
@@ -212,17 +214,19 @@ class Pyo {
**
*/
void clear();
-
- private:
- int nChannels;
- int bufferSize;
- int sampleRate;
- PyThreadState *interpreter;
- float *pyoInBuffer;
- float *pyoOutBuffer;
- callPtr *pyoCallback;
- int pyoId;
- char pyoMsg[262144];
-};
-
-#endif // PYOCLASS_H_INCLUDED
+
+ private:
+ int inChannels;
+ int outChannels;
+ int bufferSize;
+ int sampleRate;
+ int debug;
+ PyThreadState *interpreter;
+ float *pyoInBuffer;
+ float *pyoOutBuffer;
+ callPtr *pyoCallback;
+ int pyoId;
+ char pyoMsg[262144];
+};
+
+#endif // PYOCLASS_H_INCLUDED
diff --git a/embedded/juceplugin/README.md b/embedded/juceplugin/README.md
index 7de2b7c5a..44677edc0 100644
--- a/embedded/juceplugin/README.md
+++ b/embedded/juceplugin/README.md
@@ -27,11 +27,11 @@ these in the specific fields:
Extra linker flags :
- `python-config --ldflags`
+ `python3-config --ldflags --embed` -rdynamic
Extra compiler flags :
- `python-config --cflags`
+ `python3-config --cflags`
On MacOS, the default compiler in Xcode is LLVM and it will complain about
python-config command. All you have to do is to run these two commands (without
@@ -94,9 +94,13 @@ Add a Pyo object to the public attributes of the *XXXAudioProcessor* class:
------------------------------------------------------------------------------
Step 7 - Edit Source/PluginProcessor.cpp
-Add these lines to *XXXAudioProcessor::prepareToPlay* method:
+Add these lines to *XXXAudioProcessor::prepareToPlay* method
+(note that the first line is necessary, in contrast to other embedded projects,
+otherwise a PyFloat_Type error occurs and the plugin host will crash.
+Change Python version to the one you have installed or compiled Pyo for):
- pyo.setup(getTotalNumOutputChannels(), samplesPerBlock, sampleRate);
+ dlopen("libpython3.13.so.1.0", RTLD_LAZY | RTLD_GLOBAL);
+ pyo.setup(getTotalNumInputChannels(), getTotalNumOutputChannels(), samplesPerBlock, sampleRate);
pyo.exec(BinaryData::stereoDelay_py);
Replace the processing part of *XXXAudioProcessor::processBlock* method with this
@@ -104,7 +108,7 @@ line:
pyo.process(buffer);
-The processing part is the code after this comment:
+The processing part is the code after this comment (the line above should not be included in the for loop):
// This is the place where you'd normally do the guts of your plugin's
// audio processing...
@@ -126,17 +130,17 @@ Step 8 - Edit Source/PluginEditor.h
Add the inheritance to *Slider::Listener* to your *XXXAudioProcessorEditor*
class definition:
- class XXXAudioProcessorEditor : public AudioProcessorEditor,
- private Slider::Listener
+ class XXXAudioProcessorEditor : public juce::AudioProcessorEditor,
+ private juce::Slider::Listener
Add the default callback function in the public attributes of the class:
- void sliderValueChanged(Slider* slider) override;
+ void sliderValueChanged(juce::Slider* slider) override;
Create two sliders in the public attributes of the class:
- Slider p1;
- Slider p2;
+ juce::Slider p1;
+ juce::Slider p2;
------------------------------------------------------------------------------
Step 9 - Edit Source/PluginEditor.cpp
@@ -146,9 +150,9 @@ Set the sliders properties in the editor constructor function named
in the PluginEditor.cpp file). Add these lines at the end of the function:
// these define the parameters of our slider object
- p1.setSliderStyle(Slider::LinearBarVertical);
+ p1.setSliderStyle(juce::Slider::LinearBarVertical);
p1.setRange(0.0, 1.0, 0.01);
- p1.setTextBoxStyle(Slider::NoTextBox, false, 90, 0);
+ p1.setTextBoxStyle(juce::Slider::NoTextBox, false, 90, 0);
p1.setPopupDisplayEnabled(true, true, this);
p1.setTextValueSuffix(" Delay Time");
p1.setValue(0.5);
@@ -156,9 +160,9 @@ in the PluginEditor.cpp file). Add these lines at the end of the function:
// this function adds the slider to the editor
addAndMakeVisible(&p1);
- p2.setSliderStyle(Slider::LinearBarVertical);
+ p2.setSliderStyle(juce::Slider::LinearBarVertical);
p2.setRange(0.0, 1.0, 0.01);
- p2.setTextBoxStyle(Slider::NoTextBox, false, 90, 0);
+ p2.setTextBoxStyle(juce::Slider::NoTextBox, false, 90, 0);
p2.setPopupDisplayEnabled(true, true, this);
p2.setTextValueSuffix(" Delay Feedback");
p2.setValue(0.5);
@@ -177,10 +181,10 @@ Step 10 - Connect the sliders to the audio process
At the end of the file PluginEditor.cpp, create the slider's callback
function which will pass the values to the audio processing objects:
- void XXXAudioProcessorEditor::sliderValueChanged (Slider* slider)
+ void XXXAudioProcessorEditor::sliderValueChanged (juce::Slider* slider)
{
- processor.pyo.value("dtime", p1.getValue());
- processor.pyo.value("feed", p2.getValue());
+ audioProcessor.pyo.value("dtime", p1.getValue());
+ audioProcessor.pyo.value("feed", p2.getValue());
}
------------------------------------------------------------------------------
diff --git a/embedded/openframeworks/README b/embedded/openframeworks/README
index 925a7c31d..8b48fb6f8 100644
--- a/embedded/openframeworks/README
+++ b/embedded/openframeworks/README
@@ -2,10 +2,15 @@ Introduction on how to use pyo inside an OpenFrameworks project.
================================================================
To use pyo inside an OpenFrameworks project, first you need a
-working install of pyo for python 2.7 on your system (will switch
-to python 3 as soon as possible). After installing pyo, you can
+working install of pyo for python 3.13 on your system.
+After installing pyo, you can
move on and create an OpenFrameworks project.
+------------------------------------------------------------------------
+Before trying out your own project, you might want to try the pyoExample
+that comes with Pyo. Copy the pyoExample directory to OF_ROOT/app/myApps
+and make it.
+
------------------------------------------------------------------------
Step 1 - Create a default project with the project generator. To avoid
any include or linking problem, you should save your project in the
@@ -15,10 +20,10 @@ default location (OF_ROOT/apps/myApps).
Step 2 - Add pyo files to your project. You only need to copy
these files in your project's src folder:
-- from the folder pyo/embedded:
+- from the folder pyo/embedded or from pyo/embedded/openFrameworks/pyoExample:
m_pyo.h
-- from the folder pyo/embedded/openframeworks:
+- from the folder pyo/embedded/openframeworks/pyoExample:
PyoClass.cpp
PyoClass.h
@@ -27,12 +32,13 @@ Step 3 - Make sure that your project includes flags for compiling
and linking against Python. On Unix systems, you can set the Python
flags in the file config.make. Uncomment and complete these lines:
-PROJECT_LDFLAGS = `python-config --ldflags` # linker flags for Python
-PROJECT_CFLAGS = `python-config --cflags` # compiler flags for Python
+PROJECT_LDFLAGS = `python3-config --ldflags --embed` # linker flags for Python
+PROJECT_CFLAGS = `python3-config --cflags` # compiler flags for Python
------------------------------------------------------------------------
-Step 4 - Make a scripts folder at the root level of your project and
-create a python file, named "stereoDelay.py", with these lines in it:
+Step 4 - Make a scripts folder in the bin/data directory of your project.
+If /bin/data doesn't exist, creat it.
+Create a python file, named "stereoDelay.py", with these lines in it:
# Get the input sound and apply a stereo delay + reverb on it.
st_input = Input([0,1])
@@ -48,8 +54,8 @@ Step 5 - Edit src/ofApp.h
- Add audio in/out callbacks to the public attributes of the ofApp class:
-void audioIn(float * input, int bufferSize, int nChannels);
-void audioOut(float * input, int bufferSize, int nChannels);
+void audioIn(ofSoundBuffer & buffer)
+void audioOut(ofSoundBuffer & buffer)
- Add an ofSoundStream object to the public attributes of the ofApp class:
@@ -70,9 +76,9 @@ void ofApp::setup(){
int bufferSize = 256;
int nChannels = 2;
// initialize a pyo server
- pyo.setup(nChannels, bufferSize, sampleRate);
+ pyo.setup(nChannels, nChannels, bufferSize, sampleRate);
// load a python file
- pyo.loadfile("../scripts/stereoDelay.py", 0);
+ pyo.loadfile("data/scripts/stereoDelay.py", 0);
// initialize OpenFrameworks audio streaming channels
//soundStream.listDevices() // Uncomment if you need to
//soundStream.setDeviceID(1); // change the audio device.
@@ -81,20 +87,22 @@ void ofApp::setup(){
- Creates audio in/out functions:
-void ofApp::audioIn(float * input, int bufferSize, int nChannels){
+void ofApp::audioIn(ofSoundBuffer & buffer){
// send audio samples to pyo
- pyo.fillin(input);
+ pyo.fillin(&buffer[0]);
}
-void ofApp::audioOut(float * output, int bufferSize, int nChannels){
+//--------------------------------------------------------------
+void ofApp::audioOut(ofSoundBuffer & buffer){
// process and get new audio samples from pyo
- pyo.process(output);
+ pyo.process(&buffer[0]);
}
------------------------------------------------------------------------
Step 7 - Compile your project. To compile, cd to your project folder and:
# This line is for MacOS, you'll have to adapt it to your system!
+# On Linux you don't need to do anything.
make && cp -r scripts bin/*.app/Contents/Resources
------------------------------------------------------------------------
@@ -108,6 +116,6 @@ with the pyo embedded processes, see documentation comments in the file
PyoClass.cpp.
-(c) 2019 - belangeo
+(c) 2026 - belangeo
diff --git a/embedded/openframeworks/pyoExample/Makefile b/embedded/openframeworks/pyoExample/Makefile
new file mode 100644
index 000000000..d2f848272
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/Makefile
@@ -0,0 +1,13 @@
+# Attempt to load a config.make file.
+# If none is found, project defaults in config.project.make will be used.
+ifneq ($(wildcard config.make),)
+ include config.make
+endif
+
+# make sure the the OF_ROOT location is defined
+ifndef OF_ROOT
+ OF_ROOT=../../..
+endif
+
+# call the project makefile!
+include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk
diff --git a/embedded/openframeworks/pyoExample/README.md b/embedded/openframeworks/pyoExample/README.md
new file mode 100644
index 000000000..7f563039e
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/README.md
@@ -0,0 +1,5 @@
+# About pyoExample
+
+This is a simple example that demonstrates how to embed Python with the Pyo module in openFrameworks.
+It simply plays back an audio file with a stereo delay and a reverb, but there's also an interpreter entry where you can write Python code directly.
+Read the source files for info.
diff --git a/embedded/openframeworks/pyoExample/addons.make b/embedded/openframeworks/pyoExample/addons.make
new file mode 100644
index 000000000..e69de29bb
diff --git a/embedded/openframeworks/pyoExample/config.make b/embedded/openframeworks/pyoExample/config.make
new file mode 100644
index 000000000..e38971644
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/config.make
@@ -0,0 +1,143 @@
+################################################################################
+# CONFIGURE PROJECT MAKEFILE (optional)
+# This file is where we make project specific configurations.
+################################################################################
+
+################################################################################
+# OF ROOT
+# The location of your root openFrameworks installation
+# (default) OF_ROOT = ../../..
+################################################################################
+OF_ROOT = ../../..
+
+################################################################################
+# PROJECT ROOT
+# The location of the project - a starting place for searching for files
+# (default) PROJECT_ROOT = . (this directory)
+#
+################################################################################
+# PROJECT_ROOT = .
+
+################################################################################
+# PROJECT SPECIFIC CHECKS
+# This is a project defined section to create internal makefile flags to
+# conditionally enable or disable the addition of various features within
+# this makefile. For instance, if you want to make changes based on whether
+# GTK is installed, one might test that here and create a variable to check.
+################################################################################
+# None
+
+################################################################################
+# PROJECT EXTERNAL SOURCE PATHS
+# These are fully qualified paths that are not within the PROJECT_ROOT folder.
+# Like source folders in the PROJECT_ROOT, these paths are subject to
+# exlclusion via the PROJECT_EXLCUSIONS list.
+#
+# (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank)
+#
+# Note: Leave a leading space when adding list items with the += operator
+################################################################################
+# PROJECT_EXTERNAL_SOURCE_PATHS =
+
+################################################################################
+# PROJECT EXCLUSIONS
+# These makefiles assume that all folders in your current project directory
+# and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations
+# to look for source code. The any folders or files that match any of the
+# items in the PROJECT_EXCLUSIONS list below will be ignored.
+#
+# Each item in the PROJECT_EXCLUSIONS list will be treated as a complete
+# string unless teh user adds a wildcard (%) operator to match subdirectories.
+# GNU make only allows one wildcard for matching. The second wildcard (%) is
+# treated literally.
+#
+# (default) PROJECT_EXCLUSIONS = (blank)
+#
+# Will automatically exclude the following:
+#
+# $(PROJECT_ROOT)/bin%
+# $(PROJECT_ROOT)/obj%
+# $(PROJECT_ROOT)/%.xcodeproj
+#
+# Note: Leave a leading space when adding list items with the += operator
+################################################################################
+# PROJECT_EXCLUSIONS =
+
+################################################################################
+# PROJECT LINKER FLAGS
+# These flags will be sent to the linker when compiling the executable.
+#
+# (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs
+#
+# Note: Leave a leading space when adding list items with the += operator
+#
+# Currently, shared libraries that are needed are copied to the
+# $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to
+# add a runtime path to search for those shared libraries, since they aren't
+# incorporated directly into the final executable application binary.
+################################################################################
+# PROJECT_LDFLAGS=-Wl,-rpath=./libs
+PROJECT_LDFLAGS = `python3-config --ldflags --embed` # linker flags for Python
+
+################################################################################
+# PROJECT DEFINES
+# Create a space-delimited list of DEFINES. The list will be converted into
+# CFLAGS with the "-D" flag later in the makefile.
+#
+# (default) PROJECT_DEFINES = (blank)
+#
+# Note: Leave a leading space when adding list items with the += operator
+################################################################################
+# PROJECT_DEFINES =
+
+################################################################################
+# PROJECT CFLAGS
+# This is a list of fully qualified CFLAGS required when compiling for this
+# project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS
+# defined in your platform specific core configuration files. These flags are
+# presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below.
+#
+# (default) PROJECT_CFLAGS = (blank)
+#
+# Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in
+# your platform specific configuration file will be applied by default and
+# further flags here may not be needed.
+#
+# Note: Leave a leading space when adding list items with the += operator
+################################################################################
+# PROJECT_CFLAGS =
+PROJECT_CFLAGS = `python3-config --cflags` # compiler flags for Python
+
+################################################################################
+# PROJECT OPTIMIZATION CFLAGS
+# These are lists of CFLAGS that are target-specific. While any flags could
+# be conditionally added, they are usually limited to optimization flags.
+# These flags are added BEFORE the PROJECT_CFLAGS.
+#
+# PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets.
+#
+# (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank)
+#
+# PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets.
+#
+# (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank)
+#
+# Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the
+# PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration
+# file will be applied by default and further optimization flags here may not
+# be needed.
+#
+# Note: Leave a leading space when adding list items with the += operator
+################################################################################
+# PROJECT_OPTIMIZATION_CFLAGS_RELEASE =
+# PROJECT_OPTIMIZATION_CFLAGS_DEBUG =
+
+################################################################################
+# PROJECT COMPILERS
+# Custom compilers can be set for CC and CXX
+# (default) PROJECT_CXX = (blank)
+# (default) PROJECT_CC = (blank)
+# Note: Leave a leading space when adding list items with the += operator
+################################################################################
+# PROJECT_CXX =
+# PROJECT_CC =
diff --git a/embedded/openframeworks/pyoExample/pyoExample.code-workspace b/embedded/openframeworks/pyoExample/pyoExample.code-workspace
new file mode 100644
index 000000000..2bf344fc9
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/pyoExample.code-workspace
@@ -0,0 +1,12 @@
+{
+ "folders": [
+ {
+ "path": "."
+ },
+ {
+ "path": "${workspaceRoot}/../../../../libs/openFrameworks"
+ }
+ ],
+ "openFrameworksProjectGeneratorVersion": "0.103.0",
+ "settings": {}
+}
\ No newline at end of file
diff --git a/embedded/openframeworks/PyoClass.cpp b/embedded/openframeworks/pyoExample/src/PyoClass.cpp
similarity index 78%
rename from embedded/openframeworks/PyoClass.cpp
rename to embedded/openframeworks/pyoExample/src/PyoClass.cpp
index b3640f9c2..d17ce0f5e 100644
--- a/embedded/openframeworks/PyoClass.cpp
+++ b/embedded/openframeworks/pyoExample/src/PyoClass.cpp
@@ -1,4 +1,9 @@
#include "PyoClass.h"
+#include
+
+//Pyo::Pyo() {
+// gstate = PyGILState_Ensure(); // Acquire GIL
+//}
/*
** Creates a python interpreter and initialize a pyo server inside it.
@@ -12,15 +17,18 @@
**
** All arguments should be equal to the host audio settings.
*/
-void Pyo::setup(int _nChannels, int _bufferSize, int _sampleRate) {
- nChannels = _nChannels;
+void Pyo::setup(int _inChannels, int _outChannels, int _bufferSize, int _sampleRate) {
+ inChannels = _inChannels;
+ outChannels = _outChannels;
bufferSize = _bufferSize;
sampleRate = _sampleRate;
- interpreter = pyo_new_interpreter(sampleRate, bufferSize, nChannels);
+ debug = 0;
+ interpreter = pyo_new_interpreter(sampleRate, bufferSize, inChannels, outChannels);
pyoInBuffer = reinterpret_cast(pyo_get_input_buffer_address(interpreter));
pyoOutBuffer = reinterpret_cast(pyo_get_output_buffer_address(interpreter));
pyoCallback = reinterpret_cast(pyo_get_embedded_callback_address(interpreter));
pyoId = pyo_get_server_id(interpreter);
+ processing = false;
}
/*
@@ -38,7 +46,14 @@ Pyo::~Pyo() {
** *buffer : float *, float pointer pointing to the host's input buffers.
*/
void Pyo::fillin(float *buffer) {
- for (int i=0; i<(bufferSize*nChannels); i++) pyoInBuffer[i] = buffer[i];
+ processing = true;
+ for (int i=0; i<(bufferSize*inChannels); i++) pyoInBuffer[i] = buffer[i];
+ processing = false;
+}
+
+bool Pyo::isProcessing()
+{
+ return processing;
}
@@ -52,7 +67,9 @@ void Pyo::fillin(float *buffer) {
*/
void Pyo::process(float *buffer) {
pyoCallback(pyoId);
- for (int i=0; i<(bufferSize*nChannels); i++) buffer[i] = pyoOutBuffer[i];
+ processing = true;
+ for (int i=0; i<(bufferSize*outChannels); i++) buffer[i] = pyoOutBuffer[i];
+ processing = false;
}
/*
@@ -71,7 +88,7 @@ void Pyo::process(float *buffer) {
** returns 0 (no error), 1 (failed to open the file) or 2 (bad code in file).
*/
int Pyo::loadfile(const char *file, int add) {
- return pyo_exec_file(interpreter, file, pyoMsg, add);
+ return pyo_exec_file(interpreter, file, pyoMsg, add, debug);
}
/*
@@ -95,7 +112,7 @@ int Pyo::loadfile(const char *file, int add) {
*/
int Pyo::value(const char *name, float value) {
sprintf(pyoMsg, "%s.value=%f", name, value);
- return pyo_exec_statement(interpreter, pyoMsg, 0);
+ return pyo_exec_statement(interpreter, pyoMsg, debug);
}
/*
@@ -151,7 +168,7 @@ int Pyo::value(const char *name, float *value, int len) {
*/
int Pyo::set(const char *name, float value) {
sprintf(pyoMsg, "%s=%f", name, value);
- return pyo_exec_statement(interpreter, pyoMsg, 0);
+ return pyo_exec_statement(interpreter, pyoMsg, debug);
}
/*
@@ -183,12 +200,12 @@ int Pyo::set(const char *name, float *value, int len) {
strcat(pyoMsg, fchar);
}
strcat(pyoMsg, "]");
- return pyo_exec_statement(interpreter, pyoMsg, 0);
+ return pyo_exec_statement(interpreter, pyoMsg, debug);
}
/*
** Executes any raw valid python statement. With this function, one can dynamically
-** creates and manipulates audio objects and algorithms.
+** create and manipulate audio objects and algorithms.
**
** arguments:
** msg : const char *, pointer to a string containing the statement to execute.
@@ -203,13 +220,43 @@ int Pyo::set(const char *name, float *value, int len) {
*/
int Pyo::exec(const char *_msg) {
strcpy(pyoMsg, _msg);
- return pyo_exec_statement(interpreter, pyoMsg, 0);
+ return pyo_exec_statement(interpreter, pyoMsg, debug);
+}
+
+/*
+** Set the debug state
+*/
+void Pyo::setDebug(int debugVal) {
+ debug = debugVal;
+}
+
+/*
+** Get the STDOUT as a vector of strings
+*/
+std::vector Pyo::getStdout() {
+ std::vector out;
+ char *msg;
+ while (pyo_dequeue_stdout(&msg)) {
+ out.emplace_back(msg); // copy into vector
+ free(msg); // free allocated C buffer
+ }
+ return out;
+}
+
+/*
+** Return the error message stored to the pyoMsg char array
+** in case the exec() or loadfile() returns a non-zero error code
+*/
+std::string Pyo::getErrorMsg() {
+ std::string s(pyoMsg);
+ return s;
}
/*
** Shutdown and reboot the pyo server while keeping current in/out buffers.
** This will erase audio objects currently active within the server.
**
-*/void Pyo::clear() {
+*/
+void Pyo::clear() {
pyo_server_reboot(interpreter);
}
diff --git a/embedded/openframeworks/PyoClass.h b/embedded/openframeworks/pyoExample/src/PyoClass.h
similarity index 60%
rename from embedded/openframeworks/PyoClass.h
rename to embedded/openframeworks/pyoExample/src/PyoClass.h
index 2ca65b4e3..dccf69f7e 100644
--- a/embedded/openframeworks/PyoClass.h
+++ b/embedded/openframeworks/pyoExample/src/PyoClass.h
@@ -1,13 +1,17 @@
-#pragma once
+#ifndef __pyoclass_h_
+#define __pyoclass_h_
+#include
+#include
#include "m_pyo.h"
typedef int callPtr(int);
class Pyo {
public:
+ //Pyo();
~Pyo();
- void setup(int nChannels, int bufferSize, int sampleRate);
+ void setup(int inChannels, int outChannels, int bufferSize, int sampleRate);
void process(float *buffer);
void fillin(float *buffer);
void clear();
@@ -17,15 +21,25 @@ class Pyo {
int value(const char *name, float *value, int len);
int set(const char *name, float value);
int set(const char *name, float *value, int len);
+ void setDebug(int debugVal);
+ std::vector getStdout();
+ std::string getErrorMsg();
+ bool isProcessing();
private:
- int nChannels;
+ //PyGILState_STATE gstate; // to acquire and release the Global Interpreter Lock (GIL)
+ int inChannels;
+ int outChannels;
int bufferSize;
int sampleRate;
+ int debug;
PyThreadState *interpreter;
float *pyoInBuffer;
float *pyoOutBuffer;
callPtr *pyoCallback;
int pyoId;
+ bool processing;
char pyoMsg[262144];
};
+
+#endif
diff --git a/embedded/openframeworks/pyoExample/src/m_pyo.h b/embedded/openframeworks/pyoExample/src/m_pyo.h
new file mode 100644
index 000000000..37b8ced4f
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/src/m_pyo.h
@@ -0,0 +1,718 @@
+/**************************************************************************
+ * Copyright 2014-2020 Olivier Belanger *
+ * *
+ * This file is part of pyo, a python module to help digital signal *
+ * processing script creation. *
+ * *
+ * pyo is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * pyo is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with pyo. If not, see . *
+ *************************************************************************/
+#include
+
+#if !defined(_WIN32)
+#include
+#endif
+
+#include "Python.h"
+
+#ifndef __m_pyo_h_
+#define __m_pyo_h_
+
+#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
+#define INLINE inline
+extern "C" {
+#else
+#define INLINE
+#endif
+
+/* sub-interpreter counter, necessary to determine when to shut down Python */
+static int py_instance_count = 0;
+/* global interpreter boolean, used to initialize the global interpreter only once for all running [pyo~] objects */
+static int py_global_initialized = 0;
+/* global interpreter instance */
+static PyThreadState *main_tstate = NULL;
+
+/* Function prototypes to redirect Python's stdout to C
+ * Needed because it is called before it is defined
+*/
+static inline void redirect_stdout_to_c(void);
+static PyObject* PyInit_cstdout(void);
+
+/*
+** Creates a global python interpreter.
+** This is called by every new [pyo~] object,
+** but the global interpreter is initialized only once, using the py_global_initialized variable
+*/
+static void pyo_python_global_init(void)
+{
+ if (!py_global_initialized) {
+ /* redirect Python's stdout to m_pyo.h from where we can get it in here */
+ PyImport_AppendInittab("cstdout", PyInit_cstdout);
+
+ Py_Initialize();
+
+ /* Save main interpreter state */
+ main_tstate = PyThreadState_Get();
+
+ /* Release GIL */
+ PyEval_ReleaseThread(main_tstate);
+
+ py_global_initialized = 1;
+ }
+}
+
+/*
+** Creates a new python interpreter and starts a pyo server in it.
+** Each instance of pyo, in order to be fully independent of other
+** instances, must be started in its own interpreter. An instance
+** can be an object in a programming language or a plugin in a daw.
+**
+** arguments:
+** sr : float, host sampling rate.
+** bufsize : int, host buffer size.
+** chnls : int, number of in/out channels of the pyo server.
+**
+** returns the new python thread's interpreter state.
+*/
+INLINE PyThreadState * pyo_new_interpreter(float sr, int bufsize, int ichnls, int ochnls) {
+ char msg[128];
+ PyThreadState *interp;
+
+ pyo_python_global_init();
+
+ PyEval_AcquireThread(main_tstate);
+
+ interp = Py_NewInterpreter(); /* add a new sub-interpreter */
+ redirect_stdout_to_c();
+
+ /* On MacOS, trying to import wxPython in embedded python hang the process.
+ * On Linux, it crashes the host (at least in Pd)
+ */
+ PyRun_SimpleString("import os; os.environ['PYO_GUI_WX'] = '0'");
+
+ /* Force embedded audio server. */
+ PyRun_SimpleString("os.environ['PYO_SERVER_AUDIO'] = 'embedded'");
+
+ /* Set the default BPM (60 beat per minute). */
+ PyRun_SimpleString("BPM = 60.0");
+
+ PyRun_SimpleString("from pyo import *");
+ sprintf(msg, "_s_ = Server(sr=%f, nchnls=%d, buffersize=%d, duplex=1, ichnls=%d)", sr, ochnls, bufsize, ichnls);
+ PyRun_SimpleString(msg);
+ PyRun_SimpleString("_s_.boot()\n_s_.start()\n_s_.setServer()");
+ PyRun_SimpleString("_server_id_ = _s_.getServerID()");
+
+ /*
+ ** printf %p specifier behaves differently in Linux/MacOS and Windows.
+ */
+
+#if defined(_WIN32)
+ PyRun_SimpleString("_in_address_ = '0x' + _s_.getInputAddr().lower()");
+ PyRun_SimpleString("_out_address_ = '0x' + _s_.getOutputAddr().lower()");
+ PyRun_SimpleString("_emb_callback_ = '0x' + _s_.getEmbedICallbackAddr().lower()");
+#else
+ PyRun_SimpleString("_in_address_ = _s_.getInputAddr()");
+ PyRun_SimpleString("_out_address_ = _s_.getOutputAddr()");
+ PyRun_SimpleString("_emb_callback_ = _s_.getEmbedICallbackAddr()");
+#endif
+
+ PyEval_ReleaseThread(interp);
+
+ /* increase the sub-interpreter counter */
+ py_instance_count++;
+
+ return interp;
+}
+
+/*
+** Returns the address, as unsigned long, of the pyo input buffer.
+** Used this function if pyo's audio samples resolution is 32-bit.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an "unsigned long" that should be recast to a float pointer.
+*/
+INLINE unsigned long pyo_get_input_buffer_address(PyThreadState *interp) {
+ PyObject *module, *obj;
+ const char *address;
+ unsigned long uadd;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_in_address_");
+ address = PyUnicode_AsUTF8(obj);
+ uadd = strtoul(address, NULL, 0);
+ PyEval_ReleaseThread(interp);
+ return uadd;
+}
+
+/*
+** Returns the address, as unsigned long long, of the pyo input buffer.
+** Used this function if pyo's audio samples resolution is 64-bit.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an "unsigned long long" that should be recast to a double pointer.
+*/
+INLINE unsigned long long pyo_get_input_buffer_address_64(PyThreadState *interp) {
+ PyObject *module, *obj;
+ const char *address;
+ unsigned long long uadd;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_in_address_");
+ address = PyUnicode_AsUTF8(obj);
+ uadd = strtoull(address, NULL, 0);
+ PyEval_ReleaseThread(interp);
+ return uadd;
+}
+
+/*
+** Returns the address, as unsigned long, of the pyo output buffer.
+** Used this function if pyo's audio samples resolution is 32-bit.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an "unsigned long" that should be recast to a float pointer.
+*/
+INLINE unsigned long pyo_get_output_buffer_address(PyThreadState *interp) {
+ PyObject *module, *obj;
+ const char *address;
+ unsigned long uadd;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_out_address_");
+ address = PyUnicode_AsUTF8(obj);
+ uadd = strtoul(address, NULL, 0);
+ PyEval_ReleaseThread(interp);
+ return uadd;
+}
+
+/*
+** Returns the address, as unsigned long long, of the pyo output buffer.
+** Used this function if pyo's audio samples resolution is 64-bit.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an "unsigned long long" that should be recast to a double pointer.
+*/
+INLINE unsigned long long pyo_get_output_buffer_address_64(PyThreadState *interp) {
+ PyObject *module, *obj;
+ const char *address;
+ unsigned long long uadd;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_out_address_");
+ address = PyUnicode_AsUTF8(obj);
+ uadd = strtoull(address, NULL, 0);
+ PyEval_ReleaseThread(interp);
+ return uadd;
+}
+
+/*
+** Returns the address, as unsigned long, of the pyo embedded callback.
+** This callback must be called in the host's perform routine whenever
+** pyo has to compute a new buffer of samples.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an "unsigned long" that should be recast to a void pointer.
+**
+** The callback should be called with the server id (int) as argument.
+**
+** Prototype:
+** void (*callback)(int);
+*/
+INLINE unsigned long pyo_get_embedded_callback_address(PyThreadState *interp) {
+ PyObject *module, *obj;
+ const char *address;
+ unsigned long uadd;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_emb_callback_");
+ address = PyUnicode_AsUTF8(obj);
+ uadd = strtoul(address, NULL, 0);
+ PyEval_ReleaseThread(interp);
+ return uadd;
+}
+
+/*
+** Returns the address, as unsigned long long, of the pyo embedded callback.
+** This callback must be called in the host's perform routine whenever
+** pyo has to compute a new buffer of samples.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an "unsigned long long" that should be recast to a void pointer.
+**
+** The callback should be called with the server id (int) as argument.
+**
+** Prototype:
+** void (*callback)(int);
+*/
+INLINE unsigned long long pyo_get_embedded_callback_address_64(PyThreadState *interp) {
+ PyObject *module, *obj;
+ const char *address;
+ unsigned long long uadd;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_emb_callback_");
+ address = PyUnicode_AsUTF8(obj);
+ uadd = strtoull(address, NULL, 0);
+ PyEval_ReleaseThread(interp);
+ return uadd;
+}
+
+/*
+** Returns the pyo server id of this thread, as an integer.
+** The id must be pass as argument to the callback function.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+**
+** returns an integer.
+*/
+INLINE int pyo_get_server_id(PyThreadState *interp) {
+ PyObject *module, *obj;
+ int id;
+ PyEval_AcquireThread(interp);
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "_server_id_");
+ id = PyLong_AsLong(obj);
+ PyEval_ReleaseThread(interp);
+ return id;
+}
+
+/*
+** Closes the interpreter linked to the thread state given as argument.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+*/
+INLINE void pyo_end_interpreter(PyThreadState *interp) {
+ /* Clean up pyo's server. */
+ if (interp !=NULL) {
+ PyEval_AcquireThread(interp);
+ PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()");
+ PyEval_ReleaseThread(interp);
+
+ //PyGILState_STATE state = PyGILState_Ensure();
+ PyGILState_Ensure();
+
+ Py_EndInterpreter(interp);
+ }
+
+ /* decrement the sub-interpreter counter */
+ if (py_instance_count > 0) py_instance_count--;
+ /* if we delete the last sub-interpreter and the global interpreter is active
+ * finalize it all
+ **/
+ if (py_instance_count == 0 && py_global_initialized) {
+ PyEval_AcquireThread(main_tstate);
+ Py_FinalizeEx();
+ py_global_initialized = 0;
+ main_tstate = NULL;
+ }
+}
+
+/*
+** Shutdown and reboot the pyo server while keeping current in/out buffers.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+*/
+INLINE void pyo_server_reboot(PyThreadState *interp) {
+ PyEval_AcquireThread(interp);
+ PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()");
+ PyRun_SimpleString("_s_.boot(newBuffer=False).start()");
+ PyEval_ReleaseThread(interp);
+}
+
+/*
+** Reboot the pyo server with new sampling rate and buffer size.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+** sr : float, host sampling rate.
+** bufsize : int, host buffer size.
+*/
+INLINE void pyo_set_server_params(PyThreadState *interp, float sr, int bufsize) {
+ char msg[64];
+ PyEval_AcquireThread(interp);
+ PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()");
+ sprintf(msg, "_s_.setSamplingRate(%f)", sr);
+ PyRun_SimpleString(msg);
+ sprintf(msg, "_s_.setBufferSize(%d)", bufsize);
+ PyRun_SimpleString(msg);
+ PyRun_SimpleString("_s_.boot(newBuffer=False).start()");
+ PyEval_ReleaseThread(interp);
+}
+
+/*
+** This function can be used to pass the DAW's bpm value to the
+** python interpreter. Changes the value of the BPM variable in
+** the interpreter's global scope. (which defaults to 60).
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+** bpm : double, new BPM.
+*/
+INLINE void pyo_set_bpm(PyThreadState *interp, double bpm) {
+ char msg[64];
+ PyEval_AcquireThread(interp);
+ sprintf(msg, "BPM = %f", bpm);
+ PyRun_SimpleString(msg);
+ PyEval_ReleaseThread(interp);
+}
+
+/*
+** Add a MIDI event in the pyo server processing chain. When used in
+** an embedded framework, pyo can't open MIDI ports by itself. MIDI
+** inputs must be handled by the host program and sent to pyo with
+** the pyo_add_midi_event function.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+** status : int, status byte.
+** data1 : int, first data byte.
+** data2 : int, second data byte.
+*/
+INLINE void pyo_add_midi_event(PyThreadState *interp, int status, int data1, int data2) {
+ char msg[64];
+ PyEval_AcquireThread(interp);
+ sprintf(msg, "_s_.addMidiEvent(%d, %d, %d)", status, data1, data2);
+ PyRun_SimpleString(msg);
+ PyEval_ReleaseThread(interp);
+}
+
+/*
+** Returns 1 if the pyo server is started for the given thread,
+** Otherwise returns 0.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+*/
+INLINE int pyo_is_server_started(PyThreadState *interp) {
+ int started;
+ PyObject *module, *obj;
+ PyEval_AcquireThread(interp);
+ PyRun_SimpleString("started = _s_.getIsStarted()");
+ module = PyImport_AddModule("__main__");
+ obj = PyObject_GetAttrString(module, "started");
+ started = PyLong_AsLong(obj);
+ PyEval_ReleaseThread(interp);
+ return started;
+}
+
+/*
+** These two functions are used to execute code, either as a statement
+** or as a loaded file.
+** They are called by pyo_exec_file() and pyo_exec_statement()
+*/
+INLINE void copy_pyunicode_to_msg(PyObject *u, char *msg)
+{
+ if (!u) return;
+ const char *s = PyUnicode_AsUTF8(u);
+ if (s) {
+ strcpy(msg, s);
+ }
+}
+
+INLINE int pyo_exec_code(char *msg, const char *filename, int debug)
+{
+ int err = 0;
+ PyObject *codeObj = Py_CompileString(msg, filename, Py_file_input);
+ if (codeObj == NULL) {
+ if (debug) {
+ /* format compile error */
+ PyObject *ptype=NULL,*pvalue=NULL,*ptraceback=NULL;
+ PyErr_Fetch(&ptype,&pvalue,&ptraceback);
+ PyErr_NormalizeException(&ptype,&pvalue,&ptraceback);
+
+ PyObject *tbmod = PyImport_ImportModule("traceback");
+ if (tbmod) {
+ PyObject *fmt = PyObject_GetAttrString(tbmod, "format_exception");
+ if (fmt && PyCallable_Check(fmt)) {
+ PyObject *tb_arg = ptraceback ? ptraceback : Py_None;
+ PyObject *exc_list = PyObject_CallFunctionObjArgs(fmt,
+ ptype?ptype:Py_None, pvalue?pvalue:Py_None, tb_arg, NULL);
+ if (exc_list) {
+ PyObject *sep = PyUnicode_FromString("");
+ PyObject *exc_str = PyUnicode_Join(sep, exc_list);
+ if (exc_str) { copy_pyunicode_to_msg(exc_str, msg); Py_DECREF(exc_str); }
+ Py_XDECREF(sep);
+ Py_DECREF(exc_list);
+ }
+ Py_XDECREF(fmt);
+ }
+ Py_DECREF(tbmod);
+ }
+ Py_XDECREF(ptype); Py_XDECREF(pvalue); Py_XDECREF(ptraceback);
+ }
+ err = 1;
+ }
+ else {
+ PyObject *mainmod = PyImport_AddModule("__main__"); // borrowed
+ PyObject *globals = PyModule_GetDict(mainmod); // borrowed
+
+ PyObject *res = PyEval_EvalCode((PyObject *)codeObj, globals, globals);
+ if (res == NULL) {
+ if (debug) {
+ /* runtime error formatting */
+ PyObject *ptype=NULL,*pvalue=NULL,*ptraceback=NULL;
+ PyErr_Fetch(&ptype,&pvalue,&ptraceback);
+ PyErr_NormalizeException(&ptype,&pvalue,&ptraceback);
+
+ PyObject *tbmod = PyImport_ImportModule("traceback");
+ if (tbmod) {
+ PyObject *fmt = PyObject_GetAttrString(tbmod, "format_exception");
+ if (fmt && PyCallable_Check(fmt)) {
+ PyObject *tb_arg = ptraceback ? ptraceback : Py_None;
+ PyObject *exc_list = PyObject_CallFunctionObjArgs(fmt,
+ ptype?ptype:Py_None, pvalue?pvalue:Py_None, tb_arg, NULL);
+ if (exc_list) {
+ PyObject *sep = PyUnicode_FromString("");
+ PyObject *exc_str = PyUnicode_Join(sep, exc_list);
+ if (exc_str) { copy_pyunicode_to_msg(exc_str, msg); Py_DECREF(exc_str); }
+ Py_XDECREF(sep);
+ Py_DECREF(exc_list);
+ }
+ Py_XDECREF(fmt);
+ }
+ Py_DECREF(tbmod);
+ }
+ Py_XDECREF(ptype); Py_XDECREF(pvalue); Py_XDECREF(ptraceback);
+ }
+ err = 1;
+ }
+ else {
+ Py_DECREF(res);
+ }
+ Py_DECREF(codeObj);
+ }
+ return err;
+}
+
+/*
+** Execute a python script "file" in the given thread's interpreter (interp).
+** A pre-allocated string "msg" must be given to create the python command
+** used for error handling. An integer "add" is needed to indicate if the
+** pyo server should be reboot or not.
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+** file : char *, filename to execute as a python script. The file is first
+** searched in the current working directory. If not found,
+** the module will try to open it as an absolute path.
+** msg : char *, pre-allocated string used to create the python command
+** used for error handling.
+** add, int, if positive, the commands in the file will be added to whatever
+** is already running in the pyo server. If 0, the server will be
+** shutdown and reboot before executing the file.
+**
+** returns 0 (no error), 1 (failed to open the file) or 2 (bad code in file).
+*/
+INLINE int pyo_exec_file(PyThreadState *interp, const char *file, char *msg, int add, int debug) {
+ int ok, isrel, err = 0;
+ PyObject *module;
+ PyEval_AcquireThread(interp);
+ sprintf(msg, "import os\n_isrel_ = True\n_ok_ = os.path.isfile('./%s')", file);
+ PyRun_SimpleString(msg);
+ sprintf(msg, "if not _ok_:\n _isrel_ = False\n _ok_ = os.path.isfile('%s')", file);
+ PyRun_SimpleString(msg);
+ module = PyImport_AddModule("__main__");
+ ok = PyLong_AsLong(PyObject_GetAttrString(module, "_ok_"));
+ isrel = PyLong_AsLong(PyObject_GetAttrString(module, "_isrel_"));
+ if (ok) {
+ if (!add) {
+ PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()");
+ PyRun_SimpleString("_s_.boot(newBuffer=False).start()");
+ }
+ if (isrel) {
+ sprintf(msg, "exec(open('./%s').read())", file);
+ } else {
+ sprintf(msg, "exec(open('%s').read())", file);
+ }
+ err = pyo_exec_code(msg, file, debug);
+ /* error code 1 means file not loaded
+ * so if we get an error in the code, we increment the error code by one
+ */
+ if (err) err++;
+ }
+ else {
+ err = 1;
+ }
+ PyEval_ReleaseThread(interp);
+ return err;
+}
+
+/*
+** Execute a python statement "msg" in the thread's interpreter "interp".
+** If "debug" is true, the statement will be executed in a try - except
+** block. The error message, if any, will be write back in the *msg
+** pointer and the function will return 1. If no error occured, the
+** function returned 0. If debug is false, the statement is executed
+** without any error checking (unsafe but faster).
+**
+** arguments:
+** interp : pointer, pointer to the targeted Python thread state.
+** msg : char *, pointer to a string containing the statement to execute.
+** In debug mode, if an error occured, the output log will
+** be write back in this string.
+** debug, int, if positive, the commands will be executed in a try-except
+** statement. If 0, there will be no error checking, which is
+** much faster.
+**
+** returns 0 (no error) or 1 (bad code in file).
+*/
+
+INLINE int pyo_exec_statement(PyThreadState *interp, char *msg, int debug) {
+ static long input_counter = 0; /* REPL-style line counter */
+
+ int err = 0;
+
+ PyEval_AcquireThread(interp);
+
+ char filename[64];
+ snprintf(filename, sizeof(filename), "", input_counter++);
+
+ err = pyo_exec_code(msg, filename, debug);
+ PyEval_ReleaseThread(interp);
+ return err;
+}
+
+/*
+** Functions to redirect Python's stdout in here
+*/
+/* --------- Message queue for stdout ---------- */
+
+typedef struct MsgNode {
+ char *msg;
+ struct MsgNode *next;
+} MsgNode;
+
+static MsgNode *g_msg_head = NULL;
+static MsgNode *g_msg_tail = NULL;
+static pthread_mutex_t g_msg_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Add message to queue (called by Python thread in cstdout_write) */
+static void pyo_enqueue_stdout(const char *s) {
+ if (!s) return;
+
+ MsgNode *node = (MsgNode*)malloc(sizeof(MsgNode));
+ if (!node) return;
+
+ node->msg = strdup(s); /* copy string */
+ node->next = NULL;
+
+ pthread_mutex_lock(&g_msg_mutex);
+ if (g_msg_tail) {
+ g_msg_tail->next = node;
+ g_msg_tail = node;
+ } else {
+ g_msg_head = g_msg_tail = node;
+ }
+ pthread_mutex_unlock(&g_msg_mutex);
+}
+
+/* Poll one message (called by host program, e.g. in update loop) */
+/* Returns 1 if a message was dequeued, 0 otherwise */
+INLINE int pyo_dequeue_stdout(char **out_msg) {
+ pthread_mutex_lock(&g_msg_mutex);
+ MsgNode *node = g_msg_head;
+ if (!node) {
+ pthread_mutex_unlock(&g_msg_mutex);
+ return 0;
+ }
+ g_msg_head = node->next;
+ if (!g_msg_head) g_msg_tail = NULL;
+ pthread_mutex_unlock(&g_msg_mutex);
+
+ *out_msg = node->msg; /* transfer ownership */
+ free(node);
+ return 1;
+}
+
+/* --------- cstdout module hooks ---------- */
+static PyObject* cstdout_write(PyObject *self, PyObject *args) {
+ const char *s;
+ (void)(self); /* unused */
+ (void)(args);
+ if (!PyArg_ParseTuple(args, "s", &s)) return NULL;
+
+ pyo_enqueue_stdout(s);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject* cstdout_flush(PyObject *self, PyObject *args) {
+ (void)(self); /* unused */
+ (void)(args);
+ Py_RETURN_NONE;
+}
+
+/* Module method table */
+static PyMethodDef cstdout_methods[] = {
+ {"write", cstdout_write, METH_VARARGS, "Write to C stdout"},
+ {"flush", cstdout_flush, METH_VARARGS, "Flush (no-op)"},
+ {NULL, NULL, 0, NULL}
+};
+
+/* Module definition */
+static struct PyModuleDef cstdout_moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "cstdout", /* m_name */
+ "C stdout/stderr receiver", /* m_doc */
+ -1, /* m_size */
+ cstdout_methods,
+ NULL, NULL, NULL, NULL
+};
+
+static PyObject* PyInit_cstdout(void) {
+ return PyModule_Create(&cstdout_moduledef);
+}
+
+/* Call this after Py_Initialize() while holding the GIL/threadstate. */
+static inline void redirect_stdout_to_c(void) {
+ /* Build and run Python code that sets sys.stdout/sys.stderr to call cstdout.write */
+ const char *code =
+ "import sys, cstdout\n"
+ "class _CStdout:\n"
+ " def write(self, s):\n"
+ " # ensure only strings are forwarded\n"
+ " if s is None:\n"
+ " return\n"
+ " cstdout.write(str(s))\n"
+ " def flush(self):\n"
+ " cstdout.flush()\n"
+ "sys.stdout = _CStdout()\n"
+ "sys.stderr = _CStdout()\n";
+
+ if (PyRun_SimpleString(code) != 0) {
+ PyErr_Print();
+ }
+}
+/*
+** Functions to redirect Python's stdout in here done
+*/
+
+#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
+}
+#endif
+
+#endif /* __m_pyo_h_ */
diff --git a/embedded/openframeworks/pyoExample/src/main.cpp b/embedded/openframeworks/pyoExample/src/main.cpp
new file mode 100644
index 000000000..4e6518d49
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/src/main.cpp
@@ -0,0 +1,17 @@
+#include "ofMain.h"
+#include "ofApp.h"
+
+//========================================================================
+int main( ){
+
+ //Use ofGLFWWindowSettings for more options like multi-monitor fullscreen
+ ofGLWindowSettings settings;
+ settings.setSize(1024, 768);
+ settings.windowMode = OF_WINDOW; //can also be OF_FULLSCREEN
+
+ auto window = ofCreateWindow(settings);
+
+ ofRunApp(window, std::make_shared());
+ ofRunMainLoop();
+
+}
diff --git a/embedded/openframeworks/pyoExample/src/ofApp.cpp b/embedded/openframeworks/pyoExample/src/ofApp.cpp
new file mode 100644
index 000000000..6e3e7a978
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/src/ofApp.cpp
@@ -0,0 +1,178 @@
+#include "ofApp.h"
+
+//--------------------------------------------------------------
+void ofApp::setup(){
+ // define audio properties
+ int sampleRate = 48000;
+ int bufferSize = 256;
+ int nChannels = 2;
+
+ exiting = false;
+
+ // font stuff
+ ofTrueTypeFont::setGlobalDpi(72);
+ font.load("DroidSansMono.ttf", 14, true, true);
+ interpreterRectHeight = font.stringHeight("q");
+ letterWidth = font.stringWidth("a");
+ interpreterRectHeight += (interpreterRectHeight - font.stringHeight("b"));
+ interpreterRectWidth = letterWidth * 80;
+ interpreterRectXPos = 100;
+ interpreterRectYPos = 100+(interpreterRectHeight*0.2);
+ cursorTimeStamp = ofGetElapsedTimeMillis();
+ showCursor = true;
+ cursorXPos = interpreterRectXPos + (letterWidth / 2);
+ cursorYPos = interpreterRectYPos + 2;
+ cursorHeight = interpreterRectHeight - 4;
+
+ // initialize a pyo server
+ pyo.setup(nChannels, nChannels, bufferSize, sampleRate);
+ // get the STDOUT to not print the message about wxPython
+ pyo.getStdout();
+ // set the debug state to active
+ pyo.setDebug(1);
+ // load a python file
+ int err = pyo.loadfile("data/scripts/stereoDelay.py", 0);
+ if (err) {
+ if (err == 1) {
+ std::cout << "could not load file\n";
+ }
+ else {
+ std::cout << pyo.getErrorMsg() << std::endl;
+ }
+ }
+ // initialize OpenFrameworks audio streaming channels
+ // soundStream.printDeviceList(); // Uncomment if you need to
+ // soundStream.setDeviceID(1); // change the audio device.
+ ofSoundStreamSettings settings;
+ settings.numOutputChannels = nChannels;
+ settings.sampleRate = sampleRate;
+ settings.bufferSize = bufferSize;
+ settings.numBuffers = 4;
+ settings.setOutListener(this);
+ soundStream.setup(settings);
+}
+
+//--------------------------------------------------------------
+void ofApp::update(){
+ unsigned long timeStamp = ofGetElapsedTimeMillis();
+ if (timeStamp - cursorTimeStamp > CURSORBLINK) {
+ showCursor = !showCursor;
+ cursorTimeStamp = timeStamp;
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::draw(){
+ ofSetColor(ofColor::white);
+ font.drawString("Interpreter\nType \"st_rev.stop()\" to stop the sound", interpreterRectXPos, interpreterRectYPos-(interpreterRectHeight*0.4)-cursorHeight);
+ ofDrawRectangle(interpreterRectXPos, interpreterRectYPos, interpreterRectWidth, interpreterRectHeight);
+
+ ofSetColor(ofColor::black);
+ if (showCursor) {
+ ofDrawLine(cursorXPos+(typeStr.size()*letterWidth), cursorYPos, cursorXPos+(typeStr.size()*letterWidth), cursorYPos+cursorHeight);
+ }
+ if (typeStr.size() > 0) {
+ font.drawString(typeStr, cursorXPos, cursorYPos+cursorHeight-2);
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::audioIn(ofSoundBuffer & buffer){
+ if (!exiting) pyo.fillin(&buffer[0]);
+}
+
+//--------------------------------------------------------------
+void ofApp::audioOut(ofSoundBuffer & buffer){
+ if (!exiting) pyo.process(&buffer[0]);
+}
+
+//--------------------------------------------------------------
+void ofApp::exit(){
+ while (pyo.isProcessing());
+ exiting = true;
+ ofExit();
+}
+
+//--------------------------------------------------------------
+void ofApp::keyPressed(int key){
+ if (key == OF_KEY_DEL || key == OF_KEY_BACKSPACE) {
+ typeStr = typeStr.substr(0, typeStr.length()-1);
+ }
+ else if (key == OF_KEY_RETURN) {
+ int err = pyo.exec(typeStr.c_str());
+ if (err) {
+ std::cout << pyo.getErrorMsg() << std::endl;
+ }
+ else {
+ // if there's anything to be printed in the console, it can be retrieved as a vector of strings
+ // with pyo.getStdout()
+ // if this vector contains any elements, then the Python interpreter has printed something
+ std::vector v = pyo.getStdout();
+ if (v.size() > 0) {
+ // the last item of the vector is a newline character, so we don't print it
+ // hence i < v.size()-1
+ for (size_t i = 0; i < v.size()-1; i++) {
+ std::cout << v[i] << std::endl;
+ }
+ }
+ }
+ typeStr.clear();
+ }
+ else if (key == OF_KEY_SHIFT || key == OF_KEY_CONTROL || key == OF_KEY_ALT
+ || (key >= 3680 && key <= 3685)) {
+ // do nothing on shift, ctrl or alt keys
+ }
+ else {
+ typeStr += char(key);
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::keyReleased(int key){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseMoved(int x, int y ){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseDragged(int x, int y, int button){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mousePressed(int x, int y, int button){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseReleased(int x, int y, int button){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseEntered(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseExited(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::windowResized(int w, int h){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::gotMessage(ofMessage msg){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::dragEvent(ofDragInfo dragInfo){
+
+}
diff --git a/embedded/openframeworks/pyoExample/src/ofApp.h b/embedded/openframeworks/pyoExample/src/ofApp.h
new file mode 100644
index 000000000..75ca5f9d2
--- /dev/null
+++ b/embedded/openframeworks/pyoExample/src/ofApp.h
@@ -0,0 +1,58 @@
+#ifndef __of_app_h
+#define __of_app_h
+
+#include "ofMain.h"
+#include "PyoClass.h"
+#include
+#include
+
+#define CURSORBLINK 500
+
+class ofApp : public ofBaseApp{
+
+ public:
+ void setup();
+ void update();
+ void draw();
+
+ void keyPressed(int key);
+ void keyReleased(int key);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased(int x, int y, int button);
+ void mouseEntered(int x, int y);
+ void mouseExited(int x, int y);
+ void windowResized(int w, int h);
+ void dragEvent(ofDragInfo dragInfo);
+ void gotMessage(ofMessage msg);
+
+ void audioIn(ofSoundBuffer & buffer);
+ void audioOut(ofSoundBuffer & buffer);
+
+ void exit();
+
+ ofTrueTypeFont font;
+
+ ofSoundStream soundStream;
+
+ Pyo pyo;
+
+ float interpreterRectHeight;
+ float interpreterRectWidth;
+ float interpreterRectXPos;
+ float interpreterRectYPos;
+
+ unsigned long cursorTimeStamp;
+ bool showCursor;
+ float cursorXPos;
+ float cursorYPos;
+ float cursorHeight;
+
+ bool exiting;
+
+ float letterWidth;
+ string typeStr;
+};
+
+#endif
diff --git a/embedded/puredata/Makefile b/embedded/puredata/Makefile
index 01285b614..088fdd9d6 100644
--- a/embedded/puredata/Makefile
+++ b/embedded/puredata/Makefile
@@ -1,474 +1,19 @@
-## Pd library template version 1.0.14
-# For instructions on how to use this template, see:
-# http://puredata.info/docs/developer/MakefileTemplate
-LIBRARY_NAME = pyo~
+# Makefile for pyo~
-# add your .c source files, one object per file, to the SOURCES
-# variable, help files will be included automatically, and for GUI
-# objects, the matching .tcl file too
-SOURCES = pyo~.c
+lib.name = pyo~
-# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will
-# be included automatically
-PDOBJECTS = pyo~.pd
+class.sources = pyo~.c
-# example patches and related files, in the 'examples' subfolder
-EXAMPLES =
+PKG_CONFIG := pkg-config
+PYTHON=python3
-# manuals and related files, in the 'manual' subfolder
-MANUAL = manual.txt
+PY_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PYTHON))
+PY_LIBS := $(shell $(PKG_CONFIG) --libs $(PYTHON))
-# if you want to include any other files in the source and binary tarballs,
-# list them here. This can be anything from header files, test patches,
-# documentation, etc. README.txt and LICENSE.txt are required and therefore
-# automatically included
-EXTRA_DIST = m_pyo.h
+cflags += $(PY_CFLAGS) -Wno-cast-function-type -I../
+ldlibs += $(PY_LIBS)
-# unit tests and related files here, in the 'unittests' subfolder
-UNITTESTS =
+datafiles = pyo-help.pd README.md
-
-
-#------------------------------------------------------------------------------#
-#
-# things you might need to edit if you are using other C libraries
-#
-#------------------------------------------------------------------------------#
-
-ALL_CFLAGS = -I"$(PD_INCLUDE)" -I.. -Wno-cast-function-type -Wno-unused-parameter $(shell python3-config --cflags)
-ALL_LDFLAGS = $(shell python3-config --ldflags)
-SHARED_LDFLAGS =
-ALL_LIBS = $(shell python3-config --libs)
-
-
-#------------------------------------------------------------------------------#
-#
-# you shouldn't need to edit anything below here, if we did it right :)
-#
-#------------------------------------------------------------------------------#
-
-# these can be set from outside without (usually) breaking the build
-CFLAGS = -Wall -W -g
-LDFLAGS =
-LIBS =
-
-# get library version from meta file
-LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd)
-
-ALL_CFLAGS += -DPD -DVERSION='"$(LIBRARY_VERSION)"'
-
-PD_INCLUDE = $(PD_PATH)/include/pd
-# where to install the library, overridden below depending on platform
-prefix = /usr/local
-libdir = $(prefix)/lib
-pkglibdir = $(libdir)/pd-externals
-objectsdir = $(pkglibdir)
-
-INSTALL = install
-INSTALL_PROGRAM = $(INSTALL) -p -m 644
-INSTALL_DATA = $(INSTALL) -p -m 644
-INSTALL_DIR = $(INSTALL) -p -m 755 -d
-
-ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \
- $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows)
-
-DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
-ORIGDIR=pd-$(LIBRARY_NAME:~=)_$(LIBRARY_VERSION)
-
-UNAME := $(shell uname -s)
-ifeq ($(UNAME),Darwin)
- CPU := $(shell uname -p)
- ifeq ($(CPU),arm) # iPhone/iPod Touch
- SOURCES += $(SOURCES_iphoneos)
- EXTENSION = pd_darwin
- SHARED_EXTENSION = dylib
- OS = iphoneos
- PD_PATH = /Applications/Pd-extended.app/Contents/Resources
- IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
- CC=$(IPHONE_BASE)/gcc
- CPP=$(IPHONE_BASE)/cpp
- CXX=$(IPHONE_BASE)/g++
- ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk
- IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6
- OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer
- ALL_CFLAGS := $(IPHONE_CFLAGS) $(ALL_CFLAGS)
- ALL_LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT)
- SHARED_LDFLAGS += -arch armv6 -dynamiclib -undefined dynamic_lookup $(ISYSROOT)
- ALL_LIBS += -lc $(LIBS_iphoneos)
- STRIP = strip -x
- DISTBINDIR=$(DISTDIR)-$(OS)
- else # Mac OS X
- SOURCES += $(SOURCES_macosx)
- EXTENSION = pd_darwin
- SHARED_EXTENSION = dylib
- OS = macosx
- PD_PATH = /Applications/Pd-extended.app/Contents/Resources
- OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast
-# build universal 32-bit on 10.4 and 32/64 on newer
- ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8)
- FAT_FLAGS = -arch ppc -arch i386 -mmacosx-version-min=10.4
- else
- SOURCES += $(SOURCES_iphoneos)
-# Starting with Xcode 4.0, the PowerPC compiler is not installed by default
- ifeq ($(wildcard /usr/llvm-gcc-4.2/libexec/gcc/powerpc*), )
- FAT_FLAGS = -arch i386 -arch x86_64 -mmacosx-version-min=10.5
- else
- FAT_FLAGS = -arch ppc -arch i386 -arch x86_64 -mmacosx-version-min=10.4
- endif
- endif
- ALL_CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include
- # if the 'pd' binary exists, check the linking against it to aid with stripping
- BUNDLE_LOADER = $(shell test ! -e $(PD_PATH)/bin/pd || echo -bundle_loader $(PD_PATH)/bin/pd)
- ALL_LDFLAGS += $(FAT_FLAGS) -headerpad_max_install_names -bundle $(BUNDLE_LOADER) \
- -undefined dynamic_lookup -L/sw/lib
- SHARED_LDFLAGS += $(FAT_FLAGS) -dynamiclib -undefined dynamic_lookup \
- -install_name @loader_path/$(SHARED_LIB) -compatibility_version 1 -current_version 1.0
- ALL_LIBS += -lc $(LIBS_macosx)
- STRIP = strip -x
- DISTBINDIR=$(DISTDIR)-$(OS)
-# install into ~/Library/Pd on Mac OS X since /usr/local isn't used much
- pkglibdir=$(HOME)/Library/Pd
- endif
-endif
-# Tho Android uses Linux, we use this fake uname to provide an easy way to
-# setup all this things needed to cross-compile for Android using the NDK
-ifeq ($(UNAME),ANDROID)
- CPU := arm
- SOURCES += $(SOURCES_android)
- EXTENSION = so
- SHARED_EXTENSION = so
- OS = android
- PD_PATH = /usr
- NDK_BASE := /usr/local/android-ndk
- NDK_PLATFORM_LEVEL ?= 5
- NDK_ABI=arm
- NDK_SYSROOT=$(NDK_BASE)/platforms/android-$(NDK_PLATFORM_LEVEL)/arch-$(NDK_ABI)
- NDK_UNAME := $(shell uname -s | tr '[A-Z]' '[a-z]')
- NDK_COMPILER_VERSION=4.6
- NDK_TOOLCHAIN=$(wildcard \
- $(NDK_BASE)/toolchains/$(NDK_ABI)*-$(NDK_COMPILER_VERSION)/prebuilt/$(NDK_UNAME)-x86)
- CC := $(wildcard $(NDK_TOOLCHAIN)/bin/*-linux-android*-gcc) --sysroot=$(NDK_SYSROOT)
- OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
- CFLAGS +=
- LDFLAGS += -rdynamic -shared
- SHARED_LDFLAGS += -Wl,-soname,$(SHARED_LIB) -shared
- LIBS += -lc $(LIBS_android)
- STRIP := $(wildcard $(NDK_TOOLCHAIN)/bin/$(NDK_ABI)-linux-android*-strip) \
- --strip-unneeded -R .note -R .comment
- DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
-endif
-ifeq ($(UNAME),Linux)
- CPU := $(shell uname -m)
- EXTENSION = pd_linux
- ifeq ($(findstring arm,$(CPU)),arm)
- EXTENSION = l_arm
- endif
- ifeq ($(CPU),i386)
- EXTENSION = l_i386
- endif
- ifeq ($(CPU),i486)
- EXTENSION = l_i386
- endif
- ifeq ($(CPU),i586)
- EXTENSION = l_i386
- endif
- ifeq ($(CPU),i686)
- EXTENSION = l_i386
- endif
- ifeq ($(CPU),amd64)
- # amd64 and ia64 aren't the same thing, but that's how its done in pd...
- EXTENSION = l_ia64
- endif
- ifeq ($(CPU),x86_64)
- # x86_64 and ia64 aren't the same thing, but that's how its done in pd...
- EXTENSION = l_ia64
- endif
- SOURCES += $(SOURCES_linux)
- SHARED_EXTENSION = so
- OS = linux
- PD_PATH = /usr
- OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
- ALL_CFLAGS += -fPIC
- ALL_LDFLAGS += -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
- SHARED_LDFLAGS += -Wl,-soname,$(SHARED_LIB) -shared
- ALL_LIBS += -lc $(LIBS_linux)
- STRIP = strip --strip-unneeded -R .note -R .comment
- DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
-endif
-ifeq ($(UNAME),GNU)
- # GNU/Hurd, should work like GNU/Linux for basically all externals
- CPU := $(shell uname -m)
- SOURCES += $(SOURCES_linux)
- EXTENSION = pd_linux
- SHARED_EXTENSION = so
- OS = linux
- PD_PATH = /usr
- OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
- ALL_CFLAGS += -fPIC
- ALL_LDFLAGS += -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
- SHARED_LDFLAGS += -shared -Wl,-soname,$(SHARED_LIB)
- ALL_LIBS += -lc $(LIBS_linux)
- STRIP = strip --strip-unneeded -R .note -R .comment
- DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
-endif
-ifeq ($(UNAME),GNU/kFreeBSD)
- # Debian GNU/kFreeBSD, should work like GNU/Linux for basically all externals
- CPU := $(shell uname -m)
- SOURCES += $(SOURCES_linux)
- EXTENSION = pd_linux
- SHARED_EXTENSION = so
- OS = linux
- PD_PATH = /usr
- OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
- ALL_CFLAGS += -fPIC
- ALL_LDFLAGS += -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
- SHARED_LDFLAGS += -shared -Wl,-soname,$(SHARED_LIB)
- ALL_LIBS += -lc $(LIBS_linux)
- STRIP = strip --strip-unneeded -R .note -R .comment
- DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
-endif
-ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME)))
- CPU := $(shell uname -m)
- SOURCES += $(SOURCES_cygwin)
- EXTENSION = dll
- SHARED_EXTENSION = dll
- OS = cygwin
- PD_PATH = $(shell cygpath $$PROGRAMFILES)/pd
- OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
- ALL_CFLAGS +=
- ALL_LDFLAGS += -rdynamic -shared -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin"
- SHARED_LDFLAGS += -shared -Wl,-soname,$(SHARED_LIB)
- ALL_LIBS += -lc -lpd $(LIBS_cygwin)
- STRIP = strip --strip-unneeded -R .note -R .comment
- DISTBINDIR=$(DISTDIR)-$(OS)
-endif
-ifeq (MINGW,$(findstring MINGW,$(UNAME)))
- CPU := $(shell uname -m)
- SOURCES += $(SOURCES_windows)
- EXTENSION = dll
- SHARED_EXTENSION = dll
- OS = windows
- PD_PATH = $(shell cd "$$PROGRAMFILES/pd" && pwd)
- # MinGW doesn't seem to include cc so force gcc
- CC=gcc
- OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer
- ALL_CFLAGS += -mms-bitfields
- ALL_LDFLAGS += -s -shared -Wl,--enable-auto-import
- SHARED_LDFLAGS += -shared
- ALL_LIBS += -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" -L"$(PD_PATH)/obj" \
- -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 -liberty $(LIBS_windows)
- STRIP = strip --strip-unneeded -R .note -R .comment
- DISTBINDIR=$(DISTDIR)-$(OS)
-endif
-
-# in case somebody manually set the HELPPATCHES above
-HELPPATCHES ?= $(SOURCES:.c=-help.pd) $(PDOBJECTS:.pd=-help.pd)
-
-ALL_CFLAGS := $(ALL_CFLAGS) $(CFLAGS) $(OPT_CFLAGS)
-ALL_LDFLAGS := $(LDFLAGS) $(ALL_LDFLAGS)
-ALL_LIBS := $(LIBS) $(ALL_LIBS)
-
-SHARED_SOURCE ?= $(wildcard lib$(LIBRARY_NAME).c)
-SHARED_HEADER ?= $(shell test ! -e $(LIBRARY_NAME).h || echo $(LIBRARY_NAME).h)
-SHARED_LIB ?= $(SHARED_SOURCE:.c=.$(SHARED_EXTENSION))
-SHARED_TCL_LIB = $(wildcard lib$(LIBRARY_NAME).tcl)
-
-.PHONY = install libdir_install single_install install-doc install-examples install-manual install-unittests clean distclean dist etags $(LIBRARY_NAME)
-
-all: $(SOURCES:.c=.$(EXTENSION)) $(SHARED_LIB)
-
-%.o: %.c
- $(CC) $(ALL_CFLAGS) -o "$*.o" -c "$*.c"
-
-%.$(EXTENSION): %.o $(SHARED_LIB)
- $(CC) $(ALL_LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(ALL_LIBS) $(SHARED_LIB)
- chmod a-x "$*.$(EXTENSION)"
-
-# this links everything into a single binary file
-$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o lib$(LIBRARY_NAME).o
- $(CC) $(ALL_LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) \
- $(LIBRARY_NAME).o lib$(LIBRARY_NAME).o $(ALL_LIBS)
- chmod a-x $(LIBRARY_NAME).$(EXTENSION)
-
-$(SHARED_LIB): $(SHARED_SOURCE:.c=.o)
- $(CC) $(SHARED_LDFLAGS) -o $(SHARED_LIB) $(SHARED_SOURCE:.c=.o) $(ALL_LIBS)
-
-install: libdir_install
-
-# The meta and help files are explicitly installed to make sure they are
-# actually there. Those files are not optional, then need to be there.
-libdir_install: $(SOURCES:.c=.$(EXTENSION)) $(SHARED_LIB) install-doc install-examples install-manual install-unittests
- $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd \
- $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- test -z "$(strip $(SOURCES))" || (\
- $(INSTALL_PROGRAM) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \
- $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION))))
- test -z "$(strip $(SHARED_LIB))" || \
- $(INSTALL_DATA) $(SHARED_LIB) \
- $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- test -z "$(strip $(wildcard $(SOURCES:.c=.tcl)))" || \
- $(INSTALL_DATA) $(wildcard $(SOURCES:.c=.tcl)) \
- $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- test -z "$(strip $(PDOBJECTS))" || \
- $(INSTALL_DATA) $(PDOBJECTS) \
- $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- test -z "$(strip $(SHARED_TCL_LIB))" || \
- $(INSTALL_DATA) $(SHARED_TCL_LIB) \
- $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
-
-# install library linked as single binary
-single_install: $(LIBRARY_NAME) install-doc install-examples install-manual install-unittests
- $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- $(INSTALL_PROGRAM) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION)
-
-install-doc:
- $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- test -z "$(strip $(SOURCES) $(PDOBJECTS))" || \
- $(INSTALL_DATA) $(HELPPATCHES) \
- $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
- $(INSTALL_DATA) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt
- $(INSTALL_DATA) LICENSE.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/LICENSE.txt
-
-install-examples:
- test -z "$(strip $(EXAMPLES))" || \
- $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \
- for file in $(EXAMPLES); do \
- $(INSTALL_DATA) examples/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples; \
- done
-
-install-manual:
- test -z "$(strip $(MANUAL))" || \
- $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual && \
- for file in $(MANUAL); do \
- $(INSTALL_DATA) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \
- done
-
-install-unittests:
- test -z "$(strip $(UNITTESTS))" || \
- $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/unittests && \
- for file in $(UNITTESTS); do \
- $(INSTALL_DATA) unittests/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/unittests; \
- done
-
-clean:
- -rm -f -- $(SOURCES:.c=.o) $(SOURCES_LIB:.c=.o) $(SHARED_SOURCE:.c=.o)
- -rm -f -- $(SOURCES:.c=.$(EXTENSION))
- -rm -f -- $(LIBRARY_NAME).o
- -rm -f -- $(LIBRARY_NAME).$(EXTENSION)
- -rm -f -- $(SHARED_LIB)
-
-distclean: clean
- -rm -f -- $(DISTBINDIR).tar.gz
- -rm -rf -- $(DISTBINDIR)
- -rm -f -- $(DISTDIR).tar.gz
- -rm -rf -- $(DISTDIR)
- -rm -f -- $(ORIGDIR).tar.gz
- -rm -rf -- $(ORIGDIR)
-
-
-$(DISTBINDIR):
- $(INSTALL_DIR) $(DISTBINDIR)
-
-libdir: all $(DISTBINDIR)
- $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR)
- $(INSTALL_DATA) $(SOURCES) $(SHARED_SOURCE) $(SHARED_HEADER) $(DISTBINDIR)
- $(INSTALL_DATA) $(HELPPATCHES) $(DISTBINDIR)
- test -z "$(strip $(EXTRA_DIST))" || \
- $(INSTALL_DATA) $(EXTRA_DIST) $(DISTBINDIR)
-# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR)
-
-$(DISTDIR):
- $(INSTALL_DIR) $(DISTDIR)
-
-$(ORIGDIR):
- $(INSTALL_DIR) $(ORIGDIR)
-
-dist: $(DISTDIR)
- $(INSTALL_DATA) Makefile $(DISTDIR)
- $(INSTALL_DATA) README.txt $(DISTDIR)
- $(INSTALL_DATA) LICENSE.txt $(DISTDIR)
- $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTDIR)
- test -z "$(strip $(ALLSOURCES))" || \
- $(INSTALL_DATA) $(ALLSOURCES) $(DISTDIR)
- test -z "$(strip $(wildcard $(ALLSOURCES:.c=.tcl)))" || \
- $(INSTALL_DATA) $(wildcard $(ALLSOURCES:.c=.tcl)) $(DISTDIR)
- test -z "$(strip $(wildcard $(LIBRARY_NAME).c))" || \
- $(INSTALL_DATA) $(LIBRARY_NAME).c $(DISTDIR)
- test -z "$(strip $(SHARED_HEADER))" || \
- $(INSTALL_DATA) $(SHARED_HEADER) $(DISTDIR)
- test -z "$(strip $(SHARED_SOURCE))" || \
- $(INSTALL_DATA) $(SHARED_SOURCE) $(DISTDIR)
- test -z "$(strip $(SHARED_TCL_LIB))" || \
- $(INSTALL_DATA) $(SHARED_TCL_LIB) $(DISTDIR)
- test -z "$(strip $(PDOBJECTS))" || \
- $(INSTALL_DATA) $(PDOBJECTS) $(DISTDIR)
- test -z "$(strip $(HELPPATCHES))" || \
- $(INSTALL_DATA) $(HELPPATCHES) $(DISTDIR)
- test -z "$(strip $(EXTRA_DIST))" || \
- $(INSTALL_DATA) $(EXTRA_DIST) $(DISTDIR)
- test -z "$(strip $(EXAMPLES))" || \
- $(INSTALL_DIR) $(DISTDIR)/examples && \
- for file in $(EXAMPLES); do \
- $(INSTALL_DATA) examples/$$file $(DISTDIR)/examples; \
- done
- test -z "$(strip $(MANUAL))" || \
- $(INSTALL_DIR) $(DISTDIR)/manual && \
- for file in $(MANUAL); do \
- $(INSTALL_DATA) manual/$$file $(DISTDIR)/manual; \
- done
- test -z "$(strip $(UNITTESTS))" || \
- $(INSTALL_DIR) $(DISTDIR)/unittests && \
- for file in $(UNITTESTS); do \
- $(INSTALL_DATA) unittests/$$file $(DISTDIR)/unittests; \
- done
- tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR)
-
-# make a Debian source package
-dpkg-source:
- debclean
- make distclean dist
- mv $(DISTDIR) $(ORIGDIR)
- tar --exclude-vcs -czpf ../$(ORIGDIR).orig.tar.gz $(ORIGDIR)
- rm -f -- $(DISTDIR).tar.gz
- rm -rf -- $(DISTDIR) $(ORIGDIR)
- cd .. && dpkg-source -b $(LIBRARY_NAME)
-
-etags: TAGS
-
-TAGS: $(wildcard $(PD_INCLUDE)/*.h) $(SOURCES) $(SHARED_SOURCE) $(SHARED_HEADER)
- etags $(wildcard $(PD_INCLUDE)/*.h)
- etags -a *.h $(SOURCES) $(SHARED_SOURCE) $(SHARED_HEADER)
- etags -a --language=none --regex="/proc[ \t]+\([^ \t]+\)/\1/" *.tcl
-
-showsetup:
- @echo "CC: $(CC)"
- @echo "CFLAGS: $(CFLAGS)"
- @echo "LDFLAGS: $(LDFLAGS)"
- @echo "LIBS: $(LIBS)"
- @echo "ALL_CFLAGS: $(ALL_CFLAGS)"
- @echo "ALL_LDFLAGS: $(ALL_LDFLAGS)"
- @echo "ALL_LIBS: $(ALL_LIBS)"
- @echo "PD_INCLUDE: $(PD_INCLUDE)"
- @echo "PD_PATH: $(PD_PATH)"
- @echo "objectsdir: $(objectsdir)"
- @echo "LIBRARY_NAME: $(LIBRARY_NAME)"
- @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)"
- @echo "SOURCES: $(SOURCES)"
- @echo "EXTENSION: $(EXTENSION)"
- @echo "SHARED_HEADER: $(SHARED_HEADER)"
- @echo "SHARED_SOURCE: $(SHARED_SOURCE)"
- @echo "SHARED_LIB: $(SHARED_LIB)"
- @echo "SHARED_TCL_LIB: $(SHARED_TCL_LIB)"
- @echo "PDOBJECTS: $(PDOBJECTS)"
- @echo "ALLSOURCES: $(ALLSOURCES)"
- @echo "ALLSOURCES TCL: $(wildcard $(ALLSOURCES:.c=.tcl))"
- @echo "UNAME: $(UNAME)"
- @echo "CPU: $(CPU)"
- @echo "pkglibdir: $(pkglibdir)"
- @echo "DISTDIR: $(DISTDIR)"
- @echo "ORIGDIR: $(ORIGDIR)"
- @echo "NDK_TOOLCHAIN: $(NDK_TOOLCHAIN)"
- @echo "NDK_BASE: $(NDK_BASE)"
- @echo "NDK_SYSROOT: $(NDK_SYSROOT)"
+PDLIBBUILDER_DIR=../pd-lib-builder/
+include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
diff --git a/embedded/puredata/pyo~-help.pd b/embedded/puredata/pyo~-help.pd
index 285ee0637..442bf1fea 100644
--- a/embedded/puredata/pyo~-help.pd
+++ b/embedded/puredata/pyo~-help.pd
@@ -1,65 +1,80 @@
-#N canvas 82 83 935 383 10;
-#X obj 28 118 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
--1;
-#X obj 144 118 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
-1;
-#X obj 13 244 *~;
-#X obj 51 244 *~;
-#X obj 13 266 dac~;
-#X obj 72 223 hsl 128 15 0 1 0 1 empty empty master_gain -2 -8 0 10
--262144 -1 -1 9000 1;
-#X obj 274 233 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
-1;
-#X msg 274 253 \; pd dsp \$1;
-#X obj 13 69 r msg_to_pyo;
-#X text 161 117 play;
-#X obj 13 176 pyo~ 2;
-#X text 66 178 argument sets the number of audio ins/outs (defaults
-to 2).;
-#N canvas 596 170 824 583 resonators_example 0;
-#X obj 35 103 hsl 128 15 0.01 30 1 0 empty empty resonance -2 -8 0
-10 -262144 -1 -1 11100 0;
-#X floatatom 32 120 5 0 0 0 - - -;
-#X obj 21 530 s msg_to_pyo;
-#X obj 98 387 hsl 128 15 0 1 0 0 empty empty delay_time -2 -8 0 10
--262144 -1 -1 3100 0;
-#X floatatom 95 403 5 0 0 0 - - -;
-#X msg 95 418 value deltime \$1;
-#X obj 111 454 hsl 128 15 0 1 0 0 empty empty delay_feedback -2 -8
-0 10 -262144 -1 -1 10900 0;
-#X floatatom 108 470 5 0 0 0 - - -;
-#X msg 108 485 value delfeed \$1;
-#X msg 32 136 value reson \$1;
-#X obj 52 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 2600 0;
-#X obj 71 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 3500 0;
-#X obj 91 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 2000 0;
-#X obj 110 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 5700 0;
-#X obj 130 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 4800 0;
-#X obj 149 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 6300 0;
-#X obj 169 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 1400 0;
-#X obj 189 177 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 -262144
--1 -1 700 0;
-#X msg 52 306 value freqs \$1 \$2 \$3 \$4 \$5 \$6 \$7 \$8;
-#X obj 52 264 bondo 8 ____________;
-#X obj 52 285 pack f f f f f f f f;
-#X msg 21 66 read examples/resonators.py;
-#X msg 73 351 read -a examples/resonators_add_delays.py;
-#X text 52 159 resonator frequencies;
-#X text 20 30 load pyo processing file (audio signal must be given
-in pyo~ inputs).;
-#X text 168 103 adjust resonance in seconds;
-#X text 214 209 play with resonator frequencies;
-#X text 337 352 add a stereo delay taking resonator outputs as its
-input;
-#X text 234 387 adjust delay time;
-#X text 246 453 adjust delay feedback;
+#N canvas 959 203 954 737 10;
+#X msg 215 132 \; pd dsp \$1;
+#X obj 22 147 r msg_to_pyo;
+#X obj 13 186 pyo~ 2;
+#N canvas 744 125 824 690 resonators_example 0;
+#X obj 35 96 hsl 128 15 0.01 30 1 0 empty empty resonance -2 -8 0 10 #fcfcfc #000000 #000000 0 0;
+#X floatatom 32 116 5 0 0 0 - - - 0;
+#X obj 21 618 s msg_to_pyo;
+#X obj 98 450 hsl 128 15 0 1 0 0 empty empty delay_time -2 -8 0 10 #fcfcfc #000000 #000000 0 0;
+#X floatatom 95 472 5 0 0 0 - - - 0;
+#X msg 95 494 value deltime \$1;
+#X obj 111 530 hsl 128 15 0 1 0 0 empty empty delay_feedback -2 -8 0 10 #fcfcfc #000000 #000000 0 0;
+#X floatatom 108 550 5 0 0 0 - - - 0;
+#X msg 108 573 value delfeed \$1;
+#X msg 32 139 value reson \$1;
+#X obj 52 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 75 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 99 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 122 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 146 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 169 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 193 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X obj 217 197 vsl 15 80 50 500 0 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 0;
+#X msg 73 387 read -a examples/resonators_add_delays.py;
+#X text 52 176 resonator frequencies;
+#X text 20 20 load pyo processing file (audio signal must be given in pyo~ inputs).;
+#X text 168 96 adjust resonance in seconds;
+#X text 242 229 play with resonator frequencies;
+#X text 234 450 adjust delay time;
+#X text 246 529 adjust delay feedback;
+#X text 332 374 add a stereo delay taking resonator outputs as its input (the -a flat appends the script to the already loaded one(s));
+#X msg 21 56 read examples/resonators.py;
+#X obj 52 318 list prepend value freqs;
+#X obj 52 341 list trim;
+#N canvas 965 549 670 224 hot_pack 0;
+#X obj 33 25 inlet;
+#X obj 33 128 pack f f f f f f f f, f 79;
+#X obj 73 25 inlet;
+#X obj 73 48 t b f;
+#X obj 140 25 inlet;
+#X obj 140 48 t b f;
+#X obj 207 25 inlet;
+#X obj 207 48 t b f;
+#X obj 275 25 inlet;
+#X obj 275 48 t b f;
+#X obj 342 25 inlet;
+#X obj 342 48 t b f;
+#X obj 409 25 inlet;
+#X obj 409 48 t b f;
+#X obj 477 25 inlet;
+#X obj 477 48 t b f;
+#X obj 33 151 outlet;
+#X connect 0 0 1 0;
+#X connect 1 0 16 0;
+#X connect 2 0 3 0;
+#X connect 3 0 1 0;
+#X connect 3 1 1 1;
+#X connect 4 0 5 0;
+#X connect 5 0 1 0;
+#X connect 5 1 1 2;
+#X connect 6 0 7 0;
+#X connect 7 0 1 0;
+#X connect 7 1 1 3;
+#X connect 8 0 9 0;
+#X connect 9 0 1 0;
+#X connect 9 1 1 4;
+#X connect 10 0 11 0;
+#X connect 11 0 1 0;
+#X connect 11 1 1 5;
+#X connect 12 0 13 0;
+#X connect 13 0 1 0;
+#X connect 13 1 1 6;
+#X connect 14 0 15 0;
+#X connect 15 0 1 0;
+#X connect 15 1 1 7;
+#X restore 52 291 pd hot_pack;
+#X f 28;
#X connect 0 0 1 0;
#X connect 1 0 9 0;
#X connect 3 0 4 0;
@@ -69,39 +84,30 @@ input;
#X connect 7 0 8 0;
#X connect 8 0 2 0;
#X connect 9 0 2 0;
-#X connect 10 0 19 0;
-#X connect 11 0 19 1;
-#X connect 12 0 19 2;
-#X connect 13 0 19 3;
-#X connect 14 0 19 4;
-#X connect 15 0 19 5;
-#X connect 16 0 19 6;
-#X connect 17 0 19 7;
+#X connect 10 0 29 0;
+#X connect 11 0 29 1;
+#X connect 12 0 29 2;
+#X connect 13 0 29 3;
+#X connect 14 0 29 4;
+#X connect 15 0 29 5;
+#X connect 16 0 29 6;
+#X connect 17 0 29 7;
#X connect 18 0 2 0;
-#X connect 19 0 20 0;
-#X connect 19 1 20 1;
-#X connect 19 2 20 2;
-#X connect 19 3 20 3;
-#X connect 19 4 20 4;
-#X connect 19 5 20 5;
-#X connect 19 6 20 6;
-#X connect 19 7 20 7;
-#X connect 20 0 18 0;
-#X connect 21 0 2 0;
-#X connect 22 0 2 0;
-#X restore 531 36 pd resonators_example;
-#X msg 553 171 clear;
-#X obj 531 199 s msg_to_pyo;
-#X text 598 172 shutdown and reboot the server;
-#N canvas 3 83 450 300 synthesis_example 0;
+#X connect 26 0 2 0;
+#X connect 27 0 28 0;
+#X connect 28 0 2 0;
+#X connect 29 0 27 0;
+#X restore 17 327 pd resonators_example;
+#X msg 39 462 clear;
+#X obj 17 490 s msg_to_pyo;
+#X text 84 463 shutdown and reboot the server;
+#N canvas 3 83 454 371 synthesis_example 0;
#N canvas 1 75 375 358 choose_sines 0;
#X obj 57 77 metro 125;
-#X obj 78 109 hsl 128 15 0 36 0 0 empty empty empty -2 -8 0 10 -262144
--1 -1 4233 1;
-#X obj 127 144 hsl 128 15 0 36 0 0 empty empty empty -2 -8 0 10 -262144
--1 -1 4233 1;
-#X floatatom 75 125 5 0 0 0 - - -;
-#X floatatom 124 160 5 0 0 0 - - -;
+#X obj 78 109 hsl 128 15 0 36 0 0 empty empty empty -2 -8 0 10 #fcfcfc #000000 #000000 0 1;
+#X obj 127 144 hsl 128 15 0 36 0 0 empty empty empty -2 -8 0 10 #fcfcfc #000000 #000000 0 1;
+#X floatatom 75 125 5 0 0 0 - - - 0;
+#X floatatom 124 160 5 0 0 0 - - - 0;
#X obj 124 209 +;
#X obj 124 179 t b f;
#X obj 75 144 t f f;
@@ -136,34 +142,31 @@ input;
#X connect 18 0 1 0;
#X connect 18 0 2 0;
#X connect 19 0 0 1;
-#X restore 53 130 pd choose_sines;
-#X obj 41 246 s msg_to_pyo;
-#X obj 53 96 tgl 25 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1
-;
-#X obj 67 172 hsl 128 15 0 0.25 0 0 empty empty brightness -2 -8 0
-10 -262144 -1 -1 8600 0;
-#X floatatom 64 188 5 0 0 0 - - -;
-#X msg 64 203 value feed \$1;
-#X obj 158 75 hsl 128 15 50 500 0 0 empty empty metro_speed -2 -8 0
-10 -262144 -1 -1 1800 1;
-#X floatatom 155 93 5 0 0 0 - - -;
-#X floatatom 222 186 5 0 0 0 - - -;
-#X msg 222 216 set amp.fadeout \$1;
-#X msg 281 184 set amp.dur \$1;
-#X floatatom 281 165 5 0 0 0 - - -;
-#X obj 222 132 * 0.001;
-#X obj 221 111 - 20;
-#X obj 222 165 - 0.005;
-#X msg 41 33 read -a examples/random_waves.py;
-#X text 54 80 GO!;
+#X restore 53 152 pd choose_sines;
+#X obj 41 282 s msg_to_pyo;
+#X obj 53 120 tgl 25 0 empty empty empty 17 7 0 10 #fcfcfc #000000 #000000 0 1;
+#X obj 67 194 hsl 128 15 0 0.25 0 0 empty empty brightness -2 -8 0 10 #fcfcfc #000000 #000000 0 0;
+#X floatatom 64 216 5 0 0 0 - - - 0;
+#X msg 64 239 value feed \$1;
+#X obj 143 75 hsl 128 15 50 500 0 0 empty empty metro_speed -2 -8 0 10 #fcfcfc #000000 #000000 0 1;
+#X floatatom 140 93 5 0 0 0 - - - 0;
+#X floatatom 222 211 5 0 0 0 - - - 0;
+#X msg 222 238 set amp.fadeout \$1;
+#X msg 281 209 set amp.dur \$1;
+#X floatatom 281 187 5 0 0 0 - - - 0;
+#X obj 222 154 * 0.001;
+#X obj 221 133 - 20;
+#X obj 222 187 - 0.005;
+#X text 54 102 GO!;
+#X obj 140 115 t f f;
+#X msg 41 33 read examples/random_waves.py;
#X connect 0 0 1 0;
#X connect 2 0 0 0;
#X connect 3 0 4 0;
#X connect 4 0 5 0;
#X connect 5 0 1 0;
#X connect 6 0 7 0;
-#X connect 7 0 0 1;
-#X connect 7 0 13 0;
+#X connect 7 0 16 0;
#X connect 8 0 9 0;
#X connect 9 0 1 0;
#X connect 10 0 1 0;
@@ -172,150 +175,110 @@ input;
#X connect 12 0 14 0;
#X connect 13 0 12 0;
#X connect 14 0 8 0;
-#X connect 15 0 1 0;
-#X restore 531 60 pd synthesis_example;
-#N canvas 2 85 629 295 loop_soundfile 0;
+#X connect 16 0 0 1;
+#X connect 16 1 13 0;
+#X connect 17 0 1 0;
+#X restore 17 351 pd synthesis_example;
+#N canvas 420 245 629 295 loop_soundfile 0;
#X obj 43 42 openpanel;
#X obj 43 64 t a b;
-#X obj 43 172 soundfiler;
-#X obj 236 80 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
--1;
+#X obj 43 188 soundfiler;
#X obj 43 20 inlet open file;
-#X obj 419 16 inlet start;
-#X obj 236 52 sel 1;
+#X obj 236 23 inlet start;
+#X obj 236 72 sel 1;
#X obj 236 205 outlet~;
-#X obj 366 163 spigot 1;
-#X obj 43 128 pack s s s;
-#X obj 76 86 symbol \$0-table_l;
-#X obj 110 106 symbol \$0-table_r;
-#X msg 43 150 read -resize \$1 \$2 \$3;
-#X obj 44 200 table \$0-table_l;
-#X obj 44 223 table \$0-table_r;
+#X obj 366 158 spigot 1;
+#X obj 43 144 pack s s s;
+#X obj 71 95 symbol \$0-table_l;
+#X obj 100 119 symbol \$0-table_r;
+#X msg 43 166 read -resize \$1 \$2 \$3;
+#X obj 44 216 table \$0-table_l;
+#X obj 44 239 table \$0-table_r;
#X obj 442 206 outlet~;
#X obj 236 140 tabplay~ \$0-table_l;
#X obj 443 140 tabplay~ \$0-table_r;
+#X obj 236 49 t f f;
+#X obj 236 95 t b b;
+#X obj 366 184 t b b;
#X connect 0 0 1 0;
-#X connect 1 0 9 0;
+#X connect 1 0 8 0;
+#X connect 1 1 9 0;
#X connect 1 1 10 0;
-#X connect 1 1 11 0;
-#X connect 3 0 16 0;
-#X connect 3 0 17 0;
-#X connect 4 0 0 0;
-#X connect 5 0 6 0;
-#X connect 5 0 8 1;
-#X connect 6 0 3 0;
-#X connect 8 0 16 0;
-#X connect 8 0 17 0;
-#X connect 9 0 12 0;
-#X connect 10 0 9 1;
-#X connect 11 0 9 2;
-#X connect 12 0 2 0;
-#X connect 16 0 7 0;
-#X connect 16 1 8 0;
-#X connect 17 0 15 0;
-#X restore 28 136 pd loop_soundfile;
-#X text 44 118 load file;
+#X connect 3 0 0 0;
+#X connect 4 0 17 0;
+#X connect 5 0 18 0;
+#X connect 7 0 19 0;
+#X connect 8 0 11 0;
+#X connect 9 0 8 1;
+#X connect 10 0 8 2;
+#X connect 11 0 2 0;
+#X connect 15 0 6 0;
+#X connect 15 1 7 0;
+#X connect 16 0 14 0;
+#X connect 17 0 5 0;
+#X connect 17 1 7 1;
+#X connect 18 0 15 0;
+#X connect 18 1 16 0;
+#X connect 19 0 15 0;
+#X connect 19 1 16 0;
+#X restore 12 116 pd loop_soundfile;
#N canvas 302 92 532 224 conv_reverb_example 0;
-#X obj 24 110 hsl 128 15 0 1 0 0 empty empty balance -2 -8 0 10 -262144
--1 -1 8400 0;
-#X floatatom 21 127 5 0 0 0 - - -;
-#X msg 21 143 value bal \$1;
-#X obj -3 170 s msg_to_pyo;
-#X msg -3 74 read examples/cvlverb.py;
-#X text -5 38 load pyo processing file (audio signal must be given
-in pyo~ inputs).;
-#X text 162 110 balance between dry and wet signal;
+#X obj 64 110 hsl 128 15 0 1 0 0 empty empty balance -2 -8 0 10 #fcfcfc #000000 #000000 0 0;
+#X floatatom 61 130 5 0 0 0 - - - 0;
+#X msg 61 152 value bal \$1;
+#X obj 37 179 s msg_to_pyo;
+#X msg 37 74 read examples/cvlverb.py;
+#X text 35 38 load pyo processing file (audio signal must be given in pyo~ inputs).;
+#X text 202 110 balance between dry and wet signal;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 4 0 3 0;
-#X restore 531 12 pd conv_reverb_example;
-#X text 26 96 play a soundfile to send audio signals to pyo;
+#X restore 17 303 pd conv_reverb_example;
+#X text 10 69 play a soundfile to send audio signals to pyo;
#N canvas 193 300 450 300 README 0;
#X text 14 33 Author : Olivier Belanger;
-#X text 15 108 pyo website : http://ajaxsoundstudio.com/software/pyo/
-;
#X text 15 10 pyo~ : Embedded pyo scripting inside puredata.;
#X text 15 135 pyo sources : https://github.com/belangeo/pyo;
-#X text 15 163 For more details about how pyo can be embedded inside
-an host program \, see m_pyo.h in the "embedded" folder of pyo sources.
-;
-#X text 14 57 Version : 1.0.0;
-#X text 14 81 Last update : December 2018;
-#X restore 406 237 pd README;
-#N canvas 182 106 1046 508 MESSAGES 0;
+#X text 15 163 For more details about how pyo can be embedded inside an host program \, see m_pyo.h in the "embedded" folder of pyo sources.;
+#X text 14 57 Version : 1.0.6;
+#X text 14 81 Last update : March 2026;
+#X text 15 108 pyo website : https://belangeo.github.io/pyo/;
+#X restore 822 671 pd README;
+#N canvas 182 106 1049 590 MESSAGES 0;
#X text 20 53 read [-a] path/to/python/script;
-#X text 37 74 The message "read" executes the commands contained in
-the specified python script into the object's internal interpreter.
-If the "-a" flag is given \, new commands will be added to previously
-executed ones. Without the flag \, the server is shut down (this will
-erase actual processing) before the execution of the script.;
+#X text 37 74 The message "read" executes the commands contained in the specified python script into the object's internal interpreter. If the "-a" flag is given \, new commands will be added to previously executed ones. Without the flag \, the server is shut down (this will erase actual processing) before the execution of the script.;
#X text 18 164 value varname \$1 [\$2 \$3 ...];
#X text 19 250 set varname.attribute \$1 [\$2 \$3 ...];
#X text 19 379 call function [arg1 arg2 ...];
-#X text 498 250 clear;
-#X text 515 271 Shutdown and reboot pyo's server. This message will
-erase the current processing loaded into the object.;
-#X text 36 185 The messsage "value" sends value(s) to a pyo's Sig or
-SigTo object (with variable name "varname"). Values can be pyo's variables
-(already created in the loaded file) \, float or list (composed of
-floats and/or pyo objects).;
-#X text 36 399 The message "call" executes the function (or object's
-method) with optional arguments. If the callable is a method \, the
-syntax will looks like:;
+#X text 498 302 clear;
+#X text 515 323 Shutdown and reboot pyo's server. This message will erase the current processing loaded into the object.;
+#X text 36 185 The messsage "value" sends value(s) to a pyo's Sig or SigTo object (with variable name "varname"). Values can be pyo's variables (already created in the loaded file) \, float or list (composed of floats and/or pyo objects).;
+#X text 36 399 The message "call" executes the function (or object's method) with optional arguments. If the callable is a method \, the syntax will looks like:;
#X text 35 439 call varname.method [arg1 arg2 ...];
#X text 36 351 set frequencies 100 200 300 400 500 600;
#X text 499 56 create varname object [\$1 \$2 ...];
-#X text 513 75 The message "create" creates a new python object of
-the class "object" \, stored in variable "varname" \, with optional
-initialization arguments. Arguments can be of the form freq=500 or
-mul=0.3 \, without spaces. Named arguments can't be followed by unamed
-arguments.;
-#X text 36 272 The messsage "set" sends value(s) to an attribute of
-any pyo object (with variable name "varname"). Values can be pyo's
-variables (already created in the loaded file) \, float or list (composed
-of floats and/or pyo objects). This message can be used to create a
-standard python variable like (to create a list of floats in variable
-"frequencies"):;
-#X text 498 312 debug \$1;
-#X text 515 333 If \$1 is positive \, messages to pyo will be sent
-through an Exception handler. This is safer and can help to debug messages
-to pyo but it is slower. For a faster execution \, turn off debug mode.
-;
+#X text 513 75 The message "create" creates a new python object of the class "object" \, stored in variable "varname" \, with optional initialization arguments. Arguments can be of the form freq=500 or mul=0.3 \, without spaces. Named arguments can't be followed by unamed arguments.;
+#X text 36 272 The messsage "set" sends value(s) to an attribute of any pyo object (with variable name "varname"). Values can be pyo's variables (already created in the loaded file) \, float or list (composed of floats and/or pyo objects). This message can be used to create a standard python variable like (to create a list of floats in variable "frequencies"):;
+#X text 498 364 debug \$1;
+#X text 515 385 If \$1 is positive \, messages to pyo will be sent through an Exception handler. This is safer and can help to debug messages to pyo but it is slower. For a faster execution \, turn off debug mode.;
#X text 498 161 midi \$1 [\$2 \$3];
-#X text 512 180 The message "midi" sends a MIDI event to the object
-processing. Arguments are the status byte (\$1) \, the first data byte
-(\$2) and the second data byte (\$3). Data bytes can be ommited and
-defaults to 0;
-#X text 7 3 Here are the messages that can be used to control the internal
-processing of the pyo~ object.;
-#X text 4 31 --------------------------------------------------------
-;
-#X restore 406 258 pd MESSAGES;
-#X msg 567 321 call b.out;
-#X text 11 7 pyo~ object allows to execute processing with pyo (python
-dsp module) inside a puredata patch \, with any number of audio in/out
-channels.;
-#X obj 531 121 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
-1;
-#X msg 531 144 debug \$1;
-#X text 549 113 Verbose mode. If on \, error messages from pyo will
-be printed to the Pd window.;
-#X obj 532 347 s msg_to_pyo;
-#X msg 567 258 set pits 0.001 0.002 0.003 0.004;
-#X msg 567 279 create fr Rossler pitch=pits chaos=0.9 mul=250 add=500
-;
-#X msg 567 300 create b SumOsc freq=fr ratio=0.499 index=0.4 mul=0.2
-;
+#X text 512 180 The message "midi" sends a MIDI event to the object processing. Arguments are the status byte (\$1) \, the first data byte (\$2) and the second data byte (\$3). Data bytes can be ommited and defaults to 0;
+#X text 7 3 Here are the messages that can be used to control the internal processing of the pyo~ object.;
+#X text 4 31 --------------------------------------------------------;
+#X text 498 454 exec \$1;
+#X text 515 475 Execute python statements written as strings. See [pd execute_python_statements] for an example.;
+#X text 498 250 bpm;
+#X text 515 271 Set Python's BPM. Useful when used inside a DAW.;
+#X restore 527 567 pd MESSAGES;
+#X text 11 7 pyo~ object allows to execute processing with pyo (python dsp module) inside a puredata patch \, with any number of audio in/out channels.;
+#X obj 17 412 tgl 15 0 empty empty empty 17 7 0 10 #fcfcfc #000000 #000000 0 1;
+#X msg 17 435 debug \$1;
#N canvas 669 129 722 427 midi_synth_example 0;
-#X obj 34 365 s msg_to_pyo;
-#X msg 34 56 read examples/midi_synth.py;
#X msg 97 108 midi 144 72 127;
-#X obj 129 172 hsl 128 15 0 127 0 0 empty empty empty -2 -8 0 10 -262144
--1 -1 0 1;
+#X obj 129 172 hsl 128 15 0 127 0 0 empty empty empty -2 -8 0 10 #fcfcfc #000000 #000000 0 1;
#X msg 126 195 midi 176 1 \$1;
-#X obj 448 43 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
-1;
+#X obj 448 43 tgl 15 0 empty empty empty 17 7 0 10 #fcfcfc #000000 #000000 0 1;
#X obj 448 63 metro 125;
#X obj 448 249 makenote 127 100;
#X obj 448 271 pack;
@@ -324,7 +287,7 @@ be printed to the Pd window.;
#X text 212 108 note on;
#X text 213 131 note off;
#X text 228 196 control change (mod wheel);
-#X floatatom 448 228 5 0 0 0 - - -;
+#X floatatom 448 228 5 0 0 0 - - - 0;
#X obj 448 183 int;
#X obj 448 161 + 0.5;
#X obj 448 139 * 1.76;
@@ -335,46 +298,426 @@ be printed to the Pd window.;
#X obj 526 159 * 12;
#X obj 448 206 +;
#X text 467 43 random melody;
-#X connect 1 0 0 0;
-#X connect 2 0 0 0;
+#X obj 34 365 s msg_to_pyo;
+#X msg 34 56 read examples/midi_synth.py;
+#X connect 0 0 23 0;
+#X connect 1 0 2 0;
+#X connect 2 0 23 0;
#X connect 3 0 4 0;
+#X connect 4 0 17 0;
+#X connect 5 0 6 0;
+#X connect 5 1 6 1;
+#X connect 6 0 7 0;
+#X connect 7 0 23 0;
+#X connect 8 0 23 0;
+#X connect 12 0 5 0;
+#X connect 13 0 21 0;
+#X connect 14 0 13 0;
+#X connect 15 0 14 0;
+#X connect 16 0 15 0;
+#X connect 17 0 16 0;
+#X connect 17 1 18 0;
+#X connect 18 0 19 0;
+#X connect 19 0 20 0;
+#X connect 20 0 21 1;
+#X connect 21 0 12 0;
+#X connect 24 0 23 0;
+#X restore 17 375 pd midi_synth_example;
+#N canvas 988 233 720 590 execute_python_statements 0;
+#X obj 38 500 pyo~;
+#X obj 38 244 list fromsymbol;
+#X obj 38 367 list tosymbol;
+#X obj 38 269 list prepend 100 101 102 32;
+#X obj 214 324 list fromsymbol;
+#X obj 38 342 list append;
+#X msg 38 392 exec \$1;
+#X obj 38 166 loadbang;
+#X obj 38 191 t b b;
+#X text 210 266 <- prepend "def ";
+#N canvas 720 184 786 700 get_stdout_and_stderr_in_patch 0;
+#X obj 50 139 list fromsymbol;
+#X obj 50 164 list prepend 100 101 102 32;
+#X obj 50 189 list append 10 9;
+#X obj 50 214 list append;
+#X obj 50 239 list tosymbol;
+#X obj 229 196 list fromsymbol;
+#X msg 50 264 exec \$1;
+#X obj 50 489 pyo~ -stdout outlet -stderr outlet;
+#X obj 251 515 route stdout stderr;
+#X obj 306 612 print stderr;
+#X msg 99 357 exec bar;
+#X msg 95 326 debug 1;
+#X obj 251 636 print stdout;
+#X listbox 389 612 50 0 0 0 - - - 0;
+#X obj 306 543 t a a;
+#X obj 50 62 loadbang;
+#X obj 50 85 t b b;
+#N canvas 849 155 721 555 isolate_error_and_line 0;
+#X obj 92 19 inlet;
+#X obj 71 250 outlet;
+#X obj 92 68 list fromsymbol;
+#X obj 92 43 symbol;
+#X obj 71 226 list tosymbol;
+#X obj 332 393 list;
+#X obj 71 202 list;
+#X obj 384 323 list split 1;
+#X obj 437 443 outlet;
+#X obj 92 145 t l l;
+#X obj 365 219 list;
+#X obj 384 377 t f b;
+#X obj 365 296 t b l l;
+#X obj 365 244 t l l;
+#X obj 384 350 t f f;
+#N canvas 879 272 390 307 find_newline_ndxs 0;
+#X obj 30 20 inlet;
+#X obj 30 210 outlet;
+#X obj 156 210 outlet;
+#X obj 51 134 list-abs/list-find;
+#X msg 51 86 10;
+#X obj 30 60 t b b l;
+#X obj 156 185 - 1;
+#X obj 51 110 t f b;
+#X obj 51 161 list-abs/list-extend;
+#X obj 30 184 list;
+#X text 115 46 find the last newline character;
+#X connect 0 0 5 0;
+#X connect 3 0 8 0;
+#X connect 3 1 6 0;
+#X connect 4 0 7 0;
+#X connect 5 0 9 0;
+#X connect 5 1 4 0;
+#X connect 5 2 3 1;
+#X connect 6 0 2 0;
+#X connect 7 0 3 0;
+#X connect 7 1 8 1;
+#X connect 8 0 9 1;
+#X connect 9 0 1 0;
+#X restore 92 121 pd find_newline_ndxs;
+#X f 30;
+#N canvas 1392 224 404 314 find_comma_ndxs 0;
+#X obj 32 20 inlet;
+#X obj 32 243 outlet;
+#X obj 53 133 list-abs/list-find;
+#X obj 53 109 t f b;
+#X msg 53 85 44;
+#X obj 32 60 t b b l;
+#X obj 53 160 list-abs/list-extend;
+#X obj 32 183 list;
+#X text 113 46 find the first comma character;
+#X connect 0 0 5 0;
+#X connect 2 0 6 0;
+#X connect 3 0 2 0;
+#X connect 3 1 6 1;
+#X connect 4 0 3 0;
+#X connect 5 0 7 0;
+#X connect 5 1 4 0;
+#X connect 5 2 2 1;
+#X connect 6 0 7 1;
+#X connect 7 0 1 0;
+#X restore 365 272 pd find_comma_ndxs;
+#X obj 92 92 t l l;
+#N canvas 1121 277 549 298 split_str_from_last_newline 0;
+#X obj 30 20 inlet;
+#X obj 79 20 inlet;
+#X obj 130 42 inlet;
+#X obj 113 241 outlet;
+#X obj 149 147 outlet;
+#X obj 30 60 list split;
+#X obj 58 159 list;
+#X obj 58 89 t b f;
+#X obj 58 184 list split;
+#X obj 115 107 + 1;
+#X text 172 92 split the output from the last newline character to end;
+#X obj 79 129 t l l;
+#X obj 86 216 t b l;
+#X connect 0 0 5 0;
+#X connect 1 0 11 0;
+#X connect 2 0 5 1;
+#X connect 5 1 7 0;
+#X connect 6 0 8 0;
+#X connect 7 0 6 0;
+#X connect 7 1 9 0;
+#X connect 8 1 12 0;
+#X connect 9 0 8 1;
+#X connect 11 0 6 1;
+#X connect 11 1 4 0;
+#X connect 12 0 4 0;
+#X connect 12 1 3 0;
+#X restore 92 176 pd split_str_from_last_newline;
+#X obj 365 194 route bang list;
+#N canvas 1385 289 608 511 determine_second_ndx 0;
+#X obj 297 225 inlet;
+#X obj 342 111 inlet;
+#X obj 138 135 inlet;
+#X obj 49 43 inlet;
+#X obj 123 448 outlet;
+#X obj 138 174 list split 1;
+#X obj 342 137 list length;
+#X obj 276 250 list;
+#N canvas 834 476 450 300 drip-list 0;
+#X obj 69 80 until;
+#X obj 69 102 list;
+#X obj 69 14 inlet;
+#X obj 69 146 outlet;
+#X obj 69 36 t b l;
+#X obj 156 142 t b b;
+#X obj 156 164 outlet done;
+#X obj 114 62 inlet;
+#X obj 69 124 list split 1;
+#X connect 0 0 1 0;
+#X connect 1 0 8 0;
+#X connect 2 0 4 0;
#X connect 4 0 0 0;
+#X connect 4 1 1 1;
#X connect 5 0 6 0;
-#X connect 6 0 19 0;
+#X connect 5 1 0 1;
+#X connect 7 0 0 1;
+#X connect 8 0 3 0;
+#X connect 8 1 1 1;
+#X connect 8 2 5 0;
+#X restore 276 274 pd drip-list;
+#X obj 276 297 t f f;
+#X obj 276 322 >;
+#X obj 276 345 sel 1;
+#X obj 276 368 t b b;
+#X obj 276 391 f;
+#X obj 257 178 > 1;
+#X obj 123 197 f;
+#X obj 257 202 sel 1 0;
+#X obj 257 155 f;
+#X text 205 9 in case of one comma only we look for the newline character that's after the comma \, otherwise we check the string in between the two commas;
+#X obj 49 67 route float bang;
+#X connect 0 0 7 1;
+#X connect 1 0 6 0;
+#X connect 2 0 5 0;
+#X connect 3 0 19 0;
+#X connect 5 0 15 1;
+#X connect 6 0 17 1;
#X connect 7 0 8 0;
-#X connect 7 1 8 1;
#X connect 8 0 9 0;
-#X connect 9 0 0 0;
-#X connect 10 0 0 0;
-#X connect 14 0 7 0;
-#X connect 15 0 23 0;
+#X connect 9 0 10 0;
+#X connect 9 1 13 1;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 12 0 13 0;
+#X connect 12 1 8 1;
+#X connect 13 0 4 0;
+#X connect 14 0 16 0;
+#X connect 15 0 4 0;
#X connect 16 0 15 0;
-#X connect 17 0 16 0;
-#X connect 18 0 17 0;
-#X connect 19 0 18 0;
-#X connect 19 1 20 0;
-#X connect 20 0 21 0;
-#X connect 21 0 22 0;
-#X connect 22 0 23 1;
-#X connect 23 0 14 0;
-#X restore 531 84 pd midi_synth_example;
-#X text 563 238 Create a pyo script from scratch!;
-#X connect 0 0 17 0;
-#X connect 1 0 17 1;
-#X connect 2 0 4 0;
-#X connect 3 0 4 1;
+#X connect 16 1 7 0;
+#X connect 17 0 14 0;
+#X connect 19 0 10 1;
+#X connect 19 1 17 0;
+#X restore 437 395 pd determine_second_ndx;
+#N canvas 1176 555 372 384 isolate_line_nr 0;
+#X obj 30 20 inlet;
+#X obj 87 20 inlet;
+#X obj 135 20 inlet;
+#X obj 58 297 outlet;
+#X obj 85 267 outlet;
+#X obj 87 60 + 7;
+#X obj 114 111 swap;
+#X obj 114 134 -;
+#X obj 58 182 list tosymbol;
+#X obj 58 210 f;
+#X obj 58 237 t b f;
+#X obj 58 158 list split;
+#X obj 30 111 list split;
+#X obj 87 84 t f f;
+#X connect 0 0 12 0;
+#X connect 1 0 5 0;
+#X connect 2 0 6 1;
+#X connect 5 0 13 0;
+#X connect 6 0 7 0;
+#X connect 6 1 7 1;
+#X connect 7 0 11 1;
+#X connect 8 0 9 0;
+#X connect 9 0 10 0;
+#X connect 10 0 3 0;
+#X connect 10 1 4 0;
+#X connect 11 0 8 0;
+#X connect 12 1 11 0;
+#X connect 13 0 12 1;
+#X connect 13 1 6 0;
+#X restore 332 418 pd isolate_line_nr;
+#X connect 0 0 3 0;
+#X connect 2 0 17 0;
+#X connect 3 0 2 0;
+#X connect 4 0 1 0;
+#X connect 5 0 21 0;
+#X connect 6 0 4 0;
+#X connect 7 0 14 0;
+#X connect 7 1 20 1;
+#X connect 9 0 18 0;
+#X connect 9 1 20 2;
+#X connect 10 0 13 0;
+#X connect 11 0 21 1;
+#X connect 11 1 20 0;
+#X connect 12 0 5 0;
+#X connect 12 1 7 0;
+#X connect 12 2 20 3;
+#X connect 13 0 16 0;
+#X connect 13 1 5 1;
+#X connect 14 0 11 0;
+#X connect 14 1 20 0;
+#X connect 15 0 9 0;
+#X connect 15 1 18 2;
+#X connect 16 0 12 0;
+#X connect 17 0 15 0;
+#X connect 17 1 18 1;
+#X connect 18 0 6 1;
+#X connect 18 1 19 0;
+#X connect 19 0 10 0;
+#X connect 19 1 10 1;
+#X connect 20 0 21 2;
+#X connect 21 0 6 0;
+#X connect 21 1 8 0;
+#X restore 389 561 pd isolate_error_and_line;
+#X floatatom 536 589 5 0 0 0 line - - 0;
+#X msg 99 450 exec \$1;
+#X obj 99 404 bng 18 250 50 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000;
+#N canvas 740 612 458 392 another_error 0;
+#X obj 30 20 inlet;
+#X obj 30 350 outlet;
+#X msg 30 89 symbol foo():;
+#X obj 30 114 list fromsymbol;
+#X obj 30 139 list prepend 100 101 102 32;
+#X obj 30 164 list append 10 9;
+#X obj 30 189 list append;
+#X obj 30 290 list tosymbol;
+#X msg 209 78 symbol print("hello");
+#X obj 209 171 list fromsymbol;
+#X obj 30 212 list append 10 9 9;
+#X obj 30 265 list append;
+#X msg 222 224 symbol pass;
+#X obj 222 247 list fromsymbol;
+#X obj 30 60 t b b b;
+#X connect 0 0 14 0;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 10 0;
+#X connect 7 0 1 0;
+#X connect 8 0 9 0;
+#X connect 9 0 6 1;
+#X connect 10 0 11 0;
+#X connect 11 0 7 0;
+#X connect 12 0 13 0;
+#X connect 13 0 11 1;
+#X connect 14 0 2 0;
+#X connect 14 1 12 0;
+#X connect 14 2 8 0;
+#X restore 99 427 pd another_error;
+#X text 121 402 <- generate another type of error;
+#X text 156 355 <- generate an error;
+#X text 149 328 <- activate debugging;
+#X msg 229 103 symbol print("hello\ world!");
+#X msg 50 114 symbol hello():;
+#X msg 79 295 exec hello();
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 6 0;
#X connect 5 0 3 1;
-#X connect 5 0 2 1;
#X connect 6 0 7 0;
-#X connect 8 0 10 0;
-#X connect 10 0 2 0;
-#X connect 10 1 3 0;
+#X connect 7 2 8 0;
+#X connect 8 0 12 0;
+#X connect 8 1 14 0;
+#X connect 10 0 7 0;
+#X connect 11 0 7 0;
+#X connect 14 0 9 0;
+#X connect 14 1 17 0;
+#X connect 15 0 16 0;
+#X connect 16 0 26 0;
+#X connect 16 1 25 0;
+#X connect 17 0 13 0;
+#X connect 17 1 18 0;
+#X connect 19 0 7 0;
+#X connect 20 0 21 0;
+#X connect 21 0 19 0;
+#X connect 25 0 5 0;
+#X connect 26 0 0 0;
+#X connect 27 0 7 0;
+#X restore 38 538 pd get_stdout_and_stderr_in_patch;
+#X msg 214 209 symbol print(arg);
+#X msg 38 219 symbol echo(arg):;
+#X listbox 58 439 20 0 0 0 - - - 0;
+#X msg 58 461 exec echo("\$1");
+#X text 36 20 below we create a function named echo() that prints whatever you type in the symbol box below to the console. the conversion from symbol to list and back to symbol to define the function is done to handle newlines and horizontal tabs in the symbols.;
+#X text 35 97 this technique might be somewhat complicated. check the [pd create_script_from_scratch] subpatch for an easier way to create pyo objects.;
+#X text 144 293 <- append a newline and a horizontal tab;
+#X obj 38 294 list append 10 9;
+#X text 189 440 <- write something and see it printed in Pd's console;
+#X connect 1 0 3 0;
+#X connect 2 0 6 0;
+#X connect 3 0 18 0;
+#X connect 4 0 5 1;
+#X connect 5 0 2 0;
+#X connect 6 0 0 0;
+#X connect 7 0 8 0;
+#X connect 8 0 12 0;
+#X connect 8 1 11 0;
+#X connect 11 0 4 0;
+#X connect 12 0 1 0;
#X connect 13 0 14 0;
-#X connect 17 0 10 0;
-#X connect 17 1 10 1;
-#X connect 23 0 28 0;
-#X connect 25 0 26 0;
-#X connect 26 0 14 0;
-#X connect 29 0 28 0;
-#X connect 30 0 28 0;
-#X connect 31 0 28 0;
+#X connect 14 0 0 0;
+#X connect 18 0 5 0;
+#X restore 527 591 pd execute_python_statements;
+#X text 497 43 Arguments:;
+#X text 516 63 [pyo~]'s arguments don't have a specific order. these are as follows:;
+#X text 523 108 -debug:;
+#X text 539 131 this flag takes a float argument (0 or 1) after it and sets the debug mode;
+#X text 523 178 -load:;
+#X text 523 402 float:;
+#X text 536 198 this flag takes a path to a python script to load after it (see [pd args]);
+#N canvas 834 476 519 426 args 0;
+#X text 33 86 load script with argument;
+#X text 33 16 set debug mode;
+#X obj 34 41 pyo~ -debug 1;
+#X text 33 159 set nr of I/Os;
+#X obj 34 184 pyo~ 3 2;
+#X obj 116 184 pyo~ 2;
+#X text 34 326 combine arguments;
+#X obj 34 110 pyo~ -load examples/midi_synth.py;
+#X text 161 182 one float argument sets both inlets and outlets;
+#X text 33 230 set stdout to be sent to an extra control outlet instead of the console;
+#X obj 34 271 pyo~ -stdout outlet;
+#X obj 34 351 pyo~ 1 2 -debug 1 -load examples/midi_synth.py -stdout outlet -stderr outlet;
+#X restore 527 543 pd args;
+#X obj 13 219 output~;
+#X text 15 280 check the examples below;
+#N canvas 884 526 450 300 create_script_from_scratch 0;
+#X msg 67 130 call b.out;
+#X obj 32 156 s msg_to_pyo;
+#X msg 67 67 set pits 0.001 0.002 0.003 0.004;
+#X msg 67 88 create fr Rossler pitch=pits chaos=0.9 mul=250 add=500;
+#X msg 67 109 create b SumOsc freq=fr ratio=0.499 index=0.4 mul=0.2;
+#X text 63 47 Create a pyo script from scratch!;
+#X connect 0 0 1 0;
+#X connect 2 0 1 0;
+#X connect 3 0 1 0;
+#X connect 4 0 1 0;
+#X restore 527 615 pd create_script_from_scratch;
+#X text 523 245 -stdout:;
+#X text 536 265 this flag takes either "outlet" or "console" to determine where python's STDOUT will be routed. if set to "outlet" \, an extra control outlet is created and STDOUT is redirected there with the "stdout" selector \, so python's output should be routed with [route] (see [pd args]);
+#X text 523 345 -stderr:;
+#X text 536 365 similar to -stdout \, but for STDERR;
+#X text 533 426 if no flag argument preceds a float argument then this sets number of inlets and outlets. if only one float argument is provided \, then both inlets and outlets will get the same number. to set a different number of inlets and outlets \, use two float arguments. with no float arguments \, inlets and outlets are set to 2;
+#X obj 111 91 tgl 18 0 empty empty play 22 9 0 10 #fcfcfc #000000 #000000 0 1;
+#X obj 12 90 bng 18 250 50 0 empty empty load_file 22 9 0 10 #fcfcfc #000000 #000000;
+#X obj 215 109 tgl 18 0 empty empty empty 0 -9 0 10 #fcfcfc #000000 #000000 0 1;
+#X text 35 404 Verbose mode. If on \, error and other messages from python will be printed to the Pd window.;
+#X connect 1 0 2 0;
+#X connect 2 0 26 0;
+#X connect 2 1 26 1;
+#X connect 4 0 5 0;
+#X connect 8 0 2 0;
+#X connect 8 1 2 1;
+#X connect 14 0 15 0;
+#X connect 15 0 5 0;
+#X connect 34 0 8 1;
+#X connect 35 0 8 0;
+#X connect 36 0 0 0;
diff --git a/embedded/puredata/pyo~.c b/embedded/puredata/pyo~.c
index d83aab2bb..0466ab149 100644
--- a/embedded/puredata/pyo~.c
+++ b/embedded/puredata/pyo~.c
@@ -11,11 +11,16 @@ typedef struct _pyo_tilde {
int debug;
int bs;
int add;
- int chnls;
+ int ichnls;
+ int ochnls;
float sr;
+ int stdout_set;
+ int stderr_set;
const char *file;
t_sample **in;
t_sample **out;
+ t_outlet *stdout;
+ t_atom *stdout_vec;
int id; /* pyo server id */
float *inbuf; /* pyo input buffer */
float *outbuf; /* pyo output buffer */
@@ -31,14 +36,14 @@ t_int *pyo_tilde_perform(t_int *w) {
t_sample **in = x->in;
t_sample **out = x->out;
for (i=0; ichnls; j++) {
- x->inbuf[i*x->chnls+j] = in[j][i];
+ for (j=0; jichnls; j++) {
+ x->inbuf[i*x->ichnls+j] = in[j][i];
}
}
(*x->callback)(x->id);
for (i=0; ichnls; j++) {
- out[j][i] = x->outbuf[i*x->chnls+j];
+ for (j=0; jochnls; j++) {
+ out[j][i] = x->outbuf[i*x->ochnls+j];
}
}
return (w+3);
@@ -47,10 +52,10 @@ t_int *pyo_tilde_perform(t_int *w) {
void pyo_tilde_dsp(t_pyo_tilde *x, t_signal **sp) {
int i, err;
t_sample **dummy = x->in;
- for (i=0; ichnls; i++)
+ for (i=0; iichnls; i++)
*dummy++ = sp[i]->s_vec;
dummy = x->out;
- for (i=x->chnls; ichnls*2; i++)
+ for (i=x->ichnls; iichnls+x->ochnls; i++)
*dummy++ = sp[i]->s_vec;
/* reset pyo only if sampling rate or buffer size have changed */
if ((float)sp[0]->s_sr != x->sr || (int)sp[0]->s_n != x->bs) {
@@ -58,12 +63,12 @@ void pyo_tilde_dsp(t_pyo_tilde *x, t_signal **sp) {
x->bs = (int)sp[0]->s_n;
pyo_set_server_params(x->interp, x->sr, x->bs);
if (x->file != NULL) {
- err = pyo_exec_file(x->interp, x->file, x->msg, x->add);
+ err = pyo_exec_file(x->interp, x->file, x->msg, x->add, x->debug);
if (err == 1) {
post("Unable to open file < %s >", x->file);
x->file = NULL;
} else if (err == 2) {
- post("Bad code in file < %s >", x->file);
+ pd_error(x, x->msg);
x->file = NULL;
}
}
@@ -71,49 +76,30 @@ void pyo_tilde_dsp(t_pyo_tilde *x, t_signal **sp) {
dsp_add(pyo_tilde_perform, 2, x, sp[0]->s_n);
}
-void *pyo_tilde_new(t_floatarg f) {
- int i;
- t_pyo_tilde *x = (t_pyo_tilde *)pd_new(pyo_tilde_class);
-
- x->chnls = (f) ? f : 2;
- x->bs = 64;
- x->sr = 44100.0;
- x->file = NULL;
- x->add = 0;
- x->debug = 0;
-
- /* create signal inlets (first is done in pyo_tilde_setup) */
- for (i=1; ichnls; i++)
- inlet_new(&x->obj, &x->obj.ob_pd, &s_signal, &s_signal);
- /* create signal outlets */
- for (i=0; ichnls; i++)
- outlet_new(&x->obj, &s_signal);
-
- x->in = (t_sample **)getbytes(x->chnls * sizeof(t_sample **));
- x->out = (t_sample **)getbytes(x->chnls * sizeof(t_sample **));
- x->msg = (char *)getbytes(262144 * sizeof(char *));
-
- for (i=0; ichnls; i++)
- x->in[i] = x->out[i] = 0;
-
- x->interp = pyo_new_interpreter(x->sr, x->bs, x->chnls);
-
- x->inbuf = (float *)pyo_get_input_buffer_address(x->interp);
- x->outbuf = (float *)pyo_get_output_buffer_address(x->interp);
- x->callback = (void *)pyo_get_embedded_callback_address(x->interp);
- x->id = pyo_get_server_id(x->interp);
-
- return (void *)x;
+static void pyo_load_file(t_pyo_tilde *x) {
+ int err;
+ if (pyo_is_server_started(x->interp)) {
+ err = pyo_exec_file(x->interp, x->file, x->msg, x->add, x->debug);
+ if (err == 1) {
+ post("Unable to open file < %s >", x->file);
+ x->file = NULL;
+ } else if (err == 2) {
+ pd_error(x, x->msg);
+ x->file = NULL;
+ }
+ }
}
-void pyo_tilde_free(t_pyo_tilde *x) {
+static void pyo_tilde_free(t_pyo_tilde *x) {
freebytes(x->in, sizeof(x->in));
freebytes(x->out, sizeof(x->out));
freebytes(x->msg, sizeof(x->msg));
pyo_end_interpreter(x->interp);
+ if (x->stdout_set)
+ outlet_free(x->stdout);
}
-void pyo_tilde_set_value(t_pyo_tilde *x, char *att, int argc, t_atom *argv) {
+static void pyo_tilde_set_value(t_pyo_tilde *x, char *att, int argc, t_atom *argv) {
int err, bracket = 0;
char fchar[32];
t_symbol *c = atom_getsymbol(argv);
@@ -139,23 +125,27 @@ void pyo_tilde_set_value(t_pyo_tilde *x, char *att, int argc, t_atom *argv) {
strcat(x->msg, "]");
err = pyo_exec_statement(x->interp, x->msg, x->debug);
if (err)
- post("pyo~: %s", x->msg);
+ pd_error(x, x->msg);
}
-void pyo_tilde_value(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+static void pyo_tilde_value(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
char *att = ".value";
+ (void)(s); /* unused */
pyo_tilde_set_value(x, att, argc, argv);
}
-void pyo_tilde_set(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+
+static void pyo_tilde_set(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
char *att = "";
+ (void)(s); /* unused */
pyo_tilde_set_value(x, att, argc, argv);
}
-void pyo_tilde_create(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+static void pyo_tilde_create(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
int err;
const char *varname, *object;
char fchar[32];
t_symbol *c = atom_getsymbol(argv);
+ (void)(s); /* unused */
varname = c->s_name;
argc--; argv++;
c = atom_getsymbol(argv);
@@ -177,11 +167,72 @@ void pyo_tilde_create(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
strcat(x->msg, ")");
err = pyo_exec_statement(x->interp, x->msg, x->debug);
if (err)
- post("pyo~: %s", x->msg);
+ pd_error(x, x->msg);
+}
+
+static int pyo_tilde_get_stdout(t_pyo_tilde *x)
+{
+ int counter = 0;
+ char *msg;
+ while (pyo_dequeue_stdout(&msg)) {
+ if (counter == 0) sprintf(x->msg, msg);
+ else strcat(x->msg, msg);
+ free(msg);
+ counter++;
+ }
+ return counter;
}
-void pyo_tilde_midi_event(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+static void pyo_tilde_exec(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+ int err, i;
+ char fchar[32];
+ (void)(s); /* unused */
+ for (i = 0; i < argc; i++) {
+ if (argv->a_type == A_SYMBOL) {
+ if (i == 0) sprintf(x->msg, atom_getsymbol(argv)->s_name);
+ else strcat(x->msg, atom_getsymbol(argv)->s_name);
+ }
+ else if (argv->a_type == A_FLOAT) {
+ sprintf(fchar, "%f", atom_getfloat(argv));
+ if (i == 0) sprintf(x->msg, fchar);
+ else strcat(x->msg, fchar);
+ }
+ if (argc > 0)
+ strcat(x->msg, " ");
+ argv++;
+ }
+ err = pyo_exec_statement(x->interp, x->msg, x->debug);
+ if (err) {
+ /* remove the newline character at the end of STDOUT
+ * by placing the null terminating character at its position */
+ x->msg[strlen(x->msg)-1] = '\0';
+ if (x->stderr_set) {
+ t_atom *stdout_vec = x->stdout_vec;
+ SETSYMBOL(stdout_vec, gensym(x->msg));
+ outlet_anything(x->stdout, gensym("stderr"), 1, stdout_vec);
+ }
+ else {
+ pd_error(x, x->msg);
+ }
+ }
+ else if (pyo_tilde_get_stdout(x)){
+ /* remove the newline character at the end of STDOUT
+ * by placing the null terminating character at its position */
+ x->msg[strlen(x->msg)-1] = '\0';
+ if (x->stdout_set) {
+ t_atom *stdout_vec = x->stdout_vec;
+ SETSYMBOL(stdout_vec, gensym(x->msg));
+ outlet_anything(x->stdout, gensym("stdout"), 1, stdout_vec);
+ }
+ else {
+ post(x->msg);
+ }
+ }
+}
+
+static void pyo_tilde_midi_event(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
int status = 0, data1 = 0, data2 = 0;
+ (void)(s); /* unused */
if (argc > 0)
status = (int)atom_getfloat(argv);
if (argc > 1)
@@ -191,16 +242,24 @@ void pyo_tilde_midi_event(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
pyo_add_midi_event(x->interp, status, data1, data2);
}
-void pyo_tilde_clear(t_pyo_tilde *x) {
+static void pyo_tilde_bpm(t_pyo_tilde *x, t_float f) {
+ if (f < 0) {
+ pd_error(x, "BPM value can't be negative");
+ return;
+ }
+ pyo_set_bpm(x->interp, (double)f);
+}
+
+static void pyo_tilde_clear(t_pyo_tilde *x) {
pyo_server_reboot(x->interp);
}
-void pyo_tilde_debug(t_pyo_tilde *x, t_float debug) {
+static void pyo_tilde_debug(t_pyo_tilde *x, t_float debug) {
x->debug = debug <= 0 ? 0 : 1;
}
-void pyo_tilde_read(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
- int err;
+static void pyo_tilde_read(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+ (void)(s); /* unused */
switch (argc) {
case 1:
x->add = 0;
@@ -211,21 +270,13 @@ void pyo_tilde_read(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
x->file = atom_getsymbol(argv)->s_name;
break;
}
- if (pyo_is_server_started(x->interp)) {
- err = pyo_exec_file(x->interp, x->file, x->msg, x->add);
- if (err == 1) {
- post("Unable to open file < %s >", x->file);
- x->file = NULL;
- } else if (err == 2) {
- post("Bad code in file < %s >", x->file);
- x->file = NULL;
- }
- }
+ pyo_load_file(x);
}
-void pyo_tilde_call(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
+static void pyo_tilde_call(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
int err;
char fchar[32];
+ (void)(s); /* unused */
sprintf(x->msg, "%s(", atom_getsymbol(argv)->s_name);
argc--; argv++;
while (argc-- > 0) {
@@ -243,12 +294,159 @@ void pyo_tilde_call(t_pyo_tilde *x, t_symbol *s, int argc, t_atom *argv) {
strcat(x->msg, ")");
err = pyo_exec_statement(x->interp, x->msg, x->debug);
if (err)
- post("pyo~: %s", x->msg);
+ pd_error(x, x->msg);
+}
+
+static void *pyo_tilde_new(t_symbol *s, int argc, t_atom *argv) {
+ int i;
+ int ichnls_set = 0;
+ int ochnls_set = 0;
+ int load_file = 0;
+ int setting_debug = 0;
+ int loading_file = 0;
+ int debug_set = 0;
+ int setting_stdout = 0;
+ int setting_stderr = 0;
+ t_symbol *debug_flag = gensym("-debug");
+ t_symbol *load_flag = gensym("-load");
+ t_symbol *stdout_flag = gensym("-stdout");
+ t_symbol *stderr_flag = gensym("-stderr");
+ t_symbol *outlet_arg = gensym("outlet");
+ t_symbol *console_arg = gensym("console");
+
+ t_pyo_tilde *x = (t_pyo_tilde *)pd_new(pyo_tilde_class);
+ (void)(s); /* unused */
+
+ x->stdout_set = x->stderr_set = 0;
+
+ for (i = 0; i < argc; i++) {
+ if (argv->a_type == A_FLOAT) {
+ if (setting_debug) {
+ x->debug = (int)atom_getfloat(argv);
+ setting_debug = 0;
+ debug_set = 1;
+ }
+ else if (loading_file) {
+ pd_error(x, "after -load a python file path should be provided");
+ loading_file = 0;
+ return (void *)x;
+ }
+ else {
+ if (i == 0) {
+ x->ichnls = (int)atom_getfloat(argv);
+ ichnls_set = 1;
+ }
+ else {
+ x->ochnls = (int)atom_getfloat(argv);
+ ochnls_set = 1;
+ }
+ }
+ }
+ else if (argv->a_type == A_SYMBOL) {
+ t_symbol *strarg = atom_gensym(argv);
+ if (strarg == debug_flag) {
+ setting_debug = 1;
+ }
+ else if (strarg == load_flag) {
+ loading_file = 1;
+ }
+ else if (strarg == stdout_flag) {
+ setting_stdout = 1;
+ }
+ else if (strarg == stderr_flag) {
+ setting_stderr = 1;
+ }
+ else {
+ if (loading_file) {
+ x->add = 0;
+ x->file = strarg->s_name;
+ load_file = 1;
+ loading_file = 0;
+ }
+ else if (setting_stdout) {
+ if (strarg == outlet_arg) {
+ x->stdout_set = 1;
+ }
+ else if (strarg == console_arg) {
+ x->stdout_set = 0;
+ }
+ else {
+ pd_error(x, "unknown value to -stdout, must be \"outlet\" or \"console\"");
+ return (void *)x;
+ }
+ setting_stdout = 0;
+ }
+ else if (setting_stderr) {
+ if (strarg == outlet_arg) {
+ x->stderr_set = 1;
+ }
+ else if (strarg == console_arg) {
+ x->stderr_set = 0;
+ }
+ else {
+ pd_error(x, "unknown value to -stderr, must be \"outlet\" or \"console\"");
+ return (void *)x;
+ }
+ setting_stderr = 0;
+ }
+ }
+ }
+ argv++;
+ }
+ if (ichnls_set == 0) x->ichnls = 2;
+ if (ochnls_set == 0) x->ochnls = x->ichnls;
+ x->bs = 64;
+ x->sr = 44100.0;
+ if (!load_file) x->file = NULL;
+ x->add = 0;
+ if (!debug_set) x->debug = 0;
+ if (x->stdout_set|| x->stderr_set) {
+ /* vector for outputting lists out the last outlet */
+ x->stdout_vec = (t_atom *)getbytes(sizeof(t_atom));
+ x->stdout_vec[0].a_type = A_SYMBOL;
+ }
+
+ /* create signal inlets (first is done in pyo_tilde_setup) */
+ for (i=1; iichnls; i++) {
+ inlet_new(&x->obj, &x->obj.ob_pd, &s_signal, &s_signal);
+ }
+ /* create signal outlets */
+ for (i=0; iochnls; i++) {
+ outlet_new(&x->obj, &s_signal);
+ }
+ /* if set by the user, create a control outlet to route STDOUT and/or STDERR inside the patch */
+ if (x->stdout_set || x->stderr_set) {
+ x->stdout = outlet_new(&x->obj, 0);
+ }
+
+ x->in = (t_sample **)getbytes(x->ichnls * sizeof(t_sample **));
+ x->out = (t_sample **)getbytes(x->ochnls * sizeof(t_sample **));
+ x->msg = (char *)getbytes(262144 * sizeof(char *));
+
+ for (i=0; iichnls; i++)
+ x->in[i] = 0;
+
+ for (i=0; iochnls; i++)
+ x->out[i] = 0;
+
+ x->interp = pyo_new_interpreter(x->sr, x->bs, x->ichnls, x->ochnls);
+
+ x->inbuf = (float *)pyo_get_input_buffer_address(x->interp);
+ x->outbuf = (float *)pyo_get_output_buffer_address(x->interp);
+ x->callback = (void *)pyo_get_embedded_callback_address(x->interp);
+ x->id = pyo_get_server_id(x->interp);
+
+ /* dump the first stdout note about wdPython */
+ pyo_tilde_get_stdout(x);
+
+ if (load_file) pyo_load_file(x);
+
+ return (void *)x;
}
void pyo_tilde_setup(void) {
pyo_tilde_class = class_new(gensym("pyo~"), (t_newmethod)pyo_tilde_new,
- (t_method)pyo_tilde_free, sizeof(t_pyo_tilde), CLASS_DEFAULT, A_DEFFLOAT, 0);
+ (t_method)pyo_tilde_free, sizeof(t_pyo_tilde), CLASS_DEFAULT, A_GIMME, 0);
class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_dsp, gensym("dsp"), 0);
class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_clear, gensym("clear"), 0);
class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_value, gensym("value"),
@@ -262,7 +460,11 @@ void pyo_tilde_setup(void) {
class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_create, gensym("create"),
A_GIMME, 0); /* create a python object */
class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_midi_event, gensym("midi"),
- A_GIMME, 0); /* create a python object */
+ A_GIMME, 0); /* send a MIDI event */
+ class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_bpm, gensym("bpm"),
+ A_DEFFLOAT, 0); /* set python's BPM */
+ class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_exec, gensym("exec"),
+ A_GIMME, 0); /* execute a python statement */
class_addmethod(pyo_tilde_class, (t_method)pyo_tilde_debug, gensym("debug"),
A_DEFFLOAT, 0); /* set the debug (verbose) mode */
CLASS_MAINSIGNALIN(pyo_tilde_class, t_pyo_tilde, f);