#include "MidiLock.h" #include "MidiPlayer.h" #include "MidiSong.h" MidiPlayer::MidiPlayer(std::shared_ptr host, std::shared_ptr song) : host(host), song(song), trackPlayer(song->getTrack(0)) { ++_mdb; } void MidiPlayer::timeElapsed(float seconds) { curMetricTime += seconds * 120.0f / 60.0f; // fixed at 120 bpm for now if (!isPlaying) { return; } bool locked = song->lock->playerTryLock(); if (locked) { if (song->lock->dataModelDirty()) { trackPlayer.reset(); } trackPlayer.updateToMetricTime(curMetricTime, host.get()); song->lock->playerUnlock(); } else { trackPlayer.reset(); host->onLockFailed(); } } TrackPlayer::TrackPlayer(MidiTrackPtr track) : track(track) { } TrackPlayer::~TrackPlayer() { //curEvent = nullptr; } void TrackPlayer::updateToMetricTime(double time, IPlayerHost* host) { // If we had a conflict and needed to reset, then // start all over from beginning if (isReset) { curEvent = curEvent = track->begin(); noteOffTime = -1; isReset = false; loopStart = 0; } // keep processing events until we are caught up while (playOnce(time, host)) { } } bool TrackPlayer::playOnce(double metricTime, IPlayerHost* host) { bool didSomething = false; if (noteOffTime >= 0 && noteOffTime <= metricTime) { host->setGate(false); noteOffTime = -1; didSomething = true; } const double eventStart = (loopStart + curEvent->first); if (eventStart <= metricTime) { MidiEventPtr event = curEvent->second; switch (event->type) { case MidiEvent::Type::Note: { MidiNoteEventPtr note = safe_cast(event); // should now output the note. host->setGate(true); host->setCV(note->pitchCV); // and save off the note-off time. noteOffTime = note->duration + eventStart; ++curEvent; } break; case MidiEvent::Type::End: // for now, should loop. // uh oh! // assert(false); // curMetricTime = 0; // trackPlayStatus.curEvent = song->getTrack(0)->begin(); loopStart += curEvent->first; curEvent = track->begin(); break; default: assert(false); } didSomething = true; } return didSomething; } #if 0 void MidiPlayer::timeElapsed(float seconds) { curMetricTime += seconds * 120.0f / 60.0f; // fixed at 120 bpm for now bool locked = song->lock->playerTryLock(); if (locked) { while (playOnce()) { } song->lock->playerUnlock(); } else { trackPlayStatus.reset(); host->onLockFailed(); } } bool MidiPlayer::playOnce() { if (!isPlaying) { return false; } bool didSomething = false; // If we had a conflict and needed to reset, then // seek from the start to where we should be. if (trackPlayStatus.isReset) { trackPlayStatus.seekTo(song.get(), curMetricTime, host.get()); return true; } if (trackPlayStatus.noteOffTime >= 0 && trackPlayStatus.noteOffTime <= curMetricTime) { host->setGate(false); trackPlayStatus.noteOffTime = -1; didSomething = true; } if (trackPlayStatus.curEvent->first <= curMetricTime) { MidiEventPtr event = trackPlayStatus.curEvent->second; switch (event->type) { case MidiEvent::Type::Note: { MidiNoteEventPtr note = safe_cast(event); // should now output the note. host->setGate(true); host->setCV(note->pitchCV); // and save off the note-off time. trackPlayStatus.noteOffTime = note->duration + note->startTime; ++trackPlayStatus.curEvent; } break; case MidiEvent::Type::End: // for now, should loop. curMetricTime = 0; trackPlayStatus.curEvent = song->getTrack(0)->begin(); break; default: assert(false); } didSomething = true; } return didSomething; } void TrackPlayStatus::seekTo(MidiSong* song, float time, IPlayerHost* host) { isReset = false; curEvent = song->getTrack(0)->begin(); while (curEvent->second->startTime < time) { ++curEvent; if (curEvent == song->getTrack(0)->end()) { assert(false); return; } MidiEventPtr evt = curEvent->second; MidiNoteEventPtr note = safe_cast< MidiNoteEvent>(evt); assert(note); noteOffTime = note->startTime + note->duration; } } #endif