After I go through these learnings we'll think about how to apply them to creating a basic but useful operator. This operator will function like a normal counter, but taking an audio input rather than a control one. If you are using RNBO for something like building a sequencer (where you might want your whole sequencer engine to be in the audio thread for sample accurate triggers/gate), or controlling a modular synth (where you will be sending out control voltage, and might be receiving it too) an object like this can be extremely useful as a clock divider, or the basis for constructing more complex timing patterns. We'll call this object a [pulse_counter~]. It's worth pointing out that it's perfectly possible to build the same thing inside gen~, but it's much simpler and more reusable to have it as an operator.
RNBO is completely contained in a max package, so we can find everything in the Max 8 > Packages folder. RNBO is a really big package and there is a lot to look at here, but what we are interested in lives in the source > operatorsfolder. Inside here we find a lot of .js files which make up the various RNBO objects inside the [rnbo~] object. The API for these objects is not available anywhere so we'll need to do a bit of detective work here to figure out their structure (it's possible that you can discern more about how these objects work from all the C code in the RNBO project, but that's a little out of scope for basic hacking in of new objects).
The first thing that we can note is that all of these operators are written in a language which lands somewhere between Javascript, Typescript and C++. That makes IDE completion a complete non-starter at the moment (though not out of the realms of possiblity with a vscode plugin implementation). The first important thing is that all our operators in this folder either extend the Operator or StatefulOperator classes, which makes sense as we already know from our RNBO basics that we have two kinds of operators, those with or without state. So the first decision we need to make when hacking in our own objects is which of these classes to extend.
Although we are mostly looking at code here we should briefly touch on the simpleoperators.json file in the operators folder. This defines some fundamental RNBO objects which require only simple one line expressions. For example the following implements a log() operator:
"log": {
"exprtext": "rnbo_log(in1)",
"safeexprtext": "rnbo_log(in1 <= 0.0000000001 ? 0.0000000001 : in1)",
"digest": "The natural logarithm of the input (log base e)",
"alias": "ln",
"comments": [
"@seealso rnbo_exp",
"@seealso rnbo_log10",
"@seealso rnbo_e",
"@tag RNBO",
"@tag RNBO Operators",
"@tag RNBO Math",
"@tag RNBO Powers",
"@category Powers"
],
"meta": {
"in1": {
"displayName": "Input",
"digest": "Input value to be processed."
},
"out1": {
"displayName": "Natural Log Output",
"digest": "The natural log of the input."
}
}
}
We can see that it's providing a prettier wrapper around the underlying rnbo_log(x) call in the exprtext, and interestingly it also provides a safe version of the expression which ensures that the input is greater than or equal to 0.0000000001. It's not entirely clear when this safe version of the expression is used, or if there is a way to ensure the safe value is used, but presumably with more digging this could be determined.
We will see later that other rnbo operators make liberal use of these simple operators, and that operators using other operators of any type is a common pattern. There is no place in the existing operators, other than this file, where the underlying rnbo_x calls are used, so we should also stick to this rule.
All the other fields we see in the json above are also used in the full operator classes, so we can learn about them below.
At the top of most of these operator class files is some metadata which is used for the help/reference files. We won't cover making these for our hacked in operators, but it's worth having a quick look none-the-less. Since the counter.js file is probably a good starting point for the pulse counter we'll eventually build let's start there.
/** Add an amount to the current count every sample.
* @seealso rnbo_accum
* @seealso counter~
* @seealso rnbo_counter
* @tag RNBO
* @tag RNBO Generators
* @category Generators
*/
This is all pretty standard stuff, it's just calling out other objects which are similar, and putting our own object into a specific category and giving it some tags. Descriptive stuff which won't affect object operation. Presumably this is used when generating help/reference data for the operators, but that's beyond the scope of this investigation.
You will see @meta in various places throughout the operator implementations, and it allows us to specify specific details which are important for the class. The first place you may encounter @meta is above the class.
//complex_conj.js
@meta({
digest: "complex conjugate",
internal: true,
publishAsObject: false
})
digest = "";dsponly = true;dsponly value in the class to true to tell RNBO that this object should only have a ~ version. Otherwise both versions of the object will be available inside RNBO.publishAsObject = false;internal=true;alias = [ "+=", "plusequals" ];aliasonly:trueallowInline : true__forceInline(op) function, see below.Looking through the inbuilt objects it's possible to find a few important values in the clases that we need to be aware of when hacking our own operators.
Stateless operators extend the Operator class and perform their calculations in the compute()function. The compute function can have as many arguments as needed and these should be typed. The return value of the compute function should also be typed, the return value can be a list, ie [number,number] if the function does not return a value the return type should be void.
Stateful operators extend the StatefulOperator class and perform their calculations in the next()function. The same rules about arguments and return values as stateless operators apply.
@option({ digest : "Initial value", comments : "The initial value from which to begin counting. Note that this only applies to the first count loop and only when counting is enabled at startup (reset 0), because resetting the counter~ during run time will always start counting from zero." }) init = 0;
[accum] object provides a couple of examples which are worth looking at so we can understand more complex use cases for options. @ENUM resetmode = {
pre : 0,
post : 1
};
@option({ digest : "Specifies whether the count is reset before (pre) or after (post) the accumulation." }) resetmode : accum.resetmode = "post"
@option ({ digest : "Specifies the minimum value for the accumulated output." }) min : number = 0;
@option({ digest : "Specifies the maximum value for the accumulated output.", optional : true, doNotShowInMaxInspector : true }) max : number;
value : number = options.min;
number. Giving an option the number type will mean that it's a float rather than the default int type which is used when no type is specified.pre/post designations rather than 0/1. We can see that this is implemented by creating the enum inside the operator class and then referencing it in a Typescript like way resetmode : accum.resetmode = "post"optional : true, doNotShowInMaxInspector : true. There are many objects with optional attributes, but only accum has an attribute not shown in the max inspector like this. There is also a hidden : true value which can be set.options.optionVarName defaultarg@option({ digest : "Buffer to use." }, defaultarg: 1) buffername : symbol;
@option interp : buffer.interp = "linear";
@option channels : number = 1;
@ENUM//bufferphasetoindex.js
@ENUM({ scope : "buffer", global : true }) indexmode = {
phase : 0, // 0..1
samples : 1, // sample offset in buffer
signal : 2, // -1..1
lookup : 3, // -1..1
wave : 4 // between start and end
};
@option indexmode : buffer.indexmode = "phase";
The next and compute functions also have a @meta field
@meta({
x: {
digest: "Input to be accumulated",
comments: "{@variation accum Values}{@variation accum~ Signals} sent through the left inlet will \
be accumulated as long as the calculation is not reset."
},
reset : {
digest: "Non-zero input resets calculation",
comments: "A non-zero number will trigger the output to reset to 0 on the next input value"
},
out1: {
digest: "The accumulated output"
}
})
This is fairly self explanatory, and uses the digest and comments fields to provide some descriptive information of what is expected at the objects inlets and outlets.
The classes also seem to have the following variable values and functions available to be called on this
this.sr - get the current sample ratethis._voiceIndex - report current voice index (0 if not in a polyphonic context)this.vs - audio device vector sizethis.convertToNormalizedParameterValue(index, value) - in the tonormalized operator this is called directly on the class.this.convertFromNormalizedParameterValue(index, normalizedValue) - similarly called on the operator class in fromnormalized operator.We saw earlier that the json defined simple operators make use of the exprtext field to define their functionality. This is also available in class metadata and can replace the need to add a next/compute function. We can see this in the implementation of the history operator.
@meta({
publishAsObject : false,
// since history is special in the way, that it wants the last and not the actual value
// we need t define our own expression text
exprtext : "var h = new history(); out1 = h; h = in1;"
})
class history extends StatefulOperator
{
// take care when renaming this state variable - it is used by name in gen.js
@meta({ defaultarg: 1 }) value: number = 0;
getvalue(): number {
return this.value;
}
setvalue(val: number) {
this.value = val;
}
};
Inside your class you are going to have to define the various types of the values that you use. Remember that although this code has a js/ts feel it will end up as compile cpp code, and therefore we are really working with a strict type system.
Types are added to variables using a Typescript like format of var : type
int - variables instantiated without a type are by default int, but this can also be made exlicit if needed
number - The number type is a double
symbol - string type used for buffernames etc..
boolean - can be either 0/1 or true/false. most commonly existing implementations see true/false when setting values in the class as 0/1 in function signatures.
//average.js
resetFlag : boolean = false;
wantsReset : boolean = false;
...
next(x : number, windowSize : int = 0, reset : boolean = 0) : number
{
...
if (reset != 0) {
if (this.resetFlag != 1) {
this.wantsReset = 1;
this.resetFlag = 1;
}
}
else {
this.resetFlag = 0;
}
...
}
void - used to indicate there is no return value from functions
auto - types can be designated as auto, so we can assume some modern cpp is under the hood! Useful for when you return arrays etc..
Index - used for indexing channels in buffers, see buffernormalize for exampleSampleValue - Value written to and read from a sample bufferSampleIndex - Used for indexing sample positions in buffersMillisecondTime - mstime has its own type, as seen in the currenttime operatorlist - list types are lists and have a number of functions which can be called on them//found in append.js
let a: list = [];
a.concat(b); //if your input is a list ( a : list ) then you can concat another list to it with this function call
a.push(c); // push value onto list
a.pop();
a.shift();
a.unshift(x);
a.indexOf(x);
var l = a.length; //can get length like this. note not a function call
"T&" - Templated type references in quotes are used in places where templated types are used in the cpp implementation. This is generally used for passing in buffers//bufferplay.js
next(buffer: "T&", trigger : number, start: number, length: number, rate: number, channel: number) : [ number, number, number ]
Types can be declared as const in function signatures as follows
//bufferreadsample.js
compute(
buffer : "T&",
sampleIndex : "const SampleValue", // declaring these parameters as const actually helps the inliner
channel : "const Index",
start : "const SampleIndex",
end : "const SampleIndex"
) : SampleValue
It's also possible to inline a return value
// cubicinterp.js
compute(a : number, w : number, x : number, y : number, z : number) : "inline number"
You can reference other operators from within your operator code. For example the [average_rms] object uses the average and safesqrt operators in its code. Notice that the stateful average operator needs to be instantiated, but the stateless safesqrt can simply be called
// average_rms.js
av = new average();
...
return safesqrt(av.next(x*x, windowSize, reset));
One of the complexities of writing code in a hybrid language such as rnbo operators are written in, is that it totally confuses our IDE's detection of what functions are available. It's fairly obvious to see when an operator calls another operator class, but not when it calls one of the operators defined in the simpleoperators.js. After having gone through the code and trying to filter out all of these operator calls the following additional function calls are available to us and there are probably more which are simply not used in any existing operators!
clamp(x, min, max); //standard clamping function for min/max bounds
clip(x, min, max); //clip a value
wrap(x, start, end);
trunc(x); // truncate a value
floor(x);
round(x);
fract(x);
abs(x);
fabs(x);
cos(x);
atan2(x);
sin(x);
exp(x);
sign(x);
log(x);
log2f(x);
max(x, y);
min(x, y);
div(x, y);
isnan(x);
fixnan(x);
fixdenorm(x); //Replace denormal values with zero, presumably works like the gen function?
pow(base, exponent);
nextpoweroftwo(x);
sqrt(x);
pi01(); //used only in phaseshift.js
uint32_add(x, y);
uint32_trunc(x);
uint32_rshift(x, shift); // is there an equivalent uint32_,lshift(x, shift) ?
iadd(x, y); // pink.js
imul(x, y);
mix(a, b, mixamnt); // mixamnt is a phasor value in rand.js
updateDataRef(this, this.buffer); // always needs calling after buffer.setSize(x) is called on a class member. see average.js for example
evalexpr(expr); //see delay.js for usage
t = globaltransport(); //get the global transport object
t.getBeatTime(); //current beat time
t.getBeatTimeAtSample(offset);
t.getTempo();
t.getTempoAtSample(offset);
t.getTimeSignature();
t.getTimeSignatureAtSample(offset);
t.getState();
t.getStateAtSample(sampleOffset);
systemticks(); // pink.js
//noise.js - this.state is a FixedUIntArray(4);
xoshiro_reset(seed, this.state) ; // in noise.js the seed is systemticks() + voiceindex() + random(0, 10000)
xoshiro_next(this.state);
//random.js
rand01();
console.log(...args);
__forceInline(functionCall) // eg sampleIndex = __forceInline(bufferphasetoindex(lookup, 0, bufferSize));
// found in allpass.js
d = new delay(44100); //create a delay line of a specific size
d.read(x); //reads a delay value at a specific delay time
d.write(x); //writes to the delay buffer
d.step(); //steps to the next location in the delay buffer
d.init(); //needs to be called to initialize the delay line, should be called from an operators init()/dspsetup() function
d.clear(); //clears the delay line
//average.js
buffer = new Float64Buffer({ channels : 1 });
buffer.getSampleRate();
buffer.getSize();
buffer.setSize(x); // don't forget to call updateDataRef(this, this.buffer); after resizing a buffer
buffer.requestSize(this.sr + 1, 1); // what does this do?
buffer.getSample(channel, i); //takes Index types for both arguments
buffer.setSample(channel, index, value);
buffer.setZero();
buffer.getChannels();
buffer.setTouched(true);
// pink.js
rows = new IntBuffer({ size: 16, channels: 1 });
//mtof.js
buffer = new AutoAudioBuffer({ buffername : "RNBODefaultMtofLookupTable256" });
//cycle.js
//MultiAutoAudioBuffer appears to have the same set of functions as buffer, with no additional arguments
buffer = new MultiAutoAudioBuffer({
buffers: options.buffername,
updateFunc: "bufferUpdated"
});
//fftstream.js
let fsa = new FixedSampleArray(size);
fsa[i] = x;
//noise.js
state = new FixedUIntArray(4);
Well that was a lot to take in, and I'm sure there is plenty missing, but armed with all this information we can now take a look at implementing our [pulse_counter~] operator.
So lets remind ourselves what we want to do. We would like to build an operator which increases a counter every time we get receive a signal which passes above a specific threshold, once the signal has crossed the threshold the counter value shall not be increased further until the signal crosses below the threshold, or the counter is reset. Since this is fundamentally the existing [counter~] operator with some additional logic, we just need to alter the code to detect the transitions we are interested in. We will also add another inlet which allows us to set a threshold for when this happens, as some signals do not quite reach 1, or do so at a rate which doesn't work outside of the sample at a time (gen~) domain. We simply add a value to store the previous processed input value which we received, add a new input to the @meta and next() function calls, and make a few small adjustments to the code.
We can do so as follows:
/** Add an amount to the current count every time a pulse is received.
* @seealso rnbo_accum
* @seealso counter~
* @seealso rnbo_counter
* @tag RNBO
* @tag RNBO Generators
* @category Generators
*/
@meta({
dsponly : true,
digest : "count incoming pulses or gates"
})
class pulse_counter extends StatefulOperator
{
@option({ digest : "Initial value", comments : "The initial value from which to begin counting. Note that this only applies to the first count loop and only when counting is enabled at startup (reset 0), because resetting the counter~ during run time will always start counting from zero." }) init = 0;
count = 0;
carry : int = 0;
carry_flag : bool = false; //carry flag persists in this version because we sometimes return early
last : number = 0;
@meta({
a : { digest : "pulse train or gates in", comments : "Every time the input goes from 0.-1. 1 will be added to the count " },
reset : { digest : "non zero value resets the count", defaultarg : 1, comments : "A non-zero value resets the counter to zero and stops counting. A zero value starts the counter." },
limit : { digest : "count limit (zero means no limit)", defaultarg : 2, comments : "The upper limit for counting. Values above the limit are wrapped between 0 and the limit value." },
threshold : { digest : "set the threshold at which a new trigger will be registered", defaultarg: 3, comments : "The threshold at which the gate/trigger will be registered, useful for when your signal does not hit 1." },
out1 : { digest : "current count (running total)", comments : "The current value of the counter as a signal." },
out2 : { digest : "carry flag (counter hit maximum)", comments : "Outputs a signal value of 1. whenever the limit value is reached, otherwise outputs a 0." },
out3 : { digest : "carry count", comments : "The total number of times the counter~ object has reached the limit since the last reset, as a signal value. Sending a non-zero value to the middle inlet resets the carry count." }
})
next(a : number = 0, reset : number = 0, limit : number = 0, threshold : number = 0.99) : [ number, number, number ]
{
if (reset != 0) {
this.count = 0;
this.carry = 0;
this.carry_flag = 0;
}
//Ignore negative signals
else if(a >= 0) {
var floored = (a > threshold) ? 1 : 0;
//only add to the count if this is a new transition
if(floored == 1 && this.last != 1){
this.count += floored;
}
this.last = floored;
if (limit != 0) {
//TODO if count is higher than limit (ie limit changed) wrap to the correct value
if ((a > 0 && this.count >= limit) || (a < 0 && this.count <= limit)) {
this.count = 0;
this.carry += 1;
this.carry_flag = 1;
}
}
}
return [ this.count, this.carry_flag, this.carry];
}
init() {
this.count = options.init;
}
}
If you copy the above code and save it as ~/Documents/rnbo/operators/pulse_counter.jsyou will then be able to open the following max patch, which shows our new rnbo object working. (Thanks to Pete Dowling for the tip on using this folder for custom objects)
----------begin_max5_patcher----------
4945.3oc68j1iiiicetpeEL5KoGLU4Q7PW6tYwljMAnAxjdwdjuLSCCZaZa0
QVRQG0wNX6e6gGRxhVxxRtjbUcMFCPMsIond7cw2Eo9kauwXQzSrTCvuA7Sf
at4Wt8lajMIZ3lheeiwN5SKCnoxgYDxdLZwWLtS0UF6oLYyAQzU6XoofnXVX
Yuwzrka8C2LOgsLS8RfNyLuCfLE+0S9WDZlI3yEOQX9N+v.Vl7cA22XTd1gs
5uR9h4.y8VkuP0vxdNlodaFFfOK54eb6sh+b2YsFeflDR2ImQijvEQeswhW1
J3Oj4ye6f37fT17kQ4gYrjxgF3GxjMIFO5TnGKawecIh+hsdwnG3QQOA9o7W
b4rKVFOvRR8iBkOKbFdFViVxWPJrVAZ6Fi09ArZOSwa+FCZbbsluo1iHv0eI
RNQt2U0jenpI6plRXO3W97jpVoIbTVFGekmnnHOYSL1OMQqXIg49RPQ0HmpW
.RR5qfRlFSWtmbV9zF6oBVdXApmX6IYScj+OaTMJAmCZSPzx+WlDKaV1nf42
OLNgkxBynYEPeU2qXqo4AYyWGElk5+2kv.jy92V+qKfwV6rhe7+hlseArIwe
UTnT.rNoPzb4aiygYIVLPs0hbDgz3VdXN+CGsbjNS4qw7zEzDAkZQ.qFyMW5
HJJPuqpmKfsNqn6X+vvCPhYQwGuyD+Ma63YWDw6bWWysrmz44gpdmyYJxlmR
ePGBynAAEZAzm9mng96nYrLeEE.YV0IKjxWnaSWlDEDnMapddnkdVwYxWxdz
eU1V4KpNu.e39wk7PFUT4U9aXoY5skQ2jp2RZ1yJjdslxWTHDOOisKNfuJzG
.W7fqOHcazioECrjOaO9SnhS9Tsol6fsSpKzWWkqV6Gp5Me2BVxWqDoaWMoq
ZmCOq8ZKQ3ZbzRMSGSNQhK72niukpLEKkGnJJjodeZZdQG7ZNTVsTUzgiUWS
c8dpqs1y3fouEUEspMmunB4P+c.i07si2qXuR49b9FVIz4zrrD+E4YJJ0MMn
HchIDLfoblmmKWf7s.R37i2o839Y9zferbD6h3Mv0uWMjJkxUPVdn++WNq.M
TvDLuI5PN1TVhOUSNppqJc7ZLS2V8Zus58Oo7mND4FHVDu2gLnXqqLn0XP0Q
GGvfhdixfZoLt7cJCJ5JCZcFTTGLn3KOCZcmqZ5D4BZ3lSv7BckZWwjt19uM
WkNt6RGxBgz10oM2J6D8WtPl2btF1NXUXjQj.rLZ2Nt6IsRAdbKM6eNEjv31
+9LHjaaJvOEjwaEDkmHbtOgqGHQzHMHMBPef5GHLtE3GxGFCrjywxAiu9OUe
5ay62iQbIDkauX4+C0zI3gPcMOB008xqwkfrd+pwEcUiacMtntz3Rt3ZbKkI
6GGJ1CoD.kghBhs6hG828iQgQx3or52abLVOROX8zYyvGw0HmWNaVi+k1haY
I4+OHhsACDuD7u.36XoGUuO7c+1eN4mEqOfrE9XhWNKjyo9A+P3cbcgHwevh
+PTikCQP9vjC+mL+bYanp1fUsgqZC84eqw4JN7Vdkv0kQWy1QUA8.VuC1SwI
pnRbbAO+v0QGaYGxXqR+XHeMGtj8whAp8FnIajAAr9LTeJDABLkko0sV+7of
i8+TxeJIJ9vYWO9L+YwDw2nL.nnAzvUfXZBcGeK0HPgFG.WmUNGbzmE+TZfO
8vMwjubQ6kAYoNyaAjQWtkdP.2plS4Kuk4bEKlqGfChqZoyRwHCMCQNfvH0f
BGCjlXZFEbAG2mcDLgNZxbTPSR8cs7tpPeJM6ch.QiCBD8qVDHdbPf3e0h.I
iCBj7qTDnXyww.CJmmIXGgwAOoLupKDUgsUchnPiDhB8dGQgGIDE9cMhpzxw
9gnPGEQImmCAcYpW4VN1pUUeKnZaOd74cKh5BOV2z7WJt7ukx.h4CHlPf.yx
RE4zOE7A1rMy9MfU9pj7CV7LvD7nO2T4vHtuHIzzse2LiuowzKh334VYj9eD
V7KGSVB+enQKtsc5Beuo37LsTq1jrT4VdSanqfJZtVTkNow2aiZSrb09ZePm
SpOvF5bgsCevWDTfOWn.OlPA4bgBROgBMoAgd1AvF0hUHGpOdHVv7BPTsrM+
PfDznBI3W.jfOKx1VVPbEDzR37Jz.9o0U6fo2qdHFacBJCs+gZzLJJiqOJAR
OOOGLlf0FAWCZHeM9mT0LxG+j9p4DwMsDXl2L.eCKUIUqpQLxohJZjtg0ZpR
Lm440yDU4nBrJ5zIpBMzDUgMGZhp5a.DqVn8ND3EXq4MgqgQIKw5WnbdoXeA
b7wlMrDP1VtQJaiBVcBhKRkCGrCREmbYdcPvwMQUNWNtYGqSsfgtSM2Lx5sI
2L5ETiGWXt4zG84TNQ7kEoiUP8jgclaUMSkGWUVZEw5uxb6MQf7X.uI+.vyQ
4.5pujmVaj0kJDyLMg25J.mm4dyYtGMSuVcyOgMkBPPn8LKQEoJyzqi6nJ.A
cuPktv2CfmR7oPQgZch7DK5QV7AhlJwmBu.58t5e+7lvyv1D36GSBWVzlMAr
dRfD5v4jFR4e6G6nzWQFWTYtpZe6NAmGi.1s9O+vWRBxiqxqkl0aQIqXICS0
nBcNuIDOLRbAU4xHflxB3T5njuBPmxpMhhAvTU1+NmdeN7f2mynGIqtAkdpy
6YTXz50iQrF+q4IgofOJhXA3SqWChR.+4HAqoXyiOIcA8cc9ANQPefcGt2wf
BLUHYyuIBE7HkIZEC7a37vbR9wdfoPiGlB8dFSsbaj+xCzddlQE++Vpk.DsF
TDT22uYa3rTHNJg.WsY1YEb0gu80DDj7yNvqsq0ZZBXe+fEzkIT0mKP1JE8L
hKakElGMvrsOBcihaeL8K5r264YRbrLIjQK7rUfy7F1sNLi82uttL16GuklF
cxpxFZgq4TtEbBhok2YYqeu8pSbJjKJK79RSKPMya.cCihVhgGQ5Y5RNi9oo
YpPshkgiDZpHclGPzDmo4kQAJQlehidvV1t1HGhkomqI16NdSVPrCxh3YB8r
McbEMUeHHnsnodxLrjFrb+gbPFb3ZnOgHXIjbDdriDMqE4q2WkytZy55M0Vg
RdWyp+bXfnWrQCaXhqu1kKcXMTAACEMgfPGahiokqKBagahMZ4RX3LJU4Nvb
MW+8VykjWZd6fV+Uao3Hubm+I4knABeBYfhZz2QRzcHSvIfh3LvXIN0QmPZ8
2X3rx+FWVTFe7hD9TDf9YeS5s7Icr4DEscY1IFA75OplJviaYgfhKP.wAESv
UyVM68mWNmNREx6WkS37SWkhEG8jkGKIgi.Ap9z8l0S8NXbGoRiJ93tJzBpY
3JNFlyfuDmdZlP2Sx8MIhqmgyRka0cTekZc.MObrMFRO8TBaCMcsgVtilmRk
Py7l6dN7y7qbYcwOippqUEhiJurj2WmQUx0qsh5mQURWFEa8F8Zq.pt8pJNi
3u63PIW4PqygR5fC09U3TTe7htQkY8S3xli5RqnnnWbj0+wHWtXjKdQSX6V6
p33UolIPleqUyDHyyuZxF+ZlnK95eG39625mwszUcfkWlv3dCnJJriV1Wj9H
G3XJ3+sjrM1jwsluHW3ZsaAK6QF2H5czvbZ.P3DAeIqNs2hCaO2gApe34dgn
nB0CTo1vxbBtNTfluYJRNRw96SVIx471pB4bNeMAW9BjSQbfudkGm82ZZ5sO
+adqKZwwUE34SUC3PaIOfKpukF2vi670vNeMryWC670vNeMryWC670vNOvvN
e9lSNIQcdLJG+hPKnNscSR03iuVM9WqF+qUi+0pw+Z03esZ7uVM9WqF+qUi+
0pw+afpwGe9V6ewqFe+XF.Z1qjIBUU0JwcJttTd6EdwQw3n+0vmyDHSgNwEL
vJV.8Y1p2eakMR17adAtBO+iJZ.nUH5apqdwSdqmV9wPaTPY.4r0EmK5Wydq
1Eq65f7zsiAg3ujEEqThHyhsZyevx7jDVXl5BK+a+6q7zLEZX7PVmGNgaxAi
lX7VB0T.QWFbCrEbSoIZBRzaW1oo9t5rcUBi4t9itqRsn7t2KmyS8+k9hurW
qhisu+Y3Hivz4i5CSiNO3HZdX28yyEGLwzBaQFua4QAfLuoA3C7.mJVMWF+U
x.ly.vY8yeE0WJmhK.vyrZHN1mxttyMgnheeExLwHYgrxUefrDa.HvGTRLe2
kHk8uQMjdrtU70vrvdhYQuqcQQk77QIhw+0srBs6hnFywd4hJDMku+aFKgKs
KBMo1PJjymA94r8+2+AGMUzCHkEtJU7C.yOSjPbJ3wsQAkywc.YY8emrzREA
.Ujh86zlMZJveEGJ7W6y26Ywy.087dMn6C97ogOE2AB9NcH4iqEvaBiOX9aL
Z+ybWs1ydLpbcvmjnrshE1ia84KBAzygXILN6cb3ykE1vkszP9oxhCYucGEv
wgHEsKGbgvO2lvU7ApJ6wWsZE4kXI0Xr+vDcmiOHP+LU.eF1JVnk6nlK1V+G
T1omSftgPLlfbsbfilAiYyaZ9y.ucIuTUxh+lScr0r8pcgRhMmhKNY7zdKyv
oMpU6tzdWGzBDy7l.2.u+MDX2QjPxWkYQg8xtd0QT404LMA6jbppLyt+DDqV
nyaNaCC+WfutLhRBJvWO4sPtYsarIh4KodvNhSWPaiA8sD8DTB4pZdyIdXDB
Et4s4csz4cmHcVW4Q0IS32t23PvWRtYG8qbHtk4bjc+NZGP0IMyxbXQyXbt9
qgS0oyhC096x2oNTigQgreulcNbrUWcWbpl+OiR1QyJwnTcSkFuyOjxVw4MQ
HCSgwAtDe49Pmq9NmaZ8d7H56d8D5W6D561A2oyk+JjnixOo92759w.ST604
1in5RF72wbiw3aU9.4ZTFvOjqIRMj17F.9.CduNInE5d0hSd7n0BvvdVAI8R
ze6LDoQ4IKKQjE2uk.cR2JtO59gzLt6q0FG2Sete5etWriCEFb5ILfUiaRfA
6dBCNSHLP5KhP7Y.CfzF3N+UwQbUeErEPSnv4Lrm5XwWbKTn8Kh5yKCQcdk0
+E1Q8scfH+zyH+k1aqZWayWYDQC7PEjAmFHqu.FbB4Sv8EJPSoDCF2WwVxTC
EvAfKNtLim7JmAZqtOIfdJYk5+BYV9MXxp3GS0pxbHZBvcpIvCIf1dHs6Xgd
Uj16+hsuq0hPfntlLz+E1S4oNhnzO5dr05zn+nuxLvl6K+VAxbNkbjkB0phi
KAoBpq9ubkUD.AqFo3WWTFtAvucD.CMI.FpuViUJxLMPg0XIOhPNmk73zrrv
CvLyoxTWDru.wTt0MZPau.6hD63nDgUW6IpB8Q6GpqypJKPcFlEnVDyWk8jP
CYOI3kTOMZ.bwc6hfUQ9aTegGLQU+5s3xAdRy2JWOXRIKV4utjLNs3Z1QnOV
Sn.dKxsGAJPSMTzK+TlTnvd.g3.MU.Qu2ScRQEnAfJlLfnuaAdwMAt2PVKrT
SsZkdBYtWbbVOALq940PoCBZdKnR0Vw9Tp64jC90qrN+dprkLop7649NjiGM
l8AXtntqlONeTbpeI9ZnMgdHOBDcm1+BaI9mG7Tr.lnhKO3CVjC27PGjssMm
xiTevhrrrs77fDSnsCR8AKB53Z63hbHPKhyAyKc4xFSKAah4Vg5RfXOjkbZM
sQXSKLFx+epO5Qt1HjokoqKjatq6AypnpBiYZyJxxEicMswdVVXSmhEJAwgX
WSD1ESTvOzC5Q7bIDrsI4focwl09AAxocdUk1odKsMjQmz0XxgS5ripvc13B
5qoIwhHmKSrqimSGSAMbiJK6Hm84.TaDwIQwQIkhGb5gWqySdVzlD5JeVXld
VWMXOIddNvFt1e+MAWoPpwCoY2SyuWMp8Rw6Ew0Fv89gosKpGEyBUiYkeh7T
o+bipKHHeie3b8jIceQxjtW0aqoRrDVejsnC.8KoQg221PFHXxwpr4smwqYp
Gcl3U0Fjda8+eIfqNzner1w4n.dL93mJaUTdz5OjJghC8oDorSjFwkzkaY6e
P8AIXVRoOTuxqMRCowoEETb8Wl.YTIE+u6P9Kkiqj8pnFRqmscQcZDkTqiJd
w8UGSwzT+VliuAnufCPVmT70QUFda+RoyHMeQUG+sP9lA7sgpN1bFrcKTkcK
rZ7GrD2KCLetptOZKkoF2pyEpC5MlphSW675urpb6av6IwWOon0YNOEx9H36
tP4ZXc9BxmyUsRKw06Q55q65YfuDlDjEsZ7nFInd6UH+5fQC7+AI2tGTgCS2
s.gkvVeziIUGKb0C2XDyQlHhIe6sY6nOIf3FOB2jG4oy13q+veLZo5HV7C+H
8If6OTRuRa9PQopZrtdGkHDw.Jq2S9VOPlEyAx2D20ygxscjtbwZ7JOBZ0JT
c7vs0pk71U7HDxWIR4OWQWKUWPoLdTbl+N++dkMXFeppBAZshvpjnxyK.a1B
5ZDdoy8NVHWtGJr02SE+zBsDtdsMiBoqMZC1p.pN.O4AUOZcSYs8vle3C9o9
GdXRpMffnvM6KBy5JWpOpcQqJq4FyV5lSqRxN8rTJQfOfnba4eKrgUu.Ijqp
F1S+K21tczZNpzchAZ7Zoww0TTHeEF6neQYEl7JsToMo5CzgQBSfdUiWVlLF
zjka8y3rU4IJrwS1piDprnrRBy8KblSRu2e5DRiKpiKY0.c6+31+esfVSQC
-----------end_max5_patcher-----------
That's pretty cool! We wrote an operator, we can see it inside of max, and we can use it in a codebox. But let's not forget that the end goal of rnbo is to export code to another target, so let's very quickly look into the code that rnbo generates for a cpp target.
void pulse_counter_tilde_01_perform(
const Sample * a,
number reset,
number limit,
number threshold,
SampleValue * out1,
SampleValue * out2,
SampleValue * out3,
Index n
) {
auto __pulse_counter_tilde_01_last = this->pulse_counter_tilde_01_last;
auto __pulse_counter_tilde_01_carry_flag = this->pulse_counter_tilde_01_carry_flag;
auto __pulse_counter_tilde_01_carry = this->pulse_counter_tilde_01_carry;
auto __pulse_counter_tilde_01_count = this->pulse_counter_tilde_01_count;
Index i;
for (i = 0; i < n; i++) {
if (reset != 0) {
__pulse_counter_tilde_01_count = 0;
__pulse_counter_tilde_01_carry = 0;
__pulse_counter_tilde_01_carry_flag = 0;
} else if (a[(Index)i] >= 0) {
number floored = (a[(Index)i] > threshold ? 1 : 0);
if (floored == 1 && __pulse_counter_tilde_01_last != 1) {
__pulse_counter_tilde_01_count += floored;
}
__pulse_counter_tilde_01_last = floored;
if (limit != 0) {
if ((a[(Index)i] > 0 && __pulse_counter_tilde_01_count >= limit) || (a[(Index)i] < 0 && __pulse_counter_tilde_01_count <= limit)) {
__pulse_counter_tilde_01_count = 0;
__pulse_counter_tilde_01_carry += 1;
__pulse_counter_tilde_01_carry_flag = 1;
}
}
}
out1[(Index)i] = __pulse_counter_tilde_01_count;
out2[(Index)i] = __pulse_counter_tilde_01_carry_flag;
out3[(Index)i] = __pulse_counter_tilde_01_carry;
}
this->pulse_counter_tilde_01_count = __pulse_counter_tilde_01_count;
this->pulse_counter_tilde_01_carry = __pulse_counter_tilde_01_carry;
this->pulse_counter_tilde_01_carry_flag = __pulse_counter_tilde_01_carry_flag;
this->pulse_counter_tilde_01_last = __pulse_counter_tilde_01_last;
}
It's pretty clear to see how the generated code implements our operator code, but we can note that here it's operating on a block of input data, hence the for (i = 0; i < n; i++) { which wraps the main operator code.
Now we have a useful pulse counter operator which we can use in our rnbo patches and which will successfully compile and export to a target. Hopefully this gives a flavour of what's possible when we add our own operators to extend the functionality of rnbo~.
Another thing which hasn't been touched on yet is that if you are really observant you'll have noticed that operators are not the only type of rnbo object! If you look through the operator descriptions it's notable that objects such as [pack] are consipicuous by their absense! It appears for these objects which need features such as variable numbers of inlets and outlets, there is a different way to create them. More on this in future!
I'm sure there is plenty of info missing from this post, so if you have any additional info I'd love to hear from you, please let me know via Instagram, Mastodon or email!
patcher_names message, which is triggered whenever a patcher changes (i.e. is (re)compiled) means that we can quite easily use javascript to automate the whole process for us.
Using a small amount of javascript we are able to find all the instances of rnbo~ objects in our patch, determine the name of the target, and trigger an automated export to our target. Obviously this could be extended further and you could add functions to export specific lists of rnbo~ objects by name etc... but this should be enough to get you started. It's definitely already saved me some time in my a project current now has around 20 different rnbo~ objects in it!

The max patch and javascript are available to download here. However if you don't trust the contents of zip files downloaded from the internet, the compressed max patch and javascript source is provided below. Please note that if using this version you may need to reconnect the second outlet of the js object if the script is not detected on patch load, see the image above for how it should look.
The compressed max patcher is quite large because it contains two sample rnbo~ patchers to export for the sake of having a fully running example.
The javascript however is under 80 lines and just uses some basic max functions to find our patchers and send them some messages. One thing which may be worth noting is the uniq function, which just dedupes the list of patchers based on the patcher names, as it's quite possible that you might have multiple instances of the same rnbo~ patcher in your max patch due to testing/experimenting etc...
inlets = 2;
var rnboPatchers = [];
var rnboRemote;
var targetName = "c74rpi";
function setTargetName(tname){
targetName = tname;
post("target name set to", targetName, "\n");
}
function bang() {
if(inlet == 0){
this.patcher.apply(search);
rnboPatchers = uniq(rnboPatchers);
post("Found the following RNBO patchers \n");
for(i = 0; i < rnboPatchers.length; i++){
post(rnboPatchers[i].getattr("title"));
}
post("\nStarting export\n");
triggerExport();
} else if(inlet == 1){
triggerExport();
}
}
function destroyAll(){
this.patcher.apply(search);
rnboPatchers = uniq(rnboPatchers);
post("DESTROYING")
for(i = 0; i < rnboPatchers.length; i++){
post(rnboPatchers[i].getattr("title"));
rnboRemote.message("patcherdestroy", rnboPatchers[i].getattr("title"));
}
post("\n");
}
function triggerExport(){
if(rnboPatchers.length == 0){
post("\nno more patchers to export\n");
return;
}
var obj = rnboPatchers.pop();
post(obj.getattr("title"));
obj.message("export", "oscquery-export", targetName);
}
triggerExport.local = 1;
function search(obj) {
if (obj.maxclass == "rnbo~") {
rnboPatchers.push(obj);
} else if(obj.maxclass == "rnbo.remote"){
rnboRemote = obj;
var tname = obj.getattr("name");
if(tname !== ""){
setTargetName(obj.getattr("name"));
}
}
}
search.local = 1;
function uniq(a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i].getattr("title");
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = a[i];
}
}
return out;
}
uniq.local = 1;
----------begin_max5_patcher----------
26449.3oc68t2biiijun+cUeJ3USbhp693RMAeqMNwF8LcUy8Vmc5GQW8r23
F0LgVZIJa1MEoFRJ6x8Fm5y9EOHo3C.PPIRYY6bhdJaKBABjIPhLS7Ky7+90
uZ10IeNHal1+l1mzd0q9ue8qdE8iHevqJ96WMaq+mWE4mQa1rUIa2FDmO6J1
yxC9bN8yu1O9Fsrb+z7Ls7aCzB97tjzplsyOe0sgw2rLMXUN60sXt9UZllNj
efr0I+v.+uZ+yhuR3ZZ+lb8u8VCyx9Id+1v3nfb5XAc3CS1mW9o5jO7+yqeM
4et5DmT+udq1udaXl118Y4ZWGnsJINFOABVqkmPmkYA3OZsFcLokro1LOHUK
aUZ3tpNMJD+US1GS6YSwzEKCFsvaAixXR9gkq.JC5Qhx7gXMesnfLLyNM7tP
+H7z1e6tn.s6uMHMP6gj8Z93ed89vn0gjkFIaCz98PLwBSl1lrdejepVZ70I
ZYOjkGrk9M1Fdys4Z25eW.o2SnjTJIJHM6JMe7WFSfennYX9Q1tz.+0Z9qRS
viDbOjpgG8ruh1ljTberBOzxCxxIigc6S2kjEjMW6+2aCh0VGbWPTxN5niNF
xzhB+8.76.yxCyeSl19rfM6iHL6hwz89OP9KFKVyOJh8RIyiuTMR07wC73UA
ywKdBvqchxCeewZh4+VVwxBsU9wZ2FDsiNww845D5KdtF2ELt8rQxlslgtzA
oKXeDZwizpk++vSw3.11FFkLlRylmFrMIO.u+gQGyS1gWTg4J0XgzsYLRF9a
uYe7p7vj34rMl3+izu3d95jbV62f+6q8W86ZaRS1R+1XgR2f2dRW9foyAX9.
d1GT+Yw93UmeU3FsX7hNxmuNXiOlw80bYFVhYFlFFTV.xhxPbn+vQD+v8Qje
v1DcuebNipjkml7vgkzGVLy10gYXXd0pcgQTB21BFvuQjLt0+2CHR8wq6waa
CywiY7V7Tp7x6BhCwCA9qps6YUsg0h41UGO3ZIfLZOwjwsX4b92DzgLVPz9y
QQRNmCsfthvTGQlJNH57xPvLQf7bCtSkxIH6ixeXW.6MNal1+7HlkWuOOOIV
1LgsZ1zjs1t5e4NSzkexzN+T7VN7NwkAw9WGQG45CaVRz333lowA2iGicXm+
VVGg0JvW8nZvXXpKiwtvaj3qEOJCKDc8RbOiGNK8ywmAi4dLE4dU0j9Uy1f2
sRDswVEyetIfWPeFktNhBcZdRYgdUzCDxCwyU7YiA4+ewURggXFgskMaYYMM
II+A2kkSs9RMWZcmeZE4mNy+jw+rCcgQR9NJEPa0CqhBRkrryzhtdylo2rtm
zUcCXxZHZQWV3Mw9XAbZXFRV9gkfjg8c3CHvGFSegn4lyMZLvwyCNqGq8cPk
q.82sq1G+pZeEB482Rncj2UUeTXL6ibp9nzf6BK+9VUepeJl7kioc6SYLgO6
XM6P2jrNHMdeHcnv9PLitXHQYoDlW1N+UG3fUaZpejOiIX5xN4egI6LezAlB
dUyMXUQ+8f00jygo06vGNFi0gMCuewOuX3W83BcPVtIINOK7OnCBDQnCmmuo
XPx8gUqA+a94GlA2jFtNIlLFZvKHeb4aCKkylsqp9bg1hX+cb9xLIRBdH15v
78YW6mRXUExYLJeXdRRTyGU88hB1jW7Xrx5wsHhXcFE+vThRNheLVow7jsx5
a5SxVtOl8zk3UE4KIRea1NrZSE67a18e1ONbqedPdHiCXnW8Plr1awp1lf0e
n97k8j637DrEKgqBtObc9szWT80BLcjK2MVwkWGdSPVdyOK2+lrleRV9CLhd
sOZ+0E6hWhMMZWDdVzrA38GXABY2lbeVQCKWmcftPjowNTntbsVtcn9185xW
a74ROjoo.0CFgqkEtN.y.0niRpxsq1mlh+hQXUcuyOLhPoKLBfop6+ZueT3l
PrEEGL1+V+z02isls9aimAABOgx0jIgfcDESGPGuZ6oDYnlPo2LR09qwC+cI
grQg9bzhNOs82o0QbcZOghQ8PQ4CX5.7pxCAOFlE9cdcP5WpOmvC4fz67YKu
0OrPV.Ezgoc0BqCm8YX1h9QDmWWjB8.G9hNoquCuo4dnV7.Kgrf1u.thWEdB
a0WqsH6dN8cSTh+gieqNBdIdUepOGc.axPjRwIOLLOzO5GJHgXZI9CRRmUuM
qCyvRAdnjLiOLOEKXopAUGcVMx1GG9u1GTPRKVCrrCok1zrfzP+n1qUoOp5f
3FKkFwUmxDkDS7NAyv18qCSH9avO5d+Gv+XUd3cTOWPch0AuTgUvIl3DrXse
v+yZu6i+L4KkrYyL4qvYKssrnKzszYKwQsVhSFWqRhXJ+7I7ZWyl+uqT7iPh
E8XpjjGNJJeF3F+8OnUnfAyQcYqv5X7EpXa15BhD9j6K8nVV.waj9DG4cKVC
ShtV2nITFtob9CxipdmoMU3MxhZ7ms94mA49HyfZXgSS9ST31vuzy5blyCLc
X1rYwURdsYqyQLaQJHPsibzv3MIhDcRbwX1Ghw5uFuJ3CEMDUW1HVABpsB06
g5cAQB6t84nFOuQCv8AVD9Ok9yoI6Z28MUi6CjdZVyGGlg0Zwu8Q5zdk74k5
ZUeVSOCDSZZ48kZiF+U2J3g3SBH9LfySVGfMmYEVWw079ZDlGmIWI6oj4T6g
0OUgwNGKx3eMLBefHV6NVe9jjfxVceRTzqeXWwN6Skf9WX8znSGQxninQgNV
QtXGiLq8WkZJ8+oezdVqzkPOWu5Zhk9iAA8ceuFqudgSRiRR9c+aC7WOFD0+
VUm8Bfph0yWBckb4oa2MFD0el0SuvWmtKAqgv3PPK6pW3Tz7aSCxtMIZT14+
qUc1KbpZZPT.ddMFzzeonqdYHKUFQcElPjNFjzum1QiBAcRoZzqeUB83V+33
fnAnYogXRRYewkEgs8h6zaZWCp+nrFTlh762rgbGZ+QvXPx+KzdSi1cxn5Fu
.n51HCIz8R+gdpTbpuWeJRMCvTwBmqPbnFQ3k1Lx8uDGDmG1xWKMH+zuYOD+
5cTClvq4yPXNZowcK0kmT4N4BuxH234A4IlaS3YO95CW9aiIQCRSg2MTcrW5
KjibvK2+GpMfI3WrZ7z16eEqR+oMBdZSG6284I6BR8yYtRswkUT.UfOPGduE
gLLs0srMZz23SLvyzel4U9O7SMmHcupBUuDklBYsadkRMOzC07QUWaB2Ez8b
4IThyxN9CcXWcBiBet7DbbvWzz0P583NXKGF3IzY2IJpO+AaZnz0xI9l2jbC
amemBmEbC4xNFE2u8qLjrdGQxoFA2r096uEKVemFADBZ67CSORmzQw63rG4S
a50GxTTJ0ygJx7eWA87xfcf3vNJDnVs14xUyfolWUhplS1J1R1wyOUc6kEb5
tS32CvCfQxuW+G39RifwJszm7t+55jjn9nqa7ixBjeeWi1B7jWtWxES36nbI
WjvbihtCpL7a8yz1DFGlcav5moD2d7yyckqkOU6e+.CBRrSHm+h2UO5Sf0tB
TYP5wwihFMCyn3q5elv4v5dnum3o8SjA7Gqw6BDneJTTlHRYBAFAghGkaEvl
RJwsBseZaida+bUcqfgskiiiGx7T7qPKq3wikkcshcnlwSlQmGy3+lung5EO
WF0v1rY+FviNBC3OJDc0j3+METdzwS4+lIlrWOZg1lrlKGgdzCI9o09NLkCu
LUWG+a9eVi9KLYyJxxP5tjHRzvbQe7LiEisSWdbb2xnoqxGwm6EVWekW72Qh
dqFD4ecPTC859O4QmXPJOIcMKVwLEqmYbR5VL84OBVOPtHRJWjsi4PmOWaJt
50yqs7xt4+rwwLnegzQZLgQ4IR2K7D7tqOrbXLnU+XUuoUEos7sx4BZsV+1E
16hsxyxNUgs+HI2LTjaUpHfOisOL6gsW2wyQGjm9yUqgHDlVMaqe7ZhxsOTK
lV4waJuy1Sk272yn4xCR2QzpdBQS84jCvwN3Cz+2imqrCyx54zLGIfF.Kwb6
3vB9AVWcJxdICmYOWz2nfdzCuAIk2bF4KRtgmsbmIOKUR7zYZ9edz1Pw5pSa
Ck+mm8TCZYB4MEjV47FCo7lyHeQ1FJtyjmz3AbxXZkvTYTLWdkeT.iukoUC+
KQOL6EAnMEpJQIMVNWxULWJKOX2n.w12EdW3ZVJQioAG1lNrgc9kAVKV+a7v
ZUJVwSM1K8EsSM9HGRPa1lmDPlvZ5ONRVH8t2+y+x6+9+7u9928uoQzHmvEi
RtGy1VgI8Zuo30QLH6MX9Jl+c1C+oij+gNNajdGaByrPpW1vXYn5elkz6Hoj
RZZEglTAKSggGL8OLlZxzmyIILkHse4G+K+jF2ae44fcqcgyiR7rdzCzRLOc
ebX93vLYymhji3pzvqYarHufNti.Xp7Yp+cB2nGlosDbajbLthSRfjPxGkjL
TrOYWZTa+wQckI9oGdojsuamO64r+j90DsZdnDqRRZPFMSp0CaSWLaij4UmL
FWLO2oRUVgv57WkuuKi84MG7uRRzsGCObgDfmU1lSky8SjNhHd79aCWca0ME
zhAkoceXTDMkNSRVrXUP0RhKSpNXcY7WyRlxzgEM0IiaJ0oigqHXQgQgv+9W
8FZZI5MzjByaHHI7Me8KAIvhUU8WvbBhZITFQeapM5UYooZgQ0BhhkBjL1l1
9czCV0HNjkblKIAtcngvRBoKIdaupZozRBI2IKdu55vQReqOFPyi9i98H9Xw
iTA8u4oclk0LwjPP9P+5OIQW3U4Qijur+v69.IeHlSxHk3caENE.elawGxxY
9UaMm+Lz6.07sFgZ78GnF+Hs4y6aijrqCJH2ebLZ4+8G+oejpzJ9qfUEHKOk
jzyHbI+PZ9Oi7pV6m6SMLceVf10OTupUPR6kqCdQayxOTPg5ie5JC.A6FHTK
j3bmDZQIve8Z1lrJKUnFfReMZe5fGfzdq1e+iuW66Jdhdo+d9myeZ6wmwHZJ
FOlxeVB2X9K7Ct9P7pn8qCvmco8yLBdOajPiOR24o.gx.D+maZaxTik81Ha6
HG0Jglso.F6OMo0G6.VczbcDXcmsyUDV269zl3St6yUEq6KrzW3hzWXcNhg9
52rXqfjusngl1vz1e8MNBulv8FnBtzevMa7g2PiOts+Fa9zpKaqUxSttYPMd
2U5e25KTh9plceoRfM+zt9RalxoMfVfHeXncmsZZDA79lnjl46CYXS+P90sm
vIPWoDDqjxMyPC2fSHEbWC0Wy9eEi2D7u2bAvALrv6wGVfqqvR4BSo+qTSAJ
o+9CMecurKM9XxW2movUIKJbcvWHgBgN8eTLzUX4dBjduwthgmvEalSYrqvl
WK6NDFFuf0MmIdwta8yRRUN9gPtFpk.PLbOawOjpaqKy2qXgUpt6pf5rr6LZ
fBoY8yYhmRKhGegjj5UJIeiroI4aGmd4olSWRc4j4tz5lvtpPuqYt4qHg.Uw
OH2666XVK8wv38YsDIuN3yLQwoA+KkkDyn5K6RnF1REV2b9hfs6Rh123l7aG
DarFvhistAwl9ba0RcPH7hMRXrwpBXRWlY.gwFDFaPXrAgwFDFaPXrAgwFDF
aPXrAgwFDFaPXrAgwFDFaPXrAgwFDFaPXrAgwFDFaPXrAgwFDFa.SEBiMHL1
fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1
fvXaJCistHIuEx2MNyQxV1J7JxuzC7zWPiABSaZDsrf9udFs.m9J+nUqR1yV
DYX6Tm9RBsfxBMLxvqwipCp8iod5nerK5kMf6NhUkAynmK6NcFX7MwXKmmXg
ASMUI3lHrcKDMLFsL6K.EPdJELix4kSczGDFiFC3DyhKGleRw5cxlPZ2GlSb
t55fOqgdRVOE6Eb68VqJqhRnSU49BRpLLB77sdJpJBhWkrc6HAOH7lxr.sj6
HtwsnWeN3+AvcOOmb2i43ajJ4.girLVN3CAZg3nYmjgVRrVThwMjC9EZaSmG
1TCkNOVMKaLcsrVr.YtnO6ZPifcM0jHJRwdU0qiLcW1UwlgoUGklMh5z0U17
Ak5HWLG63jLM+z.hDnrP7NmTBDJHoOAhrncXUrx0xn9JC2lPBPKzHZVQVBms
KXU3l5.BkF0vGzYVtBitMJIiHWlIDszXjLbWkDwV07IrUYlM+eWo3GgjnIp4
wnI54NYgTFAvtH0RVHHDjrPl5jEBBc7FpeVSVHeipluYX4pVosEo+nVZaQ5G
Ok+aNahXI2lSSLGx71aHURK8Qjw2WXnlY974GqnTl+WLnBTQtzjC.QrpHl2B
kDMb89q6pZ3isXPSqxiITQHnGHCbpkAJSaGyysHPYaFwauv1SgWIEDdWUjsb
WnecXLgszhppIQ0G7tysDnLUrAln1Naa5wtIsH6+Xinp8Xv9CKwKdsGvtTpd
ZL2VaLzsvcZOQ3TiPMZxyeJjbDy9f3UOHIEpT0FVVTAgJxiJddU4QECC0RVO
d1TVfqaumxAIQEHIpbjf3DRhJPRTARhJPRT4UPRTARhJPRToiQNmL.mgjnBj
DU.lFjDUfjnBjDUfjnBjDUfjnBjDUfjnBjDUfjnxowLgjnBjDUfjnhP1FjDU
tT3fPRTAxXFPRTAVR.IQEHIp.IQEHIp.QUCjDUfjnBG1BjDUfjnBjDUfjnBj
DUfjnxKgjnBW3jOrXS3wphfSiwOCc0BuECH7Vl5vawPxRHqiM7Vpnhz3Gow4
MGVVQWdPdN+EWYI6SWUxWJBDGslqTVikcGF6mSbH5g1gXsSok1CcL3n5XvaB
GDlFJNJLIjBzTMJPpRKzYihZMba3ZZ.8TrxvrHjgXkMbScVMlt4eYvBKXVfs
XPi14oYZoq3zhToumLVrwBUYw5S4nvaHKzlrQgqpbjobSGoZcq9x8IaTn5Ve
zjxQTkgXNgCBjpxeJEWNMiBUEWz8jitf06wYjYIdjgdbGYFcYwsN4vxjbdfo
IUSRj9g+h+7QeZlOpNcDtN7f9REdjY402zHofnahLcMrsVniV3n6Qx.Hnp+z
0yxDQ9HCDx0wxU21yyvztURAYViNbgwBKjwUM9MSaxu15aEDEPh33lonDaWa
KcWCGGGjiG86ZirscrWr.YoibboejmCx0yw0yv0BYa41pe8WspS2ZYpaZgPd
VHyEF1ztU2wvT21zDg+Axg0sFF515ddHSOauV8Jws36BZzqF1dlld5NlKrsM
0cKlnVF3QrmtgomoEa7iVfsVdgmkkoitUqt85a1DFEQ61kUNTf8V30jQm00o
yQSZuaTQ6bLK3u55V1Vz9R2zycgqjtvO9FlMXFtGLOtQK1klPtShhcGX9wBt
8y97jaR8WGFTF42rsuk11vBaxOTKzhK1jN6C+T4mR79ZyuDyju99VsEIHLIX
cHC+cvtxY492zxlslIhoZ66aN3HFYQrzbk+paCZO9JxbpkxjJGb0bPaqbq5r
vr2W3PhJiJIFKmEvt8n5ClJCFmsMYsr2A4wm1K3fyKj7Z55gig8xZRWIKkx7
uqtK2mgsicWVgeHqSlwK.CpX2euq0GKaW43uvEa0uGAhw+Io0dP0x9C4yzht
odHngOXJ7Flijnr9pzIF+HVaV19qqdveGutaC9LpJtwr.r03MtokNSwCD5Vq
kpsRuJHrwxDecamIzXwAuuDpJOwV6K0kgy6qZXny6KubIy0HEdtX0Cqhp74v
g0TMntclsq1mRRuEKqSONrFB+jzvlNon9w88sdPvRBYqJZrvfv3m0PYkp0DE
S15oLhxAR6uU8kF0+7NKJ3ttnkqrZu53UhR2BGViz4VcZrRQXGfpkWga0AbV
0Hrapu1oSGIaETaWkQX1oAaDlqJDvzXewFOc9V+OSnycZEV6xaos5Ke66RVs
mbpP129Cj77w2VtzJq6WJIi4P+Ftmtn4jFTL47tdi0lfMqbVcstavl0K7Qda
rW4aiBbWu.4WeZ+5W05XnNhMIhLWS7RXvpbNd.sThYxt7vsg+Qk5ty9opHig
quYqjOseewv1bMQoqUtuMXsiM1xt.+25Q9S+UqCr7z0QHrgu7FaUCJICO5pj
jMcEKbXrEFeWXVX66jsVChRhuoA68KeBY9Omwok308aSVGz4XphGi4Wo4s5I
GtcT41YyV7lWW9uLSWnylZtnmM6jjaZZjWZpepyg7QCKmc8c4gXUkzRC1lfo
uAe1e6tnRkK36aelm5Xt32ikg0pkjzq6Xeqx9gWNmokC6sZn4V2Dp1U0+MRV
B5phiMqdwj4SMwvyPyMmazXlb.4i0UCn12oZMq+tc093lKn15+aL898NbfI4
H.ZLiW8QoAjEaruekG1m4mhom4Xh49TFq4yNVypetaPZ79vZFke3vuJuxmsy
eUW4SyNvhP5zLBDhwoPFVr6gwdQSc4iNbAmUZprKHNLlJHMNuZmtdayV2jfk
lUk03my84aJFjbeX0Jy+le9gYvMogqShIigF7BxGW91vSNZNSCUOAaxZAVLI
muLSrlfGlgmi6yt1OslbgJk8xSRhZ9npuWTvl7hGuKLNtEQLGKoT3CwG6eqj
u60I3GtUVeSeR1x8wrmtj.H1kkZ8dnc9QQExCZ18e1ONbqeN9bRFGvPu5gLQ
22lshfKtFyW1StiySViWkuJ39v0rS6zquVfDzM6pNsnhKW6x3OnHr+MYM+jr
7GhBZ8Q6utXW7x7.rTJ7rnYCv6OvBDHHONqngkqyNPWHB6X6c3IviRgDcSVi
QFBKy+90A46iCjjgvpZSY9.SetqMKagUjqvPByca1xuXVCuxLNn8UZtzLVqm
t3Kl0BRbXPhCCRbXPhCCRbXGAsBRbXmlvVHwgAINLHwgAINLHwg87UIQHwgA
INLHwgAINLHwgAINrmBmWAINrG2vHERbXPhCiOOERbXPhCCRbXPhCCRbXPhC
CRbXPhCCRbXhU0BRbXPhCCRbXJ5c.HwgAINLHwgAINLHwgAINLHwgAINLHwg
AINrtlAcQk3v3hx7VY8IzYNwg0Kt3uKIbUCnUwAW7r1ThKd2hRnMCT7tGMn3
Y4dnBPwa0Kn30APwCfhG.EO.Jd.T7.n3APwCfhG.EO.Jd.T7.n3APwCfhG.E
O.Jd.T7.n3APwCfhG.EO.Jd.T7.SE.EO.Jd.T7.n3APwCfhG.EO.Jd.T7.n3
APwCfhG.EO.Jd.T7.n3APwCfhG.EO.Jd.T7SLn36Bw7Vfh23QGT7UXdmff8u
f0XAoq2S01FoaPyj6FHZw3v0ou5ss4hwFA6GpLGmebrmEbCs55LFnS8WoWRQ
wIGj6pp1e+sXd+NMRcS.KqKL8HuyWZQi7wFML8hcZNX9qqICR7NVPV9kC6PB
FXpV6b4pJ5TyqJKDHmLH3KYGO+h9CUC0fd8JsXtvuGfG.XJ3XvI9Ov8kFwyt
Zzd77RwQme6u5yPW7oUi1B7jo.M25WBQvPwQ3RDTvt.+QfR9WvpRqc+sAwzE
nZ25mosAqxX1sAqelRb6IDaFsPw6C0uxf4OiCKjiRh6n3LEApLH833QQilQ2
7eNGVqrg+G0o8Sg+V5JPVXQbVQA5mBEkGFmZJDXDDJdDtRgZLoPWoz8oMM6s
6yU0UJF1VNNNdHySwUJMrhmNVV10J1gEc6rYzklg7t1dDS2MscUyNdCWvNdv
NdvNdvNdvNdvNdvNdvNdvNdvNdvNdvNdvNdvN9mJ1w2zJ1gcg7Wl1waizo1w
a4nnc71fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7OYri2Vhc7lO
Esi2vzlYGush1waA1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1w+jw
NdKI1wac4XG+2p4LWMC3MbMo+XAo.uI0.dyiv.djzR6VGy1UNyPTHWRYN32t
r6TXXQEw2d9p2eaCWiOY79a7CikTw+p0pxZ9m9baVU+SeNqr+glKrt+Yn1pC
c5pCjGsx+Y5JY4gAT4+fJ+WOZFAU9uKE2F.U9Onx+M8lfBU9Onx+AU9OYmlA
U9Onx+AU9Onx+8jf2.U9Onx+AU9uyKSBp7ePk+Cp7ePk+CJRbPk+Cp7e7fHB
T4+fJ+GT4+fJ+GT4+fJ+GT4+fJ+GT4+fJ+2ij2AfJ+GT4+fJ+GT4+fJ+GT4+
fJ+GT4+fJ+GT4+5ZFzEUk+S.XyGVJJ3bV6+9luzWfMnSSFAldrDMXuYl.j2X
GXCEAAT2vanAk+aXgTRyW+vBJgu4BrNMvBmDCEK2hPUZPvYHPVg.xJDPVg.x
JDPVg.xJDbWfCYEBHqP.YEhmNvDDxJDuBxJDCSfNjUHdZjUHjUjFrezSJD0S
s.Y92yOkBngeh12sKI5gc2lD+fliZNYAYiH4MBjgWeF5KN8OZNtNYYpMxe7T
9Xpv9o9YTq3S2dis6yGEq9n8ykeXkSOTVpEBnwfZP6mmvQNdu1.bWR3J7j0O
e+n3ts5c2S9EQ9qxCuKfMkFChSi964f3JYA165vgfGJIxiX8zyYuLICbK4AL
HOMFjxZ81k+dSEWhEFyBCjwaoVUO9LfFMZIgFdfK4IX1loRw7wv0L+bkV9Eg
nT19qKfQ+7WBgr4aQyjGSyYry5nikQIxYeOsS01QfABom0JLNMqjCT.9x5.F
IS6qRhidf3PlPBpm0JWCDtpFCK6qeA.kr9bqddXdzn3zxekzQc2U7RKQBsN4
GSx+3sI2+g3ev+yX6k2gs6OouzqwlvwgIPLhWizYDHSRBqmY.EtlMOEnoeTh
XtaSvhkxnq1u2mfwa+75hYXZ7q4mFb.X+qmq8lrvs6hBdCV.Up1aHJe7lulE
PN96yS15mGthj2FHQhSBg7T1ODzj+OxCiWkrkfxbJD4Ip1kMW6iXlC4yByIL
82rOKHsn6iShCJ69r6Cw6Gw6O2v9x+iblzzhWDVfJM9cvlwkli+F42RCFnh2
99XhGBV+zTbYIZvn98hP7I.bgPk5feip0czuROBVK5K45weHlNF27PvbZdHn
9Z5RbnOG1wWoG7p.ro2aIwaw3Dm.E83WzRt92vifLMxk9POti5wEMBVB07Ig
X45uP3GkY7ix3rbcHwIv3c2eEYCFIHLivzi3UO70WQtEI7d6cAwYzz7FsEkO
9J7dQ7a.+wUaLSxusTgnmppwTaeIQDDE2Y0HAm1tSVOJwMOo2LJZm9KA6h7W
EPtKQMReVkOJNnAzUZAXUVeHmbK.zbVwZs+D5Js+jgVP9p4UAlaJqmJ3wjtv
e0pjz0juEtm2SdEun2bOJ26aXLRvE68oxq16foPENKrM4qF6ua20J0yLapu9
2vXiwcBY7HOg356OkuQate6gMFOw6wFcrWjMmuaOz9iJBH5XUViKQlX.grPf
3Ht+XOOuE1nEdmiXenTrdKYds0En7.mWK19.5JoFsfq2MZLs6pwmxWHdqKWd
XPYeWmKbto9l0oPDiCKXOcu.2c6p8H9Wg6V+eiwv8ZmTOYerSKcvtKrr+rZc
.7paCwjq78orIwmcrl0rKwLqz38gsNInov+JBA9noUA7WBW6l2ccow0fNM7F
bbn3s21vn4Uu+pYWeSzgsVsVwGiOug3Yz3bpUScZRgpHK2fWHjE9GrCYMlKr
MaJF2BaPEa9ug2q0XhcSZ35jXxXpCqj7nx29mzP1L.GzddRaUr+NAcRgZtha
.6R+t1OkvkKzYvnY.+jD07wM99QAaxKZxtv3XND77jcxaPZ3M21SebcBtAa6
68PeZFdWIqEKIPucYl+ccYN4XEvKfJR2W0m8iCwlwGTBHaC8VglDQ2payVQj
Qzgdvd5cBd5Z7loUA2GtN+VNA8DKIZsqbM4rVQ1U0wLMCHKel7x1Ah0CQbh.
rCpztLO.a7qeNmFg2NhUwfjcQxJZb452lzvR2N1RxGkDFzywsMQySmm2Gpdj
DSThfrioNEjNllKDWvWZiZGC21crnXzQN7cTFBOxiUptCGwGxnPLS0VmboJ2
MpLr2pBCCYwjz6XO.Fl0kAC6sbpWRGIC6sWBLrLR5o8KZuEogHgA2.1twNm1
iajv0g6gjx8bNWbO1rcI+gzv4frt6BfKlDGrKgvG0muXgB7PKlrxRdnsR7P8
Kicfky0k7GTCmKV1gW.7wuEqMpq57OjCaqnmJ7Ozhoj+IfyIy1vtzJN0+rtt
GTRcPq6Tb3qF91KfkAzXmWCoaMfkBlL6kTZqLxQ5RASoKEL6covUbpwYB3az
Y5R9iogy6X4bfGe9G4xcwV8lgEG6Yq.Ozt9tYVIoqWVn7STQSwt41HPm2F3d
QhtXznyo6Jbfbml0ocxANE2.Agj2hvFndSPpFgW4eS6n4RtW760S98fGzd8n
ubu52qm8kA3Q4PzhmHWZJ5fRjFadwOv5VVb4TdYbgYT.Rz4Vs6ElJiMOCc94
YhvPNGjRKj1vOrzEvXovJX+N5xfQl4VuqGc9zTyH5YCxqkSWEb0W7oo0twnN
EPLNikwR3F26dQxsWv4FLjcOL8LWKEnbDqxmTwGCat2LH.KN3eVmC7pGKfhZ
TqHnSTyDcyNcucm2Z5fz8bP1dcdURuiGEU5tbDtjuBICW+spo7EheQHdDwvE
+ullZn41p6bjBU4PHkTG2aJcNhxtEYI2gxQ5TjmP1RW3MxAYKs0ydSo6y+lF
OMrjN3y6R01kb+WY7O9GWoEFiz9Frc0eM9e654HwqMLzY2QmkRllY9xvxLNg
xxoqvHq1JS.QG45KCnvdkcg9cJ0xYSsFknysFkW003WzXSf6.JpmAVLojUtc
CMKAD8tQp7oS06BenmANWPMCUUltSDUOb5tgT5NsOew4AAoX5bH35T.mpPyh
wjQ8qEFqRvX7dF3cHEAGxAAriNmO6YKWpajhw2UNchVLQ9yweSvVeFNTFSdD
IpFH8sFoyI0LghBsTl1WEL+l4+aZqCY.JiTVEzYXmlj89Wk5mc6WCLPVli+b
4GIzr9EJeDZJ735xHAmiNn4kf93D75CuSYZ3wGtMnowRbax.7zCZggNxzVG4
LVt54Xs2r86ut3HzPMFkPVVx0npg6XBJI9B3xceXUTv.v0lgE81AsLTx1ySA
WaFJBRiq5AtFCckCMAOrqJsg42civ062r4.XEmQpvnuiIc8igw665hzv30Ae
lcnYZv+Z1PW0wXQK4SPOhqklwwe5ASnxUfL2enFLgLst.gIj4H3NqKHXBUhO
DyA3Q5gfODSyKO7gXZd5rvKE7gfYdFp38QGcBzXMofzyPIGSaZHkyo+7AVHE
B3GSCbnzIMw5P9BwqABI.a8iWSTG8AE8S.1X9sE05lwjMgWxhsDM4Nl6Bnug
m6dvQEz4ztJJd5D5oohJ9xvAaxq3hz1zWUWrfczrpPYdR9O3LYGtxQ2b6Ubr
j1MMUMvDFSi2dpv9NmX9pdiA2S0t91wpbKq5473lpavoApaQOx.s.oi+Okvt
AZBLnul36ND1ZErqgZbUo9bFmtgUWJJyoTXyXwh3hAnKGBzkCzkCzkCzkCzk
Czk6YktbHPWtmU5xgNceycQnKGuLTD+xDhm6A04rzUJ9Y8N+3BraIwsfiYLB
.8sfX8nw0ZT+W1i2L1JaU7pVEBlhlTuZv3p8cMxSijjxox5xufcibtJcwbFm
RHWNfKl6xQsdd0SlSGIc7psLmcrzgdDTXWdsmQjljcpAMmNGna8nYLn+mMBb
6RLxSdTfdY.yyt00FADVg02lSm9JrV277gLq3pWwEJmSmJKtn47LTNrBhX4T
XcFAgr7TD8U.R8apwhfhwyoS9EUXddRcRmxzQAEgkS2oSpWPVdQ3y0dJXKB3
N8T3VNctzYrHt7bhMO1v1mSwd4z4sJU3WdQciHSTLwzsHwb5LudJXL.WQMiM
3VXYNctCTjYdNrBT4BPSy0qcKyE7E92sPzHQWVQEjlSes5QTbZ.oK8xyDVDa
FAF1SlBZyyKY.pTraNNIAcJ5MhbcSmheyouZ5QsP3.BRNka4W4qwWdDedz2h
O+ts8c0StVMgdD73BYzqFxT2XZl5p.4z0A46iCdLl1B8Q3fBGVg8xkcj9xqf
8n38sbh.MY.bH4WHw.xxcx6nS.VLB7GfzxNDGzwr6D.GCmRPjPnwLEwsrfxR
DOM75VZhD6gtNSZYEfHEgaiHTNLbrariKJG5Z3QahkjJTDiXxuJEwEf.7qVQ
EWpBuJVTAKQPUKpfY1SkKh087qdQ7OMVgpXT4ZkFoyYODsNFw.GjkmdW7gHt
VFUtEpm5YTcMMkUSi5zNd00nNMRXsMh1Rw02npG2aMN5PKEVFiXThdp0QLcD
6odGwj4JslGwfxYe08nhNZW+Mp+5eTAXn5sFHUevqRcPp.ANhpERkXvRZ8Pp
v1HI0DoZs3NIsPZsQpnEhqORsOKbV2oIm5jTwRBd0JI1iTndIwjnnTMSpoG7
EInVPAThuVDcgHG21oBT4ZBEt2xC9s7w4lsGs173zSkUpMX2L048BjkG.5Gx
iJB6Q00CfO0TPdyjuw57qPPKESBjePsnCqeaa2FzwFRkTKcrWPUG+khrBpAB
LE2HdHrUmUTDbQxyaAc.Z4hG+0d7faonkaJA6R4PuTPWKFBlh9B8CRBUgiY+
N8QIG+n.jfTBSE86DHkbDjZVWy2+ecfqoHQJBgs43w03Cgywjm8XxPTmNKEK
hiG4Vp06O4o57fjnDZtbnINdDc4vT7Ej7I0YMRvAlp7Eid4KCCSXSEeBoBeR
+R7bjtXFSBGUAriMdb1yLNxdIrzfOFyF.Nyjr1P.dyFu0CJi8Lkve1KA1snq
WbnWwnDtNebpMdLcEvrFvIGENobrsMdbT.maO2W0NHLvoB5WFBV3jrBuOLwM
dqwOR7wARxFEIYRwQ2HxjeRgotWFxaTEucGuTGt3tSl+I3h+twaU3iNV7.gV
CUn0qUasiDb6Id4ib760XVMHfr0dYm3tmOf1z72lrOVjm8EhMrdviD+qpRIp
iwzRcLTj5vzkjkx5lBZC2AqLLzo.wiON3FOpmj9WvhKrx4YBu1niiDoBr1NH
XSBz1ZB5G9vaqDeNBg3FyBMEf4F6s0KT2je+yG6UcKA5accH7aQy4NKUBibb
sXkh8TtsTID0IvFgi9h6Ee8zG201uS3Mcy2XFdbndvaGiKJFycBuYXwXui8X
g3uqfkKACdEqrT.GdrWkXr3I9jVEwjW45yBHE354PgTfgYuHyiAXBInyqTBf
BHzqtdo8gRuNsUDR85zPon0i1Z4H1qpIJgZuCsVJn7XTIEPuGSaREPvG6Tqd
QwGsYJgjuhNbmZMTMD8U.2FkP0W8IjpH6q.bGxP2WIhe5EgeEVj0CJ+p0p65
oU8h1uhVIGwes0wXFeRf.j+UrbRD5+XOVQD.xjmoLJ.ad2JhOTQBV.EqRGeH
bIr8pBkK1XVN9.Ob5UKbZYRgEHxVALBxrR5.VsbE8d5CtVkMRNjsJUcVAXaM
LcoDSvk.ePw9bPBLBERg5WkDQpk7VdNCgqUtCxTsyxRyuUyb.KMMMYUuaiiX
ooCrzT3RSVEh2Y7WZ9sOgWZtIJIIc3KNQrrHpokL7s1dwosRKNQuHWbR4CKk
RkN9EnLl7SY4mFGwRzi4ncKP9YOxOsTb4owKC4meyPz4zUmEWJdCegoIrvT3
ByuYoTJzwK27adBuvbflCwVZVbr9vVZZ.KM6wbHiwWl4SYyg9eLniyKWZdLG
mifklBWZ9+XoTJzwK07+wS3klY6hBy0zOJWIgnto2VeHlDYnlBmlJsB0P4Un
W06pzlIvABUYo7w6wudg16OkUAT66v6IGzRlhEItTEAsGjQzFWhZBxQcLCyw
+Pumx5iQp2hZeWQAMR6ezDuJ+iYCdwSwsb4YNnEOp4eP8y4hGQQcrry.UN5i
KwKf3HPVxqQXMdruumZvqq8cEIu1O191SkFwIrAeefkiMRU.vbrKdpWPyUNy
6E3bECud.OWwrse.zw1noRboUr6URMjrOEojUSImlkEpTqIeoyy4WiJUfWxu
lUNMLxoqVVBrew3lstLSY0Byp1pRMwrF6UdswTpNJxVZJulYV+rVYHrT9hXE
PZYC57fQaY6c.Cr1Z1lJODr9x9dRgjIi9Kl11Sg3r41IgEjyCGazSg47.OsG
.ZRajZEpyCu7dKXmp4AgS04EBpYmGg6KJqHjSv841p5P9jyNCi51YTC42CxJ
iJ.xPMyvwXPlY3BlY.lY.lY.lY.lY.lY.lY.lYbwXlgw.Lyn2flCLx3EhQFt
i+cY7j1HCRJDf2sYvBPxi31LLcrXWmAZP1Y3MhHJUGLdneQqL5EX8.X8.X8.
X8.X8.X8voaVfvb7wTXU.IjmIogshCxNsKfPV9ffocurjpwHX8PC6xTzHC72
QIqLj0tlpxJqkC2Ni25ZZ6ZYtvy7kicFXJ3R4Jzd72lAg6L.KM3Gy7QgwCMn
eo55S9dCybjrj8oqJ2wWDKoZhsFXMd6cXbURL3SG.IG1.k+4jXg0XMwbNhI1
Sg4k8QLuJnEWzyKqiXdY+DXdY9bcClwQI5v5I.Kyv4X4YW7yriYWl6SkIFZf
SL4DiJE50e9MyQpMyQW1y7iRzp2SgUyGotKW5yKzwvvF77huN1koEqquYURD
yhoOQxCQlHSWCaqE5nEN5d320bT0e55YYhHejAB45X4pa64YXZah2vIJ8W0n
yWXrvBYbUieyzl7qR5gfHZdfsQGgsYy1R20vwwA43Q6Gajssi8hEHKcjiK8i
7bPtdNtdFtVHaKWIuC+Uq57JrL0MsPHOKj4BCa5qP2wvT21zDg+Axg8JLLzs
087Pld1dRdCDGasKnwavv1yzzS2wbgssotaAwvx.OS7zML8LsXyKzBzBqEdV
VlN5VRdEWeylvnH5qXYkefXuw9Z9jtDnyKBc1dSFUzZGyh0I55V1Vz9U2zyc
gqhcme7ME4ZMWAIqq5sdWZBw6jk45t4lK5s+2mmbSp+5vB2nieIut+82WDk3
vp6o6+4WTsjYxBtVC2gUwLMrOWUsvhBmt35lYC+o7+7KKEO7NtzhIlTdYyqa
B7yipnUpy.6oIR4pVoQu7e8WJUsRI2C63jB16G3lP0Hpu5RVuWqpfzquzqSc
bXupdEpuPRB9CnriJ75QGGFyzdknurqoApckmJecmJcUmGWkQ3oXFt+H.Bof
q5b5x487wnnB3cTIrNNvrcuTLNp38NN946dI2ynh4H9RbLZLd5CyA+hWV5CS
7FzWHoxEccUUClUt1MYokCWGk0BFMBVAYL.qft5of9vYA2PThYRqVx+JsNtq
wjyQJHQ096uEeJ1NMRFDWameX5HUMko9.b1SjCgoq+jLVZVLrU8HZ00LacAe
5oA6GIg8WbRT0Z5mOJp8XuFoLE+OUqQ9kxkAJYxrwKIczUl0OrR8s9.39+d.
dfh4PS4Jf+C76PiTXJznuoKKNL5oeE4l.vrodCrPHFNELN8G+slLM+TYuYgJ
gCPfK8Pxoji8WvlIoc+sAwzMbZ25mosILNL61f0.Sj+1ShI9CnFZWtWcpb40
GhCywlCxzjZN3r4o8jwyRg5rGUWURMuQUy6ydw3ThRfJxOGYsJejJplyT6Dk
S8f4ofCxN5REgoSvgTifyNo9jpW+cJtUMcAm31MLud9VBDObbb7PlSQY9rgm
Hoi4kh8Z1w4LRFk3x1ejqhB2ooOG+et1CzgjVTXY3ntCI060gjlS80xezd6V
X9MWwUXD57RwTgiaAFoSuvWe81AtnBYxppnFpB1GzhyEXet.WUwJjC7IA8mD
HTqBNbwgmnFo3gL+60nYDkLt42ApL3UzDxn.Zf30hFNT7Gsn35WTXonGf6nC
VE.3N5h2TP.2QW77O.2QOGYm.ti36DfGMbGgTD2QGT2.vdzyArGwWksiS04K
drG8MCKtKL8rFVbWfbuLi6hug4ZG9Cuiyx6u4R2uNXR1W3JUSL+1xfZ6skop
Nzoe1MpW1s9EucLno7RIYKjGd9+4Y5cSpL1OF78KO41ipPNHDLHELHELHELH
ELHc7MHcvVaJL+9M7qb9nODus0kSBlDTvTcELJknOYulkJrQcx8aiwEDa5ZY
sXAheLy+XYYZsSh3xNGASWIjukB0+93rmgxRtrsnYadxlgY.KxldC0V5pZPC
x3Rw.1KEye1FtN7Dw0W+FA8Ce3ceP6GSxCz9QYJGBvcmPaF.jZ2ST9YJYbeD
eL20XJt1lz.r7o3UOLsrtmG7krU9QS59oOheA9ZYqhpoTrD.T9LOHCny7AXt
h+tom276WuE3MCm2jFrYRioifMAoXoXAZwjCiHFKd+sgqtkZq3aHB5dyAIcZ
gYZ2DdWP7KSN2vfXtyhgrCjoQ1josQ350QEr36w1FDPYuaBSyx0nhl0VGbSZ
P.gAiEFr6bFdHOg4vCI95HaklRV7esZSJ1bbZNw+MXQGugwyyS.9Y+7SKK8K
mfs6.+7q9+4O95mO5XNgwSGURVo48SkqW+HUbI08UvEdLZNLeSXTNy8nSEe6
uReCZ6iYmuwBPprYv8Q0qXQziXrxcvWL8LkH4HzYikWWN+ADGysEiTHworKJ
dhG1aJcD4H.NMQdgs4EAPZkBWDf3NangJlm0BGajmt6TDpXG6cAz3LX92FvA
Y8n4GcTfPIiKE6.6i6JAXLmK66D3FrT9gck.FFrvLR4qD.83GdQWJWFPRbxl
I0AL5ZeeTBVSBMBe8Jr4Zwu8OBRSzvRChynlouJIMMHaWR7Zx85SZVw0sB4Q
mS0A1SPdzo1oiSkprLcWzR1nUteCrD4ouptLIMijJfORRUl.UiozzoLDI9P7
pjskzf0Z3SGx7uI3wBVJGi9zSZDj7+Mkp7SzwnXP8b5JYKPsll5XKrQMUiSX
yFZbfXna355XaeIogcskKmf9yDRzRwJ6cbpOSo6W1ZO+uqoOHkmWXLv3AQ+E
bn4+uuTLI33VQ8uegub5a+BVJgqhqnrbcqUbOrrU0drEWlgXz2xr+dw3ws+1
KcauiR7WKHoFwUDhsGQtQAC2V4rFiyHDjQmpLjl4Jnh48RgitiLaAUROurY6
znecf7bSFOWW0CNFi52jkR77qp8+Uj+SI.KENLONlOipdYy4I2dN2XJlGq2l
5gsRb3pLm25RlyyH.KENNONVeAY8xl26uNKEe7ttliNoZFh2KqOvC5soku.D
R4S5M6cof84q7Ezbg.kZrT3f73VGP6zmYQPtg9.qbeWzAPtw3kq.t3ie7L+6
GXUZzgls.LUVwtGgxSxv35DRvRgiziaSNkrp.muyPjlHy35WrtKJjU1g4rzn
S4FdgnxM7vJgxG0B5Scr6Lvwt8EzX2dnzccgeAwEg8mDyLiEhJN6RJx5OJyL
gUQdAyrEOUXYVCjkQ7cJgR7jjqfTXmzkw5MI64EL0VbAwWLGpbfKnwtwPOVj
TmmubF8CUDL5B5fQREBb3mebwL5QCkz6dIsvYnBbLtnV4bLj9KkAOZvRbzuj
F8dGyIUWLid2AN5unV3XbLGUcwL5Gr7RiKoQu9wn37Eyn+HTPSswdWOZTfhp
kWeypjHFNT9jl9bcSjoqgs0BczBGcOb+OGU8mtdVlHxGYfPtNVt51ddFl1lW
Qv+Q2WQiNdgwBKjwUM9MSaxuJ3aGDEPxkKM5DSaWaKcWCGGGjiGsOrQ11N1K
VfrzQNtzOxyA45435Y3ZgrsbEz+9qV0o6sL0MsPHOKj4BCaZ2q6XXpaaZhv+
.4v5dCCcacOOjomsmfdmjFr1Ezn2Mr8LM8zcLWXaap6VP.rLvy.OcCSOSK17
As.svZgmkkoitkft+5a1DFEQ69kUPxi81j0zIiU24kfNKuEiJZqiYw5AccKa
KZepa54tvUgtxO9FFRPMbIdLWRK2klPxYYE6Bw7uER6284I2j5uNrHkDo2Bj
TM2m13uZJsogrCNdctorEE71bsLUZbvtjHRQwd9h13mfm6lQr5NigE0sydBu
OoF2qPaW2K0QyxuPAkuLAdotoh4J+5ddaeJax6wsxaSEc3rGe93p7HRw4P2P
cdHxjlqYsUhG1IUMKszA0hGZ1GOT3cAyqV9Plo7SezGAKj1aW.7Or7hfrun8
c3+MIlD8H3Mj3S4TfaZZwRyVrfpwxRIto6IvMGwcjrYM+7CcO2wCeP7P5tK.
tI27+rDlmkg7b+rr79rT3XIOeOKL9lZorpZw0j3XZpstu7yqyc0mtuHX5TSC
jxC4jdC2jdxey8FlIxCwjdCuDYwXTe4tftvnUTNZtOthgTtRO4k4dCLnwjCg
N+bndiVF44bYNbIg4Z4SiOoR9U94BufeJgfCsletS9zHzSW9R9YF6QnrBY4C
YkxEx8lGja6+jNKMjDdccWcHOmG2etF3jOjq+7br3fISR7TwyoRxyqw8mSi6
MeFqPtLVZnUoVNLVg7Wb60DpEtK+2uVnz7NrEQ4pX9uY0xQwGgN0cxMwsznt
AWlONoZpnsHmj1Rc6NNGk6EZHBaNVc8LpxVBbrCPqgL.eDFeltCX.x6dDm9Q
3fHgtOFiPyALB8dLFfFCgI6z8ptm9QHZnivyNMbHCPiGgAng2PIgFm6Q3fj0
X8HrLzvY.iPzigrFCqgNBO6zPigxk61X9H76BanSDYYIdnqedG5CR.5ixJW8
gNBO2qb4CtmA.rmoeDNDY7OFp6hFhH9GiUgnAIgewiwHDMplLzzTroEfGGO3
NlRfcLcf5XBAzghf4XxAxwzChiQB.GpAdC0Atgxf13v9spe6fXgpM6stJwCB
A54JDqUrkBWG1L6jxM1d0KhsWDI.OY.xn8kGJJj+Dh.CwWXHuTeIab1IL8j3
lIt45xho64g3R7QIdofVl+s23GF2OcFu4lRZcsX4LkdIzFdBozH0ozx7kXsC
V34lv5tHju6AITfkcGqCiuUzOiIeq6kOcfw8d58eP76cZvVRlS9W9w+xOosN
3tloDCpOHWkrms80PNuEe.f8bxgBFrpUFVpFVriC9bGKG5+S2gkGEDxqcThW
e89q6d0JRVAvZeV35fFWPwDSgqCdnYszaXQojd1AAD4yN3sD15K7zwmqhrom
+4TqI5Lw4MnbbYAtbn4FLjQTDB6HIR0VL.FvtjvJo50d3lj37pU0+M+F4uaI
LIxWaiOSiNzHxh14GGDMqWpFhGUqBBIbDQkcq+tNqAquVV+DkasMYc6aGbln
ymUZ8ESGRlVENb+n1qLZePu8jdnxc9oUqaHW1HVMhFYx+Cas1ow84b3rd5z0
7NTX44g58TcqQ3vl1X.p94K8h8G939otsOyH.7HnEnEZZBkLn9T+FPK5olOW
3MeK8Vuk.eGo0HFw2zsza4VNXP5IU+17R+lsEqPvXPMo8ynPKOGDLwzi062t
8gwff7NRGoQ2BokeqetFYGi1CI6I.z.qVQtluFdYiFdmG4Ov5hjEDuViPGKy
WqYymBJp9nPQEfXrAPoooJKrrf78YiA8td2c4uNrcAyqEsweUd3cArozXPbZ
zeOGj3IlzsKI5gc2lDOfMwFBoa+bYmQRL4DjSks+5cLnbLe7oinoea6.O43s
HITZBJxxXKpnikrwfh+dZmpsKHUi1yZEvrIqjCT.usf0ZzWZPN94ZeURbzCj
xeT3ZrHWsx0.gqpwvx95m3rrqSZClstLrM9QYAR3Y4g4QAiAe5WIcT2cE7Ge
bAk6SMxOOzDNacxOlj+waSt+Cw+f+mw51tCeXdRmagrEWfn79XvDHJbqQ5Lh
5Cj7N5LfBWS4BhnhzjnwfR+82lPK8.jU626+.SitZhYXGsp4mxDPgeqQAqmq
8lrvs6hBdCV.Up1aHdm7Mes18gQQZDmCu0OObkeDVxE9eRHjmx9gfW2+QdXY
V7mVhuH4Wxr4ZeDybHeVHUmw2rOKHsn6iShCJ69r6Cw6Gw6O2v9x+iblzzhW
DVfpleLAxv9oEn9D+jx299XhVnqeZJtL.exVUzCQH9jnAiPkZGEQGV2Q+J8H
XsnuDuhivdpX8oiwZt289e9Wd+2+m+02+t4Z+8rh0FEuB7obY4A9qmC63Kc9
Pvp.rNtkNN5jI9DeCS6wuTXhVl1MTS4vG2wvN8lzjsTi3hW+EB+HbMqDDWbT
n15PhqXv6t+JxFrj8XS.wzi3UO70WokPiNBRgKgVJJnsn7wWg2KheC3OtZiY
R9skJD8TUMlZ6KIhfn4wwZjfSa2IqGkXOU5Mih1o+RvtH+UzKcVizmTA0M0.
5Js.rJqOjS7EmFwYVq09Snqz9SFZA4qlyjNec.dwEsmJ3wjtve0pjTZgqA2y
6IuhWzateMedof.lnI67fmTodyYPl.Msdvga3QHHzHZCCCwgDQyvgfip3MhF
BhViMdXy6haWimIJDHZF9CdddKrQK7ZzqRqkLcuXQUukxx8xsoAML7+snl2f
eqSGJEA8ZwZLRqqpMZAW6ca7V5pCftp2jJWe5KNmDy6RT2045.ZpGRchH4UU
vA65D9c6p8H9tgeq+uwVS30zaxgwrO1o0Yy2EV1eVsDLu51PLQKeeJaR7Yml
ojc5MAkFuOrkDhlBEpHDXQVraQq6p7Z23k9B5UhfbJRt8rqFjjKCZiNjnC6L
asqHFKHJMfD+TUvqRmGnoHWsWV3evj9ZLWXaJu9OgMPz0J9pY2jFtNIlLl5v
KIOp7sim3rosc64IsUw96DzIE5+HtAL2tdseJgMWbXR8CQmkmjD07wM99QAa
xKZxtPrn0tD77jcxaPZ3M21SebcBtAa668PeZFdyIqEKwqrxWl4eWWlSNVyr
hKxq6q5y9wgX66BxCYbMC8FMHHlbn6sYqHhJ5POXO8NAOkgSg6CWmeamKflB
4uUog6JWS1HXvpe51rlSElL0leZV9CQAb93JccVlGfsJxOmSiv6GCyxyvGzm
Uz3x0uMogk9ihq.PJgrun.aTS2BWueylfzunw94F7QFZeG0AKqBvVN+1MFy8
C2nP1XvRu6UpiVXRqhJEkeq9R0M5mP1Y3DSSJONIxAdEG2SKSN7WvZfvP4CE
uOLlpF4MQhYZ7Nj4uLyjC7JOQbhZcxAGDPsMxbkOh6VhW5+nOwkJY8lKGLdt
wiJpIo8vj5KSWHoT9xKaOf0FeB3kGJaueO9EDGDkM96ndNxNxRmhMVzMTZob
qTn.Wfu.tIV51bse0+2wpOPtYf6uMHl3w.h+6uptq32RTdU6ZhwFZ96WGlns
KMYU.VEp3alOwxGmkUHF9jyjGnGgjdyww1KmwOu47HAb9B2.QW7O0Y2kmNqI
vTisS1RheHLJJLi3an0Sy5hgrge6Ko85OO3oJrUdaFrY9f6.C7SGa992S5zJ
HHEDmWAIIlckSsJWSH0WQ6AiIlMGMA5L8WCiWq8C9eNb69sTbIT8lHVuSHw2
Q3+Z2DdWvTay9SjcAyWrvVQy9nVkM1aFnWtiVQ2qQCRpyfBOl.qeFRkcpE9Z
cXrb44aPRVeqThGMoBJTr2KkTwov7K3PSEmcwC3oyt9qDuVSVG7s67yuk.3j
+9u72JwIHV+E7uwXfyg7y4r8oQiM8+WukQ9IaY7iKj0IFqlCTiO5JlWBblxu
wnxZv8Igs7NhTruZClYjaZbkF8Wbr9ZX+PgGRR8yGcZubeT17jCqWdIw4ixb
GRJmXBN9HhjmfSIplkDeE9X+vLhddQqIvWymoQNEHi9ZLvwpsYe7JpRdQg+N
Akjwe0m+5qHpBhUH7yLjuktOlgaRchXPD4eHi9ZlQAGGQPZTN49xGcl5e9d+
vblxaTl.gpmrOmhb06SRiV+Ll32M5YFBdDaiIQtLtiMYNuIYVeFmO9Wy8jjn
lUdRWd0xGkLxQ8pjkMauR8IT48qdhSHktO0QZHytCxSkCH+77AsTpAJVKPhi
z75MurxdKzrJpaTO0daibscz8TJydqOAY16xS26lVuapkFmBNVEmbv486Bx1
R9HQZ3496R1vieA0oEBuR1mVfvqeKI9lnGNE7c4nSw2kmiRENoE.9t.7cA36
Bv2EfuK.eW.9t.7cA36Bv2EfuK.eW.9t.7cA36Bv2EfuK.eW.9t.7cA36Bv2
EfuK.eW.9t.7cA36Bv2EfuK.eW.9t.7cA36Bv2EfuK.eWmJ9t5hDIw4cvmZ3
6J+1zfpT3ElMl76AAmBDuLoP7xfek6qMDur.DdAH7BP3EfvK.gW.Bu.DdAH7
BP3EfvK.gW.Bu.DdAH7BP3EfvK.gW.Bu.DdAH7BP3EfvK.gW.Bu.DdAH7BP3
EfvK.gW.Bu.DdAH7BP3EfvK.gW.BuNUDdY0C.uLeBCvq6SJf2kO1f33S.aWK
7XYuKSUf1kM.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.n
cAP6Bf1E.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.ncAP
6Bf10oBsK6df1k0SWnckDWl4tVmteaTRxtS.cWNFrD2kRn6BAn6BP2EftK.c
W.5t.zcAn6BP2EftK.cW.5t.zcAn6BP2EftK.cW.5t.zcAn6BP2EftK.cW.5
t.zcAn6BP2EftK.cW.5t.zcAn6BP2EftK.cW.5t.zccxklwdP2k8nftqFKGl
EEFy8PkYY4ODEzQZSS3fU8ZIijlC9R4AxgK1lj37xN4SZy9a3EJcAJEoQkWf
7mzPFZ+yWKlFOCeHFwkwjQOGl4rs3wTX6UPsIJEi8kWeypjH1R2OQX6lHSWC
aqE5nE3EpWg+HT0e55YYhHejAB45X4pa64YXZadkFpE7yl0nSWXrvBYbUiey
zl7qb9lAQAawytFcfosqsktqgiiCxwi98sQ11N1KVfrzQNtzOxyA45435Y3Z
grsb4z29qV0oqsL0MsPHOKj4BCaZWq6XXpaaZhv+.4v5ZCCcacOOjomsGmdN
CuCDeZe8d1v1yzzS2wbgssotawj1x.Ox8zML8LsXyCzBzBqEdVVlN5Vb55qu
grqk10Kq1DxdShZ1jvR67BPS9avnhV5XVv200srsn8mtom6B2d5FrXU1lDC2
lxzZzpcXMdwZ6fESyD9YtPX+sOO4lT+0g30QEBJecWgPU+1gyPqjJzBfoGjV
zCvRO.pTrnvcYXUt9hFRWudK3fbTaDYZqgro+vykGXQqCTTi58WCPhZz7A0.
HZM093.NzFuodOzplvyYaSVGzkF2t2ZbTSEoYYmISqCZPseTyCYp5nYmGd51
v0ggw8vLwh.oLSFlesz6iYhPB4lnQia1f9ylFK69xGF0ufZbdH8D+m1xtHdD
eWCDgd6XS9Wam9n8NmaRe4zXYm29vn8UjiIk5eme5g8Z92xkwrSq0S3skv0j
skfwVVf0vvwwCoSgIuqmWOaPbExkLONtTV3MTeszlW0FQ50ky0K524i7855Y
NiXTZPKMQapHqrajstQXE8TymKzuMR8YiDrQK8xPE6mFo9nQtwl83Nzl5Xi0
gts6pONpIseFEZ44ffIldfWniFC5Asel.5g9nPODga3Ztcks+VLg5tjvU3Iq
e99rwfdUu6t7WFEEhG1hoM9qxCuKfMkFChSi964f.KwjtcIQOr61j3GTmtYH
jt8ykcVIDLx1e8NlqmlO9zQzzuucfB9eKRBkN3y6RxXKpnikrwfh+dZmpsKH
Ui1yZEdErB1eDX.5GFGrVi9RCxwOW6qRhidfb4XgDPVnUtFHbUMFV1W+Dmk0
8tT5xv13GkEHgmkGlGELF7oekzQc2Uve7wESRO0H+7vcjpWjUKtPWjGcbLAh
9xRP2xKXJLU1AQTQZRzXPo+9aSvhkxnq1u2+A7O8yqKlgczpFADlEu0nf0y0
dC6N5eCV.Up1aHFq+lulck7D2iQtlvU9QXIWEP0Lnre1fa9+HOLdUB1Pqaz9
gO7tOnEmjSBukOxf2oVXNgo+l8XyUK593j3fxtO69P79Q79yMru7+HmIMsFl
PIH9EquTZN9ajeK4Iku88wDkwW+zTbY.9jsJSLIDeR.WSnRsuKgCq6nekdDr
VzWhWwQXOUr9zwXM26d+O+Ku+6+y+56e2bs+dVwZihWA9Ttr7.+0ygc7k9NH
XU.VG2RWgdxDexE6S6wunkb8ugGAYZXq8YG2QMsgAlCesrf30egvOH34f73h
iB0VGR79Bd28WQ1fgsXRKBSOhW8vWSAvCdu8tf3LBIh0hxGSPtM9Mf+3pMlI
42VpPzSU0XpsujHBJhrsrFI3z1cx5QI1Skdynnc5uDrKxeE8Z2zH8IUPcSMf
tRK.qx5C4D2uQgh5Zs+D5Js+jgVP9p4LoyWGPPMBomJ3wjtve0pjz0juEtm2
SdEun2b+Z97RAn8QvUQGF21GJ07.IyU1GLEpvp71juZr+tc2ZLQJx+ger7E1
5wbg6i.7YHKUqTaBYLtSHiG8Ij43NgLmxITyqOU.HrDLS43Pu1NKaHNCrm4k
ZyfFX+giIbMv8SabczByO6Z7LQH8oIJe777VXiV30nWkhvmtWqopWRZ4Y.sw
1RCGF8VTy69tkVEkGc8ZwVZLinuciVv0OIMdKc0cT86ws4E+Ht59w6Fr104N
iZp4ZcxGwLyBdW2acY2tZOh+8tr0+2XqF7Zd8AgwrO1ok1b2EV1eVsNJe0sg
XxU99T1j3yNMqwgzaCOMdeXqyTZdLREg.eH2J93VZ1gKOagE8xyL8nWdloK8
1kMMc5BnhnC66ZscHFexUZ.Vawb+J7SvCmQ0Q3DxXtv1roXbKrAUrYJRpp2p
aRCWmDSFScXkjGUCeU1LLQzddRaUr+NAcRgByha.yO8W6mR3xEZeTWqqY4II
QMebiueTvl7hlrKLNlCAOOYm7FjRBIR4M45DbC1126g9zL7tRVKVhWXkuLy+
ttLmbrp7EWva2W0m8iC2hUoJOjw0LzazffXhVZ2lshHinC8f8z6D7z03MSqB
tObc9scgrH9o3uV3tx0jMNHo9YPyZNUXBSa9o7AZ2rCJGuLO.aFcAzIa1H71
Q7Y6YXMCyJZb452lzvRGX1RxGkD1GDEG0LV2ta8yRR+hlmBokNjoGYijqAEX
DHgIhNI68aeg7dRSTcFWIFakcjGH8l6U916Odr1RjY1F9RJhX1BlvR9jDw34
P3Ihr96BHgHVt7BglqRdOzhtxhsJyYDVeYX.quZs9pKIo05KimRquvjb7hKk
WZYfn+vRojno0IjDM0uXxLlnwN8MT32PhJfDe2ylzLG.EFuN3ycXGu5kQhwr
KTQ3FnWjdCSkF6vyqfMzWvg9rOuJHj.Lz7pvpjsaK.88XxmvBIxBzRvFZpU9
FfXVcaPt+nGupZ+u+3O8iZr3Xjb6DjP1H9lRjWP9UxqkEPqIoZD1x0OP8gMw
UGojuxJhCSd4kBR5bwDbZSj+0AEdJpfJxMV+nd2KIcMyiLlmqnhEMqeA0i+g
bxcloXG0JwUmbb2YWm11SzPRzQRZnPxsAM03iaSTONHMcsrVr.YpTfPhlf.g
rlz7twBYo3mgGpiDxxRt5JNbi1nj3GeMpW8vpnfunrF0HWpy0rLTQiZC6AYN
VSUpMTzZqqFY6tBiyCR2Uf20XRxAr85GVDpVwH+ke7u7SuiIB8igw66DE1GT
.b1lzf+0fWzw3PK4SPG95tBF9i+JuugFfa5puzisny1dLbTf0kqiBZv7+lBF
+HHv4atDX5Y929ERXwh++Vpy40sXECCWUj53Jk0ZNcbtGGC92M9IGdZmRzC5
5.MVhCX8yN0hUxF+wM2KgOA5lwlUQ5yadI5AfQl+8VURUl42lFjc6nyBo8ZR
z5G4paxKiMgSPszfYb1y0xngH+bNQlxuSddFhf.8YivgWGmg4CHiIQE1eRSj
SPz9XMGXx6NsYwoHc6bjdw5IwVoh2iDJ.3DxoUDEkk5EmVHIfiSb31Ep6DmE
NFNHGKWmwJaV0FBXLSabOcKanSzGeaaBi0LUvlFCSlySX1w5ohIMlRMoQ+zL
o4RxXlI3lxnzI3hxfKJCtnL3hxdVdQYinlPxtHMkCsg1q3XEjEZfmwDFW6d0
LOt6U6DTrpQZ3pqZUbdbSkM3z.00oBYfVfzw+m2yraFqHqfYd5JyUKwf8npJ
mg5HjcHpxY.pxApxApxApxApxApx8rRUNCPUtmSpxMBwMvkhpbCHfAFflbHP
SNPSNPSNPSNPSNPStmUZxg.M4dNoIWeEkGySUSNg0iGQJ3Q0+hzN4p4kkrOc
U4x7h6JVqqNYqwqoCiqxOEe5.DeIM9nz.8XGflCX.RlLFm4wGA.4JO.eLHfk
bM0Xw1OFiPiAxiO6CvAL9HYefy9.r7kpLEDctGfCYMnQuTPnpj0ZrCUkLnpj
AUkrmVUkru4K8TpdVvJfRHCZx1vyY9BGSSKxJZx+agUOkpG8wtxjIpT8vM9p
Z95E6oydhqpokETlbcrh09NR1gogcZbYIdKnQLGEugHjYuETLmGGlPi7gzIT
aqZmmZl7RaE6EdieyR61AFFwyGZGZj12wr5Vet12gosre52zdap4SqR1y1oa
HmAizMVvx0cEo7tErL8mr8YtGAO1PZkK6JdUurothXcWo+dDoCkpYG4OFjqE
FGliWhwJT8RqFDFu.peJsBI2ZdO6CEzo+SdzIItOqSVmOcKl97GAqGHWDIkK
x1bcnymqc4VJpDD4SCnjAQxJmiREg6WHcDqtzPBaFY6EtDKJUMJlgxVqMFzp
erp2NTHeXTp4OkK6Y8tXq7DuSUXKw8wk0emJB3KsxuyA4o+b0ZHpe0aYQsn6
.rcEspnFEbp7FR05vOVizczBE+ZMdw0zSMN.8tPDQ+eOdtxNLKqmSybjTKIw
RL2NNrfef0UmhrWxvY1yE8MJnG8vaPR4MmQ9BhCeo3Fr1xcl7rTIwSmo4+4Q
aCEqqNsMT9e9I1FJjXdSAoUNuwPJu4LxWjsgh6L4I7FpIjoQfHRbG3AcjlKu
xOJfw2xzJ63PR8f6kPoEULS58kzX4bIWwbor7fciR8d5cg2UVUuXZvgsoCaX
meAd6H5eiGVqRwJdpwdouncpwG4PBZy17Dy1ZALlSl4UU+792n0OOBWLJ4dB
B7vjds2T75HFj8lx5o2SC9G53rQ5crILyBodYCikgp+YssIojBdl+ZxzlVSz
ppaZGL8OLlZxzmyI4CvHMRp.TiX4ZP9ywpflbj1Ihm0idfVh4o6iCyGGlIa9
PbuEqvgbMaiE4EzwcD.SkOS8uS3F8vLskTDmSNFWwIo31lDeWPZdF9bM7tzn
19ii5JS7SO7Ronq84X0I7.K5WSzp4gRrJIoAYzBRUOrMcIU84zjsSFiKlm6T
oJqPXc9qx22kw97lC9WI.x8X3gKDyBqZyox49IRGQDOd+sgqts5lBZwfxpJW
nzzoMoDxlDWUeYIE764Z+JQ2TZusBKpE2TpSGCWQJwrLJD92+p2rILMK+Mzh
97aHUnp270uDj.KVU0eAyIHpkPYD8so1nWkklpEFUKHJVJPpOVZ62QOXUi3P
VxYtjJD7gFBKIjtj3s8ppkRKIjbmrjBBc3Hou0Gw80TbOhOV7nqS5mCkm1YV
VyDSBA4C8q+jDcgWkGMR9x9Cu6CZGpamkNE.elaYUZO+1vrCaMm+Lz6.07sF
gZ78GnF+Hs4y6aijrqCpS3gcrFsLEgD1KLaV3GtWc4mtx.PvtAB0BIN2IAqx
atl+50rMYUVpPM.k9Zz9zAO.o8Vs+9Geu12U7D8R+87Om+z1iOpHOcieTlL+
8LdLk+rDtw7W3Gb8g3UQ6WGfO6R6mYD7d1H07Z8DUGi4mafEDCD7TfnGm+df
49yMsMYpKL7sQ11QNpUBMaSQ4d+oIs9XGvpilqipvzS14Jprz28osfxbmmqV
so+snEV5KbQ5KrNGkm952rXqhPeaQCMsgos+5aVVkOHbuApfK8GbyFe3MzrX
O2xeisK5yEW1Vq.zntYPMd2U5e25KTh9plc+gX1s9m10WZ0ho2tLANXjuMTy
agOdqdvGOcQ0kU7hX5RS1JlNzJ7hjfSvy8QMBQZ95EGA0OpQHBq1NoYYoqFw
2iR6cb5KvPbMG6fFPZw6ZXxfjVvtFRw5RXg5pmsl0JPWMITCK5UZUXtl7fWI
y+1anFRJI1U31FNqmrQ5z0PrvQYgUeKnL8fnPAhBEHJTfnPAhBEHJTfnPAhB
EHJTfnPAhBEHJTfnPAhBEHJTfnPAhBEHJTfnPAhBEHJTfnPAhBEfoBQgBDEJ
PTn.QgBDEJPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJ
WXQgRMTDzZjIO9TpfjZqvMoFLeZG4JO0hnEtXKuEj4subBnE7oEX0Y0vqEu4
lFam4hG9EFGp7Ftd8BGdwEdC8iK1VNg3ofS8FrGNIizrr6LYXA.AqaNurSVX
UQ0DnGVpkqYyhoROwqjo8yFdp8wWbbNq7zdi4rBdnoGKzy5kGZr3QMlyZ95G
Vr9c1h4rR4h2hOs71jn08IYz0ktARmVwZVzeMIx34x1nlyjgEAgm0sQDk0Ir
TZ.oQhDt8Awqd3XqQQEgFFxy3PngIqBEINxvPB425mOFZAwYYmg5vjKVzMmu
n86lzjj6B5oTUcnQCuTUYJeYfqMUKHjiYspDG4vTQqCr0gPDDBQvi7JJgPDD
BQPHDAgPD7UPHBBgHHDhfc7c3YJTzfPDDBQPHDAgPD7QHZyfPD7IHSCBQPHD
AgPDDBQPHDAgPDDBQPHDAONlJDhfW79SBBQvm5bPHDAg3ACBQPXIADhfPHBB
gHHDhfPHBBgHHDhfbXKPHBBgHHDhfPHBBgHHDhfuDJTU7AZdKzw67DpPU4VT
npXEKIS69BZHqG2BUkk6wGOkmsfFpWhtgAilSicDSTuAP4iKM2z83Wq+MmwR
9DMhd5IFPpZyvCAD6d1HUD5GHxNHWZQGiveExSsf..AB.DH.Pf..AB.DH.Pf
..AB.DH.Pf..AB.DH.Pf..AB.DH.Pf..AB.DH.Pf..AB.DH.Pf..AB.DfoBA
.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A
.BD.HP.fLkA.BWPl2BS7tWNw+AKdU9h1062rIHEuFQ66hRR10Dl6hCKjETbr
irs5MDELEBmcyiCN6EgnvUhCVgoFZ6Dv2MFfU8mw6ut1e0uqQ5vmlZxnJLo6
8Z6EqH40A2LDCqESt+aj02jP53q1l80unI25yjgV00iGw1+y.w9sxVbiktgF
CxMw4rY9a2EUBojIfjqeIfD9BY9R7S5CwqFk.VA2Okvy4qH0KufqJnvYWogW
S+Rk9RzRXrVwdOV0PMRGdk1ljHBJZVSbQlu1+k9+E4ZE9uP+WWQfBEgHj8sA
wzeR+BXkRN2BUPON3YShGmSSxx13udTzE48ThKAWEDmYtcG06jku.x8CR0N7
21ucGQiE1sAQeDd1hOxHizD+67CincSgnnsXh.Q22GkCAPONnXSF9AGmcNeD
2OYLefUp.YADYXJyO+oePgwlHiAw5Caz1h4PgjkirdkBUrLM+TxZWrsRo3U5
4IjKzbeVv5qXtZDuJiQhWsOMEa+bzCDzNDdWUmrOi3fe71kv30AeV6S5ZWiI
kilmeuvWJyHBiEV99KLRJlIf4.uzB1LUihLhGhhChFE.H+iU3LtrWIDeBVJm
+hO3pQ8na41j0iCNhINyDaMJVTTHS5cVgFmXMzv7BxMDlQKTyAZek974nutR
+ShpQaCihByBVkfO68oo4UkNl6SZynyRpGcXSPxutMqSP8TwIoe0d3ir9TNu
rrCOc7TjefCxzft3HkzfHe5wFE2ZEUKpnf3axukvE8uNKIZed0iWEEV93m7L
0xoNgYVNOOMVZUON92gE0kd7GZepPaExzXedRmofpNzazueEliwDPOoC0iyU
XmvPh37nSX.IvcQSwEoQc7hbafOdmsbBTPp6KNxw0.cYwQbmYE2ZfvaMi2ya
dGG7Zgp2bFxA4ZsXwB8ywMmU+r9xCndMe0wZcwZ0OYgiLKkRjXEIXISIWlDR
9kIURnOaEO8unYLSkLgj0BZ1sxxr2qNxXDpI5S8cAEFOJ9usPoAJfrw5BTnE
38g42VXfowSR+M1qKz60cizY+XniVAIUlQ.OKLxj6DTYiLS1tcjhCY7lRrkK
XQPoZk85yAi2Abk9bBWoliukDjCDNR82F7g.sBs4YmjEGRzlVhFgjC9EpNXm
G1TEkNOVMEAMcsvZAhLWzmZfnQPMvZRDEgfHU0riLcW1UwlgoVGklcF0oCoT
FKUcc5PfNcUamQfNcfNcfNcfNcfNcub0oCA5z8rPmNjDc5LtXzoihR8dyB8z
TUdQFK2xnWc5zG6LVd+P7VYdcXbdP5thLMZbfeiSQagigYjLIy6Xxi9XX79F
IcpCZsLaSZv+ZXwEPwZD8ieMRAi6rky7Iyw8AwqdPRNyupMk4LeKK8hjleQN
y2PWWXZy2TkEg15rkhtjEg1FRVDBYMeHq42Cdlfrl+kBj4grlOj07mdrrCYM
eHq4CYMeYmlAYMeHq4CYMeHq4+DAY1PVyGxZ90zP.xZ9PVyGxZ9PVyGxZ9PV
yGxZ9PVy+nXpPVy+h2eRPVy+oNGDxZ9PJRGxZ9vRBHq4CYMeHq4CYMe.IrPV
yGxZ9bXKPVyGxZ9PVyGxZ9PVyGxZ9uDxZ9bgYdKjw6cjYM+pUwTbm2PVwArx
ScfC447QLeVx9zUkPBmfybO2YWo0DE5qwa7Bi8yIdy5PCIA4aqFVwR0UBF+S
2PCIdngljglqohCMjNqgSynPU5CognIZP3n5fvbBoD15JNJLbmvQg0k6dIqK
28RVHEGZVS3pXK8ALHlr0OpJTwaBGDlKTbT3MkRUL8TcCs24dqj5CM6y8VIy
KWAPlWtBfLU8HLzjth2dHihIa2upxflzAgpZWYMoiB8gnd0jMJTcPLkJZZr3
hUth5CsytbEC2grLdpjqXnpbEJkbxFECR2lIaTn5tIioTMOCCUGESoISpNHl
RcMQdWrxUTenc1kqfbGxN5Kzg1Yme5LDO1LUBfPFCQIOioZTLDKtMNqqeTcj
gN6qezU1.lESoDS8gXcN+QwA+HecxmuIMY+NA9RF+3pvqitok7xYCiEyp555
cXV9CQh7L8gZSP700xb9kW0IeBvlj37xuHdT727aUvBnMHK7OJZ.pwVlqpQl
IE8H5nqk+9mQqlR0W2Te9TL1Vd8MqRhXWpymHWsfIxz0v1ZgNZgitGlPOGU8
mtdVlHxGYfPtNVt51ddFl1lWQt5kCcciNbgwBKjwUM9MSaxu15aEDEPR0SM9
xl1t1V5tFNNNHGO520FYa6XuXAxRG43R+HOGjqmiqmgqEx1xsU+5uZUmt0xT
2zBg7rPlKLrocqtigotsoIB+CjCqaMLzs087Pld1ds5UBVX1EznWMr8LM8zc
LWXaap6VLQsLviXOcCSOSK13Gs.svZgmkkoitUqt85a1DFEQ61kU2hH6svqI
iNqqSmilzd2nh14XVve00srsn8ktom6BWIcge7MrE8FtGtSrFsXWZBAHRExO
v7iEb6m84I2j5uNr3dDwc1qquMikqT9Ps7ITw13Ye3mJ+ztBEpXeaCWGVaWY
Uxo5sGtduY492zd2aiTeVsMvMGZrTqwQO1ZmS7NL53L3ZVHNDL.u5XdYFbeY
FmxKaRo5D47j6ldk+paCZS3y7uecP99ZG7UN7pgqkCMp7cOKL68EWka086Rj
tmEvvcW8gT0A.zoItunUybIut5M6zdgrhUQeuuZs5zdcY92dCErgxIlkM5zd
YGthaIurt2C9wNyt+tjvU0f1.+kIEM5zdYGJ48RdYGZzo8xHkfkjz9da0Z0w
85ZtkjHDOy+t5HbaVVr+trBX+TeGJVzePkfhu205iksqbFTfnk5hlHIPijzZ
On5.mCEDnhtodFeAq7dXQQQmJ0nJgNxOAwLKa+0UO3uikYsAq.cE7hlEr85l
.aryT7.klCWnlbxp7dV4gc00pjytNk+pbjOLfWaaIm7+pt1c+t7DDx+aay8E
2ZuHuupKuw7xkY6390Hn9krPKqg5+7TgGSsUnM22DAc0AAUqwPKDqgWSsCur
scGzFWa75lNsgX2PSFTmQcWnW14OahtNXX.CCXXHZXbvp+FOq93CDE.CCXX.
hB.QAvv.FFfn.PT.LLfgAHJ.DE.CCXX.hB.QAvv.FFfnflOn1cXz81AU9Ja5
dko79pVV57u6DVbVWDgwoAaSxCVF7Y+s6hB5dgo0udsNW20p8oDfPsr9Ehc3
RDwOIMrI7sZfEqdtPPA2InrqErwMC5nSq.WyZ.puC2KXy4c8p3U4XpIvxZdM
g0+7NWPH26Hr04.7tovWIpZWcfQ9p1IUCN2Z3wzM7tAwia3bOm8Rh5FWaQ8C
2aVTROYKY.09VFE1MthmW8brMu6crs7i1odsFW.YuMdbzHPn5EcaqntjWKEJ
.kiLzthQ48IsjoCCOX3ACumHCu5pAMEJqABwfgGL7fgGHDC3TvvCFdvvCDhA
KjfgGL7.gXfPLX3ACOX3ABw.NEL7fgGL7NeBwZB+gFoQ8tXc3UGwkKyA2CB6
l5ne3UcuM29w.QquDEtBoAaDce9Rfc.66dHhkCVkDutnTi7Iz+b9V+OSPKPm
uwN+b5UDO6Ke66RVsmjhDx91ev+yZdeaIVIx59kRxZkwEpAFARCJlytl91Fq
BHIDEOuqszWrxzwBsXkg805A3OpNU30cWfdFwwAxPJPNzWs9Zu.20uciCx+s
HTv0uk9ml5FVlFlA5F95me3czZE1.GjiG7PFxVFLgVbGMjsvniFGECBTDpA5
hKXfybZBZ3runkjlBQQOZxX13qaa6txEsd8FKz0qWa34t95EKzscwxbLbtbj
wfOs.jw7nerLHi4RSFCm8EsjwTJE5QSHy0NqLV333tYgoo9lE1FNWuwEYsvj
jp27WGvWHSotpsynMjrYy5kIW+aAqx4TIjJSlMI6xC2F9GUYIwY+TUR5ZVUQ
kZIlJ0p7uMa+9grak2XqZPIY3QWLjrgS9ooZrEFeWXVX6pSWsFDkDeSibr3W
lwoUaSVukTtIam8fJdLlWkl2euTJSzrES40k+K6TA5zfjFIKlXro0L7xNZIO
pHu8wxkXEzr7fOyxqXumVcLYUl3M9gQ3eK+VRUp1WKaKopDuNfTPcCiyBnEQ
yaw6OJ6DZV0LL9lko3UELOrX4YLW+JMCa1O7VP+g9grl2rB9bQxsm8Yw62RS
qcY0SXR3OjkP4xpRajizblTbkKp.2KILgLM7+Oy+lfl0pa+MjJfluFta1EFQ
WSqkknce.slLSpvnzBkXdh1uGmbOsNIl6mdSPtF9SSC7W+.qNxR913tLAySI
TKs3f6KqnZzRL0pj8rj7loX5psGIqBpgVfpQWsb4SWsmX5JdBfeMcHqrUJHc
ccYKPzMYKPnSDGW5eXvedXxcdXvcdfZjCBK237IrbPeRt55eNdSyrfH7ro4B
HElvHGJCzvQ1L1X.yXCoy3qvJRMlyZhLp4LGBHYxZZQWXhbo+v0T1bEMfUol
hlqr7Ul1rvX5OXgiQ8oM8M2rRtwnBsSIvEzhtoB35ImY4oYatTaUeOMxY9Bd
OKpk09O5WjgBuHyQXBgLPpNkNznsgq2kfYlELKCczb6qzLcYGmT6uN0Am9PF
bcVO4uaWMSmnuE7tneik+aoEdPl8Uj+zg9moAD8JXsmVLJm4mh28ji25rOko
Fvmcrlw9pXMGRi2GVj.voJ5bnhXlsyeUYAs8yyd8+mW++OVzuLqC
-----------end_max5_patcher-----------
]]>
std:: containers (standard caveats apply to using dynamic sized containers and the std lib on embedded devices).
Inspecting a std::string without pretty printing enabled looks something like this

