///////////////////////////////////////////////////////////////////////////// // Should be called whenever a Note event has been received. // We expect, that velocity is 0 on a Note Off event ///////////////////////////////////////////////////////////////////////////// s32 SEQ_NotifyNoteOn(u8 note, u8 velocity) { u8 clear_stack = 0; if( velocity ) // push note into note stack NOTESTACK_Push(¬estack, note, velocity); else { // remove note from note stack // function returns 2 if no note played anymore (all keys depressed) if( NOTESTACK_Pop(¬estack, note) == 2 ) clear_stack = 1; } // At least one note played? if( !clear_stack && notestack.len > 0 ) { // start sequencer if it isn't already running if( !SEQ_BPM_IsRunning() ) SEQ_BPM_Start(); } else { // clear stack NOTESTACK_Clear(¬estack); // no key is pressed anymore: stop sequencer SEQ_BPM_Stop(); } #if 1 // optional debug messages NOTESTACK_SendDebugMessage(¬estack); #endif return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // This hook is called when a MIDI package has been received ///////////////////////////////////////////////////////////////////////////// void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package) { // Note On received? if( midi_package.type == NoteOn && midi_package.chn == Chn1 && midi_package.velocity > 0 ) { // determine base key number (0=C, 1=C#, 2=D, ...) u8 base_key = midi_package.note % 12; // branch depending on note switch( base_key ) { case 0: // "C" starts the sequencer MIOS32_MIDI_SendDebugMessage("Start\n"); // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // start sequencer SEQ_BPM_Start(); break; case 2: // "D" stops the sequencer. If pressed twice, the sequencer will be reset MIOS32_MIDI_SendDebugMessage("Stop\n"); if( SEQ_BPM_IsRunning() ) SEQ_BPM_Stop(); // stop sequencer else SEQ_Reset(1); // reset sequencer break; case 4: // "E" pauses the sequencer // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // toggle pause mode seq_pause ^= 1; MIOS32_MIDI_SendDebugMessage("Pause %s\n", seq_pause ? "on" : "off"); // execute stop/continue depending on new mode if( seq_pause ) SEQ_BPM_Stop(); // stop sequencer else SEQ_BPM_Cont(); // continue sequencer break; case 5: // "F" switches to next file MIOS32_MIDI_SendDebugMessage("Next File\n"); SEQ_PlayFileReq(1); break; } } }
///////////////////////////////////////////////////////////////////////////// //! To control the play button function ///////////////////////////////////////////////////////////////////////////// s32 MBNG_SEQ_PlayButton(void) { // if in auto mode and BPM generator is not clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); if( MBNG_SEQ_PauseEnabled() ) { // continue sequencer MBNG_SEQ_SetPauseMode(0); SEQ_BPM_Cont(); } else { // reset sequencer MBNG_SEQ_Reset(); // and start SEQ_BPM_Start(); } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Plays the first .mid file if next == 0, the next file if next != 0 ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_PlayFile(u32 next) { // play off events before loading new file SEQ_PlayOffEvents(); char next_file[13]; if( MID_FILE_FindNext(next ? MID_FILE_UI_NameGet() : NULL, next_file) == 1 || MID_FILE_FindNext(NULL, next_file) == 1 ) { // if next file not found, try first file #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("[SEQ] next file found '%s'\n", next_file); #endif SEQ_BPM_Stop(); // stop BPM generator if( MID_FILE_open(next_file) ) { // try to open next file #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("[SEQ] file %s cannot be opened (wrong directory?)\n", next_file); #endif return -1; // file cannot be opened } if( MID_PARSER_Read() < 0 ) { // read file, stop on failure #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("[SEQ] file %s is invalid!\n", next_file); #endif return -2; // file is invalid } SEQ_BPM_Start(); // start BPM generator } else { SEQ_BPM_Stop(); // stop BPM generator #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("[SEQ] no file found\n"); #endif return -1; // file not found } 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 }
void MClock_User_Start(void) { SEQ_BPM_Start(); }
///////////////////////////////////////////////////////////////////////////// // This task is called periodically each mS to handle sequencer requests ///////////////////////////////////////////////////////////////////////////// static void TASK_SEQ(void *pvParameters) { portTickType xLastExecutionTime; u16 sdcard_check_ctr = 0; // Initialise the xLastExecutionTime variable on task entry xLastExecutionTime = xTaskGetTickCount(); while( 1 ) { vTaskDelayUntil(&xLastExecutionTime, 1 / portTICK_RATE_MS); // execute sequencer handler SEQ_Handler(); // send timestamped MIDI events SEQ_MIDI_OUT_Handler(); // each second: check if SD Card (still) available if( ++sdcard_check_ctr >= 1000 ) { sdcard_check_ctr = 0; // use a mutex if multiple tasks access the SD Card! MUTEX_SDCARD_TAKE; s32 status = FILE_CheckSDCard(); if( status == 1 ) { MIOS32_MIDI_SendDebugMessage("SD Card connected: %s\n", FILE_VolumeLabel()); } else if( status == 2 ) { MIOS32_MIDI_SendDebugMessage("SD Card disconnected\n"); // stop sequencer SEQ_BPM_Stop(); // change filename sprintf(MID_FILE_UI_NameGet(), "No SD Card"); } else if( status == 3 ) { if( !FILE_SDCardAvailable() ) { MIOS32_MIDI_SendDebugMessage("SD Card not found\n"); // change filename sprintf(MID_FILE_UI_NameGet(), "No SD Card"); } else if( !FILE_VolumeAvailable() ) { MIOS32_MIDI_SendDebugMessage("ERROR: SD Card contains invalid FAT!\n"); MIOS32_BOARD_LED_Set(0x1, 0x0); // turn off LED // change filename sprintf(MID_FILE_UI_NameGet(), "No FAT"); // stop sequencer SEQ_BPM_Stop(); } else { // change filename sprintf(MID_FILE_UI_NameGet(), "SDCard found"); // if in auto mode and BPM generator is clocked in slave mode: // change to master mode SEQ_BPM_CheckAutoMaster(); // reset sequencer SEQ_Reset(1); // request to play the first file SEQ_PlayFileReq(0); // start sequencer SEQ_BPM_Start(); } } MUTEX_SDCARD_GIVE; } } }