CAL cursus   Select English Course

Introductie
Syntax van CAL
Datatypen
Events
Constanten
Declaraties
Interactie met de gebruiker
Rekenkundige bewerkingen
Relationele functies
Logische operatoren
Muziek tijdfuncties
Flow control functies
Het aanmaken van een CAL-programma
Het uitvoeren van een CAL-programma

Introductie

In de loop der jaren zijn MIDI-sequencers uitgebreid met allerlei digitale audio-functies. Ook hadden ze eerst vooral als doel een externe synthesizer aan te sturen. Later werden softwarematige synthesizerfuncties toegevoegd. Functioneel zijn de programma’s van de diverse leveranciers steeds meer naar elkaar gegroeid, maar hebben hun eigen specifieke gebruikersgroepen gekregen. Cakewalk Professional heeft zich vanaf versie 2 onderscheiden omdat het de gebruiker de mogelijkheid biedt om eigen func­ties toe te voegen. De Cakewalk Application Language (CAL) maakt het mogelijk kleine programma's te schrijven waarmee Cakewalk aangepast kan worden aan je eigen specifieke wensen. Met latere versies van Cakewalk is CAL verder uitgebreid, maar de laatste jaren is het wat onderbelicht gebleven.

Nog steeds vergt het veel tijd en inspanning om een goede MIDI-sequence te maken. CAL biedt interessante mogelijkheden om je productiviteit te vergroten. Veel muziekliefhebbers zijn echter geen programmeur en CAL is geen eenvoudige taal die je zo maar toepast. Op internet zijn echter tal van CAL-programma's te vinden die zo in Cakewalk gebruikt kunnen worden. Ikzelf maak meestal een CAL-programma zodra ik ontdek dat een bepaalde bewerking vaak terugkomt en ik door een programma veel sneller tot een goed resultaat kom.

Hier wil ik ingaan op deze programmeertaal en hoe je deze kan toepassen. Ik zal CAL bespreken zoals het bij Cakewalk SONAR kan worden gebruikt. CAL leent zich bij uitstek voor het modificeren van een MIDI-sequence. Je kan hierbij denken aan een triller maken, de sterkte (velocity of Expression) van de noten aanpassen om een natuurlijke aanslag te krijgen, maar ook om de synthesizer instellingen te veranderen. Als een bepaalde standaard edit-functie net niet is wat je nodig hebt, kan je met CAL er één maken die precies doet wat je wilt.

Cakewalk programma's zijn in het algemeen niet zo groot, en naarmate je er handiger in wordt, zal blijken dat je steeds sneller complexere programma's maakt.

De syntax van CAL

Allereerst wil ik ingaan op de structuur en opbouw van een CAL-programma. De syntaxis van Cakewalk lijkt sterk op de programmeertaal LISP en dat is even wennen als je hiermee geen ervaring hebt.

Laten we er van uitgaan dat we twee getallen van elkaar willen aftrekken. We zijn gewend dit als volgt te schrijven:2-1

In CAL ziet dit er uit als: (- 2 1)

Het min-teken is een functie en de twee getallen zijn de variabelen die bij de aftrekking worden gebruikt. We geven in het volgende voorbeeld de functie (operator) aan met op en de variabelen met var. Een CAL-programma ziet er gestileerd weergegeven als volgt uit:

(do (op var var) (op var var))

Deze geneste structuur is kenmerkend voor CAL. Om de leesbaarheid van een programma te bevorderen, wordt het echter meestal niet op één lijn geschreven:

(do ; ieder programma begint met een do opdracht
(op var var) ; commentaar wordt achter een ; geschreven
(op var var)
) ; einde van do ; een goede gewoonte is om het einde van een
;   do opdracht aan tegeven met een ; einde van do

Binnen de do opdracht staat het programma. De do opdracht geeft een rij van opdrachten (statements) aan die bij elkaar horen en dus als het ware één functie vormen.

Variabelen moeten echter alvorens te worden gebruikt, worden gedefinieerd (gedeclareerd); anders worden ze niet door CAL herkend en gezien als een onbekende functie.

