hydrogen 1.2.6
Song.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include "Version.h"
24
25#include <cassert>
26#include <memory>
27
30
32#include <core/EventQueue.h>
33#include <core/FX/Effects.h>
34#include <core/Globals.h>
35#include <core/Timeline.h>
36#include <core/Basics/Song.h>
38#include <core/Basics/Sample.h>
43#include <core/Basics/Pattern.h>
45#include <core/Basics/Note.h>
48#include <core/Hydrogen.h>
49#include <core/Helpers/Future.h>
50#include <core/Helpers/Legacy.h>
53
54#ifdef H2CORE_HAVE_OSC
55#include <core/NsmClient.h>
56#endif
57
58#include <QDir>
59
60namespace
61{
62
63}//anonymous namespace
64namespace H2Core
65{
66
67Song::Song( const QString& sName, const QString& sAuthor, float fBpm, float fVolume )
68 : m_bIsTimelineActivated( false )
69 , m_bIsMuted( false )
71 , m_fBpm( fBpm )
72 , m_sName( sName )
73 , m_sAuthor( sAuthor )
74 , m_fVolume( fVolume )
75 , m_fMetronomeVolume( 0.5 )
76 , m_sNotes( "" )
77 , m_pPatternList( nullptr )
78 , m_pPatternGroupSequence( nullptr )
79 , m_sFilename( "" )
84 , m_fSwingFactor( 0.0 )
85 , m_bIsModified( false )
86 , m_mode( Mode::Pattern )
90 , m_pVelocityAutomationPath( nullptr )
91 , m_license( License( "", sAuthor ) )
94 , m_nPanLawType ( Sampler::RATIO_STRAIGHT_POLYGONAL )
95 , m_fPanLawKNorm ( Sampler::K_NORM_DEFAULT )
98{
99 INFOLOG( QString( "INIT '%1'" ).arg( sName ) );
100
101 m_pInstrumentList = std::make_shared<InstrumentList>();
102 m_pComponents = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
103
104 m_pVelocityAutomationPath = new AutomationPath(0.0f, 1.5f, 1.0f);
105
106 m_pTimeline = std::make_shared<Timeline>();
107}
108
110{
111 /*
112 * Warning: it is not safe to delete a song without having a lock on the audio engine.
113 * Following the current design, the caller has to care for the lock.
114 */
115
116 delete m_pPatternList;
117
119 for ( unsigned i = 0; i < m_pPatternGroupSequence->size(); ++i ) {
120 PatternList* pPatternList = ( *m_pPatternGroupSequence )[i];
121 pPatternList->clear(); // pulisco tutto, i pattern non vanno distrutti qua
122 delete pPatternList;
123 }
125 }
126
128
129 INFOLOG( QString( "DESTROY '%1'" ).arg( m_sName ) );
130}
131
132void Song::setBpm( float fBpm ) {
133 if ( fBpm > MAX_BPM ) {
134 m_fBpm = MAX_BPM;
135 WARNINGLOG( QString( "Provided bpm %1 is too high. Assigning upper bound %2 instead" )
136 .arg( fBpm ).arg( MAX_BPM ) );
137 } else if ( fBpm < MIN_BPM ) {
138 m_fBpm = MIN_BPM;
139 WARNINGLOG( QString( "Provided bpm %1 is too low. Assigning lower bound %2 instead" )
140 .arg( fBpm ).arg( MIN_BPM ) );
141 } else {
142 m_fBpm = fBpm;
143 }
144
145 if ( m_pTimeline != nullptr ) {
146 m_pTimeline->setDefaultBpm( m_fBpm );
147 }
148}
149
151 m_actionMode = actionMode;
152}
153
155 long nSongLength = 0;
156 int nColumns = m_pPatternGroupSequence->size();
157 // Sum the lengths of all pattern columns and use the macro
158 // MAX_NOTES in case some of them are of size zero.
159 for ( int i = 0; i < nColumns; i++ ) {
160 PatternList *pColumn = ( *m_pPatternGroupSequence )[ i ];
161 if ( pColumn->size() != 0 ) {
162 nSongLength += pColumn->longest_pattern_length();
163 } else {
164 nSongLength += MAX_NOTES;
165 }
166 }
167 return nSongLength;
168}
169
170bool Song::isPatternActive( int nColumn, int nRow ) const {
171 if ( nRow < 0 || nRow > m_pPatternList->size() ) {
172 return false;
173 }
174
175 auto pPattern = m_pPatternList->get( nRow );
176 if ( pPattern == nullptr ) {
177 return false;
178 }
179 if ( nColumn < 0 || nColumn >= m_pPatternGroupSequence->size() ) {
180 return false;
181 }
182 auto pColumn = ( *m_pPatternGroupSequence )[ nColumn ];
183 if ( pColumn->index( pPattern ) == -1 ) {
184 return false;
185 }
186
187 return true;
188}
189
191std::shared_ptr<Song> Song::load( const QString& sFilename, bool bSilent )
192{
193 QString sPath = Filesystem::absolute_path( sFilename, bSilent );
194 if ( sPath.isEmpty() ) {
195 return nullptr;
196 }
197
198 if ( ! bSilent ) {
199 INFOLOG( "Reading " + sPath );
200 }
201
202 XMLDoc doc;
203 if ( ! doc.read( sFilename ) && ! bSilent ) {
204 ERRORLOG( QString( "Something went wrong while loading song [%1]" )
205 .arg( sFilename ) );
206 }
207
208 XMLNode songNode = doc.firstChildElement( "song" );
209
210 if ( songNode.isNull() ) {
211 ERRORLOG( "Error reading song: 'song' node not found" );
212 return nullptr;
213 }
214
215 if ( ! bSilent ) {
216 QString sSongVersion = songNode.read_string( "version", "Unknown version", false, false );
217 if ( sSongVersion != QString( get_version().c_str() ) ) {
218 INFOLOG( QString( "Trying to load a song [%1] created with a different version [%2] of hydrogen. Current version: %3" )
219 .arg( sFilename )
220 .arg( sSongVersion )
221 .arg( get_version().c_str() ) );
222 }
223 }
224
225 auto pSong = Song::loadFrom( &songNode, sFilename, bSilent );
226 if ( pSong != nullptr ) {
227 pSong->setFilename( sFilename );
228 }
229
230 return pSong;
231}
232
233std::shared_ptr<Song> Song::loadFrom( XMLNode* pRootNode, const QString& sFilename, bool bSilent )
234{
235 auto pPreferences = Preferences::get_instance();
236
237 float fBpm = pRootNode->read_float( "bpm", 120, false, false, bSilent );
238 float fVolume = pRootNode->read_float( "volume", 0.5, false, false, bSilent );
239 QString sName( pRootNode->read_string( "name", "Untitled Song",
240 false, false, bSilent ) );
241 QString sAuthor( pRootNode->read_string( "author", "Unknown Author",
242 false, false, bSilent ) );
243
244 std::shared_ptr<Song> pSong = std::make_shared<Song>( sName, sAuthor, fBpm, fVolume );
245
246 pSong->setIsMuted( pRootNode->read_bool( "isMuted", false, true, false,
247 bSilent ) );
248 pSong->setMetronomeVolume( pRootNode->read_float( "metronomeVolume", 0.5,
249 false, false, bSilent ) );
250 pSong->setNotes( pRootNode->read_string( "notes", "...", false, false, bSilent ) );
251 pSong->setLicense( License( pRootNode->read_string( "license", "",
252 false, false, bSilent ), sAuthor ) );
253 if ( pRootNode->read_bool( "loopEnabled", false, false, false, bSilent ) ) {
254 pSong->setLoopMode( Song::LoopMode::Enabled );
255 } else {
256 pSong->setLoopMode( Song::LoopMode::Disabled );
257 }
258
259 if ( pRootNode->read_bool( "patternModeMode",
260 static_cast<bool>(Song::PatternMode::Selected),
261 false, false, bSilent ) ) {
262 pSong->setPatternMode( Song::PatternMode::Selected );
263 } else {
264 pSong->setPatternMode( Song::PatternMode::Stacked );
265 }
266
267 if ( pRootNode->read_string( "mode", "pattern", false, false, bSilent ) == "song" ) {
268 pSong->setMode( Song::Mode::Song );
269 } else {
270 pSong->setMode( Song::Mode::Pattern );
271 }
272
273 QString sPlaybackTrack( pRootNode->read_string( "playbackTrackFilename", "",
274 false, true, bSilent ) );
275 if ( sPlaybackTrack.left( 2 ) == "./" ||
276 sPlaybackTrack.left( 2 ) == ".\\" ) {
277 // Playback track has been made portable by manually
278 // converting the absolute path stored by Hydrogen into a
279 // relative one.
280 QFileInfo info( sFilename );
281 sPlaybackTrack = info.absoluteDir()
282 .filePath( sPlaybackTrack.right( sPlaybackTrack.size() - 2 ) );
283 }
284
285 // Check the file of the playback track and resort to the default
286 // in case the file can not be found.
287 if ( ! sPlaybackTrack.isEmpty() &&
288 ! Filesystem::file_exists( sPlaybackTrack, true ) ) {
289 ERRORLOG( QString( "Provided playback track file [%1] does not exist. Using empty string instead" )
290 .arg( sPlaybackTrack ) );
291 sPlaybackTrack = "";
292 }
293 pSong->setPlaybackTrackFilename( sPlaybackTrack );
294 pSong->setPlaybackTrackEnabled( pRootNode->read_bool( "playbackTrackEnabled", false,
295 false, false, bSilent ) );
296 pSong->setPlaybackTrackVolume( pRootNode->read_float( "playbackTrackVolume", 0.0,
297 false, false, bSilent ) );
298
299 pSong->setHumanizeTimeValue( pRootNode->read_float( "humanize_time", 0.0,
300 false, false, bSilent ) );
301 pSong->setHumanizeVelocityValue( pRootNode->read_float( "humanize_velocity", 0.0,
302 false, false, bSilent ) );
303 pSong->setSwingFactor( pRootNode->read_float( "swing_factor", 0.0, false, false, bSilent ) );
304 pSong->setActionMode( static_cast<Song::ActionMode>(
305 pRootNode->read_int( "action_mode",
306 static_cast<int>( Song::ActionMode::selectMode ),
307 false, false, bSilent ) ) );
308 pSong->setIsPatternEditorLocked( pRootNode->read_bool( "isPatternEditorLocked",
309 false, true, false, true ) );
310
311 bool bContainsIsTimelineActivated;
312 bool bIsTimelineActivated =
313 pRootNode->read_bool( "isTimelineActivated", false,
314 &bContainsIsTimelineActivated, true, false, true );
315 if ( ! bContainsIsTimelineActivated ) {
316 // .h2song file was created in an older version of
317 // Hydrogen. Using the Timeline state in the
318 // Preferences as a fallback.
319 bIsTimelineActivated = pPreferences->getUseTimelineBpm();
320 } else {
321 pPreferences->setUseTimelineBpm( bIsTimelineActivated );
322 }
323 pSong->setIsTimelineActivated( bIsTimelineActivated );
324
325 // pan law
326 QString sPanLawType( pRootNode->read_string( "pan_law_type",
327 "RATIO_STRAIGHT_POLYGONAL",
328 false, false, bSilent ) );
329 if ( sPanLawType == "RATIO_STRAIGHT_POLYGONAL" ) {
330 pSong->setPanLawType( Sampler::RATIO_STRAIGHT_POLYGONAL );
331 } else if ( sPanLawType == "RATIO_CONST_POWER" ) {
332 pSong->setPanLawType( Sampler::RATIO_CONST_POWER );
333 } else if ( sPanLawType == "RATIO_CONST_SUM" ) {
334 pSong->setPanLawType( Sampler::RATIO_CONST_SUM );
335 } else if ( sPanLawType == "LINEAR_STRAIGHT_POLYGONAL" ) {
336 pSong->setPanLawType( Sampler::LINEAR_STRAIGHT_POLYGONAL );
337 } else if ( sPanLawType == "LINEAR_CONST_POWER" ) {
338 pSong->setPanLawType( Sampler::LINEAR_CONST_POWER );
339 } else if ( sPanLawType == "LINEAR_CONST_SUM" ) {
340 pSong->setPanLawType( Sampler::LINEAR_CONST_SUM );
341 } else if ( sPanLawType == "POLAR_STRAIGHT_POLYGONAL" ) {
342 pSong->setPanLawType( Sampler::POLAR_STRAIGHT_POLYGONAL );
343 } else if ( sPanLawType == "POLAR_CONST_POWER" ) {
344 pSong->setPanLawType( Sampler::POLAR_CONST_POWER );
345 } else if ( sPanLawType == "POLAR_CONST_SUM" ) {
346 pSong->setPanLawType( Sampler::POLAR_CONST_SUM );
347 } else if ( sPanLawType == "QUADRATIC_STRAIGHT_POLYGONAL" ) {
348 pSong->setPanLawType( Sampler::QUADRATIC_STRAIGHT_POLYGONAL );
349 } else if ( sPanLawType == "QUADRATIC_CONST_POWER" ) {
350 pSong->setPanLawType( Sampler::QUADRATIC_CONST_POWER );
351 } else if ( sPanLawType == "QUADRATIC_CONST_SUM" ) {
352 pSong->setPanLawType( Sampler::QUADRATIC_CONST_SUM );
353 } else if ( sPanLawType == "LINEAR_CONST_K_NORM" ) {
354 pSong->setPanLawType( Sampler::LINEAR_CONST_K_NORM );
355 } else if ( sPanLawType == "POLAR_CONST_K_NORM" ) {
356 pSong->setPanLawType( Sampler::POLAR_CONST_K_NORM );
357 } else if ( sPanLawType == "RATIO_CONST_K_NORM" ) {
358 pSong->setPanLawType( Sampler::RATIO_CONST_K_NORM );
359 } else if ( sPanLawType == "QUADRATIC_CONST_K_NORM" ) {
360 pSong->setPanLawType( Sampler::QUADRATIC_CONST_K_NORM );
361 } else {
362 pSong->setPanLawType( Sampler::RATIO_STRAIGHT_POLYGONAL );
363 if ( ! bSilent ) {
364 WARNINGLOG( "Unknown pan law type in import song. Set default." );
365 }
366 }
367
368 float fPanLawKNorm = pRootNode->read_float( "pan_law_k_norm", Sampler::K_NORM_DEFAULT,
369 false, false, bSilent );
370 if ( fPanLawKNorm <= 0.0 ) {
371 if ( ! bSilent ) {
372 WARNINGLOG( QString( "Invalid pan law k in import song [%1] (<= 0). Set default k." )
373 .arg( fPanLawKNorm ) );
374 }
375 fPanLawKNorm = Sampler::K_NORM_DEFAULT;
376 }
377 pSong->setPanLawKNorm( fPanLawKNorm );
378
379 auto formatVersionNode = pRootNode->firstChildElement( "formatVersion" );
380 if ( ! formatVersionNode.isNull() ) {
381 WARNINGLOG( QString( "Song [%1] was created with a more recent version of Hydrogen than the current one!" )
382 .arg( sFilename ) );
383 XMLNode drumkitNode = pRootNode->firstChildElement( "drumkit_info" );
384 const auto pDrumkit = Future::loadDrumkit( drumkitNode, "", bSilent );
385 if ( pDrumkit != nullptr ) {
386 pSong->setInstrumentList( pDrumkit->get_instruments() );
387 pSong->getInstrumentList()->load_samples( fBpm );
388 pSong->m_pComponents = pDrumkit->get_components();
389 }
390 else {
391 ERRORLOG( "Unable to load drumkit contained in song" );
392
393 pSong->setInstrumentList( std::make_shared<InstrumentList>() );
394 pSong->m_pComponents =
395 std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
396 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0, "Main" );
397 pSong->getComponents()->push_back( pDrumkitComponent );
398 }
399 }
400 else {
401 XMLNode componentListNode = pRootNode->firstChildElement( "componentList" );
402 if ( ( ! componentListNode.isNull() ) ) {
403 XMLNode componentNode = componentListNode.firstChildElement( "drumkitComponent" );
404 while ( ! componentNode.isNull() ) {
405 auto pDrumkitComponent = DrumkitComponent::load_from(
406 &componentNode, nullptr );
407 if ( pDrumkitComponent != nullptr ) {
408 pSong->getComponents()->push_back( pDrumkitComponent );
409 }
410
411 componentNode = componentNode.nextSiblingElement( "drumkitComponent" );
412 }
413 }
414 else {
415 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0, "Main" );
416 pSong->getComponents()->push_back( pDrumkitComponent );
417 }
418
419 // Instrument List
420 //
421 // By supplying no drumkit path the individual drumkit meta infos
422 // stored in the 'instrument' nodes will be used.
423 auto pInstrumentList = InstrumentList::load_from(
424 pRootNode,
425 "", // sDrumkitPath
426 "", // sDrumkitName
427 License(), // per-instrument licenses
428 nullptr, // check for legacy format is not required in here.
429 bSilent );
430
431 if ( pInstrumentList == nullptr ) {
432 return nullptr;
433 }
434
435 pInstrumentList->load_samples( fBpm );
436 pSong->setInstrumentList( pInstrumentList );
437 }
438
439 QString sLastLoadedDrumkitPath =
440 pRootNode->read_string( "last_loaded_drumkit", "", true, false, true );
441 QString sLastLoadedDrumkitName =
442 pRootNode->read_string( "last_loaded_drumkit_name", "", true, false, true );
443
444#ifdef H2CORE_HAVE_APPIMAGE
445 sLastLoadedDrumkitPath =
446 Filesystem::rerouteDrumkitPath( sLastLoadedDrumkitPath );
447#endif
448
449 if ( sLastLoadedDrumkitPath.isEmpty() ) {
450 // Prior to version 1.2.0 the last loaded drumkit was read
451 // from the last instrument loaded and was not written to disk
452 // explicitly. This caused problems the moment the user put an
453 // instrument from a different drumkit at the end of the
454 // instrument list. To nevertheless retrieve the last loaded
455 // drumkit we will use a heuristic by taking the majority vote
456 // among the loaded instruments.
457 std::map<QString,int> loadedDrumkits;
458 for ( const auto& pInstrument : *pSong->getInstrumentList() ) {
459 if ( loadedDrumkits.find( pInstrument->get_drumkit_path() ) !=
460 loadedDrumkits.end() ) {
461 loadedDrumkits[ pInstrument->get_drumkit_path() ] += 1;
462 }
463 else {
464 loadedDrumkits[ pInstrument->get_drumkit_path() ] = 1;
465 }
466 }
467
468 QString sMostCommonDrumkit;
469 int nMax = -1;
470 for ( const auto& xx : loadedDrumkits ) {
471 if ( xx.second > nMax ) {
472 sMostCommonDrumkit = xx.first;
473 nMax = xx.second;
474 }
475 }
476
477 sLastLoadedDrumkitPath = sMostCommonDrumkit;
478 }
479 pSong->setLastLoadedDrumkitPath( sLastLoadedDrumkitPath );
480
481 // Attempt to access the last loaded drumkit to load it into the
482 // SoundLibraryDatabase in case it was a custom one (e.g. loaded
483 // via OSC or from a different system data folder due to a
484 // different install prefix).
485 auto pSoundLibraryDatabase = Hydrogen::get_instance()->getSoundLibraryDatabase();
486 auto pDrumkit = pSoundLibraryDatabase->getDrumkit( sLastLoadedDrumkitPath, true );
487
488 if ( sLastLoadedDrumkitName.isEmpty() ) {
489 // The initial song is loaded after Hydrogen was
490 // bootstrap. So, the SoundLibrary should be present and
491 // filled with all available drumkits.
492 if ( pSoundLibraryDatabase != nullptr && pDrumkit != nullptr ) {
493 sLastLoadedDrumkitName = pDrumkit->get_name();
494 }
495 }
496 pSong->setLastLoadedDrumkitName( sLastLoadedDrumkitName );
497
498 // Pattern list
499 pSong->setPatternList( PatternList::load_from( pRootNode,
500 pSong->getInstrumentList(),
501 bSilent ) );
502
503 // Virtual Patterns
504 pSong->loadVirtualPatternsFrom( pRootNode, bSilent );
505
506 // Pattern sequence
507 pSong->loadPatternGroupVectorFrom( pRootNode, bSilent );
508
509#ifdef H2CORE_HAVE_LADSPA
510 // reset FX
511 for ( int fx = 0; fx < MAX_FX; ++fx ) {
512 //LadspaFX* pFX = Effects::get_instance()->getLadspaFX( fx );
513 //delete pFX;
514 Effects::get_instance()->setLadspaFX( nullptr, fx );
515 }
516#endif
517
518 // LADSPA FX
519 XMLNode ladspaNode = pRootNode->firstChildElement( "ladspa" );
520 if ( ! ladspaNode.isNull() ) {
521 int nFX = 0;
522 XMLNode fxNode = ladspaNode.firstChildElement( "fx" );
523 while ( ! fxNode.isNull() ) {
524 QString sName = fxNode.read_string( "name", "", false, false, bSilent );
525
526 if ( sName != "no plugin" ) {
527 // FIXME: il caricamento va fatto fare all'engine, solo lui sa il samplerate esatto
528#ifdef H2CORE_HAVE_LADSPA
529 LadspaFX* pFX = LadspaFX::load( fxNode.read_string( "filename", "", false, false, bSilent ),
530 sName, 44100 );
531 Effects::get_instance()->setLadspaFX( pFX, nFX );
532 if ( pFX != nullptr ) {
533 pFX->setEnabled( fxNode.read_bool( "enabled", false, false, false, bSilent ) );
534 pFX->setVolume( fxNode.read_float( "volume", 1.0, false, false, bSilent ) );
535 XMLNode inputControlNode = fxNode.firstChildElement( "inputControlPort" );
536 while ( ! inputControlNode.isNull() ) {
537 QString sName = inputControlNode.read_string( "name", "", false, false, bSilent );
538 float fValue = inputControlNode.read_float( "value", 0.0, false, false, bSilent );
539
540 for ( unsigned nPort = 0; nPort < pFX->inputControlPorts.size(); nPort++ ) {
541 LadspaControlPort* port = pFX->inputControlPorts[ nPort ];
542 if ( QString( port->sName ) == sName ) {
543 port->fControlValue = fValue;
544 }
545 }
546 inputControlNode = inputControlNode.nextSiblingElement( "inputControlPort" );
547 }
548 }
549#endif
550 }
551 nFX++;
552 fxNode = fxNode.nextSiblingElement( "fx" );
553 }
554 } else if ( ! bSilent ) {
555 WARNINGLOG( "'ladspa' node not found" );
556 }
557
558 std::shared_ptr<Timeline> pTimeline = std::make_shared<Timeline>();
559 XMLNode bpmTimeLineNode = pRootNode->firstChildElement( "BPMTimeLine" );
560 if ( ! bpmTimeLineNode.isNull() ) {
561 XMLNode newBPMNode = bpmTimeLineNode.firstChildElement( "newBPM" );
562 while ( ! newBPMNode.isNull() ) {
563 pTimeline->addTempoMarker( newBPMNode.read_int( "BAR", 0, false, false, bSilent ),
564 newBPMNode.read_float( "BPM", 120.0, false, false, bSilent ) );
565 newBPMNode = newBPMNode.nextSiblingElement( "newBPM" );
566 }
567 } else if ( ! bSilent ) {
568 WARNINGLOG( "'BPMTimeLine' node not found" );
569 }
570
571 XMLNode timeLineTagNode = pRootNode->firstChildElement( "timeLineTag" );
572 if ( ! timeLineTagNode.isNull() ) {
573 XMLNode newTAGNode = timeLineTagNode.firstChildElement( "newTAG" );
574 while ( ! newTAGNode.isNull() ) {
575 pTimeline->addTag( newTAGNode.read_int( "BAR", 0, false, false, bSilent ),
576 newTAGNode.read_string( "TAG", "", false, false, bSilent ) );
577 newTAGNode = newTAGNode.nextSiblingElement( "newTAG" );
578 }
579 } else if ( ! bSilent ) {
580 WARNINGLOG( "TagTimeLine node not found" );
581 }
582 pSong->setTimeline( pTimeline );
583
584 // Automation Paths
585 XMLNode automationPathsNode = pRootNode->firstChildElement( "automationPaths" );
586 if ( ! automationPathsNode.isNull() ) {
587 AutomationPathSerializer pathSerializer;
588
589 XMLNode pathNode = automationPathsNode.firstChildElement( "path" );
590 while ( ! pathNode.isNull()) {
591 QString sAdjust = pathNode.read_attribute( "adjust", "velocity", false, false, bSilent );
592
593 // Select automation path to be read based on "adjust" attribute
594 AutomationPath *pPath = nullptr;
595 if ( sAdjust == "velocity" ) {
596 pPath = pSong->getVelocityAutomationPath();
597 }
598
599 if ( pPath ) {
600 pathSerializer.read_automation_path( pathNode, *pPath );
601 }
602
603 pathNode = pathNode.nextSiblingElement( "path" );
604 }
605 }
606
607 return pSong;
608}
609
611bool Song::save( const QString& sFilename, bool bSilent )
612{
613 QFileInfo fi( sFilename );
614 if ( ( Filesystem::file_exists( sFilename, true ) &&
615 ! Filesystem::file_writable( sFilename, true ) ) ||
616 ( ! Filesystem::file_exists( sFilename, true ) &&
617 ! Filesystem::dir_writable( fi.dir().absolutePath(), true ) ) ) {
618 // In case a read-only file is loaded by Hydrogen. Beware:
619 // .isWritable() will return false if the song does not exist.
620 ERRORLOG( QString( "Unable to save song to [%1]. Path is not writable!" )
621 .arg( sFilename ) );
622 return false;
623 }
624
625 if ( ! bSilent ) {
626 INFOLOG( QString( "Saving song to [%1]" ).arg( sFilename ) );
627 }
628
629 XMLDoc doc;
630 XMLNode rootNode = doc.set_root( "song" );
631
632 // In order to comply with the GPL license we have to add a
633 // license notice to the file.
634 if ( getLicense().getType() == License::GPL ) {
635 doc.appendChild( doc.createComment( License::getGPLLicenseNotice( getAuthor() ) ) );
636 }
637
638 writeTo( &rootNode, bSilent );
639
640 setFilename( sFilename );
641 setIsModified( false );
642
643 if ( ! doc.write( sFilename ) ) {
644 ERRORLOG( QString( "Error writing song to [%1]" ).arg( sFilename ) );
645 return false;
646 }
647
648 if ( ! bSilent ) {
649 INFOLOG("Save was successful.");
650 }
651
652 return true;
653}
654
655void Song::loadVirtualPatternsFrom( XMLNode* pNode, bool bSilent ) {
656
657 XMLNode virtualPatternListNode = pNode->firstChildElement( "virtualPatternList" );
658 if ( virtualPatternListNode.isNull() ) {
659 ERRORLOG( "'virtualPatternList' node not found. Aborting." );
660 return;
661 }
662
663 XMLNode virtualPatternNode = virtualPatternListNode.firstChildElement( "pattern" );
664 while ( ! virtualPatternNode.isNull() ) {
665 QString sName = virtualPatternNode.read_string( "name", sName, false, false, bSilent );
666
667 Pattern* pCurPattern = nullptr;
668 for ( const auto& pPattern : *m_pPatternList ) {
669 if ( pPattern->get_name() == sName ) {
670 pCurPattern = pPattern;
671 break;
672 }
673 }
674
675 if ( pCurPattern != nullptr ) {
676 XMLNode virtualNode = virtualPatternNode.firstChildElement( "virtual" );
677 while ( !virtualNode.isNull() ) {
678 QString sVirtualPatternName = virtualNode.firstChild().nodeValue();
679
680 Pattern* pVirtualPattern = nullptr;
681 for ( const auto& pPattern : *m_pPatternList ) {
682 if ( pPattern != nullptr &&
683 pPattern->get_name() == sVirtualPatternName ) {
684 pVirtualPattern = pPattern;
685 break;
686 }
687 }
688
689 if ( pVirtualPattern != nullptr ) {
690 pCurPattern->virtual_patterns_add( pVirtualPattern );
691 }
692 else if ( ! bSilent ) {
693 ERRORLOG( "Song had invalid virtual pattern list data (virtual)" );
694 }
695 virtualNode = virtualNode.nextSiblingElement( "virtual" );
696 }
697 }
698 else if ( ! bSilent ) {
699 ERRORLOG( "Song had invalid virtual pattern list data (name)" );
700 }
701 virtualPatternNode = virtualPatternNode.nextSiblingElement( "pattern" );
702 }
703
704 m_pPatternList->flattened_virtual_patterns_compute();
705}
706
707void Song::loadPatternGroupVectorFrom( XMLNode* pNode, bool bSilent ) {
708 XMLNode patternSequenceNode = pNode->firstChildElement( "patternSequence" );
709 if ( patternSequenceNode.isNull() ) {
710 if ( ! bSilent ) {
711 ERRORLOG( "'patternSequence' node not found. Aborting." );
712 }
713 return;
714 }
715
716 if ( ! patternSequenceNode.firstChildElement( "patternID" ).isNull() ) {
717 // back-compatibility code..
720 bSilent );
721 }
722 else {
723 // current format
724 if ( m_pPatternGroupSequence == nullptr ) {
725 m_pPatternGroupSequence = new std::vector<PatternList*>;
726 } else {
728 }
729
730 XMLNode groupNode = patternSequenceNode.firstChildElement( "group" );
731 while ( ! groupNode.isNull() ) {
732 PatternList* patternSequence = new PatternList();
733 XMLNode patternIdNode = groupNode.firstChildElement( "patternID" );
734 while ( ! patternIdNode.isNull() ) {
735 QString sPatternName = patternIdNode.firstChild().nodeValue();
736
737 Pattern* pPattern = nullptr;
738 for ( const auto& ppPat : *m_pPatternList ) {
739 if ( ppPat != nullptr ) {
740 if ( ppPat->get_name() == sPatternName ) {
741 pPattern = ppPat;
742 break;
743 }
744 }
745 }
746
747 if ( pPattern != nullptr ) {
748 patternSequence->add( pPattern );
749 } else if ( ! bSilent ) {
750 WARNINGLOG( "patternid not found in patternSequence" );
751 }
752
753 patternIdNode = patternIdNode.nextSiblingElement( "patternID" );
754 }
755 m_pPatternGroupSequence->push_back( patternSequence );
756
757 groupNode = groupNode.nextSiblingElement( "group" );
758 }
759 }
760}
761
762void Song::writeVirtualPatternsTo( XMLNode* pNode, bool bSilent ) {
763 XMLNode virtualPatternListNode = pNode->createNode( "virtualPatternList" );
764 for ( const auto& pPattern : *m_pPatternList ) {
765 if ( ! pPattern->get_virtual_patterns()->empty() ) {
766 XMLNode patternNode = virtualPatternListNode.createNode( "pattern" );
767 patternNode.write_string( "name", pPattern->get_name() );
768
769 for ( const auto& pVirtualPattern : *( pPattern->get_virtual_patterns() ) ) {
770 patternNode.write_string( "virtual", pVirtualPattern->get_name() );
771 }
772 }
773 }
774}
775
776void Song::writePatternGroupVectorTo( XMLNode* pNode, bool bSilent ) {
777 XMLNode patternSequenceNode = pNode->createNode( "patternSequence" );
778 for ( const auto& pPatternList : *m_pPatternGroupSequence ) {
779 if ( pPatternList != nullptr ) {
780 XMLNode groupNode = patternSequenceNode.createNode( "group" );
781
782 for ( const auto& pPattern : *pPatternList ) {
783 if ( pPattern != nullptr ) {
784 groupNode.write_string( "patternID", pPattern->get_name() );
785 }
786 }
787 }
788 }
789}
790
791void Song::writeTo( XMLNode* pRootNode, bool bSilent ) {
792 pRootNode->write_string( "version", QString( get_version().c_str() ) );
793 pRootNode->write_float( "bpm", m_fBpm );
794 pRootNode->write_float( "volume", m_fVolume );
795 pRootNode->write_bool( "isMuted", m_bIsMuted );
796 pRootNode->write_float( "metronomeVolume", m_fMetronomeVolume );
797 pRootNode->write_string( "name", m_sName );
798 pRootNode->write_string( "author", m_sAuthor );
799 pRootNode->write_string( "notes", m_sNotes );
800 pRootNode->write_string( "license", m_license.getLicenseString() );
801 pRootNode->write_bool( "loopEnabled", isLoopEnabled() );
802
803 bool bPatternMode = static_cast<bool>(Song::PatternMode::Selected);
805 bPatternMode = static_cast<bool>(Song::PatternMode::Stacked);
806 }
807 pRootNode->write_bool( "patternModeMode", bPatternMode );
808
809 pRootNode->write_string( "playbackTrackFilename", m_sPlaybackTrackFilename );
810 pRootNode->write_bool( "playbackTrackEnabled", m_bPlaybackTrackEnabled );
811 pRootNode->write_float( "playbackTrackVolume", m_fPlaybackTrackVolume );
812 pRootNode->write_int( "action_mode", static_cast<int>( m_actionMode ) );
813 pRootNode->write_bool( "isPatternEditorLocked",
815 pRootNode->write_bool( "isTimelineActivated", m_bIsTimelineActivated );
816
817 if ( m_mode == Song::Mode::Song ) {
818 pRootNode->write_string( "mode", QString( "song" ) );
819 } else {
820 pRootNode->write_string( "mode", QString( "pattern" ) );
821 }
822
823 QString sPanLawType; // prepare the pan law string to write
825 sPanLawType = "RATIO_STRAIGHT_POLYGONAL";
827 sPanLawType = "RATIO_CONST_POWER";
828 } else if ( m_nPanLawType == Sampler::RATIO_CONST_SUM ) {
829 sPanLawType = "RATIO_CONST_SUM";
831 sPanLawType = "LINEAR_STRAIGHT_POLYGONAL";
833 sPanLawType = "LINEAR_CONST_POWER";
835 sPanLawType = "LINEAR_CONST_SUM";
837 sPanLawType = "POLAR_STRAIGHT_POLYGONAL";
839 sPanLawType = "POLAR_CONST_POWER";
840 } else if ( m_nPanLawType == Sampler::POLAR_CONST_SUM ) {
841 sPanLawType = "POLAR_CONST_SUM";
843 sPanLawType = "QUADRATIC_STRAIGHT_POLYGONAL";
845 sPanLawType = "QUADRATIC_CONST_POWER";
847 sPanLawType = "QUADRATIC_CONST_SUM";
849 sPanLawType = "LINEAR_CONST_K_NORM";
851 sPanLawType = "POLAR_CONST_K_NORM";
853 sPanLawType = "RATIO_CONST_K_NORM";
855 sPanLawType = "QUADRATIC_CONST_K_NORM";
856 } else {
857 if ( ! bSilent ) {
858 WARNINGLOG( "Unknown pan law in saving song. Saved default type." );
859 }
860 sPanLawType = "RATIO_STRAIGHT_POLYGONAL";
861 }
862 // write the pan law string in file
863 pRootNode->write_string( "pan_law_type", sPanLawType );
864 pRootNode->write_float( "pan_law_k_norm", m_fPanLawKNorm );
865
866 pRootNode->write_float( "humanize_time", m_fHumanizeTimeValue );
867 pRootNode->write_float( "humanize_velocity", m_fHumanizeVelocityValue );
868 pRootNode->write_float( "swing_factor", m_fSwingFactor );
869
870 pRootNode->write_string( "last_loaded_drumkit", m_sLastLoadedDrumkitPath );
871 pRootNode->write_string( "last_loaded_drumkit_name", m_sLastLoadedDrumkitName );
872
873 XMLNode componentListNode = pRootNode->createNode( "componentList" );
874 for ( const auto& pComponent : *m_pComponents ) {
875 if ( pComponent != nullptr ) {
876 pComponent->save_to( &componentListNode );
877 }
878 }
879
880 m_pInstrumentList->save_to( pRootNode, -1, true, true );
881
882 m_pPatternList->save_to( pRootNode, nullptr );
883
884 writeVirtualPatternsTo( pRootNode, bSilent );
885
886 writePatternGroupVectorTo( pRootNode, bSilent );
887
888 XMLNode ladspaFxNode = pRootNode->createNode( "ladspa" );
889 for ( unsigned nFX = 0; nFX < MAX_FX; nFX++ ) {
890 XMLNode fxNode = ladspaFxNode.createNode( "fx" );
891
892#ifdef H2CORE_HAVE_LADSPA
894 if ( pFX != nullptr ) {
895 fxNode.write_string( "name", pFX->getPluginLabel() );
896 fxNode.write_string( "filename", pFX->getLibraryPath() );
897 fxNode.write_bool( "enabled", pFX->isEnabled() );
898 fxNode.write_float( "volume", pFX->getVolume() );
899
900 for ( unsigned nControl = 0; nControl < pFX->inputControlPorts.size(); nControl++ ) {
901 LadspaControlPort *pControlPort = pFX->inputControlPorts[ nControl ];
902 XMLNode controlPortNode = fxNode.createNode( "inputControlPort" );
903 controlPortNode.write_string( "name", pControlPort->sName );
904 controlPortNode.write_string( "value", QString("%1").arg( pControlPort->fControlValue ) );
905 }
906 for ( unsigned nControl = 0; nControl < pFX->outputControlPorts.size(); nControl++ ) {
907 LadspaControlPort *pControlPort = pFX->inputControlPorts[ nControl ];
908 XMLNode controlPortNode = fxNode.createNode( "outputControlPort" );
909 controlPortNode.write_string( "name", pControlPort->sName );
910 controlPortNode.write_string( "value", QString("%1").arg( pControlPort->fControlValue ) );
911 }
912 }
913#else
914 if ( false ) {
915 }
916#endif
917 else {
918 fxNode.write_string( "name", QString( "no plugin" ) );
919 fxNode.write_string( "filename", QString( "-" ) );
920 fxNode.write_bool( "enabled", false );
921 fxNode.write_float( "volume", 0.0 );
922 }
923 }
924
925 //bpm time line
926 auto pTimeline = Hydrogen::get_instance()->getTimeline();
927
928 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
929 XMLNode bpmTimeLineNode = pRootNode->createNode( "BPMTimeLine" );
930 if ( tempoMarkerVector.size() >= 1 ){
931 for ( int tt = 0; tt < static_cast<int>(tempoMarkerVector.size()); tt++){
932 if ( tt == 0 && pTimeline->isFirstTempoMarkerSpecial() ) {
933 continue;
934 }
935 XMLNode newBPMNode = bpmTimeLineNode.createNode( "newBPM" );
936 newBPMNode.write_int( "BAR", tempoMarkerVector[tt]->nColumn );
937 newBPMNode.write_float( "BPM", tempoMarkerVector[tt]->fBpm );
938 }
939 }
940
941 //time line tag
942 auto tagVector = pTimeline->getAllTags();
943 XMLNode timeLineTagNode = pRootNode->createNode( "timeLineTag" );
944 if ( tagVector.size() >= 1 ){
945 for ( int t = 0; t < static_cast<int>(tagVector.size()); t++){
946 XMLNode newTAGNode = timeLineTagNode.createNode( "newTAG" );
947 newTAGNode.write_int( "BAR", tagVector[t]->nColumn );
948 newTAGNode.write_string( "TAG", tagVector[t]->sTag );
949 }
950 }
951
952 // Automation Paths
953 XMLNode automationPathsNode = pRootNode->createNode( "automationPaths" );
955 if ( pPath != nullptr ) {
956 XMLNode pathNode = automationPathsNode.createNode( "path" );
957 pathNode.write_attribute("adjust", "velocity");
958
959 AutomationPathSerializer serializer;
960 serializer.write_automation_path(pathNode, *pPath);
961 }
962}
963
964std::shared_ptr<Song> Song::getEmptySong()
965{
966 std::shared_ptr<Song> pSong =
967 std::make_shared<Song>( Filesystem::untitled_song_name(), "hydrogen",
968 120, 0.5 );
969
970 pSong->setMetronomeVolume( 0.5 );
971 pSong->setNotes( "..." );
972 pSong->setLicense( License() );
973 pSong->setLoopMode( Song::LoopMode::Disabled );
974 pSong->setMode( Song::Mode::Pattern );
975 pSong->setHumanizeTimeValue( 0.0 );
976 pSong->setHumanizeVelocityValue( 0.0 );
977 pSong->setSwingFactor( 0.0 );
978
979 auto pInstrList = std::make_shared<InstrumentList>();
980 auto pNewInstr = std::make_shared<Instrument>( EMPTY_INSTR_ID, "New instrument" );
981 pInstrList->add( pNewInstr );
982 pSong->setInstrumentList( pInstrList );
983
984 PatternList* pPatternList = new PatternList();
985 PatternList* patternSequence = new PatternList();
986
987 for ( int nn = 0; nn < 10; ++nn ) {
988 Pattern* pEmptyPattern = new Pattern();
989
990 pEmptyPattern->set_name( QString( "Pattern %1" ).arg( nn + 1 ) );
991 pEmptyPattern->set_category( QString( "not_categorized" ) );
992 pPatternList->add( pEmptyPattern );
993
994 if ( nn == 0 ) {
995 // Only the first pattern will be activated in the
996 // SongEditor.
997 patternSequence->add( pEmptyPattern );
998 }
999 }
1000 pSong->setPatternList( pPatternList );
1001
1002 std::vector<PatternList*>* pPatternGroupVector = new std::vector<PatternList*>;
1003 pPatternGroupVector->push_back( patternSequence );
1004 pSong->setPatternGroupVector( pPatternGroupVector );
1005
1006 pSong->setFilename( Filesystem::empty_song_path() );
1007
1008 auto pSoundLibraryDatabase = Hydrogen::get_instance()->getSoundLibraryDatabase();
1009
1010 QString sDefaultDrumkitPath = Filesystem::drumkit_default_kit();
1011 auto pDrumkit = pSoundLibraryDatabase->getDrumkit( sDefaultDrumkitPath );
1012 if ( pDrumkit == nullptr ) {
1013 for ( const auto& pEntry : pSoundLibraryDatabase->getDrumkitDatabase() ) {
1014 if ( pEntry.second != nullptr ) {
1015 WARNINGLOG( QString( "Unable to retrieve default drumkit [%1]. Using kit [%2] instead." )
1016 .arg( sDefaultDrumkitPath )
1017 .arg( pEntry.first ) );
1018 pDrumkit = pEntry.second;
1019 break;
1020 }
1021 }
1022 }
1023
1024 if ( pDrumkit != nullptr ) {
1025 pSong->setDrumkit( pDrumkit, true );
1026 }
1027 else {
1028 ERRORLOG( "Unable to load drumkit" );
1029 }
1030
1031 pSong->setIsModified( false );
1032
1033 return pSong;
1034
1035}
1036
1037std::shared_ptr<DrumkitComponent> Song::getComponent( int nID ) const
1038{
1039 for ( auto pComponent : *m_pComponents ) {
1040 if ( pComponent->get_id() == nID ) {
1041 return pComponent;
1042 }
1043 }
1044
1045 return nullptr;
1046}
1047
1048
1049void Song::setSwingFactor( float factor )
1050{
1051 if ( factor < 0.0 ) {
1052 factor = 0.0;
1053 } else if ( factor > 1.0 ) {
1054 factor = 1.0;
1055 }
1056
1057 m_fSwingFactor = factor;
1058}
1059
1060void Song::setIsModified( bool bIsModified )
1061{
1062 bool Notify = false;
1063
1064 if( m_bIsModified != bIsModified ) {
1065 Notify = true;
1066 }
1067
1068 m_bIsModified = bIsModified;
1069
1070 if( Notify ) {
1072
1073 if ( Hydrogen::get_instance()->isUnderSessionManagement() ) {
1074 // If Hydrogen is under session management (NSM), tell the
1075 // NSM server that the Song was modified.
1076#ifdef H2CORE_HAVE_OSC
1077 NsmClient::get_instance()->sendDirtyState( bIsModified );
1078#endif
1079 }
1080 }
1081
1082}
1083
1085{
1086 auto pInstrumentList = getInstrumentList();
1087 for ( int i = 0; i < pInstrumentList->size(); i++ ) {
1088 if ( pInstrumentList->get( i )->has_missing_samples() ) {
1089 return true;
1090 }
1091 }
1092 return false;
1093}
1094
1096 auto pInstrumentList = getInstrumentList();
1097 for ( int i = 0; i < pInstrumentList->size(); i++ ) {
1098 pInstrumentList->get( i )->set_missing_samples( false );
1099 }
1100}
1101
1102void Song::readTempPatternList( const QString& sFilename )
1103{
1104 XMLDoc doc;
1105 if( !doc.read( sFilename ) ) {
1106 return;
1107 }
1108 XMLNode root = doc.firstChildElement( "sequence" );
1109 if ( root.isNull() ) {
1110 ERRORLOG( "sequence node not found" );
1111 return;
1112 }
1113
1114 loadVirtualPatternsFrom( &root, false );
1115 loadPatternGroupVectorFrom( &root, false );
1116}
1117
1118bool Song::writeTempPatternList( const QString& sFilename )
1119{
1120 XMLDoc doc;
1121 XMLNode root = doc.set_root( "sequence" );
1122
1123 writeVirtualPatternsTo( &root, false );
1124 writePatternGroupVectorTo( &root, false );
1125
1126 return doc.write( sFilename );
1127}
1128
1129QString Song::copyInstrumentLineToString( int nSelectedInstrument )
1130{
1131 auto pInstrument = getInstrumentList()->get( nSelectedInstrument );
1132 if ( pInstrument == nullptr ) {
1133 assert( pInstrument );
1134 ERRORLOG( QString( "Unable to retrieve instrument [%1]" )
1135 .arg( nSelectedInstrument ) );
1136 return QString();
1137 }
1138
1139 XMLDoc doc;
1140 XMLNode rootNode = doc.set_root( "instrument_line" );
1141 rootNode.write_string( "author", getAuthor() );
1142 rootNode.write_string( "license", getLicense().getLicenseString() );
1143
1144 m_pPatternList->save_to( &rootNode, pInstrument );
1145
1146 // Serialize document
1147 return doc.toString();
1148}
1149
1150bool Song::pasteInstrumentLineFromString( const QString& sSerialized, int nSelectedInstrument, std::list<Pattern *>& patterns )
1151{
1152 XMLDoc doc;
1153 if ( ! doc.setContent( sSerialized ) ) {
1154 return false;
1155 }
1156
1157 // Get current instrument
1158 auto pInstr = getInstrumentList()->get( nSelectedInstrument );
1159 assert( pInstr );
1160 if ( pInstr == nullptr ) {
1161 ERRORLOG( QString( "Unable to find instrument [%1]" )
1162 .arg( nSelectedInstrument ) );
1163 return false;
1164 }
1165
1166 // Get pattern list
1167 PatternList *pList = getPatternList();
1168 XMLNode patternNode;
1169 bool bIsNoteSelection = false;
1170 bool is_single = true;
1171
1172 // Check if document has correct structure
1173 XMLNode rootNode = doc.firstChildElement( "instrument_line" ); // root element
1174 if ( ! rootNode.isNull() ) {
1175 // Find pattern list
1176 XMLNode patternList = rootNode.firstChildElement( "patternList" );
1177 if ( patternList.isNull() ) {
1178 return false;
1179 }
1180
1181 // Parse each pattern if needed
1182 patternNode = patternList.firstChildElement( "pattern" );
1183 if ( ! patternNode.isNull() ) {
1184 is_single = (( XMLNode )patternNode.nextSiblingElement( "pattern" )).isNull();
1185 }
1186 }
1187 else {
1188 rootNode = doc.firstChildElement( "noteSelection" );
1189 if ( ! rootNode.isNull() ) {
1190 // Found a noteSelection. This contains a noteList, as a <pattern> does, so treat this as an anonymous pattern.
1191 bIsNoteSelection = true;
1192 is_single = true;
1193 patternNode = rootNode;
1194
1195 } else {
1196 ERRORLOG( "Error pasting Clipboard:instrument_line or noteSelection node not found ");
1197 return false;
1198 }
1199 }
1200
1201 while ( ! patternNode.isNull() )
1202 {
1203 QString patternName( patternNode.read_string( "name", "", false, false ) );
1204
1205 // Check if pattern name specified
1206 if ( patternName.length() > 0 || bIsNoteSelection ) {
1207 // Try to find pattern by name
1208 Pattern* pat = pList->find(patternName);
1209
1210 // If OK - check if need to add this pattern to result
1211 // If there is only one pattern, we always add it to list
1212 // If there is no selected pattern, we add all existing patterns to list (match by name)
1213 // Otherwise we add only existing selected pattern to list (match by name)
1214 if ( is_single || pat != nullptr ) {
1215
1216 pat = new Pattern( patternName,
1217 patternNode.read_string( "info", "", true, false ),
1218 patternNode.read_string( "category", "unknown", true, false ),
1219 patternNode.read_int( "size", -1, true, false ),
1220 patternNode.read_int( "denominator", 4, true, false ) );
1221
1222 // Parse pattern data
1223 XMLNode pNoteListNode = patternNode.firstChildElement( "noteList" );
1224 if ( ! pNoteListNode.isNull() )
1225 {
1226 // Parse note-by-note
1227 XMLNode noteNode = pNoteListNode.firstChildElement( "note" );
1228 while ( ! noteNode.isNull() )
1229 {
1230 XMLNode instrument = noteNode.firstChildElement( "instrument" );
1231 XMLNode instrumentText = instrument.firstChild();
1232
1233 instrumentText.setNodeValue( QString::number( pInstr->get_id() ) );
1234 Note *pNote = Note::load_from( &noteNode, getInstrumentList() );
1235
1236 pat->insert_note( pNote ); // Add note to created pattern
1237
1238 noteNode = noteNode.nextSiblingElement( "note" );
1239 }
1240 }
1241
1242 // Add loaded pattern to apply-list
1243 patterns.push_back(pat);
1244 }
1245 }
1246
1247 patternNode = patternNode.nextSiblingElement( "pattern" );
1248 }
1249
1250 return true;
1251}
1252
1253
1254void Song::setPanLawKNorm( float fKNorm ) {
1255 if ( fKNorm >= 0. ) {
1256 m_fPanLawKNorm = fKNorm;
1257 } else {
1258 WARNINGLOG("negative kNorm. Set default" );
1260 }
1261}
1262
1263void Song::setDrumkit( std::shared_ptr<Drumkit> pDrumkit, bool bConditional ) {
1264 auto pHydrogen = Hydrogen::get_instance();
1265
1266 assert ( pDrumkit );
1267 if ( pDrumkit == nullptr ) {
1268 ERRORLOG( "Invalid drumkit supplied" );
1269 return;
1270 }
1271
1272 m_sLastLoadedDrumkitName = pDrumkit->get_name();
1273 m_sLastLoadedDrumkitPath = pDrumkit->get_path();
1274
1275 // Load DrumkitComponents
1276 auto pDrumkitCompoList = pDrumkit->get_components();
1277
1278 auto pNewComponents = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
1279
1280 for ( const auto& pSrcComponent : *pDrumkitCompoList ) {
1281 auto pNewComponent = std::make_shared<DrumkitComponent>( pSrcComponent->get_id(),
1282 pSrcComponent->get_name() );
1283 pNewComponent->load_from( pSrcComponent );
1284
1285 pNewComponents->push_back( pNewComponent );
1286 }
1287 m_pComponents = pNewComponents;
1288
1290 // Load InstrumentList
1291 /*
1292 * If the old drumkit is bigger then the new drumkit,
1293 * delete all instruments with a bigger pos then
1294 * pDrumkitInstrList->size(). Otherwise the instruments
1295 * from our old instrumentlist with
1296 * pos > pDrumkitInstrList->size() stay in the
1297 * new instrumentlist
1298 */
1299 auto pDrumkitInstrList = pDrumkit->get_instruments();
1300
1301 if ( pDrumkitInstrList == m_pInstrumentList ) {
1302 // This occurs when saving a Drumkit based on the instrument
1303 // list of the current song using a different name. It stores
1304 // just a pointer to the instrument and component list of the
1305 // current song and will be set afterwards.
1306 return;
1307 }
1308
1309 int nInstrumentDiff = m_pInstrumentList->size() - pDrumkitInstrList->size();
1310 int nMaxID = -1;
1311
1312 std::shared_ptr<Instrument> pInstr, pNewInstr;
1313 for ( int nnInstr = 0; nnInstr < pDrumkitInstrList->size(); ++nnInstr ) {
1314 if ( nnInstr < m_pInstrumentList->size() ) {
1315 // Instrument exists already
1316 pInstr = m_pInstrumentList->get( nnInstr );
1317 assert( pInstr );
1318 } else {
1319 pInstr = std::make_shared<Instrument>();
1320 m_pInstrumentList->add( pInstr );
1321 }
1322
1323 pNewInstr = pDrumkitInstrList->get( nnInstr );
1324 assert( pNewInstr );
1325 INFOLOG( QString( "Loading instrument (%1 of %2) [%3]" )
1326 .arg( nnInstr + 1 )
1327 .arg( pDrumkitInstrList->size() )
1328 .arg( pNewInstr->get_name() ) );
1329
1330 // Preserve instrument IDs. Where the new drumkit has more
1331 // instruments than the song does, new instruments need new
1332 // ids.
1333 int nID = pInstr->get_id();
1334 if ( nID == EMPTY_INSTR_ID ) {
1335 nID = nMaxID + 1;
1336 }
1337 nMaxID = std::max( nID, nMaxID );
1338
1339 pInstr->load_from( pDrumkit, pNewInstr );
1340 pInstr->set_id( nID );
1341 }
1342
1343 // Discard redundant instruments (in case the last drumkit had
1344 // more instruments than the new one).
1345 if ( nInstrumentDiff >= 0 ) {
1346 for ( int i = 0; i < nInstrumentDiff ; i++ ){
1348 bConditional );
1349 }
1350 }
1351
1352 // Load samples of all instruments.
1353 m_pInstrumentList->load_samples(
1354 pHydrogen->getAudioEngine()->getTransportPosition()->getBpm() );
1355
1356 // Remap instruments in pattern list to ensure component indices for SelectedLayerInfo's are up to date
1357 // for the current kit.
1358 for ( auto &pPattern : *m_pPatternList ) {
1359 for ( auto &note : *pPattern->get_notes() ) {
1360 note.second->map_instrument( m_pInstrumentList );
1361 }
1362 }
1363
1364}
1365
1366void Song::removeInstrument( int nInstrumentNumber, bool bConditional ) {
1367 auto pHydrogen = Hydrogen::get_instance();
1368 auto pInstr = m_pInstrumentList->get( nInstrumentNumber );
1369 if ( pInstr == nullptr ) {
1370 // Error log is already printed by get().
1371 return;
1372 }
1373
1374 if ( bConditional ) {
1375 // If a note was assigned to this instrument in any pattern,
1376 // the instrument will be kept instead of discarded.
1377 for ( const auto& pPattern : *m_pPatternList ) {
1378 if ( pPattern->references( pInstr ) ) {
1379 INFOLOG("Keeping instrument #" + QString::number( nInstrumentNumber ) );
1380 return;
1381 }
1382 }
1383 } else {
1384 for ( const auto& pPattern : *m_pPatternList ) {
1385 pPattern->purge_instrument( pInstr, false );
1386 }
1387 }
1388
1389 // In case there is just this one instrument left, reset it
1390 // instead of removing it.
1391 if ( m_pInstrumentList->size() == 1 ){
1392 pInstr->set_name( (QString( "Instrument 1" )) );
1393 for ( auto& pCompo : *pInstr->get_components() ) {
1394 // remove all layers
1395 for ( int nLayer = 0; nLayer < InstrumentComponent::getMaxLayers(); nLayer++ ) {
1396 pCompo->set_layer( nullptr, nLayer );
1397 }
1398 }
1399 INFOLOG("clear last instrument to empty instrument 1 instead delete the last instrument");
1400 return;
1401 }
1402
1403 // delete the instrument from the instruments list
1404 m_pInstrumentList->del( nInstrumentNumber );
1405
1406 // At this point the instrument has been removed from both the
1407 // instrument list and every pattern in the song. Hence there's no way
1408 // (NOTE) to play on that instrument, and once all notes have stopped
1409 // playing it will be save to delete.
1410 // the ugly name is just for debugging...
1411 QString xxx_name = QString( "XXX_%1" ).arg( pInstr->get_name() );
1412 pInstr->set_name( xxx_name );
1413 pHydrogen->addInstrumentToDeathRow( pInstr );
1414}
1415
1416std::vector<std::shared_ptr<Note>> Song::getAllNotes() const {
1417
1418 std::vector<std::shared_ptr<Note>> notes;
1419
1420 long nColumnStartTick = 0;
1421 for ( int ii = 0; ii < m_pPatternGroupSequence->size(); ++ii ) {
1422
1423 auto pColumn = (*m_pPatternGroupSequence)[ ii ];
1424
1425 if ( pColumn->size() == 0 ) {
1426 // An empty column with no patterns selected (but not the
1427 // end of the song).
1428 nColumnStartTick += MAX_NOTES;
1429 continue;
1430 }
1431 else {
1432 for ( const auto& ppattern : *pColumn ) {
1433 if ( ppattern != nullptr ) {
1434 FOREACH_NOTE_CST_IT_BEGIN_LENGTH( ppattern->get_notes(), it, ppattern ) {
1435 if ( it->second != nullptr ) {
1436 // Use the copy constructor to not mess
1437 // with the song itself.
1438 std::shared_ptr<Note> pNote =
1439 std::make_shared<Note>( it->second );
1440
1441 // The position property of the note
1442 // specifies its position within the
1443 // pattern. All we need to do is to add
1444 // the pattern start tick.
1445 pNote->set_position( pNote->get_position() +
1446 nColumnStartTick );
1447 notes.push_back( pNote );
1448 }
1449 }
1450 }
1451 }
1452
1453 nColumnStartTick += pColumn->longest_pattern_length();
1454 }
1455 }
1456
1457 return notes;
1458}
1459
1460int Song::findExistingComponent( const QString& sComponentName ) const {
1461 for ( const auto& ppComponent : *m_pComponents ) {
1462 if ( ppComponent->get_name().compare( sComponentName ) == 0 ){
1463 return ppComponent->get_id();
1464 }
1465 }
1466 return -1;
1467}
1468
1469int Song::findFreeComponentID( int nStartingID ) const {
1470
1471 bool bFreeID = true;
1472
1473 for ( const auto& ppComponent : *m_pComponents ) {
1474 if ( ppComponent->get_id() == nStartingID ) {
1475 bFreeID = false;
1476 break;
1477 }
1478 }
1479
1480 if ( bFreeID ) {
1481 return nStartingID;
1482 }
1483 else {
1484 return findFreeComponentID( nStartingID + 1 );
1485 }
1486}
1487
1488QString Song::makeComponentNameUnique( const QString& sName ) const {
1489 for ( const auto& ppComponent : *m_pComponents ) {
1490 if ( ppComponent->get_name().compare( sName ) == 0 ){
1491 return makeComponentNameUnique( sName + "_new" );
1492 }
1493 }
1494 return sName;
1495}
1496
1501
1502QString Song::toQString( const QString& sPrefix, bool bShort ) const {
1503 QString s = Base::sPrintIndention;
1504 QString sOutput;
1505 if ( ! bShort ) {
1506 sOutput = QString( "%1[Song]\n" ).arg( sPrefix )
1507 .append( QString( "%1%2m_bIsTimelineActivated: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bIsTimelineActivated ) )
1508 .append( QString( "%1%2m_bIsMuted: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bIsMuted ) )
1509 .append( QString( "%1%2m_resolution: %3\n" ).arg( sPrefix ).arg( s ).arg( m_resolution ) )
1510 .append( QString( "%1%2m_fBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fBpm ) )
1511 .append( QString( "%1%2m_sName: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sName ) )
1512 .append( QString( "%1%2m_sAuthor: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sAuthor ) )
1513 .append( QString( "%1%2m_fVolume: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fVolume ) )
1514 .append( QString( "%1%2m_fMetronomeVolume: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMetronomeVolume ) )
1515 .append( QString( "%1%2m_sNotes: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sNotes ) )
1516 .append( QString( "%1" ).arg( m_pPatternList->toQString( sPrefix + s, bShort ) ) )
1517 .append( QString( "%1%2m_pPatternGroupSequence:\n" ).arg( sPrefix ).arg( s ) );
1518 for ( auto pp : *m_pPatternGroupSequence ) {
1519 if ( pp != nullptr ) {
1520 sOutput.append( QString( "%1" ).arg( pp->toQString( sPrefix + s + s, bShort ) ) );
1521 }
1522 }
1523 sOutput.append( QString( "%1" ).arg( m_pInstrumentList->toQString( sPrefix + s, bShort ) ) )
1524 .append( QString( "%1%2m_pComponents:\n" ).arg( sPrefix ).arg( s ) );
1525 for ( auto cc : *m_pComponents ) {
1526 if ( cc != nullptr ) {
1527 sOutput.append( QString( "%1" ).arg( cc->toQString( sPrefix + s + s ) ) );
1528 }
1529 }
1530 sOutput.append( QString( "%1%2m_sFilename: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sFilename ) )
1531 .append( QString( "%1%2m_loopMode: %3\n" ).arg( sPrefix ).arg( s ).arg( static_cast<int>(m_loopMode) ) )
1532 .append( QString( "%1%2m_fHumanizeTimeValue: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fHumanizeTimeValue ) )
1533 .append( QString( "%1%2m_fHumanizeVelocityValue: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fHumanizeVelocityValue ) )
1534 .append( QString( "%1%2m_fSwingFactor: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fSwingFactor ) )
1535 .append( QString( "%1%2m_bIsModified: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bIsModified ) )
1536 .append( QString( "%1%2m_latestRoundRobins\n" ).arg( sPrefix ).arg( s ) );
1537 for ( auto mm : m_latestRoundRobins ) {
1538 sOutput.append( QString( "%1%2%3 : %4\n" ).arg( sPrefix ).arg( s ).arg( mm.first ).arg( mm.second ) );
1539 }
1540 sOutput.append( QString( "%1%2m_songMode: %3\n" ).arg( sPrefix ).arg( s )
1541 .arg( static_cast<int>(m_mode )) )
1542 .append( QString( "%1%2m_sPlaybackTrackFilename: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sPlaybackTrackFilename ) )
1543 .append( QString( "%1%2m_bPlaybackTrackEnabled: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bPlaybackTrackEnabled ) )
1544 .append( QString( "%1%2m_fPlaybackTrackVolume: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fPlaybackTrackVolume ) )
1545 .append( QString( "%1" ).arg( m_pVelocityAutomationPath->toQString( sPrefix + s, bShort ) ) )
1546 .append( QString( "%1%2m_license: %3\n" ).arg( sPrefix ).arg( s ).arg( m_license.toQString( sPrefix + s, bShort ) ) )
1547 .append( QString( "%1%2m_actionMode: %3\n" ).arg( sPrefix ).arg( s )
1548 .arg( static_cast<int>(m_actionMode) ) )
1549 .append( QString( "%1%2m_nPanLawType: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nPanLawType ) )
1550 .append( QString( "%1%2m_fPanLawKNorm: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fPanLawKNorm ) )
1551 .append( QString( "%1%2m_pTimeline:\n" ).arg( sPrefix ).arg( s ) );
1552 if ( m_pTimeline != nullptr ) {
1553 sOutput.append( QString( "%1" ).arg( m_pTimeline->toQString( sPrefix + s, bShort ) ) );
1554 } else {
1555 sOutput.append( QString( "nullptr\n" ) );
1556 }
1557 sOutput.append( QString( "%1%2m_sLastLoadedDrumkitName: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sLastLoadedDrumkitName ) )
1558 .append( QString( "%1%2m_sLastLoadedDrumkitPath: %3\n" ).arg( sPrefix ).arg( s ).arg( m_sLastLoadedDrumkitPath ) );;
1559 } else {
1560
1561 sOutput = QString( "[Song]" )
1562 .append( QString( ", m_bIsTimelineActivated: %1" ).arg( m_bIsTimelineActivated ) )
1563 .append( QString( ", m_bIsMuted: %1" ).arg( m_bIsMuted ) )
1564 .append( QString( ", m_resolution: %1" ).arg( m_resolution ) )
1565 .append( QString( ", m_fBpm: %1" ).arg( m_fBpm ) )
1566 .append( QString( ", m_sName: %1" ).arg( m_sName ) )
1567 .append( QString( ", m_sAuthor: %1" ).arg( m_sAuthor ) )
1568 .append( QString( ", m_fVolume: %1" ).arg( m_fVolume ) )
1569 .append( QString( ", m_fMetronomeVolume: %1" ).arg( m_fMetronomeVolume ) )
1570 .append( QString( ", m_sNotes: %1" ).arg( m_sNotes ) )
1571 .append( QString( "%1" ).arg( m_pPatternList->toQString( sPrefix + s, bShort ) ) )
1572 .append( QString( ", m_pPatternGroupSequence:" ) );
1573 for ( auto pp : *m_pPatternGroupSequence ) {
1574 if ( pp != nullptr ) {
1575 sOutput.append( QString( "%1" ).arg( pp->toQString( sPrefix + s + s, bShort ) ) );
1576 }
1577 }
1578 sOutput.append( QString( "%1" ).arg( m_pInstrumentList->toQString( sPrefix + s, bShort ) ) )
1579 .append( QString( ", m_pComponents: [" ) );
1580 for ( auto cc : *m_pComponents ) {
1581 if ( cc != nullptr ) {
1582 sOutput.append( QString( "%1" ).arg( cc->toQString( sPrefix + s + s, bShort ) ) );
1583 }
1584 }
1585 sOutput.append( QString( "], m_sFilename: %1" ).arg( m_sFilename ) )
1586 .append( QString( ", m_loopMode: %1" ).arg( static_cast<int>(m_loopMode) ) )
1587 .append( QString( ", m_fHumanizeTimeValue: %1" ).arg( m_fHumanizeTimeValue ) )
1588 .append( QString( ", m_fHumanizeVelocityValue: %1" ).arg( m_fHumanizeVelocityValue ) )
1589 .append( QString( ", m_fSwingFactor: %1" ).arg( m_fSwingFactor ) )
1590 .append( QString( ", m_bIsModified: %1" ).arg( m_bIsModified ) )
1591 .append( QString( ", m_latestRoundRobins" ) );
1592 for ( auto mm : m_latestRoundRobins ) {
1593 sOutput.append( QString( ", %1 : %4" ).arg( mm.first ).arg( mm.second ) );
1594 }
1595 sOutput.append( QString( ", m_mode: %1" )
1596 .arg( static_cast<int>(m_mode) ) )
1597 .append( QString( ", m_sPlaybackTrackFilename: %1" ).arg( m_sPlaybackTrackFilename ) )
1598 .append( QString( ", m_bPlaybackTrackEnabled: %1" ).arg( m_bPlaybackTrackEnabled ) )
1599 .append( QString( ", m_fPlaybackTrackVolume: %1" ).arg( m_fPlaybackTrackVolume ) )
1600 .append( QString( ", m_pVelocityAutomationPath: %1" ).arg( m_pVelocityAutomationPath->toQString( sPrefix ) ) )
1601 .append( QString( ", m_license: %1" ).arg( m_license.toQString( sPrefix, bShort ) ) )
1602 .append( QString( ", m_actionMode: %1" ).arg( static_cast<int>(m_actionMode) ) )
1603 .append( QString( ", m_nPanLawType: %1" ).arg( m_nPanLawType ) )
1604 .append( QString( ", m_fPanLawKNorm: %1" ).arg( m_fPanLawKNorm ) )
1605 .append( QString( ", m_pTimeline: " ) );
1606 if ( m_pTimeline != nullptr ) {
1607 sOutput.append( QString( "%1" ).arg( m_pTimeline->toQString( sPrefix, bShort ) ) );
1608 } else {
1609 sOutput.append( QString( "nullptr" ) );
1610 }
1611 sOutput.append( QString( ", m_sLastLoadedDrumkitName: %1" ).arg( m_sLastLoadedDrumkitName ) )
1612 .append( QString( ", m_sLastLoadedDrumkitPath: %1" ).arg( m_sLastLoadedDrumkitPath ) );
1613
1614 }
1615
1616 return sOutput;
1617}
1618};
#define EMPTY_INSTR_ID
Definition Instrument.h:34
#define INFOLOG(x)
Definition Object.h:240
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
#define FOREACH_NOTE_CST_IT_BEGIN_LENGTH(_notes, _it, _pattern)
Iterate over all accessible notes between position 0 and length of _pattern in an immutable way.
Definition Pattern.h:285
void write_automation_path(QDomNode &node, const AutomationPath &path)
void read_automation_path(const QDomNode &node, AutomationPath &path)
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
static std::shared_ptr< DrumkitComponent > load_from(XMLNode *node, bool *pLegacyFormatEncountered=nullptr)
static Effects * get_instance()
Returns a pointer to the current Effects singleton stored in __instance.
Definition Effects.h:54
void setLadspaFX(LadspaFX *pFX, int nFX)
Definition Effects.cpp:99
LadspaFX * getLadspaFX(int nFX) const
Definition Effects.cpp:91
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
Definition EventQueue.h:224
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
static QString ensure_session_compatibility(const QString &sPath)
If Hydrogen is under session management, we support for paths relative to the session folder.
static QString absolute_path(const QString &sFilename, bool bSilent=false)
Convert a direct to an absolute path.
static bool file_writable(const QString &path, bool silent=false)
returns true if the given path is a possibly writable file (may exist or not)
static QString untitled_song_name()
returns untitled song name
static bool dir_writable(const QString &path, bool silent=false)
returns true if the given path is a writable regular directory
static QString rerouteDrumkitPath(const QString &sDrumkitPath)
Reroutes stored drumkit paths pointing to a temporary AppImage system data folder to the current AppI...
static bool file_exists(const QString &path, bool silent=false)
returns true if the given path is an existing regular file
static QString empty_song_path()
Provides the full path to the current empty song.
static QString drumkit_default_kit()
static std::shared_ptr< H2Core::Drumkit > loadDrumkit(XMLNode &node, const QString &sDrumkitPath, bool bSilent=false)
Definition Future.cpp:33
std::shared_ptr< Timeline > getTimeline() const
Definition Hydrogen.h:644
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< 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
LADSPA_Data fControlValue
Definition LadspaFX.h:106
std::vector< LadspaControlPort * > inputControlPorts
Definition LadspaFX.h:131
void setEnabled(bool bEnabled)
Definition LadspaFx.cpp:201
const QString & getLibraryPath() const
Definition LadspaFX.h:151
void setVolume(float fVolume)
Definition LadspaFx.cpp:472
const QString & getPluginLabel() const
Definition LadspaFX.h:142
std::vector< LadspaControlPort * > outputControlPorts
Definition LadspaFX.h:132
bool isEnabled() const
Definition LadspaFX.h:155
float getVolume() const
Definition LadspaFX.h:166
static LadspaFX * load(const QString &sLibraryPath, const QString &sPluginLabel, long nSampleRate)
Definition LadspaFx.cpp:211
static std::vector< PatternList * > * loadPatternGroupVector(XMLNode *pNode, PatternList *pPatternList, bool bSilent=false)
Definition Legacy.cpp:292
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
Definition License.h:48
@ GPL
Not a desirable license for audio data but introduced here specifically since it is already used by a...
Definition License.h:68
static QString getGPLLicenseNotice(const QString &sAuthor)
Definition License.h:194
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:101
static Note * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a note from an XMLNode
Definition Note.cpp:500
PatternList is a collection of patterns.
Definition PatternList.h:43
void add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
static PatternList * load_from(XMLNode *pNode, std::shared_ptr< InstrumentList > pInstrumentList, bool bSilent=false)
load a PatternList from an XMLNode
int longest_pattern_length(bool bIncludeVirtuals=true) const
Get the length of the longest pattern in the list.
Pattern * find(const QString &name)
find a pattern within the patterns
void clear()
empty the pattern list
int size() const
returns the numbers of patterns
void set_name(const QString &name)
get the name of the pattern
Definition Pattern.h:305
void virtual_patterns_add(Pattern *pattern)
add a pattern to __virtual_patterns
Definition Pattern.h:385
void set_category(const QString &category)
set the info of the pattern
Definition Pattern.h:325
void insert_note(Note *note)
insert a new note within __notes
Definition Pattern.h:370
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
Waveform based sampler.
Definition Sampler.h:51
static constexpr float K_NORM_DEFAULT
default k for pan law with -4.5dB center compensation, given L^k + R^k = const it is the mean comprom...
Definition Sampler.h:126
@ QUADRATIC_CONST_K_NORM
Definition Sampler.h:120
@ LINEAR_STRAIGHT_POLYGONAL
Definition Sampler.h:108
@ RATIO_STRAIGHT_POLYGONAL
Definition Sampler.h:105
@ POLAR_STRAIGHT_POLYGONAL
Definition Sampler.h:111
@ QUADRATIC_CONST_POWER
Definition Sampler.h:115
@ QUADRATIC_STRAIGHT_POLYGONAL
Definition Sampler.h:114
bool m_bIsTimelineActivated
Whether the Timeline button was pressed in the GUI or it was activated via an OSC command.
Definition Song.h:313
void setPanLawKNorm(float fKNorm)
Definition Song.cpp:1254
std::shared_ptr< Timeline > m_pTimeline
Definition Song.h:423
void loadVirtualPatternsFrom(XMLNode *pNode, bool bSilent=false)
Definition Song.cpp:655
std::shared_ptr< InstrumentList > getInstrumentList() const
Definition Song.h:525
PatternList * getPatternList() const
Definition Song.h:535
int m_nPanLawType
Definition Song.h:418
QString m_sName
author of the song
Definition Song.h:327
QString m_sFilename
Definition Song.h:344
bool save(const QString &sFilename, bool bSilent=false)
Save a song to file.
Definition Song.cpp:611
bool isLoopEnabled() const
Definition Song.h:599
void setDrumkit(std::shared_ptr< Drumkit > pDrumkit, bool bConditional)
Definition Song.cpp:1263
ActionMode m_actionMode
Stores the type of interaction with the SongEditor.
Definition Song.h:405
PatternList * m_pPatternList
Sequence of pattern groups.
Definition Song.h:337
static constexpr int nDefaultResolution
Definition Song.h:135
std::map< float, int > m_latestRoundRobins
Definition Song.h:370
float m_fVolume
Metronome volume.
Definition Song.h:332
QString getLastLoadedDrumkitPath() const
Definition Song.cpp:1497
bool writeTempPatternList(const QString &sFilename)
Definition Song.cpp:1118
QString m_sAuthor
volume of the song (0.0..1.0)
Definition Song.h:329
void readTempPatternList(const QString &sFilename)
Definition Song.cpp:1102
static std::shared_ptr< Song > load(const QString &sFilename, bool bSilent=false)
Load a song from file.
Definition Song.cpp:191
bool hasMissingSamples() const
Song was incompletely loaded from file (missing samples)
Definition Song.cpp:1084
void setBpm(float fBpm)
Definition Song.cpp:132
long lengthInTicks() const
get the length of the song, in tick units
Definition Song.cpp:154
LoopMode m_loopMode
The three states of this enum is just a way to handle the playback within Hydrogen.
Definition Song.h:352
ActionMode
Defines the type of user interaction experienced in the SongEditor.
Definition Song.h:74
@ selectMode
Holding a pressed left mouse key will draw a rectangle to select a group of Notes.
Definition Song.h:77
float m_fBpm
Current speed in beats per minutes.
Definition Song.h:324
bool pasteInstrumentLineFromString(const QString &sSerialized, int nSelectedInstrument, std::list< Pattern * > &patterns)
Definition Song.cpp:1150
bool isPatternActive(int nColumn, int nRow) const
Definition Song.cpp:170
std::shared_ptr< DrumkitComponent > getComponent(int nID) const
Definition Song.cpp:1037
void clearMissingSamples()
Definition Song.cpp:1095
QString makeComponentNameUnique(const QString &sComponentName) const
Ensures sComponentName is not used by any other component loaded into the song yet.
Definition Song.cpp:1488
const QString & getAuthor() const
Definition Song.h:584
std::shared_ptr< InstrumentList > m_pInstrumentList
list of drumkit component
Definition Song.h:341
void writeTo(XMLNode *pNode, bool bSilent=false)
Definition Song.cpp:791
void writePatternGroupVectorTo(XMLNode *pNode, bool bSilent=false)
Definition Song.cpp:776
bool m_bIsModified
Definition Song.h:369
void removeInstrument(int nInstrumentNumber, bool bConditional)
Definition Song.cpp:1366
void setActionMode(const ActionMode actionMode)
Definition Song.cpp:150
float m_fPanLawKNorm
Definition Song.h:420
AutomationPath * m_pVelocityAutomationPath
license of the song
Definition Song.h:400
void loadPatternGroupVectorFrom(XMLNode *pNode, bool bSilent=false)
Definition Song.cpp:707
QString m_sNotes
Pattern list.
Definition Song.h:335
int findFreeComponentID(int nStartingID=0) const
Definition Song.cpp:1469
void setFilename(const QString &sFilename)
Definition Song.h:594
License m_license
Definition Song.h:402
void setSwingFactor(float fFactor)
Definition Song.cpp:1049
float m_fPlaybackTrackVolume
Volume of the playback track.
Definition Song.h:399
std::shared_ptr< std::vector< std::shared_ptr< DrumkitComponent > > > m_pComponents
Definition Song.h:343
std::vector< PatternList * > * m_pPatternGroupSequence
Instrument list.
Definition Song.h:339
static std::shared_ptr< Song > getEmptySong()
Definition Song.cpp:964
PatternMode m_patternMode
Definition Song.h:353
void setIsModified(bool bIsModified)
Definition Song.cpp:1060
float m_fSwingFactor
Definition Song.h:368
bool m_bIsMuted
Resolution of the song (number of ticks per quarter)
Definition Song.h:315
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Song.cpp:1502
bool m_bIsPatternEditorLocked
If set to true, the user won't be able to select a pattern via the SongEditor.
Definition Song.h:416
PatternMode
Determines how patterns will be added to AudioEngine::m_pPlayingPatterns if transport is in Song::Mod...
Definition Song.h:101
@ Stacked
An arbitrary number of pattern can be played.
Definition Song.h:103
@ Selected
Only one pattern - the one currently selected in the GUI - will be played back.
Definition Song.h:106
const License & getLicense() const
Definition Song.h:574
float m_fHumanizeVelocityValue
Factor to scale the random contribution when humanizing velocity between 0 and AudioEngine::fHumanize...
Definition Song.h:367
AutomationPath * getVelocityAutomationPath() const
Definition Song.h:663
float m_fHumanizeTimeValue
Factor to scale the random contribution when humanizing timing between 0 and AudioEngine::fHumanizeTi...
Definition Song.h:360
QString copyInstrumentLineToString(int selectedInstrument)
Definition Song.cpp:1129
QString m_sLastLoadedDrumkitName
Convenience variable holding the name of the drumkit last loaded.
Definition Song.h:442
Mode m_mode
Definition Song.h:371
unsigned m_resolution
Definition Song.h:317
float m_fMetronomeVolume
Definition Song.h:334
std::vector< std::shared_ptr< Note > > getAllNotes() const
Definition Song.cpp:1416
void writeVirtualPatternsTo(XMLNode *pNode, bool bSilent=false)
Definition Song.cpp:762
int findExistingComponent(const QString &sComponentName) const
Checks whether a component of name sComponentName exists in m_pComponents.
Definition Song.cpp:1460
static std::shared_ptr< Song > loadFrom(XMLNode *pNode, const QString &sFilename, bool bSilent=false)
Definition Song.cpp:233
QString m_sPlaybackTrackFilename
Name of the file to be loaded as playback track.
Definition Song.h:381
bool m_bPlaybackTrackEnabled
Whether the playback track should be used at all.
Definition Song.h:390
QString m_sLastLoadedDrumkitPath
Unique identifier of the drumkit last loaded.
Definition Song.h:439
std::shared_ptr< Drumkit > getDrumkit(const QString &sDrumkitPath, bool bLoad=true)
Retrieve a drumkit from the database.
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:336
bool read(const QString &filepath, bool bSilent=false)
read the content of an xml file
Definition Xml.cpp:277
bool write(const QString &filepath)
write itself into a file
Definition Xml.cpp:311
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
void write_attribute(const QString &attribute, const QString &value)
write a string as an attribute of the node
Definition Xml.cpp:237
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_attribute(const QString &attribute, const QString &default_value, bool inexistent_ok, bool empty_ok, bool bSilent=false)
reads an attribute from the node
Definition Xml.cpp:211
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
void sendDirtyState(const bool isDirty)
Informs the NSM server whether the current H2Core::Song is modified or not.
static NsmClient * get_instance()
Definition NsmClient.h:84
#define MAX_NOTES
Maximum number of notes.
Definition config.dox:79
#define MAX_FX
Maximum number of effects.
Definition config.dox:83
#define MAX_BPM
Definition Globals.h:36
#define MIN_BPM
Definition Globals.h:35
@ EVENT_SONG_MODIFIED
Definition EventQueue.h:102
std::string get_version()
Returns the current Hydrogen version string.
Definition Version.cpp:30