├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── diff └── pd-cyclone.diff ├── love ├── freqmod │ ├── conf.lua │ └── main.lua ├── input │ ├── add~.pd │ ├── all~.pd │ ├── conf.lua │ ├── main.lua │ └── main.pd ├── major-minor │ ├── conf.lua │ ├── inst.pd │ ├── main.lua │ ├── main.pd │ ├── mario │ │ ├── 1 │ │ │ ├── noise.trk │ │ │ ├── pulse1.trk │ │ │ ├── pulse2.trk │ │ │ └── triangle.trk │ │ ├── 2 │ │ │ ├── noise.trk │ │ │ ├── pulse1.trk │ │ │ ├── pulse2.trk │ │ │ └── triangle.trk │ │ ├── 3 │ │ │ ├── noise.trk │ │ │ ├── pulse1.trk │ │ │ ├── pulse2.trk │ │ │ └── triangle.trk │ │ ├── 4 │ │ │ ├── noise.trk │ │ │ ├── pulse1.trk │ │ │ ├── pulse2.trk │ │ │ └── triangle.trk │ │ ├── 5 │ │ │ ├── noise.trk │ │ │ ├── pulse1.trk │ │ │ ├── pulse2.trk │ │ │ └── triangle.trk │ │ ├── inst │ │ │ ├── bass.inst │ │ │ ├── hhc.inst │ │ │ ├── hho.inst │ │ │ ├── puls.inst │ │ │ └── rim.inst │ │ ├── loop.trk │ │ └── main.trk │ └── pulse~.pd ├── metronome │ ├── conf.lua │ ├── main.lua │ └── main.pd ├── pdgui.lua ├── pdmain.lua ├── quicksort │ ├── conf.lua │ ├── ks.pd │ ├── main.lua │ └── main.pd ├── random-sound │ ├── conf.lua │ ├── main.lua │ └── main.pd ├── samplerate.lua └── thread │ ├── main.lua │ └── thread.lua ├── pd ├── lib │ ├── ad.pd │ ├── ct.pd │ ├── cupqb-help.pd │ ├── cupqb.pd │ ├── fm2op~.pd │ ├── pan~.pd │ ├── test_abs.pd │ ├── zp.pd │ └── zp~.pd └── test.pd ├── pdmeta.lua ├── src ├── PdObject.cpp ├── PdObject.hpp ├── luacompat.hpp └── main.cpp └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # build files 2 | *.o 3 | *.so 4 | *.dll 5 | *.dylib 6 | *.zip 7 | 8 | # VSCode folder 9 | .vscode 10 | 11 | # layout files 12 | .DS_Store 13 | .directory 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2022 Mike Will 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | LIBPD_DIR ?= $(shell pwd)/../libpd 3 | 4 | # detect platform 5 | UNAME = $(shell uname) 6 | LDFLAGS = -lstdc++ 7 | ifeq ($(UNAME), Darwin) # Mac 8 | EXT = dylib 9 | LDFLAGS += -std=c++11 -arch x86_64 -dynamiclib 10 | CXXFLAGS = -std=c++11 -arch x86_64 11 | LUA_DIR = /usr/local/include/luajit-2.1 12 | else 13 | LDFLAGS += -shared 14 | ifeq ($(OS), Windows_NT) # Windows, use Mingw 15 | EXT = dll 16 | LDLIBS = -Wl,--export-all-symbols -static-libgcc -lws2_32 -lkernel32 17 | LUA_DIR = /mingw64/include/luajit-2.1 18 | else # assume Linux 19 | EXT = so 20 | LUA_DIR = /usr/include/luajit-2.1 21 | endif 22 | endif 23 | # LUA_DIR = ../lua 24 | 25 | SRC = $(wildcard $(shell pwd)/src/*.cpp) 26 | LIBPD = $(LIBPD_DIR)/libs/libpd 27 | TARGET = luapd.$(EXT) 28 | LIBLUA = -lluajit-5.1 29 | # LIBLUA = $(LUA_DIR)/liblua.a 30 | LDLIBS += -lm -lpthread $(LIBLUA) 31 | CXXFLAGS += -I$(LUA_DIR) \ 32 | -I$(LIBPD_DIR)/libpd_wrapper -I$(LIBPD_DIR)/libpd_wrapper/util \ 33 | -I$(LIBPD_DIR)/pure-data/src -I$(LIBPD_DIR)/cpp -I./src -O3 -fPIC \ 34 | -Wall -Wextra -Wshadow -Wstrict-aliasing 35 | 36 | ifeq ($(DEBUG), true) 37 | CXXFLAGS += -g -O0 38 | endif 39 | 40 | .PHONY: dynamic clean 41 | 42 | all: CXXFLAGS += -DEXTERN=extern 43 | all: $(SRC:.cpp=.o) $(LIBPD).a 44 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $(TARGET) 45 | 46 | $(LIBPD).a: 47 | cd $(LIBPD_DIR) && make STATIC=true 48 | 49 | dynamic: $(SRC:.cpp=.o) $(LIBPD).$(EXT) 50 | $(CXX) $(LDFLAGS) $^ $(LIBLUA) -o $(TARGET) 51 | 52 | $(LIBPD).$(EXT): 53 | cd $(LIBPD_DIR) && make 54 | 55 | clean: 56 | rm -f $(shell pwd)/src/*.o 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LuaPd 2 | libpd bindings for lua and love2d 3 | 4 | ## Building Luapd 5 | 6 | ### Clone libpd and its submodules: 7 | 8 | git clone --recurse-submodules https://github.com/libpd/libpd.git 9 | 10 | ### Build libpd: 11 | 12 | cd libpd 13 | make STATIC=true 14 | 15 | ### Install LuaJIT 16 | 17 | On Debian/Ubuntu: 18 | 19 | apt install luajit libluajit-5.1-dev 20 | 21 | On Windows with MSYS2: 22 | 23 | pacman -S mingw-w64-i686-luajit mingw-w64-x86_64-luajit 24 | 25 | On MacOS with Brew: 26 | 27 | brew install luajit 28 | 29 | ### Build luapd: 30 | 31 | Luapd's Makefile assumes that the libpd folder is adjacent to the luapd folder. 32 | 33 | cd luapd 34 | make 35 | 36 | By default, `make` will try to link statically with libpd. You can also link dynamically with: 37 | 38 | make dynamic 39 | 40 | You can test luapd with: 41 | 42 | luajit test.lua 43 | 44 | ## Running LÖVE Examples 45 | 46 | ### Clone a custom fork of libpd 47 | git clone --recurse-submodules https://github.com/myQwil/libpd.git 48 | 49 | ### Clone the Quilt and Cyclone libraries adjacent to libpd: 50 | 51 | git clone --recurse-submodules https://github.com/myQwil/pd-quilt.git 52 | git clone https://github.com/porres/pd-cyclone.git 53 | 54 | Then build libpd and luapd. 55 | 56 | ### For Windows users 57 | 58 | There are 3 additional .dll files that need to be placed adjacent to the main lua file: 59 | 60 | - libgcc_s_seh-1.dll 61 | - libstdc++-6.dll 62 | - libwinpthread-1.dll 63 | 64 | These files are included in the release builds for Windows. They can also be downloaded separately here: 65 | -------------------------------------------------------------------------------- /diff/pd-cyclone.diff: -------------------------------------------------------------------------------- 1 | diff --git a/cyclone_objects/binaries/audio/greaterthan.c b/cyclone_objects/binaries/audio/greaterthan.c 2 | index de75b758..c6e2a6a3 100644 3 | --- a/cyclone_objects/binaries/audio/greaterthan.c 4 | +++ b/cyclone_objects/binaries/audio/greaterthan.c 5 | @@ -74,4 +74,5 @@ CYCLONE_OBJ_API void greaterthan_tilde_setup(void) 6 | sizeof(t_greaterthan), CLASS_DEFAULT, A_DEFFLOAT, 0); 7 | class_addmethod(greaterthan_class, nullfn, gensym("signal"), 0); 8 | class_addmethod(greaterthan_class, (t_method)greaterthan_dsp, gensym("dsp"), A_CANT, 0); 9 | + class_addcreator((t_newmethod)greaterthan_new, gensym(">~"), A_DEFFLOAT, 0); 10 | } 11 | \ No newline at end of file 12 | diff --git a/cyclone_objects/binaries/audio/lessthan.c b/cyclone_objects/binaries/audio/lessthan.c 13 | index 5395b95b..1cae4111 100644 14 | --- a/cyclone_objects/binaries/audio/lessthan.c 15 | +++ b/cyclone_objects/binaries/audio/lessthan.c 16 | @@ -74,4 +74,5 @@ CYCLONE_OBJ_API void lessthan_tilde_setup(void) 17 | sizeof(t_lessthan), CLASS_DEFAULT, A_DEFFLOAT, 0); 18 | class_addmethod(lessthan_class, nullfn, gensym("signal"), 0); 19 | class_addmethod(lessthan_class, (t_method)lessthan_dsp, gensym("dsp"), A_CANT, 0); 20 | + class_addcreator((t_newmethod)lessthan_new, gensym("<~"), A_DEFFLOAT, 0); 21 | } 22 | \ No newline at end of file 23 | -------------------------------------------------------------------------------- /love/freqmod/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.title = 'Frequency Modulator' 3 | t.window.width = 701 4 | t.window.height = 701 5 | end 6 | -------------------------------------------------------------------------------- /love/freqmod/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | package.path = '../?.lua;' .. package.path 6 | local lpd = require('pdmain') 7 | local pd, obj = lpd.pd, lpd.obj 8 | local patch ---@type PdPatch 9 | 10 | local maxfrq = 300 11 | local maxidx = 3200 12 | local mouse_y = 0 13 | local portamento = 0 14 | 15 | local width, height = love.graphics.getWidth() - 1, love.graphics.getHeight() - 1 16 | local isPressed = { false, false } 17 | local isAuto = false 18 | local hintx = width - 250 19 | local grid = {} 20 | local fm = { 21 | modfrq = 1 22 | , modidx = 150 23 | , carfrq = 400 24 | } 25 | 26 | local clr = { 27 | over = { 0.4 , 0.1 , 0.1 } 28 | , under = { 0.05, 0.15, 0.15 } 29 | } 30 | 31 | function obj.float(dest, num) 32 | fm[dest] = num 33 | end 34 | 35 | local function slope(x, min, max, len) 36 | local m = (max - min) / len 37 | return m * x + min; 38 | end 39 | 40 | function love.load() 41 | local iter = 1 / 16 42 | for i = iter, .999, iter * 2 do 43 | local wi, hi = width * i, height * i 44 | grid[#grid + 1] = { line = { wi, 0, wi, height }, color = clr.under } 45 | grid[#grid + 1] = { line = { 0, hi, width, hi }, color = clr.under } 46 | end 47 | for i = iter * 2, .999, iter * 2 do 48 | local wi, hi = width * i, height * i 49 | grid[#grid + 1] = { line = { wi, 0, wi, height }, color = clr.over } 50 | grid[#grid + 1] = { line = { 0, hi, width, hi }, color = clr.over } 51 | end 52 | 53 | lpd.init() 54 | patch = lpd.open { patch = '../../pd/test.pd', volume = 0.2 } 55 | love.keyboard.setKeyRepeat(true) 56 | pd:subscribe('modfrq') 57 | pd:subscribe('modidx') 58 | pd:subscribe('carfrq') 59 | end 60 | 61 | function love.update() 62 | lpd.update() 63 | -- pd:receiveMessages() 64 | end 65 | 66 | function love.draw() 67 | -- grid 68 | love.graphics.setLineWidth(1) 69 | for _, v in next, grid do 70 | love.graphics.setColor(v.color) 71 | love.graphics.line(v.line) 72 | end 73 | 74 | -- values 75 | love.graphics.setColor(1, 1, 1) 76 | lpd.draw() 77 | love.graphics.print('mod-freq: ' .. fm.modfrq, 0, 20) 78 | love.graphics.print('mod-index: ' .. fm.modidx, 0, 40) 79 | 80 | -- hints 81 | love.graphics.printf('[-] / [+]\n[tab]\n[space]\n[escape]' 82 | , hintx, 0, 120, 'right') 83 | love.graphics.print('portamento\nmouse-grab\nauto\nquit' 84 | , hintx + 130, 0); 85 | 86 | -- mouse press 87 | if isPressed[1] then 88 | love.graphics.setColor(1, .8, .8) 89 | love.graphics.print('Mouse1', 150, 0) 90 | end 91 | if isPressed[2] then 92 | love.graphics.setColor(.8, 1, .8) 93 | love.graphics.print('Mouse2', 300, 0) 94 | end 95 | 96 | -- circle 97 | love.graphics.setLineWidth(2) 98 | love.graphics.circle('line' 99 | , slope(fm.modfrq, 0, width, maxfrq) 100 | , slope(fm.modidx, height, 0, maxidx) 101 | , 1 + fm.carfrq / 8 102 | ) 103 | end 104 | 105 | local function pancar(x, y) 106 | pd:sendFloat('pan', slope(x, -45, 45, width)) 107 | if y ~= mouse_y then 108 | pd:sendFloat('carrier-freq', (y > mouse_y) and -1 or 1) 109 | end 110 | end 111 | 112 | local function fmod(x, y) 113 | pd:sendFloat('mod-freq', slope(x, 0, maxfrq, width)) 114 | pd:sendFloat('mod-index', slope(y, maxidx, 0, height)) 115 | end 116 | 117 | local function tone(x, y) 118 | pd:sendFloat('tone-pos', slope(x, -45, 45, width)) 119 | pd:sendMessage('tone', 'pitch', { slope(y, 100, 0, height) }) 120 | pd:sendBang('tone') 121 | end 122 | 123 | local function none() end 124 | 125 | local funcs = { 126 | [false] = { [false] = none, [true] = fmod } 127 | , [true] = { [false] = fmod, [true] = pancar } 128 | } 129 | 130 | local clkLeft = funcs[isAuto] 131 | local onClick = { clkLeft[true], tone } 132 | 133 | function love.mousepressed(x, y, btn) 134 | onClick[btn](x, y) 135 | isPressed[btn] = true 136 | end 137 | 138 | function love.mousereleased(_, _, btn) 139 | isPressed[btn] = false 140 | end 141 | 142 | function love.mousemoved(x, y) 143 | clkLeft[love.mouse.isDown(1)](x, y) 144 | mouse_y = y 145 | end 146 | 147 | function love.wheelmoved(_, y) 148 | pd:sendFloat('carrier-freq', y * 25) 149 | end 150 | 151 | local kpress = { 152 | ['+'] = function() 153 | portamento = math.max(0, portamento + 25) 154 | pd:sendFloat('portamento', portamento) 155 | end 156 | , ['-'] = function() 157 | portamento = math.max(0, portamento - 25) 158 | pd:sendFloat('portamento', portamento) 159 | end 160 | , tab = function() 161 | local state = love.mouse.isGrabbed() 162 | love.mouse.setGrabbed(not state) 163 | end 164 | , space = function() 165 | isAuto = not isAuto 166 | clkLeft = funcs[isAuto] 167 | onClick[1] = clkLeft[true] 168 | end 169 | , lctrl = function() tone(love.mouse.getPosition()) end 170 | , escape = function() love.event.push('quit') end 171 | } 172 | kpress['='] = kpress['+'] 173 | 174 | function love.keypressed(k) 175 | if kpress[k] then 176 | kpress[k]() 177 | end 178 | end 179 | 180 | function love.quit() 181 | pd:closePatch(patch) 182 | pd:computeAudio(false) 183 | end 184 | -------------------------------------------------------------------------------- /love/input/add~.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 350 250 14; 2 | #X obj 90 110 inlet~; 3 | #X obj 190 110 inlet~; 4 | #X obj 90 160 throw~ L; 5 | #X obj 190 160 throw~ R; 6 | #X text 120 70 stereo throw; 7 | #X obj 140 40 cnv 15 60 24 empty empty add~ 4 12 0 18 #000000 #fcfcfc 8 | 0; 9 | #X connect 0 0 2 0; 10 | #X connect 1 0 3 0; 11 | -------------------------------------------------------------------------------- /love/input/all~.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 350 250 14; 2 | #X obj 90 110 catch~ L; 3 | #X obj 190 110 catch~ R; 4 | #X obj 90 160 outlet~; 5 | #X obj 190 160 outlet~; 6 | #X text 120 70 stereo catch; 7 | #X obj 140 40 cnv 15 60 24 empty empty all~ 4 12 0 18 #000000 #fcfcfc 8 | 0; 9 | #X connect 0 0 2 0; 10 | #X connect 1 0 3 0; 11 | -------------------------------------------------------------------------------- /love/input/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.title = 'Input Effects' 3 | end 4 | -------------------------------------------------------------------------------- /love/input/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | local ext = { 6 | ['Linux'] = 'so' 7 | , ['Windows'] = 'dll' 8 | , ['OS X'] = 'dylib' 9 | } 10 | local stros = love.system.getOS() 11 | package.cpath = '../../?.' .. ext[stros] .. ';' .. package.cpath 12 | package.path = '../?.lua;' .. package.path 13 | 14 | local Pd = require('luapd') ---@type Pd 15 | local pd = Pd.Base() 16 | local gui = require('pdgui')(pd) 17 | local patch ---@type PdPatch 18 | local silence ---@type PdArray 19 | local buttons, sliders 20 | 21 | local mic ---@type love.RecordingDevice 22 | local source ---@type love.Source 23 | local sdIn, sdOut ---@type love.SoundData, love.SoundData 24 | 25 | local ptr ---@type lightuserdata 26 | local srate = require('samplerate') 27 | local ticks ,bufs ,bitdepth ,chIn ,chOut ,i ,n ,step 28 | = 1 ,33 ,16 ,2 ,2 ,0 ,0 ,0 29 | 30 | local function set_mode(self) 31 | pd:sendFloat(self.dest, self.val) 32 | end 33 | 34 | function love.load() 35 | love.graphics.setFont(love.graphics.newFont(16)) 36 | if not pd:init(chIn, chOut, srate) then 37 | print('Could not initialize pd') 38 | love.event.quit() 39 | end 40 | pd:addToSearchPath('../../pd/lib') 41 | patch = pd:openPatch('main.pd') 42 | pd:computeAudio(true) 43 | 44 | local devices = love.audio.getRecordingDevices() 45 | mic = devices[1] 46 | local size = pd.blockSize() * ticks 47 | if not mic:start(size * bufs, srate, bitdepth, chIn) then 48 | print("Could not start a recording device") 49 | love.event.quit() 50 | end 51 | 52 | sdOut = love.sound.newSoundData(size, srate, bitdepth, chOut) 53 | source = love.audio.newQueueableSource(srate, bitdepth, chOut, bufs) 54 | size = size * chIn 55 | silence = Pd.Array(size / 2) -- short to float 56 | step = size * 2 -- short to char 57 | 58 | local dlr = patch:dollarZeroStr() 59 | local w, h = love.graphics.getDimensions() 60 | gui.button.dest = dlr..'mode' 61 | gui.button.size = 33 62 | local half = 33 / 2 63 | gui.button.click = set_mode 64 | buttons = { 65 | gui.button(w * 1/3 - half, h * 1/3 - half, { val = 0 66 | , label = { text = 'echo' } }) 67 | , gui.button(w * 2/3 - half, h * 1/3 - half, { val = 1 68 | , label = { text = 'cos' } }) 69 | , gui.button(w * 1/3 - half, h * 2/3 - half, { val = 2 70 | , label = { text = 'octave\ndoubler', y = -20 } }) 71 | , gui.button(w * 2/3 - half, h * 2/3 - half, { val = 3 72 | , label = { text = 'timbre\nstamp', y = -20 } }) 73 | , gui.button(w * 1/2 - half, h * 1/2 - half, { val = 4 74 | , label = { text = 'off' } }) 75 | } 76 | 77 | gui.slider.rad = 25 78 | gui.slider.len = h - 50 79 | local opt1 = { dest = dlr..'opt1', min = 0, max = 1, num = 0 80 | , label = { text = '', x = -5 }, fmt = '%s%.4g' } 81 | local opt2 = { dest = dlr..'opt2', min = 0, max = 1, num = 0 82 | , label = { text = '', x = -5 }, fmt = '%s%.4g' } 83 | local vol = { dest = dlr..'vol' , min = -60, max = 0, num = 0 84 | , label = { text = 'volume', x = -100 }, change = gui.volChange } 85 | sliders = { 86 | gui.slider(20, 25, { y = opt1 }, { rgb = { .25, .66, .66 } }) 87 | , gui.slider(90, 25, { y = opt2 }, { rgb = { .33, .5, .66 } }) 88 | , gui.slider(w - 140, 25, { y = vol }, { rgb = { .75, .5, .75 } }) 89 | } 90 | sliders[3]:send() 91 | end 92 | 93 | function love.update(dt) 94 | while source:getFreeBufferCount() > 0 do 95 | if i + step > n then 96 | i = 0 97 | sdIn = mic:getData() 98 | if sdIn then 99 | ptr = sdIn:getPointer() 100 | n = sdIn:getSize() 101 | else 102 | ptr = silence() 103 | n = step 104 | end 105 | end 106 | pd:processShort(ticks, Pd.offset(ptr, i), sdOut:getPointer()) 107 | source:queue(sdOut) 108 | i = i + step 109 | end 110 | source:play() 111 | 112 | gui.updateSliders(sliders) 113 | for j = #buttons, 1, -1 do 114 | buttons[j]:update(dt) 115 | end 116 | end 117 | 118 | function love.mousepressed(x, y) 119 | for j = #buttons, 1, -1 do 120 | if buttons[j]:mousepressed(x, y) then return end 121 | end 122 | end 123 | 124 | function love.draw() 125 | for j = 1, #sliders do sliders[j]:draw() end 126 | for j = 1, #buttons do buttons[j]:draw() end 127 | end 128 | 129 | function love.quit() 130 | pd:closePatch(patch) 131 | pd:computeAudio(false) 132 | end 133 | -------------------------------------------------------------------------------- /love/input/main.pd: -------------------------------------------------------------------------------- 1 | #N canvas 150 400 450 300 14; 2 | #N canvas 600 400 450 300 echo 0; 3 | #X obj 70 120 adc~; 4 | #X obj 40 20 inlet; 5 | #X obj 40 50 switch~; 6 | #X obj 300 20 r \$0opt1; 7 | #X obj 160 230 delwrite~ \$0-echoR 500; 8 | #X obj 160 260 delwrite~ \$0-echoL 500; 9 | #X obj 160 120 delread4~ \$0-echoL; 10 | #X obj 160 150 delread4~ \$0-echoR; 11 | #X obj 120 20 r \$0opt2; 12 | #X obj 70 190 *~; 13 | #X obj 130 190 *~; 14 | #X obj 120 80 zp~ 25 0.5; 15 | #X obj 300 80 zp~ 25 150; 16 | #X obj 120 50 sly log 0.001 0.97 1; 17 | #X obj 300 50 sly 50 500 1; 18 | #X obj 70 260 add~; 19 | #X connect 0 0 9 0; 20 | #X connect 0 1 10 0; 21 | #X connect 1 0 2 0; 22 | #X connect 3 0 14 0; 23 | #X connect 6 0 9 0; 24 | #X connect 7 0 10 0; 25 | #X connect 8 0 13 0; 26 | #X connect 9 0 5 0; 27 | #X connect 9 0 15 0; 28 | #X connect 10 0 4 0; 29 | #X connect 10 0 15 1; 30 | #X connect 11 0 9 1; 31 | #X connect 11 0 10 1; 32 | #X connect 12 0 7 0; 33 | #X connect 12 0 6 0; 34 | #X connect 13 0 11 0; 35 | #X connect 14 0 12 0; 36 | #X restore 50 170 pd echo; 37 | #X obj 50 140 == 0; 38 | #N canvas 600 400 450 300 cos 0; 39 | #X obj 170 100 adc~; 40 | #X obj 40 40 inlet; 41 | #X obj 40 70 switch~; 42 | #X obj 170 140 cos~; 43 | #X obj 220 140 cos~; 44 | #X obj 170 180 add~; 45 | #X connect 0 0 3 0; 46 | #X connect 0 1 4 0; 47 | #X connect 1 0 2 0; 48 | #X connect 3 0 5 0; 49 | #X connect 4 0 5 1; 50 | #X restore 130 170 pd cos; 51 | #X obj 130 140 == 1; 52 | #N canvas 600 400 450 300 8ve 0; 53 | #X obj 40 30 inlet; 54 | #X obj 40 60 switch~; 55 | #X obj 120 120 adc~; 56 | #X obj 120 200 add~; 57 | #N canvas 730 350 450 450 8ve 0; 58 | #X obj 60 110 moses 1; 59 | #X obj 80 140 mtof; 60 | #X obj 190 200 samplerate~; 61 | #X obj 80 200 t f b; 62 | #X obj 80 370 +~; 63 | #X obj 80 230 +; 64 | #X obj 80 170 expr 500/$f1; 65 | #X obj 80 290 line~; 66 | #X obj 80 260 pack 0 20; 67 | #X text 130 140 fundamental frequency; 68 | #X text 200 170 1/2 period \, in msec; 69 | #X text 200 250 estimate fiddle~ delay; 70 | #X obj 190 230 expr 2048000/$f1; 71 | #X text 200 270 as one window (in msec); 72 | #X obj 60 80 sigmund~ -npts 2048; 73 | #X obj 60 20 inlet~; 74 | #X obj 80 400 outlet~; 75 | #X obj 70 50 delwrite~ G06-L 100; 76 | #X obj 80 320 vd~ G06-L; 77 | #X obj 190 320 delread~ G06-L; 78 | #X connect 0 1 1 0; 79 | #X connect 1 0 6 0; 80 | #X connect 2 0 12 0; 81 | #X connect 3 0 5 0; 82 | #X connect 3 1 2 0; 83 | #X connect 4 0 16 0; 84 | #X connect 5 0 8 0; 85 | #X connect 6 0 3 0; 86 | #X connect 7 0 18 0; 87 | #X connect 8 0 7 0; 88 | #X connect 12 0 5 1; 89 | #X connect 12 0 19 0; 90 | #X connect 14 0 0 0; 91 | #X connect 15 0 14 0; 92 | #X connect 15 0 17 0; 93 | #X connect 18 0 4 0; 94 | #X connect 19 0 4 1; 95 | #X restore 120 160 pd 8ve; 96 | #N canvas 1180 350 450 450 8ve 0; 97 | #X obj 60 110 moses 1; 98 | #X obj 80 140 mtof; 99 | #X obj 190 200 samplerate~; 100 | #X obj 80 200 t f b; 101 | #X obj 80 370 +~; 102 | #X obj 80 230 +; 103 | #X obj 80 170 expr 500/$f1; 104 | #X obj 80 290 line~; 105 | #X obj 80 260 pack 0 20; 106 | #X text 130 140 fundamental frequency; 107 | #X text 200 170 1/2 period \, in msec; 108 | #X text 200 250 estimate fiddle~ delay; 109 | #X obj 190 230 expr 2048000/$f1; 110 | #X text 200 270 as one window (in msec); 111 | #X obj 60 80 sigmund~ -npts 2048; 112 | #X obj 60 20 inlet~; 113 | #X obj 80 400 outlet~; 114 | #X obj 70 50 delwrite~ G06-R 100; 115 | #X obj 80 320 vd~ G06-R; 116 | #X obj 190 320 delread~ G06-R; 117 | #X connect 0 1 1 0; 118 | #X connect 1 0 6 0; 119 | #X connect 2 0 12 0; 120 | #X connect 3 0 5 0; 121 | #X connect 3 1 2 0; 122 | #X connect 4 0 16 0; 123 | #X connect 5 0 8 0; 124 | #X connect 6 0 3 0; 125 | #X connect 7 0 18 0; 126 | #X connect 8 0 7 0; 127 | #X connect 12 0 5 1; 128 | #X connect 12 0 19 0; 129 | #X connect 14 0 0 0; 130 | #X connect 15 0 14 0; 131 | #X connect 15 0 17 0; 132 | #X connect 18 0 4 0; 133 | #X connect 19 0 4 1; 134 | #X restore 180 160 pd 8ve; 135 | #X connect 0 0 1 0; 136 | #X connect 2 0 4 0; 137 | #X connect 2 1 5 0; 138 | #X connect 4 0 3 0; 139 | #X connect 5 0 3 1; 140 | #X restore 210 170 pd 8ve; 141 | #X obj 210 140 == 2; 142 | #X obj 180 30 hradio 22 1 0 5 empty empty empty 0 -11 0 14 #7c7c7c #fcfcfc #fcfcfc 0; 143 | #N canvas 600 400 450 300 stamp 0; 144 | #X obj 40 30 inlet; 145 | #X obj 40 60 switch~; 146 | #N canvas 350 20 620 600 fft-analysis 0; 147 | #X obj 90 450 *~; 148 | #X obj 50 450 *~; 149 | #X obj 300 250 *~; 150 | #X obj 260 250 *~; 151 | #X obj 260 280 +~; 152 | #X obj 50 190 *~; 153 | #X obj 50 160 inlet~; 154 | #X obj 50 220 rfft~; 155 | #X obj 50 510 *~; 156 | #X obj 260 220 rfft~; 157 | #X obj 50 480 rifft~; 158 | #X obj 50 540 outlet~; 159 | #X text 420 280 modulus; 160 | #X obj 180 350 *~; 161 | #X obj 390 440 block~ 1024 4; 162 | #X obj 90 190 tabreceive~ \$0-hann; 163 | #X obj 390 470 loadbang; 164 | #X obj 180 390 *~ 0.00065; 165 | #X obj 170 510 tabreceive~ \$0-hann; 166 | #X obj 260 190 *~; 167 | #X obj 260 160 inlet~; 168 | #X obj 380 190 tabreceive~ \$0-hann; 169 | #X obj 260 310 sqrt~; 170 | #X text 420 300 of control; 171 | #X text 420 320 amplitude; 172 | #X text 110 160 filter input; 173 | #X text 410 160 control source; 174 | #X text 410 220 Fourier transform; 175 | #X text 40 10 Internal workings of the timbre stamping algorithm. First the "filter input" is treated as in the compressor patch \, multiplying each channel amplitude by one over its modulus (but limited by the "squelch" parameter.) It is then multiplied by the modulus of the channel amplitude for the control source (which is Fourier analyzed in parallel with the filter input.); 176 | #X text 380 360 multiply the two amplitude; 177 | #X text 380 380 factors (for compression; 178 | #X text 380 400 and to apply new timbre); 179 | #X msg 390 500 \; window-size 1024 \; squelch 500; 180 | #X obj 170 450 *~; 181 | #X obj 130 450 *~; 182 | #X obj 380 250 *~; 183 | #X obj 340 250 *~; 184 | #X obj 340 280 +~; 185 | #X obj 130 510 *~; 186 | #X obj 340 220 rfft~; 187 | #X obj 130 480 rifft~; 188 | #X obj 130 540 outlet~; 189 | #X obj 280 350 *~; 190 | #X obj 280 390 *~ 0.00065; 191 | #X obj 340 190 *~; 192 | #X obj 340 160 inlet~; 193 | #X obj 340 310 sqrt~; 194 | #N canvas 730 350 450 330 recip 0; 195 | #X obj 110 70 *~; 196 | #X obj 70 70 *~; 197 | #X obj 70 100 +~; 198 | #X obj 70 250 clip~; 199 | #X obj 90 190 r squelch; 200 | #X obj 90 220 expr 0.01*$f1*$f1; 201 | #X obj 70 130 +~ 1e-20; 202 | #X obj 70 160 rsqrt~; 203 | #X text 140 80 reciprocal; 204 | #X text 140 100 modulus of; 205 | #X text 140 120 filter input; 206 | #X text 140 140 amplitude; 207 | #X obj 70 30 inlet~; 208 | #X obj 130 30 inlet~; 209 | #X obj 70 280 outlet~; 210 | #X connect 0 0 2 1; 211 | #X connect 1 0 2 0; 212 | #X connect 2 0 6 0; 213 | #X connect 3 0 14 0; 214 | #X connect 4 0 5 0; 215 | #X connect 5 0 3 2; 216 | #X connect 6 0 7 0; 217 | #X connect 7 0 3 0; 218 | #X connect 12 0 1 0; 219 | #X connect 12 0 1 1; 220 | #X connect 13 0 0 0; 221 | #X connect 13 0 0 1; 222 | #X restore 180 310 pd recip; 223 | #X connect 0 0 10 1; 224 | #X connect 1 0 10 0; 225 | #X connect 2 0 4 1; 226 | #X connect 3 0 4 0; 227 | #X connect 4 0 22 0; 228 | #X connect 5 0 7 0; 229 | #X connect 6 0 5 0; 230 | #X connect 7 0 1 0; 231 | #X connect 7 0 34 0; 232 | #X connect 7 0 47 0; 233 | #X connect 7 1 0 0; 234 | #X connect 7 1 33 0; 235 | #X connect 7 1 47 1; 236 | #X connect 8 0 11 0; 237 | #X connect 9 0 3 0; 238 | #X connect 9 0 3 1; 239 | #X connect 9 1 2 0; 240 | #X connect 9 1 2 1; 241 | #X connect 10 0 8 0; 242 | #X connect 13 0 17 0; 243 | #X connect 15 0 5 1; 244 | #X connect 16 0 32 0; 245 | #X connect 17 0 0 1; 246 | #X connect 17 0 1 1; 247 | #X connect 18 0 8 1; 248 | #X connect 18 0 38 1; 249 | #X connect 19 0 9 0; 250 | #X connect 20 0 19 0; 251 | #X connect 21 0 19 1; 252 | #X connect 21 0 44 1; 253 | #X connect 22 0 13 1; 254 | #X connect 33 0 40 1; 255 | #X connect 34 0 40 0; 256 | #X connect 35 0 37 1; 257 | #X connect 36 0 37 0; 258 | #X connect 37 0 46 0; 259 | #X connect 38 0 41 0; 260 | #X connect 39 0 36 0; 261 | #X connect 39 0 36 1; 262 | #X connect 39 1 35 0; 263 | #X connect 39 1 35 1; 264 | #X connect 40 0 38 0; 265 | #X connect 42 0 43 0; 266 | #X connect 43 0 33 1; 267 | #X connect 43 0 34 1; 268 | #X connect 44 0 39 0; 269 | #X connect 45 0 44 0; 270 | #X connect 46 0 42 1; 271 | #X connect 47 0 13 0; 272 | #X connect 47 0 42 0; 273 | #X restore 230 150 pd fft-analysis; 274 | #N canvas 950 160 560 520 hann-window 0; 275 | #N canvas 0 0 450 300 (subpatch) 0; 276 | #X array \$0-hann 1024 float 0; 277 | #X coords 0 1 1023 0 300 100 1; 278 | #X restore 120 390 graph; 279 | #X obj 360 210 osc~; 280 | #X obj 360 240 *~ -0.5; 281 | #X obj 360 270 +~ 0.5; 282 | #X obj 340 330 tabwrite~ \$0-hann; 283 | #X obj 60 110 r window-size; 284 | #X obj 170 240 /; 285 | #X obj 190 200 samplerate~; 286 | #X obj 170 330 s window-sec; 287 | #X obj 60 330 s window-hz; 288 | #X obj 180 270 * 1000; 289 | #X obj 180 300 s window-msec; 290 | #X obj 60 140 t f b f; 291 | #X msg 190 140 resize \$1; 292 | #X obj 190 170 s \$0-hann; 293 | #X obj 310 140 r window-hz; 294 | #X msg 390 170 0; 295 | #X obj 310 170 t f b; 296 | #X text 50 20 calculate Hann window table (variable window size) and constants window-hz (fundamental frequency of analysis) \, window-sec and window-msec (analysis window size in seconds and msec)., f 56; 297 | #X obj 60 240 @/; 298 | #X connect 1 0 2 0; 299 | #X connect 2 0 3 0; 300 | #X connect 3 0 4 0; 301 | #X connect 5 0 12 0; 302 | #X connect 6 0 8 0; 303 | #X connect 6 0 10 0; 304 | #X connect 7 0 6 1; 305 | #X connect 7 0 19 1; 306 | #X connect 10 0 11 0; 307 | #X connect 12 0 6 0; 308 | #X connect 12 0 19 0; 309 | #X connect 12 1 7 0; 310 | #X connect 12 2 13 0; 311 | #X connect 13 0 14 0; 312 | #X connect 15 0 17 0; 313 | #X connect 16 0 1 1; 314 | #X connect 17 0 1 0; 315 | #X connect 17 1 4 0; 316 | #X connect 17 1 16 0; 317 | #X connect 19 0 9 0; 318 | #X restore 230 90 pd hann-window; 319 | #N canvas 380 50 450 300 signals 0; 320 | #X obj 90 230 outlet~; 321 | #X obj 270 230 outlet~; 322 | #X obj 270 190 adc~; 323 | #X obj 180 110 tgl 19 0 empty empty empty 0 -10 0 12 #000000 #fcfcfc #fcfcfc 0 1; 324 | #X obj 210 110 loadbang; 325 | #X obj 90 50 r \$0opt1; 326 | #X obj 90 140 zp~ 25 25; 327 | #X obj 90 80 sly log 12.5 500 1; 328 | #X obj 340 230 outlet~; 329 | #X obj 90 190 fm2op~ 25 0 1 4; 330 | #N canvas 260 200 450 300 rand 0; 331 | #X obj 240 100 metro 10000; 332 | #X obj 240 130 rind 2 8; 333 | #X obj 240 160 zp~ 10000 4; 334 | #X obj 100 190 outlet~; 335 | #X obj 240 190 outlet~; 336 | #X obj 240 60 inlet; 337 | #X obj 100 130 rind 1 2.5; 338 | #X floatatom 190 100 0 0 0 0 - - - 0; 339 | #X obj 100 100 metro 600; 340 | #X floatatom 190 130 0 0 0 0 - - - 0; 341 | #X obj 100 160 zp~ 600 1; 342 | #X connect 0 0 1 0; 343 | #X connect 1 0 2 0; 344 | #X connect 2 0 4 0; 345 | #X connect 5 0 0 0; 346 | #X connect 6 0 10 0; 347 | #X connect 7 0 6 2; 348 | #X connect 8 0 6 0; 349 | #X connect 9 0 8 1; 350 | #X connect 9 0 10 1; 351 | #X connect 10 0 3 0; 352 | #X restore 180 140 pd rand; 353 | #X connect 2 0 1 0; 354 | #X connect 2 1 8 0; 355 | #X connect 3 0 10 0; 356 | #X connect 4 0 10 0; 357 | #X connect 5 0 7 0; 358 | #X connect 6 0 9 0; 359 | #X connect 7 0 6 0; 360 | #X connect 9 0 0 0; 361 | #X connect 10 0 9 2; 362 | #X connect 10 1 9 3; 363 | #X restore 230 120 pd signals; 364 | #X obj 60 190 s squelch; 365 | #X obj 60 120 r \$0opt2; 366 | #X obj 60 150 sly 0 2000 1; 367 | #X obj 230 190 add~; 368 | #X connect 0 0 1 0; 369 | #X connect 2 0 8 0; 370 | #X connect 2 1 8 1; 371 | #X connect 4 0 2 0; 372 | #X connect 4 1 2 1; 373 | #X connect 4 2 2 2; 374 | #X connect 6 0 7 0; 375 | #X connect 7 0 5 0; 376 | #X restore 290 170 pd stamp; 377 | #X obj 290 140 == 3; 378 | #X obj 180 60 s \$0mode; 379 | #X obj 50 110 r \$0mode; 380 | #X obj 130 110 r \$0mode; 381 | #X obj 210 110 r \$0mode; 382 | #X obj 290 110 r \$0mode; 383 | #X obj 70 20 loadbang; 384 | #X msg 70 50 0; 385 | #X obj 50 200 r \$0mode; 386 | #X obj 50 230 == 4; 387 | #N canvas 600 400 450 300 off 0; 388 | #X obj 190 150 adc~; 389 | #X obj 40 30 inlet; 390 | #X obj 40 60 switch~; 391 | #X obj 190 180 add~; 392 | #X connect 0 0 3 0; 393 | #X connect 0 1 3 1; 394 | #X connect 1 0 2 0; 395 | #X restore 50 260 pd off; 396 | #X obj 190 200 hsl 187 22 0 1 0 0 empty empty empty -2 -11 0 14 #000000 #fcfcfc #fcfcfc 0 1; 397 | #X obj 200 230 hsl 187 22 0 1 0 0 empty empty empty -2 -11 0 14 #000000 #fcfcfc #fcfcfc 0 1; 398 | #X obj 190 260 s \$0opt1; 399 | #X obj 270 260 s \$0opt2; 400 | #N canvas 600 400 450 300 vol 0; 401 | #X obj 130 80 all~; 402 | #X obj 130 130 *~; 403 | #X obj 170 130 *~; 404 | #X obj 180 50 r \$0vol; 405 | #X obj 180 80 zp~ 25 1; 406 | #X obj 130 180 dac~; 407 | #X obj 290 30 vsl 22 187 0.001 1 1 0 empty empty empty 0 -9 0 14 #000000 #fcfcfc #fcfcfc 0 1; 408 | #X obj 290 230 s \$0vol; 409 | #X connect 0 0 1 0; 410 | #X connect 0 1 2 0; 411 | #X connect 1 0 5 0; 412 | #X connect 2 0 5 1; 413 | #X connect 3 0 4 0; 414 | #X connect 4 0 2 1; 415 | #X connect 4 0 1 1; 416 | #X connect 6 0 7 0; 417 | #X restore 290 80 pd vol; 418 | #X connect 1 0 0 0; 419 | #X connect 3 0 2 0; 420 | #X connect 5 0 4 0; 421 | #X connect 6 0 9 0; 422 | #X connect 8 0 7 0; 423 | #X connect 10 0 1 0; 424 | #X connect 11 0 3 0; 425 | #X connect 12 0 5 0; 426 | #X connect 13 0 8 0; 427 | #X connect 14 0 15 0; 428 | #X connect 15 0 6 0; 429 | #X connect 16 0 17 0; 430 | #X connect 17 0 18 0; 431 | #X connect 19 0 21 0; 432 | #X connect 20 0 22 0; 433 | -------------------------------------------------------------------------------- /love/major-minor/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.title = 'Scale Shifter' 3 | t.window.width = 800 4 | t.window.height = 600 5 | end 6 | -------------------------------------------------------------------------------- /love/major-minor/inst.pd: -------------------------------------------------------------------------------- 1 | #N canvas 210 460 510 380 14; 2 | #X obj 120 170 textfile; 3 | #X obj 190 310 muse; 4 | #X obj 30 280 t; 5 | #X obj 100 310 ct; 6 | #X obj 40 170 r loop; 7 | #X obj 120 20 r goto; 8 | #X obj 40 140 r \$0txt; 9 | #X obj 300 340 outlet~; 10 | #X obj 360 50 inlet; 11 | #N canvas 670 570 350 300 scl 0; 12 | #X obj 70 30 inlet; 13 | #X obj 140 240 outlet; 14 | #X obj 70 60 t b a; 15 | #X obj 70 110 s \$0txt; 16 | #X obj 70 140 r scl; 17 | #X obj 140 110 route default; 18 | #X obj 140 140 array get default; 19 | #X msg 140 60 default; 20 | #X msg 260 180 peek; 21 | #X obj 70 200 route list; 22 | #X obj 260 210 loadbang; 23 | #X obj 70 170 list prepend # d; 24 | #X msg 260 240 strict 1; 25 | #X connect 0 0 2 0; 26 | #X connect 2 0 3 0; 27 | #X connect 2 1 5 0; 28 | #X connect 4 0 11 0; 29 | #X connect 5 0 6 0; 30 | #X connect 5 1 1 0; 31 | #X connect 6 0 11 0; 32 | #X connect 7 0 5 0; 33 | #X connect 8 0 1 0; 34 | #X connect 9 0 1 0; 35 | #X connect 10 0 12 0; 36 | #X connect 11 0 9 0; 37 | #X connect 12 0 1 0; 38 | #X restore 150 250 pd scl; 39 | #X obj 100 340 s \$0txt; 40 | #X obj 120 80 symbol \$1; 41 | #X obj 150 50 t b f; 42 | #X obj 100 250 inlet; 43 | #X msg 120 140 read \$3/\$2/\$1.trk cr \, rewind; 44 | #X obj 120 110 paq s 1 .; 45 | #N canvas 810 440 450 370 vline 0; 46 | #X obj 90 40 inlet; 47 | #X obj 170 300 outlet~; 48 | #X obj 200 40 inlet; 49 | #X obj 150 40 inlet; 50 | #X obj 140 70 paq 0 .; 51 | #X obj 170 260 vline~; 52 | #X obj 170 160 textfile; 53 | #X obj 170 130 until; 54 | #X msg 250 130 rewind; 55 | #X obj 170 190 unpack f f f; 56 | #X obj 90 150 t b f; 57 | #X obj 160 220 * 1; 58 | #X obj 190 220 * 1; 59 | #X obj 230 220 * 1; 60 | #X obj 310 190 r ms; 61 | #X msg 140 100 read \$2/inst/\$1.inst cr \, rewind; 62 | #X connect 0 0 10 0; 63 | #X connect 2 0 4 1; 64 | #X connect 3 0 4 0; 65 | #X connect 4 0 15 0; 66 | #X connect 5 0 1 0; 67 | #X connect 6 0 9 0; 68 | #X connect 6 1 8 0; 69 | #X connect 6 1 7 1; 70 | #X connect 7 0 6 0; 71 | #X connect 8 0 6 0; 72 | #X connect 9 0 11 0; 73 | #X connect 9 1 12 0; 74 | #X connect 9 2 13 0; 75 | #X connect 10 0 7 0; 76 | #X connect 10 1 11 1; 77 | #X connect 11 0 5 0; 78 | #X connect 12 0 5 1; 79 | #X connect 13 0 5 2; 80 | #X connect 14 0 13 1; 81 | #X connect 14 0 12 1; 82 | #X connect 15 0 6 0; 83 | #X restore 300 310 pd vline; 84 | #X obj 330 250 b; 85 | #X obj 340 280 s \$0txt; 86 | #X obj 370 250 t b f; 87 | #X obj 250 310 1; 88 | #X obj 250 280 b; 89 | #X obj 190 340 outlet~; 90 | #X obj 210 250 unpaq a a a f; 91 | #X obj 120 210 route rest scl list float inst note goto; 92 | #X obj 410 280 s \$0goto; 93 | #X obj 180 20 r \$0goto; 94 | #X obj 40 210 spigot 1; 95 | #X msg 30 310 0; 96 | #X obj 20 110 t; 97 | #X msg 20 240 set 0; 98 | #X obj 20 80 r rewind; 99 | #X obj 140 280 route float; 100 | #X connect 0 0 24 0; 101 | #X connect 0 1 27 0; 102 | #X connect 1 0 22 0; 103 | #X connect 2 0 28 0; 104 | #X connect 2 1 11 0; 105 | #X connect 3 0 10 0; 106 | #X connect 4 0 27 1; 107 | #X connect 5 0 12 0; 108 | #X connect 6 0 0 0; 109 | #X connect 8 0 15 2; 110 | #X connect 8 0 16 2; 111 | #X connect 9 0 1 0; 112 | #X connect 11 0 15 0; 113 | #X connect 12 0 11 0; 114 | #X connect 12 1 15 1; 115 | #X connect 13 0 3 0; 116 | #X connect 14 0 0 0; 117 | #X connect 15 0 14 0; 118 | #X connect 16 0 7 0; 119 | #X connect 17 0 18 0; 120 | #X connect 19 0 18 0; 121 | #X connect 19 1 25 0; 122 | #X connect 20 0 16 0; 123 | #X connect 21 0 20 0; 124 | #X connect 23 0 1 0; 125 | #X connect 23 1 21 0; 126 | #X connect 23 1 32 0; 127 | #X connect 23 2 16 1; 128 | #X connect 23 3 20 1; 129 | #X connect 24 0 3 1; 130 | #X connect 24 1 9 0; 131 | #X connect 24 2 23 0; 132 | #X connect 24 3 23 0; 133 | #X connect 24 4 16 1; 134 | #X connect 24 5 17 0; 135 | #X connect 24 6 19 0; 136 | #X connect 24 7 23 0; 137 | #X connect 26 0 12 0; 138 | #X connect 27 0 2 0; 139 | #X connect 28 0 3 0; 140 | #X connect 29 0 30 0; 141 | #X connect 29 1 11 0; 142 | #X connect 30 0 3 0; 143 | #X connect 31 0 29 0; 144 | #X connect 32 0 3 1; 145 | -------------------------------------------------------------------------------- /love/major-minor/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | package.path = '../?.lua;' .. package.path 6 | local lpd = require('pdmain') 7 | local pd = lpd.pd 8 | local patch ---@type PdPatch 9 | 10 | local gui = require('pdgui')(pd) 11 | local scale = Pd.Array(6) 12 | local doremi = 're:\nmi:\nfa:\nso:\nla:\nti:' 13 | local sliders, buttons, toggles 14 | 15 | 16 | -- Widget Callbacks 17 | 18 | local function invup(self) 19 | pd:sendMessage(self.dest, '>1') 20 | pd:sendMessage(self.dest, 'send') 21 | pd:readArray('default', scale) 22 | end 23 | 24 | local function invdn(self) 25 | pd:sendMessage(self.dest, '<1') 26 | pd:sendMessage(self.dest, 'send') 27 | pd:readArray('default', scale) 28 | end 29 | 30 | local function melmin(self) 31 | pd:sendList(self.dest, { 0, 2, 3, 5, 7, 9, 11 }) 32 | pd:sendMessage(self.dest, 'send') 33 | pd:readArray('default', scale) 34 | end 35 | 36 | local function mixob6(self) 37 | pd:sendList(self.dest, { 0, 2, 4, 5, 7, 8, 10 }) 38 | pd:sendMessage(self.dest, 'send') 39 | pd:readArray('default', scale) 40 | end 41 | 42 | local function stop() 43 | pd:sendBang('stop') 44 | toggles.pause:click(true) 45 | end 46 | 47 | local function sclChange(self, num) 48 | self.num = num 49 | pd:sendFloat(self.dest, self.num) 50 | pd:readArray('default', scale) 51 | end 52 | 53 | function love.load() 54 | lpd.init() 55 | patch = lpd.open { play = false } 56 | 57 | local dlr = patch:dollarZeroStr() 58 | local width, height = love.graphics.getDimensions() 59 | 60 | local maj = { dest = 'maj-min', min = 1, max = 0, num = 1 61 | , snap = .25, gap = 12, change = sclChange } 62 | local scl = { dest = 'mode', min = 0, max = 7, num = 0 63 | , snap = .5, change = sclChange } 64 | local phase = { dest = 'phase', min = .5, max = 0, num = .5 65 | , snap = 1 / 48, gap = 0 } 66 | local tempo = { dest = 'tempo', min = .25, max = 4, num = 1 67 | , snap = 2, gap = 10, log = true } 68 | local vol = { dest = dlr .. 'vol', min = -60, max = 0, num = -15 69 | , snap = 10, len = height - 100 70 | , label = { text = 'volume', x = -100 }, change = gui.volChange } 71 | 72 | local sx = 20 73 | gui.slider.rad = 25 74 | gui.slider.len = width - 150 75 | sliders = { 76 | gui.slider(sx, height * 2 / 6, { x = maj }, { rgb = { .25, .66, .66 } }) 77 | , gui.slider(sx, height * 3 / 6, { x = scl }, { rgb = { .33, .5, .66 } }) 78 | , gui.slider(sx, height * 4 / 6, { x = phase }, { rgb = { .5, .66, .25 } }) 79 | , gui.slider(sx, height * 5 / 6, { x = tempo }, { rgb = { .75, .25, .25 } }) 80 | , gui.slider(width - 90, 60, { y = vol }, { rgb = { .75, .5, .75 } }) 81 | } 82 | 83 | local bx = 175 84 | gui.button.size = 33 85 | gui.button.dest = 'scdef' 86 | buttons = { 87 | gui.button(bx, 50, { label = { text = 'inv-' } 88 | , click = invdn }) 89 | , gui.button(bx + 75, 50, { label = { text = 'inv+' } 90 | , click = invup }) 91 | , gui.button(bx + 300, 50, { label = { text = 'melodic-minor' } 92 | , click = melmin }) 93 | , gui.button(bx + 300, 100, { label = { text = 'mixo-b6', y = 60 } 94 | , click = mixob6 }) 95 | , gui.button(bx + 375, 75, { label = { text = 'stop', x = 40, y = 30 } 96 | , click = stop }) 97 | } 98 | 99 | gui.toggle.on = true 100 | gui.toggle.size = 33 101 | toggles = { 102 | gui.toggle(bx + 150, 50, { dest = 'repeat', on = false }) 103 | , gui.toggle(bx + 225, 50, { dest = 'pause', on = false }) 104 | , gui.toggle(bx, 100, { dest = 'pulse1', label = { y = 60 } }) 105 | , gui.toggle(bx + 75, 100, { dest = 'pulse2', label = { y = 60 } }) 106 | , gui.toggle(bx + 150, 100, { dest = 'triangle', label = { y = 60 } }) 107 | , gui.toggle(bx + 225, 100, { dest = 'noise', label = { y = 60 } }) 108 | } 109 | toggles.pause = toggles[2] 110 | 111 | for _, v in next, sliders do 112 | v:send() 113 | end 114 | pd:sendBang(dlr .. 'play') 115 | pd:readArray('default', scale) 116 | end 117 | 118 | function love.update(dt) 119 | gui.updateSliders(sliders) 120 | for i = #buttons, 1, -1 do buttons[i]:update(dt) end 121 | lpd.update() 122 | end 123 | 124 | function love.mousepressed(x, y) 125 | for i = #buttons, 1, -1 do 126 | if buttons[i]:mousepressed(x, y) then return end 127 | end 128 | for i = #toggles, 1, -1 do 129 | if toggles[i]:mousepressed(x, y) then return end 130 | end 131 | end 132 | 133 | function love.keypressed() 134 | pd:sendList('scdef', { 0, 2, 4, 5, 7, 9, 11 }) 135 | pd:sendMessage('scdef', 'send') 136 | end 137 | 138 | function love.draw() 139 | for i = 1, #sliders do sliders[i]:draw() end 140 | for i = 1, #buttons do buttons[i]:draw() end 141 | for i = 1, #toggles do toggles[i]:draw() end 142 | 143 | local str = '' 144 | for i = 1, #scale do 145 | str = str .. string.format('%.2f', scale[i]) .. '\n' 146 | end 147 | love.graphics.printf(doremi, 10, 10, 30, 'right') 148 | love.graphics.printf(str, 45, 10, 50, 'right') 149 | end 150 | 151 | function love.quit() 152 | pd:closePatch(patch) 153 | pd:computeAudio(false) 154 | end 155 | -------------------------------------------------------------------------------- /love/major-minor/main.pd: -------------------------------------------------------------------------------- 1 | #N canvas 200 300 650 570 14; 2 | #X declare -lib blunt; 3 | #X obj 350 120 tgl 15 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 4 | #fcfcfc 0 1; 5 | #X msg 300 120 0; 6 | #X obj 40 280 pulse~; 7 | #X obj 100 330 *~; 8 | #X obj 310 470 *~; 9 | #X obj 350 470 hsl 128 20 0.001 1 1 0 empty empty empty -2 -8 0 10 10 | #000000 #fcfcfc #fcfcfc 0 1; 11 | #X obj 300 500 dac~; 12 | #X obj 300 150 s met; 13 | #X floatatom 350 500 0 0 0 0 - - - 0; 14 | #X obj 490 150 s ms; 15 | #X obj 90 150 s tempo; 16 | #X obj 420 60 r tempo; 17 | #X obj 490 120 / 1; 18 | #X obj 40 250 inst pulse1; 19 | #X obj 490 40 bng 15 250 50 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 20 | #fcfcfc; 21 | #X obj 230 150 s loop; 22 | #X floatatom 490 90 0 0 0 0 - - - 0; 23 | #X floatatom 530 150 0 0 0 0 - - - 0; 24 | #X obj 490 60 `f 50 !; 25 | #X obj 160 150 s goto; 26 | #N canvas 810 290 550 400 default-scale 0; 27 | #X obj 190 60 array define default 6; 28 | #X obj 90 340 array set default; 29 | #X obj 350 170 b !; 30 | #X obj 350 150 bng 15 250 50 0 empty empty empty 17 7 0 10 #000000 31 | #fcfcfc #fcfcfc; 32 | #X obj 40 340 s scl; 33 | #X floatatom 40 30 0 0 0 0 - - - 0; 34 | #X obj 40 60 / 100; 35 | #X obj 40 310 t a a; 36 | #X obj 40 180 pack f f f; 37 | #X obj 180 100 r scdef; 38 | #X obj 230 200 route scale; 39 | #X msg 350 230 2 4 5 7 9 11; 40 | #X obj 100 60 r maj-min; 41 | #X obj 250 100 r mode; 42 | #X msg 250 130 send >\$1; 43 | #X obj 180 170 muse 0 2 4 5 7 9 11; 44 | #X obj 230 230 list split 1; 45 | #X msg 40 210 . 2 \$1 5 7 \$2 \$3 \, send; 46 | #X obj 40 100 expr 3 + $f1 \; 8 + $f1 \; 10 + $f1; 47 | #X connect 2 0 11 0; 48 | #X connect 3 0 2 0; 49 | #X connect 5 0 6 0; 50 | #X connect 6 0 18 0; 51 | #X connect 7 0 4 0; 52 | #X connect 7 1 1 0; 53 | #X connect 8 0 17 0; 54 | #X connect 9 0 15 0; 55 | #X connect 10 0 16 0; 56 | #X connect 11 0 7 0; 57 | #X connect 12 0 18 0; 58 | #X connect 13 0 14 0; 59 | #X connect 14 0 15 0; 60 | #X connect 15 1 10 0; 61 | #X connect 16 1 7 0; 62 | #X connect 17 0 15 0; 63 | #X connect 18 0 8 0; 64 | #X connect 18 1 8 1; 65 | #X connect 18 2 8 2; 66 | #X restore 160 30 pd default-scale; 67 | #X obj 180 280 pulse~; 68 | #X obj 180 250 inst pulse2; 69 | #X obj 240 330 *~; 70 | #X obj 100 360 *~; 71 | #X obj 140 250 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 #000000 72 | #fcfcfc #fcfcfc 0 1; 73 | #X obj 240 360 *~; 74 | #X obj 280 250 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 #000000 75 | #fcfcfc #fcfcfc 0 1; 76 | #X obj 140 180 tgl 15 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 77 | #fcfcfc 0 1; 78 | #X obj 60 180 loadbang; 79 | #X obj 400 330 *~; 80 | #X obj 400 360 *~; 81 | #X obj 440 250 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 #000000 82 | #fcfcfc #fcfcfc 0 1; 83 | #X obj 310 440 *~ 0.25; 84 | #X obj 320 250 inst triangle; 85 | #X msg 70 60 1; 86 | #X msg 90 90 0.5; 87 | #N canvas 810 510 460 340 metro 0; 88 | #X obj 30 100 metro; 89 | #X obj 30 60 inlet; 90 | #X obj 80 60 inlet; 91 | #X obj 30 160 outlet; 92 | #X obj 30 130 t; 93 | #X obj 90 230 textfile; 94 | #X obj 270 100 loadbang; 95 | #X obj 90 160 ct; 96 | #X obj 90 290 s goto; 97 | #X msg 180 130 read mario/main.trk cr \, rewind; 98 | #X obj 40 230 b; 99 | #X msg 200 160 read mario/\$1.trk cr \, rewind; 100 | #X obj 280 230 t b a; 101 | #X obj 280 260 symbol; 102 | #X obj 90 260 route goto stop float; 103 | #X obj 150 290 s stop; 104 | #X obj 90 190 spigot 1; 105 | #X obj 160 30 r repeat; 106 | #X obj 140 60 !, f 1; 107 | #X obj 140 30 tgl 15 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 108 | #fcfcfc 0 1; 109 | #X msg 90 130 set 0; 110 | #X obj 90 100 t; 111 | #X obj 40 30 r rewind; 112 | #X obj 180 100 spigot 1; 113 | #X connect 0 0 4 0; 114 | #X connect 1 0 0 0; 115 | #X connect 2 0 0 1; 116 | #X connect 4 0 3 0; 117 | #X connect 4 1 7 0; 118 | #X connect 5 0 14 0; 119 | #X connect 6 0 9 0; 120 | #X connect 7 0 16 0; 121 | #X connect 9 0 5 0; 122 | #X connect 10 0 5 0; 123 | #X connect 11 0 5 0; 124 | #X connect 12 0 5 0; 125 | #X connect 12 1 13 0; 126 | #X connect 13 0 11 0; 127 | #X connect 14 0 8 0; 128 | #X connect 14 0 10 0; 129 | #X connect 14 1 15 0; 130 | #X connect 14 2 7 1; 131 | #X connect 14 3 12 0; 132 | #X connect 16 0 5 0; 133 | #X connect 17 0 18 0; 134 | #X connect 18 0 16 1; 135 | #X connect 18 0 23 1; 136 | #X connect 19 0 18 0; 137 | #X connect 20 0 7 0; 138 | #X connect 21 0 20 0; 139 | #X connect 21 1 23 0; 140 | #X connect 22 0 21 0; 141 | #X connect 23 0 9 0; 142 | #X restore 350 150 pd metro; 143 | #X obj 160 90 cupqb 1; 144 | #X obj 530 330 *~; 145 | #X obj 530 360 *~; 146 | #X obj 580 250 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 #000000 147 | #fcfcfc #fcfcfc 0 1; 148 | #X obj 480 250 inst noise; 149 | #N canvas 290 500 450 300 crush 0; 150 | #X obj 220 100 phasor~; 151 | #X obj 150 140 samphold~; 152 | #X obj 150 100 noise~; 153 | #X obj 150 170 outlet~; 154 | #X obj 220 70 inlet~; 155 | #X connect 0 0 1 1; 156 | #X connect 1 0 3 0; 157 | #X connect 2 0 1 0; 158 | #X connect 4 0 0 0; 159 | #X restore 480 280 pd crush; 160 | #X obj 310 30 r \$0play; 161 | #X obj 380 440 r \$0vol; 162 | #X obj 170 330 r phase; 163 | #X msg 420 90 . \$1; 164 | #X obj 390 30 r pause; 165 | #X obj 390 60 !, f 1; 166 | #X obj 100 390 r pulse1; 167 | #X obj 240 390 r pulse2; 168 | #X obj 400 390 r triangle; 169 | #X obj 530 390 r noise; 170 | #N canvas 790 250 450 300 preload 0; 171 | #X obj 40 90 until; 172 | #X obj 40 120 f; 173 | #X obj 110 120 + 1; 174 | #X obj 70 60 sel; 175 | #X obj 160 150 pack s f; 176 | #X obj 160 90 t b f; 177 | #X obj 160 210 textfile; 178 | #X obj 110 150 mod 5; 179 | #X msg 160 120 pulse1 \, pulse2 \, triangle \, noise; 180 | #X msg 160 180 read mario/\$2/\$1.trk; 181 | #X obj 40 30 loadbang; 182 | #X connect 0 0 1 0; 183 | #X connect 1 0 2 0; 184 | #X connect 2 0 5 0; 185 | #X connect 2 0 7 0; 186 | #X connect 3 0 0 1; 187 | #X connect 4 0 9 0; 188 | #X connect 5 0 8 0; 189 | #X connect 5 1 4 1; 190 | #X connect 7 0 3 0; 191 | #X connect 7 0 1 1; 192 | #X connect 8 0 4 0; 193 | #X connect 9 0 6 0; 194 | #X connect 10 0 0 0; 195 | #X restore 60 470 pd preload; 196 | #X obj 60 500 declare -lib blunt; 197 | #X obj 230 130 tgl 15 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 198 | #fcfcfc 0 1; 199 | #X msg 110 120 0.25; 200 | #X obj 470 180 sym mario !; 201 | #X obj 230 100 s rewind; 202 | #X msg 350 60 0; 203 | #X obj 230 60 r stop; 204 | #X obj 290 60 t; 205 | #N canvas 290 500 570 300 tri-tab 0; 206 | #X obj 220 90 outlet~; 207 | #X obj 220 30 inlet~; 208 | #X obj 40 230 array define \$0tri 35; 209 | #X obj 40 200 array set \$0tri 1; 210 | #X obj 40 100 loadbang; 211 | #X msg 40 130 0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1 0.875 0.75 212 | 0.625 0.5 0.375 0.25 0.125 0 -0.125 -0.25 -0.375 -0.5 -0.625 -0.75 213 | -0.875 -1 -0.875 -0.75 -0.625 -0.5 -0.375 -0.25 -0.125 0; 214 | #X obj 220 60 tabosc2~ \$0tri 0.67; 215 | #X connect 1 0 6 0; 216 | #X connect 4 0 5 0; 217 | #X connect 5 0 3 0; 218 | #X connect 6 0 0 0; 219 | #X restore 320 280 pd tri-tab; 220 | #X connect 0 0 37 0; 221 | #X connect 1 0 7 0; 222 | #X connect 2 0 3 0; 223 | #X connect 3 0 24 0; 224 | #X connect 4 0 6 0; 225 | #X connect 4 0 6 1; 226 | #X connect 5 0 4 1; 227 | #X connect 5 0 8 0; 228 | #X connect 11 0 47 0; 229 | #X connect 12 0 9 0; 230 | #X connect 12 0 17 0; 231 | #X connect 12 0 37 1; 232 | #X connect 13 0 2 0; 233 | #X connect 13 1 3 1; 234 | #X connect 14 0 18 0; 235 | #X connect 16 0 12 0; 236 | #X connect 18 0 16 0; 237 | #X connect 21 0 23 0; 238 | #X connect 22 0 21 0; 239 | #X connect 22 1 23 1; 240 | #X connect 23 0 26 0; 241 | #X connect 24 0 33 0; 242 | #X connect 25 0 24 1; 243 | #X connect 26 0 33 0; 244 | #X connect 27 0 26 1; 245 | #X connect 28 0 27 0; 246 | #X connect 28 0 25 0; 247 | #X connect 28 0 32 0; 248 | #X connect 28 0 41 0; 249 | #X connect 29 0 28 0; 250 | #X connect 30 0 31 0; 251 | #X connect 31 0 33 0; 252 | #X connect 32 0 31 1; 253 | #X connect 33 0 4 0; 254 | #X connect 34 0 63 0; 255 | #X connect 34 1 30 1; 256 | #X connect 35 0 10 0; 257 | #X connect 36 0 10 0; 258 | #X connect 37 0 13 0; 259 | #X connect 37 0 42 0; 260 | #X connect 37 0 34 0; 261 | #X connect 37 0 22 0; 262 | #X connect 38 0 19 0; 263 | #X connect 39 0 40 0; 264 | #X connect 40 0 33 0; 265 | #X connect 41 0 40 1; 266 | #X connect 42 0 43 0; 267 | #X connect 42 1 39 1; 268 | #X connect 43 0 39 0; 269 | #X connect 44 0 0 0; 270 | #X connect 45 0 4 1; 271 | #X connect 46 0 2 1; 272 | #X connect 46 0 21 1; 273 | #X connect 47 0 12 0; 274 | #X connect 48 0 49 0; 275 | #X connect 49 0 0 0; 276 | #X connect 50 0 24 1; 277 | #X connect 51 0 26 1; 278 | #X connect 52 0 31 1; 279 | #X connect 53 0 40 1; 280 | #X connect 56 0 15 0; 281 | #X connect 57 0 10 0; 282 | #X connect 58 0 13 1; 283 | #X connect 58 0 42 1; 284 | #X connect 58 0 34 1; 285 | #X connect 58 0 22 1; 286 | #X connect 60 0 0 0; 287 | #X connect 61 0 62 0; 288 | #X connect 62 0 59 0; 289 | #X connect 62 1 60 0; 290 | #X connect 63 0 30 0; 291 | -------------------------------------------------------------------------------- /love/major-minor/mario/1/noise.trk: -------------------------------------------------------------------------------- 1 | scl # 1, scl size 1, scl default 2 | 3 | note dur inst 4 | 11 6 hho 5 | 6 | 12 3 hhc 7 | 11 6 hho 8 | 9 | 12 3 hhc 10 | 11 6 hho 11 | 12 | ~ 9 13 | 14 | 15 | ~ 6 16 | 17 | 12 3 hhc 18 | ~ ~ 19 | ~ ~ 20 | -------------------------------------------------------------------------------- /love/major-minor/mario/1/pulse1.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 9 3 puls 5 | ~ 6 6 | 7 | ~ ~ 8 | 9 | 7 3 10 | 9 6 11 | 12 | 11 24 13 | -------------------------------------------------------------------------------- /love/major-minor/mario/1/pulse2.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 4-1 3 puls 5 | ~ 6 6 | 7 | ~ ~ 8 | 9 | ~ 3 10 | ~ 6 11 | 12 | 3+6 12 13 | 14 | 15 | 16 | 4 ~ 17 | -------------------------------------------------------------------------------- /love/major-minor/mario/1/triangle.trk: -------------------------------------------------------------------------------- 1 | scl # 48, scl size 7, scl default 2 | 3 | note dur inst 4 | 1 3 bass 5 | ~ 6 6 | 7 | ~ ~ 8 | 9 | ~ 3 10 | ~ 6 11 | 12 | 11 12 13 | 14 | 15 | 16 | 4 ~ 17 | -------------------------------------------------------------------------------- /love/major-minor/mario/2/noise.trk: -------------------------------------------------------------------------------- 1 | scl # 1, scl size 1, scl default 2 | 3 | note dur inst 4 | 8.2 6 rim 5 | 6 | 12 4 hhc 7 | ~ 2 8 | 11 6 hho 9 | 10 | 12 4 hhc 11 | ~ 2 12 | -------------------------------------------------------------------------------- /love/major-minor/mario/2/pulse1.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 7 9 puls 5 | 6 | 7 | 4 ~ 8 | 9 | 10 | 2 ~ 11 | 12 | 13 | 5 6 14 | 15 | 5+2 ~ 16 | 17 | 5+1 3 18 | 5 6 19 | 20 | 4 4 21 | 9 ~ 22 | 23 | 11 ~ 24 | 12 6 25 | 26 | 10 3 27 | 11 6 28 | 29 | 9 ~ 30 | 31 | 7 3 32 | 8 ~ 33 | 7-1 9 34 | -------------------------------------------------------------------------------- /love/major-minor/mario/2/pulse2.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 2 9 puls 5 | 6 | 7 | 0 ~ 8 | 9 | 10 | -3 ~ 11 | 12 | 13 | 0 6 14 | 15 | 0+2 ~ 16 | 17 | 0+1 3 18 | 0 6 19 | 20 | ~ 4 21 | 4 ~ 22 | 23 | 6 ~ 24 | 7 6 25 | 26 | 5 3 27 | 6 6 28 | 29 | 5 ~ 30 | 31 | 2 3 32 | 3 ~ 33 | 1 9 34 | -------------------------------------------------------------------------------- /love/major-minor/mario/2/triangle.trk: -------------------------------------------------------------------------------- 1 | scl # 48, scl size 7, scl default 2 | 3 | note dur inst 4 | 4 9 bass 5 | 6 | 7 | 2 ~ 8 | 9 | 10 | 0 ~ 11 | 12 | 13 | 3 6 14 | 15 | 4 ~ 16 | 17 | 3+1 3 18 | 3 6 19 | 20 | 2 4 21 | 7 ~ 22 | 23 | 9 ~ 24 | 10 6 25 | 26 | 8 3 27 | 9 6 28 | 29 | 7 ~ 30 | 31 | 5 3 32 | 7-1 ~ 33 | 4 9 34 | -------------------------------------------------------------------------------- /love/major-minor/mario/3/noise.trk: -------------------------------------------------------------------------------- 1 | goto 2 2 | -------------------------------------------------------------------------------- /love/major-minor/mario/3/pulse1.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | rest 6 5 | 6 | 11 3 puls 7 | 11-1 ~ 8 | 11-2 ~ 9 | 9-1 6 10 | 11 | 9 ~ 12 | 13 | 5-1 3 14 | 5 ~ 15 | 7 6 16 | 17 | 5 3 18 | 7 ~ 19 | 8 9 20 | 21 | 22 | 11 3 23 | 11-1 ~ 24 | 11-2 ~ 25 | 9-1 6 26 | 27 | 9 ~ 28 | 29 | 14 ~ 30 | 31 | ~ 3 32 | ~ 18 33 | 34 | 35 | 36 | 37 | 38 | 11 3 39 | 11-1 ~ 40 | 11-2 ~ 41 | 9-1 6 42 | 43 | 9 ~ 44 | 45 | 5-1 3 46 | 5 ~ 47 | 7 6 48 | 49 | 5 3 50 | 7 ~ 51 | 8 9 52 | 53 | 54 | 7+3 ~ 55 | 56 | 57 | 8 ~ 58 | 59 | 60 | 7 24 61 | -------------------------------------------------------------------------------- /love/major-minor/mario/3/pulse2.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | rest 6 5 | 6 | 9 3 puls 7 | 9-1 ~ 8 | 9-2 ~ 9 | 6 6 10 | 11 | 7 ~ 12 | 13 | 2 3 14 | 3 ~ 15 | 4 6 16 | 17 | 0 3 18 | 2 ~ 19 | 3 9 20 | 21 | 22 | 9 3 23 | 9-1 ~ 24 | 9-2 ~ 25 | 6 6 26 | 27 | 7 ~ 28 | 29 | 10 ~ 30 | 31 | ~ 3 32 | ~ 18 33 | 34 | 35 | 36 | 37 | 38 | 9 3 39 | 9-1 ~ 40 | 9-2 ~ 41 | 6 6 42 | 43 | 7 ~ 44 | 45 | 2 3 46 | 3 ~ 47 | 4 6 48 | 49 | 0 3 50 | 2 ~ 51 | 3 9 52 | 53 | 54 | 7-4 ~ 55 | 56 | 57 | 1+3 ~ 58 | 59 | 60 | 2 24 61 | -------------------------------------------------------------------------------- /love/major-minor/mario/3/triangle.trk: -------------------------------------------------------------------------------- 1 | scl # 48, scl size 7, scl default 2 | 3 | note dur inst 4 | 0 9 bass 5 | 6 | 7 | 4 ~ 8 | 9 | 10 | 7 6 11 | 12 | 3 9 13 | 14 | 15 | 7 3 16 | ~ 6 17 | 18 | 3 ~ 19 | 20 | 0 9 21 | 22 | 23 | 2 ~ 24 | 25 | 26 | 4 3 27 | 7 6 28 | 29 | 18 ~ 30 | 31 | ~ 3 32 | ~ 6 33 | 34 | 4 ~ 35 | 36 | 0 9 37 | 38 | 39 | 4 ~ 40 | 41 | 42 | 7 6 43 | 44 | 3 9 45 | 46 | 47 | 7 3 48 | ~ 6 49 | 50 | 3 ~ 51 | 52 | 0 ~ 53 | 54 | 7-4 9 55 | 56 | 57 | 7-2 ~ 58 | 59 | 60 | 7 ~ 61 | 62 | 63 | 4 3 64 | ~ 6 65 | 66 | 0 ~ 67 | -------------------------------------------------------------------------------- /love/major-minor/mario/4/noise.trk: -------------------------------------------------------------------------------- 1 | goto 1 2 | -------------------------------------------------------------------------------- /love/major-minor/mario/4/pulse1.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 7 3 puls 5 | ~ 6 6 | 7 | ~ ~ 8 | 9 | ~ 3 10 | 8 6 11 | 12 | 9 3 13 | 7 6 14 | 15 | 5 3 16 | 4 12 17 | 18 | 19 | 20 | 7 3 21 | ~ 6 22 | 23 | ~ ~ 24 | 25 | ~ 3 26 | 8 ~ 27 | 9 27 28 | -------------------------------------------------------------------------------- /love/major-minor/mario/4/pulse2.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 7-4 3 puls 5 | ~ 6 6 | 7 | ~ ~ 8 | 9 | ~ 3 10 | 8-4 6 11 | 12 | 4 3 13 | 2 6 14 | 15 | ~ 3 16 | 0 12 17 | 18 | 19 | 20 | 7-4 3 21 | ~ 6 22 | 23 | ~ ~ 24 | 25 | ~ 3 26 | 8-4 ~ 27 | 4 27 28 | -------------------------------------------------------------------------------- /love/major-minor/mario/4/triangle.trk: -------------------------------------------------------------------------------- 1 | scl # 48, scl size 7, scl default 2 | 3 | note dur inst 4 | 0-4 9 bass 5 | 6 | 7 | 4-4 ~ 8 | 9 | 10 | 7-4 6 11 | 12 | 4 9 13 | 14 | 15 | 0 ~ 16 | 17 | 18 | -3 6 19 | 20 | 0-4 9 21 | 22 | 23 | 4-4 ~ 24 | 25 | 26 | 7-4 6 27 | 28 | 4 9 29 | 30 | 31 | 0 ~ 32 | 33 | 34 | -3 6 35 | -------------------------------------------------------------------------------- /love/major-minor/mario/5/noise.trk: -------------------------------------------------------------------------------- 1 | scl # 1, scl size 1, scl default 2 | 3 | note dur inst 4 | 12 9 hhc 5 | 6 | 7 | ~ 3 8 | 11 6 hho 9 | 10 | 12 ~ hhc 11 | -------------------------------------------------------------------------------- /love/major-minor/mario/5/pulse1.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 9 3 puls 5 | 7 6 6 | 7 | 5-2 9 8 | 9 | 10 | 5-1 6 11 | 12 | 5 3 13 | 10 6 14 | 15 | ~ 3 16 | 5 12 17 | 18 | 19 | 20 | 7-1 4 21 | 12 ~ 22 | 23 | ~ ~ 24 | ~ ~ 25 | 11 ~ 26 | 27 | 10 ~ 28 | 9 3 29 | 7 6 30 | 31 | 5 3 32 | 4 12 33 | 34 | 35 | 36 | 9 3 37 | 7 6 38 | 39 | 5-2 9 40 | 41 | 42 | 5-1 6 43 | 44 | 5 3 45 | 10 6 46 | 47 | ~ 3 48 | 5 12 49 | 50 | 51 | 52 | 7-1 3 53 | 10 6 54 | 55 | ~ 3 56 | ~ 4 57 | 9 ~ 58 | 59 | 8 ~ 60 | 7 24 61 | -------------------------------------------------------------------------------- /love/major-minor/mario/5/pulse2.trk: -------------------------------------------------------------------------------- 1 | scl # 60, scl size 7, scl default 2 | 3 | note dur inst 4 | 7 3 puls 5 | 5 6 6 | 7 | 2 9 8 | 9 | 10 | 3-1 6 11 | 12 | 3 3 13 | 7 6 14 | 15 | ~ 3 16 | 3 12 17 | 18 | 19 | 20 | 4 4 21 | 10 ~ 22 | 23 | ~ ~ 24 | ~ ~ 25 | 9 ~ 26 | 27 | 8 ~ 28 | 7 3 29 | 5 6 30 | 31 | 3 3 32 | 2 12 33 | 34 | 35 | 36 | 7 3 37 | 5 6 38 | 39 | 2 9 40 | 41 | 42 | 3-1 6 43 | 44 | 3 3 45 | 7 6 46 | 47 | ~ 3 48 | 3 12 49 | 50 | 51 | 52 | 4 3 53 | 8 6 54 | 55 | ~ 3 56 | ~ 4 57 | 7 ~ 58 | 59 | 7-1 ~ 60 | 4 3 61 | 2 6 62 | 63 | ~ 3 64 | 0 12 65 | -------------------------------------------------------------------------------- /love/major-minor/mario/5/triangle.trk: -------------------------------------------------------------------------------- 1 | scl # 48, scl size 7, scl default 2 | 3 | note dur inst 4 | 0 9 bass 5 | 6 | 7 | 4-1 3 8 | 4 6 9 | 10 | 7 ~ 11 | 12 | 3 ~ 13 | 14 | ~ ~ 15 | 16 | 7 3 17 | ~ 3 18 | 3 6 19 | 20 | 1 9 21 | 22 | 23 | 3 3 24 | 4 6 25 | 26 | 7-1 ~ 27 | 28 | 4 ~ 29 | 30 | ~ ~ 31 | 32 | 7 3 33 | ~ 3 34 | 4 6 35 | 36 | 0 9 37 | 38 | 39 | 4-1 3 40 | 4 6 41 | 42 | 7 ~ 43 | 44 | 3 ~ 45 | 46 | ~ ~ 47 | 48 | 7 3 49 | ~ 3 50 | 3 6 51 | 52 | 4 9 53 | 54 | 55 | ~ 3 56 | ~ 4 57 | 5 ~ 58 | 7-1 ~ 59 | 60 | 7 6 61 | 62 | 4 ~ 63 | 64 | 0 12 65 | -------------------------------------------------------------------------------- /love/major-minor/mario/inst/bass.inst: -------------------------------------------------------------------------------- 1 | 1 0.03 2 | 0 0.03 2.4 3 | -------------------------------------------------------------------------------- /love/major-minor/mario/inst/hhc.inst: -------------------------------------------------------------------------------- 1 | 0.58 0.03 2 | 0.38 0.25 0.03 3 | 0 0.03 0.3 4 | -------------------------------------------------------------------------------- /love/major-minor/mario/inst/hho.inst: -------------------------------------------------------------------------------- 1 | 0.46 0.03 2 | 0.28 1.75 0.03 3 | 0 0.03 1.9 4 | -------------------------------------------------------------------------------- /love/major-minor/mario/inst/puls.inst: -------------------------------------------------------------------------------- 1 | 0.4 0.03 0.27 2 | 0.2 2 0.32 3 | 0 0.03 2.6 4 | -------------------------------------------------------------------------------- /love/major-minor/mario/inst/rim.inst: -------------------------------------------------------------------------------- 1 | 0.6 0.03 2 | 0.45 0.4 0.03 3 | 0 0.1 0.45 4 | -------------------------------------------------------------------------------- /love/major-minor/mario/loop.trk: -------------------------------------------------------------------------------- 1 | goto 2 2 | 192 3 | 4 | goto 3 5 | 384 6 | 7 | goto 4 8 | 144 9 | 10 | goto 1 11 | 48 12 | 13 | goto 2 14 | 192 15 | 16 | goto 5 17 | 384 18 | 19 | goto 4 20 | 144 21 | 22 | goto 1 23 | 48 24 | 25 | goto 5 26 | 192 27 | 28 | loop 29 | -------------------------------------------------------------------------------- /love/major-minor/mario/main.trk: -------------------------------------------------------------------------------- 1 | goto 1 2 | 48 3 | 4 | loop 5 | -------------------------------------------------------------------------------- /love/major-minor/pulse~.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 370 310 14; 2 | #X declare -lib cyclone; 3 | #X obj 50 100 inlet~; 4 | #X obj 150 100 inlet; 5 | #X obj 150 130 zp~ 5 0.5; 6 | #X obj 240 100 sel; 7 | #X msg 240 130 now \$1; 8 | #X obj 50 160 >~; 9 | #X obj 50 250 outlet~; 10 | #X obj 170 190 cnv 15 86 24 empty empty pulse~ 4 12 0 18 #000000 #fcfcfc 11 | 0; 12 | #X obj 50 190 -~ 0.5; 13 | #X obj 140 250 declare -lib cyclone; 14 | #X obj 150 30 loadbang; 15 | #X obj 160 70 inlet; 16 | #X obj 50 130 phasor~; 17 | #X obj 50 70 \$2; 18 | #X obj 240 70 \$1; 19 | #X obj 50 220 *~ 2; 20 | #X connect 0 0 12 0; 21 | #X connect 1 0 2 0; 22 | #X connect 2 0 5 1; 23 | #X connect 3 1 4 0; 24 | #X connect 4 0 2 0; 25 | #X connect 5 0 8 0; 26 | #X connect 8 0 15 0; 27 | #X connect 10 0 13 0; 28 | #X connect 10 0 14 0; 29 | #X connect 11 0 12 1; 30 | #X connect 12 0 5 0; 31 | #X connect 13 0 0 0; 32 | #X connect 14 0 3 0; 33 | #X connect 15 0 6 0; 34 | -------------------------------------------------------------------------------- /love/metronome/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.title = 'Metronome' 3 | t.window.width = 300 4 | t.window.height = 600 5 | end 6 | -------------------------------------------------------------------------------- /love/metronome/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | package.path = '../?.lua;' .. package.path 6 | local lpd = require('pdmain') 7 | local pd = lpd.pd 8 | local patch ---@type PdPatch 9 | 10 | local gui = require('pdgui')(pd) 11 | local sliders, buttons 12 | 13 | 14 | -- Widget Callbacks 15 | 16 | local function btnClick(self) 17 | pd:sendFloat(self.dest, self.num) 18 | sliders[1].axis.y.num = self.num 19 | sliders[1].axis.y.bpm = 60000 / self.num 20 | sliders[1]:pos('y') 21 | end 22 | 23 | local function metChange(self, num) 24 | self.num = num 25 | self.bpm = 60000 / self.num 26 | pd:sendFloat(self.dest, self.num) 27 | end 28 | 29 | local lbl = 'milliseconds per beat:\nbeats per minute:' 30 | 31 | local function metDraw(self) 32 | local str = string.format('%.4g', self.num) .. '\n' 33 | .. string.format('%.4g', self.bpm) 34 | love.graphics.printf(lbl, 10, 10, 200, 'right') 35 | love.graphics.printf(str, 215, 10, 50, 'left') 36 | end 37 | 38 | function love.load() 39 | lpd.init() 40 | patch = lpd.open { play = false } 41 | 42 | local dlr = patch:dollarZeroStr() 43 | local width, height = love.graphics.getDimensions() 44 | 45 | local met = { 46 | dest = dlr..'met', min = 2000, max = 20, num = 1000, snap = 10 47 | , log = true, change = metChange, drawText = metDraw 48 | } 49 | met.bpm = 60000 / met.num 50 | local vol = { 51 | dest = dlr..'vol', min = -60, max = 0, num = -15, snap = 10 52 | , change = gui.volChange, label = { text = 'volume', y = 530 } 53 | } 54 | 55 | local rad = 25 56 | gui.slider.rad = rad 57 | gui.slider.lblx = -100 58 | gui.slider.len = height - 100 59 | sliders = { 60 | gui.slider(rad * 2, 60, { y = met }, { rgb = { .25, .66, .66 } }) 61 | , gui.slider(width - rad * 4, 60, { y = vol }, { rgb = { .75, .5, .75 } }) 62 | } 63 | 64 | gui.button.size = 33 65 | gui.button.click = btnClick 66 | gui.button.dest = dlr..'met' 67 | local bx, by = width / 2 - 16, 235 68 | buttons = { 69 | gui.button(bx, by, { label = { text = '750' }, num = 750 }) 70 | , gui.button(bx, by + 75, { label = { text = '875' }, num = 875 }) 71 | , gui.button(bx, by + 150, { label = { text = '1000' }, num = 1000 }) 72 | } 73 | 74 | sliders[2]:send() 75 | pd:sendFloat(dlr..'accent1', 12) 76 | pd:sendFloat(dlr..'accent2', 4) 77 | pd:sendFloat(dlr..'play', met.num) 78 | end 79 | 80 | function love.update(dt) 81 | gui.updateSliders(sliders) 82 | for i = #buttons, 1, -1 do buttons[i]:update(dt) end 83 | lpd.update() 84 | end 85 | 86 | function love.mousepressed(x, y) 87 | for i = #buttons, 1, -1 do 88 | if buttons[i]:mousepressed(x, y) then return end 89 | end 90 | end 91 | 92 | function love.draw() 93 | for i = 1, #sliders do sliders[i]:draw() end 94 | for i = 1, #buttons do buttons[i]:draw() end 95 | end 96 | 97 | function love.quit() 98 | pd:closePatch(patch) 99 | pd:computeAudio(false) 100 | end 101 | -------------------------------------------------------------------------------- /love/metronome/main.pd: -------------------------------------------------------------------------------- 1 | #N canvas 200 460 460 400 14; 2 | #X declare -path pd; 3 | #X obj 40 270 *~; 4 | #X obj 40 340 dac~; 5 | #X obj 110 240 noise~; 6 | #X obj 40 300 *~; 7 | #X floatatom 160 330 0 0 0 0 - - - 0; 8 | #X obj 110 270 hsl 128 20 0.001 1 1 0 empty empty volume 35 10 0 12 #2f004d #fcfcfc #fcfcfc 0 1; 9 | #N canvas 970 490 450 300 vol 0; 10 | #X obj 150 180 zp~ 15; 11 | #X msg 150 110 0; 12 | #X obj 150 50 inlet; 13 | #X obj 150 210 outlet~; 14 | #X obj 210 180 outlet; 15 | #X obj 150 140 f; 16 | #X obj 150 80 moses 0.00101; 17 | #X obj 190 140 r \$0vol; 18 | #X connect 0 0 3 0; 19 | #X connect 1 0 5 0; 20 | #X connect 2 0 6 0; 21 | #X connect 5 0 0 0; 22 | #X connect 5 0 4 0; 23 | #X connect 6 0 1 0; 24 | #X connect 6 1 5 0; 25 | #X connect 7 0 0 0; 26 | #X restore 110 300 pd vol; 27 | #X floatatom 40 100 0 0 0 0 - - - 0; 28 | #N canvas 560 520 360 350 velocity 0; 29 | #X obj 30 20 inlet; 30 | #X obj 30 80 mod 4; 31 | #X obj 30 50 f; 32 | #X obj 70 50 + 1; 33 | #X obj 30 300 outlet; 34 | #X obj 130 50 inlet; 35 | #X obj 30 110 t f f; 36 | #X obj 30 240 *; 37 | #X obj 220 50 inlet; 38 | #X obj 30 270 @/ 1; 39 | #X floatatom 90 300 0 0 0 0 - - - 0; 40 | #X obj 230 110 % 4; 41 | #X obj 230 140 sel; 42 | #X obj 30 140 sel; 43 | #X obj 30 200 unpack; 44 | #X obj 80 270 outlet; 45 | #X obj 100 200 pack; 46 | #X obj 230 200 unpack; 47 | #X obj 100 170 sly 1.25 2.5 4; 48 | #X msg 280 170 12 3.5; 49 | #X msg 230 170 24 1; 50 | #X msg 30 170 1 72; 51 | #X obj 90 20 inlet; 52 | #X connect 0 0 2 0; 53 | #X connect 1 0 3 0; 54 | #X connect 1 0 6 0; 55 | #X connect 2 0 1 0; 56 | #X connect 3 0 2 1; 57 | #X connect 5 0 1 1; 58 | #X connect 5 0 18 3; 59 | #X connect 6 0 13 0; 60 | #X connect 6 1 11 0; 61 | #X connect 7 0 9 0; 62 | #X connect 8 0 11 1; 63 | #X connect 9 0 4 0; 64 | #X connect 9 0 10 0; 65 | #X connect 11 0 12 0; 66 | #X connect 12 0 20 0; 67 | #X connect 12 1 19 0; 68 | #X connect 13 0 21 0; 69 | #X connect 13 1 18 0; 70 | #X connect 14 0 7 0; 71 | #X connect 14 1 15 0; 72 | #X connect 16 0 14 0; 73 | #X connect 17 0 16 1; 74 | #X connect 17 1 7 1; 75 | #X connect 18 0 16 0; 76 | #X connect 19 0 17 0; 77 | #X connect 20 0 17 0; 78 | #X connect 21 0 14 0; 79 | #X connect 22 0 2 1; 80 | #X restore 40 200 pd velocity; 81 | #X msg 40 70 750; 82 | #X msg 120 70 1000; 83 | #X msg 80 70 875; 84 | #X obj 40 140 @/ 1000; 85 | #X msg 340 70 4; 86 | #X msg 300 170 4; 87 | #X floatatom 300 200 0 0 0 0 - - - 0; 88 | #X floatatom 300 100 0 0 0 0 - - - 0; 89 | #X msg 300 70 12; 90 | #X obj 40 240 ad 1.5; 91 | #X obj 110 140 t f f; 92 | #X obj 40 170 metro~; 93 | #X obj 300 270 r \$0play; 94 | #X obj 300 300 pipe 500; 95 | #X obj 150 100 r \$0beat; 96 | #X obj 120 170 r \$0accent1; 97 | #X obj 140 200 r \$0accent2; 98 | #X obj 300 130 s \$0accent1; 99 | #X obj 300 230 s \$0accent2; 100 | #X obj 40 30 declare -path pd; 101 | #X obj 80 100 r \$0met; 102 | #X obj 300 330 s \$0met; 103 | #X connect 0 0 3 0; 104 | #X connect 2 0 0 1; 105 | #X connect 3 0 1 0; 106 | #X connect 3 0 1 1; 107 | #X connect 5 0 6 0; 108 | #X connect 6 0 3 1; 109 | #X connect 6 1 4 0; 110 | #X connect 7 0 12 0; 111 | #X connect 8 0 18 0; 112 | #X connect 8 1 18 2; 113 | #X connect 9 0 7 0; 114 | #X connect 10 0 7 0; 115 | #X connect 11 0 7 0; 116 | #X connect 12 0 20 0; 117 | #X connect 13 0 16 0; 118 | #X connect 14 0 15 0; 119 | #X connect 15 0 27 0; 120 | #X connect 16 0 26 0; 121 | #X connect 17 0 16 0; 122 | #X connect 18 0 0 0; 123 | #X connect 19 0 20 1; 124 | #X connect 19 1 8 1; 125 | #X connect 20 0 8 0; 126 | #X connect 21 0 22 0; 127 | #X connect 22 0 30 0; 128 | #X connect 23 0 19 0; 129 | #X connect 24 0 8 2; 130 | #X connect 25 0 8 3; 131 | #X connect 29 0 12 0; 132 | -------------------------------------------------------------------------------- /love/pdgui.lua: -------------------------------------------------------------------------------- 1 | local function clamp(x, min, max) 2 | return (x < min and min) or (x > max and max) or x 3 | end 4 | 5 | local function Fif(cond, T, F) 6 | if cond then return T else return F end 7 | end 8 | 9 | local function getOptions(opt, default) 10 | if type(opt) ~= 'table' then 11 | opt = default 12 | else 13 | setmetatable(opt, {__index = default}) 14 | end 15 | return opt 16 | end 17 | 18 | local epsilon = 2.2204460492503131e-16 19 | 20 | local pd ---@type PdBase 21 | local gui = {} -- Pd gui library 22 | local focus -- the slider receiving focus or nil 23 | 24 | ------------------------------------------------------------------------- 25 | -------------------------------- Sliders -------------------------------- 26 | ------------------------------------------------------------------------- 27 | 28 | local function slider_send(sl, ax) 29 | if ax then 30 | sl.axis[ax]:change(sl.axis[ax].num) 31 | else 32 | for _, v in next, sl.axis do 33 | v:change(v.num) 34 | end 35 | end 36 | end 37 | 38 | local function slider_check(sl, x, ax) 39 | local v = sl.axis[ax] 40 | local dx = 'd'..ax 41 | x = clamp(x, v.xmin, v.xmax) 42 | if sl[dx] ~= x then 43 | sl[dx] = x 44 | x = x - v.xmin 45 | if v.snap then 46 | local grav = math.floor(x / v.sm + 0.5) * v.sm 47 | if v.gap == 0 or (x >= grav - v.gap and x <= grav + v.gap) then 48 | x = grav 49 | end 50 | end 51 | 52 | local num = v.log 53 | and (math.exp(v.m * x) * v.b) 54 | or v.m * x + v.b 55 | if math.abs(num) < epsilon then 56 | num = 0 57 | end 58 | if v.num ~= num then 59 | v:change(num) 60 | end 61 | sl['c'..ax] = x + v.xmin 62 | end 63 | end 64 | 65 | local function slider_update(sl, x, y) 66 | if focus == sl then 67 | if sl.axis.x then slider_check(sl, x, 'x') end 68 | if sl.axis.y then slider_check(sl, y, 'y') end 69 | return true 70 | elseif focus == nil 71 | and x >= sl.x and x < sl.xx 72 | and y >= sl.y and y < sl.yy then 73 | if sl.axis.x then slider_check(sl, x, 'x') end 74 | if sl.axis.y then slider_check(sl, y, 'y') end 75 | focus = sl 76 | return true 77 | end 78 | return false 79 | end 80 | 81 | local function sliders_update(t) 82 | if love.mouse.isDown(1) then 83 | local x, y = love.mouse.getPosition() 84 | -- reverse list order to prioritize items rendered last 85 | for i = #t, 1, -1 do 86 | if t[i]:update(x, y) then break end 87 | end 88 | elseif focus then 89 | focus = nil 90 | end 91 | end 92 | 93 | local function axis_draw_text(ax) 94 | love.graphics.print(string.format(ax.fmt, ax.label.text, ax.num) 95 | , ax.label.x, ax.label.y) 96 | end 97 | 98 | local function axis_draw_text_fixed(self) 99 | local precision = self.prec - string.len(tostring(math.floor(self.num + 0.0001))) 100 | precision = string.format('%.'..precision..'f', self.num) 101 | love.graphics.print(string.format(self.fmt, self.label.text, precision) 102 | , self.label.x, self.label.y) 103 | end 104 | 105 | local function slider_draw(sl) 106 | love.graphics.setColor(0.1, 0.1, 0.1) 107 | love.graphics.rectangle('fill', sl.x, sl.y, sl.xlen, sl.ylen, sl.rad) 108 | love.graphics.setColor(0.25, 0.25, 0.25) 109 | love.graphics.rectangle('line', sl.x, sl.y, sl.xlen, sl.ylen, sl.rad) 110 | 111 | love.graphics.setColor(sl.rgb) 112 | love.graphics.circle('fill', sl.cx, sl.cy, sl.rad) 113 | love.graphics.setColor(1, 1, 1) 114 | love.graphics.circle('line', sl.cx, sl.cy, sl.rad) 115 | 116 | for _, v in next, sl.axis do 117 | v:drawText() 118 | end 119 | end 120 | 121 | local function slider_minmax(v, min, max, diam) 122 | if min == 0.0 and max == 0.0 then 123 | max = 1.0 124 | end 125 | if max > 0.0 then 126 | if min <= 0.0 then 127 | min = 0.01 * max 128 | end 129 | else 130 | if min > 0.0 then 131 | max = 0.01 * min 132 | end 133 | end 134 | v.min = min 135 | v.max = max 136 | return math.log(v.max / v.min) / (v.len - diam) 137 | end 138 | 139 | local function slider_pos(sl, ax) 140 | local v = sl.axis[ax] 141 | if not v then return end 142 | 143 | -- determine knob position 144 | local cx = 'c'..ax 145 | sl[cx] = (v.m == 0 and v.xmin) 146 | or (v.log and v.xmin + math.log(v.num / v.b) / v.m) 147 | or (v.xmin + (v.num - v.b) / v.m) 148 | sl['d'..ax] = sl[cx] -- internal position for snapping 149 | end 150 | 151 | local function slider_axis(sl, v, ax) 152 | local x = ax 153 | local diam = sl.rad * 2 154 | if type(v) ~= 'table' then 155 | sl['c'..x] = sl[x] + sl.rad 156 | sl[x..x] = sl[x] + diam 157 | sl[x..'len'] = diam 158 | else 159 | setmetatable(v, {__index = sl}) 160 | local xlen = x..'len' 161 | local xx = x..x 162 | if v.len < diam then v.len = diam end 163 | sl[xlen] = v.len 164 | sl[xx] = sl[x] + v.len 165 | v.xmin = sl[x] + sl.rad 166 | v.xmax = sl[xx] - sl.rad 167 | 168 | -- swap min/max if slider is vertical 169 | if ax == 'y' then 170 | v.min, v.max = v.max, v.min 171 | end 172 | 173 | v.m = v.log and 174 | slider_minmax(v, v.min, v.max, diam) or (v.max - v.min) / (v.len - diam) 175 | v.b = v.min 176 | 177 | -- swap min/max back 178 | if ax == 'y' then 179 | v.min, v.max = v.max, v.min 180 | end 181 | 182 | v.num = not v.num and v.min or (v.min > v.max and 183 | clamp(v.num, v.max, v.min) or clamp(v.num, v.min, v.max)) 184 | 185 | -- snap to grid 186 | if v.snap then 187 | v.sm = v.log and math.log(v.snap) / v.m or v.snap / v.m 188 | if v.sm == 0 then 189 | v.sm = 1 190 | end 191 | end 192 | sl:pos(ax) 193 | 194 | v.label = v.label or {} 195 | v.label.text = v.label.text or v.dest 196 | v.label.x = math.floor((v.label.x or v.lblx) + sl.x + sl.rad) 197 | v.label.y = math.floor((v.label.y or v.lbly) + sl.y - 24) 198 | end 199 | end 200 | 201 | local function slider_new(self, x, y, axis, opt) 202 | opt = getOptions(opt, self) 203 | local sl = { 204 | x = x 205 | , y = y 206 | , axis = axis 207 | } 208 | setmetatable(sl, {__index = opt}) 209 | sl.rad = math.abs(sl.rad) 210 | slider_axis(sl, sl.axis.x, 'x') 211 | slider_axis(sl, sl.axis.y, 'y') 212 | return sl 213 | end 214 | 215 | ----------------------------------------------------------------------- 216 | -------------------------------- Boxes -------------------------------- 217 | ----------------------------------------------------------------------- 218 | 219 | local function gui_box(x, y, opt) 220 | local box = { 221 | x = x 222 | , y = y 223 | } 224 | setmetatable(box, {__index = opt}) 225 | box.xx = box.x + box.size 226 | box.yy = box.y + box.size 227 | 228 | local label = opt.label or {} 229 | label.text = label.text or box.dest 230 | label.x = math.floor((label.x or box.lblx) + box.x) 231 | label.y = math.floor((label.y or box.lbly) + box.y - 24) 232 | box.label = label 233 | 234 | return box 235 | end 236 | 237 | local function button_draw(bt) 238 | love.graphics.setColor(.25, .25, .25) 239 | love.graphics.rectangle('fill', bt.x, bt.y, bt.size, bt.size, 5) 240 | 241 | if bt.circ.on then 242 | love.graphics.setColor(.85, .85, .85) 243 | love.graphics.circle('fill', bt.circ.x, bt.circ.y, bt.circ.rad) 244 | end 245 | love.graphics.setColor(.5, .5, .5) 246 | love.graphics.circle('line', bt.circ.x, bt.circ.y, bt.circ.rad) 247 | 248 | love.graphics.setColor(1, 1, 1) 249 | love.graphics.rectangle('line', bt.x, bt.y, bt.size, bt.size, 5) 250 | 251 | love.graphics.print(bt.label.text, bt.label.x, bt.label.y) 252 | end 253 | 254 | local function button_update(bt, dt) 255 | if bt.circ.on then 256 | bt.circ.dt = bt.circ.dt - dt 257 | if bt.circ.dt <= 0 then 258 | bt.circ.on = false 259 | end 260 | end 261 | end 262 | 263 | local function button_mousepressed(bt, x, y) 264 | if x >= bt.x and x < bt.xx 265 | and y >= bt.y and y < bt.yy then 266 | bt.circ.on = true 267 | bt.circ.dt = bt.circ.delay 268 | bt:click() 269 | return true 270 | else return false end 271 | end 272 | 273 | local function button_new(self, x, y, opt) 274 | opt = getOptions(opt, self) 275 | local btn = gui_box(x, y, opt) 276 | btn.circ = { dt = 0, on = false, delay = opt.delay } 277 | btn.circ.rad = btn.size * 5 / 11 278 | btn.circ.x = btn.x + btn.size / 2 279 | btn.circ.y = btn.y + btn.size / 2 280 | return btn 281 | end 282 | 283 | local function toggle_mousepressed(tg, x, y) 284 | if x >= tg.x and x < tg.xx 285 | and y >= tg.y and y < tg.yy then 286 | tg:click() 287 | return true 288 | else return false end 289 | end 290 | 291 | local function toggle_draw(tg) 292 | love.graphics.setColor(.25, .25, .25) 293 | love.graphics.rectangle('fill', tg.x, tg.y, tg.size, tg.size, 5) 294 | love.graphics.setColor(1, 1, 1) 295 | love.graphics.rectangle('line', tg.x, tg.y, tg.size, tg.size, 5) 296 | 297 | if tg.on then 298 | love.graphics.line(tg.x + 5, tg.y + 5, tg.xx - 5, tg.yy - 5) 299 | love.graphics.line(tg.x + 5, tg.yy - 5, tg.xx - 5, tg.y + 5) 300 | end 301 | 302 | love.graphics.print(tg.label.text, tg.label.x, tg.label.y) 303 | end 304 | 305 | local function toggle_new(self, x, y, opt) 306 | opt = getOptions(opt, self) 307 | local tgl = gui_box(x, y, opt) 308 | return tgl 309 | end 310 | 311 | -- Default Callbacks 312 | 313 | local function slider_change(self, num) 314 | self.num = num 315 | pd:sendFloat(self.dest, self.num) 316 | end 317 | 318 | local function vol_change(self, num) 319 | self.num = num 320 | if num > self.min then 321 | self.fmt = '%s: %+.1f dB' 322 | pd:sendFloat(self.dest, 10^(self.num / 20)) 323 | else 324 | self.fmt = '%s: -∞ dB' 325 | pd:sendFloat(self.dest, 0) 326 | end 327 | end 328 | 329 | local function button_click(self) 330 | pd:sendBang(self.dest) 331 | end 332 | 333 | local function toggle_click(self, on) 334 | self.on = Fif(on ~= nil, on, not self.on) 335 | pd:sendFloat(self.dest, self.on and self.non0 or 0) 336 | end 337 | 338 | -- Reset all widget properties to their default values 339 | function gui:reset() 340 | self.slider = { 341 | change = slider_change 342 | , send = slider_send 343 | , update = slider_update 344 | , draw = slider_draw 345 | , drawText = axis_draw_text 346 | , pos = slider_pos 347 | , rgb = { .5, .5, .5 } -- knob color 348 | , log = false -- logarithmic scaling 349 | -- snap to a grid with spacing of this amount relative to the scale. 350 | -- nil or false disables snapping. 351 | , snap = nil 352 | -- a snap point's gravitational radius in pixels. 353 | -- if gap is 0, knob will settle exclusively on snap points. 354 | , gap = 7 355 | , fmt = '%s: %g' -- string format when displaying name and value 356 | , prec = 5 -- precision - used alongside fixed precision mode 357 | , rad = 25 -- knob radius 358 | , len = 100 -- axis length 359 | , dest = 'foo' -- send-to destination 360 | , lblx = 0 -- label x offset 361 | , lbly = 0 -- label y offset 362 | } 363 | self.button = { 364 | click = button_click 365 | , draw = button_draw 366 | , update = button_update 367 | , mousepressed = button_mousepressed 368 | , delay = 0.2 -- circle display duration on click 369 | , size = 25 370 | , dest = 'foo' -- send-to destination 371 | , lblx = 0 -- label x offset 372 | , lbly = 0 -- label y offset 373 | } 374 | self.toggle = { 375 | click = toggle_click 376 | , draw = toggle_draw 377 | , mousepressed = toggle_mousepressed 378 | , on = false -- initial state 379 | , non0 = 1 -- non-zero value 380 | , size = 25 381 | , dest = 'foo' -- send-to destination 382 | , lblx = 0 -- label x offset 383 | , lbly = 0 -- label y offset 384 | } 385 | self.updateSliders = sliders_update 386 | self.volChange = vol_change 387 | self.drawFixed = axis_draw_text_fixed 388 | setmetatable(self.slider, { __call = slider_new }) 389 | setmetatable(self.button, { __call = button_new }) 390 | setmetatable(self.toggle, { __call = toggle_new }) 391 | end 392 | 393 | ---@param base PdBase 394 | return function(base) 395 | pd = base 396 | gui:reset() 397 | return gui 398 | end 399 | -------------------------------------------------------------------------------- /love/pdmain.lua: -------------------------------------------------------------------------------- 1 | local function clamp(x, min, max) 2 | return (x < min and min) or (x > max and max) or x 3 | end 4 | 5 | local ext = { 6 | ['Linux'] = 'so' 7 | , ['Windows'] = 'dll' 8 | , ['OS X'] = 'dylib' 9 | } 10 | local stros = love.system.getOS() 11 | package.cpath = '../../?.' .. ext[stros] .. ';' .. package.cpath 12 | 13 | Pd = require('luapd') ---@type Pd 14 | local ticks, bufs ---@type integer, integer 15 | local message ---@type string 16 | 17 | local lpd = { -- default options 18 | ticks = 1 19 | , bufs = 33 20 | , play = true 21 | , patch = 'main.pd' 22 | , volume = 1 23 | } 24 | 25 | lpd.pd = Pd.Base() 26 | lpd.obj = Pd.Object { 27 | print = function(msg) 28 | message = msg 29 | print(msg) 30 | end 31 | } 32 | local pd = lpd.pd 33 | local sdata ---@type love.SoundData 34 | local source ---@type love.Source 35 | local srate = require('samplerate') 36 | 37 | local chIn = 0 38 | local chOut = 2 39 | local queued = false 40 | local bitdepth = 16 41 | 42 | local function getOptions(opt) 43 | if type(opt) ~= 'table' then 44 | opt = lpd 45 | else 46 | setmetatable(opt, {__index = lpd}) 47 | end 48 | return opt 49 | end 50 | 51 | ---@param opt table|nil # A list of options 52 | function lpd.init(opt) 53 | opt = getOptions(opt) 54 | ticks, bufs = opt.ticks, opt.bufs 55 | pd:setReceiver(opt.obj) 56 | if not pd:init(chIn, chOut, srate, queued) then 57 | print('Could not initialize pd') 58 | love.event.quit() 59 | end 60 | pd:addToSearchPath('../../pd/lib') 61 | pd:computeAudio(true) 62 | 63 | local size = pd.blockSize() * ticks 64 | sdata = love.sound.newSoundData(size, srate, bitdepth, chOut) 65 | source = love.audio.newQueueableSource(srate, bitdepth, chOut, bufs) 66 | love.graphics.setFont(love.graphics.newFont(16)) 67 | end 68 | 69 | ---@param opt table|nil # A list of options 70 | ---@return PdPatch 71 | function lpd.open(opt) 72 | opt = getOptions(opt) 73 | local play, patch, volume = opt.play, opt.patch, clamp(opt.volume, -1, 1) 74 | patch = pd:openPatch(patch) 75 | local dlr = patch:dollarZeroStr() 76 | pd:sendFloat(dlr .. 'vol', volume) 77 | if play then 78 | pd:sendBang(dlr .. 'play') 79 | end 80 | return patch 81 | end 82 | 83 | function lpd.update() 84 | while source:getFreeBufferCount() > 0 do 85 | pd:processShort(ticks, nil, sdata:getPointer()) 86 | source:queue(sdata) 87 | end 88 | source:play() -- keep playing if there are underruns 89 | end 90 | 91 | function lpd.draw() 92 | love.graphics.print(message, 0, 0) 93 | end 94 | 95 | function lpd.print_delay() 96 | local blk = pd.blockSize() 97 | local sr = srate / 1000 98 | print('delay = ' .. ticks .. ' * ' .. bufs .. ' * ' .. blk 99 | .. ' / ' .. sr .. ' = ' .. ticks * bufs * blk / sr .. ' ms') 100 | end 101 | 102 | return lpd 103 | -------------------------------------------------------------------------------- /love/quicksort/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.title = 'Sorting Visual' 3 | end 4 | -------------------------------------------------------------------------------- /love/quicksort/ks.pd: -------------------------------------------------------------------------------- 1 | #N canvas 510 280 360 380 14; 2 | #X declare -lib blunt; 3 | #X obj 50 50 noise~; 4 | #X obj 50 220 *~; 5 | #X obj 70 180 vline~; 6 | #X obj 50 260 +~; 7 | #X obj 120 20 inlet; 8 | #X obj 50 320 outlet~; 9 | #X obj 60 290 delwrite~ \$0buf 1000; 10 | #X obj 170 150 delread4~ \$0buf; 11 | #X obj 170 90 @/ 1000; 12 | #X obj 240 320 block~ 1; 13 | #N canvas 400 200 450 300 lib 0; 14 | #X obj 140 120 declare -lib blunt; 15 | #X restore 50 20 pd lib; 16 | #X obj 170 180 *~ 0.999; 17 | #X obj 250 180 inlet; 18 | #X msg 70 150 \$1 \, 0 0 \$2; 19 | #X obj 70 120 pack; 20 | #X obj 120 50 t f f; 21 | #N canvas 730 350 450 300 loud 0; 22 | #X obj 160 100 inlet; 23 | #X obj 160 190 outlet; 24 | #X obj 160 160 clip 0.01 1; 25 | #X floatatom 270 160 0 0 0 0 - - - 0; 26 | #X floatatom 110 70 0 0 0 0 - - - 0; 27 | #X obj 110 100 mtof; 28 | #X obj 160 130 slx log 12 1800 1; 29 | #X connect 0 0 6 0; 30 | #X connect 2 0 1 0; 31 | #X connect 4 0 5 0; 32 | #X connect 5 0 6 0; 33 | #X connect 6 0 2 0; 34 | #X connect 6 0 3 0; 35 | #X restore 70 90 pd loud; 36 | #X obj 170 220 lop~ 6000; 37 | #X text 200 50 Karplus-Strong; 38 | #X connect 0 0 1 0; 39 | #X connect 1 0 3 0; 40 | #X connect 2 0 1 1; 41 | #X connect 3 0 5 0; 42 | #X connect 3 0 6 0; 43 | #X connect 4 0 15 0; 44 | #X connect 7 0 11 0; 45 | #X connect 8 0 7 0; 46 | #X connect 8 0 14 1; 47 | #X connect 11 0 17 0; 48 | #X connect 12 0 17 1; 49 | #X connect 13 0 2 0; 50 | #X connect 14 0 13 0; 51 | #X connect 15 0 16 0; 52 | #X connect 15 1 8 0; 53 | #X connect 16 0 14 0; 54 | #X connect 17 0 3 1; 55 | -------------------------------------------------------------------------------- /love/quicksort/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | math.randomseed(os.time()) 6 | 7 | package.path = '../?.lua;' .. package.path 8 | local lpd = require('pdmain') 9 | local pd = lpd.pd 10 | local patch ---@type PdPatch 11 | local dlra, dlrb ---@type string, string 12 | 13 | local margin = 5 14 | local arr = { size = 26 } 15 | arr.lo, arr.hi = 1, arr.size 16 | local frac = 1 / (arr.size + 1 + margin * 2) 17 | local auto = { on = false } 18 | local swaps, sorter, mode 19 | 20 | local rgb = { 21 | normal = { 0.3, 0.3, 0.3 } 22 | , swap = { 0.0, 1.0, 1.0 } 23 | , range = { 0.5, 0.0, 0.5 } 24 | , pivot = { 0.3, 0.7, 1.0 } 25 | } 26 | 27 | local gui = require('pdgui')(pd) 28 | local sliders, buttons, toggles 29 | 30 | local function setRange(lo, hi) 31 | local w = love.graphics.getWidth() 32 | arr.lo = w * frac * (lo + margin) - 10 33 | arr.hi = w * frac * (hi + margin) + 10 34 | end 35 | 36 | local function resetSort() 37 | swaps = nil 38 | sorter.step = 0 39 | end 40 | 41 | local function quicksort(a, lo, hi) 42 | while lo < hi do 43 | local pivot = a[lo] 44 | local i, j = lo + 1, hi 45 | while true do 46 | while a[j] > pivot do 47 | j = j - 1 48 | end 49 | while a[i] < pivot and i < j do 50 | i = i + 1 51 | end 52 | if i >= j then 53 | if j ~= lo then 54 | a[lo], a[j] = a[j], a[lo] 55 | swaps[#swaps + 1] = {lo, j, lo, hi, true} 56 | end 57 | break 58 | end 59 | a[i], a[j] = a[j], a[i] 60 | swaps[#swaps + 1] = {i, j, lo, hi, false} 61 | i = i + 1 ; j = j - 1 62 | end 63 | if j - lo < hi - j then 64 | quicksort(a, lo, j - 1) 65 | lo = j + 1 66 | else 67 | quicksort(a, j + 1, hi) 68 | hi = j - 1 69 | end 70 | end 71 | end 72 | 73 | ---------- Widget Callbacks ---------- 74 | 75 | local function btnShuffle() 76 | resetSort() 77 | setRange(1, arr.size) 78 | sorter.on = false 79 | for i = #arr, 2, -1 do 80 | local j = math.random(i) 81 | arr[i].x, arr[j].x = arr[j].x, arr[i].x 82 | arr[i], arr[j] = arr[j], arr[i] 83 | arr[i].rgb = rgb.normal 84 | end 85 | arr[1].rgb = rgb.normal 86 | end 87 | 88 | local function tglMode(self, on) 89 | auto.on = false 90 | sorter.on = false 91 | self.on = not self.on 92 | pd:sendFloat(self.dest, self.on and self.non0 or 0) 93 | end 94 | 95 | local function tglSort(self) 96 | auto.on = false 97 | mode.on = false 98 | pd:sendFloat(mode.dest, 0) 99 | self.on = not self.on 100 | if swaps or not self.on then 101 | return 102 | end 103 | 104 | local a = {} 105 | swaps = {} 106 | for i = 1, #arr do 107 | a[#a + 1] = arr[i].val 108 | end 109 | quicksort(a, 1, #a) 110 | if #swaps > 0 then 111 | self.on = true 112 | self.time = 0 113 | else 114 | self.on = false 115 | swaps = nil 116 | end 117 | end 118 | 119 | local function tempoChange(self, num) 120 | self.num = num 121 | sorter.dur = num / 1000 122 | auto.dur = sorter.dur * 2 123 | pd:sendFloat(self.dest, 1 / sorter.dur) 124 | end 125 | 126 | -------------------------------------- 127 | 128 | function love.load() 129 | lpd.init() 130 | patch = lpd.open({ play = false }) 131 | 132 | local w, h = love.graphics.getDimensions() 133 | local dlr = patch:dollarZeroStr() 134 | dlra, dlrb = dlr..'a', dlr..'b' 135 | 136 | -- fill the array 137 | for i = 1, arr.size do 138 | arr[i] = { 139 | val = i - 1 140 | , x = w * frac * (i + margin) - 5 141 | , y = h - h * frac * (i + margin) 142 | , rgb = rgb.normal 143 | } 144 | end 145 | arr.y = h - h * frac * (arr.size + margin) 146 | setRange(1, arr.size) 147 | 148 | gui.button.size, gui.toggle.size = 30, 30 149 | local half = gui.button.size / 2 150 | buttons = { 151 | gui.button(w * 3/8 - half, 50, { dest = 'shuffle' 152 | , label = { x = -13 }, click = btnShuffle }) 153 | } 154 | 155 | toggles = { 156 | gui.toggle(w * 4/8 - half, 50, { dest = 'sort' 157 | , label = { x = -1 }, click = tglSort }) 158 | , gui.toggle(w * 5/8 - half, 50, { dest = dlr..'mode' 159 | , label = { text = 'random', x = -18 }, click = tglMode }) 160 | } 161 | sorter = toggles[1] 162 | mode = toggles[2] 163 | for k, v in next, { time = 1, dur = 1, step = 0 } do 164 | sorter[k] = v 165 | end 166 | 167 | local vol = { 168 | dest = dlr..'vol', min = -60, max = 0, num = -15 169 | , label = { text = 'volume', x = -100 }, snap = 10 170 | , len = h - 100, change = gui.volChange 171 | } 172 | local tpo = { 173 | dest = dlr..'met', min = 4000, max = 31.25, num = sorter.dur * 1000 174 | , label = { text = 'tempo' }, snap = 2, log = true, len = h - 100 175 | , fmt = '%s: %s ms', drawText = gui.drawFixed, change = tempoChange 176 | } 177 | 178 | gui.slider.rad = 25 179 | gui.slider.len = w - 150 180 | local h = gui.slider.rad / 2 181 | sliders = { 182 | gui.slider(w - 90, 60, { y = vol }, { rgb = { .75, .5, .75 } }) 183 | , gui.slider(40 , 60, { y = tpo }, { rgb = { .5, .75, .75 } }) 184 | } 185 | for _, v in next, sliders do 186 | v:send() 187 | end 188 | pd:sendBang(dlr..'play') 189 | end 190 | 191 | function love.update(dt) 192 | gui.updateSliders(sliders) 193 | for i = #buttons, 1, -1 do buttons[i]:update(dt) end 194 | if auto.on then 195 | auto.time = auto.time - dt 196 | if auto.time <= 0 then 197 | btnShuffle() 198 | tglSort(sorter) 199 | sorter.time = auto.time + auto.dur 200 | end 201 | elseif sorter.on then 202 | sorter.time = sorter.time - dt 203 | if sorter.time <= 0 then 204 | if sorter.step > 0 then 205 | local i, j = swaps[sorter.step][1], swaps[sorter.step][2] 206 | arr[i].rgb, arr[j].rgb = rgb.normal, rgb.normal 207 | end 208 | sorter.step = sorter.step + 1 209 | local i, j = swaps[sorter.step][1], swaps[sorter.step][2] 210 | setRange(swaps[sorter.step][3], swaps[sorter.step][4]) 211 | 212 | arr[i].x, arr[j].x = arr[j].x, arr[i].x 213 | arr[i], arr[j] = arr[j], arr[i] 214 | arr[i].rgb = rgb.swap 215 | arr[j].rgb = swaps[sorter.step][5] and rgb.pivot or rgb.swap 216 | local a, b = arr[i].val, arr[j].val 217 | local diff = b - a 218 | if diff < 5 and a < 5 then 219 | b = b + 10 220 | elseif diff < 10 and a < 10 then 221 | b = b + 5 222 | end 223 | pd:sendFloat(dlra, a) 224 | pd:sendFloat(dlrb, b) 225 | 226 | if sorter.step >= #swaps then 227 | auto.on = true 228 | local even = #swaps % 2 == 0 and 2 or 1 229 | auto.time = sorter.time + auto.dur / even 230 | resetSort() 231 | end 232 | sorter.time = sorter.time + sorter.dur 233 | end 234 | end 235 | lpd.update() 236 | end 237 | 238 | function love.mousepressed(x, y) 239 | for i = #buttons, 1, -1 do 240 | if buttons[i]:mousepressed(x, y) then return end 241 | end 242 | for i = #toggles, 1, -1 do 243 | if toggles[i]:mousepressed(x, y) then return end 244 | end 245 | end 246 | 247 | function love.draw() 248 | for i = 1, #sliders do sliders[i]:draw() end 249 | for i = 1, #buttons do buttons[i]:draw() end 250 | for i = 1, #toggles do toggles[i]:draw() end 251 | 252 | local h = love.graphics.getHeight() 253 | h = h - 50 254 | for i = 1, #arr do 255 | love.graphics.setColor(arr[i].rgb) 256 | local x, y = arr[i].x, arr[i].y 257 | love.graphics.rectangle('fill', x, y, 10, h - y, 5) 258 | end 259 | love.graphics.setColor(rgb.range) 260 | love.graphics.line(arr.lo, arr.y, arr.lo, h) 261 | love.graphics.line(arr.hi, arr.y, arr.hi, h) 262 | end 263 | 264 | function love.quit() 265 | pd:closePatch(patch) 266 | pd:computeAudio(false) 267 | end 268 | -------------------------------------------------------------------------------- /love/quicksort/main.pd: -------------------------------------------------------------------------------- 1 | #N canvas 60 320 380 370 14; 2 | #X obj 50 250 dac~; 3 | #X obj 50 30 r \$0a; 4 | #X obj 50 220 *~; 5 | #X obj 190 30 r \$0b; 6 | #X obj 110 190 r \$0vol; 7 | #X obj 50 190 *~ 0.5; 8 | #X obj 190 60 t f b; 9 | #X obj 190 90 pipe; 10 | #X obj 50 60 t f b; 11 | #X obj 50 90 pipe; 12 | #X obj 90 220 hsl 187 22 0.001 1 1 0 empty empty empty -2 -11 0 14 #000000 #fcfcfc #fcfcfc 0 1; 13 | #X obj 240 90 rind 15; 14 | #X obj 100 90 rind 15; 15 | #N canvas 440 320 360 300 rand 0; 16 | #X obj 120 160 ct 4; 17 | #X floatatom 170 230 0 0 0 0 - - - 0; 18 | #X floatatom 260 230 0 0 0 0 - - - 0; 19 | #X obj 120 230 s \$0a; 20 | #X obj 210 230 s \$0b; 21 | #X obj 120 40 r \$0met; 22 | #X obj 210 100 loadbang; 23 | #X floatatom 190 40 0 0 0 0 - - - 0; 24 | #X msg 210 160 nop 1; 25 | #X obj 190 70 @/ 1000; 26 | #X obj 120 100 metro~ 1; 27 | #X obj 40 40 inlet; 28 | #X obj 40 70 switch~; 29 | #X obj 120 200 rand 11; 30 | #X obj 210 200 rand 11 26; 31 | #X connect 0 0 13 0; 32 | #X connect 5 0 10 0; 33 | #X connect 6 0 8 0; 34 | #X connect 7 0 9 0; 35 | #X connect 8 0 13 0; 36 | #X connect 8 0 14 0; 37 | #X connect 9 0 10 0; 38 | #X connect 10 0 0 0; 39 | #X connect 10 0 14 0; 40 | #X connect 11 0 12 0; 41 | #X connect 13 0 1 0; 42 | #X connect 13 0 3 0; 43 | #X connect 14 0 2 0; 44 | #X connect 14 0 4 0; 45 | #X restore 140 310 pd rand; 46 | #X obj 50 150 ks; 47 | #X obj 190 150 ks; 48 | #X obj 190 120 muse 28 2 5 7 8; 49 | #X obj 50 120 muse 28 2 5 7 8; 50 | #X obj 110 270 tgl 22 0 empty empty empty 0 -11 0 14 #000000 #fcfcfc #fcfcfc 0 1; 51 | #X obj 140 270 r \$0mode; 52 | #X connect 1 0 8 0; 53 | #X connect 2 0 0 0; 54 | #X connect 2 0 0 1; 55 | #X connect 3 0 6 0; 56 | #X connect 4 0 2 1; 57 | #X connect 5 0 2 0; 58 | #X connect 6 0 7 0; 59 | #X connect 6 1 11 0; 60 | #X connect 7 0 16 0; 61 | #X connect 8 0 9 0; 62 | #X connect 8 1 12 0; 63 | #X connect 9 0 17 0; 64 | #X connect 10 0 2 1; 65 | #X connect 11 0 7 1; 66 | #X connect 12 0 9 1; 67 | #X connect 14 0 5 0; 68 | #X connect 15 0 5 0; 69 | #X connect 16 0 15 0; 70 | #X connect 17 0 14 0; 71 | #X connect 18 0 13 0; 72 | #X connect 19 0 13 0; 73 | -------------------------------------------------------------------------------- /love/random-sound/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.title = 'Random Sound Generator' 3 | end 4 | -------------------------------------------------------------------------------- /love/random-sound/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | package.path = '../?.lua;' .. package.path 6 | local lpd = require('pdmain') 7 | local pd = lpd.pd 8 | local patch ---@type PdPatch 9 | 10 | local gui = require('pdgui')(pd) 11 | local sliders 12 | 13 | function love.load() 14 | lpd.init() 15 | patch = lpd.open { play = false } 16 | 17 | local dlr = patch:dollarZeroStr() 18 | local width, height = love.graphics.getDimensions() 19 | 20 | local met = { 21 | dest = dlr..'met', min = 11, max = 6000, num = 1000, log = true 22 | , label = { text = 'milliseconds per beat' } 23 | } 24 | local vol = { 25 | dest = dlr..'vol', min = -60, max = 0, num = -15 26 | , label = { text = 'volume', x = -100 } 27 | , snap = 10, len = height - 100, change = gui.volChange 28 | } 29 | 30 | gui.slider.rad = 25 31 | gui.slider.len = width - 150 32 | local h = gui.slider.rad / 2 33 | sliders = { 34 | gui.slider(20, height / 2 - h, { x = met }, { rgb = { .25, .66, .66 } }) 35 | , gui.slider(width - 90, 60, { y = vol }, { rgb = { .75, .5, .75 } }) 36 | } 37 | 38 | for _, v in next, sliders do 39 | v:send() 40 | end 41 | pd:sendBang(dlr .. 'play') 42 | end 43 | 44 | function love.update() 45 | gui.updateSliders(sliders) 46 | lpd.update() 47 | end 48 | 49 | function love.draw() 50 | for i = 1, #sliders do 51 | sliders[i]:draw() 52 | end 53 | end 54 | 55 | function love.quit() 56 | pd:closePatch(patch) 57 | pd:computeAudio(false) 58 | end 59 | -------------------------------------------------------------------------------- /love/random-sound/main.pd: -------------------------------------------------------------------------------- 1 | #N canvas 740 300 670 590 14; 2 | #X obj 370 530 dac~; 3 | #X obj 390 490 *~; 4 | #X floatatom 440 460 0 0 0 0 - - - 0; 5 | #N canvas 0 50 450 250 (subpatch) 0; 6 | #X array \$0view 480 float 2; 7 | #X coords 0 1.0833 480 -1.0833 240 130 1 0 0; 8 | #X restore 350 40 graph; 9 | #X floatatom 570 400 0 0 0 0 - - - 0; 10 | #X obj 490 410 tgl 15 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 11 | #fcfcfc 0 1; 12 | #X obj 390 400 *~; 13 | #X obj 220 230 tgl 17 0 empty empty start 19 7 0 12 #000000 #fcfcfc 14 | #fcfcfc 0 1; 15 | #X obj 40 70 tgl 15 0 empty empty pause 19 7 0 12 #000000 #fcfcfc #fcfcfc 16 | 0 1; 17 | #X obj 490 430 metro 20; 18 | #N canvas 930 450 400 300 pause 0; 19 | #X msg 80 160 0, f 2; 20 | #X obj 220 110 spigot; 21 | #X obj 220 150 f; 22 | #X obj 70 80 t f f; 23 | #X obj 70 50 inlet; 24 | #X obj 220 50 inlet; 25 | #X obj 150 220 outlet; 26 | #X obj 80 220 outlet; 27 | #X obj 220 220 outlet; 28 | #X obj 120 160 t; 29 | #X msg 110 190 1, f 2; 30 | #X obj 220 190 sel 0; 31 | #X obj 70 110 sel 1; 32 | #X obj 270 80 == 0; 33 | #X connect 0 0 7 0; 34 | #X connect 1 0 0 0; 35 | #X connect 1 0 2 0; 36 | #X connect 2 0 12 0; 37 | #X connect 2 0 11 0; 38 | #X connect 3 0 12 0; 39 | #X connect 3 1 13 0; 40 | #X connect 3 1 2 1; 41 | #X connect 4 0 3 0; 42 | #X connect 5 0 1 0; 43 | #X connect 9 0 10 0; 44 | #X connect 9 1 6 0; 45 | #X connect 10 0 7 0; 46 | #X connect 11 0 8 0; 47 | #X connect 12 0 9 0; 48 | #X connect 13 0 1 1; 49 | #X restore 220 250 pd pause; 50 | #X obj 90 310 -; 51 | #X obj 180 340 spigot; 52 | #X obj 160 310 t f f; 53 | #X floatatom 30 430 5 0 0 0 - - - 0; 54 | #X obj 40 130 t b b b; 55 | #X obj 240 380 f; 56 | #X obj 40 100 metro; 57 | #X obj 280 380 3000; 58 | #X floatatom 150 170 0 0 0 0 - - - 0; 59 | #X floatatom 330 350 0 0 0 0 - - - 0; 60 | #X obj 330 380 * 10; 61 | #X obj 420 370 ad 10 130; 62 | #N canvas 850 360 550 480 vals 0; 63 | #X obj 290 420 outlet~; 64 | #X obj 40 420 outlet~; 65 | #X obj 180 420 outlet~; 66 | #X obj 400 420 outlet~; 67 | #X obj 400 370 curve~ -0.5; 68 | #X obj 60 160 bng 15 250 50 0 empty empty empty 17 7 0 10 #000000 #fcfcfc 69 | #fcfcfc; 70 | #X obj 210 260 @pow 2; 71 | #X obj 190 110 t a; 72 | #X obj 190 50 r \$0bt; 73 | #X obj 320 260 @pow 2; 74 | #X floatatom 370 110 0 0 0 0 - - - 0; 75 | #X obj 400 330 pack; 76 | #X obj 440 270 r \$0met; 77 | #X floatatom 340 110 0 0 0 0 - - - 0; 78 | #X floatatom 470 110 0 0 0 0 - - - 0; 79 | #X floatatom 230 110 0 0 0 0 - - - 0; 80 | #X obj 430 230 rind 0 2; 81 | #X floatatom 260 110 0 0 0 0 - - - 0; 82 | #X obj 210 230 rand -1 2; 83 | #X obj 320 230 rand -2 2; 84 | #X obj 40 230 rand 7; 85 | #X obj 40 260 muse 50 2 4 5 7 9 10; 86 | #X connect 4 0 3 0; 87 | #X connect 5 0 20 0; 88 | #X connect 6 0 2 0; 89 | #X connect 7 0 16 0; 90 | #X connect 7 0 18 0; 91 | #X connect 7 0 19 0; 92 | #X connect 7 0 20 0; 93 | #X connect 8 0 7 0; 94 | #X connect 9 0 0 0; 95 | #X connect 10 0 19 2; 96 | #X connect 11 0 4 0; 97 | #X connect 12 0 11 1; 98 | #X connect 13 0 19 1; 99 | #X connect 14 0 16 2; 100 | #X connect 15 0 18 1; 101 | #X connect 16 0 11 0; 102 | #X connect 17 0 18 2; 103 | #X connect 18 0 6 0; 104 | #X connect 19 0 9 0; 105 | #X connect 20 0 21 0; 106 | #X connect 21 0 1 0; 107 | #X restore 380 210 pd vals; 108 | #N canvas 150 130 450 300 info 0; 109 | #X text 160 100 artist : myQwil; 110 | #X text 160 120 title : ModStep; 111 | #X text 160 140 l!ength : 179700; 112 | #X text 160 160 length : 0; 113 | #X restore 20 530 pd info; 114 | #X obj 100 430 bng 15 250 50 0 empty empty empty 17 7 0 10 #000000 115 | #fcfcfc #fcfcfc; 116 | #X obj 390 430 *~ 0.6; 117 | #N canvas 160 550 450 300 mix 0; 118 | #X obj 100 80 r pre; 119 | #X obj 160 230 s met; 120 | #X floatatom 70 40 5 0 0 0 - - - 0; 121 | #X obj 230 120 % 32; 122 | #X obj 100 120 moses 32; 123 | #X floatatom 160 200 0 0 0 0 - - - 0; 124 | #X obj 100 150 sly 50 750 32; 125 | #X obj 230 150 sly 750 6000 31; 126 | #X connect 0 0 4 0; 127 | #X connect 2 0 4 0; 128 | #X connect 3 0 7 0; 129 | #X connect 4 0 6 0; 130 | #X connect 4 1 3 0; 131 | #X connect 5 0 1 0; 132 | #X connect 6 0 5 0; 133 | #X connect 7 0 5 0; 134 | #X restore 90 530 pd mix; 135 | #X obj 60 270 rind; 136 | #X obj 150 200 t b f; 137 | #X obj 440 490 / 1000; 138 | #X floatatom 20 310 0 0 0 0 - - - 0; 139 | #X floatatom 250 140 0 0 0 0 - - - 0; 140 | #X text 20 500 myQwil; 141 | #X obj 450 520 r \$0vol; 142 | #X obj 490 460 tabwrite~ \$0view; 143 | #X obj 220 200 r \$0play; 144 | #X obj 100 460 s \$0play; 145 | #X floatatom 230 110 0 0 0 0 - - - 0; 146 | #X obj 180 110 rind; 147 | #X obj 30 400 f 1000 !; 148 | #X obj 380 270 fm2op~, f 8; 149 | #X obj 110 40 r \$0met; 150 | #X obj 40 40 r \$0tog; 151 | #X obj 210 50 r \$0met; 152 | #X obj 80 200 r \$0met; 153 | #X obj 300 200 r \$0bt; 154 | #X obj 160 380 s \$0tog; 155 | #X obj 40 160 s \$0bt; 156 | #X obj 110 280 r \$0atk; 157 | #X obj 180 140 s \$0atk; 158 | #X obj 280 420 s \$0dcy; 159 | #X obj 420 310 r \$0bt; 160 | #X obj 480 310 r \$0atk; 161 | #X obj 550 310 r \$0dcy; 162 | #X obj 30 460 s \$0met; 163 | #X msg 420 340 1; 164 | #X obj 210 80 / 32; 165 | #X obj 150 240 * 2.5; 166 | #X obj 80 240 / 1.5; 167 | #X connect 1 0 0 0; 168 | #X connect 1 0 0 1; 169 | #X connect 2 0 30 0; 170 | #X connect 4 0 9 1; 171 | #X connect 5 0 9 0; 172 | #X connect 6 0 26 0; 173 | #X connect 7 0 10 0; 174 | #X connect 8 0 17 0; 175 | #X connect 9 0 35 0; 176 | #X connect 10 0 13 0; 177 | #X connect 10 1 16 0; 178 | #X connect 10 2 18 0; 179 | #X connect 11 0 12 0; 180 | #X connect 12 0 16 0; 181 | #X connect 13 0 47 0; 182 | #X connect 13 1 12 1; 183 | #X connect 14 0 55 0; 184 | #X connect 15 0 48 0; 185 | #X connect 15 1 28 0; 186 | #X connect 15 2 39 0; 187 | #X connect 16 0 51 0; 188 | #X connect 17 0 15 0; 189 | #X connect 18 0 51 0; 190 | #X connect 19 0 29 0; 191 | #X connect 20 0 21 0; 192 | #X connect 21 0 18 1; 193 | #X connect 21 0 51 0; 194 | #X connect 22 0 6 1; 195 | #X connect 23 0 41 0; 196 | #X connect 23 1 41 1; 197 | #X connect 23 2 41 2; 198 | #X connect 23 3 41 3; 199 | #X connect 25 0 37 0; 200 | #X connect 26 0 1 0; 201 | #X connect 26 0 35 0; 202 | #X connect 28 0 11 0; 203 | #X connect 28 0 31 0; 204 | #X connect 29 0 58 0; 205 | #X connect 29 1 58 1; 206 | #X connect 30 0 1 1; 207 | #X connect 34 0 1 1; 208 | #X connect 36 0 7 0; 209 | #X connect 39 0 32 0; 210 | #X connect 39 0 50 0; 211 | #X connect 40 0 14 0; 212 | #X connect 41 0 6 0; 213 | #X connect 42 0 17 1; 214 | #X connect 43 0 8 0; 215 | #X connect 44 0 57 0; 216 | #X connect 45 0 58 0; 217 | #X connect 45 0 59 0; 218 | #X connect 46 0 10 1; 219 | #X connect 49 0 11 1; 220 | #X connect 52 0 56 0; 221 | #X connect 53 0 22 1; 222 | #X connect 54 0 22 2; 223 | #X connect 56 0 22 0; 224 | #X connect 57 0 38 0; 225 | #X connect 57 0 39 2; 226 | #X connect 58 0 28 2; 227 | #X connect 59 0 28 1; 228 | -------------------------------------------------------------------------------- /love/samplerate.lua: -------------------------------------------------------------------------------- 1 | -- get the playback device's sample rate, code courtesy of zorg 2 | local ffi = require('ffi') 3 | local openal = (ffi.os == 'Windows') and ffi.load('OpenAL32') or ffi.C 4 | ffi.cdef [[ 5 | typedef struct ALCcontext ALCcontext; 6 | typedef struct ALCdevice ALCdevice; 7 | typedef int ALCenum; 8 | typedef int ALCsizei; 9 | typedef int ALCint; 10 | typedef void ALCvoid; 11 | 12 | void alcGetIntegerv( 13 | ALCdevice *device 14 | ,ALCenum param 15 | ,ALCsizei size 16 | ,ALCint *data 17 | ); 18 | 19 | ALCcontext *alcGetCurrentContext(ALCvoid); 20 | ALCdevice *alcGetContextsDevice(ALCcontext *context); 21 | ]] 22 | 23 | local srate = ffi.new('ALCint[1]') ---@type integer[] 24 | local ALC_FREQUENCY = 0x1007 25 | local context = openal.alcGetCurrentContext() 26 | local device = openal.alcGetContextsDevice(context) 27 | openal.alcGetIntegerv(device, ALC_FREQUENCY, 1, srate) 28 | 29 | return srate[0] 30 | -------------------------------------------------------------------------------- /love/thread/main.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | function love.load() 6 | local thread = love.thread.newThread('thread.lua') 7 | thread:start() 8 | end 9 | 10 | function love.mousepressed() 11 | love.thread.getChannel('msg'):push(true) 12 | end 13 | 14 | function love.quit() 15 | love.thread.getChannel('stop'):push(true) 16 | end 17 | -------------------------------------------------------------------------------- /love/thread/thread.lua: -------------------------------------------------------------------------------- 1 | require('love.audio') 2 | require('love.event') 3 | require('love.font') 4 | require('love.graphics') 5 | require('love.sound') 6 | require('love.system') 7 | require('love.timer') 8 | 9 | package.path = '../?.lua;' .. package.path 10 | local lpd = require('pdmain') 11 | local pd = lpd.pd 12 | 13 | lpd.init { ticks = 2, bufs = 16 } 14 | lpd.print_delay() 15 | local patch = lpd.open({ patch = '../../pd/test.pd', volume = 0.2 }) 16 | 17 | while true do 18 | lpd.update() 19 | if love.thread.getChannel('stop'):pop() then 20 | pd:closePatch(patch) 21 | pd:computeAudio(false) 22 | return 23 | elseif love.thread.getChannel('msg'):pop() then 24 | pd:sendFloat('tone-pos', 0) 25 | pd:sendMessage('tone', 'pitch', { 50 }) 26 | pd:sendBang('tone') 27 | end 28 | love.timer.sleep(0.001) 29 | end 30 | -------------------------------------------------------------------------------- /pd/lib/ad.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 560 420 14; 2 | #X declare -lib cyclone; 3 | #X obj 40 40 inlet peak; 4 | #X obj 320 70 inlet attack; 5 | #X obj 430 70 inlet decay; 6 | #X obj 40 70 route float bang a d z; 7 | #X obj 230 70 unpaq f f; 8 | #X obj 260 150 t f; 9 | #X obj 360 150 t f; 10 | #X obj 50 100 1; 11 | #X obj 50 130 moses; 12 | #X obj 50 160 * -1; 13 | #X msg 50 250 0; 14 | #X obj 110 160 sel; 15 | #X msg 170 190 \$1; 16 | #X obj 50 280 pack 0 10; 17 | #X obj 60 190 pipe 10; 18 | #X obj 90 220 t b f; 19 | #X obj 240 250 del \$1; 20 | #X obj 240 280 pack 0 \$2; 21 | #X obj 150 380 outlet~; 22 | #X text 380 280 attack-decay; 23 | #X obj 410 250 cnv 15 36 24 empty empty ad 4 12 0 18 #000000 #fcfcfc 24 | 0; 25 | #X obj 110 130 f \$3; 26 | #X obj 110 100 loadbang; 27 | #X obj 150 350 curve~; 28 | #N canvas 400 200 450 300 lib 0; 29 | #X obj 140 120 declare -lib cyclone; 30 | #X restore 470 40 pd lib; 31 | #X msg 160 320 \$1 \$2 -0.5; 32 | #N canvas 848 305 450 300 attack 0; 33 | #X obj 210 50 inlet delay; 34 | #X obj 210 110 * 0.5; 35 | #X obj 110 230 outlet; 36 | #X msg 110 200 \$2 \$3 0.5 \$1 \$3 -0.5; 37 | #X obj 110 50 inlet peak; 38 | #X obj 110 80 t f f; 39 | #X obj 150 110 * 0.5; 40 | #X obj 110 170 pack 0 0 0; 41 | #X obj 310 80 \$1; 42 | #X obj 310 50 loadbang; 43 | #X connect 0 0 1 0; 44 | #X connect 1 0 7 2; 45 | #X connect 3 0 2 0; 46 | #X connect 4 0 5 0; 47 | #X connect 5 0 7 0; 48 | #X connect 5 1 6 0; 49 | #X connect 6 0 7 1; 50 | #X connect 7 0 3 0; 51 | #X connect 8 0 1 0; 52 | #X connect 9 0 8 0; 53 | #X restore 150 280 pd attack; 54 | #X connect 0 0 3 0; 55 | #X connect 1 0 5 0; 56 | #X connect 2 0 6 0; 57 | #X connect 3 0 7 0; 58 | #X connect 3 1 7 0; 59 | #X connect 3 2 5 0; 60 | #X connect 3 3 6 0; 61 | #X connect 3 4 12 0; 62 | #X connect 3 5 4 0; 63 | #X connect 4 0 5 0; 64 | #X connect 4 1 6 0; 65 | #X connect 5 0 16 1; 66 | #X connect 5 0 26 1; 67 | #X connect 6 0 17 1; 68 | #X connect 7 0 8 0; 69 | #X connect 8 0 9 0; 70 | #X connect 8 1 15 0; 71 | #X connect 9 0 14 0; 72 | #X connect 9 0 10 0; 73 | #X connect 10 0 13 0; 74 | #X connect 11 1 12 0; 75 | #X connect 12 0 13 1; 76 | #X connect 12 0 14 1; 77 | #X connect 13 0 25 0; 78 | #X connect 14 0 15 0; 79 | #X connect 15 0 16 0; 80 | #X connect 15 1 26 0; 81 | #X connect 16 0 17 0; 82 | #X connect 17 0 25 0; 83 | #X connect 21 0 11 0; 84 | #X connect 22 0 21 0; 85 | #X connect 23 0 18 0; 86 | #X connect 25 0 23 0; 87 | #X connect 26 0 23 0; 88 | -------------------------------------------------------------------------------- /pd/lib/ct.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 380 350 14; 2 | #X obj 140 90 inlet; 3 | #X obj 210 190 inlet; 4 | #X obj 160 190 % \$1; 5 | #X obj 200 160 + 1; 6 | #X obj 100 260 sel \$2; 7 | #X obj 100 290 outlet; 8 | #X obj 200 290 outlet; 9 | #X text 50 50 send a bang on each zero remainder; 10 | #X obj 100 20 cnv 15 178 24 empty empty count-trigger 4 12 0 18 #000000 11 | #fcfcfc 0; 12 | #X obj 160 160 f; 13 | #X obj 160 220 t f f; 14 | #X obj 140 120 route on set; 15 | #X connect 0 0 11 0; 16 | #X connect 1 0 2 1; 17 | #X connect 2 0 3 0; 18 | #X connect 2 0 10 0; 19 | #X connect 3 0 9 1; 20 | #X connect 4 0 5 0; 21 | #X connect 9 0 2 0; 22 | #X connect 10 0 4 0; 23 | #X connect 10 1 6 0; 24 | #X connect 11 0 4 1; 25 | #X connect 11 1 9 1; 26 | #X connect 11 2 9 0; 27 | -------------------------------------------------------------------------------- /pd/lib/cupqb-help.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 570 340 14; 2 | #X floatatom 250 280 0 0 0 0 - - - 0; 3 | #X msg 360 190 25; 4 | #X msg 260 150 123; 5 | #X msg 210 190 50; 6 | #X msg 230 150 1, f 2; 7 | #X msg 300 150 1, f 2; 8 | #X obj 330 150 bng 15 250 50 0 empty empty empty 17 7 0 10 #ff8300 9 | #ff0400 #fcfcfc; 10 | #X obj 340 190 bng 15 250 50 0 empty empty empty 17 7 0 10 #ff8300 11 | #ff0400 #fcfcfc; 12 | #X obj 190 190 bng 15 250 50 0 empty empty empty 17 7 0 10 #ff8300 13 | #ff0400 #fcfcfc; 14 | #X obj 260 30 cnv 15 72 24 empty empty cupqb 4 12 0 18 #000000 #fcfcfc 15 | 0; 16 | #X text 40 60 the left-most and right-most inlets will subtract and 17 | add to the current value respectively \, while the middle inlet will 18 | output the current value and allow for it to be set to a different 19 | value.; 20 | #X msg 350 150 set 24; 21 | #X obj 250 230 cupqb 42 2; 22 | #X msg 360 220 set 10; 23 | #X connect 1 0 12 2; 24 | #X connect 2 0 12 1; 25 | #X connect 3 0 12 0; 26 | #X connect 4 0 12 0; 27 | #X connect 5 0 12 2; 28 | #X connect 6 0 12 1; 29 | #X connect 7 0 12 2; 30 | #X connect 8 0 12 0; 31 | #X connect 11 0 12 1; 32 | #X connect 12 0 0 0; 33 | #X connect 13 0 12 2; 34 | -------------------------------------------------------------------------------- /pd/lib/cupqb.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 530 440 14; 2 | #X obj 270 40 inlet; 3 | #X obj 220 210 inlet; 4 | #X obj 360 210 inlet; 5 | #X obj 140 80 route set step - +; 6 | #X obj 140 110 s \$0set; 7 | #X obj 140 140 s \$0itr; 8 | #X obj 300 80 s \$0tonbx; 9 | #X obj 50 50 loadbang; 10 | #X obj 70 120 \$1; 11 | #X obj 70 150 sel; 12 | #X msg 100 180 set \$1; 13 | #X obj 100 210 s \$0tonbx; 14 | #X obj 50 200 \$2; 15 | #X obj 50 230 sel; 16 | #X obj 80 260 s \$0itr; 17 | #X floatatom 220 140 5 0 0 0 - \$0tonbx \$0frnbx 0; 18 | #X obj 220 160 bng 20 250 50 0 \$0-sdummy \$0-rdummy empty 17 7 0 10 19 | #ff8300 #ff0400 #000000; 20 | #X obj 240 160 bng 20 250 50 0 \$0-sdummy \$0-rdummy empty 17 7 0 10 21 | #ff8300 #ff0400 #000000; 22 | #X obj 220 240 route set; 23 | #X obj 190 280 1; 24 | #X obj 190 310 * -1; 25 | #X obj 360 240 route set; 26 | #X obj 330 280 1; 27 | #X obj 230 280 r \$0itr; 28 | #X obj 370 280 r \$0itr; 29 | #X obj 290 340 + \$1; 30 | #X obj 290 370 s \$0tonbx; 31 | #X obj 330 310 r \$0set; 32 | #X obj 400 310 r \$0frnbx; 33 | #X obj 400 370 outlet; 34 | #X text 300 150 number box with cup; 35 | #X obj 340 120 cnv 15 74 24 empty empty cupqb 4 12 0 18 #000000 #fcfcfc 36 | 0; 37 | #X connect 0 0 3 0; 38 | #X connect 1 0 18 0; 39 | #X connect 2 0 21 0; 40 | #X connect 3 0 10 0; 41 | #X connect 3 0 4 0; 42 | #X connect 3 1 5 0; 43 | #X connect 3 2 19 0; 44 | #X connect 3 3 22 0; 45 | #X connect 3 4 6 0; 46 | #X connect 7 0 8 0; 47 | #X connect 7 0 12 0; 48 | #X connect 8 0 9 0; 49 | #X connect 9 1 10 0; 50 | #X connect 10 0 11 0; 51 | #X connect 12 0 13 0; 52 | #X connect 13 1 14 0; 53 | #X connect 16 0 19 0; 54 | #X connect 17 0 22 0; 55 | #X connect 18 0 19 1; 56 | #X connect 18 1 19 0; 57 | #X connect 19 0 20 0; 58 | #X connect 20 0 25 0; 59 | #X connect 21 0 22 1; 60 | #X connect 21 1 22 0; 61 | #X connect 22 0 25 0; 62 | #X connect 23 0 19 1; 63 | #X connect 24 0 22 1; 64 | #X connect 25 0 26 0; 65 | #X connect 27 0 25 1; 66 | #X connect 28 0 25 1; 67 | #X connect 28 0 29 0; 68 | #X coords 0 -1 1 1 42 46 2 220 137; 69 | -------------------------------------------------------------------------------- /pd/lib/fm2op~.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 380 430 14; 2 | #X obj 60 240 phasor~; 3 | #X obj 140 240 phasor~; 4 | #X obj 40 160 inlet~ carrier; 5 | #X obj 60 200 *~; 6 | #X obj 140 200 *~; 7 | #X obj 140 270 cos~; 8 | #X obj 190 300 *~; 9 | #X obj 60 340 cos~; 10 | #X obj 60 370 outlet~; 11 | #X obj 230 170 inlet phase; 12 | #X obj 130 20 loadbang; 13 | #X obj 40 70 \$1; 14 | #X obj 80 70 \$2; 15 | #X obj 200 340 cnv 15 86 24 empty empty fm2op~ 4 12 0 18 #000000 #fcfcfc 16 | 0; 17 | #X obj 230 200 unpack f f; 18 | #X obj 80 130 inlet~ c-ratio; 19 | #X obj 160 100 inlet~ m-ratio; 20 | #X obj 210 130 inlet~ m-index; 21 | #X obj 160 70 \$3; 22 | #X obj 210 70 \$4; 23 | #X text 190 370 2 Operator FM; 24 | #X connect 0 0 7 0; 25 | #X connect 1 0 5 0; 26 | #X connect 2 0 3 0; 27 | #X connect 2 0 4 0; 28 | #X connect 3 0 0 0; 29 | #X connect 4 0 1 0; 30 | #X connect 5 0 6 0; 31 | #X connect 6 0 7 0; 32 | #X connect 7 0 8 0; 33 | #X connect 9 0 14 0; 34 | #X connect 10 0 11 0; 35 | #X connect 10 0 12 0; 36 | #X connect 10 0 18 0; 37 | #X connect 10 0 19 0; 38 | #X connect 11 0 2 0; 39 | #X connect 12 0 15 0; 40 | #X connect 14 0 0 1; 41 | #X connect 14 1 1 1; 42 | #X connect 15 0 3 1; 43 | #X connect 16 0 4 1; 44 | #X connect 17 0 6 1; 45 | #X connect 18 0 16 0; 46 | #X connect 19 0 17 0; 47 | -------------------------------------------------------------------------------- /pd/lib/pan~.pd: -------------------------------------------------------------------------------- 1 | #N canvas 580 390 440 300 12; 2 | #X obj 70 60 inlet~; 3 | #X obj 210 60 clip -45 45; 4 | #X obj 210 90 * 0.0174533; 5 | #X obj 210 40 inlet angle; 6 | #X obj 140 160 expr 0.707107 * (cos($f1) + sin($f1)); 7 | #X obj 140 130 expr 0.707107 * (cos($f1) - sin($f1)); 8 | #X obj 70 210 *~; 9 | #X obj 100 210 *~; 10 | #X obj 210 240 outlet~ right; 11 | #X obj 70 240 outlet~ left; 12 | #X obj 140 40 loadbang; 13 | #X msg 140 60 0; 14 | #X connect 0 0 6 0; 15 | #X connect 0 0 7 0; 16 | #X connect 1 0 2 0; 17 | #X connect 2 0 4 0; 18 | #X connect 2 0 5 0; 19 | #X connect 3 0 1 0; 20 | #X connect 4 0 7 1; 21 | #X connect 5 0 6 1; 22 | #X connect 6 0 9 0; 23 | #X connect 7 0 8 0; 24 | #X connect 10 0 11 0; 25 | #X connect 11 0 1 0; 26 | -------------------------------------------------------------------------------- /pd/lib/test_abs.pd: -------------------------------------------------------------------------------- 1 | #N canvas 80 290 330 200 10; 2 | #X text 10 30 this is a test abstraction ... does it load?; 3 | #X obj 30 80 inlet; 4 | #X obj 30 150 print PD; 5 | #X msg 30 110 test_abs: Hello World!; 6 | #X connect 1 0 3 0; 7 | #X connect 3 0 2 0; 8 | -------------------------------------------------------------------------------- /pd/lib/zp.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 350 250 14; 2 | #X obj 90 90 inlet; 3 | #X obj 160 90 inlet; 4 | #X obj 220 90 inlet; 5 | #X obj 90 120 pack 0 \$1; 6 | #X obj 90 150 line \$2 \$3; 7 | #X obj 90 180 outlet; 8 | #X text 70 60 ramp between value changes; 9 | #X obj 160 30 cnv 15 36 24 empty empty zp 4 12 0 18 #000000 #fcfcfc 10 | 0; 11 | #X connect 0 0 3 0; 12 | #X connect 1 0 3 1; 13 | #X connect 2 0 4 2; 14 | #X connect 3 0 4 0; 15 | #X connect 4 0 5 0; 16 | -------------------------------------------------------------------------------- /pd/lib/zp~.pd: -------------------------------------------------------------------------------- 1 | #N canvas 400 200 350 270 14; 2 | #X obj 140 90 inlet; 3 | #X obj 220 90 inlet; 4 | #X obj 140 120 route now; 5 | #X obj 160 150 pack 0 \$1; 6 | #X obj 70 210 sel; 7 | #X obj 160 180 line~; 8 | #X obj 160 210 outlet~; 9 | #X text 30 60 ramp between value changes (signal); 10 | #X obj 150 30 cnv 15 48 24 empty empty zp~ 4 12 0 18 #000000 #fcfcfc 11 | 0; 12 | #X obj 70 180 \$2; 13 | #X obj 70 150 loadbang; 14 | #X connect 0 0 2 0; 15 | #X connect 1 0 3 1; 16 | #X connect 2 0 5 0; 17 | #X connect 2 1 3 0; 18 | #X connect 3 0 5 0; 19 | #X connect 4 1 5 0; 20 | #X connect 5 0 6 0; 21 | #X connect 9 0 4 0; 22 | #X connect 10 0 9 0; 23 | -------------------------------------------------------------------------------- /pd/test.pd: -------------------------------------------------------------------------------- 1 | #N canvas 610 270 500 430 12; 2 | #X obj 250 340 dac~; 3 | #N canvas 360 100 670 280 test 0; 4 | #N canvas 0 20 450 300 (subpatch) 0; 5 | #X array array1 10 float 3; 6 | #A 0 0.0857145 0.328572 0.500001 0.57143 0.514287 0.47143 0.357144 7 | 0.285715 0.057143 0; 8 | #X coords 0 1 10 -1 200 140 1; 9 | #X restore 490 60 graph; 10 | #X obj 150 190 tabread array1; 11 | #X obj 150 100 until; 12 | #X obj 150 120 f; 13 | #X obj 180 120 + 1; 14 | #X obj 220 120 sel 0; 15 | #X obj 180 150 mod 10; 16 | #X obj 130 20 inlet; 17 | #X msg 10 190 FINISH ARRAY TEST; 18 | #X msg 280 200 START ARRAY TEST; 19 | #X obj 130 60 t b b b; 20 | #X obj 10 230 print PD; 21 | #X obj 280 240 print PD; 22 | #X obj 150 230 print PD array1; 23 | #X connect 1 0 13 0; 24 | #X connect 2 0 3 0; 25 | #X connect 3 0 4 0; 26 | #X connect 3 0 1 0; 27 | #X connect 4 0 6 0; 28 | #X connect 5 0 2 1; 29 | #X connect 6 0 3 1; 30 | #X connect 6 0 5 0; 31 | #X connect 7 0 10 0; 32 | #X connect 8 0 11 0; 33 | #X connect 9 0 12 0; 34 | #X connect 10 0 8 0; 35 | #X connect 10 1 2 0; 36 | #X connect 10 2 9 0; 37 | #X restore 50 250 pd test array; 38 | #N canvas 0 20 940 260 test 0; 39 | #X obj 170 230 noteout 1; 40 | #X obj 290 20 inlet; 41 | #X obj 290 50 t b b b b b b b b; 42 | #X obj 300 230 ctlout 1; 43 | #X obj 410 230 pgmout 1; 44 | #X msg 410 200 100; 45 | #X obj 490 230 bendout 1; 46 | #X obj 580 230 touchout 1; 47 | #X obj 690 230 polytouchout 1; 48 | #X msg 490 200 2000; 49 | #X msg 30 200 START MIDI TEST; 50 | #X msg 930 210 MIDI TEST FINISHED; 51 | #X obj 30 230 print PD; 52 | #X obj 930 240 print PD; 53 | #X obj 820 240 midiout; 54 | #X obj 820 210 unpack f f; 55 | #X msg 820 180 239 0; 56 | #X text 460 270 note: bendout values are -8192 - 8192; 57 | #X obj 820 140 t b b; 58 | #X text 290 260 note: val ctl; 59 | #X text 680 170 note: val note; 60 | #X text 800 260 note: byte port; 61 | #X msg 690 200 100 64; 62 | #X msg 170 200 60 64; 63 | #X msg 300 200 100 64; 64 | #X msg 580 200 100; 65 | #X connect 1 0 2 0; 66 | #X connect 2 0 18 0; 67 | #X connect 2 1 22 0; 68 | #X connect 2 2 25 0; 69 | #X connect 2 3 9 0; 70 | #X connect 2 4 5 0; 71 | #X connect 2 5 24 0; 72 | #X connect 2 6 23 0; 73 | #X connect 2 7 10 0; 74 | #X connect 5 0 4 0; 75 | #X connect 9 0 6 0; 76 | #X connect 10 0 12 0; 77 | #X connect 11 0 13 0; 78 | #X connect 15 0 14 0; 79 | #X connect 15 1 14 1; 80 | #X connect 16 0 15 0; 81 | #X connect 18 0 11 0; 82 | #X connect 18 1 16 0; 83 | #X connect 22 0 8 0; 84 | #X connect 23 0 0 0; 85 | #X connect 24 0 3 0; 86 | #X connect 25 0 7 0; 87 | #X restore 70 220 pd test midi; 88 | #N canvas 510 330 420 420 sines 0; 89 | #X obj 20 310 *~ 0.2; 90 | #X obj 20 380 outlet~; 91 | #X obj 20 20 r mod-freq; 92 | #X obj 150 30 r mod-index; 93 | #X obj 100 180 r carrier-freq; 94 | #X obj 90 210 + 400; 95 | #X obj 140 210 f; 96 | #X obj 70 280 print carrier-freq; 97 | #X obj 20 340 pan~; 98 | #X obj 90 380 outlet~; 99 | #X obj 60 340 r pan; 100 | #X obj 20 250 +~; 101 | #X obj 20 220 *~; 102 | #X obj 250 60 print portamento; 103 | #X obj 20 280 osc~; 104 | #X obj 20 180 osc~; 105 | #X obj 200 150 s modidx; 106 | #X obj 40 100 s modfrq; 107 | #X obj 230 280 s carfrq; 108 | #X obj 170 320 r portamento; 109 | #X obj 170 380 s _portamento; 110 | #X obj 240 30 r _portamento; 111 | #X obj 210 180 r _portamento; 112 | #X obj 40 70 zp 5 1 10; 113 | #X obj 200 120 zp 5 150 10; 114 | #X obj 230 250 zp 5 400 10; 115 | #X obj 20 150 zp~ 5 1; 116 | #X obj 100 150 zp~ 5 150; 117 | #X obj 90 250 zp~ 5 400; 118 | #X obj 170 350 max 5; 119 | #X connect 0 0 8 0; 120 | #X connect 2 0 23 0; 121 | #X connect 2 0 26 0; 122 | #X connect 3 0 24 0; 123 | #X connect 3 0 27 0; 124 | #X connect 4 0 5 0; 125 | #X connect 5 0 6 0; 126 | #X connect 5 0 7 0; 127 | #X connect 5 0 25 0; 128 | #X connect 5 0 28 0; 129 | #X connect 6 0 5 1; 130 | #X connect 8 0 1 0; 131 | #X connect 8 1 9 0; 132 | #X connect 10 0 8 1; 133 | #X connect 11 0 14 0; 134 | #X connect 12 0 11 0; 135 | #X connect 14 0 0 0; 136 | #X connect 15 0 12 0; 137 | #X connect 19 0 29 0; 138 | #X connect 21 0 13 0; 139 | #X connect 21 0 23 1; 140 | #X connect 21 0 24 1; 141 | #X connect 21 0 26 1; 142 | #X connect 21 0 27 1; 143 | #X connect 22 0 25 1; 144 | #X connect 22 0 28 1; 145 | #X connect 23 0 17 0; 146 | #X connect 24 0 16 0; 147 | #X connect 25 0 18 0; 148 | #X connect 26 0 15 0; 149 | #X connect 27 0 12 1; 150 | #X connect 28 0 11 1; 151 | #X connect 29 0 20 0; 152 | #X restore 190 240 pd sines; 153 | #N canvas 690 250 300 490 tone 0; 154 | #X obj 140 290 line~; 155 | #X obj 30 320 *~; 156 | #X obj 30 20 r tone; 157 | #X obj 30 140 mtof; 158 | #X obj 160 170 t b b; 159 | #X obj 160 140 bng 15 250 50 0 empty empty empty 17 7 0 10 #fcfcfc 160 | #000000 #000000; 161 | #X msg 140 240 1 2; 162 | #X obj 200 210 delay 100; 163 | #X msg 110 60 pitch 60; 164 | #X obj 170 330 env~; 165 | #X obj 170 360 change; 166 | #X obj 170 390 s env; 167 | #X obj 30 60 list trim; 168 | #X obj 30 100 route pitch bang; 169 | #X obj 30 350 *~ 0.4; 170 | #X obj 30 170 phasor~ 200; 171 | #X obj 30 410 outlet~; 172 | #X msg 200 240 0 500; 173 | #X obj 30 280 clip~ -1 1; 174 | #X obj 30 220 -~ 0.5; 175 | #X obj 30 250 *~ 2; 176 | #X text 40 200 make saw; 177 | #X obj 30 380 pan~; 178 | #X obj 80 370 r tone-pos; 179 | #X obj 90 410 outlet~; 180 | #X connect 0 0 1 1; 181 | #X connect 0 0 9 0; 182 | #X connect 1 0 14 0; 183 | #X connect 2 0 12 0; 184 | #X connect 3 0 15 0; 185 | #X connect 4 0 7 0; 186 | #X connect 4 1 6 0; 187 | #X connect 5 0 4 0; 188 | #X connect 6 0 0 0; 189 | #X connect 7 0 17 0; 190 | #X connect 8 0 13 0; 191 | #X connect 9 0 10 0; 192 | #X connect 10 0 11 0; 193 | #X connect 12 0 13 0; 194 | #X connect 13 0 3 0; 195 | #X connect 13 1 4 0; 196 | #X connect 14 0 22 0; 197 | #X connect 15 0 19 0; 198 | #X connect 17 0 0 0; 199 | #X connect 18 0 1 0; 200 | #X connect 19 0 20 0; 201 | #X connect 20 0 18 0; 202 | #X connect 22 0 16 0; 203 | #X connect 22 1 24 0; 204 | #X connect 23 0 22 1; 205 | #X restore 340 240 pd tone; 206 | #X obj 230 60 print PD dollar zero; 207 | #X obj 110 60 print PD; 208 | #X msg 30 120 bang; 209 | #N canvas 630 560 330 180 patch 0; 210 | #X obj 40 80 \$0; 211 | #X msg 40 110 PATCH OPENED: \$1; 212 | #X obj 40 50 loadbang; 213 | #X obj 200 80 \$0; 214 | #X msg 200 110 PATCH CLOSED: \$1; 215 | #X text 200 50 [closebang]; 216 | #X obj 40 140 print PD; 217 | #X obj 200 140 print PD; 218 | #X connect 0 0 1 0; 219 | #X connect 1 0 6 0; 220 | #X connect 2 0 0 0; 221 | #X connect 3 0 4 0; 222 | #X connect 4 0 7 0; 223 | #X restore 310 120 pd patch open close; 224 | #X obj 30 60 list trim; 225 | #X obj 30 90 route test; 226 | #N canvas 0 20 900 190 midi 0; 227 | #X obj 20 20 notein; 228 | #X obj 20 60 pack f f f; 229 | #X obj 150 60 pack f f f; 230 | #X obj 150 20 ctlin; 231 | #X obj 280 20 pgmin; 232 | #X obj 280 60 pack f f; 233 | #X obj 390 20 bendin; 234 | #X obj 390 60 pack f f; 235 | #X obj 500 20 touchin; 236 | #X obj 500 60 pack f f; 237 | #X obj 650 20 polytouchin; 238 | #X obj 810 60 pack f f; 239 | #X obj 810 20 midiin; 240 | #X obj 390 190 print PD MIDI; 241 | #X obj 650 60 pack f f f; 242 | #X text 330 1 note: bendin values are 0 - 16383; 243 | #X obj 930 60 pack f f; 244 | #X obj 930 20 sysexin; 245 | #X msg 20 90 notein \$3 \$1 \$2; 246 | #X msg 150 90 ctlin \$3 \$2 \$1; 247 | #X msg 280 90 pgm \$2 \$1; 248 | #X msg 390 90 bendin \$2 \$1; 249 | #X msg 500 90 touchin \$2 \$1; 250 | #X msg 650 90 polytouchin \$3 \$2 \$1; 251 | #X msg 810 100 midiin \$2 \$1; 252 | #X msg 930 90 sysexin \$2 \$1; 253 | #X connect 0 0 1 0; 254 | #X connect 0 1 1 1; 255 | #X connect 0 2 1 2; 256 | #X connect 1 0 18 0; 257 | #X connect 2 0 19 0; 258 | #X connect 3 0 2 0; 259 | #X connect 3 1 2 1; 260 | #X connect 3 2 2 2; 261 | #X connect 4 0 5 0; 262 | #X connect 4 1 5 1; 263 | #X connect 5 0 20 0; 264 | #X connect 6 0 7 0; 265 | #X connect 6 1 7 1; 266 | #X connect 7 0 21 0; 267 | #X connect 8 0 9 0; 268 | #X connect 8 1 9 1; 269 | #X connect 9 0 22 0; 270 | #X connect 10 0 14 0; 271 | #X connect 10 1 14 1; 272 | #X connect 10 2 14 2; 273 | #X connect 11 0 24 0; 274 | #X connect 12 0 11 0; 275 | #X connect 12 1 11 1; 276 | #X connect 14 0 23 0; 277 | #X connect 16 0 25 0; 278 | #X connect 17 0 16 0; 279 | #X connect 17 1 16 1; 280 | #X connect 18 0 13 0; 281 | #X connect 19 0 13 0; 282 | #X connect 20 0 13 0; 283 | #X connect 21 0 13 0; 284 | #X connect 22 0 13 0; 285 | #X connect 23 0 13 0; 286 | #X connect 24 0 13 0; 287 | #X connect 25 0 13 0; 288 | #X restore 310 150 pd midi in; 289 | #N canvas 0 20 780 190 test 0; 290 | #X obj 210 140 f 100; 291 | #X obj 270 140 symbol kaaa; 292 | #X obj 160 140 bang; 293 | #X obj 370 140 list 100 2.3 test 1 2 3; 294 | #X obj 310 20 inlet; 295 | #X obj 310 60 t b b b b b b b; 296 | #X msg 30 140 START MSG TEST; 297 | #X msg 750 130 MSG TEST FINISH; 298 | #X obj 30 170 print PD; 299 | #X obj 750 170 print PD; 300 | #X obj 210 180 s toLua; 301 | #X msg 560 140 \; toLua kaa 1 2.3 test; 302 | #X connect 0 0 10 0; 303 | #X connect 1 0 10 0; 304 | #X connect 2 0 10 0; 305 | #X connect 3 0 10 0; 306 | #X connect 4 0 5 0; 307 | #X connect 5 0 7 0; 308 | #X connect 5 1 11 0; 309 | #X connect 5 2 3 0; 310 | #X connect 5 3 1 0; 311 | #X connect 5 4 0 0; 312 | #X connect 5 5 2 0; 313 | #X connect 5 6 6 0; 314 | #X connect 6 0 8 0; 315 | #X connect 7 0 9 0; 316 | #X restore 90 190 pd test message; 317 | #N canvas 550 260 260 280 delay 0; 318 | #X obj 40 20 inlet~; 319 | #X obj 40 230 outlet~; 320 | #X obj 40 190 delread~ \$0-delay1; 321 | #X obj 40 60 delwrite~ \$0-delay1 5000; 322 | #X msg 40 160 1000; 323 | #X obj 40 120 loadbang; 324 | #X connect 0 0 3 0; 325 | #X connect 2 0 1 0; 326 | #X connect 4 0 2 0; 327 | #X connect 5 0 4 0; 328 | #X restore 260 200 pd delay; 329 | #X obj 260 170 adc~; 330 | #N canvas 740 360 370 180 license 0; 331 | #X text 10 40 BSD Simplified License; 332 | #X text 9 10 Copyright (c) 2012 Dan Wilcox ; 333 | #X text 10 130 See https://github.com/libpd/libpd for documentation 334 | ; 335 | #X text 10 160 Adapted from the ofxPd openFrameworks addon example: 336 | https://github.com/danomatika/ofxPd; 337 | #X text 10 70 For information on usage and redistribution \, and for 338 | a DISCLAIMER OF ALL WARRANTIES \, see the file \, "LICENSE.txt" \, 339 | in this distribution.; 340 | #X restore 30 370 pd license; 341 | #X obj 30 150 t b b b b; 342 | #X obj 30 280 test_abs; 343 | #N canvas 660 60 460 300 scope~ 0; 344 | #X obj 30 30 inlet~ audio; 345 | #X obj 80 180 metro 100; 346 | #X msg 80 140 1; 347 | #X obj 60 60 clip~ -1 1; 348 | #X obj 80 110 loadbang; 349 | #N canvas 0 20 450 300 (subpatch) 0; 350 | #X array scope 512 float 2; 351 | #X coords 0 1 512 -1 200 140 1; 352 | #X restore 190 30 graph; 353 | #X obj 60 220 tabwrite~ scope; 354 | #X obj 20 250 outlet~; 355 | #X connect 0 0 3 0; 356 | #X connect 0 0 7 0; 357 | #X connect 1 0 6 0; 358 | #X connect 2 0 1 0; 359 | #X connect 3 0 6 0; 360 | #X connect 4 0 2 0; 361 | #X restore 260 240 pd scope~; 362 | #X obj 230 30 r \$0-fromLua; 363 | #X text 300 370 Dan Wilcox 2012 BSD; 364 | #X obj 30 30 r fromLua; 365 | #N canvas 0 20 230 200 runtime 0; 366 | #X obj 40 40 loadbang; 367 | #X obj 40 60 metro 500; 368 | #X obj 40 170 print; 369 | #X obj 40 90 f 0; 370 | #X obj 80 90 + 1; 371 | #X obj 90 170 noteout; 372 | #X obj 90 140 makenote 1000 500; 373 | #X connect 0 0 1 0; 374 | #X connect 1 0 3 0; 375 | #X connect 3 0 2 0; 376 | #X connect 3 0 4 0; 377 | #X connect 3 0 6 0; 378 | #X connect 4 0 3 1; 379 | #X connect 6 0 5 0; 380 | #X connect 6 1 5 1; 381 | #X restore 150 370 pd runtime test; 382 | #X obj 250 310 *~; 383 | #X obj 280 310 *~; 384 | #X obj 320 310 r \$0vol; 385 | #X floatatom 380 280 0 0 0 0 - - - 0; 386 | #X connect 3 0 23 0; 387 | #X connect 3 1 24 0; 388 | #X connect 4 0 23 0; 389 | #X connect 4 1 24 0; 390 | #X connect 7 0 16 0; 391 | #X connect 9 0 10 0; 392 | #X connect 10 0 7 0; 393 | #X connect 13 0 18 0; 394 | #X connect 14 0 13 0; 395 | #X connect 16 0 17 0; 396 | #X connect 16 1 1 0; 397 | #X connect 16 2 2 0; 398 | #X connect 16 3 12 0; 399 | #X connect 18 0 23 0; 400 | #X connect 18 0 24 0; 401 | #X connect 19 0 5 0; 402 | #X connect 21 0 6 0; 403 | #X connect 21 0 9 0; 404 | #X connect 23 0 0 0; 405 | #X connect 24 0 0 1; 406 | #X connect 25 0 24 1; 407 | #X connect 25 0 23 1; 408 | #X connect 26 0 24 1; 409 | #X connect 26 0 23 1; 410 | -------------------------------------------------------------------------------- /pdmeta.lua: -------------------------------------------------------------------------------- 1 | ---@meta 2 | 3 | ---The LuaPd library 4 | ---@class Pd 5 | local Pd = {} 6 | 7 | --------------------------------------------------------- 8 | ------------------------- Array ------------------------- 9 | 10 | ---A wrapper for a std::vector\ 11 | ---@class PdArray 12 | Pd._Array = {} 13 | 14 | ---@param size? integer # size of the array (default 0) 15 | ---@return PdArray 16 | function Pd.Array(size) end 17 | 18 | --------------------------------------------------------- 19 | ------------------------- Helper ------------------------ 20 | 21 | ---get a raw pointer with an offset in bytes 22 | ---@param data lightuserdata 23 | ---@param bytes? integer 24 | ---@return lightuserdata 25 | function Pd.offset(data, bytes) end 26 | 27 | --------------------------------------------------------- 28 | ------------------------- Patch ------------------------- 29 | 30 | ---A pd patch 31 | --- 32 | ---If you use the copy constructor/operator, keep in mind the libpd void* 33 | ---pointer patch handle is copied and problems can arise if one object is used 34 | ---to close a patch that other copies may be referring to 35 | ---@class PdPatch 36 | Pd._Patch = {} 37 | 38 | ---@overload fun(filename:string, path?:string): PdPatch 39 | ---@overload fun(patch?:PdPatch): PdPatch # copy constructor 40 | ---@param handle lightuserdata # the raw pointer to the patch instance 41 | ---@param dollarZero integer # the unqiue instance $0 ID 42 | ---@param filename string # the patch filename 43 | ---@param path? string # the parent dir path for the file (default ".") 44 | ---@return PdPatch 45 | function Pd.Patch(handle, dollarZero, filename, path) end 46 | 47 | ---get the raw pointer to the patch instance 48 | ---@return lightuserdata 49 | function Pd._Patch:handle() end 50 | 51 | ---get the unqiue instance $0 ID 52 | ---@return integer 53 | function Pd._Patch:dollarZero() end 54 | 55 | ---get the patch filename 56 | ---@return string 57 | function Pd._Patch:filename() end 58 | 59 | ---get the parent dir path for the file 60 | ---@return string 61 | function Pd._Patch:path() end 62 | 63 | ---get the unique instance $0 ID as a string 64 | ---@return string 65 | function Pd._Patch:dollarZeroStr() end 66 | 67 | ---is the patch pointer valid? 68 | ---@return boolean 69 | function Pd._Patch:isValid() end 70 | 71 | ---clear patch pointer and dollar zero (does not close patch!) 72 | --- 73 | ---note: does not clear filename and path so the object can be reused 74 | ---for opening multiple instances 75 | function Pd._Patch:clear() end 76 | 77 | 78 | ------------------------------------------------------------ 79 | ------------------------- PdObject ------------------------- 80 | 81 | ---A pd message receiver 82 | ---@class PdObject 83 | ---@field print fun(message:string) 84 | ---@field bang fun(dest:string) 85 | ---@field float fun(dest:string, num:number) 86 | ---@field symbol fun(dest:string, symbol:string) 87 | ---@field list fun(dest:string, list:table) 88 | ---@field message fun(dest:string, msg:string, list:table) 89 | ---@field noteOn fun(channel:integer, pitch:integer, velocity:integer) 90 | ---@field controlChange fun(channel:integer, controller:integer, value:integer) 91 | ---@field programChange fun(channel:integer, value:integer) 92 | ---@field pitchBend fun(channel:integer, value:integer) 93 | ---@field afterTouch fun(channel:integer, value:integer) 94 | ---@field polyAfterTouch fun(channel:integer, pitch:integer, value:integer) 95 | ---@field midiByte fun(port:integer, byte:integer) 96 | Pd._Object = {} 97 | 98 | ---@param callbacks? function[] # a list of callback functions 99 | ---@return PdObject 100 | function Pd.Object(callbacks) end 101 | 102 | ---set multiple callback functions 103 | ---@param funcs table 104 | function Pd._Object:setFuncs(funcs) end 105 | 106 | 107 | ---------------------------------------------------------- 108 | ------------------------- PdBase ------------------------- 109 | 110 | ---A Pure Data instance 111 | --- 112 | ---note: libpd currently does not support multiple states and it is 113 | ---suggested that you use only one PdBase-derived object at a time 114 | --- 115 | ---calls from multiple PdBase instances currently use a global context 116 | ---kept in a singleton object, thus only one Receiver & one MidiReceiver 117 | ---can be used within a single program 118 | --- 119 | ---multiple context support will be added if/when it is included within libpd 120 | ---@class PdBase 121 | Pd._Base = {} 122 | 123 | ---@return PdBase 124 | function Pd.Base() end 125 | 126 | 127 | ----- Initializing Pd ----- 128 | 129 | ---initialize resources and set up the audio processing 130 | --- 131 | ---set the audio latency by setting the libpd ticks per buffer: 132 | ---ticks per buffer * lib pd block size (always 64) 133 | --- 134 | ---ie 4 ticks per buffer * 64 = buffer len of 512 135 | --- 136 | ---you can call this again after loading patches & setting receivers 137 | ---in order to update the audio settings 138 | --- 139 | ---the lower the number of ticks, the faster the audio processing 140 | ---if you experience audio dropouts (audible clicks), increase the 141 | ---ticks per buffer 142 | --- 143 | ---set queued = true to use the built in ringbuffers for message and 144 | ---midi event passing, you will then need to call receiveMessages() and 145 | ---receiveMidi() in order to pass messages from the ringbuffers to your 146 | ---PdReceiver and PdMidiReceiver implementations 147 | --- 148 | ---the queued ringbuffers are useful when you need to receive events 149 | ---on a gui thread and don't want to use locking 150 | --- 151 | ---note: must be called before processing 152 | ---@param numInChannels integer # the number of audio-in channels 153 | ---@param numOutChannels integer # the number of audio-out channels 154 | ---@param sampleRate integer # the audio sample rate 155 | ---@param queued? boolean # whether to use ringbuffers for message and midi event passing 156 | ---@return boolean # true if setup successfully 157 | function Pd._Base:init(numInChannels, numOutChannels, sampleRate, queued) end 158 | 159 | ---clear resources 160 | function Pd._Base:clear() end 161 | 162 | 163 | ----- Adding Search Paths ----- 164 | 165 | ---add to the pd search path 166 | ---takes an absolute or relative path (in data folder) 167 | --- 168 | ---note: fails silently if path not found 169 | ---@param path string # search path 170 | function Pd._Base:addToSearchPath(path) end 171 | 172 | ---clear the current pd search path 173 | function Pd._Base:clearSearchPath() end 174 | 175 | 176 | ----- Opening Patches ----- 177 | 178 | ---open a patch file (aka somefile.pd) at a specified parent dir path 179 | --- 180 | ---if no path is specified, the parent dir will be local 181 | --- 182 | ---or open a patch file using the filename and path of an existing patch 183 | ---@overload fun(_, patch:PdPatch): PdPatch # use filename and path of an existing patch 184 | ---@param name string # the name of the patch 185 | ---@param path? string # the parent directory (default ".") 186 | ---@return PdPatch 187 | function Pd._Base:openPatch(name, path) end 188 | 189 | ---close a patch file 190 | ---@overload fun(_, patch:PdPatch) 191 | ---@param name string # the patch's basename (filename without extension) 192 | function Pd._Base:closePatch(name) end 193 | 194 | 195 | ----- Audio Processing ----- 196 | 197 | --- one of these must be called for audio dsp and message io to occur 198 | --- 199 | --- inBuffer must be an array of the right size and never null 200 | --- use inBuffer = new type[0] if no input is desired 201 | --- 202 | --- outBuffer must be an array of size outBufferSize from openAudio call 203 | --- 204 | --- note: raw does not interlace the buffers 205 | 206 | ---process float buffers for a given number of ticks 207 | ---@param ticks integer # the number of ticks to process 208 | ---@param inBuffer lightuserdata|nil # audio-in buffer 209 | ---@param outBuffer lightuserdata|nil # audio-out buffer 210 | ---@return boolean # false on error 211 | function Pd._Base:processFloat(ticks, inBuffer, outBuffer) end 212 | 213 | ---process short buffers for a given number of ticks 214 | ---@param ticks integer # the number of ticks to process 215 | ---@param inBuffer lightuserdata|nil # audio-in buffer 216 | ---@param outBuffer lightuserdata|nil # audio-out buffer 217 | ---@return boolean # false on error 218 | function Pd._Base:processShort(ticks, inBuffer, outBuffer) end 219 | 220 | ---process double buffers for a given number of ticks 221 | ---@param ticks integer # the number of ticks to process 222 | ---@param inBuffer lightuserdata|nil # audio-in buffer 223 | ---@param outBuffer lightuserdata|nil # audio-out buffer 224 | ---@return boolean # false on error 225 | function Pd._Base:processDouble(ticks, inBuffer, outBuffer) end 226 | 227 | ---process one pd tick, writes raw float data to/from buffers 228 | ---@param inBuffer lightuserdata|nil # audio-in buffer 229 | ---@param outBuffer lightuserdata|nil # audio-out buffer 230 | ---@return boolean # false on error 231 | function Pd._Base:processRaw(inBuffer, outBuffer) end 232 | 233 | ---process one pd tick, writes raw short data to/from buffers 234 | ---@param inBuffer lightuserdata|nil # audio-in buffer 235 | ---@param outBuffer lightuserdata|nil # audio-out buffer 236 | ---@return boolean # false on error 237 | function Pd._Base:processRawShort(inBuffer, outBuffer) end 238 | 239 | ---process one pd tick, writes raw double data to/from buffers 240 | ---@param inBuffer lightuserdata|nil # audio-in buffer 241 | ---@param outBuffer lightuserdata|nil # audio-out buffer 242 | ---@return boolean # false on error 243 | function Pd._Base:processRawDouble(inBuffer, outBuffer) end 244 | 245 | 246 | ----- Audio Processing Control ----- 247 | 248 | ---start/stop audio processing 249 | --- 250 | ---in general, once started, you won't need to turn off audio 251 | ---@param state boolean 252 | function Pd._Base:computeAudio(state) end 253 | 254 | 255 | ----- Message Receiving ----- 256 | 257 | ---subscribe to messages sent by a pd send source 258 | --- 259 | ---it acts like a virtual pd receive object 260 | ---@param source string 261 | function Pd._Base:subscribe(source) end 262 | 263 | ---unsubscribe from messages sent by a pd send source 264 | ---@param source string 265 | function Pd._Base:unsubscribe(source) end 266 | 267 | ---is a pd send source subscribed? 268 | ---@param source string 269 | ---@return boolean 270 | function Pd._Base:exists(source) end 271 | 272 | ---receivers will be unsubscribed from *all* pd send sources 273 | function Pd._Base:unsubscribeAll() end 274 | 275 | 276 | ----- Receiving from the Message Queues ----- 277 | 278 | ---process waiting messages 279 | function Pd._Base:receiveMessages() end 280 | 281 | ---process waiting midi messages 282 | function Pd._Base:receiveMidi() end 283 | 284 | 285 | ----- Event Receiving via Callbacks ----- 286 | 287 | ---set the incoming event receiver, disables the event queue 288 | --- 289 | ---automatically receives from all currently subscribed sources 290 | --- 291 | ---set this to NULL to disable callback receiving and re-enable the 292 | ---event queue 293 | ---@param receiver PdObject 294 | function Pd._Base:setReceiver(receiver) end 295 | 296 | ---set the incoming midi event receiver, disables the midi queue 297 | --- 298 | ---automatically receives from all midi channels 299 | --- 300 | ---set this to NULL to disable callback receiving and re-enable the 301 | ---event queue 302 | ---@param receiver PdObject 303 | function Pd._Base:setMidiReceiver(receiver) end 304 | 305 | 306 | ----- Send Functions ----- 307 | 308 | ---send a bang message 309 | ---@param dest string # the destination 310 | function Pd._Base:sendBang(dest) end 311 | 312 | ---send a float 313 | ---@param dest string # the destination 314 | ---@param value number # a float 315 | function Pd._Base:sendFloat(dest, value) end 316 | 317 | ---send a symbol 318 | ---@param dest string # the destination 319 | ---@param symbol string # a string 320 | function Pd._Base:sendSymbol(dest, symbol) end 321 | 322 | 323 | ----- Sending Compound Messages ----- 324 | 325 | ---start a compound list or message 326 | function Pd._Base:startMessage() end 327 | 328 | ---add a float to the current compound list or message 329 | ---@param num number 330 | function Pd._Base:addFloat(num) end 331 | 332 | ---add a symbol to the current compound list or message 333 | ---@param symbol string 334 | function Pd._Base:addSymbol(symbol) end 335 | 336 | ---add a float or symbol to the current compound list or message 337 | ---@param atom string|number 338 | function Pd._Base:addAtom(atom) end 339 | 340 | ---finish and send as a list 341 | ---@param dest string # the destination 342 | function Pd._Base:finishList(dest) end 343 | 344 | ---finish and send as a list with a specific message name 345 | ---@param dest string # the destination 346 | ---@param msg string # the message 347 | function Pd._Base:finishMessage(dest, msg) end 348 | 349 | ---send a list using a table 350 | ---@param dest string # the destination 351 | ---@param list table # a table 352 | function Pd._Base:sendList(dest, list) end 353 | 354 | ---send a message and accompanying args 355 | ---@param dest string # the destination 356 | ---@param msg string # the message 357 | ---@param list table|nil # accompanying args 358 | function Pd._Base:sendMessage(dest, msg, list) end 359 | 360 | 361 | ----- Sending MIDI ----- 362 | 363 | ---send a MIDI note on 364 | --- 365 | ---pd does not use note off MIDI messages, so send a note on with vel = 0 366 | ---@param channel integer 367 | ---@param pitch integer 368 | ---@param velocity? integer # default 64 369 | function Pd._Base:sendNoteOn(channel, pitch, velocity) end 370 | 371 | ---send a MIDI control change 372 | ---@param channel integer 373 | ---@param controller integer 374 | ---@param value integer 375 | function Pd._Base:sendControlChange(channel, controller, value) end 376 | 377 | ---send a MIDI program change 378 | ---@param channel integer 379 | ---@param value integer 380 | function Pd._Base:sendProgramChange(channel, value) end 381 | 382 | ---send a MIDI pitch bend 383 | --- 384 | ---in pd: [bendin] takes 0 - 16383 while [bendout] returns -8192 - 8191 385 | ---@param channel integer 386 | ---@param value integer 387 | function Pd._Base:sendPitchBend(channel, value) end 388 | 389 | ---send a MIDI aftertouch 390 | ---@param channel integer 391 | ---@param value integer 392 | function Pd._Base:sendAftertouch(channel, value) end 393 | 394 | ---send a MIDI poly aftertouch 395 | ---@param channel integer 396 | ---@param pitch integer 397 | ---@param value integer 398 | function Pd._Base:sendPolyAftertouch(channel, pitch, value) end 399 | 400 | ---send a raw MIDI byte 401 | --- 402 | ---value is a raw midi byte value 0 - 255 403 | ---port is the raw portmidi port #, similar to a channel 404 | --- 405 | ---for some reason, [midiin], [sysexin] & [realtimein] add 2 to the 406 | ---port num, so sending to port 1 in PdBase returns port 3 in pd 407 | --- 408 | ---however, [midiout], [sysexout], & [realtimeout] do not add to the 409 | ---port num, so sending port 1 to [midiout] returns port 1 in PdBase 410 | ---@param port integer 411 | ---@param value integer 412 | function Pd._Base:sendMidiByte(port, value) end 413 | 414 | ---send a raw MIDI sysex byte 415 | ---@param port integer 416 | ---@param value integer 417 | function Pd._Base:sendSysex(port, value) end 418 | 419 | ---send a raw MIDI realtime byte 420 | ---@param port integer 421 | ---@param value integer 422 | function Pd._Base:sendSysRealTime(port, value) end 423 | 424 | ---is a message or byte stream currently in progress? 425 | ---@return boolean 426 | function Pd._Base:isMessageInProgress() end 427 | 428 | 429 | ----- Array Access ----- 430 | 431 | ---get the size of a pd array 432 | ---@param name string 433 | ---@return integer # 0 if array not found 434 | function Pd._Base:arraySize(name) end 435 | 436 | ---(re)size a pd array 437 | --- 438 | ---sizes <= 0 are clipped to 1 439 | ---@param name string 440 | ---@param size integer 441 | ---@return boolean # true on success, false on failure 442 | function Pd._Base:resizeArray(name, size) end 443 | 444 | ---read from a pd array 445 | --- 446 | ---resizes given vector to readLen, checks readLen and offset 447 | --- 448 | ---calling without setting readLen and offset reads the whole array: 449 | ---@param name string # the name of the pd array 450 | ---@param dest number[] # the vector that the array will be written to 451 | ---@param readLen? integer # the amount to read (default -1) 452 | ---@param offset? integer # the offset (default 0) 453 | ---@return boolean # true on success, false on failure 454 | function Pd._Base:readArray(name, dest, readLen, offset) end 455 | 456 | ---write to a pd array 457 | --- 458 | ---calling without setting writeLen and offset writes the whole array: 459 | ---@param name string # the name of the pd array 460 | ---@param source number[] # the vector that the array will read from 461 | ---@param writeLen? integer # the amount to write (default -1) 462 | ---@param offset? integer # the offset (default 0) 463 | ---@return boolean # true on success, false on failure 464 | function Pd._Base:writeArray(name, source, writeLen, offset) end 465 | 466 | ---clear array and set to a specific value 467 | ---@param name string 468 | ---@param value? integer # default 0 469 | function Pd._Base:clearArray(name, value) end 470 | 471 | 472 | ----- Utils ----- 473 | 474 | ---has the global pd instance been initialized? 475 | ---@return boolean 476 | function Pd._Base:isInited() end 477 | 478 | ---is the global pd instance using the ringerbuffer queue 479 | ---for message padding? 480 | ---@return boolean 481 | function Pd._Base:isQueued() end 482 | 483 | ---get the blocksize of pd (sample length per channel) 484 | ---@return integer 485 | function Pd._Base.blockSize() end 486 | 487 | ---set the max length of messages and lists, default: 32 488 | ---@param len integer 489 | function Pd._Base:setMaxMessageLen(len) end 490 | 491 | ---get the max length of messages and lists 492 | ---@return integer 493 | function Pd._Base:maxMessageLen() end 494 | -------------------------------------------------------------------------------- /src/PdObject.cpp: -------------------------------------------------------------------------------- 1 | #include "PdObject.hpp" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | using namespace pd; 7 | 8 | #define NMSG 13 9 | 10 | static const char *const msgname[] = { 11 | "print", "bang", "float", "symbol", "list", "message" 12 | , "noteOn", "controlChange", "programChange", "pitchBend" 13 | , "aftertouch", "polyAftertouch", "midiByte" 14 | }; 15 | 16 | PdObject::PdObject(lua_State *l) : L(l) { 17 | for (int i = 0; i < NMSG; i++) { 18 | *msgs[i] = LUA_REFNIL; 19 | } 20 | if (lua_istable(L, 1)) { 21 | setFuncs(); 22 | } 23 | } 24 | 25 | void PdObject::setFuncs() { 26 | int ref; 27 | for (int i = 0; i < NMSG; i++) { 28 | lua_getfield(L, -1, msgname[i]); 29 | if ((ref = luaL_ref(L, LUA_REGISTRYINDEX)) != LUA_REFNIL) { 30 | *msgs[i] = ref; 31 | } 32 | } 33 | } 34 | 35 | void PdObject::setFunc(const char *name) { 36 | for (int i = 0; i < NMSG; i++) { 37 | if (!strcmp(msgname[i], name)) { 38 | *msgs[i] = luaL_ref(L, LUA_REGISTRYINDEX); 39 | return; 40 | } 41 | } 42 | } 43 | 44 | //-------------------------------------------------------------- 45 | void PdObject::print(const string &message) { 46 | if (fnprint == LUA_REFNIL) { 47 | return; 48 | } 49 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnprint); 50 | lua_pushstring(L, message.c_str()); 51 | lua_call(L, 1, 0); 52 | } 53 | 54 | //-------------------------------------------------------------- 55 | void PdObject::receiveBang(const string &dest) { 56 | if (fnbang == LUA_REFNIL) { 57 | return; 58 | } 59 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnbang); 60 | lua_pushstring(L, dest.c_str()); 61 | lua_call(L, 1, 0); 62 | } 63 | 64 | void PdObject::receiveFloat(const string &dest, float num) { 65 | if (fnfloat == LUA_REFNIL) { 66 | return; 67 | } 68 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnfloat); 69 | lua_pushstring(L, dest.c_str()); 70 | lua_pushnumber(L, num); 71 | lua_call(L, 2, 0); 72 | } 73 | 74 | void PdObject::receiveSymbol(const string &dest, const string &symbol) { 75 | if (fnsymbol == LUA_REFNIL) { 76 | return; 77 | } 78 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnsymbol); 79 | lua_pushstring(L, dest.c_str()); 80 | lua_pushstring(L, symbol.c_str()); 81 | lua_call(L, 2, 0); 82 | } 83 | 84 | static void listToTable(lua_State *L, const List &list) { 85 | unsigned int i = 0, len = list.len(); 86 | lua_createtable(L, len, 0); 87 | for (; i < len; i++) { 88 | if (list.isFloat(i)) { 89 | lua_pushnumber(L, list.getFloat(i)); 90 | } else if (list.isSymbol(i)) { 91 | lua_pushstring(L, list.getSymbol(i).c_str()); 92 | } 93 | lua_rawseti(L, -2, i + 1); 94 | } 95 | } 96 | 97 | void PdObject::receiveList(const string &dest, const List &list) { 98 | if (fnlist == LUA_REFNIL) { 99 | return; 100 | } 101 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnlist); 102 | lua_pushstring(L, dest.c_str()); 103 | listToTable(L, list); 104 | lua_call(L, 2, 0); 105 | } 106 | 107 | void PdObject::receiveMessage(const string &dest, const string &msg, const List &list) { 108 | if (fnmessage == LUA_REFNIL) { 109 | return; 110 | } 111 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnmessage); 112 | lua_pushstring(L, dest.c_str()); 113 | lua_pushstring(L, msg.c_str()); 114 | listToTable(L, list); 115 | lua_call(L, 3, 0); 116 | } 117 | 118 | //-------------------------------------------------------------- 119 | void PdObject::receiveNoteOn(const int channel, const int pitch, const int velocity) { 120 | if (fnnote == LUA_REFNIL) { 121 | return; 122 | } 123 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnnote); 124 | lua_pushinteger(L, channel); 125 | lua_pushinteger(L, pitch); 126 | lua_pushinteger(L, velocity); 127 | lua_call(L, 3, 0); 128 | } 129 | 130 | void PdObject::receiveControlChange 131 | (const int channel, const int controller, const int value) { 132 | if (fnctrl == LUA_REFNIL) { 133 | return; 134 | } 135 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnctrl); 136 | lua_pushinteger(L, channel); 137 | lua_pushinteger(L, controller); 138 | lua_pushinteger(L, value); 139 | lua_call(L, 3, 0); 140 | } 141 | 142 | void PdObject::receiveProgramChange(const int channel, const int value) { 143 | if (fnprog == LUA_REFNIL) { 144 | return; 145 | } 146 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnprog); 147 | lua_pushinteger(L, channel); 148 | lua_pushinteger(L, value); 149 | lua_call(L, 2, 0); 150 | } 151 | 152 | void PdObject::receivePitchBend(const int channel, const int value) { 153 | if (fnpitch == LUA_REFNIL) { 154 | return; 155 | } 156 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnpitch); 157 | lua_pushinteger(L, channel); 158 | lua_pushinteger(L, value); 159 | lua_call(L, 2, 0); 160 | } 161 | 162 | void PdObject::receiveAftertouch(const int channel, const int value) { 163 | if (fnafter == LUA_REFNIL) { 164 | return; 165 | } 166 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnafter); 167 | lua_pushinteger(L, channel); 168 | lua_pushinteger(L, value); 169 | lua_call(L, 2, 0); 170 | } 171 | 172 | void PdObject::receivePolyAftertouch 173 | (const int channel, const int pitch, const int value) { 174 | if (fnpoly == LUA_REFNIL) { 175 | return; 176 | } 177 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnpoly); 178 | lua_pushinteger(L, channel); 179 | lua_pushinteger(L, pitch); 180 | lua_pushinteger(L, value); 181 | lua_call(L, 3, 0); 182 | } 183 | 184 | void PdObject::receiveMidiByte(const int port, const int byte) { 185 | if (fnbyte == LUA_REFNIL) { 186 | return; 187 | } 188 | lua_rawgeti(L, LUA_REGISTRYINDEX, fnbyte); 189 | lua_pushinteger(L, port); 190 | lua_pushinteger(L, byte); 191 | lua_call(L, 2, 0); 192 | } 193 | -------------------------------------------------------------------------------- /src/PdObject.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Dan Wilcox 3 | * 4 | * BSD Simplified License. 5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. 7 | * 8 | * See https://github.com/libpd/libpd for documentation 9 | * 10 | * This file was adapted from the ofxPd openFrameworks addon example: 11 | * https://github.com/danomatika/ofxPd 12 | * 13 | */ 14 | #pragma once 15 | 16 | #include "PdBase.hpp" 17 | #include "luacompat.hpp" 18 | 19 | // custom receiver class 20 | class PdObject : public pd::PdReceiver, public pd::PdMidiReceiver { 21 | public: 22 | PdObject(lua_State *); 23 | void setFuncs(); 24 | void setFunc(const char *name); 25 | 26 | private: 27 | lua_State *L; 28 | 29 | // callback refs 30 | int fnprint, fnbang, fnfloat, fnsymbol, fnlist, fnmessage 31 | , fnnote, fnctrl, fnprog, fnpitch, fnafter, fnpoly, fnbyte; 32 | 33 | int *msgs[13] = { 34 | &fnprint, &fnbang, &fnfloat, &fnsymbol, &fnlist , &fnmessage 35 | , &fnnote , &fnctrl, &fnprog , &fnpitch , &fnafter, &fnpoly, &fnbyte 36 | }; 37 | 38 | // pd message receiver callbacks 39 | void print(const std::string &message); 40 | void receiveBang(const std::string &dest); 41 | void receiveFloat(const std::string &dest, float num); 42 | void receiveSymbol(const std::string &dest, const std::string &symbol); 43 | void receiveList(const std::string &dest, const pd::List &list); 44 | void receiveMessage(const std::string &dest, const std::string &msg 45 | , const pd::List &list); 46 | 47 | // pd midi receiver callbacks 48 | void receiveNoteOn(const int channel, const int pitch, const int velocity); 49 | void receiveControlChange(const int channel, const int controller, const int value); 50 | void receiveProgramChange(const int channel, const int value); 51 | void receivePitchBend(const int channel, const int value); 52 | void receiveAftertouch(const int channel, const int value); 53 | void receivePolyAftertouch(const int channel, const int pitch, const int value); 54 | void receiveMidiByte(const int port, const int byte); 55 | }; 56 | -------------------------------------------------------------------------------- /src/luacompat.hpp: -------------------------------------------------------------------------------- 1 | #include "lua.hpp" 2 | 3 | // -------------- Compatibility for different lua versions --------------------- 4 | 5 | #if LUA_VERSION_NUM == 501 6 | 7 | #define lua_rawlen(L,i) lua_objlen(L,(i)) 8 | 9 | #if LUAJIT_VERSION_NUM < 20100 10 | 11 | #define luaL_setmetatable(L,n) (luaL_getmetatable(L,(n)) ,lua_setmetatable(L,-2)) 12 | LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { 13 | luaL_checkstack(L, nup, "too many upvalues"); 14 | for (; l->name != NULL; l++) { /* fill the table with given functions */ 15 | if (l->func == NULL) /* place holder? */ 16 | lua_pushboolean(L, 0); 17 | else { 18 | int i; 19 | for (i = 0; i < nup; i++) /* copy upvalues to the top */ 20 | lua_pushvalue(L, -nup); 21 | lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ 22 | } 23 | lua_setfield(L, -(nup + 2), l->name); 24 | } 25 | lua_pop(L, nup); /* remove upvalues */ 26 | } 27 | 28 | #define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) 29 | #define UNUSED(x) ((void)(x)) 30 | LUA_API lua_Number lua_version (lua_State *L) { 31 | UNUSED(L); 32 | return LUA_VERSION_NUM; 33 | } 34 | 35 | LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { 36 | lua_Number v = lua_version(L); 37 | if (sz != LUAL_NUMSIZES) /* check numeric types */ 38 | luaL_error(L, "core and library have incompatible numeric types"); 39 | else if (v != ver) 40 | luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", 41 | (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v); 42 | } 43 | 44 | #define luaL_checkversion(L) \ 45 | luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) 46 | 47 | #define luaL_newlibtable(L,l) \ 48 | lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) 49 | 50 | #define luaL_newlib(L,l) \ 51 | (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) 52 | 53 | #endif // LUAJIT_VERSION_NUM 54 | #endif // LUA_VERSION_NUM 55 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "PdObject.hpp" 2 | #include 3 | #include 4 | 5 | using std::vector; 6 | using namespace pd; 7 | 8 | extern "C" { int luaopen_luapd(lua_State *L); } 9 | 10 | #define LUA_PDARRAY "PdArray" 11 | #define LUA_PDPATCH "PdPatch" 12 | #define LUA_PDOBJECT "PdObject" 13 | #define LUA_PDBASE "PdBase" 14 | 15 | // ----------------------------------------------------------------------------- 16 | // ------------------------ Array ---------------------------------------------- 17 | // ----------------------------------------------------------------------------- 18 | 19 | static int pdarray_new(lua_State *L) { 20 | int n = lua_isnoneornil(L, 1) ? 0 : luaL_checkinteger(L, 1); 21 | *(vector**)lua_newuserdata(L, sizeof(vector*)) 22 | = new vector(n); 23 | luaL_setmetatable(L, LUA_PDARRAY); 24 | return 1; 25 | } 26 | 27 | static int pdarray_gc(lua_State *L) { 28 | vector *a = *(vector**)luaL_checkudata(L, 1, LUA_PDARRAY); 29 | delete a; 30 | return 0; 31 | } 32 | 33 | static int pdarray_len(lua_State *L) { 34 | vector *a = *(vector**)luaL_checkudata(L, 1, LUA_PDARRAY); 35 | lua_pushinteger(L, a->size()); 36 | return 1; 37 | } 38 | 39 | static int pdarray_index(lua_State *L) { 40 | vector *a = *(vector**)luaL_checkudata(L, 1, LUA_PDARRAY); 41 | std::size_t i = luaL_checkinteger(L, 2); 42 | if (i <= 0 || a->size() < i) { 43 | return luaL_error(L, "PdArray: index out of bounds"); 44 | } 45 | lua_pushnumber(L, (*a)[i - 1]); 46 | return 1; 47 | } 48 | 49 | static int pdarray_newindex(lua_State *L) { 50 | vector *a = *(vector**)luaL_checkudata(L, 1, LUA_PDARRAY); 51 | std::size_t i = luaL_checkinteger(L, 2); 52 | float f = luaL_checknumber(L, 3); 53 | if (i <= 0) { 54 | return luaL_error(L, "PdArray: index cannot be less than 1"); 55 | } 56 | if (i > a->size()) { 57 | a->resize(i, 0); 58 | } 59 | (*a)[i - 1] = f; 60 | return 0; 61 | } 62 | 63 | static int pdarray_call(lua_State *L) { 64 | vector *a = *(vector**)luaL_checkudata(L, 1, LUA_PDARRAY); 65 | std::size_t i = lua_isnoneornil(L, 2) ? 0 : lua_tointeger(L, 2); 66 | if (i >= a->size()) { 67 | return luaL_error(L, "PdArray: offset out of bounds"); 68 | } 69 | lua_pushlightuserdata(L, &(*a)[i]); 70 | return 1; 71 | } 72 | 73 | static int pdhelper_offset(lua_State *L) { 74 | char *ptr = (char *)lua_touserdata(L, 1); 75 | int i = lua_isnoneornil(L, 2) ? 0 : lua_tointeger(L, 2); 76 | lua_pushlightuserdata(L, ptr + i); 77 | return 1; 78 | } 79 | 80 | // ----------------------------------------------------------------------------- 81 | // ------------------------ PdPatch -------------------------------------------- 82 | // ----------------------------------------------------------------------------- 83 | 84 | static int pdpatch_new(lua_State *L) { 85 | Patch p; 86 | if (lua_isnoneornil(L, 1)) { 87 | p = Patch(); 88 | } else if (lua_isuserdata(L, 1)) { 89 | p = Patch(**(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH)); 90 | } else if (lua_islightuserdata(L, 1)) { 91 | void *handle = lua_touserdata(L, 1); 92 | int dollarZero = luaL_checkinteger(L, 2); 93 | const char *name = luaL_checkstring(L, 3); 94 | const char *path = lua_isnoneornil(L, 4) ? "." : luaL_checkstring(L, 4); 95 | p = Patch(handle, dollarZero, name, path); 96 | } else { 97 | const char *name = luaL_checkstring(L, 1); 98 | const char *path = lua_isnoneornil(L, 2) ? "." : luaL_checkstring(L, 2); 99 | p = Patch(name, path); 100 | } 101 | *(Patch **)lua_newuserdata(L, sizeof(Patch *)) = new Patch(p); 102 | luaL_setmetatable(L, LUA_PDPATCH); 103 | return 1; 104 | } 105 | 106 | static int pdpatch_gc(lua_State *L) { 107 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 108 | delete p; 109 | return 0; 110 | } 111 | 112 | static int pdpatch_handle(lua_State *L) { 113 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 114 | lua_pushlightuserdata(L, p->handle()); 115 | return 1; 116 | } 117 | 118 | static int pdpatch_dollarZero(lua_State *L) { 119 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 120 | lua_pushinteger(L, p->dollarZero()); 121 | return 1; 122 | } 123 | 124 | static int pdpatch_filename(lua_State *L) { 125 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 126 | lua_pushstring(L, p->filename().c_str()); 127 | return 1; 128 | } 129 | 130 | static int pdpatch_path(lua_State *L) { 131 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 132 | lua_pushstring(L, p->path().c_str()); 133 | return 1; 134 | } 135 | 136 | static int pdpatch_dollarZeroStr(lua_State *L) { 137 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 138 | lua_pushstring(L, p->dollarZeroStr().c_str()); 139 | return 1; 140 | } 141 | 142 | static int pdpatch_isValid(lua_State *L) { 143 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 144 | lua_pushboolean(L, p->isValid()); 145 | return 1; 146 | } 147 | 148 | static int pdpatch_clear(lua_State *L) { 149 | Patch *p = *(Patch **)luaL_checkudata(L, 1, LUA_PDPATCH); 150 | p->clear(); 151 | return 0; 152 | } 153 | 154 | // ----------------------------------------------------------------------------- 155 | // ------------------------ PdObject ------------------------------------------- 156 | // ----------------------------------------------------------------------------- 157 | 158 | static int pdobject_new(lua_State *L) { 159 | lua_settop(L, 1); 160 | *(PdObject **)lua_newuserdata(L, sizeof(PdObject *)) = new PdObject(L); 161 | luaL_setmetatable(L, LUA_PDOBJECT); 162 | return 1; 163 | } 164 | 165 | static int pdobject_gc(lua_State *L) { 166 | PdObject *o = *(PdObject **)luaL_checkudata(L, 1, LUA_PDOBJECT); 167 | delete o; 168 | return 0; 169 | } 170 | 171 | static int pdobject_newindex(lua_State *L) { 172 | PdObject *o = *(PdObject **)luaL_checkudata(L, 1, LUA_PDOBJECT); 173 | const char *name = luaL_checkstring(L, 2); 174 | luaL_checktype(L, 3, LUA_TFUNCTION); 175 | lua_settop(L, 3); 176 | o->setFunc(name); 177 | return 0; 178 | } 179 | 180 | static int pdobject_setFuncs(lua_State *L) { 181 | PdObject *o = *(PdObject **)luaL_checkudata(L, 1, LUA_PDOBJECT); 182 | luaL_checktype(L, 2, LUA_TTABLE); 183 | lua_settop(L, 2); 184 | o->setFuncs(); 185 | return 0; 186 | } 187 | // ----------------------------------------------------------------------------- 188 | // ------------------------ PdBase --------------------------------------------- 189 | // ----------------------------------------------------------------------------- 190 | 191 | static int pdbase_new(lua_State *L) { 192 | *(PdBase **)lua_newuserdata(L, sizeof(PdBase *)) = new PdBase(); 193 | luaL_setmetatable(L, LUA_PDBASE); 194 | return 1; 195 | } 196 | 197 | static int pdbase_gc(lua_State *L) { 198 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 199 | delete b; 200 | return 0; 201 | } 202 | 203 | static int pdbase_init(lua_State *L) { 204 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 205 | int chIn = luaL_checkinteger(L, 2); 206 | int chOut = luaL_checkinteger(L, 3); 207 | int srate = luaL_checkinteger(L, 4); 208 | bool queued = lua_isnoneornil(L, 5) ? false : lua_toboolean(L, 5); 209 | lua_pushboolean(L, b->init(chIn, chOut, srate, queued)); 210 | return 1; 211 | } 212 | 213 | static int pdbase_clear(lua_State *L) { 214 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 215 | b->clear(); 216 | return 0; 217 | } 218 | 219 | static int pdbase_addToSearchPath(lua_State *L) { 220 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 221 | const char *path = luaL_checkstring(L, 2); 222 | b->addToSearchPath(path); 223 | return 0; 224 | } 225 | 226 | static int pdbase_clearSearchPath(lua_State *L) { 227 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 228 | b->clearSearchPath(); 229 | return 0; 230 | } 231 | 232 | static int pdbase_openPatch(lua_State *L) { 233 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 234 | Patch *p; 235 | if (lua_type(L, 2) == LUA_TSTRING) { 236 | const char *patch = lua_tostring(L, 2); 237 | const char *path = lua_isnoneornil(L, 3) ? "." : luaL_checkstring(L, 3); 238 | p = new Patch(b->openPatch(patch, path)); 239 | } else { 240 | p = new Patch(b->openPatch(**(Patch **)luaL_checkudata(L, 2, LUA_PDPATCH))); 241 | } 242 | *(Patch **)lua_newuserdata(L, sizeof(Patch *)) = p; 243 | luaL_setmetatable(L, LUA_PDPATCH); 244 | return 1; 245 | } 246 | 247 | static int pdbase_closePatch(lua_State *L) { 248 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 249 | if (lua_type(L, 2) == LUA_TSTRING) { 250 | const char *patch = luaL_checkstring(L, 2); 251 | b->closePatch(patch); 252 | } else { 253 | Patch *p = *(Patch **)luaL_checkudata(L, 2, LUA_PDPATCH); 254 | b->closePatch(*p); 255 | } 256 | return 0; 257 | } 258 | 259 | static int pdbase_processFloat(lua_State *L) { 260 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 261 | int ticks = luaL_checkinteger(L, 2); 262 | float *in = lua_isnoneornil(L, 3) ? 0 : (float *)lua_touserdata(L, 3); 263 | float *out = lua_isnoneornil(L, 4) ? 0 : (float *)lua_touserdata(L, 4); 264 | lua_pushboolean(L, b->processFloat(ticks, in, out)); 265 | return 1; 266 | } 267 | 268 | static int pdbase_processShort(lua_State *L) { 269 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 270 | int ticks = luaL_checkinteger(L, 2); 271 | short *in = lua_isnoneornil(L, 3) ? 0 : (short *)lua_touserdata(L, 3); 272 | short *out = lua_isnoneornil(L, 4) ? 0 : (short *)lua_touserdata(L, 4); 273 | lua_pushboolean(L, b->processShort(ticks, in, out)); 274 | return 1; 275 | } 276 | 277 | static int pdbase_processDouble(lua_State *L) { 278 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 279 | int ticks = luaL_checkinteger(L, 2); 280 | double *in = lua_isnoneornil(L, 3) ? 0 : (double *)lua_touserdata(L, 3); 281 | double *out = lua_isnoneornil(L, 4) ? 0 : (double *)lua_touserdata(L, 4); 282 | lua_pushboolean(L, b->processDouble(ticks, in, out)); 283 | return 1; 284 | } 285 | 286 | static int pdbase_processRaw(lua_State *L) { 287 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 288 | float *in = lua_isnoneornil(L, 2) ? 0 : (float *)lua_touserdata(L, 2); 289 | float *out = lua_isnoneornil(L, 3) ? 0 : (float *)lua_touserdata(L, 3); 290 | lua_pushboolean(L, b->processRaw(in, out)); 291 | return 1; 292 | } 293 | 294 | static int pdbase_processRawShort(lua_State *L) { 295 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 296 | short *in = lua_isnoneornil(L, 2) ? 0 : (short *)lua_touserdata(L, 2); 297 | short *out = lua_isnoneornil(L, 3) ? 0 : (short *)lua_touserdata(L, 3); 298 | lua_pushboolean(L, b->processRawShort(in, out)); 299 | return 1; 300 | } 301 | 302 | static int pdbase_processRawDouble(lua_State *L) { 303 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 304 | double *in = lua_isnoneornil(L, 2) ? 0 : (double *)lua_touserdata(L, 2); 305 | double *out = lua_isnoneornil(L, 3) ? 0 : (double *)lua_touserdata(L, 3); 306 | lua_pushboolean(L, b->processRawDouble(in, out)); 307 | return 1; 308 | } 309 | 310 | static int pdbase_computeAudio(lua_State *L) { 311 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 312 | bool state = lua_toboolean(L, 2); 313 | b->computeAudio(state); 314 | return 0; 315 | } 316 | 317 | static int pdbase_subscribe(lua_State *L) { 318 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 319 | const char *source = luaL_checkstring(L, 2); 320 | b->subscribe(source); 321 | return 0; 322 | } 323 | 324 | static int pdbase_unsubscribe(lua_State *L) { 325 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 326 | const char *source = luaL_checkstring(L, 2); 327 | b->unsubscribe(source); 328 | return 0; 329 | } 330 | 331 | static int pdbase_exists(lua_State *L) { 332 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 333 | const char *source = luaL_checkstring(L, 2); 334 | lua_pushboolean(L, b->exists(source)); 335 | return 1; 336 | } 337 | 338 | static int pdbase_unsubscribeAll(lua_State *L) { 339 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 340 | b->unsubscribeAll(); 341 | return 0; 342 | } 343 | 344 | static int pdbase_receiveMessages(lua_State *L) { 345 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 346 | b->receiveMessages(); 347 | return 0; 348 | } 349 | 350 | static int pdbase_receiveMidi(lua_State *L) { 351 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 352 | b->receiveMidi(); 353 | return 0; 354 | } 355 | 356 | static int pdbase_setReceiver(lua_State *L) { 357 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 358 | PdObject *o = *(PdObject **)(lua_isuserdata(L, 2) ? 359 | luaL_checkudata(L, 2, LUA_PDOBJECT) : nullptr); 360 | b->setReceiver(o); 361 | return 0; 362 | } 363 | 364 | static int pdbase_setMidiReceiver(lua_State *L) { 365 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 366 | PdObject *o = *(PdObject **)(lua_isuserdata(L, 2) ? 367 | luaL_checkudata(L, 2, LUA_PDOBJECT) : nullptr); 368 | b->setMidiReceiver(o); 369 | return 0; 370 | } 371 | 372 | static int pdbase_sendBang(lua_State *L) { 373 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 374 | const char *dest = luaL_checkstring(L, 2); 375 | b->sendBang(dest); 376 | return 0; 377 | } 378 | 379 | static int pdbase_sendFloat(lua_State *L) { 380 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 381 | const char *dest = luaL_checkstring(L, 2); 382 | float value = luaL_checknumber(L, 3); 383 | b->sendFloat(dest, value); 384 | return 0; 385 | } 386 | 387 | static int pdbase_sendSymbol(lua_State *L) { 388 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 389 | const char *dest = luaL_checkstring(L, 2); 390 | const char *symbol = luaL_checkstring(L, 3); 391 | b->sendSymbol(dest, symbol); 392 | return 0; 393 | } 394 | 395 | static int pdbase_startMessage(lua_State *L) { 396 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 397 | b->startMessage(); 398 | return 0; 399 | } 400 | 401 | static int pdbase_addFloat(lua_State *L) { 402 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 403 | float num = luaL_checknumber(L, 2); 404 | b->addFloat(num); 405 | return 0; 406 | } 407 | 408 | static int pdbase_addSymbol(lua_State *L) { 409 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 410 | const char *symbol = luaL_checkstring(L, 2); 411 | b->addSymbol(symbol); 412 | return 0; 413 | } 414 | 415 | static int pdbase_addAtom(lua_State *L) { 416 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 417 | int typ = lua_type(L, 2); 418 | if (typ == LUA_TNUMBER) { 419 | float num = lua_tonumber(L, 2); 420 | b->addFloat(num); 421 | } else if (typ == LUA_TSTRING) { 422 | const char *symbol = lua_tostring(L, 2); 423 | b->addSymbol(symbol); 424 | } 425 | return 0; 426 | } 427 | 428 | static int pdbase_finishList(lua_State *L) { 429 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 430 | const char *dest = luaL_checkstring(L, 2); 431 | b->finishList(dest); 432 | return 0; 433 | } 434 | 435 | static int pdbase_finishMessage(lua_State *L) { 436 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 437 | const char *dest = luaL_checkstring(L, 2); 438 | const char *msg = luaL_checkstring(L, 3); 439 | b->finishMessage(dest, msg); 440 | return 0; 441 | } 442 | 443 | static List tableToList(lua_State *L, int idx) { 444 | List list = List(); 445 | size_t i = 0, len = lua_rawlen(L, idx); 446 | for (; i < len; i++) { 447 | lua_pushinteger(L, i + 1); 448 | lua_gettable(L, idx); 449 | if (lua_type(L, -1) == LUA_TNIL) { 450 | break; 451 | } else if (lua_type(L, -1) == LUA_TNUMBER) { 452 | list.addFloat(lua_tonumber(L, -1)); 453 | } else if (lua_type(L, -1) == LUA_TSTRING) { 454 | list.addSymbol(lua_tostring(L, -1)); 455 | } 456 | lua_pop(L, 1); 457 | } 458 | return list; 459 | } 460 | 461 | static int pdbase_sendList(lua_State *L) { 462 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 463 | const char *dest = luaL_checkstring(L, 2); 464 | luaL_checktype(L, 3, LUA_TTABLE); 465 | List list = tableToList(L, 3); 466 | b->sendList(dest, list); 467 | return 0; 468 | } 469 | 470 | static int pdbase_sendMessage(lua_State *L) { 471 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 472 | const char *dest = luaL_checkstring(L, 2); 473 | const char *msg = luaL_checkstring(L, 3); 474 | List list = lua_type(L, 4) == LUA_TTABLE ? tableToList(L, 4) : List(); 475 | b->sendMessage(dest, msg, list); 476 | return 0; 477 | } 478 | 479 | static int pdbase_sendNoteOn(lua_State *L) { 480 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 481 | int channel = luaL_checkinteger(L, 2); 482 | int pitch = luaL_checkinteger(L, 3); 483 | int velocity = lua_isnoneornil(L, 4) ? 64 : luaL_checkinteger(L, 4); 484 | b->sendNoteOn(channel, pitch, velocity); 485 | return 0; 486 | } 487 | 488 | static int pdbase_sendControlChange(lua_State *L) { 489 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 490 | int channel = luaL_checkinteger(L, 2); 491 | int controller = luaL_checkinteger(L, 3); 492 | int value = luaL_checkinteger(L, 4); 493 | b->sendControlChange(channel, controller, value); 494 | return 0; 495 | } 496 | 497 | static int pdbase_sendProgramChange(lua_State *L) { 498 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 499 | int channel = luaL_checkinteger(L, 2); 500 | int value = luaL_checkinteger(L, 3); 501 | b->sendProgramChange(channel, value); 502 | return 0; 503 | } 504 | 505 | static int pdbase_sendPitchBend(lua_State *L) { 506 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 507 | int channel = luaL_checkinteger(L, 2); 508 | int value = luaL_checkinteger(L, 3); 509 | b->sendPitchBend(channel, value); 510 | return 0; 511 | } 512 | 513 | static int pdbase_sendAftertouch(lua_State *L) { 514 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 515 | int channel = luaL_checkinteger(L, 2); 516 | int value = luaL_checkinteger(L, 3); 517 | b->sendAftertouch(channel, value); 518 | return 0; 519 | } 520 | 521 | static int pdbase_sendPolyAftertouch(lua_State *L) { 522 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 523 | int channel = luaL_checkinteger(L, 2); 524 | int pitch = luaL_checkinteger(L, 3); 525 | int value = luaL_checkinteger(L, 4); 526 | b->sendPolyAftertouch(channel, pitch, value); 527 | return 0; 528 | } 529 | 530 | static int pdbase_sendMidiByte(lua_State *L) { 531 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 532 | int port = luaL_checkinteger(L, 2); 533 | int value = luaL_checkinteger(L, 3); 534 | b->sendMidiByte(port, value); 535 | return 0; 536 | } 537 | 538 | static int pdbase_sendSysex(lua_State *L) { 539 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 540 | int port = luaL_checkinteger(L, 2); 541 | int value = luaL_checkinteger(L, 3); 542 | b->sendSysex(port, value); 543 | return 0; 544 | } 545 | 546 | static int pdbase_sendSysRealTime(lua_State *L) { 547 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 548 | int port = luaL_checkinteger(L, 2); 549 | int value = luaL_checkinteger(L, 3); 550 | b->sendSysRealTime(port, value); 551 | return 0; 552 | } 553 | 554 | static int pdbase_isMessageInProgress(lua_State *L) { 555 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 556 | lua_pushboolean(L, b->isMessageInProgress()); 557 | return 1; 558 | } 559 | 560 | static int pdbase_arraySize(lua_State *L) { 561 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 562 | const char *name = luaL_checkstring(L, 2); 563 | lua_pushinteger(L, b->arraySize(name)); 564 | return 1; 565 | } 566 | 567 | static int pdbase_resizeArray(lua_State *L) { 568 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 569 | const char *name = luaL_checkstring(L, 2); 570 | long size = luaL_checkinteger(L, 3); 571 | lua_pushboolean(L, b->resizeArray(name, size)); 572 | return 1; 573 | } 574 | 575 | static int pdbase_readArray(lua_State *L) { 576 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 577 | const char *name = luaL_checkstring(L, 2); 578 | luaL_checktype(L, 3, LUA_TUSERDATA); 579 | vector *a = *(vector**)lua_touserdata(L, 3); 580 | int readLen = lua_isnoneornil(L, 4) ? -1 : luaL_checkinteger(L, 4); 581 | int offset = lua_isnoneornil(L, 5) ? 0 : luaL_checkinteger(L, 5); 582 | lua_pushboolean(L, b->readArray(name, *a, readLen, offset)); 583 | return 1; 584 | } 585 | 586 | static int pdbase_writeArray(lua_State *L) { 587 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 588 | const char *name = luaL_checkstring(L, 2); 589 | luaL_checktype(L, 3, LUA_TUSERDATA); 590 | vector *a = *(vector**)lua_touserdata(L, 3); 591 | int writeLen = lua_isnoneornil(L, 4) ? -1 : luaL_checkinteger(L, 4); 592 | int offset = lua_isnoneornil(L, 5) ? 0 : luaL_checkinteger(L, 5); 593 | lua_pushboolean(L, b->writeArray(name, *a, writeLen, offset)); 594 | return 1; 595 | } 596 | 597 | static int pdbase_clearArray(lua_State *L) { 598 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 599 | const char *name = luaL_checkstring(L, 2); 600 | int value = lua_isnoneornil(L, 3) ? 0 : luaL_checkinteger(L, 3); 601 | b->clearArray(name, value); 602 | return 0; 603 | } 604 | 605 | static int pdbase_isInited(lua_State *L) { 606 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 607 | lua_pushboolean(L, b->isInited()); 608 | return 1; 609 | } 610 | 611 | static int pdbase_isQueued(lua_State *L) { 612 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 613 | lua_pushboolean(L, b->isQueued()); 614 | return 1; 615 | } 616 | 617 | static int pdbase_blockSize(lua_State *L) { 618 | lua_pushinteger(L, PdBase::blockSize()); 619 | return 1; 620 | } 621 | 622 | static int pdbase_setMaxMessageLen(lua_State *L) { 623 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 624 | unsigned int len = luaL_checkinteger(L, 2); 625 | b->setMaxMessageLen(len); 626 | return 0; 627 | } 628 | 629 | static int pdbase_maxMessageLen(lua_State *L) { 630 | PdBase *b = *(PdBase **)luaL_checkudata(L, 1, LUA_PDBASE); 631 | lua_pushinteger(L, b->maxMessageLen()); 632 | return 1; 633 | } 634 | 635 | static int pdbase_shl(lua_State *) { 636 | // TODO 637 | return 0; 638 | } 639 | 640 | // ----------------------------------------------------------------------------- 641 | // ------------------------ Registers ------------------------------------------ 642 | // ----------------------------------------------------------------------------- 643 | 644 | static void pdarray_reg(lua_State *L) { 645 | static const luaL_Reg meta[] = { 646 | { "__gc" , pdarray_gc } 647 | , { "__len" , pdarray_len } 648 | , { "__index" , pdarray_index } 649 | , { "__newindex", pdarray_newindex } 650 | , { "__call" , pdarray_call } 651 | , { NULL , NULL } 652 | }; 653 | luaL_newmetatable(L, LUA_PDARRAY); 654 | luaL_setfuncs(L, meta, 0); 655 | lua_pop(L, 1); 656 | 657 | lua_pushliteral(L, "Array"); 658 | lua_pushcfunction(L, pdarray_new); 659 | lua_settable(L, -3); 660 | } 661 | 662 | static void pdpatch_reg(lua_State *L) { 663 | static const luaL_Reg meta[] = { 664 | { "__gc" , pdpatch_gc } 665 | , { NULL , NULL } 666 | }; 667 | static const luaL_Reg meth[] = { 668 | { "handle" , pdpatch_handle } 669 | , { "dollarZero" , pdpatch_dollarZero } 670 | , { "filename" , pdpatch_filename } 671 | , { "path" , pdpatch_path } 672 | , { "dollarZeroStr", pdpatch_dollarZeroStr } 673 | , { "isValid" , pdpatch_isValid } 674 | , { "clear" , pdpatch_clear } 675 | , { NULL , NULL } 676 | }; 677 | luaL_newmetatable(L, LUA_PDPATCH); 678 | luaL_setfuncs(L, meta, 0); 679 | luaL_newlib(L, meth); 680 | lua_setfield(L, -2, "__index"); 681 | lua_pop(L, 1); 682 | 683 | lua_pushliteral(L, "Patch"); 684 | lua_pushcfunction(L, pdpatch_new); 685 | lua_settable(L, -3); 686 | } 687 | 688 | static void pdobject_reg(lua_State *L) { 689 | static const luaL_Reg meta[] = { 690 | { "__gc" , pdobject_gc } 691 | , { "__newindex", pdobject_newindex } 692 | , { NULL , NULL } 693 | }; 694 | static const luaL_Reg meth[] = { 695 | { "setFuncs" , pdobject_setFuncs } 696 | , { NULL , NULL } 697 | }; 698 | luaL_newmetatable(L, LUA_PDOBJECT); 699 | luaL_setfuncs(L, meta, 0); 700 | luaL_newlib(L, meth); 701 | lua_setfield(L, -2, "__index"); 702 | lua_pop(L, 1); 703 | 704 | lua_pushliteral(L, "Object"); 705 | lua_pushcfunction(L, pdobject_new); 706 | lua_settable(L, -3); 707 | } 708 | 709 | static void pdbase_reg(lua_State *L) { 710 | static const luaL_Reg meta[] = { 711 | { "__gc" , pdbase_gc } 712 | , { "__shl" , pdbase_shl } 713 | , { NULL , NULL } 714 | }; 715 | static const luaL_Reg meth[] = { 716 | // Initializing Pd 717 | { "init" , pdbase_init } 718 | , { "clear" , pdbase_clear } 719 | // Adding Search Paths 720 | , { "addToSearchPath" , pdbase_addToSearchPath } 721 | , { "clearSearchPath" , pdbase_clearSearchPath } 722 | // Opening Patches 723 | , { "openPatch" , pdbase_openPatch } 724 | , { "closePatch" , pdbase_closePatch } 725 | // Audio Processing 726 | , { "processFloat" , pdbase_processFloat } 727 | , { "processShort" , pdbase_processShort } 728 | , { "processDouble" , pdbase_processDouble } 729 | , { "processRaw" , pdbase_processRaw } 730 | , { "processRawShort" , pdbase_processRawShort } 731 | , { "processRawDouble" , pdbase_processRawDouble } 732 | // Audio Processing Control 733 | , { "computeAudio" , pdbase_computeAudio } 734 | // Message Receiving 735 | , { "subscribe" , pdbase_subscribe } 736 | , { "unsubscribe" , pdbase_unsubscribe } 737 | , { "exists" , pdbase_exists } 738 | , { "unsubscribeAll" , pdbase_unsubscribeAll } 739 | // Receiving from the Message Queues 740 | , { "receiveMessages" , pdbase_receiveMessages } 741 | , { "receiveMidi" , pdbase_receiveMidi } 742 | // Event Receiving via Callbacks 743 | , { "setReceiver" , pdbase_setReceiver } 744 | , { "setMidiReceiver" , pdbase_setMidiReceiver } 745 | // Send Functions 746 | , { "sendBang" , pdbase_sendBang } 747 | , { "sendFloat" , pdbase_sendFloat } 748 | , { "sendSymbol" , pdbase_sendSymbol } 749 | // Sending Compound Messages 750 | , { "startMessage" , pdbase_startMessage } 751 | , { "addFloat" , pdbase_addFloat } 752 | , { "addSymbol" , pdbase_addSymbol } 753 | , { "addAtom" , pdbase_addAtom } 754 | , { "finishList" , pdbase_finishList } 755 | , { "finishMessage" , pdbase_finishMessage } 756 | , { "sendList" , pdbase_sendList } 757 | , { "sendMessage" , pdbase_sendMessage } 758 | // Sending MIDI 759 | , { "sendNoteOn" , pdbase_sendNoteOn } 760 | , { "sendControlChange" , pdbase_sendControlChange } 761 | , { "sendProgramChange" , pdbase_sendProgramChange } 762 | , { "sendPitchBend" , pdbase_sendPitchBend } 763 | , { "sendAftertouch" , pdbase_sendAftertouch } 764 | , { "sendPolyAftertouch" , pdbase_sendPolyAftertouch } 765 | , { "sendMidiByte" , pdbase_sendMidiByte } 766 | , { "sendSysex" , pdbase_sendSysex } 767 | , { "sendSysRealTime" , pdbase_sendSysRealTime } 768 | , { "isMessageInProgress", pdbase_isMessageInProgress } 769 | // Array Access 770 | , { "arraySize" , pdbase_arraySize } 771 | , { "resizeArray" , pdbase_resizeArray } 772 | , { "readArray" , pdbase_readArray } 773 | , { "writeArray" , pdbase_writeArray } 774 | , { "clearArray" , pdbase_clearArray } 775 | // Utils 776 | , { "isInited" , pdbase_isInited } 777 | , { "isQueued" , pdbase_isQueued } 778 | , { "blockSize" , pdbase_blockSize } 779 | , { "setMaxMessageLen" , pdbase_setMaxMessageLen } 780 | , { "maxMessageLen" , pdbase_maxMessageLen } 781 | , { NULL , NULL } 782 | }; 783 | luaL_newmetatable(L, LUA_PDBASE); 784 | luaL_setfuncs(L, meta, 0); 785 | luaL_newlib(L, meth); 786 | lua_setfield(L, -2, "__index"); 787 | lua_pop(L, 1); 788 | 789 | static const luaL_Reg static_meta[] = { 790 | { "__call" , pdbase_new } 791 | , { NULL , NULL } 792 | }; 793 | static const luaL_Reg static_meth[] = { 794 | { "blockSize", pdbase_blockSize } 795 | , { NULL , NULL } 796 | }; 797 | lua_pushliteral(L, "Base"); 798 | luaL_newlib(L, static_meth); 799 | lua_newtable(L); 800 | luaL_setfuncs(L, static_meta, 0); 801 | lua_setmetatable(L, -2); 802 | lua_settable(L, -3); 803 | } 804 | 805 | int luaopen_luapd(lua_State *L) { 806 | lua_newtable(L); 807 | pdarray_reg(L); 808 | pdpatch_reg(L); 809 | pdobject_reg(L); 810 | pdbase_reg(L); 811 | 812 | lua_pushliteral(L, "offset"); 813 | lua_pushcfunction(L, pdhelper_offset); 814 | lua_settable(L, -3); 815 | return 1; 816 | } 817 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 2 | require('lldebugger').start() 3 | end 4 | 5 | local ext = { 6 | Linux = 'so' 7 | , Windows = 'dll' 8 | , OSX = 'dylib' 9 | } 10 | local ffi = require('ffi') 11 | package.cpath = './?.'..ext[ffi.os]..';'..package.cpath 12 | 13 | local Pd = require('luapd') ---@type Pd 14 | 15 | local inChannels ,outChannels ,sampleRate ,queued 16 | = 1 ,2 ,48000 ,false 17 | 18 | -- our pd engine 19 | local pd = Pd.Base() 20 | 21 | -- custom receiver object for messages and midi 22 | local obj = Pd.Object{ 23 | -- message callbacks 24 | print = function(msg) print('Lua: print ' ..msg) end 25 | , bang = function(dest) print('Lua: bang ' ..dest) end 26 | , float = function(dest, num) print('Lua: float ' ..dest..': '..num) end 27 | , symbol = function(dest, sym) print('Lua: symbol '..dest..': '..sym) end 28 | , list = function(dest, list) 29 | print('Lua: list '..dest..': '..table.concat(list, ' ')) 30 | end 31 | , message = function(dest, msg, list) 32 | print('Lua: message '..dest..': |'..msg..'|'..table.concat(list, ' ')) 33 | end 34 | 35 | -- midi callbacks 36 | , noteOn = function(channel, pitch, velocity) 37 | print('Lua MIDI: note on: '..channel..' '..pitch..' '..velocity) 38 | end 39 | , controlChange = function(channel, controller, value) 40 | print('Lua MIDI: control change: '..channel..' '..controller..' '..value) 41 | end 42 | , programChange = function(channel, value) 43 | print('Lua MIDI: program change: '..channel..' '..value) 44 | end 45 | , pitchBend = function(channel, value) 46 | print('Lua MIDI: pitch bend: '..channel..' '..value) 47 | end 48 | , aftertouch = function(channel, value) 49 | print('Lua MIDI: aftertouch: '..channel..' '..value) 50 | end 51 | , polyAftertouch = function(channel, pitch, value) 52 | print('Lua MIDI: poly aftertouch: '..channel..' '..pitch..' '..value) 53 | end 54 | , midiByte = function(port, byte) 55 | print('Lua MIDI: midi byte: '..port..' '..byte) 56 | end 57 | } 58 | 59 | local block = pd.blockSize() -- or Pd.Base.blockSize() 60 | local inbuf = Pd.Array(block * inChannels) 61 | local outbuf = Pd.Array(block * outChannels) 62 | 63 | 64 | -- init pd 65 | -- 66 | -- set 4th arg to true for queued message passing using an internal ringbuffer 67 | -- 68 | -- in this test, messages should return immediately when not queued otherwise 69 | -- they should all return at once when pd is processing at the end of this 70 | -- function 71 | -- 72 | if not pd:init(inChannels, outChannels, sampleRate, queued) then 73 | print('Could not init pd') 74 | os.exit() 75 | end 76 | 77 | local midiChan = 1 -- midi channels are 0-15 78 | 79 | -- subscribe to receive source names 80 | pd:subscribe('toLua') 81 | pd:subscribe('env') 82 | 83 | -- set receivers 84 | pd:setReceiver(obj) 85 | pd:setMidiReceiver(obj) 86 | 87 | -- add the data/pd folder to the search path 88 | pd:addToSearchPath('pd/lib') 89 | 90 | -- audio processing on 91 | pd:computeAudio(true) 92 | 93 | 94 | print('BEGIN Patch Test') 95 | -- open patch 96 | local patch = pd:openPatch('pd/test.pd', '.') 97 | 98 | -- close patch 99 | pd:closePatch(patch) 100 | 101 | -- open patch again 102 | patch = pd:openPatch(patch) 103 | 104 | -- process any received messages 105 | -- 106 | -- in a normal case (not a test like this), you would call this in 107 | -- your application main loop 108 | pd:processFloat(1, inbuf(), outbuf()) 109 | -- pd:receiveMessages() 110 | print('FINISH Patch Test\n') 111 | 112 | 113 | -- (re)assign a callback function 114 | function obj.print(msg) 115 | print('foobar: '..msg) 116 | end 117 | 118 | -- (re)assign multiple callback functions 119 | obj:setFuncs{ 120 | noteOn = function(channel, pitch, velocity) 121 | print('noteOn: '..channel..' '..pitch..' '..velocity) 122 | end 123 | , midiByte = function(port, byte) 124 | print('\nfoo-midi-byte: '..port..' '..byte..'\n') 125 | end 126 | } 127 | 128 | 129 | print('BEGIN Message Test') 130 | -- test basic atoms 131 | pd:sendBang ('fromLua') 132 | pd:sendFloat ('fromLua', 100) 133 | pd:sendSymbol ('fromLua', 'test string') 134 | 135 | -- send a list 136 | pd:startMessage() 137 | pd:addFloat (1.23) 138 | pd:addSymbol ('a symbol') 139 | pd:finishList('fromLua') 140 | 141 | -- send a message to the $0 receiver ie $0-toOF 142 | pd:startMessage() 143 | pd:addAtom(1.23) 144 | pd:addAtom('a symbol') 145 | pd:finishList(patch:dollarZeroStr()..'-fromLua') 146 | 147 | -- send a list using a table 148 | local t = {1.23, 'sent from a Lua table'} 149 | pd:sendList ('fromLua', t) 150 | pd:sendMessage ('fromLua', 'msg', t) 151 | print('FINISH Message Test\n') 152 | 153 | 154 | print('BEGIN MIDI Test') 155 | -- send functions 156 | pd:sendNoteOn (midiChan, 60) 157 | pd:sendControlChange (midiChan, 0, 64) 158 | pd:sendProgramChange (midiChan, 100) 159 | pd:sendPitchBend (midiChan, 2000) 160 | pd:sendAftertouch (midiChan, 100) 161 | pd:sendPolyAftertouch (midiChan, 64, 100) 162 | pd:sendMidiByte (0, 239) 163 | pd:sendSysex (0, 239) 164 | pd:sendSysRealTime (0, 239) 165 | print('FINISH MIDI Test\n') 166 | 167 | 168 | print('BEGIN Array Test') 169 | -- array check length 170 | print('array1 len: '..pd:arraySize('array1')) 171 | 172 | local array1 = Pd.Array() 173 | 174 | local function readArray1() 175 | pd:readArray('array1', array1) 176 | local msg = 'array1' 177 | for i=1, #array1 do 178 | msg = msg..' '..array1[i] 179 | end 180 | print(msg) 181 | end 182 | 183 | -- read array 184 | readArray1() 185 | 186 | -- write array 187 | for i=1, #array1 do 188 | array1[i] = i 189 | end 190 | pd:writeArray('array1', array1); 191 | readArray1() 192 | 193 | -- clear array 194 | pd:clearArray('array1', 10); 195 | readArray1() 196 | print('FINISH Array Test\n') 197 | 198 | print('BEGIN PD Test') 199 | pd:sendSymbol('fromLua', 'test'); 200 | print('FINISH PD Test\n') 201 | 202 | 203 | -- play a tone by sending a list 204 | -- [list tone pitch 72 ( 205 | pd:sendList('tone', {'pitch', 72}); 206 | pd:sendBang('tone'); 207 | 208 | -- now run pd for ten seconds (logical time) 209 | -- you should see all the messages from pd print now 210 | -- since processFloat actually runs the pd dsp engine and the recieve 211 | -- functions pass messages to our PdObject 212 | print('Processing PD') 213 | for _ = 0, (10 * sampleRate / block) do 214 | pd:processFloat(1, inbuf(), outbuf()) 215 | -- pd:receiveMessages() 216 | -- pd:receiveMidi() 217 | end 218 | 219 | -- be nice and clean up on exit 220 | pd:closePatch(patch) 221 | pd:computeAudio(false) 222 | --------------------------------------------------------------------------------