A function returns a value and a procedure just executes commands. The name function comes from math. It is used to calculate a value based on input. A procedure is a set of command which can be executed in order.
In most programming languages, even functions can have a set of commands. Hence the difference is only in the returning a value part. While procedures can pass values in and out of them, their sole purpose is to get something done, where a function’s sole purpose is to solve a problem and return the answer.
Listed below are some of the most common functions and procedures used in configurations. This is by no means an all-inclusive list and will be updated regularly.
filter_sample_1 ( fresh-data , saved-data )
This function compares the latest reading to a saved reading from the previous pass and returns a value that “meets in the middle.” This can accomplish several tasks. This can prevent shades from “flapping” if one contact is barely making or can prevent shades from physically moving too quickly. If shade position starts at a value of 0, and the next pass is a value of 100, this will return a value of 50 the first pass after the change happens, 75 the pass after that, etc. Because the passes occur so frequently, this is a barely noticeable effect. Also available is filter_sample_2, which is a little shower to react, filter_sample_3 which is even slower, and filter_sample_4, which is quite slow.
Example use: exprb3 = filter_sample_3 ( exprb3 , saved_swell )
In the example, exprb3 is being compared to a value this is towed away in saved_swell and returning the average of the two. The saved_swell is then updated to hold the new average for next time. The stored byte can be any named byte that is not cleared between passes (see preserved data).
count_bits ( highbyte , lowbyte , #bytes )
Counts number of bits active in buffer and returns the count.
Example use: if count_bits ( Swell >> 8 , Swell , 8 ) > 20 then zerobuf ( Swell >> 8 , Swell , 8 ) end if
The example counts the number of notes on. If more than 20 notes are turned on, then all notes are cleared. While the threshold may need to be set higher, this would generally indicate a malfunction to have that many notes playing at once.
Auto_Bass ( button , src_k , dst_k , mask )
This takes the lowest note playing in the manual and plays it in the pedal division (or whatever division you select). This is normally inserted in a chamber controller right before the coupler tree. Button is any bit that dictates whether the effect is on or off. Mask is the number of the highest note that should be affected (notes above that will be ignored).
Example: Auto Bass ( stop_11 , gr_k , pd_k , 22 )
Auto_Melody ( button , src_k , dst_k , mask )
This takes the highest note playing in the manual and plays it in another division. This is normally inserted in a chamber controller right before the coupler tree. Button is any bit that dictates whether the effect is on or off. Mask is the number of the lowest note that should be affected (notes below that will be ignored).
Example: Auto Melody ( stop_12 , gr_k , so_k , 25 )
Also Available: Auto_Medody_I, Auto_Melody_II (slightly different aggressiveness with note hunting)
all_midi_off ( byte in x_chan )
Sends a MIDI “All Notes Off” message for the channel number passed into the procedure. This procedure uses 0 based numbers, so channel 1 is passed as “0” and channel 16 is passed as “15”.
Example use: all_midi_off ( 0 ) — clear MIDI channel 1
See also: midi_clear
bit_map ( byte , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 , bit7 , bit8 )
Allows a byte to be defined bit by bit (pin by pin). This has a number of common uses, including piston mapping. Pistons are normally wired to available pins for wiring convenience, not for software convenience. In pistons, order is important (especially for the first 10, which function as quick-dial pistons for generals).
Example use: bit_map ( pistonsb1 , in1_3 , in1_1 , in1_2 , in2_4 , in2_5 ,
in2_6 , in3_1 , in3_2 , in3_3 , in3_4 )
blade_set_8 ( expr-byte)
Makes an analog control (like a potentiometer or linear transducer) work like a contact roller. For every 15 counts, a bit is added. So the procedure reads the byte (for a value of 30 for example which is 0x1E) and converts it to the correct number of bits on for an 8 bit engine. This assumes calibrations are to a value of 127 and not 256. The example above would be written blade_set_8 (exprb1) and would output a value of 0×03, which is the first two bits turned on. In the chamber, it is easy to then assign a driver byte to the expression byte and simply wire the 8 blades to that byte.
card_in ( number , number )
Polls an input card and puts the found data in the buffer listed under number. This should be one of the first things to occur in a config file. This procedure is the same whether there is a single string of cards (as on a C-I controller) or multiple strings (C-II / C-III). A “0” is substituted for the card number on an unused string set. This is for cards that are positive input (jumpers in PD position).
Example use: Card_in ( 1 , 0 )
Card_in ( 2 , 0 )
Card_in ( 3 , 0 ) etc…
card_in_n ( number , number )
Polls an input card and puts the found data in the buffer listed under number. This should be one of the first things to occur in a config file. This procedure is the same whether there is a single string of cards (as on a C-I controller) or multiple strings (C-II / C-III). A “0” is substituted for the card number on an unused string set. This is for cards that are negative input (jumpers in PU position).
Example use: Card_in_n ( 1 , 0 )
card_out ( number , number )
Takes the data in the driver card buffer and actually sends it to the driver card. Driver cards send data to the last card in the chain first, so note the example clearly! This follows a dual number format like the card_in statements.
Example use: Card_out ( 3 , 0 )
Card_out ( 2 , 0 )
Card_out ( 1 , 0 ) etc…
Normally input cards are connected closest to the controller, and then output cards are plugged in after them. If an output card is inserted in the middle of the input cards, this statement will make the logic flow “work out.”
Example use: Card_in ( 1 , 0 )
Card_in ( 2 , 0 ) etc…
clear_a_tab ( button , stop# )
Typically contained within other logic, pressing the button will TOGGLE the stop number. In order to make it actually clear the tab and not activate it accidentally, use the stop_# bit as the button, then the procedure will only run when the stop is on.
Example use: if in2_29 then clear_a_tab ( stop_12 , 12 ) end if
coil_remap ( ti , bit , qty , outcrd , pin )
If stop coils were wired in wrong order, or were not wired in a standard, readily addressable order, than it becomes necessary to be able to re-route the pairs of on-off one pair at a time (or in multiples). This routine will hunt through the “final product” that the C/A software produces and will copy individual on/off pairs to destinations in output card buffers.
Example use: coil_remap ( 1 , 13 , 4 , 7 , 47 )
This example puts the driver wires for stops 13-16 on driver card 7 starting at pin 47.
control_button ( bit in , bit out )
Because the controllers scan very rapidly and because they do exactly what they are told every scan, if you would write a line that raises the memory level every time a button is pressed, it would be difficult to control. Every single pass would activate the function, so the memory level would be increased 30 levels every half second. There is internal logic that can detect a single button press and only execute one time. Additional logic will prevent a “second touch” of the control upon release.
Example use: Control_button ( In4_35 , s_mem_level_inc )
direct_midi_expression ( channel , byte-1 , byte-2 )
This procedure sends the contents of byte-1 as an MIDI expression message on the channel listed. It stores the value in byte 2 (should be a saved byte) in order to only send the message when there is a change. Channels are 1 based in this procedure. Every few seconds, the procedure will send data regardless of whether it has changed or not.
Example use: direct_midi_expression ( 1 , exprb1 , saved_solo )
format_7_seg ( byte-in, byte-out-tens , byte-out-ones )
The number fed into this procedure is processed and converted into seven-segment drive bits. Assign the out bytes to driver card bytes and connect the 7-segment displays to the driver cards. The example below uses the indic buffer to hold the digit values. This is advantageous because if they are wired out of order, the indic byte bits are already assigned the names indic_1 :: indic_16.
Example use: var byte ones_dig at indic
var byte tens_dig at indic + 1
format_7_seg ( mem_level , tens_dig , ones_dig )
out1b1 = ones_dig
out1b2 = tens_dig
Sends a MIDI “All Notes Off” message for every channel sequentially, starting at channel 16. This procedure is automatically run once during controller initialization whether MIDI ports are configured or not. This may be a handy thing to run if the attached MIDI device is not reliable or can’t keep track of excessive MIDI traffic. Opus-Two can generate MIDI traffic at an amazing rate, and that will often overrun buffers of some receiving devices (generally if MIDI is a low priority for the device it will over-run, this is known to happen on a surprising variety of equipment. This will NOT occur unless something is causing it (such as an expression that is faltering between stages – if that happens, consider a filter_sample.
bit_clr ( buffer/high , buffer/low , bit number )
Low level function, probably should not be found in a config file. Clears a specific bit that hasn’t been declared with a local name. For things with local bit declarations (such as stops), it is best to simply declare them (stop_12 = off or stop_12 = low). A procedure that may require this to be variable would call this procedure.
An example use would be: bit_clr (TI1 >>8 , TI1 , x ) where x would be the value to be cleared (such as 12).
bit_set ( buffer/high , buffer/low , bit number )
Low level procedure that sets a specific bit on. This can be used for a wide variety of features such as piston sequencers, smart couplers (auto pedal, auto melody), and even functions like tuning assistants.
An example use would be: bit_set (Swell >>8 , Swell , x ) where x would be the value to be set (passing a value of “25” would be like holding down middle C).
bit_tgl ( buffer/high , buffer/low , bit number )
Toggles the state of a specific bit. This is most commonly used for features like reversible controls.
An example use would be: bit_tgl (TI1 >>8 , TI1 , x ) where x would be the value to be toggled (passing a value of “25” would turn stop 25 on if it was off and would turn it off if it was already on).
merge_buf ( src-buf/high , src-buf/low, dest-buf/high, dest-buf/low, # bytes )
Take the contents of one buffer and merges it with another. Extremely useful because it does not overwrite the destination (think of it like a logical OR on a bit by bit basis). Couplers use this procedure to merge source and destination keying. Floating divisions will typically use merge_buf so that multiple floating assignments don’t overwrite one another. # of bytes is how many bytes to execute for (8 = 64 bits which covers a full keyboard).
An example use would be: merge_buf ( Swell >> 8 , Swell , Solo >> 8 , Solo , 8 ) where the contents of the Swell keying buffers would be merged with whatever is already in the solo buffers.
move_buf ( src-buf/high , src-buf/low , dest-buf/high , dest-buf/low , # bytes )
Takes the contents of the source buffer and copies them into the destination buffer, replacing and overwriting what is already there. Otherwise identical to merge_buf.
midi_ch_coder_acorn ( buffer , channel , first-note , enable-bit )
Take an entire 64 bit buffer and send its contents as MIDI notes. Most commonly used to send stop on/off messages as notes (easy to keep track of, really easy to see in editing software, compatible with almost anything). Be aware, channel is 0 based, so its best to do the -1 math right in the procedure call so you can keep track of it later (see example for channel 16). Most keying buffers will start at note 36 (Low C).
Example: midi_ch_coder_acorn ( TI1 , ( 16 – 1 ) , 36 , high ) — 64 on ch 11
See Also: Midi Note Offsets, Midi Formatting
reverse_rank ( buf/high , buf/low , bytes , offset, wb )
This procedure completely reverses a rank when it has been wired backward. The input card would be mapped to its correct location and then this procedure is executed and modifies that location. Number of bytes is how much data gets reversed (almost always going to be 8), and the offset corrects for the fact that the top 3 bits of the 64 bit buffer are empty, and they shouldn’t wind up in the locations of the first three notes.
Example: reverse_rank ( Swell >> 8 , Swell , 8 , – 3 , wb_8 )
stp_cplr ( name , dest , source , pitch )
The stp_cplr does just what the name implies, but has a few “under the hood” things that it invokes. When the first stp_cplr is run, the controller:
- Sends the following collected and mapped data to chamber controllers:
- Accompany Keying
- Great Keying
- Choir Keying
- Solo Keying
- Pedal 1st & Pedal 2nd Touch
- Stops 1-192
- Expression bytes 1-8 (64 bits regardless of application)
- Great 2nd Touch Keying
- Bombard Keying
- Erases all the information collected from the input cards (this is why we map it or use it in the first half of the file).
- Initializes and runs the combination action if necessary (if a piston, reversible, or cancel was pressed).
The name is the name of the bit that activates the coupler, which could be stop_33 type name or S_G_8 if declared.
Think of a coupler like a copy-paste. Destination is where keying gets pasted. Source is where it is copied from. So a swell to pedal 8’ is actually copying pedal keying to the swell manual at 8’ (unison). They are listed in the config file with the destination first and source second so they will read the same way as the engraved tabs ( sw_k , pd_k, p_4 is swell to pedal 4’ )
Example: stp_cplr ( stop_11 , sw_k , pd_k , p_8 ) – Swell to Pedal 8’
Midi_PC_Code_from_TI ( ch , TIx , numstops )
This is normally called fairly close to the end of the file and will send program change codes representing the stops in the TI buffer. Because there are 128 PC codes in each channel available, and there are two used for each stop (1 for on, a second for off), no more than 64 stops can be controlled per channel. Because TI blocks are sized at 64 stops, it is normally as simple as matching each used TI block to a channel.
zero_buf ( high , low , bytes)
This clears the contents of the buffer.
Example: zero_buf ( Swell >> 8 , Swell , 8 ) — clear all swell notes