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