///////////////////////////////////////////////////////////////////////////// // Called when a GP button has been toggled ///////////////////////////////////////////////////////////////////////////// s32 SEQ_UI_PAGES_GP_Button_Handler(u8 button, u8 depressed) { if( ui_controller_mode ) return SEQ_UI_PAGES_GP_Button_Handler_Controller(button, depressed); // no page reacts on depressed buttons if( depressed ) return 0; // clear button pressed: clear track if( seq_ui_button_state.CLEAR ) { u8 seq = (button >= 8) ? 1 : 0; u8 track_offset = seq ? 8 : 0; ui_selected_tracks = seq ? 0xff00 : 0x00ff; seq_record_state.ARMED_TRACKS = seq ? 0xff00 : 0x00ff; u8 seq_button = button % 8; switch( seq_button ) { case 0: { // clear note triggers (values are kept but not played) u8 track = track_offset; int num_t_steps = SEQ_TRG_NumStepsGet(track); int step8; u8 instrument = 0; u8 layer = 0; for(step8=0; step8<(num_t_steps/8); ++step8) SEQ_TRG_Set8(track, step8, layer, instrument, 0x00); } break; case 1: { // reset velocity values to 64 u8 track = track_offset + 1; int num_p_layers = SEQ_PAR_NumLayersGet(track); int par_layer; for(par_layer=0; par_layer<num_p_layers; ++par_layer) { int num_p_instruments = SEQ_PAR_NumInstrumentsGet(track); int num_p_steps = SEQ_PAR_NumStepsGet(track);; u8 init_value = 64; int step; int instrument; for(instrument=0; instrument<num_p_instruments; ++instrument) for(step=0; step<num_p_steps; ++step) SEQ_PAR_Set(track, step, par_layer, instrument, init_value); } } break; case 2: { // reset note length values to half step u8 track = track_offset + 2; int num_p_layers = SEQ_PAR_NumLayersGet(track); int par_layer; for(par_layer=0; par_layer<num_p_layers; ++par_layer) { int num_p_instruments = SEQ_PAR_NumInstrumentsGet(track); int num_p_steps = SEQ_PAR_NumStepsGet(track);; u8 init_value = 51; // half length int step; int instrument; for(instrument=0; instrument<num_p_instruments; ++instrument) for(step=0; step<num_p_steps; ++step) SEQ_PAR_Set(track, step, par_layer, instrument, init_value); } } break; default: { u8 track = track_offset + seq_button; if( seq_button == 3 ) { // special treatmend for pitchbend int par_layer = 0; { int num_p_instruments = SEQ_PAR_NumInstrumentsGet(track); int num_p_steps = SEQ_PAR_NumStepsGet(track);; u8 init_value = 64; int step; int instrument; for(instrument=0; instrument<num_p_instruments; ++instrument) for(step=0; step<num_p_steps; ++step) SEQ_PAR_Set(track, step, par_layer, instrument, init_value); } // send pitchbend 0 for proper reset of MIDI device SEQ_LAYER_DirectSendEvent(track, par_layer); } // clear CCs { int num_p_layers = SEQ_PAR_NumLayersGet(track); int par_layer; for(par_layer=(track == 3) ? 1 : 0; par_layer<num_p_layers; ++par_layer) { // don't touch pitchbender int num_p_instruments = SEQ_PAR_NumInstrumentsGet(track); int num_p_steps = SEQ_PAR_NumStepsGet(track);; u8 init_value = 64; int step; int instrument; for(instrument=0; instrument<num_p_instruments; ++instrument) for(step=0; step<num_p_steps; ++step) SEQ_PAR_Set(track, step, par_layer, instrument, init_value); // disable CC assignment SEQ_CC_Set(track, SEQ_CC_LAY_CONST_B1+par_layer, 0x80); } } } } return 0; } switch( ui_page ) { /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_LOAD: case SEQ_UI_PAGE_SAVE: { // not atomic so that files can be stored in background //portENTER_CRITICAL(); u8 group; for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { seq_pattern_t *pattern = &ui_selected_pattern[group]; pattern->bank = group; // always same as group if( button < 8 ) { pattern->group = button; ui_selected_pattern_changing = 1; } else { pattern->num = button-8; ui_selected_pattern_changing = 0; if( ui_page == SEQ_UI_PAGE_SAVE ) { //DEBUG_MSG("BEGIN Save %d:%c%d\n", pattern->bank+1, 'A'+pattern->group, pattern->num+1); s32 status = 0; if( (status=SEQ_PATTERN_Save(group, *pattern)) < 0 ) SEQ_UI_SDCardErrMsg(2000, status); else load_save_notifier_ctr = 300; // notify about save operation for 300 mS //DEBUG_MSG("END Save %d:%c%d\n", pattern->bank+1, 'A'+pattern->group, pattern->num+1); } else { //DEBUG_MSG("BEGIN Load %d:%c%d\n", pattern->bank+1, 'A'+pattern->group, pattern->num+1); SEQ_PATTERN_Change(group, *pattern, 0); load_save_notifier_ctr = 300; // notify about load operation for 300 mS //DEBUG_MSG("END Load %d:%c%d\n", pattern->bank+1, 'A'+pattern->group, pattern->num+1); } } } //portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_TRIGGER: { // should be atomic portENTER_CRITICAL(); ui_selected_step = button; u8 track; // only change triggers if track 0 and 8 for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) if( ui_selected_tracks & (1 << track) ) { u8 *trg_ptr = (u8 *)&seq_trg_layer_value[track][2*ui_selected_step_view + (button>>3)]; *trg_ptr ^= (1 << (button&7)); } portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_LENGTH: { // should be atomic portENTER_CRITICAL(); u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { if( ui_selected_tracks & (1 << track) ) { if( seq_ui_button_state.LENGTH_PRESSED ) { if( (track >= 0 && track <= 2) || (track >= 8 && track <= 10) ) SEQ_CC_Set(track, SEQ_CC_LOOP, 16*ui_selected_step_view + button); else SEQ_CC_Set(track, SEQ_CC_LOOP, 64*ui_selected_step_view + 4*button + 3); // 4x resolution } else { if( (track >= 0 && track <= 2) || (track >= 8 && track <= 10) ) SEQ_CC_Set(track, SEQ_CC_LENGTH, 16*ui_selected_step_view + button); else SEQ_CC_Set(track, SEQ_CC_LENGTH, 64*ui_selected_step_view + 4*button + 3); // 4x resolution } } } portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_PROGRESSION: { // should be atomic portENTER_CRITICAL(); ui_selected_progression_preset = button; seq_ui_pages_progression_presets_t *preset = (seq_ui_pages_progression_presets_t *)&seq_ui_pages_progression_presets[ui_selected_progression_preset]; u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) { if( ui_selected_tracks & (1 << track) ) { SEQ_CC_Set(track, SEQ_CC_STEPS_FORWARD, (preset->steps_forward > 0) ? (preset->steps_forward-1) : 0); SEQ_CC_Set(track, SEQ_CC_STEPS_JMPBCK, preset->steps_jump_back); SEQ_CC_Set(track, SEQ_CC_STEPS_REPLAY, preset->steps_replay); SEQ_CC_Set(track, SEQ_CC_STEPS_REPEAT, preset->steps_repeat); SEQ_CC_Set(track, SEQ_CC_STEPS_SKIP, preset->steps_skip); SEQ_CC_Set(track, SEQ_CC_STEPS_RS_INTERVAL, preset->steps_rs_interval); } } // GP1 also requests synch to measure // it's a good idea to do this for all tracks so that both sequences are in synch again if( button == 0 ) SEQ_CORE_ManualSynchToMeasure(0xffff); portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_GROOVE: { // should be atomic portENTER_CRITICAL(); // first button turns off groove function // remaining buttons select custom groove #1..15 u8 groove_style = 0; if( button > 0 ) // note: starting at second custom groove, the first groove is always "off" groove_style = button+8-1; SEQ_UI_CC_Set(SEQ_CC_GROOVE_STYLE, groove_style); portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_ECHO: { // should be atomic portENTER_CRITICAL(); ui_selected_echo_preset = button; seq_ui_pages_echo_presets_t *preset = (seq_ui_pages_echo_presets_t *)&seq_ui_pages_echo_presets[ui_selected_echo_preset]; u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) { if( ui_selected_tracks & (1 << track) ) { SEQ_CC_Set(track, SEQ_CC_ECHO_REPEATS, preset->repeats); SEQ_CC_Set(track, SEQ_CC_ECHO_DELAY, preset->delay); SEQ_CC_Set(track, SEQ_CC_ECHO_VELOCITY, preset->velocity); SEQ_CC_Set(track, SEQ_CC_ECHO_FB_VELOCITY, preset->fb_velocity); SEQ_CC_Set(track, SEQ_CC_ECHO_FB_NOTE, preset->fb_note); SEQ_CC_Set(track, SEQ_CC_ECHO_FB_GATELENGTH, preset->fb_gatelength); SEQ_CC_Set(track, SEQ_CC_ECHO_FB_TICKS, preset->fb_ticks); } } portEXIT_CRITICAL(); } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_HUMANIZER: { // should be atomic portENTER_CRITICAL(); ui_selected_humanizer_preset = button; seq_ui_pages_humanizer_presets_t *preset = (seq_ui_pages_humanizer_presets_t *)&seq_ui_pages_humanizer_presets[ui_selected_humanizer_preset]; u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) { if( ui_selected_tracks & (1 << track) ) { SEQ_CC_Set(track, SEQ_CC_HUMANIZE_MODE, preset->mode); SEQ_CC_Set(track, SEQ_CC_HUMANIZE_VALUE, preset->value); } } portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_LFO: { // should be atomic portENTER_CRITICAL(); ui_selected_lfo_preset = button; seq_ui_pages_lfo_presets_t *preset = (seq_ui_pages_lfo_presets_t *)&seq_ui_pages_lfo_presets[ui_selected_lfo_preset]; u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) { if( ui_selected_tracks & (1 << track) ) { SEQ_CC_Set(track, SEQ_CC_LFO_WAVEFORM, preset->waveform); SEQ_CC_Set(track, SEQ_CC_LFO_AMPLITUDE, preset->amplitude); SEQ_CC_Set(track, SEQ_CC_LFO_PHASE, preset->phase); SEQ_CC_Set(track, SEQ_CC_LFO_STEPS, preset->steps); SEQ_CC_Set(track, SEQ_CC_LFO_STEPS_RST, preset->steps_rst); SEQ_CC_Set(track, SEQ_CC_LFO_ENABLE_FLAGS, preset->enable_flags); SEQ_CC_Set(track, SEQ_CC_LFO_CC, preset->cc); SEQ_CC_Set(track, SEQ_CC_LFO_CC_OFFSET, preset->cc_offset); SEQ_CC_Set(track, SEQ_CC_LFO_CC_PPQN, preset->cc_ppqn); } } portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_SCALE: { // should be atomic portENTER_CRITICAL(); // if SCALE button not pressed: select root key if( !seq_ui_button_state.SCALE_PRESSED ) { if( button < 12 ) { seq_core_global_scale_root_selection = button + 1; } else { seq_core_global_scale_root_selection = 0; // via keyboard } } else { // if pressed: select scale ui_selected_scale = button; if( ui_selected_scale == 0 ) { // disable force-to-scale for both note tracks (makes sense, since the scale itself is global as well) u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) seq_cc_trk[track].mode.FORCE_SCALE = 0; } else { // select scale seq_core_global_scale = seq_ui_pages_scale_presets[ui_selected_scale]; // enable force-to-scale for both note tracks (makes sense, since the scale itself is global as well) u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) seq_cc_trk[track].mode.FORCE_SCALE = 1; } } portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_MUTE: { // should be atomic portENTER_CRITICAL(); seq_core_trk_muted ^= (1 << button); portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_MIDICHN: { // should be atomic portENTER_CRITICAL(); u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) if( ui_selected_tracks & (1 << track) ) SEQ_CC_Set(track, SEQ_CC_MIDI_CHANNEL, button); portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_REC_ARM: { // should be atomic portENTER_CRITICAL(); // allow to select an unplayed track if( button >= 8 && (seq_record_state.ARMED_TRACKS & 0x00ff) ) seq_record_state.ARMED_TRACKS = 0xff00; else if( button < 8 && (seq_record_state.ARMED_TRACKS & 0xff00) ) seq_record_state.ARMED_TRACKS = 0x00ff; else { seq_record_state.ARMED_TRACKS ^= (1 << button); } portEXIT_CRITICAL(); return 0; } break; /////////////////////////////////////////////////////////////////////////// case SEQ_UI_PAGE_REC_STEP: { case SEQ_UI_PAGE_REC_LIVE: // should be atomic portENTER_CRITICAL(); seq_record_step = 16*ui_selected_step_view + button; // we will always clear the current step for more comfortable handling // (no need to select the Trigger page for doing this) u8 track; // only change triggers if track 0 and 8 for(track=0; track<SEQ_CORE_NUM_TRACKS; track+=8) if( seq_record_state.ARMED_TRACKS & (1 << track) ) { u8 *trg_ptr = (u8 *)&seq_trg_layer_value[track][2*ui_selected_step_view + (button>>3)]; *trg_ptr &= ~(1 << (button&7)); SEQ_RECORD_Reset(track); } portEXIT_CRITICAL(); return 0; } break;
///////////////////////////////////////////////////////////////////////////// // 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) { // ensure that original screen will be print immediately ui_hold_msg_ctr = 0; #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 // select step within view ui_selected_step = (ui_selected_step_view << 4) | (u8)encoder; // show edit page for 2 seconds ui_hold_msg_ctr = 2000; // forward manual trigger to SEQ_CORE SEQ_CORE_ManualTrigger(ui_selected_step); 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( depressed ) return 0; // ignore when button depressed // ensure that original screen will be print immediately ui_hold_msg_ctr = 0; #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: { // Request synch to measure for all selected tracks SEQ_CORE_ManualSynchToMeasure(); return 1; } break; 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) { // branch to edit page if requested if( ui_hold_msg_ctr ) return SEQ_UI_EDIT_LCD_Handler(high_prio, SEQ_UI_EDIT_MODE_MANUAL); // layout: // 00000000001111111111222222222233333333330000000000111111111122222222223333333333 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 // <--------------------------------------><--------------------------------------> // Selected Tracks: 1234 1234 1234 1234 Press SELECT to Synch to Measure! // lower line: print step 1..16/17..32/... /////////////////////////////////////////////////////////////////////////// SEQ_LCD_CursorSet(0, 0); SEQ_LCD_PrintString(" Selected Tracks: "); u8 track; for(track=0; track<16; ++track) { if( seq_core_trk[track].state.SYNC_MEASURE ) SEQ_LCD_PrintChar('S'); else if( SEQ_UI_IsSelectedTrack(track) ) SEQ_LCD_PrintChar('*'); else SEQ_LCD_PrintChar('1' + (track&3)); if( (track&3) == 3 ) SEQ_LCD_PrintChar(' '); } SEQ_LCD_PrintSpaces(1); SEQ_LCD_PrintString(" Press SELECT to Synch to Measure! "); /////////////////////////////////////////////////////////////////////////// SEQ_LCD_CursorSet(0, 1); u8 step; for(step=0; step<16; ++step) SEQ_LCD_PrintFormattedString(" %2d ", 16*ui_selected_step_view + step + 1); return 0; // no error } ///////////////////////////////////////////////////////////////////////////// // Initialisation ///////////////////////////////////////////////////////////////////////////// s32 SEQ_UI_MANUAL_Init(u32 mode) { // install callback routines SEQ_UI_InstallButtonCallback(Button_Handler); SEQ_UI_InstallEncoderCallback(Encoder_Handler); SEQ_UI_InstallLEDCallback(LED_Handler); SEQ_UI_InstallLCDCallback(LCD_Handler); return 0; // no error }