hydrogen 1.2.6
Instrument.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
24
25#include <cassert>
26
27#include <core/Hydrogen.h>
28
29#include <core/Helpers/Legacy.h>
30#include <core/Helpers/Xml.h>
31
32#include <core/Basics/Adsr.h>
33#include <core/Basics/Sample.h>
34#include <core/Basics/Drumkit.h>
39#include <core/IO/MidiCommon.h>
42
43namespace H2Core
44{
45
46Instrument::Instrument( const int id, const QString& name, std::shared_ptr<ADSR> adsr )
47 : __id( id )
48 , __name( name )
49 , __gain( 1.0 )
50 , __volume( 1.0 )
51 , m_fPan( 0.f )
52 , __peak_l( 0.0 )
53 , __peak_r( 0.0 )
54 , __adsr( adsr )
55 , __filter_active( false )
56 , __filter_cutoff( 1.0 )
57 , __filter_resonance( 0.0 )
58 , __pitch_offset( 0.0 )
60 , __midi_out_note( MidiMessage::instrumentOffset + id )
61 , __midi_out_channel( -1 )
62 , __stop_notes( false )
64 , __active( true )
65 , __soloed( false )
66 , __muted( false )
67 , __mute_group( -1 )
68 , __queued( 0 )
69 , __hihat_grp( -1 )
70 , __lower_cc( 0 )
71 , __higher_cc( 127 )
72 , __components( nullptr )
75 , __apply_velocity( true )
77 , m_bHasMissingSamples( false )
78{
79 if ( __adsr == nullptr ) {
80 __adsr = std::make_shared<ADSR>();
81 }
82
85 }
86
89 }
90
91 for ( int i=0; i<MAX_FX; i++ ) {
92 __fx_level[i] = 0.0;
93 }
94 __components = new std::vector<std::shared_ptr<InstrumentComponent>>();
95}
96
97Instrument::Instrument( std::shared_ptr<Instrument> other )
98 : __id( other->get_id() )
99 , __name( other->get_name() )
100 , __gain( other->__gain )
101 , __volume( other->get_volume() )
102 , m_fPan( other->getPan() )
103 , __peak_l( other->get_peak_l() )
104 , __peak_r( other->get_peak_r() )
105 , __adsr( std::make_shared<ADSR>( *( other->get_adsr() ) ) )
109 , __pitch_offset( other->get_pitch_offset() )
113 , __stop_notes( other->is_stop_notes() )
115 , __active( other->is_active() )
116 , __soloed( other->is_soloed() )
117 , __muted( other->is_muted() )
118 , __mute_group( other->get_mute_group() )
119 , __queued( other->is_queued() )
120 , __hihat_grp( other->get_hihat_grp() )
121 , __lower_cc( other->get_lower_cc() )
122 , __higher_cc( other->get_higher_cc() )
123 , __components( nullptr )
129 , __drumkit_path( other->get_drumkit_path() )
131{
132 for ( int i=0; i<MAX_FX; i++ ) {
133 __fx_level[i] = other->get_fx_level( i );
134 }
135
136 __components = new std::vector<std::shared_ptr<InstrumentComponent>>();
137 for ( auto& pComponent : *other->get_components() ) {
138 __components->push_back( std::make_shared<InstrumentComponent>( pComponent ) );
139 }
140}
141
143{
144 delete __components;
145}
146
147std::shared_ptr<Instrument> Instrument::load_instrument( const QString& drumkit_path, const QString& instrument_name )
148{
149 auto pInstrument = std::make_shared<Instrument>();
150 pInstrument->load_from( drumkit_path, instrument_name );
151 return pInstrument;
152}
153
154void Instrument::load_from( std::shared_ptr<Drumkit> pDrumkit, std::shared_ptr<Instrument> pInstrument )
155{
156 assert( pDrumkit );
157 if ( pDrumkit == nullptr ) {
158 ERRORLOG( "Invalid drumkit supplied" );
159 return;
160 }
161
162 this->get_components()->clear();
163
164 set_missing_samples( false );
165
166 for ( const auto& pSrcComponent : *pInstrument->get_components() ) {
167 auto pMyComponent = std::make_shared<InstrumentComponent>( pSrcComponent->get_drumkit_componentID() );
168 pMyComponent->set_gain( pSrcComponent->get_gain() );
169
170 this->get_components()->push_back( pMyComponent );
171
172 for ( int i = 0; i < InstrumentComponent::getMaxLayers(); i++ ) {
173 auto src_layer = pSrcComponent->get_layer( i );
174 auto my_layer = pMyComponent->get_layer( i );
175
176 if( src_layer == nullptr ) {
177 pMyComponent->set_layer( nullptr, i );
178 }
179 else {
180 std::shared_ptr<Sample> pSample = nullptr;
181 QString sSamplePath;
182
183 if ( src_layer->get_sample() != nullptr ) {
184 QString sSamplePath = pDrumkit->get_path() + "/" + src_layer->get_sample()->get_filename();
185 pSample = Sample::load( sSamplePath );
186 }
187
188 if ( pSample == nullptr ) {
189 _ERRORLOG( QString( "Error loading sample %1. Creating a new empty layer." )
190 .arg( sSamplePath ) );
191 set_missing_samples( true );
192 pMyComponent->set_layer( nullptr, i );
193
194 }
195 else {
196 pSample->setLicense( pDrumkit->get_license() );
197 pMyComponent->set_layer( std::make_shared<InstrumentLayer>( src_layer, pSample ), i );
198 }
199 }
200 my_layer = nullptr;
201 }
202 }
203
204 this->set_id( pInstrument->get_id() );
205 this->set_name( pInstrument->get_name() );
206 this->set_drumkit_path( pDrumkit->get_path() );
207 this->set_drumkit_name( pDrumkit->get_name() );
208 this->set_gain( pInstrument->get_gain() );
209 this->set_volume( pInstrument->get_volume() );
210 this->setPan( pInstrument->getPan() );
211 this->set_adsr( std::make_shared<ADSR>( *( pInstrument->get_adsr() ) ) );
212 this->set_filter_active( pInstrument->is_filter_active() );
213 this->set_filter_cutoff( pInstrument->get_filter_cutoff() );
214 this->set_filter_resonance( pInstrument->get_filter_resonance() );
215 this->set_pitch_offset( pInstrument->get_pitch_offset() );
216 this->set_random_pitch_factor( pInstrument->get_random_pitch_factor() );
217 this->set_mute_group( pInstrument->get_mute_group() );
218 this->set_midi_out_channel( pInstrument->get_midi_out_channel() );
219 this->set_midi_out_note( pInstrument->get_midi_out_note() );
220 this->set_stop_notes( pInstrument->is_stop_notes() );
221 this->set_sample_selection_alg( pInstrument->sample_selection_alg() );
222 this->set_active( pInstrument->is_active() );
223 this->set_soloed( pInstrument->is_soloed() );
224 this->set_muted( pInstrument->is_muted() );
225 this->set_hihat_grp( pInstrument->get_hihat_grp() );
226 this->set_lower_cc( pInstrument->get_lower_cc() );
227 this->set_higher_cc( pInstrument->get_higher_cc() );
228 this->set_apply_velocity ( pInstrument->get_apply_velocity() );
229
230 for ( int ii = 0; ii < MAX_FX; ++ii ) {
231 this->set_fx_level( pInstrument->get_fx_level( ii ), ii );
232 }
233}
234
235void Instrument::load_from( const QString& sDrumkitPath, const QString& sInstrumentName )
236{
237 std::shared_ptr<Drumkit> pDrumkit;
238
239 // Try to retrieve the name from cache first.
240 auto pHydrogen = Hydrogen::get_instance();
241 if ( pHydrogen != nullptr ) {
242 pDrumkit = pHydrogen->getSoundLibraryDatabase()->getDrumkit( sDrumkitPath );
243 }
244
245 assert( pDrumkit );
246 if ( pDrumkit == nullptr ) {
247 ERRORLOG( QString( "Unable to load instrument: corresponding drumkit [%1] could not be loaded" )
248 .arg( sDrumkitPath ) );
249 return;
250 }
251
252 auto pInstrument = pDrumkit->get_instruments()->find( sInstrumentName );
253 if ( pInstrument != nullptr ) {
254 load_from( pDrumkit, pInstrument );
255 }
256 else {
257 ERRORLOG( QString( "Unable to load instrument: instrument [%1] could not be found in drumkit [%2]" )
258 .arg( sInstrumentName ).arg( sDrumkitPath ) );
259 }
260}
261
262std::shared_ptr<Instrument> Instrument::load_from( XMLNode* pNode,
263 const QString& sDrumkitPath,
264 const QString& sDrumkitName,
265 const License& license,
266 bool* pLegacyFormatEncountered,
267 bool bSilent )
268{
269 // We use -2 instead of EMPTY_INSTR_ID (-1) to allow for loading
270 // empty instruments as well (e.g. during unit tests or as part of
271 // dummy kits)
272 int nId = pNode->read_int( "id", -2, false, false, bSilent );
273 if ( nId == -2 ) {
274 if ( pLegacyFormatEncountered != nullptr ) {
275 *pLegacyFormatEncountered = true;
276 }
277 return nullptr;
278 }
279
280 auto pInstrument =
281 std::make_shared<Instrument>(
282 nId,
283 pNode->read_string( "name", "", false, false, bSilent ),
284 std::make_shared<ADSR>( pNode->read_int( "Attack", 0, true, false, bSilent ),
285 pNode->read_int( "Decay", 0, true, false, bSilent ),
286 pNode->read_float( "Sustain", 1.0f, true, false, bSilent ),
287 pNode->read_int( "Release", 1000, true, false, bSilent ) ) );
288
289 QString sInstrumentDrumkitPath, sInstrumentDrumkitName;
290 if ( sDrumkitPath.isEmpty() || sDrumkitName.isEmpty() ) {
291 // Instrument is not read as part of a Drumkit but as part of
292 // a Song. The drumkit meta info will be read from disk.
293 sInstrumentDrumkitName = pNode->read_string( "drumkit", "", false,
294 false, bSilent );
295
296 if ( ! pNode->firstChildElement( "drumkitPath" ).isNull() ) {
297 // Current format
298 sInstrumentDrumkitPath = pNode->read_string( "drumkitPath", "",
299 false, false, bSilent );
300
301#ifdef H2CORE_HAVE_APPIMAGE
302 sInstrumentDrumkitPath =
303 Filesystem::rerouteDrumkitPath( sInstrumentDrumkitPath );
304#endif
305
306 // Check whether corresponding drumkit exist.
307 // When tweaking or assembling drumkits locally their
308 // absolute paths serve as unique identifiers to keep them
309 // apart. But in terms of portability (and to assure
310 // backward compatibility) paths are bad and we will use
311 // the drumkit name and check whether we can find the kit
312 // on the local system.
313 if ( ! Filesystem::drumkit_valid( sInstrumentDrumkitPath ) ) {
314 WARNINGLOG( QString( "Couldn't find drumkit at [%1]. Searching for [%2] instead." )
315 .arg( sInstrumentDrumkitPath )
316 .arg( sInstrumentDrumkitName ) );
317 sInstrumentDrumkitPath = "";
318 }
319 }
320
321 if ( sInstrumentDrumkitPath.isEmpty() ) {
322 if ( ! pNode->firstChildElement( "drumkitLookup" ).isNull() ) {
323 // Format introduced in #1f2a06b and used in (at least)
324 // releases 1.1.0-beta1, 1.1.0, and 1.1.1.
325 //
326 // Using the additional lookup variable two drumkits holding
327 // the same name but one of the residing in user-space and
328 // the other one in system-space can be distinguished.
329 Filesystem::Lookup lookup =
330 static_cast<Filesystem::Lookup>(
331 pNode->read_int( "drumkitLookup",
332 static_cast<int>(Filesystem::Lookup::stacked),
333 false, false, bSilent ) );
334
335 sInstrumentDrumkitPath =
336 Filesystem::drumkit_path_search( sInstrumentDrumkitName,
337 lookup, true );
338
339 if ( sInstrumentDrumkitPath.isEmpty() &&
340 lookup != Filesystem::Lookup::stacked ) {
341 // Drumkit could not be found.
342 //
343 // It's possible the song was composed with a
344 // custom version of a system-level drumkit stored
345 // in user space. When loaded again in a fresh
346 // installed Hydrogen the custom user-level one
347 // will not be present anymore but it's plausible
348 // to fall back to the system-level one. (The
349 // other way around is also possible but much more
350 // unlikely. Nevertheless we will use the stacked
351 // search in one final effort)
352 sInstrumentDrumkitPath =
353 Filesystem::drumkit_path_search( sInstrumentDrumkitName,
355 true );
356
357 if ( sInstrumentDrumkitPath.isEmpty() ) {
358 ERRORLOG( QString( "Drumkit [%1] could neither found at system nor at user level." )
359 .arg( sInstrumentDrumkitName ) );
360 }
361 else if ( ! bSilent ) {
362 WARNINGLOG( QString( "Drumkit [%1] could not found using lookup type [%2]. Falling back to [%3] found using stacked search" )
363 .arg( sInstrumentDrumkitName )
364 .arg( static_cast<int>(lookup) )
365 .arg( sInstrumentDrumkitPath ) );
366 }
367 }
368
369 if ( pLegacyFormatEncountered != nullptr ) {
370 *pLegacyFormatEncountered = true;
371 }
372 }
373 else if ( ! pNode->firstChildElement( "drumkit" ).isNull() ) {
374 // Format used from version 0.9.7 till 1.1.0.
375 //
376 // It features just the name of the drumkit an relies on
377 // it being unique throught the entire search path.
378 sInstrumentDrumkitPath =
379 Filesystem::drumkit_path_search( sInstrumentDrumkitName,
381 bSilent );
382
383 if ( pLegacyFormatEncountered != nullptr ) {
384 *pLegacyFormatEncountered = true;
385 }
386 }
387 else {
388 // Format used prior to 0.9.7 which worked with absolute
389 // paths for the samples instead of relative ones.
390 sInstrumentDrumkitPath = "";
391
392 if ( pLegacyFormatEncountered != nullptr ) {
393 *pLegacyFormatEncountered = true;
394 }
395 }
396 }
397 }
398 else {
399 sInstrumentDrumkitPath = sDrumkitPath;
400 sInstrumentDrumkitName = sDrumkitName;
401 }
402
403 pInstrument->set_drumkit_path( sInstrumentDrumkitPath );
404 pInstrument->__drumkit_name = sInstrumentDrumkitName;
405
406 pInstrument->set_volume( pNode->read_float( "volume", 1.0f,
407 true, true, bSilent ) );
408 pInstrument->set_muted( pNode->read_bool( "isMuted", false,
409 true, true, bSilent ) );
410 pInstrument->set_soloed( pNode->read_bool( "isSoloed", false,
411 true, false, true ) );
412 bool bFound, bFound2;
413 float fPan = pNode->read_float( "pan", 0.f, &bFound,
414 true, true, true );
415 if ( !bFound ) {
416 // check if pan is expressed in the old fashion (version <=
417 // 1.1 ) with the pair (pan_L, pan_R)
418 float fPanL = pNode->read_float( "pan_L", 1.f, &bFound,
419 true, true, bSilent );
420 float fPanR = pNode->read_float( "pan_R", 1.f, &bFound2,
421 true, true, bSilent );
422 if ( bFound == true && bFound2 == true ) { // found nodes pan_L and pan_R
423 fPan = Sampler::getRatioPan( fPanL, fPanR ); // convert to single pan parameter
424 }
425 }
426 pInstrument->setPan( fPan );
427
428 pInstrument->set_apply_velocity( pNode->read_bool( "applyVelocity", true,
429 false, true, bSilent ) );
430 pInstrument->set_filter_active( pNode->read_bool( "filterActive", true,
431 false, true, bSilent ) );
432 pInstrument->set_filter_cutoff( pNode->read_float( "filterCutoff", 1.0f,
433 true, false, bSilent ) );
434 pInstrument->set_filter_resonance( pNode->read_float( "filterResonance", 0.0f,
435 true, false, bSilent ) );
436 pInstrument->set_pitch_offset( pNode->read_float( "pitchOffset", 0.0f,
437 true, false, true ) );
438 pInstrument->set_random_pitch_factor( pNode->read_float( "randomPitchFactor", 0.0f,
439 true, false, bSilent ) );
440 pInstrument->set_gain( pNode->read_float( "gain", 1.0f,
441 true, false, bSilent ) );
442 pInstrument->set_mute_group( pNode->read_int( "muteGroup", -1,
443 true, false, bSilent ) );
444 pInstrument->set_midi_out_channel( pNode->read_int( "midiOutChannel", -1,
445 true, false, bSilent ) );
446 pInstrument->set_midi_out_note( pNode->read_int( "midiOutNote", pInstrument->__midi_out_note,
447 true, false, bSilent ) );
448 pInstrument->set_stop_notes( pNode->read_bool( "isStopNote", true,
449 false, true, bSilent ) );
450
451 // For versions >= 2.0 the sample selection algorithm was moved on component
452 // level. That's why we suppress warning logs in here, as there can be quite
453 // a number downgrading Hydrogen.
454 QString sRead_sample_select_algo = pNode->read_string( "sampleSelectionAlgo", "VELOCITY",
455 true, true, true );
456 if ( sRead_sample_select_algo.compare("VELOCITY") == 0 ) {
457 pInstrument->set_sample_selection_alg( VELOCITY );
458 }
459 else if ( sRead_sample_select_algo.compare("ROUND_ROBIN") == 0 ) {
460 pInstrument->set_sample_selection_alg( ROUND_ROBIN );
461 }
462 else if ( sRead_sample_select_algo.compare("RANDOM") == 0 ) {
463 pInstrument->set_sample_selection_alg( RANDOM );
464 }
465
466 pInstrument->set_hihat_grp( pNode->read_int( "isHihat", -1,
467 true, true, bSilent ) );
468 pInstrument->set_lower_cc( pNode->read_int( "lower_cc", 0,
469 true, true, bSilent ) );
470 pInstrument->set_higher_cc( pNode->read_int( "higher_cc", 127,
471 true, true, bSilent ) );
472
473 for ( int i=0; i<MAX_FX; i++ ) {
474 pInstrument->set_fx_level( pNode->read_float( QString( "FX%1Level" ).arg( i+1 ), 0.0,
475 true, true, bSilent ), i );
476 }
477
478 // This license will be applied to all samples contained in this
479 // instrument.
480 License instrumentLicense;
481 if ( license == License() ) {
482 // No/empty license supplied. We will use the license stored
483 // in the drumkit.xml file found in __drumkit_name. But since
484 // loading it from file is a rather expensive action, we will
485 // query it from the Drumkit database. If, for some reasons,
486 // the drumkit is not present yet, the License will be loaded
487 // directly.
488 auto pSoundLibraryDatabase = Hydrogen::get_instance()->getSoundLibraryDatabase();
489 if ( pSoundLibraryDatabase != nullptr ) {
490
491 // It is important to _not_ load the drumkit into the
492 // database as this code is part of the drumkit load
493 // itself. In case two drumkits contain an instrument from
494 // each other an infinite loop would be created.
495 auto pDrumkit = pSoundLibraryDatabase->getDrumkit(
496 pInstrument->get_drumkit_path(), false );
497 if ( pDrumkit == nullptr ) {
498 // Drumkit is not present in the database yet. Load
499 // its license from disk.
500 instrumentLicense = Drumkit::loadLicenseFrom( pInstrument->get_drumkit_path() );
501 } else {
502 instrumentLicense = pDrumkit->get_license();
503 }
504 }
505 } else {
506 instrumentLicense = license;
507 }
508
509 if ( ! pNode->firstChildElement( "instrumentComponent" ).isNull() ) {
510 // current format
511 XMLNode componentNode = pNode->firstChildElement( "instrumentComponent" );
512 while ( ! componentNode.isNull() ) {
513 pInstrument->get_components()->
514 push_back( InstrumentComponent::load_from( &componentNode,
515 pInstrument->get_drumkit_path(),
516 instrumentLicense, bSilent ) );
517 componentNode = componentNode.nextSiblingElement( "instrumentComponent" );
518 }
519 }
520 else {
521 // back compatibility code
522 auto pCompo = Legacy::loadInstrumentComponent( pNode, pInstrument->get_drumkit_path(),
523 instrumentLicense, bSilent );
524 if ( pCompo == nullptr ) {
525 ERRORLOG( QString( "Unable to load component for instrument [%1]. Aborting." )
526 .arg( pInstrument->get_name() ) );
527 return nullptr;
528 }
529
530 pInstrument->get_components()->push_back( pCompo );
531
532 if ( pLegacyFormatEncountered != nullptr ) {
533 *pLegacyFormatEncountered = true;
534 }
535}
536
537 // Sanity checks
538
539 // There has to be at least one InstrumentComponent
540 if ( pInstrument->get_components()->size() == 0 ) {
541 pInstrument->get_components()->push_back(
542 std::make_shared<InstrumentComponent>( 0 ) );
543 }
544
545 // Check whether there are missing samples
546 bool bSampleFound = false;
547 for ( const auto& pComponent : *pInstrument->get_components() ) {
548 if ( pComponent == nullptr ) {
549 ERRORLOG( "Invalid component. Something went wrong loading the instrument" );
550 pInstrument->set_muted( true );
551 pInstrument->set_missing_samples( true );
552 break;
553 }
554
555 for ( const auto& pLayer : *pComponent ) {
556 if ( pLayer == nullptr ) {
557 // The component is filled with nullptr up to
558 // InstrumentComponent::m_nMaxLayers.
559 continue;
560 }
561
562 if ( pLayer->get_sample() != nullptr ) {
563 if ( ! bSampleFound ) {
564 bSampleFound = true;
565 }
566 } else {
567 pInstrument->set_missing_samples( true );
568 }
569 }
570 }
571
572 if ( ! bSampleFound ) {
573 pInstrument->set_muted( true );
574 }
575
576 return pInstrument;
577}
578
579void Instrument::load_samples( float fBpm )
580{
581 for ( auto& pComponent : *get_components() ) {
582 for ( int i = 0; i < InstrumentComponent::getMaxLayers(); i++ ) {
583 auto pLayer = pComponent->get_layer( i );
584 if ( pLayer != nullptr ) {
585 pLayer->load_sample( fBpm );
586 }
587 }
588 }
589}
590
592{
593 for ( auto& pComponent : *get_components() ) {
594 for ( int i = 0; i < InstrumentComponent::getMaxLayers(); i++ ) {
595 auto pLayer = pComponent->get_layer( i );
596 if( pLayer ){
597 pLayer->unload_sample();
598 }
599 }
600 }
601}
602
603void Instrument::save_to( XMLNode* node, int component_id, bool bRecentVersion, bool bFull )
604{
605 XMLNode InstrumentNode = node->createNode( "instrument" );
606 InstrumentNode.write_int( "id", __id );
607 InstrumentNode.write_string( "name", __name );
608
609 if ( bFull ) {
610 InstrumentNode.write_string( "drumkitPath", __drumkit_path );
611 InstrumentNode.write_string( "drumkit", __drumkit_name );
612 }
613
614 InstrumentNode.write_float( "volume", __volume );
615 InstrumentNode.write_bool( "isMuted", __muted );
616 InstrumentNode.write_bool( "isSoloed", __soloed );
617
618 // We still store the pan using the old format to allow drumkits
619 // being created with Hydrogen versions v1.2 to be valid for prior
620 // versions too. After a couple of years and when all major Linux
621 // distributions ship a version >= 1.2 we can drop this part and
622 // just store the plain pan.
623 if ( getPan() >= 0.0 ) {
624 InstrumentNode.write_float( "pan_L", 1.0 - getPan() );
625 InstrumentNode.write_float( "pan_R", 1.0 );
626 }
627 else {
628 InstrumentNode.write_float( "pan_L", 1.0 );
629 InstrumentNode.write_float( "pan_R", getPan() + 1.0 );
630 }
631
632 InstrumentNode.write_float( "pitchOffset", __pitch_offset );
633 InstrumentNode.write_float( "randomPitchFactor", __random_pitch_factor );
634 InstrumentNode.write_float( "gain", __gain );
635 InstrumentNode.write_bool( "applyVelocity", __apply_velocity );
636 InstrumentNode.write_bool( "filterActive", __filter_active );
637 InstrumentNode.write_float( "filterCutoff", __filter_cutoff );
638 InstrumentNode.write_float( "filterResonance", __filter_resonance );
639 InstrumentNode.write_int( "Attack", __adsr->getAttack() );
640 InstrumentNode.write_int( "Decay", __adsr->getDecay() );
641 InstrumentNode.write_float( "Sustain", __adsr->getSustain() );
642 InstrumentNode.write_int( "Release", __adsr->getRelease() );
643 InstrumentNode.write_int( "muteGroup", __mute_group );
644 InstrumentNode.write_int( "midiOutChannel", __midi_out_channel );
645 InstrumentNode.write_int( "midiOutNote", __midi_out_note );
646 InstrumentNode.write_bool( "isStopNote", __stop_notes );
647
648 switch ( __sample_selection_alg ) {
649 case VELOCITY:
650 InstrumentNode.write_string( "sampleSelectionAlgo", "VELOCITY" );
651 break;
652 case RANDOM:
653 InstrumentNode.write_string( "sampleSelectionAlgo", "RANDOM" );
654 break;
655 case ROUND_ROBIN:
656 InstrumentNode.write_string( "sampleSelectionAlgo", "ROUND_ROBIN" );
657 break;
658 }
659
660 InstrumentNode.write_int( "isHihat", __hihat_grp );
661 InstrumentNode.write_int( "lower_cc", __lower_cc );
662 InstrumentNode.write_int( "higher_cc", __higher_cc );
663
664 for ( int i=0; i<MAX_FX; i++ ) {
665 InstrumentNode.write_float( QString( "FX%1Level" ).arg( i+1 ), __fx_level[i] );
666 }
667
668 for ( auto& pComponent : *__components ) {
669 if ( pComponent != nullptr &&
670 ( component_id == -1 ||
671 pComponent->get_drumkit_componentID() == component_id ) ) {
672 pComponent->save_to( &InstrumentNode, component_id, bRecentVersion, bFull );
673 }
674 }
675}
676
677void Instrument::set_adsr( std::shared_ptr<ADSR> adsr )
678{
679 __adsr = adsr;
680}
681
683{
684 if ( fValue < fPitchMin || fValue > fPitchMax ) {
685 WARNINGLOG( QString( "Provided pitch out of bound [%1;%2]. Rounding to nearest allowed value." )
686 .arg( fPitchMin ).arg( fPitchMax ) );
687 }
688 __pitch_offset = std::clamp( fValue, fPitchMin, fPitchMax );
689}
690
691std::shared_ptr<InstrumentComponent> Instrument::get_component( int DrumkitComponentID )
692{
693 for ( const auto& pComponent : *get_components() ) {
694 if( pComponent->get_drumkit_componentID() == DrumkitComponentID ) {
695 return pComponent;
696 }
697 }
698
699 return nullptr;
700}
701
706
708 for ( const auto& pComponent : *__components ) {
709 if ( pComponent != nullptr ) {
710 for ( const auto& pLayer : *pComponent ) {
711 if ( pLayer != nullptr ) {
712 if ( pLayer->get_sample() != nullptr ) {
713 return true;
714 }
715 }
716 }
717 }
718 }
719
720 return false;
721}
722
723QString Instrument::toQString( const QString& sPrefix, bool bShort ) const {
724 QString s = Base::sPrintIndention;
725 QString sOutput;
726 if ( ! bShort ) {
727 sOutput = QString( "%1[Instrument]\n" ).arg( sPrefix )
728 .append( QString( "%1%2id: %3\n" ).arg( sPrefix ).arg( s ).arg( __id ) )
729 .append( QString( "%1%2name: %3\n" ).arg( sPrefix ).arg( s ).arg( __name ) )
730 .append( QString( "%1%2drumkit_path: %3\n" ).arg( sPrefix ).arg( s ).arg( __drumkit_path ) )
731 .append( QString( "%1%2drumkit_name: %3\n" ).arg( sPrefix ).arg( s ).arg( __drumkit_name ) )
732 .append( QString( "%1%2gain: %3\n" ).arg( sPrefix ).arg( s ).arg( __gain ) )
733 .append( QString( "%1%2volume: %3\n" ).arg( sPrefix ).arg( s ).arg( __volume ) )
734 .append( QString( "%1%2pan: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fPan ) )
735 .append( QString( "%1%2peak_l: %3\n" ).arg( sPrefix ).arg( s ).arg( __peak_l ) )
736 .append( QString( "%1%2peak_r: %3\n" ).arg( sPrefix ).arg( s ).arg( __peak_r ) )
737 .append( QString( "%1" ).arg( __adsr->toQString( sPrefix + s, bShort ) ) )
738 .append( QString( "%1%2filter_active: %3\n" ).arg( sPrefix ).arg( s ).arg( __filter_active ) )
739 .append( QString( "%1%2filter_cutoff: %3\n" ).arg( sPrefix ).arg( s ).arg( __filter_cutoff ) )
740 .append( QString( "%1%2filter_resonance: %3\n" ).arg( sPrefix ).arg( s ).arg( __filter_resonance ) )
741 .append( QString( "%1%2random_pitch_factor: %3\n" ).arg( sPrefix ).arg( s ).arg( __random_pitch_factor ) )
742 .append( QString( "%1%2pitch_offset: %3\n" ).arg( sPrefix ).arg( s ).arg( __pitch_offset ) )
743 .append( QString( "%1%2midi_out_note: %3\n" ).arg( sPrefix ).arg( s ).arg( __midi_out_note ) )
744 .append( QString( "%1%2midi_out_channel: %3\n" ).arg( sPrefix ).arg( s ).arg( __midi_out_channel ) )
745 .append( QString( "%1%2stop_notes: %3\n" ).arg( sPrefix ).arg( s ).arg( __stop_notes ) )
746 .append( QString( "%1%2sample_selection_alg: %3\n" ).arg( sPrefix ).arg( s ).arg( __sample_selection_alg ) )
747 .append( QString( "%1%2active: %3\n" ).arg( sPrefix ).arg( s ).arg( __active ) )
748 .append( QString( "%1%2soloed: %3\n" ).arg( sPrefix ).arg( s ).arg( __soloed ) )
749 .append( QString( "%1%2muted: %3\n" ).arg( sPrefix ).arg( s ).arg( __muted ) )
750 .append( QString( "%1%2mute_group: %3\n" ).arg( sPrefix ).arg( s ).arg( __mute_group ) )
751 .append( QString( "%1%2queued: %3\n" ).arg( sPrefix ).arg( s ).arg( __queued ) ) ;
752 sOutput.append( QString( "%1%2fx_level: [ " ).arg( sPrefix ).arg( s ) );
753 for ( auto ff : __fx_level ) {
754 sOutput.append( QString( "%1 " ).arg( ff ) );
755 }
756 sOutput.append( QString( "]\n" ) )
757 .append( QString( "%1%2hihat_grp: %3\n" ).arg( sPrefix ).arg( s ).arg( __hihat_grp ) )
758 .append( QString( "%1%2lower_cc: %3\n" ).arg( sPrefix ).arg( s ).arg( __lower_cc ) )
759 .append( QString( "%1%2higher_cc: %3\n" ).arg( sPrefix ).arg( s ).arg( __higher_cc ) )
760 .append( QString( "%1%2is_preview_instrument: %3\n" ).arg( sPrefix ).arg( s ).arg( __is_preview_instrument ) )
761 .append( QString( "%1%2is_metronome_instrument: %3\n" ).arg( sPrefix ).arg( s ).arg( __is_metronome_instrument ) )
762 .append( QString( "%1%2apply_velocity: %3\n" ).arg( sPrefix ).arg( s ).arg( __apply_velocity ) )
763 .append( QString( "%1%2current_instr_for_export: %3\n" ).arg( sPrefix ).arg( s ).arg( __current_instr_for_export ) )
764 .append( QString( "%1%2m_bHasMissingSamples: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bHasMissingSamples ) )
765 .append( QString( "%1%2components:\n" ).arg( sPrefix ).arg( s ) );
766 for ( auto cc : *__components ) {
767 if ( cc != nullptr ) {
768 sOutput.append( QString( "%1" ).arg( cc->toQString( sPrefix + s + s, bShort ) ) );
769 }
770 }
771 } else {
772
773 sOutput = QString( "[Instrument]" )
774 .append( QString( " id: %1" ).arg( __id ) )
775 .append( QString( ", name: %1" ).arg( __name ) )
776 .append( QString( ", drumkit_path: %1" ).arg( __drumkit_path ) )
777 .append( QString( ", drumkit_name: %1" ).arg( __drumkit_name ) )
778 .append( QString( ", gain: %1" ).arg( __gain ) )
779 .append( QString( ", volume: %1" ).arg( __volume ) )
780 .append( QString( ", pan: %1" ).arg( m_fPan ) )
781 .append( QString( ", peak_l: %1" ).arg( __peak_l ) )
782 .append( QString( ", peak_r: %1" ).arg( __peak_r ) )
783 .append( QString( ", [%1" ).arg( __adsr->toQString( sPrefix + s, bShort ).replace( "\n", "]" ) ) )
784 .append( QString( ", filter_active: %1" ).arg( __filter_active ) )
785 .append( QString( ", filter_cutoff: %1" ).arg( __filter_cutoff ) )
786 .append( QString( ", filter_resonance: %1" ).arg( __filter_resonance ) )
787 .append( QString( ", random_pitch_factor: %1" ).arg( __random_pitch_factor ) )
788 .append( QString( ", pitch_offset: %1" ).arg( __pitch_offset ) )
789 .append( QString( ", midi_out_note: %1" ).arg( __midi_out_note ) )
790 .append( QString( ", midi_out_channel: %1" ).arg( __midi_out_channel ) )
791 .append( QString( ", stop_notes: %1" ).arg( __stop_notes ) )
792 .append( QString( ", sample_selection_alg: %1" ).arg( __sample_selection_alg ) )
793 .append( QString( ", active: %1" ).arg( __active ) )
794 .append( QString( ", soloed: %1" ).arg( __soloed ) )
795 .append( QString( ", muted: %1" ).arg( __muted ) )
796 .append( QString( ", mute_group: %1" ).arg( __mute_group ) )
797 .append( QString( ", queued: %1" ).arg( __queued ) ) ;
798 sOutput.append( QString( ", fx_level: [ " ) );
799 for ( auto ff : __fx_level ) {
800 sOutput.append( QString( "%1 " ).arg( ff ) );
801 }
802 sOutput.append( QString( "]" ) )
803 .append( QString( ", hihat_grp: %1" ).arg( __hihat_grp ) )
804 .append( QString( ", lower_cc: %1" ).arg( __lower_cc ) )
805 .append( QString( ", higher_cc: %1" ).arg( __higher_cc ) )
806 .append( QString( ", is_preview_instrument: %1" ).arg( __is_preview_instrument ) )
807 .append( QString( ", is_metronome_instrument: %1" ).arg( __is_metronome_instrument ) )
808 .append( QString( ", apply_velocity: %1" ).arg( __apply_velocity ) )
809 .append( QString( ", current_instr_for_export: %1" ).arg( __current_instr_for_export ) )
810 .append( QString( ", m_bHasMissingSamples: %1" ).arg( m_bHasMissingSamples ) )
811 .append( QString( ", components: [" ) );
812 for ( auto cc : *__components ) {
813 if ( cc != nullptr ) {
814 sOutput.append( QString( " %1" ).arg( cc->get_drumkit_componentID() ) );
815 }
816 }
817 sOutput.append(" ]\n");
818 }
819
820 return sOutput;
821}
822
823};
824
825/* vim: set softtabstop=4 noexpandtab: */
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
#define _ERRORLOG(x)
Definition Object.h:248
Attack Decay Sustain Release envelope.
Definition Adsr.h:38
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
static License loadLicenseFrom(const QString &sDrumkitDir, bool bSilent=false)
Loads the license information of a drumkit contained in directory sDrumkitDir.
Definition Drumkit.cpp:243
static QString ensure_session_compatibility(const QString &sPath)
If Hydrogen is under session management, we support for paths relative to the session folder.
static bool drumkit_valid(const QString &dk_path)
returns true if the path contains a usable drumkit
static QString rerouteDrumkitPath(const QString &sDrumkitPath)
Reroutes stored drumkit paths pointing to a temporary AppImage system data folder to the current AppI...
Lookup
Whenever a drumkit is loaded by name a collision between a user and a system drumkit carrying the sam...
Definition Filesystem.h:55
@ stacked
First, looks in the system drumkits and, afterwards, in the user drumkits.
Definition Filesystem.h:63
static QString drumkit_path_search(const QString &dk_name, Lookup lookup=Lookup::stacked, bool bSilent=false)
Returns the path to a H2Core::Drumkit folder.
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
SoundLibraryDatabase * getSoundLibraryDatabase() const
Definition Hydrogen.h:95
static std::shared_ptr< InstrumentComponent > load_from(XMLNode *pNode, const QString &sDrumkitPath, const License &drumkitLicense=License(), bool bSilent=false)
bool has_missing_samples() const
Definition Instrument.h:312
QString __name
Name of the Instrument.
Definition Instrument.h:341
float __peak_r
right current peak value
Definition Instrument.h:362
float get_filter_resonance() const
get the filter resonance of the instrument
Definition Instrument.h:535
float __pitch_offset
instrument main pitch offset
Definition Instrument.h:374
float get_filter_cutoff() const
get the filter cutoff of the instrument
Definition Instrument.h:545
int get_mute_group() const
get the mute group of the instrument
Definition Instrument.h:437
void set_name(const QString &name)
get the name of the instrument
Definition Instrument.h:399
int __hihat_grp
the instrument is part of a hihat
Definition Instrument.h:385
float get_peak_r() const
get the right peak of the instrument
Definition Instrument.h:565
void set_pitch_offset(float val)
set the pitch offset of the instrument
bool __active
is the instrument active?
Definition Instrument.h:379
const QString & get_name() const
set the id of the instrument
Definition Instrument.h:405
void set_drumkit_name(const QString &sName)
get the name of the related drumkits
Definition Instrument.h:686
bool __is_preview_instrument
is the instrument an hydrogen preview instrument?
Definition Instrument.h:388
float get_pitch_offset() const
get the pitch offset of the instrument
Definition Instrument.h:590
bool get_apply_velocity() const
Definition Instrument.h:726
void set_sample_selection_alg(SampleSelectionAlgo selected_algo)
Definition Instrument.h:641
bool is_stop_notes() const
get the stop notes of the instrument
Definition Instrument.h:636
static constexpr float fPitchMin
Minimum support pitch value.
Definition Instrument.h:322
~Instrument()
destructor
bool __is_metronome_instrument
is the instrument an metronome instrument?
Definition Instrument.h:389
static std::shared_ptr< Instrument > load_instrument(const QString &drumkit_path, const QString &instrument_name)
creates a new Instrument, loads samples from a given instrument within a given drumkit
void set_fx_level(float level, int index)
set the fx level of the instrument
Definition Instrument.h:570
bool __stop_notes
will the note automatically generate a note off after being on
Definition Instrument.h:377
void set_filter_resonance(float val)
set the filter resonance of the instrument
Definition Instrument.h:530
float m_fPan
pan of the instrument, [-1;1] from left to right, as requested by Sampler PanLaws
Definition Instrument.h:360
bool __soloed
is the instrument in solo mode?
Definition Instrument.h:380
int __midi_out_channel
midi out channel
Definition Instrument.h:376
float __random_pitch_factor
Factor to scale the random contribution when humanizing pitch between 0 and AudioEngine::fHumanizePit...
Definition Instrument.h:373
void set_drumkit_path(const QString &sPath)
get the path of the related drumkits
Definition Instrument.h:681
int __mute_group
mute group of the instrument
Definition Instrument.h:382
int __id
Identifier of an instrument, which should be unique.
Definition Instrument.h:338
void set_soloed(bool soloed)
set the soloed status of the instrument
Definition Instrument.h:605
void set_adsr(std::shared_ptr< ADSR > adsr)
set the ADSR of the instrument
int get_higher_cc() const
set the path of the related drumkit
Definition Instrument.h:676
void set_volume(float volume)
set the volume of the instrument
Definition Instrument.h:510
float __filter_resonance
filter resonant frequency (0..1)
Definition Instrument.h:366
float __filter_cutoff
filter cutoff (0..1)
Definition Instrument.h:365
void set_random_pitch_factor(float val)
set the random pitch factor of the instrument
Definition Instrument.h:580
QString __drumkit_name
Name of the Drumkit found at __drumkit_path.
Definition Instrument.h:357
float __fx_level[MAX_FX]
Ladspa FX level array.
Definition Instrument.h:384
void set_filter_active(bool active)
activate the filter of the instrument
Definition Instrument.h:520
SampleSelectionAlgo __sample_selection_alg
how Hydrogen will chose the sample to use
Definition Instrument.h:378
void load_samples(float fBpm=120)
Calls the InstrumentLayer::load_sample() member function of all layers of each component of the Instr...
bool __muted
is the instrument muted?
Definition Instrument.h:381
int get_hihat_grp() const
Definition Instrument.h:656
bool is_muted() const
get muted status of the instrument
Definition Instrument.h:479
bool __filter_active
is filter active?
Definition Instrument.h:364
int get_lower_cc() const
Definition Instrument.h:666
bool is_queued() const
get the queued status of the instrument
Definition Instrument.h:626
float __gain
gain of the instrument
Definition Instrument.h:358
QString __drumkit_path
Path of the Drumkit this Instrument belongs to.
Definition Instrument.h:349
std::shared_ptr< InstrumentComponent > get_component(int DrumkitComponentID)
static constexpr float fPitchMax
Maximum support pitch value.
Definition Instrument.h:320
void set_midi_out_channel(int channel)
set the midi out channel of the instrument
Definition Instrument.h:447
void set_apply_velocity(bool apply_velocity)
Definition Instrument.h:721
bool m_bHasMissingSamples
does the instrument have missing sample files?
Definition Instrument.h:393
bool is_soloed() const
get the soloed status of the instrument
Definition Instrument.h:610
int get_midi_out_note() const
get the midi out note of the instrument
Definition Instrument.h:460
void set_stop_notes(bool stopnotes)
set the stop notes status of the instrument
Definition Instrument.h:631
void set_gain(float gain)
set gain of the instrument
Definition Instrument.h:500
void set_midi_out_note(int note)
set the midi out note of the instrument
Definition Instrument.h:465
int __higher_cc
higher cc level
Definition Instrument.h:387
void set_hihat_grp(int hihat_grp)
Definition Instrument.h:651
void set_id(const int id)
get the id of the instrument
Definition Instrument.h:411
float get_peak_l() const
get the left peak of the instrument
Definition Instrument.h:555
float __peak_l
left current peak value
Definition Instrument.h:361
float get_random_pitch_factor() const
get the random pitch factor of the instrument
Definition Instrument.h:585
void setPan(float val)
set pan of the instrument
Definition Instrument.h:484
void set_mute_group(int group)
set the mute group of the instrument
Definition Instrument.h:432
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
std::shared_ptr< ADSR > get_adsr() const
get the ADSR of the instrument
Definition Instrument.h:422
bool __current_instr_for_export
is the instrument currently being exported?
Definition Instrument.h:392
void set_active(bool active)
set the active status of the instrument
Definition Instrument.h:595
int __lower_cc
lower cc level
Definition Instrument.h:386
std::vector< std::shared_ptr< InstrumentComponent > > * __components
InstrumentLayer array.
Definition Instrument.h:390
std::shared_ptr< ADSR > __adsr
attack delay sustain release instance
Definition Instrument.h:363
void set_filter_cutoff(float val)
set the filter cutoff of the instrument
Definition Instrument.h:540
void unload_samples()
Calls the InstrumentLayer::unload_sample() member function of all layers of each component of the Ins...
float __volume
volume of the instrument
Definition Instrument.h:359
Instrument(const int id=EMPTY_INSTR_ID, const QString &name="Empty Instrument", std::shared_ptr< ADSR > adsr=nullptr)
constructor
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.
bool is_filter_active() const
get the status of the filter of the instrument
Definition Instrument.h:525
int __queued
count the number of notes queued within Sampler::__playing_notes_queue or std::priority_queue m_songN...
Definition Instrument.h:383
void set_missing_samples(bool bHasMissingSamples)
Definition Instrument.h:313
void set_higher_cc(int message)
Definition Instrument.h:671
void save_to(XMLNode *node, int component_id, bool bRecentVersion=true, bool bFull=false)
save the instrument within the given XMLNode
bool __apply_velocity
change the sample gain based on velocity
Definition Instrument.h:391
SampleSelectionAlgo sample_selection_alg() const
Definition Instrument.h:646
void set_lower_cc(int message)
Definition Instrument.h:661
std::vector< std::shared_ptr< InstrumentComponent > > * get_components()
Definition Instrument.h:716
int get_id() const
Returns __id.
Definition Instrument.h:417
bool is_active() const
get the active status of the instrument
Definition Instrument.h:600
float getPan() const
get pan of the instrument
Definition Instrument.h:495
QString get_drumkit_path() const
set the name of the related drumkit
int get_midi_out_channel() const
get the midi out channel of the instrument
Definition Instrument.h:442
void set_muted(bool muted)
set muted status of the instrument
Definition Instrument.h:474
int __midi_out_note
midi out note
Definition Instrument.h:375
float get_volume() const
get the volume of the instrument
Definition Instrument.h:515
bool hasSamples() const
Whether the instrument contains at least one non-missing sample.
static std::shared_ptr< InstrumentComponent > loadInstrumentComponent(XMLNode *pNode, const QString &sDrumkitPath, const License &drumkitLicense, bool bSilent=false)
Backward compatibility code to load an InstrumentComponent from an Instrument which itself did not co...
Definition Legacy.cpp:48
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
Definition License.h:48
static std::shared_ptr< Sample > load(const QString &filepath, const License &license=License())
Definition Sample.cpp:136
static float getRatioPan(float fPan_L, float fPan_R)
This function is used to load old version files (v<=1.1).
Definition Sampler.cpp:252
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:151
bool read_bool(const QString &node, bool default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a boolean stored into a child node
Definition Xml.cpp:165
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:76
float read_float(const QString &node, float default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a float stored into a child node
Definition Xml.cpp:120
void write_float(const QString &node, const float value)
write a float into a child node
Definition Xml.cpp:261
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:44
void write_string(const QString &node, const QString &value)
write a string into a child node
Definition Xml.cpp:250
void write_bool(const QString &node, const bool value)
write a boolean 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:265
#define MAX_FX
Maximum number of effects.
Definition config.dox:83
#define MIDI_OUT_NOTE_MIN
Definition Globals.h:30
#define MIDI_OUT_NOTE_MAX
Definition Globals.h:31