Alvorens dieper op de taal in te gaan, wil ik bespreken hoe CAL programma’s gebeurtenissen (Events) in een MIDI-sequence behandelt. Events kunnen zijn: Chords, ‘Channel aftertouch’, Controllers, Expressions, Hair pins, ‘Key aftertouch’, Lyrics, ‘Non-registered Parameter Numbers’, Notes, Patches, ‘Registered Parameter Numbers’, ‘System Exclusive Data Messages’, Text, Wave, Windows ‘Media Control Interface commands’ en Wheel. Het is een mengeling van MIDI, Audio en blad­muziek zaken.

Een MIDI-sequence is georganiseerd per track met Events die op een bepaalde tijd plaatsvinden. Ook is het goed om te realiseren dat als je een MIDI-Channel associeert met een track, alle Events in deze track over het betreffende channel van deze track worden uitgezonden. Een CAL-programma werkt op (een deel van) geselecteerde tracks en loopt één voor één langs deze tracks en bewerkt eventueel de Events in de betreffende track.

Om een beter gevoel te krijgen hoe dit gaat nemen we een klein programma als voorbeeld. In het voorbeeld worden alle noten die zijn geselecteerd op een velocity 100 gezet. De geselecteerde range wordt in het Cakewalk ‘user window’ aangegeven met de waarden in de vensters From en Thru.

1 (forEachEvent ; proces iedere geselecteerde Event
;    binnen de range
2 (if (== Event.Kind NOTE) ; als het een note is,
3 (= Note.Vel 100) ; dan zet je de note velocity op 100
4 ) ; einde van if
5 ) ; einde van forEachEvent

Ik heb regelnummers ingevoerd die er normaal niet zijn, maar nu helpen bij de uitleg. Het inspringen maakt het programma leesbaarder, maar is niet strikt noodzakelijk.

Omdat er geen eigen variabelen worden gebruikt, heeft het programma geen declaraties nodig.

  1. De functie forEachEvent geeft aan dat de opdrachten binnen deze functie voor alle Events die door de gebruiker geselecteerd zijn, worden uitgevoerd. Dat kan dus zijn in alle tracks, in een deel hiervan of op een aantal Events in één track. Deze functie is een heel belangrijke in CAL en wordt veelvuldig gebruikt.
  2. Als (if) het Event type (Event.Kind) gelijk is (= =)[1] aan een NOTE,
  3. wordt de note velocity (Note.Vel) wordt gelijkgemaakt (=) aan 100.
  4. Geeft het einde van de functie if aan.
  5. Geeft het einde van de functie forEachEvent aan.

In dit programma zie je verder, dat een statement meer dan één regel mag bevatten:

Ik wil nu verder ingaan op de belangrijkste data typen, Events, variabelen en constanten.

Datatypen

CAL kent als variabelen: integers en strings. Een integer in de grootte van 16- of 32-bits - kan zowel met als zonder teken zijn. Integers met teken zijn:

int 16-bits, van – 32676 tot +32767;
long 32-bits, van ruim min 2 miljard tot ruim plus 2 miljard.

Integers zonder teken zijn:

word 16-bits, van 0 tot 65536;
dword 32-bits, van 0 tot over de 4 miljard

Verder kennen we nog de string: een stuk tekst

De programmeur kan variabelen definiëren van bovengenoemde typen en er een naam aan toekennen. Daar komen we later nog op terug.

Events (gebeurtenissen)

In CAL kennen we Events (gebeurtenissen). Een Event is van een bepaald type (Event.Kind), gebeurt op een bepaalde tijd (Event.Time) en is gekoppeld aan een Channel (Event.Chan).

We kennen de volgende belangrijke typen Events:

