///////////////////////////////////////////////////////////////////////////// // Initialisation ///////////////////////////////////////////////////////////////////////////// s32 SEQ_Init(u32 mode) { // initialize the Notestack #if 0 NOTESTACK_Init(¬estack, NOTESTACK_MODE_PUSH_TOP, ¬estack_items[0], NOTESTACK_SIZE); #else // for an arpeggiator we prefer sorted mode // activate hold mode as well. Stack will be cleared whenever no note is played anymore NOTESTACK_Init(¬estack, NOTESTACK_MODE_SORT_HOLD, ¬estack_items[0], NOTESTACK_SIZE); #endif // and the arp counter arp_counter = 0; // reset sequencer SEQ_Reset(); // init BPM generator SEQ_BPM_Init(0); SEQ_BPM_PPQN_Set(384); SEQ_BPM_Set(120.0); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Resets song position of sequencer ///////////////////////////////////////////////////////////////////////////// s32 SEQ_Reset(u8 play_off_events) { // since timebase has been changed, ensure that Off-Events are played // (otherwise they will be played much later...) if( play_off_events ) SEQ_PlayOffEvents(); // release pause and FFWD mode seq_pause = 0; ffwd_silent_mode = 0; next_prefetch = 0; prefetch_offset = 0; // restart song MID_PARSER_RestartSong(); // set initial BPM (according to MIDI file spec) SEQ_BPM_PPQN_Set(384); // not specified SEQ_BPM_Set(120.0); // reset BPM tick SEQ_BPM_TickSet(0); return 0; // no error }
void MClock_SetBPM(float bpm) { if (SEQ_BPM_Set(bpm) < 0) { mClock.status.run = 0; // handle failure by pausing master clock } else { mClock.bpm = bpm; // store the tempo } }
///////////////////////////////////////////////////////////////////////////// //! Initialisation ///////////////////////////////////////////////////////////////////////////// s32 MBNG_SEQ_Init(u32 mode) { if( mode != 0 ) return -1; // only mode 0 supported // init BPM generator SEQ_BPM_Init(0); SEQ_BPM_Set(120.0); // reset sequencer MBNG_SEQ_Reset(); // disable pause after power-on MBNG_SEQ_SetPauseMode(0); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // (Public) Tap Tempo Function // Can also be accessed by pressing MENU+PLAY ///////////////////////////////////////////////////////////////////////////// s32 SEQ_UI_BPM_TapTempo(void) { // determine current timestamp u32 timestamp = MIOS32_TIMESTAMP_Get(); u32 delay = MIOS32_TIMESTAMP_GetDelay(tap_tempo_last_timestamp); // if delay < 100 (mS) we assume a bouncing button - ignore this tap! if( delay < 100 ) return -1; // reset counter if last timestamp zero (first tap) or difference between timestamps > 2 seconds (< 30 BPM...) if( !tap_tempo_last_timestamp || delay > 2000 ) { resetTapTempo(); } else { // store measured delay tap_tempo_delay[tap_tempo_beat_ix] = delay; // increment index, wrap at number of stored tap delays tap_tempo_beat_ix = (tap_tempo_beat_ix + 1) % TAP_DELAY_STORAGE_SIZE; } // store timestamp tap_tempo_last_timestamp = timestamp; // take over BPM and start sequencer once we reached the 5th tap if( tap_tempo_beat_ctr >= TAP_DELAY_STORAGE_SIZE ) { // hold counter at delay storage size + 1 (for display) tap_tempo_beat_ctr = TAP_DELAY_STORAGE_SIZE + 1; // determine BPM int tap; int accumulated_delay = 0; for(tap=0; tap<TAP_DELAY_STORAGE_SIZE; ++tap) accumulated_delay += tap_tempo_delay[tap]; u32 bpm = 60000000 / (accumulated_delay / TAP_DELAY_STORAGE_SIZE); // set BPM float bpmF = bpm / 1000.0; seq_core_bpm_preset_tempo[seq_core_bpm_preset_num] = bpmF; SEQ_BPM_Set(bpmF); // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // if sequencer not running: start it! if( !SEQ_BPM_IsRunning() ) SEQ_BPM_Start(); } else { // increment counter ++tap_tempo_beat_ctr; } // print message char buffer1[30]; if( tap_tempo_beat_ctr > TAP_DELAY_STORAGE_SIZE ) { float bpm = SEQ_BPM_Get(); sprintf(buffer1, "Tap Tempo: %3d.%d", (int)bpm, (int)(10*bpm)%10); } else sprintf(buffer1, "Tap Tempo: ???.?"); char buffer2[30]; int i; for(i=0; i<((tap_tempo_beat_ix+1)*4); ++i) buffer2[i] = '>'; for(; i<16; ++i) buffer2[i] = ' '; buffer2[i] = 0; SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 2000, buffer1, buffer2); return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // called when a Meta event should be played/processed at a given tick ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_PlayMeta(u8 track, u8 meta, u32 len, u8 *buffer, u32 tick) { switch( meta ) { case 0x00: // Sequence Number if( len == 2 ) { u32 seq_number = (buffer[0] << 8) | buffer[1]; #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Sequence Number %u\n", track, tick, seq_number); #endif } else { #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Sequence Number with %d bytes -- ERROR: expecting 2 bytes!\n", track, tick, len); #endif } break; case 0x01: // Text Event #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Text: %s\n", track, tick, buffer); #endif break; case 0x02: // Copyright Notice #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Copyright: %s\n", track, tick, buffer); #endif break; case 0x03: // Sequence/Track Name #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Track Name: %s\n", track, tick, buffer); #endif break; case 0x04: // Instrument Name #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Instr. Name: %s\n", track, tick, buffer); #endif break; case 0x05: // Lyric #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Lyric: %s\n", track, tick, buffer); #endif break; case 0x06: // Marker #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Marker: %s\n", track, tick, buffer); #endif break; case 0x07: // Cue Point #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Cue Point: %s\n", track, tick, buffer); #endif break; case 0x20: // Channel Prefix if( len == 1 ) { u32 prefix = *buffer; #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Channel Prefix %u\n", track, tick, prefix); #endif } else { #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Channel Prefix with %d bytes -- ERROR: expecting 1 byte!\n", track, tick, len); #endif } break; case 0x2f: // End of Track #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - End of Track\n", track, tick, meta); #endif break; case 0x51: // Set Tempo if( len == 3 ) { u32 tempo_us = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; float bpm = 60.0 * (1E6 / (float)tempo_us); SEQ_BPM_PPQN_Set(MIDI_PARSER_PPQN_Get()); // set tempo immediately on first tick if( tick == 0 ) { SEQ_BPM_Set(bpm); } else { // put tempo change request into the queue mios32_midi_package_t tempo_package; // or Softis? tempo_package.ALL = (u32)bpm; SEQ_MIDI_OUT_Send(DEFAULT, tempo_package, SEQ_MIDI_OUT_TempoEvent, tick, 0); } #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Tempo to %u uS -> %u BPM\n", track, tick, tempo_us, (u32)bpm); #endif } else { #if DEBUG_VERBOSE_LEVEL >= 2 DEBUG_MSG("[SEQ:%d:%u] Meta - Tempo with %u bytes -- ERROR: expecting 3 bytes!\n", track, tick, len); #endif } break; // other known events which are not handled here: // 0x54: SMPTE offset // 0x58: Time Signature // 0x59: Key Signature // 0x7f: Sequencer Specific Meta Event #if DEBUG_VERBOSE_LEVEL >= 2 default: DEBUG_MSG("[SEQ:%d:%u] Meta Event 0x%02x with length %u not processed\n", track, tick, meta, len); #endif } return 0; }
static void mclkBpmSet(u32 ix, u16 value) { SEQ_BPM_Set((float)value); }