hydrogen 1.2.6
PatternList.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#include <algorithm>
25
26#include <core/Helpers/Xml.h>
28#include <core/Basics/Pattern.h>
29
31
32namespace H2Core
33{
34
35
39
41{
42 assert( __patterns.size() == 0 );
43 for ( int i=0; i<other->size(); i++ ) {
44 ( *this ) << ( new Pattern( ( *other )[i] ) );
45 }
46}
47
49{
50 for ( int i = 0; i < __patterns.size(); ++i ) {
51 assert ( __patterns[i] );
52 delete __patterns[i];
53 }
54}
55
56PatternList* PatternList::load_from( XMLNode* pNode, std::shared_ptr<InstrumentList> pInstrumentList, bool bSilent ) {
57 XMLNode patternsNode = pNode->firstChildElement( "patternList" );
58 if ( patternsNode.isNull() ) {
59 ERRORLOG( "'patternList' node not found. Unable to load pattern list." );
60 return nullptr;
61 }
62
63 PatternList* pPatternList = new PatternList();
64 int nPatternCount = 0;
65
66 XMLNode patternNode = patternsNode.firstChildElement( "pattern" );
67 while ( !patternNode.isNull() ) {
68 nPatternCount++;
69 Pattern* pPattern = Pattern::load_from( &patternNode, pInstrumentList, bSilent );
70 if ( pPattern != nullptr ) {
71 pPatternList->add( pPattern );
72 }
73 else {
74 ERRORLOG( "Error loading pattern" );
75 delete pPatternList;
76 return nullptr;
77 }
78 patternNode = patternNode.nextSiblingElement( "pattern" );
79 }
80 if ( nPatternCount == 0 && ! bSilent ) {
81 WARNINGLOG( "0 patterns?" );
82 }
83
84 return pPatternList;
85}
86
87void PatternList::save_to( XMLNode* pNode, const std::shared_ptr<Instrument> pInstrumentOnly ) const {
88 XMLNode patternListNode = pNode->createNode( "patternList" );
89
90 for ( const auto& pPattern : __patterns ) {
91 if ( pPattern != nullptr ) {
92 pPattern->save_to( &patternListNode, pInstrumentOnly );
93 }
94 }
95}
96
97void PatternList::add( Pattern* pPattern, bool bAddVirtuals )
98{
100 if ( pPattern == nullptr ) {
101 ERRORLOG( "Provided pattern is invalid" );
102 return;
103 }
104
105 // do nothing if already in __patterns
106 if ( index( pPattern ) != -1 ) {
107 INFOLOG( "Provided pattern is already contained" );
108 return;
109 }
110 else if ( ! bAddVirtuals ) {
111 // Check whether the pattern is contained as a virtual
112 // pattern.
113 for ( const auto& ppPattern : __patterns ) {
114 auto pVirtualPatterns = ppPattern->get_virtual_patterns();
115 if ( pVirtualPatterns->find( pPattern ) != pVirtualPatterns->end() ) {
116 // Provided pattern is already contained as virtual pattern
117 return;
118 }
119 }
120 }
121
122 // In case the added pattern is a virtual one, deactivate the
123 // individual patterns it encompasses in case one of them was
124 // already activated. (They will be only activated as virtual
125 // patterns from here on).
126 auto pVirtualPatterns = pPattern->get_virtual_patterns();
127 for ( int ii = __patterns.size() - 1; ii >= 0 && ii < __patterns.size(); --ii ) {
128 auto ppPattern = __patterns[ ii ];
129 if ( pVirtualPatterns->find( ppPattern ) != pVirtualPatterns->end() ) {
130 del( ii );
131 }
132 }
133
134 __patterns.push_back( pPattern );
135
136 if ( bAddVirtuals ) {
137 pPattern->addFlattenedVirtualPatterns( this );
138 }
139}
140
141void PatternList::insert( int nIdx, Pattern* pPattern )
142{
144 // do nothing if already in __patterns
145 if ( index( pPattern ) != -1 ) {
146 return;
147 }
148 if ( nIdx > __patterns.size() ) {
149 __patterns.resize( nIdx );
150 }
151 __patterns.insert( __patterns.begin() + nIdx, pPattern );
152}
153
155{
157 if ( idx < 0 || idx >= __patterns.size() ) {
158 ERRORLOG( QString( "idx %1 out of [0;%2]" ).arg( idx ).arg( size() ) );
159 return nullptr;
160 }
161 assert( idx >= 0 && idx < __patterns.size() );
162 return __patterns[idx];
163}
164
165Pattern* PatternList::get( int idx ) const
166{
168 if ( idx < 0 || idx >= __patterns.size() ) {
169 ERRORLOG( QString( "idx %1 out of [0;%2]" ).arg( idx ).arg( size() ) );
170 return nullptr;
171 }
172 assert( idx >= 0 && idx < __patterns.size() );
173 return __patterns[idx];
174}
175
176int PatternList::index( const Pattern* pattern ) const
177{
178 for( int i=0; i<__patterns.size(); i++ ) {
179 if ( __patterns[i]==pattern ) {
180 return i;
181 }
182 }
183 return -1;
184}
185
187{
189 if ( idx >= 0 && idx < __patterns.size() ) {
190 Pattern* pattern = __patterns[idx];
191 __patterns.erase( __patterns.begin() + idx );
192 return pattern;
193 }
194 return nullptr;
195}
196
198{
200 for( int i=0; i<__patterns.size(); i++ ) {
201 if( __patterns[i]==pattern ) {
202 return del( i );
203 }
204 }
205 return nullptr;
206}
207
209{
211 /*
212 * if we insert a new pattern (copy, add new pattern, undo delete pattern and so on will do this)
213 * idx is > __pattern.size(). that's why i add +1 to assert expression
214 */
215
216 assert( idx >= 0 && idx <= __patterns.size() +1 );
217 if( idx < 0 || idx >= __patterns.size() ) {
218 ERRORLOG( QString( "index out of bounds %1 (size:%2)" ).arg( idx ).arg( __patterns.size() ) );
219 return nullptr;
220 }
221
222 __patterns.insert( __patterns.begin() + idx, pattern );
223 __patterns.erase( __patterns.begin() + idx + 1 );
224
225 //create return pattern after patternlist tätatä to return the right one
226 Pattern* ret = __patterns[idx];
227 return ret;
228}
229
231{
232 for( int i=0; i<__patterns.size(); i++ ) {
233 __patterns[i]->set_to_old();
234 }
235}
236
237Pattern* PatternList::find( const QString& name )
238{
239 for( int i=0; i<__patterns.size(); i++ ) {
240 if ( __patterns[i]->get_name()==name ) return __patterns[i];
241 }
242 return nullptr;
243}
244
245void PatternList::swap( int idx_a, int idx_b )
246{
248 assert( idx_a >= 0 && idx_a < __patterns.size() );
249 assert( idx_b >= 0 && idx_b < __patterns.size() );
250 if( idx_a == idx_b ) return;
251 //DEBUGLOG(QString("===>> SWAP %1 %2").arg(idx_a).arg(idx_b) );
252 Pattern* tmp = __patterns[idx_a];
253 __patterns[idx_a] = __patterns[idx_b];
254 __patterns[idx_b] = tmp;
255}
256
257void PatternList::move( int idx_a, int idx_b )
258{
260 assert( idx_a >= 0 && idx_a < __patterns.size() );
261 assert( idx_b >= 0 && idx_b < __patterns.size() );
262 if( idx_a == idx_b ) return;
263 //DEBUGLOG(QString("===>> MOVE %1 %2").arg(idx_a).arg(idx_b) );
264 Pattern* tmp = __patterns[idx_a];
265 __patterns.erase( __patterns.begin() + idx_a );
266 __patterns.insert( __patterns.begin() + idx_b, tmp );
267}
268
270{
271 for ( int i=0 ; i<__patterns.size() ; i++ ) {
272 __patterns[i]->flattened_virtual_patterns_clear();
273 }
274 for ( int i=0 ; i<__patterns.size() ; i++ ) {
275 __patterns[i]->flattened_virtual_patterns_compute();
276 }
277}
278
280{
281 for( int i=0; i<__patterns.size(); i++ ) __patterns[i]->virtual_patterns_del( pattern );
282}
283
284bool PatternList::check_name( QString patternName, Pattern* ignore )
285{
286 if (patternName == "") {
287 return false;
288 }
289
290 for (uint i = 0; i < __patterns.size(); i++) {
291 if ( __patterns[i] != ignore && __patterns[i]->get_name() == patternName ) {
292 return false;
293 }
294 }
295 return true;
296}
297
298QString PatternList::find_unused_pattern_name( QString sourceName, Pattern* ignore )
299{
300 QString unusedPatternNameCandidate;
301
302 if( sourceName.isEmpty() ) {
303 sourceName = "Pattern 11";
304 }
305
306 int i = 1;
307 QString suffix = "";
308 unusedPatternNameCandidate = sourceName;
309
310 // Check if the sourceName already has a number suffix, and if so, start
311 // searching for an unused name from that number.
312 QRegularExpression numberSuffixRe("(.+) #(\\d+)$");
313 QRegularExpressionMatch match = numberSuffixRe.match(sourceName);
314 if (match.hasMatch()) {
315 QString numberSuffix = match.captured(2);
316
317 i = numberSuffix.toInt();
318 suffix = " #" + QString::number(i);
319 unusedPatternNameCandidate = match.captured(1);
320 }
321
322 while( !check_name( unusedPatternNameCandidate + suffix, ignore ) ) {
323 suffix = " #" + QString::number(i);
324 i++;
325 }
326
327 unusedPatternNameCandidate += suffix;
328
329 return unusedPatternNameCandidate;
330}
331
332int PatternList::longest_pattern_length( bool bIncludeVirtuals ) const {
333 int nMax = -1;
334 for ( const auto ppPattern : __patterns ) {
335 if ( ppPattern->get_length() > nMax ) {
336 nMax = ppPattern->get_length();
337 }
338
339 if ( bIncludeVirtuals ) {
340 for ( const auto ppVirtualPattern : *ppPattern->get_flattened_virtual_patterns() ) {
341 if ( ppVirtualPattern->get_length() > nMax ) {
342 nMax = ppVirtualPattern->get_length();
343 }
344 }
345 }
346 }
347 return nMax;
348}
349
350bool operator==( const PatternList& pLhs, const PatternList& pRhs ) {
351 if ( pLhs.size() != pRhs.size() ) {
352 return false;
353 }
354
355 for ( int ii = 0; ii < pLhs.size(); ii++ ) {
356 if ( pLhs.get( ii ) != pRhs.get( ii ) ) {
357 return false;
358 }
359 }
360
361 return true;
362}
363
364bool operator!=( const PatternList& pLhs, const PatternList& pRhs ) {
365 if ( pLhs.size() != pRhs.size() ) {
366 return true;
367 }
368
369 for ( int ii = 0; ii < pLhs.size(); ii++ ) {
370 if ( pLhs.get( ii ) != pRhs.get( ii ) ) {
371 return true;
372 }
373 }
374
375 return false;
376}
377
378QString PatternList::toQString( const QString& sPrefix, bool bShort ) const {
379 QString s = Base::sPrintIndention;
380 QString sOutput;
381 if ( ! bShort ) {
382 sOutput = QString( "%1[PatternList]\n" ).arg( sPrefix );
383 for ( auto pp : __patterns ) {
384 if ( pp != nullptr ) {
385 sOutput.append( QString( "%1" ).arg( pp->toQString( sPrefix + s, bShort ) ) );
386 }
387 }
388 } else {
389 sOutput = QString( "[PatternList] " );
390 for ( auto pp : __patterns ) {
391 if ( pp != nullptr ) {
392 sOutput.append( QString( "[%1] " ).arg( pp->get_name() ) );
393 }
394 }
395 }
396
397 return sOutput;
398}
399
400
401std::vector<Pattern*>::iterator PatternList::begin() {
403 return __patterns.begin();
404}
405
406std::vector<Pattern*>::iterator PatternList::end() {
407 return __patterns.end();
408}
409
410std::vector<Pattern*>::const_iterator PatternList::cbegin() const {
412 return __patterns.begin();
413}
414
415std::vector<Pattern*>::const_iterator PatternList::cend() const {
416 return __patterns.end();
417}
418
419}
420
421
422
423/* vim: set softtabstop=4 noexpandtab: */
#define ASSERT_AUDIO_ENGINE_LOCKED(x)
#define INFOLOG(x)
Definition Object.h:240
#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
void save_to(XMLNode *pNode, const std::shared_ptr< Instrument > pInstrumentOnly=nullptr) const
bool check_name(QString patternName, Pattern *ignore=NULL)
check if a pattern with name patternName already exists in this list
void add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
std::vector< Pattern * >::iterator end()
static PatternList * load_from(XMLNode *pNode, std::shared_ptr< InstrumentList > pInstrumentList, bool bSilent=false)
load a PatternList from an XMLNode
int index(const Pattern *pattern) const
get the index of the pattern within the patterns
void set_to_old()
mark all patterns as old
Pattern * replace(int idx, Pattern *pattern)
replace the pattern at a given index with a new one
~PatternList()
destructor
void move(int idx_a, int idx_b)
move a pattern from a position to another
std::vector< Pattern * >::const_iterator cend() const
QString find_unused_pattern_name(QString sourceName, Pattern *ignore=NULL)
find unused patternName
std::vector< Pattern * >::const_iterator cbegin() const
Pattern * del(int idx)
remove the pattern at a given index, does not delete it
void flattened_virtual_patterns_compute()
call compute_flattened_virtual_patterns on each pattern
PatternList()
constructor
std::vector< Pattern * >::iterator begin()
Iteration.
int longest_pattern_length(bool bIncludeVirtuals=true) const
Get the length of the longest pattern in the list.
std::vector< Pattern * > __patterns
the list of patterns
void swap(int idx_a, int idx_b)
swap the patterns of two different indexes
Pattern * find(const QString &name)
find a pattern within the patterns
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
void virtual_pattern_del(Pattern *pattern)
call del_virtual_pattern on each pattern
int size() const
returns the numbers of patterns
void insert(int idx, Pattern *pattern)
insert a pattern into the list
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
Definition Pattern.h:46
const virtual_patterns_t * get_virtual_patterns() const
get the flattened virtual pattern set
Definition Pattern.h:360
static Pattern * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a pattern from an XMLNode
Definition Pattern.cpp:116
void addFlattenedVirtualPatterns(PatternList *pPatternList)
Add content of __flattened_virtual_patterns into pPatternList.
Definition Pattern.cpp:337
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:44
bool operator!=(std::shared_ptr< TransportPosition > pLhs, std::shared_ptr< TransportPosition > pRhs)
bool operator==(std::shared_ptr< TransportPosition > pLhs, std::shared_ptr< TransportPosition > pRhs)