Event.Kind Variabelen Type Betekenis Toegestane waarden
CHANAFT ChanAft.Val int Sterkte van de druk 0..127
CONTROL Control.Num Int Control nummer 0..127
Control.Val Int Control waarde 0..127
KEYAFT KeyAft.Key Int Hoogte van de noot 0..127
KeyAft.Val Int Sterkte van de druk 0..127
PATCH Patch.Bank Int Instrument bank -1..16383[2]
Patch.Num Int Instrument nummer 0..127
Note.Dur word Lengte van de noot 0..65535
Note.Key Int Hoogte van de noot 0..127
Note.Vel Int Sterkte van de noot 0..127
WHEEL Wheel.Val Int> Wheel waarde -8192..8191

Een Event.Chan kan de waarde 0 tot 15 hebben. In het Cakewalk ‘user window’ zullen deze waarden worden getoond van 1 tot 16.

Event.Time is een variabele van het type dword. Het wordt uitgedrukt in‘raw time’(clock ticks). Deze tijd kan worden omgerekend in maatnummer, tel en aantal tikken (Measure:Beat:Tick).

Er zijn nog meer Events, maar die zijn voor het begrip nu niet van belang. Later komen deze wel aan de orde.

Verder zijn er nog variabelen, die ook in het Cakewalk ‘user window’ zijn te vinden:

From  Begin marker. Het begin van het geselecteerde gebied.
Now De positie van de ‘bar’ cursor.
Thru Eind marker. Het eind van het geslecteerde gebied.
End Het eind van de sequence.

Deze variabelen zijn alle van het type dword.

Constanten

Cakewalk kent ook een aantal vooraf gedefinieerde constanten:

TRUE Een boolean constante die aangeeft dat iets waar is. De waarde is ‘1’.
FALSE           Een boolean constante die aangeeft dat iets niet waar is. De waarde is ‘0’.
TIMEBASE Deze constante geeft het aantal tikken per kwartnoot aan.
VERSION Versienummer van CAL.

Nu we de belangrijkste datatypen hebben leren kennen, kunnen we de functies van CAL bekijken. De functies zijn onder te verdelen in: declaraties, toewijzingen (assignments) , invoer, uitvoer, buffer, rekenkundig, relationeel, boolean, ‘control flow’, ‘musical time’, menu, etc. We zullen een programma laten zien, dat goed aangeeft hoe CAL je productiviteit kan verhogen. Het programma maakt van iedere geselecteerde noot een staccato noot en verhoogt de velocity met 25%.

1 (do
2 (forEachEvent ; voor iedere geselecteerde
;  event
3 (if (== Event.Kind NOTE) ; als het event een noot is
4 (do ; dan
5 (= Note.Dur (/ Note.Dur 2)) ; halveer je de nootlengten en
6 (= Note.Vel (/ (* Note.Vel 125) 100)) ; verhoog de velocity met 25%
7 (if (> Note.Vel 127) (= Note.Vel 127)) ; zorg dat de velocity nooit
;   groter wordt dan127
8 ) ; einde van do
9 ) ; einde van if
10 ) ; einde van forEachEvent
11 ) ; end of do

We gaan nu na wat er gebeurt op iedere regel van dit programma.

  1. Het programma openen we met een do-opdracht.
  2. Binnen forEachEvent lopen we langs alle geselecteerde Events.
  3. Als het Event een noot is, voeren we alles uit binnen de volgende do-opdracht.
  4. Binnen deze do-opdracht nesten we alle opdrachten voor wanneer het Event een noot is.
  5. We halveren de lengte van de noot (/ Note.Dur 2) en maken Note.Dur gelijk aan het resultaat met (= Note.Dur ……).
  6. We vermenigvuldigen de velocity van de noot met 125, delen het door 100 en bergen het resultaat weer op in Note.Vel.
  7. Als de velocity groter is dan 127 maken we het gelijk aan 127, want groter mag het niet zijn.
  8. Hier sluiten we de tweede do-opdracht af.
  9. Hier sluiten we de if-functie af.
  10. Hier sluiten we de forEachEvent-functie af.
  11. Hier sluiten we de eerste do-opdracht af en daarmee het programma.