That's not too helpful, although we can see the contents of the string in _M_dataplus _M_p it's not easy as once we have a larger string contents. For example the entire contents of a text file is not going to be possible to see.

We could go and inspect the memory since we have the address but again this is not very easy

Luckily GDB supports pretty printing variables using python, and it's quite easy to enable this. Unluckily the toolchain provided by Electrosmith was not compiled with Python support enabled so we're going to have to jump through some extra hoops to make it work.
Firstly we need to get an installation of the arm-non-eabi toolchain which has python support, we could of course compile it ourselves, but there are a lot of tools and a lot of headers and other things included with the toolchain, so it's much easier to get a pre-packaged version. The official arm versions of the toolchain also don't contain a copy of GDB compiled with Python, but luckily Xpack comes to the rescue here. You can find the install instructions for Xpack on their website https://xpack.github.io/dev-tools/arm-none-eabi-gcc/install/ I followed the manual install instructions as I didn't want/need to install their tool and I wanted to place the install inside the Daisy Toolchain folder. On OSX this is located at /Library/DaisyToolchain/0.2.0 and we need the toolchain to be in the arm subfolder. Since I want to keep the original toolchain, just incase, I renamed that folder to OLD-arm, and then installed the Xpack toolchain inside of the arm folder (actually I symlinked it from where I keep all my code, but that's un-necessary for most setups)

