void TempoAtGrid (COMMAND_T* ct) { // Get tempo map and grid BR_Envelope tempoMap(GetTempoEnv()); if (!tempoMap.CountSelected()) return; double grid; GetConfig("projgriddiv", grid); // Loop through selected points Undo_BeginBlock2(NULL); int count = tempoMap.Count()-1; for (int i = 0; i < tempoMap.CountSelected(); ++i) { // Get tempo points int id = tempoMap.GetSelected(i); double t0, t1, b0, b1; int s0; tempoMap.GetPoint(id, &t0, &b0, &s0, NULL); tempoMap.GetPoint(id+1, &t1, &b1, NULL, NULL); // If last tempo point is selected, fake second point as if it's at the end of project (markers and regions included) if (id == count) { b1 = b0; t1 = EndOfProject(true, true); } // "fake" bpm for second point (needed for TempoAtPosition) if (s0 == SQUARE) b1 = b0; // Grid diving starts again from the start of the measure in which tempo marker is so we need to calculate // the offset between where grid line really is and where it would be if we just divided QN with grid spacing. double beat; int measure; GetTempoTimeSigMarker(NULL, id, NULL, &measure, &beat, NULL, NULL, NULL, NULL); double offset = grid - fmod(TimeMap_timeToQN(TimeMap2_beatsToTime(0, 0, &measure)), grid); // Find first grid line and then loop through the rest creating tempo points double pGridLn = t0, gridLn = TimeMap_timeToQN(t0); gridLn = TimeMap_QNToTime(gridLn-offset - fmod(gridLn,grid)); // it can be before tempo point but next while should correct that while (true) { // Search for the next grid line while (gridLn < pGridLn + MAX_GRID_DIV) // MAX_GRID_DIV acts as safety net to prevent accidental multiple points around grid lines/tempo points gridLn = TimeMap_QNToTime(TimeMap_timeToQN(gridLn) + grid); // Create points until the next point if (gridLn < t1 - MAX_GRID_DIV) tempoMap.CreatePoint(tempoMap.Count(), gridLn, TempoAtPosition(b0, b1, t0, t1, gridLn), s0, 0, false); else break; pGridLn = gridLn; } } tempoMap.Commit(); Undo_EndBlock2(NULL, SWS_CMD_SHORTNAME(ct), UNDO_STATE_ALL); }
void BR_MidiItemTimePos::Restore (double timeOffset /*=0*/) { SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", 0); for (size_t i = 0; i < savedMidiTakes.size(); ++i) { BR_MidiItemTimePos::MidiTake* midiTake = &savedMidiTakes[i]; MediaItem_Take* take = midiTake->take; int noteCount, ccCount, textCount; if (MIDI_CountEvts(take, ¬eCount, &ccCount, &textCount)) { for (int i = 0; i < noteCount; ++i) MIDI_DeleteNote(take, 0); for (int i = 0; i < ccCount; ++i) MIDI_DeleteCC(take, 0); for (int i = 0; i < textCount; ++i) MIDI_DeleteTextSysexEvt(take, 0); } if (looped && loopStart != -1 && loopEnd != -1) { SetMediaItemTakeInfo_Value(take, "D_STARTOFFS", 0); MIDI_SetItemExtents(item, TimeMap_timeToQN(loopStart), TimeMap_timeToQN(loopEnd)); SetMediaItemInfo_Value(item , "B_LOOPSRC", 1); // because MIDI_SetItemExtents() disables looping TrimItem(item, position, position + length, true); SetMediaItemTakeInfo_Value(take, "D_STARTOFFS", loopedOffset); } else { TrimItem(item, position, position + length, true); } for (size_t i = 0; i < midiTake->noteEvents.size(); ++i) midiTake->noteEvents[i].InsertEvent(take, timeOffset); for (size_t i = 0; i < midiTake->ccEvents.size(); ++i) midiTake->ccEvents[i].InsertEvent(take, timeOffset); for (size_t i = 0; i < midiTake->sysEvents.size(); ++i) midiTake->sysEvents[i].InsertEvent(take, timeOffset); } SetMediaItemInfo_Value(item, "C_BEATATTACHMODE", timeBase); }