/// Initialize ExpandFigurationCommand /// @author Tom Breton (Tehom) void ExpandFigurationCommand::initialise(SegmentSelection selection) { FigurationVector figs; int figSourceID = -1; bool gotFigSource = false; RG_DEBUG << "Initializing ExpandFigurationCommand" << endl; // Update, because if we need new IDs they mustn't duplicate old // IDs, so we must be up to date on what IDs are there. SegmentFigData::SegmentFigDataMap segMap = SegmentFigData::getInvolvedSegments(true, this); for (SegmentSelection::iterator i = selection.begin(); i != selection.end(); ++i) { SegmentFigData& segmentData = SegmentFigData::findOrAdd(segMap, *i); // If it's used here, it's not uninvolved, so unless it's a // figuration, it's a chord source. if (segmentData.isa(SegmentFigData::Uninvolved)) { segmentData.convertType(SegmentFigData::ChordSource); } segmentData.addTagIfNeeded(*i, this); if (gotFigSource || !segmentData.isa(SegmentFigData::FigurationSource)) { continue; } figSourceID = segmentData.getID(); figs = FigurationSourceMap::getFigurations(*i); if (!figs.empty()) { gotFigSource = true; } } // If we didn't find a real figuration, there's nothing to do. if (!gotFigSource) { return; } SourcedFiguration sourcedfigs(figSourceID, figs); // Expand figuration in each segment in selection except the // figuration segment itself. for (SegmentSelection::iterator i = selection.begin(); i != selection.end(); ++i) { Segment *s = (*i); SegmentFigData::SegmentFigDataMap::iterator it = segMap.find(*i); Q_ASSERT_X(it != segMap.end(), "ExpandFigurationCommand::initialise", "didn't find the segment"); SegmentFigData& segmentData = it->second; if (!segmentData.isa(SegmentFigData::ChordSource)) { continue; } // Make a target segment Segment *target = s->clone(false); // Temporarily attach it to composition so that expand can // work. m_composition->weakAddSegment(target); target->clear(); target->fillWithRests(s->getEndTime()); SegmentFigData::addTag(target, this, SegmentID::Target); m_newSegments.insert(target); /** Add notes to target segment **/ for (Segment::iterator e = s->begin(); e != s->end(); ++e) { // Non-notes they don't imply there's a chord here. // We add them to target segment in case they are // clefs or key changes. if ((*e)->isa(SegmentID::EventType)) { continue; } if (!(*e)->isa(Note::EventType)) { target->insert(new Event(**e)); } } // rawStartTime is the apparent start time before we take bars // into account. We step it along the composition, finding // valid places to expand. Specifically, on bar lines not // already part of an expansion. timeT rawStartTime = s->getStartTime(); while (1) { timeT figurationStartTime = rawStartTimeToExact(rawStartTime); if (rawStartTime >= s->getEndTime()) { break; } timeT timePastFiguration = SegmentFigData::expand(sourcedfigs, ChordSegment(s, segmentData.getID()), target, figurationStartTime); // If we didn't expand, ensure we don't try endlessly at // the same place. if (timePastFiguration == figurationStartTime) { ++timePastFiguration; } rawStartTime = timePastFiguration; } // Detach from composition, because SegmentInsertCommand does // the actual placing m_composition->weakDetachSegment(target); Command *c = new SegmentInsertCommand(m_composition, target, s->getTrack()); addCommand(c); } }
// Initialize FitToBeatsCommand // @author Tom Breton (Tehom) void FitToBeatsCommand::initialise(Segment *s) { m_oldTempi.clear(); m_newTempi.clear(); m_oldSegments.clear(); m_newSegments.clear(); // Get the real times from the beat segment vecRealTime beatRealTimes; int success = getBeatRealTimes(s, beatRealTimes); if(!success) { return; } // Store the current tempos getCurrentTempi(*m_composition, m_oldTempi); tempoT defaultTempo = m_composition->getCompositionDefaultTempo(); // A temporary copy of the composition. It is not intended to be // a complete copy, it just provides a place for new segments and // tempi to live until we have fully copied events to their new // state. Composition scratchComposition; scratchComposition.clear(); scratchComposition.setCompositionDefaultTempo(defaultTempo); // Set tempos in scratchComposition such that each observed beat // in beatRealTimes takes one beatTime. { // Starting time is the same for both. timeT firstBeatTime = m_composition->getElapsedTimeForRealTime(beatRealTimes[0]); unsigned int numBeats = beatRealTimes.size(); // Get interval between beats from time signature. // Get time signature TimeSignature timeSig = m_composition->getTimeSignatureAt(firstBeatTime); timeT beatTime = timeSig.getBeatDuration(); // We're going to visit the beats in reverse order, and always // remembering the next beat (the next beat time-wise, which // the iterator visited last time) vecRealTime::const_reverse_iterator i = beatRealTimes.rbegin(); // Treat the final beat specially timeT finalBeatTime = firstBeatTime + ((numBeats - 1) * beatTime); RealTime finalRealTime = beatRealTimes.back(); scratchComposition.addTempoAtTime(finalBeatTime, defaultTempo, -1); // Step past it ++i; // Set up loop variables timeT nextBeatTime = finalBeatTime; RealTime nextRealTime = finalRealTime; // nextTempo is unused, it will be used if we make ramped // tempi. /* tempoT nextTempo = defaultTempo; */ // Treat all the other beats. while (i != beatRealTimes.rend()) { timeT timeNow = nextBeatTime - beatTime; RealTime realTimeNow = *i; RealTime realTimeDelta = nextRealTime - realTimeNow; // Calculate what tempoT will get us to the right real // time. For now, we use unramped tempi. tempoT rampTo = -1; tempoT tempo = Composition::timeRatioToTempo(realTimeDelta, beatTime, rampTo); scratchComposition.addTempoAtTime(timeNow, tempo, rampTo); // Step nextBeatTime = timeNow; nextRealTime = realTimeNow; /* nextTempo = tempo; */ ++i; } } // We don't try to copy over tempo changes that are outside the // range of the groove segment (before or after). We don't try to // correct for accumulated error. // Done setting Tempi // Collect tempi getCurrentTempi(scratchComposition, m_newTempi); // Copy all the events to scratchComposition. The copies will be // at the same realtime but not the same timeT. Even events in // the groove segment get copied. segmentcontainer &origSegments = m_composition->getSegments(); for (Composition::iterator i = origSegments.begin(); i != origSegments.end(); ++i) { Segment * const oldSegment = *i; // We'd prefer to just make a segment with no events that's // otherwise the same as the old one but we can't. Segment *newSegment = oldSegment->clone(false); newSegment->clear(); // Add the segments into appropriate containers. // scratchComposition owns the new segments during initialise, // but m_newSegments will own them after initialise returns. m_oldSegments.insert(oldSegment); m_newSegments.insert(newSegment); scratchComposition.addSegment(newSegment); //Iterate over notes in the old segment. const timeT earliestTime = 0; for (Segment::iterator j = oldSegment->findTime(earliestTime); oldSegment->isBeforeEndMarker(j); ++j) { // Get the old-timed event times. timeT oldStartTime = (*j)->getAbsoluteTime(); timeT duration = (*j)->getDuration(); // Get the real event times. RealTime RealStartTime = m_composition->getElapsedRealTime(oldStartTime); RealTime RealEndTime; if (duration == 0) { RealEndTime = RealStartTime; } else { timeT oldEndTime = oldStartTime + duration; RealEndTime = m_composition->getElapsedRealTime(oldEndTime); } // Get the new target times. Use scratchComposition // because its times use the new Tempi. timeT newStartTime = scratchComposition.getElapsedTimeForRealTime(RealStartTime); timeT newDuration; if (duration == 0) { newDuration = 0; } else { timeT newEndTime = scratchComposition.getElapsedTimeForRealTime(RealEndTime); newDuration = newEndTime - newStartTime; } // Add a parallel event in the new segment. newSegment->insert(new Event(**j, newStartTime, newDuration)); } } // Detach the segments before scratchComposition goes out of // scope. m_newSegments contains exactly the segments that need // to be detached. for (segmentcontainer::iterator i = m_newSegments.begin(); i != m_newSegments.end(); ++i) { scratchComposition.weakDetachSegment(*i); } // We do the actual swapping of old <-> new in (un)execute. }