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