Wat je ziet is dat bij een geneste structuur, zoals (= Note.Dur (/ Note.Dur 2)), CAL dit verwerkt van binnen naar buiten. Dus eerste wordt (/Note.Dur 2) berekend en daarna dit resultaat gebruikt in (= Note.Dur …..).

Declaraties

Voordat we variabelen in een CAL-programma kunnen gebruiken, moeten ze eerst zijn gedefinieerd (gedeclareerd). Variabelen declareren we als volgt:

(int <naam> [<waarde>]) ; [3]declareert een integer en kent
;      eventueel een waarde toe
(dword <naam> [<waarde>]) ; declareert een dubbel word en kent
;       eventueel een waarde toe
(long <naam> [<waarde>]) ; declareert een long en kent
;      eventueel een waarde toe
(string <naam> ["Dit is tekst"]) ; declareert een string en kent
;      eventueel tekst toe
(word <naam> [<waarde>])           ; declareert een word en kent
;      eventueel een waarde toe
(undef <naam>)                        ; wist de naam van een variabele
;      uit het geheugen

Als we geen waarde aan de variabele hebben gegeven, dan is het onvoorspelbaar wat de waarde is.

Later zal ik laten zien hoe de declaratie in een programma wordt gebruikt.

Interactie met de gebruiker

We hebben invoer- en uitvoerfuncties. De invoerfuncties zijn:

(getInt <naam> <prompt> <minimum> <maximum>)

(getWord <naam> <prompt> <minimum> <maximum>)

(getTime <naam> <prompt>)

Hierin is:

<naam> de naam van de variabele;
<prompt>           het bericht dat in een ‘window’ op het scherm verschijnt;
<minimum> de minimum waarde die de invoer mag hebben;
<maximum> de maximale waarde die de invoer mag hebben.

Je ziet dat met getInt invoer voor een integer variabele kan worden gevraagd. Bij getWord geldt hetzelfde voor een word variabele. Bij getTime wordt de invoer van de gebruiker die in ‘Measure:Beat:Tick’ wordt ingevoerd, omgezet in ‘raw time’ en opgeslagen in een dword variabele. Bij getTime kunnen geen limieten worden opgegeven.

We breiden het eerste voorbeeld uit 'De syntax van CAL' nu uit met het vragen aan de gebruiker om de gewenste Note Velocity waarde op te geven.

1

(do

; open het programma

2

    (int nVelocity 100)

; declareer de variabele nVelocity

3

    (getInt nVelocity “Velocity: “ 0 127)

; vraag om invoer

4

    (forEachEvent

; proces iedere geselecteerde
; Event binnen de range

5

        (if  (== Event.Kind NOTE)

; Als het een note is,

6

           (= Note.Vel nVelocity)               

; dan zet  je de note velocity
; op de gevraagde waarde

7

        )

; einde van  if

8

    )

; einde van forEachEvent

9

)

; einde van do

In de toegevoegde programmaregels gebeurt het volgende:

  1. Het programma wordt geopend met een do statement. Alle programma statements staan binnen deze do en het afsluitende haakje hiervan vind je in regel 9;
  2. De integer nVelocity met als initiële waarde 100 wordt gedeclareerd;
  3. In een ‘window’ wordt een bericht 'Velocity' gezet met als initiële waarde 100. Dit bericht is de string tussen dubbele aanhalingstekens. Toegestane waarden lopen van 0 tot 127;
  1. De ‘noot ‘ krijgt hier de waarde die de gebruiker aangegeven heeft;
  1. Het do statement wordt met een haakje sluiten afgesloten. Dit is dus ook het einde van het programma.

De uitvoerfuncties zijn:

(message <waarde> [[<waarde>] …...])

(pause <waarde> [[<waarde>] …...])

(format <waarde> [[<waarde>] …...])

Deze functies geven een bericht in de ‘status bar’ van Cakewalk. Bij de pause functie stopt Cakewalk. Via een ‘dialoog window’ krijgt de gebruiker de mogelijkheid door te gaan (OK) of het proces af te breken (CANCEL). Het bericht van message en pause in de ‘status bar’ kan opgebouwd zijn uit een willekeurige combinatie van variabelen van het type: string, integer, long, word en dword.

