hydrogen 1.2.3
Timeline.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23
24#include <algorithm>
25#include <core/Timeline.h>
26#include <core/Hydrogen.h>
27#include <core/Basics/Song.h>
28
29namespace H2Core
30{
31
33 , m_fDefaultBpm( 120 ) {
34}
35
37 m_tempoMarkers.clear();
38 m_tags.clear();
39}
40
44
47
48void Timeline::addTempoMarker( int nColumn, float fBpm ) {
49 if ( fBpm < MIN_BPM ) {
50 fBpm = MIN_BPM;
51 WARNINGLOG( QString( "Provided bpm %1 is too low. Assigning lower bound %2 instead" )
52 .arg( fBpm ).arg( MIN_BPM ) );
53 } else if ( fBpm > MAX_BPM ) {
54 fBpm = MAX_BPM;
55 WARNINGLOG( QString( "Provided bpm %1 is too high. Assigning upper bound %2 instead" )
56 .arg( fBpm ).arg( MAX_BPM ) );
57 }
58
59 if ( hasColumnTempoMarker( nColumn ) ) {
60 ERRORLOG( QString( "There is already a tempo marker present in column %1. Please remove it first." )
61 .arg( nColumn ) );
62 return;
63 }
64
65 std::shared_ptr<TempoMarker> pTempoMarker = std::make_shared<TempoMarker>();
66 pTempoMarker->nColumn = nColumn;
67 pTempoMarker->fBpm = fBpm;
68
69 m_tempoMarkers.push_back( pTempoMarker );
71}
72
73void Timeline::deleteTempoMarker( int nColumn ) {
74 // Erase the value to set the new value
75 if ( m_tempoMarkers.size() >= 1 ){
76 for ( int t = 0; t < m_tempoMarkers.size(); t++ ){
77 if ( m_tempoMarkers[t]->nColumn == nColumn ) {
78 m_tempoMarkers.erase( m_tempoMarkers.begin() + t);
79 }
80 }
81 }
82
84}
85
86float Timeline::getTempoAtColumn( int nColumn ) const {
87 auto pHydrogen = Hydrogen::get_instance();
88
89 if ( m_tempoMarkers.size() == 0 ) {
90 return m_fDefaultBpm;
91 }
92
93 float fBpm = m_fDefaultBpm;
94 // When transport is stopped nColumn is set to -1 by the
95 // AudioEngine.
96 if ( nColumn == -1 ) {
97 nColumn = 0;
98 }
99 if ( isFirstTempoMarkerSpecial() && nColumn < m_tempoMarkers[ 0 ]->nColumn ) {
100 fBpm = m_fDefaultBpm;
101 } else {
102 for ( int ii = 0; ii < static_cast<int>(m_tempoMarkers.size()); ii++) {
103 if ( m_tempoMarkers[ ii ]->nColumn > nColumn ) {
104 break;
105 }
106 fBpm = m_tempoMarkers[ ii ]->fBpm;
107 }
108 }
109 return fBpm;
110}
111
113 if ( m_tempoMarkers.size() == 0 ) {
114 return true;
115 }
116
117 return m_tempoMarkers[ 0 ]->nColumn != 0;
118}
119
120bool Timeline::hasColumnTempoMarker( int nColumn ) const {
121 for ( const auto& tt : m_tempoMarkers ){
122 if ( tt->nColumn == nColumn ) {
123 return true;
124 }
125 }
126 return false;
127}
128
129std::shared_ptr<const Timeline::TempoMarker> Timeline::getTempoMarkerAtColumn( int nColumn ) const {
130 if ( isFirstTempoMarkerSpecial() && nColumn == 0 ) {
131
132 std::shared_ptr<TempoMarker> pTempoMarker = std::make_shared<TempoMarker>();
133 pTempoMarker->nColumn = 0;
134 pTempoMarker->fBpm = Hydrogen::get_instance()->getSong()->getBpm();
135 return pTempoMarker;
136 }
137
138 for ( const auto& tt : m_tempoMarkers ){
139 if ( tt->nColumn == nColumn ) {
140 return tt;
141 }
142 }
143 return nullptr;
144}
145
146const std::vector<std::shared_ptr<const Timeline::TempoMarker>> Timeline::getAllTempoMarkers() const {
148
149 std::shared_ptr<TempoMarker> pTempoMarker = std::make_shared<TempoMarker>();
150 pTempoMarker->nColumn = 0;
151 pTempoMarker->fBpm = m_fDefaultBpm;
152
153 int nNumberOfTempoMarkers = m_tempoMarkers.size();
154 std::vector<std::shared_ptr<const TempoMarker>> tmpVector;
155 tmpVector.resize( nNumberOfTempoMarkers + 1 );
156 tmpVector[ 0 ] = pTempoMarker;
157
158 if ( nNumberOfTempoMarkers != 0 ) {
159 for ( int ii = 0; ii < nNumberOfTempoMarkers; ++ii ) {
160 // Since the returned vector is const, there is no
161 // need to make a deep copy.
162 tmpVector[ ii + 1 ] = m_tempoMarkers[ ii ];
163 }
164 }
165 return tmpVector;
166 }
167
168 return m_tempoMarkers;
169}
170
172 sort( m_tempoMarkers.begin(), m_tempoMarkers.end(),
174}
175
176void Timeline::addTag( int nColumn, QString sTag ) {
177
178 if ( hasColumnTag( nColumn ) ) {
179 ERRORLOG( QString( "There is already a tag present in column %1. Please remove it first." )
180 .arg( nColumn ) );
181 return;
182 }
183
184 std::shared_ptr<Tag> pTag( new Tag );
185 pTag->nColumn = nColumn;
186 pTag->sTag = sTag;
187
188 m_tags.push_back( std::move( pTag ) );
189 sortTags();
190}
191
192void Timeline::deleteTag( int nColumn ) {
193
194 // Erase the value to set the new value
195 if ( m_tags.size() >= 1 ){
196 for ( int t = 0; t < m_tags.size(); t++ ){
197 if ( m_tags[t]->nColumn == nColumn ) {
198 m_tags.erase( m_tags.begin() + t);
199 }
200 }
201 }
202
203 sortTags();
204}
205
206const QString Timeline::getTagAtColumn( int nColumn ) const {
207
208 QString sCurrentTag("");
209
210 for ( int t = 0; t < static_cast<int>(m_tags.size()); t++ ){
211 if ( m_tags[t]->nColumn > nColumn ){
212 break;
213 }
214 sCurrentTag = m_tags[t]->sTag;
215 }
216
217 return sCurrentTag;
218}
219
220bool Timeline::hasColumnTag( int nColumn ) const {
221 for ( const auto& tt : m_tags ){
222 if ( tt->nColumn == nColumn ) {
223 return true;
224 }
225 }
226 return false;
227}
228
230{
231 //sort the timeline vector to beats a < b
232 sort(m_tags.begin(), m_tags.end(), TagComparator());
233}
234
235QString Timeline::toQString( const QString& sPrefix, bool bShort ) const {
236 QString s = Base::sPrintIndention;
237 QString sOutput;
238 if ( ! bShort ) {
239 sOutput = QString( "%1[Timeline]\n" ).arg( sPrefix )
240 .append( QString( "%1%2m_fDefaultBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fDefaultBpm ) )
241 .append( QString( "%1%2m_tempoMarkers:\n" ).arg( sPrefix ).arg( s ) );
242 for ( auto const& tt : m_tempoMarkers ) {
243 if ( tt != nullptr ) {
244 sOutput.append( QString( "%1[column: %2 , bpm: %3]\n" ).arg( sPrefix + s + s ).arg( tt->nColumn ).arg( tt->fBpm ) );
245 }
246 }
247 sOutput.append( QString( "%1%2m_tags:\n" ).arg( sPrefix ).arg( s ) );
248 for ( auto const& tt : m_tags ) {
249 if ( tt != nullptr ) {
250 sOutput.append( QString( "%1[column: %2 , tag: %3]\n" ).arg( sPrefix + s + s ).arg( tt->nColumn ).arg( tt->sTag ) );
251 }
252 }
253 } else {
254
255 sOutput = QString( "%1[Timeline] " ).arg( sPrefix )
256 .append( QString( "m_fDefaultBpm: %1, " ).arg( m_fDefaultBpm ) )
257 .append( QString( "m_tempoMarkers: [" ) );
258 for ( auto const& tt : m_tempoMarkers ) {
259 if ( tt != nullptr ) {
260 sOutput.append( QString( " [column: %1 , bpm: %2]" ).arg( tt->nColumn ).arg( tt->fBpm ) );
261 }
262 }
263 sOutput.append( QString( "], m_tags: [" ) );
264 for ( auto const& tt : m_tags ) {
265 if ( tt != nullptr ) {
266 sOutput.append( QString( " [column: %1 , tag: %2]" ).arg( tt->nColumn ).arg( tt->sTag ) );
267 }
268 }
269 sOutput.append(" ]");
270 }
271
272 return sOutput;
273}
274
275QString Timeline::TempoMarker::getPrettyString( int nDecimals ) const {
276
277 int nPrec = 7;
278 if ( nDecimals >= 0 ) {
279 nPrec = std::min( nDecimals + ( fBpm >= 100 ? 3 : 2 ),
280 7 );
281 }
282 QString sOut = QString::number( fBpm, 'g', nPrec );
283
284 return std::move( sOut );
285}
286
287QString Timeline::TempoMarker::toQString( const QString& sPrefix, bool bShort ) const {
288 QString s = Base::sPrintIndention;
289 QString sOutput;
290 if ( ! bShort ) {
291 sOutput = QString( "%1[TempoMarker]\n" ).arg( sPrefix )
292 .append( QString( "%1%2nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg( nColumn ) )
293 .append( QString( "%1%2fBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( fBpm ) );
294 } else {
295
296 sOutput = QString( "%1[TempoMarker] " ).arg( sPrefix )
297 .append( QString( "nColumn: %3, " ).arg( nColumn ) )
298 .append( QString( "fBpm: %3" ).arg( fBpm ) );
299 }
300
301 return sOutput;
302}
303
304QString Timeline::Tag::toQString( const QString& sPrefix, bool bShort ) const {
305 QString s = Base::sPrintIndention;
306 QString sOutput;
307 if ( ! bShort ) {
308 sOutput = QString( "%1[TempoMarker]\n" ).arg( sPrefix )
309 .append( QString( "%1%2nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg( nColumn ) )
310 .append( QString( "%1%2sTag: %3\n" ).arg( sPrefix ).arg( s ).arg( sTag ) );
311 } else {
312
313 sOutput = QString( "%1[TempoMarker] " ).arg( sPrefix )
314 .append( QString( "nColumn: %3, " ).arg( nColumn ) )
315 .append( QString( "sTag: %3" ).arg( sTag ) );
316 }
317
318 return sOutput;
319}
320
321};
322
#define WARNINGLOG(x)
Definition Object.h:238
#define ERRORLOG(x)
Definition Object.h:239
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
void deleteTag(int nColumn)
Definition Timeline.cpp:192
void deleteTempoMarker(int nColumn)
Delete all tempo markers except for the first one and mark the tempo of the Timeline m_bUnset.
Definition Timeline.cpp:73
void sortTempoMarkers()
Definition Timeline.cpp:171
bool hasColumnTempoMarker(int nColumn) const
Definition Timeline.cpp:120
bool isFirstTempoMarkerSpecial() const
Whether there is a TempoMarker introduced by the user at the first column.
Definition Timeline.cpp:112
float getTempoAtColumn(int nColumn) const
Returns the tempo of the Song at a given column.
Definition Timeline.cpp:86
bool hasColumnTag(int nColumn) const
Definition Timeline.cpp:220
void addTag(int nColumn, QString sTag)
Adds a Tag to the Timeline.
Definition Timeline.cpp:176
std::shared_ptr< const Timeline::TempoMarker > getTempoMarkerAtColumn(int nColumn) const
Definition Timeline.cpp:129
void activate()
Registers the current playback tempo to m_fDefaultBpm.
Definition Timeline.cpp:41
void deactivate()
Convencience function in order to create a symmetric pair with activate.
Definition Timeline.cpp:45
std::vector< std::shared_ptr< const TempoMarker > > m_tempoMarkers
Definition Timeline.h:207
std::vector< std::shared_ptr< const Tag > > m_tags
Definition Timeline.h:208
const std::vector< std::shared_ptr< const TempoMarker > > getAllTempoMarkers() const
Definition Timeline.cpp:146
const QString getTagAtColumn(int nColumn) const
Returns the tag of the Song at a given column.
Definition Timeline.cpp:206
void addTempoMarker(int nColumn, float fBpm)
Adds a TempoMarker to the Timeline.
Definition Timeline.cpp:48
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Timeline.cpp:235
float m_fDefaultBpm
Tempo used for the special tempo marker.
Definition Timeline.h:216
#define MAX_BPM
Definition Globals.h:36
#define MIN_BPM
Definition Globals.h:35
Tag specifies a note added to a certain position in the Song.
Definition Timeline.h:100
QString toQString(const QString &sPrefix="", bool bShort=true) const
Definition Timeline.cpp:304
QString toQString(const QString &sPrefix="", bool bShort=true) const
Definition Timeline.cpp:287
QString getPrettyString(int nDecimals=2) const
Definition Timeline.cpp:275