1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18 #define RG_MODULE_STRING "[CreateTempoMapFromSegmentCommand]"
19
20 #include "CreateTempoMapFromSegmentCommand.h"
21
22 #include "misc/Debug.h"
23 #include "base/Composition.h"
24 #include "base/NotationTypes.h"
25 #include "base/RealTime.h"
26 #include "base/Segment.h"
27
28
29 namespace Rosegarden
30 {
31
CreateTempoMapFromSegmentCommand(Segment * groove)32 CreateTempoMapFromSegmentCommand::CreateTempoMapFromSegmentCommand(Segment *groove) :
33 NamedCommand(tr("Set Tempos from Beat Segment")),
34 m_composition(groove->getComposition())
35 {
36 initialise(groove);
37 }
38
~CreateTempoMapFromSegmentCommand()39 CreateTempoMapFromSegmentCommand::~CreateTempoMapFromSegmentCommand()
40 {
41 // nothing
42 }
43
44 void
execute()45 CreateTempoMapFromSegmentCommand::execute()
46 {
47 for (TempoMap::iterator i = m_oldTempi.begin(); i != m_oldTempi.end(); ++i) {
48 int n = m_composition->getTempoChangeNumberAt(i->first);
49 if (n < m_composition->getTempoChangeCount()) {
50 m_composition->removeTempoChange(n);
51 }
52 }
53
54 for (TempoMap::iterator i = m_newTempi.begin(); i != m_newTempi.end(); ++i) {
55 m_composition->addTempoAtTime(i->first, i->second);
56 }
57 }
58
59 void
unexecute()60 CreateTempoMapFromSegmentCommand::unexecute()
61 {
62 for (TempoMap::iterator i = m_newTempi.begin(); i != m_newTempi.end(); ++i) {
63 int n = m_composition->getTempoChangeNumberAt(i->first);
64 if (n < m_composition->getTempoChangeCount()) {
65 m_composition->removeTempoChange(n);
66 }
67 }
68
69 for (TempoMap::iterator i = m_oldTempi.begin(); i != m_oldTempi.end(); ++i) {
70 m_composition->addTempoAtTime(i->first, i->second);
71 }
72 }
73
74 void
initialise(Segment * s)75 CreateTempoMapFromSegmentCommand::initialise(Segment *s)
76 {
77 m_oldTempi.clear();
78 m_newTempi.clear();
79
80 //!!! need an additional option: per-chord, per-beat, per-bar.
81 // Let's work per-beat for the moment. Even for this, we should
82 // probably use TimeSignature.getDivisions()
83
84 std::vector<timeT> beatTimeTs;
85 std::vector<RealTime> beatRealTimes;
86
87 int startBar = m_composition->getBarNumber(s->getStartTime());
88 int barNo = startBar;
89 int beat = 0;
90
91 for (Segment::iterator i = s->begin(); s->isBeforeEndMarker(i); ++i) {
92 if ((*i)->isa(Note::EventType)) {
93
94 bool isNew;
95 TimeSignature sig =
96 m_composition->getTimeSignatureInBar(barNo, isNew);
97
98 beatTimeTs.push_back(m_composition->getBarStart(barNo) +
99 beat * sig.getBeatDuration());
100
101 if (++beat >= sig.getBeatsPerBar()) {
102 ++barNo;
103 beat = 0;
104 }
105
106 beatRealTimes.push_back(s->getComposition()->getElapsedRealTime
107 ((*i)->getAbsoluteTime()));
108 }
109 }
110
111 if (beatTimeTs.size() < 2)
112 return ;
113
114 tempoT prevTempo = 0;
115
116 // set up m_oldTempi and prevTempo
117
118 timeT firstBeatTimeT = *beatTimeTs.begin();
119 timeT lastBeatTimeT = *(beatTimeTs.end() - 1);
120
121 for (int i = m_composition->getTempoChangeNumberAt(firstBeatTimeT - 1) + 1;
122 i <= m_composition->getTempoChangeNumberAt(lastBeatTimeT - 1); ++i) {
123
124 std::pair<timeT, tempoT> tempoChange =
125 m_composition->getTempoChange(i);
126 m_oldTempi[tempoChange.first] = tempoChange.second;
127 if (prevTempo == 0)
128 prevTempo = tempoChange.second;
129 }
130
131 RG_DEBUG << "starting tempo: " << prevTempo;
132
133 timeT quarter = Note(Note::Crotchet).getDuration();
134
135 for (size_t beat = 1; beat < beatTimeTs.size(); ++beat) {
136
137 timeT beatTime = beatTimeTs[beat] - beatTimeTs[beat - 1];
138 RealTime beatRealTime = beatRealTimes[beat] - beatRealTimes[beat - 1];
139
140 // Calculate tempo to nearest qpm.
141 // This is 60 / {quarter note duration in seconds}
142 // = 60 / ( {beat in seconds} * {quarter in ticks} / { beat in ticks} )
143 // = ( 60 * {beat in ticks} ) / ( {beat in seconds} * {quarter in ticks} )
144 // Precision is deliberately limited to qpm to avoid silly values.
145
146 double beatSec = double(beatRealTime.sec) +
147 double(beatRealTime.usec() / 1000000.0);
148 double qpm = (60.0 * beatTime) / (beatSec * quarter);
149 tempoT tempo = Composition::getTempoForQpm(int(qpm + 0.001));
150
151 RG_DEBUG << "prev beat: " << beatTimeTs[beat] << ", prev beat real time " << beatRealTimes[beat];
152 RG_DEBUG << "time " << beatTime << ", rt " << beatRealTime << ", beatSec " << beatSec << ", tempo " << tempo;
153
154 if (tempo != prevTempo) {
155 m_newTempi[beatTimeTs[beat - 1]] = tempo;
156 prevTempo = tempo;
157 }
158 }
159
160 }
161
162 }
163