66 if ( pSong ==
nullptr ) {
71 pSong->setVolume( fMasterVolumeValue );
81 if ( pInstr !=
nullptr ) {
83 pInstr->set_volume( fVolumeValue );
86 pHydrogen->setSelectedInstrumentNumber( nStrip );
89 pHydrogen->setIsModified(
true );
99 if ( pSong ==
nullptr ) {
103 auto pInstrumentList = pSong->getInstrumentList();
104 auto pInstrument = pInstrumentList->get( nInstrument );
105 if( pInstrument ==
nullptr ) {
106 ERRORLOG( QString(
"Unable to retrieve instrument (Par. 1) [%1]" )
107 .arg( nInstrument ) );
111 pInstrument->set_pitch_offset( fValue );
129 auto pSong = pHydrogen->getSong();
131 if ( pSong ==
nullptr ) {
136 pSong->setIsMuted( bIsMuted );
138 pHydrogen->setIsModified(
true );
146 if ( pInstr ==
nullptr ) {
157 if ( pInstr !=
nullptr ) {
158 pInstr->set_muted( bIsMuted );
162 pHydrogen->setIsModified(
true );
173 if ( pInstr ==
nullptr ) {
184 if ( pInstr !=
nullptr ) {
186 pInstr->set_soloed( isSoloed );
190 pHydrogen->setIsModified(
true );
202 if ( pInstr !=
nullptr ) {
204 pInstr->setPanWithRangeFrom0To1( fValue );
208 pHydrogen->setIsModified(
true );
210 if ( bSelectStrip ) {
211 pHydrogen->setSelectedInstrumentNumber( nStrip );
225 if ( pInstr !=
nullptr ) {
227 pInstr->setPan( fValue );
231 pHydrogen->setIsModified(
true );
233 if ( bSelectStrip ) {
234 pHydrogen->setSelectedInstrumentNumber( nStrip );
245 if ( pSong ==
nullptr ) {
250 float fMasterVolume = pSong->getVolume();
252#ifdef H2CORE_HAVE_OSC
255 std::shared_ptr<Action> pFeedbackAction =
256 std::make_shared<Action>(
"MASTER_VOLUME_ABSOLUTE" );
258 pFeedbackAction->setValue( QString(
"%1")
259 .arg( fMasterVolume ) );
274 if ( pInstr !=
nullptr ) {
276 float fStripVolume = pInstr->get_volume();
278#ifdef H2CORE_HAVE_OSC
281 std::shared_ptr<Action> pFeedbackAction =
282 std::make_shared<Action>(
"STRIP_VOLUME_ABSOLUTE" );
284 pFeedbackAction->setParameter1( QString(
"%1").arg( nStrip + 1 ) );
285 pFeedbackAction->setValue( QString(
"%1").arg( fStripVolume ) );
293 QString(
"%1").arg( nStrip ) );
304#ifdef H2CORE_HAVE_OSC
305 if ( pPref->getOscFeedbackEnabled() ) {
306 std::shared_ptr<Action> pFeedbackAction =
307 std::make_shared<Action>(
"TOGGLE_METRONOME" );
309 pFeedbackAction->setParameter1( QString(
"%1")
310 .arg(
static_cast<int>(pPref->m_bUseMetronome) ) );
320 static_cast<int>(pPref->m_bUseMetronome) * 127 );
325 if ( pSong ==
nullptr ) {
330#ifdef H2CORE_HAVE_OSC
332 std::shared_ptr<Action> pFeedbackAction =
333 std::make_shared<Action>(
"MUTE_TOGGLE" );
335 pFeedbackAction->setParameter1( QString(
"%1")
336 .arg(
static_cast<int>(pSong->getIsMuted()) ) );
346 static_cast<int>(pSong->getIsMuted()) * 127 );
351 if ( pInstr !=
nullptr ) {
353#ifdef H2CORE_HAVE_OSC
355 std::shared_ptr<Action> pFeedbackAction =
356 std::make_shared<Action>(
"STRIP_MUTE_TOGGLE" );
358 pFeedbackAction->setParameter1( QString(
"%1").arg( nStrip + 1 ) );
359 pFeedbackAction->setValue( QString(
"%1")
360 .arg(
static_cast<int>(pInstr->is_muted()) ) );
368 QString(
"%1").arg( nStrip ) );
371 static_cast<int>(pInstr->is_muted()) * 127 );
379 if ( pInstr !=
nullptr ) {
381#ifdef H2CORE_HAVE_OSC
383 std::shared_ptr<Action> pFeedbackAction =
384 std::make_shared<Action>(
"STRIP_SOLO_TOGGLE" );
386 pFeedbackAction->setParameter1( QString(
"%1").arg( nStrip + 1 ) );
387 pFeedbackAction->setValue( QString(
"%1")
388 .arg(
static_cast<int>(pInstr->is_soloed()) ) );
395 QString(
"%1").arg( nStrip ) );
398 static_cast<int>(pInstr->is_soloed()) * 127 );
406 if ( pInstr !=
nullptr ) {
408#ifdef H2CORE_HAVE_OSC
410 std::shared_ptr<Action> pFeedbackAction =
411 std::make_shared<Action>(
"PAN_ABSOLUTE" );
413 pFeedbackAction->setParameter1( QString(
"%1").arg( nStrip + 1 ) );
414 pFeedbackAction->setValue( QString(
"%1")
415 .arg( pInstr->getPanWithRangeFrom0To1() ) );
422 QString(
"%1").arg( nStrip ) );
425 pInstr->getPanWithRangeFrom0To1() * 127 );
437 if ( pHydrogen->
getSong() ==
nullptr ) {
442 for (
auto param : params ) {
443 if ( pMidiDriver !=
nullptr &&
454 if ( pSong ==
nullptr ) {
459 auto pInstr = pSong->getInstrumentList()->get( nStrip );
460 if ( pInstr ==
nullptr ) {
461 ERRORLOG( QString(
"Couldn't find instrument [%1]" ).arg( nStrip ) );
475 auto pSong = pHydrogen->getSong();
477 if ( pSong ==
nullptr ) {
485 auto pInstrList = pSong->getInstrumentList();
486 for (
int ii = 0; ii < pInstrList->size(); ii++){
487 auto pInstr = pInstrList->get( ii );
488 if ( pInstr !=
nullptr ) {
523 pHydrogen->sequencer_stop();
536 if ( pHydrogen->isUnderSessionManagement() ) {
537 pHydrogen->restartDrivers();
540 pHydrogen->setSessionDrumkitNeedsRelinking(
true );
543 pSong->setFilename( sSongPath );
545 pHydrogen->setSong( pSong );
560 pHydrogen->sequencer_stop();
569 std::shared_ptr<Song> pSong;
570 if ( ! sRecoverSongPath.isEmpty() ) {
573 if ( pSong !=
nullptr ) {
574 pSong->setFilename( sSongPath );
580 if ( pSong ==
nullptr ) {
581 ERRORLOG( QString(
"Unable to open song [%1]." )
596 pHydrogen->sequencer_stop();
599 if ( pSong ==
nullptr ) {
600 ERRORLOG( QString(
"Unable to open song." ) );
604 return setSong( pSong, bRelinking );
612 pHydrogen->setSong( pSong, bRelinking );
614 if ( pHydrogen->isUnderSessionManagement() ) {
615 pHydrogen->restartDrivers();
631 pHydrogen->setIsModified(
false );
639 auto pSong = pHydrogen->getSong();
641 if ( pSong ==
nullptr ) {
647 QString sSongPath = pSong->getFilename();
649 if ( sSongPath.isEmpty() ) {
650 ERRORLOG(
"Unable to save song. Empty filename!" );
654#ifdef H2CORE_HAVE_OSC
655 if ( pHydrogen->isUnderSessionManagement() &&
656 pHydrogen->getSessionDrumkitNeedsRelinking() &&
657 ! pHydrogen->getSessionIsExported() ) {
662 QString sSessionDrumkitPath = pSong->getLastLoadedDrumkitPath();
664 auto drumkitDatabase = pHydrogen->getSoundLibraryDatabase()->getDrumkitDatabase();
665 if ( drumkitDatabase.find( sSessionDrumkitPath ) != drumkitDatabase.end() ) {
671 pHydrogen->getSoundLibraryDatabase()->updateDrumkit( sSessionDrumkitPath );
677 bool bSaved = pSong->save( sSongPath );
679 ERRORLOG( QString(
"Current song [%1] could not be saved!" )
695 auto pSong = pHydrogen->getSong();
697 if ( pSong ==
nullptr ) {
708 QString sPreviousFilename( pSong->getFilename() );
709 pSong->setFilename( sNewFilename );
719 if ( ! pHydrogen->isUnderSessionManagement() ) {
746 if ( pHydrogen->getSong() ==
nullptr ) {
751 pHydrogen->setIsTimelineActivated( bActivate );
753 if ( pHydrogen->getJackTimebaseState() ==
755 WARNINGLOG( QString(
"Timeline usage was [%1] in the Preferences. But these changes won't have an effect as long as there is still an external JACK Timebase controller." )
756 .arg( bActivate ?
"enabled" :
"disabled" ) );
758 WARNINGLOG( QString(
"Timeline usage was [%1] in the Preferences. But these changes won't have an effect as long as Pattern Mode is still activated." )
759 .arg( bActivate ?
"enabled" :
"disabled" ) );
767 auto pAudioEngine = pHydrogen->getAudioEngine();
768 auto pTimeline = pHydrogen->getTimeline();
770 if ( pHydrogen->getSong() ==
nullptr ) {
777 pTimeline->deleteTempoMarker( nPosition );
778 pTimeline->addTempoMarker( nPosition, fBpm );
779 pHydrogen->getAudioEngine()->handleTimelineChange();
781 pAudioEngine->unlock();
783 pHydrogen->setIsModified(
true );
792 auto pAudioEngine = pHydrogen->getAudioEngine();
794 if ( pHydrogen->getSong() ==
nullptr ) {
801 pHydrogen->getTimeline()->deleteTempoMarker( nPosition );
802 pHydrogen->getAudioEngine()->handleTimelineChange();
804 pAudioEngine->unlock();
806 pHydrogen->setIsModified(
true );
814 auto pTimeline = pHydrogen->getTimeline();
816 if ( pHydrogen->getSong() ==
nullptr ) {
821 pTimeline->deleteTag( nPosition );
822 pTimeline->addTag( nPosition, sText );
824 pHydrogen->setIsModified(
true );
833 auto pAudioEngine = pHydrogen->getAudioEngine();
835 if ( pHydrogen->getSong() ==
nullptr ) {
840 pHydrogen->getTimeline()->deleteTag( nPosition );
842 pHydrogen->setIsModified(
true );
850#ifdef H2CORE_HAVE_JACK
852 ERRORLOG(
"Unable to (de)activate Jack transport. Please select the Jack driver first." );
868 ERRORLOG(
"Unable to (de)activate Jack transport. Your Hydrogen version was not compiled with jack support." );
876#ifdef H2CORE_HAVE_JACK
877 if ( !pHydrogen->hasJackAudioDriver() ) {
878 ERRORLOG(
"Unable to (de)activate JACK Timebase support. Please select the JACK driver first." );
882 pHydrogen->getAudioEngine()->lock(
RIGHT_HERE );
886 pHydrogen->initJackTimebaseControl();
890 pHydrogen->releaseJackTimebaseControl();
892 pHydrogen->getAudioEngine()->unlock();
896 ERRORLOG(
"Unable to (de)activate JACK Timebase support. Your Hydrogen version was not compiled with JACK support." );
904 auto pAudioEngine = pHydrogen->getAudioEngine();
905 auto pSong = pHydrogen->getSong();
907 if ( pSong ==
nullptr ) {
918 pHydrogen->sequencer_stop();
929 pAudioEngine->handleSongModeChanged();
931 pAudioEngine->unlock();
939 auto pSong = pHydrogen->getSong();
940 auto pAudioEngine = pHydrogen->getAudioEngine();
942 if ( pHydrogen->getSong() ==
nullptr ) {
948 bool bChange =
false;
955 }
else if ( ! bActivate &&
960 if ( pSong->lengthInTicks() <
961 pAudioEngine->getTransportPosition()->getTick() ) {
970 pAudioEngine->handleLoopModeChanged();
971 pAudioEngine->unlock();
975 static_cast<int>( bActivate ) );
985 if ( pDrumkit ==
nullptr ) {
986 ERRORLOG( QString(
"Drumkit [%1] could not be loaded." )
995 if ( pDrumkit !=
nullptr ) {
998 auto pSong = pHydrogen->getSong();
999 if ( pSong !=
nullptr ) {
1001 INFOLOG( QString(
"Setting drumkit [%1] located at [%2]" )
1002 .arg( pDrumkit->get_name() )
1003 .arg( pDrumkit->get_path() ) );
1005 pHydrogen->getAudioEngine()->lock(
RIGHT_HERE );
1007 pSong->setDrumkit( pDrumkit, bConditional );
1009 if ( pHydrogen->getSelectedInstrumentNumber() >=
1010 pSong->getInstrumentList()->size() ) {
1011 pHydrogen->setSelectedInstrumentNumber(
1012 std::max( 0, pSong->getInstrumentList()->size() -1 ),
1016 pHydrogen->renameJackPorts( pSong );
1018 pHydrogen->getAudioEngine()->unlock();
1022 pHydrogen->setIsModified(
true );
1026 if ( pHydrogen->isUnderSessionManagement() ) {
1027 pHydrogen->setSessionDrumkitNeedsRelinking(
true );
1038 ERRORLOG(
"Provided Drumkit is not valid" );
1047 if ( sNewPath.isEmpty() ) {
1048 INFOLOG( QString(
"Upgrading kit at [%1] inplace." )
1049 .arg( sDrumkitPath ) );
1051 INFOLOG( QString(
"Upgrading kit at [%1] into [%2]." )
1052 .arg( sDrumkitPath ).arg( sNewPath ) );
1055 QFileInfo sourceFileInfo( sDrumkitPath );
1056 if ( ! sNewPath.isEmpty() ) {
1068 ERRORLOG( QString(
"Unable to upgrade drumkit [%1] in place: Folder is in read-only mode" )
1069 .arg( sDrumkitPath ) );
1074 QString sTemporaryFolder, sDrumkitDir;
1076 bool bIsCompressed, bLegacyFormatEncountered;
1078 sDrumkitPath, &bIsCompressed, &sDrumkitDir, &sTemporaryFolder,
1079 &bLegacyFormatEncountered );
1081 if ( pDrumkit ==
nullptr ) {
1082 ERRORLOG( QString(
"Unable to load drumkit from source path [%1]" )
1083 .arg( sDrumkitPath ) );
1090 if ( ! sNewPath.isEmpty() ) {
1095 if ( ! bIsCompressed ) {
1097 QDir drumkitDir( sDrumkitDir );
1098 for (
const auto& ssFile : drumkitDir.entryList( QDir::Files ) ) {
1101 if ( ssFile.contains(
".xml" ) ) {
1105 sNewPath +
"/" + ssFile,
true,
true );
1109 sPath = sDrumkitDir;
1115 if ( ! bIsCompressed ) {
1118 QString sBackupPath =
1121 sBackupPath,
true,
true ) ) {
1122 ERRORLOG( QString(
"Unable to backup source drumkit XML file from [%1] to [%2]. We abort instead of overwriting things." )
1124 .arg( sBackupPath ) );
1130 ERRORLOG( QString(
"Unable to backup source .h2drumkit file from [%1] to [%2]. We abort instead of overwriting things." )
1131 .arg( sDrumkitPath ).arg( sBackupPath ) );
1136 sPath = sDrumkitDir;
1139 if ( ! pDrumkit->save( sPath, -1,
true,
true ) ) {
1140 ERRORLOG( QString(
"Error while saving upgraded kit to [%1]" )
1147 if ( bIsCompressed ) {
1148 QString sExportPath;
1149 if ( ! sNewPath.isEmpty() ) {
1150 sExportPath = sNewPath;
1152 sExportPath = sourceFileInfo.dir().absolutePath();
1155 if ( ! pDrumkit->exportTo( sExportPath,
"",
true,
nullptr,
false ) ) {
1156 ERRORLOG( QString(
"Unable to export upgrade drumkit to [%1]" )
1157 .arg( sExportPath ) );
1161 INFOLOG( QString(
"Upgraded drumkit exported as [%1]" )
1162 .arg( sExportPath +
"/" + pDrumkit->get_name() +
1167 if ( ! sTemporaryFolder.isEmpty() ) {
1171 INFOLOG( QString(
"Drumkit [%1] successfully upgraded!" )
1172 .arg( sDrumkitPath ) );
1179 INFOLOG( QString(
"Validating kit [%1]" ).arg( sDrumkitPath ) );
1181 QString sTemporaryFolder, sDrumkitDir;
1183 bool bIsCompressed, bLegacyFormatEncountered;
1185 sDrumkitPath, &bIsCompressed, &sDrumkitDir, &sTemporaryFolder,
1186 &bLegacyFormatEncountered );
1188 if ( pDrumkit ==
nullptr ) {
1189 ERRORLOG( QString(
"Unable to load drumkit from source path [%1]" )
1190 .arg( sDrumkitPath ) );
1195 ERRORLOG( QString(
"Something went wrong in the drumkit retrieval of [%1]. Unable to load from [%2]" )
1196 .arg( sDrumkitPath ).arg( sDrumkitDir ) );
1202 ERRORLOG( QString(
"Drumkit XML file [%1] can not be parsed." )
1207 XMLNode root = doc.firstChildElement(
"drumkit_info" );
1208 if ( root.isNull() ) {
1209 ERRORLOG( QString(
"Drumkit file [%1] seems bricked: 'drumkit_info' node not found" )
1214 if ( bLegacyFormatEncountered && ! bCheckLegacyVersions ) {
1215 ERRORLOG( QString(
"Drumkit [%1] uses a legacy format" )
1216 .arg( sDrumkitPath ) );
1220 INFOLOG( QString(
"Drumkit [%1] is valid!" )
1221 .arg( sDrumkitPath ) );
1227 const QString& sDrumkitPath,
1228 bool* bIsCompressed,
1229 QString *sDrumkitDir,
1230 QString* sTemporaryFolder,
1231 bool* pLegacyFormatEncountered )
1234 std::shared_ptr<Drumkit> pDrumkit =
nullptr;
1240 if ( bIsCompressed ==
nullptr || sTemporaryFolder ==
nullptr ||
1241 sDrumkitDir ==
nullptr || pLegacyFormatEncountered ==
nullptr ) {
1246 *bIsCompressed =
false;
1247 *sTemporaryFolder =
"";
1249 *pLegacyFormatEncountered =
false;
1251 QFileInfo sourceFileInfo( sDrumkitPath );
1257 sDrumkitPath,
false, pLegacyFormatEncountered,
true );
1258 *sDrumkitDir = sDrumkitPath;
1263 ERRORLOG( QString(
"Drumkit file [%1] not readable" )
1264 .arg( sDrumkitPath ) );
1270 QString sDrumkitDirPath = QFileInfo( sDrumkitPath ).absoluteDir().absolutePath();
1272 sDrumkitDirPath,
false, pLegacyFormatEncountered,
true );
1273 *sDrumkitDir = sourceFileInfo.dir().absolutePath();
1278 ERRORLOG( QString(
"Drumkit archive [%1] not readable" )
1279 .arg( sDrumkitPath ) );
1283 *bIsCompressed =
true;
1288 QTemporaryDir tmpDir( sTemplateName );
1289 tmpDir.setAutoRemove(
false );
1290 if ( ! tmpDir.isValid() ) {
1291 ERRORLOG( QString(
"Unable to create temporary folder using template name [%1]" )
1292 .arg( sTemplateName ) );
1296 *sTemporaryFolder = tmpDir.path();
1302 ERRORLOG( QString(
"Unabled to extract provided drumkit [%1] into [%2]" )
1303 .arg( sDrumkitPath ).arg( tmpDir.path() ) );
1314 QDir extractedDir( tmpDir.path() );
1315 QStringList extractedContent =
1316 extractedDir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot );
1317 QStringList extractedFolders =
1318 extractedDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
1319 if ( ( extractedContent.size() != extractedFolders.size() ) ||
1320 ( extractedFolders.size() != 1 ) ) {
1321 ERRORLOG( QString(
"Unsupported content of [%1]. Expected a single folder within the archive containing all samples, metadata, as well as the drumkit.xml file. Instead:\n" )
1322 .arg( sDrumkitPath ) );
1323 for (
const auto& sFile : extractedContent ) {
1330 *sDrumkitDir,
false, pLegacyFormatEncountered,
true );
1333 ERRORLOG( QString(
"Provided source path [%1] does not point to a Hydrogen drumkit" )
1334 .arg( sDrumkitPath ) );
1342 const QString& sTargetDir,
1343 QString* pInstalledPath,
1344 bool* pEncodingIssuesDetected ) {
1346 if ( pInstalledPath !=
nullptr ) {
1347 *pInstalledPath =
"";
1349 if ( pEncodingIssuesDetected !=
nullptr ) {
1350 *pEncodingIssuesDetected =
false;
1354 bool bInstall =
false;
1355 if ( sTargetDir.isEmpty() ) {
1357 INFOLOG( QString(
"Installing drumkit [%1]" ).arg( sDrumkitPath ) );
1360 INFOLOG( QString(
"Extracting drumkit [%1] to [%2]" )
1361 .arg( sDrumkitPath ).arg( sTargetDir ) );
1362 sTarget = sTargetDir;
1366 ERRORLOG( QString(
"Target dir [%1] is neither a writable folder nor can it be created." )
1371 QFileInfo sKitInfo( sDrumkitPath );
1374 ERRORLOG( QString(
"Invalid drumkit path [%1]. Please provide an absolute path to a .h2drumkit file." )
1375 .arg( sDrumkitPath ) );
1380 pEncodingIssuesDetected,
true ) ) {
1381 ERRORLOG( QString(
"Unabled to extract provided drumkit [%1] into [%2]" )
1382 .arg( sDrumkitPath ).arg( sTarget ) );
1395 if ( nPatternGroup < -1 ) {
1396 ERRORLOG( QString(
"Provided column [%1] too low. Assigning 0 instead." )
1397 .arg( nPatternGroup ) );
1402 if ( pHydrogen->getSong() ==
nullptr ) {
1407 long nTotalTick = pHydrogen->getTickForColumn( nPatternGroup );
1408 if ( nTotalTick < 0 ) {
1410 ERRORLOG( QString(
"Provided column [%1] violates the allowed range [0;%2). No relocation done." )
1411 .arg( nPatternGroup )
1412 .arg( pHydrogen->getSong()->getPatternGroupVector()->size() ) );
1427 auto pAudioEngine = pHydrogen->getAudioEngine();
1429 if ( pHydrogen->getSong() ==
nullptr ) {
1436 pAudioEngine->locate( nTick, bWithJackBroadcast );
1438 pAudioEngine->unlock();
1448 return setPattern( pPattern, pPatternList->size() );
1452 auto pSong = pHydrogen->getSong();
1454 if ( pHydrogen->getSong() ==
nullptr ) {
1459 auto pPatternList = pSong->getPatternList();
1462 if ( pNewPattern ==
nullptr ) {
1463 ERRORLOG( QString(
"Unable to loading the pattern [%1]" ).arg( sPath ) );
1467 if ( nPatternPosition == -1 ) {
1468 nPatternPosition = pPatternList->size();
1471 return setPattern( pNewPattern, nPatternPosition );
1477 if ( pHydrogen->getSong() ==
nullptr ) {
1482 auto pPatternList = pHydrogen->getSong()->getPatternList();
1485 if ( !pPatternList->check_name( pPattern->
get_name() ) ){
1486 pPattern->
set_name( pPatternList->find_unused_pattern_name( pPattern->
get_name() ) );
1489 pPatternList->insert( nPatternPosition, pPattern );
1490 if ( pHydrogen->isPatternEditorLocked() ) {
1491 pHydrogen->updateSelectedPattern(
true );
1493 pHydrogen->setSelectedPatternNumber( nPatternPosition );
1495 pHydrogen->setIsModified(
true );
1506 auto pAudioEngine = pHydrogen->getAudioEngine();
1507 auto pSong = pHydrogen->getSong();
1510 if ( pSong ==
nullptr ) {
1515 INFOLOG( QString(
"Deleting pattern [%1]" ).arg( nPatternNumber ) );
1517 auto pPatternList = pSong->getPatternList();
1518 auto pPatternGroupVector = pSong->getPatternGroupVector();
1519 auto pPlayingPatterns = pAudioEngine->getPlayingPatterns();
1520 auto pNextPatterns = pAudioEngine->getNextPatterns();
1522 int nSelectedPatternNumber = pHydrogen->getSelectedPatternNumber();
1523 auto pPattern = pPatternList->get( nPatternNumber );
1525 if ( pPattern ==
nullptr ) {
1526 ERRORLOG( QString(
"Pattern [%1] not found" ).arg( nPatternNumber ) );
1534 if ( pPatternList->size() == 0 ) {
1536 pPatternList->add( pEmptyPattern );
1541 for (
const auto& ppatternList : *pPatternGroupVector ) {
1542 for (
int ii = 0; ii < ppatternList->size(); ++ii ) {
1543 if ( ppatternList->get( ii ) == pPattern ) {
1544 ppatternList->del( ii );
1554 for (
int ii = pPatternGroupVector->size() - 1; ii >= 0; --ii ) {
1555 pColumn = pPatternGroupVector->at( ii );
1556 if ( pColumn->
size() == 0 ) {
1557 pPatternGroupVector->erase( pPatternGroupVector->begin() + ii );
1565 if ( pHydrogen->isPatternEditorLocked() ) {
1566 pHydrogen->updateSelectedPattern(
false );
1567 }
else if ( nPatternNumber == nSelectedPatternNumber ) {
1568 pHydrogen->setSelectedPatternNumber( std::max( 0, nPatternNumber - 1 ),
1576 for (
int ii = 0; ii < pNextPatterns->size(); ++ii ) {
1577 if ( pNextPatterns->get( ii ) == pPattern ) {
1578 pAudioEngine->toggleNextPattern( nPatternNumber );
1585 pAudioEngine->removePlayingPattern( pPattern );
1588 pPatternList->
del( pPattern );
1590 pHydrogen->updateSongSize();
1592 pAudioEngine->unlock();
1595 for (
const auto& ppattern : *pPatternList ) {
1598 ppattern->get_virtual_patterns()->find( pPattern );
1599 if ( it != ppattern->get_virtual_patterns()->end() ) {
1600 ppattern->virtual_patterns_del( *it );
1604 pHydrogen->updateVirtualPatterns();
1605 pHydrogen->setIsModified(
true );
1613 int nPatternNumber ) {
1616 auto pSong = pHydrogen->
getSong();
1617 if ( pSong ==
nullptr ) {
1623 if ( nPatternNumber != -1 ) {
1624 nPattern = nPatternNumber;
1629 auto pPattern = pSong->getPatternList()->get( nPattern );
1630 if ( pPattern ==
nullptr ) {
1631 ERRORLOG( QString(
"Couldn't find pattern [%1]" ).arg( nPattern ) );
1635 auto pInstrument = pSong->getInstrumentList()->get( nInstrument );
1636 if ( pInstrument ==
nullptr ) {
1637 ERRORLOG( QString(
"Couldn't find instrument [%1]" ).arg( nInstrument ) );
1641 pPattern->purge_instrument( pInstrument,
true );
1653 if ( pHydrogen->getSong() ==
nullptr ) {
1658 auto pSong = pHydrogen->getSong();
1659 auto pAudioEngine = pHydrogen->getAudioEngine();
1660 auto pPatternList = pSong->getPatternList();
1661 std::vector<PatternList*>* pColumns = pSong->getPatternGroupVector();
1663 if ( nRow < 0 || nRow > pPatternList->size() ) {
1664 ERRORLOG( QString(
"Provided row [%1] is out of bound [0,%2]" )
1665 .arg( nRow ).arg( pPatternList->size() ) );
1669 auto pNewPattern = pPatternList->get( nRow );
1670 if ( pNewPattern ==
nullptr ) {
1671 ERRORLOG( QString(
"Unable to obtain Pattern in row [%1]." )
1678 if ( nColumn >= 0 && nColumn < pColumns->size() ) {
1680 auto pPattern = pColumn->
del( pNewPattern );
1681 if ( pPattern ==
nullptr ) {
1683 pColumn->
add( pNewPattern );
1688 for (
int ii = pColumns->size() - 1; ii >= 0; ii-- ) {
1690 if ( pColumn->
size() == 0 ) {
1691 pColumns->erase( pColumns->begin() + ii );
1699 else if ( nColumn >= pColumns->size() ) {
1703 for (
int ii = 0; nColumn - pColumns->size() + 1; ii++ ) {
1705 pColumns->push_back( pColumn );
1707 pColumn->
add( pNewPattern );
1711 ERRORLOG( QString(
"Provided column [%1] is out of bound [0,%2]" )
1712 .arg( nColumn ).arg( pColumns->size() ) );
1713 pAudioEngine->unlock();
1717 pHydrogen->updateSongSize();
1718 pHydrogen->updateSelectedPattern(
false );
1720 pAudioEngine->unlock();
1722 pHydrogen->setIsModified(
true );
1735 auto pSong = pHydrogen->getSong();
1736 if ( pSong ==
nullptr ) {
1741 std::shared_ptr<Instrument> pInstrument =
nullptr;
1742 int nInstrument = 0;
1745 auto pInstrumentList = pSong->getInstrumentList();
1747 if ( pPref->__playselectedinstrument ){
1748 nInstrument = pHydrogen->getSelectedInstrumentNumber();
1749 pInstrument = pInstrumentList->get( pHydrogen->getSelectedInstrumentNumber());
1750 if ( pInstrument ==
nullptr ) {
1754 sMode =
"Play Selected Instrument";
1756 else if ( pPref->m_bMidiFixedMapping ){
1757 pInstrument = pInstrumentList->findMidiNote( nNote );
1758 if ( pInstrument ==
nullptr ) {
1759 WARNINGLOG( QString(
"Unable to map note [%1] to instrument" )
1763 nInstrument = pInstrumentList->index( pInstrument );
1764 sMode =
"Map to Output MIDI note";
1768 if( nInstrument < 0 || nInstrument >= pInstrumentList->size()) {
1769 WARNINGLOG( QString(
"Instrument number [%1] - derived from note [%2] - out of bound note [%3,%4]" )
1770 .arg( nInstrument ).arg( nNote )
1771 .arg( 0 ).arg( pInstrumentList->size() ) );
1775 pInstrument = pInstrumentList->get( nInstrument );
1776 if ( pInstrument ==
nullptr ) {
1777 WARNINGLOG( QString(
"Unable to retrieve instrument [%1]" )
1778 .arg( nInstrument ) );
1781 sMode =
"Map to instrument list position";
1787 const int nHihatOpenness = pHydrogen->getHihatOpenness();
1788 if ( pInstrument !=
nullptr &&
1789 pInstrument->get_hihat_grp() >= 0 &&
1790 ( nHihatOpenness < pInstrument->get_lower_cc() ||
1791 nHihatOpenness > pInstrument->get_higher_cc() ) ) {
1793 for (
int i = 0; i <= pInstrumentList->size(); i++ ) {
1794 auto ppInstr = pInstrumentList->get( i );
1795 if ( ppInstr !=
nullptr &&
1796 pInstrument->get_hihat_grp() == ppInstr->get_hihat_grp() &&
1797 nHihatOpenness >= ppInstr->get_lower_cc() &&
1798 nHihatOpenness <= ppInstr->get_higher_cc() ) {
1801 sMode =
"Hihat Pressure Group";
1807 INFOLOG( QString(
"[%1] mapped note [%2] to instrument [%3]" )
1808 .arg( sMode ).arg( nNote ).arg( nInstrument ) );
1810 return pHydrogen->addRealtimeNote( nInstrument, fVelocity, bNoteOff, nNote );
1816 auto pAudioEngine = pHydrogen->getAudioEngine();
1818 pAudioEngine->getMetronomeInstrument()->set_volume(
1819 pPref->m_fMetronomeVolume );
1834 bool bAlreadyContained =
false;
1836 std::vector<QString> recentFiles = pPref->getRecentFiles();
1841 const QString sFilenameCleaned = QDir::cleanPath( sFilename );
1843 recentFiles.insert( recentFiles.begin(), sFilenameCleaned );
1845 if ( std::find( recentFiles.begin(), recentFiles.end(),
1846 sFilenameCleaned ) != recentFiles.end() ) {
1850 std::vector<QString> sTmpVec;
1851 for (
const auto& ssFilename : recentFiles ) {
1852 if ( std::find( sTmpVec.begin(), sTmpVec.end(), ssFilename ) ==
1855 sTmpVec.push_back( ssFilename );
1859 recentFiles = sTmpVec;
1862 pPref->setRecentFiles( recentFiles );
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
@ Playing
Transport is rolling.
void unlock()
Mutex unlocking of the AudioEngine.
void lock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
bool locateToColumn(int nPatternGroup)
Relocates transport to the beginning of a particular column/Pattern group.
bool setPattern(Pattern *pPattern, int nPatternNumber)
Opens a pattern to the current pattern list.
bool clearInstrumentInPattern(int nInstrumentNumber, int nPatternNumber=-1)
Deletes all notes for instrument pInstrument in a specified pattern.
bool addTag(int nPosition, const QString &sText)
Adds a tag to the Timeline.
bool setSong(std::shared_ptr< Song > pSong, bool bRelinking=true)
Sets a H2Core::Song to be used by Hydrogen.
bool sendStripIsSoloedFeedback(int nStrip)
bool sendMetronomeIsActiveFeedback()
bool setMetronomeIsActive(bool isActive)
void updatePreferences()
In case a different preferences file was loaded with Hydrogen already fully set up this function refr...
bool openSong(const QString &songPath, const QString &sRecoverSongPath="")
Opens the H2Core::Song specified in songPath.
bool locateToTick(long nTick, bool bWithJackBroadcast=true)
Relocates transport to a particular tick.
bool handleNote(int nNote, float fVelocity, bool bNoteOff=false)
Handle an incoming note event, e.g.
bool sendStripPanFeedback(int nStrip)
bool deleteTempoMarker(int nPosition)
Delete a tempo marker from the Timeline.
std::shared_ptr< Instrument > getStrip(int nStrip) const
bool newPattern(const QString &sPatternName)
Creates an empty pattern and adds it to the pattern list.
bool toggleStripIsMuted(int nStrip)
bool newSong(const QString &songPath)
Create an empty H2Core::Song, which will be stored in songPath.
bool savePreferences()
Saves the current state of the H2Core::Preferences.
bool extractDrumkit(const QString &sDrumkitPath, const QString &sTargetDir="", QString *pInstalledPath=nullptr, bool *pEncodingIssuesDetected=nullptr)
Extracts the compressed .h2drumkit file in sDrumkitPath into sTargetDir.
bool sendMasterIsMutedFeedback()
bool setStripVolume(int nStrip, float fVolumeValue, bool bSelectStrip)
bool toggleStripIsSoloed(int nStrip)
bool sendStripVolumeFeedback(int nStrip)
bool activateSongMode(bool bActivate)
Switches between Song and Pattern mode of playback.
bool validateDrumkit(const QString &sDrumkitPath, bool bCheckLegacyVersions=false)
Checks whether the provided drumkit in sDrumkitPath can be found, can be loaded, and does comply with...
bool setDrumkit(const QString &sDrumkit, bool bConditional=true)
Wrapper around setDrumkit() that allows loading drumkits by name or path.
bool setInstrumentPitch(int nInstrument, float fValue)
bool setStripPanSym(int nStrip, float fValue, bool bSelectStrip)
bool toggleGridCell(int nColumn, int nRow)
Fills or clears a specific grid cell in the SongEditor.
bool removePattern(int nPatternNumber)
Removes a pattern from the pattern list.
bool activateLoopMode(bool bActivate)
Toggle loop mode of playback.
std::shared_ptr< Drumkit > retrieveDrumkit(const QString &sDrumkitPath, bool *bIsCompressed, QString *sDrumkitDir, QString *sTemporaryFolder, bool *pLegacyFormatEncountered)
Loads the drumkit specified in sDrumkitPath.
bool upgradeDrumkit(const QString &sDrumkitPath, const QString &sNewPath="")
Upgrades the drumkit found at absolute path sDrumkitPath.
bool activateJackTimebaseControl(bool bActivate)
(Un)registers Hydrogen as JACK Timebaes constroller.
void insertRecentFile(const QString sFilename)
Add sFilename to the list of recent songs in Preferences::m_recentFiles.
bool setMasterIsMuted(bool isMuted)
bool activateJackTransport(bool bActivate)
(De)activates the usage of Jack transport.
bool setStripIsMuted(int nStrip, bool isMuted)
bool setMasterVolume(float masterVolumeValue)
bool activateTimeline(bool bActivate)
(De)activates the usage of the Timeline.
bool sendStripIsMutedFeedback(int nStrip)
bool setStripIsSoloed(int nStrip, bool isSoloed)
bool sendMasterVolumeFeedback()
bool initExternalControlInterfaces()
bool saveSongAs(const QString &sNewFilename)
Saves the current H2Core::Song to the path provided in sNewFilename.
bool saveSong()
Saves the current H2Core::Song.
bool deleteTag(int nPosition)
Delete a tag from the Timeline.
bool setStripPan(int nStrip, float fValue, bool bSelectStrip)
bool quit()
Triggers the shutdown of Hydrogen.
bool addTempoMarker(int nPosition, float fBpm)
Adds a tempo marker to the Timeline.
const int m_nDefaultMidiFeedbackChannel
bool openPattern(const QString &sPath, int nPatternNumber=-1)
Opens a pattern from disk and adds it to the pattern list.
bool handleOutgoingControlChanges(std::vector< int > params, int nValue)
static bool install(const QString &sSourcePath, const QString &sTargetPath="", QString *pInstalledPath=nullptr, bool *pEncodingIssuesDetected=nullptr, bool bSilent=false)
Extract a .h2drumkit file.
static std::shared_ptr< Drumkit > load(const QString &dk_dir, bool bUpgrade=true, bool *pLegacyFormatEncountered=nullptr, bool bSilent=false)
Load drumkit information from a directory.
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 bool file_copy(const QString &src, const QString &dst, bool overwrite=false, bool bSilent=false)
copy a source file to a destination
static bool dir_readable(const QString &path, bool silent=false)
returns true if the given path is a readable regular directory
static bool isSongPathValid(const QString &sSongPath, bool bCheckExistance=false)
Checks the path pointing to a .h2song.
static bool drumkit_valid(const QString &dk_path)
returns true if the path contains a usable drumkit
static QString usr_drumkits_dir()
returns user drumkits path
static QString drumkit_backup_path(const QString &dk_path)
Create a backup path from a drumkit path.
static bool dir_writable(const QString &path, bool silent=false)
returns true if the given path is a writable regular directory
static QString empty_song_path()
Provides the full path to the current empty song.
static QString drumkit_file(const QString &dk_path)
returns the path to the xml file within a supposed drumkit path
static QString tmp_dir()
returns temp path
static const QString drumkit_ext
static bool path_usable(const QString &path, bool create=true, bool silent=false)
returns true if the path is a readable and writable regular directory, create if it not exists
static QString drumkit_xml()
Returns filename and extension of the expected drumkit file.
static bool file_readable(const QString &path, bool silent=false)
returns true if the given path is an existing readable regular file
std::shared_ptr< Song > getSong() const
Get the current song.
@ unavailable
No GUI available.
@ ready
There is a working GUI.
int getSelectedPatternNumber() const
void setSelectedInstrumentNumber(int nInstrument, bool bTriggerEvent=true)
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
MidiOutput * getMidiOutput() const
AudioEngine * getAudioEngine() const
SoundLibraryDatabase * getSoundLibraryDatabase() const
GUIState getGUIState() const
@ Listener
An external program is Timebase controller and Hydrogen will disregard all tempo markers on the Timel...
static constexpr int instrumentOffset
When recording notes using MIDI NOTE_ON events this offset will be applied to the provided pitch in o...
virtual void handleOutgoingControlChange(int param, int value, int channel)=0
PatternList is a collection of patterns.
void add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
Pattern * del(int idx)
remove the pattern at a given index, does not delete it
int size() const
returns the numbers of patterns
Pattern class is a Note container.
void set_name(const QString &name)
get the name of the pattern
const QString & get_name() const
set the category of the pattern
static Pattern * load_file(const QString &sPpatternPath, std::shared_ptr< InstrumentList > pInstruments, bool bSilent=false)
load a pattern from a file
virtual_patterns_t::const_iterator virtual_patterns_cst_it_t
Manager for User Preferences File (singleton)
int m_bJackTimebaseMode
Specifies if Hydrogen support the of JACK Timebase protocol.
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
bool savePreferences()
Save the preferences file.
bool m_bEnableMidiFeedback
void setLastSongFilename(const QString &filename)
bool m_bUseMetronome
If set to true, samples of the metronome will be added to H2Core::AudioEngine::m_songNoteQueue and th...
int m_bJackTransportMode
Specifies whether or not Hydrogen will use the JACK transport system.
@ NO_JACK_TRANSPORT
Specifies whether or not to use JACK transport capabilities.
@ USE_JACK_TRANSPORT
Specifies whether or not to use JACK transport capabilities.
@ NO_JACK_TIMEBASE_CONTROL
Specifies that Hydrogen should not be in control of JACK Timebase information.
@ USE_JACK_TIMEBASE_CONTROL
Specifies that Hydrogen should attempt to acquire JACK Timebase control.
static std::shared_ptr< Song > load(const QString &sFilename, bool bSilent=false)
Load a song from file.
@ Finishing
Transport is still in loop mode (frames and ticks larger than song size are allowed) but playback end...
static std::shared_ptr< Song > getEmptySong()
void updateDrumkits(bool bTriggerEvent=true)
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.
bool read(const QString &filepath, bool bSilent=false)
read the content of an xml file
XMLNode is a subclass of QDomNode with read and write values methods.
The MidiMap maps MidiActions to MidiEvents.
std::vector< int > findCCValuesByActionParam1(QString sActionType, QString sParam1)
static MidiMap * get_instance()
Returns a pointer to the current MidiMap singleton stored in __instance.
std::vector< int > findCCValuesByActionType(QString sActionType)
static void linkDrumkit(std::shared_ptr< H2Core::Song > pSong)
Responsible for linking a drumkit on user or system level into the session folder and updating all co...
static OscServer * get_instance()
Returns a pointer to the current OscServer singleton stored in __instance.
void handleAction(std::shared_ptr< Action > pAction)
Function called by H2Core::CoreActionController::initExternalControlInterfaces() to inform all client...
@ EVENT_RELOCATION
Triggered in case there is a relocation of the transport position while trasnsport is not rolling.
@ EVENT_INSTRUMENT_PARAMETERS_CHANGED
Some parameters of an instrument have been changed.
@ EVENT_DRUMKIT_LOADED
A the current drumkit was replaced by a new one.
@ EVENT_JACK_TRANSPORT_ACTIVATION
Toggles the button indicating the usage JACK transport.
@ EVENT_PATTERN_MODIFIED
A pattern was added, deleted, or modified.
@ EVENT_TIMELINE_UPDATE
Tells the GUI some parts of the Timeline (tempo markers or tags) were modified.
@ EVENT_UPDATE_SONG
Event triggering HydrogenApp::updateSongEvent() whenever the Song was changed outside of the GUI,...
@ EVENT_LOOP_MODE_ACTIVATION
Toggles the button indicating the usage loop mode.
@ EVENT_GRID_CELL_TOGGLED
@ EVENT_QUIT
Triggering HydrogenApp::quitEvent() and enables a shutdown of the entire application via the command ...
@ EVENT_UPDATE_PREFERENCES
Event triggering the loading or saving of the H2Core::Preferences whenever they were changed outside ...