' ********************************************************************
' *                  PIC firmware for the <Cemba> robot              *
' *                      pulse and hold coding                       *
' *                  coded by dr.Godfried-Willem Raes                *
' * http://www.logosfoundation.org/instrum_gwr/cemba/picworks        *
' *                  source code development directory:              *
' *                            cemba.bas                             *
' ********************************************************************
' 23.01.2016: first prototype PCB coded. These boards serve 14 notes each.
' 26.04.2018: Starting from the player piano code with note repeats,
'             coding for <Pos>, using the 18F4620 processor
' 27.04.2018: Tektronix measurements:
'             repeat frequencies:
'                        127 = 30 ms, so repeat frequency is 16 Hz (16 notes a second)
' 09.12.2019: Upgrade start to better timer0 implementation.
'             Velo1 = 660us
'             Velo64 = 26.5ms
'             velo127 = 52.2ms   glitchfree now!
' 21.02.2021: Board 2 replaced, as we had a persistent fault on note 55.
'             Did we upgrade the other PIC's in 2019?   After the diary for <pos> we did.(09.12.2019)
' 2022: same boards also used for <RorO>
'----------------------------------------------------------------------------------------------------
' 05.05.2024: Starting code for <Cemba>
'             4 PCB's good for 56 notes.
' 16.06.2024: midi channel changed to 13, the hub board translates whatever channel to this one.
'             First series of 4 PICs programmed.
' 17.06.2024: First tests on the instrument.
'             note repetitions appear not very reliable.
'             velo's need some scaling.
' 18.06.2024: musical tests with some midi files.
' 19.06.2024: 4 different velo scalings implemented, a low string demands somewhat higher velo values.
'             measurements for board 1:
'             velo 1 = 9 ms
'             velo 20 = 12.5 ms
'             velo 64 = 32 ms
'             velo 127 = 50 ms
'             We could also differentiate for back keys versus white ones, as the forces involved are different.
'             Chromatic notes for board1 : 37, 39, 42, 44, 46
'                             for board2 : 49, 51, 54, 56, 58, 61
'                             for board3 : 63, 66, 68, 70, 73, 75
'                             for board4 : 78, 80, 82, 85, 87
' 20.06.2024: Looks like automated note repeats are pretty useless here..
'             also, we can improve the code by switching the hold on, on reception of note-on's
' 27.06.2024: note repetitions ommitted from code. To enable, just define reps.
'             velocities rescaled
'             lite on  note 120 implemented for board 4
'             Hub firmware upgraded as well
' 28.06.2024: Two different lookup tables implemented: for 1 register and for 2 registers.
'             the controller for this selection must come from the hub board.
'             Controller 67 is implemented here to do just this.
' 01.07.2024: prog.change and reg.1, 2 etc. improved. Half bubble sort applied.
'             now 2 regs use the sum of the individual reg. velo's
'             Controller 67 now takes the values 0,1,2,3.
' 02.07.2024: bubble sorts added and code improved for speed.
' 03.07.2024: Cleanup. This be version 1.0
' 02.09.2024: client Marc Sinan complaints: shaky behavior observed...
'             measurement and evaluation session under heavy midi loads required.
'             $reps code removed, although that only contributes to readability.
'             velo-hold tests passed. Problem must be somewhere else...
' 11.09.2024: Source code prepared for passing over to Kristof Lauwers.

 Include "18F4620.inc"
' Mapping defines for midi-events on pin outputs and inputs:
    ' for cemba it ought to  be:
    $define Hol13      PORTA.2    ' output 1 op het PCB!    ' 48    ok     pin4
    $define Vel13      PORTA.1       '       ok     pin3
    $define Hol12      PORTA.0       ' 47    ok     pin2
    $define Vel12      PORTA.3       '       ok     pin5

    $define Hol11      PORTA.4       ' 46
    $define Vel11      PORTA.5
    $define Hol10      PORTE.0       ' 45
    $define Vel10      PORTE.1

    $define Hol9      PORTE.2       ' 44
    $define Vel9      PORTB.4
    $define Hol8      PORTB.3       ' 43
    $define Vel8      PORTB.2

    $define Hol7      PORTB.1       ' 42
    $define Vel7      PORTB.0
    $define Hol6      PORTD.7       ' 41
    $define Vel6      PORTD.6

    $define Hol5      PORTD.5       '40
    $define Vel5      PORTD.4
    $define Hol4      PORTC.5       '39
    $define Vel4      PORTC.4

    $define Hol3      PORTD.3       '38
    $define Vel3      PORTD.2
    $define Hol2      PORTC.0       '37
    $define Vel2      PORTC.1

    $define Hol1      PORTC.2       '36
    $define Vel1      PORTC.3
    $define Hol0      PORTD.0       '35
    $define Vel0      PORTD.1
    'red LED for debug:
    $define Debug_Led   PORTB.5   ' for testing - red led - watchdog - pin 38

DECLARE All_Digital = True        ' makes all analog pins, digital for I/O
CLEAR SSPCON1.5                   ' make sure RC3 is free for use.
' configure the input and output pins:
TRISA = %11000000     'bits set to 0 are output, 1 = input  - bits 6 and 7 are the clock!
TRISB = %11100000     'bits 6 and 7 are for the ICP, bit 5 is the red LED
TRISC = %11000000     'RC6 en RC7 zijn USART  I/O and must be set to input
TRISD = %00000000     'all bits can be used
TRISE = %11101000     'low nibble only, bit3 (RE3) is MCLR
                      'bit 4 most be zero to disable PSP mode on port D  - gwr 20.01.2012
                      'this was the bug we had!

$define board1       ' change only this for each board compilation
                     ' board 1 is for the lowest 14 notes, starting from note 35.

$ifdef board1
'constant definitions:
Symbol noot0 = 35     ' board 1
Symbol noot1 = 36
Symbol noot2 = 37
Symbol noot3 = 38
Symbol noot4 = 39
Symbol noot5 = 40
Symbol noot6 = 41
Symbol noot7 = 42
Symbol noot8 = 43
Symbol noot9 = 44
Symbol noot10 = 45
Symbol noot11 = 46
Symbol noot12 = 47
Symbol noot13 = 48
$ENDIF
$ifdef board2
Symbol noot0 = 49     ' board 2
Symbol noot1 = 50
Symbol noot2 = 51
Symbol noot3 = 52
Symbol noot4 = 53
Symbol noot5 = 54
Symbol noot6 = 55
Symbol noot7 = 56
Symbol noot8 = 57
Symbol noot9 = 58
Symbol noot10 = 59
Symbol noot11 = 60
Symbol noot12 = 61
Symbol noot13 = 62
$ENDIF
$ifdef board3
Symbol noot0 = 63     ' board 3
Symbol noot1 = 64
Symbol noot2 = 65
Symbol noot3 = 66
Symbol noot4 = 67
Symbol noot5 = 68
Symbol noot6 = 69
Symbol noot7 = 70
Symbol noot8 = 71
Symbol noot9 = 72
Symbol noot10 = 73
Symbol noot11 = 74
Symbol noot12 = 75
Symbol noot13 = 76
$ENDIF
$ifdef board4
Symbol noot0 = 77     ' board 4
Symbol noot1 = 78
Symbol noot2 = 79
Symbol noot3 = 80
Symbol noot4 = 81
Symbol noot5 = 82
Symbol noot6 = 83
Symbol noot7 = 84
Symbol noot8 = 85
Symbol noot9 = 86
Symbol noot10 = 87
Symbol noot11 = 88     ' highest note, E7
Symbol noot12 = 120    ' E10 lite
Symbol noot13 = 121    ' not yet mounted
$ENDIF


'initialisations for the midi input parser:
Symbol Midichannel = 13                        ' Cemba_Channel internal
Symbol NoteOff_Status = 128 + Midichannel      ' 2 bytes follow
Symbol NoteOn_Status  = 144 + Midichannel
Symbol Keypres_Status = 160 + Midichannel
Symbol Control_Status = 176 + Midichannel
Symbol ProgChange_Status = 192 + Midichannel   ' 1 byte message
Symbol Aftertouch_Status = 208 + Midichannel   ' 1 byte follows
Symbol Pitchbend_Status  = 224 + Midichannel   ' lsb msb follow
Symbol NrTasks = 14
Symbol LastTask = 13
Symbol MinVel = 180

' Setup the USART
DECLARE Hserial_Baud = 31250                   ' Set baud rate for the USART to MIDI specs.
DECLARE Hserial_TXSTA = 0x24                   ' instead of the normal 0x20 - ?? 0x24
' Declare Hserial_Clear = On                   ' should clear on errors. Bytes get lost of course...
' Create variables:
    DIM Cnt AS DWORD System                       '32 bit counter
    DIM CntHw AS Cnt.Word1 ' Word System          'used in the timer0 interrupt, where it is incremented
    DIM CntLw AS TMR0L.Word                       'this is the trick to read both TMR0L and TMR0H
                                                  'it makes Cntlw the low word of cnt, when we use cnt.word0=CntLw
    DIM time AS Cnt                               'alias