De format functie is niet echt een uitvoerfunctie, maar converteert een combinatie van variabelen tot één string.

De functie message wordt vooral gebruikt om bijvoorbeeld extra informatie aan de gebruiker te verschaffen of voortgangsberichten naar de gebruiker te sturen. De pause functie is handig bij het testen van programma's.

We breiden ons voorbeeld programma nu uit met een extra informatie bij het vragen van invoer door de gebruiker.

1

(do

; open het programma

2

    (int nVelocity 100)

; declareer de variabele nVelocity

2a

    (message "Toegestane waarden voor velocity zijn: 0 tot en met 127")

3

    (getInt nVelocity “Velocity: “ 0 127)

; vraag om invoer

4

    (forEachEvent

; proces iedere geselecteerde
; Event binnen de range

5

        (if  (== Event.Kind NOTE)

; Als het een note is,

6

                (= Note.Vel nVelocity)               

; dan zet  je de note velocity
; op de gevraagde waarde

7

       )

; einde van  if

8

   )

; einde van forEachEvent

9

)

; einde van do

We verklaren hieronder alleen de toegevoegde programmaregel:

2a.   Alvorens de gebruiker om invoer te vragen wordt met de functie message een bericht op de status bar’ gegeven, waarin de toegestane waarde van de invoer wordt aangegeven.

Rekenkundige bewerkingen

In een programma zal je vaak rekenkundige bewerkingen moeten uitvoeren op je gegevens in de MIDI-sequence. CAL kent de volgende rekenkundige functies:

(+   <operand1> <operand2>) ; telt operand2 bij operand1
(-    <operand1> <operand2>) ; trekt operand2 van operand1 af
(*    <operand1> <operand2>) ; vermenigvuldigt operand2 met operand1
(/    <operand1> <operand2>) ; deelt operand1 door operand2
(%  <operand1> <operand2>) ; rest van de deling van operand1 door
;   operand2
(++ < variabele>) ; verhoogt de waarde van variabele met één
(--   <variabele>) ; verlaagt de waarde van de variabele met
;   één 
(random <minimum> <maximum>)         ; geeft een willekeurig getal tussen minimum
;   en maximum

De meeste functies spreken voor zich, maar de “%” (modulo) functie kan wel een nadere toelichting gebruiken. In CAL bestaan alleen hele getallen. Als er door een deling een rest overblijft, kan deze met de “%” functie worden gevonden. Ik zal dit toelichten met een voorbeeld. In dit voorbeeld willen we weten wat het versienummer is van CAL en dat aan de gebruiker laten zien.

Versienummers zien er bijvoorbeeld uit als: 4.5.  CAL bewaart zijn versienummer in de constante VERSION. Daar CAL alleen hele getallen kent is dit versienummer vermenigvuldigd met 10. Dus in VERSION staat volgens ons voorbeeld: 45.

Een bericht naar de gebruiker kunnen we als volgt programmeren:

(pause "De versie van CAL is: " (/ VERSION 10) "." (% VERSION 10))

Het getal voor de punt volgt uit: (/ VERSION 10) en het getal na de punt berekenen we met (% VERSION 10). Analyseer zelf hoe het bericht is samengesteld met een aantal strings en deze berekeningen. De gebruiker krijgt te zien: De versie van CAL is: 4.5.

De “random” functie is een beetje vreemde eend in de bijt. Hiermee kan een zogenaamd willekeurig getal worden gegenereerd, dat ligt tussen het minimum en maximum.

Relationele functies

Met deze functies vergelijken we twee getallen. Met de uitkomst van dergelijke vergelijkingen (TRUE of FALSE) kan het verloop van een programma worden beïnvloed. CAL kent de volgende relationele functies:

(== <operand1> <operand2>)         ; test of operand1 gelijk is aan operand2

(!=  <operand1> <operand2>)         ; test of operand1 ongelijk is aan operand2

