GeoMaestro
Data Structures
Data structures in GeoMaestro are very simple: they're all plain arrays. I started defining classes then it appeared that KeyKit does not yet efficiently dispose of them (there's no garbage collection for objects). On the other hand, Tim made a great job in making array management very simple and powerful.
So here you will find the required fields and organisation for an array to be correctly managed as a point, a circle, a segment, a scene, a piste or a ligne. Along with these definitions you will have a list of the useful functions more or less related to such arrays.
We can divide the data structures in two different groups: the internal representations specific to GeoMaestro, such as events, lignes, pistes, collections, scenes, regions and scores, and the structures for general interest in KeyKit, such as points/vectors, circles, segments, log and memories.
Sometimes the "double quotes" are not easy to manage (like in eval() statements, or in menubuttons).
So specific global variables are defined in initialisations.k to replace string indexes in arrays (for example, a point can be defined as ["x"=1, "y"=2]
or as [Xs=1, Ys=2]
).
Here are these aliases:
Xs = "x"
Ys = "y"
Cs = "c"
Rs = "r"
Ph = "nodur"
As = "actif"
Ls = "label"
SCOREs = "score"
SCRIPTs = "script"
Ss = "s"
PACs = "PAC"
Ds = "desc"
INTERs = "inter"
RACs = "rac"
CERs = "cer"
PAs = "A"
PBs = "B"
T0s = "t0"
T1s = "t1"
Ts = "tempo"
... so please do not give these names to your own variables ! also, do not change this mapping (although you can add more to it) because it is quite often used within the GeoMaestro code. You can use the NoDoubleQuote()
function (with no argument) to have the current list of aliases (if you give it a string argument, it will perform the replacements and return the result, either a string without double quotes or an empty string along with an error message)
GeoMaestro internal representations
First comes the event scene. That's where everything else comes from, since it holds the very atoms of compositions: the events.
An event is an array with 4 to 8 fields:
ev["x"]
is the x-position of the event in the scene
ev["y"]
is.. guess what
ev["nodur"]
is the "no"te and "dur"ation of the event. Actually, any valid KeyKit phrase. It should not have a channel value, except if it is to belong to a GeoMaestro channel (ch>16). More of this below.
ev["actif"]
says whether we ignore or not this event. 1 makes it alive, 0 invisible and silent.
when using the graphic tool, ev["s"]
is 1 if the event is part of the current selection.
Sometimes it can be useful to get rid of the " thing (like in an eval()
statement, for exemple). So you can use Xs
for "x"
, Ys
for "y"
, Ph
for "nodur",
As
for "actif"
and Ss
for "s"
(these global variables are defined in initialisations.k
)
So these two event definitions are equivalent:
ev = ["x"=1, "y"=2, "nodur"='a,bc', "actif"=1]
ev = [Xs=1, Ys=2, Ph= 'a,bc', As=1]
The 3 other (optional) fields are:
"label"
which is a string containing a name or id for the event, in case you want to give it a special meaning and need a hook to get it (note that you cannot rely on its index in the Ev
array since it is submitted to changes)
"script"
may contain the name of a function. This function will be called with automatically computed arguments whenever the event is listened to by a projector. See here for more about this very powerful feature.
"score"
may contain a Csound score. See the documentation relating to the generation of Csound scores.
The event scene is storing all events, each event being associated to one of the NbCan
channels availables. By default, NbCan
is set to 20 (this happens in the initialisations.k
file). This is intended to provide 16 MIDI channels, each of them associated to a single instrument/patch, and 4 GeoMaestro channels where you can for ewample store complex events involving several instruments, that is, in MIDI termes, bank and program change messages (ultimately the notes from these channels will be dispatched into the 16 MIDI ones); you may also want a large number of channels because you want to play with a large number of different distortions procedures (see below for more).
The event scene is the one and only dedicated global variable Ev
. Here is its organisation:
(ch
ranges from 1 to NbCan
)
Ev[ch][0]
number of events in channel ch
Ev[ch]["PAC"]
initialisation phrase for channel ch
Ev[ch][n]
event number n
in channel ch
We can also have a Ev["score"]
containing the first lines of a Csound score, and a Ev["orc"]
containing the filename of its associated orchestra. See here for more
As I said before, the nodurs of the events in the first 16 channels should not contain a channel value (more exactly, channel value should be one, the default in KeyKit when nothing is specified), nor a program change message. Instead, an initialisation phrase (usually a program change message with maybe a bank change, defining a patch/instrument) should be stored in the "PAC"
field associated to that channel. This phrase will be automatically put at the beginning of any rendered phrase generated by a projector, before any note. More generally, you can put in the "PAC"
any phrase you want to see at the beginning of your MIDI file when the corresponding channel is used.
Now for the GeoMaestro channels: when rendering, the notes from the nodurs will be accordingly attributed to the MIDI channel corresponding to their channel value. For example, if Ev[17][1]["nodur"] = 'ac2, bc15'
then 'a'
will be played on MIDI channel 2 and 'b'
on MIDI channel 15, while if Ev[15][1]["nodur"] = 'a, b'
then 'a,b'
will be played on MIDI channel 15.
This means that if the nodur does not contain a program change, the notes in a GeoMaestro channels will be played with the instrument corresponding to their MIDI channel value. What's the point here? We could have directly stored the events in the corresponding MIDI channel... Well, the main idea is that each GeoMaestro channel have its own set of distortion functions, so that when you attribute an event to a specific channel, you not only associate it with an instrument, but also with a particular way it will be listened to. That makes a big part of the principles of composition in GeoMaestro.
Also, you can store in GeoMaestro channels nodurs which are whole phrases, including program changes. In this case, the notes will also be affected to the corresponding MIDI channel, but they will bring their program changes along, so they will not use the instrument defined in the "PAC"
field for that channel. In this way, even if each one of the 16 MIDI channel is associated to a single instrument, you still can use as many instruments as you want by writing the program changes in the nodur of an event from a GeoMaestro channel. This is a delicate thing, since the ultimate merging in the same MIDI channel of notes associated to different program changes can lead to confusion, so check carefully what's happening there. See here for more on this topic.
The "PAC"
field for a GeoMaestro channel does not have real meaning, and should be left blank.
I know a lot of what I say here is not so clear, so you're welcome to have a look at the tutorials section when you'll see the whole thing on basic examples.
There's one more field in the Ev
array: Ev["comm"]
, where are stored all comments; this is only interesting when working with the GUI (see tutorial 4 for an example of use)
Ev["comm"][n]
comment number n
Ev["comm"][n]["text"]
content of the comment flag
Ev["comm"][n]["x"]
& ["y"]
commented point (in GeoMaestro units)
Ev["comm"][n]["dx"]
& ["dy"]
place of the flag with respect to the previous point (in pixels)
Related functions:
WriteEv, LoadEv
InitPAC
ActiveCanal, DesactiveCanal,
ActiveTout, DesactiveTout,
ActiveRegion, DesactiveRegion
... see this page for more
Another important data structure is the ligne. It's a French word meaning "line". This is the type of array returned by a projector. It contains the musical phrase, but also some information on the way it has been calculated, so that you can have a look at the intermediary stages of the calculation (which the GUI can automatically display) and also use them into your composition, as arguments to other projector calls.
If AnimInLignes
is set to 1, the lignes also contains the details for every projected event, so that the GUI is able to draw the projections in real time when playing the rendered phrase (see [hear] button).
If ProjInLignes
is set to 1, the geometry of all projections is recorded in the "proj" field of the ligne, so that the ligne can be re-calculated much faster if only events nodurs or distortion functions settings are changed. Such a re-calculation must be performed by the function ProjAgain()
.
Here is the format:
ligne["ph"]
this is the rendered phrase, what you will eventually want to hear !
ligne[0]
...its starting time
ligne[1]
...its ending time
ligne["tempo"]
is used to set the tempo when exporting the ligne as a MIDI file
ligne["para"]
may keep a record of some of the arguments in the projector call
ligne["rac"]
is an array whose format is projector-specific. It is used to help make a transition from this projector to an associated or following other projector, in a particular algorithm. Usually, it records the last position of a moving segment or circle (see the projector Brown
for an example)
ligne["inter"]
is also an array with a format depending on the projector. It is used to store intermediary positions or stages in the calculation. See the projectors documentation for details.The usual syntax is:
["inter"][0]
number of intermediary supports/stages (at the first level of hierarchy only)
["inter"][n]["A"]
and
["B"]
when support is a segment
["inter"][n]["c"]
and ["s"]
(for sense) when support is a circle
["inter"][n]["t0"]
starting time, and ["t1"]
ending time for this stage
["inter"][n][p]...
nested "inter" fields (what I called "hierarchy" just above)
see tutorial 2 for an example
Note that when this syntax is followed, the GUI tool is able to display automatically all "inter"
supports when you ask it to display the ligne.
If AnimInLignes
is 1, the following "anim"
field is also calculated by the projectors:
ligne["anim"][time tag]
for each projected event, a time tag is calculated. It's the time attribute of the projected nodur in the rendered phrase, multiplied by 100, plus a number from 0 to 99 used to differenciate the projections happening at the same time. So there's one and only "time tag" array for each projection. Here are the fields in this array:
[time tag]["ch"]
event channel
[time tag]["n"]
event number
[time tag]["P"]
projected point (on support)
[time tag]["ph"]
projected nodur
If ProjInLignes
is 1, the following "proj"
field is also calculated by the projectors:
ligne["proj"][time tag]
for each projected event, a time tag is calculated. It's the time attribute of the projected nodur in the rendered phrase, plus an arbitrary offset allowing negative time values, multiplied by 100, plus a number from 0 to 99 used to differenciate the projections happening at the same time. Here are the fields in this array:
[time tag]["ch"]
event channel
[time tag]["ne"]
event number
[time tag]["t"]
time of projection
[time tag]["d"]
distance from support
[time tag]["s"]
relative side
[time tag]["tht"]
angle of projection
We can also have a ligne["score"]
field if Csound events are projected. See here for more.
Related functions:
AddLignes, MergeLignes
ExMIDI
... see this page for more
A collection is a set of events all belonging to the same channel.
coll["nom"]
collection name
coll["ch"]
collection channel
coll[0]
number of events
coll[n]
event number n
Related functions:
Convo, Rot, Sca, ScaXY, Trans
CExport, CImport, ListColl
PlusCol
Maillage
Idem, DansChoix, RandomNotes
A piste (french for "track" or "trail") is simply a list of points. Their order can matter or not, depending on how you use it (see here for more)
piste[0]
number of points
piste[n]
point number n
Related functions:
Piste
Rot, Trans, Sca, ScaXY
List, ListPiste
... see this page for more
A region is an array defining a part of the event scene by giving a set of conditions on the channel and on the position of events ("region" is French for "area")
blob[0]
number of tests
blob[n]
test number n
blob[1]
to blob[n]
are strings: they are supposed to contains test conditions on the variables Xx_, Yy_
and Cc_
(for example "Xx_*Xx_+Yy_*Yy_ <= 2" or "(Cc_ > 10) && (Cc_ == 16)"
).
The final region is defined by blob[0] || blob[1] || ... || blob[n]
.
See here for more about the usage of regions.
Related functions:
RegionET, RegionOU, RDisque, RTri, RRect, RCanal
Geo
ActiveRegion, DesactiveRegion
... see this page for more
A score is an array containing any number of lines composing part of a Csound score.
score[0]
number of lines
score[n]
line number n
(string) ; can be any valid Csound score line. See here for more.
Related functions:
PrintScore, ReadScore, ExScore
InitScore, AddToScore
SCp2Clicks, SCpn, SCgetpn,
SCpnAdd
... see this page for more
Structures of general interest
points/vectors
v = ["x", "y"]
or
v = ["r", "theta"]
only for use with Getpolar()
and Setpolar()
You can use the xy()
KeyKit function to define a point, but beware that this function transform float arguments to integers. So it's better to use the xyd()
function ("d" stands for "decimal"), and you need to if you want to give float values to "x"
or "y"
).
Related functions:
xyd
Geo,Setpolar,Getpolar,Dist,Thet
ListSeg
... see these pages for more: point, vector
segments
seg = ["x0", "y0", "x1", "y1"]
... see above about xy()
and xyd()
. It also apply to segments.
Related functions:
Seg, SegA, SegB
... see this page for more
circles
cir = ["c", "r"]
were "c"
is a point and "r"
a float
Related functions:
Cerc
Geo, ListCer,Peri
... see this page for more
LOG
LOG[n]
string recording a console command line (from Hist
)
LOG
is a reserved global variable intended to save part of the console History, in order to use it in a function definition or as a script-like feature. You can define with HLog()
which parts of the History you want to keep in LOG. You can evaluate LOG
like a program with EvaLog()
. You can write LOG
as a function in a .k file with ExLog()
.
Related functions:
HLog, EvaLog, ExLog
memories: , FKK
, FIF
and SNARF
These are history arrays associated with different things:
FKK
stores formula strings used when calling KKSR()
or KKSS()
(which happens when you use the [operation] button in the graphic tool)
FIF
stores test statements like the ones used by the activation buttons of the GUI.
A basic history feature is implemented in the GUI using these arrays. For exemple, typing "::
" at the prompt for "operation" recalls the last formula you typed there.
SNARF
is an extended Snarf, storing phrases used as argument to Snarph()
. The Snarf itself is not automatically stored in SNARF
. You have to make a "Snarph(Snarf)
" for that. The Snarph()
function is also used to get the phrases back and to delete some of them.
All these arrays are dumped with the GUI tool, so you can keep them from an invocation of KeyKit to another by writing pages (.kp files). See KeyKit documentation for more about that.
Related functions:
Snarph
-- Back --