'    Dim Tim1 As TMR1L.Word                        'not used here
'    Dim Tim2 As TMR2                              'not used here
'    Dim Cnt3 As Dword System
'    Dim Cnt3Hw As Cnt3.Word1
'    Dim Tim3 As TMR3L.Word                        ' same trick for timer3
'    Dim Sr as TMR0L.7                             '512 S/s   - this works but these DO NOT WORK!!!:
                                                  ' Dim Sr as CntLw.Byte1   does not
                                                  ' Dim Sr As TMR0H.0  'sampling rate bit, 256 S/s
                                                  ' DIM Sr as CntLw.8
                                                  ' As TMR0H.1  would be 128 S/s
                                                  ' As TMR0H.2  would be 64 S/s
                                                  ' As TMR0H.3  would be 32 S/s
                                                  ' As TMR0H.4  would be 16 S/s
    DIM Bytein  AS BYTE System                    ' midi byte read from buffer
    DIM StBit AS Bytein.7                         ' highest bit of ByteIn
    DIM i AS BYTE System                          ' general purpose counter
    ' midi variables
    DIM statusbyte AS BYTE System
    DIM noteUit AS BYTE System                    ' note off + release value
    DIM release AS BYTE System
    DIM noteAan AS BYTE System                    ' note on + release value
    DIM velo AS BYTE System
    DIM notePres AS BYTE                          ' note pressure + pressure value
    DIM pres AS BYTE
    DIM Ctrl AS BYTE System                       ' continuous controller + value
    DIM value AS BYTE System
    DIM prog AS BYTE System                       ' program change + program-byte
'    Dim aft  As Byte System                       ' channel aftertouch
'    Dim pblsb As Byte System                      ' pitch bend lsb
'    Dim pbmsb As Byte System                      ' pitch bend msb

    DIM CC69 AS BYTE                             ' 0= light automation off, 1 = automation ON
    DIM CC67 AS BYTE                            ' for controller 67, force correction in function of registers drawn
                                                ' possible values are only 0, 1,2,3.
 '   Dim CC66 As  Byte System                     ' global on/off switch
 '   Dim PowerOn As CC66.0                        ' handled on the hub board.

    DIM velo0 AS WORD
    DIM velo1 AS WORD
    DIM velo2 AS WORD
    DIM velo3 AS WORD
    DIM velo4 AS WORD
    DIM velo5 AS WORD
    DIM velo6 AS WORD
    DIM velo7 AS WORD
    DIM velo8 AS WORD
    DIM velo9 AS WORD
    DIM velo10 AS WORD
    DIM velo11 AS WORD
    DIM velo12 AS WORD
    DIM velo13 AS WORD

    DIM maxtim AS time.31                 ' overflow bit, will cause timer reset after 1h45
    DIM TimVals[NrTasks] AS DWORD         ' lijst met timer waarden - de kleinste is eerst aan de beurt
    DIM Nxt AS DWORD System               ' waarde voor de eerstvolgende timer
    DIM idx AS BYTE System                ' index voor de eerstvolgende timer
    DIM Ringbuffer[256] AS BYTE
    DIM Resort AS BYTE
    DIM Resort_flag AS Resort.0           ' flag to signal the requirement to resort timers
'-----------------------------------------------------------------------------------------
' Load the USART Interrupt handler and buffer read subroutines into memory
Include "cemba_Irq.inc"                 ' for UART,Timer0, Timer3 Interrupt
'Clear                           ' clear all RAM
'-----------------------------------------------------------------------------------------
DIM vels[128] AS WORD                    ' velocity lookup table in use
DIM velm[128] AS WORD                    ' for reg2.
DIM velf[128] AS WORD                    ' idem. for use with 2 registers drawn
DIM Trajekt AS WORD
DIM Stepsize AS Float

' Main program starts here
MAIN:
    Low Debug_Led
    DelayMS 10                       ' wait for stability
    Low Vel0
    Low Vel1
    Low Vel2
    Low Vel3
    Low Vel4
    Low Vel5
    Low Vel6
    Low Vel7
    Low Vel8
    Low Vel9
    Low Vel10
    Low Vel11
    Low Vel12
    Low Vel13
    Low Hol0
    Low Hol1
    Low Hol2
    Low Hol3
    Low Hol4
    Low Hol5
    Low Hol6
    Low Hol7
    Low Hol8
    Low Hol9
    Low Hol10
    Low Hol11
    Low Hol12
    Low Hol13
    SET TimVals

    CLEAR velo0
    CLEAR velo1
    CLEAR velo2
    CLEAR velo3
    CLEAR velo4
    CLEAR velo5
    CLEAR velo6
    CLEAR velo7
    CLEAR velo8
    CLEAR velo9
    CLEAR velo10
    CLEAR velo11
    CLEAR velo12
    CLEAR velo13

    Init_Usart_Interrupt             ' Initiate the USART serial buffer interrupt
                                     ' this procedure is in the include file
    Clear_Serial_Buffer              ' Clear the serial buffer and reset its pointers
                                     ' in the include as well
                                     ' Configure Timer0 for:
'                                      Clear TMR0L and TMR0H registers
'                                      Interrupt on Timer0 overflow
'                                      16-bit operation
'                                      Internal clock source  40MHz
'                                      1:256 Prescaler : thus 40MHz / 256 = 156.250kHz
'                                      6.4 us per clock-tick
'        Opentimer0 (Timer_INT_On & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256)
'        replacing above macro with in-line coding:
         CLEAR T1CON
         CLEAR IntConBits_T0IF      ' clear interrupt flag
         SET INTCONBITS_T0IE        ' enable interrupt on overflow
         T0CON = %10000111           ' bit 7 = enable/disable
                                     ' bit 6 = 1=8 bot, 0=16 bit
                                     ' bit 5 = 1 pin input, 0= Internal Clk0
                                     ' bit 4 = HL or LH transition when bit5 =1
                                     ' bit 3 = 1= bypass prescaler, 0= input from prescaler
                                     ' bit 2-0 = prescaler select: 111= 1:256
                                     ' Setup the High priorities for the interrupts
' TIMER1: if enabled, all midi-in is blocked, so it must interfere with the UART
             ' Configure Timer1 for:
'                        Clear TMR1L and TMR1H registers
'                        Interrupt on Timer1 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'
'    OpenTimer1(TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_8)    ' dit kompileert o.k.

' TIMER2: if enabled, the UART stops working...
'    Opentimer2 (Timer_Int_On & T2_POST_1_16 & T2_PS_1_16)     ' dit lukt... maar de timer is nodig voor de UART...

' TIMER3:
' Configure Timer3 for:
'                        Interrupt on Timer3 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'                        Don’t sync external clock input
'    T3_OSC1En_On ()     ' macro
'   OpenTimer3(TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8 & T3_SYNC_EXT_OFF) ' fout, but == voorbeeld???
'   Opentimer3 (Timer_Int_On & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8 & T3_SYNC_EXT_OFF & T3_SOURCE_CCP)  ' fout
'    OpenTimer3 (Timer_INT_ON & T3_16BIT_RW  & T3_PS_1_8 & T3_SYNC_EXT_OFF)                                ' fout
'    OpenTimer3 (0xFFFF & Timer_INT_On & T3_16BIT_RW)
'    OpenTimer3(T3_8BIT_RW & T3_SOURCE_EXT & T3_PS_1_1 & T3_SYNC_EXT_OFF)  ' copied from manual, fout!!!
 '   OpenTimer3(TIMER_INT_OFF & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8)
 ' doing it this way seems to work:
    CLEAR T3CON
    CLEAR PIR2BITS_TMR3IF ' clear IRQ flag
    SET PIE2BITS_TMR3IE   ' irq on
    'T3_OSC1En_On ()       ' macro  - sets T3CON
'    Clear Tim3            ' Clear TMR3L And TMR3H registers
    SET RCONbits_IPEN      ' Enable priority interrupts
    CLEAR IPR2bits_TMR3IP   ' Set Timer3 as a low priority interrupt source
    ' we can also set T3Con in one instruction as:
    T3CON = %10110001               ' oef, now it works...
                            ' bit 7 = 16 bit mode
                            ' bit 6,3 = 0, 0
                            ' bit 5,4 = 1:8 prescale
                            ' bit 2 = 0
                            ' bit 1 = 0 Internal clock = Fosc/4
                            ' bit 0 : 1= enable timer 3, 0= disable
                            ' maximum count = 52.42ms, 1 tick =0.8uS, freq.=19Hz
    ' Set up priority interrupts.
  '  IPR1bits_TMR1IP = 0    ' Set Timer1 as a low priority interrupt source
  '  INTCONbits_PEIE = 1    ' Enable peripheral interrupts
  '  INTCONbits_GIE = 1     ' Enable global interrupts
                            ' read the default lookup tables in Vels[] and Velf[]
