├── examples ├── tune-oneliner-2.py ├── test-cpu.py ├── tune-oneliner-1.py ├── fx-harmonizer.py ├── analog-in-out.py ├── fx-multi-ring-mod.py ├── fx-smoothdelay.py ├── music-box.py ├── fx-degradator.py └── fx-ring-mod.py ├── install_pyo_on_board.sh ├── common ├── settings.json ├── PyoClass.h ├── render.cpp ├── PyoClass.cpp └── m_pyo.h ├── README.md └── LICENSE /examples/tune-oneliner-2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Oneliner-2 - Algorithmic tune written on a single line! 3 | 4 | """ 5 | t=Disto(BandSplit(Noise(),6,50,14000,Sine(.05,0,8,9),Osc(SquareTable(20),Randh(2,9,[.16,.12,.15,.17,.2,.1]),0,2,.2,.2)),[.8,.8,.7,.7,.6,.6],.7,Port(Clip(RandInt(4,[.05,.08,.04,.06,.07,.09])))).out() 6 | -------------------------------------------------------------------------------- /examples/test-cpu.py: -------------------------------------------------------------------------------- 1 | """ 2 | CPU and optimization test file. 3 | 4 | without optimizations: 30 sines = 60.3% CPU 5 | with optimizations: 30 sines = 55.0% CPU 6 | 7 | """ 8 | import random 9 | 10 | NUM = 30 11 | 12 | freqs = [random.uniform(400, 600) for i in range(NUM)] 13 | 14 | sines = Sine(freqs, mul=0.05).out() 15 | 16 | -------------------------------------------------------------------------------- /examples/tune-oneliner-1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Oneliner-1 - Algorithmic tune written on a single line! 3 | 4 | """ 5 | t=Degrade(FM(Choice([50]*9+[75,99],10,RandInt(3,.2,2,4)),[.25,.33,.5,.75],Sine(.03,0,4,4),TrigEnv(Metro(.1,6).play(),CosTable([(0,0),(49,1),(500,.3),(8191,0)]),Sine(.07,0,.15,.45)),Sine([100,101],0,0.03)).mix(2),Sine(0.02,0,3,7),Sine(.05,0,.06,.1),0.3).out() 6 | -------------------------------------------------------------------------------- /examples/fx-harmonizer.py: -------------------------------------------------------------------------------- 1 | # Analog inputs (1 = transposition factors, 2 = feedback) 2 | i1 = Input(2) 3 | i2 = Input(3) 4 | 5 | src = Input([0,1]) 6 | #t = SndTable(SNDS_PATH+"/transparent.aif") 7 | #src = Osc(t, t.getRate(), mul=0.5) 8 | 9 | # Controllers scaling 10 | t1 = i1 * 24 - 12 11 | t2 = i1 * 24.2 - 12.1 12 | 13 | # DSP 14 | harmo = Harmonizer(src, [t1,t2], i2*0.9, winsize=0.05, mul=0.4).out() 15 | dry = Mix(src, voices=2, mul=0.4).out() 16 | 17 | -------------------------------------------------------------------------------- /install_pyo_on_board.sh: -------------------------------------------------------------------------------- 1 | mkdir tmp 2 | cd tmp 3 | echo "=== Downloading pyo-bela binaries. ===" 4 | wget -q https://github.com/belangeo/pyo-bela/releases/download/v0.9.0/dist-packages.tar.bz2 5 | echo "=== Extracting pyo-bela binaries. ===" 6 | tar xjf dist-packages.tar.bz2 7 | echo "=== Copying files to the BBB. ===" 8 | scp -r -q dist-packages/ root@192.168.7.2:/usr/local/lib/python2.7/ 9 | echo "=== Cleanup. ===" 10 | cd .. 11 | rm -r tmp 12 | echo "=== Done! ===" 13 | 14 | -------------------------------------------------------------------------------- /examples/analog-in-out.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example shows how to manage analog inputs and outputs. 3 | 4 | """ 5 | 6 | # Analog inputs comes right after the stereo audio channels. 7 | i1 = Tone(Input(2), 8) # analog in 0 8 | i2 = Tone(Input(3), 8) # analog in 1 9 | 10 | lfo = Sine([8,10], mul=0.2) 11 | 12 | # Analog outputs comes right after the stereo audio channels. 13 | o1 = Clip(i1+lfo[0], 0, 1).out(2) # analog out 0 14 | o2 = Clip(i2+lfo[1], 0, 1).out(3) # analog out 1 15 | 16 | -------------------------------------------------------------------------------- /examples/fx-multi-ring-mod.py: -------------------------------------------------------------------------------- 1 | # Analog inputs 2 | i1 = Input(2) 3 | i2 = Input(3) 4 | 5 | src = Noise(.5) 6 | #t = SndTable(SNDS_PATH+"/transparent.aif") 7 | #src = Osc(t, t.getRate(), mul=0.5) 8 | 9 | # Controllers scaling 10 | m1 = i1 * 20 + 0.1 11 | m2 = m1 * 1.25 12 | m3 = m2 * 1.25 13 | m4 = m3 * 1.25 14 | 15 | # DSP 16 | modus = FastSine([m1,m2,m3,m4], mul=0.5, add=0.5) 17 | bands = BandSplit(src, num=4, min=50, max=5000, q=i2*20+1, mul=modus) 18 | mix = Mix(bands, voices=2, mul=i2*2+0.5).out() 19 | -------------------------------------------------------------------------------- /examples/fx-smoothdelay.py: -------------------------------------------------------------------------------- 1 | # Analog inputs (1 = degrade ctrl, 2 = lowpass filter cutoff) 2 | i1 = Input(2) 3 | i2 = Input(3) 4 | 5 | src = Input([0,1]) 6 | #t = SndTable(SNDS_PATH+"/transparent.aif") 7 | #src = Osc(t, t.getRate(), mul=0.5) 8 | 9 | # Controllers scaling 10 | d1 = i1 * 0.49 + 0.005 11 | d2 = i1 * 0.48 + 0.006 12 | d3 = i1 * 0.39 + 0.007 13 | d4 = i1 * 0.38 + 0.008 14 | 15 | # DSP 16 | delay = SmoothDelay(src, [d1,d2,d3,d4], i2*0.99, crossfade=0.005, mul=0.35).out() 17 | dry = Mix(src, voices=2, mul=0.4).out() 18 | 19 | -------------------------------------------------------------------------------- /examples/music-box.py: -------------------------------------------------------------------------------- 1 | """ 2 | Music box - Four voices randomly choosing frequencies 3 | over a common scale. 4 | 5 | """ 6 | # Set to True to control the global gain with analog input 0. 7 | WITH_ANALOG_INPUT = False 8 | 9 | if WITH_ANALOG_INPUT: 10 | v = Tone(Input(2), 8) # analog input 0 => global gain 11 | else: 12 | v = Sig(0.5) 13 | 14 | mid_freqs = [midiToHz(m+7) for m in [60,62,63.93,65,67.01,69,71,72]] 15 | high_freqs = [midiToHz(m+7) for m in [72,74,75.93,77,79.01]] 16 | freqs = [mid_freqs,mid_freqs,high_freqs,high_freqs] 17 | 18 | chx = Choice(choice=freqs, freq=[2,3,3,4]) 19 | port = Port(chx, risetime=.001, falltime=.001) 20 | sines = SineLoop(port, feedback=[.057,.033,.035,.016], 21 | mul=[.1,.1,.05,.05]*v).out() 22 | 23 | -------------------------------------------------------------------------------- /common/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileName": "render.cpp", 3 | "CLArgs": { 4 | "-p": "16", 5 | "-C": "8", 6 | "-B": "16", 7 | "-H": "-6", 8 | "-N": "1", 9 | "-G": "1", 10 | "-M": "0", 11 | "-D": "0", 12 | "-A": "0", 13 | "--pga-gain-left": "10", 14 | "--pga-gain-right": "10", 15 | "user": "", 16 | "make": "CPPFLAGS=-I/usr/include/python2.7 -I/usr/include/arm-linux-gnueabihf/python2.7 -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g0 -fstack-protector-strong -Wformat -Werror=format-security -DNDEBUG -fwrapv -O3 -Wall -Wstrict-prototypes;LDFLAGS=-L/usr/lib/python1.7/config-arm-linux-gnueabihf -L/usr/lib;LDLIBS=-lpython2.7 -lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O2 -Wl,-Bsymbolic-functions" 17 | }, 18 | "breakpoints": [] 19 | } 20 | -------------------------------------------------------------------------------- /examples/fx-degradator.py: -------------------------------------------------------------------------------- 1 | """ 2 | fx-degradator.py - Signal quality degradator. 3 | 4 | This example shows signal degradation effect with the 5 | degradation factor and a lowpass filter cutoff controlled 6 | by analog inputs (use audio inputs after the stereo audio 7 | channels, ie. Input(2) is analog-in 0, Input(3) is 8 | analog-in 1, etc.). 9 | 10 | """ 11 | # Set to True if you want to control the degradation 12 | # and lowpass filter with analog inputs. 13 | WITH_ANALOG_INPUT = True 14 | # If False, set frequency and brightness values. 15 | DEGRADATION = 0.75 # 0 -> 1 16 | LPCUTOFF = 2500 # (Hz) 17 | 18 | # stereo input 19 | src = Input([0,1]) 20 | 21 | if WITH_ANALOG_INPUT: 22 | # analog-in 0 (degradation) 23 | i1 = Input(2) 24 | # analog-in 1 (lowpass filter cutoff) 25 | i2 = Input(3) 26 | freq = (1.0 - i2) * 10000 + 50 27 | else: 28 | i1 = DEGRADATION 29 | freq = LPCUTOFF 30 | 31 | # Controllers scaling 32 | invi1 = 1.0 - i1 33 | bit = invi1 * 4.0 + 2.0 34 | sr = invi1 * 0.48 + 0.02 35 | sr2 = invi1 * 0.49 + 0.01 36 | 37 | # DSP 38 | deg = Degrade(src, bit, [sr,sr2]) 39 | lp = MoogLP(deg, freq, res=i1, mul=0.4).out() 40 | 41 | -------------------------------------------------------------------------------- /common/PyoClass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "m_pyo.h" 4 | 5 | typedef int callPtr(int); 6 | 7 | class Pyo { 8 | public: 9 | ~Pyo(); 10 | void setup(int nChannels, int bufferSize, int sampleRate, int nAnalogChannels); 11 | void process(float *buffer); 12 | void fillin(const float *buffer); 13 | void analogin(const float *buffer); 14 | void analogout(float *buffer); 15 | void clear(); 16 | void midievent(int status, int data1, int data2); 17 | int loadfile(const char *file, int add); 18 | int exec(const char *msg); 19 | int value(const char *name, float value); 20 | int value(const char *name, float *value, int len); 21 | int set(const char *name, float value); 22 | int set(const char *name, float *value, int len); 23 | 24 | private: 25 | int nChannels; 26 | int bufferSize; 27 | int sampleRate; 28 | int nAnalogChannels; 29 | int nTotalChannels; 30 | PyThreadState *interpreter; 31 | float *pyoInBuffer; 32 | float *pyoOutBuffer; 33 | callPtr *pyoCallback; 34 | int pyoId; 35 | char pyoMsg[262144]; 36 | }; 37 | -------------------------------------------------------------------------------- /common/render.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "PyoClass.h" 6 | 7 | Pyo pyo; 8 | 9 | void Bela_userSettings(BelaInitSettings *settings) { 10 | settings->periodSize = 32; 11 | settings->uniformSampleRate = 1; 12 | settings->numAnalogInChannels = 8; 13 | settings->numAnalogOutChannels = 8; 14 | settings->analogOutputsPersist = 0; 15 | } 16 | 17 | bool setup(BelaContext *context, void *userData) { 18 | // Initialize a pyo server. 19 | pyo.setup(context->audioOutChannels, context->audioFrames, 20 | context->audioSampleRate, context->analogOutChannels); 21 | // Load a python file. 22 | char filename[] = "main.py"; 23 | int ret = pyo.loadfile(filename, 0); 24 | if (ret != 0) { 25 | printf("Error: file \"%s\" not found", filename); 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | 32 | void render(BelaContext *context, void *userData) { 33 | // Fill pyo input buffer (channels 0-1) with audio samples. 34 | pyo.fillin(context->audioIn); 35 | // Fill pyo input buffer (channels 2+) with analog inputs. 36 | pyo.analogin(context->analogIn); 37 | // Call pyo processing function and retrieve back stereo outputs. 38 | pyo.process(context->audioOut); 39 | // Get back pyo output channels 2+ as analog outputs. 40 | if (context->analogOut != NULL) { 41 | pyo.analogout(context->analogOut); 42 | } 43 | } 44 | 45 | void cleanup(BelaContext *context, void *userData) {} 46 | 47 | -------------------------------------------------------------------------------- /examples/fx-ring-mod.py: -------------------------------------------------------------------------------- 1 | """ 2 | fx-ring-mod.py - Stereo ring modulator. 3 | 4 | This example shows how to build a ring modulation effect 5 | with modulator's frequency and brightness controlled by 6 | analog inputs (use audio inputs after the stereo audio 7 | channels, ie. Input(2) is analog-in 0, Input(3) is 8 | analog-in 1, etc.). 9 | 10 | It also show how to send signal to analog outputs. Again, 11 | use the outputs after the stereo audio channels, ie. 12 | .out(2) writes to analog-out 0, .out(3) to analog-out 1, 13 | etc.). 14 | 15 | """ 16 | # Set to True if you want to control the modulator 17 | # frequency and brightness with analog inputs. 18 | WITH_ANALOG_INPUT = True 19 | # If False, set frequency and brightness values. 20 | FREQUENCY = 500 # Hz 21 | BRIGHTNESS = 0.05 # 0 -> 0.2 22 | 23 | # If True, a positive value is sent on analog-out 0 and 1 24 | # whenever there is an output signal (can be used to build 25 | # a cheap vumeter with leds). 26 | WITH_ANALOG_OUTPUT = True 27 | 28 | # stereo input 29 | src = Input([0,1]) 30 | 31 | # Don't know if the noise comes from my mic, 32 | # but it sounds better with a gate on the input! 33 | gate = Gate(src.mix(), thresh=-60, risetime=.005, 34 | falltime=.02, lookahead=1, outputAmp=True) 35 | srcg = src * gate 36 | 37 | if WITH_ANALOG_INPUT: 38 | # analog-in 0 (modulator's frequency) 39 | i0 = Tone(Input(2), 8) 40 | freq = Scale(i0, 0, 1, 1, 1000, 3) 41 | 42 | # analog-in 1 (modulator's brightness) 43 | i1 = Tone(Input(3), 8) 44 | feed = i1 * 0.2 45 | else: 46 | freq = FREQUENCY 47 | feed = BRIGHTNESS 48 | 49 | # Modulation oscillator 50 | mod = SineLoop(freq, feed) 51 | 52 | # Ring modulation and stereo output 53 | out = (srcg * mod).out() 54 | 55 | if WITH_ANALOG_OUTPUT: 56 | # analog out 0-1 (stereo vumeter) 57 | fol = Sqrt(Clip(Follower(out, mul=4))).out(2) 58 | 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | How to use pyo on the BeagleBone Black with the Bela cape 2 | ========================================================= 3 | 4 | This tutorial has been tested with a board flashed with the 5 | Bela image _v0.3.4_ and the latest Bela sources from the 6 | github repo dated June 16, 2018. 7 | 8 | For instructions on how to flash your board, see the Bela 9 | wiki: 10 | 11 | [https://github.com/BelaPlatform/Bela/wiki/Manage-your-SD-card#flashing-an-sd-card](https://github.com/BelaPlatform/Bela/wiki/Manage-your-SD-card#flashing-an-sd-card) 12 | 13 | Step 1 - Clone or download the needed repos from github 14 | ------------------------------------------------------- 15 | 16 | You will need two repos to run pyo projects with Bela platform. 17 | 18 | - Bela: Core code and mandatory stuff to communicate with the board. 19 | 20 | https://github.com/BelaPlatform/Bela 21 | 22 | - Pyo-bela: Interface pyo/bela, pre-compiled binaries and project template. 23 | 24 | https://github.com/belangeo/pyo-bela 25 | 26 | The next steps assumes that the two repositories are side-by-side 27 | in the same directory and that the board is plugged to the host 28 | computer with a usb cable. 29 | 30 | Step 2 - Install pyo binaries on the board 31 | ------------------------------------------ 32 | 33 | To download and install the latest pyo binaries on the board, run the 34 | script _install\_pyo\_on\_board.sh_ from pyo-bela sources (or follow 35 | instructions on the [release page](https://github.com/belangeo/pyo-bela/releases)): 36 | 37 | cd pyo-bela 38 | ./install_pyo_on_board.sh 39 | 40 | Step 3 - Prepare the host for managing a pyo-bela project 41 | ----------------------------------------------------------- 42 | 43 | Copy the pyo-bela/build_pyo.sh script to Bela/scripts folder and make it executable: 44 | 45 | cp pyo-bela/build_pyo.sh Bela/scripts 46 | chmod +x Bela/scripts/build_pyo.sh 47 | 48 | At this point, if you never update the board (i.e. you just flashed your sd card), you 49 | should call the script update_board to copy the latest framework from Bela to your board: 50 | 51 | cd Bela/scripts 52 | ./update_board 53 | 54 | Step 4 - Compile and run a pyo-bela project 55 | ------------------------------------------- 56 | 57 | From Bela/scripts folder, compile and run any of the examples in the 58 | pyo-bela/examples folder. The first argument to the build_pyo.sh script is 59 | the python file to execute (the project will have the same name as this file, 60 | without ".py"). The second argument is the path of the pyo-bela/common folder, 61 | which contain all needed files to run a bela project. 62 | 63 | cd Bela/scripts 64 | ./build_pyo.sh ../../pyo-bela/examples/music-box.py ../../pyo-bela/common 65 | 66 | The second argument can be ommited if an environment variable called PYO_BELA_COMMON 67 | is set to point to the location of pyo-bela/common folder. 68 | 69 | export PYO_BELA_COMMON=/path/to/pyo-bela/common 70 | ./build_pyo.sh ../../pyo-bela/examples/music-box.py 71 | 72 | Documentation 73 | ============= 74 | 75 | For a complete description of functions that can be used to communicate 76 | with the pyo embedded processes, see documentation comments in the file 77 | common/PyoClass.cpp. 78 | 79 | (c) 2018 - belangeo 80 | 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /common/PyoClass.cpp: -------------------------------------------------------------------------------- 1 | #include "PyoClass.h" 2 | 3 | /* 4 | ** Creates a python interpreter and initialize a pyo server inside it. 5 | ** This function must be called, once per Pyo object, before any other 6 | ** calls. 7 | ** 8 | ** arguments: 9 | ** nChannels : int, number of in/out channels. 10 | ** bufferSize : int, number of samples per buffer. 11 | ** sampleRate : int, sample rate frequency. 12 | ** nAnalogChannels : int, number of analog channels. 13 | ** 14 | ** All arguments should be equal to the host audio settings. 15 | */ 16 | void Pyo::setup(int _nChannels, int _bufferSize, int _sampleRate, int _nAnalogChannels) { 17 | nChannels = _nChannels; 18 | bufferSize = _bufferSize; 19 | sampleRate = _sampleRate; 20 | nAnalogChannels = _nAnalogChannels; 21 | nTotalChannels = nChannels+nAnalogChannels; 22 | interpreter = pyo_new_interpreter(sampleRate, bufferSize, nTotalChannels); 23 | pyoInBuffer = reinterpret_cast(pyo_get_input_buffer_address(interpreter)); 24 | pyoOutBuffer = reinterpret_cast(pyo_get_output_buffer_address(interpreter)); 25 | pyoCallback = reinterpret_cast(pyo_get_embedded_callback_address(interpreter)); 26 | pyoId = pyo_get_server_id(interpreter); 27 | } 28 | 29 | /* 30 | ** Terminates this object's interpreter. 31 | */ 32 | Pyo::~Pyo() { 33 | pyo_end_interpreter(interpreter); 34 | } 35 | 36 | /* 37 | ** This function fills pyo's input buffers with new samples. Should be called 38 | ** once per process block, inside the host's render function. 39 | ** 40 | ** arguments: 41 | ** *buffer : float *, float pointer pointing to the host's input buffers. 42 | */ 43 | void Pyo::fillin(const float *buffer) { 44 | for (int i=0; i 2 | #include 3 | #include "Python.h" 4 | 5 | #ifndef __m_pyo_h_ 6 | #define __m_pyo_h_ 7 | 8 | #if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) 9 | #define INLINE inline 10 | extern "C" { 11 | #else 12 | #define INLINE 13 | #endif 14 | 15 | /* Unicode/string handling. */ 16 | #if PY_MAJOR_VERSION >= 3 17 | #define PyInt_AsLong PyLong_AsLong 18 | #define PY_STRING_CHECK(a) PyUnicode_Check(arg) 19 | #define PY_STRING_AS_STRING(a) PyUnicode_AsUTF8(a) 20 | #else 21 | #define PY_STRING_CHECK(a) (PyUnicode_Check(a) || PyBytes_Check(a)) 22 | #define PY_STRING_AS_STRING(a) PyBytes_AsString(a) 23 | #endif 24 | 25 | /* libpython handle. libpython must be made available to the program loaded 26 | ** in a new interpreter. */ 27 | static void *libpython_handle; 28 | 29 | /* 30 | ** Creates a new python interpreter and starts a pyo server in it. 31 | ** Each instance of pyo, in order to be fully independent of other 32 | ** instances, must be started in its own interpreter. An instance 33 | ** can be an object in a programming language or a plugin in a daw. 34 | ** 35 | ** arguments: 36 | ** sr : float, host sampling rate. 37 | ** bufsize : int, host buffer size. 38 | ** chnls : int, number of in/out channels of the pyo server. 39 | ** 40 | ** returns the new python thread's interpreter state. 41 | */ 42 | INLINE PyThreadState * pyo_new_interpreter(float sr, int bufsize, int chnls) { 43 | char msg[64]; 44 | PyThreadState *interp; 45 | if(!Py_IsInitialized()) { 46 | Py_InitializeEx(0); 47 | PyEval_InitThreads(); 48 | PyEval_ReleaseLock(); 49 | } 50 | 51 | /* This call hardcodes 2.7 as the python version to be used to embed pyo in 52 | a C or C++ program. This is not a good idea and must be fixed when everthing 53 | is stable. 54 | */ 55 | if (libpython_handle == NULL) { 56 | libpython_handle = dlopen("libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); 57 | } 58 | 59 | PyEval_AcquireLock(); /* get the GIL */ 60 | interp = Py_NewInterpreter(); /* add a new sub-interpreter */ 61 | PyRun_SimpleString("from pyo import *"); 62 | sprintf(msg, "_s_ = Server(%f, %d, %d, 1, 'embedded')", sr, chnls, bufsize); 63 | PyRun_SimpleString(msg); 64 | PyRun_SimpleString("_s_.boot()\n_s_.start()\n_s_.setServer()"); 65 | PyRun_SimpleString("_in_address_ = _s_.getInputAddr()"); 66 | PyRun_SimpleString("_out_address_ = _s_.getOutputAddr()"); 67 | PyRun_SimpleString("_server_id_ = _s_.getServerID()"); 68 | PyRun_SimpleString("_emb_callback_ = _s_.getEmbedICallbackAddr()"); 69 | PyEval_ReleaseThread(interp); 70 | return interp; 71 | } 72 | 73 | /* 74 | ** Returns the address, as unsigned long, of the pyo input buffer. 75 | ** Used this function if pyo's audio samples resolution is 32-bit. 76 | ** 77 | ** arguments: 78 | ** interp : pointer, pointer to the targeted Python thread state. 79 | ** 80 | ** returns an "unsigned long" that should be recast to a float pointer. 81 | */ 82 | INLINE unsigned long pyo_get_input_buffer_address(PyThreadState *interp) { 83 | PyObject *module, *obj; 84 | char *address; 85 | unsigned long uadd; 86 | PyEval_AcquireThread(interp); 87 | module = PyImport_AddModule("__main__"); 88 | obj = PyObject_GetAttrString(module, "_in_address_"); 89 | address = PY_STRING_AS_STRING(obj); 90 | uadd = strtoul(address, NULL, 0); 91 | PyEval_ReleaseThread(interp); 92 | return uadd; 93 | } 94 | 95 | /* 96 | ** Returns the address, as unsigned long long, of the pyo input buffer. 97 | ** Used this function if pyo's audio samples resolution is 64-bit. 98 | ** 99 | ** arguments: 100 | ** interp : pointer, pointer to the targeted Python thread state. 101 | ** 102 | ** returns an "unsigned long long" that should be recast to a double pointer. 103 | */ 104 | INLINE unsigned long long pyo_get_input_buffer_address_64(PyThreadState *interp) { 105 | PyObject *module, *obj; 106 | char *address; 107 | unsigned long long uadd; 108 | PyEval_AcquireThread(interp); 109 | module = PyImport_AddModule("__main__"); 110 | obj = PyObject_GetAttrString(module, "_in_address_"); 111 | address = PY_STRING_AS_STRING(obj); 112 | uadd = strtoull(address, NULL, 0); 113 | PyEval_ReleaseThread(interp); 114 | return uadd; 115 | } 116 | 117 | /* 118 | ** Returns the address, as unsigned long, of the pyo output buffer. 119 | ** Used this function if pyo's audio samples resolution is 32-bit. 120 | ** 121 | ** arguments: 122 | ** interp : pointer, pointer to the targeted Python thread state. 123 | ** 124 | ** returns an "unsigned long" that should be recast to a float pointer. 125 | */ 126 | INLINE unsigned long pyo_get_output_buffer_address(PyThreadState *interp) { 127 | PyObject *module, *obj; 128 | char *address; 129 | unsigned long uadd; 130 | PyEval_AcquireThread(interp); 131 | module = PyImport_AddModule("__main__"); 132 | obj = PyObject_GetAttrString(module, "_out_address_"); 133 | address = PY_STRING_AS_STRING(obj); 134 | uadd = strtoul(address, NULL, 0); 135 | PyEval_ReleaseThread(interp); 136 | return uadd; 137 | } 138 | 139 | /* 140 | ** Returns the address, as unsigned long, of the pyo embedded callback. 141 | ** This callback must be called in the host's perform routine whenever 142 | ** pyo has to compute a new buffer of samples. 143 | ** 144 | ** arguments: 145 | ** interp : pointer, pointer to the targeted Python thread state. 146 | ** 147 | ** returns an "unsigned long" that should be recast to a void pointer. 148 | ** 149 | ** The callback should be called with the server id (int) as argument. 150 | ** 151 | ** Prototype: 152 | ** void (*callback)(int); 153 | */ 154 | INLINE unsigned long pyo_get_embedded_callback_address(PyThreadState *interp) { 155 | PyObject *module, *obj; 156 | char *address; 157 | unsigned long uadd; 158 | PyEval_AcquireThread(interp); 159 | module = PyImport_AddModule("__main__"); 160 | obj = PyObject_GetAttrString(module, "_emb_callback_"); 161 | address = PY_STRING_AS_STRING(obj); 162 | uadd = strtoul(address, NULL, 0); 163 | PyEval_ReleaseThread(interp); 164 | return uadd; 165 | } 166 | 167 | /* 168 | ** Returns the pyo server id of this thread, as an integer. 169 | ** The id must be pass as argument to the callback function. 170 | ** 171 | ** arguments: 172 | ** interp : pointer, pointer to the targeted Python thread state. 173 | ** 174 | ** returns an integer. 175 | */ 176 | INLINE int pyo_get_server_id(PyThreadState *interp) { 177 | PyObject *module, *obj; 178 | int id; 179 | PyEval_AcquireThread(interp); 180 | module = PyImport_AddModule("__main__"); 181 | obj = PyObject_GetAttrString(module, "_server_id_"); 182 | id = PyInt_AsLong(obj); 183 | PyEval_ReleaseThread(interp); 184 | return id; 185 | } 186 | 187 | /* 188 | ** Closes the interpreter linked to the thread state given as argument. 189 | ** 190 | ** arguments: 191 | ** interp : pointer, pointer to the targeted Python thread state. 192 | */ 193 | INLINE void pyo_end_interpreter(PyThreadState *interp) { 194 | PyEval_AcquireThread(interp); 195 | PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()"); 196 | PyEval_ReleaseThread(interp); 197 | 198 | /* Old method (causing segfault) */ 199 | //PyEval_AcquireThread(interp); 200 | //Py_EndInterpreter(interp); 201 | //PyEval_ReleaseLock(); 202 | 203 | /* New method (seems to be ok) */ 204 | PyThreadState_Swap(interp); 205 | PyThreadState_Swap(NULL); 206 | PyThreadState_Clear(interp); 207 | PyThreadState_Delete(interp); 208 | 209 | if (libpython_handle != NULL) { 210 | dlclose(libpython_handle); 211 | } 212 | } 213 | 214 | /* 215 | ** Shutdown and reboot the pyo server while keeping current in/out buffers. 216 | ** 217 | ** arguments: 218 | ** interp : pointer, pointer to the targeted Python thread state. 219 | */ 220 | INLINE void pyo_server_reboot(PyThreadState *interp) { 221 | PyEval_AcquireThread(interp); 222 | PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()"); 223 | PyRun_SimpleString("_s_.boot(newBuffer=False).start()"); 224 | PyEval_ReleaseThread(interp); 225 | } 226 | 227 | /* 228 | ** Reboot the pyo server with new sampling rate and buffer size. 229 | ** 230 | ** arguments: 231 | ** interp : pointer, pointer to the targeted Python thread state. 232 | ** sr : float, host sampling rate. 233 | ** bufsize : int, host buffer size. 234 | */ 235 | INLINE void pyo_set_server_params(PyThreadState *interp, float sr, int bufsize) { 236 | char msg[64]; 237 | PyEval_AcquireThread(interp); 238 | PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()"); 239 | sprintf(msg, "_s_.setSamplingRate(%f)", sr); 240 | PyRun_SimpleString(msg); 241 | sprintf(msg, "_s_.setBufferSize(%d)", bufsize); 242 | PyRun_SimpleString(msg); 243 | PyRun_SimpleString("_s_.boot(newBuffer=False).start()"); 244 | PyEval_ReleaseThread(interp); 245 | } 246 | 247 | /* 248 | ** Add a MIDI event in the pyo server processing chain. When used in 249 | ** an embedded framework, pyo can't open MIDI ports by itself. MIDI 250 | ** inputs must be handled by the host program and sent to pyo with 251 | ** the pyo_add_midi_event function. 252 | ** 253 | ** arguments: 254 | ** interp : pointer, pointer to the targeted Python thread state. 255 | ** status : int, status byte. 256 | ** data1 : int, first data byte. 257 | ** data2 : int, second data byte. 258 | */ 259 | INLINE void pyo_add_midi_event(PyThreadState *interp, int status, int data1, int data2) { 260 | char msg[64]; 261 | PyEval_AcquireThread(interp); 262 | sprintf(msg, "_s_.addMidiEvent(%d, %d, %d)", status, data1, data2); 263 | PyRun_SimpleString(msg); 264 | PyEval_ReleaseThread(interp); 265 | } 266 | 267 | /* 268 | ** Returns 1 if the pyo server is started for the given thread, 269 | ** Otherwise returns 0. 270 | ** 271 | ** arguments: 272 | ** interp : pointer, pointer to the targeted Python thread state. 273 | */ 274 | INLINE int pyo_is_server_started(PyThreadState *interp) { 275 | int started; 276 | PyObject *module, *obj; 277 | PyEval_AcquireThread(interp); 278 | PyRun_SimpleString("started = _s_.getIsStarted()"); 279 | module = PyImport_AddModule("__main__"); 280 | obj = PyObject_GetAttrString(module, "started"); 281 | started = PyInt_AsLong(obj); 282 | PyEval_ReleaseThread(interp); 283 | return started; 284 | } 285 | 286 | /* 287 | ** Execute a python script "file" in the given thread's interpreter (interp). 288 | ** A pre-allocated string "msg" must be given to create the python command 289 | ** used for error handling. An integer "add" is needed to indicate if the 290 | ** pyo server should be reboot or not. 291 | ** 292 | ** arguments: 293 | ** interp : pointer, pointer to the targeted Python thread state. 294 | ** file : char *, filename to execute as a python script. The file is first 295 | ** searched in the current working directory. If not found, 296 | ** the module will try to open it as an absolute path. 297 | ** msg : char *, pre-allocated string used to create the python command 298 | ** used for error handling. 299 | ** add, int, if positive, the commands in the file will be added to whatever 300 | ** is already running in the pyo server. If 0, the server will be 301 | ** shutdown and reboot before executing the file. 302 | */ 303 | INLINE int pyo_exec_file(PyThreadState *interp, const char *file, char *msg, int add) { 304 | int ok, err = 0; 305 | PyObject *module, *obj; 306 | PyEval_AcquireThread(interp); 307 | sprintf(msg, "import os\n_ok_ = os.path.isfile('./%s')", file); 308 | PyRun_SimpleString(msg); 309 | sprintf(msg, "if not _ok_:\n _ok_ = os.path.isfile('%s')", file); 310 | PyRun_SimpleString(msg); 311 | module = PyImport_AddModule("__main__"); 312 | obj = PyObject_GetAttrString(module, "_ok_"); 313 | ok = PyInt_AsLong(obj); 314 | if (ok) { 315 | sprintf(msg, "try:\n exec(open('./%s').read())\nexcept:\n exec(open('%s').read())", 316 | file, file); 317 | if (!add) { 318 | PyRun_SimpleString("_s_.setServer()\n_s_.stop()\n_s_.shutdown()"); 319 | PyRun_SimpleString("_s_.boot(newBuffer=False).start()"); 320 | } 321 | PyRun_SimpleString(msg); 322 | } 323 | else 324 | err = 1; 325 | PyEval_ReleaseThread(interp); 326 | return err; 327 | } 328 | 329 | /* 330 | ** Execute a python statement "msg" in the thread's interpreter "interp". 331 | ** If "debug" is true, the statement will be executed in a try - except 332 | ** block. The error message, if any, will be write back in the *msg 333 | ** pointer and the function will return 1. If no error occured, the 334 | ** function returned 0. If debug is false, the statement is executed 335 | ** without any error checking (unsafe but faster). 336 | ** 337 | ** arguments: 338 | ** interp : pointer, pointer to the targeted Python thread state. 339 | ** msg : char *, pointer to a string containing the statement to execute. 340 | ** In debug mode, if an error occured, the output log will 341 | ** be write back in this string. 342 | ** debug, int, if positive, the commands will be executed in a try-except 343 | ** statement. If 0, there will be no error checking, which is 344 | ** much faster. 345 | */ 346 | INLINE int pyo_exec_statement(PyThreadState *interp, char *msg, int debug) { 347 | int err = 0; 348 | if (debug) { 349 | PyObject *module, *obj; 350 | char pp[26] = "_error_=None\ntry:\n "; 351 | memmove(msg + strlen(pp), msg, strlen(msg)+1); 352 | memmove(msg, pp, strlen(pp)); 353 | strcat(msg, "\nexcept Exception, _e_:\n _error_=str(_e_)"); 354 | PyEval_AcquireThread(interp); 355 | PyRun_SimpleString(msg); 356 | module = PyImport_AddModule("__main__"); 357 | obj = PyObject_GetAttrString(module, "_error_"); 358 | if (obj != Py_None) { 359 | strcpy(msg, PY_STRING_AS_STRING(obj)); 360 | err = 1; 361 | } 362 | PyEval_ReleaseThread(interp); 363 | } 364 | else { 365 | PyEval_AcquireThread(interp); 366 | PyRun_SimpleString(msg); 367 | PyEval_ReleaseThread(interp); 368 | } 369 | return err; 370 | } 371 | 372 | #if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) 373 | } 374 | #endif 375 | 376 | #endif /* __m_pyo_h_ */ 377 | --------------------------------------------------------------------------------