Jump to content

Recommended Posts

Posted (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 by julienvoirin

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...