Velo_Lookup ()

HRSOut Control_Status, 120, 64           ' adding this dummy made the whole program work...
                              ' we have no explanation for this behavior.

                              ' start the main program loop:
DO                            ' Create an infinite loop
        Cnt.Word0 = CntLw            ' read timer 0
        Bytein = GetMidiIn ()
                                     ' Start the midi parser.
        Midi_Parse:
            IF Bytein > Control_Status THEN    ' here higher statusses are not implemented.
                IF Bytein > 253 THEN       '254 = midiclock, 255= reset
                                           'midiclock can interrupt all other msg's...
                                           '255 had to be intercepted since thats what we
                                           'get when no new byte flows in.
                ELSE
                    CLEAR statusbyte       'reset the status byte
                END IF
                GOTO Check_Timers          'throw away...
            EndIf
            IF StBit =1 THEN               'should be faster than If Bytein > 127 Then
                                           'status byte received, bit 7 is set
                CLEAR statusbyte           'if on another channel, the statusbyte needs a reset
                SELECT Bytein              'eqv to Select case ByteIn
                       CASE NoteOff_Status
                            statusbyte = Bytein
                            SET noteUit          'reset value. Cannot be 0 !!!
                            SET release          '0 is a valid midi note!
                       CASE NoteOn_Status
                            statusbyte = Bytein
                            SET noteAan '= 255
                            SET velo '= 255
                       CASE Keypres_Status          'not used here
                            statusbyte = Bytein
                            SET notePres
                            SET pres
                       CASE Control_Status          ' controllers and switches
                            statusbyte = Bytein
                            SET Ctrl
                            SET value
'                       Case ProgChange_Status       ' used for different lookup tables
'                            statusbyte = Bytein
'                            prog = 255
'                       Case Aftertouch_Status
'                            statusbyte = Bytein
'                            Set aft
'                       Case Pitchbend_Status        ' not on this board.
'                            statusbyte = Bytein
'                            pblsb = 255
'                            pbmsb = 255
                END SELECT
            ELSE                                        'midi byte is 7 bits
                SELECT statusbyte
                       CASE 0                           'not a message for this channel
                            GOTO Check_Timers           'disregard
                       CASE NoteOff_Status
                            IF noteUit = 255 THEN
                                noteUit = Bytein
                            ELSE
                                release = Bytein        'message complete, so we can do the action...
                                IF noteUit < noot7 THEN       ' first half of the bubble sort...
                                SELECT noteUit
                                       CASE noot0
                                            SET TimVals[0]
                                            CLEAR Vel0
                                            CLEAR Hol0
                                       CASE noot1
                                            SET TimVals[1]
                                            CLEAR Vel1
                                            CLEAR Hol1
                                       CASE noot2
                                            SET TimVals[2]
                                            CLEAR Vel2
                                            CLEAR Hol2
                                       CASE noot3
                                            SET TimVals[3]
                                            CLEAR Vel3
                                            CLEAR Hol3
                                       CASE noot4
                                            SET TimVals[4]
                                            CLEAR Vel4
                                            CLEAR Hol4
                                       CASE noot5
                                            SET TimVals[5]
                                            CLEAR Vel5
                                            CLEAR Hol5
                                       CASE noot6
                                            SET TimVals[6]
                                            CLEAR Vel6
                                            CLEAR Hol6
                                EndSelect
                                ELSE         ' second half of the bubble sort
                                SELECT noteUit
                                       CASE noot7
                                            SET TimVals[7]
                                            CLEAR Vel7
                                            CLEAR Hol7
                                       CASE noot8
                                            SET TimVals[8]
                                            CLEAR Vel8
                                            CLEAR Hol8
                                       CASE noot9
                                            SET TimVals[9]
                                            CLEAR Vel9
                                            CLEAR Hol9
                                       CASE noot10
                                            SET TimVals[10]
                                            CLEAR Vel10
                                            CLEAR Hol10
                                       CASE noot11
                                            SET TimVals[11]
                                            CLEAR Vel11
                                            CLEAR Hol11
                                       $ifndef board4
                                       CASE noot12
                                            SET TimVals[12]
                                            CLEAR Vel12
                                            CLEAR Hol12
                                       CASE noot13
                                            SET TimVals[13]
                                            CLEAR Vel13
                                            CLEAR Hol13
                                       $ELSE
                                            ' for board4 only
                                       CASE noot12        ' this note has as red LED light!
                                            CLEAR Hol12
                                            CLEAR Vel12
                                            SET TimVals[12]
                                       CASE noot13
                                            CLEAR Hol13   ' nc
                                       $ENDIF
                                EndSelect
                                EndIf
                                SET noteUit         ' reset
                                SET Resort_flag         ' mandatory, as we changed timers!
                            EndIf
                       CASE NoteOn_Status
                            IF noteAan = 255 THEN
                                noteAan = Bytein
                            ELSE
                                velo = Bytein
'                                If velo = 0 Then                 ' as we can rely on the parser to X-late these commands
'                                                                 ' to real noteoff's, we can leave out this entire if construction
'                                Else
                                    IF noteAan < noot7 THEN
                                    SELECT noteAan
                                           CASE noot0
                                                ' in principle it could be that Hol0 is still on.
                                                ' this is not a problem for the hardware however.
                                                ' Obviously, in this case the note will not be plucked and the user
                                                ' is requesting faster note repetitions than what the instrument allows.
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[0] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[0] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[0] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[0] = time + MinVel ' = vels[1] = 180  voor stil indrukken toetsen...
                                                EndSelect
                                                SET Vel0                 ' set the velo bit high
                                                SET Hol0
                                           CASE noot1
                                               SELECT CC67
                                                    CASE 1
                                                        TimVals[1] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[1] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[1] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[1] = time + MinVel
                                                EndSelect
                                                SET Vel1
                                                SET Hol1
                                           CASE noot2
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[2] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[2] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[2] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[2] = time + MinVel
                                                EndSelect
                                                SET Vel2
                                                SET Hol2
                                           CASE noot3
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[3] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[3] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[3] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[3] = time + MinVel
                                                EndSelect
                                                SET Vel3
                                                SET Hol3
                                           CASE noot4
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[4] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[4] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[4] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[4] = time + MinVel
                                                EndSelect
                                                SET Vel4
                                                SET Hol4
                                           CASE noot5
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[5] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[5] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[5] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[5] = time + MinVel
                                                EndSelect
                                                SET Vel5
                                                SET Hol5
                                           CASE noot6
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[6] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[6] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[6] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[6] = time + MinVel
                                                EndSelect
                                                SET Vel6
                                                SET Hol6
                                    EndSelect
                                    ELSE
                                    SELECT noteAan
                                           CASE noot7
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[7] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[7] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[7] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[7] = time + MinVel
                                                EndSelect
                                                SET Vel7
                                                SET Hol7
                                           CASE noot8
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[8] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[8] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[8] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[8] = time + MinVel
                                                EndSelect
                                                SET Vel8
                                                SET Hol8
                                           CASE noot9
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[9] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[9] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[9] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[9] = time + MinVel
                                                EndSelect
                                                SET Vel9
                                                SET Hol9
                                           CASE noot10                    ' only for boards 1 to 4
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[10] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[10] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[10] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[10] = time + MinVel
                                                EndSelect
                                                SET Vel10
                                                SET Hol10
                                           CASE noot11
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[11] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[11] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[11] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[11] = time + MinVel
                                                EndSelect
                                                SET Vel11
                                                SET Hol11
                                           $ifndef board4
                                           CASE noot12
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[12] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[12] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[12] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[12] = time + MinVel
                                                EndSelect
                                                SET Vel12
                                                SET Hol12
                                           CASE noot13
                                                SELECT CC67
                                                    CASE 1
                                                        TimVals[13] = time + vels[velo]
                                                    CASE 2
                                                        TimVals[13] = time + velm[velo]
                                                    CASE 3
                                                        TimVals[13] = time + velf[velo]
                                                    CASE ELSE
                                                        TimVals[13] = time + MinVel
                                                EndSelect
                                                SET Vel13
                                                SET Hol13
                                           $ELSE
                                           CASE noot12
                                              '  If CC69 = 0 Then     ' manual mode, no automation
                                                    velo12 = vels[127]       ' read lookup
                                                    TimVals[12] = time + velo12
                                                    SET Vel12
                                                    SET Hol12
                                              '  EndIf
                                           CASE noot13
                                              '  If CC69 = 0 Then
                                                    SET Hol13       ' not yet mounted
                                              '  EndIf
                                           $ENDIF
                                           CASE ELSE
                                                SET noteAan
                                                GOTO Check_Timers
                                    EndSelect
                                    EndIf
                                    SET noteAan                'reset !!!
                                    SET Resort_flag
                                'EndIf
                            EndIf
