///////////////////////////////////////////////////////////////////////////// // Called by MIDI Tx Notificaton hook if a MIDI event should be sent // Allows to provide additional MIDI ports // If 1 is returned, package will be filtered! ///////////////////////////////////////////////////////////////////////////// s32 SEQ_MIDI_PORT_NotifyMIDITx(mios32_midi_port_t port, mios32_midi_package_t package) { // MIDI Out monitor function u8 mon_filtered = 0; if( seq_midi_port_mon_filter.MIDI_CLOCK && package.evnt0 == 0xf8 ) mon_filtered = 1; else if( seq_midi_port_mon_filter.ACTIVE_SENSE && package.evnt0 == 0xfe ) mon_filtered = 1; if( !mon_filtered ) { int port_ix = -1; if( port != DEFAULT ) { port_ix = SEQ_MIDI_PORT_OutIxGet(port); if( !port_ix ) port_ix = -1; // port not mapped } if( port_ix >= 0 ) { midi_out_package[port_ix] = package; midi_out_ctr[port_ix] = 20; // 2 seconds lifetime seq_midi_port_out_combined_ctr = 5; // 500 mS lifetime } } // DIN Sync Event (0xf9 sent over port 0xff) if( port == 0xff && package.evnt0 == 0xf9 ) { SEQ_CV_Clk_Trigger(package.evnt1); // second byte contains clock number (see also 0xf9 generation in seq_core) return 1; // filter package } if( (port & 0xf0) == OSC0 ) { // OSC1..4 port // avoid OSC feedback in seq_live.c (can cause infinite loops or stack overflows) if( filter_osc_packets || OSC_CLIENT_SendMIDIEvent(port & 0xf, package) >= 0 ) return 1; // filter package } else if( port == 0x80 ) { // AOUT port if( SEQ_CV_SendPackage(port & 0xf, package) ) return 1; // filter package } else if( port == 0xc0 ) { // Multi OUT port int i; u32 mask = 1; for(i=0; i<16; ++i, mask <<= 1) { if( seq_midi_port_multi_enable_flags & mask ) { // USB0/1/2/3, UART0/1/2/3, IIC0/1/2/3, OSC0/1/2/3 mios32_midi_port_t port = 0x10 + ((i&0xc) << 2) + (i&3); if( port != seq_blm_port ) // ensure that no note will be sent to BLM port if enabled MIOS32_MIDI_SendPackage(port, package); } } if( seq_midi_port_multi_enable_flags & (1 << 16) ) { MIOS32_MIDI_SendPackage(0x80, package); // AOUT } return 1; // filter forwarding } return 0; // don't filter package }
///////////////////////////////////////////////////////////////////////////// // Send a message to KissBox ///////////////////////////////////////////////////////////////////////////// s32 TERMINAL_KissboxSendMsg(char *msg) { mios32_midi_package_t p; p.ALL = 0; p.type = 0x1; // reserved in USB MIDI spec, used to send strings to KissBox int len; for(len=strlen(msg); len > 0; len-=3, msg+=3) { p.evnt0 = (len >= 1) ? msg[0] : 0; p.evnt1 = (len >= 2) ? msg[1] : 0; p.evnt2 = (len >= 3) ? msg[2] : 0; #if 0 MIOS32_MIDI_SendDebugMessage("[KISSBOX_MSG] 0x%08x\n", p.ALL); #endif MIOS32_MIDI_SendPackage(SPIM0, p); } // special case: message length is dividable by 3: we need to send an extra package with 0 to terminate the message if( p.evnt2 ) { p.evnt0 = 0; p.evnt1 = 0; p.evnt2 = 0; #if 0 MIOS32_MIDI_SendDebugMessage("[KISSBOX_MSG] 0x%08x\n", p.ALL); #endif MIOS32_MIDI_SendPackage(SPIM0, p); } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // This hook is called when a MIDI package has been received ///////////////////////////////////////////////////////////////////////////// void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package) { // SysEx handled by APP_SYSEX_Parser() if( midi_package.type >= 4 && midi_package.type <= 7 ) return; switch( port ) { case USB0: MIOS32_MIDI_SendPackage(UART0, midi_package); OSC_CLIENT_SendMIDIEvent(0, midi_package); led_trigger[0] = LED_PWM_PERIOD; // Board LED led_trigger[1] = LED_PWM_PERIOD; // J5A.0 break; case USB1: MIOS32_MIDI_SendPackage(UART1, midi_package); OSC_CLIENT_SendMIDIEvent(1, midi_package); led_trigger[0] = LED_PWM_PERIOD; // Board LED led_trigger[2] = LED_PWM_PERIOD; // J5A.1 break; case UART0: MIOS32_MIDI_SendPackage(USB0, midi_package); OSC_CLIENT_SendMIDIEvent(2, midi_package); led_trigger[0] = LED_PWM_PERIOD; // Board LED led_trigger[3] = LED_PWM_PERIOD; // J5A.2 break; case UART1: MIOS32_MIDI_SendPackage(USB1, midi_package); OSC_CLIENT_SendMIDIEvent(3, midi_package); led_trigger[0] = LED_PWM_PERIOD; // Board LED led_trigger[4] = LED_PWM_PERIOD; // J5A.3 break; } // forward to MIDI Monitor // SysEx messages have to be filtered for USB0 and UART0 to avoid data corruption // (the SysEx stream would interfere with monitor messages) u8 filter_sysex_message = (port == USB0) || (port == UART0); MIDIMON_Receive(port, midi_package, filter_sysex_message); }
///////////////////////////////////////////////////////////////////////////// // This function plays all "off" events // Should be called on sequencer reset/restart/pause to avoid hanging notes ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_PlayOffEvents(void) { // play "off events" SEQ_MIDI_OUT_FlushQueue(); // send Note Off to all channels // TODO: howto handle different ports? // TODO: should we also send Note Off events? Or should we trace Note On events and send Off if required? int chn; mios32_midi_package_t midi_package; midi_package.type = CC; midi_package.event = CC; midi_package.evnt2 = 0; for(chn=0; chn<16; ++chn) { midi_package.chn = chn; midi_package.evnt1 = 123; // All Notes Off MIOS32_MIDI_SendPackage(DEFAULT, midi_package); midi_package.evnt1 = 121; // Controller Reset MIOS32_MIDI_SendPackage(DEFAULT, midi_package); } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // This function is called from LC_VPOT_SendENCEvent() in lc_vpot.c // when GPC mode is activated and a V-Pot has been moved ///////////////////////////////////////////////////////////////////////////// s32 LC_GPC_SendENCEvent(u8 encoder, s32 incrementer) { u8 entry = gpc_offset + encoder; int prev_value = gpc_abs_value[entry]; int new_value = prev_value; int min = 0; int max = 127; // add incrementer to absolute value if( incrementer >= 0 ) { if( (new_value += incrementer) >= max ) new_value = max; } else { if( (new_value += incrementer < min ) ) new_value = min; } if( new_value == prev_value ) return 0; // no change // store new value LC_GPC_AbsValue_Received(entry, (u8)new_value); // send value #if 0 u8 evnt0 = MIOS_MPROC_EVENT_TABLE[2*entry+0]; u8 evnt1 = MIOS_MPROC_EVENT_TABLE[2*entry+1]; #else // TODO: define new table u8 evnt0 = 0x90; u8 evnt1 = entry; #endif u8 evnt2 = (u8)new_value; mios32_midi_package_t p; p.ALL = 0; p.type = (evnt0 >> 4) & 0xf; p.evnt0 = evnt0; p.evnt1 = evnt1; p.evnt2 = evnt2; MIOS32_MIDI_SendPackage(DEFAULT, p); return 1; // new value sent }
///////////////////////////////////////////////////////////////////////////// // Receives a MIDI package from APP_NotifyReceivedEvent (-> app.c) ///////////////////////////////////////////////////////////////////////////// s32 SEQ_MIDI_IN_Receive(mios32_midi_port_t port, mios32_midi_package_t midi_package) { s32 status = 0; // check if we should record this event u8 should_be_recorded = ui_page == SEQ_UI_PAGE_TRKREC && ((!seq_midi_in_rec_port || port == seq_midi_in_rec_port) && midi_package.chn == (seq_midi_in_rec_channel-1)); // search for matching ports // status[0] set if at least one port matched // status[1] set if remote function active int bus; for(bus=0; bus<SEQ_MIDI_IN_NUM_BUSSES; ++bus) { // filter MIDI port (if 0: no filter, listen to all ports) if( (!seq_midi_in_port[bus] || port == seq_midi_in_port[bus]) && midi_package.chn == (seq_midi_in_channel[bus]-1) ) { switch( midi_package.event ) { case NoteOff: if( remote_active ) { if( seq_hwcfg_midi_remote.key && midi_package.note == seq_hwcfg_midi_remote.key ) { remote_active = 0; // send "button depressed" state to all remote functions int i; for(i=0; i<128; ++i) SEQ_UI_REMOTE_MIDI_Keyboard(i, 1); // depressed } else { SEQ_UI_REMOTE_MIDI_Keyboard(midi_package.note, 1); // depressed } status |= 2; } else { if( !should_be_recorded && midi_package.note >= seq_midi_in_lower[bus] && (!seq_midi_in_upper[bus] || midi_package.note <= seq_midi_in_upper[bus]) ) { if( seq_midi_in_options[bus].MODE_PLAY ) { MUTEX_MIDIOUT_TAKE; SEQ_CORE_PlayLive(SEQ_UI_VisibleTrackGet(), midi_package); MUTEX_MIDIOUT_GIVE; } else { #if 0 // octave normalisation - too complicated for normal users... mios32_midi_package_t p = midi_package; if( seq_midi_in_lower[bus] ) { // normalize to first octave int normalized_note = 0x30 + p.note - ((int)seq_midi_in_lower[bus]/12)*12; while( normalized_note > 127 ) normalized_note -= 12; while( normalized_note < 0 ) normalized_note += 12; p.note = normalized_note; } SEQ_MIDI_IN_BusReceive(0xf0+bus, p, 0); #else SEQ_MIDI_IN_BusReceive(0xf0+bus, midi_package, 0); #endif } status |= 1; } } break; case NoteOn: if( remote_active ) SEQ_UI_REMOTE_MIDI_Keyboard(midi_package.note, midi_package.velocity ? 0 : 1); // depressed else { if( seq_hwcfg_midi_remote.key && midi_package.note == seq_hwcfg_midi_remote.key ) { remote_active = 1; status |= 2; } else { if( !should_be_recorded && midi_package.note >= seq_midi_in_lower[bus] && (!seq_midi_in_upper[bus] || midi_package.note <= seq_midi_in_upper[bus]) ) { if( seq_midi_in_options[bus].MODE_PLAY ) { MUTEX_MIDIOUT_TAKE; SEQ_CORE_PlayLive(SEQ_UI_VisibleTrackGet(), midi_package); MUTEX_MIDIOUT_GIVE; } else { #if 0 // octave normalisation - too complicated for normal users... mios32_midi_package_t p = midi_package; if( seq_midi_in_lower[bus] ) { // normalize to first octave int normalized_note = 0x30 + p.note - ((int)seq_midi_in_lower[bus]/12)*12; while( normalized_note > 127 ) normalized_note -= 12; while( normalized_note < 0 ) normalized_note += 12; p.note = normalized_note; } SEQ_MIDI_IN_BusReceive(0xf0+bus, p, 0); #else SEQ_MIDI_IN_BusReceive(0xf0+bus, midi_package, 0); #endif } status |= 1; } } } break; case CC: if( !should_be_recorded ) { if( seq_midi_in_options[bus].MODE_PLAY ) { MUTEX_MIDIOUT_TAKE; SEQ_CORE_PlayLive(SEQ_UI_VisibleTrackGet(), midi_package); MUTEX_MIDIOUT_GIVE; } else { SEQ_MIDI_IN_BusReceive(0xf0+bus, midi_package, 0); } } status |= 1; break; default: if( seq_midi_in_options[bus].MODE_PLAY ) { MUTEX_MIDIOUT_TAKE; SEQ_CORE_PlayLive(SEQ_UI_VisibleTrackGet(), midi_package); MUTEX_MIDIOUT_GIVE; } } } } // record function if( !(status & 2) && should_be_recorded ) { SEQ_RECORD_Receive(midi_package, SEQ_UI_VisibleTrackGet()); } // Section Changer if( !(status & 2) && (seq_midi_in_sect_port && port == seq_midi_in_sect_port && midi_package.chn == (seq_midi_in_sect_channel-1)) ) { u8 forward_event = 1; switch( midi_package.event ) { case NoteOff: MUTEX_MIDIIN_TAKE; if( (status = SEQ_MIDI_IN_Receive_NoteSC(midi_package.note, 0x00)) >= 1 ) forward_event = 0; MUTEX_MIDIIN_GIVE; break; case NoteOn: MUTEX_MIDIIN_TAKE; if( (status = SEQ_MIDI_IN_Receive_NoteSC(midi_package.note, midi_package.velocity)) >= 1 ) forward_event = 0; MUTEX_MIDIIN_GIVE; break; } if( seq_midi_in_sect_fwd_port && forward_event ) { // octave hasn't been taken, optionally forward to forwarding port MUTEX_MIDIOUT_TAKE; MIOS32_MIDI_SendPackage(seq_midi_in_sect_fwd_port, midi_package); MUTEX_MIDIOUT_GIVE; } } #if PATCH_CHANGER_ENABLED // Patch Changer (currently assigned to channel+1) if( !(status & 2) && (seq_midi_in_sect_port && port == seq_midi_in_sect_port && midi_package.chn == (seq_midi_in_sect_channel)) ) { u8 forward_event = 1; switch( midi_package.event ) { case NoteOff: MUTEX_MIDIIN_TAKE; if( (status = SEQ_MIDI_IN_Receive_NotePC(midi_package.note, 0x00)) >= 1 ) forward_event = 0; MUTEX_MIDIIN_GIVE; break; case NoteOn: MUTEX_MIDIIN_TAKE; if( (status = SEQ_MIDI_IN_Receive_NotePC(midi_package.note, midi_package.velocity)) >= 1 ) forward_event = 0; MUTEX_MIDIIN_GIVE; break; } if( seq_midi_in_sect_fwd_port && forward_event ) { // octave hasn't been taken, optionally forward to forwarding port MUTEX_MIDIOUT_TAKE; MIOS32_MIDI_SendPackage(seq_midi_in_sect_fwd_port, midi_package); MUTEX_MIDIOUT_GIVE; } } #endif return status; }
///////////////////////////////////////////////////////////////////////////// // This hook is called when a MIDI package has been received ///////////////////////////////////////////////////////////////////////////// void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package) { // Note On? if( midi_package.type == NoteOn && midi_package.velocity > 0 ) { #if DEFAULT_MIDI_CHANNEL if( midi_package.chn != (DEFAULT_MIDI_CHANNEL-1) ) return; #endif // using the played note as index for "last played" array u8 note_ix = midi_package.note; // get scaled note if( selected_scale ) SCALE_Note(&midi_package, selected_scale-1, selected_root); // same note already played from another key? then send note off and play it again int i; u8 *last_played_note_ptr = (u8 *)&last_played_note[0]; for(i=0; i<128; ++i, ++last_played_note_ptr) if( *last_played_note_ptr == midi_package.note ) { mios32_midi_package_t p = midi_package; p.velocity = 0; // forward Note Off event to MIDI ports MIOS32_MIDI_SendPackage(USB0, p); MIOS32_MIDI_SendPackage(UART0, p); MIOS32_MIDI_SendPackage(UART1, p); *last_played_note_ptr = 0x80; } // store note last_played_note[note_ix] = midi_package.note; // forward Note On event to MIDI ports MIOS32_MIDI_SendPackage(USB0, midi_package); MIOS32_MIDI_SendPackage(UART0, midi_package); MIOS32_MIDI_SendPackage(UART1, midi_package); return; } // Note Off? if( midi_package.type == NoteOff || (midi_package.type == NoteOn && midi_package.velocity == 0) ) { #if DEFAULT_MIDI_CHANNEL if( midi_package.chn != (DEFAULT_MIDI_CHANNEL-1) ) return; #endif // using the played note as index for "last played" array u8 note_ix = midi_package.note; // map to last played note if( last_played_note[note_ix] < 0x80 ) { midi_package.note = last_played_note[note_ix]; last_played_note[note_ix] = 0x80; // forward to MIDI ports MIOS32_MIDI_SendPackage(USB0, midi_package); MIOS32_MIDI_SendPackage(UART0, midi_package); MIOS32_MIDI_SendPackage(UART1, midi_package); } return; } // CC? if( midi_package.type == CC ) { #if DEFAULT_MIDI_CHANNEL if( midi_package.chn != (DEFAULT_MIDI_CHANNEL-1) ) return; #endif switch( midi_package.cc_number ) { case 16: if( midi_package.value <= SCALE_NumGet() ) { selected_scale = midi_package.value; display_update = 1; } break; case 17: if( midi_package.value < 12 ) { selected_root = midi_package.value; display_update = 1; } break; } return; } }
///////////////////////////////////////////////////////////////////////////// // Local encoder callback function // Should return: // 1 if value has been changed // 0 if value hasn't been changed // -1 if invalid or unsupported encoder ///////////////////////////////////////////////////////////////////////////// static s32 Encoder_Handler(seq_ui_encoder_t encoder, s32 incrementer) { u8 visible_track = SEQ_UI_VisibleTrackGet(); switch( encoder ) { case SEQ_UI_ENCODER_GP1: ui_selected_item = ITEM_GXTY; break; case SEQ_UI_ENCODER_GP2: ui_selected_item = ITEM_WAVEFORM; break; case SEQ_UI_ENCODER_GP3: ui_selected_item = ITEM_AMPLITUDE; break; case SEQ_UI_ENCODER_GP4: ui_selected_item = ITEM_PHASE; break; case SEQ_UI_ENCODER_GP5: ui_selected_item = ITEM_STEPS; break; case SEQ_UI_ENCODER_GP6: ui_selected_item = ITEM_STEPS_RST; break; case SEQ_UI_ENCODER_GP7: ui_selected_item = ITEM_ENABLE_ONE_SHOT; break; case SEQ_UI_ENCODER_GP8: return -1; // not mapped case SEQ_UI_ENCODER_GP9: ui_selected_item = ITEM_ENABLE_NOTE; break; case SEQ_UI_ENCODER_GP10: ui_selected_item = ITEM_ENABLE_VELOCITY; break; case SEQ_UI_ENCODER_GP11: ui_selected_item = ITEM_ENABLE_LENGTH; break; case SEQ_UI_ENCODER_GP12: ui_selected_item = ITEM_ENABLE_CC; break; case SEQ_UI_ENCODER_GP13: return -1; // not mapped case SEQ_UI_ENCODER_GP14: // CC number selection now has to be confirmed with GP button if( ui_selected_item != ITEM_CC ) { edit_cc_number = SEQ_CC_Get(visible_track, SEQ_CC_LFO_CC); ui_selected_item = ITEM_CC; SEQ_UI_Msg(SEQ_UI_MSG_USER, 2000, "Please confirm CC", "with GP button!"); } else if( incrementer == 0 ) { if( edit_cc_number != SEQ_CC_Get(visible_track, SEQ_CC_LFO_CC) ) { SEQ_CC_Set(visible_track, SEQ_CC_LFO_CC, edit_cc_number); SEQ_UI_Msg(SEQ_UI_MSG_USER, 2000, "CC number", "has been changed."); } // send event mios32_midi_package_t p; if( SEQ_LFO_FastCC_Event(visible_track, 0, &p, 1) >= 1 ) { MUTEX_MIDIOUT_TAKE; MIOS32_MIDI_SendPackage(SEQ_CC_Get(visible_track, SEQ_CC_MIDI_PORT), p); MUTEX_MIDIOUT_GIVE; } } break; case SEQ_UI_ENCODER_GP15: ui_selected_item = ITEM_CC_OFFSET; break; case SEQ_UI_ENCODER_GP16: ui_selected_item = ITEM_CC_PPQN; break; } // for GP encoders and Datawheel switch( ui_selected_item ) { case ITEM_GXTY: return SEQ_UI_GxTyInc(incrementer); case ITEM_WAVEFORM: return SEQ_UI_CC_Inc(SEQ_CC_LFO_WAVEFORM, 0, 22, incrementer); case ITEM_AMPLITUDE: return SEQ_UI_CC_Inc(SEQ_CC_LFO_AMPLITUDE, 0, 255, incrementer); case ITEM_PHASE: return SEQ_UI_CC_Inc(SEQ_CC_LFO_PHASE, 0, 99, incrementer); case ITEM_STEPS: return SEQ_UI_CC_Inc(SEQ_CC_LFO_STEPS, 0, 255, incrementer); case ITEM_STEPS_RST: return SEQ_UI_CC_Inc(SEQ_CC_LFO_STEPS_RST, 0, 255, incrementer); case ITEM_ENABLE_ONE_SHOT: case ITEM_ENABLE_NOTE: case ITEM_ENABLE_VELOCITY: case ITEM_ENABLE_LENGTH: case ITEM_ENABLE_CC: { u8 flag = ui_selected_item - ITEM_ENABLE_ONE_SHOT; u8 mask = 1 << flag; u8 value = SEQ_CC_Get(visible_track, SEQ_CC_LFO_ENABLE_FLAGS); if( incrementer == 0 ) // toggle SEQ_UI_CC_SetFlags(SEQ_CC_LFO_ENABLE_FLAGS, mask, value ^ mask); else if( incrementer > 0 ) SEQ_UI_CC_SetFlags(SEQ_CC_LFO_ENABLE_FLAGS, mask, mask); else SEQ_UI_CC_SetFlags(SEQ_CC_LFO_ENABLE_FLAGS, mask, 0); } break; case ITEM_CC: { // CC number selection now has to be confirmed with GP button s32 status = SEQ_UI_Var8_Inc(&edit_cc_number, 0, 127, incrementer); mios32_midi_port_t port = SEQ_CC_Get(visible_track, SEQ_CC_MIDI_PORT); u8 loopback = port == 0xf0; if( !edit_cc_number ) { SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "LFO CC", "disabled"); } else { SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, loopback ? "Loopback CC" : "Controller:", (char *)SEQ_CC_LABELS_Get(port, edit_cc_number)); } return status; } break; case ITEM_CC_OFFSET: return SEQ_UI_CC_Inc(SEQ_CC_LFO_CC_OFFSET, 0, 127, incrementer); case ITEM_CC_PPQN: return SEQ_UI_CC_Inc(SEQ_CC_LFO_CC_PPQN, 0, 8, incrementer); } return -1; // invalid or unsupported encoder }