Skip to content

Commit 0782bea

Browse files
authored
Merge pull request #78 from pingdynasty/feature/soul
SOUL patch support
2 parents 7e14b85 + b46d145 commit 0782bea

7 files changed

Lines changed: 129 additions & 17 deletions

File tree

Makefile

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ else ifdef TEST
4444
PATCHNAME ?= $(TEST)
4545
PATCHCLASS ?= $(PATCHNAME)Patch
4646
PATCHFILE ?= $(PATCHNAME)Patch.hpp
47+
else ifdef SOUL
48+
# options for SOUL patch compilation
49+
PATCHNAME ?= $(SOUL)
50+
PATCHCLASS ?= SoulPatch
51+
PATCHFILE ?= SoulPatch.hpp
52+
SOULCLASS ?= $(SOUL)
53+
SOULFILE ?= $(SOUL).soulpatch
54+
SOULHPP ?= $(SOUL).hpp
55+
DEPS += soul
4756
else
4857
# options for C++ compilation
4958
PATCHNAME ?= "Template"
@@ -65,14 +74,15 @@ export BUILD BUILDROOT TARGET
6574
export PATCHNAME PATCHCLASS PATCHSOURCE
6675
export PATCHFILE PATCHIN PATCHOUT
6776
export HEAVYTOKEN HEAVYSERVICETOKEN HEAVY
77+
export SOUL SOULCLASS SOULFILE SOULHPP
6878
export LDSCRIPT CPPFLAGS EMCCFLAGS ASFLAGS
6979
export CONFIG PLATFORM
7080

7181
DEPS += $(BUILD)/registerpatch.cpp $(BUILD)/registerpatch.h $(BUILD)/Source/startup.s
7282

7383
all: patch
7484

75-
.PHONY: .FORCE clean realclean run store docs help
85+
.PHONY: .FORCE patch libs faust gen heavy web minify clean realclean run store docs help
7686

7787
.FORCE:
7888
@mkdir -p $(BUILD)/Source
@@ -114,6 +124,9 @@ heavy: .FORCE
114124
gen: .FORCE
115125
@$(MAKE) -s -f gen.mk gen
116126

127+
soul: .FORCE
128+
@$(MAKE) -s -f soul.mk soul
129+
117130
sysex: patch $(BUILD)/$(TARGET).syx ## package patch binary as MIDI sysex
118131
@echo Built sysex $(PATCHNAME) in $(BUILD)/$(TARGET).syx
119132

