23#include <core/config.h>
148 QRect oldGeometry = pWindow->geometry();
152 pWindow->move( prop.
x, prop.
y );
160 QRect newGeometry = pWindow->geometry();
161 if ( !( flags &
SetX ) ) {
162 newGeometry.setX( oldGeometry.x() );
164 if ( !( flags &
SetY ) ) {
165 newGeometry.setY( oldGeometry.y() );
168 newGeometry.setWidth( oldGeometry.width() );
171 newGeometry.setHeight( oldGeometry.height() );
175 if ( pWindow->minimumSize() == pWindow->maximumSize() ) {
176 if ( pWindow->isFullScreen()) {
177 pWindow->showNormal();
181 if ( oldGeometry != newGeometry ) {
182 pWindow->setGeometry( newGeometry );
189 prop.
x = pWindow->x();
190 prop.
y = pWindow->y();
191 prop.
height = pWindow->height();
192 prop.
width = pWindow->width();
193 prop.
visible = pWindow->isVisible();
223 #ifdef H2CORE_HAVE_LADSPA
224 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
225 delete m_pLadspaFXProperties[nFX];
255 m_pTab->setObjectName(
"TabbedInterface" );
276 pSouthPanel->setObjectName(
"SouthPanel" );
277 QHBoxLayout *pEditorHBox =
new QHBoxLayout();
278 pEditorHBox->setSpacing( 5 );
279 pEditorHBox->setContentsMargins( 0, 0, 0, 0 );
280 pSouthPanel->setLayout( pEditorHBox );
288 m_pTab->setMovable(
false );
289 m_pTab->setTabsClosable(
false );
290 m_pTab->addTab( pSouthPanel, tr(
"Instrument + Pattern") );
325 mainArea->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
326 mainArea->setMinimumSize( 1000,
358#ifdef H2CORE_HAVE_LADSPA
360 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
362 m_pLadspaFXProperties[nFX]->hide();
390#ifdef H2CORE_HAVE_LADSPA
391 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
392 m_pLadspaFXProperties[nFX]->close();
399 auto pCoreActionController = pHydrogen->getCoreActionController();
403 QFileInfo fileInfo( sFilename );
406 if ( fileInfo.isRelative() ) {
407 sFilename = fileInfo.absoluteFilePath();
412 QString sBaseName( fileInfo.completeBaseName() );
413 if ( sBaseName.startsWith(
"." ) ) {
414 sBaseName.remove( 0, 1 );
418 QFileInfo autoSaveFileRecent( QString(
"%1/.%2.autosave.h2song" )
419 .arg( fileInfo.absoluteDir().absolutePath() )
422 QFileInfo autoSaveFileOld( QString(
"%1/%2.autosave.h2song" )
423 .arg( fileInfo.absoluteDir().absolutePath() )
425 QString sRecoverFilename =
"";
426 if ( autoSaveFileRecent.exists() &&
427 autoSaveFileRecent.lastModified() >
428 fileInfo.lastModified() ) {
429 sRecoverFilename = autoSaveFileRecent.absoluteFilePath();
430 }
else if ( autoSaveFileOld.exists() &&
431 autoSaveFileOld.lastModified() >
432 fileInfo.lastModified() ) {
433 sRecoverFilename = autoSaveFileOld.absoluteFilePath();
436 if ( ! sRecoverFilename.isEmpty() ) {
440 msgBox.setText( tr(
"There are unsaved changes." ) );
441 msgBox.setInformativeText( tr(
"Do you want to recover them?" ) );
442 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Discard );
443 msgBox.setDefaultButton( QMessageBox::Discard );
444 msgBox.setWindowTitle(
"Hydrogen" );
445 msgBox.setIcon( QMessageBox::Question );
446 int nRet = msgBox.exec();
448 if ( nRet == QMessageBox::Discard ) {
449 sRecoverFilename =
"";
453 if ( ! pCoreActionController->openSong( sFilename, sRecoverFilename ) ) {
457 msgBox.setText( tr(
"Error loading song." ) );
458 msgBox.setWindowTitle(
"Hydrogen" );
459 msgBox.setIcon( QMessageBox::Warning );
470 if ( ! pCoreActionController->openSong( pSong ) ) {
474 msgBox.setText( tr(
"Error loading song." ) );
475 msgBox.setWindowTitle(
"Hydrogen" );
476 msgBox.setIcon( QMessageBox::Warning );
486 auto pCoreActionController = pHydrogen->getCoreActionController();
491 QFileInfo fileInfo( sFilename );
495 QString sBaseName( fileInfo.completeBaseName() );
496 if ( sBaseName.startsWith(
"." ) ) {
497 sBaseName.remove( 0, 1 );
500 QFileInfo autoSaveFile( QString(
"%1/.%2.autosave.h2song" )
501 .arg( fileInfo.absoluteDir().absolutePath() )
503 QString sRecoverFilename =
"";
509 if ( autoSaveFile.exists() ) {
510 sRecoverFilename = autoSaveFile.absoluteFilePath();
513 if ( ! sRecoverFilename.isEmpty() ) {
517 msgBox.setText( tr(
"There are unsaved changes." ) );
518 msgBox.setInformativeText( tr(
"Do you want to recover them?" ) );
519 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Discard );
520 msgBox.setDefaultButton( QMessageBox::Discard );
521 msgBox.setWindowTitle(
"Hydrogen" );
522 msgBox.setIcon( QMessageBox::Question );
523 int nRet = msgBox.exec();
525 if ( nRet == QMessageBox::Discard ) {
526 sRecoverFilename =
"";
530 if ( sRecoverFilename.isEmpty() ) {
534 if ( ! pCoreActionController->openSong( sFilename, sRecoverFilename ) ) {
538 msgBox.setText( tr(
"Error loading song." ) );
539 msgBox.setWindowTitle(
"Hydrogen" );
540 msgBox.setIcon( QMessageBox::Warning );
549 pHydrogen->setIsModified(
true );
564 m_pTab->setCurrentIndex( 2 );
582 m_pTab->setCurrentIndex( 1 );
595 preferencesDialog.exec();
610 if ( pAudioDriver ==
nullptr ) {
611 ERRORLOG(
"AudioDriver is not ready!" );
620 if ( pSong ==
nullptr ) {
628 QString sSongName( pSong->getName() );
629 QString sFilePath( pSong->getFilename() );
632 sFilePath.isEmpty() ) {
635 if ( ! sSongName.isEmpty() ) {
639 QFileInfo fileInfo( sFilePath );
642 sSongName == fileInfo.completeBaseName() ) {
647 sTitle = fileInfo.fileName();
653 sTitle = QString(
"%1 [%2]" ).arg( sSongName )
654 .arg( fileInfo.fileName() );
658 if( pSong->getIsModified() ){
659 sTitle.append(
" (" + tr(
"modified" ) +
")" );
663 QString(
" - " ) + sTitle ) );
705 QApplication::setOverrideCursor(Qt::WaitCursor);
709 QApplication::restoreOverrideCursor();
711 QApplication::setOverrideCursor(Qt::WaitCursor);
714 QApplication::restoreOverrideCursor();
755 switch ( event.
type ) {
761 pListener->playingPatternsChangedEvent();
765 pListener->nextPatternsChangedEvent();
769 pListener->patternModifiedEvent();
773 pListener->songModifiedEvent();
777 pListener->selectedPatternChangedEvent();
781 pListener->selectedInstrumentChangedEvent();
785 pListener->instrumentParametersChangedEvent( event.
value );
789 pListener->midiActivityEvent();
793 pListener->noteOnEvent( event.
value );
797 pListener->errorEvent( event.
value );
801 pListener->XRunEvent();
805 pListener->metronomeEvent( event.
value );
809 pListener->progressEvent( event.
value );
813 pListener->jacksessionEvent( event.
value );
817 pListener->playlistLoadSongEvent( event.
value );
821 pListener->undoRedoActionEvent( event.
value );
825 pListener->tempoChangedEvent( event.
value );
829 pListener->updatePreferencesEvent( event.
value );
833 pListener->updateSongEvent( event.
value );
837 pListener->quitEvent( event.
value );
841 pListener->timelineActivationEvent();
845 pListener->timelineUpdateEvent( event.
value );
849 pListener->jackTransportActivationEvent();
853 pListener->jackTimebaseStateChangedEvent( event.
value );
857 pListener->songModeActivationEvent();
861 pListener->stackedModeActivationEvent( event.
value );
865 pListener->loopModeActivationEvent();
869 pListener->actionModeChangeEvent( event.
value );
873 pListener->gridCellToggledEvent();
877 pListener->drumkitLoadedEvent();
881 pListener->patternEditorLockedEvent();
885 pListener->relocationEvent();
889 pListener->bbtChangedEvent();
893 pListener->songSizeChangedEvent();
897 pListener->driverChangedEvent();
901 pListener->playbackTrackChangedEvent();
905 pListener->soundLibraryChangedEvent();
909 pListener->nextShotEvent();
913 pListener->midiMapChangedEvent();
917 ERRORLOG( QString(
"[onEventQueueTimer] Unhandled event: %1").arg( event.
type ) );
926 auto pInstrument = pSong->getInstrumentList()->
938 pUndoStack->beginMacro( tr(
"Input Midi Note" ) );
955 pUndoStack->push( action );
974 pUndoStack->push( action );
975 pUndoStack->endMacro();
982 if ( pListener !=
nullptr ) {
991 if ( *it == pListener ) {
1002 if ( pListener !=
nullptr ) {
1010 if ( *it == pListener ) {
1024 if ( *it == ppEventListener ) {
1051 QString sPreferencesFilename;
1054 const QString sPreferencesOverwritePath =
1056 if ( sPreferencesOverwritePath.isEmpty() ) {
1059 sPreferencesFilename = sPreferencesOverwritePath;
1062 if ( nValue == 0 ) {
1064 QString(
" Into: ") + sPreferencesFilename );
1065 }
else if ( nValue == 1 ) {
1109#ifdef H2CORE_HAVE_LADSPA
1111 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
1112 m_pLadspaFXProperties[nFX]->hide();
1120 QString(
" From: ") + sPreferencesFilename );
1124 ERRORLOG( QString(
"Unknown event parameter [%1] in HydrogenApp::updatePreferencesEvent" )
1133 auto pSong = pHydrogen->getSong();
1134 if ( pSong ==
nullptr ) {
1138 if ( nValue == 0 ) {
1146 }
else if ( nValue == 1 ) {
1148 QString sFilename = pSong->getFilename();
1154 }
else if ( nValue == 2 ) {
1160 QMessageBox::information(
m_pMainForm,
"Hydrogen", tr(
"Song is read-only.\nUse 'Save as' to enable autosave." ) );
1186 if ( ! pPref->m_bShowExportDrumkitLicenseWarning &&
1187 ! pPref->m_bShowExportDrumkitCopyleftWarning &&
1188 ! pPref->m_bShowExportDrumkitAttributionWarning ) {
1194 auto drumkitLicense = pDrumkit->get_license();
1195 auto contentVector = pDrumkit->summarizeContent();
1197 QStringList additionalLicenses;
1198 bool bCopyleftLicenseFound = drumkitLicense.isCopyleft();
1199 bool bAttributionRequired = drumkitLicense.hasAttribution();
1201 for (
const auto& ccontent : contentVector ) {
1202 QString sSampleLicense = QString(
"%1 (by %2)" )
1203 .arg( ccontent->m_license.getLicenseString() )
1204 .arg( ccontent->m_license.getCopyrightHolder() );
1206 if ( ccontent->m_license != drumkitLicense &&
1207 ! additionalLicenses.contains( sSampleLicense ) ) {
1208 additionalLicenses << sSampleLicense;
1210 bCopyleftLicenseFound = bCopyleftLicenseFound ||
1211 ccontent->m_license.isCopyleft();
1212 bAttributionRequired = bAttributionRequired ||
1213 ccontent->m_license.hasAttribution();
1217 if ( additionalLicenses.size() > 0 &&
1218 pPref->m_bShowExportDrumkitLicenseWarning ) {
1220 QString sMsg = tr(
"Some sample licenses deviate from the one assigned to the overall drumkit [%1] and will be overwritten. Are you sure?" )
1221 .arg( drumkitLicense.getLicenseString() );
1223 sMsg.append(
"\n" );
1224 for (
const auto& sLicense : additionalLicenses ) {
1225 sMsg.append( QString(
"\n- %1" ).arg( sLicense ) );
1228 QMessageBox licenseWarning;
1229 licenseWarning.setWindowTitle( pCommonStrings->getLicenseWarningWindowTitle() );
1230 licenseWarning.setText( sMsg );
1231 licenseWarning.addButton( pCommonStrings->getButtonOk(),
1232 QMessageBox::AcceptRole );
1234 licenseWarning.addButton( pCommonStrings->getMutableDialog(),
1235 QMessageBox::YesRole );
1236 auto pRejectButton =
1237 licenseWarning.addButton( pCommonStrings->getButtonCancel(),
1238 QMessageBox::RejectRole );
1240 licenseWarning.exec();
1242 if ( licenseWarning.clickedButton() == pMuteButton ) {
1243 pPref->m_bShowExportDrumkitLicenseWarning =
false;
1245 else if ( licenseWarning.clickedButton() == pRejectButton ) {
1246 ERRORLOG(
"Aborted on overwriting licenses" );
1251 if ( bCopyleftLicenseFound &&
1252 pPref->m_bShowExportDrumkitCopyleftWarning ) {
1253 QMessageBox copyleftWarning;
1254 copyleftWarning.setWindowTitle( pCommonStrings->getLicenseWarningWindowTitle() );
1255 copyleftWarning.setText( pCommonStrings->getLicenseCopyleftWarning() );
1256 copyleftWarning.addButton( pCommonStrings->getButtonOk(),
1257 QMessageBox::AcceptRole );
1259 copyleftWarning.addButton( pCommonStrings->getMutableDialog(),
1260 QMessageBox::YesRole );
1261 auto pRejectButton =
1262 copyleftWarning.addButton( pCommonStrings->getButtonCancel(),
1263 QMessageBox::RejectRole );
1265 copyleftWarning.exec();
1267 if ( copyleftWarning.clickedButton() == pMuteButton ) {
1268 pPref->m_bShowExportDrumkitCopyleftWarning =
false;
1270 else if ( copyleftWarning.clickedButton() == pRejectButton ) {
1271 ERRORLOG(
"Aborted on copyleft licenses" );
1276 if ( bAttributionRequired &&
1277 pPref->m_bShowExportDrumkitAttributionWarning ) {
1278 QMessageBox attributionWarning;
1279 attributionWarning.setWindowTitle( pCommonStrings->getLicenseWarningWindowTitle() );
1280 attributionWarning.setText( pCommonStrings->getLicenseAttributionWarning() );
1281 attributionWarning.addButton( pCommonStrings->getButtonOk(),
1282 QMessageBox::AcceptRole );
1284 attributionWarning.addButton( pCommonStrings->getMutableDialog(),
1285 QMessageBox::YesRole );
1286 auto pRejectButton =
1287 attributionWarning.addButton( pCommonStrings->getButtonCancel(),
1288 QMessageBox::RejectRole );
1290 attributionWarning.exec();
1292 if ( attributionWarning.clickedButton() == pMuteButton ) {
1293 pPref->m_bShowExportDrumkitAttributionWarning =
false;
1295 else if ( attributionWarning.clickedButton() == pRejectButton ) {
1296 ERRORLOG(
"Aborted on attribution licenses" );
1307 getMetronomeInstrument()->set_volume(
constexpr uint16_t QUEUE_TIMER_PERIOD
Amount of time to pass between successive calls to HydrogenApp::onEventQueueTimer() in milliseconds.
static constexpr int m_nMinimumHeight
bool locateToColumn(int nPatternGroup)
Relocates transport to the beginning of a particular column/Pattern group.
Object handling the communication between the core of Hydrogen and its GUI.
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
std::vector< AddMidiNoteVector > m_addMidiNoteVector
Event pop_event()
Reads out the next event of the EventQueue.
Basic building block for the communication between the core of Hydrogen and its GUI.
EventType type
Specifies the context the event is create in and which function should be triggered to handle it.
int value
Additional information to describe the actual context of the engine.
static QString usr_config_path()
static QString untitled_song_name()
returns untitled song name
static const QString & getPreferencesOverwritePath()
static bool rm(const QString &path, bool recursive=false, bool bSilent=false)
remove a path
static QString empty_song_path()
Provides the full path to the current empty song.
static QString tmp_dir()
returns temp path
std::shared_ptr< Song > getSong() const
Get the current song.
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
AudioEngine * getAudioEngine() const
AudioOutput * getAudioOutput() const
Used to display audio driver info.
CoreActionController * getCoreActionController() const
A note plays an associated instrument with a velocity left and right pan.
int get_position() const
__position accessor
Octave get_octave()
__octave accessor
float get_lead_lag() const
__lead_lag accessor
int get_length() const
__length accessor
float get_velocity() const
__velocity accessor
Key get_key()
__key accessor
float get_probability() const
float getPan() const
get pan of the note.
int get_instrument_id() const
__instrument_id accessor
Manager for User Preferences File (singleton)
WindowProperties getAudioEngineInfoProperties()
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
void setLastOpenTab(int n)
WindowProperties getMainFormProperties()
InterfaceTheme::Layout getDefaultUILayout()
WindowProperties getDirectorProperties()
WindowProperties getMixerProperties()
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ AudioTab
Any option in the Audio tab appeared.
WindowProperties getPatternEditorProperties()
WindowProperties getLadspaProperties(unsigned nFX)
WindowProperties getInstrumentRackProperties()
WindowProperties getSongEditorProperties()
WindowProperties getPlaylistDialogProperties()
AudioEngineInfoForm * m_pAudioEngineInfoForm
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
std::shared_ptr< CommonStrings > getCommonStrings()
std::shared_ptr< CommonStrings > m_pCommonStrings
void updateEventListeners()
int m_nPreferencesUpdateTimeout
QUndoStack * m_pUndoStack
std::vector< EventListener * > m_eventListeners
void showAudioEngineInfoForm()
void showFilesystemInfoForm()
QScrollArea * m_pMainScrollArea
Used for accessibility reasons to show scroll bars in case Hydrogen has to be shrunk below its minimu...
SongEditorPanel * m_pSongEditorPanel
InstrumentRack * getInstrumentRack()
virtual void updatePreferencesEvent(int nValue) override
Handles the loading and saving of the H2Core::Preferences from the core part of H2Core::Hydrogen.
static bool openSong(QString sFilename)
void onPreferencesChanged(H2Core::Preferences::Changes changes)
PlayerControl * m_pPlayerControl
PlaylistDialog * m_pPlaylistDialog
virtual void updateSongEvent(int nValue) override
Refreshes and updates the GUI after the Song was changed in the core part of Hydrogen.
static bool checkDrumkitLicense(std::shared_ptr< H2Core::Drumkit > pDrumkit)
PatternEditorPanel * m_pPatternEditorPanel
void showStatusBarMessage(const QString &sMessage, const QString &sCaller="")
QVBoxLayout * m_pMainVBox
QTimer * m_pPreferencesUpdateTimer
virtual void drumkitLoadedEvent() override
void showMixer(bool bShow)
void showPlaylistDialog()
void onEventQueueTimer()
Function called every QUEUE_TIMER_PERIOD millisecond to pop all Events from the EventQueue and invoke...
void preferencesChanged(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
std::set< EventListener * > m_eventListenersToRemove
void changePreferences(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
QTimer * m_pEventQueueTimer
void showInstrumentPanel(bool)
void showSampleEditor(QString name, int mSelectedComponemt, int mSelectedLayer)
std::set< EventListener * > m_eventListenersToAdd
static bool recoverEmptySong()
Specialized version of openSong( QString sFilename ) trying to open the autosave file corresponding t...
void currentTabChanged(int)
void removeEventListener(EventListener *pListener)
virtual void songModifiedEvent() override
InstrumentRack * m_pInstrumentRack
virtual void XRunEvent() override
H2Core::WindowProperties getWindowProperties(QWidget *pWindow)
void showPreferencesDialog()
void cleanupTemporaryFiles()
Removes temporary files that were created for undo'ing things.
H2Core::Preferences::Changes m_bufferedChanges
void setWindowProperties(QWidget *pWindow, H2Core::WindowProperties &prop, unsigned flags=SetAll)
SampleEditor * m_pSampleEditor
void setupSinglePanedInterface()
void propagatePreferences()
HydrogenApp(MainForm *pMainForm)
FilesystemInfoForm * m_pFilesystemInfoForm
static HydrogenApp * m_pInstance
HydrogenApp instance.
bool m_bHideKeyboardCursor
static constexpr int m_nMinimumHeight
450 - InstrumentEditor layer view + 24 - tab button
void onPreferencesChanged(H2Core::Preferences::Changes changes)
static constexpr int m_nMinimumHeight
This dialog is used to use the H2PlayList.
This dialog is used to preview audiofiles.
static constexpr int m_nMinimumHeight
static constexpr int m_nMinimumHeight
static constexpr int m_nMinimumHeight
Default value of Preferences::m_nSongEditorGridHeight * 5 (patterns)
#define MAX_FX
Maximum number of effects.
@ EVENT_RELOCATION
Triggered in case there is a relocation of the transport position while trasnsport is not rolling.
@ EVENT_PLAYBACK_TRACK_CHANGED
@ EVENT_INSTRUMENT_PARAMETERS_CHANGED
Some parameters of an instrument have been changed.
@ EVENT_METRONOME
Triggered when a metronome note is passed to the H2Core::Sampler.
@ EVENT_PLAYLIST_LOADSONG
@ 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_PLAYING_PATTERNS_CHANGED
The list of currently played patterns (AudioEngine::getPlayingPatterns()) did change.
@ EVENT_ACTION_MODE_CHANGE
Switches between select mode (0) and draw mode (1) in the *SongEditor.
@ EVENT_SELECTED_INSTRUMENT_CHANGED
@ EVENT_PATTERN_MODIFIED
A pattern was added, deleted, or modified.
@ EVENT_TIMELINE_ACTIVATION
Enables/disables the usage of the Timeline.
@ EVENT_SOUND_LIBRARY_CHANGED
@ EVENT_TIMELINE_UPDATE
Tells the GUI some parts of the Timeline (tempo markers or tags) were modified.
@ EVENT_SONG_SIZE_CHANGED
@ EVENT_UPDATE_SONG
Event triggering HydrogenApp::updateSongEvent() whenever the Song was changed outside of the GUI,...
@ EVENT_BBT_CHANGED
The coarse grained transport position in beats and bars did change.
@ EVENT_LOOP_MODE_ACTIVATION
Toggles the button indicating the usage loop mode.
@ EVENT_GRID_CELL_TOGGLED
@ EVENT_NONE
Fallback event.
@ EVENT_NEXT_PATTERNS_CHANGED
Used in Song::PatternMode::Stacked to indicate that a either AudioEngine::getNextPatterns() did chang...
@ 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 ...
@ EVENT_JACK_TIMEBASE_STATE_CHANGED
Toggles the button indicating the usage JACK Timebase control and informs the GUI about a state chang...
@ EVENT_PATTERN_EDITOR_LOCKED
Locks the PatternEditor on the pattern currently played back.
@ EVENT_SONG_MODE_ACTIVATION
@ EVENT_SELECTED_PATTERN_CHANGED
Another pattern was selected via MIDI or the GUI without affecting the audio transport.
@ EVENT_STACKED_MODE_ACTIVATION
Song::PatternMode::Stacked (0) or Song::PatternMode::Selected (1) was activated.
std::string get_version()
Returns the current Hydrogen version string.