hydrogen 1.2.3
Pattern.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#include <core/Basics/Pattern.h>
24
25#include <cassert>
26
27#include <core/Basics/Note.h>
30#include <core/Hydrogen.h>
31
33#include <core/Helpers/Legacy.h>
34
35namespace H2Core
36{
37
38Pattern::Pattern( const QString& name, const QString& info, const QString& category, int length, int denominator )
39 : __length( length )
40 , __denominator( denominator)
41 , __name( name )
42 , __info( info )
43 , __category( category )
44{
45}
46
48 : __length( other->get_length() )
49 , __denominator( other->get_denominator() )
50 , __name( other->get_name() )
51 , __info( other->get_info() )
52 , __category( other->get_category() )
53{
55 __notes.insert( std::make_pair( it->first, new Note( it->second ) ) );
56 }
57}
58
60{
61 for( notes_cst_it_t it=__notes.begin(); it!=__notes.end(); it++ ) {
62 delete it->second;
63 }
64}
65
66bool Pattern::loadDoc( const QString& sPatternPath, std::shared_ptr<InstrumentList> pInstrumentList, XMLDoc* pDoc, bool bSilent )
67{
68 if ( ! Filesystem::file_readable( sPatternPath, bSilent ) ) {
69 return false;
70 }
71
72 bool bReadingSuccessful = true;
73
74 if ( ! pDoc->read( sPatternPath, Filesystem::pattern_xsd_path() ) ) {
75 if ( ! pDoc->read( sPatternPath, nullptr ) ) {
76 ERRORLOG( QString( "Unable to read pattern [%1]" )
77 .arg( sPatternPath ) );
78 return false;
79 }
80 else {
81 if ( ! bSilent ) {
82 WARNINGLOG( QString( "Pattern [%1] does not validate the current pattern schema. Loading might fail." )
83 .arg( sPatternPath ) );
84 }
85 bReadingSuccessful = false;
86 }
87 }
88
89 XMLNode root = pDoc->firstChildElement( "drumkit_pattern" );
90 if ( root.isNull() ) {
91 ERRORLOG( QString( "'drumkit_pattern' node not found in [%1]" )
92 .arg( sPatternPath ) );
93 return false;
94 }
95
96 XMLNode pattern_node = root.firstChildElement( "pattern" );
97 if ( pattern_node.isNull() ) {
98 ERRORLOG( QString( "'pattern' node not found in [%1]" )
99 .arg( sPatternPath ) );
100 return false;
101 }
102
103 return bReadingSuccessful;
104}
105
106Pattern* Pattern::load_file( const QString& sPatternPath, std::shared_ptr<InstrumentList> pInstrumentList )
107{
108 INFOLOG( QString( "Load pattern %1" ).arg( sPatternPath ) );
109
110 XMLDoc doc;
111 if ( ! loadDoc( sPatternPath, pInstrumentList, &doc, false ) ) {
112 // Try former pattern version
113 return Legacy::load_drumkit_pattern( sPatternPath, pInstrumentList );
114 }
115
116 XMLNode root = doc.firstChildElement( "drumkit_pattern" );
117 XMLNode pattern_node = root.firstChildElement( "pattern" );
118 return load_from( &pattern_node, pInstrumentList );
119}
120
121Pattern* Pattern::load_from( XMLNode* node, std::shared_ptr<InstrumentList> pInstrumentList, bool bSilent )
122{
123 Pattern* pPattern = new Pattern(
124 node->read_string( "name", nullptr, false, false ),
125 node->read_string( "info", "", false, true ),
126 node->read_string( "category", "unknown", false, true, true ),
127 node->read_int( "size", -1, false, false ),
128 node->read_int( "denominator", 4, false, false )
129 );
130
131 if ( pInstrumentList == nullptr ) {
132 ERRORLOG( "Invalid instrument list provided" );
133 return pPattern;
134 }
135
136 XMLNode note_list_node = node->firstChildElement( "noteList" );
137 if ( !note_list_node.isNull() ) {
138 XMLNode note_node = note_list_node.firstChildElement( "note" );
139 while ( !note_node.isNull() ) {
140 Note* pNote = Note::load_from( &note_node, pInstrumentList, bSilent );
141 assert( pNote );
142 if ( pNote != nullptr ) {
143 pPattern->insert_note( pNote );
144 }
145 note_node = note_node.nextSiblingElement( "note" );
146 }
147 }
148
149 return pPattern;
150}
151
152bool Pattern::save_file( const QString& drumkit_name, const QString& author, const License& license, const QString& pattern_path, bool overwrite ) const
153{
154 INFOLOG( QString( "Saving pattern into %1" ).arg( pattern_path ) );
155 if( !overwrite && Filesystem::file_exists( pattern_path, true ) ) {
156 ERRORLOG( QString( "pattern %1 already exists" ).arg( pattern_path ) );
157 return false;
158 }
159 XMLDoc doc;
160 XMLNode root = doc.set_root( "drumkit_pattern", "drumkit_pattern" );
161 root.write_string( "drumkit_name", drumkit_name );
162 root.write_string( "author", author ); // FIXME this is never loaded back
163 root.write_string( "license", license.getLicenseString() );
164 // FIXME this is never loaded back
165 save_to( &root );
166 return doc.write( pattern_path );
167}
168
169void Pattern::save_to( XMLNode* node, const std::shared_ptr<Instrument> pInstrumentOnly ) const
170{
171 XMLNode pattern_node = node->createNode( "pattern" );
172 pattern_node.write_string( "name", __name );
173 pattern_node.write_string( "info", __info );
174 pattern_node.write_string( "category", __category );
175 pattern_node.write_int( "size", __length );
176 pattern_node.write_int( "denominator", __denominator );
177
178 int nId = ( pInstrumentOnly == nullptr ? -1 : pInstrumentOnly->get_id() );
179
180 XMLNode note_list_node = pattern_node.createNode( "noteList" );
181 for( auto it = __notes.cbegin(); it != __notes.cend(); ++it ) {
182 auto pNote = it->second;
183 if ( pNote != nullptr &&
184 ( pInstrumentOnly == nullptr ||
185 pNote->get_instrument()->get_id() == nId ) ) {
186 XMLNode note_node = note_list_node.createNode( "note" );
187 pNote->save_to( &note_node );
188 }
189 }
190}
191
192Note* Pattern::find_note( int idx_a, int idx_b, std::shared_ptr<Instrument> instrument, Note::Key key, Note::Octave octave, bool strict ) const
193{
194 for( notes_cst_it_t it=__notes.lower_bound( idx_a ); it!=__notes.upper_bound( idx_a ); it++ ) {
195 Note* note = it->second;
196 assert( note );
197 if ( note->match( instrument, key, octave ) ) return note;
198 }
199 if( idx_b==-1 ) return nullptr;
200 for( notes_cst_it_t it=__notes.lower_bound( idx_b ); it!=__notes.upper_bound( idx_b ); it++ ) {
201 Note* note = it->second;
202 assert( note );
203 if ( note->match( instrument, key, octave ) ) return note;
204 }
205 if( strict ) return nullptr;
206 // TODO maybe not start from 0 but idx_b-X
207 for ( int n=0; n<idx_b; n++ ) {
208 for( notes_cst_it_t it=__notes.lower_bound( n ); it!=__notes.upper_bound( n ); it++ ) {
209 Note* note = it->second;
210 assert( note );
211 if ( note->match( instrument, key, octave ) && ( ( idx_b<=note->get_position()+note->get_length() ) && idx_b>=note->get_position() ) ) return note;
212 }
213 }
214 return nullptr;
215}
216
217Note* Pattern::find_note( int idx_a, int idx_b, std::shared_ptr<Instrument> instrument, bool strict ) const
218{
220 for( it=__notes.lower_bound( idx_a ); it!=__notes.upper_bound( idx_a ); it++ ) {
221 Note* note = it->second;
222 assert( note );
223 if ( note->get_instrument() == instrument ) return note;
224 }
225 if( idx_b==-1 ) return nullptr;
226 for( it=__notes.lower_bound( idx_b ); it!=__notes.upper_bound( idx_b ); it++ ) {
227 Note* note = it->second;
228 assert( note );
229 if ( note->get_instrument() == instrument ) return note;
230 }
231 if ( strict ) return nullptr;
232 // TODO maybe not start from 0 but idx_b-X
233 for ( int n=0; n<idx_b; n++ ) {
234 for( it=__notes.lower_bound( n ); it!=__notes.upper_bound( n ); it++ ) {
235 Note* note = it->second;
236 assert( note );
237 if ( note->get_instrument() == instrument && ( ( idx_b<=note->get_position()+note->get_length() ) && idx_b>=note->get_position() ) ) return note;
238 }
239 }
240
241 return nullptr;
242}
243
245{
246 int pos = note->get_position();
247 for( notes_it_t it=__notes.lower_bound( pos ); it!=__notes.end() && it->first == pos; ++it ) {
248 if( it->second==note ) {
249 __notes.erase( it );
250 break;
251 }
252 }
253}
254
255bool Pattern::references( std::shared_ptr<Instrument> instr )
256{
257 for( notes_cst_it_t it=__notes.begin(); it!=__notes.end(); it++ ) {
258 Note* note = it->second;
259 assert( note );
260 if ( note->get_instrument() == instr ) {
261 return true;
262 }
263 }
264 return false;
265}
266
267void Pattern::purge_instrument( std::shared_ptr<Instrument> instr, bool bRequiresLock )
268{
269 bool locked = false;
270 std::list< Note* > slate;
271 for( notes_it_t it=__notes.begin(); it!=__notes.end(); ) {
272 Note* note = it->second;
273 assert( note );
274 if ( note->get_instrument() == instr ) {
275 if ( !locked && bRequiresLock ) {
277 locked = true;
278 }
279 slate.push_back( note );
280 __notes.erase( it++ );
281 } else {
282 ++it;
283 }
284 }
285 if ( locked ) {
287 }
288 while ( slate.size() ) {
289 delete slate.front();
290 slate.pop_front();
291 }
292}
293
295{
296 for( notes_cst_it_t it=__notes.begin(); it!=__notes.end(); it++ ) {
297 Note* note = it->second;
298 assert( note );
299 note->set_just_recorded( false );
300 }
301}
302
304{
305 // __flattened_virtual_patterns must have been cleared before
306 if( __flattened_virtual_patterns.size() >= __virtual_patterns.size() ) return;
307 // for each virtual pattern
308 for( virtual_patterns_cst_it_t it0=__virtual_patterns.begin(); it0!=__virtual_patterns.end(); ++it0 ) {
309 __flattened_virtual_patterns.insert( *it0 ); // add it
310 ( *it0 )->flattened_virtual_patterns_compute(); // build it's flattened virtual patterns set
311 // for each pattern of it's flattened virtual pattern set
312 for( virtual_patterns_cst_it_t it1=( *it0 )->get_flattened_virtual_patterns()->begin(); it1!=( *it0 )->get_flattened_virtual_patterns()->end(); ++it1 ) {
313 // add the pattern
314 __flattened_virtual_patterns.insert( *it1 );
315 }
316 }
317}
318
321 it!=__flattened_virtual_patterns.end(); ++it ) {
322 pPatternList->add( *it, true );
323 }
324}
325
328 it!=__flattened_virtual_patterns.end(); ++it ) {
329 pPatternList->del( *it );
330 }
331}
332
334 int nMax = __length;
336 it!=__flattened_virtual_patterns.end(); ++it ) {
337 if ( (*it)->__length > nMax ) {
338 nMax = (*it)->__length;
339 }
340 }
341
342 return nMax;
343}
344
345bool Pattern::isVirtual() const {
346 return __flattened_virtual_patterns.size() > 0;
347}
348
349std::set<Pattern*>::iterator Pattern::begin() {
350 return __flattened_virtual_patterns.begin();
351}
352
353std::set<Pattern*>::iterator Pattern::end() {
354 return __flattened_virtual_patterns.end();
355}
356
357QString Pattern::toQString( const QString& sPrefix, bool bShort ) const {
358 QString s = Base::sPrintIndention;
359 QString sOutput;
360 if ( ! bShort ) {
361 sOutput = QString( "%1[Pattern]\n" ).arg( sPrefix )
362 .append( QString( "%1%2length: %3\n" ).arg( sPrefix ).arg( s ).arg( __length ) )
363 .append( QString( "%1%2denominator: %3\n" ).arg( sPrefix ).arg( s ).arg( __denominator ) )
364 .append( QString( "%1%2name: %3\n" ).arg( sPrefix ).arg( s ).arg( __name ) )
365 .append( QString( "%1%2category: %3\n" ).arg( sPrefix ).arg( s ).arg( __category ) )
366 .append( QString( "%1%2info: %3\n" ).arg( sPrefix ).arg( s ).arg( __info ) )
367 .append( QString( "%1%2Notes:\n" ).arg( sPrefix ).arg( s ) );
368
369 for ( auto it = __notes.begin(); it != __notes.end(); it++ ) {
370 if ( it->second != nullptr ) {
371 sOutput.append( QString( "%1" ).arg( it->second->toQString( sPrefix + s + s, bShort ) ) );
372 }
373 }
374
375 sOutput.append( QString( "%1%2Virtual_patterns:\n" ).arg( sPrefix ).arg( s ) );
376 for ( auto ii : __virtual_patterns ) {
377 if ( ii != nullptr ) {
378 sOutput.append( QString( "%1" ).arg( ii->toQString( sPrefix + s + s, bShort ) ) );
379 }
380 }
381
382 sOutput.append( QString( "%1%2Flattened_virtual_patterns:\n" ).arg( sPrefix ).arg( s ) );
383 for ( auto ii : __flattened_virtual_patterns ) {
384 if ( ii != nullptr ) {
385 sOutput.append( QString( "%1" ).arg( ii->toQString( sPrefix + s + s, bShort ) ) );
386 }
387 }
388 } else {
389
390 sOutput = QString( "[Pattern]" )
391 .append( QString( " length: %1" ).arg( __length ) )
392 .append( QString( ", denominator: %1" ).arg( __denominator ) )
393 .append( QString( ", name: %1" ).arg( __name ) )
394 .append( QString( ", category: %1" ).arg( __category ) )
395 .append( QString( ", info: %1" ).arg( __info ) )
396 .append( QString( ", [Notes: " ) );
397 for ( auto it = __notes.begin(); it != __notes.end(); it++ ) {
398 if ( it->second != nullptr ) {
399 sOutput.append( QString( "[%2, %3] " )
400 .arg( it->second->get_instrument()->get_name() )
401 .arg( it->second->get_position() ) );
402 }
403 }
404 sOutput.append( "]" );
405 if ( __virtual_patterns.size() != 0 ) {
406 sOutput.append( ", Virtual_patterns: {" );
407 }
408 for ( auto ii : __virtual_patterns ) {
409 if ( ii != nullptr ) {
410 sOutput.append( QString( "%1" ).arg( ii->toQString( sPrefix + s + s, bShort ) ) );
411 }
412 }
413 if ( __flattened_virtual_patterns.size() != 0 ) {
414 sOutput.append( "}, Flattened_virtual_patterns: {" );
415 }
416 for ( auto ii : __flattened_virtual_patterns ) {
417 if ( ii != nullptr ) {
418 sOutput.append( QString( "%1" ).arg( ii->toQString( sPrefix + s + s, bShort ) ) );
419 }
420 }
421 if ( __flattened_virtual_patterns.size() != 0 ) {
422 sOutput.append( "}" );
423 }
424 }
425 return sOutput;
426}
427
428};
429
430/* vim: set softtabstop=4 noexpandtab: */
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:59
#define INFOLOG(x)
Definition Object.h:237
#define WARNINGLOG(x)
Definition Object.h:238
#define ERRORLOG(x)
Definition Object.h:239
#define FOREACH_NOTE_CST_IT_BEGIN_END(_notes, _it)
Iterate over all provided notes in an immutable way.
Definition Pattern.h:268
void unlock()
Mutex unlocking of the AudioEngine.
void lock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
static QString pattern_xsd_path()
returns the path to the pattern XSD (xml schema definition) file
static bool file_exists(const QString &path, bool silent=false)
returns true if the given path is an existing regular file
static bool file_readable(const QString &path, bool silent=false)
returns true if the given path is an existing readable regular file
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:649
static Pattern * load_drumkit_pattern(const QString &pattern_path, std::shared_ptr< InstrumentList > instrList)
load pattern from a file
Definition Legacy.cpp:116
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
Definition License.h:48
QString getLicenseString() const
Definition License.h:146
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
int get_position() const
__position accessor
Definition Note.h:535
static Note * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a note from an XMLNode
Definition Note.cpp:502
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
void set_just_recorded(bool value)
__just_recorded setter
Definition Note.h:600
int get_length() const
__length accessor
Definition Note.h:560
Key
possible keys
Definition Note.h:106
Octave
possible octaves
Definition Note.h:110
bool match(std::shared_ptr< Instrument > instrument, Key key, Octave octave) const
return true if instrument, key and octave matches with internal
Definition Note.h:713
PatternList is a collection of patterns.
Definition PatternList.h:43
void add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
Pattern * del(int idx)
remove the pattern at a given index, does not delete it
Pattern class is a Note container.
Definition Pattern.h:46
QString __name
the name of thepattern
Definition Pattern.h:252
bool save_file(const QString &drumkit_name, const QString &author, const License &license, const QString &pattern_path, bool overwrite=false) const
save a pattern into an xml file
Definition Pattern.cpp:152
void set_to_old()
mark all notes as old
Definition Pattern.cpp:294
QString __info
a description of the pattern
Definition Pattern.h:254
void purge_instrument(std::shared_ptr< Instrument > instr, bool bRequiredLock=true)
delete the notes referencing the given instrument The function is thread safe (it locks the audio dat...
Definition Pattern.cpp:267
bool references(std::shared_ptr< Instrument > instr)
check if this pattern contains a note referencing the given instrument
Definition Pattern.cpp:255
virtual_patterns_t __virtual_patterns
a list of patterns directly referenced by this one
Definition Pattern.h:256
Pattern(const QString &name="Pattern", const QString &info="", const QString &category="not_categorized", int length=MAX_NOTES, int denominator=4)
constructor
Definition Pattern.cpp:38
notes_t __notes
a multimap (hash with possible multiple values for one key) of note
Definition Pattern.h:255
static Pattern * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a pattern from an XMLNode
Definition Pattern.cpp:121
void flattened_virtual_patterns_compute()
compute virtual_pattern_transitive_closure_set based on virtual_pattern_transitive_closure_set virtua...
Definition Pattern.cpp:303
void remove_note(Note *note)
removes a given note from __notes, it's not deleted
Definition Pattern.cpp:244
int __length
Determines the accessible range or notes within the pattern.
Definition Pattern.h:250
notes_t::iterator notes_it_t
multimap note const iterator type
Definition Pattern.h:52
std::set< Pattern * >::iterator begin()
allow iteration of all contained virtual patterns.
Definition Pattern.cpp:349
const notes_t * get_notes() const
get the virtual pattern set
Definition Pattern.h:355
~Pattern()
destructor
Definition Pattern.cpp:59
notes_t::const_iterator notes_cst_it_t
note set type;
Definition Pattern.h:54
static Pattern * load_file(const QString &pattern_path, std::shared_ptr< InstrumentList > instruments)
load a pattern from a file
Definition Pattern.cpp:106
void removeFlattenedVirtualPatterns(PatternList *pPatternList)
Add content of __flattened_virtual_patterns into pPatternList.
Definition Pattern.cpp:326
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Pattern.cpp:357
Note * find_note(int idx_a, int idx_b, std::shared_ptr< Instrument > instrument, bool strict=true) const
search for a note at a given index within __notes which correspond to the given arguments
Definition Pattern.cpp:217
virtual_patterns_t::const_iterator virtual_patterns_cst_it_t
Definition Pattern.h:60
void save_to(XMLNode *node, const std::shared_ptr< Instrument > instrumentOnly=nullptr) const
save the pattern within the given XMLNode
Definition Pattern.cpp:169
virtual_patterns_t __flattened_virtual_patterns
the complete list of virtual patterns
Definition Pattern.h:257
QString __category
the category of the pattern
Definition Pattern.h:253
bool isVirtual() const
Whether the pattern holds at least one virtual pattern.
Definition Pattern.cpp:345
int longestVirtualPatternLength() const
Definition Pattern.cpp:333
void insert_note(Note *note)
insert a new note within __notes
Definition Pattern.h:370
std::set< Pattern * >::iterator end()
Definition Pattern.cpp:353
int __denominator
the meter denominator of the pattern used in meter (eg 4/4)
Definition Pattern.h:251
void addFlattenedVirtualPatterns(PatternList *pPatternList)
Add content of __flattened_virtual_patterns into pPatternList.
Definition Pattern.cpp:319
static bool loadDoc(const QString &sPatternPath, std::shared_ptr< InstrumentList > pInstrumentList, XMLDoc *pDoc, bool bSilent=false)
Loads the pattern stored in sPatternPath into pDoc and takes care of all the error handling.
Definition Pattern.cpp:66
XMLDoc is a subclass of QDomDocument with read and write methods.
Definition Xml.h:182
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
Definition Xml.cpp:391
bool read(const QString &filepath, const QString &schemapath=nullptr, bool bSilent=false)
read the content of an xml file
Definition Xml.cpp:296
bool write(const QString &filepath)
write itself into a file
Definition Xml.cpp:370
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
int read_int(const QString &node, int default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads an integer stored into a child node
Definition Xml.cpp:170
QString read_string(const QString &node, const QString &default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a string stored into a child node
Definition Xml.cpp:95
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:63
void write_string(const QString &node, const QString &value)
write a string into a child node
Definition Xml.cpp:269
void write_int(const QString &node, const int value)
write an integer into a child node
Definition Xml.cpp:284