s32 SEQ_TERMINAL_PrintTracks(void *_output_function) { void (*out)(char *format, ...) = _output_function; char str_buffer[128]; MUTEX_MIDIOUT_TAKE; out("Track Overview:\n"); out("===============\n"); out("| Track | Mode | Layer P/T/I | Steps P/T | Length | Port | Chn. | Muted |\n"); out("+-------+-------+-------------+-----------+--------+-------+------+-------+\n"); u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { seq_event_mode_t event_mode = SEQ_CC_Get(track, SEQ_CC_MIDI_EVENT_MODE); u16 num_instruments = SEQ_TRG_NumInstrumentsGet(track); u16 num_par_layers = SEQ_PAR_NumLayersGet(track); u16 num_par_steps = SEQ_PAR_NumStepsGet(track); u16 num_trg_layers = SEQ_TRG_NumLayersGet(track); u16 num_trg_steps = SEQ_TRG_NumStepsGet(track); u16 length = (u16)SEQ_CC_Get(track, SEQ_CC_LENGTH) + 1; mios32_midi_port_t midi_port = SEQ_CC_Get(track, SEQ_CC_MIDI_PORT); u8 midi_chn = SEQ_CC_Get(track, SEQ_CC_MIDI_CHANNEL) + 1; sprintf(str_buffer, "| G%dT%d | %s |", (track/4)+1, (track%4)+1, SEQ_LAYER_GetEvntModeName(event_mode)); sprintf((char *)(str_buffer + strlen(str_buffer)), " %2d/%2d/%2d | %3d/%3d | %3d | %s%c | %2d |", num_par_layers, num_trg_layers, num_instruments, num_par_steps, num_trg_steps, length, SEQ_MIDI_PORT_OutNameGet(SEQ_MIDI_PORT_OutIxGet(midi_port)), SEQ_MIDI_PORT_OutCheckAvailable(midi_port) ? ' ' : '*', midi_chn); if( seq_core_trk_muted & (1 << track) ) sprintf((char *)(str_buffer + strlen(str_buffer)), " yes |\n"); else if( seq_core_trk[track].layer_muted ) sprintf((char *)(str_buffer + strlen(str_buffer)), " layer |\n"); else sprintf((char *)(str_buffer + strlen(str_buffer)), " no |\n"); out(str_buffer); } out("+-------+-------+-------------+-----------+--------+-------+------+-------+\n"); out("done.\n"); MUTEX_MIDIOUT_GIVE; return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // 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 } }
///////////////////////////////////////////////////////////////////////////// // 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;