julienvoirin Posted June 12, 2011 Report Share Posted June 12, 2011 (edited) Dear Guys I've enhanced a controller for Oberheim Matrix 1000/6 developped by Jackchaos I'd like to code a rather basic arpeggiator as those used in Juno 60. As of today, I've "stolen" some code blocks from jambonbill and done an arpeggio : it is automatically synced to external beatclock (if received), UP and DOWN patterns work, speed can be controlled. I'd like to implement : UPDOWN patterns, and random gate time, hold button. Here are some relevant extracts of the complete project : device .c (routing midi received notes) //////////////////////////////////////////////////////////////////////////// // Router with arpegiator /////////////////////////////////////////////////////////////////////////// void Router_Arp(unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) { // check if note on or off event at channel 1 has been received if( evnt0 == 0x80 || evnt0 == 0x90 ) { // if note off event: force evnt2 to 0 for easier handling of 'LED off' if(evnt0 == 0x80) evnt2 = 0; } if(router_arp_tag == 1){ // arp is activated MIOS_MIDI_InterfaceSet(router_device); // set IIC interface switch( evnt0 & 0xf0 ) { case 0x80: // Note-Off: 3 bytes rem_arp(evnt0, evnt1, evnt2);//REMOVE NOTE FROM ARP BUFFER return; break; case 0x90: // Note-On: 3 bytes add_arp(evnt0, evnt1, evnt2); // ADD NOTE FROM ARP BUFFER return; break; case 0xa0: // Aftertouch: 3 bytes case 0xb0: // CC: 3 bytes case 0xe0: // Pitchbend: 3 bytes MIOS_MIDI_TxBufferPut(evnt0); MIOS_MIDI_TxBufferPut(evnt1); MIOS_MIDI_TxBufferPut(evnt2); break; //case 0xc0: // Program Change: 2 bytes :: ignore program change for the moment, will be managed by Chaosmatrix case 0xd0: // Poly Aftertouch: 2 bytes MIOS_MIDI_TxBufferPut(evnt0); MIOS_MIDI_TxBufferPut(evnt1); break; default: // note: status messages must be handled within MPROC_NotifyReceivedByte() break; } MIOS_MIDI_InterfaceAutoSet(); // switch back to default interface : is in rem_arp MIDI_Incoming = 1; } if(router_arp_tag == 0){ // arp is desactivated switch( evnt0 & 0xf0 ) { case 0x80: // Note-Off: 3 bytes case 0x90: // Note-On: 3 bytes MIOS_MIDI_InterfaceSet(router_device); // set IIC interface MIOS_MIDI_TxBufferPut(evnt0); MIOS_MIDI_TxBufferPut(evnt1); MIOS_MIDI_TxBufferPut(evnt2); MIOS_MIDI_InterfaceAutoSet(); // switch back to default interface MIDI_Incoming = 1; break; case 0xa0: // Aftertouch: 3 bytes case 0xb0: // CC: 3 bytes case 0xe0: // Pitchbend: 3 bytes MIOS_MIDI_InterfaceSet(router_device); // set IIC interface MIOS_MIDI_TxBufferPut(evnt0); MIOS_MIDI_TxBufferPut(evnt1); MIOS_MIDI_TxBufferPut(evnt2); MIOS_MIDI_InterfaceAutoSet(); // switch back to default interface MIDI_Incoming = 1; break; //case 0xc0: // Program Change: 2 bytes :: ignore program change for the moment, will be managed by Chaosmatrix case 0xd0: // Poly Aftertouch: 2 bytes MIOS_MIDI_InterfaceSet(router_device); // set IIC interface MIOS_MIDI_TxBufferPut(evnt0); MIOS_MIDI_TxBufferPut(evnt1); MIOS_MIDI_InterfaceAutoSet(); // switch back to default interface MIDI_Incoming = 1; break; default: // note: status messages must be handled within MPROC_NotifyReceivedByte() break; } } // notify display handler in DISPLAY_Tick() that DOUT value has changed last_dout_pin = evnt1; } arp.c (UP & DOWN algo) #include <cmios.h> #include "main.h" #include "arp.h" #include "mclock.h" #include "midi.h" #include "din.h" unsigned char arp_speed; unsigned char arp_motif; unsigned char arp_buffer[32]; // Arp buffer ( 32 should be enough ;) ) unsigned char arp_step; // Arp position (pointer) unsigned char arp_n; // Arp length unsigned char arp_tik; // Seq position (ppqn) unsigned char last_arp; // Last played note unsigned char add_arp_velocity; // for ARP() et add_arp functions unsigned char last_played_note; unsigned char new_note; unsigned char router_arp_tag; // tag which indicates if arp if active while routing midi events ////////////////////////////////////////////////////////////////////////////////////// /// This function activate the arpegiator ////////////////////////////////////////////////////////////////////////////////////// void Active_Arp(unsigned char state) { switch (state){ case active_arp_on: MCLOCK_DoPlay(); router_arp_tag = 1; break; case active_arp_off: MCLOCK_DoStop(); router_arp_tag = 0; break; } } ////////////////////////////////////////////////////////////////////////////////////////// // Arp function // THIS FUNCTION IS CALLED PERIODICALY BY THE CLOCKBOX /////////////////////////////////////////////////////////////////////////////////////////////// void ARP() { if(arp_n == 0){ // here we could stop the clockbox ;), and restart synced it when buffer != empty return; // ARP BUFFER IS EMPTY } arp_tik++; if(arp_tik >= arp_speed){ // arp speeds : 1/3/6/12/24) :: unsigned char arp_speed is arp_sync_tempo in ui_arp.c arp_tik = 0; //reset // KILL LAST PLAYED NOTE if(last_arp < 0x80){ MIOS_MIDI_TxBufferPut(last_played_note); // Note on MIOS_MIDI_TxBufferPut(last_arp); MIOS_MIDI_TxBufferPut(0x00); // velocity null (note off) } // PLAY NEW NOTE MIOS_MIDI_TxBufferPut(new_note); // Note on MIOS_MIDI_TxBufferPut(arp_buffer[arp_step]); //MIOS_MIDI_TxBufferPut(80); // VELOCITY MAX MIOS_MIDI_TxBufferPut(add_arp_velocity); last_arp = arp_buffer[arp_step]; // SAVE LAST PLAYED NOTE arp_step++; // PUSH ARP POINTER if(arp_step >= arp_n) arp_step = 0; // LOOP ARP POINTER } return; } ///////////////////////////////////////////////////////////////////////////////////// // ADD NOTE TO ARPEGGIO ///////////////////////////////////////////////////////////////////////////////////// void add_arp(unsigned char evnt0, unsigned char note, unsigned char evnt2) { unsigned char i; unsigned char swap; new_note = evnt0; // 1st define the velocity of the note in the arp add_arp_velocity = evnt2; // I SORT NOTES TO GET INTERESTING RESULTS // switch(arp_motif){ if (arp_motif>3) arp_motif = 0; case 0: // up pattern : tested :) for(i=0; i<arp_n; i++){ if(note < arp_buffer[i]){ // swap swap = arp_buffer[i]; arp_buffer[i] = note; note = swap; } } break; case 1: // down pattern : tested :) for(i=0; i<arp_n; i++){ if(note > arp_buffer[i]){ // swap swap = arp_buffer[i]; arp_buffer[i] = note; note = swap; } } break; case 2: // play order : tested :) for(i=0; i<arp_n; i++){ if(note < arp_buffer[i]){ // swap swap = arp_buffer[i-1]; // a->b arp_buffer[i-1] = note; // b->c note = swap; // c->a } } break; default: break; } /* // updown pattern : to test if(note > arp_buffer[i]){ // swap swap = arp_buffer[i]; arp_buffer[i] = note; note = swap; } if(note < arp_buffer[i]){ // swap swap = arp_buffer[i-1]; arp_buffer[i-1] = note; note = swap; } } */ arp_buffer[arp_n] = note; arp_n++; // INCREMENT ARP NOTE NUMBER COUNTER #if DEBUG MIOS_LCD_CursorSet(0xc0 + LCD_Offset); MIOS_LCD_PrintCString("ARP_LEN:"); MIOS_LCD_PrintBCD2(arp_n); // SHOW ARP LENGTH #endif return; } ///////////////////////////////////////////////////////////////////////////////////// //REMove NOTE FROM ARPEGGIO ///////////////////////////////////////////////////////////////////////////////////// void rem_arp(unsigned char evnt0, unsigned char note, unsigned char evnt2) { unsigned char i; last_played_note = evnt0; for(i=0; i < arp_n; i++) { if(arp_buffer[i] >= note) arp_buffer[i] = arp_buffer[i+1]; // unstack (swap) } arp_n--; // DECREMENT ARP NOTE NUMBER COUNTER if(arp_n == 0){ // Kill last played note MIOS_MIDI_TxBufferPut(evnt0); // Note MIOS_MIDI_TxBufferPut(last_arp); // Note Number MIOS_MIDI_TxBufferPut(0x00); // Velocity null last_arp = 0x80; // null note } #if DEBUG MIOS_LCD_CursorSet(0xc0 + LCD_Offset); MIOS_LCD_PrintCString("ARP_LEN:"); MIOS_LCD_PrintBCD2(arp_n);//ARP LENGTH #endif return; } ui_arp.c (buttons definition of the control surface) unsigned char enc_value; // general purpose value for test unsigned char arp_on; // launch clockbox (PLAY/STOP) unsigned char arp_mode; // up, down, up-down1, updown2, play_order, random, chord unsigned char arp_sync_tempo; // 1/32 ... 1 bar unsigned char arp_gate; // gate time unsigned char arp_pattern; // defined pattern unsigned char arp_keysync; // on/off unsigned char arp_oct; // 1,2,3, octaves ///////////////////////////////////////////////////////////////////////////// // Display Arp pages, // page 1 // page 2 // page 3 ///////////////////////////////////////////////////////////////////////////// void UI_Display_Arp() { switch(SoftPanel.Page) { case SOFT_PAGE1: /* ARP PAGE 1 [] [] [ ] [] [] 01234567890123456789 BPM Arpeg Speed ___ On Off d 1/8 u */ MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_PATCH].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_EDIT].dout_pin); // off MIOS_DOUT_PinSet1(DIN_ConfigMap[DIN_ARP].dout_pin); // ON MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_OSCILLATORS].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_FILTER].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_ENVELOPES].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_KEYBOARD].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_MATRIX].dout_pin); // off MIOS_DOUT_PinSet1(DIN_ConfigMap[DIN_PAGE].dout_pin); // on MIOS_LCD_Clear(); //1st line MIOS_LCD_CursorSet(0 + LCD_Offset); MIOS_LCD_PrintCString("BPM Arpeg Speed "); MIOS_LCD_CursorSet(64 + LCD_Offset); if (external_clk_received == 1){ MIOS_LCD_PrintCString("EXT"); // print EXT for BPM }else{ MIOS_LCD_PrintBCD3(bpm); } MIOS_LCD_CursorSet(69 + LCD_Offset); MIOS_LCD_PrintCString("On Off"); MIOS_LCD_CursorSet(64+14 + LCD_Offset); MIOS_LCD_PrintBCD2(arp_speed); MIOS_LCD_CursorSet(64+13 + LCD_Offset); MIOS_LCD_PrintChar(CHAR_DOWN); MIOS_LCD_CursorSet(64+19 + LCD_Offset); MIOS_LCD_PrintChar(CHAR_UP); break; case SOFT_PAGE2: /* ARP PAGE 2 [] [] [ ] [] [] 01234567890123456789 BPM Oct - Motif ___ On Off d Up u */ MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_PATCH].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_EDIT].dout_pin); // off MIOS_DOUT_PinSet1(DIN_ConfigMap[DIN_ARP].dout_pin); // ON MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_OSCILLATORS].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_FILTER].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_ENVELOPES].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_KEYBOARD].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_MATRIX].dout_pin); // off MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_PAGE].dout_pin); // off MIOS_LCD_Clear(); //1st line MIOS_LCD_CursorSet(0 + LCD_Offset); MIOS_LCD_PrintCString("BPM Oct - Motif "); MIOS_LCD_CursorSet(64 + LCD_Offset); if (external_clk_received == 1){ MIOS_LCD_PrintCString("EXT"); // print EXT for BPM }else{ MIOS_LCD_PrintBCD3(bpm); } //MIOS_LCD_PrintBCD3(bpm); MIOS_LCD_CursorSet(69 + LCD_Offset); MIOS_LCD_PrintBCD1(arp_oct); MIOS_LCD_CursorSet(64+14 + LCD_Offset); MIOS_LCD_PrintBCD2(arp_motif); MIOS_LCD_CursorSet(64+13 + LCD_Offset); MIOS_LCD_PrintChar(CHAR_DOWN); MIOS_LCD_CursorSet(64+19 + LCD_Offset); MIOS_LCD_PrintChar(CHAR_UP); break; } } ///////////////////////////////////////////////////////////////////////////// // Configure Arp pages ///////////////////////////////////////////////////////////////////////////// void UI_Handle_Arp() { if(SoftPanel.Page == SOFT_PAGE1){ switch (SoftPanel.Button){ case DIN_PAGE: SoftPanel.Page = SOFT_PAGE2; break; case SOFT_EDIT_1: // // do stuff break; case SOFT_EDIT_2: // // do stuff Active_Arp(active_arp_on); break; case SOFT_EDIT_3: // // do stuff Active_Arp(active_arp_off); break; case SOFT_EDIT_4: // // do stuff arp_speed--; break; case SOFT_EDIT_5: // // do stuff arp_speed++; break; case SOFT_EDIT_INC: // increment // break; case SOFT_EDIT_DEC: // decrement // break; } // encoder : set BPM :: WORK :) if(bpm < 50) bpm = 50; if(bpm > 250) bpm = 250; bpm += SoftPanel.EncoderValue; bpm = (unsigned int)MCLOCK_BPMGet()-48; MCLOCK_BPMSet((unsigned char)bpm+48) ; // realtime display feature app_flags.Display_DIN_Req = 1; // MUST HAVE or BPM encoder value not realtime print } else { switch (SoftPanel.Button){ case DIN_PAGE: SoftPanel.Page = SOFT_PAGE1; break; case SOFT_EDIT_5: // increment arp_motif arp_motif++; if(arp_motif > 5) arp_motif = 5; break; case SOFT_EDIT_4: // decrement arp_motif arp_motif--; if(arp_motif > 5) arp_motif = 0; break; case SOFT_EDIT_INC: // increment // do stuff break; case SOFT_EDIT_DEC: // decrement // do stuff break; } // encoder : set BPM :: WORK :) if(bpm < 50) bpm = 50; if(bpm > 250) bpm = 250; bpm += SoftPanel.EncoderValue; bpm = (unsigned int)MCLOCK_BPMGet()-48; MCLOCK_BPMSet((unsigned char)bpm+48) ; // realtime display feature app_flags.Display_DIN_Req = 1; // MUST HAVE or BPM encoder value not realtime print } } mclock.c (the seq core) copied to TK basic clockbox ///////////////////////////////////////////////////////////////////////////// // Include files ///////////////////////////////////////////////////////////////////////////// #include <cmios.h> #include <pic18fregs.h> #include "main.h" #include "mclock.h" #include "device.h" #include "arp.h" #include "din.h" ///////////////////////////////////////////////////////////////////////////// // Global variables ///////////////////////////////////////////////////////////////////////////// mclock_state_t mclock_state; // the mclock state variable mclock_pin_state_t mclock_pin_state; // state of the clock output pin unsigned char mclock_tick_ctr; // requests MIDI clocks unsigned char external_clk_received; // external clock event tag unsigned char bpm; // holds the current BPM setting unsigned char mclock_ctr_24; // counts from 0..23 unsigned char mclock_ctr_beats; // counts the quarter notes 0..3 unsigned char mclock_ctr_measures; // counts the measures (up to 65535) ///////////////////////////////////////////////////////////////////////////// // Local variables ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // This function initializes the MIDI clock module ///////////////////////////////////////////////////////////////////////////// void MCLOCK_Init(void) { mclock_state.ALL = 0; mclock_pin_state.ALL = 0; mclock_tick_ctr = 0; MCLOCK_ResetMeter(); } ///////////////////////////////////////////////////////////////////////////// // This function should be called from USER_Tick to send MIDI clock and // MIDI Clock Start/Stop/Continue ///////////////////////////////////////////////////////////////////////////// void MCLOCK_Tick(void) { // start request? Send 0xfa and enter RUN mode if( mclock_state.START_REQ ) { mclock_state.START_REQ = 0; MIOS_MIDI_TxBufferPut(0xfa); mclock_state.RUN = 1; // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } // continue request? Send 0xfb and release pause if( mclock_state.CONT_REQ ) { mclock_state.CONT_REQ = 0; MIOS_MIDI_TxBufferPut(0xfb); mclock_state.PAUSE = 0; // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } // stop request? Send 0xfc and leave RUN mode if( mclock_state.STOP_REQ ) { mclock_state.STOP_REQ = 0; MIOS_MIDI_TxBufferPut(0xfc); mclock_state.RUN = 0; mclock_state.PAUSE = 0; mclock_tick_ctr = 0; // prevent that any requested 0xf8 will be sent // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } // send 0xf8 until counter is 0 while( mclock_tick_ctr ) { //-------> do not send midi clock to i2c to avoid problem with M1000 : it doesn't like too much data // /* MIOS_MIDI_InterfaceSet(0x00); // route F8 to default midi out MIOS_MIDI_TxBufferPut(0xf8); MIOS_MIDI_InterfaceSet(router_device); */ // decrementing the counter *MUST* be an atomic operation, otherwise it could // conflict with MCLOCK_Timer() // however, I guess that the compiler will generate a single decf instruction, // which is atomic... but better to write it in this way, who knows, how SDCC // will behave in future... INTCONbits.GIE = 0; // disable interrupts --mclock_tick_ctr; INTCONbits.GIE = 1; // enable interrupts // increment the meter counters ARP();//ARPEGGIATOR if( ++mclock_ctr_24 == 24 ) { mclock_ctr_24 = 0; if( ++mclock_ctr_beats == 4 ) { mclock_ctr_beats = 0; ++mclock_ctr_measures; } } // heartbeat tempo led : if(mclock_ctr_24 == 0){ MIOS_DOUT_PinSet1(DIN_ConfigMap[DIN_SHIFT].dout_pin); // HeartBeat }else{ MIOS_DOUT_PinSet0(DIN_ConfigMap[DIN_SHIFT].dout_pin); // indicates Off } // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } } ///////////////////////////////////////////////////////////////////////////// // This function should be called from USER_Timer to update the MIDI clock ///////////////////////////////////////////////////////////////////////////// void MCLOCK_Timer(void) { // just increment a clock counter if in RUN and not in PAUSE mode // it will be decremented in MCLOCK_Tick - each step sends one 0xf8 event // the advantage of this counter is, that a clock event can never get // lost, regardless how much the CPU is loaded // (however, in this application the CPU is very unbusy, but I prepared // this for even more complex programs...) if( mclock_state.RUN && !mclock_state.PAUSE) { ++mclock_tick_ctr; mclock_pin_state.CLK_REQ = 1; } } ///////////////////////////////////////////////////////////////////////////// // This internal function divides 3125000 / BPM // The formula: // -> delay = 60 / BPM * 24 // timer is clocked at 10 MHz, and we are using a 1:8 prescaler // -> timer cycles = ((60/BPM*24)/8) / 100E-9 // -> 3125000 / BPM // // the 24 Bit / 16 Bit division routine has been created by Nikolai Golovchenko, // and is published at: // http://www.piclist.org/techref/microchip/math/div/24by16.htm ///////////////////////////////////////////////////////////////////////////// unsigned char AARGB0; unsigned char AARGB1; unsigned char AARGB2; unsigned char BARGB0; unsigned char BARGB1; unsigned char LOOPCOUNT; unsigned char REMB0; unsigned char REMB1; unsigned int MCLOCK_GetTimerValue(unsigned char bpm) { //;Inputs: //; Dividend - AARGB0:AARGB1:AARGB2 (0 - most significant!) //; Divisor - BARGB0:BARGB1 //;Temporary: //; Counter - LOOPCOUNT //; Remainder- REMB0:REMB1 //;Output: //; Quotient - AARGB0:AARGB1:AARGB2 LOOPCOUNT = 24; AARGB0 = (unsigned char)(3125000L >> 16L); AARGB1 = (unsigned char)(3125000L >> 8L); AARGB2 = (unsigned char)(3125000L >> 0L); BARGB0 = 0; BARGB1 = bpm; REMB0 = 0; REMB0 = 1; __asm banksel _LOOPCOUNT; CLRF _REMB0 CLRF _REMB1 MOVLW 24 MOVWF _LOOPCOUNT LOOPU2416: RLCF _AARGB2, W ;shift dividend left to move next bit to remainder RLCF _AARGB1, F ; RLCF _AARGB0, F ; RLCF _REMB1, F ;shift carry (next dividend bit) into remainder RLCF _REMB0, F RLCF _AARGB2, F ;finish shifting the dividend and save carry in AARGB2.0, ;since remainder can be 17 bit long in some cases ;(e.g. 0x800000/0xFFFF). This bit will also serve ;as the next result bit. MOVF _BARGB1, W ;substract divisor from 16-bit remainder SUBWF _REMB1, F ; MOVF _BARGB0, W ; SKPC ; INCFSZ _BARGB0, W ; SUBWF _REMB0, F ; ;here we also need to take into account the 17th bit of remainder, which ;is in AARGB2.0. If we dont have a borrow after subtracting from lower ;16 bits of remainder, then there is no borrow regardless of 17th bit ;value. But, if we have the borrow, then that will depend on 17th bit ;value. If it is 1, then no final borrow will occur. If it is 0, borrow ;will occur. These values match the borrow flag polarity. SKPNC ;if no borrow after 16 bit subtraction BSF _AARGB2, 0 ;then there is no borrow in result. Overwrite ;AARGB2.0 with 1 to indicate no ;borrow. ;if borrow did occur, AARGB2.0 already ;holds the final borrow value (0-borrow, ;1-no borrow) BTFSC _AARGB2, 0 ;if no borrow after 17-bit subtraction BRA UOK46LL ;skip remainder restoration. ADDWF _REMB0, F ;restore higher byte of remainder. (w ;contains the value subtracted from it ;previously) MOVF _BARGB1, W ;restore lower byte of remainder ADDWF _REMB1, F ; UOK46LL: DECFSZ _LOOPCOUNT, f ;decrement counter BRA LOOPU2416 ;and repeat the loop if not zero. movff _AARGB1, _PRODL movf _AARGB2, W return __endasm; return 0; // dummy return } ///////////////////////////////////////////////////////////////////////////// // These functions are used to set/query the BPM ///////////////////////////////////////////////////////////////////////////// void MCLOCK_BPMSet(unsigned char _bpm) { // re-init timer depending on new BPM value bpm = _bpm; MIOS_TIMER_ReInit(3, MCLOCK_GetTimerValue(bpm)); } unsigned char MCLOCK_BPMGet(void) { return bpm; } ///////////////////////////////////////////////////////////////////////////// // This function resets the mclock_ctr variables ///////////////////////////////////////////////////////////////////////////// void MCLOCK_ResetMeter(void) { mclock_ctr_24 = 0; mclock_ctr_beats = 0; mclock_ctr_measures = 0; } ///////////////////////////////////////////////////////////////////////////// // This function sends the current song position ///////////////////////////////////////////////////////////////////////////// void MCLOCK_SendMeter(void) { unsigned int songpos = (mclock_ctr_beats << 2) | (mclock_ctr_measures << 4); MIOS_MIDI_TxBufferPut(0xf2); MIOS_MIDI_TxBufferPut((unsigned char)(songpos & 0x7f)); MIOS_MIDI_TxBufferPut((unsigned char)(songpos >> 7) & 0x7f); } ///////////////////////////////////////////////////////////////////////////// // These functions are used to control the MCLOCK handler from external ///////////////////////////////////////////////////////////////////////////// void MCLOCK_DoStop(void) { if( mclock_state.RUN == 0 ) { // reset song position of already in stop mode MCLOCK_ResetMeter(); // send Song Position //MCLOCK_SendMeter(); } // request stop mclock_state.STOP_REQ = 1; // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } void MCLOCK_DoPause(void) { // if in RUN mode: if( mclock_state.RUN ) { // toggle pause mode if( mclock_state.PAUSE ) { mclock_state.CONT_REQ = 1; } else { mclock_state.PAUSE = 1; } } else { // Stop mode: just toggle PAUSE mclock_state.PAUSE = mclock_state.PAUSE ? 0 : 1; } // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } void MCLOCK_DoPlay(void) { // reset meter counters MCLOCK_ResetMeter(); // send Song Position //MCLOCK_SendMeter(); // request start mclock_state.START_REQ = 1; // request display update app_flags.DISPLAY_UPDATE_REQ = 1; } main.c (get note from external midi keyboard) ///////////////////////////////////////////////////////////////////////////// // This function is called by MIOS in the mainloop when nothing else is to do ///////////////////////////////////////////////////////////////////////////// void Tick(void) __wparam { // the timing of delayed midi transmits is a low priority LivePanel_HandleTransmitDelay(); // live panel MIDI_HandleMatrixModTransmitDelay(); // from soft panel MIDI_HandleDelayedVoiceParam(); // from soft panel MIDI_HandleFilterSustainTransmitDelay(); // from live panel // this routine sends the MIDI clock (and Start/Continue/Stop) if requested MCLOCK_Tick(); } ///////////////////////////////////////////////////////////////////////////// // This function is periodically called by MIOS. The frequency has to be // initialized with MIOS_Timer_Set ///////////////////////////////////////////////////////////////////////////// void Timer(void) __wparam { LivePanel_BlinkLFOs(); LivePanel_BlinkLEDs(); // forward timer event to MIDI clock module (-> mclock.c) if(external_clk_received == 0){ MCLOCK_Timer(); }else{ } } ///////////////////////////////////////////////////////////////////////////// // This function is called by MIOS when the display content should be // initialized. Thats the case during startup and after a temporary message // has been printed on the screen ///////////////////////////////////////////////////////////////////////////// void DISPLAY_Init(void) __wparam { MIOS_LCD_Clear(); // request display update app_flags.Display_Pot_Req = 0; // start by displaying patches app_flags.Display_DIN_Req = 1; last_din_pin = DIN_PATCH; } ///////////////////////////////////////////////////////////////////////////// // This function is called in the mainloop when no temporary message is shown // on screen. Print the realtime messages here // // the live panel should only display on the lcd when we're in patch mode. ///////////////////////////////////////////////////////////////////////////// void DISPLAY_Tick(void) __wparam { // display encoder movements if( app_flags.Display_ENC_Req ) { if(last_encoder == ENCODER_SOFT) { SoftPanel_DisplayHandler(); } else // dco encoders { if (SoftPanel.Mode == Patch) Encoders_DisplayOscFrequency(last_encoder); } app_flags.Display_ENC_Req = 0; // handled } // display analog pot movements if( app_flags.Display_Pot_Req && SoftPanel.Mode == Patch) { LivePanel_DisplayAin(); app_flags.Display_Pot_Req = 0; // handled } // display buttons if( app_flags.Display_DIN_Req ) { if(DIN_ConfigMap[last_din_pin].group == BUTGRP_LIVE && SoftPanel.Mode == Patch ) LivePanel_DisplayDin(last_din_pin); else SoftPanel_DisplayHandler (); app_flags.Display_DIN_Req = 0; // handled } } ///////////////////////////////////////////////////////////////////////////// // This function is called by MIOS when a complete MIDI event has been received ///////////////////////////////////////////////////////////////////////////// void MPROC_NotifyReceivedEvnt(unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) __wparam { unsigned char evnt0_; // reNormed midi bytes unsigned char evnt2_; // real note off msg (to use a Roland Juno2 as a keyboard for example): switch(evnt0 & 0xf0){ case 0x90: if(evnt2 == 0){ evnt0_ = (evnt0 - 0x10); evnt2_ = evnt2; } else{ evnt0_ = evnt0; evnt2_ = evnt2; } break; default: evnt0_ = evnt0; evnt2_ = evnt2; break; } Router(evnt0_, evnt1, evnt2_); // see -> device.c Router_Arp(evnt0_, evnt1, evnt2_); // see arp.c } ///////////////////////////////////////////////////////////////////////////// // This function is called by MIOS when a MIDI event has been received // which has been specified in the MIOS_MPROC_EVENT_TABLE ///////////////////////////////////////////////////////////////////////////// void MPROC_NotifyFoundEvent(unsigned entry, unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) __wparam { } ///////////////////////////////////////////////////////////////////////////// // This function is called by MIOS when a MIDI event has not been completly // received within 2 seconds ///////////////////////////////////////////////////////////////////////////// void MPROC_NotifyTimeout(void) __wparam { } ///////////////////////////////////////////////////////////////////////////// // This function is called by MIOS when a MIDI byte has been received ///////////////////////////////////////////////////////////////////////////// void MPROC_NotifyReceivedByte(unsigned char byte) __wparam { static unsigned char fx_status; if(MIDI_ReceivingEditBuffer) { MIDI_HandleEditBuffer(byte); // call HandleEdiBuffer to get EditBuffer param/values } else if(MIDI_ReceivingBank) { MIDI_HandleBankDump(byte); // call HandleBankDump to get EditBuffer of Bank patches } else { // normal MIDI events are forwarded in MPROC_NotifyReceivedEvnt // this function handles sysex and realtime messages if( byte & 0x80 ) { // Status message if( byte >= 0xf0 ) MIOS_MIDI_TxBufferPut(byte); // transfer byte if(byte == 0xf8){ external_clk_received = 1; ++mclock_tick_ctr; // to control the arpegiator clock externally }else{ external_clk_received = 0; } // determine status if( byte == 0xf0 ) { fx_status = 0xff; // forward until 0xf7 } else if( byte == 0xf7 ) { fx_status = 0; // f7 reached, no forward } else if( byte == 0xf1 || byte == 0xf3 ) { fx_status = 1; // expecting one additional byte } else if( byte == 0xf2 ) { fx_status = 2; // expecting two additional bytes } else { fx_status = 0; // expecting no additional byte } } else { // check if fx status active if( fx_status ) { // forward data byte MIOS_MIDI_TxBufferPut(byte); // decrement counter if required if( fx_status != 0xff ) --fx_status; } } } // update display // if(!external_clk_received) // app_flags.Display_DIN_Req = 1; // DON'T WORK MAKE DISPLAY SHINING ALL TIME } The whole project code is available below for reference. Thanks for your help and interest :flowers:MatrixBoxSource_JV_beta v0.80_ext_clk.zip Edited June 12, 2011 by julienvoirin Quote Link to comment Share on other sites More sharing options...
ilmenator Posted June 13, 2011 Report Share Posted June 13, 2011 Great project, an arpeggiator seems to be very useful with the Matrix! I love the one on my K5k... Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.