Jump to content

Coding an Arpeggiator on MIOS 8


julienvoirin
 Share

Recommended Posts

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
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...