It's worth noting that this is upgrading your entire build toolchain, by default Daisy uses arm-non-eabi-gcc 10.3.1 and the current version of Xpack is 13.2.1. You can get old versions from the Xpack github releases page but so far I've not found any issues with using the newer version.
You'll see in the new version of the toolchain that we have a binary called arm-none-eabi-gdb-py3this is the version of the tool that is compiled with python support and which will let us pretty print, so what we need to do now is get VScode and cortex-debug to use this instead of the normal version. Luckily cortex-debug is very configurable so it's simple to do this. Just add
"gdbPath": "/Library/DaisyToolchain/0.2.0/arm/bin/arm-none-eabi-gdb-py3",
to your launch.json configuration for your project


xattr -r -d com.apple.quarantine .
the -r flag will make it run recursively, so it should sort everything out with a single command.
At this point you should be up and running and able to debug your projects again. However pretty printing still isn't enabled yet, as there are a couple more steps to do. The gdb wiki explains these steps but essentially you need to clone the pretty printers repository using Subversion by running
svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python
and then you need to create a .gdbinit file in your Home ~/ folder which tells gdb to use them and where they are located. For example mine is as follows:
python
import sys
sys.path.insert(0, '/Users/twhiston/Code/gdb_printers')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
At this point when you start debugging pretty printing should "just work" so let's take a look at our fi object again and what we can see now