'                       $ifdef reps
'                       Case Keypres_Status           'used for note repetitions
'                            If notePres = 255 Then
'                                notePres = Bytein
'                            Else
'                                pres = Bytein
'                                KeyPres ()
'                            EndIf
'                       $endif
                       CASE Control_Status    'this is where the action takes place for controllers
                            IF Ctrl = 255 THEN
                                Ctrl = Bytein
                            ELSE
                                value = Bytein
                                Controller ()
                            EndIf
'                       Case ProgChange_Status                 ' could be used to select alternative lookups
'                            If prog = 255 Then                'single byte message
'                                 prog = Bytein                'weak coding...
'                                 ProgChange ()
'                            EndIf
'                       Case Else
'                            'GoTo Check_Timers                ' no sorting
                END SELECT
            EndIf

IF Resort_flag = 1 THEN
    idx = SortTimers ()      ' so we resort only if an incoming midi command changed something
EndIf

Check_Timers:
IF idx < NrTasks THEN           ' we moeten alleen checken wanneer er een timer loopt
   IF time >= Nxt THEN         ' nagaan of de eerstvolgende timer afgelopen is...
        ' in dit geval is de eerste timer afgelopen en moeten we de juiste aktie ondernemen:
        SET Nxt.31                                    ' timer reset, is immers afgelopen
        ' aan de hand van idx weten we welke timer dit is
        ' bubble-sort approach, ought to  be faster than a single select structure.
        IF idx < 4 THEN
            SELECT idx
               CASE 0                             ' noot0
                       ' de velo-tijd is verstreken en de spoel moet nu
                       ' op haar houdspanning worden geschakeld.
                       CLEAR Vel0                  ' cut velo-voltage
                       'Set Hol0                    ' switch hold voltage on
                       SET TimVals[0]              ' set to infinite= disable
                       ' de houdspanning wordt uitgeschakeld bij ontvangst van een note-off commando
               CASE 1
                       CLEAR Vel1                  ' cut velo-voltage
                       SET TimVals[1]              ' set to infinite= disable
               CASE 2                             ' noot2
                       CLEAR Vel2                  ' cut velo-voltage
                       SET TimVals[2]              ' set to infinite= disable
               CASE 3                             ' noot3
                       CLEAR Vel3                  ' cut velo-voltage
                       SET TimVals[3]              ' set to infinite= disable
            EndSelect
        ELSEIF idx < 8 THEN
            SELECT idx
               CASE 4                             ' noot4
                       CLEAR Vel4                  ' cut velo-voltage
                       SET TimVals[4]              ' set to infinite= disable
               CASE 5                             ' noot5
                       CLEAR Vel5                  ' cut velo-voltage
                       SET TimVals[5]              ' set to infinite= disable
               CASE 6                             ' noot6
                       CLEAR Vel6                  ' cut velo-voltage
                       SET TimVals[6]              ' set to infinite= disable
               CASE 7                             ' noot7
                       CLEAR Vel7                  ' cut velo-voltage
                       SET TimVals[7]              ' set to infinite= disable
            EndSelect
        ELSEIF idx < 12 THEN
            SELECT idx
               CASE 8                             ' noot8
                       CLEAR Vel8                  ' cut velo-voltage
                       SET TimVals[8]              ' set to infinite= disable
               CASE 9                             ' noot9
                       CLEAR Vel9                  ' cut velo-voltage
                       SET TimVals[9]              ' set to infinite= disable
               CASE 10                             ' noot10
                       CLEAR Vel10                  ' cut velo-voltage
                       SET TimVals[10]              ' set to infinite= disable
               CASE 11                             ' noot11
                       CLEAR Vel11                  ' cut velo-voltage
                       SET TimVals[11]              ' set to infinite= disable
            EndSelect
        ELSE
            SELECT idx
             '   $ifndef board4
               CASE 12                             ' noot12  - lite
                       CLEAR Vel12                  ' cut velo-voltage
                       SET TimVals[12]              ' set to infinite= disable
                $ifndef board4
               CASE 13                             ' noot13
                       CLEAR Vel13                  ' cut velo-voltage
                       SET TimVals[13]              ' set to infinite= disable
               $ENDIF
               'Case Else
               '     ' in dit geval is idx geset
               '     GoTo jumpout
            EndSelect
        EndIf
        SET Resort_flag
  EndIf
      ' beveiliging tegen overflow crashes...
      IF maxtim = 1 THEN
                    CLEAR time
                    SET TimVals
      EndIf
  ELSE
      ' idx > 13, no timers running, so to avoid overflows, we can reset the loop timer
      IF maxtim = 1 THEN CLEAR time     ' 16.06.2015
EndIf
     'Btg PORTB.5                       ' average loop-speed: (Tektronix measurement)
                                       ' 5.5 us under no load  up to 10us jittering under full load
LOOP

Proc SortTimers (), BYTE
    'look up the next smallest timer value in the Timvals array
    ' zoek de de volgende kleinste timer waarde:
    SET Result
    SET Nxt.31
    CLEAR i
    DO
        IF TimVals[i] < Nxt THEN
            Nxt = TimVals[i]
            Result = i
        EndIf
        Inc i
    LOOP UNTIL i > LastTask
    CLEAR Resort_flag
EndProc



Proc ProgChange()
    SET prog
EndProc

Proc Pitchbend ()
    SET pblsb
EndProc

Proc Aftertouch ()
    SET aft                             ' reset
EndProc

Proc Controller ()
    SELECT Ctrl
           CASE 66
                'on/off for the robot
                IF value = 0 THEN
                    PowerDown ()
                ELSE
                    AllNotesOff ()
                EndIf
                ' following handled on the hub board...
'           Case 69                 ' automated lights on/off
'                If value = 0 Then
'                    Clear CC69
'                Else
'                    Set CC69
'                EndIf
           CASE 67
                 ' now we have values 0,1,2,3
                ' sent by the hub board, signaling the registers in use
                ' if value = 1 or 2, only one register is in use
                ' if value = 3, two registers are used and we have to increase
                ' the velo-values.Therefore we have three velo-loopup tables: Vels[], velm[] and Velf[]
                CC67 = value & %00000011      ' 0-3
                                              ' 0 = no registers, so no-velo mode for ppp effect
                                              ' 1 = register 1
                                              ' 2 = register 2, needs 10% more force
                                              ' 3 = both registers together
           CASE 123
                AllNotesOff ()
    END SELECT
    SET Ctrl                                  'mandatory reset
EndProc


Proc PowerDown()
     ' must perform a controller reset!!!
               SET TimVals
               CLEAR Vel0
               CLEAR Vel1
               CLEAR Vel2
               CLEAR Vel3
               CLEAR Vel4
               CLEAR Vel5
               CLEAR Vel6
               CLEAR Vel7
               CLEAR Vel8
               CLEAR Vel9
               CLEAR Vel10
               CLEAR Vel11
               CLEAR Vel12
               CLEAR Vel13
               CLEAR Hol0
               CLEAR Hol1
               CLEAR Hol2
               CLEAR Hol3
               CLEAR Hol4
               CLEAR Hol5
               CLEAR Hol6
               CLEAR Hol7
               CLEAR Hol8
               CLEAR Hol9
               CLEAR Hol10
               CLEAR Hol11
               CLEAR Hol12
               CLEAR Hol13
               SET Resort_flag
EndProc

Proc AllNotesOff ()
                SET TimVals
                CLEAR Vel0
                CLEAR Vel1
                CLEAR Vel2
                CLEAR Vel3
                CLEAR Vel4
                CLEAR Vel5
                CLEAR Vel6
                CLEAR Vel7
                CLEAR Vel8
                CLEAR Vel9
                CLEAR Vel10
                CLEAR Vel11
                CLEAR Vel12
                CLEAR Vel13
                CLEAR Hol0
                CLEAR Hol1
                CLEAR Hol2
                CLEAR Hol3
                CLEAR Hol4
                CLEAR Hol5
                CLEAR Hol6
                CLEAR Hol7
                CLEAR Hol8
                CLEAR Hol9
                CLEAR Hol10
                CLEAR Hol11
                CLEAR Hol12
                CLEAR Hol13
                SET Resort_flag
EndProc

Proc Velo_Lookup ()
' met bovenstaande lookup, was velo 20 de kleinste zinnige waarde. Dit was ppp, waarbij slechts de dempertjes worden opgelicht.
' Bleek dat de lage tonen hogere velo waarden nodig hebben dan de hoge.
' bij velo  64 spelen alle noten. De teoretische maximum repetitiesnelheid wordt dan 20 Hz.
' Vandaar onze beslissing om 4 lookup's toe te passen, een voor elke groep van 14 noten.
' probleem: bij 2 registers samen ontwikkelen de solenoids niet voldoende kracht voor de noten 37,39,42 en 44
' ook niet wanneer we de pulsspanning verhogen tot -18V. Dit hangt af van de afregeling van het instrument zelf: de plektra mogen niet gelijktijdig
' de snaar raken. Het tijdsverschil kan geregeld worden met het schroefje onderaan elk dokje.

    vels[1] = MinVel        ' for all boards.
    velm[1] = MinVel
    velf[1] = MinVel