(<   <operand1> <operand2>)         ; test of operand1 kleiner is dan operand2

(<= <operand1> <operand2>)         ; test of operand1 kleiner is dan of gelijk is
                                                             ;    aan operand2

(>   <operand1> <operand2>)         ; test of operand1 groter is dan operand2

(>= <operand1> <operand2>)         ; test of operand1 groter is dan of gelijk is
                                                             ;   aan operand2

De uitkomst van dergelijke functies is altijd een waarde 1 (TRUE) of 0 (FALSE). Een voorbeeld van het gebruik hebben we al gezien bij de introductie:

(if  (== Event.Kind NOTE)
; hier staat de functie die wordt uitgevoerd bij een uitkomst gelijk aan TRUE
) ; einde van if

Logische operatoren

Logische operatoren worden gebruikt om testen te combineren.  Dit wordt het duidelijkst met een voorbeeld. CAL kent twee logische operatoren:

(&& <operand1> <operand2>)        ; TRUE als beide operanden TRUE zijn

(||     <operand1><operand2>)         ; TRUE als minstens één operand TRUE is

Als voorbeeld gebruiken we een test of het Event een NOTE is en de Velocity gelijk is aan 60:

(if  (&& (== Event.Kind NOTE) (== Note.Vel 60))
; hier staat de functie die wordt uitgevoerd bij een uitkomst gelijk aan TRUE
) ; einde van if

Muziek tijdfuncties

CAL kent enkele functies voor het omzetten van muziek tijd in de SONAR tijd.

SONAR kent de ‘measure’, ‘beat’ en ‘tick’ (M:B:T). In de muziek kennen we de maat (measure) en tel (beat). Voor MIDI is een grotere nauwkeurigheid nodig, daarom kent SONAR ook de kloktik (tick). Deze maat, tel en kloktikken kunnen worden omgezet in de ruwe tijd (raw time) van SONAR. Deze ‘raw time’ is het aantal kloktikken vanaf het begin van een muziekstuk. Uit de ‘raw time’ kunnen we de maat, tel en overblijvende kloktikken bepalen.

(meas <rawtime>)>            ; bepaalt de maat uit de ruwe tijd

(beat <rawtime>)                ; bepaalt de tel uit de ruwe tijd

(tick <rawtime>)>               ; bepaalt de kloktikken uit de ruwe tijd

Het is goed om te onthouden dat een maat altijd groter of gelijk is aan 1. Dit geldt ook voor de tel. Het aantal kloktikken loopt van nul tot één tik minder dan de lengte van een tel uitgedrukt in kloktikken.

Omgekeerd kunnen we ook de ‘raw time’ uit de maat, tel en kloktikken bepalen:

(makeTime <maat> <tel> <kloktikken>)

Zo kan je het begin van de vijftiende maat in kloktikken bepalen met:

(makeTime  15  1  0)

Mede met de constante TIMEBASE die het aantal kloktikken per kwartnoot geeft, kunnen we bijvoorbeeld nootwaarden omrekenen in kloktikken en omgekeerd.

Flow control functies

Programma's zullen afhankelijk van de waarden van variabelen vaak een andere bewerking moeten uitvoeren. Om dit te bereiken gebruiken we relationele functies in ‘flow control’ functies. De al eerder aangehaalde “ forEachEvent” functie, “do” functie en “if” functie zijn zulke ‘flow control’ functies.

Allereerst wil ik dieper op de “forEachEvent” functie ingaan. Deze functie is een heel belangrijke functie binnen CAL. In een CAL-programma zal je in de meeste gevallen werken op geselecteerde Events. Door de “forEachEvent” functie te gebruiken leg je de focus van je programma op de geselecteerde Events. Dat wil dus zeggen dat de niet geselecteerde Events niet worden gezien. De functie ziet er als volgt uit:

(forEachEvent <functie>)

In het algemeen zal je niet één functie binnen  een “forEachEvent” functie uitvoeren. Wil je meer functies uitvoeren dan moet je ze combineren binnen de “do” functie. Voordat ik dit in een voorbeeld laat zien, behandel ik eerst de “if” functie:

