75#include <sys/socket.h>
78#ifdef H2CORE_HAVE_LASH
79#include <lash-1.0/lash/lash.h>
91 : QMainWindow( nullptr )
92 , m_sPreviousAutoSaveFilename(
"" )
97 setObjectName(
"MainForm" );
98 setMinimumSize( QSize( 1000, 500 ) );
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() ) );
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() ) {
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(
this,
"Hydrogen",
230 QString(
"%1 [%2]\n%3" )
231 .arg( pCommonStrings->getAudioDriverStartError() )
232 .arg( pPref->m_sAudioDriver )
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" );
322 if ( ! bUnderSessionManagement ) {
484 if ( nAutosavesPerHour > 0 ) {
485 if ( nAutosavesPerHour > 360 ) {
486 ERRORLOG( QString(
"Too many autosaves per hour set [%1]. Using 360 - once a second - instead." )
487 .arg( nAutosavesPerHour ) );
488 nAutosavesPerHour = 360;
491 static_cast<float>(nAutosavesPerHour) ) );
499#ifdef H2CORE_HAVE_LASH
505 WARNINGLOG(
"[LASH] Not connected to server!");
509 bool keep_running =
true;
513 std::string songFilename;
514 QString filenameSong;
519 switch (lash_event_get_type(event)) {
525 songFilename.append(lash_event_get_string(event));
526 songFilename.append(
"/hydrogen.h2song");
528 filenameSong = QString::fromLocal8Bit( songFilename.c_str() );
529 song->setFilename( filenameSong );
536 case LASH_Restore_File:
538 songFilename.append(lash_event_get_string(event));
539 songFilename.append(
"/hydrogen.h2song");
541 INFOLOG( QString(
"[LASH] Restore file: %1")
542 .arg( songFilename.c_str() ) );
544 filenameSong = QString::fromLocal8Bit( songFilename.c_str() );
555 keep_running =
false;
565 lash_event_destroy(event);
571 lashPollTimer->stop();
580 QMessageBox donationDialog;
581 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." ) );
582 donationDialog.setStandardButtons( QMessageBox::Cancel );
583 donationDialog.addButton( tr(
"&Donate!" ), QMessageBox::AcceptRole );
585 int nRet = donationDialog.exec();
587 if ( nRet == QMessageBox::AcceptRole ) {
588 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"));
621 if ( bUnderSessionManagement ) {
626 QMessageBox confirmationBox;
627 confirmationBox.setText( tr(
"Replace current song with empty one?" ) );
628 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." ) );
629 confirmationBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
630 confirmationBox.setDefaultButton( QMessageBox::No );
632 int confirmationChoice = confirmationBox.exec();
634 if ( confirmationChoice == QMessageBox::No ) {
644 QString sBaseName( fileInfo.completeBaseName() );
645 if ( sBaseName.startsWith(
"." ) ) {
646 sBaseName.remove( 0, 1 );
648 QFileInfo autoSaveFile( QString(
"%1/.%2.autosave.h2song" )
649 .arg( fileInfo.absoluteDir().absolutePath() )
651 if ( autoSaveFile.exists() ) {
667 auto pSong = pHydrogen->getSong();
669 if ( pSong ==
nullptr ) {
673 const bool bUnderSessionManagement = pHydrogen->isUnderSessionManagement();
674 if ( bUnderSessionManagement &&
675 pHydrogen->getSessionDrumkitNeedsRelinking() ) {
686 if ( QMessageBox::information(
this,
"Hydrogen",
687 tr(
"\nThere have been recent changes to the drumkit settings.\n"
688 "The session needs to be saved before exporting will can be continued.\n" ),
689 QMessageBox::Save | QMessageBox::Cancel,
691 == QMessageBox::Cancel ) {
692 INFOLOG(
"Exporting cancelled at relinking" );
706 fd.setFileMode( QFileDialog::AnyFile );
708 fd.setAcceptMode( QFileDialog::AcceptSave );
709 fd.setDirectory( sPath );
711 if ( bUnderSessionManagement ) {
712 fd.setWindowTitle( tr(
"Export song from Session" ) );
714 fd.setWindowTitle( tr(
"Save song" ) );
719 QString sDefaultFilename;
723 QString sLastFilename = pSong->getFilename();
724 QString sLastLoadedDrumkitPath = pSong->getLastLoadedDrumkitPath();
728 }
else if ( sLastFilename.isEmpty() ) {
729 sDefaultFilename = pHydrogen->getSong()->getName();
731 QFileInfo fileInfo( sLastFilename );
732 sDefaultFilename = fileInfo.completeBaseName();
736 fd.selectFile( sDefaultFilename );
738 if (fd.exec() == QDialog::Accepted) {
739 QString sNewFilename = fd.selectedFiles().first();
741 if ( ! sNewFilename.isEmpty() ) {
748#ifdef H2CORE_HAVE_OSC
755 if ( bUnderSessionManagement ) {
756 pHydrogen->setSessionIsExported(
true );
759 QMessageBox::warning(
this,
"Hydrogen",
760 tr(
"Drumkit [%1] used in session could not found on your system. Please install it in to make the exported song work properly." )
761 .arg( pSong->getLastLoadedDrumkitName() ) );
773#ifdef H2CORE_HAVE_OSC
777 if ( bUnderSessionManagement ) {
778 pSong->setFilename( sLastFilename );
781 pHydrogen->setSessionIsExported(
false );
803 auto pSong = pHydrogen->getSong();
805 if ( pSong ==
nullptr ) {
809 auto pCoreActionController = pHydrogen->getCoreActionController();
810 QString sFilename = pSong->getFilename();
812 if ( sNewFilename.isEmpty() &&
813 ( sFilename.isEmpty() ||
822 if ( pSong->hasMissingSamples() ) {
823 if ( QMessageBox::information(
this,
"Hydrogen",
824 tr(
"Some samples used by this song failed to load. If you save the song now "
825 "these missing samples will be removed from the song entirely.\n"
826 "Are you sure you want to save?" ),
827 QMessageBox::Save | QMessageBox::Cancel,
829 == QMessageBox::Cancel ) {
832 pSong->clearMissingSamples();
839 if ( sNewFilename.isEmpty() ) {
840 bSaved = pCoreActionController->saveSong();
842 bSaved = pCoreActionController->saveSongAs( sNewFilename );
846 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save song.") );
881 QDesktopServices::openUrl(QString(
"https://github.com/hydrogen-music/hydrogen/issues"));
889 QStringList languages;
891 if ( !sPreferredLanguage.isNull() ) {
892 languages << sPreferredLanguage;
894 languages << QLocale::system().uiLanguages()
898 for ( QString sLang : languages ) {
899 QStringList sCandidates ( sLang );
900 QStringList s = sLang.split(
'-');
901 if ( s.size() != 1 ) {
904 for ( QString sCandidate : sCandidates ) {
905 QString sManualPath = QString(
"%1/manual_%2.html" ) .arg( sDocPath ).arg( sCandidate );
907 QDesktopServices::openUrl( QUrl::fromLocalFile( sManualPath ) );
916 QDesktopServices::openUrl( QString(
"http://hydrogen-music.org/documentation/manual/manual_en.html" ) );
928 if ( nPatternRow == -1 ) {
932 if ( nPatternRow == -1 ) {
933 QMessageBox::warning(
this,
"Hydrogen", tr(
"No pattern selected.") );
937 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
939 Pattern *pPattern = pSong->getPatternList()->get( nPatternRow );
946 QString title = tr(
"Save Pattern as ..." );
948 fd.setWindowTitle( title );
949 fd.setDirectory( sPath );
950 fd.selectFile( pPattern->
get_name() );
951 fd.setFileMode( QFileDialog::AnyFile );
953 fd.setAcceptMode( QFileDialog::AcceptSave );
957 if ( fd.exec() != QDialog::Accepted ) {
961 QFileInfo fileInfo = fd.selectedFiles().first();
963 QString filePath = fileInfo.absoluteFilePath();
965 QString originalName = pPattern->
get_name();
966 pPattern->
set_name( fileInfo.baseName() );
970 if ( path.isEmpty() ) {
971 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export pattern.") );
989 QString sWindowTitle;
991 sWindowTitle = tr(
"Import song into Session" );
993 sWindowTitle = tr(
"Open song" );
1003 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1011 fd.setAcceptMode( QFileDialog::AcceptOpen );
1012 fd.setFileMode ( QFileDialog::ExistingFiles );
1013 fd.setDirectory ( sPath );
1016 fd.setWindowTitle ( tr (
"Open Pattern" ) );
1018 if ( fd.exec() == QDialog::Accepted ) {
1021 for (
auto& ssFilename : fd.selectedFiles() ) {
1024 if ( pNewPattern ==
nullptr ) {
1029 nRow = pSong->getPatternList()->size();
1043 QString sWindowTitle;
1045 sWindowTitle = tr(
"Open Demo Song" );
1047 sWindowTitle = tr(
"Import Demo Song into Session" );
1056 if ( pHydrogen->getAudioEngine()->getState() ==
1058 pHydrogen->sequencer_stop();
1073 fd.setAcceptMode( QFileDialog::AcceptOpen );
1074 fd.setFileMode( QFileDialog::ExistingFile );
1075 fd.setDirectory( sPath );
1077 fd.setWindowTitle( sWindowTitle );
1080 if ( fd.exec() == QDialog::Accepted ) {
1084 sFilename = fd.selectedFiles().first();
1087 if ( !sFilename.isEmpty() ) {
1090 ! pHydrogen->isUnderSessionManagement() ) {
1091 pHydrogen->getSong()->setFilename(
"" );
1127 if( this->isFullScreen() ){
1130 this->showFullScreen();
1230 QString sNewName = QInputDialog::getText(
this,
"Hydrogen", tr(
"Component name" ), QLineEdit::Normal,
"New Component", &bIsOkPressed );
1231 if ( bIsOkPressed ) {
1235 pHydrogen->
getSong()->getComponents()->push_back( pDrumkitComponent );
1242#ifdef H2CORE_HAVE_JACK
1262 QMessageBox::information(
this,
1264 tr(
"Clear all instruments?"),
1265 QMessageBox::Cancel | QMessageBox::Ok,
1266 QMessageBox::Cancel)) {
1267 case QMessageBox::Ok:
1270 case QMessageBox::Cancel:
1280 auto pList = pSong->getInstrumentList();
1281 for (uint i = pList->size(); i > 0; i--) {
1291 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1292 auto pSelectedInstrument = pSong->getInstrumentList()->get( nInstrument );
1293 if ( pSelectedInstrument ==
nullptr ) {
1294 ERRORLOG(
"No instrument selected" );
1298 std::list< Note* > noteList;
1299 PatternList *pPatternList = pSong->getPatternList();
1301 QString sInstrumentName = pSelectedInstrument->get_name();
1302 QString sDrumkitPath = pSelectedInstrument->get_drumkit_path();
1304 for (
int i = 0; i < pPatternList->
size(); i++ ) {
1308 Note *pNote = it->second;
1312 noteList.push_back( pNote );
1319 sInstrumentName, nInstrument );
1327 auto pSong = pHydrogen->getSong();
1329 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()
1330 ->getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
1332 if ( pDrumkit !=
nullptr ){
1334 auto pNewDrumkit = std::make_shared<Drumkit>( pDrumkit );
1335 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
1336 pNewDrumkit->set_components( pSong->getComponents() );
1338 exportDialog.exec();
1341 QMessageBox::warning(
this,
"Hydrogen", QString(
"%1 [%2]")
1343 .arg( pHydrogen->getLastLoadedDrumkitPath() ) );
1367 auto pSong = pHydrogen->getSong();
1369 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()->
1370 getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
1372 pHydrogen->getLastLoadedDrumkitPath() );
1376 if ( pDrumkit !=
nullptr &&
1377 ( drumkitType == Filesystem::DrumkitType::User ||
1378 drumkitType == Filesystem::DrumkitType::SessionReadWrite ) ) {
1379 auto pNewDrumkit = std::make_shared<Drumkit>(pDrumkit);
1380 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
1381 pNewDrumkit->set_components( pSong->getComponents() );
1384 ERRORLOG(
"User cancelled dialog due to licensing issues." );
1388 if ( ! pNewDrumkit->save() ) {
1389 QMessageBox::information(
this,
"Hydrogen", tr(
"Saving of this library failed."));
1393 pHydrogen->getSoundLibraryDatabase()->updateDrumkits();
1444 pPanel->setHidden( pPanel->isVisible() );
1497#ifdef H2CORE_HAVE_LADSPA
1499 for (uint nFX = 0; nFX <
MAX_FX; nFX++) {
1521 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
1523 menuBar()->setFont( font );
1563 ERRORLOG(
"[MainForm::onPlayStopAccelEvent()] Unhandled case." );
1579 auto pAudioEngine = pHydrogen->getAudioEngine();
1581 pHydrogen->getSong()->setBpm( pAudioEngine->getTransportPosition()->getBpm() + 0.1 );
1584 pAudioEngine->setNextBpm( pAudioEngine->getTransportPosition()->getBpm() + 0.1 );
1585 pAudioEngine->unlock();
1594 auto pAudioEngine = pHydrogen->getAudioEngine();
1596 pHydrogen->getSong()->setBpm( pAudioEngine->getTransportPosition()->getBpm() - 0.1 );
1599 pAudioEngine->setNextBpm( pAudioEngine->getTransportPosition()->getBpm() - 0.1 );
1600 pAudioEngine->unlock();
1614 for ( uint i = 0; i < recentUsedSongs.size(); ++i ) {
1615 sFilename = recentUsedSongs[ i ];
1617 if ( !sFilename.isEmpty() ) {
1618 QAction *pAction =
new QAction(
this );
1619 pAction->setText( sFilename );
1642 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." ) );
1645 QObject::connect( fix, SIGNAL( clicked() ),
1655 if ( pSong->getInstrumentList()->has_all_midi_notes_same() ) {
1661 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?") );
1663 QObject::connect( fix, SIGNAL(clicked()),
this, SLOT(
onFixMidiSetup()) );
1677 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not write to temporary directory %1.").arg(sTempDir) );
1683 INFOLOG(
"Fixing MIDI setup" );
1685 auto pSong = pHydrogen->getSong();
1686 if ( pSong !=
nullptr ) {
1687 pSong->getInstrumentList()->set_default_midi_out_notes();
1688 pHydrogen->setIsModified(
true );
1697 INFOLOG(
"Fixing MIDI setup" );
1708 QString loc = QLocale::system().name();
1721 if ( loc.contains(
"de" ) || loc.contains(
"DE" )){
1748 else if ( loc.contains(
"fr" ) || loc.contains(
"FR" )){
1811 if ( e->type() == QEvent::FileOpen ) {
1813 QFileOpenEvent *fe =
dynamic_cast<QFileOpenEvent*
>(e);
1814 assert( fe !=
nullptr );
1815 QString sFileName = fe->file();
1819 pHydrogenApp->openSong( sFileName );
1826 bool loadlist = pHydrogenApp->getPlayListDialog()->loadListByFileName( sFileName );
1833 }
else if ( e->type() == QEvent::KeyPress ) {
1835 QKeyEvent *k = (QKeyEvent *)e;
1837 if ( k->matches( QKeySequence::StandardKey::Undo ) ) {
1841 }
else if ( k->matches( QKeySequence::StandardKey::Redo ) ) {
1854 if ( pHydrogen->getAudioOutput() ==
nullptr ||
1855 dynamic_cast<NullDriver*
>(pHydrogen->getAudioOutput()) !=
nullptr ) {
1856 QMessageBox::warning(
this,
"Hydrogen",
1858 .arg( pCommonStrings->getAudioDriverNotPresent() )
1859 .arg( pCommonStrings->getAudioDriverErrorHint() ) );
1863 switch ( k->modifiers() ) {
1864 case Qt::NoModifier:
1869 case Qt::ControlModifier:
1874 case Qt::AltModifier:
1884 pHydrogen->handleBeatCounter();
1888 case Qt::Key_Backspace:
1903 case Qt::Key_Backslash:
1904 pHydrogen->onTapTempoAccelEvent();
1909 if ( k->modifiers() ==
1910 ( Qt::ControlModifier | Qt::ShiftModifier ) ) {
1913 }
else if ( k->modifiers() == Qt::ControlModifier ) {
1934 pHydrogen->__panic();
1940 pHydrogen->getCoreActionController()->locateToColumn( pHydrogen->getAudioEngine()->getTransportPosition()->getColumn() - 1 );
1945 pHydrogen->getCoreActionController()->locateToColumn( pHydrogen->getAudioEngine()->getTransportPosition()->getColumn() + 1 );
1951 if ( k->modifiers() == Qt::NoModifier ) {
1957 int row = (*found).second;
1959 float velocity = 0.8;
1961 pHydrogen->addRealtimeNote( row, velocity, 0.f,
false, row + 36 );
1980 INFOLOG(
"[action_debug_printObjects]" );
2009 QMessageBox::information(
2012 tr(
"\nThe LilyPond export is an experimental feature.\n"
2013 "It should work like a charm provided that you use the "
2014 "GMRockKit, and that you do not use triplet.\n" ),
2023 fd.setFileMode( QFileDialog::AnyFile );
2024 fd.setNameFilter( tr(
"LilyPond file (*.ly)" ) );
2025 fd.setDirectory( sPath );
2026 fd.setWindowTitle( tr(
"Export LilyPond file" ) );
2027 fd.setAcceptMode( QFileDialog::AcceptSave );
2030 if ( fd.exec() == QDialog::Accepted ) {
2032 sFilename = fd.selectedFiles().first();
2035 if ( !sFilename.isEmpty() ) {
2036 if ( sFilename.endsWith(
".ly" ) ==
false ) {
2044 ly.
write( sFilename );
2053 switch (nErrorCode) {
2055 msg = tr(
"Unknown audio driver" );
2059 msg = tr(
"Error starting audio driver" );
2063 msg = tr(
"Jack driver: server shutdown" );
2067 msg = tr(
"Jack driver: cannot activate client" );
2071 msg = tr(
"Jack driver: cannot connect output port" );
2075 msg = tr(
"Jack driver: cannot disconnect client" );
2079 msg = tr(
"Jack driver: error in port register" );
2083 msg = QString( tr(
"OSC Server: Cannot connect to given port, using port %1 instead" ) ).arg(
Preferences::get_instance()->m_nOscTemporaryPort );
2087 msg = tr(
"Playback track couldn't be read" );
2091 msg = QString( tr(
"Unknown error %1" ) ).arg( nErrorCode );
2093 QMessageBox::information(
this,
"Hydrogen", msg );
2100 QString songFilename;
2110 .arg( nIndex +1 ) );
2133 if ( pDialog->exec() ) {
2155 if ( H2CORE_IS_DEVEL_BUILD ) {
2156 if(isDevelWarningEnabled) {
2159 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!" );
2160 QMessageBox develMessageBox(
this );
2161 develMessageBox.setText( msg );
2162 develMessageBox.addButton( pCommonStrings->getButtonOk(),
2163 QMessageBox::YesRole );
2164 develMessageBox.addButton( pCommonStrings->getMutableDialog(),
2165 QMessageBox::AcceptRole );
2167 if( develMessageBox.exec() == 1 ){
2174 if ( !isDevelWarningEnabled ) {
2190 QString sOldFilename = pSong->getFilename();
2193 if ( !sOldFilename.isEmpty() ) {
2195 QFileInfo fileInfo( sOldFilename );
2199 QString sBaseName( fileInfo.completeBaseName() );
2200 if ( sBaseName.startsWith(
"." ) ) {
2201 sBaseName.remove( 0, 1 );
2204 QString sAbsoluteDir( fileInfo.absoluteDir().absolutePath() );
2207 sNewName = QString(
"%1%2.autosave.h2song" )
2210 WARNINGLOG( QString(
"Path of current song [%1] is not writable. Autosave will store the song as [%2] instead." )
2211 .arg( sOldFilename ).arg( sNewName ) );
2213 sNewName = QString(
"%1/.%2.autosave.h2song" )
2214 .arg( sAbsoluteDir ).arg( sBaseName );
2219 sNewName = QString(
"%1autosave.h2song" )
2231 std::shared_ptr<Song> pSong = pHydrogen->getSong();
2234 if ( pSong->getIsModified() ) {
2235 QString sOldFilename = pSong->getFilename();
2246 pSong->save( sAutoSaveFilename );
2248 pSong->setFilename( sOldFilename );
2249 pHydrogen->setIsModified(
true );
2262 if ( songnumber == -1 ) {
2271 QString message = (tr(
"Playlist: Song No. %1").arg( songnumber + 1)) + QString(
" --- Songname: ") + songname + QString(
" --- Author: ") +
Hydrogen::get_instance()->
getSong()->getAuthor();
2284 QMessageBox::information(
this,
"Hydrogen",
2285 tr(
"\nThe document contains unsaved changes.\n"
2286 "Do you want to save the changes?\n"),
2287 pCommonStrings->getButtonSave(),
2288 pCommonStrings->getButtonDiscard(),
2289 pCommonStrings->getButtonCancel(),
2318 QMessageBox::information(
2321 tr(
"\nThe current playlist contains unsaved changes.\n"
2322 "Do you want to discard the changes?\n"),
2323 pCommonStrings->getButtonDiscard(),
2324 pCommonStrings->getButtonCancel(),
2353 snUsr1->setEnabled(
false);
2355 ::read(
sigusr1Fd[1], &tmp,
sizeof(tmp));
2358 snUsr1->setEnabled(
true);
2364 m_pUndoView->setAttribute(Qt::WA_QuitOnClose,
false);
2377 if ( nValue == 0 ) {
2382 }
else if ( nValue == 1 ) {
2395 ERRORLOG( QString(
"Unknown event parameter [%1] MainForm::updatePreferencesEvent" )
2404 }
else if(nEvent == 1) {
2414 if( nSongnumber+step >= 0 && nSongnumber+step <= nPlaylistSize-1 ){
2430 auto pSong = pHydrogen->getSong();
2432 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()
2433 ->getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
2435 if ( pDrumkit ==
nullptr ) {
2436 ERRORLOG( QString(
"Unable to find drumkit at path [%1]. Trying drumkit name [%2] instead." )
2437 .arg( pHydrogen->getLastLoadedDrumkitPath() )
2438 .arg( pHydrogen->getLastLoadedDrumkitName() ) );
2441 const QString sDrumkitPath =
2443 Filesystem::Lookup::stacked,
true );
2444 pDrumkit = pHydrogen->getSoundLibraryDatabase()
2445 ->getDrumkit( sDrumkitPath );
2448 if ( pDrumkit ==
nullptr && ! bDrumkitNameLocked ) {
2449 ERRORLOG( QString(
"Unable to find drumkit of name [%1] either. Falling back to empty one." )
2450 .arg( pHydrogen->getLastLoadedDrumkitName() ) );
2453 pDrumkit = std::make_shared<Drumkit>();
2456 if ( pDrumkit !=
nullptr ){
2458 auto pNewDrumkit = std::make_shared<Drumkit>( pDrumkit );
2459 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
2460 pNewDrumkit->set_components( pSong->getComponents() );
2463 if ( dialog.exec() == QDialog::Accepted ) {
2466 if ( pNewDrumkit->get_path() != pDrumkit->get_path() ) {
2470 pHydrogen->getCoreActionController()->setDrumkit( pNewDrumkit,
false );
2475 QMessageBox::warning(
this,
"Hydrogen", QString(
"%1 [%2]")
2477 .arg( pHydrogen->getLastLoadedDrumkitPath() ) );
2482 if ( nValue == 0 || nValue == 1 ) {
2495 auto pSong = pHydrogen->
getSong();
2500 if ( pSong ==
nullptr ) {
2504 if ( pObject->inherits(
"SongEditorPanel" ) ) {
2506 if ( pHydrogen->
getMode() != Song::Mode::Song ) {
2507 pCoreActionController->activateSongMode(
true );
2510 const int nCursorColumn =
2519 if ( nCursorColumn >= pSong->getPatternGroupVector()->size() ) {
2520 ERRORLOG( QString(
"Cursor column [%1] is outside of the current song [0,%2]" )
2521 .arg( nCursorColumn )
2522 .arg( pSong->getPatternGroupVector()->size() - 1 ) );
2526 if ( ! pCoreActionController->locateToColumn( nCursorColumn ) ) {
2531 }
else if ( pObject->inherits(
"PatternEditorPanel" ) ) {
2535 if ( pHydrogen->
getMode() != Song::Mode::Pattern ) {
2536 pCoreActionController->activateSongMode(
false );
2544 if ( ! pCoreActionController->locateToTick( nCursorColumn ) ) {
2549 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="", 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.
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
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 string in createDriver() does not match any of the choices for Preferences::m_sAud...
@ 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
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
const notes_t * get_notes() const
get the virtual pattern set
std::multimap< int, Note * > notes_t
< multimap note type
static Pattern * load_file(const QString &pattern_path, std::shared_ptr< InstrumentList > instruments)
load a pattern from a file
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)
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)
PlayerControl * getPlayerControl()
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
std::shared_ptr< CommonStrings > getCommonStrings()
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
void showAudioEngineInfoForm()
void showFilesystemInfoForm()
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="")
void showMixer(bool bShow)
void showPlaylistDialog()
static bool recoverEmptySong()
Specialized version of openSong( QString sFilename ) trying to open the autosave file corresponding t...
H2Core::WindowProperties getWindowProperties(QWidget *pWindow)
AudioEngineInfoForm * getAudioEngineInfoForm()
void showPreferencesDialog()
SongEditorPanel * getSongEditorPanel()
void setText(const QString &text)
QPushButton * addButton(const QString &label)
void setTitle(const QString &text)
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 action_file_save_as()
Project > Save As / Export from Session handling function.
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
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.
bool loadListByFileName(QString filename)
static void setPalette(QApplication *pQApp)
Function used to update the global palette of the QApplication.
void toggleAutomationAreaVisibility()
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