That's looking a lot better than it did before, and we can easily see the string contents.
Although string is a somewhat trivial example this setup makes a huge difference when we have more complex structures such as a vector (again caveats apply here, if you are using vectors you might want to look at managing memory yourself etc.). This is what a vector looks like before pretty printing

and this is after

This year one of the few things I had the time to work on was this REPL for Cycling '74's Max software. Its basically a fully extensible REPL built using OpenGL and Typescript that you can use to control your max patches. At it's most basic, using the default configuration file, it's intended to be used to output messages, either all at once or a line at a time, so you can use [route] and [routepass] objects to direct those messages around your patches. This allows you to control Max patches in a flexible livecoding like fashion and makes it very easy to hack a livecoding interface into your existing patches without too much effort. It will also route any GLRepl specific command back to itself, which allows you to do things like read in text files directly from the REPL, alter the REPL's behaviour, normally controlled through max message, arguments and attributes, directly. With only a few extra objects you can create a livecoding environment which allows you to interact directly with the jit.world in which the REPL is rendered. You can see this in the GL EXAMPLE patch which you can find inside the GLRepl Overview, found in the Max Extra's menu.
However if you look beyond the default configuration you'll find a highly extensible framework which allows you to attach functions to individual keypresses, using either code
//Typescript signature is actually
//const functionOne = (k: number, ctx: {}) => {
const functionOne = (k, ctx) => {
return `some message`;
};
glrepl.manager.kp.preloadFunction('doSomething', functionOne);
or config
{
"bindings": [
{
"id": "pushSpace",
"asciiCode": -2,
"functions": [
"doSomething"
]
},
{
"id": "replaceLine-alt-p",
"asciiCode": 960,
"functions": [
"var pb = ctx.tb.pasteBinGet(); var startLine = ctx.c.line(); ctx.deleteLine(); if(ctx.c.line() < ctx.tb.length()-1){ctx.jumpLine(-1);ctx.jumpTo(1);} if(startLine === 0){ctx.jumpTo(0); ctx.newLine(); ctx.jumpTo(2); }else { ctx.newLine(); } for(var i = 0; i < pb.length; i++){for (var a = 0; a < pb[i].length; a++) {var char = pb[i].charCodeAt(a); ctx.keyPress(char)}}"
]
}
]
}
transform input text before outputting it using formatters written in javascript,
// This example is in typescript for clarity, and user-repl.js needs
// to be in the type of archaic javascript that max understands but
// hopefully you get the idea.
// See examples/custom-formatter/user-repl.js for a full pure javascript
// implementation of a custom formatter.
//To create a lot of extensions for the repl it's recommended to look into
// using typescript, transpiling and generating your user-repl.js file.
class UppercaseFormatter implements TextFormatter {
id: string = "uppercase"
format(strArr: Array<string>, ctx: {}): Array<string> {
// Example implementation that returns all strings in uppercase
return strArr.map(str => str.toUpperCase());
}
}
glrepl.manager.preloadFormatter(new UppercaseFormatter);
// then include via repl json config: {"settings"{"textbuffer": {"formatters": ["uppercase"]}}}
playback text files a keypress at a time to build up application state, and loads more. If you do want to extend the REPL with code by writing text-formatters, programatically binding keys, or extending the core you should read the README.md file included in the package and on Github, as it will explain how you can get started with this in more depth than the included help and overview patches. If you develop any cool core features please do make a pull request on Github!
You can download it now from the Max package manager in the app, or from my github. It's recommended that unless you're a developer you download it directly from the package manager. It's extremely flexible so I'm excited to see what people might do with it.

