'**************************************************************** '* Name : Llor_Pic2.BAS * '* Author : Godfried-Willem RAES * '* Notice : Copyleft (c) 2017 Logosoft Public Domain * '* Date : 17.10.2017 * '* Version : 2.0 * '* Notes : using priority timers and loopcounter * '**************************************************************** '15.12.2004: Original coding by Johannes Taelman for the 18F252 processor. ' As this PCB does not have a programming connector, ' code changes require you to change the chip on the PCB everytime. '14.10.2017: Starting from Psch code model, rewritten for use with Llor ' bidirectional solenoids. ' With automatic note repeats. '15.10.2017: first working version. Velo rescaling required. ' Note-off with release implemented. '16.10.2017: Controller 66 implementation removed again. ' Now it will just perform a controller reset and notes off. ' On cold boot, all solenoids will move to the back position now. ' Velo scalings rechecked on Llor. ' Range is now 18ms to 75ms. ' Tests o.k. on Llor. ' 16.10.2017: Starting code for PIC2 , from the PIC1 model. ' 23u20: Up and running... ' scope tests o.k. ' We now have exactly 128 system variables. This is the maximum. ' 17.10.2017: Midi ringbuffer declared in main code instead of in IRQ include. ' First test on : mapping was fully wrong! ' Badly documented in V1.0 coding. ' 18.10.2017: All solenoids working again. 'Include "18F2525.inc" 'version for the Bello board. (40MHz) Include "18F2620.inc" 'also possible, more memory available 'Include "18F2520.inc" 'not possible as it has too little user ram (40MHz) 'Include "18F25K20.inc" 'for test & debug on an Amicus board. (64MHz) Declare Bootloader = 0 Declare Watchdog = 0 'Declare Label_Bank_Resets = 1 'Declare Icd_Req = 0 ' Mapping defines for midi-events on pin outputs and inputs: $define Bel44 PORTB.5 ' PORTA.2 ' 44 = hard beaters $define Bel45 PORTB.3 ' PORTA.1 ' 45 $define Bel46 PORTC.5 ' PORTA.0 ' 46 $define Bel47 PORTB.1 ' PORTA.3 ' 47 $define Bel48 PORTA.2 ' PORTA.4 ' 48 - soft beaters $define Bel49 PORTA.1 ' PORTA.5 ' 49 $define Bel50 PORTA.0 ' PORTB.7 ' 50 $define Bel51 PORTA.3 ' PORTB.6 ' 51 $define Bel52 PORTA.4 ' PORTB.5 ' 52 $define Bel53 PORTA.5 ' PORTB.4 ' 53 $define Bel54 PORTB.7 ' PORTB.3 ' 54 $define Bel55 PORTB.6 ' PORTB.2 ' 55 $define Bel56 PORTB.4 ' PORTB.1 ' 56 $define Bel57 PORTB.2 ' PORTB.0 ' 57 $define Bel58 PORTC.4 ' PORTC.5 ' 58 $define Bel59 PORTB.0 ' PORTC.4 ' 59 $define Debug_Led PORTC.0 ' for testing - red led - watchdog ' flashing happens in the IRQ handler Symbol CntLw = TMR0L.Word0 ' 16 bit timer 0 alias ' configure the input and output pins: Clear SSPCON1.5 'RC3 must be available for I/O TRISA = %11000000 'bits set to 0 are output, 1 = input TRISB = %00000000 ' we use all bits, thus disabling the ICD changed 19.11.2015 TRISC = %11000000 'RC1 en RC2 zijn pwm outputs and must be set to output 'RC6 en RC7 zijn USART I/O and must be set to input 'constant definitions: 'initialisations for the midi input parser: Symbol Midichannel = 11 ' Llor and Belly channel Symbol NoteOff_Status = 128 + Midichannel ' 2 bytes follow Symbol NoteOn_Status = 144 + Midichannel Symbol Keypres_Status = 160 + Midichannel ' 2 bytes follow 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 'application specific constants 'Symbol fPWM = PWMminF * 4 ' in avoidance of audible artifacts ' not used in this code Symbol note0 = 44 ' f-beater shell 9 Symbol note1 = 45 ' f-beater shell 10 Symbol note2 = 46 ' f-beater shell 11 Symbol note3 = 47 ' f-beater shell 12 Symbol note4 = 48 ' p-beater shell 1 Symbol note5 = 49 Symbol note6 = 50 Symbol note7 = 51 Symbol note8 = 52 Symbol note9 = 53 Symbol note10 = 54 Symbol note11 = 55 Symbol note12 = 56 Symbol note13 = 57 Symbol note14 = 58 Symbol note15 = 59 ' p-beater shell 12 ' 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 All_Digital = True ' Declare Hserial_Clear = On ' should clear on errors. Bytes get lost of course... ' Create variables ' Dim Cnt As Dword System ' 32bit counter ' Dim CntHw As Cnt.Word1 'used in the timer0 interrupt, to create a 32 bit timer ' Dim CntLw As TMR0L.Word 'this is the trick to read both TMR0L and TMR0H as a single 16 bit value 'it makes Cntlw the low word of cnt 'We still have to copy the contents of CntLw to Cnt, as CntLw is not Cnt.word0 ! ' by doing Cnt.Byte1 = TMR0H : Cnt.Byte0 = TMR0L before using the timer value in cnt ' or Cnt.word0 = CntLw ' macro definition: '$define Gettime Cnt.Word0 = CntLw ' time = Cnt Dim Tim3 As TMR3L.Word ' used in the low interrupt ' not used in this code. '----------------------------------------------------------------------------------------- ' Load the USART Interrupt handler And buffer read subroutines into memory Dim RingBuffer[256] As Byte ' ringbuffer for MIDI-input data Include "Llor_Irq_PIC2.inc" ' our own version for UART And Timer0/3 Interrupt 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 ' used for solenoid anchor return on note-off. Dim noteAan As Byte System ' note on + attack value Dim velo As Byte System Dim notePres As Byte System ' note pressure + pressure value Dim pres As Byte System 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 VelFlags As Word System ' bits 0 - 15 used as flags for active timers Dim NoteRepeats As Word System ' bits used as flags NoteRepeats.0 to NoteRepeats.15 for the repeats Dim CC66 As Byte System ' global on/off switch Dim time As Dword System ' 32-bit, incremented in low IRQ ' or loopcounter Dim maxtim As time.31 ' overflow bit, will cause timer reset after 1h45' Dim t As Byte System ' loopcounter, running at 4 times time-clock Dim tog As Byte System Dim tg As tog.0 ' divide by 4 bit ' Dim overflow As tog.7 ' bit gets set when time.31 gets set Dim velo0 As Word System Dim velo1 As Word System Dim velo2 As Word System Dim velo3 As Word System Dim velo4 As Word System Dim velo5 As Word System Dim velo6 As Word System Dim velo7 As Word System Dim velo8 As Word System Dim velo9 As Word System Dim velo10 As Word System Dim velo11 As Word System Dim velo12 As Word System Dim velo13 As Word System Dim velo14 As Word System Dim velo15 As Word System Dim Rate0 As Word System Dim Rate1 As Word System ' set with keypressure Dim Rate2 As Word System Dim Rate3 As Word System Dim Rate4 As Word System Dim Rate5 As Word System Dim Rate6 As Word System Dim Rate7 As Word System Dim Rate8 As Word System Dim Rate9 As Word System ' set with keypressure Dim Rate10 As Word System Dim Rate11 As Word System Dim Rate12 As Word System Dim Rate13 As Word System Dim Rate14 As Word System Dim Rate15 As Word System Dim Pres0 As Byte 'System 'indexes for durations lookup Dim Pres1 As Byte 'System Dim Pres2 As Byte 'System Dim Pres3 As Byte 'System Dim Pres4 As Byte 'System Dim Pres5 As Byte 'System Dim Pres6 As Byte 'System Dim Pres7 As Byte 'System Dim Pres8 As Byte 'System 'indexes for durations lookup Dim Pres9 As Byte 'System Dim Pres10 As Byte 'System Dim Pres11 As Byte 'System Dim Pres12 As Byte 'System Dim Pres13 As Byte 'System Dim Pres14 As Byte 'System Dim Pres15 As Byte 'System Dim TimVals[16] 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 CC30 As Byte Dim MinVal As Word ' lookup-tables: ' velocity lookup tables: Dim Vels[128] As Word ' lookup for note repetitions: Dim Dur[128] As Word MAIN: 'make sure we initialize the used pins on start up: Low Bel44 Low Bel45 Low Bel46 Low Bel47 Low Bel48 Low Bel49 Low Bel50 Low Bel51 Low Bel52 Low Bel53 Low Bel54 Low Bel55 Low Bel56 Low Bel57 Low Bel58 Low Bel59 Low PORTC.1 ' not used Low PORTC.2 ' not used DelayMS 50 ' wait for stability 'Clear ' clears all RAM 'Low Debug_Led Clear Rate0 Clear Rate1 Clear Rate2 Clear Rate3 Clear Rate4 Clear Rate5 Clear Rate6 Clear Rate7 Clear Rate8 Clear Rate9 Clear Rate10 Clear Rate11 Clear Rate12 Clear Rate13 Clear Rate14 Clear Rate15 Clear Pres0 Clear Pres1 Clear Pres2 Clear Pres3 Clear Pres4 Clear Pres5 Clear Pres6 Clear Pres7 Clear Pres8 Clear Pres9 Clear Pres10 Clear Pres11 Clear Pres12 Clear Pres13 Clear Pres14 Clear Pres15 Clear CC66 Clear CC30 'Clear CC31 GoSub Dur_Lookup ' note repetition periods GoSub Vels_Lookup ' velocity scalings Clear NoteRepeats ' repeat flags Clear VelFlags 'Low PORTB ' redundant Init_Usart_Interrupt ' Initiate the USART serial buffer interrupt ' this procedure is in the IRQ include file Clear_Serial_Buffer ' Clear the serial buffer and reset its pointers ' in the IRQ 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 ' Opentimer0 (Timer_INT_On & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256) 'in macro file. Clear T1CON ' clear timer1 flags Clear IntConBits_T0IF ' clear interrupt flag Set INTCONBITS_T0IE ' enable interrupt on overflow T0CON = %10000111 ' bit 7 = enable/disable ' bit 6 = 1=8 bit, 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 ' open and start timer3 for counting: Clear T3CON Clear PIR2BITS_TMR3IF ' clear IRQ flag ' Set PIE2BITS_TMR3IE ' irq on 'Clear Tim3 ' Clear TMR3L And TMR3H registers Tim3 = 65506 ' 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 , 0= 8 bit mode : only affect the way it's written ' 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, lowest freq.=19Hz ' reset all timers on startup: Set TimVals Set idx Set Nxt.31 Clear time ' so we start from 0 ' start the main program loop: --------------------------------------------------------- Loop: Inc t ' byte If t.1 = tg Then Btg tg Inc time ' dword - so this counts at loopspeed / 4 EndIf ' = time = t >> 2 not working: compiler bug! ' Create an infinite loop Bytein = HRSIn ' Read data from the serial buffer, with no timeout ' Start the midi parser. Midi_Parse: If Bytein > Control_Status Then ' here higher statusses are not implemented. If Bytein < 254 Then Clear statusbyte GoTo Check_Timers ' we do not need to re-sort existing timers in this case 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 '= 255 'reset value. Cannot be 0 !!! Case NoteOn_Status statusbyte = Bytein Set noteAan Case Keypres_Status ' used for note repetition speed statusbyte = Bytein Set notePres Case Control_Status ' only 66 and 123 statusbyte = Bytein Set Ctrl ' Case ProgChange_Status ' statusbyte = Bytein ' prog = 255 ' Case Aftertouch_Status ' statusbyte = Bytein ' aft = 255 ' Case Pitchbend_Status ' statusbyte = Bytein ' pblsb = 255 ' pbmsb = 255 EndSelect GoTo Check_Timers ' no re-sorting of timers required Else ' midi byte is 7 bits Select statusbyte Case 0 'not a message for this channel GoTo Check_Timers 'disregard - no resort required Case NoteOff_Status If noteUit = 255 Then noteUit = Bytein GoTo Check_Timers Else 'release = Bytein 'message complete, so we can do the action... ' here we use the release byte to force the solenoids to return ' using a timer. This can only be used from a programming language ' as no sequencers support note-off with release Select noteUit Case note0 Clear NoteRepeats.0 Case note1 Clear NoteRepeats.1 Case note2 Clear NoteRepeats.2 Case note3 Clear NoteRepeats.3 Case note4 Clear NoteRepeats.4 Case note5 Clear NoteRepeats.5 Case note6 Clear NoteRepeats.6 Case note7 Clear NoteRepeats.7 Case note8 Clear NoteRepeats.8 Case note9 Clear NoteRepeats.9 Case note10 Clear NoteRepeats.10 Case note11 Clear NoteRepeats.11 Case note12 Clear NoteRepeats.12 Case note13 Clear NoteRepeats.13 Case note14 Clear NoteRepeats.14 Case note15 Clear NoteRepeats.15 Case Else Set noteUit GoTo Check_Timers ' no re-sorting EndSelect Set noteUit '= 255 reset GoTo resort ' note-off happened, so resort! EndIf Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then Select noteAan Case note0 Clear NoteRepeats.0 If VelFlags.0 = 0 Then Set TimVals[0] ' this cancels a wait timer in case repeats were active Case note1 Clear NoteRepeats.1 If VelFlags.1 = 0 Then Set TimVals[1] Case note2 Clear NoteRepeats.2 If VelFlags.2 = 0 Then Set TimVals[2] Case note3 Clear NoteRepeats.3 If VelFlags.3 = 0 Then Set TimVals[3] Case note4 Clear NoteRepeats.4 If VelFlags.4 = 0 Then Set TimVals[4] Case note5 Clear NoteRepeats.5 If VelFlags.5 = 0 Then Set TimVals[5] Case note6 Clear NoteRepeats.6 If VelFlags.6 = 0 Then Set TimVals[6] Case note7 Clear NoteRepeats.7 If VelFlags.7 = 0 Then Set TimVals[7] Case note8 Clear NoteRepeats.8 If VelFlags.8 = 0 Then Set TimVals[8] ' this cancels a wait timer in case repeats were active Case note9 Clear NoteRepeats.9 If VelFlags.9 = 0 Then Set TimVals[9] Case note10 Clear NoteRepeats.10 If VelFlags.10 = 0 Then Set TimVals[10] Case note11 Clear NoteRepeats.11 If VelFlags.11 = 0 Then Set TimVals[11] Case note12 Clear NoteRepeats.12 If VelFlags.12 = 0 Then Set TimVals[12] Case note13 Clear NoteRepeats.13 If VelFlags.13 = 0 Then Set TimVals[13] Case note14 Clear NoteRepeats.14 If VelFlags.14 = 0 Then Set TimVals[14] Case note15 Clear NoteRepeats.15 If VelFlags.15 = 0 Then Set TimVals[15] Case Else Set noteAan 'not a note for this board GoTo Check_Timers 'no resort required EndSelect ' here we need resort! Set noteAan GoTo resort EndIf ' If CC66 > 0 Then ' so power ON must have been set! ' Select noteAan & CC66 ' when CC66 is ON, it has all bits set. Select noteAan Case note0 'hier moeten we een pulse timer starten, duur evenredig met velo 'na afloop van die puls moet de spoel uitgeschakeld worden ' daarna moet een wachttimer ingeschakeld worden 'voor zover een repeat speed is ingesteld met keypressure ' na de wachttijd moet een nieuwe puls gegeven worden met de bestaande velo ' note off schakelt de repeats uiteraard uit. ' Om te snelle noten en plakkende spoelen te vermijden, zouden we ook kunnen ' testen op Velflags.0 = 0 en indien niet 0, weigeren de noot te spelen... Set Bel44 ' stroom aan velo0 = Vels[velo] ' word var's Set VelFlags.0 TimVals[0] = time + velo0 ' Practical fail-safe coding: If Pres0 > 0 Then Set NoteRepeats.0 MinVal = velo0 << 1 ' add velo time If Dur[Pres0] <= MinVal Then Rate0 = MinVal Else Rate0 = Dur[Pres0] - MinVal EndIf Else Clear NoteRepeats.0 EndIf Case note1 Set Bel45 velo1 = Vels[velo] Set VelFlags.1 TimVals[1] = time + velo1 If Pres1 > 0 Then Set NoteRepeats.1 MinVal = velo1 << 1 ' add velo time * 2 If Dur[Pres1] <= MinVal Then Rate1 = MinVal Else Rate1 = Dur[Pres1] - MinVal EndIf Else Clear NoteRepeats.1 EndIf Case note2 Set Bel46 velo2 = Vels[velo] Set VelFlags.2 TimVals[2] = time + velo2 If Pres2 > 0 Then Set NoteRepeats.2 MinVal = velo2 << 1 If Dur[Pres2] <= MinVal Then Rate2 = MinVal Else Rate2 = Dur[Pres2] - MinVal EndIf Else Clear NoteRepeats.2 EndIf Case note3 Set Bel47 velo3 = Vels[velo] Set VelFlags.3 TimVals[3] = time + velo3 If Pres3 > 0 Then Set NoteRepeats.3 MinVal = velo3 << 1 If Dur[Pres3] <= MinVal Then Rate3 = MinVal Else Rate3 = Dur[Pres3] - MinVal EndIf Else Clear NoteRepeats.3 EndIf Case note4 Set Bel48 velo4 = Vels[velo] Set VelFlags.4 TimVals[4] = time + velo4 If Pres4 > 0 Then Set NoteRepeats.4 MinVal = velo4 << 1 If Dur[Pres4] <= MinVal Then Rate4 = MinVal Else Rate4 = Dur[Pres4] - MinVal EndIf Else Clear NoteRepeats.4 EndIf Case note5 Set Bel49 velo5 = Vels[velo] Set VelFlags.5 TimVals[5] = time + velo5 If Pres5 > 0 Then Set NoteRepeats.5 MinVal = velo5 << 1 If Dur[Pres5] <= MinVal Then Rate5 = MinVal Else Rate5 = Dur[Pres5] - MinVal EndIf Else Clear NoteRepeats.5 EndIf Case note6 Set Bel50 velo6 = Vels[velo] Set VelFlags.6 TimVals[6] = time + velo6 If Pres6 > 0 Then Set NoteRepeats.6 MinVal = velo6 << 1 If Dur[Pres6] <= MinVal Then Rate6 = MinVal Else Rate6 = Dur[Pres6] - MinVal EndIf Else Clear NoteRepeats.6 EndIf Case note7 Set Bel51 velo7 = Vels[velo] Set VelFlags.7 TimVals[7] = time + velo7 If Pres7 > 0 Then Set NoteRepeats.7 MinVal = velo7 << 1 If Dur[Pres7] <= MinVal Then Rate7 = MinVal Else Rate7 = Dur[Pres7] - MinVal EndIf Else Clear NoteRepeats.7 EndIf Case note8 Set Bel52 velo8 = Vels[velo] Set VelFlags.8 TimVals[8] = time + velo8 If Pres8 > 0 Then Set NoteRepeats.8 MinVal = velo8 << 1 If Dur[Pres8] <= MinVal Then Rate8 = MinVal Else Rate8 = Dur[Pres8] - MinVal EndIf Else Clear NoteRepeats.8 EndIf Case note9 Set Bel53 velo9 = Vels[velo] Set VelFlags.9 TimVals[9] = time + velo9 If Pres9 > 0 Then Set NoteRepeats.9 MinVal = velo9 << 1 If Dur[Pres9] <= MinVal Then Rate9 = MinVal Else Rate9 = Dur[Pres9] - MinVal EndIf Else Clear NoteRepeats.9 EndIf Case note10 Set Bel54 velo10 = Vels[velo] Set VelFlags.10 TimVals[10] = time + velo10 If Pres10 > 0 Then Set NoteRepeats.10 MinVal = velo10 << 1 If Dur[Pres10] <= MinVal Then Rate10 = MinVal Else Rate10 = Dur[Pres10] - MinVal EndIf Else Clear NoteRepeats.10 EndIf Case note11 Set Bel55 velo11 = Vels[velo] Set VelFlags.11 TimVals[11] = time + velo11 If Pres11 > 0 Then Set NoteRepeats.11 MinVal = velo11 << 1 If Dur[Pres11] <= MinVal Then Rate11 = MinVal Else Rate11 = Dur[Pres11] - MinVal EndIf Else Clear NoteRepeats.11 EndIf Case note12 Set Bel56 velo12 = Vels[velo] Set VelFlags.12 TimVals[12] = time + velo12 If Pres12 > 0 Then Set NoteRepeats.12 MinVal = velo12 << 1 If Dur[Pres12] <= MinVal Then Rate12 = MinVal Else Rate12 = Dur[Pres12] - MinVal EndIf Else Clear NoteRepeats.12 EndIf Case note13 Set Bel57 velo13 = Vels[velo] Set VelFlags.13 TimVals[13] = time + velo13 If Pres13 > 0 Then Set NoteRepeats.13 MinVal = velo13 << 1 If Dur[Pres13] <= MinVal Then Rate13 = MinVal Else Rate13 = Dur[Pres13] - MinVal EndIf Else Clear NoteRepeats.13 EndIf Case note14 Set Bel58 velo14 = Vels[velo] Set VelFlags.14 TimVals[14] = time + velo14 If Pres14 > 0 Then Set NoteRepeats.14 MinVal = velo14 << 1 If Dur[Pres14] <= MinVal Then Rate14 = MinVal Else Rate14 = Dur[Pres14] - MinVal EndIf Else Clear NoteRepeats.14 EndIf Case note15 Set Bel59 velo15 = Vels[velo] Set VelFlags.15 TimVals[15] = time + velo15 If Pres15 > 0 Then Set NoteRepeats.15 MinVal = velo15 << 1 If Dur[Pres15] <= MinVal Then Rate15 = MinVal Else Rate15 = Dur[Pres15] - MinVal EndIf Else Clear NoteRepeats.15 EndIf Case Else Set noteAan GoTo Check_Timers ' no re-sorting required EndSelect ' Else ' power is OFF ' Set noteAan ' GoTo Check_Timers ' no re-sort ' EndIf ' CC66 endif Set noteAan GoTo resort EndIf Case Keypres_Status 'used for note repetition speed modulation If notePres = 255 Then notePres = Bytein GoTo Check_Timers Else Select notePres Case note0 Pres0 = Bytein If Pres0 > 0 Then Rate0 = Dur[Pres0] ' note that we set NoteRepeats.0 only on reception of a note-on ' this way we can program the repeats prior to playing. Else Clear Rate0 Clear NoteRepeats.0 EndIf Case note1 Pres1 = Bytein If Pres1 > 0 Then Rate1 = Dur[Pres1] Else Clear Rate1 Clear NoteRepeats.1 EndIf Case note2 Pres2 = Bytein If Pres2 > 0 Then Rate2 = Dur[Pres2] Else Clear Rate2 Clear NoteRepeats.2 EndIf Case note3 Pres3 = Bytein If Pres3 > 0 Then Rate3 = Dur[Pres3] Else Clear Rate3 Clear NoteRepeats.3 EndIf Case note4 Pres4 = Bytein If Pres4 > 0 Then Rate4 = Dur[Pres4] Else Clear Rate4 Clear NoteRepeats.4 EndIf Case note5 Pres5 = Bytein If Pres5 > 0 Then Rate5 = Dur[Pres5] Else Clear Rate5 Clear NoteRepeats.5 EndIf Case note6 Pres6 = Bytein If Pres6 > 0 Then Rate6 = Dur[Pres6] Else Clear Rate6 Clear NoteRepeats.6 EndIf Case note7 Pres7 = Bytein If Pres7 > 0 Then Rate7 = Dur[Pres7] Else Clear Rate7 Clear NoteRepeats.7 EndIf Case note8 Pres8 = Bytein If Pres8 > 0 Then Rate8 = Dur[Pres8] Else Clear Rate8 Clear NoteRepeats.8 EndIf Case note9 Pres9 = Bytein If Pres9 > 0 Then Rate9 = Dur[Pres9] Else Clear Rate9 Clear NoteRepeats.9 EndIf Case note10 Pres10 = Bytein If Pres10 > 0 Then Rate10 = Dur[Pres10] Else Clear Rate10 Clear NoteRepeats.10 EndIf Case note11 Pres11 = Bytein If Pres11 > 0 Then Rate11 = Dur[Pres11] Else Clear Rate11 Clear NoteRepeats.11 EndIf Case note12 Pres12 = Bytein If Pres12 > 0 Then Rate12 = Dur[Pres12] Else Clear Rate12 Clear NoteRepeats.12 EndIf Case note13 Pres13 = Bytein If Pres13 > 0 Then Rate13 = Dur[Pres13] Else Clear Rate13 Clear NoteRepeats.13 EndIf Case note14 Pres14 = Bytein If Pres14 > 0 Then Rate14 = Dur[Pres14] Else Clear Rate14 Clear NoteRepeats.14 EndIf Case note15 Pres15 = Bytein If Pres15 > 0 Then Rate15 = Dur[Pres15] Else Clear Rate15 Clear NoteRepeats.15 EndIf 'Case Else ' Set notePres ' GoTo Check_Timers ' no sort required EndSelect Set notePres '= 255 GoTo Check_Timers ' no sort required EndIf Case Control_Status 'this is where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein GoTo Check_Timers ' no sort required Else value = Bytein GoSub Controller ' this will be followed with a re-sort EndIf EndSelect EndIf resort: GoSub SortTimers ' so we resort only if an incoming midi command changed something Check_Timers: ' here we check the Task counters and compare them with the 32 bit cnt value ' copied in time using the TimVals[] dword variables: If idx < 16 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 Select idx Case 0 If NoteRepeats.0 = 0 Then ' in this case we no not have note repeats Clear Bel44 ' velo pulse ended Clear VelFlags.0 Set TimVals[0] Else ' in this case note repeats are active... If VelFlags.0 = 1 Then Clear Bel44 TimVals[0] = time + Rate0 ' repeat action, set a wait time Clear VelFlags.0 Else ' in this case the wait time is over, so we start again Set Bel44 TimVals[0] = time + velo0 Set VelFlags.0 EndIf EndIf Case 1 If NoteRepeats.1 = 0 Then ' in this case we no not have note repeats Clear Bel45 ' velo pulse ended Clear VelFlags.1 Set TimVals[1] Else ' in this case note repeats are active... If VelFlags.1 = 1 Then Clear Bel45 TimVals[1] = time + Rate1 ' repeat action, set a wait time Clear VelFlags.1 Else ' in this case the wait time is over, so we start again Set Bel45 TimVals[1] = time + velo1 Set VelFlags.1 EndIf EndIf Case 2 If NoteRepeats.2 = 0 Then ' in this case we no not have note repeats Clear Bel46 ' velo pulse ended Clear VelFlags.2 Set TimVals[2] Else ' in this case note repeats are active... If VelFlags.2 = 1 Then Clear Bel46 TimVals[2] = time + Rate2 ' repeat action, set a wait time Clear VelFlags.2 Else ' in this case the wait time is over, so we start again Set Bel46 TimVals[2] = time + velo2 Set VelFlags.2 EndIf EndIf Case 3 If NoteRepeats.3 = 0 Then ' in this case we no not have note repeats Clear Bel47 ' velo pulse ended Clear VelFlags.3 Set TimVals[3] Else ' in this case note repeats are active... If VelFlags.3 = 1 Then Clear Bel47 TimVals[3] = time + Rate3 ' repeat action, set a wait time Clear VelFlags.3 Else ' in this case the wait time is over, so we start again Set Bel47 TimVals[3] = time + velo3 Set VelFlags.3 EndIf EndIf Case 4 If NoteRepeats.4 = 0 Then ' in this case we no not have note repeats Clear Bel48 ' velo pulse ended Clear VelFlags.4 Set TimVals[4] Else ' in this case note repeats are active... If VelFlags.4 = 1 Then Clear Bel48 TimVals[4] = time + Rate4 ' repeat action, set a wait time Clear VelFlags.4 Else ' in this case the wait time is over, so we start again Set Bel48 TimVals[4] = time + velo4 Set VelFlags.4 EndIf EndIf Case 5 If NoteRepeats.5 = 0 Then ' in this case we no not have note repeats Clear Bel49 ' velo pulse ended Clear VelFlags.5 Set TimVals[5] Else ' in this case note repeats are active... If VelFlags.5 = 1 Then Clear Bel49 TimVals[5] = time + Rate5 ' repeat action, set a wait time Clear VelFlags.5 Else ' in this case the wait time is over, so we start again Set Bel49 TimVals[5] = time + velo5 Set VelFlags.5 EndIf EndIf Case 6 If NoteRepeats.6 = 0 Then ' in this case we no not have note repeats Clear Bel50 ' velo pulse ended Clear VelFlags.6 Set TimVals[6] Else ' in this case note repeats are active... If VelFlags.6 = 1 Then Clear Bel50 TimVals[6] = time + Rate6 ' repeat action, set a wait time Clear VelFlags.6 Else ' in this case the wait time is over, so we start again Set Bel50 TimVals[6] = time + velo6 Set VelFlags.6 EndIf EndIf Case 7 If NoteRepeats.7 = 0 Then ' in this case we no not have note repeats Clear Bel51 ' velo pulse ended Clear VelFlags.7 Set TimVals[7] Else ' in this case note repeats are active... If VelFlags.7 = 1 Then Clear Bel51 TimVals[7] = time + Rate7 ' repeat action, set a wait time Clear VelFlags.7 Else ' in this case the wait time is over, so we start again Set Bel51 TimVals[7] = time + velo7 Set VelFlags.7 EndIf EndIf Case 8 If NoteRepeats.8 = 0 Then ' in this case we no not have note repeats Clear Bel52 ' velo pulse ended Clear VelFlags.8 Set TimVals[8] Else ' in this case note repeats are active... If VelFlags.8 = 1 Then Clear Bel52 TimVals[8] = time + Rate8 ' repeat action, set a wait time Clear VelFlags.8 Else ' in this case the wait time is over, so we start again Set Bel52 TimVals[8] = time + velo8 Set VelFlags.8 EndIf EndIf Case 9 If NoteRepeats.9 = 0 Then ' in this case we no not have note repeats Clear Bel53 ' velo pulse ended Clear VelFlags.9 Set TimVals[9] Else ' in this case note repeats are active... If VelFlags.9 = 1 Then Clear Bel53 TimVals[9] = time + Rate9 ' repeat action, set a wait time Clear VelFlags.9 Else ' in this case the wait time is over, so we start again Set Bel53 TimVals[9] = time + velo9 Set VelFlags.9 EndIf EndIf Case 10 If NoteRepeats.10 = 0 Then ' in this case we no not have note repeats Clear Bel54 ' velo pulse ended Clear VelFlags.10 Set TimVals[10] Else ' in this case note repeats are active... If VelFlags.10 = 1 Then Clear Bel54 TimVals[10] = time + Rate10 ' repeat action, set a wait time Clear VelFlags.10 Else ' in this case the wait time is over, so we start again Set Bel54 TimVals[10] = time + velo10 Set VelFlags.10 EndIf EndIf Case 11 If NoteRepeats.11 = 0 Then ' in this case we no not have note repeats Clear Bel55 ' velo pulse ended Clear VelFlags.11 Set TimVals[11] Else ' in this case note repeats are active... If VelFlags.11 = 1 Then Clear Bel55 TimVals[11] = time + Rate11 ' repeat action, set a wait time Clear VelFlags.11 Else ' in this case the wait time is over, so we start again Set Bel55 TimVals[11] = time + velo11 Set VelFlags.11 EndIf EndIf Case 12 If NoteRepeats.12 = 0 Then ' in this case we no not have note repeats Clear Bel56 ' velo pulse ended Clear VelFlags.12 Set TimVals[12] Else ' in this case note repeats are active... If VelFlags.12 = 1 Then Clear Bel56 TimVals[12] = time + Rate12 ' repeat action, set a wait time Clear VelFlags.12 Else ' in this case the wait time is over, so we start again Set Bel56 TimVals[12] = time + velo12 Set VelFlags.12 EndIf EndIf Case 13 If NoteRepeats.13 = 0 Then ' in this case we no not have note repeats Clear Bel57 ' velo pulse ended Clear VelFlags.13 Set TimVals[13] Else ' in this case note repeats are active... If VelFlags.13 = 1 Then Clear Bel57 TimVals[13] = time + Rate13 ' repeat action, set a wait time Clear VelFlags.13 Else ' in this case the wait time is over, so we start again Set Bel57 TimVals[13] = time + velo13 Set VelFlags.13 EndIf EndIf Case 14 If NoteRepeats.14 = 0 Then ' in this case we no not have note repeats Clear Bel58 ' velo pulse ended Clear VelFlags.14 Set TimVals[14] Else ' in this case note repeats are active... If VelFlags.14 = 1 Then Clear Bel58 TimVals[14] = time + Rate14 ' repeat action, set a wait time Clear VelFlags.14 Else ' in this case the wait time is over, so we start again Set Bel58 TimVals[14] = time + velo14 Set VelFlags.14 EndIf EndIf Case 15 If NoteRepeats.15 = 0 Then ' in this case we no not have note repeats Clear Bel59 ' velo pulse ended Clear VelFlags.15 Set TimVals[15] Else ' in this case note repeats are active... If VelFlags.15 = 1 Then Clear Bel59 TimVals[15] = time + Rate15 ' repeat action, set a wait time Clear VelFlags.15 Else ' in this case the wait time is over, so we start again Set Bel59 TimVals[15] = time + velo15 Set VelFlags.15 EndIf EndIf 'Case Else ' ' in dit geval is idx geset ' GoTo jumpout End Select GoSub SortTimers ' find a new nxt and idx 'If time >= Nxt Then GoTo Check_Timers ' this causes an endless loop! ' we could try this ' if idx < 11 then ' if time >= Nxt then goto Check_Timers ' endif EndIf ' beveiliging tegen overflow crashes... If maxtim = 1 Then Clear time Clear VelFlags Clear NoteRepeats Set TimVals Clear Bel44 Clear Bel45 Clear Bel46 Clear Bel47 Clear Bel48 Clear Bel49 Clear Bel50 Clear Bel51 Clear Bel52 Clear Bel53 Clear Bel54 Clear Bel55 Clear Bel56 Clear Bel57 Clear Bel58 Clear Bel59 EndIf Else ' idx > 11, no timers running, so to avoid overflows, we can reset the timer If maxtim = 1 Then Clear time ' 16.06.2015 EndIf Btg PORTC.3 ' average loop-speed: 5.9 microseconds. (Tektronix measurement) GoTo Loop ' end of the main loop SortTimers: 'look up the next smallest timer value in the Timvals array ' zoek de de volgende kleinste timer waarde: Set idx ' makes it 255 Set Nxt.31 ' nxt is set on entry. - for speed, we just set the highest bit For i = 0 To 15 If TimVals[i] < Nxt Then Nxt = TimVals[i] ' 8 dword comparisons idx = i EndIf Next i ' dit volstaat als bescherming tegen timer overflows: ' If idx > 11 Then ' indien geen enkele timer geladen is... ' Clear time.Byte3 ' reset ' EndIf Return 'ProgChange: ' Set prog '= 255 'this is not realy required 'Return 'Pitchbend: ' 'only implemented on dsPIC based robots ' Set pblsb '= 255 'Return 'Aftertouch: ' 'this is the channel aftertouch, affecting all notes ' Set aft '= 255 'not mandatory 'Return Controller: Select Ctrl Case 30 ' controller to set all repeat frequencies to one and the same value at once If value = 0 Then Clear NoteRepeats Clear Rate0 Clear Pres0 Clear Rate1 Clear Pres1 Clear Rate2 Clear Pres2 Clear Rate3 Clear Pres3 Clear Rate4 Clear Pres4 Clear Rate5 Clear Pres5 Clear Rate6 Clear Pres6 Clear Rate7 Clear Pres7 Clear Rate8 Clear Pres8 Clear Rate9 Clear Pres9 Clear Rate10 Clear Pres10 Clear Rate11 Clear Pres11 Clear Rate12 Clear Pres12 Clear Rate13 Clear Pres13 Clear Rate14 Clear Pres14 Clear Rate15 Clear Pres15 Else Pres0 = value Rate0 = Dur[value] Pres1 = value Rate1 = Dur[value] Pres2 = value Rate2 = Dur[value] Pres3 = value Rate3 = Dur[value] Pres4 = value Rate4 = Dur[value] Pres5 = value Rate5 = Dur[value] Pres6 = value Rate6 = Dur[value] Pres7 = value Rate7 = Dur[value] Pres8 = value Rate8 = Dur[value] Pres9 = value Rate9 = Dur[value] Pres10 = value Rate10 = Dur[value] Pres11 = value Rate11 = Dur[value] Pres12 = value Rate12 = Dur[value] Pres13 = value Rate13 = Dur[value] Pres14 = value Rate14 = Dur[value] Pres15 = value Rate15= Dur[value] EndIf Case 66 'on/off for the robot If value = 0 Then Clear VelFlags 'stop all running timers Clear NoteRepeats Set TimVals Set idx Clear Bel44 Clear Bel45 Clear Bel46 Clear Bel47 Clear Bel48 Clear Bel49 Clear Bel50 Clear Bel51 Clear Bel52 Clear Bel53 Clear Bel54 Clear Bel55 Clear Bel56 Clear Bel57 Clear Bel58 Clear Bel59 ' reset all repetition rates: Clear Rate0 Clear Rate1 Clear Rate2 Clear Rate3 Clear Rate4 Clear Rate5 Clear Rate6 Clear Rate7 Clear Rate8 Clear Rate9 Clear Rate10 Clear Rate11 Clear Rate12 Clear Rate13 Clear Rate14 Clear Rate15 Clear Pres0 Clear Pres1 Clear Pres2 Clear Pres3 Clear Pres4 Clear Pres5 Clear Pres6 Clear Pres7 Clear Pres8 Clear Pres9 Clear Pres10 Clear Pres11 Clear Pres12 Clear Pres13 Clear Pres14 Clear Pres15 Clear CC66 Clear CC30 Clear time Else Set CC66 '= value EndIf Case 123 Clear VelFlags Clear NoteRepeats Clear Bel44 Clear Bel45 Clear Bel46 Clear Bel47 Clear Bel48 Clear Bel49 Clear Bel50 Clear Bel51 Clear Bel52 Clear Bel53 Clear Bel54 Clear Bel55 Clear Bel56 Clear Bel57 Clear Bel58 Clear Bel59 Clear time Set TimVals Set idx EndSelect Set Ctrl '= 255 mandatory reset Return Dur_Lookup: ' range for Llor: 65535 to 6553 - same values as for PIC1 ' linear increment = 464 For i = 0 To 127 Dur[i] = 65535 - (464 * i) Next i Return Vels_Lookup: ' the range we had on PIC version 1.0 (2004) for PIC1 was 1ms to 75ms ' so, for compatibility we should do: 'For i = 1 to 127 ' Vels[i] = 76 + (52.83 * (i-1)) 'next i ' now we should have a range from 2 ms up to 75ms ' FOR PIC1 CODING: ' measurements performed 16.10.2017 lead to following scaling: 'Vels[0] = 500 'For i = 1 To 127 ' Vels[i] = 1660 + (40 * (i-1)) 'Next i ' measurement: Vels[1] = 18 ms ' Vels[127] = 75 ms ' 1 unit = 590 us ' measured with Tektronix TDS2024C, 16.10.2017 ' FOR PIC2 CODING: (first test scaling): Vels[0] = 64 For i = 1 To 127 Vels[i] = 76 + (52.83 * (i-1)) Next i Return '[EOF]