#include #include "MIDIPlayer.h" MIDIPlayer::MIDIPlayer() { uSamplesRemaining = 0; uSampleRate = 1000; uTimeCurrent = 0; uTimeEnd = 0; uTimeLoopStart = 0; initialized = false; } void MIDIPlayer::setSampleRate(unsigned long rate) { if(mStream.size()) { for(unsigned long i = 0; i < mStream.size(); i++) { mStream.at(i).m_timestamp = (unsigned long)((uint64_t)mStream.at(i).m_timestamp * rate / uSampleRate); } } if(uTimeCurrent) { uTimeCurrent = static_cast(static_cast(uTimeCurrent) * rate / uSampleRate); } if(uTimeEnd) { uTimeEnd = static_cast(static_cast(uTimeEnd) * rate / uSampleRate); } if(uTimeLoopStart) { uTimeLoopStart = static_cast(static_cast(uTimeLoopStart) * rate / uSampleRate); } uSampleRate = rate; shutdown(); } bool MIDIPlayer::Load(const midi_container &midi_file, unsigned subsong, unsigned loop_mode, unsigned clean_flags) { assert(!mStream.size()); midi_file.serialize_as_stream(subsong, mStream, mSysexMap, uStreamLoopStart, uStreamEnd, clean_flags); if(mStream.size()) { uStreamPosition = 0; uTimeCurrent = 0; uLoopMode = loop_mode; uTimeEnd = midi_file.get_timestamp_end(subsong, true) + 1000; if(uLoopMode & loop_mode_enable) { uTimeLoopStart = midi_file.get_timestamp_loop_start(subsong, true); unsigned long uTimeLoopEnd = midi_file.get_timestamp_loop_end(subsong, true); if(uTimeLoopStart != ~0UL || uTimeLoopEnd != ~0UL) { uLoopMode |= loop_mode_force; } if(uTimeLoopStart == ~0UL) uTimeLoopStart = 0; if(uTimeLoopEnd == ~0UL) uTimeLoopEnd = uTimeEnd - 1000; if((uLoopMode & loop_mode_force)) { unsigned long i; uint8_t nullByte = 0; std::vector note_on; note_on.resize(128 * 16, nullByte); memset(¬e_on[0], 0, sizeof(note_on)); for(i = 0; i < mStream.size() && i < uStreamEnd; i++) { uint32_t ev = mStream.at(i).m_event & 0x800000F0; if(ev == 0x90 || ev == 0x80) { const unsigned long port = (mStream.at(i).m_event & 0x7F000000) >> 24; const unsigned long ch = mStream.at(i).m_event & 0x0F; const unsigned long note = (mStream.at(i).m_event >> 8) & 0x7F; const bool on = (ev == 0x90) && (mStream.at(i).m_event & 0xFF0000); const unsigned long bit = 1 << port; note_on.at(ch * 128 + note) = (note_on.at(ch * 128 + note) & ~bit) | (bit * on); } } mStream.resize(i); uTimeEnd = uTimeLoopEnd - 1; if(uTimeEnd < mStream.at(i - 1).m_timestamp) uTimeEnd = mStream.at(i - 1).m_timestamp; for(unsigned long j = 0; j < 128 * 16; j++) { if(note_on.at(j)) { for(unsigned long k = 0; k < 8; k++) { if(note_on.at(j) & (1 << k)) { mStream.push_back(midi_stream_event(uTimeEnd, static_cast((k << 24) + (j >> 7) + (j & 0x7F) * 0x100 + 0x90))); } } } } uTimeEnd = uTimeLoopEnd; } } if(uSampleRate != 1000) { unsigned long rate = static_cast(uSampleRate); uSampleRate = 1000; setSampleRate(rate); } return true; } return false; } unsigned long MIDIPlayer::Play(float *out, unsigned long count) { assert(mStream.size()); if(!startup()) return 0; unsigned long done = 0; const unsigned int needs_block_size = send_event_needs_time(); unsigned int into_block = 0; // This should be a multiple of block size, and have leftover while(uSamplesRemaining && done < count) { unsigned long todo = uSamplesRemaining; if(todo > count - done) todo = count - done; if(needs_block_size && todo > needs_block_size) todo = needs_block_size; if(todo < needs_block_size) { uSamplesRemaining = 0; into_block = todo; break; } render(out + done * 2, todo); uSamplesRemaining -= todo; done += todo; uTimeCurrent += todo; } while(done < count) { unsigned long todo = uTimeEnd - uTimeCurrent; if(todo > count - done) todo = count - done; const unsigned long time_target = todo + uTimeCurrent; unsigned long stream_end = uStreamPosition; while(stream_end < mStream.size() && mStream.at(stream_end).m_timestamp < time_target) stream_end++; if(stream_end > uStreamPosition) { for(; uStreamPosition < stream_end; uStreamPosition++) { const midi_stream_event &me = mStream.at(uStreamPosition); unsigned long samples_todo = me.m_timestamp - uTimeCurrent - into_block; if(samples_todo) { if(samples_todo > count - done) { uSamplesRemaining = samples_todo - (count - done); samples_todo = count - done; } if(!needs_block_size && samples_todo) { render(out + done * 2, samples_todo); done += samples_todo; uTimeCurrent += samples_todo; } if(uSamplesRemaining) { uSamplesRemaining += into_block; return done; } } if(needs_block_size) { into_block += samples_todo; while(into_block >= needs_block_size) { render(out + done * 2, needs_block_size); done += needs_block_size; into_block -= needs_block_size; uTimeCurrent += needs_block_size; } send_event_time_filtered(me.m_event, into_block); } else send_event_filtered(me.m_event); } } if(done < count) { unsigned long samples_todo; if(uStreamPosition < mStream.size()) samples_todo = mStream.at(uStreamPosition).m_timestamp; else samples_todo = uTimeEnd; samples_todo -= uTimeCurrent; if(needs_block_size) into_block = samples_todo; if(samples_todo > count - done) samples_todo = count - done; if(needs_block_size && samples_todo > needs_block_size) samples_todo = needs_block_size; if(samples_todo >= needs_block_size) { render(out + done * 2, samples_todo); done += samples_todo; uTimeCurrent += samples_todo; if(needs_block_size) into_block -= samples_todo; } } if(!needs_block_size) uTimeCurrent = time_target; if(time_target >= uTimeEnd) { if(uStreamPosition < mStream.size()) { for(; uStreamPosition < mStream.size(); uStreamPosition++) { if(needs_block_size) send_event_time_filtered(mStream.at(uStreamPosition).m_event, into_block); else send_event_filtered(mStream.at(uStreamPosition).m_event); } } if((uLoopMode & (loop_mode_enable | loop_mode_force)) == (loop_mode_enable | loop_mode_force)) { if(uStreamLoopStart == ~0) { uStreamPosition = 0; uTimeCurrent = 0; } else { uStreamPosition = uStreamLoopStart; uTimeCurrent = uTimeLoopStart; } } else break; } } uSamplesRemaining = into_block; return done; } void MIDIPlayer::Seek(unsigned long sample) { if(sample >= uTimeEnd) { if((uLoopMode & (loop_mode_enable | loop_mode_force)) == (loop_mode_enable | loop_mode_force)) { while(sample >= uTimeEnd) sample -= uTimeEnd - uTimeLoopStart; } else { sample = uTimeEnd; } } if(uTimeCurrent > sample) { uStreamPosition = 0; shutdown(); } if(!startup()) return; uTimeCurrent = sample; std::vector filler; unsigned long stream_start = uStreamPosition; for(; uStreamPosition < mStream.size() && mStream.at(uStreamPosition).m_timestamp < uTimeCurrent; uStreamPosition++) ; if(uStreamPosition == mStream.size()) uSamplesRemaining = uTimeEnd - uTimeCurrent; else uSamplesRemaining = mStream.at(uStreamPosition).m_timestamp - uTimeCurrent; if(uStreamPosition > stream_start) { filler.resize(uStreamPosition - stream_start); filler.assign(mStream.begin() + stream_start, mStream.begin() + uStreamPosition); unsigned long i, j; for(i = 0, stream_start = uStreamPosition - stream_start; i < stream_start; i++) { midi_stream_event &e = filler.at(i); if((e.m_event & 0x800000F0) == 0x90 && (e.m_event & 0xFF0000)) // note on { if((e.m_event & 0x0F) == 9) // hax { e.m_event = 0; continue; } const uint32_t m = (e.m_event & 0x7F00FF0F) | 0x80; // note off const uint32_t m2 = (e.m_event & 0x7F00FFFF); // also note off for(j = i + 1; j < stream_start; j++) { midi_stream_event &e2 = filler.at(j); if((e2.m_event & 0xFF00FFFF) == m || e2.m_event == m2) { // kill 'em e.m_event = 0; e2.m_event = 0; break; } } } } float *temp; const unsigned int needs_time = send_event_needs_time(); if(needs_time) { temp = new float[needs_time * 2]; if(temp) { render(temp, needs_time); // flush events unsigned int render_junk = 0; bool timestamp_set = false; unsigned last_timestamp = 0; for(i = 0; i < stream_start; i++) { if(filler[i].m_event) { send_event_time_filtered(filler[i].m_event, render_junk); if(timestamp_set) { if(filler[i].m_timestamp != last_timestamp) { render_junk += 16; } } last_timestamp = filler[i].m_timestamp; timestamp_set = true; if(render_junk >= needs_time) { render(temp, needs_time); render_junk -= needs_time; } } } render(temp, needs_time); delete[] temp; } } else { temp = new float[16 * 2]; if(temp) { render(temp, 16); bool timestamp_set = false; unsigned last_timestamp = 0; for(i = 0; i < stream_start; i++) { if(filler[i].m_event) { if(timestamp_set) { if(filler[i].m_timestamp != last_timestamp) { render(temp, 16); } } last_timestamp = filler[i].m_timestamp; timestamp_set = true; send_event_filtered(filler[i].m_event); } } render(temp, 16); delete[] temp; } } } } void MIDIPlayer::setLoopMode(unsigned int mode) { if(uLoopMode != mode) { if(mode & loop_mode_enable) uTimeEnd -= uSampleRate; else uTimeEnd += uSampleRate; } uLoopMode = mode; } void MIDIPlayer::send_event_filtered(uint32_t b) { if(!(b & 0x80000000u)) { send_event(b); } else { const unsigned int p_index = b & 0xffffff; const uint8_t *p_data; size_t p_size, p_port; mSysexMap.get_entry(p_index, p_data, p_size, p_port); send_sysex_filtered(p_data, p_size, p_port); } } void MIDIPlayer::send_event_time_filtered(uint32_t b, unsigned int time) { if(!(b & 0x80000000u)) { if(reverb_chorus_disabled) { const uint32_t _b = b & 0x7FF0; if(_b == 0x5BB0 || _b == 0x5DB0) return; } send_event_time(b, time); } else { const unsigned int p_index = b & 0xffffff; const uint8_t *p_data; size_t p_size, p_port; mSysexMap.get_entry(p_index, p_data, p_size, p_port); send_sysex_time_filtered(p_data, p_size, p_port, time); } } void MIDIPlayer::setFilterMode(filter_mode m, bool disable_reverb_chorus) { mode = m; reverb_chorus_disabled = disable_reverb_chorus; if(initialized) { sysex_reset(0, 0); sysex_reset(1, 0); sysex_reset(2, 0); } } static const uint8_t syx_reset_gm[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 }; static const uint8_t syx_reset_gm2[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x03, 0xF7 }; static const uint8_t syx_reset_gs[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 }; static const uint8_t syx_reset_xg[] = { 0xF0, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 }; static const uint8_t syx_gs_limit_bank_lsb[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x41, 0x00, 0x03, 0x00, 0xF7 }; static bool syx_equal(const uint8_t *a, const uint8_t *b) { while(*a != 0xF7 && *b != 0xF7 && *a == *b) { a++; b++; } return *a == *b; } static bool syx_is_reset(const uint8_t *data) { return syx_equal(data, &syx_reset_gm[0]) || syx_equal(data, &syx_reset_gm2[0]) || syx_equal(data, &syx_reset_gs[0]) || syx_equal(data, &syx_reset_xg[0]); } void MIDIPlayer::sysex_send_gs(size_t port, uint8_t *data, size_t size, unsigned int time) { unsigned long i; unsigned char checksum = 0; for(i = 5; i + 1 < size && data[i + 1] != 0xF7; ++i) checksum += data[i]; checksum = (128 - checksum) & 127; data[i] = checksum; if(time) send_sysex_time(data, size, port, time); else send_sysex(data, size, port); } void MIDIPlayer::sysex_reset_sc(uint32_t port, unsigned int time) { unsigned int i; uint8_t message[11]; memcpy(&message[0], &syx_gs_limit_bank_lsb[0], sizeof(message)); message[7] = 1; switch(mode) { default: break; case filter_sc55: message[8] = 1; break; case filter_sc88: message[8] = 2; break; case filter_sc88pro: message[8] = 3; break; case filter_sc8850: case filter_default: message[8] = 4; break; } for(i = 0x41; i <= 0x49; ++i) { message[6] = i; sysex_send_gs(port, &message[0], sizeof(message), time); } message[6] = 0x40; sysex_send_gs(port, &message[0], sizeof(message), time); for(i = 0x4A; i <= 0x4F; ++i) { message[6] = i; sysex_send_gs(port, &message[0], sizeof(message), time); } } void MIDIPlayer::sysex_reset(size_t port, unsigned int time) { if(initialized) { if(time) { send_sysex_time(&syx_reset_xg[0], sizeof(syx_reset_xg), port, time); send_sysex_time(&syx_reset_gm2[0], sizeof(syx_reset_gm2), port, time); send_sysex_time(&syx_reset_gm[0], sizeof(syx_reset_gm), port, time); } else { send_sysex(&syx_reset_xg[0], sizeof(syx_reset_xg), port); send_sysex(&syx_reset_gm2[0], sizeof(syx_reset_gm2), port); send_sysex(&syx_reset_gm[0], sizeof(syx_reset_gm), port); } switch(mode) { case filter_gm: /* if (time) send_sysex_time(syx_reset_gm, sizeof(syx_reset_gm), port, time); else send_sysex(syx_reset_gm, sizeof(syx_reset_gm), port); */ break; case filter_gm2: if(time) send_sysex_time(&syx_reset_gm2[0], sizeof(syx_reset_gm2), port, time); else send_sysex(&syx_reset_gm2[0], sizeof(syx_reset_gm2), port); break; case filter_sc55: case filter_sc88: case filter_sc88pro: case filter_sc8850: case filter_default: if(time) send_sysex_time(&syx_reset_gs[0], sizeof(syx_reset_gs), port, time); else send_sysex(&syx_reset_gs[0], sizeof(syx_reset_gs), port); sysex_reset_sc(port, time); break; case filter_xg: if(time) send_sysex_time(&syx_reset_xg[0], sizeof(syx_reset_xg), port, time); else send_sysex(&syx_reset_xg[0], sizeof(syx_reset_xg), port); break; } { unsigned int i; for(i = 0; i < 16; ++i) { if(time) { send_event_time(0x78B0 + i + (port << 24), time); send_event_time(0x79B0 + i + (port << 24), time); if(mode != filter_xg || i != 9) { send_event_time(0x20B0 + i + (port << 24), time); send_event_time(0x00B0 + i + (port << 24), time); send_event_time(0xC0 + i + (port << 24), time); } } else { send_event(0x78B0 + i + (port << 24)); send_event(0x79B0 + i + (port << 24)); if(mode != filter_xg || i != 9) { send_event(0x20B0 + i + (port << 24)); send_event(0x00B0 + i + (port << 24)); send_event(0xC0 + i + (port << 24)); } } } } if(mode == filter_xg) { if(time) { send_event_time(0x20B9 + (port << 24), time); send_event_time(0x7F00B9 + (port << 24), time); send_event_time(0xC9 + (port << 24), time); } else { send_event(0x20B9 + (port << 24)); send_event(0x7F00B9 + (port << 24)); send_event(0xC9 + (port << 24)); } } if(reverb_chorus_disabled) { unsigned int i; if(time) { for(i = 0; i < 16; ++i) { send_event_time(0x5BB0 + i + (port << 24), time); send_event_time(0x5DB0 + i + (port << 24), time); } } else { for(i = 0; i < 16; ++i) { send_event(0x5BB0 + i + (port << 24)); send_event(0x5DB0 + i + (port << 24)); } } } } } void MIDIPlayer::send_sysex_filtered(const uint8_t *data, size_t size, size_t port) { send_sysex(data, size, port); if(syx_is_reset(data) && mode != filter_default) { sysex_reset(port, 0); } } void MIDIPlayer::send_sysex_time_filtered(const uint8_t *data, size_t size, size_t port, unsigned int time) { send_sysex_time(data, size, port, time); if(syx_is_reset(data) && mode != filter_default) { sysex_reset(port, time); } } bool MIDIPlayer::GetLastError(std::string &p_out) { return get_last_error(p_out); }