The text of this program is different than you would use in a real CAL program because of the addition of "nesting indention level" numbers at the start of each line of code. The reason they are there is because:

  1. HTML has limited ability to translate indention in normal fonts and so in order to demonstrate indention, that empty space has to be filled with something.
  2. With the addition of the red nesting level numbers, it is easy to see the nesting level of each line of code, see how the THEN and ELSE parts of an "if" function line up and how the closing brackets line up with the corresponding opening ones.

As far as the comment lines, they all follow the rules for comments in code because each comment line is preceded with a semi-colon. The colors serve to distinguish the nature of the comments. The blue comments explain what the code is doing in that section. The green ones denote the specific action being taken by the next line of code as a way of keeping strait the steps being taken within a larger blue-commented section.

Program Text for COLOR.CAL

; version 1.0 - A note velocity scaling tool for Cakewalk (c) 1997 by D. Glen Cardenas




;Because some patches have uneven output volume as pitch

;goes up or down, and because sometimes as a part is being played,

;the hand action becomes uneven as the player moves up or down the keys,

;this program was developed to correct the velocities of notes either

;higher or lower based on note pitch. This function raises or lowers the note

;velocities of the selected range by a percentage based on the pitch

;of the note. The farther from the reference note the target note is,

;the greater is the applied change.




;Set the FROM and THRU markers and select the track or tracks to

;be effected by this function. From the first box the user selects the

;percentage of change per octave to be applied to the notes. Enter a positive

;number to increase velocity as pitch changes or a negative number to lower

;velocity as the pitch changes. A maximum change of plus or minus 100 percent

;per octave can be requested. From the last box select the pitch number around

;which all notes will be altered. If you enter a positive number, the notes

;above the pitch will be adjusted. If you enter a negative number, the notes

;below the pitch will be adjusted.


; ---------------END OF DOCUMENTATION-----------------



;----------------START OF CODE BLOCK-------------------

;As always, we begin this program with an opening "do" statement.

