hydrogen 1.2.6
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-2025 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::setDefaultBpm( float fDefaultBpm ) {
49 m_fDefaultBpm = fDefaultBpm;
50}
51
52void Timeline::addTempoMarker( int nColumn, float fBpm ) {
53 if ( fBpm < MIN_BPM ) {
54 fBpm = MIN_BPM;
55 WARNINGLOG( QString( "Provided bpm %1 is too low. Assigning lower bound %2 instead" )
56 .arg( fBpm ).arg( MIN_BPM ) );
57 } else if ( fBpm > MAX_BPM ) {
58 fBpm = MAX_BPM;
59 WARNINGLOG( QString( "Provided bpm %1 is too high. Assigning upper bound %2 instead" )
60 .arg( fBpm ).arg( MAX_BPM ) );
61 }
62
63 if ( hasColumnTempoMarker( nColumn ) ) {
64 ERRORLOG( QString( "There is already a tempo marker present in column %1. Please remove it first." )
65 .arg( nColumn ) );
66 return;
67 }
68
69 std::shared_ptr<TempoMarker> pTempoMarker = std::make_shared<TempoMarker>();
70 pTempoMarker->nColumn = nColumn;
71 pTempoMarker->fBpm = fBpm;
72
73 m_tempoMarkers.push_back( pTempoMarker );
75}
76
77void Timeline::deleteTempoMarker( int nColumn ) {
78 // Erase the value to set the new value
79 if ( m_tempoMarkers.size() >= 1 ){
80 for ( int t = 0; t < m_tempoMarkers.size(); t++ ){
81 if ( m_tempoMarkers[t]->nColumn == nColumn ) {
82 m_tempoMarkers.erase( m_tempoMarkers.begin() + t);
83 }
84 }
85 }
86
88}
89
90float Timeline::getTempoAtColumn( int nColumn ) const {
91 auto pHydrogen = Hydrogen::get_instance();
92
93 if ( m_tempoMarkers.size() == 0 ) {
94 return m_fDefaultBpm;
95 }
96
97 float fBpm = m_fDefaultBpm;
98 // When transport is stopped nColumn is set to -1 by the
99 // AudioEngine.
100 if ( nColumn == -1 ) {
101 nColumn = 0;
102 }
103 if ( isFirstTempoMarkerSpecial() && nColumn < m_tempoMarkers[ 0 ]->nColumn ) {
104 fBpm = m_fDefaultBpm;
105 } else {
106 for ( int ii = 0; ii < static_cast<int>(m_tempoMarkers.size()); ii++) {
107 if ( m_tempoMarkers[ ii ]->nColumn > nColumn ) {
108 break;
109 }
110 fBpm = m_tempoMarkers[ ii ]->fBpm;
111 }
112 }
113 return fBpm;
114}
115
117 if ( m_tempoMarkers.size() == 0 ) {
118 return true;
119 }
120
121 return m_tempoMarkers[ 0 ]->nColumn != 0;
122}
123
124bool Timeline::hasColumnTempoMarker( int nColumn ) const {
125 for ( const auto& tt : m_tempoMarkers ){
126 if ( tt->nColumn == nColumn ) {
127 return true;
128 }
129 }
130 return false;
131}
132
133std::shared_ptr<const Timeline::TempoMarker> Timeline::getTempoMarkerAtColumn( int nColumn ) const {
134 if ( isFirstTempoMarkerSpecial() && nColumn == 0 ) {
135
136 std::shared_ptr<TempoMarker> pTempoMarker = std::make_shared<TempoMarker>();
137 pTempoMarker->nColumn = 0;
138 pTempoMarker->fBpm = Hydrogen::get_instance()->getSong()->getBpm();
139 return pTempoMarker;
140 }
141
142 for ( const auto& tt : m_tempoMarkers ){
143 if ( tt->nColumn == nColumn ) {
144 return tt;
145 }
146 }
147 return nullptr;
148}
149
150const std::vector<std::shared_ptr<const Timeline::TempoMarker>> Timeline::getAllTempoMarkers() const {
152
153 std::shared_ptr<TempoMarker> pTempoMarker = std::make_shared<TempoMarker>();
154 pTempoMarker->nColumn = 0;
155 pTempoMarker->fBpm = m_fDefaultBpm;
156
157 int nNumberOfTempoMarkers = m_tempoMarkers.size();
158 std::vector<std::shared_ptr<const TempoMarker>> tmpVector;
159 tmpVector.resize( nNumberOfTempoMarkers + 1 );
160 tmpVector[ 0 ] = pTempoMarker;
161
162 if ( nNumberOfTempoMarkers != 0 ) {
163 for ( int ii = 0; ii < nNumberOfTempoMarkers; ++ii ) {
164 // Since the returned vector is const, there is no
165 // need to make a deep copy.
166 tmpVector[ ii + 1 ] = m_tempoMarkers[ ii ];
167 }
168 }
169 return tmpVector;
170 }
171
172 return m_tempoMarkers;
173}
174
176 sort( m_tempoMarkers.begin(), m_tempoMarkers.end(),
178}
179
180void Timeline::addTag( int nColumn, QString sTag ) {
181
182 if ( hasColumnTag( nColumn ) ) {
183 ERRORLOG( QString( "There is already a tag present in column %1. Please remove it first." )
184 .arg( nColumn ) );
185 return;
186 }
187
188 std::shared_ptr<Tag> pTag( new Tag );
189 pTag->nColumn = nColumn;
190 pTag->sTag = sTag;
191
192 m_tags.push_back( std::move( pTag ) );
193 sortTags();
194}
195
196void Timeline::deleteTag( int nColumn ) {
197
198 // Erase the value to set the new value
199 if ( m_tags.size() >= 1 ){
200 for ( int t = 0; t < m_tags.size(); t++ ){
201 if ( m_tags[t]->nColumn == nColumn ) {
202 m_tags.erase( m_tags.begin() + t);
203 }
204 }
205 }
206
207 sortTags();
208}
209
210const QString Timeline::getTagAtColumn( int nColumn ) const {
211
212 QString sCurrentTag("");
213
214 for ( int t = 0; t < static_cast<int>(m_tags.size()); t++ ){
215 if ( m_tags[t]->nColumn > nColumn ){
216 break;
217 }
218 sCurrentTag = m_tags[t]->sTag;
219 }
220
221 return sCurrentTag;
222}
223
224bool Timeline::hasColumnTag( int nColumn ) const {
225 for ( const auto& tt : m_tags ){
226 if ( tt->nColumn == nColumn ) {
227 return true;
228 }
229 }
230 return false;
231}
232
234{
235 //sort the timeline vector to beats a < b
236 sort(m_tags.begin(), m_tags.end(), TagComparator());
237}
238
239QString Timeline::toQString( const QString& sPrefix, bool bShort ) const {
240 QString s = Base::sPrintIndention;
241 QString sOutput;
242 if ( ! bShort ) {
243 sOutput = QString( "%1[Timeline]\n" ).arg( sPrefix )
244 .append( QString( "%1%2m_fDefaultBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fDefaultBpm ) )
245 .append( QString( "%1%2m_tempoMarkers:\n" ).arg( sPrefix ).arg( s ) );
246 for ( auto const& tt : m_tempoMarkers ) {
247 if ( tt != nullptr ) {
248 sOutput.append( QString( "%1[column: %2 , bpm: %3]\n" ).arg( sPrefix + s + s ).arg( tt->nColumn ).arg( tt->fBpm ) );
249 }
250 }
251 sOutput.append( QString( "%1%2m_tags:\n" ).arg( sPrefix ).arg( s ) );
252 for ( auto const& tt : m_tags ) {
253 if ( tt != nullptr ) {
254 sOutput.append( QString( "%1[column: %2 , tag: %3]\n" ).arg( sPrefix + s + s ).arg( tt->nColumn ).arg( tt->sTag ) );
255 }
256 }
257 } else {
258
259 sOutput = QString( "%1[Timeline] " ).arg( sPrefix )
260 .append( QString( "m_fDefaultBpm: %1, " ).arg( m_fDefaultBpm ) )
261 .append( QString( "m_tempoMarkers: [" ) );
262 for ( auto const& tt : m_tempoMarkers ) {
263 if ( tt != nullptr ) {
264 sOutput.append( QString( " [column: %1 , bpm: %2]" ).arg( tt->nColumn ).arg( tt->fBpm ) );
265 }
266 }
267 sOutput.append( QString( "], m_tags: [" ) );
268 for ( auto const& tt : m_tags ) {
269 if ( tt != nullptr ) {
270 sOutput.append( QString( " [column: %1 , tag: %2]" ).arg( tt->nColumn ).arg( tt->sTag ) );
271 }
272 }
273 sOutput.append(" ]");
274 }
275
276 return sOutput;
277}
278
279QString Timeline::TempoMarker::getPrettyString( int nDecimals ) const {
280
281 int nPrec = 7;
282 if ( nDecimals >= 0 ) {
283 nPrec = std::min( nDecimals + ( fBpm >= 100 ? 3 : 2 ),
284 7 );
285 }
286 QString sOut = QString::number( fBpm, 'g', nPrec );
287
288 return std::move( sOut );
289}
290
291QString Timeline::TempoMarker::toQString( const QString& sPrefix, bool bShort ) const {
292 QString s = Base::sPrintIndention;
293 QString sOutput;
294 if ( ! bShort ) {
295 sOutput = QString( "%1[TempoMarker]\n" ).arg( sPrefix )
296 .append( QString( "%1%2nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg( nColumn ) )
297 .append( QString( "%1%2fBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( fBpm ) );
298 } else {
299
300 sOutput = QString( "%1[TempoMarker] " ).arg( sPrefix )
301 .append( QString( "nColumn: %3, " ).arg( nColumn ) )
302 .append( QString( "fBpm: %3" ).arg( fBpm ) );
303 }
304
305 return sOutput;
306}
307
308QString Timeline::Tag::toQString( const QString& sPrefix, bool bShort ) const {
309 QString s = Base::sPrintIndention;
310 QString sOutput;
311 if ( ! bShort ) {
312 sOutput = QString( "%1[TempoMarker]\n" ).arg( sPrefix )
313 .append( QString( "%1%2nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg( nColumn ) )
314 .append( QString( "%1%2sTag: %3\n" ).arg( sPrefix ).arg( s ).arg( sTag ) );
315 } else {
316
317 sOutput = QString( "%1[TempoMarker] " ).arg( sPrefix )
318 .append( QString( "nColumn: %3, " ).arg( nColumn ) )
319 .append( QString( "sTag: %3" ).arg( sTag ) );
320 }
321
322 return sOutput;
323}
324
325};
326
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
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:123
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
void deleteTag(int nColumn)
Definition Timeline.cpp:196
void setDefaultBpm(float fDefaultBpm)
Definition Timeline.cpp:48
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:77
void sortTempoMarkers()
Definition Timeline.cpp:175
bool hasColumnTempoMarker(int nColumn) const
Definition Timeline.cpp:124
bool isFirstTempoMarkerSpecial() const
Whether there is a TempoMarker introduced by the user at the first column.
Definition Timeline.cpp:116
float getTempoAtColumn(int nColumn) const
Returns the tempo of the Song at a given column.
Definition Timeline.cpp:90
bool hasColumnTag(int nColumn) const
Definition Timeline.cpp:224
void addTag(int nColumn, QString sTag)
Adds a Tag to the Timeline.
Definition Timeline.cpp:180
std::shared_ptr< const Timeline::TempoMarker > getTempoMarkerAtColumn(int nColumn) const
Definition Timeline.cpp:133
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:209
std::vector< std::shared_ptr< const Tag > > m_tags
Definition Timeline.h:210
const std::vector< std::shared_ptr< const TempoMarker > > getAllTempoMarkers() const
Definition Timeline.cpp:150
const QString getTagAtColumn(int nColumn) const
Returns the tag of the Song at a given column.
Definition Timeline.cpp:210
void addTempoMarker(int nColumn, float fBpm)
Adds a TempoMarker to the Timeline.
Definition Timeline.cpp:52
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Timeline.cpp:239
float m_fDefaultBpm
Tempo used for the special tempo marker.
Definition Timeline.h:218
#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:308
QString toQString(const QString &sPrefix="", bool bShort=true) const
Definition Timeline.cpp:291
QString getPrettyString(int nDecimals=2) const
Definition Timeline.cpp:279