///////////////////////////////////////////////////////////////////////////// // Init logo functions ///////////////////////////////////////////////////////////////////////////// s32 SEQ_LCD_LOGO_Init(u32 mode) { // we generate our own charset // setting "None" results into proper re-initialisation after logo has been displayed SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_None); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Initialisation ///////////////////////////////////////////////////////////////////////////// s32 SEQ_UI_INFO_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); // load charset (if this hasn't been done yet) SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_Menu); SEQ_UI_INFO_UpdateList(); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Initialisation ///////////////////////////////////////////////////////////////////////////// s32 SEQ_UI_SYSEX_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); // load charset (if this hasn't been done yet) SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_Menu); dir_name[0] = 0; #if TEST_LIST dir_num_items = 32; #else SEQ_UI_SYSEX_UpdateDirList(); #endif 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) { 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 ) {
///////////////////////////////////////////////////////////////////////////// // 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( SEQ_FILE_FormattingRequired() ) return 0; // no button action as long as files not available u8 group; s32 status; switch( button ) { case SEQ_UI_BUTTON_Edit: { if ( depressed && (selected_page == PAGE_REMIX) ) { // we want to show vertical VU meters normal mode SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_VBars); selected_page = PAGE_MAIN; // // here we run the commands for remix states // u16 remix_map_tmp; // if we are in no preview_mode them call the demix procedment in case we got some request if ( (seq_pattern_remix_map != seq_pattern_demix_map) && ( remix_mode ) ) { remix_map_tmp = seq_pattern_remix_map; seq_pattern_remix_map = ~(seq_pattern_remix_map ^ seq_pattern_demix_map); if (ableton_api) { // TODO: implements a ableton remotescript to the clip control functionality u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { // if we got the track bit setup inside our remix_map, them do not send mixer data for that track channel, let it be mixed down if ( ((1 << track) | seq_pattern_remix_map) == seq_pattern_remix_map ) { // do nothing... } else { // delay our task for ableton get a breath before change the pattern line vTaskDelay(50); // send slot play envet to ableton: cc (111 + track) with value 127 to channel 16 MIOS32_MIDI_SendCC(ableton_port, 15, (111 + track), 127); } } } // if we are in remix mode lets start the process if (remix_map_tmp == 0) { //pattern_name = pattern_name_remix; preview_mode = 0; remix_mode = 0; // set the pattern_remix_timer reference pattern_remix_timer = MIOS32_SYS_TimeGet(); } // Change Pattern u8 group; for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { // change pattern SEQ_PATTERN_Change(group, seq_pattern[group], 0); } // copy the pattern name for future reference sprintf(pattern_name, seq_pattern_name[0]); // getting back seq_pattern_remix_map = remix_map_tmp; seq_pattern_demix_map = seq_pattern_remix_map; } } else if ( !depressed ) { // we want to show horizontal VU meters in remix mode SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_HBars); selected_page = PAGE_REMIX; } return 1; } break; case SEQ_UI_BUTTON_Select: { if(!depressed) { selected_page = PAGE_OPTION; return 1; } if( (depressed) && (selected_page == PAGE_OPTION) ) { selected_page = PAGE_MAIN; } } break; default: break; } if( depressed ) return 0; // ignore when button depressed if its not SEQ_UI_BUTTON_Select or SEQ_UI_BUTTON_Edit switch( selected_page ) { /////////////////////////////////////////////////////////////////////////// // REMIX PAGE case PAGE_REMIX: { if( button <= SEQ_UI_BUTTON_GP16 ) { // re-using encoder routine return Encoder_Handler(button, 0); } } break; /////////////////////////////////////////////////////////////////////////// // OPTION PAGE case PAGE_OPTION: { switch( button ) { case SEQ_UI_BUTTON_GP1: // Save Pattern(all 4 groups at once) for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { if( (status=SEQ_PATTERN_Save(group, seq_pattern[group])) < 0 ) { SEQ_UI_SDCardErrMsg(2000, status); return 0; } } // Save the pattern mixer SEQ_MIXER_Save(SEQ_MIXER_NumGet()); break; case SEQ_UI_BUTTON_GP2: // Edit Pattern Name //SEQ_LCD_Clear(); selected_page = PAGE_NAME_EDIT; break; case SEQ_UI_BUTTON_GP3: // Copy Pattern // We are going to use the multicopy procedure SEQ_UI_PATTERN_Copy(); // coping mixer SEQ_UI_MIXER_Copy(); break; case SEQ_UI_BUTTON_GP4: // Paste Pattern // We are going to use the multicopy procedure SEQ_UI_PATTERN_Paste(); // paste mixer SEQ_UI_MIXER_Paste(); break; case SEQ_UI_BUTTON_GP5: case SEQ_UI_BUTTON_GP6: return -1; // not mapped case SEQ_UI_BUTTON_GP7: case SEQ_UI_BUTTON_GP8: // Track Delay Page //SEQ_LCD_Clear(); //selected_page = PAGE_TRK_DELAY; //break; return -1; // not mapped case SEQ_UI_BUTTON_GP9: case SEQ_UI_BUTTON_GP10: return -1; // not mapped case SEQ_UI_BUTTON_GP11: case SEQ_UI_BUTTON_GP12: return -1; // not mapped case SEQ_UI_BUTTON_GP13: case SEQ_UI_BUTTON_GP14: // Auto Save switch auto_save ^= 1; break; break; case SEQ_UI_BUTTON_GP15: case SEQ_UI_BUTTON_GP16: // Ableton API switch ableton_api ^= 1; return -1; // not mapped } return 0; } break; /////////////////////////////////////////////////////////////////////////// // PATTERN NAME EDIT PAGE case PAGE_NAME_EDIT: { return Encoder_Handler((seq_ui_encoder_t)button, 0); } break; /////////////////////////////////////////////////////////////////////////// // TRACK DELAY PAGE case PAGE_TRK_DELAY: { } break; /////////////////////////////////////////////////////////////////////////// // MAIN PAGE case PAGE_MAIN: { // Pattern Change Group Requests if( button <= SEQ_UI_BUTTON_GP8 ) { u8 group; for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { if( selected_pattern[group].group == button ) { if( SEQ_FILE_B_NumPatterns(selected_pattern[group].bank) > 64 ) { selected_pattern[group].lower ^= 1; } else { selected_pattern[group].lower = 0; // toggling not required } } else { selected_pattern[group].group = button; preview_bank = button; //preview_mode = 0; } } SEQ_PATTERN_PeekName(selected_pattern[0], preview_name); preview_mode = 1; return 1; // value always changed } // Pattern Change Request if( button >= SEQ_UI_BUTTON_GP9 && button <= SEQ_UI_BUTTON_GP16 ) { //u8 group; s32 status = 0; // setting save patterns for later autosave procedure //for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) // save_pattern[group] = seq_pattern[group]; // change preview pattern selected_pattern[0].num = button-8; selected_pattern[1].num = button-8; selected_pattern[2].num = button-8; selected_pattern[3].num = button-8; if ( !preview_mode ) { preview_num = button-8; SEQ_PATTERN_PeekName(selected_pattern[0], preview_name); preview_mode = 1; // for security reasons in live environment, we can not accept rapid 2x press of pattern button. // if the buttons are not good quality they can send a 2x rapid signal os pressing in just one physical pressing // the time we need is bassicly the human time to read the name of patterns requested // start time in count pc_time_control = seq_core_timestamp_ms + 200; // vTaksDelay(200); } else { // if ( remix_mode == 0 ) { //char str[30]; //sprintf(str, "%d = %d %d = %d", selected_pattern[0].num, preview_num, selected_pattern[0].group, preview_bank); //SEQ_UI_Msg(SEQ_UI_MSG_USER, 2000, str, "debug"); // Check for remix state if ( (selected_pattern[0].num == preview_num) && (selected_pattern[0].group == preview_bank) ) { //if ( pc_time_control >= seq_core_timestamp_ms ) // return 0; // do nothing, just to be shure we are not handle acidental double clicks, it happens! // if we got some remix bit setup, enter in remix mode if (seq_pattern_remix_map != 0) { // set the remix mode remix_mode = 1; } } } // Change Pattern if ( (selected_pattern[0].num == preview_num) && (selected_pattern[0].group == preview_bank) ) { if ( pc_time_control >= seq_core_timestamp_ms ) return 0; // do nothing, just to be shure we are not handle acidental double clicks if (ableton_api) { u8 ableton_pattern_number = 0; // // ABLETON API PATTERN CHANGE HANDLE // // send ableton event to change the line clips if (selected_pattern[0].lower) { ableton_pattern_number = ((selected_pattern[0].group - 1) * 8) + button; } else { ableton_pattern_number = (((selected_pattern[0].group - 1) + 8) * 8) + button; } // midi_cc to send = 110 // midi_chn = 16 // midi_port = ableton_port // here we need to send a clip line change request MIOS32_MIDI_SendCC(ableton_port, 15, 110, ableton_pattern_number); // delay our task for ableton get a breath before change the pattern line vTaskDelay(50); // clip triggering u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { // if we got the track bit setup inside our remix_map, them do not send mixer data for that track channel, let it be mixed down if ( ((1 << track) | seq_pattern_remix_map) == seq_pattern_remix_map ) { // do nothing... } else { // delay our task for ableton get a breath before change the pattern line vTaskDelay(50); // send clip slot play envet to ableton cc (111 + track) with value 127 to channel 16 MIOS32_MIDI_SendCC(ableton_port, 15, (111 + track), 127); } } // send a global clip change(all clips in the clip line) // send play envet to ableton cc 20 with value 127 to channel 16 //MIOS32_MIDI_SendCC(ableton_port, 15, 20, 127); } // // Autosave Pattern // if (auto_save) { // Autosave all 4 group of patterns for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { // Save to SD Card if( (status=SEQ_PATTERN_Save(group, seq_pattern[group])) < 0 ) { SEQ_UI_SDCardErrMsg(2000, status); } } // Autosave Mixer Map SEQ_MIXER_Save(SEQ_MIXER_NumGet()); } // if we are not in remix mode if (seq_pattern_remix_map == 0) { // keep the name of old pattern(because we still got 1 or more tracks belongs to this pattern) //pattern_name = seq_pattern_name; // set timer for mixed pattern pattern_remix_timer = MIOS32_SYS_TimeGet(); } // // Pattern change request // for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { // change pattern //selected_pattern[group].num = button-8; SEQ_PATTERN_Change(group, selected_pattern[group], 0); //if ( remix_mode ) { // keep the pattern name... //sprintf(seq_pattern_name[group], pattern_name); //} } if ( !remix_mode ) { // copy the pattern name for future reference //sprintf(pattern_name, seq_pattern_name[0]); SEQ_PATTERN_PeekName(selected_pattern[0], pattern_name); // TODO: sync this with the pattern change handled by SEQ_PATTERN_Handler() //pattern_timer.seconds = 0; preview_mode = 0; } // support for demix seq_pattern_demix_map = seq_pattern_remix_map; //preview_mode = 0; // Request a Pattern Preview } else { preview_num = button-8; SEQ_PATTERN_PeekName(selected_pattern[0], preview_name); // for security reasons in live environment, we can not accept 2x rapid press of pattern button. // if the buttons are not good quality they can send a 2x rapid signal os pressing in just one physical pressing // the time we need is bassicly the human time to read the name of patterns requested // start time in count pc_time_control = seq_core_timestamp_ms + 200; } } return 1; // value always changed } u8 visible_track = SEQ_UI_VisibleTrackGet(); switch( button ) { case SEQ_UI_BUTTON_Right: // switch to next track if( visible_track < (SEQ_CORE_NUM_TRACKS - 1) ) visible_track++; ui_selected_tracks = (1 << visible_track); ui_selected_group = visible_track / 4; return 1; // value always changed case SEQ_UI_BUTTON_Left: // switch to previous track if( visible_track >= 1 ) visible_track--; ui_selected_tracks = (1 << visible_track); ui_selected_group = visible_track / 4; 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 } break; /////////////////////////////////////////////////////////////////////////// // Page do not exist default: break; } return 0; }