(if  <conditie>  < TRUE expressie>  [<FALSE expressie>])

De conditie zal een relationele functie zijn eventueel gecombineerd met een logische functie.

Voor de duidelijkheid kan ik de functie ook - met toelichtend commentaar - schrijven als:

(if  <conditie>           ; als de conditie waar is
; dan
<TRUE expressie> ; de uit te voeren functies als de
;   conditie TRUE is
; anders
[<FALSE expressie>] ; de uit te voeren functie als de
;   conditie FALSE is
) ; einde van if

In de eerder gegeven voorbeelden hadden we het optionele anders-deel weggelaten. Je ziet ook hier dat er dus maar één functie kan worden uitgevoerd bij TRUE en één bij FALSE.  Wil je dus meer functies uitvoeren, dan zal je weer de “do” functie moeten gebruiken.

In het volgende voorbeeld laat ik zien hoe één en ander werkt. In dit voorbeeld tellen we het aantal geselecteerde noten. Noten die op hetzelfde moment vallen worden als één noot gezien. In de variabele teller houden we het aantal noten bij en in de variabele vorige de tijd van de daarvoor gevonden noot.

(do
  (word teller 0)                                       ; definieer de teller en zet op 0
  (dword vorige 0)                                  ; definieer vorige tijd en zet op 0
  (forEachEvent                                      ; we testen op alle geselecteerde Events
     (if (== Event.Kind NOTE)                 ; test of het een NOTE is
         (do                                                  ; hier gebruiken we een do om een
                                                                 ; een aantal functies te combineren
            ; dan is het een note
            ; test de gevonden NOTE
            ; als de tijd==0 (vorige==0) en de teller==0, is het altijd de eerste NOTE
            (if (&&  (== teller 0)  (== Event.Time 0))
               ; dan
               (= teller 1)
               ; anders
               ; als de  tijd van  de vorige NOTE ongelijk is aan de huidige tijd
               (if (!= Event.Time vorige)  ; first event on time zero
                                                                 ; zet de teller op 1
                    ; dan verhogen we de teller en maken ‘vorige’ gelijk aan de huidige tijd
                    (do                                       ; hier gebruiken we een do om een
                                                                 ; aantal functies te combineren
                        (++ teller)                         ; verhoog de teller
                        (= vorige Event.Time)    ; zet vorige op de huidige tijd
                     ) ; einde van do
                ) ; einde van if
             ) ; einde van if
         ) ; einde van do
    ) ; einde van if
  ) ; einde van forEachEvent
) ; einde van do en het programma

Bekijk dit voorbeeld goed; hierin zie je belangrijke constructies zoals ze in CAL voorkomen.

Naast de do, if en forEachEvent functies zijn er ook nog andere functies: switch, while en  include.

(switch <index> <case1> <case1result> <case2> <case2result> ….)

Met deze functie kan aan de hand van een specifieke waarde van de variabele index een bewerking worden uitgevoerd. Een praktisch voorbeeld helpt dit duidelijk te maken. Ik laat nu een stukje programma zien waarmee je de GM drumset in een andere drumset om kan zetten.

(switch nNote
    40         (= nNewNote 50)              ; zet Electric snare om in Snare low
    41         (= nNewNote 55)              ; zet Low floor tom om in Tom bass
) ; einde van switch

Op deze manier kan je dus een hele reeks noten omzetten in een andere reeks.

(while  <conditie>  <actie>)

Zolang de conditie waar is, wordt het programma binnen deze functie doorlopen.  Laten we een praktisch voorbeeld bekijken. In een sequence op de plaats rPositie willen we het aantal kloktikken weten dat een tel bevat.

; eerst bepalen we het begin van de maat
(= rTemp (makeTime (meas rPositie) 1 0))        ; rTemp bevat het begin van de
                                                                                 ;   maat
