///////////////////////////////////////////////////////////////////////////// // 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 // GP button selects preset if( button <= SEQ_UI_BUTTON_GP16 ) { // set preset seq_core_bpm_preset_num = button; // change Tempo SEQ_CORE_BPM_Update(seq_core_bpm_preset_tempo[seq_core_bpm_preset_num], seq_core_bpm_preset_ramp[seq_core_bpm_preset_num]); #if 0 // and enter BPM page again SEQ_UI_PageSet(SEQ_UI_PAGE_BPM); #endif // (NO!) return 1; } // TODO // too bad, that the menu handling concept doesn't allow to react on exit button here // this would allow to exit preset selection mode w/o exiting BPM page return -1; // ignore remaining buttons }
///////////////////////////////////////////////////////////////////////////// // 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 // reset tap tempo if any button != GP16 has been pressed if( button != SEQ_UI_BUTTON_GP16 ) resetTapTempo(); #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 switch( button ) { case SEQ_UI_BUTTON_GP6: case SEQ_UI_BUTTON_GP7: // fire preset SEQ_CORE_BPM_Update(seq_core_bpm_preset_tempo[seq_core_bpm_preset_num], seq_core_bpm_preset_ramp[seq_core_bpm_preset_num]); return 1; case SEQ_UI_BUTTON_GP8: // enter preset selection page SEQ_UI_PageSet(SEQ_UI_PAGE_BPM_PRESETS); return 1; } // re-use encoder handler - only select UI item, don't increment return Encoder_Handler((int)button, 0); } // remaining buttons: switch( button ) { case SEQ_UI_BUTTON_Select: case SEQ_UI_BUTTON_Right: if( ++ui_selected_item >= NUM_OF_ITEMS ) ui_selected_item = 0; return 1; // value always changed case SEQ_UI_BUTTON_Left: if( ui_selected_item == 0 ) ui_selected_item = NUM_OF_ITEMS-1; else --ui_selected_item; return 1; // value always changed case SEQ_UI_BUTTON_Up: return Encoder_Handler(SEQ_UI_ENCODER_Datawheel, 1); case SEQ_UI_BUTTON_Down: 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) { if( high_prio ) return 0; // there are no high-priority updates // layout: // 00000000001111111111222222222233333333330000000000111111111122222222223333333333 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 // <--------------------------------------><--------------------------------------> // Mode Preset Tempo Ramp Fire Preset MClk In/Out Ext. Tap // Master 1 140.0 1s Preset Page USB1 I:on O:off Restart Tempo /////////////////////////////////////////////////////////////////////////// SEQ_LCD_CursorSet(0, 0); SEQ_LCD_PrintString(" Mode Preset Tempo Ramp Fire Preset MClk In/Out "); SEQ_LCD_PrintString(seq_core_state.EXT_RESTART_REQ ? "Ongoing" : " Ext. "); SEQ_LCD_PrintString(" Tap "); /////////////////////////////////////////////////////////////////////////// SEQ_LCD_CursorSet(0, 1); if( ui_selected_item == ITEM_MODE && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(6); } else { const char mode_str[3][7] = { " Auto ", "Master", "Slave "}; SEQ_LCD_PrintString((char *)mode_str[SEQ_BPM_ModeGet()]); } SEQ_LCD_PrintSpaces(2); /////////////////////////////////////////////////////////////////////////// if( ui_selected_item == ITEM_PRESET && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(2); } else { SEQ_LCD_PrintFormattedString("%2d", seq_core_bpm_preset_num+1); } SEQ_LCD_PrintSpaces(3); /////////////////////////////////////////////////////////////////////////// if( ui_selected_item == ITEM_BPM && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(5); } else { float bpm = seq_core_bpm_preset_tempo[seq_core_bpm_preset_num]; SEQ_LCD_PrintFormattedString("%3d.%d", (int)bpm, (int)(10*bpm)%10); } SEQ_LCD_PrintSpaces(2); /////////////////////////////////////////////////////////////////////////// if( ui_selected_item == ITEM_RAMP && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(3); } else { float ramp = seq_core_bpm_preset_ramp[seq_core_bpm_preset_num]; SEQ_LCD_PrintFormattedString("%2ds", (int)ramp); } /////////////////////////////////////////////////////////////////////////// SEQ_LCD_PrintString(" Preset Page "); /////////////////////////////////////////////////////////////////////////// if( ui_selected_item == ITEM_MCLK_PORT && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(4); } else { SEQ_LCD_PrintString(SEQ_MIDI_PORT_ClkNameGet(SEQ_MIDI_PORT_ClkIxGet(selected_mclk_port))); } SEQ_LCD_PrintSpaces(1); /////////////////////////////////////////////////////////////////////////// SEQ_LCD_PrintString("I:"); if( ui_selected_item == ITEM_MCLK_IN && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(3); } else { s32 status = SEQ_MIDI_ROUTER_MIDIClockInGet(selected_mclk_port); if( !SEQ_MIDI_PORT_ClkCheckAvailable(selected_mclk_port) ) status = -1; // MIDI In port not available switch( status ) { case 0: SEQ_LCD_PrintString("off"); break; case 1: SEQ_LCD_PrintString("on "); break; default: SEQ_LCD_PrintString("---"); } } SEQ_LCD_PrintSpaces(1); /////////////////////////////////////////////////////////////////////////// SEQ_LCD_PrintString("O:"); if( ui_selected_item == ITEM_MCLK_OUT && ui_cursor_flash ) { SEQ_LCD_PrintSpaces(3); } else { s32 status = SEQ_MIDI_ROUTER_MIDIClockOutGet(selected_mclk_port); if( !SEQ_MIDI_PORT_ClkCheckAvailable(selected_mclk_port) ) status = -1; // MIDI Out port not available switch( status ) { case 0: SEQ_LCD_PrintString("off"); break; case 1: SEQ_LCD_PrintString("on "); break; default: SEQ_LCD_PrintString("---"); } } SEQ_LCD_PrintSpaces(3); // DIN Sync moved to CV configuration SEQ_LCD_PrintSpaces(3+4); /////////////////////////////////////////////////////////////////////////// SEQ_LCD_PrintString("Restart Tempo"); return 0; // no error } ///////////////////////////////////////////////////////////////////////////// // Local exit function ///////////////////////////////////////////////////////////////////////////// static s32 EXIT_Handler(void) { s32 status = 0; if( store_file_required ) { // write config file MUTEX_SDCARD_TAKE; if( (status=SEQ_FILE_C_Write(seq_file_session_name)) < 0 ) SEQ_UI_SDCardErrMsg(2000, status); MUTEX_SDCARD_GIVE; store_file_required = 0; } return status; }
///////////////////////////////////////////////////////////////////////////// // 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) { // reset tap tempo if any encoder != GP16 has been pressed if( encoder != SEQ_UI_ENCODER_GP16 ) resetTapTempo(); switch( encoder ) { case SEQ_UI_ENCODER_GP1: ui_selected_item = ITEM_MODE; break; case SEQ_UI_ENCODER_GP2: ui_selected_item = ITEM_PRESET; break; case SEQ_UI_ENCODER_GP3: ui_selected_item = ITEM_BPM; // special feature: these two encoders increment *10 incrementer *= 10; break; case SEQ_UI_ENCODER_GP4: ui_selected_item = ITEM_BPM; break; case SEQ_UI_ENCODER_GP5: ui_selected_item = ITEM_RAMP; break; case SEQ_UI_ENCODER_GP6: case SEQ_UI_ENCODER_GP7: case SEQ_UI_ENCODER_GP8: return -1; // not mapped to encoder case SEQ_UI_ENCODER_GP9: ui_selected_item = ITEM_MCLK_PORT; break; case SEQ_UI_ENCODER_GP10: ui_selected_item = ITEM_MCLK_IN; break; case SEQ_UI_ENCODER_GP11: ui_selected_item = ITEM_MCLK_OUT; break; case SEQ_UI_ENCODER_GP12: case SEQ_UI_ENCODER_GP13: return -1; // not used (yet) case SEQ_UI_ENCODER_GP14: case SEQ_UI_ENCODER_GP15: // external restart request should be atomic portENTER_CRITICAL(); seq_core_state.EXT_RESTART_REQ = 1; portEXIT_CRITICAL(); return 1; case SEQ_UI_ENCODER_GP16: SEQ_UI_BPM_TapTempo(); return 1; } // for GP encoders and Datawheel switch( ui_selected_item ) { case ITEM_MODE: { u8 value = SEQ_BPM_ModeGet(); if( SEQ_UI_Var8_Inc(&value, 0, 2, incrementer) ) { SEQ_BPM_ModeSet(value); store_file_required = 1; return 1; // value has been changed } else return 0; // value hasn't been changed } break; case ITEM_PRESET: { return SEQ_UI_Var8_Inc(&seq_core_bpm_preset_num, 0, SEQ_CORE_NUM_BPM_PRESETS-1, incrementer); } break; case ITEM_BPM: { u16 value = (u16)(seq_core_bpm_preset_tempo[seq_core_bpm_preset_num]*10); if( SEQ_UI_Var16_Inc(&value, 25, 3000, incrementer) ) { // at 384ppqn, the minimum BPM rate is ca. 2.5 // set new BPM seq_core_bpm_preset_tempo[seq_core_bpm_preset_num] = (float)value/10.0; SEQ_CORE_BPM_Update(seq_core_bpm_preset_tempo[seq_core_bpm_preset_num], seq_core_bpm_preset_ramp[seq_core_bpm_preset_num]); store_file_required = 1; return 1; // value has been changed } else return 0; // value hasn't been changed } break; case ITEM_RAMP: { u16 value = (u16)seq_core_bpm_preset_ramp[seq_core_bpm_preset_num]; if( SEQ_UI_Var16_Inc(&value, 0, 99, incrementer) ) { seq_core_bpm_preset_ramp[seq_core_bpm_preset_num] = (float)value; store_file_required = 1; return 1; // value has been changed } else return 0; // value hasn't been changed } break; case ITEM_MCLK_PORT: { u8 port_ix = SEQ_MIDI_PORT_ClkIxGet(selected_mclk_port); if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_ClkNumGet()-1, incrementer) >= 0 ) { selected_mclk_port = SEQ_MIDI_PORT_ClkPortGet(port_ix); return 1; // value changed } return 0; // no change } break; case ITEM_MCLK_IN: { s32 status = SEQ_MIDI_ROUTER_MIDIClockInGet(selected_mclk_port); if( status < 0 ) return 0; // no change u8 enable = status; if( SEQ_UI_Var8_Inc(&enable, 0, 1, incrementer) >= 0 ) { SEQ_MIDI_ROUTER_MIDIClockInSet(selected_mclk_port, enable); store_file_required = 1; return 1; // value changed } return 0; // no change } break; case ITEM_MCLK_OUT: { s32 status = SEQ_MIDI_ROUTER_MIDIClockOutGet(selected_mclk_port); if( status < 0 ) return 0; // no change u8 enable = status; if( SEQ_UI_Var8_Inc(&enable, 0, 1, incrementer) >= 0 ) { SEQ_MIDI_ROUTER_MIDIClockOutSet(selected_mclk_port, enable); store_file_required = 1; return 1; // value changed } return 0; // no change } break; } return -1; // invalid or unsupported encoder }
///////////////////////////////////////////////////////////////////////////// // 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( SEQ_FILE_FormattingRequired() ) return 0; // no encoder action as long as files not available switch( selected_page ) { /////////////////////////////////////////////////////////////////////////// // REMIX PAGE case PAGE_REMIX: { // TODO: finish the incrementer helper for gp encoders if( encoder <= SEQ_UI_ENCODER_GP16 ) { if ( preview_mode ) { // the remix state if( ((1 << encoder) | seq_pattern_remix_map) == seq_pattern_remix_map ) { // if we already got the requested bit setup // unset him inside remix map seq_pattern_remix_map &= ~(1 << encoder); } else { // else setup it! // set him inside remix map seq_pattern_remix_map |= (1 << encoder); } } else { // the demix state // in this state we can not mix, just turn mixed states track into demixed state if( ((1 << encoder) | seq_pattern_remix_map) == seq_pattern_remix_map ) { // if we already got the requested bit setup inside remix map // unset him inside remix map seq_pattern_remix_map &= ~(1 << encoder); } else if ( ((1 << encoder) | seq_pattern_demix_map) == seq_pattern_demix_map ) { // if we already got the requested bit setup demix map // set him inside remix map seq_pattern_remix_map |= (1 << encoder); } } if ( !preview_mode && ableton_api) { // send slot play envet to ableton cc (111 + track) with value 127 to channel 16 MIOS32_MIDI_SendCC(ableton_port, 15, (111 + encoder), 127); } } } break; /////////////////////////////////////////////////////////////////////////// // OPTION PAGE case PAGE_OPTION: { } break; /////////////////////////////////////////////////////////////////////////// // PATTERN NAME EDIT PAGE case PAGE_NAME_EDIT: { u8 group; s32 status; // if we got a OK button pressed if (encoder==SEQ_UI_ENCODER_GP16) { // Get back to main page selected_page = PAGE_MAIN; return 1; } if( (status=SEQ_UI_KeyPad_Handler(encoder, incrementer, (char *)&seq_pattern_name[0], 20)) < 0 ) { // error return 0; } else { // copy the name of group 1 pattern to all other pattern groups for(group=1; group<SEQ_CORE_NUM_GROUPS; ++group) { //SEQ_LABEL_CopyPresetCategory(seq_pattern[0].num, (char *)&seq_pattern_name[group][0]); sprintf(seq_pattern_name[group], seq_pattern_name[0]); } // copy the pattern name sprintf(pattern_name, seq_pattern_name[0]); } return 1; } break; /////////////////////////////////////////////////////////////////////////// // TRACK DELAY PAGE case PAGE_TRK_DELAY: { } break; /////////////////////////////////////////////////////////////////////////// // MAIN PAGE case PAGE_MAIN: { switch( encoder ) { case SEQ_UI_ENCODER_Datawheel: incrementer = incrementer; u16 value = (u16)(seq_core_bpm_preset_tempo[seq_core_bpm_preset_num]*10); if( SEQ_UI_Var16_Inc(&value, 25, 3000, incrementer) ) { // at 384ppqn, the minimum BPM rate is ca. 2.5 // set new BPM seq_core_bpm_preset_tempo[seq_core_bpm_preset_num] = (float)value/10.0; SEQ_CORE_BPM_Update(seq_core_bpm_preset_tempo[seq_core_bpm_preset_num], seq_core_bpm_preset_ramp[seq_core_bpm_preset_num]); return 1; // value has been changed } else { return 0; // value hasn't been changed } default: return -1; // invalid or unsupported encoder } } break; /////////////////////////////////////////////////////////////////////////// // Page do not exist default: break; } return 0; }
///////////////////////////////////////////////////////////////////////////// // fetches the pos entries of a song // returns -1 if recursion counter reached max position ///////////////////////////////////////////////////////////////////////////// s32 SEQ_SONG_FetchPos(u8 force_immediate_change, u8 dont_dump_mixer_map) { int recursion_ctr = 0; u8 again; do { again = 0; // stop song once recursion counter reached 64 loops if( ++recursion_ctr >= 64 ) { SEQ_BPM_Stop(); return -1; // recursion detected } // reset song position if we reached the end (loop over 128 steps) if( song_pos >= SEQ_SONG_NUM_STEPS ) song_pos = 0; // get step entry seq_song_step_t *s = (seq_song_step_t *)&seq_song_steps[song_pos]; // branch depending on action switch( s->action ) { case SEQ_SONG_ACTION_End: #if 0 if( song_active ) // not in phrase mode SEQ_BPM_Stop(); #else song_finished = 1; // deactivate song incrementer #endif break; case SEQ_SONG_ACTION_JmpPos: song_pos = s->action_value % SEQ_SONG_NUM_STEPS; again = 1; break; case SEQ_SONG_ACTION_JmpSong: { if( song_active ) { // not in phrase mode u32 new_song_num = s->action_value % SEQ_SONG_NUM; SEQ_SONG_Save(song_num); SEQ_SONG_Load(new_song_num); song_pos = 0; again = 1; } } break; case SEQ_SONG_ACTION_SelMixerMap: SEQ_MIXER_Load(s->action_value); SEQ_MIDI_IN_ExtCtrlSend(SEQ_MIDI_IN_EXT_CTRL_MIXER_MAP, s->action_value, 0); if( !dont_dump_mixer_map ) SEQ_MIXER_SendAll(); ++song_pos; again = 1; break; case SEQ_SONG_ACTION_Tempo: { float bpm = (float)s->action_value; if( bpm < 25.0 ) bpm = 25.0; float ramp = (float)s->pattern_g1; SEQ_CORE_BPM_Update(bpm, ramp); ++song_pos; again = 1; } break; case SEQ_SONG_ACTION_Mutes: { // access to seq_core_trk[] must be atomic! portENTER_CRITICAL(); seq_core_trk_muted = ((s->pattern_g1 & 0x0f) << 0) | ((s->pattern_g2 & 0x0f) << 4) | ((s->pattern_g3 & 0x0f) << 8) | ((s->pattern_g4 & 0x0f) << 12); portEXIT_CRITICAL(); ++song_pos; again = 1; } break; case SEQ_SONG_ACTION_GuideTrack: { if( s->action_value <= 16 ) seq_song_guide_track = s->action_value; ++song_pos; again = 1; } break; default: if( s->action >= SEQ_SONG_ACTION_Loop1 && s->action <= SEQ_SONG_ACTION_Loop16 ) { song_loop_ctr = 0; song_loop_ctr_max = s->action - SEQ_SONG_ACTION_Loop1; // TODO: implement prefetching until end of step! SEQ_SONG_FetchHlp_PatternChange(0, s->pattern_g1, s->bank_g1, force_immediate_change); SEQ_SONG_FetchHlp_PatternChange(1, s->pattern_g2, s->bank_g2, force_immediate_change); SEQ_SONG_FetchHlp_PatternChange(2, s->pattern_g3, s->bank_g3, force_immediate_change); SEQ_SONG_FetchHlp_PatternChange(3, s->pattern_g4, s->bank_g4, force_immediate_change); } } } while( again ); return 0; // no error }