/****************************************************************************** * BR_MidiItemTimePos * ******************************************************************************/ BR_MidiItemTimePos::BR_MidiItemTimePos (MediaItem* item) : item (item), position (GetMediaItemInfo_Value(item, "D_POSITION")), length (GetMediaItemInfo_Value(item, "D_LENGTH")), timeBase (GetMediaItemInfo_Value(item, "C_BEATATTACHMODE")), looped (!!GetMediaItemInfo_Value(item, "B_LOOPSRC")), loopStart (-1), loopEnd (-1), loopedOffset (0) { // When restoring position and lenght, the only way to restore it for looped MIDI items is to use MIDI_SetItemExtents which will disable looping // for the whole item. Since we have no idea what will happen before this->Restore() is called take data for active MIDI item right now instead of // in this->Restore() (for example, if client calls SetIgnoreTempo() from BR_Util.h before restoring to disable "ignore project tempo" length of MIDI item source may change) if (looped) { if (MediaItem_Take* activeTake = GetActiveTake(item)) { double sourceLenPPQ = GetMidiSourceLengthPPQ(activeTake, true); loopStart = MIDI_GetProjTimeFromPPQPos(activeTake, sourceLenPPQ); // we're not using MIDI_GetProjTimeFromPPQPos(activeTake, 0) because later we will use MIDI_SetItemExtents to make sure looped item loopEnd = MIDI_GetProjTimeFromPPQPos(activeTake, sourceLenPPQ*2); // position info is restored and 0 PPQ in take could theoretically be behind project start in which case MIDI_SetItemExtents wont' work properly loopedOffset = GetMediaItemTakeInfo_Value(activeTake, "D_STARTOFFS"); } } int takeCount = CountTakes(item); for (int i = 0; i < takeCount; ++i) { MediaItem_Take* take = GetTake(item, i); int noteCount, ccCount, textCount; int midiEventCount = MIDI_CountEvts(take, ¬eCount, &ccCount, &textCount); // In case of looped item, if active take wasn't midi, get looped position here for first MIDI take if (looped && loopStart == -1 && loopEnd == -1 && IsMidi(take, NULL) && (midiEventCount > 0 || i == takeCount - 1)) { double sourceLenPPQ = GetMidiSourceLengthPPQ(take, true); loopStart = MIDI_GetProjTimeFromPPQPos(take, sourceLenPPQ); loopEnd = MIDI_GetProjTimeFromPPQPos(take, sourceLenPPQ*2); loopedOffset = GetMediaItemTakeInfo_Value(take, "D_STARTOFFS"); } if (midiEventCount > 0) { savedMidiTakes.push_back(BR_MidiItemTimePos::MidiTake(take, noteCount, ccCount, textCount)); BR_MidiItemTimePos::MidiTake* midiTake = &savedMidiTakes.back(); for (int i = 0; i < noteCount; ++i) midiTake->noteEvents.push_back(BR_MidiItemTimePos::MidiTake::NoteEvent(take, i)); for (int i = 0; i < ccCount; ++i) midiTake->ccEvents.push_back(BR_MidiItemTimePos::MidiTake::CCEvent(take, i)); for (int i = 0; i < textCount; ++i) midiTake->sysEvents.push_back(BR_MidiItemTimePos::MidiTake::SysEvent(take, i)); } } }
static void MidiTakePreview (int mode, MediaItem_Take* take, MediaTrack* track, double volume, double startOffset, double measureSync, bool pauseDuringPrev) { /* mode: 0 -> stop * * 1 -> start * * 2 -> toggle */ // First stop any ongoing preview RegisterCsurfPlayState(false, MidiTakePreviewPlayState); if (g_itemPreviewPlaying) { if (g_ItemPreview.preview_track) { StopTrackPreview(&g_ItemPreview); SendAllNotesOff((MediaTrack*)g_ItemPreview.preview_track); } else { StopPreview(&g_ItemPreview); } g_itemPreviewPlaying = false; plugin_register("-timer",(void*)MidiTakePreviewTimer); delete g_ItemPreview.src; if (g_itemPreviewPaused && mode != 1) // requesting new preview while old one is still playing shouldn't unpause playback { if (IsPaused(NULL)) OnPauseButtonEx(NULL); g_itemPreviewPaused = false; } // Toggled preview off...treat it as stop if (mode == 2) mode = 0; } // About IsRecording: REAPER won't preview anything during recording but extension will still think preview is in progress if we let it continue here if (mode == 0 || IsRecording(NULL)) return; if (take) { PreventUIRefresh(1); // item may get resized temporarily so hide it from the user MediaItem* item = GetMediaItemTake_Item(take); MediaItem_Take* oldTake = GetActiveTake(item); bool itemMuteState = *(bool*)GetSetMediaItemInfo(item, "B_MUTE", NULL); GetSetMediaItemInfo(item, "B_MUTE", &g_bFalse); // needs to be set before getting the source SetActiveTake(take); // active item take and editor take may differ // If timebase in MIDI editor is set to Beats (source), make sure item is croped so full MIDI source is visible beforing getting the source double itemStart = -1; double itemEnd = -1; double takeOffset = 0; double itemPositionOffset = 0; if (GetToggleCommandState2(SectionFromUniqueID(SECTION_MIDI_EDITOR), 40470) > 0) // Timebase: Beats(source) { itemStart = GetMediaItemInfo_Value(item, "D_POSITION"); itemEnd = itemStart + GetMediaItemInfo_Value(item, "D_LENGTH"); takeOffset = GetMediaItemTakeInfo_Value(take, "D_STARTOFFS"); double sourceLenPPQ = GetMidiSourceLengthPPQ(take, NULL); if (takeOffset != 0) SetMediaItemTakeInfo_Value(take, "D_STARTOFFS", 0); double itemSourceStart = MIDI_GetProjTimeFromPPQPos(take, 0); if (itemSourceStart < 0) { itemPositionOffset = abs(itemSourceStart); SetMediaItemInfo_Value(item, "D_POSITION", itemStart + itemPositionOffset); itemSourceStart = MIDI_GetProjTimeFromPPQPos(take, 0); } SetMediaItemInfo_Value(item, "D_POSITION", itemSourceStart); SetMediaItemInfo_Value(item, "D_LENGTH", MIDI_GetProjTimeFromPPQPos(take, sourceLenPPQ) - itemSourceStart); } double effectiveTakeLen = EffectiveMidiTakeEnd(take, true, true, true) - GetMediaItemInfo_Value(item, "D_POSITION"); PCM_source* src = DuplicateSource((PCM_source*)item); // must be item source otherwise item/take volume won't get accounted for if (src && effectiveTakeLen > 0) { GetSetMediaItemInfo((MediaItem*)src, "D_POSITION", &g_d0); GetSetMediaItemInfo((MediaItem*)src, "D_LENGTH", &effectiveTakeLen); if (!g_ItemPreview.src) { #ifdef _WIN32 InitializeCriticalSection(&g_ItemPreview.cs); #else pthread_mutex_init(&g_ItemPreview.mutex, NULL); #endif g_ItemPreview.loop = false; } g_ItemPreview.src = src; g_ItemPreview.m_out_chan = (track) ? (-1) : (0); g_ItemPreview.curpos = startOffset; g_ItemPreview.volume = volume; g_ItemPreview.preview_track = track; // Pause before preview otherwise MidiTakePreviewPlayState will stop it g_itemPreviewPaused = pauseDuringPrev; if (g_itemPreviewPaused && IsPlaying(NULL) && !IsPaused(NULL)) OnPauseButton(); if (g_ItemPreview.preview_track) g_itemPreviewPlaying = !!PlayTrackPreview2Ex(NULL, &g_ItemPreview, 1, measureSync); else g_itemPreviewPlaying = !!PlayPreviewEx(&g_ItemPreview, 1, measureSync); if (g_itemPreviewPlaying) { plugin_register("timer",(void*)MidiTakePreviewTimer); RegisterCsurfPlayState(true, MidiTakePreviewPlayState); } else delete g_ItemPreview.src; } if (itemStart != -1) { SetMediaItemInfo_Value(item, "D_POSITION", itemStart + itemPositionOffset); SetMediaItemInfo_Value(item, "D_LENGTH", itemEnd - itemStart); if (itemPositionOffset != 0) SetMediaItemInfo_Value(item, "D_POSITION", itemStart); if (takeOffset != 0) SetMediaItemTakeInfo_Value(take, "D_STARTOFFS", takeOffset); } SetActiveTake(oldTake); GetSetMediaItemInfo(item, "B_MUTE", &itemMuteState); PreventUIRefresh(-1); } }
double EffectiveMidiTakeStart (MediaItem_Take* take, bool ignoreMutedEvents, bool ignoreTextEvents, bool ignoreEventsOutsideItemBoundaries) { int noteCount, ccCount, sysCount; if (take && MIDI_CountEvts(take, ¬eCount, &ccCount, &sysCount)) { MediaItem* item = GetMediaItemTake_Item(take); double itemStart = GetMediaItemInfo_Value(item, "D_POSITION"); double itemStartPPQ = MIDI_GetPPQPosFromProjTime(take, itemStart); double itemEndPPQ = (!ignoreEventsOutsideItemBoundaries) ? 0 : MIDI_GetPPQPosFromProjTime(take, GetMediaItemInfo_Value(item, "D_LENGTH") + itemStart); int loopCount = (!ignoreEventsOutsideItemBoundaries) ? 0 : GetLoopCount(take, 0, NULL); double sourceLenPPQ = (!ignoreEventsOutsideItemBoundaries) ? 0 : GetMidiSourceLengthPPQ(take, true); bool validNote = false, validCC = false, validSys = false; double noteStart, ccStart, sysStart; double loopOffset = 0; for (int i = 0; i < noteCount; ++i) { bool muted; double start, end; MIDI_GetNote(take, i, NULL, &muted, &start, &end, NULL, NULL, NULL); if ((ignoreMutedEvents && !muted) || !ignoreMutedEvents) { if (!ignoreEventsOutsideItemBoundaries) { noteStart = start; validNote = true; break; } else { start += loopOffset; end += loopOffset; if (AreOverlapped(start, end, itemStartPPQ, itemEndPPQ)) { if (itemStartPPQ > start) start = itemStartPPQ; noteStart = start; validNote = true; break; } } } if (ignoreEventsOutsideItemBoundaries && loopCount > 0 && i == noteCount - 1) { if (sourceLenPPQ > 0 && loopOffset == 0) { loopOffset = sourceLenPPQ; i = -1; } } } loopOffset = 0; for (int i = 0; i < ccCount; ++i) { bool muted; double pos; MIDI_GetCC(take, i, NULL, &muted, &pos, NULL, NULL, NULL, NULL); if ((ignoreMutedEvents && !muted) || !ignoreMutedEvents) { if (!ignoreEventsOutsideItemBoundaries) { ccStart = pos; validCC = true; break; } else { pos += loopOffset; if (CheckBounds(pos, itemStartPPQ, itemEndPPQ)) { ccStart = pos; validCC = true; break; } } } if (ignoreEventsOutsideItemBoundaries && loopCount > 0 && i == ccCount - 1) { if (sourceLenPPQ > 0 && loopOffset == 0) { loopOffset = sourceLenPPQ; i = -1; } } } for (int i = 0; i < sysCount; ++i) { bool muted; double pos; int type; MIDI_GetTextSysexEvt(take, i, NULL, &muted, &pos, &type, NULL, NULL); if (((ignoreMutedEvents && !muted) || !ignoreMutedEvents) && ((ignoreTextEvents && type == -1) || !ignoreTextEvents)) { if (!ignoreEventsOutsideItemBoundaries) { sysStart = pos; validSys = true; break; } else { pos += loopOffset; if (CheckBounds(pos, itemStartPPQ, itemEndPPQ)) { sysStart = pos; validSys = true; break; } } } if (ignoreEventsOutsideItemBoundaries && loopCount > 0 && i == sysCount - 1) { if (sourceLenPPQ > 0 && loopOffset == 0) { loopOffset = sourceLenPPQ; i = -1; } } } if (validNote || validCC || validSys) { if (!validNote) noteStart = (validSys) ? sysStart : ccStart; if (!validCC) ccStart = (validSys) ? sysStart : noteStart; if (!validSys) sysStart = (ccStart) ? ccStart : noteStart; return MIDI_GetProjTimeFromPPQPos(take, min(min(noteStart, ccStart), sysStart)); } else { return GetMediaItemInfo_Value(item, "D_POSITION"); } } else { return GetMediaItemInfo_Value(GetMediaItemTake_Item(take), "D_POSITION"); } }
double GetOriginalPpqPos (MediaItem_Take* take, double ppqPos, bool* loopedItem, double* posVisInsertStartPpq, double* posVisInsertEndPpq) { double returnPos = 0; MediaItem* item = GetMediaItemTake_Item(take); if (!take || !item || !IsMidi(take, NULL)) { WritePtr(loopedItem, false); WritePtr(posVisInsertStartPpq, 0.0); WritePtr(posVisInsertEndPpq, 0.0); } else { double itemStart = GetMediaItemInfo_Value(item, "D_POSITION"); double itemEnd = itemStart + GetMediaItemInfo_Value(item, "D_LENGTH"); if (GetMediaItemInfo_Value(item, "B_LOOPSRC") == 0) { WritePtr(loopedItem, false); WritePtr(posVisInsertStartPpq, MIDI_GetPPQPosFromProjTime(take, itemStart)); WritePtr(posVisInsertEndPpq, MIDI_GetPPQPosFromProjTime(take, itemEnd)); returnPos = ppqPos; } else { WritePtr(loopedItem, true); double visibleItemStartPpq = MIDI_GetPPQPosFromProjTime(take, itemStart); double visibleItemEndPpq = MIDI_GetPPQPosFromProjTime(take, itemEnd); double sourceLenPpq = GetMidiSourceLengthPPQ(take, true); // Deduct take offset to get correct current loop iteration double itemStartPpq = MIDI_GetPPQPosFromProjTime(take, itemStart - GetMediaItemTakeInfo_Value(take, "D_STARTOFFS")); int currentLoop; int loopCount = GetLoopCount(take, MIDI_GetProjTimeFromPPQPos(take, ppqPos), ¤tLoop); returnPos = (ppqPos >= visibleItemStartPpq) ? (ppqPos - (currentLoop * sourceLenPpq)) : ppqPos; if (ppqPos > visibleItemEndPpq) // position after item end { WritePtr(posVisInsertStartPpq, 0.0); WritePtr(posVisInsertEndPpq, 0.0); } else if (ppqPos < visibleItemStartPpq || currentLoop == 0) // position in first loop iteration or before it { WritePtr(posVisInsertStartPpq, visibleItemStartPpq); WritePtr(posVisInsertEndPpq, (visibleItemEndPpq - visibleItemStartPpq >= sourceLenPpq) ? itemStartPpq + sourceLenPpq : visibleItemEndPpq); } else if (currentLoop == loopCount) // position in last loop iteration { WritePtr(posVisInsertStartPpq, itemStartPpq); WritePtr(posVisInsertEndPpq, itemStartPpq + (visibleItemEndPpq - (currentLoop * sourceLenPpq))); } else // position in other loop iterations { WritePtr(posVisInsertStartPpq, itemStartPpq); WritePtr(posVisInsertEndPpq, itemStartPpq + sourceLenPpq); } } } return returnPos; }
double EffectiveMidiTakeEnd (MediaItem_Take* take, bool ignoreMutedEvents, bool ignoreTextEvents, bool ignoreEventsOutsideItemBoundaries) { int noteCount, ccCount, sysCount; if (take && MIDI_CountEvts(take, ¬eCount, &ccCount, &sysCount)) { MediaItem* item = GetMediaItemTake_Item(take); double itemStart = GetMediaItemInfo_Value(item, "D_POSITION"); double itemStartPPQ = MIDI_GetPPQPosFromProjTime(take, itemStart); double itemEndPPQ = (!ignoreEventsOutsideItemBoundaries) ? 0 : MIDI_GetPPQPosFromProjTime(take, GetMediaItemInfo_Value(item, "D_LENGTH") + itemStart); int loopCount = GetLoopCount(take, 0, NULL); double sourceLenPPQ = GetMidiSourceLengthPPQ(take, true); double effectiveTakeEndPPQ = -1; for (int i = 0; i < noteCount; ++i) { bool muted; double noteStart, noteEnd; MIDI_GetNote(take, i, NULL, &muted, ¬eStart, ¬eEnd, NULL, NULL, NULL); if (((ignoreMutedEvents && !muted) || !ignoreMutedEvents)) { noteEnd += loopCount*sourceLenPPQ; if (!ignoreEventsOutsideItemBoundaries) { if (noteEnd > effectiveTakeEndPPQ) effectiveTakeEndPPQ = noteEnd; } else { noteStart += loopCount*sourceLenPPQ; if (CheckBounds(noteStart, itemStartPPQ, itemEndPPQ)) { if (noteEnd > itemEndPPQ) noteEnd = itemEndPPQ; if (noteEnd > effectiveTakeEndPPQ) effectiveTakeEndPPQ = noteEnd; } else if (loopCount > 0) { noteStart -= sourceLenPPQ; if (CheckBounds(noteStart, itemStartPPQ, itemEndPPQ)) { noteEnd -= sourceLenPPQ; if (noteEnd > itemEndPPQ) noteEnd = itemEndPPQ; if (noteEnd > effectiveTakeEndPPQ) effectiveTakeEndPPQ = noteEnd; } } } } } for (int i = ccCount - 1; i >= 0; --i) { bool muted; double pos; MIDI_GetCC(take, i, NULL, &muted, &pos, NULL, NULL, NULL, NULL); if ((ignoreMutedEvents && !muted) || !ignoreMutedEvents) { pos += loopCount*sourceLenPPQ; if (!ignoreEventsOutsideItemBoundaries) { if (pos > effectiveTakeEndPPQ) effectiveTakeEndPPQ = pos + 1; // add 1 ppq so length definitely includes that last event break; } else { if (CheckBounds(pos, itemStartPPQ, itemEndPPQ)) { if (pos > effectiveTakeEndPPQ) { effectiveTakeEndPPQ = pos + 1; break; } } else if (loopCount > 0) { pos -= sourceLenPPQ; if (CheckBounds(pos, itemStartPPQ, itemEndPPQ)) { if (pos > effectiveTakeEndPPQ) { effectiveTakeEndPPQ = pos + 1; break; } } } } } } for (int i = 0; i < sysCount; ++i) { bool muted; double pos; int type; MIDI_GetTextSysexEvt(take, i, NULL, &muted, &pos, &type, NULL, NULL); if (((ignoreMutedEvents && !muted) || !ignoreMutedEvents) && ((ignoreTextEvents && type == -1) || !ignoreTextEvents)) { pos += loopCount*sourceLenPPQ; if (!ignoreEventsOutsideItemBoundaries) { if (pos > effectiveTakeEndPPQ) effectiveTakeEndPPQ = pos + 1; // add 1 ppq so length definitely includes that last event } else { if (CheckBounds(pos, itemStartPPQ, itemEndPPQ)) { if (pos > effectiveTakeEndPPQ) effectiveTakeEndPPQ = pos + 1; } else if (loopCount > 0) { pos -= sourceLenPPQ; if (CheckBounds(pos, itemStartPPQ, itemEndPPQ)) { if (pos > effectiveTakeEndPPQ) effectiveTakeEndPPQ = pos + 1; } } } } } return (effectiveTakeEndPPQ == -1) ? itemStart : MIDI_GetProjTimeFromPPQPos(take, effectiveTakeEndPPQ); } return 0; }