67Song::Song(
const QString& sName,
const QString& sAuthor,
float fBpm,
float fVolume )
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" )
155 long nSongLength = 0;
159 for (
int i = 0; i < nColumns; i++ ) {
160 PatternList *pColumn = ( *m_pPatternGroupSequence )[ i ];
161 if ( pColumn->
size() != 0 ) {
176 if ( pPattern ==
nullptr ) {
182 auto pColumn = ( *m_pPatternGroupSequence )[ nColumn ];
183 if ( pColumn->index( pPattern ) == -1 ) {
191std::shared_ptr<Song>
Song::load(
const QString& sFilename,
bool bSilent )
194 if ( sPath.isEmpty() ) {
203 if ( ! doc.
read( sFilename ) && ! bSilent ) {
204 ERRORLOG( QString(
"Something went wrong while loading song [%1]" )
208 XMLNode songNode = doc.firstChildElement(
"song" );
210 if ( songNode.isNull() ) {
211 ERRORLOG(
"Error reading song: 'song' node not found" );
216 QString sSongVersion = songNode.
read_string(
"version",
"Unknown version",
false,
false );
217 if ( sSongVersion != QString(
get_version().c_str() ) ) {
218 INFOLOG( QString(
"Trying to load a song [%1] created with a different version [%2] of hydrogen. Current version: %3" )
226 if ( pSong !=
nullptr ) {
227 pSong->setFilename( sFilename );
237 float fBpm = pRootNode->
read_float(
"bpm", 120,
false,
false, bSilent );
238 float fVolume = pRootNode->
read_float(
"volume", 0.5,
false,
false, bSilent );
239 QString sName( pRootNode->
read_string(
"name",
"Untitled Song",
240 false,
false, bSilent ) );
241 QString sAuthor( pRootNode->
read_string(
"author",
"Unknown Author",
242 false,
false, bSilent ) );
244 std::shared_ptr<Song> pSong = std::make_shared<Song>( sName, sAuthor, fBpm, fVolume );
246 pSong->setIsMuted( pRootNode->
read_bool(
"isMuted",
false,
true,
false,
248 pSong->setMetronomeVolume( pRootNode->
read_float(
"metronomeVolume", 0.5,
249 false,
false, bSilent ) );
250 pSong->setNotes( pRootNode->
read_string(
"notes",
"...",
false,
false, bSilent ) );
252 false,
false, bSilent ), sAuthor ) );
253 if ( pRootNode->
read_bool(
"loopEnabled",
false,
false,
false, bSilent ) ) {
259 if ( pRootNode->
read_bool(
"patternModeMode",
261 false,
false, bSilent ) ) {
267 if ( pRootNode->
read_string(
"mode",
"pattern",
false,
false, bSilent ) ==
"song" ) {
273 QString sPlaybackTrack( pRootNode->
read_string(
"playbackTrackFilename",
"",
274 false,
true, bSilent ) );
275 if ( sPlaybackTrack.left( 2 ) ==
"./" ||
276 sPlaybackTrack.left( 2 ) ==
".\\" ) {
280 QFileInfo info( sFilename );
281 sPlaybackTrack = info.absoluteDir()
282 .filePath( sPlaybackTrack.right( sPlaybackTrack.size() - 2 ) );
287 if ( ! sPlaybackTrack.isEmpty() &&
289 ERRORLOG( QString(
"Provided playback track file [%1] does not exist. Using empty string instead" )
290 .arg( sPlaybackTrack ) );
293 pSong->setPlaybackTrackFilename( sPlaybackTrack );
294 pSong->setPlaybackTrackEnabled( pRootNode->
read_bool(
"playbackTrackEnabled",
false,
295 false,
false, bSilent ) );
296 pSong->setPlaybackTrackVolume( pRootNode->
read_float(
"playbackTrackVolume", 0.0,
297 false,
false, bSilent ) );
299 pSong->setHumanizeTimeValue( pRootNode->
read_float(
"humanize_time", 0.0,
300 false,
false, bSilent ) );
301 pSong->setHumanizeVelocityValue( pRootNode->
read_float(
"humanize_velocity", 0.0,
302 false,
false, bSilent ) );
303 pSong->setSwingFactor( pRootNode->
read_float(
"swing_factor", 0.0,
false,
false, bSilent ) );
307 false,
false, bSilent ) ) );
308 pSong->setIsPatternEditorLocked( pRootNode->
read_bool(
"isPatternEditorLocked",
309 false,
true,
false,
true ) );
311 bool bContainsIsTimelineActivated;
312 bool bIsTimelineActivated =
313 pRootNode->
read_bool(
"isTimelineActivated",
false,
314 &bContainsIsTimelineActivated,
true,
false,
true );
315 if ( ! bContainsIsTimelineActivated ) {
319 bIsTimelineActivated = pPreferences->getUseTimelineBpm();
321 pPreferences->setUseTimelineBpm( bIsTimelineActivated );
323 pSong->setIsTimelineActivated( bIsTimelineActivated );
326 QString sPanLawType( pRootNode->
read_string(
"pan_law_type",
327 "RATIO_STRAIGHT_POLYGONAL",
328 false,
false, bSilent ) );
329 if ( sPanLawType ==
"RATIO_STRAIGHT_POLYGONAL" ) {
331 }
else if ( sPanLawType ==
"RATIO_CONST_POWER" ) {
333 }
else if ( sPanLawType ==
"RATIO_CONST_SUM" ) {
335 }
else if ( sPanLawType ==
"LINEAR_STRAIGHT_POLYGONAL" ) {
337 }
else if ( sPanLawType ==
"LINEAR_CONST_POWER" ) {
339 }
else if ( sPanLawType ==
"LINEAR_CONST_SUM" ) {
341 }
else if ( sPanLawType ==
"POLAR_STRAIGHT_POLYGONAL" ) {
343 }
else if ( sPanLawType ==
"POLAR_CONST_POWER" ) {
345 }
else if ( sPanLawType ==
"POLAR_CONST_SUM" ) {
347 }
else if ( sPanLawType ==
"QUADRATIC_STRAIGHT_POLYGONAL" ) {
349 }
else if ( sPanLawType ==
"QUADRATIC_CONST_POWER" ) {
351 }
else if ( sPanLawType ==
"QUADRATIC_CONST_SUM" ) {
353 }
else if ( sPanLawType ==
"LINEAR_CONST_K_NORM" ) {
355 }
else if ( sPanLawType ==
"POLAR_CONST_K_NORM" ) {
357 }
else if ( sPanLawType ==
"RATIO_CONST_K_NORM" ) {
359 }
else if ( sPanLawType ==
"QUADRATIC_CONST_K_NORM" ) {
364 WARNINGLOG(
"Unknown pan law type in import song. Set default." );
369 false,
false, bSilent );
370 if ( fPanLawKNorm <= 0.0 ) {
372 WARNINGLOG( QString(
"Invalid pan law k in import song [%1] (<= 0). Set default k." )
373 .arg( fPanLawKNorm ) );
377 pSong->setPanLawKNorm( fPanLawKNorm );
379 auto formatVersionNode = pRootNode->firstChildElement(
"formatVersion" );
380 if ( ! formatVersionNode.isNull() ) {
381 WARNINGLOG( QString(
"Song [%1] was created with a more recent version of Hydrogen than the current one!" )
383 XMLNode drumkitNode = pRootNode->firstChildElement(
"drumkit_info" );
385 if ( pDrumkit !=
nullptr ) {
386 pSong->setInstrumentList( pDrumkit->get_instruments() );
387 pSong->getInstrumentList()->load_samples( fBpm );
388 pSong->m_pComponents = pDrumkit->get_components();
391 ERRORLOG(
"Unable to load drumkit contained in song" );
393 pSong->setInstrumentList( std::make_shared<InstrumentList>() );
394 pSong->m_pComponents =
395 std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
396 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0,
"Main" );
397 pSong->getComponents()->push_back( pDrumkitComponent );
401 XMLNode componentListNode = pRootNode->firstChildElement(
"componentList" );
402 if ( ( ! componentListNode.isNull() ) ) {
403 XMLNode componentNode = componentListNode.firstChildElement(
"drumkitComponent" );
404 while ( ! componentNode.isNull() ) {
406 &componentNode,
nullptr );
407 if ( pDrumkitComponent !=
nullptr ) {
408 pSong->getComponents()->push_back( pDrumkitComponent );
411 componentNode = componentNode.nextSiblingElement(
"drumkitComponent" );
415 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0,
"Main" );
416 pSong->getComponents()->push_back( pDrumkitComponent );
431 if ( pInstrumentList ==
nullptr ) {
435 pInstrumentList->load_samples( fBpm );
436 pSong->setInstrumentList( pInstrumentList );
439 QString sLastLoadedDrumkitPath =
440 pRootNode->
read_string(
"last_loaded_drumkit",
"",
true,
false,
true );
441 QString sLastLoadedDrumkitName =
442 pRootNode->
read_string(
"last_loaded_drumkit_name",
"",
true,
false,
true );
444#ifdef H2CORE_HAVE_APPIMAGE
445 sLastLoadedDrumkitPath =
449 if ( sLastLoadedDrumkitPath.isEmpty() ) {
457 std::map<QString,int> loadedDrumkits;
458 for (
const auto& pInstrument : *pSong->getInstrumentList() ) {
459 if ( loadedDrumkits.find( pInstrument->get_drumkit_path() ) !=
460 loadedDrumkits.end() ) {
461 loadedDrumkits[ pInstrument->get_drumkit_path() ] += 1;
464 loadedDrumkits[ pInstrument->get_drumkit_path() ] = 1;
468 QString sMostCommonDrumkit;
470 for (
const auto& xx : loadedDrumkits ) {
471 if ( xx.second > nMax ) {
472 sMostCommonDrumkit = xx.first;
477 sLastLoadedDrumkitPath = sMostCommonDrumkit;
479 pSong->setLastLoadedDrumkitPath( sLastLoadedDrumkitPath );
486 auto pDrumkit = pSoundLibraryDatabase->
getDrumkit( sLastLoadedDrumkitPath,
true );
488 if ( sLastLoadedDrumkitName.isEmpty() ) {
492 if ( pSoundLibraryDatabase !=
nullptr && pDrumkit !=
nullptr ) {
493 sLastLoadedDrumkitName = pDrumkit->get_name();
496 pSong->setLastLoadedDrumkitName( sLastLoadedDrumkitName );
500 pSong->getInstrumentList(),
504 pSong->loadVirtualPatternsFrom( pRootNode, bSilent );
507 pSong->loadPatternGroupVectorFrom( pRootNode, bSilent );
509#ifdef H2CORE_HAVE_LADSPA
511 for (
int fx = 0; fx <
MAX_FX; ++fx ) {
519 XMLNode ladspaNode = pRootNode->firstChildElement(
"ladspa" );
520 if ( ! ladspaNode.isNull() ) {
522 XMLNode fxNode = ladspaNode.firstChildElement(
"fx" );
523 while ( ! fxNode.isNull() ) {
524 QString sName = fxNode.
read_string(
"name",
"",
false,
false, bSilent );
526 if ( sName !=
"no plugin" ) {
528#ifdef H2CORE_HAVE_LADSPA
532 if ( pFX !=
nullptr ) {
535 XMLNode inputControlNode = fxNode.firstChildElement(
"inputControlPort" );
536 while ( ! inputControlNode.isNull() ) {
537 QString sName = inputControlNode.
read_string(
"name",
"",
false,
false, bSilent );
538 float fValue = inputControlNode.
read_float(
"value", 0.0,
false,
false, bSilent );
542 if ( QString( port->
sName ) == sName ) {
546 inputControlNode = inputControlNode.nextSiblingElement(
"inputControlPort" );
552 fxNode = fxNode.nextSiblingElement(
"fx" );
554 }
else if ( ! bSilent ) {
558 std::shared_ptr<Timeline> pTimeline = std::make_shared<Timeline>();
559 XMLNode bpmTimeLineNode = pRootNode->firstChildElement(
"BPMTimeLine" );
560 if ( ! bpmTimeLineNode.isNull() ) {
561 XMLNode newBPMNode = bpmTimeLineNode.firstChildElement(
"newBPM" );
562 while ( ! newBPMNode.isNull() ) {
563 pTimeline->addTempoMarker( newBPMNode.
read_int(
"BAR", 0,
false,
false, bSilent ),
564 newBPMNode.
read_float(
"BPM", 120.0,
false,
false, bSilent ) );
565 newBPMNode = newBPMNode.nextSiblingElement(
"newBPM" );
567 }
else if ( ! bSilent ) {
571 XMLNode timeLineTagNode = pRootNode->firstChildElement(
"timeLineTag" );
572 if ( ! timeLineTagNode.isNull() ) {
573 XMLNode newTAGNode = timeLineTagNode.firstChildElement(
"newTAG" );
574 while ( ! newTAGNode.isNull() ) {
575 pTimeline->addTag( newTAGNode.
read_int(
"BAR", 0,
false,
false, bSilent ),
576 newTAGNode.
read_string(
"TAG",
"",
false,
false, bSilent ) );
577 newTAGNode = newTAGNode.nextSiblingElement(
"newTAG" );
579 }
else if ( ! bSilent ) {
582 pSong->setTimeline( pTimeline );
585 XMLNode automationPathsNode = pRootNode->firstChildElement(
"automationPaths" );
586 if ( ! automationPathsNode.isNull() ) {
589 XMLNode pathNode = automationPathsNode.firstChildElement(
"path" );
590 while ( ! pathNode.isNull()) {
591 QString sAdjust = pathNode.
read_attribute(
"adjust",
"velocity",
false,
false, bSilent );
595 if ( sAdjust ==
"velocity" ) {
596 pPath = pSong->getVelocityAutomationPath();
603 pathNode = pathNode.nextSiblingElement(
"path" );
613 QFileInfo fi( sFilename );
620 ERRORLOG( QString(
"Unable to save song to [%1]. Path is not writable!" )
626 INFOLOG( QString(
"Saving song to [%1]" ).arg( sFilename ) );
643 if ( ! doc.
write( sFilename ) ) {
644 ERRORLOG( QString(
"Error writing song to [%1]" ).arg( sFilename ) );
649 INFOLOG(
"Save was successful.");
657 XMLNode virtualPatternListNode = pNode->firstChildElement(
"virtualPatternList" );
658 if ( virtualPatternListNode.isNull() ) {
659 ERRORLOG(
"'virtualPatternList' node not found. Aborting." );
663 XMLNode virtualPatternNode = virtualPatternListNode.firstChildElement(
"pattern" );
664 while ( ! virtualPatternNode.isNull() ) {
665 QString sName = virtualPatternNode.
read_string(
"name", sName,
false,
false, bSilent );
667 Pattern* pCurPattern =
nullptr;
669 if ( pPattern->get_name() == sName ) {
670 pCurPattern = pPattern;
675 if ( pCurPattern !=
nullptr ) {
676 XMLNode virtualNode = virtualPatternNode.firstChildElement(
"virtual" );
677 while ( !virtualNode.isNull() ) {
678 QString sVirtualPatternName = virtualNode.firstChild().nodeValue();
680 Pattern* pVirtualPattern =
nullptr;
682 if ( pPattern !=
nullptr &&
683 pPattern->get_name() == sVirtualPatternName ) {
684 pVirtualPattern = pPattern;
689 if ( pVirtualPattern !=
nullptr ) {
692 else if ( ! bSilent ) {
693 ERRORLOG(
"Song had invalid virtual pattern list data (virtual)" );
695 virtualNode = virtualNode.nextSiblingElement(
"virtual" );
698 else if ( ! bSilent ) {
699 ERRORLOG(
"Song had invalid virtual pattern list data (name)" );
701 virtualPatternNode = virtualPatternNode.nextSiblingElement(
"pattern" );
708 XMLNode patternSequenceNode = pNode->firstChildElement(
"patternSequence" );
709 if ( patternSequenceNode.isNull() ) {
711 ERRORLOG(
"'patternSequence' node not found. Aborting." );
716 if ( ! patternSequenceNode.firstChildElement(
"patternID" ).isNull() ) {
730 XMLNode groupNode = patternSequenceNode.firstChildElement(
"group" );
731 while ( ! groupNode.isNull() ) {
733 XMLNode patternIdNode = groupNode.firstChildElement(
"patternID" );
734 while ( ! patternIdNode.isNull() ) {
735 QString sPatternName = patternIdNode.firstChild().nodeValue();
739 if ( ppPat !=
nullptr ) {
740 if ( ppPat->get_name() == sPatternName ) {
747 if ( pPattern !=
nullptr ) {
748 patternSequence->
add( pPattern );
749 }
else if ( ! bSilent ) {
750 WARNINGLOG(
"patternid not found in patternSequence" );
753 patternIdNode = patternIdNode.nextSiblingElement(
"patternID" );
757 groupNode = groupNode.nextSiblingElement(
"group" );
765 if ( ! pPattern->get_virtual_patterns()->empty() ) {
767 patternNode.
write_string(
"name", pPattern->get_name() );
769 for (
const auto& pVirtualPattern : *( pPattern->get_virtual_patterns() ) ) {
770 patternNode.
write_string(
"virtual", pVirtualPattern->get_name() );
779 if ( pPatternList !=
nullptr ) {
782 for (
const auto& pPattern : *pPatternList ) {
783 if ( pPattern !=
nullptr ) {
784 groupNode.
write_string(
"patternID", pPattern->get_name() );
807 pRootNode->
write_bool(
"patternModeMode", bPatternMode );
813 pRootNode->
write_bool(
"isPatternEditorLocked",
820 pRootNode->
write_string(
"mode", QString(
"pattern" ) );
825 sPanLawType =
"RATIO_STRAIGHT_POLYGONAL";
827 sPanLawType =
"RATIO_CONST_POWER";
829 sPanLawType =
"RATIO_CONST_SUM";
831 sPanLawType =
"LINEAR_STRAIGHT_POLYGONAL";
833 sPanLawType =
"LINEAR_CONST_POWER";
835 sPanLawType =
"LINEAR_CONST_SUM";
837 sPanLawType =
"POLAR_STRAIGHT_POLYGONAL";
839 sPanLawType =
"POLAR_CONST_POWER";
841 sPanLawType =
"POLAR_CONST_SUM";
843 sPanLawType =
"QUADRATIC_STRAIGHT_POLYGONAL";
845 sPanLawType =
"QUADRATIC_CONST_POWER";
847 sPanLawType =
"QUADRATIC_CONST_SUM";
849 sPanLawType =
"LINEAR_CONST_K_NORM";
851 sPanLawType =
"POLAR_CONST_K_NORM";
853 sPanLawType =
"RATIO_CONST_K_NORM";
855 sPanLawType =
"QUADRATIC_CONST_K_NORM";
858 WARNINGLOG(
"Unknown pan law in saving song. Saved default type." );
860 sPanLawType =
"RATIO_STRAIGHT_POLYGONAL";
875 if ( pComponent !=
nullptr ) {
876 pComponent->save_to( &componentListNode );
889 for (
unsigned nFX = 0; nFX <
MAX_FX; nFX++ ) {
892#ifdef H2CORE_HAVE_LADSPA
894 if ( pFX !=
nullptr ) {
900 for (
unsigned nControl = 0; nControl < pFX->
inputControlPorts.size(); nControl++ ) {
906 for (
unsigned nControl = 0; nControl < pFX->
outputControlPorts.size(); nControl++ ) {
928 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
930 if ( tempoMarkerVector.size() >= 1 ){
931 for (
int tt = 0; tt < static_cast<int>(tempoMarkerVector.size()); tt++){
932 if ( tt == 0 && pTimeline->isFirstTempoMarkerSpecial() ) {
936 newBPMNode.
write_int(
"BAR", tempoMarkerVector[tt]->nColumn );
937 newBPMNode.
write_float(
"BPM", tempoMarkerVector[tt]->fBpm );
942 auto tagVector = pTimeline->getAllTags();
944 if ( tagVector.size() >= 1 ){
945 for (
int t = 0; t < static_cast<int>(tagVector.size()); t++){
947 newTAGNode.
write_int(
"BAR", tagVector[t]->nColumn );
955 if ( pPath !=
nullptr ) {
966 std::shared_ptr<Song> pSong =
970 pSong->setMetronomeVolume( 0.5 );
971 pSong->setNotes(
"..." );
972 pSong->setLicense(
License() );
975 pSong->setHumanizeTimeValue( 0.0 );
976 pSong->setHumanizeVelocityValue( 0.0 );
977 pSong->setSwingFactor( 0.0 );
979 auto pInstrList = std::make_shared<InstrumentList>();
980 auto pNewInstr = std::make_shared<Instrument>(
EMPTY_INSTR_ID,
"New instrument" );
981 pInstrList->add( pNewInstr );
982 pSong->setInstrumentList( pInstrList );
987 for (
int nn = 0; nn < 10; ++nn ) {
990 pEmptyPattern->
set_name( QString(
"Pattern %1" ).arg( nn + 1 ) );
991 pEmptyPattern->
set_category( QString(
"not_categorized" ) );
992 pPatternList->
add( pEmptyPattern );
997 patternSequence->
add( pEmptyPattern );
1000 pSong->setPatternList( pPatternList );
1002 std::vector<PatternList*>* pPatternGroupVector =
new std::vector<PatternList*>;
1003 pPatternGroupVector->push_back( patternSequence );
1004 pSong->setPatternGroupVector( pPatternGroupVector );
1011 auto pDrumkit = pSoundLibraryDatabase->getDrumkit( sDefaultDrumkitPath );
1012 if ( pDrumkit ==
nullptr ) {
1013 for (
const auto& pEntry : pSoundLibraryDatabase->getDrumkitDatabase() ) {
1014 if ( pEntry.second !=
nullptr ) {
1015 WARNINGLOG( QString(
"Unable to retrieve default drumkit [%1]. Using kit [%2] instead." )
1016 .arg( sDefaultDrumkitPath )
1017 .arg( pEntry.first ) );
1018 pDrumkit = pEntry.second;
1024 if ( pDrumkit !=
nullptr ) {
1025 pSong->setDrumkit( pDrumkit,
true );
1028 ERRORLOG(
"Unable to load drumkit" );
1031 pSong->setIsModified(
false );
1040 if ( pComponent->get_id() == nID ) {
1051 if ( factor < 0.0 ) {
1053 }
else if ( factor > 1.0 ) {
1062 bool Notify =
false;
1076#ifdef H2CORE_HAVE_OSC
1087 for (
int i = 0; i < pInstrumentList->size(); i++ ) {
1088 if ( pInstrumentList->get( i )->has_missing_samples() ) {
1097 for (
int i = 0; i < pInstrumentList->size(); i++ ) {
1098 pInstrumentList->get( i )->set_missing_samples(
false );
1105 if( !doc.
read( sFilename ) ) {
1108 XMLNode root = doc.firstChildElement(
"sequence" );
1109 if ( root.isNull() ) {
1110 ERRORLOG(
"sequence node not found" );
1126 return doc.
write( sFilename );
1132 if ( pInstrument ==
nullptr ) {
1133 assert( pInstrument );
1134 ERRORLOG( QString(
"Unable to retrieve instrument [%1]" )
1135 .arg( nSelectedInstrument ) );
1147 return doc.toString();
1153 if ( ! doc.setContent( sSerialized ) ) {
1160 if ( pInstr ==
nullptr ) {
1161 ERRORLOG( QString(
"Unable to find instrument [%1]" )
1162 .arg( nSelectedInstrument ) );
1169 bool bIsNoteSelection =
false;
1170 bool is_single =
true;
1173 XMLNode rootNode = doc.firstChildElement(
"instrument_line" );
1174 if ( ! rootNode.isNull() ) {
1176 XMLNode patternList = rootNode.firstChildElement(
"patternList" );
1177 if ( patternList.isNull() ) {
1182 patternNode = patternList.firstChildElement(
"pattern" );
1183 if ( ! patternNode.isNull() ) {
1184 is_single = ((
XMLNode )patternNode.nextSiblingElement(
"pattern" )).isNull();
1188 rootNode = doc.firstChildElement(
"noteSelection" );
1189 if ( ! rootNode.isNull() ) {
1191 bIsNoteSelection =
true;
1193 patternNode = rootNode;
1196 ERRORLOG(
"Error pasting Clipboard:instrument_line or noteSelection node not found ");
1201 while ( ! patternNode.isNull() )
1203 QString patternName( patternNode.
read_string(
"name",
"",
false,
false ) );
1206 if ( patternName.length() > 0 || bIsNoteSelection ) {
1214 if ( is_single || pat !=
nullptr ) {
1216 pat =
new Pattern( patternName,
1217 patternNode.
read_string(
"info",
"",
true,
false ),
1218 patternNode.
read_string(
"category",
"unknown",
true,
false ),
1219 patternNode.
read_int(
"size", -1,
true,
false ),
1220 patternNode.
read_int(
"denominator", 4,
true,
false ) );
1223 XMLNode pNoteListNode = patternNode.firstChildElement(
"noteList" );
1224 if ( ! pNoteListNode.isNull() )
1227 XMLNode noteNode = pNoteListNode.firstChildElement(
"note" );
1228 while ( ! noteNode.isNull() )
1230 XMLNode instrument = noteNode.firstChildElement(
"instrument" );
1231 XMLNode instrumentText = instrument.firstChild();
1233 instrumentText.setNodeValue( QString::number( pInstr->get_id() ) );
1238 noteNode = noteNode.nextSiblingElement(
"note" );
1243 patterns.push_back(pat);
1247 patternNode = patternNode.nextSiblingElement(
"pattern" );
1255 if ( fKNorm >= 0. ) {
1266 assert ( pDrumkit );
1267 if ( pDrumkit ==
nullptr ) {
1268 ERRORLOG(
"Invalid drumkit supplied" );
1276 auto pDrumkitCompoList = pDrumkit->get_components();
1278 auto pNewComponents = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
1280 for (
const auto& pSrcComponent : *pDrumkitCompoList ) {
1281 auto pNewComponent = std::make_shared<DrumkitComponent>( pSrcComponent->get_id(),
1282 pSrcComponent->get_name() );
1283 pNewComponent->load_from( pSrcComponent );
1285 pNewComponents->push_back( pNewComponent );
1299 auto pDrumkitInstrList = pDrumkit->get_instruments();
1312 std::shared_ptr<Instrument> pInstr, pNewInstr;
1313 for (
int nnInstr = 0; nnInstr < pDrumkitInstrList->size(); ++nnInstr ) {
1314 if ( nnInstr < m_pInstrumentList->size() ) {
1319 pInstr = std::make_shared<Instrument>();
1323 pNewInstr = pDrumkitInstrList->get( nnInstr );
1324 assert( pNewInstr );
1325 INFOLOG( QString(
"Loading instrument (%1 of %2) [%3]" )
1327 .arg( pDrumkitInstrList->size() )
1328 .arg( pNewInstr->get_name() ) );
1333 int nID = pInstr->get_id();
1337 nMaxID = std::max( nID, nMaxID );
1339 pInstr->load_from( pDrumkit, pNewInstr );
1340 pInstr->set_id( nID );
1345 if ( nInstrumentDiff >= 0 ) {
1346 for (
int i = 0; i < nInstrumentDiff ; i++ ){
1354 pHydrogen->getAudioEngine()->getTransportPosition()->getBpm() );
1359 for (
auto ¬e : *pPattern->get_notes() ) {
1369 if ( pInstr ==
nullptr ) {
1374 if ( bConditional ) {
1378 if ( pPattern->references( pInstr ) ) {
1379 INFOLOG(
"Keeping instrument #" + QString::number( nInstrumentNumber ) );
1385 pPattern->purge_instrument( pInstr,
false );
1392 pInstr->set_name( (QString(
"Instrument 1" )) );
1393 for (
auto& pCompo : *pInstr->get_components() ) {
1396 pCompo->set_layer(
nullptr, nLayer );
1399 INFOLOG(
"clear last instrument to empty instrument 1 instead delete the last instrument");
1411 QString xxx_name = QString(
"XXX_%1" ).arg( pInstr->get_name() );
1412 pInstr->set_name( xxx_name );
1413 pHydrogen->addInstrumentToDeathRow( pInstr );
1418 std::vector<std::shared_ptr<Note>> notes;
1420 long nColumnStartTick = 0;
1423 auto pColumn = (*m_pPatternGroupSequence)[ ii ];
1425 if ( pColumn->size() == 0 ) {
1432 for (
const auto& ppattern : *pColumn ) {
1433 if ( ppattern !=
nullptr ) {
1435 if ( it->second !=
nullptr ) {
1438 std::shared_ptr<Note> pNote =
1439 std::make_shared<Note>( it->second );
1445 pNote->set_position( pNote->get_position() +
1447 notes.push_back( pNote );
1453 nColumnStartTick += pColumn->longest_pattern_length();
1462 if ( ppComponent->get_name().compare( sComponentName ) == 0 ){
1463 return ppComponent->get_id();
1471 bool bFreeID =
true;
1474 if ( ppComponent->get_id() == nStartingID ) {
1490 if ( ppComponent->get_name().compare( sName ) == 0 ){
1506 sOutput = QString(
"%1[Song]\n" ).arg( sPrefix )
1507 .append( QString(
"%1%2m_bIsTimelineActivated: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_bIsTimelineActivated ) )
1508 .append( QString(
"%1%2m_bIsMuted: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_bIsMuted ) )
1509 .append( QString(
"%1%2m_resolution: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_resolution ) )
1510 .append( QString(
"%1%2m_fBpm: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fBpm ) )
1511 .append( QString(
"%1%2m_sName: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sName ) )
1512 .append( QString(
"%1%2m_sAuthor: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sAuthor ) )
1513 .append( QString(
"%1%2m_fVolume: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fVolume ) )
1514 .append( QString(
"%1%2m_fMetronomeVolume: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fMetronomeVolume ) )
1515 .append( QString(
"%1%2m_sNotes: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sNotes ) )
1516 .append( QString(
"%1" ).arg(
m_pPatternList->toQString( sPrefix + s, bShort ) ) )
1517 .append( QString(
"%1%2m_pPatternGroupSequence:\n" ).arg( sPrefix ).arg( s ) );
1519 if ( pp !=
nullptr ) {
1520 sOutput.append( QString(
"%1" ).arg( pp->toQString( sPrefix + s + s, bShort ) ) );
1523 sOutput.append( QString(
"%1" ).arg(
m_pInstrumentList->toQString( sPrefix + s, bShort ) ) )
1524 .append( QString(
"%1%2m_pComponents:\n" ).arg( sPrefix ).arg( s ) );
1526 if ( cc !=
nullptr ) {
1527 sOutput.append( QString(
"%1" ).arg( cc->toQString( sPrefix + s + s ) ) );
1530 sOutput.append( QString(
"%1%2m_sFilename: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sFilename ) )
1531 .append( QString(
"%1%2m_loopMode: %3\n" ).arg( sPrefix ).arg( s ).arg(
static_cast<int>(
m_loopMode) ) )
1532 .append( QString(
"%1%2m_fHumanizeTimeValue: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fHumanizeTimeValue ) )
1534 .append( QString(
"%1%2m_fSwingFactor: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fSwingFactor ) )
1535 .append( QString(
"%1%2m_bIsModified: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_bIsModified ) )
1536 .append( QString(
"%1%2m_latestRoundRobins\n" ).arg( sPrefix ).arg( s ) );
1538 sOutput.append( QString(
"%1%2%3 : %4\n" ).arg( sPrefix ).arg( s ).arg( mm.first ).arg( mm.second ) );
1540 sOutput.append( QString(
"%1%2m_songMode: %3\n" ).arg( sPrefix ).arg( s )
1541 .arg(
static_cast<int>(
m_mode )) )
1543 .append( QString(
"%1%2m_bPlaybackTrackEnabled: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_bPlaybackTrackEnabled ) )
1544 .append( QString(
"%1%2m_fPlaybackTrackVolume: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fPlaybackTrackVolume ) )
1546 .append( QString(
"%1%2m_license: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_license.toQString( sPrefix + s, bShort ) ) )
1547 .append( QString(
"%1%2m_actionMode: %3\n" ).arg( sPrefix ).arg( s )
1549 .append( QString(
"%1%2m_nPanLawType: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nPanLawType ) )
1550 .append( QString(
"%1%2m_fPanLawKNorm: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fPanLawKNorm ) )
1551 .append( QString(
"%1%2m_pTimeline:\n" ).arg( sPrefix ).arg( s ) );
1553 sOutput.append( QString(
"%1" ).arg(
m_pTimeline->toQString( sPrefix + s, bShort ) ) );
1555 sOutput.append( QString(
"nullptr\n" ) );
1557 sOutput.append( QString(
"%1%2m_sLastLoadedDrumkitName: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sLastLoadedDrumkitName ) )
1558 .append( QString(
"%1%2m_sLastLoadedDrumkitPath: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sLastLoadedDrumkitPath ) );;
1561 sOutput = QString(
"[Song]" )
1563 .append( QString(
", m_bIsMuted: %1" ).arg(
m_bIsMuted ) )
1564 .append( QString(
", m_resolution: %1" ).arg(
m_resolution ) )
1565 .append( QString(
", m_fBpm: %1" ).arg(
m_fBpm ) )
1566 .append( QString(
", m_sName: %1" ).arg(
m_sName ) )
1567 .append( QString(
", m_sAuthor: %1" ).arg(
m_sAuthor ) )
1568 .append( QString(
", m_fVolume: %1" ).arg(
m_fVolume ) )
1570 .append( QString(
", m_sNotes: %1" ).arg(
m_sNotes ) )
1571 .append( QString(
"%1" ).arg(
m_pPatternList->toQString( sPrefix + s, bShort ) ) )
1572 .append( QString(
", m_pPatternGroupSequence:" ) );
1574 if ( pp !=
nullptr ) {
1575 sOutput.append( QString(
"%1" ).arg( pp->toQString( sPrefix + s + s, bShort ) ) );
1578 sOutput.append( QString(
"%1" ).arg(
m_pInstrumentList->toQString( sPrefix + s, bShort ) ) )
1579 .append( QString(
", m_pComponents: [" ) );
1581 if ( cc !=
nullptr ) {
1582 sOutput.append( QString(
"%1" ).arg( cc->toQString( sPrefix + s + s, bShort ) ) );
1585 sOutput.append( QString(
"], m_sFilename: %1" ).arg(
m_sFilename ) )
1586 .append( QString(
", m_loopMode: %1" ).arg(
static_cast<int>(
m_loopMode) ) )
1589 .append( QString(
", m_fSwingFactor: %1" ).arg(
m_fSwingFactor ) )
1590 .append( QString(
", m_bIsModified: %1" ).arg(
m_bIsModified ) )
1591 .append( QString(
", m_latestRoundRobins" ) );
1593 sOutput.append( QString(
", %1 : %4" ).arg( mm.first ).arg( mm.second ) );
1595 sOutput.append( QString(
", m_mode: %1" )
1596 .arg(
static_cast<int>(
m_mode) ) )
1601 .append( QString(
", m_license: %1" ).arg(
m_license.toQString( sPrefix, bShort ) ) )
1602 .append( QString(
", m_actionMode: %1" ).arg(
static_cast<int>(
m_actionMode) ) )
1603 .append( QString(
", m_nPanLawType: %1" ).arg(
m_nPanLawType ) )
1604 .append( QString(
", m_fPanLawKNorm: %1" ).arg(
m_fPanLawKNorm ) )
1605 .append( QString(
", m_pTimeline: " ) );
1607 sOutput.append( QString(
"%1" ).arg(
m_pTimeline->toQString( sPrefix, bShort ) ) );
1609 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)
static QString sPrintIndention
String used to format the debugging string output of some core classes.
static std::shared_ptr< DrumkitComponent > load_from(XMLNode *node, bool *pLegacyFormatEncountered=nullptr)
static Effects * get_instance()
Returns a pointer to the current Effects singleton stored in __instance.
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::shared_ptr< H2Core::Drumkit > loadDrumkit(XMLNode &node, const QString &sDrumkitPath, bool bSilent=false)
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 *pLegacyFormatEncountered=nullptr, 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...
static QString getGPLLicenseNotice(const QString &sAuthor)
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 add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
static PatternList * load_from(XMLNode *pNode, std::shared_ptr< InstrumentList > pInstrumentList, bool bSilent=false)
load a PatternList from an XMLNode
int longest_pattern_length(bool bIncludeVirtuals=true) const
Get the length of the longest pattern in the list.
Pattern * find(const QString &name)
find a pattern within the patterns
void clear()
empty the pattern list
int size() const
returns the numbers of patterns
void set_name(const QString &name)
get the name of the pattern
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 constexpr float K_NORM_DEFAULT
default k for pan law with -4.5dB center compensation, given L^k + R^k = const it is the mean comprom...
@ 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.
static constexpr int nDefaultResolution
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, 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.