README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ It is used to build, run and store patches written in PD, FAUST, Max Gen and C++
1313
* emcc (to make web) [[3]](#ref3)
1414
* faust2owl (to compile FAUST patches) [[4]](#ref4)
1515
* The heavy hvcc compiler (to compile PD patches) [[5]](#ref5)
16+
* The soul compiler (to compile SOUL patches) [[6]](#ref6)
1617

17-
* On Windows, you'll need a MAKE utility [[6]](#ref6). You'll also need to open common.mk and point TOOLROOT to your gcc installation directory, using a path string without spaces (such as using 8.3 filenames).
18+
* On Windows, you'll need a MAKE utility [[7]](#ref7). You'll also need to open common.mk and point TOOLROOT to your gcc installation directory, using a path string without spaces (such as using 8.3 filenames).
1819

1920
## Preparing the environment
2021
This has been done on a Ubuntu server 18.04
@@ -36,9 +37,7 @@ to compile puredata patches you need hvcc
3637

3738
compile FirmwareSender
3839

39-
FirmwareSender makes possible to use `run` and `store` make targets or by
40-
invoking it directly to run/store a compiled patch to the device using sysex
41-
codes.
40+
FirmwareSender makes it possible to use `run` and `store` make targets, or by invoking FirmwareSender directly to run/store a compiled patch to the device using sysex codes.
4241

4342
$ sudo apt install libasound2-dev libcurl4-openssl-dev pkg-config
4443
$ git clone https://github.com/pingdynasty/FirmwareSender
@@ -107,8 +106,6 @@ Note: assign OWL parameters with slider metadata: `[OWL:A]`, `[OWL:B]` et c. For
107106

108107
* put your PD patch file (e.g. `Foo.pd`) into `PatchSource`
109108
* `make HEAVY=Foo run`
110-
111-
Note: assign OWL parameters with PD receivers called `Channel-A`, `Channel-B`, etc.
112109

113110
## Building Max Gen patches
114111
Requires the `.cpp` and `.h` files of a Gen patch generated by Max Gen.
@@ -119,6 +116,11 @@ To compile and run a Gen patch called `Foo`:
119116

120117
Note: use OWL parameters in Gen with parameter names: `A`, `B`, `C`, `D`, `Exp`, and `Push`.
121118

119+
## Building SOUL patches
120+
121+
* put your SOUL patch file (e.g. 'Foo.soul') into `PatchSource`
122+
* `make SOUL=Foo run`
123+
122124
## Using FirmwareSender
123125

124126
If you prefer to build the patch first and send it later to the device you can
@@ -128,16 +130,15 @@ do it like this from the main directory of OwlProgram (this will store in slot 6
128130

129131
# Examples
130132

131-
Compile the puredata file owl_hypersaw.pd[[7]](#ref7):
133+
Compile the puredata file owl_hypersaw.pd[[8]](#ref8):
132134

133135
make HEAVY=owl_hypersaw clean patch
134136

135-
136137
Compile puredata file owl_hypersaw.pd and send to device to be run immediately:
137138

138139
make HEAVY=owl_hypersaw clean run
139140

140-
Compile KickBox[[8]](#ref8) C++ patch:
141+
Compile KickBox[[9]](#ref9) C++ patch:
141142

142143
make PATCHNAME=KickBox clean patch
143144

@@ -153,9 +154,11 @@ Compile KickBox[[8]](#ref8) C++ patch:
153154

154155
<a name="ref5">[5]</a> https://github.com/enzienaudio/hvcc
155156

156-
<a name="ref6">[6]</a> http://sourceforge.net/projects/mingw/
157+
<a name="ref6">[6]</a> https://github.com/soul-lang/SOUL
158+
159+
<a name="ref7">[7]</a> http://sourceforge.net/projects/mingw/
157160

158-
<a name="ref7">[7]</a> https://www.rebeltech.org/patch-library/patch/4_saw
161+
<a name="ref8">[8]</a> https://www.rebeltech.org/patch-library/patch/4_saw
159162

160-
<a name="ref8">[8]</a> https://github.com/marsus/MyPatches (AudioDisplay.hpp, BassDrum.hpp, Cymbal.hpp, Drum.hpp, KickBoxPatch.hpp, Oscillators.hpp, Sequence.h, SynthVoice.hpp, bjorklund.h)
163+
<a name="ref9">[9]</a> https://github.com/marsus/MyPatches (AudioDisplay.hpp, BassDrum.hpp, Cymbal.hpp, Drum.hpp, KickBoxPatch.hpp, Oscillators.hpp, Sequence.h, SynthVoice.hpp, bjorklund.h)
161164

SoulSource/SoulPatch.hpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#ifndef __SoulPatch_hpp__
2+
#define __SoulPatch_hpp__
3+
4+
#include "Patch.h"
5+
6+
#undef min
7+
#undef max
8+
#undef sin
9+
#undef cos
10+
#undef exp
11+
#undef sqrt
12+
#undef pow
13+
#undef log
14+
#undef log10
15+
16+
#define SOUL_CPP_ASSERT(x)
17+
18+
#include "soul.hpp"
19+
20+
#define MAX_MIDI_MESSAGES 32
21+
class SoulPatch : public Patch {
22+
private:
23+
SOULPATCH soulpatch;
24+
std::vector<SOULPATCH::Parameter> params;
25+
AudioBuffer* outputBuffer;
26+
SOULPATCH::MIDIMessage midiBuffer[32];
27+
size_t numMIDIMessages = 0;
28+
public:
29+
SoulPatch(){
30+
soulpatch.init(getSampleRate(), 0);
31+
params = soulpatch.createParameterList();
32+
for(size_t i=0; i<params.size(); ++i){
33+
registerParameter(PatchParameterId(PARAMETER_A+i), params[i].properties.name);
34+
}
35+
ASSERT(getNumberOfChannels() >= SOULPATCH::numAudioInputChannels, "Too many input channels in SOUL patch");
36+
ASSERT(getNumberOfChannels() >= SOULPATCH::numAudioOutputChannels, "Too many output channels in SOUL patch");
37+
outputBuffer = AudioBuffer::create(SOULPATCH::numAudioOutputChannels, getBlockSize());
38+
}
39+
void processMidi(MidiMessage msg){
40+
if(numMIDIMessages < MAX_MIDI_MESSAGES){
41+
midiBuffer[numMIDIMessages] = {0, msg.data[1], msg.data[2], msg.data[3]};
42+
numMIDIMessages++;
43+
}
44+
}
45+
void processAudio(AudioBuffer &buffer){
46+
SOULPATCH::RenderContext<float> ctx;
47+
for(size_t i=0; i<params.size(); ++i){
48+
float min = params[i].properties.minValue;
49+
float max = params[i].properties.maxValue;
50+
float value = getParameterValue(PatchParameterId(PARAMETER_A+i));
51+
params[i].setValue(value * (max-min) + min);
52+
}
53+
for(size_t i=0; i<ctx.inputChannels.size(); ++i)
54+
ctx.inputChannels[i] = buffer.getSamples(i);
55+
for(size_t i=0; i<ctx.outputChannels.size(); ++i){
56+
outputBuffer->clear();
57+
ctx.outputChannels[i] = outputBuffer->getSamples(i);
58+
// ctx.outputChannels[i] = buffer.getSamples(i); // in-place
59+
}
60+
// debugMessage("xruns", (int)soulpatch.getNumXRuns());
61+
ctx.numFrames = buffer.getSize();
62+
ctx.incomingMIDI.messages = &midiBuffer[0];
63+
// ctx.incomingMIDI.numMessages = 0;
64+
ctx.incomingMIDI.numMessages = numMIDIMessages;
65+
soulpatch.render(ctx);
66+
numMIDIMessages = 0;
67+
for(size_t i=0; i<ctx.outputChannels.size(); ++i){
68+
buffer.getSamples(i).copyFrom(outputBuffer->getSamples(i));
69+
}
70+
}
71+
};
72+
73+
#endif // __SoulPatch_hpp__

Source/operators.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <stdlib.h>
22
#include "heap.h"
3+
#include "message.h"
34

45
extern "C" void *__gxx_personality_v0;
56
extern "C" void __cxa_end_cleanup (void);
@@ -19,3 +20,9 @@ extern "C" int __cxa_guard_acquire ( __int64_t *guard_object ){ return !*(char *
1920
// Sets the first byte of the guard object to a non-zero value.
2021
extern "C" void __cxa_guard_release ( __int64_t *guard_object ){ *(char *)guard_object = 1; }
2122
extern "C" void __cxa_guard_abort ( __int64_t *guard_object ){}
23+
24+
namespace std {
25+
void __throw_bad_alloc (void) { error(PROGRAM_ERROR_STATUS, "bad alloc"); while(1); }
26+
void __throw_bad_function_call() { error(PROGRAM_ERROR_STATUS, "bad func"); while(1); }
27+
void __throw_length_error (const char *) { error(PROGRAM_ERROR_STATUS, "bad len"); while(1); }
28+
}

gen.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
GENSRC ?= $(BUILDROOT)/GenSource
22

3-
.PHONY: .FORCE
3+
.PHONY: .FORCE gen
44

55
$(BUILD)/Source/gen.h: .FORCE
66
@echo "#include \"$(GEN).h\"" > $@

soul.mk

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
SOULSRC ?= $(BUILDROOT)/SoulSource
2+
SOULCC ?= Tools/soul
3+
SOULINC = $(BUILD)/Source/soul.hpp
4+
5+
vpath %.soul $(PATCHSOURCE)
6+
vpath %.soulpatch $(PATCHSOURCE)
7+
8+
.PHONY: .FORCE soul
9+
10+
$(SOULINC): .FORCE
11+
@echo "#include \"$(SOULHPP)\"" > $@
12+
@echo "#define SOULPATCH $(SOULCLASS)" >> $@
13+
14+
$(BUILD)/Source/$(SOULHPP): $(SOULFILE)
15+
@$(SOULCC) generate --cpp $< --output=$@
16+
17+
soul: $(SOULINC) $(BUILD)/Source/$(SOULHPP)
18+
@cp $(SOULSRC)/* $(BUILD)/Source

web.mk

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ EMCC ?= emcc
77
EMAR ?= emar
88
EMCCFLAGS += -fno-rtti -fno-exceptions
99
# EMCCFLAGS += -s ASSERTIONS=1 -Wall
10-
EMCCFLAGS += -Dnullptr=NULL
1110
EMCCFLAGS += -I$(SOURCE) -I$(PATCHSOURCE) -I$(LIBSOURCE) -I$(GENSOURCE) -I$(BUILD)
1211
EMCCFLAGS += -I$(BUILD)/Source
1312
EMCCFLAGS += -ILibraries -ILibraries/KissFFT -DHV_SIMD_NONE
1413
EMCCFLAGS += -Wno-warn-absolute-paths
1514
EMCCFLAGS += -Wno-unknown-warning-option
16-
EMCCFLAGS += -Wno-c++11-extensions
1715
EMCCFLAGS += --memory-init-file 0 # don't create separate memory init file .mem
1816
EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_WEB_setup','_WEB_setParameter','_WEB_getParameter','_WEB_processBlock','_WEB_getPatchName','_WEB_getParameterName','_WEB_getMessage','_WEB_getStatus','_WEB_getButtons','_WEB_setButton', '_WEB_processMidi', '_malloc']"
1917
EMCCFLAGS += -s "EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap']"
@@ -32,7 +30,7 @@ EMCCFLAGS += -s WASM=0 # disables wasm output
3230

3331
CPPFLAGS =
3432
CFLAGS = $(EMCCFLAGS) -std=gnu11
35-
CXXFLAGS = $(EMCCFLAGS) -std=gnu++11
33+
CXXFLAGS = $(EMCCFLAGS) -std=gnu++14
3634
LDFLAGS = $(EMCCFLAGS)
3735

3836
EMCC_OBJS = $(addprefix $(WEBDIR)/, $(notdir $(CPP_SRC:.cpp=.o)))

0 commit comments

Comments
 (0)