📄 interpretcommand.cpp
字号:
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: *//* Rosegarden A MIDI and audio sequencer and musical notation editor. This program is Copyright 2000-2007 Guillaume Laurent <glaurent@telegraph-road.org>, Chris Cannam <cannam@all-day-breakfast.com>, Richard Bown <richard.bown@ferventsoftware.com> The moral rights of Guillaume Laurent, Chris Cannam, and Richard Bown to claim authorship of this work have been asserted. Other copyrights also apply to some parts of this work. Please see the AUTHORS file and individual file headers for details. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information.*/#include "InterpretCommand.h"#include "base/Composition.h"#include "base/Event.h"#include "base/NotationTypes.h"#include "misc/Debug.h"#include "base/Quantizer.h"#include "base/Segment.h"#include "base/Sets.h"#include "base/BaseProperties.h"#include "base/Selection.h"#include "document/BasicSelectionCommand.h"#include <qstring.h>namespace Rosegarden{using namespace BaseProperties;const int InterpretCommand::NoInterpretation = 0;const int InterpretCommand::GuessDirections = (1<<0);const int InterpretCommand::ApplyTextDynamics = (1<<1);const int InterpretCommand::ApplyHairpins = (1<<2);const int InterpretCommand::StressBeats = (1<<3);const int InterpretCommand::Articulate = (1<<4);const int InterpretCommand::AllInterpretations= (1<<5) - 1;InterpretCommand::~InterpretCommand(){ for (IndicationMap::iterator i = m_indications.begin(); i != m_indications.end(); ++i) { delete i->second; }}voidInterpretCommand::modifySegment(){ // Of all the interpretations, Articulate is the only one that // changes event times or durations. This means we must apply it // last, as the selection cannot be used after it's been applied, // because the events in the selection will have been recreated // with their new timings. // The default velocity for new notes is 100, and the range is // 0-127 (in practice this seems to be roughly logarithmic rather // than linear, though perhaps that's an illusion). // We should only apply interpretation to those events actually // selected, but when applying things like hairpins and text // dynamics we need to take into account all dynamics that may // cover our events even if they're not selected or are not within // the time range of the selection at all. So first we'd better // find all the likely indications, starting at (for the sake of // argument) three bars before the start of the selection: Segment &segment(getSegment()); timeT t = m_selection->getStartTime(); for (int i = 0; i < 3; ++i) t = segment.getBarStartForTime(t); Segment::iterator itr = segment.findTime(t); while (itr != segment.end()) { timeT eventTime = (*itr)->getAbsoluteTime(); if (eventTime > m_selection->getEndTime()) break; if ((*itr)->isa(Indication::EventType)) { m_indications[eventTime] = new Indication(**itr); } ++itr; } //!!! need the option of ignoring current velocities or adjusting //them: at the moment ApplyTextDynamics ignores them and the //others adjust them if (m_interpretations & GuessDirections) guessDirections(); if (m_interpretations & ApplyTextDynamics) applyTextDynamics(); if (m_interpretations & ApplyHairpins) applyHairpins(); if (m_interpretations & StressBeats) stressBeats(); if (m_interpretations & Articulate) articulate(); //!!! Finally, in future we should extend this to allow // indications on one segment (e.g. top line of piano staff) to // affect another (e.g. bottom line). All together now: "Even // Rosegarden 2.1 could do that!"}voidInterpretCommand::guessDirections(){ //...}voidInterpretCommand::applyTextDynamics(){ // laborious Segment &segment(getSegment()); int velocity = 100; timeT startTime = m_selection->getStartTime(); timeT endTime = m_selection->getEndTime(); for (Segment::iterator i = segment.begin(); segment.isBeforeEndMarker(i); ++i) { timeT t = (*i)->getAbsoluteTime(); if (t > endTime) break; if (Text::isTextOfType(*i, Text::Dynamic)) { std::string text; if ((*i)->get <String>(Text::TextPropertyName, text)) { velocity = getVelocityForDynamic(text); } } if (t >= startTime && (*i)->isa(Note::EventType) && m_selection->contains(*i)) { (*i)->set <Int>(VELOCITY, velocity); } }}intInterpretCommand::getVelocityForDynamic(std::string text){ int velocity = 100; // should do case-insensitive matching with whitespace // removed. can surely be cleverer about this too! if (text == "ppppp") velocity = 10; else if (text == "pppp") velocity = 20; else if (text == "ppp") velocity = 30; else if (text == "pp") velocity = 40; else if (text == "p") velocity = 60; else if (text == "mp") velocity = 80; else if (text == "mf") velocity = 90; else if (text == "f") velocity = 105; else if (text == "ff") velocity = 110; else if (text == "fff") velocity = 115; else if (text == "ffff") velocity = 120; else if (text == "fffff") velocity = 125; NOTATION_DEBUG << "InterpretCommand::getVelocityForDynamic: unrecognised dynamic " << text << endl; return velocity;}voidInterpretCommand::applyHairpins(){ Segment &segment(getSegment()); int velocityToApply = -1; for (EventSelection::eventcontainer::iterator ecitr = m_selection->getSegmentEvents().begin(); ecitr != m_selection->getSegmentEvents().end(); ++ecitr) { Event *e = *ecitr; if (Text::isTextOfType(e, Text::Dynamic)) { velocityToApply = -1; } if (!e->isa(Note::EventType)) continue; bool crescendo = true; IndicationMap::iterator inditr = findEnclosingIndication(e, Indication::Crescendo); // we can't be in both crescendo and decrescendo -- at least, // not meaningfully if (inditr == m_indications.end()) { inditr = findEnclosingIndication(e, Indication::Decrescendo); if (inditr == m_indications.end()) { if (velocityToApply > 0) { e->set <Int>(VELOCITY, velocityToApply); } continue; } crescendo = false; } // The starting velocity for the indication is easy -- it's // just the velocity of the last note at or before the // indication begins that has a velocity timeT hairpinStartTime = inditr->first; // ensure we scan all of the events at this time: Segment::iterator itr(segment.findTime(hairpinStartTime + 1)); while (itr == segment.end() || (*itr)->getAbsoluteTime() > hairpinStartTime || !(*itr)->isa(Note::EventType) || !(*itr)->has(VELOCITY)) { if (itr == segment.begin()) { itr = segment.end(); break; } --itr; } long startingVelocity = 100; if (itr != segment.end()) { (*itr)->get <Int>(VELOCITY, startingVelocity); } // The ending velocity is harder. If there's a dynamic change // directly after the hairpin, then we want to use that // dynamic's velocity unless it opposes the hairpin's // direction. If there isn't, or it does oppose the hairpin, // we should probably make the degree of change caused by the // hairpin depend on its total duration. long endingVelocity = startingVelocity; timeT hairpinEndTime = inditr->first + inditr->second->getIndicationDuration(); itr = segment.findTime(hairpinEndTime); while (itr != segment.end()) { if (Text::isTextOfType(*itr, Text::Dynamic)) { std::string text; if ((*itr)->get <String>(Text::TextPropertyName, text)) { endingVelocity = getVelocityForDynamic(text); break; } } if ((*itr)->getAbsoluteTime() > (hairpinEndTime + Note(Note::Crotchet).getDuration())) break; ++itr; } if (( crescendo && (endingVelocity < startingVelocity)) || (!crescendo && (endingVelocity > startingVelocity))) { // we've got it wrong; prefer following the hairpin to // following whatever direction we got the dynamic from endingVelocity = startingVelocity; // and then fall into the next conditional to set up the // velocities } if (endingVelocity == startingVelocity) { // calculate an ending velocity based on starting velocity // and hairpin duration (okay, we'll leave that bit for later) endingVelocity = startingVelocity * (crescendo ? 120 : 80) / 100; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -