Dear Guys
I’ve enhanced a controller for Oberheim Matrix 1000/6 developped by Jackchaos
http://discourse.midibox.org/t/topic/13345
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](< base_url >/applications/core/interface/file/attachment.php?id=8864)