$ifdef board1
'    Dit is het board voor de laagste 14 noten. (35 tot 48)
'    1 unit is 24.8 us here.
'    vels[1] = 330           ' 9.5 ms
'    vels[127] = 2100        ' 52.2 ms
'    ' trajekt = 1770, stepsize = 1770 / 126 = 14
'    For i = 2 To 127
'        vels[i] = vels[i-1] + 14
'    Next i
    ' lookup voor reg1 alleen:
'    vels[1] = MinVel     ' 200 = 5 ms   330 = 9.5 ms
    vels[2] = 660           ' 19 ms
    vels[127] = 3300        ' 95 ms
    ' trajekt = 2640, stepsize = 2640 / 126 = 21
    Trajekt = vels[127] - vels[2]
    Stepsize = Trajekt / 125.0
    FOR i = 3 TO 127
        vels[i] = vels[i-1] + Stepsize
    NEXT i

    ' lookup voor reg2 alleen:
'    velm[1] = 200 ' 5 ms   330 = 9.5 ms
'    velm[2] = 660           ' 19 ms
'    velm[127] = 3300        ' 95 ms
'    ' trajekt = 2640, stepsize = 2640 / 126 = 21
'    Trajekt = vels[127] - vels[2]
'    Stepsize = Trajekt / 125.0
'    For i = 3 To 127
'        vels[i] = vels[i-1] + Stepsize
'    Next i
     FOR i = 2 TO 127
        velm[i] = vels[i] + (vels[i] / 10)    ' 10% more
     NEXT i

    ' lookup voor 2 registers:
    ' requires quite high values for the lowest 5 black keys...
'    velf[1] = 200 '330
    velf[2] = 1360
   ' velf[127] = 8000           ' 198 ms  not enough for lowest 4 black keys
    velf[127] = 10080          ' 250 ms
    Trajekt = velf[127] - velf[2]
    Stepsize = Trajekt / 125.0
    FOR i = 3 TO 127
        velf[i] = velf[i-1] + Stepsize
    NEXT i
$ENDIF

$ifdef board2
'    vels[1] = 330           ' 9.5 ms
'    vels[127] = 1968
'    ' trajekt = 1638, stepsize = 1638 / 126 = 13
'    For i = 2 To 127
'        vels[i] = vels[i-1] + 13
'    Next i

'    vels[1] = 200 '330           ' 9.5 ms
    vels[2] = 630           ' 18 ms
    vels[127] = 2900
    ' trajekt = 2270, stepsize = 2270 / 126 = 18
    Trajekt = vels[127] - vels[2]
    Stepsize = Trajekt / 125.0
    FOR i = 3 TO 127
        vels[i] = vels[i-1] + Stepsize
    NEXT i

'    velm[1] = 200
    FOR i = 2 TO 127
        velm[i] = vels[i] + (vels[i] / 10)
    NEXT i

    ' lookup voor 2 registers:
'    velf[1] = 200 '330
'    velf[2] = 760
'    velf[127] = 5300
'    Trajekt = velf[127] - velf[2]
'    Stepsize = Trajekt / 125.0
'    For i = 3 To 127
'        velf[i] = velf[i-1] + Stepsize
'    Next i
    ' following the theory, we should do this:
    FOR i = 2 TO 127
        velf[i] = vels[i] + velm[i]
    NEXT i
$ENDIF

$ifdef board3
'    vels[1] = 330           ' 9.5 ms
'    vels[127] = 1842
'    ' trajekt = 1512, stepsize = 1512 / 126 = 12
'    For i = 2 To 127
'        vels[i] = vels[i-1] + 12
'    Next i
'    vels[1] = 200 '310
    vels[2] = 610           ' 17 ms
    vels[127] = 2700
    ' trajekt = 2090, stepsize = 2090 / 126 = 17
    Trajekt = vels[127] - vels[2]
    Stepsize = Trajekt / 125.0
    FOR i = 3 TO 127
        vels[i] = vels[i-1] + 17
    NEXT i

'    velm[1] = 200
    FOR i = 2 TO 127
        velm[i] = vels[i] + (vels[i] / 10)
    NEXT i

    ' lookup voor 2 registers:
'    velf[1] = 200 '310
'    velf[2] = 710
'    velf[127] = 4900
'    Trajekt = velf[127] - velf[2]
'    Stepsize = Trajekt / 125.0
'    For i = 3 To 127
'        velf[i] = velf[i-1] + Stepsize
'    Next i
    ' theory:
    FOR i = 2 TO 127
        velf[i] = vels[i] + velm[i]
    NEXT i

$ENDIF

$ifdef board4
'    vels[1] = 330           ' 9.5 ms
'    vels[127] = 1716
'    ' trajekt = 1386, stepsize = 1386 / 126 = 11
'    For i = 2 To 127
'        vels[i] = vels[i-1] + 11
'    Next i
'    vels[1] = 200
    vels[2] = 600           ' 16 ms
    vels[127] = 2600
    ' trajekt = 2000, stepsize = 2000 / 126 = 16
    Trajekt = vels[127] - vels[2]
    Stepsize = Trajekt / 125.0
    FOR i = 3 TO 127
        vels[i] = vels[i-1] + 16
    NEXT i

'    velm[1] = 200
    FOR i = 2 TO 127
        velm[i] = vels[i] + (vels[i] / 10)
    NEXT i

    ' lookup voor 2 registers:
    ' in theorie zouden we moeten doen: velf[] = vels[] + velm[]
'    velf[1] = 200
'    velf[2] = 680
'    velf[127] = 4500
'    Trajekt = velf[127] - velf[2]
'    Stepsize = Trajekt / 125.0
'    For i = 3 To 127
'        velf[i] = velf[i-1] + Stepsize
'    Next i
    FOR i = 2 TO 127
        velf[i] = vels[i] + velm[i]
    NEXT i
$ENDIF
EndProc



'' following scales stem from our player-piano coding.
'Proc Vels_Lookup_0 ()
' ' lookup table for the velocity controlled pulse durations
''             vels[1] = 52
''             ' maxvel = 44ms or 44000/10.5 =  4190 units
''             ' stepsize = (4190 - 52) / 126 = 32.8
''             For i = 2 To 127
''                 vels[i] = vels[i-1] + 32.8
''             Next i
''             '   resultaat:       [23.01.2016, 18u10]
''               ' 1 = 52 => 550 us
''               ' 127 = 4190 => 44ms
''               ' dit komt overeen met prog change 0 to boards 1 tot 9 in pp2-firmware versie 2005.
''             ' rescaling 09.12.2019:
''             For i = 1 To 127
''                vels[i] = vels[i] >> 1  ' divide by 2     - vels[1] wordt nu 26
''             Next i
''                ' meting 09.12.2019, confirmed 18.06.2024
''                '  velo = 1  => 660us        vels[1] = 26
''                '  velo = 20 => 9.5 ms       vels[20] = 330
''                '  velo = 64  => 26.5 ms     vels[64] =
''                ' velo = 127  => 52.2 ms     vels[127] =  2100
''                ' so, one unit must be ca. 400 us
'EndProc

