{ "version": "https://jsonfeed.org/version/1", "title": "Tom Whiston - Sonic Arts", "description": "", "home_page_url": "https://tomwhiston.com", "feed_url": "https://tomwhiston.com/feed.json", "user_comment": "", "author": { "name": "Tom Whiston" }, "items": [ { "id": "https://tomwhiston.com/hacking-rnbo/", "url": "https://tomwhiston.com/hacking-rnbo/", "title": "Hacking RNBO Operators", "summary": "Thanks to a hint from a colleague I realised that it's possible to hack new operators into RNBO, however it's completely undocumented and there…", "content_html": "

Thanks to a hint from a colleague I realised that it's possible to hack new operators into RNBO, however it's completely undocumented and there is no official SDK for this, so we need to do a bit of learning and reverse engineering to make this happen. In this post I'll try to outline what I've learned to help others with hacking in their own objects. This document is far from complete, and is more an overview of what is used in the existing c74 implemented objects than an exhaustive guide to what's possible. Hopefully it's useful as a primer though. It's worth noting that this is completely unsupported, and its possible that everything described below will change in future, so this is only really for you if you're comfortable experimenting.

\n

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.

\n

The Basics

\n

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).

\n

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.

\n

Simple Operators

\n

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:

\n
\"log\": {\n\t\t\"exprtext\": \"rnbo_log(in1)\",\n\t\t\"safeexprtext\": \"rnbo_log(in1 <= 0.0000000001 ? 0.0000000001 : in1)\",\n\t\t\"digest\": \"The natural logarithm of the input (log base e)\",\n\t\t\"alias\": \"ln\",\n\t\t\"comments\": [\n\t\t\t\"@seealso rnbo_exp\",\n\t\t\t\"@seealso rnbo_log10\",\n\t\t\t\"@seealso rnbo_e\",\n\t\t\t\"@tag RNBO\",\n\t\t\t\"@tag RNBO Operators\",\n\t\t\t\"@tag RNBO Math\",\n\t\t\t\"@tag RNBO Powers\",\n\t\t\t\"@category Powers\"\n\t\t],\n\t\t\"meta\": {\n\t\t\t\"in1\": {\n\t\t\t\t\"displayName\": \"Input\",\n\t\t\t\t\"digest\": \"Input value to be processed.\"\n\t\t\t},\n\t\t\t\"out1\": {\n\t\t\t\t\"displayName\": \"Natural Log Output\",\n\t\t\t\t\"digest\": \"The natural log of the input.\"\n\t\t\t}\n\t\t}\n\t}
\n

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.

\n

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.

\n

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.

\n

Header Metadata

\n

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.

\n
/** Add an amount to the current count every sample.\n * @seealso rnbo_accum\n * @seealso counter~\n * @seealso rnbo_counter\n * @tag RNBO\n * @tag RNBO Generators\n * @category Generators\n */
\n

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.

\n

Class @meta

\n

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.

\n
//complex_conj.js\n@meta({\n    digest: \"complex conjugate\",\n    internal: true,\n    publishAsObject: false\n})
\n
digest = \"\";
\n
The digest string provides a brief description which will be shown in the object autocomplete box when adding your object to a patch. It is not a required field and if omitted max will simply show the object name as the digest, as is the case with the counter object.
\n
 
\n
\n
dsponly = true;
\n
You can set the 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.
\n
 
\n
\n
\n
publishAsObject = false;
\n
What this does is currently unclear to me, as even with this set to false the object is available in rnbo.
\n
 
\n
internal=true;
\n
\n
Setting this to true will make the object unavailable inside rnbo~ patchers. Thus it is useful for operators which should only be called from inside other operators.
\n
 
\n
\n
\n
alias = [ \"+=\", \"plusequals\" ];
\n
The alias field is an array of alises for the object, which can also be used to instantiate it in max, the above example is taken from the accum object.
\n
 
\n
\n
aliasonly:true
\n
\n
\n
\n
Setting this to true will only make the operator accessible via it's aliases, and not the name given to the class.
\n
 
\n
\n
\n
allowInline : true
\n
If this is set to true the compiler will be able to inline this class when compiling code. This is done by using the __forceInline(op) function, see below.
\n
\n
\n

Class Data

\n

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.

\n

Stateless

\n

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.

\n

Stateful

\n

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.

\n
\n

Shared

\n
\n
\n
 
\n
\n
@option
\n
Options are fixed attributes which are exposed to the inspector panel in Max, an option includes metadata such as a digest string and a longer comment string, which will be shown in the inspector. It is then followed by a variable and it's default value. In the counter operator we have the following simple example of an option
\n
\n
@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;
\n
\n
 
\n
The [accum] object provides a couple of examples which are worth looking at so we can understand more complex use cases for options.
\n
\n
\t@ENUM resetmode = {\n\t\tpre : 0,\n\t\tpost : 1\n\t};\n\n\t@option({ digest : \"Specifies whether the count is reset before (pre) or after (post) the accumulation.\" }) resetmode : accum.resetmode = \"post\"\n\t@option ({ digest : \"Specifies the minimum value for the accumulated output.\" }) min : number = 0;\n\t@option({ digest : \"Specifies the maximum value for the accumulated output.\", optional : true, doNotShowInMaxInspector : true }) max : number;\n\n    value : number = options.min;\n
\n
\nHere we can note a few interesting things:
\n
 
\n
1. It's possible to give an option a type, in this case 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.
\n
 
\n
2. It's possible to create and use Enum objects for options, and if we do so then they will show in the inspector with the 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\"
\n
 
\n
3. It's possible to add options but make them optional and also to hide them from the end user. This is given in the metadata by specifying the fields 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.
\n
 
\n
4. We can see that this class has a stateful value which references one of the options when the object is instantiated. We can do this by using options.optionVarName 
\n
 
\n
Looking through other options there is one other important value to note, defaultarg
\n
\n
@option({ digest : \"Buffer to use.\" }, defaultarg: 1) buffername : symbol;
\n
\n
In the context of class level options defaultarg specifies that this can be set by the first argument value added to the operators instance
\n
 
\n
It is finally worth noting that there is a shorter form of option specification as follows
\n
\n
@option interp : buffer.interp = \"linear\";\n@option channels : number = 1;
\n
\n
 
\n
 
\n
@ENUM
\n
As seen above we can create enums in RNBO. Enums also have some metadata which can be added
\n
\n
//bufferphasetoindex.js\n\t@ENUM({ scope : \"buffer\", global : true }) indexmode = {\n\t\tphase : 0,\t\t// 0..1\n\t\tsamples : 1,\t// sample offset in buffer\n\t\tsignal : 2,\t\t// -1..1\n\t\tlookup : 3,\t\t// -1..1\n\t\twave : 4\t\t// between start and end\n\t};\n\n\t@option indexmode : buffer.indexmode = \"phase\";\n
\n
\n
As we can see above scope affect how you reference the enum values elsewhere in your code.
\n
 
\n

next()/compute() @meta

\n

The next and compute functions also have a @meta field

\n
@meta({\n\t\tx: {\n\t\t\tdigest: \"Input to be accumulated\",\n\t\t\tcomments: \"{@variation accum Values}{@variation accum~ Signals} sent through the left inlet will \\\n\t\t\tbe accumulated as long as the calculation is not reset.\"\n\t\t},\n\t\treset : {\n\t\t\tdigest: \"Non-zero input resets calculation\",\n\t\t\tcomments: \"A non-zero number will trigger the output to reset to 0 on the next input value\"\n\t\t},\n\t\tout1: {\n\t\t\tdigest: \"The accumulated output\"\n\t\t}\n\t})
\n

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.

\n
 
\n

Values and functions

\n

The classes also seem to have the following variable values and functions available to be called on this

\n
 
\n
\n
this.sr - get the current sample rate
\n
this._voiceIndex - report current voice index (0 if not in a polyphonic context)
\n
this.vs - audio device vector size
\n
 
\n
this.convertToNormalizedParameterValue(index, value) - in the tonormalized operator this is called directly on the class.
\n
this.convertFromNormalizedParameterValue(index, normalizedValue) - similarly called on the operator class in fromnormalized operator.
\n
\n

Exprtext in classes

\n

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.

\n
@meta({\n\tpublishAsObject : false,\n\t// since history is special in the way, that it wants the last and not the actual value\n\t// we need t define our own expression text\n\texprtext : \"var h = new history(); out1 = h; h = in1;\"\n})\nclass history extends StatefulOperator\n{\n\t// take care when renaming this state variable - it is used by name in gen.js\n\t@meta({ defaultarg: 1 }) value: number = 0;\n\n\tgetvalue(): number {\n\t\treturn this.value;\n\t}\n\n\tsetvalue(val: number) {\n\t\tthis.value = val;\n\t}\n};
\n
\n
\n

Types

\n

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.

\n

Types are added to variables using a Typescript like format of var : type

\n

int - variables instantiated without a type are by default int, but this can also be made exlicit if needed

\n

number - The number type is a double

\n

symbol - string type used for buffernames etc..

\n

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.

\n
//average.js\nresetFlag : boolean = false;\nwantsReset : boolean = false;\n...\nnext(x : number, windowSize : int = 0, reset : boolean = 0) : number\n{\n...\nif (reset != 0) {\n  if (this.resetFlag != 1) {\n    this.wantsReset = 1;\n    this.resetFlag = 1;\n  }\n}\nelse {\n  this.resetFlag = 0;\n}\n...\n}
\n

void - used to indicate there is no return value from functions

\n

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..

\n
\n
 
\n
Index - used for indexing channels in buffers, see buffernormalize for example
\n
 
\n
\n
SampleValue - Value written to and read from a sample buffer
\n
 
\n
\n
SampleIndex - Used for indexing sample positions in buffers
\n
 
\n
MillisecondTime - mstime has its own type, as seen in the currenttime operator
\n
 
\n
list - list types are lists and have a number of functions which can be called on them
\n
\n
\n
\n
//found in append.js\nlet a: list = [];\na.concat(b); //if your input is a list ( a : list ) then you can concat another list to it with this function call\na.push(c); // push value onto list\na.pop();\na.shift();\na.unshift(x);\na.indexOf(x);\nvar l = a.length; //can get length like this. note not a function call
\n
\n
\"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
\n
\n
//bufferplay.js\nnext(buffer: \"T&\", trigger : number, start: number, length: number, rate: number, channel: number) : [ number, number, number ]\n
\n

CONST

\n
\n
\n

Types can be declared as const in function signatures as follows

\n
//bufferreadsample.js\n\tcompute(\n\t\tbuffer : \"T&\",\n\t\tsampleIndex : \"const SampleValue\",\t// declaring these parameters as const actually helps the inliner\n\t\tchannel : \"const Index\",\n\t\tstart : \"const SampleIndex\",\n\t\tend : \"const SampleIndex\"\n\t) : SampleValue\n\t
\n
\n
\n

Inline

\n

It's also possible to inline a return value

\n
// cubicinterp.js\ncompute(a : number, w : number, x : number, y : number, z : number) : \"inline number\"
\n

External Classes/Functions

\n

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

\n
// average_rms.js\nav = new average();\n...\nreturn safesqrt(av.next(x*x, windowSize, reset));
\n

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!

\n

Inbuilt Functions

\n
clamp(x, min, max); //standard clamping function for min/max bounds\nclip(x, min, max); //clip a value\nwrap(x, start, end);\ntrunc(x); // truncate a value\nfloor(x);\nround(x);\nfract(x);\nabs(x);\nfabs(x);\ncos(x);\natan2(x);\nsin(x);\nexp(x);\nsign(x);\nlog(x);\nlog2f(x);\nmax(x, y);\nmin(x, y);\ndiv(x, y);\n\nisnan(x);\nfixnan(x);\nfixdenorm(x); //Replace denormal values with zero, presumably works like the gen function?\n\npow(base, exponent);\nnextpoweroftwo(x);\nsqrt(x);\n\npi01(); //used only in phaseshift.js\n\nuint32_add(x, y);\nuint32_trunc(x);\nuint32_rshift(x, shift); // is there an equivalent uint32_,lshift(x, shift) ?\n\niadd(x, y); // pink.js\nimul(x, y);\n\nmix(a, b, mixamnt); // mixamnt is a phasor value in rand.js\n\nupdateDataRef(this, this.buffer); // always needs calling after buffer.setSize(x) is called on a class member. see average.js for example\n\nevalexpr(expr); //see delay.js for usage\n\nt = globaltransport(); //get the global transport object\nt.getBeatTime(); //current beat time\nt.getBeatTimeAtSample(offset);\nt.getTempo();\nt.getTempoAtSample(offset);\nt.getTimeSignature();\nt.getTimeSignatureAtSample(offset);\nt.getState();\nt.getStateAtSample(sampleOffset);\n\nsystemticks(); // pink.js\n\n//noise.js  - this.state is a FixedUIntArray(4);\nxoshiro_reset(seed, this.state) ; // in noise.js the seed is systemticks() + voiceindex() + random(0, 10000)\nxoshiro_next(this.state);\n\n//random.js\nrand01();\n\nconsole.log(...args);\n\n__forceInline(functionCall) // eg sampleIndex = __forceInline(bufferphasetoindex(lookup, 0, bufferSize)); 
\n

Classes

\n
// found in allpass.js\nd = new delay(44100); //create a delay line of a specific size\nd.read(x); //reads a delay value at a specific delay time\nd.write(x); //writes to the delay buffer\nd.step(); //steps to the next location in the delay buffer\nd.init(); //needs to be called to initialize the delay line, should be called from an operators init()/dspsetup() function\nd.clear(); //clears the delay line\n\n//average.js\nbuffer = new Float64Buffer({ channels : 1 });\nbuffer.getSampleRate();\nbuffer.getSize();\nbuffer.setSize(x); // don't forget to call updateDataRef(this, this.buffer); after resizing a buffer\nbuffer.requestSize(this.sr + 1, 1); // what does this do?\nbuffer.getSample(channel, i); //takes Index types for both arguments\nbuffer.setSample(channel, index, value);\nbuffer.setZero();\nbuffer.getChannels();\nbuffer.setTouched(true);\n\n// pink.js\nrows = new IntBuffer({ size: 16, channels: 1 });\n\n//mtof.js\nbuffer = new AutoAudioBuffer({ buffername : \"RNBODefaultMtofLookupTable256\" });\n\n//cycle.js\n//MultiAutoAudioBuffer appears to have the same set of functions as buffer, with no additional arguments\nbuffer = new MultiAutoAudioBuffer({\n\t\tbuffers: options.buffername,\n\t\tupdateFunc: \"bufferUpdated\"\n\t});\n\n//fftstream.js\nlet fsa = new FixedSampleArray(size);\nfsa[i] = x;\n\n//noise.js\nstate = new FixedUIntArray(4);\n
\n

 

\n

[pulse_counter~] object

\n

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.

\n

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.

\n

We can do so as follows:

\n
/** Add an amount to the current count every time a pulse is received.\n * @seealso rnbo_accum\n * @seealso counter~\n * @seealso rnbo_counter\n * @tag RNBO\n * @tag RNBO Generators\n * @category Generators\n */\n\n@meta({\n\tdsponly : true,\n\tdigest : \"count incoming pulses or gates\"\n})\nclass pulse_counter extends StatefulOperator\n{\n\n\t@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;\n\n\tcount = 0;\n\n\tcarry : int = 0;\n\tcarry_flag : bool = false; //carry flag persists in this version because we sometimes return early\n\n\tlast : number = 0;\n\n\n\t@meta({\n\t\ta : { digest : \"pulse train or gates in\", comments : \"Every time the input goes from 0.-1. 1 will be added to the count \" },\n\t\treset : { 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.\" },\n\t\tlimit : { 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.\" },\n\t\tthreshold : { 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.\" },\n\t\tout1 : { digest : \"current count (running total)\", comments : \"The current value of the counter as a signal.\" },\n\t\tout2 : { digest : \"carry flag (counter hit maximum)\", comments : \"Outputs a signal value of 1. whenever the limit value is reached, otherwise outputs a 0.\" },\n\t\tout3 : { 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.\" }\n\t})\n\tnext(a : number = 0, reset : number = 0, limit : number = 0, threshold : number = 0.99) : [ number, number, number ]\n\t{\n\t\tif (reset != 0) {\n\t\t\tthis.count = 0;\n\t\t\tthis.carry = 0;\n\t\t\tthis.carry_flag = 0;\n\t\t}\n\t\t//Ignore negative signals\n\t\telse if(a >= 0) {\n\n\t\t\tvar floored = (a > threshold) ? 1 : 0;\n\n\t\t\t//only add to the count if this is a new transition\n\t\t\tif(floored == 1 && this.last != 1){\n\t\t\t\tthis.count += floored;\n\t\t\t}\n\n\t\t\tthis.last = floored;\n\n\t\t\tif (limit != 0) {\n\t\t\t\t//TODO if count is higher than limit (ie limit changed) wrap to the correct value\n\t\t\t\tif ((a > 0 && this.count >= limit) || (a < 0 && this.count <= limit)) {\n\t\t\t\t\tthis.count = 0;\n\t\t\t\t\tthis.carry += 1;\n\t\t\t\t\tthis.carry_flag = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [ this.count, this.carry_flag, this.carry];\n\t}\n\n\tinit() {\n\t\tthis.count = options.init;\n\t}\n}\n
\n

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)

\n
\n----------begin_max5_patcher----------\n4945.3oc68j1iiiicetpeEL5KoGLU4Q7PW6tYwljMAnAxjdwdjuLSCCZaZa0\nQVRQG0wNX6e6gGRxhVxxRtjbUcMFCPMsIond7cw2Eo9kauwXQzSrTCvuA7Sf\nat4Wt8lajMIZ3lheeiwN5SKCnoxgYDxdLZwWLtS0UF6oLYyAQzU6XoofnXVX\nYuwzrka8C2LOgsLS8RfNyLuCfLE+0S9WDZlI3yEOQX9N+v.Vl7cA22XTd1gs\n5uR9h4.y8VkuP0vxdNlodaFFfOK54eb6sh+b2YsFeflDR2ImQijvEQeswhW1\nJ3Oj4ye6f37fT17kQ4gYrjxgF3GxjMIFO5TnGKawecIh+hsdwnG3QQOA9o7W\nb4rKVFOvRR8iBkOKbFdFViVxWPJrVAZ6Fi09ArZOSwa+FCZbbsluo1iHv0eI\nRNQt2U0jenpI6plRXO3W97jpVoIbTVFGekmnnHOYSL1OMQqXIg49RPQ0HmpW\n.RR5qfRlFSWtmbV9zF6oBVdXApmX6IYScj+OaTMJAmCZSPzx+WlDKaV1nf42\nOLNgkxBynYEPeU2qXqo4AYyWGElk5+2kv.jy92V+qKfwV6rhe7+hlseArIwe\nUTnT.rNoPzb4aiygYIVLPs0hbDgz3VdXN+CGsbjNS4qw7zEzDAkZQ.qFyMW5\nHJJPuqpmKfsNqn6X+vvCPhYQwGuyD+Ma63YWDw6bWWysrmz44gpdmyYJxlmR\nePGBynAAEZAzm9mng96nYrLeEE.YV0IKjxWnaSWlDEDnMapddnkdVwYxWxdz\neU1V4KpNu.e39wk7PFUT4U9aXoY5skQ2jp2RZ1yJjdslxWTHDOOisKNfuJzG\n.W7fqOHcazioECrjOaO9SnhS9Tsol6fsSpKzWWkqV6Gp5Me2BVxWqDoaWMoq\nZmCOq8ZKQ3ZbzRMSGSNQhK72niukpLEKkGnJJjodeZZdQG7ZNTVsTUzgiUWS\nc8dpqs1y3fouEUEspMmunB4P+c.i07si2qXuR49b9FVIz4zrrD+E4YJJ0MMn\nHchIDLfoblmmKWf7s.R37i2o839Y9zferbD6h3Mv0uWMjJkxUPVdn++WNq.M\nTvDLuI5PN1TVhOUSNppqJc7ZLS2V8Zus58Oo7mND4FHVDu2gLnXqqLn0XP0Q\nGGvfhdixfZoLt7cJCJ5JCZcFTTGLn3KOCZcmqZ5D4BZ3lSv7BckZWwjt19uM\nWkNt6RGxBgz10oM2J6D8WtPl2btF1NXUXjQj.rLZ2Nt6IsRAdbKM6eNEjv31\n+9LHjaaJvOEjwaEDkmHbtOgqGHQzHMHMBPef5GHLtE3GxGFCrjywxAiu9OUe\n5ay62iQbIDkauX4+C0zI3gPcMOB008xqwkfrd+pwEcUiacMtntz3Rt3ZbKkI\n6GGJ1CoD.kghBhs6hG828iQgQx3or52abLVOROX8zYyvGw0HmWNaVi+k1haY\nI4+OHhsACDuD7u.36XoGUuO7c+1eN4mEqOfrE9XhWNKjyo9A+P3cbcgHwevh\n+PTikCQP9vjC+mL+bYanp1fUsgqZC84eqw4JN7Vdkv0kQWy1QUA8.VuC1SwI\npnRbbAO+v0QGaYGxXqR+XHeMGtj8whAp8FnIajAAr9LTeJDABLkko0sV+7of\ni8+TxeJIJ9vYWO9L+YwDw2nL.nnAzvUfXZBcGeK0HPgFG.WmUNGbzmE+TZfO\n8vMwjubQ6kAYoNyaAjQWtkdP.2plS4Kuk4bEKlqGfChqZoyRwHCMCQNfvH0f\nBGCjlXZFEbAG2mcDLgNZxbTPSR8cs7tpPeJM6ch.QiCBD8qVDHdbPf3e0h.I\niCBj7qTDnXyww.CJmmIXGgwAOoLupKDUgsUchnPiDhB8dGQgGIDE9cMhpzxw\n9gnPGEQImmCAcYpW4VN1pUUeKnZaOd74cKh5BOV2z7WJt7ukx.h4CHlPf.yx\nRE4zOE7A1rMy9MfU9pj7CV7LvD7nO2T4vHtuHIzzse2LiuowzKh334VYj9eD\nV7KGSVB+enQKtsc5Beuo37LsTq1jrT4VdSanqfJZtVTkNow2aiZSrb09ZePm\nSpOvF5bgsCevWDTfOWn.OlPA4bgBROgBMoAgd1AvF0hUHGpOdHVv7BPTsrM+\nPfDznBI3W.jfOKx1VVPbEDzR37Jz.9o0U6fo2qdHFacBJCs+gZzLJJiqOJAR\nOOOGLlf0FAWCZHeM9mT0LxG+j9p4DwMsDXl2L.eCKUIUqpQLxohJZjtg0ZpR\nLm440yDU4nBrJ5zIpBMzDUgMGZhp5a.DqVn8ND3EXq4MgqgQIKw5WnbdoXeA\nb7wlMrDP1VtQJaiBVcBhKRkCGrCREmbYdcPvwMQUNWNtYGqSsfgtSM2Lx5sI\n2L5ETiGWXt4zG84TNQ7kEoiUP8jgclaUMSkGWUVZEw5uxb6MQf7X.uI+.vyQ\n4.5pujmVaj0kJDyLMg25J.mm4dyYtGMSuVcyOgMkBPPn8LKQEoJyzqi6nJ.A\ncuPktv2CfmR7oPQgZch7DK5QV7AhlJwmBu.58t5e+7lvyv1D36GSBWVzlMAr\ndRfD5v4jFR4e6G6nzWQFWTYtpZe6NAmGi.1s9O+vWRBxiqxqkl0aQIqXICS0\nnBcNuIDOLRbAU4xHflxB3T5njuBPmxpMhhAvTU1+NmdeN7f2mynGIqtAkdpy\n6YTXz50iQrF+q4IgofOJhXA3SqWChR.+4HAqoXyiOIcA8cc9ANQPefcGt2wf\nBLUHYyuIBE7HkIZEC7a37vbR9wdfoPiGlB8dFSsbaj+xCzddlQE++Vpk.DsF\nTDT22uYa3rTHNJg.WsY1YEb0gu80DDj7yNvqsq0ZZBXe+fEzkIT0mKP1JE8L\nhKakElGMvrsOBcihaeL8K5r264YRbrLIjQK7rUfy7F1sNLi82uttL16GuklF\ncxpxFZgq4TtEbBhok2YYqeu8pSbJjKJK79RSKPMya.cCihVhgGQ5Y5RNi9oo\nYpPshkgiDZpHclGPzDmo4kQAJQlehidvV1t1HGhkomqI16NdSVPrCxh3YB8r\nMcbEMUeHHnsnodxLrjFrb+gbPFb3ZnOgHXIjbDdriDMqE4q2WkytZy55M0Vg\nRdWyp+bXfnWrQCaXhqu1kKcXMTAACEMgfPGahiokqKBagahMZ4RX3LJU4Nvb\nMW+8VykjWZd6fV+Uao3Hubm+I4knABeBYfhZz2QRzcHSvIfh3LvXIN0QmPZ8\n2X3rx+FWVTFe7hD9TDf9YeS5s7Icr4DEscY1IFA75OplJviaYgfhKP.wAESv\nUyVM68mWNmNREx6WkS37SWkhEG8jkGKIgi.Ap9z8l0S8NXbGoRiJ93tJzBpY\n3JNFlyfuDmdZlP2Sx8MIhqmgyRka0cTekZc.MObrMFRO8TBaCMcsgVtilmRk\nPy7l6dN7y7qbYcwOippqUEhiJurj2WmQUx0qsh5mQURWFEa8F8Zq.pt8pJNi\n3u63PIW4PqygR5fC09U3TTe7htQkY8S3xli5RqnnnWbj0+wHWtXjKdQSX6V6\np33UolIPleqUyDHyyuZxF+ZlnK95eG39625mwszUcfkWlv3dCnJJriV1Wj9H\nG3XJ3+sjrM1jwsluHW3ZsaAK6QF2H5czvbZ.P3DAeIqNs2hCaO2gApe34dgn\nnB0CTo1vxbBtNTfluYJRNRw96SVIx471pB4bNeMAW9BjSQbfudkGm82ZZ5sO\n+adqKZwwUE34SUC3PaIOfKpukF2vi670vNeMryWC670vNeMryWC670vNOvvN\ne9lSNIQcdLJG+hPKnNscSR03iuVM9WqF+qUi+0pw+Z03esZ7uVM9WqF+qUi+\n0pw+afpwGe9V6ewqFe+XF.Z1qjIBUU0JwcJttTd6EdwQw3n+0vmyDHSgNwEL\nvJV.8Y1p2eakMR17adAtBO+iJZ.nUH5apqdwSdqmV9wPaTPY.4r0EmK5Wydq\n1Eq65f7zsiAg3ujEEqThHyhsZyevx7jDVXl5BK+a+6q7zLEZX7PVmGNgaxAi\nlX7VB0T.QWFbCrEbSoIZBRzaW1oo9t5rcUBi4t9itqRsn7t2KmyS8+k9hurW\nqhisu+Y3Hivz4i5CSiNO3HZdX28yyEGLwzBaQFua4QAfLuoA3C7.mJVMWF+U\nx.ly.vY8yeE0WJmhK.vyrZHN1mxttyMgnheeExLwHYgrxUefrDa.HvGTRLe2\nkHk8uQMjdrtU70vrvdhYQuqcQQk77QIhw+0srBs6hnFywd4hJDMku+aFKgKs\nKBMo1PJjymA94r8+2+AGMUzCHkEtJU7C.yOSjPbJ3wsQAkywc.YY8emrzREA\n.Ujh86zlMZJveEGJ7W6y26Ywy.087dMn6C97ogOE2AB9NcH4iqEvaBiOX9aL\nZ+ybWs1ydLpbcvmjnrshE1ia84KBAzygXILN6cb3ykE1vkszP9oxhCYucGEv\nwgHEsKGbgvO2lvU7ApJ6wWsZE4kXI0Xr+vDcmiOHP+LU.eF1JVnk6nlK1V+G\nT1omSftgPLlfbsbfilAiYyaZ9y.ucIuTUxh+lScr0r8pcgRhMmhKNY7zdKyv\noMpU6tzdWGzBDy7l.2.u+MDX2QjPxWkYQg8xtd0QT404LMA6jbppLyt+DDqV\nnyaNaCC+WfutLhRBJvWO4sPtYsarIh4KodvNhSWPaiA8sD8DTB4pZdyIdXDB\nEt4s4csz4cmHcVW4Q0IS32t23PvWRtYG8qbHtk4bjc+NZGP0IMyxbXQyXbt9\nqgS0oyhC096x2oNTigQgreulcNbrUWcWbpl+OiR1QyJwnTcSkFuyOjxVw4MQ\nHCSgwAtDe49Pmq9NmaZ8d7H56d8D5W6D561A2oyk+JjnixOo92759w.ST604\n1in5RF72wbiw3aU9.4ZTFvOjqIRMj17F.9.CduNInE5d0hSd7n0BvvdVAI8R\nze6LDoQ4IKKQjE2uk.cR2JtO59gzLt6q0FG2Sete5etWriCEFb5ILfUiaRfA\n6dBCNSHLP5KhP7Y.CfzF3N+UwQbUeErEPSnv4Lrm5XwWbKTn8Kh5yKCQcdk0\n+E1Q8scfH+zyH+k1aqZWayWYDQC7PEjAmFHqu.FbB4Sv8EJPSoDCF2WwVxTC\nEvAfKNtLim7JmAZqtOIfdJYk5+BYV9MXxp3GS0pxbHZBvcpIvCIf1dHs6Xgd\nUj16+hsuq0hPfntlLz+E1S4oNhnzO5dr05zn+nuxLvl6K+VAxbNkbjkB0phi\nKAoBpq9ubkUD.AqFo3WWTFtAvucD.CMI.FpuViUJxLMPg0XIOhPNmk73zrrv\nCvLyoxTWDru.wTt0MZPau.6hD63nDgUW6IpB8Q6GpqypJKPcFlEnVDyWk8jP\nCYOI3kTOMZ.bwc6hfUQ9aTegGLQU+5s3xAdRy2JWOXRIKV4utjLNs3Z1QnOV\nSn.dKxsGAJPSMTzK+TlTnvd.g3.MU.Qu2ScRQEnAfJlLfnuaAdwMAt2PVKrT\nSsZkdBYtWbbVOALq940PoCBZdKnR0Vw9Tp64jC90qrN+dprkLop7649NjiGM\nl8AXtntqlONeTbpeI9ZnMgdHOBDcm1+BaI9mG7Tr.lnhKO3CVjC27PGjssMm\nxiTevhrrrs77fDSnsCR8AKB53Z63hbHPKhyAyKc4xFSKAah4Vg5RfXOjkbZM\nsQXSKLFx+epO5Qt1HjokoqKjatq6AypnpBiYZyJxxEicMswdVVXSmhEJAwgX\nWSD1ESTvOzC5Q7bIDrsI4focwl09AAxocdUk1odKsMjQmz0XxgS5ripvc13B\n5qoIwhHmKSrqimSGSAMbiJK6Hm84.TaDwIQwQIkhGb5gWqySdVzlD5JeVXld\nVWMXOIddNvFt1e+MAWoPpwCoY2SyuWMp8Rw6Ew0Fv89gosKpGEyBUiYkeh7T\no+bipKHHeie3b8jIceQxjtW0aqoRrDVejsnC.8KoQg221PFHXxwpr4smwqYp\nGcl3U0Fjda8+eIfqNzner1w4n.dL93mJaUTdz5OjJghC8oDorSjFwkzkaY6e\nP8AIXVRoOTuxqMRCowoEETb8Wl.YTIE+u6P9Kkiqj8pnFRqmscQcZDkTqiJd\nw8UGSwzT+VliuAnufCPVmT70QUFda+RoyHMeQUG+sP9lA7sgpN1bFrcKTkcK\nrZ7GrD2KCLetptOZKkoF2pyEpC5MlphSW675urpb6av6IwWOon0YNOEx9H36\ntP4ZXc9BxmyUsRKw06Q55q65YfuDlDjEsZ7nFInd6UH+5fQC7+AI2tGTgCS2\ns.gkvVeziIUGKb0C2XDyQlHhIe6sY6nOIf3FOB2jG4oy13q+veLZo5HV7C+H\n8If6OTRuRa9PQopZrtdGkHDw.Jq2S9VOPlEyAx2D20ygxscjtbwZ7JOBZ0JT\nc7vs0pk71U7HDxWIR4OWQWKUWPoLdTbl+N++dkMXFeppBAZshvpjnxyK.a1B\n5ZDdoy8NVHWtGJr02SE+zBsDtdsMiBoqMZC1p.pN.O4AUOZcSYs8vle3C9o9\nGdXRpMffnvM6KBy5JWpOpcQqJq4FyV5lSqRxN8rTJQfOfnba4eKrgUu.Ijqp\nF1S+K21tczZNpzchAZ7Zoww0TTHeEF6neQYEl7JsToMo5CzgQBSfdUiWVlLF\nzjka8y3rU4IJrwS1piDprnrRBy8KblSRu2e5DRiKpiKY0.c6+31+esfVSQC\n-----------end_max5_patcher-----------\n\n
\n

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.

\n
void pulse_counter_tilde_01_perform(\n    const Sample * a,\n    number reset,\n    number limit,\n    number threshold,\n    SampleValue * out1,\n    SampleValue * out2,\n    SampleValue * out3,\n    Index n\n) {\n    auto __pulse_counter_tilde_01_last = this->pulse_counter_tilde_01_last;\n    auto __pulse_counter_tilde_01_carry_flag = this->pulse_counter_tilde_01_carry_flag;\n    auto __pulse_counter_tilde_01_carry = this->pulse_counter_tilde_01_carry;\n    auto __pulse_counter_tilde_01_count = this->pulse_counter_tilde_01_count;\n    Index i;\n\n    for (i = 0; i < n; i++) {\n        if (reset != 0) {\n            __pulse_counter_tilde_01_count = 0;\n            __pulse_counter_tilde_01_carry = 0;\n            __pulse_counter_tilde_01_carry_flag = 0;\n        } else if (a[(Index)i] >= 0) {\n            number floored = (a[(Index)i] > threshold ? 1 : 0);\n\n            if (floored == 1 && __pulse_counter_tilde_01_last != 1) {\n                __pulse_counter_tilde_01_count += floored;\n            }\n\n            __pulse_counter_tilde_01_last = floored;\n\n            if (limit != 0) {\n                if ((a[(Index)i] > 0 && __pulse_counter_tilde_01_count >= limit) || (a[(Index)i] < 0 && __pulse_counter_tilde_01_count <= limit)) {\n                    __pulse_counter_tilde_01_count = 0;\n                    __pulse_counter_tilde_01_carry += 1;\n                    __pulse_counter_tilde_01_carry_flag = 1;\n                }\n            }\n        }\n\n        out1[(Index)i] = __pulse_counter_tilde_01_count;\n        out2[(Index)i] = __pulse_counter_tilde_01_carry_flag;\n        out3[(Index)i] = __pulse_counter_tilde_01_carry;\n    }\n\n    this->pulse_counter_tilde_01_count = __pulse_counter_tilde_01_count;\n    this->pulse_counter_tilde_01_carry = __pulse_counter_tilde_01_carry;\n    this->pulse_counter_tilde_01_carry_flag = __pulse_counter_tilde_01_carry_flag;\n    this->pulse_counter_tilde_01_last = __pulse_counter_tilde_01_last;\n}
\n

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.

\n

Wrapping Up

\n

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~.

\n

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!

\n

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!

\n
\n
", "author": { "name": "Tom Whiston" }, "tags": [ "blog" ], "date_published": "2024-09-18T12:06:42+01:00", "date_modified": "2024-10-26T16:56:01+01:00" }, { "id": "https://tomwhiston.com/rnbo/", "url": "https://tomwhiston.com/rnbo/", "title": "Exporting multiple RNBO patches using JavaScript", "summary": "If you are developing a modular system using RNBO you might have quite a few different patches inside your max patch which you wish…", "content_html": "

If you are developing a modular system using RNBO you might have quite a few different patches inside your max patch which you wish to export at once. Before the latest release of RNBO this was time consuming as you would need to send export messages to each patcher individually, and more importantly you would need to wait for the compilation to succeed before exporting the next patch, which meant that it could not easily be automated. However with the release of 1.3.2 the new 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.

\n

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!

\n
\"\"
\n

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.

\n

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.

\n

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...

\n
inlets = 2;\nvar rnboPatchers = [];\nvar rnboRemote;\nvar targetName = \"c74rpi\";\n\nfunction setTargetName(tname){\n\ttargetName = tname;\n\tpost(\"target name set to\", targetName, \"\\n\");\n}\n\nfunction bang() {\n\tif(inlet == 0){\n    \tthis.patcher.apply(search);\n\t\trnboPatchers = uniq(rnboPatchers);\n\t\n\t\tpost(\"Found the following RNBO patchers \\n\");\n\t\tfor(i = 0; i < rnboPatchers.length; i++){\n\t\t\tpost(rnboPatchers[i].getattr(\"title\"));\n\t\t}\n\t\tpost(\"\\nStarting export\\n\");\n\t\ttriggerExport();\n\t\n\t} else if(inlet == 1){\n\t\ttriggerExport();\n\t}\n\t\n}\n\nfunction destroyAll(){\n\tthis.patcher.apply(search);\n\trnboPatchers = uniq(rnboPatchers);\n\tpost(\"DESTROYING\")\n\tfor(i = 0; i < rnboPatchers.length; i++){\n\t\t\tpost(rnboPatchers[i].getattr(\"title\"));\n\t\t\trnboRemote.message(\"patcherdestroy\", rnboPatchers[i].getattr(\"title\"));\n\t}\n\tpost(\"\\n\");\n}\n\nfunction triggerExport(){\n\tif(rnboPatchers.length == 0){\n\t\tpost(\"\\nno more patchers to export\\n\");\n\t\treturn;\n\t}\n\tvar obj = rnboPatchers.pop();\n\tpost(obj.getattr(\"title\"));\n\tobj.message(\"export\", \"oscquery-export\", targetName);\t\n}\ntriggerExport.local = 1;\n\nfunction search(obj) {\n    if (obj.maxclass == \"rnbo~\") {\n\t\trnboPatchers.push(obj);\n    } else if(obj.maxclass == \"rnbo.remote\"){\n\t\trnboRemote = obj;\n\t\tvar tname = obj.getattr(\"name\");\n\t\tif(tname !== \"\"){\n\t\t\tsetTargetName(obj.getattr(\"name\"));\n\t\t}\n\t}\n}\nsearch.local = 1;\n\nfunction uniq(a) {\n    var seen = {};\n    var out = [];\n    var len = a.length;\n    var j = 0;\n    for(var i = 0; i < len; i++) {\n         var item = a[i].getattr(\"title\");\n         if(seen[item] !== 1) {\n               seen[item] = 1;\n               out[j++] = a[i];\n         }\n    }\n    return out;\n}\nuniq.local = 1;\n
\n
----------begin_max5_patcher----------\n26449.3oc68t2biiijun+cUeJ3USbhp693RMAeqMNwF8LcUy8Vmc5GQW8r23\nF0LgVZIJa1MEoFRJ6x8Fm5y9EOHo3C.PPIRYY6bhdJaKBABjIPhLS7Ky7+90\nuZ10IeNHal1+l1mzd0q9ue8qdE8iHevqJ96WMaq+mWE4mQa1rUIa2FDmO6J1\nyxC9bN8yu1O9Fsrb+z7Ls7aCzB97tjzplsyOe0sgw2rLMXUN60sXt9UZllNj\nefr0I+v.+uZ+yhuR3ZZ+lb8u8VCyx9Id+1v3nfb5XAc3CS1mW9o5jO7+yqeM\n4et5DmT+udq1udaXl118Y4ZWGnsJINFOABVqkmPmkYA3OZsFcLokro1LOHUK\naUZ3tpNMJD+US1GS6YSwzEKCFsvaAixXR9gkq.JC5Qhx7gXMesnfLLyNM7tP\n+H7z1e6tn.s6uMHMP6gj8Z93ed89vn0gjkFIaCz98PLwBSl1lrdejepVZ70I\nZYOjkGrk9M1Fdys4Z25eW.o2SnjTJIJHM6JMe7WFSfennYX9Q1tz.+0Z9qRS\nviDbOjpgG8ruh1ljTberBOzxCxxIigc6S2kjEjMW6+2aCh0VGbWPTxN5niNF\nxzhB+8.76.yxCyeSl19rfM6iHL6hwz89OP9KFKVyOJh8RIyiuTMR07wC73UA\nywKdBvqchxCeewZh4+VVwxBsU9wZ2FDsiNww845D5KdtF2ELt8rQxlslgtzA\noKXeDZwizpk++vSw3.11FFkLlRylmFrMIO.u+gQGyS1gWTg4J0XgzsYLRF9a\nuYe7p7vj34rMl3+izu3d95jbV62f+6q8W86ZaRS1R+1XgR2f2dRW9foyAX9.\nd1GT+Yw93UmeU3FsX7hNxmuNXiOlw80bYFVhYFlFFTV.xhxPbn+vQD+v8Qje\nv1DcuebNipjkml7vgkzGVLy10gYXXd0pcgQTB21BFvuQjLt0+2CHR8wq6waa\nCywiY7V7Tp7x6BhCwCA9qps6YUsg0h41UGO3ZIfLZOwjwsX4b92DzgLVPz9y\nQQRNmCsfthvTGQlJNH57xPvLQf7bCtSkxIH6ixeXW.6MNal1+7HlkWuOOOIV\n1LgsZ1zjs1t5e4NSzkexzN+T7VN7NwkAw9WGQG45CaVRz333lowA2iGicXm+\nVVGg0JvW8nZvXXpKiwtvaj3qEOJCKDc8RbOiGNK8ywmAi4dLE4dU0j9Uy1f2\nsRDswVEyetIfWPeFktNhBcZdRYgdUzCDxCwyU7YiA4+ewURggXFgskMaYYMM\nII+A2kkSs9RMWZcmeZE4mNy+jw+rCcgQR9NJEPa0CqhBRkrryzhtdylo2rtm\nzUcCXxZHZQWV3Mw9XAbZXFRV9gkfjg8c3CHvGFSegn4lyMZLvwyCNqGq8cPk\nq.82sq1G+pZeEB482Rncj2UUeTXL6ibp9nzf6BK+9VUepeJl7kioc6SYLgO6\nXM6P2jrNHMdeHcnv9PLitXHQYoDlW1N+UG3fUaZpejOiIX5xN4egI6LezAlB\ndUyMXUQ+8f00jygo06vGNFi0gMCuewOuX3W83BcPVtIINOK7OnCBDQnCmmuo\nXPx8gUqA+a94GlA2jFtNIlLFZvKHeb4aCKkylsqp9bg1hX+cb9xLIRBdH15v\n78YW6mRXUExYLJeXdRRTyGU88hB1jW7Xrx5wsHhXcFE+vThRNheLVow7jsx5\na5SxVtOl8zk3UE4KIRea1NrZSE67a18e1ONbqedPdHiCXnW8Plr1awp1lf0e\nn97k8j637DrEKgqBtObc9szWT80BLcjK2MVwkWGdSPVdyOK2+lrleRV9CLhd\nsOZ+0E6hWhMMZWDdVzrA38GXABY2lbeVQCKWmcftPjowNTntbsVtcn9185xW\na74ROjoo.0CFgqkEtN.y.0niRpxsq1mlh+hQXUcuyOLhPoKLBfop6+ZueT3l\nPrEEGL1+V+z02isls9aimAABOgx0jIgfcDESGPGuZ6oDYnlPo2LR09qwC+cI\ngrQg9bzhNOs82o0QbcZOghQ8PQ4CX5.7pxCAOFlE9cdcP5WpOmvC4fz67YKu\n0OrPV.Ezgoc0BqCm8YX1h9QDmWWjB8.G9hNoquCuo4dnV7.Kgrf1u.thWEdB\na0WqsH6dN8cSTh+gieqNBdIdUepOGc.axPjRwIOLLOzO5GJHgXZI9CRRmUuM\nqCyvRAdnjLiOLOEKXopAUGcVMx1GG9u1GTPRKVCrrCok1zrfzP+n1qUoOp5f\n3FKkFwUmxDkDS7NAyv18qCSH9avO5d+Gv+XUd3cTOWPch0AuTgUvIl3DrXse\nv+yZu6i+L4KkrYyL4qvYKssrnKzszYKwQsVhSFWqRhXJ+7I7ZWyl+uqT7iPh\nE8XpjjGNJJeF3F+8OnUnfAyQcYqv5X7EpXa15BhD9j6K8nVV.waj9DG4cKVC\nShtV2nITFtob9CxipdmoMU3MxhZ7ms94mA49HyfZXgSS9ST31vuzy5blyCLc\nX1rYwURdsYqyQLaQJHPsibzv3MIhDcRbwX1Ghw5uFuJ3CEMDUW1HVABpsB06\ng5cAQB6t84nFOuQCv8AVD9Ok9yoI6Z28MUi6CjdZVyGGlg0Zwu8Q5zdk74k5\nZUeVSOCDSZZ48kZiF+U2J3g3SBH9LfySVGfMmYEVWw079ZDlGmIWI6oj4T6g\n0OUgwNGKx3eMLBefHV6NVe9jjfxVceRTzqeXWwN6Skf9WX8znSGQxninQgNV\nQtXGiLq8WkZJ8+oezdVqzkPOWu5Zhk9iAA8ceuFqudgSRiRR9c+aC7WOFD0+\nVUm8Bfph0yWBckb4oa2MFD0el0SuvWmtKAqgv3PPK6pW3Tz7aSCxtMIZT14+\nqUc1KbpZZPT.ddMFzzeonqdYHKUFQcElPjNFjzum1QiBAcRoZzqeUB83V+33\nfnAnYogXRRYewkEgs8h6zaZWCp+nrFTlh762rgbGZ+QvXPx+KzdSi1cxn5Fu\n.n51HCIz8R+gdpTbpuWeJRMCvTwBmqPbnFQ3k1Lx8uDGDmG1xWKMH+zuYOD+\n5cTClvq4yPXNZowcK0kmT4N4BuxH234A4IlaS3YO95CW9aiIQCRSg2MTcrW5\nKjibvK2+GpMfI3WrZ7z16eEqR+oMBdZSG6284I6BR8yYtRswkUT.UfOPGduE\ngLLs0srMZz23SLvyzel4U9O7SMmHcupBUuDklBYsadkRMOzC07QUWaB2Ez8b\n4IThyxN9CcXWcBiBet7DbbvWzz0P583NXKGF3IzY2IJpO+AaZnz0xI9l2jbC\namemBmEbC4xNFE2u8qLjrdGQxoFA2r096uEKVemFADBZ67CSORmzQw63rG4S\na50GxTTJ0ygJx7eWA87xfcf3vNJDnVs14xUyfolWUhplS1J1R1wyOUc6kEb5\ntS32CvCfQxuW+G39RifwJszm7t+55jjn9nqa7ixBjeeWi1B7jWtWxES36nbI\nWjvbihtCpL7a8yz1DFGlcav5moD2d7yyckqkOU6e+.CBRrSHm+h2UO5Sf0tB\nTYP5wwihFMCyn3q5elv4v5dnum3o8SjA7Gqw6BDneJTTlHRYBAFAghGkaEvl\nRJwsBseZaida+bUcqfgskiiiGx7T7qPKq3wikkcshcnlwSlQmGy3+lung5EO\nWF0v1rY+FviNBC3OJDc0j3+METdzwS4+lIlrWOZg1lrlKGgdzCI9o09NLkCu\nLUWG+a9eVi9KLYyJxxP5tjHRzvbQe7LiEisSWdbb2xnoqxGwm6EVWekW72Qh\ndqFD4ecPTC859O4QmXPJOIcMKVwLEqmYbR5VL84OBVOPtHRJWjsi4PmOWaJt\n50yqs7xt4+rwwLnegzQZLgQ4IR2K7D7tqOrbXLnU+XUuoUEos7sx4BZsV+1E\n16hsxyxNUgs+HI2LTjaUpHfOisOL6gsW2wyQGjm9yUqgHDlVMaqe7ZhxsOTK\nlV4waJuy1Sk272yn4xCR2QzpdBQS84jCvwN3Cz+2imqrCyx54zLGIfF.Kwb6\n3vB9AVWcJxdICmYOWz2nfdzCuAIk2bF4KRtgmsbmIOKUR7zYZ9edz1Pw5pSa\nCk+mm8TCZYB4MEjV47FCo7lyHeQ1FJtyjmz3AbxXZkvTYTLWdkeT.iukoUC+\nKQOL6EAnMEpJQIMVNWxULWJKOX2n.w12EdW3ZVJQioAG1lNrgc9kAVKV+a7v\nZUJVwSM1K8EsSM9HGRPa1lmDPlvZ5ONRVH8t2+y+x6+9+7u9928uoQzHmvEi\nRtGy1VgI8Zuo30QLH6MX9Jl+c1C+oij+gNNajdGaByrPpW1vXYn5elkz6Hoj\nRZZEglTAKSggGL8OLlZxzmyIILkHse4G+K+jF2ae44fcqcgyiR7rdzCzRLOc\nebX93vLYymhji3pzvqYarHufNti.Xp7Yp+cB2nGlosDbajbLthSRfjPxGkjL\nTrOYWZTa+wQckI9oGdojsuamO64r+j90DsZdnDqRRZPFMSp0CaSWLaij4UmL\nFWLO2oRUVgv57WkuuKi84MG7uRRzsGCObgDfmU1lSky8SjNhHd79aCWca0ME\nzhAkoceXTDMkNSRVrXUP0RhKSpNXcY7WyRlxzgEM0IiaJ0oigqHXQgQgv+9W\n8FZZI5MzjByaHHI7Me8KAIvhUU8WvbBhZITFQeapM5UYooZgQ0BhhkBjL1l1\n9czCV0HNjkblKIAtcngvRBoKIdaupZozRBI2IKdu55vQReqOFPyi9i98H9Xw\niTA8u4oclk0LwjPP9P+5OIQW3U4Qijur+v69.IeHlSxHk3caENE.elawGxxY\n9UaMm+Lz6.07sFgZ78GnF+Hs4y6aijrqCJH2ebLZ4+8G+oejpzJ9qfUEHKOk\njzyHbI+PZ9Oi7pV6m6SMLceVf10OTupUPR6kqCdQayxOTPg5ie5JC.A6FHTK\nj3bmDZQIve8Z1lrJKUnFfReMZe5fGfzdq1e+iuW66Jdhdo+d9myeZ6wmwHZJ\nFOlxeVB2X9K7Ct9P7pn8qCvmco8yLBdOajPiOR24o.gx.D+maZaxTik81Ha6\nHG0Jglso.F6OMo0G6.VczbcDXcmsyUDV269zl3St6yUEq6KrzW3hzWXcNhg9\n52rXqfjusngl1vz1e8MNBulv8FnBtzevMa7g2PiOts+Fa9zpKaqUxSttYPMd\n2U5e25KTh9plceoRfM+zt9RalxoMfVfHeXncmsZZDA79lnjl46CYXS+P90sm\nvIPWoDDqjxMyPC2fSHEbWC0Wy9eEi2D7u2bAvALrv6wGVfqqvR4BSo+qTSAJ\no+9CMecurKM9XxW2movUIKJbcvWHgBgN8eTLzUX4dBjduwthgmvEalSYrqvl\nWK6NDFFuf0MmIdwta8yRRUN9gPtFpk.PLbOawOjpaqKy2qXgUpt6pf5rr6LZ\nfBoY8yYhmRKhGegjj5UJIeiroI4aGmd4olSWRc4j4tz5lvtpPuqYt4qHg.Uw\nOH2666XVK8wv38YsDIuN3yLQwoA+KkkDyn5K6RnF1REV2b9hfs6Rh123l7aG\nDarFvhistAwl9ba0RcPH7hMRXrwpBXRWlY.gwFDFaPXrAgwFDFaPXrAgwFDF\naPXrAgwFDFaPXrAgwFDFaPXrAgwFDFaPXrAgwFDFaPXrAgwFDFa.SEBiMHL1\nfvXCBiMHL1fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1fvXCBiMHL1\nfvXaJCistHIuEx2MNyQxV1J7JxuzC7zWPiABSaZDsrf9udFs.m9J+nUqR1yV\nDYX6Tm9RBsfxBMLxvqwipCp8iod5nerK5kMf6NhUkAynmK6NcFX7MwXKmmXg\nASMUI3lHrcKDMLFsL6K.EPdJELix4kSczGDFiFC3DyhKGleRw5cxlPZ2GlSb\nt55fOqgdRVOE6Eb68VqJqhRnSU49BRpLLB77sdJpJBhWkrc6HAOH7lxr.sj6\nHtwsnWeN3+AvcOOmb2i43ajJ4.girLVN3CAZg3nYmjgVRrVThwMjC9EZaSmG\n1TCkNOVMKaLcsrVr.YtnO6ZPifcM0jHJRwdU0qiLcW1UwlgoUGklMh5z0U17\nAk5HWLG63jLM+z.hDnrP7NmTBDJHoOAhrncXUrx0xn9JC2lPBPKzHZVQVBms\nKXU3l5.BkF0vGzYVtBitMJIiHWlIDszXjLbWkDwV07IrUYlM+eWo3GgjnIp4\nwnI54NYgTFAvtH0RVHHDjrPl5jEBBc7FpeVSVHeipluYX4pVosEo+nVZaQ5G\nOk+aNahXI2lSSLGx71aHURK8Qjw2WXnlY974GqnTl+WLnBTQtzjC.QrpHl2B\nkDMb89q6pZ3isXPSqxiITQHnGHCbpkAJSaGyysHPYaFwauv1SgWIEDdWUjsb\nWnecXLgszhppIQ0G7tysDnLUrAln1Naa5wtIsH6+Xinp8Xv9CKwKdsGvtTpd\nZL2VaLzsvcZOQ3TiPMZxyeJjbDy9f3UOHIEpT0FVVTAgJxiJddU4QECC0RVO\nd1TVfqaumxAIQEHIpbjf3DRhJPRTARhJPRT4UPRTARhJPRToiQNmL.mgjnBj\nDU.lFjDUfjnBjDUfjnBjDUfjnBjDUfjnBjDUfjnxowLgjnBjDUfjnhP1FjDU\ntT3fPRTAxXFPRTAVR.IQEHIp.IQEHIp.QUCjDUfjnBG1BjDUfjnBjDUfjnBj\nDUfjnxKgjnBW3jOrXS3wphfSiwOCc0BuECH7Vl5vawPxRHqiM7Vpnhz3Gow4\nMGVVQWdPdN+EWYI6SWUxWJBDGslqTVikcGF6mSbH5g1gXsSok1CcL3n5XvaB\nGDlFJNJLIjBzTMJPpRKzYihZMba3ZZ.8TrxvrHjgXkMbScVMlt4eYvBKXVfs\nXPi14oYZoq3zhToumLVrwBUYw5S4nvaHKzlrQgqpbjobSGoZcq9x8IaTn5Ve\nzjxQTkgXNgCBjpxeJEWNMiBUEWz8jitf06wYjYIdjgdbGYFcYwsN4vxjbdfo\nIUSRj9g+h+7QeZlOpNcDtN7f9REdjY402zHofnahLcMrsVniV3n6Qx.Hnp+z\n0yxDQ9HCDx0wxU21yyvztURAYViNbgwBKjwUM9MSaxu15aEDEPh33lonDaWa\nKcWCGGGjiG86ZirscrWr.YoibboejmCx0yw0yv0BYa41pe8WspS2ZYpaZgPd\nVHyEF1ztU2wvT21zDg+Axg0sFF515ddHSOauV8Jws36BZzqF1dlld5NlKrsM\n0cKlnVF3QrmtgomoEa7iVfsVdgmkkoitUqt85a1DFEQ61kUNTf8V30jQm00o\nyQSZuaTQ6bLK3u55V1Vz9R2zycgqjtvO9FlMXFtGLOtQK1klPtShhcGX9wBt\n8y97jaR8WGFTF42rsuk11vBaxOTKzhK1jN6C+T4mR79ZyuDyju99VsEIHLIX\ncHC+cvtxY492zxlslIhoZ66aN3HFYQrzbk+paCZO9JxbpkxjJGb0bPaqbq5r\nvr2W3PhJiJIFKmEvt8n5ClJCFmsMYsr2A4wm1K3fyKj7Z55gig8xZRWIKkx7\nuqtK2mgsicWVgeHqSlwK.CpX2euq0GKaW43uvEa0uGAhw+Io0dP0x9C4yzht\nodHngOXJ7Flijnr9pzIF+HVaV19qqdveGutaC9LpJtwr.r03MtokNSwCD5Vq\nkpsRuJHrwxDecamIzXwAuuDpJOwV6K0kgy6qZXny6KubIy0HEdtX0Cqhp74v\ng0TMntclsq1mRRuEKqSONrFB+jzvlNon9w88sdPvRBYqJZrvfv3m0PYkp0DE\nS15oLhxAR6uU8kF0+7NKJ3ttnkqrZu53UhR2BGViz4VcZrRQXGfpkWga0AbV\n0Hrapu1oSGIaETaWkQX1oAaDlqJDvzXewFOc9V+OSnycZEV6xaos5Ke66RVs\nmbpP129Cj77w2VtzJq6WJIi4P+Ftmtn4jFTL47tdi0lfMqbVcstavl0K7Qda\nrW4aiBbWu.4WeZ+5W05XnNhMIhLWS7RXvpbNd.sThYxt7vsg+Qk5ty9opHig\nquYqjOseewv1bMQoqUtuMXsiM1xt.+25Q9S+UqCr7z0QHrgu7FaUCJICO5pj\njMcEKbXrEFeWXVX66jsVChRhuoA68KeBY9Omwok308aSVGz4XphGi4Wo4s5I\nGtcT41YyV7lWW9uLSWnylZtnmM6jjaZZjWZpepyg7QCKmc8c4gXUkzRC1lfo\nuAe1e6tnRkK36aelm5Xt32ikg0pkjzq6Xeqx9gWNmokC6sZn4V2Dp1U0+MRV\nB5phiMqdwj4SMwvyPyMmazXlb.4i0UCn12oZMq+tc093lKn15+aL898NbfI4\nH.ZLiW8QoAjEaruekG1m4mhom4Xh49TFq4yNVypetaPZ79vZFke3vuJuxmsy\neUW4SyNvhP5zLBDhwoPFVr6gwdQSc4iNbAmUZprKHNLlJHMNuZmtdayV2jfk\nlUk03my84aJFjbeX0Jy+le9gYvMogqShIigF7BxGW91vSNZNSCUOAaxZAVLI\nmuLSrlfGlgmi6yt1OslbgJk8xSRhZ9npuWTvl7hGuKLNtEQLGKoT3CwG6eqj\nu60I3GtUVeSeR1x8wrmtj.H1kkZ8dnc9QQExCZ18e1ONbqeN9bRFGvPu5gLQ\n22lshfKtFyW1StiySViWkuJ39v0rS6zquVfDzM6pNsnhKW6x3OnHr+MYM+jr\n7GhBZ8Q6utXW7x7.rTJ7rnYCv6OvBDHHONqngkqyNPWHB6X6c3IviRgDcSVi\nQFBKy+90A46iCjjgvpZSY9.SetqMKagUjqvPByca1xuXVCuxLNn8UZtzLVqm\nt3Kl0BRbXPhCCRbXPhCCRbXGAsBRbXmlvVHwgAINLHwgAINLHwg87UIQHwgA\nINLHwgAINLHwgAINrmBmWAINrG2vHERbXPhCiOOERbXPhCCRbXPhCCRbXPhC\nCRbXPhCCRbXhU0BRbXPhCCRbXJ5c.HwgAINLHwgAINLHwgAINLHwgAINLHwg\nAINrtlAcQk3v3hx7VY8IzYNwg0Kt3uKIbUCnUwAW7r1ThKd2hRnMCT7tGMn3\nY4dnBPwa0Kn30APwCfhG.EO.Jd.T7.n3APwCfhG.EO.Jd.T7.n3APwCfhG.E\nO.Jd.T7.n3APwCfhG.EO.Jd.T7.SE.EO.Jd.T7.n3APwCfhG.EO.Jd.T7.n3\nAPwCfhG.EO.Jd.T7.n3APwCfhG.EO.Jd.T7SLn36Bw7Vfh23QGT7UXdmff8u\nf0XAoq2S01FoaPyj6FHZw3v0ou5ss4hwFA6GpLGmebrmEbCs55LFnS8WoWRQ\nwIGj6pp1e+sXd+NMRcS.KqKL8HuyWZQi7wFML8hcZNX9qqICR7NVPV9kC6PB\nFXpV6b4pJ5TyqJKDHmLH3KYGO+h9CUC0fd8JsXtvuGfG.XJ3XvI9Ov8kFwyt\nZzd77RwQme6u5yPW7oUi1B7jo.M25WBQvPwQ3RDTvt.+QfR9WvpRqc+sAwzE\nnZ25mosAqxX1sAqelRb6IDaFsPw6C0uxf4OiCKjiRh6n3LEApLH833QQilQ2\n7eNGVqrg+G0o8Sg+V5JPVXQbVQA5mBEkGFmZJDXDDJdDtRgZLoPWoz8oMM6s\n6yU0UJF1VNNNdHySwUJMrhmNVV10J1gEc6rYzklg7t1dDS2MscUyNdCWvNdv\nNdvNdvNdvNdvNdvNdvNdvNdvNdvNdvNdvNdvN9mJ1w2zJ1gcg7Wl1waizo1w\na4nnc71fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7fc7OYri2Vhc7lO\nEsi2vzlYGush1waA1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1wC1w+jw\nNdKI1wac4XG+2p4LWMC3MbMo+XAo.uI0.dyiv.djzR6VGy1UNyPTHWRYN32t\nr6TXXQEw2d9p2eaCWiOY79a7CikTw+p0pxZ9m9baVU+SeNqr+glKrt+Yn1pC\nc5pCjGsx+Y5JY4gAT4+fJ+WOZFAU9uKE2F.U9Onx+M8lfBU9Onx+AU9OYmlA\nU9Onx+AU9Onx+8jf2.U9Onx+AU9uyKSBp7ePk+Cp7ePk+CJRbPk+Cp7e7fHB\nT4+fJ+GT4+fJ+GT4+fJ+GT4+fJ+GT4+fJ+2ij2AfJ+GT4+fJ+GT4+fJ+GT4+\nfJ+GT4+fJ+GT4+5ZFzEUk+S.XyGVJJ3bV6+9luzWfMnSSFAldrDMXuYl.j2X\nGXCEAAT2vanAk+aXgTRyW+vBJgu4BrNMvBmDCEK2hPUZPvYHPVg.xJDPVg.x\nJDPVg.xJDbWfCYEBHqP.YEhmNvDDxJDuBxJDCSfNjUHdZjUHjUjFrezSJD0S\ns.Y92yOkBngeh12sKI5gc2lD+fliZNYAYiH4MBjgWeF5KN8OZNtNYYpMxe7T\n9Xpv9o9YTq3S2dis6yGEq9n8ykeXkSOTVpEBnwfZP6mmvQNdu1.bWR3J7j0O\ne+n3ts5c2S9EQ9qxCuKfMkFChSi964f3JYA165vgfGJIxiX8zyYuLICbK4AL\nHOMFjxZ81k+dSEWhEFyBCjwaoVUO9LfFMZIgFdfK4IX1loRw7wv0L+bkV9Eg\nnT19qKfQ+7WBgr4aQyjGSyYry5nikQIxYeOsS01QfABom0JLNMqjCT.9x5.F\nIS6qRhidf3PlPBpm0JWCDtpFCK6qeA.kr9bqddXdzn3zxekzQc2U7RKQBsN4\nGSx+3sI2+g3ev+yX6k2gs6OouzqwlvwgIPLhWizYDHSRBqmY.EtlMOEnoeTh\nXtaSvhkxnq1u2mfwa+75hYXZ7q4mFb.X+qmq8lrvs6hBdCV.Up1aHJe7lulE\nPN96yS15mGthj2FHQhSBg7T1ODzj+OxCiWkrkfxbJD4Ip1kMW6iXlC4yByIL\n82rOKHsn6iShCJ69r6Cw6Gw6O2v9x+iblzzhWDVfJM9cvlwkli+F42RCFnh2\n99XhGBV+zTbYIZvn98hP7I.bgPk5feip0czuROBVK5K45weHlNF27PvbZdHn\n9Z5RbnOG1wWoG7p.ro2aIwaw3Dm.E83WzRt92vifLMxk9POti5wEMBVB07Ig\nX45uP3GkY7ix3rbcHwIv3c2eEYCFIHLivzi3UO70WQtEI7d6cAwYzz7FsEkO\n9J7dQ7a.+wUaLSxusTgnmppwTaeIQDDE2Y0HAm1tSVOJwMOo2LJZm9KA6h7W\nEPtKQMReVkOJNnAzUZAXUVeHmbK.zbVwZs+D5Js+jgVP9p4UAlaJqmJ3wjtv\ne0pjz0juEtm2SdEun2bOJ26aXLRvE68oxq16foPENKrM4qF6ua20J0yLapu9\n2vXiwcBY7HOg356OkuQate6gMFOw6wFcrWjMmuaOz9iJBH5XUViKQlX.grPf\n3Ht+XOOuE1nEdmiXenTrdKYds0En7.mWK19.5JoFsfq2MZLs6pwmxWHdqKWd\nXPYeWmKbto9l0oPDiCKXOcu.2c6p8H9Wg6V+eiwv8ZmTOYerSKcvtKrr+rZc\n.7paCwjq78orIwmcrl0rKwLqz38gsNInov+JBA9noUA7WBW6l2ccow0fNM7F\nbbn3s21vn4Uu+pYWeSzgsVsVwGiOug3Yz3bpUScZRgpHK2fWHjE9GrCYMlKr\nMaJF2BaPEa9ug2q0XhcSZ35jXxXpCqj7nx29mzP1L.GzddRaUr+NAcRgZtha\n.6R+t1OkvkKzYvnY.+jD07wM99QAaxKZxtv3XND77jcxaPZ3M21SebcBtAa6\n68PeZFdWIqEKIPucYl+ccYN4XEvKfJR2W0m8iCwlwGTBHaC8VglDQ2payVQj\nQzgdvd5cBd5Z7loUA2GtN+VNA8DKIZsqbM4rVQ1U0wLMCHKel7x1Ah0CQbh.\nrCpztLO.a7qeNmFg2NhUwfjcQxJZb452lzvR2N1RxGkDFzywsMQySmm2Gpdj\nDSThfrioNEjNllKDWvWZiZGC21crnXzQN7cTFBOxiUptCGwGxnPLS0VmboJ2\nMpLr2pBCCYwjz6XO.Fl0kAC6sbpWRGIC6sWBLrLR5o8KZuEogHgA2.1twNm1\niajv0g6gjx8bNWbO1rcI+gzv4frt6BfKlDGrKgvG0muXgB7PKlrxRdnsR7P8\nKicfky0k7GTCmKV1gW.7wuEqMpq57OjCaqnmJ7Ozhoj+IfyIy1vtzJN0+rtt\nGTRcPq6Tb3qF91KfkAzXmWCoaMfkBlL6kTZqLxQ5RASoKEL6covUbpwYB3az\nY5R9iogy6X4bfGe9G4xcwV8lgEG6Yq.Ozt9tYVIoqWVn7STQSwt41HPm2F3d\nQhtXznyo6Jbfbml0ocxANE2.Agj2hvFndSPpFgW4eS6n4RtW760S98fGzd8n\nubu52qm8kA3Q4PzhmHWZJ5fRjFadwOv5VVb4TdYbgYT.Rz4Vs6ElJiMOCc94\nYhvPNGjRKj1vOrzEvXovJX+N5xfQl4VuqGc9zTyH5YCxqkSWEb0W7oo0twnN\nEPLNikwR3F26dQxsWv4FLjcOL8LWKEnbDqxmTwGCat2LH.KN3eVmC7pGKfhZ\nTqHnSTyDcyNcucm2Z5fz8bP1dcdURuiGEU5tbDtjuBICW+spo7EheQHdDwvE\n+ullZn41p6bjBU4PHkTG2aJcNhxtEYI2gxQ5TjmP1RW3MxAYKs0ydSo6y+lF\nOMrjN3y6R01kb+WY7O9GWoEFiz9Frc0eM9e654HwqMLzY2QmkRllY9xvxLNg\nxxoqvHq1JS.QG45KCnvdkcg9cJ0xYSsFknysFkW003WzXSf6.JpmAVLojUtc\nCMKAD8tQp7oS06BenmANWPMCUUltSDUOb5tgT5NsOew4AAoX5bH35T.mpPyh\nwjQ8qEFqRvX7dF3cHEAGxAAriNmO6YKWpajhw2UNchVLQ9yweSvVeFNTFSdD\nIpFH8sFoyI0LghBsTl1WEL+l4+aZqCY.JiTVEzYXmlj89Wk5mc6WCLPVli+b\n4GIzr9EJeDZJ735xHAmiNn4kf93D75CuSYZ3wGtMnowRbax.7zCZggNxzVG4\nLVt54Xs2r86ut3HzPMFkPVVx0npg6XBJI9B3xceXUTv.v0lgE81AsLTx1ySA\nWaFJBRiq5AtFCckCMAOrqJsg42civ062r4.XEmQpvnuiIc8igw665hzv30Ae\nlcnYZv+Z1PW0wXQK4SPOhqklwwe5ASnxUfL2enFLgLst.gIj4H3NqKHXBUhO\nDyA3Q5gfODSyKO7gXZd5rvKE7gfYdFp38QGcBzXMofzyPIGSaZHkyo+7AVHE\nB3GSCbnzIMw5P9BwqABI.a8iWSTG8AE8S.1X9sE05lwjMgWxhsDM4Nl6Bnug\nm6dvQEz4ztJJd5D5oohJ9xvAaxq3hz1zWUWrfczrpPYdR9O3LYGtxQ2b6Ubr\nj1MMUMvDFSi2dpv9NmX9pdiA2S0t91wpbKq5473lpavoApaQOx.s.oi+Okvt\nAZBLnul36ND1ZErqgZbUo9bFmtgUWJJyoTXyXwh3hAnKGBzkCzkCzkCzkCzk\nCzk6YktbHPWtmU5xgNceycQnKGuLTD+xDhm6A04rzUJ9Y8N+3BraIwsfiYLB\n.8sfX8nw0ZT+W1i2L1JaU7pVEBlhlTuZv3p8cMxSijjxox5xufcibtJcwbFm\nRHWNfKl6xQsdd0SlSGIc7psLmcrzgdDTXWdsmQjljcpAMmNGna8nYLn+mMBb\n6RLxSdTfdY.yyt00FADVg02lSm9JrV277gLq3pWwEJmSmJKtn47LTNrBhX4T\nXcFAgr7TD8U.R8apwhfhwyoS9EUXddRcRmxzQAEgkS2oSpWPVdQ3y0dJXKB3\nN8T3VNctzYrHt7bhMO1v1mSwd4z4sJU3WdQciHSTLwzsHwb5LudJXL.WQMiM\n3VXYNctCTjYdNrBT4BPSy0qcKyE7E92sPzHQWVQEjlSes5QTbZ.oK8xyDVDa\nFAF1SlBZyyKY.pTraNNIAcJ5MhbcSmheyouZ5QsP3.BRNka4W4qwWdDedz2h\nO+ts8c0StVMgdD73BYzqFxT2XZl5p.4z0A46iCdLl1B8Q3fBGVg8xkcj9xqf\n8n38sbh.MY.bH4WHw.xxcx6nS.VLB7GfzxNDGzwr6D.GCmRPjPnwLEwsrfxR\nDOM75VZhD6gtNSZYEfHEgaiHTNLbrariKJG5Z3QahkjJTDiXxuJEwEf.7qVQ\nEWpBuJVTAKQPUKpfY1SkKh087qdQ7OMVgpXT4ZkFoyYODsNFw.GjkmdW7gHt\nVFUtEpm5YTcMMkUSi5zNd00nNMRXsMh1Rw02npG2aMN5PKEVFiXThdp0QLcD\n6odGwj4JslGwfxYe08nhNZW+Mp+5eTAXn5sFHUevqRcPp.ANhpERkXvRZ8Pp\nv1HI0DoZs3NIsPZsQpnEhqORsOKbV2oIm5jTwRBd0JI1iTndIwjnnTMSpoG7\nEInVPAThuVDcgHG21oBT4ZBEt2xC9s7w4lsGs173zSkUpMX2L048BjkG.5Gx\niJB6Q00CfO0TPdyjuw57qPPKESBjePsnCqeaa2FzwFRkTKcrWPUG+khrBpAB\nLE2HdHrUmUTDbQxyaAc.Z4hG+0d7faonkaJA6R4PuTPWKFBlh9B8CRBUgiY+\nN8QIG+n.jfTBSE86DHkbDjZVWy2+ecfqoHQJBgs43w03Cgywjm8XxPTmNKEK\nhiG4Vp06O4o57fjnDZtbnINdDc4vT7Ej7I0YMRvAlp7Eid4KCCSXSEeBoBeR\n+R7bjtXFSBGUAriMdb1yLNxdIrzfOFyF.Nyjr1P.dyFu0CJi8Lkve1KA1snq\nWbnWwnDtNebpMdLcEvrFvIGENobrsMdbT.maO2W0NHLvoB5WFBV3jrBuOLwM\ndqwOR7wARxFEIYRwQ2HxjeRgotWFxaTEucGuTGt3tSl+I3h+twaU3iNV7.gV\nCUn0qUasiDb6Id4ib760XVMHfr0dYm3tmOf1z72lrOVjm8EhMrdviD+qpRIp\niwzRcLTj5vzkjkx5lBZC2AqLLzo.wiON3FOpmj9WvhKrx4YBu1niiDoBr1NH\nXSBz1ZB5G9vaqDeNBg3FyBMEf4F6s0KT2je+yG6UcKA5accH7aQy4NKUBibb\nsXkh8TtsTID0IvFgi9h6Ee8zG201uS3Mcy2XFdbndvaGiKJFycBuYXwXui8X\ng3uqfkKACdEqrT.GdrWkXr3I9jVEwjW45yBHE354PgTfgYuHyiAXBInyqTBf\nBHzqtdo8gRuNsUDR85zPon0i1Z4H1qpIJgZuCsVJn7XTIEPuGSaREPvG6Tqd\nQwGsYJgjuhNbmZMTMD8U.2FkP0W8IjpH6q.bGxP2WIhe5EgeEVj0CJ+p0p65\noU8h1uhVIGwes0wXFeRf.j+UrbRD5+XOVQD.xjmoLJ.ad2JhOTQBV.EqRGeH\nbIr8pBkK1XVN9.Ob5UKbZYRgEHxVALBxrR5.VsbE8d5CtVkMRNjsJUcVAXaM\nLcoDSvk.ePw9bPBLBERg5WkDQpk7VdNCgqUtCxTsyxRyuUyb.KMMMYUuaiiX\nooCrzT3RSVEh2Y7WZ9sOgWZtIJIIc3KNQrrHpokL7s1dwosRKNQuHWbR4CKk\nRkN9EnLl7SY4mFGwRzi4ncKP9YOxOsTb4owKC4meyPz4zUmEWJdCegoIrvT3\nByuYoTJzwK27adBuvbflCwVZVbr9vVZZ.KM6wbHiwWl4SYyg9eLniyKWZdLG\nmifklBWZ9+XoTJzwK07+wS3klY6hBy0zOJWIgnto2VeHlDYnlBmlJsB0P4Un\nW06pzlIvABUYo7w6wudg16OkUAT66v6IGzRlhEItTEAsGjQzFWhZBxQcLCyw\n+Pumx5iQp2hZeWQAMR6ezDuJ+iYCdwSwsb4YNnEOp4eP8y4hGQQcrry.UN5i\nKwKf3HPVxqQXMdruumZvqq8cEIu1O191SkFwIrAeefkiMRU.vbrKdpWPyUNy\n6E3bECud.OWwrse.zw1noRboUr6URMjrOEojUSImlkEpTqIeoyy4WiJUfWxu\nlUNMLxoqVVBrew3lstLSY0Byp1pRMwrF6UdswTpNJxVZJulYV+rVYHrT9hXE\nPZYC57fQaY6c.Cr1Z1lJODr9x9dRgjIi9Kl11Sg3r41IgEjyCGazSg47.OsG\n.ZRajZEpyCu7dKXmp4AgS04EBpYmGg6KJqHjSv841p5P9jyNCi51YTC42CxJ\niJ.xPMyvwXPlY3BlY.lY.lY.lY.lY.lY.lY.lYbwXlgw.Lyn2flCLx3EhQFt\ni+cY7j1HCRJDf2sYvBPxi31LLcrXWmAZP1Y3MhHJUGLdneQqL5EX8.X8.X8.\nX8.X8.X8voaVfvb7wTXU.IjmIogshCxNsKfPV9ffocurjpwHX8PC6xTzHC72\nQIqLj0tlpxJqkC2Ni25ZZ6ZYtvy7kicFXJ3R4Jzd72lAg6L.KM3Gy7QgwCMn\neo55S9dCybjrj8oqJ2wWDKoZhsFXMd6cXbURL3SG.IG1.k+4jXg0XMwbNhI1\nSg4k8QLuJnEWzyKqiXdY+DXdY9bcClwQI5v5I.Kyv4X4YW7yriYWl6SkIFZf\nSL4DiJE50e9MyQpMyQW1y7iRzp2SgUyGotKW5yKzwvvF77huN1koEqquYURD\nyhoOQxCQlHSWCaqE5nEN5d320bT0e55YYhHejAB45X4pa64YXZah2vIJ8W0n\nyWXrvBYbUieyzl7qR5gfHZdfsQGgsYy1R20vwwA43Q6Gajssi8hEHKcjiK8i\n7bPtdNtdFtVHaKWIuC+Uq57JrL0MsPHOKj4BCa5qP2wvT21zDg+Axg8JLLzs\n087Pld1dRdCDGasKnwavv1yzzS2wbgssotaAwvx.OS7zML8LsXyKzBzBqEdV\nVlN5VRdEWeylvnH5qXYkefXuw9Z9jtDnyKBc1dSFUzZGyh0I55V1Vz9U2zyc\ngqhcme7ME4ZMWAIqq5sdWZBw6jk45t4lK5s+2mmbSp+5vB2nieIut+82WDk3\nvp6o6+4WTsjYxBtVC2gUwLMrOWUsvhBmt35lYC+o7+7KKEO7NtzhIlTdYyqa\nB7yipnUpy.6oIR4pVoQu7e8WJUsRI2C63jB16G3lP0Hpu5RVuWqpfzquzqSc\nbXupdEpuPRB9CnriJ75QGGFyzdknurqoApckmJecmJcUmGWkQ3oXFt+H.Bof\nq5b5x487wnnB3cTIrNNvrcuTLNp38NN946dI2ynh4H9RbLZLd5CyA+hWV5CS\n7FzWHoxEccUUClUt1MYokCWGk0BFMBVAYL.qft5of9vYA2PThYRqVx+JsNtq\nwjyQJHQ096uEeJ1NMRFDWameX5HUMko9.b1SjCgoq+jLVZVLrU8HZ00LacAe\n5oA6GIg8WbRT0Z5mOJp8XuFoLE+OUqQ9kxkAJYxrwKIczUl0OrR8s9.39+d.\ndfh4PS4Jf+C76PiTXJznuoKKNL5oeE4l.vrodCrPHFNELN8G+slLM+TYuYgJ\ngCPfK8Pxoji8WvlIoc+sAwzMbZ25mosILNL61f0.Sj+1ShI9CnFZWtWcpb40\nGhCywlCxzjZN3r4o8jwyRg5rGUWURMuQUy6ydw3ThRfJxOGYsJejJplyT6Dk\nS8f4ofCxN5REgoSvgTifyNo9jpW+cJtUMcAm31MLud9VBDObbb7PlSQY9rgm\nHoi4kh8Z1w4LRFk3x1ejqhB2ooOG+et1CzgjVTXY3ntCI060gjlS80xezd6V\nX9MWwUXD57RwTgiaAFoSuvWe81AtnBYxppnFpB1GzhyEXet.WUwJjC7IA8mD\nHTqBNbwgmnFo3gL+60nYDkLt42ApL3UzDxn.Zf30hFNT7Gsn35WTXonGf6nC\nVE.3N5h2TP.2QW77O.2QOGYm.ti36DfGMbGgTD2QGT2.vdzyArGwWksiS04K\ndrG8MCKtKL8rFVbWfbuLi6hug4ZG9Cuiyx6u4R2uNXR1W3JUSL+1xfZ6skop\nNzoe1MpW1s9EucLno7RIYKjGd9+4Y5cSpL1OF78KO41ipPNHDLHELHELHELH\nELHc7MHcvVaJL+9M7qb9nODus0kSBlDTvTcELJknOYulkJrQcx8aiwEDa5ZY\nsXAheLy+XYYZsSh3xNGASWIjukB0+93rmgxRtrsnYadxlgY.KxldC0V5pZPC\nx3Rw.1KEye1FtN7Dw0W+FA8Ce3ceP6GSxCz9QYJGBvcmPaF.jZ2ST9YJYbeD\neL20XJt1lz.r7o3UOLsrtmG7krU9QS59oOheA9ZYqhpoTrD.T9LOHCny7AXt\nh+tom276WuE3MCm2jFrYRioifMAoXoXAZwjCiHFKd+sgqtkZq3aHB5dyAIcZ\ngYZ2DdWP7KSN2vfXtyhgrCjoQ1josQ350QEr36w1FDPYuaBSyx0nhl0VGbSZ\nP.gAiEFr6bFdHOg4vCI95HaklRV7esZSJ1bbZNw+MXQGugwyyS.9Y+7SKK8K\nmfs6.+7q9+4O95mO5XNgwSGURVo48SkqW+HUbI08UvEdLZNLeSXTNy8nSEe6\nuReCZ6iYmuwBPprYv8Q0qXQziXrxcvWL8LkH4HzYikWWN+ADGysEiTHworKJ\ndhG1aJcD4H.NMQdgs4EAPZkBWDf3NangJlm0BGajmt6TDpXG6cAz3LX92FvA\nY8n4GcTfPIiKE6.6i6JAXLmK66D3FrT9gck.FFrvLR4qD.83GdQWJWFPRbxl\nI0AL5ZeeTBVSBMBe8Jr4Zwu8OBRSzvRChynlouJIMMHaWR7Zx85SZVw0sB4Q\nmS0A1SPdzo1oiSkprLcWzR1nUteCrD4ouptLIMijJfORRUl.UiozzoLDI9P7\npjskzf0Z3SGx7uI3wBVJGi9zSZDj7+Mkp7SzwnXP8b5JYKPsll5XKrQMUiSX\nyFZbfXna355XaeIogcskKmf9yDRzRwJ6cbpOSo6W1ZO+uqoOHkmWXLv3AQ+E\nbn4+uuTLI33VQ8uegub5a+BVJgqhqnrbcqUbOrrU0drEWlgXz2xr+dw3ws+1\nKcauiR7WKHoFwUDhsGQtQAC2V4rFiyHDjQmpLjl4Jnh48RgitiLaAUROurY6\nznecf7bSFOWW0CNFi52jkR77qp8+Uj+SI.KENLONlOipdYy4I2dN2XJlGq2l\n5gsRb3pLm25RlyyH.KENNONVeAY8xl26uNKEe7ttliNoZFh2KqOvC5soku.D\nR4S5M6cof84q7Ezbg.kZrT3f73VGP6zmYQPtg9.qbeWzAPtw3kq.t3ie7L+6\nGXUZzgls.LUVwtGgxSxv35DRvRgiziaSNkrp.muyPjlHy35WrtKJjU1g4rzn\nS4FdgnxM7vJgxG0B5Scr6Lvwt8EzX2dnzccgeAwEg8mDyLiEhJN6RJx5OJyL\ngUQdAyrEOUXYVCjkQ7cJgR7jjqfTXmzkw5MI64EL0VbAwWLGpbfKnwtwPOVj\nTmmubF8CUDL5B5fQREBb3mebwL5QCkz6dIsvYnBbLtnV4bLj9KkAOZvRbzuj\nF8dGyIUWLid2AN5unV3XbLGUcwL5Gr7RiKoQu9wn37Eyn+HTPSswdWOZTfhp\nkWeypjHFNT9jl9bcSjoqgs0BczBGcOb+OGU8mtdVlHxGYfPtNVt51ddFl1lW\nQv+Q2WQiNdgwBKjwUM9MSaxuJ3aGDEPxkKM5DSaWaKcWCGGGjiGsOrQ11N1K\nVfrzQNtzOxyA45435Y3ZgrsbEz+9qV0o6sL0MsPHOKj4BCaZ2q6XXpaaZhv+\n.4v5dCCcacOOjomsmfdmjFr1Ezn2Mr8LM8zcLWXaap6VP.rLvy.OcCSOSK17\nAs.svZgmkkoitkft+5a1DFEQ69kUPxi81j0zIiU24kfNKuEiJZqiYw5AccKa\nKZepa54tvUgtxO9FFRPMbIdLWRK2klPxYYE6Bw7uER6284I2j5uNrHkDo2Bj\nTM2m13uZJsogrCNdctorEE71bsLUZbvtjHRQwd9h13mfm6lQr5NigE0sydBu\nOoF2qPaW2K0QyxuPAkuLAdotoh4J+5ddaeJax6wsxaSEc3rGe93p7HRw4P2P\ncdHxjlqYsUhG1IUMKszA0hGZ1GOT3cAyqV9Plo7SezGAKj1aW.7Or7hfrun8\nc3+MIlD8H3Mj3S4TfaZZwRyVrfpwxRIto6IvMGwcjrYM+7CcO2wCeP7P5tK.\ntI27+rDlmkg7b+rr79rT3XIOeOKL9lZorpZw0j3XZpstu7yqyc0mtuHX5TSC\njxC4jdC2jdxey8FlIxCwjdCuDYwXTe4tftvnUTNZtOthgTtRO4k4dCLnwjCg\nN+bndiVF44bYNbIg4Z4SiOoR9U94BufeJgfCsletS9zHzSW9R9YF6QnrBY4C\nYkxEx8lGja6+jNKMjDdccWcHOmG2etF3jOjq+7br3fISR7TwyoRxyqw8mSi6\nMeFqPtLVZnUoVNLVg7Wb60DpEtK+2uVnz7NrEQ4pX9uY0xQwGgN0cxMwsznt\nAWlONoZpnsHmj1Rc6NNGk6EZHBaNVc8LpxVBbrCPqgL.eDFeltCX.x6dDm9Q\n3fHgtOFiPyALB8dLFfFCgI6z8ptm9QHZnivyNMbHCPiGgAng2PIgFm6Q3fj0\nX8HrLzvY.iPzigrFCqgNBO6zPigxk61X9H76BanSDYYIdnqedG5CR.5ixJW8\ngNBO2qb4CtmA.rmoeDNDY7OFp6hFhH9GiUgnAIgewiwHDMplLzzTroEfGGO3\nNlRfcLcf5XBAzghf4XxAxwzChiQB.GpAdC0Atgxf13v9spe6fXgpM6stJwCB\nA54JDqUrkBWG1L6jxM1d0KhsWDI.OY.xn8kGJJj+Dh.CwWXHuTeIab1IL8j3\nlIt45xho64g3R7QIdofVl+s23GF2OcFu4lRZcsX4LkdIzFdBozH0ozx7kXsC\nV34lv5tHju6AITfkcGqCiuUzOiIeq6kOcfw8d58eP76cZvVRlS9W9w+xOosN\n3tloDCpOHWkrms80PNuEe.f8bxgBFrpUFVpFVriC9bGKG5+S2gkGEDxqcThW\ne89q6d0JRVAvZeV35fFWPwDSgqCdnYszaXQojd1AAD4yN3sD15K7zwmqhrom\n+4TqI5Lw4MnbbYAtbn4FLjQTDB6HIR0VL.FvtjvJo50d3lj37pU0+M+F4uaI\nLIxWaiOSiNzHxh14GGDMqWpFhGUqBBIbDQkcq+tNqAquVV+DkasMYc6aGbln\nymUZ8ESGRlVENb+n1qLZePu8jdnxc9oUqaHW1HVMhFYx+Cas1ow84b3rd5z0\n7NTX44g58TcqQ3vl1X.p94K8h8G939otsOyH.7HnEnEZZBkLn9T+FPK5olOW\n3MeK8Vuk.eGo0HFw2zsza4VNXP5IU+17R+lsEqPvXPMo8ynPKOGDLwzi062t\n8gwff7NRGoQ2BokeqetFYGi1CI6I.z.qVQtluFdYiFdmG4Ov5hjEDuViPGKy\nWqYymBJp9nPQEfXrAPoooJKrrf78YiA8td2c4uNrcAyqEsweUd3cArozXPbZ\nzeOGj3IlzsKI5gc2lDOfMwFBoa+bYmQRL4DjSks+5cLnbLe7oinoea6.O43s\nHITZBJxxXKpnikrwfh+dZmpsKHUi1yZEvrIqjCT.usf0ZzWZPN94ZeURbzCj\nxeT3ZrHWsx0.gqpwvx95m3rrqSZClstLrM9QYAR3Y4g4QAiAe5WIcT2cE7Ge\nbAk6SMxOOzDNacxOlj+waSt+Cw+f+mw51tCeXdRmagrEWfn79XvDHJbqQ5Lh\n5Cj7N5LfBWS4BhnhzjnwfR+82lPK8.jU626+.SitZhYXGsp4mxDPgeqQAqmq\n8lrvs6hBdCV.Up1aHdm7Mes18gQQZDmCu0OObkeDVxE9eRHjmx9gfW2+QdXY\nV7mVhuH4Wxr4ZeDybHeVHUmw2rOKHsn6iShCJ69r6Cw6Gw6O2v9x+iblzzhW\nDVfpleLAxv9oEn9D+jx299XhVnqeZJtL.exVUzCQH9jnAiPkZGEQGV2Q+J8H\nXsnuDuhivdpX8oiwZt289e9Wd+2+m+02+t4Z+8rh0FEuB7obY4A9qmC63Kc9\nPvp.rNtkNN5jI9DeCS6wuTXhVl1MTS4vG2wvN8lzjsTi3hW+EB+HbMqDDWbT\nn15PhqXv6t+JxFrj8XS.wzi3UO70WokPiNBRgKgVJJnsn7wWg2KheC3OtZiY\nR9skJD8TUMlZ6KIhfn4wwZjfSa2IqGkXOU5Mih1o+RvtH+UzKcVizmTA0M0.\n5Js.rJqOjS7EmFwYVq09Snqz9SFZA4qlyjNec.dwEsmJ3wjtve0pjTZgqA2y\n6IuhWzateMedof.lnI67fmTodyYPl.Msdvga3QHHzHZCCCwgDQyvgfip3MhF\nBhViMdXy6haWimIJDHZF9CdddKrQK7ZzqRqkLcuXQUukxx8xsoAML7+snl2f\neqSGJEA8ZwZLRqqpMZAW6ca7V5pCftp2jJWe5KNmDy6RT2045.ZpGRchH4UU\nvA65D9c6p8H9tgeq+uwVS30zaxgwrO1o0Yy2EV1eVsDLu51PLQKeeJaR7Yml\nojc5MAkFuOrkDhlBEpHDXQVraQq6p7Z23k9B5UhfbJRt8rqFjjKCZiNjnC6L\nasqHFKHJMfD+TUvqRmGnoHWsWV3evj9ZLWXaJu9OgMPz0J9pY2jFtNIlLl5v\nKIOp7sim3rosc64IsUw96DzIE5+HtAL2tdseJgMWbXR8CQmkmjD07wM99QAa\nxKZxtPrn0tD77jcxaPZ3M21SebcBtAa668PeZFdyIqEKwqrxWl4eWWlSNVyr\nhKxq6q5y9wgX66BxCYbMC8FMHHlbn6sYqHhJ5POXO8NAOkgSg6CWmeamKflB\n4uUog6JWS1HXvpe51rlSElL0leZV9CQAb93JccVlGfsJxOmSiv6GCyxyvGzm\nUz3x0uMogk9ihq.PJgrun.aTS2BWueylfzunw94F7QFZeG0AKqBvVN+1MFy8\nC2nP1XvRu6UpiVXRqhJEkeq9R0M5mP1Y3DSSJONIxAdEG2SKSN7WvZfvP4CE\nuOLlpF4MQhYZ7Nj4uLyjC7JOQbhZcxAGDPsMxbkOh6VhW5+nOwkJY8lKGLdt\nwiJpIo8vj5KSWHoT9xKaOf0FeB3kGJaueO9EDGDkM96ndNxNxRmhMVzMTZob\nqTn.Wfu.tIV51bse0+2wpOPtYf6uMHl3w.h+6uptq32RTdU6ZhwFZ96WGlns\nKMYU.VEp3alOwxGmkUHF9jyjGnGgjdyww1KmwOu47HAb9B2.QW7O0Y2kmNqI\nvTisS1RheHLJJLi3an0Sy5hgrge6Ko85OO3oJrUdaFrY9f6.C7SGa992S5zJ\nHHEDmWAIIlckSsJWSH0WQ6AiIlMGMA5L8WCiWq8C9eNb69sTbIT8lHVuSHw2\nQ3+Z2DdWvTay9SjcAyWrvVQy9nVkM1aFnWtiVQ2qQCRpyfBOl.qeFRkcpE9Z\ncXrb44aPRVeqThGMoBJTr2KkTwov7K3PSEmcwC3oyt9qDuVSVG7s67yuk.3j\n+9u72JwIHV+E7uwXfyg7y4r8oQiM8+WukQ9IaY7iKj0IFqlCTiO5JlWBblxu\nwnxZv8Igs7NhTruZClYjaZbkF8Wbr9ZX+PgGRR8yGcZubeT17jCqWdIw4ixb\nGRJmXBN9HhjmfSIplkDeE9X+vLhddQqIvWymoQNEHi9ZLvwpsYe7JpRdQg+N\nAkjwe0m+5qHpBhUH7yLjuktOlgaRchXPD4eHi9ZlQAGGQPZTN49xGcl5e9d+\nvblxaTl.gpmrOmhb06SRiV+Ll32M5YFBdDaiIQtLtiMYNuIYVeFmO9Wy8jjn\nlUdRWd0xGkLxQ8pjkMauR8IT48qdhSHktO0QZHytCxSkCH+77AsTpAJVKPhi\nz75MurxdKzrJpaTO0daibscz8TJydqOAY16xS26lVuapkFmBNVEmbv486Bx1\nR9HQZ3496R1vieA0oEBuR1mVfvqeKI9lnGNE7c4nSw2kmiRENoE.9t.7cA36\nBv2EfuK.eW.9t.7cA36Bv2EfuK.eW.9t.7cA36Bv2EfuK.eW.9t.7cA36Bv2\nEfuK.eW.9t.7cA36Bv2EfuK.eW.9t.7cA36Bv2EfuK.eWmJ9t5hDIw4cvmZ3\n6J+1zfpT3ElMl76AAmBDuLoP7xfek6qMDur.DdAH7BP3EfvK.gW.Bu.DdAH7\nBP3EfvK.gW.Bu.DdAH7BP3EfvK.gW.Bu.DdAH7BP3EfvK.gW.Bu.DdAH7BP3\nEfvK.gW.Bu.DdAH7BP3EfvK.gW.BuNUDdY0C.uLeBCvq6SJf2kO1f33S.aWK\n7XYuKSUf1kM.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.n\ncAP6Bf1E.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.ncAP6Bf1E.sK.ZW.zt.ncAP\n6Bf10oBsK6df1k0SWnckDWl4tVmteaTRxtS.cWNFrD2kRn6BAn6BP2EftK.c\nW.5t.zcAn6BP2EftK.cW.5t.zcAn6BP2EftK.cW.5t.zcAn6BP2EftK.cW.5\nt.zcAn6BP2EftK.cW.5t.zcAn6BP2EftK.cW.5t.zccxklwdP2k8nftqFKGl\nEEFy8PkYY4ODEzQZSS3fU8ZIijlC9R4AxgK1lj37xN4SZy9a3EJcAJEoQkWf\n7mzPFZ+yWKlFOCeHFwkwjQOGl4rs3wTX6UPsIJEi8kWeypjH1R2OQX6lHSWC\naqE5nE3EpWg+HT0e55YYhHejAB45X4pa64YXZadkFpE7yl0nSWXrvBYbUiey\nzl7qb9lAQAawytFcfosqsktqgiiCxwi98sQ11N1KVfrzQNtzOxyA45435Y3Z\ngrsb4z29qV0oqsL0MsPHOKj4BCaZWq6XXpaaZhv+.4v5ZCCcacOOjomsGmdN\nCuCDeZe8d1v1yzzS2wbgssotawj1x.Ox8zML8LsXyCzBzBqEdVVlN5Vb55qu\ngrqk10Kq1DxdShZ1jvR67BPS9avnhV5XVv200srsn8mtom6B2d5FrXU1lDC2\nlxzZzpcXMdwZ6fESyD9YtPX+sOO4lT+0g30QEBJecWgPU+1gyPqjJzBfoGjV\nzCvRO.pTrnvcYXUt9hFRWudK3fbTaDYZqgro+vykGXQqCTTi58WCPhZz7A0.\nHZM093.NzFuodOzplvyYaSVGzkF2t2ZbTSEoYYmISqCZPseTyCYp5nYmGd51\nv0ggw8vLwh.oLSFlesz6iYhPB4lnQia1f9ylFK69xGF0ufZbdH8D+m1xtHdD\neWCDgd6XS9Wam9n8NmaRe4zXYm29vn8UjiIk5eme5g8Z92xkwrSq0S3skv0j\nskfwVVf0vvwwCoSgIuqmWOaPbExkLONtTV3MTeszlW0FQ50ky0K524i7855Y\nNiXTZPKMQapHqrajstQXE8TymKzuMR8YiDrQK8xPE6mFo9nQtwl83Nzl5Xi0\ngts6pONpIseFEZ44ffIldfWniFC5Asel.5g9nPODga3Ztcks+VLg5tjvU3Iq\ne99rwfdUu6t7WFEEhG1hoM9qxCuKfMkFChSi964f.KwjtcIQOr61j3GTmtYH\njt8ykcVIDLx1e8NlqmlO9zQzzuucfB9eKRBkN3y6RxXKpnikrwfh+dZmpsKH\nUi1yZEdErB1eDX.5GFGrVi9RCxwOW6qRhidfb4XgDPVnUtFHbUMFV1W+Dmk0\n8tT5xv13GkEHgmkGlGELF7oekzQc2Uve7wESRO0H+7vcjpWjUKtPWjGcbLAh\n9xRP2xKXJLU1AQTQZRzXPo+9aSvhkxnq1u2+A7O8yqKlgczpFADlEu0nf0y0\ndC6N5eCV.Up1aHFq+lulck7D2iQtlvU9QXIWEP0Lnre1fa9+HOLdUB1Pqaz9\ngO7tOnEmjSBukOxf2oVXNgo+l8XyUK593j3fxtO69P79Q79yMru7+HmIMsFl\nPIH9EquTZN9ajeK4Iku88wDkwW+zTbY.9jsJSLIDeR.WSnRsuKgCq6nekdDr\nVzWhWwQXOUr9zwXM26d+O+Ku+6+y+56e2bs+dVwZihWA9Ttr7.+0ygc7k9NH\nXU.VG2RWgdxDexE6S6wunkb8ugGAYZXq8YG2QMsgAlCesrf30egvOH34f73h\niB0VGR79Bd28WQ1fgsXRKBSOhW8vWSAvCdu8tf3LBIh0hxGSPtM9Mf+3pMlI\n42VpPzSU0XpsujHBJhrsrFI3z1cx5QI1Skdynnc5uDrKxeE8Z2zH8IUPcSMf\ntRK.qx5C4D2uQgh5Zs+D5Js+jgVP9p4LoyWGPPMBomJ3wjtve0pjz0juEtm2\nSdEun2b+Z97RAn8QvUQGF21GJ07.IyU1GLEpvp71juZr+tc2ZLQJx+ger7E1\n5wbg6i.7YHKUqTaBYLtSHiG8Ij43NgLmxITyqOU.HrDLS43Pu1NKaHNCrm4k\nZyfFX+giIbMv8SabczByO6Z7LQH8oIJe777VXiV30nWkhvmtWqopWRZ4Y.sw\n1RCGF8VTy69tkVEkGc8ZwVZLinuciVv0OIMdKc0cT86ws4E+Ht59w6Fr104N\niZp4ZcxGwLyBdW2acY2tZOh+8tr0+2XqF7Zd8AgwrO1ok1b2EV1eVsNJe0sg\nXxU99T1j3yNMqwgzaCOMdeXqyTZdLREg.eH2J93VZ1gKOagE8xyL8nWdloK8\n1kMMc5BnhnC66ZscHFexUZ.Vawb+J7SvCmQ0Q3DxXtv1roXbKrAUrYJRpp2p\naRCWmDSFScXkjGUCeU1LLQzddRaUr+NAcRgByha.yO8W6mR3xEZeTWqqY4II\nQMebiueTvl7hlrKLNlCAOOYm7FjRBIR4M45DbC1126g9zL7tRVKVhWXkuLy+\nttLmbrp7EWva2W0m8iC2hUoJOjw0LzazffXhVZ2lshHinC8f8z6D7z03MSqB\ntObc9scgrH9o3uV3tx0jMNHo9YPyZNUXBSa9o7AZ2rCJGuLO.aFcAzIa1H71\nQ7Y6YXMCyJZb452lzvRGX1RxGkD1GDEG0LV2ta8yRR+hlmBokNjoGYijqAEX\nDHgIhNI68aeg7dRSTcFWIFakcjGH8l6U916Odr1RjY1F9RJhX1BlvR9jDw34\nP3Ihr96BHgHVt7BglqRdOzhtxhsJyYDVeYX.quZs9pKIo05KimRquvjb7hKk\nWZYfn+vRojno0IjDM0uXxLlnwN8MT32PhJfDe2ylzLG.EFuN3ycXGu5kQhwr\nKTQ3FnWjdCSkF6vyqfMzWvg9rOuJHj.Lz7pvpjsaK.88XxmvBIxBzRvFZpU9\nFfXVcaPt+nGupZ+u+3O8iZr3Xjb6DjP1H9lRjWP9UxqkEPqIoZD1x0OP8gMw\nUGojuxJhCSd4kBR5bwDbZSj+0AEdJpfJxMV+nd2KIcMyiLlmqnhEMqeA0i+g\nbxcloXG0JwUmbb2YWm11SzPRzQRZnPxsAM03iaSTONHMcsrVr.YpTfPhlf.g\nrlz7twBYo3mgGpiDxxRt5JNbi1nj3GeMpW8vpnfunrF0HWpy0rLTQiZC6AYN\nVSUpMTzZqqFY6tBiyCR2Uf20XRxAr85GVDpVwH+ke7u7SuiIB8igw66DE1GT\n.b1lzf+0fWzw3PK4SPG95tBF9i+JuugFfa5puzisny1dLbTf0kqiBZv7+lBF\n+HHv4atDX5Y929ERXwh++Vpy40sXECCWUj53Jk0ZNcbtGGC92M9IGdZmRzC5\n5.MVhCX8yN0hUxF+wM2KgOA5lwlUQ5yadI5AfQl+8VURUl42lFjc6nyBo8ZR\nz5G4paxKiMgSPszfYb1y0xngH+bNQlxuSddFhf.8YivgWGmg4CHiIQE1eRSj\nSPz9XMGXx6NsYwoHc6bjdw5IwVoh2iDJ.3DxoUDEkk5EmVHIfiSb31Ep6DmE\nNFNHGKWmwJaV0FBXLSabOcKanSzGeaaBi0LUvlFCSlySX1w5ohIMlRMoQ+zL\no4RxXlI3lxnzI3hxfKJCtnL3hxdVdQYinlPxtHMkCsg1q3XEjEZfmwDFW6d0\nLOt6U6DTrpQZ3pqZUbdbSkM3z.00oBYfVfzw+m2yraFqHqfYd5JyUKwf8npJ\nmg5HjcHpxY.pxApxApxApxApxApx8rRUNCPUtmSpxMBwMvkhpbCHfAFflbHP\nSNPSNPSNPSNPSNPStmUZxg.M4dNoIWeEkGySUSNg0iGQJ3Q0+hzN4p4kkrOc\nU4x7h6JVqqNYqwqoCiqxOEe5.DeIM9nz.8XGflCX.RlLFm4wGA.4JO.eLHfk\nbM0Xw1OFiPiAxiO6CvAL9HYefy9.r7kpLEDctGfCYMnQuTPnpj0ZrCUkLnpj\nAUkrmVUkru4K8TpdVvJfRHCZx1vyY9BGSSKxJZx+agUOkpG8wtxjIpT8vM9p\nZ95E6oydhqpokETlbcrh09NR1gogcZbYIdKnQLGEugHjYuETLmGGlPi7gzIT\naqZmmZl7RaE6EdieyR61AFFwyGZGZj12wr5Vet12gosre52zdap4SqR1y1oa\nHmAizMVvx0cEo7tErL8mr8YtGAO1PZkK6JdUurothXcWo+dDoCkpYG4OFjqE\nFGliWhwJT8RqFDFu.peJsBI2ZdO6CEzo+SdzIItOqSVmOcKl97GAqGHWDIkK\nx1bcnymqc4VJpDD4SCnjAQxJmiREg6WHcDqtzPBaFY6EtDKJUMJlgxVqMFzp\nerp2NTHeXTp4OkK6Y8tXq7DuSUXKw8wk0emJB3KsxuyA4o+b0ZHpe0aYQsn6\n.rcEspnFEbp7FR05vOVizczBE+ZMdw0zSMN.8tPDQ+eOdtxNLKqmSybjTKIw\nRL2NNrfef0UmhrWxvY1yE8MJnG8vaPR4MmQ9BhCeo3Fr1xcl7rTIwSmo4+4Q\naCEqqNsMT9e9I1FJjXdSAoUNuwPJu4LxWjsgh6L4I7FpIjoQfHRbG3AcjlKu\nxOJfw2xzJ63PR8f6kPoEULS58kzX4bIWwbor7fciR8d5cg2UVUuXZvgsoCaX\nmeAd6H5eiGVqRwJdpwdouncpwG4PBZy17Dy1ZALlSl4UU+792n0OOBWLJ4dB\nB7vjds2T75HFj8lx5o2SC9G53rQ5crILyBodYCikgp+YssIojBdl+ZxzlVSz\nppaZGL8OLlZxzmyI4CvHMRp.TiX4ZP9ywpflbj1Ihm0idfVh4o6iCyGGlIa9\nPbuEqvgbMaiE4EzwcD.SkOS8uS3F8vLskTDmSNFWwIo31lDeWPZdF9bM7tzn\n19ii5JS7SO7Ronq84X0I7.K5WSzp4gRrJIoAYzBRUOrMcIU84zjsSFiKlm6T\noJqPXc9qx22kw97lC9WI.x8X3gKDyBqZyox49IRGQDOd+sgqts5lBZwfxpJW\nnzzoMoDxlDWUeYIE764Z+JQ2TZusBKpE2TpSGCWQJwrLJD92+p2rILMK+Mzh\n97aHUnp270uDj.KVU0eAyIHpkPYD8so1nWkklpEFUKHJVJPpOVZ62QOXUi3P\nVxYtjJD7gFBKIjtj3s8ppkRKIjbmrjBBc3Hou0Gw80TbOhOV7nqS5mCkm1YV\nVyDSBA4C8q+jDcgWkGMR9x9Cu6CZGpamkNE.elaYUZO+1vrCaMm+Lz6.07sF\ngZ78GnF+Hs4y6aijrqCpS3gcrFsLEgD1KLaV3GtWc4mtx.PvtAB0BIN2IAqx\natl+50rMYUVpPM.k9Zz9zAO.o8Vs+9Geu12U7D8R+87Om+z1iOpHOcieTlL+\n8LdLk+rDtw7W3Gb8g3UQ6WGfO6R6mYD7d1H07Z8DUGi4mafEDCD7TfnGm+df\n49yMsMYpKL7sQ11QNpUBMaSQ4d+oIs9XGvpilqipvzS14Jprz28osfxbmmqV\nso+snEV5KbQ5KrNGkm952rXqhPeaQCMsgos+5aVVkOHbuApfK8GbyFe3MzrX\nO2xeisK5yEW1Vq.zntYPMd2U5e25KTh9plc+gX1s9m10WZ0ho2tLANXjuMTy\nagOdqdvGOcQ0kU7hX5RS1JlNzJ7hjfSvy8QMBQZ95EGA0OpQHBq1NoYYoqFw\n2iR6cb5KvPbMG6fFPZw6ZXxfjVvtFRw5RXg5pmsl0JPWMITCK5UZUXtl7fWI\ny+1anFRJI1U31FNqmrQ5z0PrvQYgUeKnL8fnPAhBEHJTfnPAhBEHJTfnPAhB\nEHJTfnPAhBEHJTfnPAhBEHJTfnPAhBEHJTfnPAhBEHJTfnPAhBEfoBQgBDEJ\nPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJPTn.QgBDEJ\nWXQgRMTDzZjIO9TpfjZqvMoFLeZG4JO0hnEtXKuEj4subBnE7oEX0Y0vqEu4\nlFam4hG9EFGp7Ftd8BGdwEdC8iK1VNg3ofS8FrGNIizrr6LYXA.AqaNurSVX\nUQ0DnGVpkqYyhoROwqjo8yFdp8wWbbNq7zdi4rBdnoGKzy5kGZr3QMlyZ95G\nVr9c1h4rR4h2hOs71jn08IYz0ktARmVwZVzeMIx34x1nlyjgEAgm0sQDk0Ir\nTZ.oQhDt8Awqd3XqQQEgFFxy3PngIqBEINxvPB425mOFZAwYYmg5vjKVzMmu\nn86lzjj6B5oTUcnQCuTUYJeYfqMUKHjiYspDG4vTQqCr0gPDDBQvi7JJgPDD\nBQPHDAgPD7UPHBBgHHDhfc7c3YJTzfPDDBQPHDAgPD7QHZyfPD7IHSCBQPHD\nAgPDDBQPHDAgPDDBQPHDAONlJDhfW79SBBQvm5bPHDAg3ACBQPXIADhfPHBB\ngHHDhfPHBBgHHDhfbXKPHBBgHHDhfPHBBgHHDhfuDJTU7AZdKzw67DpPU4VT\nnpXEKIS69BZHqG2BUkk6wGOkmsfFpWhtgAilSicDSTuAP4iKM2z83Wq+MmwR\n9DMhd5IFPpZyvCAD6d1HUD5GHxNHWZQGiveExSsf..AB.DH.Pf..AB.DH.Pf\n..AB.DH.Pf..AB.DH.Pf..AB.DH.Pf..AB.DH.Pf..AB.DH.Pf..AB.DfoBA\n.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A.BD.HP.f.A\n.BD.HP.fLkA.BWPl2BS7tWNw+AKdU9h1062rIHEuFQ66hRR10Dl6hCKjETbr\nirs5MDELEBmcyiCN6EgnvUhCVgoFZ6Dv2MFfU8mw6ut1e0uqQ5vmlZxnJLo6\n8Z6EqH40A2LDCqESt+aj02jP53q1l80unI25yjgV00iGw1+y.w9sxVbiktgF\nCxMw4rY9a2EUBojIfjqeIfD9BY9R7S5CwqFk.VA2Okvy4qH0KufqJnvYWogW\nS+Rk9RzRXrVwdOV0PMRGdk1ljHBJZVSbQlu1+k9+E4ZE9uP+WWQfBEgHj8sA\nwzeR+BXkRN2BUPON3YShGmSSxx13udTzE48ThKAWEDmYtcG06jku.x8CR0N7\n21ucGQiE1sAQeDd1hOxHizD+67CincSgnnsXh.Q22GkCAPONnXSF9AGmcNeD\n2OYLefUp.YADYXJyO+oePgwlHiAw5Caz1h4PgjkirdkBUrLM+TxZWrsRo3U5\n4IjKzbeVv5qXtZDuJiQhWsOMEa+bzCDzNDdWUmrOi3fe71kv30AeV6S5ZWiI\nkilmeuvWJyHBiEV99KLRJlIf4.uzB1LUihLhGhhChFE.H+iU3LtrWIDeBVJm\n+hO3pQ8na41j0iCNhINyDaMJVTTHS5cVgFmXMzv7BxMDlQKTyAZek974nutR\n+ShpQaCihByBVkfO68oo4UkNl6SZynyRpGcXSPxutMqSP8TwIoe0d3ir9TNu\nrrCOc7TjefCxzft3HkzfHe5wFE2ZEUKpnf3axukvE8uNKIZed0iWEEV93m7L\n0xoNgYVNOOMVZUON92gE0kd7GZepPaExzXedRmofpNzazueEliwDPOoC0iyU\nXmvPh37nSX.IvcQSwEoQc7hbafOdmsbBTPp6KNxw0.cYwQbmYE2ZfvaMi2ya\ndGG7Zgp2bFxA4ZsXwB8ywMmU+r9xCndMe0wZcwZ0OYgiLKkRjXEIXISIWlDR\n9kIURnOaEO8unYLSkLgj0BZ1sxxr2qNxXDpI5S8cAEFOJ9usPoAJfrw5BTnE\n38g42VXfowSR+M1qKz60cizY+XniVAIUlQ.OKLxj6DTYiLS1tcjhCY7lRrkK\nXQPoZk85yAi2Abk9bBWoliukDjCDNR82F7g.sBs4YmjEGRzlVhFgjC9EpNXm\nG1TEkNOVMEAMcsvZAhLWzmZfnQPMvZRDEgfHU0riLcW1UwlgoVGklcF0oCoT\nFKUcc5PfNcUamQfNcfNcfNcfNcfNcub0oCA5z8rPmNjDc5LtXzoihR8dyB8z\nTUdQFK2xnWc5zG6LVd+P7VYdcXbdP5thLMZbfeiSQagigYjLIy6Xxi9XX79F\nIcpCZsLaSZv+ZXwEPwZD8ieMRAi6rky7Iyw8AwqdPRNyupMk4LeKK8hjleQN\ny2PWWXZy2TkEg15rkhtjEg1FRVDBYMeHq42Cdlfrl+kBj4grlOj07mdrrCYM\neHq4CYMeYmlAYMeHq4CYMeHq4+DAY1PVyGxZ90zP.xZ9PVyGxZ9PVyGxZ9PV\nyGxZ9PVy+nXpPVy+h2eRPVy+oNGDxZ9PJRGxZ9vRBHq4CYMeHq4CYMe.IrPV\nyGxZ9bXKPVyGxZ9PVyGxZ9PVyGxZ9uDxZ9bgYdKjw6cjYM+pUwTbm2PVwArx\nScfC447QLeVx9zUkPBmfybO2YWo0DE5qwa7Bi8yIdy5PCIA4aqFVwR0UBF+S\n2PCIdngljglqohCMjNqgSynPU5CognIZP3n5fvbBoD15JNJLbmvQg0k6dIqK\n28RVHEGZVS3pXK8ALHlr0OpJTwaBGDlKTbT3MkRUL8TcCs24dqj5CM6y8VIy\nKWAPlWtBfLU8HLzjth2dHihIa2upxflzAgpZWYMoiB8gnd0jMJTcPLkJZZr3\nhUth5CsytbEC2grLdpjqXnpbEJkbxFECR2lIaTn5tIioTMOCCUGESoISpNHl\nRcMQdWrxUTenc1kqfbGxN5Kzg1Yme5LDO1LUBfPFCQIOioZTLDKtMNqqeTcj\ngN6qezU1.lESoDS8gXcN+QwA+HecxmuIMY+NA9RF+3pvqitok7xYCiEyp555\ncXV9CQh7L8gZSP700xb9kW0IeBvlj37xuHdT727aUvBnMHK7OJZ.pwVlqpQl\nIE8H5nqk+9mQqlR0W2Te9TL1Vd8MqRhXWpymHWsfIxz0v1ZgNZgitGlPOGU8\nmtdVlHxGYfPtNVt51ddFl1lWQt5kCcciNbgwBKjwUM9MSaxu15aEDEPR0SM9\nxl1t1V5tFNNNHGO520FYa6XuXAxRG43R+HOGjqmiqmgqEx1xsU+5uZUmt0xT\n2zBg7rPlKLrocqtigotsoIB+CjCqaMLzs087Pld1ds5UBVX1EznWMr8LM8zc\nLWXaap6VLQsLviXOcCSOSK13Gs.svZgmkkoitUqt85a1DFEQ61kU2hH6svqI\niNqqSmilzd2nh14XVve00srsn8ktom6BWIcge7MrE8FtGtSrFsXWZBAHRExO\nv7iEb6m84I2j5uNr3dDwc1qquMikqT9Ps7ITw13Ye3mJ+ztBEpXeaCWGVaWY\nUxo5sGtduY492zd2aiTeVsMvMGZrTqwQO1ZmS7NL53L3ZVHNDL.u5XdYFbeY\nFmxKaRo5D47j6ldk+paCZS3y7uecP99ZG7UN7pgqkCMp7cOKL68EWka086Rj\ntmEvvcW8gT0A.zoItunUybIut5M6zdgrhUQeuuZs5zdcY92dCErgxIlkM5zd\nYGthaIurt2C9wNyt+tjvU0f1.+kIEM5zdYGJ48RdYGZzo8xHkfkjz9da0Z0w\n85ZtkjHDOy+t5HbaVVr+trBX+TeGJVzePkfhu205iksqbFTfnk5hlHIPijzZ\nOn5.mCEDnhtodFeAq7dXQQQmJ0nJgNxOAwLKa+0UO3uikYsAq.cE7hlEr85l\n.aryT7.klCWnlbxp7dV4gc00pjytNk+pbjOLfWaaIm7+pt1c+t7DDx+aay8E\n2ZuHuupKuw7xkY6390Hn9krPKqg5+7TgGSsUnM22DAc0AAUqwPKDqgWSsCur\nscGzFWa75lNsgX2PSFTmQcWnW14OahtNXX.CCXXHZXbvp+FOq93CDE.CCXX.\nhB.QAvv.FFfn.PT.LLfgAHJ.DE.CCXX.hB.QAvv.FFfnflOn1cXz81AU9Ja5\ndko79pVV57u6DVbVWDgwoAaSxCVF7Y+s6hB5dgo0udsNW20p8oDfPsr9Ehc3\nRDwOIMrI7sZfEqdtPPA2InrqErwMC5nSq.WyZ.puC2KXy4c8p3U4XpIvxZdM\ng0+7NWPH26Hr04.7tovWIpZWcfQ9p1IUCN2Z3wzM7tAwia3bOm8Rh5FWaQ8C\n2aVTROYKY.09VFE1MthmW8brMu6crs7i1odsFW.YuMdbzHPn5EcaqntjWKEJ\n.kiLzthQ48IsjoCCOX3ACumHCu5pAMEJqABwfgGL7fgGHDC3TvvCFdvvCDhA\nKjfgGL7.gXfPLX3ACOX3ABw.NEL7fgGL7NeBwZB+gFoQ8tXc3UGwkKyA2CB6\nl5ne3UcuM29w.QquDEtBoAaDce9Rfc.66dHhkCVkDutnTi7Iz+b9V+OSPKPm\nuwN+b5UDO6Ke66RVsmjhDx91ev+yZdeaIVIx59kRxZkwEpAFARCJlytl91Fq\nBHIDEOuqszWrxzwBsXkg805A3OpNU30cWfdFwwAxPJPNzWs9Zu.20uciCx+s\nHTv0uk9ml5FVlFlA5F95me3czZE1.GjiG7PFxVFLgVbGMjsvniFGECBTDpA5\nhKXfybZBZ3runkjlBQQOZxX13qaa6txEsd8FKz0qWa34t95EKzscwxbLbtbj\nwfOs.jw7nerLHi4RSFCm8EsjwTJE5QSHy0NqLV333tYgoo9lE1FNWuwEYsvj\njp27WGvWHSotpsynMjrYy5kIW+aAqx4TIjJSlMI6xC2F9GUYIwY+TUR5ZVUQ\nkZIlJ0p7uMa+9grak2XqZPIY3QWLjrgS9ooZrEFeWXVX6pSWsFDkDeSibr3W\nlwoUaSVukTtIam8fJdLlWkl2euTJSzrES40k+K6TA5zfjFIKlXro0L7xNZIO\npHu8wxkXEzr7fOyxqXumVcLYUl3M9gQ3eK+VRUp1WKaKopDuNfTPcCiyBnEQ\nyaw6OJ6DZV0LL9lko3UELOrX4YLW+JMCa1O7VP+g9grl2rB9bQxsm8Yw62RS\nqcY0SXR3OjkP4xpRajizblTbkKp.2KILgLM7+Oy+lfl0pa+MjJfluFta1EFQ\nWSqkknce.slLSpvnzBkXdh1uGmbOsNIl6mdSPtF9SSC7W+.qNxR913tLAySI\nTKs3f6KqnZzRL0pj8rj7loX5psGIqBpgVfpQWsb4SWsmX5JdBfeMcHqrUJHc\nccYKPzMYKPnSDGW5eXvedXxcdXvcdfZjCBK237IrbPeRt55eNdSyrfH7ro4B\nHElvHGJCzvQ1L1X.yXCoy3qvJRMlyZhLp4LGBHYxZZQWXhbo+v0T1bEMfUol\nhlqr7Ul1rvX5OXgiQ8oM8M2rRtwnBsSIvEzhtoB35ImY4oYatTaUeOMxY9Bd\nOKpk09O5WjgBuHyQXBgLPpNkNznsgq2kfYlELKCczb6qzLcYGmT6uN0Am9PF\nbcVO4uaWMSmnuE7tneik+aoEdPl8Uj+zg9moAD8JXsmVLJm4mh28ji25rOko\nFvmcrlw9pXMGRi2GVj.voJ5bnhXlsyeUYAs8yyd8+mW++OVzuLqC\n-----------end_max5_patcher-----------
", "author": { "name": "Tom Whiston" }, "tags": [ "blog" ], "date_published": "2024-08-11T16:29:25+01:00", "date_modified": "2024-08-11T16:44:27+01:00" }, { "id": "https://tomwhiston.com/enabling-pretty-printing-in-vscode-for-cortex-debug/", "url": "https://tomwhiston.com/enabling-pretty-printing-in-vscode-for-cortex-debug/", "title": "Enabling Pretty Printing in VSCode for cortex-debug", "summary": "Sooner or later if you're working with the Daisy development boards you're going to end up using some type which you can't easily see…", "content_html": "

Sooner or later if you're working with the Daisy development boards you're going to end up using some type which you can't easily see the values of with the breakpoint debugger. An example of these would be any of the std:: containers (standard caveats apply to using dynamic sized containers and the std lib on embedded devices).

\n

Inspecting a std::string without pretty printing enabled looks something like this

\n
\"\"
\n

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.

\n
\"\"
\n

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

\n
\"\"
\n

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.

\n

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)

\n
\"\"
\n

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.

\n

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

\n
\"gdbPath\": \"/Library/DaisyToolchain/0.2.0/arm/bin/arm-none-eabi-gdb-py3\",
\n

to your launch.json configuration for your project

\n
\n
\"\"
\n
At this point if you debug your project you would expect it to work, but unfortunately if you have a modern version of osx you're going to get into some issues with Gatekeeper because the Xpack toolchain is not signed.
\n
\"\"
\n
Luckily this is easy to solve, just go to the toolchain directory in your terminal and remove the quarantine attribute from all the files by running
\n
\n
xattr -r -d com.apple.quarantine .
\n

the -r flag will make it run recursively, so it should sort everything out with a single command.

\n

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

\n
svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python
\n

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:

\n
python\nimport sys\nsys.path.insert(0, '/Users/twhiston/Code/gdb_printers')\nfrom libstdcxx.v6.printers import register_libstdcxx_printers\nregister_libstdcxx_printers (None)\nend
\n

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

\n
\"\"
\n

That's looking a lot better than it did before, and we can easily see the string contents.

\n

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

\n
\"\"
\n

and this is after

\n
\"\"
\n
\n
I think we can all agree that the second version is much more useful for value debugging!
\n
", "author": { "name": "Tom Whiston" }, "tags": [ "blog" ], "date_published": "2024-07-15T22:24:36+01:00", "date_modified": "2024-07-17T13:26:28+01:00" }, { "id": "https://tomwhiston.com/glrepl/", "url": "https://tomwhiston.com/glrepl/", "title": "GLRepl", "summary": "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…", "content_html": "

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.

\n

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

\n
//Typescript signature is actually\n//const functionOne = (k: number, ctx: {}) => {\nconst functionOne = (k, ctx) => {\n    return `some message`;\n};\nglrepl.manager.kp.preloadFunction('doSomething', functionOne);
\n

or config

\n
{\n    \"bindings\": [\n        {\n            \"id\": \"pushSpace\",\n            \"asciiCode\": -2,\n            \"functions\": [\n                \"doSomething\"\n            ]\n        },\n        {\n             \"id\": \"replaceLine-alt-p\",\n             \"asciiCode\": 960,\n             \"functions\": [\n                 \"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)}}\"\n             ]\n         }\n    ]\n}
\n

transform input text before outputting it using formatters written in javascript,

\n
// This example is in typescript for clarity, and user-repl.js needs\n// to be in the type of archaic javascript that max understands but\n// hopefully you get the idea.\n// See examples/custom-formatter/user-repl.js for a full pure javascript\n// implementation of a custom formatter.\n//To create a lot of extensions for the repl it's recommended to look into \n// using typescript, transpiling and generating your user-repl.js file.\n\nclass UppercaseFormatter implements TextFormatter {\n    id: string = \"uppercase\"\n    format(strArr: Array<string>, ctx: {}): Array<string> {\n        // Example implementation that returns all strings in uppercase\n        return strArr.map(str => str.toUpperCase());\n    }\n}\nglrepl.manager.preloadFormatter(new UppercaseFormatter);\n// then include via repl json config: {\"settings\"{\"textbuffer\": {\"formatters\": [\"uppercase\"]}}}
\n

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! 

\n
\n

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.

\n
\"GLRepl
\n
The GLRepl Package, as seen in the Max package manager
\n
\n

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.

", "image": "https://tomwhiston.com/media/posts/27/icon.png", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "max", "code" ], "date_published": "2023-12-31T16:09:41+00:00", "date_modified": "2024-07-17T12:39:40+01:00" }, { "id": "https://tomwhiston.com/pitch-black-systems/", "url": "https://tomwhiston.com/pitch-black-systems/", "title": "Pitch Black Systems", "summary": "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…", "content_html": "\n

\n 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.\n

\n\n

\n 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.\n

\n
\n\n
\n \"\"\n \n
\n\n
\n \"\"\n \n
\n
\n\n

\n All music by Tom Whiston.

Art by Tom Whiston.

Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com\n

\n
\n\n

\n Release Date: 11th March 2022

Label: Vigilanimides VGL005

Download: Bandcamp

Streaming: No \n

", "image": "https://tomwhiston.com/media/posts/24/tom-whiston_PBS_art_DONE_DIGIPACK-layout-Front.jpg", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "music" ], "date_published": "2022-07-18T13:29:45+01:00", "date_modified": "2022-07-18T13:58:05+01:00" }, { "id": "https://tomwhiston.com/acts-of-devotion/", "url": "https://tomwhiston.com/acts-of-devotion/", "title": "Acts of Devotion", "summary": "“The Hand becometh these things as it ceaseth to be known unto thine own senses; as Feeling waneth in the Hand of the Artist,…", "content_html": "\n
\n
“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”
\n
Chumbley 2010:29
\n
\n\n

\n 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.
\n

\n\n

\n 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. \n

\n\n
\n
\n
\n \n \"cover\n \n \n
\n \n \"all\n \n \n
\n \n \"talismanic\n \n \n
\n \n \"talismanic\n \n \n
\n \n \"talismanic\n \n \n
\n \n \"talismanic\n \n \n
\n \n \"cassettes\n \n \n
\n
\n
\n
\n\n

\n Spirit Performance Channel Tom Whiston.\n

\n\n

\n Art by Tom Whiston. \n

\n\n

\n Inside cover hand design by Dana Whiston\n

\n\n

\n Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com\n

\n
\n\n

\n Release Date: 6th August 2021

Label: Vigilanimides VGL004

Download: Bandcamp

Streaming: No \n

", "image": "https://tomwhiston.com/media/posts/23/cover.jpg", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "music" ], "date_published": "2021-08-11T11:52:16+01:00", "date_modified": "2021-08-11T11:57:47+01:00" }, { "id": "https://tomwhiston.com/to-wound-the-autumnal-city/", "url": "https://tomwhiston.com/to-wound-the-autumnal-city/", "title": "To Wound the Autumnal City", "summary": "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…", "content_html": "\n

\n 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. \n

\n\n

\n Side A and B written in 2009/10. Bonus Track 2021\n

\n\n

\n Available as digital and strictly limited cassette edition of 50.\n

\n\n
\n \"\"\n \n
\n
\n\n

\n Music and mixing by Tom Whiston.\n

\n\n

\n Art by Tom Whiston. Cassette Print by Dana Whiston\n

\n\n

\n Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com\n

\n
\n\n

\n Release Date: 1st June 2021

Label: Vigilanimides VGL003

Download: Bandcamp

Streaming: No \n

", "image": "https://tomwhiston.com/media/posts/22/front.jpg", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "music" ], "date_published": "2021-06-03T09:42:44+01:00", "date_modified": "2021-06-03T09:53:31+01:00" }, { "id": "https://tomwhiston.com/linnsigil/", "url": "https://tomwhiston.com/linnsigil/", "title": "LinnSigil", "summary": "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…", "content_html": "\n

\n 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.\n

\n\n

\n Code\n

\n\n

\n Download Releases\n

\n\n

\n \n

", "image": "https://tomwhiston.com/media/posts/21/Screenshot-2020-08-16-at-16.02.32.png", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "max", "code" ], "date_published": "2020-08-16T15:55:27+01:00", "date_modified": "2020-08-16T16:05:11+01:00" }, { "id": "https://tomwhiston.com/meditations-i/", "url": "https://tomwhiston.com/meditations-i/", "title": "Meditations I", "summary": "A deep listening meditation for invoking Baphometic energies Headphone listening recommended Performed 25th - 31st May 2020 Hurdy-Gurdy and Effects - Tom Whiston Mixing…", "content_html": "\n

\n A deep listening meditation for invoking Baphometic energies

Headphone listening recommended

Performed 25th - 31st May 2020\n

\n
\n\n

\n Hurdy-Gurdy and Effects - Tom Whiston\n

\n\n

\n Mixing - Tom Whiston\n

\n\n

\n Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
\n

\n
\n\n

\n Release Date: 31st July 2020

Label:  Vigilanimides VGL002

Download: Bandcamp

Streaming: No \n

", "image": "https://tomwhiston.com/media/posts/20/IMG_0108.jpeg", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "music" ], "date_published": "2020-07-30T14:36:28+01:00", "date_modified": "2020-07-30T14:45:39+01:00" }, { "id": "https://tomwhiston.com/tom-whiston-zurich/", "url": "https://tomwhiston.com/tom-whiston-zurich/", "title": "47°22'0.01"N 8°33'0.00" E", "summary": "A new EP of music about my time living in Zurich, made largely with samples from around the city. Mastered by Gregg Janman at…", "content_html": "

A new EP of music about my time living in Zurich, made largely with samples from around the city.

\n

Mastered by Gregg Janman at Hermetech Mastering - hermetechmastering.com
Photography and Artwork by Arnaud Marquis - arnaudmarquis.ch

\n

 

\n

Release Date: 14th May 2020

\n

Label:  Vigilanimides VGL001

\n

Download: Bandcamp

\n

Streaming: No 

", "image": "https://tomwhiston.com/media/posts/19/47deg220.01-N-8deg330.00-E-artwork.jpg", "author": { "name": "Tom Whiston" }, "tags": [ "posts", "music", "modular" ], "date_published": "2020-05-14T09:22:14+01:00", "date_modified": "2020-07-30T14:37:50+01:00" } ] }