///////////////////////////////////////////////////////////////////////////// // called when a MIDI event should be played at a given tick ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_PlayEvent(u8 track, mios32_midi_package_t midi_package, u32 tick) { // ignore all events in silent mode (for SEQ_SongPos function) // we could implement a more intelligent parser, which stores the sent CC/program change, etc... // and sends the last received values before restarting the song... if( ffwd_silent_mode ) return 0; seq_midi_out_event_type_t event_type = SEQ_MIDI_OUT_OnEvent; if( midi_package.event == NoteOff || (midi_package.event == NoteOn && midi_package.velocity == 0) ) event_type = SEQ_MIDI_OUT_OffEvent; // output events on DEFAULT port u32 status = 0; status |= SEQ_MIDI_OUT_Send(DEFAULT, midi_package, event_type, tick, 0); return status; }
///////////////////////////////////////////////////////////////////////////// // 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 }
///////////////////////////////////////////////////////////////////////////// // 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; }