'Proc Vels_Lookup_1 ()
'' velo lookup scale prog.change 1 Same range as 0 but logarithmic
'vels[1]= 52     ' duur=  .000546
'vels[2]= 56     ' duur=  .000588
'vels[3]= 58     ' duur=  .000609
'vels[4]= 60     ' duur=  .00063
'vels[5]= 62     ' duur=  .000651
'vels[6]= 64     ' duur=  .000672
'vels[7]= 66     ' duur=  .000693
'vels[8]= 69     ' duur=  .0007245
'vels[9]= 71     ' duur=  .0007455
'vels[10]= 73     ' duur=  .0007665
'vels[11]= 76     ' duur=  .000798
'vels[12]= 79     ' duur=  .0008295
'vels[13]= 81     ' duur=  .0008505
'vels[14]= 84     ' duur=  .000882
'vels[15]= 87     ' duur=  .0009135
'vels[16]= 90     ' duur=  .000945
'vels[17]= 94     ' duur=  .000987
'vels[18]= 97     ' duur=  .0010185
'vels[19]= 100     ' duur=  .00105
'vels[20]= 104     ' duur=  .001092
'vels[21]= 107     ' duur=  .0011235
'vels[22]= 111     ' duur=  .0011655
'vels[23]= 115     ' duur=  .0012075
'vels[24]= 119     ' duur=  .0012495
'vels[25]= 123     ' duur=  .0012915
'vels[26]= 128     ' duur=  .001344
'vels[27]= 132     ' duur=  .001386
'vels[28]= 137     ' duur=  .0014385
'vels[29]= 142     ' duur=  .001491
'vels[30]= 147     ' duur=  .0015435
'vels[31]= 152     ' duur=  .001596
'vels[32]= 157     ' duur=  .0016485
'vels[33]= 163     ' duur=  .0017115
'vels[34]= 168     ' duur=  .001764
'vels[35]= 174     ' duur=  .001827
'vels[36]= 180     ' duur=  .00189
'vels[37]= 187     ' duur=  .0019635
'vels[38]= 193     ' duur=  .0020265
'vels[39]= 200     ' duur=  .0021
'vels[40]= 207     ' duur=  .0021735
'vels[41]= 214     ' duur=  .002247
'vels[42]= 222     ' duur=  .002331
'vels[43]= 230     ' duur=  .002415
'vels[44]= 238     ' duur=  .002499
'vels[45]= 246     ' duur=  .002583
'vels[46]= 255     ' duur=  .0026775
'vels[47]= 264     ' duur=  .002772
'vels[48]= 273     ' duur=  .0028665
'vels[49]= 283     ' duur=  .0029715
'vels[50]= 293     ' duur=  .0030765
'vels[51]= 303     ' duur=  .0031815
'vels[52]= 314     ' duur=  .003297
'vels[53]= 325     ' duur=  .0034125
'vels[54]= 336     ' duur=  .003528
'vels[55]= 348     ' duur=  .003654
'vels[56]= 360     ' duur=  .00378
'vels[57]= 373     ' duur=  .0039165
'vels[58]= 386     ' duur=  .004053
'vels[59]= 400     ' duur=  .0042
'vels[60]= 414     ' duur=  .004347
'vels[61]= 428     ' duur=  .004494
'vels[62]= 443     ' duur=  .0046515
'vels[63]= 459     ' duur=  .0048195
'vels[64]= 475     ' duur=  .0049875
'vels[65]= 492     ' duur=  .005166
'vels[66]= 509     ' duur=  .0053445
'vels[67]= 527     ' duur=  .0055335
'vels[68]= 545     ' duur=  .0057225
'vels[69]= 564     ' duur=  .005922
'vels[70]= 584     ' duur=  .006132
'vels[71]= 605     ' duur=  .0063525
'vels[72]= 626     ' duur=  .006573
'vels[73]= 648     ' duur=  .006804
'vels[74]= 671     ' duur=  .0070455
'vels[75]= 695     ' duur=  .0072975
'vels[76]= 719     ' duur=  .0075495
'vels[77]= 744     ' duur=  .007812
'vels[78]= 770     ' duur=  .008085
'vels[79]= 798     ' duur=  .008379
'vels[80]= 826     ' duur=  .008673
'vels[81]= 855     ' duur=  .0089775
'vels[82]= 885     ' duur=  .0092925
'vels[83]= 916     ' duur=  .009618
'vels[84]= 948     ' duur=  .009954
'vels[85]= 981     ' duur=  .0103005
'vels[86]= 1016     ' duur=  .010668
'vels[87]= 1052     ' duur=  .011046
'vels[88]= 1089     ' duur=  .0114345
'vels[89]= 1127     ' duur=  .0118335
'vels[90]= 1166     ' duur=  .012243
'vels[91]= 1207     ' duur=  .0126735
'vels[92]= 1250     ' duur=  .013125
'vels[93]= 1294     ' duur=  .013587
'vels[94]= 1339     ' duur=  .0140595
'vels[95]= 1386     ' duur=  .014553
'vels[96]= 1435     ' duur=  .0150675
'vels[97]= 1486     ' duur=  .015603
'vels[98]= 1538     ' duur=  .016149
'vels[99]= 1592     ' duur=  .016716
'vels[100]= 1648     ' duur=  .017304
'vels[101]= 1706     ' duur=  .017913
'vels[102]= 1766     ' duur=  .018543
'vels[103]= 1828     ' duur=  .019194
'vels[104]= 1892     ' duur=  .019866
'vels[105]= 1959     ' duur=  .0205695
'vels[106]= 2028     ' duur=  .021294
'vels[107]= 2099     ' duur=  .0220395
'vels[108]= 2173     ' duur=  .0228165
'vels[109]= 2249     ' duur=  .0236145
'vels[110]= 2328     ' duur=  .024444
'vels[111]= 2410     ' duur=  .025305
'vels[112]= 2495     ' duur=  .0261975
'vels[113]= 2583     ' duur=  .0271215
'vels[114]= 2674     ' duur=  .028077
'vels[115]= 2768     ' duur=  .029064
'vels[116]= 2865     ' duur=  .0300825
'vels[117]= 2966     ' duur=  .031143
'vels[118]= 3070     ' duur=  .032235
'vels[119]= 3178     ' duur=  .033369
'vels[120]= 3290     ' duur=  .034545
'vels[121]= 3405     ' duur=  .0357525
'vels[122]= 3525     ' duur=  .0370125
'vels[123]= 3649     ' duur=  .0383145
'vels[124]= 3777     ' duur=  .0396585
'vels[125]= 3910     ' duur=  .041055
'vels[126]= 4048     ' duur=  .042504
'vels[127]= 4190     ' duur=  .043995
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'    ' this gives: velo[1] = 640 us
'    '             velo[20] = 1.4 ms
'    '             velo[64] = 6 ms
'    '             velo[127] = 50 ms
'EndProc

'Proc Vels_Lookup_2 ()
' ' lookup table for the velocity controlled pulse durations
' ' minvel = 500us or, 500us / 10.5us = 47.6 units
' ' maxvel = 27ms or, 27000us / 10.5us = 2571 units
' ' stepsize = (2571 - 47.6) / 126 = 20.02
' vels[1] = 47
'For i = 2 To 127
'    vels[i] = vels[i-1] + 20
'Next i
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'EndProc

'Proc Vels_Lookup_3 ()
' ' lookup table for the velocity controlled pulse durations
' ' minvel = 480 us or, 45.7 units
' ' maxvel = 17ms or, 17000us / 10.5us = 1619 units
' ' stepsize = (1619 - 45) / 126 = 12.49
' vels[1] = 46
'For i = 2 To 127
'    vels[i] = vels[i-1] + 12.49
'Next i
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'EndProc

