///////////////////////////////////////////////////////////////////////////// // Should be called whenever a Note event has been received. // We expect, that velocity is 0 on a Note Off event ///////////////////////////////////////////////////////////////////////////// s32 SEQ_NotifyNoteOn(u8 note, u8 velocity) { u8 clear_stack = 0; if( velocity ) // push note into note stack NOTESTACK_Push(¬estack, note, velocity); else { // remove note from note stack // function returns 2 if no note played anymore (all keys depressed) if( NOTESTACK_Pop(¬estack, note) == 2 ) clear_stack = 1; } // At least one note played? if( !clear_stack && notestack.len > 0 ) { // start sequencer if it isn't already running if( !SEQ_BPM_IsRunning() ) SEQ_BPM_Start(); } else { // clear stack NOTESTACK_Clear(¬estack); // no key is pressed anymore: stop sequencer SEQ_BPM_Stop(); } #if 1 // optional debug messages NOTESTACK_SendDebugMessage(¬estack); #endif return 0; // no error }
///////////////////////////////////////////////////////////////////////////// //! To control the play/stop button function ///////////////////////////////////////////////////////////////////////////// s32 MBNG_SEQ_PlayStopButton(void) { if( SEQ_BPM_IsRunning() ) { MBNG_SEQ_StopButton(); } else { MBNG_SEQ_PlayButton(); } 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) { // Note On received? if( midi_package.type == NoteOn && midi_package.chn == Chn1 && midi_package.velocity > 0 ) { // determine base key number (0=C, 1=C#, 2=D, ...) u8 base_key = midi_package.note % 12; // branch depending on note switch( base_key ) { case 0: // "C" starts the sequencer MIOS32_MIDI_SendDebugMessage("Start\n"); // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // start sequencer SEQ_BPM_Start(); break; case 2: // "D" stops the sequencer. If pressed twice, the sequencer will be reset MIOS32_MIDI_SendDebugMessage("Stop\n"); if( SEQ_BPM_IsRunning() ) SEQ_BPM_Stop(); // stop sequencer else SEQ_Reset(1); // reset sequencer break; case 4: // "E" pauses the sequencer // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // toggle pause mode seq_pause ^= 1; MIOS32_MIDI_SendDebugMessage("Pause %s\n", seq_pause ? "on" : "off"); // execute stop/continue depending on new mode if( seq_pause ) SEQ_BPM_Stop(); // stop sequencer else SEQ_BPM_Cont(); // continue sequencer break; case 5: // "F" switches to next file MIOS32_MIDI_SendDebugMessage("Next File\n"); SEQ_PlayFileReq(1); break; } } }
///////////////////////////////////////////////////////////////////////////// //! To control the stop button function ///////////////////////////////////////////////////////////////////////////// s32 MBNG_SEQ_StopButton(void) { if( SEQ_BPM_IsRunning() ) { SEQ_BPM_Stop(); // stop sequencer } else { // reset sequencer MBNG_SEQ_Reset(); // disable pause mode MBNG_SEQ_SetPauseMode(0); } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// //! To control the stop button function ///////////////////////////////////////////////////////////////////////////// s32 MBNG_SEQ_PauseButton(void) { // if in auto mode and BPM generator is not clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // toggle pause seq_pause ^= 1; // execute stop/continue depending on new mode MIOS32_IRQ_Disable(); if( seq_pause ) { SEQ_BPM_Stop(); } else { if( !SEQ_BPM_IsRunning() ) SEQ_BPM_Cont(); } MIOS32_IRQ_Enable(); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // (Public) Tap Tempo Function // Can also be accessed by pressing MENU+PLAY ///////////////////////////////////////////////////////////////////////////// s32 SEQ_UI_BPM_TapTempo(void) { // determine current timestamp u32 timestamp = MIOS32_TIMESTAMP_Get(); u32 delay = MIOS32_TIMESTAMP_GetDelay(tap_tempo_last_timestamp); // if delay < 100 (mS) we assume a bouncing button - ignore this tap! if( delay < 100 ) return -1; // reset counter if last timestamp zero (first tap) or difference between timestamps > 2 seconds (< 30 BPM...) if( !tap_tempo_last_timestamp || delay > 2000 ) { resetTapTempo(); } else { // store measured delay tap_tempo_delay[tap_tempo_beat_ix] = delay; // increment index, wrap at number of stored tap delays tap_tempo_beat_ix = (tap_tempo_beat_ix + 1) % TAP_DELAY_STORAGE_SIZE; } // store timestamp tap_tempo_last_timestamp = timestamp; // take over BPM and start sequencer once we reached the 5th tap if( tap_tempo_beat_ctr >= TAP_DELAY_STORAGE_SIZE ) { // hold counter at delay storage size + 1 (for display) tap_tempo_beat_ctr = TAP_DELAY_STORAGE_SIZE + 1; // determine BPM int tap; int accumulated_delay = 0; for(tap=0; tap<TAP_DELAY_STORAGE_SIZE; ++tap) accumulated_delay += tap_tempo_delay[tap]; u32 bpm = 60000000 / (accumulated_delay / TAP_DELAY_STORAGE_SIZE); // set BPM float bpmF = bpm / 1000.0; seq_core_bpm_preset_tempo[seq_core_bpm_preset_num] = bpmF; SEQ_BPM_Set(bpmF); // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // if sequencer not running: start it! if( !SEQ_BPM_IsRunning() ) SEQ_BPM_Start(); } else { // increment counter ++tap_tempo_beat_ctr; } // print message char buffer1[30]; if( tap_tempo_beat_ctr > TAP_DELAY_STORAGE_SIZE ) { float bpm = SEQ_BPM_Get(); sprintf(buffer1, "Tap Tempo: %3d.%d", (int)bpm, (int)(10*bpm)%10); } else sprintf(buffer1, "Tap Tempo: ???.?"); char buffer2[30]; int i; for(i=0; i<((tap_tempo_beat_ix+1)*4); ++i) buffer2[i] = '>'; for(; i<16; ++i) buffer2[i] = ' '; buffer2[i] = 0; SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 2000, buffer1, buffer2); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // 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) { #if 0 // leads to: comparison is always true due to limited range of data type if( (encoder >= SEQ_UI_ENCODER_GP1 && encoder <= SEQ_UI_ENCODER_GP16) ) { #else if( encoder <= SEQ_UI_ENCODER_GP16 ) { #endif if( seq_ui_button_state.SELECT_PRESSED && !seq_ui_button_state.CHANGE_ALL_STEPS ) { // select button pressed: indirect MUTED flag modification (taken over when select button depressed) u16 mask = 1 << encoder; if( incrementer < 0 || (incrementer == 0 && !(latched_mute & mask)) ) latched_mute |= mask; else latched_mute &= ~mask; } else { // select button not pressed: direct MUTED flag modification // access to seq_core_trk[] must be atomic! portENTER_CRITICAL(); u8 visible_track = SEQ_UI_VisibleTrackGet(); u16 mask = 1 << encoder; u16 *muted = (u16 *)&seq_core_trk_muted; if( seq_ui_button_state.CHANGE_ALL_STEPS ) { switch( encoder ) { case SEQ_UI_ENCODER_GP1: case SEQ_UI_ENCODER_GP2: *muted = 0xffff; SEQ_UI_Msg(SEQ_UI_MSG_USER, 1000, "All Tracks", "muted"); break; case SEQ_UI_ENCODER_GP3: case SEQ_UI_ENCODER_GP4: case SEQ_UI_ENCODER_GP5: muted = (u16 *)&seq_core_trk[visible_track].layer_muted; *muted = 0xffff; SEQ_UI_Msg(SEQ_UI_MSG_USER, 1000, "All Layers", "of current Track muted"); break; case SEQ_UI_ENCODER_GP6: case SEQ_UI_ENCODER_GP7: case SEQ_UI_ENCODER_GP8: { *muted = 0xffff; int track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { seq_core_trk[track].layer_muted = 0xffff; } SEQ_UI_Msg(SEQ_UI_MSG_USER, 1000, "All Layers", "and Tracks muted"); } break; case SEQ_UI_ENCODER_GP9: case SEQ_UI_ENCODER_GP10: *muted = 0x0000; SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "All Tracks", "unmuted"); break; case SEQ_UI_ENCODER_GP11: case SEQ_UI_ENCODER_GP12: case SEQ_UI_ENCODER_GP13: muted = (u16 *)&seq_core_trk[visible_track].layer_muted; *muted = 0x0000; SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "All Layers", "of current Track unmuted"); break; case SEQ_UI_ENCODER_GP14: case SEQ_UI_ENCODER_GP15: case SEQ_UI_ENCODER_GP16: { *muted = 0x0000; int track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { seq_core_trk[track].layer_muted = 0x0000; } SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "All Layers", "and Tracks unmuted"); } break; } } else { if( seq_ui_button_state.MUTE_PRESSED ) muted = (u16 *)&seq_core_trk[visible_track].layer_muted; else if( SEQ_BPM_IsRunning() ) { // Synched Mutes only when sequencer is running if( !(*muted & mask) && seq_core_options.SYNCHED_MUTE && !seq_ui_button_state.FAST_ENCODERS ) { // Fast button will disable synched mute muted = (u16 *)&seq_core_trk_synched_mute; } else if( (*muted & mask) && seq_core_options.SYNCHED_UNMUTE && !seq_ui_button_state.FAST_ENCODERS ) { // Fast button will disable synched unmute muted = (u16 *)&seq_core_trk_synched_unmute; } } else { // clear synched mutes/unmutes if sequencer not running seq_core_trk_synched_mute = 0; seq_core_trk_synched_unmute = 0; } if( incrementer < 0 ) *muted |= mask; else if( incrementer > 0 ) *muted &= ~mask; else *muted ^= mask; } portEXIT_CRITICAL(); if( muted == ((u16 *)&seq_core_trk_muted) ) { // send to external SEQ_MIDI_IN_ExtCtrlSend(SEQ_MIDI_IN_EXT_CTRL_MUTES, (*muted & mask) ? 127 : 0, encoder); } } return 1; // value changed } return -1; // invalid or unsupported encoder } ///////////////////////////////////////////////////////////////////////////// // Local button callback function // Should return: // 1 if value has been changed // 0 if value hasn't been changed // -1 if invalid or unsupported button ///////////////////////////////////////////////////////////////////////////// static s32 Button_Handler(seq_ui_button_t button, s32 depressed) { #if 0 // leads to: comparison is always true due to limited range of data type if( button >= SEQ_UI_BUTTON_GP1 && button <= SEQ_UI_BUTTON_GP16 ) { #else if( button <= SEQ_UI_BUTTON_GP16 ) { #endif if( depressed ) return 0; // ignore when button depressed // re-using encoder routine return Encoder_Handler(button, 0); } switch( button ) { case SEQ_UI_BUTTON_Select: portENTER_CRITICAL(); if( depressed ) { // select button released: take over latched mutes if( seq_ui_button_state.MUTE_PRESSED ) { u8 visible_track = SEQ_UI_VisibleTrackGet(); seq_core_trk[visible_track].layer_muted = latched_mute; } else { u16 new_mutes = latched_mute & ~seq_core_trk_muted; if( SEQ_BPM_IsRunning() && seq_core_options.SYNCHED_MUTE && !seq_ui_button_state.FAST_ENCODERS ) // Fast button will disable synched mute seq_core_trk_synched_mute |= new_mutes; else seq_core_trk_muted |= new_mutes; u16 new_unmutes = ~latched_mute & seq_core_trk_muted; if( SEQ_BPM_IsRunning() && seq_core_options.SYNCHED_UNMUTE && !seq_ui_button_state.FAST_ENCODERS ) // Fast button will disable synched unmute seq_core_trk_synched_unmute |= new_unmutes; else seq_core_trk_muted &= ~new_unmutes; } } else { // select pressed: init latched mutes which will be taken over once SELECT button released if( seq_ui_button_state.MUTE_PRESSED ) { u8 visible_track = SEQ_UI_VisibleTrackGet(); latched_mute = seq_core_trk[visible_track].layer_muted; } else { latched_mute = seq_core_trk_muted; } } portEXIT_CRITICAL(); return 1; } return -1; // invalid or unsupported button } ///////////////////////////////////////////////////////////////////////////// // Local Display Handler function // IN: <high_prio>: if set, a high-priority LCD update is requested ///////////////////////////////////////////////////////////////////////////// static s32 LCD_Handler(u8 high_prio) { // layout: // 00000000001111111111222222222233333333330000000000111111111122222222223333333333 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 // <--------------------------------------><--------------------------------------> // > 1< 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // ...horizontal VU meters... // Mute All screen: // 00000000001111111111222222222233333333330000000000111111111122222222223333333333 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 // <--------------------------------------><--------------------------------------> // Mute Mute Mute all Tracks Unmute Unmute Unmute all Tracks // all Tracks G1T1 Layers and all Layersall Tracks G1T1 Layers and all Layers if( seq_ui_button_state.CHANGE_ALL_STEPS ) { if( high_prio ) return 0; SEQ_LCD_CursorSet(0, 0); SEQ_LCD_PrintString(" Mute Mute Mute all Tracks Unmute Unmute Unmute all Tracks"); SEQ_LCD_CursorSet(0, 1); SEQ_LCD_PrintString("all Tracks "); SEQ_LCD_PrintGxTy(ui_selected_group, ui_selected_tracks); SEQ_LCD_PrintString(" Layers and all Layersall Tracks "); SEQ_LCD_PrintGxTy(ui_selected_group, ui_selected_tracks); SEQ_LCD_PrintString(" Layers and all Layers"); return 0; } if( high_prio ) { /////////////////////////////////////////////////////////////////////////// // frequently update VU meters SEQ_LCD_CursorSet(0, 1); u8 track; u16 mute_flags = 0; u16 mute_flags_from_midi = 0; if( !ui_cursor_flash && seq_ui_button_state.SELECT_PRESSED ) { mute_flags = latched_mute; } else { if( seq_ui_button_state.MUTE_PRESSED ) { u8 visible_track = SEQ_UI_VisibleTrackGet(); mute_flags = seq_core_trk[visible_track].layer_muted; mute_flags_from_midi = seq_core_trk[visible_track].layer_muted_from_midi; } else { mute_flags = seq_core_trk_muted; } } if( seq_ui_button_state.MUTE_PRESSED ) { u8 layer; u16 mask = (1 << 0); for(layer=0; layer<16; ++layer, mask <<= 1) if( mute_flags_from_midi & mask ) { SEQ_LCD_PrintString("MIDI "); } else if( mute_flags & mask ) { SEQ_LCD_PrintString("Mute "); } else { SEQ_LCD_PrintHBar((seq_layer_vu_meter[layer] >> 3) & 0xf); } } else { int remaining_steps = (seq_core_steps_per_measure - seq_core_state.ref_step) + 1; seq_core_trk_t *t = &seq_core_trk[0]; u16 mask = (1 << 0); for(track=0; track<16; ++t, ++track, mask <<= 1) if( mute_flags & mask ) { if( !seq_ui_button_state.SELECT_PRESSED && (seq_core_trk_synched_unmute & mask) ) { SEQ_LCD_PrintFormattedString("U%3d ", remaining_steps); } else { SEQ_LCD_PrintString("Mute "); } } else { if( !seq_ui_button_state.SELECT_PRESSED && (seq_core_trk_synched_mute & mask) ) { SEQ_LCD_PrintFormattedString("M%3d ", remaining_steps); } else { SEQ_LCD_PrintHBar(t->vu_meter >> 3); } } } } else {
///////////////////////////////////////////////////////////////////////////// // 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(); int num_steps = SEQ_TRG_NumStepsGet(visible_track); int track_length = (int)SEQ_CC_Get(visible_track, SEQ_CC_LENGTH) + 1; #if 0 // leads to: comparison is always true due to limited range of data type if( (encoder >= SEQ_UI_ENCODER_GP1 && encoder <= SEQ_UI_ENCODER_GP16) ) { #else if( encoder <= SEQ_UI_ENCODER_GP16 ) { #endif if( seq_ui_button_state.SELECT_PRESSED ) { int section; if( num_steps > 128 ) section = encoder / (track_length / 16); else if( num_steps > 64 ) section = encoder / ((2 * track_length) / 16); else section = encoder / ((4 * track_length) / 16); // operation should be atomic (change all selected tracks) u8 track; seq_core_trk_t *t = &seq_core_trk[0]; MIOS32_IRQ_Disable(); for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track, ++t) if( ui_selected_tracks & (1 << track) ) t->play_section = section; MIOS32_IRQ_Enable(); } else { // won't work if Follow function active! if( seq_core_state.FOLLOW ) { // print message and exit SEQ_UI_Msg((encoder <= SEQ_UI_ENCODER_GP8) ? SEQ_UI_MSG_USER : SEQ_UI_MSG_USER_R, 2000, "\"Follow\" active!", "Please deactivate!"); return 1; } } // select new step view ui_selected_step_view = (encoder * (num_steps/16)) / 16; // select step within view if( !seq_ui_button_state.CHANGE_ALL_STEPS ) { // don't change the selected step if ALL function is active, otherwise the ramp can't be changed over multiple views ui_selected_step = (ui_selected_step_view << 4) | (ui_selected_step & 0xf); } if( seq_hwcfg_button_beh.step_view ) { // if toggle function active: jump back to previous menu // this is especially useful for the emulated MBSEQ, where we can only click on a single button // (stepview gets deactivated when clicking on GP button) if( seq_ui_button_state.STEP_VIEW ) { seq_ui_button_state.STEP_VIEW = 0; SEQ_UI_PageSet(ui_stepview_prev_page); } } return 1; // value changed } else if( encoder == SEQ_UI_ENCODER_Datawheel ) { if( SEQ_UI_Var8_Inc(&ui_selected_step_view, 0, (num_steps-1)/16, incrementer) >= 1 ) { // select step within view if( !seq_ui_button_state.CHANGE_ALL_STEPS ) { // don't change the selected step if ALL function is active, otherwise the ramp can't be changed over multiple views ui_selected_step = (ui_selected_step_view << 4) | (ui_selected_step & 0xf); } } else { return 0; } } return -1; // invalid or unsupported encoder } ///////////////////////////////////////////////////////////////////////////// // Local button callback function // Should return: // 1 if value has been changed // 0 if value hasn't been changed // -1 if invalid or unsupported button ///////////////////////////////////////////////////////////////////////////// static s32 Button_Handler(seq_ui_button_t button, s32 depressed) { if( depressed ) return 0; // ignore when button depressed #if 0 // leads to: comparison is always true due to limited range of data type if( button >= SEQ_UI_BUTTON_GP1 && button <= SEQ_UI_BUTTON_GP16 ) { #else if( button <= SEQ_UI_BUTTON_GP16 ) { #endif // -> same handling like for encoders return Encoder_Handler(button, 0); } switch( button ) { case SEQ_UI_BUTTON_Select: return 1; // selects section mode, checked via seq_ui_button_state.SELECT_PRESSED case SEQ_UI_BUTTON_Right: case SEQ_UI_BUTTON_Up: if( depressed ) return 0; // ignore when button depressed return Encoder_Handler(SEQ_UI_ENCODER_Datawheel, 1); case SEQ_UI_BUTTON_Left: case SEQ_UI_BUTTON_Down: if( depressed ) return 0; // ignore when button depressed return Encoder_Handler(SEQ_UI_ENCODER_Datawheel, -1); } return -1; // invalid or unsupported button } ///////////////////////////////////////////////////////////////////////////// // Local Display Handler function // IN: <high_prio>: if set, a high-priority LCD update is requested ///////////////////////////////////////////////////////////////////////////// static s32 LCD_Handler(u8 high_prio) { // layout: // 00000000001111111111222222222233333333330000000000111111111122222222223333333333 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 // <--------------------------------------><--------------------------------------> // 1 17 33 49 // *... *... *... *... *... *... *... *... *... *... *... *... *... *... *... *... // 5 character usage in 256 step view (5 chars have to display 16 steps, 8 special chars available, // due to this limitation, we only display 15 steps (shouldn't really hurt)) // 43210 43210 43210 43210 43210 // * * * * . . . * . . . * . . . (Small charset) // 5 character usage in 128 step view (5 chars have to display 8 steps): // 43210 43210 43210 43210 43210 // * * . * * . . . (Medium charset) // 5 character usage in 64 step view (5 chars have to display 4 steps): // 43210 43210 43210 43210 43210 // * * * * * (Big charset) u8 visible_track = SEQ_UI_VisibleTrackGet(); u8 event_mode = SEQ_CC_Get(visible_track, SEQ_CC_MIDI_EVENT_MODE); int num_steps = SEQ_TRG_NumStepsGet(visible_track); int steps_per_item = num_steps / 16; int played_step = SEQ_BPM_IsRunning() ? seq_core_trk[visible_track].step : -1; int i; if( !high_prio ) { SEQ_LCD_CursorSet(0, 0); for(i=0; i<16; ++i) if( ((i*steps_per_item) % 16) || ((((i*steps_per_item)/16) == ui_selected_step_view) && ui_cursor_flash) ) SEQ_LCD_PrintSpaces(5); else SEQ_LCD_PrintFormattedString("%-3d ", i*steps_per_item+1); // print flashing *LOOPED* at right corner if loop mode activated to remind that steps will be played differntly if( (ui_cursor_flash_overrun_ctr & 1) && seq_core_state.LOOP ) { SEQ_LCD_CursorSet(71, 0); SEQ_LCD_PrintString(" *LOOPED*"); } else if( (ui_cursor_flash_overrun_ctr & 1) && seq_core_trk[visible_track].play_section > 0 ) { SEQ_LCD_CursorSet(71, 0); SEQ_LCD_PrintFormattedString(" *Sect.%c*", 'A'+seq_core_trk[visible_track].play_section); } else { if( event_mode == SEQ_EVENT_MODE_Drum ) { // print drum name at the rightmost side SEQ_LCD_CursorSet(75, 0); SEQ_LCD_PrintTrackDrum(visible_track, ui_selected_instrument, (char *)seq_core_trk[visible_track].name); } else { // print trigger layer and name at the rightmost side SEQ_LCD_CursorSet(73, 0); SEQ_LCD_PrintFormattedString("%c:%s", 'A' + ui_selected_trg_layer, SEQ_TRG_AssignedTypeStr(visible_track, ui_selected_trg_layer)); } } } SEQ_LCD_CursorSet(0, 1); if( steps_per_item > 8 ) { SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_DrumSymbolsSmall); for(i=0; i<16; ++i) { u16 step = i*steps_per_item; u8 step8 = step / 8; u8 layer = (event_mode == SEQ_EVENT_MODE_Drum) ? 0 : ui_selected_trg_layer; u16 steps = (SEQ_TRG_Get8(visible_track, step8+1, layer, ui_selected_instrument) << 8) | (SEQ_TRG_Get8(visible_track, step8+0, layer, ui_selected_instrument) << 0); if( played_step >= step && played_step < (step+16) ) steps ^= (1 << (played_step % 16)); int j; for(j=0; j<5; ++j) { SEQ_LCD_PrintChar(steps & 0x7); steps >>= 3; } } } else if( steps_per_item > 4 ) {
///////////////////////////////////////////////////////////////////////////// // This task is running endless in background ///////////////////////////////////////////////////////////////////////////// void APP_Background(void) { // set LED depending on sequencer run state MIOS32_BOARD_LED_Set(1, SEQ_BPM_IsRunning() ? 1 : 0); }
///////////////////////////////////////////////////////////////////////////// // Called from SEQ_MIDI_IN_Receive() if MIDI event has been received on // matching IN port and channel ///////////////////////////////////////////////////////////////////////////// s32 SEQ_RECORD_Receive(mios32_midi_package_t midi_package, u8 track) { // step recording mode? // Note: if sequencer is not running, "Live Recording" will be handled like "Step Recording" u8 step_record_mode = seq_record_options.STEP_RECORD || !SEQ_BPM_IsRunning(); #if MBSEQV4L // extra for MBSEQ V4L: seq_record_state.ARMED_TRACKS and auto-assignment if( !seq_record_state.ARMED_TRACKS ) return 0; // no track armed track = 0; if( seq_record_state.ARMED_TRACKS & 0xff00) track = 8; // search for free track/layer if( (midi_package.event == NoteOn) || (midi_package.event == NoteOff) ) { // fine, we will record Note in selected track } else if( midi_package.event == PitchBend ) { track += 3; // G1T4 resp. G3T4 } else if( midi_package.event == CC ) { const u8 track_layer_cc_table[19][2] = { { 4, 0 }, { 5, 0 }, { 6, 0 }, { 7, 0 }, { 7, 1 }, { 7, 2 }, { 7, 3 }, { 6, 1 }, { 6, 2 }, { 6, 3 }, { 5, 1 }, { 5, 2 }, { 5, 3 }, { 4, 1 }, { 4, 2 }, { 4, 3 }, { 3, 1 }, { 3, 2 }, { 3, 3 }, }; // search for same (or free) CC entry // new track/layer search algorithm since V4L.082 u8 seq_track_offset = track; // depends on sequence int par_layer = 0; int i; u8 free_layer_found = 0; for(i=0; i<19 && !free_layer_found; ++i) { track = seq_track_offset + track_layer_cc_table[i][0]; par_layer = track_layer_cc_table[i][1]; seq_cc_trk_t *tcc = &seq_cc_trk[track]; u8 *layer_type_ptr = (u8 *)&tcc->lay_const[0*16 + par_layer]; u8 *layer_cc_ptr = (u8 *)&tcc->lay_const[1*16 + par_layer]; if( *layer_type_ptr == SEQ_PAR_Type_CC && (*layer_cc_ptr >= 0x80 || *layer_cc_ptr == midi_package.cc_number) && (seq_record_state.ARMED_TRACKS & (1 << track)) ) { if( *layer_cc_ptr >= 0x80 ) { *layer_cc_ptr = midi_package.cc_number; // assing CC number to free track // initialize whole layer with invalid value 0xc0 (indicates: not recorded) int num_p_steps = SEQ_PAR_NumStepsGet(track); int instrument = 0; int step; for(step=0; step<num_p_steps; ++step) SEQ_PAR_Set(track, step, par_layer, instrument, 0xc0); #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ_RECORD_Receive] free CC layer found for CC#%d in track #%d.%c\n", midi_package.cc_number, track+1, 'A'+par_layer); #endif } free_layer_found = 1; } } if( !free_layer_found ) { #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ_RECORD_Receive] no free CC layer found for CC#%d\n", midi_package.cc_number); #endif return 0; // no free layer } } else { return 0; // event not relevant } // exit if track not armed if( !(seq_record_state.ARMED_TRACKS & (1 << track)) ) return 0; #else // MBSEQV4 (without L) if( midi_package.event == CC && track == SEQ_UI_VisibleTrackGet() ) { // search for same (or free) CC entry seq_cc_trk_t *tcc = &seq_cc_trk[track]; u8 free_layer_found = 0; { u8 num_p_layers = SEQ_PAR_NumLayersGet(track); u8 *layer_type_ptr = (u8 *)&tcc->lay_const[0*16]; u8 *layer_cc_ptr = (u8 *)&tcc->lay_const[1*16]; int par_layer; for(par_layer=0; par_layer<num_p_layers && !free_layer_found; ++par_layer, ++layer_type_ptr, ++layer_cc_ptr) { if( *layer_type_ptr == SEQ_PAR_Type_CC && (*layer_cc_ptr >= 0x80 || *layer_cc_ptr == midi_package.cc_number) ) { if( *layer_cc_ptr >= 0x80 ) { *layer_cc_ptr = midi_package.cc_number; // assing CC number to free track // initialize whole layer with invalid value 0xc0 (indicates: not recorded) int num_p_steps = SEQ_PAR_NumStepsGet(track); int instrument = 0; int step; for(step=0; step<num_p_steps; ++step) SEQ_PAR_Set(track, step, par_layer, instrument, 0xc0); #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ_RECORD_Receive] free CC layer found for CC#%d in track #%d.%c\n", midi_package.cc_number, track+1, 'A'+par_layer); #endif } free_layer_found = 1; break; } } } if( !free_layer_found ) { #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ_RECORD_Receive] no free CC layer found for CC#%d\n", midi_package.cc_number); #endif return 0; // no free layer } } #endif #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ_RECORD_Receive] %02x %02x %02x -> track #%d\n", midi_package.evnt0, midi_package.evnt1, midi_package.evnt2, track+1); #endif // exit if track number too high if( track >= SEQ_CORE_NUM_TRACKS ) return -1; // unsupported track seq_core_trk_t *t = &seq_core_trk[track]; seq_cc_trk_t *tcc = &seq_cc_trk[track]; // branch depending on event u8 rec_event = 0; u8 send_note_off = 0; switch( midi_package.event ) { case NoteOff: case NoteOn: { midi_package.note &= 0x7f; // to avoid array overwrites u32 note_mask = 1 << (midi_package.note & 0x1f); // if Note Off and new note number matches with recorded note number if( midi_package.event == NoteOff || midi_package.velocity == 0 ) { if( seq_record_played_notes[midi_package.note>>5] & note_mask ) { MIOS32_IRQ_Disable(); // note not active anymore seq_record_played_notes[midi_package.note>>5] &= ~note_mask; // determine duration in mS (for step recording function) u16 duration_ms = MIOS32_TIMESTAMP_Get() - seq_record_note_timestamp_ms[midi_package.note]; // map to BPM int duration = (int)((float)duration_ms / ((1000.0*60.0) / SEQ_BPM_EffectiveGet() / (float)SEQ_BPM_PPQN_Get())); #if DEBUG_VERBOSE_LEVEL >= 3 DEBUG_MSG("[SEQ_RECORD_Receive] duration of note 0x%02x was %d mS (%d ticks)\n", midi_package.note, duration_ms, duration); #endif // insert length into current step u8 instrument = 0; int len; if( step_record_mode ) { len = 71; // 75% if( tcc->event_mode != SEQ_EVENT_MODE_Drum ) len = (duration <= 96) ? duration : 96; // for duration >= 96 the length will be stretched after record } else { len = SEQ_BPM_TickGet() - t->rec_timestamp; if( len < 1 ) len = 1; else if( len > 95 ) len = 95; } int len_step = step_record_mode ? ui_selected_step : t->step; u8 num_p_layers = SEQ_PAR_NumLayersGet(track); while( 1 ) { if( tcc->event_mode == SEQ_EVENT_MODE_Combined ) { // extra for MBSEQ V4L: // search for note in track 1/8, insert length into track 3/10 int par_layer; for(par_layer=0; par_layer<num_p_layers; ++par_layer) { if( SEQ_PAR_Get(track, len_step, par_layer, instrument) == midi_package.note ) { SEQ_PAR_Set(track+2, len_step, par_layer, instrument, len); break; } } } else { if( tcc->link_par_layer_length >= 0 ) SEQ_PAR_Set(track, len_step, tcc->link_par_layer_length, instrument, len); } if( !step_record_mode ) break; if( tcc->event_mode == SEQ_EVENT_MODE_Drum ) break; if( duration <= 0 ) break; duration -= 96; // insert length into all following steps until a gate is set if( ++len_step > tcc->length ) // TODO: handle this correctly if track is played backwards len_step = tcc->loop; if( SEQ_TRG_GateGet(track, len_step, instrument) ) break; len = (duration > 0) ? 96 : -duration; // copy notes u8 *layer_type_ptr = (u8 *)&tcc->lay_const[0*16]; int par_layer; for(par_layer=0; par_layer<num_p_layers; ++par_layer, ++layer_type_ptr) { if( *layer_type_ptr == SEQ_PAR_Type_Note || *layer_type_ptr == SEQ_PAR_Type_Chord ) { u8 note = SEQ_PAR_Get(track, ui_selected_step, par_layer, instrument); SEQ_PAR_Set(track, len_step, par_layer, instrument, note); } } } MIOS32_IRQ_Enable(); } if( step_record_mode && seq_record_options.FWD_MIDI ) { // send Note Off events of current track if no key is played anymore u8 any_note_played = seq_record_played_notes[0] || seq_record_played_notes[1] || seq_record_played_notes[2] || seq_record_played_notes[3]; if( !any_note_played ) send_note_off = 1; } } else { MIOS32_IRQ_Disable(); if( step_record_mode && tcc->event_mode != SEQ_EVENT_MODE_Drum ) { // check if another note is already played u8 any_note_played = seq_record_played_notes[0] || seq_record_played_notes[1] || seq_record_played_notes[2] || seq_record_played_notes[3]; // if not: clear poly counter and all notes (so that new chord can be entered if all keys were released) if( !any_note_played ) { t->rec_poly_ctr = 0; u8 num_p_layers = SEQ_PAR_NumLayersGet(track); u8 *layer_type_ptr = (u8 *)&tcc->lay_const[0*16]; int par_layer; u8 instrument = 0; for(par_layer=0; par_layer<num_p_layers; ++par_layer, ++layer_type_ptr) { if( *layer_type_ptr == SEQ_PAR_Type_Note || *layer_type_ptr == SEQ_PAR_Type_Chord ) SEQ_PAR_Set(track, ui_selected_step, par_layer, instrument, 0x00); } } } // note is active seq_record_played_notes[midi_package.note>>5] |= note_mask; // start measuring length t->rec_timestamp = SEQ_BPM_TickGet(); // for step record function: independent from BPM seq_record_note_timestamp_ms[midi_package.note & 0x7f] = MIOS32_TIMESTAMP_Get(); // note: 16bit only MIOS32_IRQ_Enable(); // record event rec_event = 1; } } break; case CC: case PitchBend: { rec_event = 1; } break; default: { #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ_RECORD_Receive] event %x not supported.\n", midi_package.event); #endif return -2; // unsupported event } }
///////////////////////////////////////////////////////////////////////////// // This hook is called before the shift register chain is scanned ///////////////////////////////////////////////////////////////////////////// void APP_SRIO_ServicePrepare(void) { static u8 led_digit_ctr = 0; if( ++led_digit_ctr >= 7 ) led_digit_ctr = 0; #ifndef MBSEQV4L if( seq_hwcfg_blm.enabled ) { // prepare DOUT registers of BLM to drive the column BLM_PrepareCol(); } #else BLM_CHEAPO_PrepareCol(); #endif if( seq_hwcfg_blm8x8.enabled ) { // prepare DOUT registers of 8x8 BLM to drive the row BLM_X_PrepareRow(); } // TK: using MIOS32_DOUT_SRSet/PinSet instead of SEQ_LED_SRSet/PinSet to ensure compatibility with MBSEQV4L if( seq_hwcfg_bpm_digits.enabled ) { // invert for common anodes u8 inversion_mask = (seq_hwcfg_bpm_digits.enabled == 2) ? 0xff : 0x00; u8 common_enable = (seq_hwcfg_bpm_digits.enabled == 2) ? 1 : 0; float bpm = SEQ_BPM_EffectiveGet(); if( led_digit_ctr == 0 ) { u8 sr_value = SEQ_LED_DigitPatternGet(((int)(bpm*10)) % 10); MIOS32_DOUT_SRSet(seq_hwcfg_bpm_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common1_pin, common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common3_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common4_pin, !common_enable); } else if( led_digit_ctr == 1 ) { u8 sr_value = SEQ_LED_DigitPatternGet((int)bpm % 10) | 0x80; // +dot MIOS32_DOUT_SRSet(seq_hwcfg_bpm_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common2_pin, common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common3_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common4_pin, !common_enable); } else if( led_digit_ctr == 2 ) { u8 sr_value = SEQ_LED_DigitPatternGet(((int)bpm / 10) % 10); MIOS32_DOUT_SRSet(seq_hwcfg_bpm_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common3_pin, common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common4_pin, !common_enable); } else if( led_digit_ctr == 3 ) { u8 sr_value = SEQ_LED_DigitPatternGet(((int)bpm / 100) % 10); MIOS32_DOUT_SRSet(seq_hwcfg_bpm_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common3_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common4_pin, common_enable); } else { // not displaying bpm digit in this cycle, disable common pins MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common3_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_bpm_digits.common4_pin, !common_enable); } } if( seq_hwcfg_step_digits.enabled ) { // invert for common anodes u8 inversion_mask = (seq_hwcfg_step_digits.enabled == 2) ? 0xff : 0x00; u8 common_enable = (seq_hwcfg_step_digits.enabled == 2) ? 1 : 0; int step = (int)(SEQ_BPM_IsRunning() ? seq_core_trk[SEQ_UI_VisibleTrackGet()].step : ui_selected_step) + 1; if( led_digit_ctr == 4 ) { u8 sr_value = SEQ_LED_DigitPatternGet(step % 10); MIOS32_DOUT_SRSet(seq_hwcfg_step_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common1_pin, common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common3_pin, !common_enable); } else if( led_digit_ctr == 5 ) { u8 sr_value = SEQ_LED_DigitPatternGet((step / 10) % 10); MIOS32_DOUT_SRSet(seq_hwcfg_step_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common2_pin, common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common3_pin, !common_enable); } else if( led_digit_ctr == 6 ) { u8 sr_value = SEQ_LED_DigitPatternGet((step / 100) % 10); MIOS32_DOUT_SRSet(seq_hwcfg_step_digits.segments_sr - 1, sr_value ^ inversion_mask); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common3_pin, common_enable); } else { // not displaying step digit in this cycle, disable common pins MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common1_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common2_pin, !common_enable); MIOS32_DOUT_PinSet(seq_hwcfg_step_digits.common3_pin, !common_enable); } } SEQ_TPD_LED_Update(); }
///////////////////////////////////////////////////////////////////////////// //! This function can overrule the display output //! If it returns 0, the original SCS output will be print //! If it returns 1, the output copied into line1 and/or line2 will be print //! If a line is not changed (line[0] = 0 or line[1] = 0), the original output //! will be displayed - this allows to overrule only a single line ///////////////////////////////////////////////////////////////////////////// static s32 displayHook(char *line1, char *line2) { static u8 special_chars_from_mbng = 0; // switch between charsets if( SCS_MenuStateGet() == SCS_MENU_STATE_MAINPAGE && !special_chars_from_mbng ) { special_chars_from_mbng = 1; MBNG_LCD_SpecialCharsReInit(); } else if( SCS_MenuStateGet() != SCS_MENU_STATE_MAINPAGE && special_chars_from_mbng ) { special_chars_from_mbng = 0; SCS_LCD_SpecialCharsReInit(); } if( SCS_MenuStateGet() != SCS_MENU_STATE_MAINPAGE && extraPage ) { char msdStr[5]; TASK_MSD_FlagStrGet(msdStr); u8 clkMode = SEQ_BPM_ModeGet(); sprintf(line1, "Clk BPM DOUT MSD "); sprintf(line2, "%s %s Off %s", SEQ_BPM_IsRunning() ? "STOP" : "PLAY", (clkMode == SEQ_BPM_MODE_Slave) ? "Slve" : ((clkMode == SEQ_BPM_MODE_Master) ? "Mstr" : "Auto"), TASK_MSD_EnableGet() ? msdStr : "----"); return 1; } // overlay in MSD mode (user should disable soon since this sucks performance) if( TASK_MSD_EnableGet() ) { char msdStr[5]; TASK_MSD_FlagStrGet(msdStr); sprintf(line1, "[ MSD ACTIVE: %s ]", msdStr); } if( SCS_MenuStateGet() == SCS_MENU_STATE_MAINPAGE ) { // overlayed by LCD handler in MBNG_LCD! // only exception: not in MIDI learn mode if( MBNG_EVENT_MidiLearnModeGet() ) { MBNG_EVENT_MidiLearnStatusMsg(line1, line2); SCS_DisplayUpdateRequest(); // fast updates } else if( MBNG_EVENT_EventLearnIdGet() ) { MBNG_EVENT_EventLearnStatusMsg(line1, line2); SCS_DisplayUpdateRequest(); // fast updates } return 1; } if( SCS_MenuStateGet() == SCS_MENU_STATE_SELECT_PAGE ) { if( line1[0] == 0 ) { // no MSD overlay? if( MBNG_FILE_StatusMsgGet() ) sprintf(line1, MBNG_FILE_StatusMsgGet()); else sprintf(line1, "Config: %s", mbng_file_c_config_name); } return 1; } if( SCS_MenuPageGet() == pageDsk ) { // Disk page: we want to show the patch at upper line, and menu items at lower line if( line1[0] == 0 ) { // no MSD overlay? if( MBNG_FILE_StatusMsgGet() ) sprintf(line1, MBNG_FILE_StatusMsgGet()); else sprintf(line1, "Config: %s", mbng_file_c_config_name); } sprintf(line2, "Load Save"); return 1; } if( SCS_MenuPageGet() == pageMON ) { u8 fastRefresh = line1[0] == 0; int i; for(i=0; i<SCS_NumMenuItemsGet(); ++i) { u8 portIx = 1 + i + monPageOffset; if( fastRefresh ) { // no MSD overlay? mios32_midi_port_t port = MIDI_PORT_InPortGet(portIx); mios32_midi_package_t package = MIDI_PORT_InPackageGet(port); if( port == 0xff ) { strcat(line1, " "); } else if( package.type ) { char buffer[6]; MIDI_PORT_EventNameGet(package, buffer, 5); strcat(line1, buffer); } else { strcat(line1, MIDI_PORT_InNameGet(portIx)); strcat(line1, " "); } // insert arrow at upper right corner int numItems = MIDI_PORT_OutNumGet() - 1; int lastColumn = SCS_NumMenuItemsGet()*SCS_MENU_ITEM_WIDTH - 1; if( monPageOffset == 0 ) line1[lastColumn] = MIOS32_LCD_TypeIsGLCD() ? '>' : 3; // right arrow else if( monPageOffset >= (numItems-SCS_NumMenuItemsGet()) ) line1[lastColumn] = MIOS32_LCD_TypeIsGLCD() ? '<' : 1; // left arrow else line1[lastColumn] = MIOS32_LCD_TypeIsGLCD() ? '-' : 2; // left/right arrow } mios32_midi_port_t port = MIDI_PORT_OutPortGet(portIx); mios32_midi_package_t package = MIDI_PORT_OutPackageGet(port); if( port == 0xff ) { strcat(line2, " "); } else if( package.type ) { char buffer[6]; MIDI_PORT_EventNameGet(package, buffer, 5); strcat(line2, buffer); } else { strcat(line2, MIDI_PORT_OutNameGet(portIx)); strcat(line2, " "); } } // request LCD update - this will lead to fast refresh rate in monitor screen if( fastRefresh ) SCS_DisplayUpdateRequest(); return 1; } if( SCS_MenuPageGet() == pageLearn ) { if( MBNG_EVENT_MidiLearnModeGet() ) { MBNG_EVENT_MidiLearnStatusMsg(line1, line2); } else { if( line1[0] == 0 ) { // no MSD overlay? sprintf(line1, " Common NRPN "); } sprintf(line2, " Learn Learn "); } // fast updates SCS_DisplayUpdateRequest(); return 1; } return (line1[0] != 0) ? 1 : 0; // return 1 if MSD overlay }
static void stringStop(u32 ix, u16 value, char *label) { sprintf(label, SEQ_BPM_IsRunning() ? " " : " ** "); }
static void stringPlay(u32 ix, u16 value, char *label) { sprintf(label, SEQ_BPM_IsRunning() ? " ** " : " "); }