;This tells CAL that the program will be made up of multiple statements.

 0 (do

 ;Next we "include" the program "" to be sure the user is running

;CAL version 2 or later. If not, the "" program will end all CAL execution.

0 1 (include "")

;We only need 2 variables to hold our user input plus one for calculations and one for operating mode.

;The first, "percent", is the percentage of change per octave we will apply to each effected note. Its value

;is initialized to a default value of 10% per octave.

0 1 (int percent 10)

;The next is "note". It is the reference pitch around which change will occur. It is initialized to change

;all notes below 60 or C5.

0 1 (int note -60)

;The variable "temp" is a temporary working variable to hold calculation results.

0 1 (int temp 0)

;Last is "mode", a variable to hold the results of testing "note" to see whether the user has entered

;a positive or negative value.

0 1 (int mode 0)

;We now ask the user to input the strength of change to be applied and assign the value to "percent".

0 1 (getInt percent "Enter Percentage Of Change Per Octave To Be Applied (+ or - 1 to 100%) " -100 100)

;If the user requested 0 as the amount of change, the program will do nothing so we abort.

0 1 (if (== percent 0) (exit))

;The user next enters the reference note. If positive, all notes above it will be effected. If negative, all notes

;below it will be effected. Recall the default value was -60, and so the prompt reflects this fact.

0 1 (getInt note "Input + or - Reference Note (Default is Change All Below Middle C) " -127 127)

;If a negative value is given for the note, we correct it to a positive value and then set "mode" to 1

;as a marker that we are going to want to work on all notes below this reference. It would be possible

;to leave "note" a negative value and use this distinction as the marker instead of introducing another

;variable like "mode", but the code is easier to read and follow keeping "note" positive during the math

;given that adding the extra variable and code to use it is no big deal.

;IF the note is a negative number…

0 1 (if (< note 0)

;THEN do the following...

0 1 2 (do

;Set "mode" to 1.

0 1 2 3 (= mode 1)

;Invert "note" to a positive value.

0 1 2 3 (= note (- 0 note))

;Close the "do".

0 1 2 )

;Close the "if" statement.

0 1 )

;Now for the loop. We will scan the sequence and for every note we find, test to see if it falls within

;the range of our change. If so, we calculate the amount of change and apply it to the note.

0 1 (forEachEvent

;We are looking only for notes.

0 1 2 (if (== Event.Kind NOTE)

;Here we find out if we will be effecting notes above or below the reference. If "mode" equals 1,

;then we do this block of code to effect notes below the reference. If "mode" equals 0, we will

;skip down to the ELSE part.

;IF we will be effecting only notes below the reference…

0 1 2 3 (if (== mode 1)

;THEN do this...

0 1 2 3 4 (do

;IF the note being scanned is below the reference...

0 1 2 3 4 5 (if (< Note.Key note)

;THEN do the following.

0 1 2 3 4 5 6 (do

;To change the current note by a percentage based in its distance from the reference note, we must

;first determine that distance. The unit of measurement for this distance is the octave which is equal

;to a span of 12 semi-tones. To find the amount of octaves in distance, we first subtract the pitch

;of the current note from the reference note. We then multiply this amount by 100 to raise any fractional

;value into whole numbers (in other words, to magnify or scale the value up by 100 times. Remember,

;CAL doesn't see fractions and doesn't round off). Now we divide the result by 12 to give us the span

;as a fraction of one or more octaves.

0 1 2 3 4 5 6 7 (= temp (/ (* (- note Note.Key) 100) 12))

;Now that we have the span, we calculate the amount of change by multiplying the span in octaves

;by the percent of change per octave. We divide the result by 100 to remove the scaling factor,

;then add 100 to this result in order to make it a percentage change.

0 1 2 3 4 5 6 7 (= temp (+ (/ (* percent temp) 100) 100))

;We now multiply the current note velocity by this percentage of change and then divide the result

;by 100 to remove the percent factor.

0 1 2 3 4 5 6 7 (= Note.Vel (/ (* Note.Vel temp) 100))

;As a last step, we test the resulting velocity to make sure it is not out of range. If the result is less than

;0, we correct it to 0.

0 1 2 3 4 5 6 7 (if (< Note.Vel 0) (= Note.Vel 0))

;If the result is greater than 127, we correct it to 127.

0 1 2 3 4 5 6 7 (if (> Note.Vel 127) (= Note.Vel 127))

;All that remains is to close our nested functions and we're finished. The loop will continue to run

;until all of the events in the selected range are examined.

;Close the "do" that nested the calculations.

0 1 2 3 4 5 6 )

;Close the "if" that tested for the note being below the reference.

0 1 2 3 4 5 )

;Close the "do" containing the code for changing notes below the reference.

0 1 2 3 4 )

;If the request is to effect notes above the reference, we execute this block as an ELSE part. It works

;just like the THEN part above, except it is executed only if "mode" equals 0.

;If we will be effecting only notes above the reference, do the following...

0 1 2 3 4 (do

;IF the note being scanned is above the reference,

0 1 2 3 4 5 (if (> Note.Key note)

;THEN do this...

0 1 2 3 4 5 6 (do

;The following block of code is exactly like the block above except this time we subtract "note" from

;the pitch of the current note because we are dealing with notes higher than the reference in "note".

;For details on the rest of this section, see the block above.

;Calculate the span.

0 1 2 3 4 5 6 7 (= temp (/ (* (- Note.Key note) 100) 12))

;Calculate the change.

0 1 2 3 4 5 6 7 (= temp (+ (/ (* percent temp) 100) 100))

;Change the note's velocity.

0 1 2 3 4 5 6 7 (= Note.Vel (/ (* Note.Vel temp) 100))

;Correct for over range.

0 1 2 3 4 5 6 7 (if (< Note.Vel 0) (= Note.Vel 0))

0 1 2 3 4 5 6 7 (if (> Note.Vel 127) (= Note.Vel 127))

;Close the "do" that nested the calculations.

0 1 2 3 4 5 6 )

;Close the "if" that tested for notes being above the reference.

0 1 2 3 4 5 )

;Close the "do" containing the code for changing notes above the reference.

0 1 2 3 4 )

;Close the "if" testing for the value of "mode".

0 1 2 3 )

;close the "do" containing the nest of code for the loop.

0 1 2 )

;Run the loop until all notes have been scanned, then close the loop.

0 1 )

;Close the "do" containing the code for this program.

0 )

;---------------------------END OF PROGRAM--------------------------------

Demonstrations of Running COLOR.CAL

To demonstrate what this program does to the notes in a sequence, look at the following "snapshots" of the track view in Cakewalk version 6 showing note velocities before and after the CAL program is run.


1) This is a test pattern sequence of notes starting at F2 and continuing to G7 passing through C5, or Middle C. All of the notes have the same velocity of 64.


2) Below is the result of running the above program with a "percent" value of 15% per octave and keeping the reference "note" default value of -60 so that only notes below Middle C have their velocities increased.


3) Below is the original group of notes again, but after running the program using the same 15% per octave change but selecting notes above Middle C by entering positive 60 for "note".


4) Finely we see an example of entering a negative "percent" so that the selected notes drop in velocity instead of rise as was shown above. In this example, we set "percent" to 25% and ran the CAL program twice on the test sequence. Once using a positive 60 for the "note" value and again using a negative 60 for "note". This shows how you can use multiple passes of the same program to achieve sophisticated function curves.