'' velo lookup scale prog.change 4 Same range as 2 but logarithmic
'Proc Vels_Lookup_4 ()
'vels[1]= 47     ' duur=  .0004935
'vels[2]= 50     ' duur=  .000525
'vels[3]= 52     ' duur=  .000546
'vels[4]= 53     ' duur=  .0005565
'vels[5]= 55     ' duur=  .0005775
'vels[6]= 57     ' duur=  .0005985
'vels[7]= 59     ' duur=  .0006195
'vels[8]= 60     ' duur=  .00063
'vels[9]= 62     ' duur=  .000651
'vels[10]= 64     ' duur=  .000672
'vels[11]= 66     ' duur=  .000693
'vels[12]= 69     ' duur=  .0007245
'vels[13]= 71     ' duur=  .0007455
'vels[14]= 73     ' duur=  .0007665
'vels[15]= 75     ' duur=  .0007875
'vels[16]= 78     ' duur=  .000819
'vels[17]= 80     ' duur=  .00084
'vels[18]= 83     ' duur=  .0008715
'vels[19]= 86     ' duur=  .000903
'vels[20]= 88     ' duur=  .000924
'vels[21]= 91     ' duur=  .0009555
'vels[22]= 94     ' duur=  .000987
'vels[23]= 97     ' duur=  .0010185
'vels[24]= 100     ' duur=  .00105
'vels[25]= 103     ' duur=  .0010815
'vels[26]= 107     ' duur=  .0011235
'vels[27]= 110     ' duur=  .001155
'vels[28]= 114     ' duur=  .001197
'vels[29]= 117     ' duur=  .0012285
'vels[30]= 121     ' duur=  .0012705
'vels[31]= 125     ' duur=  .0013125
'vels[32]= 129     ' duur=  .0013545
'vels[33]= 133     ' duur=  .0013965
'vels[34]= 137     ' duur=  .0014385
'vels[35]= 142     ' duur=  .001491
'vels[36]= 146     ' duur=  .001533
'vels[37]= 151     ' duur=  .0015855
'vels[38]= 156     ' duur=  .001638
'vels[39]= 161     ' duur=  .0016905
'vels[40]= 166     ' duur=  .001743
'vels[41]= 171     ' duur=  .0017955
'vels[42]= 177     ' duur=  .0018585
'vels[43]= 182     ' duur=  .001911
'vels[44]= 188     ' duur=  .001974
'vels[45]= 194     ' duur=  .002037
'vels[46]= 200     ' duur=  .0021
'vels[47]= 207     ' duur=  .0021735
'vels[48]= 213     ' duur=  .0022365
'vels[49]= 220     ' duur=  .00231
'vels[50]= 227     ' duur=  .0023835
'vels[51]= 234     ' duur=  .002457
'vels[52]= 242     ' duur=  .002541
'vels[53]= 250     ' duur=  .002625
'vels[54]= 258     ' duur=  .002709
'vels[55]= 266     ' duur=  .002793
'vels[56]= 274     ' duur=  .002877
'vels[57]= 283     ' duur=  .0029715
'vels[58]= 292     ' duur=  .003066
'vels[59]= 302     ' duur=  .003171
'vels[60]= 311     ' duur=  .0032655
'vels[61]= 321     ' duur=  .0033705
'vels[62]= 332     ' duur=  .003486
'vels[63]= 342     ' duur=  .003591
'vels[64]= 353     ' duur=  .0037065
'vels[65]= 364     ' duur=  .003822
'vels[66]= 376     ' duur=  .003948
'vels[67]= 388     ' duur=  .004074
'vels[68]= 401     ' duur=  .0042105
'vels[69]= 413     ' duur=  .0043365
'vels[70]= 427     ' duur=  .0044835
'vels[71]= 440     ' duur=  .00462
'vels[72]= 454     ' duur=  .004767
'vels[73]= 469     ' duur=  .0049245
'vels[74]= 484     ' duur=  .005082
'vels[75]= 499     ' duur=  .0052395
'vels[76]= 515     ' duur=  .0054075
'vels[77]= 532     ' duur=  .005586
'vels[78]= 549     ' duur=  .0057645
'vels[79]= 567     ' duur=  .0059535
'vels[80]= 585     ' duur=  .0061425
'vels[81]= 603     ' duur=  .0063315
'vels[82]= 623     ' duur=  .0065415
'vels[83]= 643     ' duur=  .0067515
'vels[84]= 663     ' duur=  .0069615
'vels[85]= 684     ' duur=  .007182
'vels[86]= 706     ' duur=  .007413
'vels[87]= 729     ' duur=  .0076545
'vels[88]= 752     ' duur=  .007896
'vels[89]= 776     ' duur=  .008148
'vels[90]= 801     ' duur=  .0084105
'vels[91]= 827     ' duur=  .0086835
'vels[92]= 853     ' duur=  .0089565
'vels[93]= 881     ' duur=  .0092505
'vels[94]= 909     ' duur=  .0095445
'vels[95]= 938     ' duur=  .009849
'vels[96]= 968     ' duur=  .010164
'vels[97]= 999     ' duur=  .0104895
'vels[98]= 1031     ' duur=  .0108255
'vels[99]= 1064     ' duur=  .011172
'vels[100]= 1098     ' duur=  .011529
'vels[101]= 1133     ' duur=  .0118965
'vels[102]= 1169     ' duur=  .0122745
'vels[103]= 1207     ' duur=  .0126735
'vels[104]= 1246     ' duur=  .013083
'vels[105]= 1285     ' duur=  .0134925
'vels[106]= 1327     ' duur=  .0139335
'vels[107]= 1369     ' duur=  .0143745
'vels[108]= 1413     ' duur=  .0148365
'vels[109]= 1458     ' duur=  .015309
'vels[110]= 1505     ' duur=  .0158025
'vels[111]= 1553     ' duur=  .0163065
'vels[112]= 1603     ' duur=  .0168315
'vels[113]= 1654     ' duur=  .017367
'vels[114]= 1707     ' duur=  .0179235
'vels[115]= 1761     ' duur=  .0184905
'vels[116]= 1818     ' duur=  .019089
'vels[117]= 1876     ' duur=  .019698
'vels[118]= 1936     ' duur=  .020328
'vels[119]= 1998     ' duur=  .020979
'vels[120]= 2062     ' duur=  .021651
'vels[121]= 2128     ' duur=  .022344
'vels[122]= 2196     ' duur=  .023058
'vels[123]= 2267     ' duur=  .0238035
'vels[124]= 2339     ' duur=  .0245595
'vels[125]= 2414     ' duur=  .025347
'vels[126]= 2491     ' duur=  .0261555
'vels[127]= 2571     ' duur=  .0269955
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'EndProc

'' velo lookup scale prog.change 5 Same range as 3 but logarithmic
'Proc Vels_Lookup_5 ()
'vels[1]= 46     ' duur=  .000483
'vels[2]= 49     ' duur=  .0005145
'vels[3]= 50     ' duur=  .000525
'vels[4]= 51     ' duur=  .0005355
'vels[5]= 53     ' duur=  .0005565
'vels[6]= 54     ' duur=  .000567
'vels[7]= 56     ' duur=  .000588
'vels[8]= 58     ' duur=  .000609
'vels[9]= 59     ' duur=  .0006195
'vels[10]= 61     ' duur=  .0006405
'vels[11]= 63     ' duur=  .0006615
'vels[12]= 64     ' duur=  .000672
'vels[13]= 66     ' duur=  .000693
'vels[14]= 68     ' duur=  .000714
'vels[15]= 70     ' duur=  .000735
'vels[16]= 72     ' duur=  .000756
'vels[17]= 74     ' duur=  .000777
'vels[18]= 76     ' duur=  .000798
'vels[19]= 78     ' duur=  .000819
'vels[20]= 81     ' duur=  .0008505
'vels[21]= 83     ' duur=  .0008715
'vels[22]= 85     ' duur=  .0008925
'vels[23]= 88     ' duur=  .000924
'vels[24]= 90     ' duur=  .000945
'vels[25]= 93     ' duur=  .0009765
'vels[26]= 95     ' duur=  .0009975
'vels[27]= 98     ' duur=  .001029
'vels[28]= 101     ' duur=  .0010605
'vels[29]= 104     ' duur=  .001092
'vels[30]= 107     ' duur=  .0011235
'vels[31]= 110     ' duur=  .001155
'vels[32]= 113     ' duur=  .0011865
'vels[33]= 116     ' duur=  .001218
'vels[34]= 119     ' duur=  .0012495
'vels[35]= 123     ' duur=  .0012915
'vels[36]= 126     ' duur=  .001323
'vels[37]= 130     ' duur=  .001365
'vels[38]= 134     ' duur=  .001407
'vels[39]= 137     ' duur=  .0014385
'vels[40]= 141     ' duur=  .0014805
'vels[41]= 145     ' duur=  .0015225
'vels[42]= 149     ' duur=  .0015645
'vels[43]= 154     ' duur=  .001617
'vels[44]= 158     ' duur=  .001659
'vels[45]= 162     ' duur=  .001701
'vels[46]= 167     ' duur=  .0017535
'vels[47]= 172     ' duur=  .001806
'vels[48]= 177     ' duur=  .0018585
'vels[49]= 182     ' duur=  .001911
'vels[50]= 187     ' duur=  .0019635
'vels[51]= 192     ' duur=  .002016
'vels[52]= 198     ' duur=  .002079
'vels[53]= 203     ' duur=  .0021315
'vels[54]= 209     ' duur=  .0021945
'vels[55]= 215     ' duur=  .0022575
'vels[56]= 221     ' duur=  .0023205
'vels[57]= 227     ' duur=  .0023835
'vels[58]= 234     ' duur=  .002457
'vels[59]= 241     ' duur=  .0025305
'vels[60]= 247     ' duur=  .0025935
'vels[61]= 254     ' duur=  .002667
'vels[62]= 262     ' duur=  .002751
'vels[63]= 269     ' duur=  .0028245
'vels[64]= 277     ' duur=  .0029085
'vels[65]= 285     ' duur=  .0029925
'vels[66]= 293     ' duur=  .0030765
'vels[67]= 301     ' duur=  .0031605
'vels[68]= 310     ' duur=  .003255
'vels[69]= 318     ' duur=  .003339
'vels[70]= 327     ' duur=  .0034335
'vels[71]= 337     ' duur=  .0035385
'vels[72]= 346     ' duur=  .003633
'vels[73]= 356     ' duur=  .003738
'vels[74]= 366     ' duur=  .003843
'vels[75]= 377     ' duur=  .0039585
'vels[76]= 387     ' duur=  .0040635
'vels[77]= 398     ' duur=  .004179
'vels[78]= 410     ' duur=  .004305
'vels[79]= 421     ' duur=  .0044205
'vels[80]= 433     ' duur=  .0045465
'vels[81]= 446     ' duur=  .004683
'vels[82]= 458     ' duur=  .004809
'vels[83]= 471     ' duur=  .0049455
'vels[84]= 485     ' duur=  .0050925
'vels[85]= 499     ' duur=  .0052395
'vels[86]= 513     ' duur=  .0053865
'vels[87]= 527     ' duur=  .0055335
'vels[88]= 542     ' duur=  .005691
'vels[89]= 558     ' duur=  .005859
'vels[90]= 574     ' duur=  .006027
'vels[91]= 590     ' duur=  .006195
'vels[92]= 607     ' duur=  .0063735
'vels[93]= 624     ' duur=  .006552
'vels[94]= 642     ' duur=  .006741
'vels[95]= 660     ' duur=  .00693
'vels[96]= 679     ' duur=  .0071295
'vels[97]= 698     ' duur=  .007329
'vels[98]= 718     ' duur=  .007539
'vels[99]= 738     ' duur=  .007749
'vels[100]= 759     ' duur=  .0079695
'vels[101]= 781     ' duur=  .0082005
'vels[102]= 803     ' duur=  .0084315
'vels[103]= 826     ' duur=  .008673
'vels[104]= 850     ' duur=  .008925
'vels[105]= 874     ' duur=  .009177
'vels[106]= 899     ' duur=  .0094395
'vels[107]= 924     ' duur=  .009702
'vels[108]= 950     ' duur=  .009975
'vels[109]= 977     ' duur=  .0102585
'vels[110]= 1005     ' duur=  .0105525
'vels[111]= 1034     ' duur=  .010857
'vels[112]= 1063     ' duur=  .0111615
'vels[113]= 1093     ' duur=  .0114765
'vels[114]= 1124     ' duur=  .011802
'vels[115]= 1156     ' duur=  .012138
'vels[116]= 1189     ' duur=  .0124845
'vels[117]= 1223     ' duur=  .0128415
'vels[118]= 1258     ' duur=  .013209
'vels[119]= 1294     ' duur=  .013587
'vels[120]= 1330     ' duur=  .013965
'vels[121]= 1368     ' duur=  .014364
'vels[122]= 1407     ' duur=  .0147735
'vels[123]= 1447     ' duur=  .0151935
'vels[124]= 1488     ' duur=  .015624
'vels[125]= 1531     ' duur=  .0160755
'vels[126]= 1574     ' duur=  .016527
'vels[127]= 1619     ' duur=  .0169995
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'EndProc