Full credit to Timo Hoogland for writing the th.gl.texteditor object (which is used in his fantastic Max livecoding environment Mercury) upon which this complete rewrite and extension in Typescript is based.
]]>
Pitch Black Systems is an EP of music originally written around 2008-10 when I was somewhere between my Machinochrist and Ascetic monikers, wondering what to do with myself and what I wanted to make next. Influenced heavily by psychedelic music, from Delerium Records through to more electronically inclined artists such as Ott, Shpongle or Eat Static.
Finished versions of these tracks were thought lost to the mists of time, but a surprise find in my parents CD collection has allowed these to be mastered and released for the first time. Available digitally or as a limited edition digipack CD.
All music by Tom Whiston.
Art by Tom Whiston.
Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
Release Date: 11th March 2022
Label: Vigilanimides VGL005
Download: Bandcamp
Streaming: No
“The Hand becometh these things as it ceaseth to be known unto thine own senses; as Feeling waneth in the Hand of the Artist, so waxeth the Presence of the Invoked”
The Acts of Devotion are four improvised performances culled from the artist's daily magical practice throughout 2020. These performances create a space for the numinous by inviting the spirits to ride the performer during the improvisational process, and were created with the minimum of conscious intervention into the performance.
The standard cassette version is limited to 46 copies and features hand-stamped artwork and is numbered by hand.
A special talismanic edition of four copies, one for each track, is also available. Each of these copies comes with a card and silver foil slipcase of the album art and a custom hand-drawn album cover inside the slipcase, made in response to the track which it represents, and finished with the artist's blood.
Spirit Performance Channel Tom Whiston.
Art by Tom Whiston.
Inside cover hand design by Dana Whiston
Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
Release Date: 6th August 2021
Label: Vigilanimides VGL004
Download: Bandcamp
Streaming: No
To Wound the Autumnal City draws inspiration from Samuel Delaney's Dhalgren. A speculative hauntology of an acid-fried garage psych-rock band who never left in Bellona.
Side A and B written in 2009/10. Bonus Track 2021
Available as digital and strictly limited cassette edition of 50.
Music and mixing by Tom Whiston.
Art by Tom Whiston. Cassette Print by Dana Whiston
Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
Release Date: 1st June 2021
Label: Vigilanimides VGL003
Download: Bandcamp
Streaming: No
Inspired by the excellent SigilPen this max/msp app will let you map any pattern, including magic squares, to a linnstrument. It's "unique feature" compared to other linnstrument apps is that it can span any pattern drawn across the whole playing area, and optionally collapse it down to a single octave, I've found that this is really useful for exploring different patterns.
]]>
A deep listening meditation for invoking Baphometic energies
Headphone listening recommended
Performed 25th - 31st May 2020
Hurdy-Gurdy and Effects - Tom Whiston
Mixing - Tom Whiston
Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
Release Date: 31st July 2020
Label: Vigilanimides VGL002
Download: Bandcamp
Streaming: No
A new EP of music about my time living in Zurich, made largely with samples from around the city.
Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
Photography and Artwork by Arnaud Marquis - arnaudmarquis.ch
Release Date: 14th May 2020
Label: Vigilanimides VGL001
Download: Bandcamp
Streaming: No
]]>