67Song::Song(
const QString& sName,
const QString& sAuthor,
float fBpm,
float fVolume )
68 : m_bIsTimelineActivated( false )
73 , m_sAuthor( sAuthor )
74 , m_fVolume( fVolume )
75 , m_fMetronomeVolume( 0.5 )
77 , m_pPatternList( nullptr )
78 , m_pPatternGroupSequence( nullptr )
82 , m_fHumanizeTimeValue( 0.0 )
83 , m_fHumanizeVelocityValue( 0.0 )
84 , m_fSwingFactor( 0.0 )
85 , m_bIsModified( false )
87 , m_sPlaybackTrackFilename(
"" )
88 , m_bPlaybackTrackEnabled( false )
89 , m_fPlaybackTrackVolume( 0.0 )
90 , m_pVelocityAutomationPath( nullptr )
91 , m_license(
License(
"", sAuthor ) )
93 , m_bIsPatternEditorLocked( false )
94 , m_nPanLawType (
Sampler::RATIO_STRAIGHT_POLYGONAL )
95 , m_fPanLawKNorm (
Sampler::K_NORM_DEFAULT )
96 , m_sLastLoadedDrumkitName(
"" )
97 , m_sLastLoadedDrumkitPath(
"" )
99 INFOLOG( QString(
"INIT '%1'" ).arg( sName ) );
102 m_pComponents = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
120 PatternList* pPatternList = ( *m_pPatternGroupSequence )[i];
121 pPatternList->
clear();
135 WARNINGLOG( QString(
"Provided bpm %1 is too high. Assigning upper bound %2 instead" )
139 WARNINGLOG( QString(
"Provided bpm %1 is too low. Assigning lower bound %2 instead" )
151 long nSongLength = 0;
155 for (
int i = 0; i < nColumns; i++ ) {
156 PatternList *pColumn = ( *m_pPatternGroupSequence )[ i ];
157 if ( pColumn->
size() != 0 ) {
172 if ( pPattern ==
nullptr ) {
178 auto pColumn = ( *m_pPatternGroupSequence )[ nColumn ];
179 if ( pColumn->index( pPattern ) == -1 ) {
187std::shared_ptr<Song>
Song::load(
const QString& sFilename,
bool bSilent )
190 if ( sPath.isEmpty() ) {
199 if ( ! doc.
read( sFilename ) && ! bSilent ) {
200 ERRORLOG( QString(
"Something went wrong while loading song [%1]" )
204 XMLNode songNode = doc.firstChildElement(
"song" );
206 if ( songNode.isNull() ) {
207 ERRORLOG(
"Error reading song: 'song' node not found" );
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" )
222 if ( pSong !=
nullptr ) {
223 pSong->setFilename( sFilename );
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 ) );
240 std::shared_ptr<Song> pSong = std::make_shared<Song>( sName, sAuthor, fBpm, fVolume );
242 pSong->setIsMuted( pRootNode->
read_bool(
"isMuted",
false,
true,
false,
244 pSong->setMetronomeVolume( pRootNode->
read_float(
"metronomeVolume", 0.5,
245 false,
false, bSilent ) );
246 pSong->setNotes( pRootNode->
read_string(
"notes",
"...",
false,
false, bSilent ) );
248 false,
false, bSilent ), sAuthor ) );
249 if ( pRootNode->
read_bool(
"loopEnabled",
false,
false,
false, bSilent ) ) {
255 if ( pRootNode->
read_bool(
"patternModeMode",
257 false,
false, bSilent ) ) {
263 if ( pRootNode->
read_string(
"mode",
"pattern",
false,
false, bSilent ) ==
"song" ) {
269 QString sPlaybackTrack( pRootNode->
read_string(
"playbackTrackFilename",
"",
270 false,
true, bSilent ) );
271 if ( sPlaybackTrack.left( 2 ) ==
"./" ||
272 sPlaybackTrack.left( 2 ) ==
".\\" ) {
276 QFileInfo info( sFilename );
277 sPlaybackTrack = info.absoluteDir()
278 .filePath( sPlaybackTrack.right( sPlaybackTrack.size() - 2 ) );
283 if ( ! sPlaybackTrack.isEmpty() &&
285 ERRORLOG( QString(
"Provided playback track file [%1] does not exist. Using empty string instead" )
286 .arg( sPlaybackTrack ) );
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 ) );
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 ) );
303 false,
false, bSilent ) ) );
304 pSong->setIsPatternEditorLocked( pRootNode->
read_bool(
"isPatternEditorLocked",
305 false,
true,
false,
true ) );
307 bool bContainsIsTimelineActivated;
308 bool bIsTimelineActivated =
309 pRootNode->
read_bool(
"isTimelineActivated",
false,
310 &bContainsIsTimelineActivated,
true,
false,
true );
311 if ( ! bContainsIsTimelineActivated ) {
315 bIsTimelineActivated = pPreferences->getUseTimelineBpm();
317 pPreferences->setUseTimelineBpm( bIsTimelineActivated );
319 pSong->setIsTimelineActivated( bIsTimelineActivated );
322 QString sPanLawType( pRootNode->
read_string(
"pan_law_type",
323 "RATIO_STRAIGHT_POLYGONAL",
324 false,
false, bSilent ) );
325 if ( sPanLawType ==
"RATIO_STRAIGHT_POLYGONAL" ) {
327 }
else if ( sPanLawType ==
"RATIO_CONST_POWER" ) {
329 }
else if ( sPanLawType ==
"RATIO_CONST_SUM" ) {
331 }
else if ( sPanLawType ==
"LINEAR_STRAIGHT_POLYGONAL" ) {
333 }
else if ( sPanLawType ==
"LINEAR_CONST_POWER" ) {
335 }
else if ( sPanLawType ==
"LINEAR_CONST_SUM" ) {
337 }
else if ( sPanLawType ==
"POLAR_STRAIGHT_POLYGONAL" ) {
339 }
else if ( sPanLawType ==
"POLAR_CONST_POWER" ) {
341 }
else if ( sPanLawType ==
"POLAR_CONST_SUM" ) {
343 }
else if ( sPanLawType ==
"QUADRATIC_STRAIGHT_POLYGONAL" ) {
345 }
else if ( sPanLawType ==
"QUADRATIC_CONST_POWER" ) {
347 }
else if ( sPanLawType ==
"QUADRATIC_CONST_SUM" ) {
349 }
else if ( sPanLawType ==
"LINEAR_CONST_K_NORM" ) {
351 }
else if ( sPanLawType ==
"POLAR_CONST_K_NORM" ) {
353 }
else if ( sPanLawType ==
"RATIO_CONST_K_NORM" ) {
355 }
else if ( sPanLawType ==
"QUADRATIC_CONST_K_NORM" ) {
360 WARNINGLOG(
"Unknown pan law type in import song. Set default." );
365 false,
false, bSilent );
366 if ( fPanLawKNorm <= 0.0 ) {
368 WARNINGLOG( QString(
"Invalid pan law k in import song [%1] (<= 0). Set default k." )
369 .arg( fPanLawKNorm ) );
373 pSong->setPanLawKNorm( fPanLawKNorm );
375 XMLNode drumkitNode = pRootNode->firstChildElement(
"drumkit_info" );
376 if ( ! drumkitNode.isNull() ) {
380 pSong->m_pComponents = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>(pDrumkitComponents);
383 XMLNode componentListNode = pRootNode->firstChildElement(
"componentList" );
384 if ( ( ! componentListNode.isNull() ) ) {
385 XMLNode componentNode = componentListNode.firstChildElement(
"drumkitComponent" );
386 while ( ! componentNode.isNull() ) {
388 if ( pDrumkitComponent !=
nullptr ) {
389 pSong->getComponents()->push_back( pDrumkitComponent );
392 componentNode = componentNode.nextSiblingElement(
"drumkitComponent" );
396 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0,
"Main" );
397 pSong->getComponents()->push_back( pDrumkitComponent );
402 std::shared_ptr<InstrumentList> pInstrumentList;
403 if ( ! drumkitNode.isNull() ) {
421 if ( pInstrumentList ==
nullptr ) {
425 pInstrumentList->load_samples( fBpm );
426 pSong->setInstrumentList( pInstrumentList );
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 );
433#ifdef H2CORE_HAVE_APPIMAGE
434 sLastLoadedDrumkitPath =
438 if ( sLastLoadedDrumkitPath.isEmpty() ) {
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;
453 loadedDrumkits[ pInstrument->get_drumkit_path() ] = 1;
457 QString sMostCommonDrumkit;
459 for (
const auto& xx : loadedDrumkits ) {
460 if ( xx.second > nMax ) {
461 sMostCommonDrumkit = xx.first;
466 sLastLoadedDrumkitPath = sMostCommonDrumkit;
468 pSong->setLastLoadedDrumkitPath( sLastLoadedDrumkitPath );
475 auto pDrumkit = pSoundLibraryDatabase->
getDrumkit( sLastLoadedDrumkitPath,
true );
477 if ( sLastLoadedDrumkitName.isEmpty() ) {
481 if ( pSoundLibraryDatabase !=
nullptr && pDrumkit !=
nullptr ) {
482 sLastLoadedDrumkitName = pDrumkit->get_name();
485 pSong->setLastLoadedDrumkitName( sLastLoadedDrumkitName );
489 pSong->getInstrumentList(),
493 pSong->loadVirtualPatternsFrom( pRootNode, bSilent );
496 pSong->loadPatternGroupVectorFrom( pRootNode, bSilent );
498#ifdef H2CORE_HAVE_LADSPA
500 for (
int fx = 0; fx <
MAX_FX; ++fx ) {
508 XMLNode ladspaNode = pRootNode->firstChildElement(
"ladspa" );
509 if ( ! ladspaNode.isNull() ) {
511 XMLNode fxNode = ladspaNode.firstChildElement(
"fx" );
512 while ( ! fxNode.isNull() ) {
513 QString sName = fxNode.
read_string(
"name",
"",
false,
false, bSilent );
515 if ( sName !=
"no plugin" ) {
517#ifdef H2CORE_HAVE_LADSPA
521 if ( pFX !=
nullptr ) {
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 );
531 if ( QString( port->
sName ) == sName ) {
535 inputControlNode = inputControlNode.nextSiblingElement(
"inputControlPort" );
541 fxNode = fxNode.nextSiblingElement(
"fx" );
543 }
else if ( ! bSilent ) {
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" );
556 }
else if ( ! bSilent ) {
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" );
568 }
else if ( ! bSilent ) {
571 pSong->setTimeline( pTimeline );
574 XMLNode automationPathsNode = pRootNode->firstChildElement(
"automationPaths" );
575 if ( ! automationPathsNode.isNull() ) {
578 XMLNode pathNode = automationPathsNode.firstChildElement(
"path" );
579 while ( ! pathNode.isNull()) {
580 QString sAdjust = pathNode.
read_attribute(
"adjust",
"velocity",
false,
false, bSilent );
584 if ( sAdjust ==
"velocity" ) {
585 pPath = pSong->getVelocityAutomationPath();
592 pathNode = pathNode.nextSiblingElement(
"path" );
602 QFileInfo fi( sFilename );
609 ERRORLOG( QString(
"Unable to save song to [%1]. Path is not writable!" )
615 INFOLOG( QString(
"Saving song to [%1]" ).arg( sFilename ) );
632 if ( ! doc.
write( sFilename ) ) {
633 ERRORLOG( QString(
"Error writing song to [%1]" ).arg( sFilename ) );
638 INFOLOG(
"Save was successful.");
646 XMLNode virtualPatternListNode = pNode->firstChildElement(
"virtualPatternList" );
647 if ( virtualPatternListNode.isNull() ) {
648 ERRORLOG(
"'virtualPatternList' node not found. Aborting." );
652 XMLNode virtualPatternNode = virtualPatternListNode.firstChildElement(
"pattern" );
653 while ( ! virtualPatternNode.isNull() ) {
654 QString sName = virtualPatternNode.
read_string(
"name", sName,
false,
false, bSilent );
656 Pattern* pCurPattern =
nullptr;
658 if ( pPattern->get_name() == sName ) {
659 pCurPattern = pPattern;
664 if ( pCurPattern !=
nullptr ) {
665 XMLNode virtualNode = virtualPatternNode.firstChildElement(
"virtual" );
666 while ( !virtualNode.isNull() ) {
667 QString sVirtualPatternName = virtualNode.firstChild().nodeValue();
669 Pattern* pVirtualPattern =
nullptr;
671 if ( pPattern !=
nullptr &&
672 pPattern->get_name() == sVirtualPatternName ) {
673 pVirtualPattern = pPattern;
678 if ( pVirtualPattern !=
nullptr ) {
681 else if ( ! bSilent ) {
682 ERRORLOG(
"Song had invalid virtual pattern list data (virtual)" );
684 virtualNode = virtualNode.nextSiblingElement(
"virtual" );
687 else if ( ! bSilent ) {
688 ERRORLOG(
"Song had invalid virtual pattern list data (name)" );
690 virtualPatternNode = virtualPatternNode.nextSiblingElement(
"pattern" );
697 XMLNode patternSequenceNode = pNode->firstChildElement(
"patternSequence" );
698 if ( patternSequenceNode.isNull() ) {
700 ERRORLOG(
"'patternSequence' node not found. Aborting." );
705 if ( ! patternSequenceNode.firstChildElement(
"patternID" ).isNull() ) {
719 XMLNode groupNode = patternSequenceNode.firstChildElement(
"group" );
720 while ( ! groupNode.isNull() ) {
722 XMLNode patternIdNode = groupNode.firstChildElement(
"patternID" );
723 while ( ! patternIdNode.isNull() ) {
724 QString sPatternName = patternIdNode.firstChild().nodeValue();
728 if ( ppPat !=
nullptr ) {
729 if ( ppPat->get_name() == sPatternName ) {
736 if ( pPattern !=
nullptr ) {
737 patternSequence->
add( pPattern );
738 }
else if ( ! bSilent ) {
739 WARNINGLOG(
"patternid not found in patternSequence" );
742 patternIdNode = patternIdNode.nextSiblingElement(
"patternID" );
746 groupNode = groupNode.nextSiblingElement(
"group" );
754 if ( ! pPattern->get_virtual_patterns()->empty() ) {
756 patternNode.
write_string(
"name", pPattern->get_name() );
758 for (
const auto& pVirtualPattern : *( pPattern->get_virtual_patterns() ) ) {
759 patternNode.
write_string(
"virtual", pVirtualPattern->get_name() );
768 if ( pPatternList !=
nullptr ) {
771 for (
const auto& pPattern : *pPatternList ) {
772 if ( pPattern !=
nullptr ) {
773 groupNode.
write_string(
"patternID", pPattern->get_name() );
796 pRootNode->
write_bool(
"patternModeMode", bPatternMode );
802 pRootNode->
write_bool(
"isPatternEditorLocked",
809 pRootNode->
write_string(
"mode", QString(
"pattern" ) );
814 sPanLawType =
"RATIO_STRAIGHT_POLYGONAL";
816 sPanLawType =
"RATIO_CONST_POWER";
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";
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";
847 WARNINGLOG(
"Unknown pan law in saving song. Saved default type." );
849 sPanLawType =
"RATIO_STRAIGHT_POLYGONAL";
864 if ( pComponent !=
nullptr ) {
865 pComponent->save_to( &componentListNode );
878 for (
unsigned nFX = 0; nFX <
MAX_FX; nFX++ ) {
881#ifdef H2CORE_HAVE_LADSPA
883 if ( pFX !=
nullptr ) {
889 for (
unsigned nControl = 0; nControl < pFX->
inputControlPorts.size(); nControl++ ) {
895 for (
unsigned nControl = 0; nControl < pFX->
outputControlPorts.size(); nControl++ ) {
917 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
919 if ( tempoMarkerVector.size() >= 1 ){
920 for (
int tt = 0; tt < static_cast<int>(tempoMarkerVector.size()); tt++){
921 if ( tt == 0 && pTimeline->isFirstTempoMarkerSpecial() ) {
925 newBPMNode.
write_int(
"BAR", tempoMarkerVector[tt]->nColumn );
926 newBPMNode.
write_float(
"BPM", tempoMarkerVector[tt]->fBpm );
931 auto tagVector = pTimeline->getAllTags();
933 if ( tagVector.size() >= 1 ){
934 for (
int t = 0; t < static_cast<int>(tagVector.size()); t++){
936 newTAGNode.
write_int(
"BAR", tagVector[t]->nColumn );
944 if ( pPath !=
nullptr ) {
955 std::shared_ptr<Song> pSong =
959 pSong->setMetronomeVolume( 0.5 );
960 pSong->setNotes(
"..." );
961 pSong->setLicense(
License() );
964 pSong->setHumanizeTimeValue( 0.0 );
965 pSong->setHumanizeVelocityValue( 0.0 );
966 pSong->setSwingFactor( 0.0 );
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 );
976 for (
int nn = 0; nn < 10; ++nn ) {
979 pEmptyPattern->
set_name( QString(
"Pattern %1" ).arg( nn + 1 ) );
980 pEmptyPattern->
set_category( QString(
"not_categorized" ) );
981 pPatternList->
add( pEmptyPattern );
986 patternSequence->
add( pEmptyPattern );
989 pSong->setPatternList( pPatternList );
991 std::vector<PatternList*>* pPatternGroupVector =
new std::vector<PatternList*>;
992 pPatternGroupVector->push_back( patternSequence );
993 pSong->setPatternGroupVector( pPatternGroupVector );
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;
1013 if ( pDrumkit !=
nullptr ) {
1014 pSong->setDrumkit( pDrumkit,
true );
1017 ERRORLOG(
"Unable to load drumkit" );
1020 pSong->setIsModified(
false );
1029 if ( pComponent->get_id() == nID ) {
1040 if ( factor < 0.0 ) {
1042 }
else if ( factor > 1.0 ) {
1051 bool Notify =
false;
1065#ifdef H2CORE_HAVE_OSC
1076 for (
int i = 0; i < pInstrumentList->size(); i++ ) {
1077 if ( pInstrumentList->get( i )->has_missing_samples() ) {
1086 for (
int i = 0; i < pInstrumentList->size(); i++ ) {
1087 pInstrumentList->get( i )->set_missing_samples(
false );
1094 if( !doc.
read( sFilename ) ) {
1097 XMLNode root = doc.firstChildElement(
"sequence" );
1098 if ( root.isNull() ) {
1099 ERRORLOG(
"sequence node not found" );
1115 return doc.
write( sFilename );
1121 if ( pInstrument ==
nullptr ) {
1122 assert( pInstrument );
1123 ERRORLOG( QString(
"Unable to retrieve instrument [%1]" )
1124 .arg( nSelectedInstrument ) );
1136 return doc.toString();
1142 if ( ! doc.setContent( sSerialized ) ) {
1149 if ( pInstr ==
nullptr ) {
1150 ERRORLOG( QString(
"Unable to find instrument [%1]" )
1151 .arg( nSelectedInstrument ) );
1158 bool bIsNoteSelection =
false;
1159 bool is_single =
true;
1162 XMLNode rootNode = doc.firstChildElement(
"instrument_line" );
1163 if ( ! rootNode.isNull() ) {
1165 XMLNode patternList = rootNode.firstChildElement(
"patternList" );
1166 if ( patternList.isNull() ) {
1171 patternNode = patternList.firstChildElement(
"pattern" );
1172 if ( ! patternNode.isNull() ) {
1173 is_single = ((
XMLNode )patternNode.nextSiblingElement(
"pattern" )).isNull();
1177 rootNode = doc.firstChildElement(
"noteSelection" );
1178 if ( ! rootNode.isNull() ) {
1180 bIsNoteSelection =
true;
1182 patternNode = rootNode;
1185 ERRORLOG(
"Error pasting Clipboard:instrument_line or noteSelection node not found ");
1190 while ( ! patternNode.isNull() )
1192 QString patternName( patternNode.
read_string(
"name",
"",
false,
false ) );
1195 if ( patternName.length() > 0 || bIsNoteSelection ) {
1203 if ( is_single || pat !=
nullptr ) {
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 ) );
1212 XMLNode pNoteListNode = patternNode.firstChildElement(
"noteList" );
1213 if ( ! pNoteListNode.isNull() )
1216 XMLNode noteNode = pNoteListNode.firstChildElement(
"note" );
1217 while ( ! noteNode.isNull() )
1219 XMLNode instrument = noteNode.firstChildElement(
"instrument" );
1220 XMLNode instrumentText = instrument.firstChild();
1222 instrumentText.setNodeValue( QString::number( pInstr->get_id() ) );
1227 noteNode = noteNode.nextSiblingElement(
"note" );
1232 patterns.push_back(pat);
1236 patternNode = patternNode.nextSiblingElement(
"pattern" );
1244 if ( fKNorm >= 0. ) {
1255 assert ( pDrumkit );
1256 if ( pDrumkit ==
nullptr ) {
1257 ERRORLOG(
"Invalid drumkit supplied" );
1265 auto pDrumkitCompoList = pDrumkit->get_components();
1267 auto pNewComponents = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
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 );
1274 pNewComponents->push_back( pNewComponent );
1288 auto pDrumkitInstrList = pDrumkit->get_instruments();
1301 std::shared_ptr<Instrument> pInstr, pNewInstr;
1302 for (
int nnInstr = 0; nnInstr < pDrumkitInstrList->size(); ++nnInstr ) {
1303 if ( nnInstr < m_pInstrumentList->size() ) {
1308 pInstr = std::make_shared<Instrument>();
1312 pNewInstr = pDrumkitInstrList->get( nnInstr );
1313 assert( pNewInstr );
1314 INFOLOG( QString(
"Loading instrument (%1 of %2) [%3]" )
1316 .arg( pDrumkitInstrList->size() )
1317 .arg( pNewInstr->get_name() ) );
1322 int nID = pInstr->get_id();
1326 nMaxID = std::max( nID, nMaxID );
1328 pInstr->load_from( pDrumkit, pNewInstr );
1329 pInstr->set_id( nID );
1334 if ( nInstrumentDiff >= 0 ) {
1335 for (
int i = 0; i < nInstrumentDiff ; i++ ){
1343 pHydrogen->getAudioEngine()->getTransportPosition()->getBpm() );
1348 for (
auto ¬e : *pPattern->get_notes() ) {
1358 if ( pInstr ==
nullptr ) {
1363 if ( bConditional ) {
1367 if ( pPattern->references( pInstr ) ) {
1368 INFOLOG(
"Keeping instrument #" + QString::number( nInstrumentNumber ) );
1374 pPattern->purge_instrument( pInstr,
false );
1381 pInstr->set_name( (QString(
"Instrument 1" )) );
1382 for (
auto& pCompo : *pInstr->get_components() ) {
1385 pCompo->set_layer(
nullptr, nLayer );
1388 INFOLOG(
"clear last instrument to empty instrument 1 instead delete the last instrument");
1400 QString xxx_name = QString(
"XXX_%1" ).arg( pInstr->get_name() );
1401 pInstr->set_name( xxx_name );
1402 pHydrogen->addInstrumentToDeathRow( pInstr );
1407 std::vector<std::shared_ptr<Note>> notes;
1409 long nColumnStartTick = 0;
1412 auto pColumn = (*m_pPatternGroupSequence)[ ii ];
1414 if ( pColumn->size() == 0 ) {
1421 for (
const auto& ppattern : *pColumn ) {
1422 if ( ppattern !=
nullptr ) {
1424 if ( it->second !=
nullptr ) {
1427 std::shared_ptr<Note> pNote =
1428 std::make_shared<Note>( it->second );
1434 pNote->set_position( pNote->get_position() +
1436 notes.push_back( pNote );
1442 nColumnStartTick += pColumn->longest_pattern_length();
1451 if ( ppComponent->get_name().compare( sComponentName ) == 0 ){
1452 return ppComponent->get_id();
1460 bool bFreeID =
true;
1463 if ( ppComponent->get_id() == nStartingID ) {
1479 if ( ppComponent->get_name().compare( sName ) == 0 ){
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 ) )
1506 .append( QString(
"%1%2m_pPatternGroupSequence:\n" ).arg( sPrefix ).arg( s ) );
1508 if ( pp !=
nullptr ) {
1509 sOutput.append( QString(
"%1" ).arg( pp->toQString( sPrefix + s + s, bShort ) ) );
1512 sOutput.append( QString(
"%1" ).arg(
m_pInstrumentList->toQString( sPrefix + s, bShort ) ) )
1513 .append( QString(
"%1%2m_pComponents:\n" ).arg( sPrefix ).arg( s ) );
1515 if ( cc !=
nullptr ) {
1516 sOutput.append( QString(
"%1" ).arg( cc->toQString( sPrefix + s + s ) ) );
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 ) )
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 ) );
1527 sOutput.append( QString(
"%1%2%3 : %4\n" ).arg( sPrefix ).arg( s ).arg( mm.first ).arg( mm.second ) );
1529 sOutput.append( QString(
"%1%2m_songMode: %3\n" ).arg( sPrefix ).arg( s )
1530 .arg(
static_cast<int>(
m_mode )) )
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 ) )
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 )
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 ) );
1542 sOutput.append( QString(
"%1" ).arg(
m_pTimeline->toQString( sPrefix + s, bShort ) ) );
1544 sOutput.append( QString(
"nullptr\n" ) );
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 ) );;
1550 sOutput = QString(
"[Song]" )
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 ) )
1559 .append( QString(
", m_sNotes: %1" ).arg(
m_sNotes ) )
1561 .append( QString(
", m_pPatternGroupSequence:" ) );
1563 if ( pp !=
nullptr ) {
1564 sOutput.append( QString(
"%1" ).arg( pp->toQString( sPrefix + s + s, bShort ) ) );
1567 sOutput.append( QString(
"%1" ).arg(
m_pInstrumentList->toQString( sPrefix + s, bShort ) ) )
1568 .append( QString(
", m_pComponents: [" ) );
1570 if ( cc !=
nullptr ) {
1571 sOutput.append( QString(
"%1" ).arg( cc->toQString( sPrefix + s + s, bShort ) ) );
1574 sOutput.append( QString(
"], m_sFilename: %1" ).arg(
m_sFilename ) )
1575 .append( QString(
", m_loopMode: %1" ).arg(
static_cast<int>(
m_loopMode) ) )
1578 .append( QString(
", m_fSwingFactor: %1" ).arg(
m_fSwingFactor ) )
1579 .append( QString(
", m_bIsModified: %1" ).arg(
m_bIsModified ) )
1580 .append( QString(
", m_latestRoundRobins" ) );
1582 sOutput.append( QString(
", %1 : %4" ).arg( mm.first ).arg( mm.second ) );
1584 sOutput.append( QString(
", m_mode: %1" )
1585 .arg(
static_cast<int>(
m_mode) ) )
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: " ) );
1596 sOutput.append( QString(
"%1" ).arg(
m_pTimeline->toQString( sPrefix, bShort ) ) );
1598 sOutput.append( QString(
"nullptr" ) );
#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.
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.
static std::shared_ptr< DrumkitComponent > load_from(XMLNode *node)
static Effects * get_instance()
Returns a pointer to the current Effects singleton stored in __instance.
void setLadspaFX(LadspaFX *pFX, int nFX)
LadspaFX * getLadspaFX(int nFX) const
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
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)
std::shared_ptr< Timeline > getTimeline() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
SoundLibraryDatabase * getSoundLibraryDatabase() const
static int getMaxLayers()
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
std::vector< LadspaControlPort * > inputControlPorts
void setEnabled(bool bEnabled)
const QString & getLibraryPath() const
void setVolume(float fVolume)
const QString & getPluginLabel() const
std::vector< LadspaControlPort * > outputControlPorts
static LadspaFX * load(const QString &sLibraryPath, const QString &sPluginLabel, long nSampleRate)
static std::vector< PatternList * > * loadPatternGroupVector(XMLNode *pNode, PatternList *pPatternList, bool bSilent=false)
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
@ GPL
Not a desirable license for audio data but introduced here specifically since it is already used by a...
QString getLicenseString() const
static QString getGPLLicenseNotice(const QString &sAuthor)
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
A note plays an associated instrument with a velocity left and right pan.
static Note * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a note from an XMLNode
PatternList is a collection of patterns.
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.
void set_name(const QString &name)
get the name of the pattern
void virtual_patterns_add(Pattern *pattern)
add a pattern to __virtual_patterns
void set_category(const QString &category)
set the info of the pattern
void insert_note(Note *note)
insert a new note within __notes
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
static const float K_NORM_DEFAULT
default k for pan law with such that L^k + R^k = const must be initialised in Sampler....
@ LINEAR_STRAIGHT_POLYGONAL
@ RATIO_STRAIGHT_POLYGONAL
@ POLAR_STRAIGHT_POLYGONAL
@ QUADRATIC_STRAIGHT_POLYGONAL
bool m_bIsTimelineActivated
Whether the Timeline button was pressed in the GUI or it was activated via an OSC command.
void setPanLawKNorm(float fKNorm)
std::shared_ptr< Timeline > m_pTimeline
void loadVirtualPatternsFrom(XMLNode *pNode, bool bSilent=false)
std::shared_ptr< InstrumentList > getInstrumentList() const
PatternList * getPatternList() const
QString m_sName
author of the song
bool save(const QString &sFilename, bool bSilent=false)
Save a song to file.
bool isLoopEnabled() const
void setDrumkit(std::shared_ptr< Drumkit > pDrumkit, bool bConditional)
ActionMode m_actionMode
Stores the type of interaction with the SongEditor.
PatternList * m_pPatternList
Sequence of pattern groups.
std::map< float, int > m_latestRoundRobins
float m_fVolume
Metronome volume.
QString getLastLoadedDrumkitPath() const
bool writeTempPatternList(const QString &sFilename)
QString m_sAuthor
volume of the song (0.0..1.0)
void readTempPatternList(const QString &sFilename)
static std::shared_ptr< Song > load(const QString &sFilename, bool bSilent=false)
Load a song from file.
bool hasMissingSamples() const
Song was incompletely loaded from file (missing samples)
long lengthInTicks() const
get the length of the song, in tick units
LoopMode m_loopMode
The three states of this enum is just a way to handle the playback within Hydrogen.
ActionMode
Defines the type of user interaction experienced in the SongEditor.
@ 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.
bool pasteInstrumentLineFromString(const QString &sSerialized, int nSelectedInstrument, std::list< Pattern * > &patterns)
bool isPatternActive(int nColumn, int nRow) const
std::shared_ptr< DrumkitComponent > getComponent(int nID) const
void clearMissingSamples()
QString makeComponentNameUnique(const QString &sComponentName) const
Ensures sComponentName is not used by any other component loaded into the song yet.
const QString & getAuthor() const
std::shared_ptr< InstrumentList > m_pInstrumentList
list of drumkit component
void writeTo(XMLNode *pNode, bool bSilent=false)
void writePatternGroupVectorTo(XMLNode *pNode, bool bSilent=false)
void removeInstrument(int nInstrumentNumber, bool bConditional)
void setActionMode(const ActionMode actionMode)
AutomationPath * m_pVelocityAutomationPath
license of the song
void loadPatternGroupVectorFrom(XMLNode *pNode, bool bSilent=false)
QString m_sNotes
Pattern list.
int findFreeComponentID(int nStartingID=0) const
void setFilename(const QString &sFilename)
void setSwingFactor(float fFactor)
float m_fPlaybackTrackVolume
Volume of the playback track.
std::shared_ptr< std::vector< std::shared_ptr< DrumkitComponent > > > m_pComponents
std::vector< PatternList * > * m_pPatternGroupSequence
Instrument list.
static std::shared_ptr< Song > getEmptySong()
PatternMode m_patternMode
void setIsModified(bool bIsModified)
bool m_bIsMuted
Resolution of the song (number of ticks per quarter)
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
bool m_bIsPatternEditorLocked
If set to true, the user won't be able to select a pattern via the SongEditor.
PatternMode
Determines how patterns will be added to AudioEngine::m_pPlayingPatterns if transport is in Song::Mod...
@ 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
float m_fHumanizeVelocityValue
Factor to scale the random contribution when humanizing velocity between 0 and AudioEngine::fHumanize...
AutomationPath * getVelocityAutomationPath() const
float m_fHumanizeTimeValue
Factor to scale the random contribution when humanizing timing between 0 and AudioEngine::fHumanizeTi...
QString copyInstrumentLineToString(int selectedInstrument)
QString m_sLastLoadedDrumkitName
Convenience variable holding the name of the drumkit last loaded.
std::vector< std::shared_ptr< Note > > getAllNotes() const
void writeVirtualPatternsTo(XMLNode *pNode, bool bSilent=false)
int findExistingComponent(const QString &sComponentName) const
Checks whether a component of name sComponentName exists in m_pComponents.
static std::shared_ptr< Song > loadFrom(XMLNode *pNode, const QString &sFilename, bool bSilent=false)
QString m_sPlaybackTrackFilename
Name of the file to be loaded as playback track.
bool m_bPlaybackTrackEnabled
Whether the playback track should be used at all.
QString m_sLastLoadedDrumkitPath
Unique identifier of the drumkit last loaded.
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.
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
bool read(const QString &filepath, const QString &schemapath=nullptr, bool bSilent=false)
read the content of an xml file
bool write(const QString &filepath)
write itself into a file
XMLNode is a subclass of QDomNode with read and write values methods.
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
void write_attribute(const QString &attribute, const QString &value)
write a string as an attribute of the node
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
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
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
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
void write_float(const QString &node, const float value)
write a float into a child node
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
void write_string(const QString &node, const QString &value)
write a string into a child node
void write_bool(const QString &node, const bool value)
write a boolean into a child node
void write_int(const QString &node, const int value)
write an integer into a child node
void sendDirtyState(const bool isDirty)
Informs the NSM server whether the current H2Core::Song is modified or not.
static NsmClient * get_instance()
#define MAX_NOTES
Maximum number of notes.
#define MAX_FX
Maximum number of effects.
std::string get_version()
Returns the current Hydrogen version string.