23#include <core/config.h>
71 : m_pMainForm( pMainForm )
73 , m_pPatternEditorPanel( nullptr )
74 , m_pAudioEngineInfoForm( nullptr )
75 , m_pSongEditorPanel( nullptr )
76 , m_pPlayerControl( nullptr )
77 , m_pPlaylistDialog( nullptr )
78 , m_pSampleEditor( nullptr )
79 , m_pDirector( nullptr )
80 , m_nPreferencesUpdateTimeout( 100 )
146 QRect oldGeometry = pWindow->geometry();
150 pWindow->move( prop.
x, prop.
y );
158 QRect newGeometry = pWindow->geometry();
159 if ( !( flags &
SetX ) ) {
160 newGeometry.setX( oldGeometry.x() );
162 if ( !( flags &
SetY ) ) {
163 newGeometry.setY( oldGeometry.y() );
166 newGeometry.setWidth( oldGeometry.width() );
169 newGeometry.setHeight( oldGeometry.height() );
173 if ( pWindow->minimumSize() == pWindow->maximumSize() ) {
174 if ( pWindow->isFullScreen()) {
175 pWindow->showNormal();
179 if ( oldGeometry != newGeometry ) {
180 pWindow->setGeometry( newGeometry );
187 prop.
x = pWindow->x();
188 prop.
y = pWindow->y();
189 prop.
height = pWindow->height();
190 prop.
width = pWindow->width();
191 prop.
visible = pWindow->isVisible();
221 #ifdef H2CORE_HAVE_LADSPA
222 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
223 delete m_pLadspaFXProperties[nFX];
252 m_pTab =
new QTabWidget(
nullptr );
253 m_pTab->setObjectName(
"TabbedInterface" );
256 if( layout == InterfaceTheme::Layout::SinglePane ) {
268 if( layout == InterfaceTheme::Layout::Tabbed ) {
274 pSouthPanel->setObjectName(
"SouthPanel" );
275 QHBoxLayout *pEditorHBox =
new QHBoxLayout();
276 pEditorHBox->setSpacing( 5 );
277 pEditorHBox->setMargin( 0 );
278 pSouthPanel->setLayout( pEditorHBox );
285 if( layout == InterfaceTheme::Layout::Tabbed ){
286 m_pTab->setMovable(
false );
287 m_pTab->setTabsClosable(
false );
288 m_pTab->addTab( pSouthPanel, tr(
"Instrument + Pattern") );
316 if( layout == InterfaceTheme::Layout::SinglePane ) {
333 if ( layout != InterfaceTheme::Layout::SinglePane ) {
338 if( layout == InterfaceTheme::Layout::Tabbed ){
344#ifdef H2CORE_HAVE_LADSPA
346 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
348 m_pLadspaFXProperties[nFX]->hide();
354 if( layout == InterfaceTheme::Layout::Tabbed ){
376#ifdef H2CORE_HAVE_LADSPA
377 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
378 m_pLadspaFXProperties[nFX]->close();
385 auto pCoreActionController = pHydrogen->getCoreActionController();
389 QFileInfo fileInfo( sFilename );
392 if ( fileInfo.isRelative() ) {
393 sFilename = fileInfo.absoluteFilePath();
398 QString sBaseName( fileInfo.completeBaseName() );
399 if ( sBaseName.startsWith(
"." ) ) {
400 sBaseName.remove( 0, 1 );
404 QFileInfo autoSaveFileRecent( QString(
"%1/.%2.autosave.h2song" )
405 .arg( fileInfo.absoluteDir().absolutePath() )
408 QFileInfo autoSaveFileOld( QString(
"%1/%2.autosave.h2song" )
409 .arg( fileInfo.absoluteDir().absolutePath() )
411 QString sRecoverFilename =
"";
412 if ( autoSaveFileRecent.exists() &&
413 autoSaveFileRecent.lastModified() >
414 fileInfo.lastModified() ) {
415 sRecoverFilename = autoSaveFileRecent.absoluteFilePath();
416 }
else if ( autoSaveFileOld.exists() &&
417 autoSaveFileOld.lastModified() >
418 fileInfo.lastModified() ) {
419 sRecoverFilename = autoSaveFileOld.absoluteFilePath();
422 if ( ! sRecoverFilename.isEmpty() ) {
426 msgBox.setText( tr(
"There are unsaved changes." ) );
427 msgBox.setInformativeText( tr(
"Do you want to recover them?" ) );
428 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Discard );
429 msgBox.setDefaultButton( QMessageBox::Discard );
430 msgBox.setWindowTitle(
"Hydrogen" );
431 msgBox.setIcon( QMessageBox::Question );
432 int nRet = msgBox.exec();
434 if ( nRet == QMessageBox::Discard ) {
435 sRecoverFilename =
"";
439 if ( ! pCoreActionController->openSong( sFilename, sRecoverFilename ) ) {
443 msgBox.setText( tr(
"Error loading song." ) );
444 msgBox.setWindowTitle(
"Hydrogen" );
445 msgBox.setIcon( QMessageBox::Warning );
456 if ( ! pCoreActionController->openSong( pSong ) ) {
460 msgBox.setText( tr(
"Error loading song." ) );
461 msgBox.setWindowTitle(
"Hydrogen" );
462 msgBox.setIcon( QMessageBox::Warning );
472 auto pCoreActionController = pHydrogen->getCoreActionController();
477 QFileInfo fileInfo( sFilename );
481 QString sBaseName( fileInfo.completeBaseName() );
482 if ( sBaseName.startsWith(
"." ) ) {
483 sBaseName.remove( 0, 1 );
486 QFileInfo autoSaveFile( QString(
"%1/.%2.autosave.h2song" )
487 .arg( fileInfo.absoluteDir().absolutePath() )
489 QString sRecoverFilename =
"";
495 if ( autoSaveFile.exists() ) {
496 sRecoverFilename = autoSaveFile.absoluteFilePath();
499 if ( ! sRecoverFilename.isEmpty() ) {
503 msgBox.setText( tr(
"There are unsaved changes." ) );
504 msgBox.setInformativeText( tr(
"Do you want to recover them?" ) );
505 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Discard );
506 msgBox.setDefaultButton( QMessageBox::Discard );
507 msgBox.setWindowTitle(
"Hydrogen" );
508 msgBox.setIcon( QMessageBox::Question );
509 int nRet = msgBox.exec();
511 if ( nRet == QMessageBox::Discard ) {
512 sRecoverFilename =
"";
516 if ( sRecoverFilename.isEmpty() ) {
520 if ( ! pCoreActionController->openSong( sFilename, sRecoverFilename ) ) {
524 msgBox.setText( tr(
"Error loading song." ) );
525 msgBox.setWindowTitle(
"Hydrogen" );
526 msgBox.setIcon( QMessageBox::Warning );
535 pHydrogen->setIsModified(
true );
549 if ( layout == InterfaceTheme::Layout::Tabbed ) {
550 m_pTab->setCurrentIndex( 2 );
567 if ( layout == InterfaceTheme::Layout::Tabbed ) {
568 m_pTab->setCurrentIndex( 1 );
581 preferencesDialog.exec();
596 if ( pAudioDriver ==
nullptr ) {
597 ERRORLOG(
"AudioDriver is not ready!" );
606 if ( pSong ==
nullptr ) {
614 QString sSongName( pSong->getName() );
615 QString sFilePath( pSong->getFilename() );
618 sFilePath.isEmpty() ) {
621 if ( ! sSongName.isEmpty() ) {
625 QFileInfo fileInfo( sFilePath );
628 sSongName == fileInfo.completeBaseName() ) {
633 sTitle = fileInfo.fileName();
639 sTitle = QString(
"%1 [%2]" ).arg( sSongName )
640 .arg( fileInfo.fileName() );
644 if( pSong->getIsModified() ){
645 sTitle.append(
" (" + tr(
"modified" ) +
")" );
649 QString(
" - " ) + sTitle ) );
691 QApplication::setOverrideCursor(Qt::WaitCursor);
695 QApplication::restoreOverrideCursor();
697 QApplication::setOverrideCursor(Qt::WaitCursor);
700 QApplication::restoreOverrideCursor();
729 switch ( event.
type ) {
891 ERRORLOG( QString(
"[onEventQueueTimer] Unhandled event: %1").arg( event.
type ) );
900 auto pInstrument = pSong->getInstrumentList()->
912 pUndoStack->beginMacro( tr(
"Input Midi Note" ) );
929 pUndoStack->push( action );
948 pUndoStack->push( action );
949 pUndoStack->endMacro();
983 QString sPreferencesFilename;
986 const QString sPreferencesOverwritePath =
988 if ( sPreferencesOverwritePath.isEmpty() ) {
991 sPreferencesFilename = sPreferencesOverwritePath;
996 QString(
" Into: ") + sPreferencesFilename );
997 }
else if ( nValue == 1 ) {
1028 if ( layout != InterfaceTheme::Layout::SinglePane ) {
1041#ifdef H2CORE_HAVE_LADSPA
1043 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
1044 m_pLadspaFXProperties[nFX]->hide();
1052 QString(
" From: ") + sPreferencesFilename );
1056 ERRORLOG( QString(
"Unknown event parameter [%1] in HydrogenApp::updatePreferencesEvent" )
1065 auto pSong = pHydrogen->getSong();
1066 if ( pSong ==
nullptr ) {
1070 if ( nValue == 0 ) {
1078 }
else if ( nValue == 1 ) {
1080 QString sFilename = pSong->getFilename();
1086 }
else if ( nValue == 2 ) {
1092 QMessageBox::information(
m_pMainForm,
"Hydrogen", tr(
"Song is read-only.\nUse 'Save as' to enable autosave." ) );
1118 if ( ! pPref->m_bShowExportDrumkitLicenseWarning &&
1119 ! pPref->m_bShowExportDrumkitCopyleftWarning &&
1120 ! pPref->m_bShowExportDrumkitAttributionWarning ) {
1126 auto drumkitLicense = pDrumkit->get_license();
1127 auto contentVector = pDrumkit->summarizeContent();
1129 QStringList additionalLicenses;
1130 bool bCopyleftLicenseFound = drumkitLicense.isCopyleft();
1131 bool bAttributionRequired = drumkitLicense.hasAttribution();
1133 for (
const auto& ccontent : contentVector ) {
1134 QString sSampleLicense = QString(
"%1 (by %2)" )
1135 .arg( ccontent->m_license.getLicenseString() )
1136 .arg( ccontent->m_license.getCopyrightHolder() );
1138 if ( ccontent->m_license != drumkitLicense &&
1139 ! additionalLicenses.contains( sSampleLicense ) ) {
1140 additionalLicenses << sSampleLicense;
1142 bCopyleftLicenseFound = bCopyleftLicenseFound ||
1143 ccontent->m_license.isCopyleft();
1144 bAttributionRequired = bAttributionRequired ||
1145 ccontent->m_license.hasAttribution();
1149 if ( additionalLicenses.size() > 0 &&
1150 pPref->m_bShowExportDrumkitLicenseWarning ) {
1152 QString sMsg = tr(
"Some sample licenses deviate from the one assigned to the overall drumkit [%1] and will be overwritten. Are you sure?" )
1153 .arg( drumkitLicense.getLicenseString() );
1155 sMsg.append(
"\n" );
1156 for (
const auto& sLicense : additionalLicenses ) {
1157 sMsg.append( QString(
"\n- %1" ).arg( sLicense ) );
1160 QMessageBox licenseWarning;
1161 licenseWarning.setWindowTitle( pCommonStrings->getLicenseWarningWindowTitle() );
1162 licenseWarning.setText( sMsg );
1163 licenseWarning.addButton( pCommonStrings->getButtonOk(),
1164 QMessageBox::AcceptRole );
1166 licenseWarning.addButton( pCommonStrings->getMutableDialog(),
1167 QMessageBox::YesRole );
1168 auto pRejectButton =
1169 licenseWarning.addButton( pCommonStrings->getButtonCancel(),
1170 QMessageBox::RejectRole );
1172 licenseWarning.exec();
1174 if ( licenseWarning.clickedButton() == pMuteButton ) {
1175 pPref->m_bShowExportDrumkitLicenseWarning =
false;
1177 else if ( licenseWarning.clickedButton() == pRejectButton ) {
1178 ERRORLOG(
"Aborted on overwriting licenses" );
1183 if ( bCopyleftLicenseFound &&
1184 pPref->m_bShowExportDrumkitCopyleftWarning ) {
1185 QMessageBox copyleftWarning;
1186 copyleftWarning.setWindowTitle( pCommonStrings->getLicenseWarningWindowTitle() );
1187 copyleftWarning.setText( pCommonStrings->getLicenseCopyleftWarning() );
1188 copyleftWarning.addButton( pCommonStrings->getButtonOk(),
1189 QMessageBox::AcceptRole );
1191 copyleftWarning.addButton( pCommonStrings->getMutableDialog(),
1192 QMessageBox::YesRole );
1193 auto pRejectButton =
1194 copyleftWarning.addButton( pCommonStrings->getButtonCancel(),
1195 QMessageBox::RejectRole );
1197 copyleftWarning.exec();
1199 if ( copyleftWarning.clickedButton() == pMuteButton ) {
1200 pPref->m_bShowExportDrumkitCopyleftWarning =
false;
1202 else if ( copyleftWarning.clickedButton() == pRejectButton ) {
1203 ERRORLOG(
"Aborted on copyleft licenses" );
1208 if ( bAttributionRequired &&
1209 pPref->m_bShowExportDrumkitAttributionWarning ) {
1210 QMessageBox attributionWarning;
1211 attributionWarning.setWindowTitle( pCommonStrings->getLicenseWarningWindowTitle() );
1212 attributionWarning.setText( pCommonStrings->getLicenseAttributionWarning() );
1213 attributionWarning.addButton( pCommonStrings->getButtonOk(),
1214 QMessageBox::AcceptRole );
1216 attributionWarning.addButton( pCommonStrings->getMutableDialog(),
1217 QMessageBox::YesRole );
1218 auto pRejectButton =
1219 attributionWarning.addButton( pCommonStrings->getButtonCancel(),
1220 QMessageBox::RejectRole );
1222 attributionWarning.exec();
1224 if ( attributionWarning.clickedButton() == pMuteButton ) {
1225 pPref->m_bShowExportDrumkitAttributionWarning =
false;
1227 else if ( attributionWarning.clickedButton() == pRejectButton ) {
1228 ERRORLOG(
"Aborted on attribution licenses" );
1239 getMetronomeInstrument()->set_volume(
constexpr uint16_t QUEUE_TIMER_PERIOD
Amount of time to pass between successive calls to HydrogenApp::onEventQueueTimer() in milliseconds.
virtual void tempoChangedEvent(int nValue)
virtual void midiActivityEvent()
virtual void metronomeEvent(int nValue)
virtual void actionModeChangeEvent(int nValue)
virtual void timelineActivationEvent()
virtual void midiMapChangedEvent()
virtual void jackTransportActivationEvent()
virtual void loopModeActivationEvent()
virtual void instrumentParametersChangedEvent(int nInstrumentNumber)
virtual void relocationEvent()
virtual void songModeActivationEvent()
virtual void updatePreferencesEvent(int nValue)
virtual void playbackTrackChangedEvent()
virtual void stateChangedEvent(H2Core::AudioEngine::State state)
virtual void noteOnEvent(int nInstrument)
virtual void soundLibraryChangedEvent()
virtual void nextShotEvent()
virtual void bbtChangedEvent()
virtual void selectedInstrumentChangedEvent()
virtual void patternModifiedEvent()
virtual void playingPatternsChangedEvent()
virtual void stackedModeActivationEvent(int nValue)
virtual void nextPatternsChangedEvent()
virtual void jackTimebaseStateChangedEvent()
virtual void patternEditorLockedEvent()
virtual void progressEvent(int nValue)
virtual void errorEvent(int nErrorCode)
virtual void playlistLoadSongEvent(int nIndex)
virtual void timelineUpdateEvent(int nValue)
virtual void selectedPatternChangedEvent()
virtual void updateSongEvent(int nValue)
virtual void undoRedoActionEvent(int nValue)
virtual void jacksessionEvent(int nValue)
virtual void songSizeChangedEvent()
virtual void songModifiedEvent()
virtual void quitEvent(int nValue)
virtual void gridCellToggledEvent()
virtual void drumkitLoadedEvent()
virtual void driverChangedEvent()
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()
returns user 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
int m_nPreferencesUpdateTimeout
QUndoStack * m_pUndoStack
void showAudioEngineInfoForm()
void showFilesystemInfoForm()
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
std::vector< EventListener * > m_EventListeners
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.
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)
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
void update_instrument_checkbox(bool show)
void update_mixer_checkbox()
void onPreferencesChanged(H2Core::Preferences::Changes changes)
void update_director_checkbox()
void update_playlist_checkbox()
PatternEditorRuler * getPatternEditorRuler()
void updatePosition(bool bForce=false)
Queries the audio engine to update the current position of the playhead.
void showStatusBarMessage(const QString &msg, const QString &sCaller="")
This dialog is used to use the H2PlayList.
This dialog is used to preview audiofiles.
#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 master and informs the GUI about a state change...
@ 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.