76#include <sys/socket.h>
79#ifdef H2CORE_HAVE_LASH
80#include <lash-1.0/lash/lash.h>
92 : QMainWindow( nullptr )
98 setObjectName(
"MainForm" );
101 if (::socketpair(AF_UNIX, SOCK_STREAM, 0,
sigusr1Fd)) {
102 qFatal(
"Couldn't create HUP socketpair");
104 snUsr1 =
new QSocketNotifier(
sigusr1Fd[1], QSocketNotifier::Read,
this);
116 if ( ! pHydrogen->isUnderSessionManagement() ){
117 std::shared_ptr<H2Core::Song>pSong =
nullptr;
119 if ( sSongFilename.isEmpty() ) {
120 if ( pPref->isRestoreLastSongEnabled() ) {
121 sSongFilename = pPref->getLastSongFilename();
126 if ( !sSongFilename.isEmpty() ) {
134 if ( sSongFilename.isEmpty() || ! bRet ) {
140 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
146 h2app->addEventListener(
this );
152 h2app->showStatusBarMessage( tr(
"Hydrogen Ready.") );
158 h2app->getMixer()->installEventFilter (
this);
159 h2app->getPatternEditorPanel()->installEventFilter (
this);
160 h2app->getSongEditorPanel()->installEventFilter (
this);
161 h2app->getPlayerControl()->installEventFilter(
this);
163 h2app->getAudioEngineInfoForm()->installEventFilter(
this);
164 h2app->getDirector()->installEventFilter(
this);
166 installEventFilter(
this );
171#ifdef H2CORE_HAVE_LASH
173 if ( pPref->useLash() ){
179 if ( pPref->m_sMidiDriver ==
"ALSA" ) {
184 lashPollTimer =
new QTimer(
this);
185 connect( lashPollTimer, SIGNAL( timeout() ),
this, SLOT(
onLashPollTimer() ) );
186 lashPollTimer->start(500);
192 QTimer *playlistDisplayTimer =
new QTimer(
this);
194 playlistDisplayTimer->start(30000);
198 pHydrogen->setBcOffsetAdjust();
204 if ( pPref->isRestoreLastPlaylistEnabled() &&
205 ! pPref->getLastPlaylistFilename().isEmpty() ) {
206 bool bLoadSuccessful =
h2app->getPlayListDialog()->loadListByFileName(
207 pPref->getLastPlaylistFilename() );
208 if ( bLoadSuccessful ) {
209 if ( pPref->getPlaylistDialogProperties().visible ){
217 _ERRORLOG( QString(
"Unable to load last playlist [%1]" )
218 .arg( pPref->getLastPlaylistFilename() ) );
227 if ( pHydrogen->getAudioOutput() ==
nullptr ||
228 dynamic_cast<NullDriver*
>(pHydrogen->getAudioOutput()) !=
nullptr ) {
229 QMessageBox::warning(
230 this,
"Hydrogen", QString(
"%1 [%2]\n%3" )
231 .arg( pCommonStrings->getAudioDriverStartError() )
233 .arg( pCommonStrings->getAudioDriverErrorHint() ) );
244 if ( ! pHydrogen->getSong()->getIsModified() ) {
257 if (
h2app !=
nullptr) {
271 QMenuBar *pMenubar =
new QMenuBar(
this );
272 pMenubar->setObjectName(
"MainMenu" );
273 setMenuBar( pMenubar );
276 m_pFileMenu = pMenubar->addMenu( tr(
"Pro&ject" ) );
282 QString sLabelNew, sLabelOpen, sLabelOpenRecent, sLabelSaveAs, sLabelOpenDemo;
284 if ( bUnderSessionManagement ) {
288 sLabelNew = tr(
"Replace With &New Song" );
293 sLabelOpen = tr(
"Imp&ort Into Session" );
298 sLabelOpenRecent = tr(
"Import &Recent Into Session" );
304 sLabelSaveAs = tr(
"Export From Session &As..." );
306 sLabelNew = tr(
"&New" );
307 sLabelOpen = tr(
"&Open" );
308 sLabelOpenRecent = tr(
"Open &Recent" );
309 sLabelSaveAs = tr(
"Save &As..." );
310 sLabelOpenDemo = tr(
"Open &Demo" );
315 pActionFileNew->setShortcut( QKeySequence(
"Ctrl+N" ) );
325 pActionFileOpen->setShortcut( QKeySequence(
"Ctrl+O" ) );
326 if ( ! bUnderSessionManagement ) {
329 pActionFileDemo->setShortcut( QKeySequence(
"Ctrl+D" ) );
337 pActionFileSave->setShortcut( QKeySequence(
"Ctrl+S" ) );
340 pActionFileSaveAs->setShortcut( QKeySequence(
"Ctrl+Shift+S" ) );
346 pActionOpenPattern->setShortcut( QKeySequence(
"Ctrl+Shift+P" ) );
347 auto pActionExportPattern =
m_pFileMenu->addAction(
349 pActionExportPattern->setShortcut( QKeySequence(
"Ctrl+P" ) );
355 pActionExportMidi->setShortcut( QKeySequence(
"Ctrl+M" ) );
358 pActionExportSong->setShortcut( QKeySequence(
"Ctrl+E" ) );
359 auto pActionExportLilyPond =
m_pFileMenu->addAction(
361 pActionExportLilyPond->setShortcut( QKeySequence(
"Ctrl+L" ) );
369 pActionQuit->setShortcut( QKeySequence(
"Ctrl+Q" ) );
380 pActionUndo->setShortcut( QKeySequence(
"Ctrl+Z" ) );
383 pActionRedo->setShortcut( QKeySequence(
"Shift+Ctrl+Z" ) );
458 pActionFullScreen->setShortcut( QKeySequence(
"Alt+F" ) );
486 pActionPreferences->setShortcut( QKeySequence(
"Alt+P" ) );
517 pActionUserManual->setShortcut( QKeySequence(
"Ctrl+?" ) );
520 pActionAbout->setShortcut( QKeySequence( tr(
"",
"Info|About") ) );
529 if ( nAutosavesPerHour > 0 ) {
530 if ( nAutosavesPerHour > 360 ) {
531 ERRORLOG( QString(
"Too many autosaves per hour set [%1]. Using 360 - once a second - instead." )
532 .arg( nAutosavesPerHour ) );
533 nAutosavesPerHour = 360;
536 static_cast<float>(nAutosavesPerHour) ) );
544#ifdef H2CORE_HAVE_LASH
550 WARNINGLOG(
"[LASH] Not connected to server!");
554 bool keep_running =
true;
558 std::string songFilename;
559 QString filenameSong;
564 switch (lash_event_get_type(event)) {
570 songFilename.append(lash_event_get_string(event));
571 songFilename.append(
"/hydrogen.h2song");
573 filenameSong = QString::fromLocal8Bit( songFilename.c_str() );
574 song->setFilename( filenameSong );
581 case LASH_Restore_File:
583 songFilename.append(lash_event_get_string(event));
584 songFilename.append(
"/hydrogen.h2song");
586 INFOLOG( QString(
"[LASH] Restore file: %1")
587 .arg( songFilename.c_str() ) );
589 filenameSong = QString::fromLocal8Bit( songFilename.c_str() );
600 keep_running =
false;
610 lash_event_destroy(event);
616 lashPollTimer->stop();
625 QMessageBox donationDialog;
626 donationDialog.setText( tr(
"Hydrogen is an open source project which is developed by multiple people in their spare time. By making a donation you can say 'thank you' to the involved persons." ) );
627 donationDialog.setStandardButtons( QMessageBox::Cancel );
628 donationDialog.addButton( tr(
"&Donate!" ), QMessageBox::AcceptRole );
630 int nRet = donationDialog.exec();
632 if ( nRet == QMessageBox::AcceptRole ) {
633 QDesktopServices::openUrl(QUrl::fromEncoded(
"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=sebastian%2emoors%40gmail%2ecom&lc=DE&item_name=Hydrogen%20donation&no_note=0¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHostedGuest"));
666 if ( bUnderSessionManagement ) {
671 QMessageBox confirmationBox;
672 confirmationBox.setText( tr(
"Replace current song with empty one?" ) );
673 confirmationBox.setInformativeText( tr(
"You won't be able to undo this action after saving the new song! Please export the current song from the session first in order to keep it." ) );
674 confirmationBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
675 confirmationBox.setDefaultButton( QMessageBox::No );
677 int confirmationChoice = confirmationBox.exec();
679 if ( confirmationChoice == QMessageBox::No ) {
689 QString sBaseName( fileInfo.completeBaseName() );
690 if ( sBaseName.startsWith(
"." ) ) {
691 sBaseName.remove( 0, 1 );
693 QFileInfo autoSaveFile( QString(
"%1/.%2.autosave.h2song" )
694 .arg( fileInfo.absoluteDir().absolutePath() )
696 if ( autoSaveFile.exists() ) {
700 h2app->openSong( pSong );
712 auto pSong = pHydrogen->getSong();
714 if ( pSong ==
nullptr ) {
718 const bool bUnderSessionManagement = pHydrogen->isUnderSessionManagement();
719 if ( bUnderSessionManagement &&
720 pHydrogen->getSessionDrumkitNeedsRelinking() ) {
731 if ( QMessageBox::information(
733 tr(
"\nThere have been recent changes to the drumkit settings.\n"
734 "The session needs to be saved before exporting will can be continued.\n" ),
735 QMessageBox::Save | QMessageBox::Cancel,
737 == QMessageBox::Cancel ) {
738 INFOLOG(
"Exporting cancelled at relinking" );
754 fd.setFileMode( QFileDialog::AnyFile );
756 fd.setAcceptMode( QFileDialog::AcceptSave );
757 fd.setDirectory( sPath );
759 if ( bUnderSessionManagement ) {
760 fd.setWindowTitle( tr(
"Export song from Session" ) );
762 fd.setWindowTitle( tr(
"Save song" ) );
767 QString sDefaultFilename;
771 QString sLastFilename = pSong->getFilename();
772 QString sLastLoadedDrumkitPath = pSong->getLastLoadedDrumkitPath();
776 }
else if ( sLastFilename.isEmpty() ) {
777 sDefaultFilename = pHydrogen->getSong()->getName();
779 QFileInfo fileInfo( sLastFilename );
780 sDefaultFilename = fileInfo.completeBaseName();
784 fd.selectFile( sDefaultFilename );
786 if (fd.exec() == QDialog::Accepted) {
787 QString sNewFilename = fd.selectedFiles().first();
789 if ( ! sNewFilename.isEmpty() ) {
796#ifdef H2CORE_HAVE_OSC
803 if ( bUnderSessionManagement ) {
804 pHydrogen->setSessionIsExported(
true );
807 QMessageBox::warning(
this,
"Hydrogen",
808 tr(
"Drumkit [%1] used in session could not found on your system. Please install it in to make the exported song work properly." )
809 .arg( pSong->getLastLoadedDrumkitName() ) );
824#ifdef H2CORE_HAVE_OSC
828 if ( bUnderSessionManagement ) {
829 pSong->setFilename( sLastFilename );
831 h2app->showStatusBarMessage( tr(
"Song exported as: ") + sDefaultFilename );
832 pHydrogen->setSessionIsExported(
false );
835 h2app->showStatusBarMessage( tr(
"Song saved as: ") + sDefaultFilename );
838 h2app->showStatusBarMessage( tr(
"Song saved as: ") + sDefaultFilename );
841 h2app->updateWindowTitle();
856 auto pSong = pHydrogen->getSong();
858 if ( pSong ==
nullptr ) {
862 auto pCoreActionController = pHydrogen->getCoreActionController();
863 QString sFilename = pSong->getFilename();
865 if ( sNewFilename.isEmpty() &&
866 ( sFilename.isEmpty() ||
875 if ( pSong->hasMissingSamples() ) {
876 if ( QMessageBox::information(
this,
"Hydrogen",
877 tr(
"Some samples used by this song failed to load. If you save the song now "
878 "these missing samples will be removed from the song entirely.\n"
879 "Are you sure you want to save?" ),
880 QMessageBox::Save | QMessageBox::Cancel,
882 == QMessageBox::Cancel ) {
885 pSong->clearMissingSamples();
892 if ( sNewFilename.isEmpty() ) {
893 bSaved = pCoreActionController->saveSong();
895 bSaved = pCoreActionController->saveSongAs( sNewFilename );
899 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save song.") );
903 h2app->showStatusBarMessage( tr(
"Song saved into") + QString(
": ") +
936 QDesktopServices::openUrl(QString(
"https://github.com/hydrogen-music/hydrogen/issues"));
944 QStringList languages;
946 if ( !sPreferredLanguage.isNull() ) {
947 languages << sPreferredLanguage;
949 languages << QLocale::system().uiLanguages()
953 for ( QString sLang : languages ) {
954 QStringList sCandidates ( sLang );
955 QStringList s = sLang.split(
'-');
956 if ( s.size() != 1 ) {
959 for ( QString sCandidate : sCandidates ) {
960 QString sManualPath = QString(
"%1/manual_%2.html" ) .arg( sDocPath ).arg( sCandidate );
962 QDesktopServices::openUrl( QUrl::fromLocalFile( sManualPath ) );
971 QDesktopServices::openUrl( QString(
"http://hydrogen-music.org/documentation/manual/manual_en.html" ) );
983 if ( nPatternRow == -1 ) {
987 if ( nPatternRow == -1 ) {
988 QMessageBox::warning(
this,
"Hydrogen", tr(
"No pattern selected.") );
992 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
994 Pattern *pPattern = pSong->getPatternList()->get( nPatternRow );
1001 QString title = tr(
"Save Pattern as ..." );
1003 fd.setWindowTitle( title );
1004 fd.setDirectory( sPath );
1005 fd.selectFile( pPattern->
get_name() );
1006 fd.setFileMode( QFileDialog::AnyFile );
1008 fd.setAcceptMode( QFileDialog::AcceptSave );
1012 if ( fd.exec() != QDialog::Accepted ) {
1016 QFileInfo fileInfo( fd.selectedFiles().first() );
1018 QString filePath = fileInfo.absoluteFilePath();
1020 QString originalName = pPattern->
get_name();
1021 pPattern->
set_name( fileInfo.baseName() );
1023 pPattern->
set_name( originalName );
1025 if ( path.isEmpty() ) {
1026 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export pattern.") );
1030 h2app->showStatusBarMessage( tr(
"Pattern saved." ) );
1044 QString sWindowTitle;
1046 sWindowTitle = tr(
"Import song into Session" );
1048 sWindowTitle = tr(
"Open song" );
1058 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1066 fd.setAcceptMode( QFileDialog::AcceptOpen );
1067 fd.setFileMode ( QFileDialog::ExistingFiles );
1068 fd.setDirectory ( sPath );
1071 fd.setWindowTitle ( tr (
"Open Pattern" ) );
1073 if ( fd.exec() == QDialog::Accepted ) {
1076 for (
auto& ssFilename : fd.selectedFiles() ) {
1079 if ( pNewPattern ==
nullptr ) {
1084 nRow = pSong->getPatternList()->size();
1098 QString sWindowTitle;
1100 sWindowTitle = tr(
"Open Demo Song" );
1102 sWindowTitle = tr(
"Import Demo Song into Session" );
1111 if ( pHydrogen->getAudioEngine()->getState() ==
1113 pHydrogen->sequencer_stop();
1128 fd.setAcceptMode( QFileDialog::AcceptOpen );
1129 fd.setFileMode( QFileDialog::ExistingFile );
1130 fd.setDirectory( sPath );
1132 fd.setWindowTitle( sWindowTitle );
1135 if ( fd.exec() == QDialog::Accepted ) {
1139 sFilename = fd.selectedFiles().first();
1142 if ( !sFilename.isEmpty() ) {
1145 ! pHydrogen->isUnderSessionManagement() ) {
1146 pHydrogen->getSong()->setFilename(
"" );
1153 h2app->showPreferencesDialog();
1158 h2app->showPlaylistDialog();
1170 h2app->showDirector();
1182 if( this->isFullScreen() ){
1185 this->showFullScreen();
1192 h2app->showMixer( !isVisible );
1204 h2app->showAudioEngineInfoForm();
1209 h2app->showFilesystemInfoForm();
1253 bool isVisible =
h2app->getSongEditorPanel()->isVisible();
1254 h2app->getSongEditorPanel()->setHidden( isVisible );
1259 h2app->getSongEditorPanel()->showTimeline();
1265 h2app->getSongEditorPanel()->showPlaybackTrack();
1270 h2app->getSongEditorPanel()->toggleAutomationAreaVisibility();
1285 QString sNewName = QInputDialog::getText(
this,
"Hydrogen", tr(
"Component name" ), QLineEdit::Normal,
"New Component", &bIsOkPressed );
1286 if ( bIsOkPressed ) {
1290 pHydrogen->
getSong()->getComponents()->push_back( pDrumkitComponent );
1297#ifdef H2CORE_HAVE_JACK
1317 QMessageBox::information(
this,
1319 tr(
"Clear all instruments?"),
1320 QMessageBox::Cancel | QMessageBox::Ok,
1321 QMessageBox::Cancel)) {
1322 case QMessageBox::Ok:
1325 case QMessageBox::Cancel:
1335 auto pList = pSong->getInstrumentList();
1336 for (uint i = pList->size(); i > 0; i--) {
1346 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1347 auto pSelectedInstrument = pSong->getInstrumentList()->get( nInstrument );
1348 if ( pSelectedInstrument ==
nullptr ) {
1349 ERRORLOG(
"No instrument selected" );
1353 std::list< Note* > noteList;
1354 PatternList *pPatternList = pSong->getPatternList();
1356 QString sInstrumentName = pSelectedInstrument->get_name();
1357 QString sDrumkitPath = pSelectedInstrument->get_drumkit_path();
1359 for (
int i = 0; i < pPatternList->
size(); i++ ) {
1363 Note *pNote = it->second;
1367 noteList.push_back( pNote );
1374 sInstrumentName, nInstrument );
1382 auto pSong = pHydrogen->getSong();
1384 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()
1385 ->getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
1387 if ( pDrumkit !=
nullptr ){
1389 auto pNewDrumkit = std::make_shared<Drumkit>( pDrumkit );
1390 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
1391 pNewDrumkit->set_components( pSong->getComponents() );
1393 exportDialog.exec();
1396 QMessageBox::warning(
this,
"Hydrogen", QString(
"%1 [%2]")
1398 .arg( pHydrogen->getLastLoadedDrumkitPath() ) );
1422 auto pSong = pHydrogen->getSong();
1424 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()->
1425 getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
1427 pHydrogen->getLastLoadedDrumkitPath() );
1431 if ( pDrumkit !=
nullptr &&
1434 auto pNewDrumkit = std::make_shared<Drumkit>(pDrumkit);
1435 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
1436 pNewDrumkit->set_components( pSong->getComponents() );
1439 ERRORLOG(
"User cancelled dialog due to licensing issues." );
1443 if ( ! pNewDrumkit->save() ) {
1444 QMessageBox::information(
this,
"Hydrogen", tr(
"Saving of this library failed."));
1448 pHydrogen->getSoundLibraryDatabase()->updateDrumkits();
1499 pPanel->setHidden( pPanel->isVisible() );
1548 h2app->getWindowProperties(
h2app->getPlayListDialog() ) );
1550 h2app->getWindowProperties(
h2app->getDirector() ) );
1552#ifdef H2CORE_HAVE_LADSPA
1554 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
1576 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
1578 menuBar()->setFont( font );
1618 ERRORLOG(
"[MainForm::onPlayStopAccelEvent()] Unhandled case." );
1634 auto pAudioEngine = pHydrogen->getAudioEngine();
1636 pHydrogen->getSong()->setBpm( pAudioEngine->getTransportPosition()->getBpm() + 0.1 );
1639 pAudioEngine->setNextBpm( pAudioEngine->getTransportPosition()->getBpm() + 0.1 );
1640 pAudioEngine->unlock();
1649 auto pAudioEngine = pHydrogen->getAudioEngine();
1651 pHydrogen->getSong()->setBpm( pAudioEngine->getTransportPosition()->getBpm() - 0.1 );
1654 pAudioEngine->setNextBpm( pAudioEngine->getTransportPosition()->getBpm() - 0.1 );
1655 pAudioEngine->unlock();
1669 for ( uint i = 0; i < recentUsedSongs.size(); ++i ) {
1670 sFilename = recentUsedSongs[ i ];
1672 if ( !sFilename.isEmpty() ) {
1673 QAction *pAction =
new QAction(
this );
1674 pAction->setText( sFilename );
1697 m_pMissingSamplesInfoBar->setText( tr(
"Some samples used in this song could not be loaded. This may be because it uses an older default drumkit. This might be fixed by opening a new drumkit." ) );
1700 QObject::connect( fix, SIGNAL( clicked() ),
1710 if ( pSong->getInstrumentList()->has_all_midi_notes_same() ) {
1716 m_pMidiSetupInfoBar->setText( tr(
"MIDI out notes are not configured for this drumkit, so exporting this song to MIDI file may fail. Would you like Hydrogen to automatically fix this by assigning default values?") );
1718 QObject::connect( fix, SIGNAL(clicked()),
this, SLOT(
onFixMidiSetup()) );
1732 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not write to temporary directory %1.").arg(sTempDir) );
1738 INFOLOG(
"Fixing MIDI setup" );
1740 auto pSong = pHydrogen->getSong();
1741 if ( pSong !=
nullptr ) {
1742 pSong->getInstrumentList()->set_default_midi_out_notes();
1743 pHydrogen->setIsModified(
true );
1752 INFOLOG(
"Fixing MIDI setup" );
1763 QString loc = QLocale::system().name();
1779 if ( loc.contains(
"de" ) || loc.contains(
"DE" )){
1806 else if ( loc.contains(
"fr" ) || loc.contains(
"FR" )){
1869 if ( e->type() == QEvent::FileOpen ) {
1871 QFileOpenEvent *fe =
dynamic_cast<QFileOpenEvent*
>(e);
1872 assert( fe !=
nullptr );
1873 QString sFileName = fe->file();
1877 pHydrogenApp->openSong( sFileName );
1884 bool loadlist = pHydrogenApp->getPlayListDialog()->loadListByFileName( sFileName );
1891 }
else if ( e->type() == QEvent::KeyPress ) {
1893 QKeyEvent *k = (QKeyEvent *)e;
1895 if ( k->matches( QKeySequence::StandardKey::Undo ) ) {
1899 }
else if ( k->matches( QKeySequence::StandardKey::Redo ) ) {
1912 if ( pHydrogen->getAudioOutput() ==
nullptr ||
1913 dynamic_cast<NullDriver*
>(pHydrogen->getAudioOutput()) !=
nullptr ) {
1914 QMessageBox::warning(
this,
"Hydrogen",
1916 .arg( pCommonStrings->getAudioDriverNotPresent() )
1917 .arg( pCommonStrings->getAudioDriverErrorHint() ) );
1921 switch ( k->modifiers() ) {
1922 case Qt::NoModifier:
1927 case Qt::ControlModifier:
1932 case Qt::AltModifier:
1942 pHydrogen->handleBeatCounter();
1946 case Qt::Key_Backspace:
1961 case Qt::Key_Backslash:
1962 pHydrogen->onTapTempoAccelEvent();
1967 if ( k->modifiers() ==
1968 ( Qt::ControlModifier | Qt::ShiftModifier ) ) {
1971 }
else if ( k->modifiers() == Qt::ControlModifier ) {
1992 pHydrogen->__panic();
1998 pHydrogen->getCoreActionController()->locateToColumn( pHydrogen->getAudioEngine()->getTransportPosition()->getColumn() - 1 );
2003 pHydrogen->getCoreActionController()->locateToColumn( pHydrogen->getAudioEngine()->getTransportPosition()->getColumn() + 1 );
2009 if ( k->modifiers() == Qt::NoModifier ) {
2012 const int nNote = (*found).second;
2014 INFOLOG( QString(
"[Virtual Keyboard] triggering note [%1]" )
2017 pHydrogen->getCoreActionController()->handleNote(
2018 nNote, 0.8,
false );
2037 INFOLOG(
"[action_debug_printObjects]" );
2066 QMessageBox::information(
2069 tr(
"\nThe LilyPond export is an experimental feature.\n"
2070 "It should work like a charm provided that you use the "
2071 "GMRockKit, and that you do not use triplet.\n" ),
2080 fd.setFileMode( QFileDialog::AnyFile );
2081 fd.setNameFilter( tr(
"LilyPond file (*.ly)" ) );
2082 fd.setDirectory( sPath );
2083 fd.setWindowTitle( tr(
"Export LilyPond file" ) );
2084 fd.setAcceptMode( QFileDialog::AcceptSave );
2087 if ( fd.exec() == QDialog::Accepted ) {
2089 sFilename = fd.selectedFiles().first();
2092 if ( !sFilename.isEmpty() ) {
2093 if ( sFilename.endsWith(
".ly" ) ==
false ) {
2101 ly.
write( sFilename );
2110 switch (nErrorCode) {
2112 msg = tr(
"Unknown audio driver" );
2116 msg = tr(
"Error starting audio driver" );
2120 msg = tr(
"Jack driver: server shutdown" );
2124 msg = tr(
"Jack driver: cannot activate client" );
2128 msg = tr(
"Jack driver: cannot connect output port" );
2132 msg = tr(
"Jack driver: cannot disconnect client" );
2136 msg = tr(
"Jack driver: error in port register" );
2140 msg = QString( tr(
"OSC Server: Cannot connect to given port, using port %1 instead" ) ).arg(
Preferences::get_instance()->m_nOscTemporaryPort );
2144 msg = tr(
"Playback track couldn't be read" );
2148 msg = QString( tr(
"Unknown error %1" ) ).arg( nErrorCode );
2150 QMessageBox::information(
this,
"Hydrogen", msg );
2157 QString songFilename;
2167 .arg( nIndex +1 ) );
2190 if ( pDialog->exec() ) {
2212 if ( H2CORE_IS_DEVEL_BUILD ) {
2213 if(isDevelWarningEnabled) {
2216 QString msg = tr(
"You're using a development version of Hydrogen, please help us reporting bugs or suggestions in the hydrogen-devel mailing list.<br><br>Thank you!" );
2217 QMessageBox develMessageBox(
this );
2218 develMessageBox.setText( msg );
2219 develMessageBox.addButton( pCommonStrings->getButtonOk(),
2220 QMessageBox::YesRole );
2221 develMessageBox.addButton( pCommonStrings->getMutableDialog(),
2222 QMessageBox::AcceptRole );
2224 if( develMessageBox.exec() == 1 ){
2231 if ( !isDevelWarningEnabled ) {
2247 QString sOldFilename = pSong->getFilename();
2250 if ( !sOldFilename.isEmpty() ) {
2252 QFileInfo fileInfo( sOldFilename );
2256 QString sBaseName( fileInfo.completeBaseName() );
2257 if ( sBaseName.startsWith(
"." ) ) {
2258 sBaseName.remove( 0, 1 );
2261 QString sAbsoluteDir( fileInfo.absoluteDir().absolutePath() );
2264 sNewName = QString(
"%1%2.autosave.h2song" )
2267 WARNINGLOG( QString(
"Path of current song [%1] is not writable. Autosave will store the song as [%2] instead." )
2268 .arg( sOldFilename ).arg( sNewName ) );
2270 sNewName = QString(
"%1/.%2.autosave.h2song" )
2271 .arg( sAbsoluteDir ).arg( sBaseName );
2276 sNewName = QString(
"%1autosave.h2song" )
2288 std::shared_ptr<Song> pSong = pHydrogen->getSong();
2291 if ( pSong->getIsModified() ) {
2292 QString sOldFilename = pSong->getFilename();
2303 pSong->save( sAutoSaveFilename );
2305 pSong->setFilename( sOldFilename );
2306 pHydrogen->setIsModified(
true );
2319 if ( songnumber == -1 ) {
2328 QString message = (tr(
"Playlist: Song No. %1").arg( songnumber + 1)) + QString(
" --- Songname: ") + songname + QString(
" --- Author: ") +
Hydrogen::get_instance()->
getSong()->getAuthor();
2341 QMessageBox::information(
2342 this,
"Hydrogen", tr(
"\nThe document contains unsaved changes.\n"
2343 "Do you want to save the changes?\n"),
2344 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
2345 QMessageBox::Save ) ) {
2346 case QMessageBox::Save:
2361 case QMessageBox::Discard:
2365 case QMessageBox::Cancel:
2373 QMessageBox::information(
2375 tr(
"\nThe current playlist contains unsaved changes.\n"
2376 "Do you want to discard the changes?\n"),
2377 QMessageBox::Discard | QMessageBox::Cancel,
2378 QMessageBox::Discard ) ) {
2379 case QMessageBox::Discard:
2383 case QMessageBox::Cancel:
2401 snUsr1->setEnabled(
false);
2403 ::read(
sigusr1Fd[1], &tmp,
sizeof(tmp));
2406 snUsr1->setEnabled(
true);
2412 m_pUndoView->setAttribute(Qt::WA_QuitOnClose,
false);
2416 h2app->m_pUndoStack->undo();
2420 h2app->m_pUndoStack->redo();
2425 if ( nValue == 0 ) {
2430 }
else if ( nValue == 1 ) {
2443 ERRORLOG( QString(
"Unknown event parameter [%1] MainForm::updatePreferencesEvent" )
2451 h2app->m_pUndoStack->undo();
2452 }
else if(nEvent == 1) {
2453 h2app->m_pUndoStack->redo();
2462 if( nSongnumber+step >= 0 && nSongnumber+step <= nPlaylistSize-1 ){
2478 auto pSong = pHydrogen->getSong();
2480 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()
2481 ->getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
2483 if ( pDrumkit ==
nullptr ) {
2484 ERRORLOG( QString(
"Unable to find drumkit at path [%1]. Trying drumkit name [%2] instead." )
2485 .arg( pHydrogen->getLastLoadedDrumkitPath() )
2486 .arg( pHydrogen->getLastLoadedDrumkitName() ) );
2489 const QString sDrumkitPath =
2492 pDrumkit = pHydrogen->getSoundLibraryDatabase()
2493 ->getDrumkit( sDrumkitPath );
2496 if ( pDrumkit ==
nullptr && ! bDrumkitNameLocked ) {
2497 ERRORLOG( QString(
"Unable to find drumkit of name [%1] either. Falling back to empty one." )
2498 .arg( pHydrogen->getLastLoadedDrumkitName() ) );
2501 pDrumkit = std::make_shared<Drumkit>();
2504 if ( pDrumkit !=
nullptr ){
2506 auto pNewDrumkit = std::make_shared<Drumkit>( pDrumkit );
2507 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
2508 pNewDrumkit->set_components( pSong->getComponents() );
2511 if ( dialog.exec() == QDialog::Accepted ) {
2514 if ( pNewDrumkit->get_path() != pDrumkit->get_path() ) {
2518 pHydrogen->getCoreActionController()->setDrumkit( pNewDrumkit,
false );
2523 QMessageBox::warning(
this,
"Hydrogen", QString(
"%1 [%2]")
2525 .arg( pHydrogen->getLastLoadedDrumkitPath() ) );
2530 if ( nValue == 0 || nValue == 1 ) {
2543 auto pSong = pHydrogen->
getSong();
2548 if ( pSong ==
nullptr ) {
2552 if ( pObject->inherits(
"SongEditorPanel" ) ) {
2555 pCoreActionController->activateSongMode(
true );
2558 const int nCursorColumn =
2567 if ( nCursorColumn >= pSong->getPatternGroupVector()->size() ) {
2568 ERRORLOG( QString(
"Cursor column [%1] is outside of the current song [0,%2]" )
2569 .arg( nCursorColumn )
2570 .arg( pSong->getPatternGroupVector()->size() - 1 ) );
2574 if ( ! pCoreActionController->locateToColumn( nCursorColumn ) ) {
2579 }
else if ( pObject->inherits(
"PatternEditorPanel" ) ) {
2584 pCoreActionController->activateSongMode(
false );
2592 if ( ! pCoreActionController->locateToTick( nCursorColumn ) ) {
2597 ERRORLOG( QString(
"Unknown object class" ) );
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
#define FOREACH_NOTE_CST_IT_BEGIN_END(_notes, _it)
Iterate over all provided notes in an immutable way.
virtual void selectedInstrumentChangedEvent()
Dialog for exporting song to midi.
Dialog for exporting song.
Custom file dialog checking whether the user has write access to the selected folder before allowing ...
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
static void write_objects_map_to_cerr()
output objects map to stderr
bool locateToColumn(int nPatternGroup)
Relocates transport to the beginning of a particular column/Pattern group.
static bool install(const QString &sSourcePath, const QString &sTargetPath="", QString *pInstalledPath=nullptr, bool *pEncodingIssuesDetected=nullptr, bool bSilent=false)
Extract a .h2drumkit file.
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 savePatternPath(const QString &filePath, Pattern *pattern, std::shared_ptr< Song > song, const QString &drumkitName)
save the given pattern to filePath will overwrite an existing file
static const QString patterns_filter_name
static QString demos_dir()
returns system demos path
static bool dir_readable(const QString &path, bool silent=false)
returns true if the given path is a readable regular directory
static const QString patterns_ext
static QString songs_dir()
returns user songs path
static DrumkitType determineDrumkitType(const QString &sPath)
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 bool rm(const QString &path, bool recursive=false, bool bSilent=false)
remove a path
static QString doc_dir()
returns documentation path
static bool dir_writable(const QString &path, bool silent=false)
returns true if the given path is a writable regular directory
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.
@ stacked
First, looks in the system drumkits and, afterwards, in the user drumkits.
static QString drumkit_path_search(const QString &dk_name, Lookup lookup=Lookup::stacked, bool bSilent=false)
Returns the path to a H2Core::Drumkit folder.
static const QString playlist_ext
static const QString songs_filter_name
static QString tmp_dir()
returns temp path
@ SessionReadWrite
Kit was loaded via a NSM session, OSC command, or CLI option, only persist for the current Hydrogen s...
@ User
Kit was installed by the user, is automatically loaded, and most probably writable.
static QString usr_data_path()
returns user data path
static QString log_file_path()
returns the full path (including filename) of the logfile
static const QString drumkit_ext
static const QString songs_ext
static QString default_song_name()
Default option to offer the user when saving an empty song to disk.
static QString patterns_dir()
returns user patterns path
void sequencer_stop()
Stop the internal sequencer.
void renameJackPorts(std::shared_ptr< Song > pSong)
Calls audioEngine_renameJackPorts() if Preferences::m_bJackTrackOuts is set to true.
std::shared_ptr< Song > getSong() const
Get the current song.
bool isUnderSessionManagement() const
Song::Mode getMode() const
int getSelectedPatternNumber() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
AudioEngine * getAudioEngine() const
@ UNKNOWN_DRIVER
The provided input in createDriver() does not match any of the choices for H2Core::Preferences::Audio...
@ ERROR_STARTING_DRIVER
Unable to connect the audio driver stored in H2Core::AudioEngine::m_pAudioDriver in audioEngine_start...
@ JACK_CANNOT_ACTIVATE_CLIENT
@ JACK_ERROR_IN_PORT_REGISTER
Unable to register output ports for the JACK client using jack_port_register() (jack/jack....
@ OSC_CANNOT_CONNECT_TO_PORT
Unable to start the OSC server with the given port number.
@ JACK_CANNOT_CLOSE_CLIENT
The client of Hydrogen can not be disconnected from the JACK server using jack_client_close() (jack/j...
@ JACK_CANNOT_CONNECT_OUTPUT_PORT
Unable to connect either the JackAudioDriver::output_port_1 and the JackAudioDriver::output_port_name...
void setSessionDrumkitNeedsRelinking(bool bNeedsRelinking)
SoundLibraryDatabase * getSoundLibraryDatabase() const
QString getLastLoadedDrumkitName() const
CoreActionController * getCoreActionController() const
void sequencer_play()
Start the internal sequencer.
A class to convert a Hydrogen song to LilyPond format.
void extractData(const Song &song)
void write(const QString &sFilename) const
Class for writing logs to the console.
static unsigned bit_mask()
return the current log level bit mask
static Logger * get_instance()
Returns a pointer to the current H2Core::Logger singleton stored in __instance.
static void set_bit_mask(unsigned msk)
set the bitmask
static constexpr int instrumentOffset
When recording notes using MIDI NOTE_ON events this offset will be applied to the provided pitch in o...
A note plays an associated instrument with a velocity left and right pan.
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
void set_pattern_idx(int value)
__pattern_idx setter
PatternList is a collection of patterns.
int size() const
returns the numbers of patterns
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
void set_name(const QString &name)
get the name of the pattern
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
const notes_t * get_notes() const
get the virtual pattern set
std::multimap< int, Note * > notes_t
< multimap note type
void setNextSongByNumber(int SongNumber)
int getActiveSongNumber()
void activateSong(int SongNumber)
static Playlist * get_instance()
Returns a pointer to the current Playlist singleton stored in __instance.
bool getSongFilenameByNumber(int songNumber, QString &fileName)
Manager for User Preferences File (singleton)
QString getLastSaveSongAsDirectory() const
bool getShowDevelWarning()
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString getLastExportPatternAsDirectory() const
void setLadspaProperties(unsigned nFX, const WindowProperties &prop)
bool __playselectedinstrument
void setMixerProperties(const WindowProperties &prop)
void setPatternEditorProperties(const WindowProperties &prop)
void setLastExportLilypondDirectory(QString sPath)
void setSongEditorProperties(const WindowProperties &prop)
QString getLastExportLilypondDirectory() const
void setShowDevelWarning(bool value)
QString getLastOpenSongDirectory() const
bool savePreferences()
Save the preferences file.
void setInstrumentRackProperties(const WindowProperties &prop)
QString getLastOpenPatternDirectory() const
void setDirectorProperties(const WindowProperties &prop)
void setPlaylistDialogProperties(const WindowProperties &prop)
static QString audioDriverToQString(const AudioDriver &driver)
void setLastOpenSongDirectory(QString sPath)
void setLastOpenPatternDirectory(QString sPath)
void setMainFormProperties(const WindowProperties &prop)
void setAudioEngineInfoProperties(const WindowProperties &prop)
std::vector< QString > getRecentFiles() const
const QString & getPreferredLanguage()
bool getShowAutomationArea()
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ GeneralTab
Any option in the General tab appeared.
@ Font
Either the font size or font family have changed.
@ Colors
At least one of the colors has changed.
bool getShowPlaybackTrack() const
void setLastSaveSongAsDirectory(QString sPath)
void setLastPlaylistFilename(const QString &filename)
void setLastExportPatternAsDirectory(QString sPath)
static std::shared_ptr< Song > getEmptySong()
void updatePatterns(bool bTriggerEvent=true)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
std::shared_ptr< CommonStrings > getCommonStrings()
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
InstrumentRack * getInstrumentRack()
static bool openSong(QString sFilename)
PlaylistDialog * getPlayListDialog()
static bool checkDrumkitLicense(std::shared_ptr< H2Core::Drumkit > pDrumkit)
void showStatusBarMessage(const QString &sMessage, const QString &sCaller="")
static bool recoverEmptySong()
Specialized version of openSong( QString sFilename ) trying to open the autosave file corresponding t...
SongEditorPanel * getSongEditorPanel()
static InstrumentEditorPanel * get_instance()
static int findFreeDrumkitComponentId(int startingPoint=0)
lash_event_t * getNextEvent()
void sendEvent(LASH_Event_Type eventType, const char *value)
static LashClient * get_instance()
void startPlaybackAtCursor(QObject *pObject)
Relocates to current position of the cursor and starts playback if the transport isn't rolling yet.
void onBPMMinusAccelEvent()
void update_instrument_checkbox(bool show)
virtual void errorEvent(int nErrorCode) override
bool handleSelectNextPrevSongOnPlaylist(int step)
void action_window_showTimeline()
void action_debug_logLevel_warn()
void action_file_export_lilypond()
void action_debug_logLevel_debug()
void action_window_showPatternEditor()
QAction * m_pViewTimelineAction
void action_file_export_pattern_as(int nPatternRow=-1)
void closeAll()
Wrapper around savePreferences() and quit() method of m_pQApp.
void action_debug_logLevel_error()
bool eventFilter(QObject *o, QEvent *e) override
void savePreferences()
Stores the current state of the GUI (position, width, height, and visibility of the widgets) in the H...
void onFixMissingSamples()
QMenu * m_pRecentFilesMenu
void action_window_showAutomationArea()
void action_debug_showFilesystemInfo()
void updateRecentUsedSongList()
void action_file_open()
Project > Open / Import into Session handling function.
void action_window_showSongEditor()
Shows the song editor.
QAction * m_pViewPlaybackTrackAction
std::map< int, int > keycodeInstrumentMap
bool action_file_save_as()
Project > Save As / Export from Session handling function.
void action_instruments_importLibrary()
void action_file_open_recent(QAction *pAction)
void action_banks_properties()
void onRestartAccelEvent()
void checkMissingSamples()
void action_debug_printObjects()
print the object map
void action_window_showPlaylistDialog()
QString getAutoSaveFilename()
void action_inputMode_instrument()
void update_automation_checkbox()
QAction * m_pViewPlaylistEditorAction
void action_file_export()
void action_instruments_addInstrument()
void createMenuBar()
Create the menubar.
virtual void updatePreferencesEvent(int nValue) override
Handles the loading and saving of the H2Core::Preferences from the core part of H2Core::Hydrogen.
MainForm(QApplication *pQApplication, QString sSongFilename)
void update_mixer_checkbox()
virtual void quitEvent(int) override
void onPlayStopAccelEvent()
bool action_file_exit()
return true if the app needs to be closed.
void action_file_new()
Project > New handling function.
void onPreferencesChanged(H2Core::Preferences::Changes changes)
void closeEvent(QCloseEvent *ev) override
Window close event.
virtual void updateSongEvent(int nValue) override
void action_debug_logLevel_none()
QAction * m_pDrumkitAction
virtual void undoRedoActionEvent(int nEvent) override
void action_instruments_exportLibrary()
bool prepareSongOpening()
virtual void jacksessionEvent(int nValue) override
QAction * m_pInstrumentAction
void action_instruments_onlineImportLibrary()
void startAutosaveTimer()
debug only
void update_playback_track_group()
void openSongWithDialog(const QString &sWindowTitle, const QString &sPath, bool bIsDemo)
QMenu * m_pInstrumentsMenu
void action_instruments_saveAsLibrary()
void update_director_checkbox()
QAction * m_pViewMixerAction
void functionDeleteInstrument(int nInstrument)
void action_instruments_clearAll()
void action_window_showMixer()
void action_instruments_saveLibrary()
InfoBar * m_pMidiSetupInfoBar
QString m_sPreviousAutoSaveFilename
Since the filename of the current song does change whenever the users uses "Save As" multiple autosav...
void update_playlist_checkbox()
void onPlaylistDisplayTimer()
void action_window_showInstrumentRack()
void action_instruments_addComponent()
void action_inputMode_drumkit()
QAction * m_pViewDirectorAction
void checkNecessaryDirectories()
void action_file_openDemo()
QAction * m_pViewAutomationPathAction
void showPreferencesDialog()
void editDrumkitProperties(bool bDrumkitNameLocked)
InfoBar * m_pMissingSamplesInfoBar
bool handleUnsavedChanges()
void action_window_toggleFullscreen()
void action_debug_openLogfile()
void action_file_export_midi()
void action_file_openPattern()
QAction * m_pViewMixerInstrumentRackAction
void action_debug_showAudioEngineInfo()
void action_window_showPlaybackTrack()
void action_window_show_DirectorWidget()
virtual void playlistLoadSongEvent(int nIndex) override
void onBPMPlusAccelEvent()
static void usr1SignalHandler(int unused)
void action_file_songProperties()
QActionGroup * m_pViewPlaybackTrackActionGroup
void action_debug_logLevel_info()
static int dereferenceDrumkit(std::shared_ptr< H2Core::Song > pSong)
Replaces a path in Song::m_sLastLoadedDrumkitPath pointing to the session folder with one pointing to...
static void replaceDrumkitPath(std::shared_ptr< H2Core::Song > pSong, const QString &sDrumkitPath)
Replaces @H2Core::Song::m_sLastLoadedDrumkitPath as well as all @H2Core::Instrument::__drumkit_path b...
DrumPatternEditor * getDrumPatternEditor()
void clearSelection()
Clear the pattern editor selection.
static void setPalette(QApplication *pQApp)
Function used to update the global palette of the QApplication.
SongEditor * getSongEditor() const
int getCursorColumn() const
This dialog is used to import a SoundLibrary file from a local file or via HTTP.
#define MAX_FX
Maximum number of effects.
@ EVENT_SELECTED_INSTRUMENT_CHANGED