julienvoirin Posted June 12, 2011 Report 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
ilmenator Posted June 13, 2011 Report Posted June 13, 2011 Great project, an arpeggiator seems to be very useful with the Matrix! I love the one on my K5k...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now