static s32 SEQ_MIDI_IN_Receive_NotePC(u8 note, u8 velocity) { int octave = note / 12; int octave_taken = 0; int group; for(group=0; group<4; ++group) { if( octave == (seq_midi_in_sect_note[group] / 12) ) { octave_taken = 1; notestack_t *n = &patch_changer_notestack[group]; int patch = -1; if( velocity ) { // Note On NOTESTACK_Push(n, note, velocity); patch = n->note_items[0].note % 12; } else { // Note Off if( NOTESTACK_Pop(n, note) > 0 && n->len ) { patch = n->note_items[0].note % 12; } } // switch to new patch if required if( patch >= 0 && patch < 8 ) { seq_pattern_t pattern = seq_pattern[group]; if( pattern.num != patch ) { pattern.num = patch; pattern.DISABLED = 0; pattern.SYNCHED = 0; SEQ_PATTERN_Change(group, pattern); } } #if DEBUG_VERBOSE_LEVEL >= 0 DEBUG_MSG("NOTESTACK_PATCH_CHANGER_G%d:\n", group+1); NOTESTACK_SendDebugMessage(n); #endif } } return octave_taken; // return 1 if octave has been taken, otherwise 0 }
///////////////////////////////////////////////////////////////////////////// // Help function for pattern switching ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_SONG_FetchHlp_PatternChange(u8 group, u8 pattern, u8 bank, u8 force_immediate_change) { if( pattern < 0x80 ) { if( force_immediate_change || pattern != seq_pattern[group].pattern ) { seq_pattern_t p; p.ALL = 0; p.pattern = pattern; p.bank = bank; SEQ_PATTERN_Change(group, p, force_immediate_change); } else { // pattern not reloaded... we have to consider the RATOPC flag which is normally done in SEQ_PATTERN_Handler() if( seq_core_options.RATOPC ) { MIOS32_IRQ_Disable(); // must be atomic seq_core_state.reset_trkpos_req |= (0xf << (4*group)); MIOS32_IRQ_Enable(); } } } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // 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; }
///////////////////////////////////////////////////////////////////////////// // CC has been received over selected port and channel ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_MIDI_IN_Receive_CC(u8 bus, u8 cc, u8 value) { static u8 nrpn_lsb = 0; static u8 nrpn_msb = 0; if( bus >= SEQ_MIDI_IN_NUM_BUSSES ) return -1; // MIDI Remote Function? if( seq_hwcfg_midi_remote.cc && seq_hwcfg_midi_remote.cc == cc ) { remote_active = value >= 0x40; return 1; } // Remaining functions switch( cc ) { case 0x01: // ModWheel -> Morph Value case 0x03: // Global Scale return SEQ_CC_MIDI_Set(0, cc, value); case 0x70: // Pattern Group #1 case 0x71: // Pattern Group #2 case 0x72: // Pattern Group #3 case 0x73: { // Pattern Group #4 u8 group = cc-0x70; seq_pattern_t pattern = seq_pattern[group]; if( value < SEQ_FILE_B_NumPatterns(pattern.bank) ) { pattern.pattern = value; pattern.DISABLED = 0; pattern.SYNCHED = 0; SEQ_PATTERN_Change(group, pattern, 0); } return 1; } break; case 0x74: // Bank Group #1 case 0x75: // Bank Group #2 case 0x76: // Bank Group #3 case 0x77: { // Bank Group #4 u8 group = cc-0x74; seq_pattern_t pattern = seq_pattern[group]; if( value < SEQ_FILE_B_NUM_BANKS ) { pattern.bank = value; pattern.DISABLED = 0; pattern.SYNCHED = 0; SEQ_PATTERN_Change(group, pattern, 0); } return 1; } break; case 0x62: // NRPN LSB (selects parameter) nrpn_lsb = value; return 1; case 0x63: // NRPN MSB (selects track) nrpn_msb = value; return 1; case 0x06: // NRPN Value LSB (sets parameter) if( nrpn_msb < SEQ_CORE_NUM_TRACKS) return SEQ_CC_MIDI_Set(nrpn_msb, nrpn_lsb, value); break; case 0x7b: // all notes off (transposer, arpeggiator, patch changer) if( value == 0 ) { SEQ_MIDI_IN_ResetAllStacks(); SEQ_CV_ResetAllChannels(); } break; } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // 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;
///////////////////////////////////////////////////////////////////////////// // fetches the pos entries of a song // returns -1 if recursion counter reached max position ///////////////////////////////////////////////////////////////////////////// s32 SEQ_SONG_FetchPos(u8 force_immediate_change) { int recursion_ctr = 0; DEBUG_MSG("SEQ_SONG_FetchPos gestart"); 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); 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; seq_pattern_t p; // TODO: implement prefetching until end of step! if( s->pattern_g1 < 0x80 ) { p.ALL = 0; p.pattern = s->pattern_g1; p.bank = s->bank_g1; if (seq_hwcfg_mb909.enabled && seq_hwcfg_mb909.multimachine==0){ seq_core_trk_muted = ~((1<< s->bank_g2) & 0x0f); } SEQ_PATTERN_Change(0, p, force_immediate_change); } if( s->pattern_g2 < 0x80 ) { p.ALL = 0; p.pattern = s->pattern_g2; p.bank = s->bank_g2; SEQ_PATTERN_Change(1, p, force_immediate_change); } /* if( s->pattern_g3 < 0x80 ) { p.ALL = 0; p.pattern = s->pattern_g3; p.bank = s->bank_g3; SEQ_PATTERN_Change(2, p, force_immediate_change); } if( s->pattern_g4 < 0x80 ) { p.ALL = 0; p.pattern = s->pattern_g4; p.bank = s->bank_g4; SEQ_PATTERN_Change(3, p, force_immediate_change); } */ } } } while( again ); return 0; // no error }