hydrogen 1.2.3
InstrumentList.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
28#include <core/Basics/Sample.h>
29
30#include <core/Helpers/Xml.h>
31#include <core/License.h>
32
33#include <set>
34
35namespace H2Core
36{
37
41
42InstrumentList::InstrumentList( std::shared_ptr<InstrumentList> other ) : Object( *other )
43{
44 assert( other );
45 assert( __instruments.size() == 0 );
46 for ( int i=0; i<other->size(); i++ ) {
47 ( *this ) << ( std::make_shared<Instrument>( ( *other )[i] ) );
48 }
49}
50
54
56{
57 for( int i=0; i<__instruments.size(); i++ ) {
58 __instruments[i]->load_samples( fBpm );
59 }
60}
61
63{
64 for( int i=0; i<__instruments.size(); i++ ) {
65 __instruments[i]->unload_samples();
66 }
67}
68
69std::shared_ptr<InstrumentList> InstrumentList::load_from( XMLNode* pNode, const QString& sDrumkitPath, const QString& sDrumkitName, const License& license, bool bSilent )
70{
71 XMLNode instrumentListNode = pNode->firstChildElement( "instrumentList" );
72 if ( instrumentListNode.isNull() ) {
73 ERRORLOG( "'instrumentList' node not found. Unable to load instrument list." );
74 return nullptr;
75 }
76
77 auto pInstrumentList = std::make_shared<InstrumentList>();
78 XMLNode instrumentNode = instrumentListNode.firstChildElement( "instrument" );
79 int nCount = 0;
80 while ( !instrumentNode.isNull() ) {
81 nCount++;
82 if ( nCount > MAX_INSTRUMENTS ) {
83 ERRORLOG( QString( "instrument nCount >= %1 (MAX_INSTRUMENTS), stop reading instruments" )
84 .arg( MAX_INSTRUMENTS ) );
85 break;
86 }
87
88 auto pInstrument = Instrument::load_from( &instrumentNode,
89 sDrumkitPath,
90 sDrumkitName,
91 license, bSilent );
92 if ( pInstrument != nullptr ) {
93 ( *pInstrumentList ) << pInstrument;
94 }
95 else {
96 ERRORLOG( QString( "Unable to load instrument [%1]. The drumkit is corrupted. Skipping instrument" )
97 .arg( nCount ) );
98 nCount--;
99 }
100 instrumentNode = instrumentNode.nextSiblingElement( "instrument" );
101 }
102
103 if ( nCount == 0 ) {
104 ERRORLOG( "Newly created instrument list does not contain any instruments. Aborting." );
105 return nullptr;
106 }
107
108 return pInstrumentList;
109}
110
111void InstrumentList::save_to( XMLNode* node, int component_id, bool bRecentVersion, bool bFull )
112{
113 XMLNode instruments_node = node->createNode( "instrumentList" );
114 for ( const auto& pInstrument : __instruments ) {
115 assert( pInstrument );
116 assert( pInstrument->get_adsr() );
117 if ( pInstrument != nullptr && pInstrument->get_adsr() != nullptr ) {
118 pInstrument->save_to( &instruments_node, component_id, bRecentVersion, bFull );
119 }
120 }
121}
122
123void InstrumentList::operator<<( std::shared_ptr<Instrument> instrument )
124{
125 // do nothing if already in __instruments
126 for( int i=0; i<__instruments.size(); i++ ) {
127 if( __instruments[i]==instrument ) return;
128 }
129 __instruments.push_back( instrument );
130}
131
132bool InstrumentList::operator==( std::shared_ptr<InstrumentList> pOther ) const {
133 if ( pOther != nullptr && size() == pOther->size() ) {
134 for ( int ii = 0; ii < size(); ++ii ) {
135 if ( get( ii ).get() != pOther->get( ii ).get() ) {
136 return false;
137 }
138 }
139
140 return true;
141 }
142
143 return false;
144}
145
146bool InstrumentList::operator!=( std::shared_ptr<InstrumentList> pOther ) const {
147 if ( pOther != nullptr && size() == pOther->size() ) {
148 for ( int ii = 0; ii < size(); ++ii ) {
149 if ( get( ii ).get() != pOther->get( ii ).get() ) {
150 return true;
151 }
152 }
153
154 return false;
155 }
156
157 return true;
158}
159
160void InstrumentList::add( std::shared_ptr<Instrument> instrument )
161{
162 // do nothing if already in __instruments
163 for( int i=0; i<__instruments.size(); i++ ) {
164 if( __instruments[i]==instrument ) return;
165 }
166 __instruments.push_back( instrument );
167}
168
169void InstrumentList::insert( int idx, std::shared_ptr<Instrument> instrument )
170{
171 // do nothing if already in __instruments
172 for( int i=0; i<__instruments.size(); i++ ) {
173 if( __instruments[i]==instrument ) return;
174 }
175 __instruments.insert( __instruments.begin() + idx, instrument );
176}
177
178std::shared_ptr<Instrument> InstrumentList::operator[]( int idx )
179{
180 if ( idx < 0 || idx >= __instruments.size() ) {
181 ERRORLOG( QString( "idx %1 out of [0;%2]" ).arg( idx ).arg( size() ) );
182 return nullptr;
183 }
184 assert( idx >= 0 && idx < __instruments.size() );
185 return __instruments[idx];
186}
187
189{
190 bool is_valid_index = true;
191
192 if ( idx < 0 || idx >= __instruments.size() ) {
193 is_valid_index = false;
194 }
195
196 return is_valid_index;
197}
198
199std::shared_ptr<Instrument> InstrumentList::get( int idx ) const
200{
201 if ( ! is_valid_index( idx ) ) {
202 ERRORLOG( QString( "idx %1 out of [0;%2]" ).arg( idx ).arg( size() ) );
203 return nullptr;
204 }
205 assert( idx >= 0 && idx < __instruments.size() );
206 return __instruments.at( idx );
207}
208
209int InstrumentList::index( std::shared_ptr<Instrument> instr )
210{
211 for( int i=0; i<__instruments.size(); i++ ) {
212 if ( __instruments[i]==instr ) return i;
213 }
214 return -1;
215}
216
217std::shared_ptr<Instrument> InstrumentList::find( const int id )
218{
219 for( int i=0; i<__instruments.size(); i++ ) {
220 if ( __instruments[i]->get_id()==id ) return __instruments[i];
221 }
222 return nullptr;
223}
224
225std::shared_ptr<Instrument> InstrumentList::find( const QString& name )
226{
227 for( int i=0; i<__instruments.size(); i++ ) {
228 if ( __instruments[i]->get_name()==name ) return __instruments[i];
229 }
230 return nullptr;
231}
232
233std::shared_ptr<Instrument> InstrumentList::findMidiNote( const int note )
234{
235 for( int i=0; i<__instruments.size(); i++ ) {
236 if ( __instruments[i]->get_midi_out_note()==note ) return __instruments[i];
237 }
238 return nullptr;
239}
240
241std::shared_ptr<Instrument> InstrumentList::del( int idx )
242{
243 assert( idx >= 0 && idx < __instruments.size() );
244 auto instrument = __instruments[idx];
245 __instruments.erase( __instruments.begin() + idx );
246 return instrument;
247}
248
249std::shared_ptr<Instrument> InstrumentList::del( std::shared_ptr<Instrument> instrument )
250{
251 for( int i=0; i<__instruments.size(); i++ ) {
252 if( __instruments[i]==instrument ) {
253 __instruments.erase( __instruments.begin() + i );
254 return instrument;
255 }
256 }
257 return nullptr;
258}
259
260void InstrumentList::swap( int idx_a, int idx_b )
261{
262 assert( idx_a >= 0 && idx_a < __instruments.size() );
263 assert( idx_b >= 0 && idx_b < __instruments.size() );
264 if( idx_a == idx_b ) return;
265 //DEBUGLOG(QString("===>> SWAP %1 %2").arg(idx_a).arg(idx_b) );
266 auto tmp = __instruments[idx_a];
267 __instruments[idx_a] = __instruments[idx_b];
268 __instruments[idx_b] = tmp;
269}
270
271void InstrumentList::move( int idx_a, int idx_b )
272{
273 assert( idx_a >= 0 && idx_a < __instruments.size() );
274 assert( idx_b >= 0 && idx_b < __instruments.size() );
275 if( idx_a == idx_b ) return;
276 //DEBUGLOG(QString("===>> MOVE %1 %2").arg(idx_a).arg(idx_b) );
277 auto tmp = __instruments[idx_a];
278 __instruments.erase( __instruments.begin() + idx_a );
279 __instruments.insert( __instruments.begin() + idx_b, tmp );
280}
281
282std::vector<std::shared_ptr<InstrumentList::Content>> InstrumentList::summarizeContent( const std::shared_ptr<std::vector<std::shared_ptr<DrumkitComponent>>> pDrumkitComponents ) const {
283 std::vector<std::shared_ptr<InstrumentList::Content>> results;
284
285 for ( const auto& ppInstrument : __instruments ) {
286 if ( ppInstrument != nullptr ) {
287 for ( const auto& ppInstrumentComponent : *ppInstrument->get_components() ) {
288 if ( ppInstrumentComponent != nullptr ) {
289 for ( const auto& ppInstrumentLayer : *ppInstrumentComponent ) {
290 if ( ppInstrumentLayer != nullptr ) {
291 auto pSample = ppInstrumentLayer->get_sample();
292 if ( pSample != nullptr ) {
293 // Map component ID to component
294 // name.
295 bool bFound = false;
296 QString sComponentName;
297 for ( const auto& ppDrumkitComponent : *pDrumkitComponents ) {
298 if ( ppInstrumentComponent->get_drumkit_componentID() ==
299 ppDrumkitComponent->get_id() ) {
300 bFound = true;
301 sComponentName = ppDrumkitComponent->get_name();
302 break;
303 }
304 }
305
306 if ( ! bFound ) {
307 sComponentName = pDrumkitComponents->front()->get_name();
308 }
309
310 results.push_back( std::make_shared<Content>(
311 ppInstrument->get_name(), // m_sInstrumentName
312 sComponentName, // m_sComponentName
313 pSample->get_filename(), // m_sSampleName
314 pSample->get_filepath(), // m_sFullSamplePath
315 pSample->getLicense() // m_license
316 ) );
317 }
318 }
319 }
320 }
321 }
322 }
323 }
324
325 return results;
326}
327
329{
330 if ( has_all_midi_notes_same() ) {
331 WARNINGLOG( "Same MIDI note assigned to every instrument. Assigning default values." );
333 }
334}
335
337{
338 if (__instruments.size() < 2) {
339 return false;
340 }
341
342 std::set<int> notes;
343 for( int i=0; i<__instruments.size(); i++ ) {
344 auto instr = __instruments[i];
345 notes.insert( instr->get_midi_out_note() );
346 }
347 return notes.size() == 1;
348}
349
351{
352 for( int i=0; i<__instruments.size(); i++ ) {
353 __instruments[i]->set_midi_out_note( i + 36 );
354 }
355}
356
357QString InstrumentList::toQString( const QString& sPrefix, bool bShort ) const {
358 QString s = Base::sPrintIndention;
359 QString sOutput;
360 if ( ! bShort ) {
361 sOutput = QString( "%1[InstrumentList]\n" ).arg( sPrefix );
362 for ( auto ii : __instruments ) {
363 if ( ii != nullptr ) {
364 sOutput.append( QString( "%1" ).arg( ii->toQString( sPrefix + s, bShort ) ) );
365 }
366 }
367 } else {
368 sOutput = QString( "[InstrumentList] " );
369 for ( auto ii : __instruments ) {
370 if ( ii != nullptr ) {
371 sOutput.append( QString( "(%1: %2) " ).arg( ii->get_id() )
372 .arg( ii->get_name() ) );
373 }
374 }
375 }
376
377 return sOutput;
378}
379
380
381std::vector<std::shared_ptr<Instrument>>::iterator InstrumentList::begin() {
382 return __instruments.begin();
383}
384
385std::vector<std::shared_ptr<Instrument>>::iterator InstrumentList::end() {
386 return __instruments.end();
387}
388
389QString InstrumentList::Content::toQString( const QString& sPrefix, bool bShort ) const {
390
391 QString s = Base::sPrintIndention;
392 QString sOutput;
393 if ( ! bShort ) {
394 sOutput = QString( "\n" ).arg( sPrefix )
395 .append( QString( "%1%2m_sInstrumentName: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sInstrumentName ) )
396 .append( QString( "%1%2m_sComponentName: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sComponentName ) )
397 .append( QString( "%1%2m_sSampleName: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sSampleName ) )
398 .append( QString( "%1%2m_sFullSamplePath: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sFullSamplePath ) )
399 .append( QString( "%1%2m_license: %3\n" ).arg( m_license.toQString( sPrefix + s, bShort ) ) );
400 } else {
401 sOutput = QString( "m_sInstrumentName: %1\n" ).arg( m_sInstrumentName )
402 .append( QString( ", m_sComponentName: %1\n" ).arg( m_sComponentName ) )
403 .append( QString( ", m_sSampleName: %1\n" ).arg( m_sSampleName ) )
404 .append( QString( ", m_sFullSamplePath: %1\n" ).arg( m_sFullSamplePath ) )
405 .append( QString( ", m_license: %1\n" ).arg( m_license.toQString( "", bShort ) ) );
406 }
407
408 return sOutput;
409}
410
411
413{
414 for ( auto &pInstrument : __instruments ) {
415 if ( pInstrument->is_soloed() ) {
416 return true;
417 }
418 }
419 return false;
420}
421
422
423};
424
425/* vim: set softtabstop=4 noexpandtab: */
#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
void insert(int idx, std::shared_ptr< Instrument > instrument)
insert an instrument into the list
int index(std::shared_ptr< Instrument > instrument)
get the index of an instrument within the instruments
void add(std::shared_ptr< Instrument > instrument)
add an instrument to the list
std::vector< std::shared_ptr< Instrument > >::iterator end()
std::shared_ptr< Instrument > operator[](int idx)
get an instrument from the list
bool has_all_midi_notes_same() const
Check if all instruments have assigned the same MIDI out note.
std::shared_ptr< Instrument > findMidiNote(const int note)
find an instrument which play the given midi note
void move(int idx_a, int idx_b)
move an instrument from a position to another
bool is_valid_index(int idx) const
check if there is a idx is a valid index for this list without throwing an error messaage
void load_samples(float fBpm=120)
Calls the Instrument::load_samples() member function of all Instruments in __instruments.
std::shared_ptr< Instrument > find(const int i)
find an instrument within the instruments
std::shared_ptr< Instrument > get(int idx) const
get an instrument from the list
void fix_issue_307()
Fix GitHub issue #307, so called "Hi Bongo fiasco".
std::vector< std::shared_ptr< Instrument > >::iterator begin()
Iteration.
void operator<<(std::shared_ptr< Instrument > instrument)
add an instrument to the list
void swap(int idx_a, int idx_b)
swap the instruments of two different indexes
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
std::vector< std::shared_ptr< Content > > summarizeContent(const std::shared_ptr< std::vector< std::shared_ptr< DrumkitComponent > > > pDrumkitComponents) const
Returns vector of lists containing instrument name, component name, file name, the license of all ass...
std::shared_ptr< Instrument > del(int idx)
remove the instrument at a given index, does not delete it
void set_default_midi_out_notes()
Set each instrument consecuteve MIDI out notes, starting from 36.
bool isAnyInstrumentSoloed()
Check if any instrument in the list is solo'd.
void unload_samples()
Calls the Instrument::unload_samples() member function of all Instruments in __instruments.
bool operator==(std::shared_ptr< InstrumentList > pOther) const
Superficial comparison check.
std::vector< std::shared_ptr< Instrument > > __instruments
the list of instruments
void save_to(XMLNode *node, int component_id, bool bRecentVersion=true, bool bFull=false)
save the instrument list within the given XMLNode
static std::shared_ptr< InstrumentList > load_from(XMLNode *node, const QString &sDrumkitPath, const QString &sDrumkitName, const License &license=License(), bool bSilent=false)
load an instrument list from an XMLNode
int size() const
returns the numbers of instruments
bool operator!=(std::shared_ptr< InstrumentList > pOther) const
void load_from(const QString &drumkit_path, const QString &instrument_name)
loads instrument from a given instrument within a given drumkit into a live Instrument object.
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
Definition License.h:48
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition License.cpp:151
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:63
#define MAX_INSTRUMENTS
Maximum number of instruments allowed in Hydrogen.
Definition config.dox:70
QString toQString(const QString &sPrefix="", bool bShort=true) const