///////////////////////////////////////////////////////////////////////////// // called by the UI to handle the "Fwd" button ///////////////////////////////////////////////////////////////////////////// s32 SEQ_SONG_Fwd(void) { if( ui_page == SEQ_UI_PAGE_SONG ) { song_pos = ui_song_edit_pos; song_loop_ctr = 0; SEQ_SONG_NextPos(); if( ui_song_edit_pos != song_pos ) ui_song_edit_pos = song_pos; else { // increment if possible if( song_pos < SEQ_SONG_NUM_STEPS ) { ++song_pos; SEQ_SONG_FetchPos(0, 0); ui_song_edit_pos = song_pos; // update display immediately seq_ui_display_update_req = 1; } } } else { u32 bpm_tick = SEQ_BPM_TickGet(); u32 ticks_per_pattern = ((u32)seq_core_steps_per_pattern+1) * (SEQ_BPM_PPQN_Get()/4); u32 measure = bpm_tick / ticks_per_pattern; u32 next_bpm_tick = (measure+1) * ticks_per_pattern; SEQ_CORE_Reset(next_bpm_tick); SEQ_SONG_NextPos(); SEQ_BPM_TickSet(next_bpm_tick); } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Sets new song position (new_song_pos resolution: 16th notes) ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_SongPos(u16 new_song_pos) { u16 new_tick = new_song_pos * (SEQ_BPM_PPQN_Get() / 4); // set new tick value SEQ_BPM_TickSet(new_tick); #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ] Setting new song position %u (-> %u ticks)\n", new_song_pos, new_tick); #endif // since timebase has been changed, ensure that Off-Events are played // (otherwise they will be played much later...) SEQ_PlayOffEvents(); // restart song MID_PARSER_RestartSong(); // release pause seq_pause = 0; if( new_song_pos > 1 ) { // (silently) fast forward to requested position ffwd_silent_mode = 1; MID_PARSER_FetchEvents(0, new_tick-1); ffwd_silent_mode = 0; } // when do we expect the next prefetch: next_prefetch = new_tick; prefetch_offset = new_tick; return 0; // no error }
///////////////////////////////////////////////////////////////////////////// //! Sets new song position (new_song_pos resolution: 16th notes) ///////////////////////////////////////////////////////////////////////////// static s32 MBNG_SEQ_SongPos(u16 new_song_pos) { u32 new_tick = new_song_pos * (SEQ_BPM_PPQN_Get() / 4); // set new tick value SEQ_BPM_TickSet(new_tick); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// //! performs a single bpm tick ///////////////////////////////////////////////////////////////////////////// static s32 MBNG_SEQ_Tick(u32 bpm_tick) { // send MIDI clock depending on ppqn if( (bpm_tick % (SEQ_BPM_PPQN_Get()/24)) == 0 ) { MIDI_ROUTER_SendMIDIClockEvent(0xf8, bpm_tick); } return 0; // no error }
void Clocks_Init(void) { SEQ_BPM_TickSet(0); // reset sequencer SEQ_BPM_Init(0); // initialize SEQ module sequencer SEQ_BPM_PPQN_Set(VXPPQN); // set internal clock resolution as per the define (usually 384) MClock_SetBPM(100.0); // Master BPM mClock.status.all = 0; // bit 7 is run/stop mClock.ticked = 0; // we haven't gotten that far yet mClock.timesigu = 4; // Upper value of the Master Time Signature, min 2 mClock.timesigl = 4; // Lower value of the Master Time Signature, min 2 mClock.res = SEQ_BPM_PPQN_Get(); // fill this from the SEQ_BPM module to be safe mClock.rt_latency = VXLATENCY; // initialise from define MClock_Init(); // init the vX master clock MClock_Reset(); }
///////////////////////////////////////////////////////////////////////////// // called by the UI to handle the "Rew" button ///////////////////////////////////////////////////////////////////////////// s32 SEQ_SONG_Rew(void) { if( ui_page == SEQ_UI_PAGE_SONG ) { song_pos = ui_song_edit_pos; song_loop_ctr = 0; SEQ_SONG_PrevPos(); ui_song_edit_pos = song_pos; } else { u32 bpm_tick = SEQ_BPM_TickGet(); u32 ticks_per_pattern = ((u32)seq_core_steps_per_pattern+1) * (SEQ_BPM_PPQN_Get()/4); u32 measure = bpm_tick / ticks_per_pattern; u32 next_bpm_tick = measure ? ((measure-1) * ticks_per_pattern) : 0; SEQ_CORE_Reset(next_bpm_tick); SEQ_SONG_PrevPos(); SEQ_BPM_TickSet(next_bpm_tick); } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // performs a single bpm tick ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_Tick(u32 bpm_tick) { // whenever we reach a new 16th note (96 ticks @384 ppqn): if( (bpm_tick % (SEQ_BPM_PPQN_Get()/4)) == 0 ) { // ensure that arp is reseted on first bpm_tick if( bpm_tick == 0 ) arp_counter = 0; else { // increment arpeggiator counter ++arp_counter; // reset once we reached length of notestack if( arp_counter >= notestack.len ) arp_counter = 0; } if( notestack.len > 0 ) { // get note/velocity/length from notestack u8 note = notestack_items[arp_counter].note; u8 velocity = notestack_items[arp_counter].tag; u8 length = 72; // always the same, could be varied, e.g. via CC // put note into queue if all values are != 0 if( note && velocity && length ) { mios32_midi_package_t midi_package; midi_package.type = NoteOn; // package type must match with event! midi_package.event = NoteOn; midi_package.chn = Chn1; midi_package.note = note; midi_package.velocity = velocity; SEQ_MIDI_OUT_Send(DEFAULT, midi_package, SEQ_MIDI_OUT_OnOffEvent, bpm_tick, length); } } } return 0; // no error }
void MClock_Init(void) { mClock.cyclelen = (unsigned int) (((SEQ_BPM_PPQN_Get() * 4) * mClock.timesigu) / mClock.timesigl); // Length of master track measured in ticks. midiclockdivider = (SEQ_BPM_PPQN_Get())/24; // Set up the divider for 24ppqn output }
///////////////////////////////////////////////////////////////////////////// // 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 } }