'Proc Vels_Lookup_6 ()
'   ' linear mapping that will work for all repetition frequencies
' ' lookup table for the velocity controlled pulse durations
' ' minvel = 480 us or, 45.7 units
' ' maxvel = 3000 units  (=Dur[127]
' ' stepsize = (3000 - 45) / 126 = 23.45
' vels[1] = 45
'For i = 2 To 127
'    vels[i] = vels[i-1] + 23.45
'Next i
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'EndProc


'Proc Vels_Lookup_7 ()
'   ' velo lookup scale prog.change 7 Same range as 6 but logarithmic
'   ' log mapping that will work for all repetition speeds
'vels[1]= 44     ' duur=  .000462
'vels[2]= 47     ' duur=  .0004935
'vels[3]= 49     ' duur=  .0005145
'vels[4]= 50     ' duur=  .000525
'vels[5]= 52     ' duur=  .000546
'vels[6]= 54     ' duur=  .000567
'vels[7]= 56     ' duur=  .000588
'vels[8]= 57     ' duur=  .0005985
'vels[9]= 59     ' duur=  .0006195
'vels[10]= 61     ' duur=  .0006405
'vels[11]= 63     ' duur=  .0006615
'vels[12]= 66     ' duur=  .000693
'vels[13]= 68     ' duur=  .000714
'vels[14]= 70     ' duur=  .000735
'vels[15]= 72     ' duur=  .000756
'vels[16]= 75     ' duur=  .0007875
'vels[17]= 77     ' duur=  .0008085
'vels[18]= 80     ' duur=  .00084
'vels[19]= 83     ' duur=  .0008715
'vels[20]= 86     ' duur=  .000903
'vels[21]= 88     ' duur=  .000924
'vels[22]= 91     ' duur=  .0009555
'vels[23]= 95     ' duur=  .0009975
'vels[24]= 98     ' duur=  .001029
'vels[25]= 101     ' duur=  .0010605
'vels[26]= 104     ' duur=  .001092
'vels[27]= 108     ' duur=  .001134
'vels[28]= 112     ' duur=  .001176
'vels[29]= 115     ' duur=  .0012075
'vels[30]= 119     ' duur=  .0012495
'vels[31]= 123     ' duur=  .0012915
'vels[32]= 127     ' duur=  .0013335
'vels[33]= 132     ' duur=  .001386
'vels[34]= 136     ' duur=  .001428
'vels[35]= 141     ' duur=  .0014805
'vels[36]= 146     ' duur=  .001533
'vels[37]= 151     ' duur=  .0015855
'vels[38]= 156     ' duur=  .001638
'vels[39]= 161     ' duur=  .0016905
'vels[40]= 166     ' duur=  .001743
'vels[41]= 172     ' duur=  .001806
'vels[42]= 178     ' duur=  .001869
'vels[43]= 184     ' duur=  .001932
'vels[44]= 190     ' duur=  .001995
'vels[45]= 196     ' duur=  .002058
'vels[46]= 203     ' duur=  .0021315
'vels[47]= 210     ' duur=  .002205
'vels[48]= 217     ' duur=  .0022785
'vels[49]= 224     ' duur=  .002352
'vels[50]= 232     ' duur=  .002436
'vels[51]= 240     ' duur=  .00252
'vels[52]= 248     ' duur=  .002604
'vels[53]= 256     ' duur=  .002688
'vels[54]= 265     ' duur=  .0027825
'vels[55]= 274     ' duur=  .002877
'vels[56]= 283     ' duur=  .0029715
'vels[57]= 293     ' duur=  .0030765
'vels[58]= 303     ' duur=  .0031815
'vels[59]= 313     ' duur=  .0032865
'vels[60]= 323     ' duur=  .0033915
'vels[61]= 334     ' duur=  .003507
'vels[62]= 346     ' duur=  .003633
'vels[63]= 357     ' duur=  .0037485
'vels[64]= 369     ' duur=  .0038745  s
'vels[65]= 382     ' duur=  .004011
'vels[66]= 395     ' duur=  .0041475
'vels[67]= 408     ' duur=  .004284
'vels[68]= 422     ' duur=  .004431
'vels[69]= 436     ' duur=  .004578
'vels[70]= 451     ' duur=  .0047355
'vels[71]= 466     ' duur=  .004893
'vels[72]= 482     ' duur=  .005061
'vels[73]= 498     ' duur=  .005229
'vels[74]= 515     ' duur=  .0054075
'vels[75]= 533     ' duur=  .0055965
'vels[76]= 551     ' duur=  .0057855
'vels[77]= 569     ' duur=  .0059745
'vels[78]= 588     ' duur=  .006174
'vels[79]= 608     ' duur=  .006384
'vels[80]= 629     ' duur=  .0066045
'vels[81]= 650     ' duur=  .006825
'vels[82]= 672     ' duur=  .007056
'vels[83]= 695     ' duur=  .0072975
'vels[84]= 718     ' duur=  .007539
'vels[85]= 743     ' duur=  .0078015
'vels[86]= 768     ' duur=  .008064
'vels[87]= 794     ' duur=  .008337
'vels[88]= 820     ' duur=  .00861
'vels[89]= 848     ' duur=  .008904
'vels[90]= 877     ' duur=  .0092085
'vels[91]= 906     ' duur=  .009513
'vels[92]= 937     ' duur=  .0098385
'vels[93]= 969     ' duur=  .0101745
'vels[94]= 1002     ' duur=  .010521
'vels[95]= 1035     ' duur=  .0108675
'vels[96]= 1070     ' duur=  .011235
'vels[97]= 1107     ' duur=  .0116235
'vels[98]= 1144     ' duur=  .012012
'vels[99]= 1183     ' duur=  .0124215
'vels[100]= 1223     ' duur=  .0128415
'vels[101]= 1264     ' duur=  .013272
'vels[102]= 1307     ' duur=  .0137235
'vels[103]= 1351     ' duur=  .0141855
'vels[104]= 1396     ' duur=  .014658
'vels[105]= 1444     ' duur=  .015162
'vels[106]= 1493     ' duur=  .0156765
'vels[107]= 1543     ' duur=  .0162015
'vels[108]= 1595     ' duur=  .0167475
'vels[109]= 1649     ' duur=  .0173145
'vels[110]= 1705     ' duur=  .0179025
'vels[111]= 1762     ' duur=  .018501
'vels[112]= 1822     ' duur=  .019131
'vels[113]= 1884     ' duur=  .019782
'vels[114]= 1947     ' duur=  .0204435
'vels[115]= 2013     ' duur=  .0211365
'vels[116]= 2081     ' duur=  .0218505
'vels[117]= 2151     ' duur=  .0225855
'vels[118]= 2224     ' duur=  .023352
'vels[119]= 2299     ' duur=  .0241395
'vels[120]= 2377     ' duur=  .0249585
'vels[121]= 2457     ' duur=  .0257985
'vels[122]= 2541     ' duur=  .0266805
'vels[123]= 2626     ' duur=  .027573
'vels[124]= 2715     ' duur=  .0285075
'vels[125]= 2807     ' duur=  .0294735
'vels[126]= 2902     ' duur=  .030471
'vels[127]= 3000     ' duur=  .0315
'    For i = 1 To 127
'        vels[i] = vels[i] >> 1
'    Next i
'EndProc

'[EOF]