; Bepaal aantal kloktikken in een tel
(while (< (beat rTemp) 2)                                       ; zolang de tel kleiner is dan 2
    (++ rTemp)                                                          ;  verhoog rTemp
) ; einde van while
; nu berekenen we het aantal kloktikken van een tel
(-=  (rTemp  (makeTime  (meas  rPositie) 1 0)) ; trek het einde van de eerste tel af
                                                                                 ;  van het begin van de maat
; rTemp bevat het aantal kloktikken van een tel op de plaats rPositie

We verhogen rTemp net zolang tot we in de volgende tel komen. Dan vallen we uit de while functie. Nu trekken we het ‘einde van de eerste tel’ af van het ‘begin van de maat’ en bergen het resultaat op in rTemp.

(include <file naam>)

Deze functie geeft de mogelijkheid om een ander CAL-programma op te roepen binnen een CAL-programma.

Een goed voorbeeld van deze functie is het oproepen van een programma om het versienummer te testen. Hierbij als voorbeeld een programma om MIDI-controllers in een sequence in te voegen, waarbij eerst een versietest wordt gedaan:

(do
    (int nCALVersion 20)                                                  ; vereiste CAL version
    (include "+need version.cal")                                     ; test versienummer van CAL
    (int nControlNum 7)                                                     ; Controller nummer default
    (getInt nControlNum "Controller number: " 1 127)   ; haal controller nummer
    (include "+Controller.cal")                                          ; proces het invoegen
) ; einde van do
; einde van het programma


Het aangeroepen programma.

(if (<  VERSION nCALVersion)
    (do
        (pause "MK003: This program requires CAL version " (/ nCALVersion 10)
            "." (% nCALVersion 10) “ or higher”))
         (exit)
     ); einde van do
); einde van if
; einde van programma

Het programma ‘+controller.cal’ is een vrij uitgebreid programma dat te ver gaat voor deze artikelenreeks.   Geïnteresseerden kunnen het echter vinden in de CAL bibliotheek op deze WEB-site.

Het aanmaken van een CAL-programma

In eerdere versies van Cakewalk was een speciale CAL-editor ingebouwd. Met de introductie van Cakewalk SONAR is deze verdwenen. We zullen daarom van standaard editors gebruik moeten maken. [4]

CAL-programma's zijn tekstbestanden die we bijvoorbeeld kunnen maken met ‘Microsoft Word’. Het eerder getoonde voorbeelden kunnen zonder de regelnummers worden ingetikt met ‘Word. Daarna  slaan we ze op met de opdracht ‘Opslaan als..’, waarbij we aangeven dat het een tekstbestand is. We geven het de  extensie ‘CAL’ [5] . De bestandsnaam zetten we tussen dubbele aanhalingstekens [6] . Het programma zou Note velocity.cal” kunnen heten.

Het uitvoeren van een CAL-programma

Van de MIDI-sequence selecteer je in Cakewalk de track of range waarop je het programma wilt laten werken. Daarna roep je een lijst van CAL-programma's op doormiddel van het pull-down’menu ‘'Process/Run CAL...  Ctrl+F1'’[7] of direct door de toetsencombinatie ‘Ctrl+F1. Uit deze lijst kun je kiezen welk programma moet worden uitgevoerd. Selecteer het programma en kies ‘Openen.

Voettnoten:

[1] Tussen de beide gelijktekens zit geen spatie.
[2] ‘-1’ betekent: niet gedefinieerd.
[3]
<naam>  geeft een verplichte variabele naam aan. Variabelen tussen [ ] zijn optioneel.
[4] Een CAL-editor in embryonaal stadium is te vinden op: http://www.collusioninc.org.uk
[5] De te gebruiken naam van de map, waarin we het bestand moeten opbergen, kan gevonden worden onder  Cakewalks ‘Options/Global…/Folders.
[6] De dubbele aanhalingstekens zorgen er voor dat ‘Word’ geen extensie TXT toevoegt aan de bestandsnaam.
[7]
Dit geldt voor Cakewalk SONAR. In vroegere versies van Cakewalk worden andere menu's gebruikt.

Top of this page