24#include <core/config.h>
31#include <QtCore/QFile>
32#include <QtCore/QFileInfo>
33#include <QtCore/QCoreApplication>
41#define LOCAL_DATA_PATH "data/"
43#define DEMOS "demo_songs/"
45#define DRUMKITS "drumkits/"
48#define PATTERNS "patterns/"
49#define PLAYLISTS "playlists/"
50#define PLUGINS "plugins/"
51#define REPOSITORIES "repositories/"
52#define SCRIPTS "scripts/"
54#define THEMES "themes/"
55#define TMP "hydrogen/"
61#define CLICK_SAMPLE "click.wav"
62#define EMPTY_SAMPLE "emptySample.wav"
63#define DEFAULT_SONG "DefaultSong"
64#define EMPTY_SONG_BASE "emptySong"
65#define USR_CONFIG "hydrogen.conf"
66#define SYS_CONFIG "hydrogen.default.conf"
67#define LOG_FILE "hydrogen.log"
68#define DRUMKIT_XML "drumkit.xml"
69#define DRUMKIT_XSD "drumkit.xsd"
70#define DRUMPAT_XSD "drumkit_pattern.xsd"
71#define DRUMKIT_DEFAULT_KIT "GMRockKit"
72#define PLAYLIST_XSD "playlist.xsd"
74#define AUTOSAVE "autosave"
76#define UNTITLED_SONG "Untitled Song"
77#define UNTITLED_PLAYLIST "untitled.h2playlist"
80#define PATTERN_FILTER "*.h2pattern"
81#define PLAYLIST_FILTER "*.h2playlist"
82#define SONG_FILTER "*.h2song"
83#define THEME_FILTER "*.h2theme"
129#ifdef H2CORE_HAVE_BUNDLE
131 __sys_data_path = QCoreApplication::applicationDirPath().append(
"/../Resources/data/" ) ;
133 __sys_data_path = QCoreApplication::applicationDirPath().append(
"/data/" ) ;
135 __usr_data_path = QDir::homePath().append(
"/Library/Application Support/Hydrogen/data/" );
138 __sys_data_path = QCoreApplication::applicationDirPath().append(
"/data/" ) ;
142#ifdef H2CORE_HAVE_APPIMAGE
157 char* ladspaPath = getenv(
"LADSPA_PATH" );
159 INFOLOG(
"Found LADSPA_PATH environment variable" );
160 QString sLadspaPath = QString::fromLocal8Bit( ladspaPath );
162 while ( ( pos = sLadspaPath.indexOf(
":" ) ) != -1 ) {
163 QString sPath = sLadspaPath.left( pos );
165 sLadspaPath = sLadspaPath.mid( pos + 1, sLadspaPath.length() );
170 __ladspa_paths << QFileInfo( QCoreApplication::applicationDirPath(),
"/../Resources/plugins" ).canonicalFilePath();
171 __ladspa_paths << QFileInfo(
"/Library/Audio/Plug-Ins/LADSPA/" ).canonicalFilePath();
172 __ladspa_paths << QFileInfo( QDir::homePath(),
"/Library/Audio/Plug-Ins/LADSPA" ).canonicalFilePath();
174 __ladspa_paths << QFileInfo(
"/usr/lib/ladspa" ).canonicalFilePath();
175 __ladspa_paths << QFileInfo(
"/usr/local/lib/ladspa" ).canonicalFilePath();
176 __ladspa_paths << QFileInfo(
"/usr/lib64/ladspa" ).canonicalFilePath();
177 __ladspa_paths << QFileInfo(
"/usr/local/lib64/ladspa" ).canonicalFilePath();
197 QFileInfo fi( path );
199 QFileInfo folder( path.left( path.lastIndexOf(
"/" ) ) );
200 if( !folder.isDir() ) {
202 ERRORLOG( QString(
"%1 is not a directory" ).arg( folder.fileName() ) );
206 if( !folder.isWritable() ) {
208 ERRORLOG( QString(
"%1 is not writable" ).arg( folder.fileName() ) );
214 if( ( perms &
is_dir ) && !fi.isDir() ) {
216 ERRORLOG( QString(
"%1 is not a directory" ).arg( path ) );
220 if( ( perms &
is_file ) && !fi.isFile() ) {
222 ERRORLOG( QString(
"%1 is not a file" ).arg( path ) );
226 if( ( perms &
is_readable ) && !fi.isReadable() ) {
228 ERRORLOG( QString(
"%1 is not readable" ).arg( path ) );
232 if( ( perms &
is_writable ) && !fi.isWritable() ) {
234 ERRORLOG( QString(
"%1 is not writable" ).arg( path ) );
240 ERRORLOG( QString(
"%1 is not executable" ).arg( path ) );
278 if ( !QDir(
"/" ).mkpath( QDir( path ).absolutePath() ) ) {
279 ERRORLOG( QString(
"unable to create directory : %1" ).arg( path ) );
287 if ( !QDir( path ).exists() ) {
289 INFOLOG( QString(
"create user directory : %1" ).arg( path ) );
291 if ( create && !QDir(
"/" ).mkpath( path ) ) {
292 ERRORLOG( QString(
"unable to create user directory : %1" ).arg( path ) );
302 ERRORLOG( QString(
"unable to write to %1" ).arg( dst ) );
306 if ( !file.open( QIODevice::WriteOnly ) ) {
307 ERRORLOG( QString(
"unable to write to %1" ).arg( dst ) );
310 file.write( content.toUtf8().data() );
319 WARNINGLOG( QString(
"do not overwrite %1 with %2 as it already exists" ).arg( dst ).arg( src ) );
323 ERRORLOG( QString(
"unable to copy %1 to %2, %1 is not readable" ).arg( src ).arg( dst ) );
327 ERRORLOG( QString(
"unable to copy %1 to %2, %2 is not writable" ).arg( src ).arg( dst ) );
331 INFOLOG( QString(
"copy %1 to %2" ).arg( src ).arg( dst ) );
337 rm( dst,
true, bSilent );
340 return QFile::copy( src, dst );
347 bool ret = file.remove();
349 ERRORLOG( QString(
"unable to remove file %1" ).arg( path ) );
354 ERRORLOG( QString(
"%1 is neither a file nor a directory ?!?!" ).arg( path ) );
359 bool ret = dir.rmdir( path );
361 ERRORLOG( QString(
"unable to remove dir %1 without recursive argument, maybe it is not empty?" ).arg( path ) );
365 return rm_fr( path, bSilent );
371 INFOLOG( QString(
"Removing [%1] recursively" ).arg( path ) );
376 QFileInfoList entries = dir.entryInfoList( QDir::NoDotAndDotDot | QDir::AllEntries );
377 for (
int idx = 0; ( ( idx < entries.size() ) && ret ); idx++ ) {
378 QFileInfo entryInfo = entries[idx];
379 if ( entryInfo.isDir() && !entryInfo.isSymLink() ) {
380 ret =
rm_fr( entryInfo.absoluteFilePath(), bSilent );
382 QFile file( entryInfo.absoluteFilePath() );
383 if ( !file.remove() ) {
384 ERRORLOG( QString(
"unable to remove %1" ).arg( entryInfo.absoluteFilePath() ) );
389 if ( !dir.rmdir( dir.absolutePath() ) ) {
390 ERRORLOG( QString(
"unable to remove %1" ).arg( dir.absolutePath() ) );
487 if ( nIterations > 1000 ) {
488 ERRORLOG(
"That's a bit much. Something is wrong in here." );
578 if ( dk_name.isEmpty() ) {
626 return QDir::tempPath() +
"/" +
TMP;
631 QString validBase = base;
632 validBase.remove( QRegExp(
"[^a-zA-Z0-9._]" ) );
634 QFileInfo f( validBase );
635 QString templateName(
tmp_dir() +
"/" );
636 if ( f.suffix().isEmpty() ) {
637 templateName += validBase.left( 20 );
639 templateName += f.completeBaseName().left( 20 ) +
"-XXXXXX." + f.suffix();
641 QTemporaryFile file( templateName);
642 file.setAutoRemove(
false );
645 return file.fileName();
652 QStringList possible = QDir( path ).entryList( QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot );
653 foreach (
const QString& dk, possible ) {
657 ERRORLOG( QString(
"drumkit %1 is not usable" ).arg( dk ) );
702 return fname.midRef( idx ).toString();
717 int index = fname.indexOf(
"/", start );
718 QString dk_name = fname.midRef( start , index - start).toString();
727 int index = fname.indexOf(
"/", start);
728 QString dk_name = fname.midRef( start, index - start).toString();
750#ifdef H2CORE_HAVE_OSC
758 QString sDrumkitPath = QString(
"%1/%2" )
763 QFileInfo drumkitPathInfo( sDrumkitPath );
764 if ( drumkitPathInfo.isSymLink() ) {
765 sDrumkitPath = drumkitPathInfo.symLinkTarget();
770 QString sDrumkitXMLPath = QString(
"%1/%2" )
771 .arg( sDrumkitPath ).arg(
"drumkit.xml" );
773 QString sSessionDrumkitName(
"seemsLikeTheKitCouldNotBeRetrievedFromTheDatabase" );
775 if ( pSoundLibraryDatabase !=
nullptr ) {
776 auto pDrumkit = pSoundLibraryDatabase->
getDrumkit( sDrumkitPath );
777 if ( pDrumkit !=
nullptr ) {
778 sSessionDrumkitName = pDrumkit->get_name();
782 if ( dk_name == sSessionDrumkitName ) {
786 else if ( ! bSilent ) {
787 NsmClient::printError( QString(
"Local drumkit [%1] name [%2] and the one stored in .h2song file [%3] do not match!" )
788 .arg( sDrumkitXMLPath )
789 .arg( sSessionDrumkitName )
809 ERRORLOG( QString(
"drumkit [%1] not found using lookup type [%2]" )
811 .arg(
static_cast<int>(lookup)));
829 ERRORLOG( QString(
"drumkit %1 not found with lookup mode [%2]" )
830 .arg( dk_name ).arg(
static_cast<int>(lookup) ) );
835#ifdef H2CORE_HAVE_OSC
837 if ( pHydrogen !=
nullptr &&
838 pHydrogen->isUnderSessionManagement() ) {
842 QFileInfo
info( dk_path );
843 if (
info.isRelative() ) {
844 QString sAbsoluteDrumkitPath = QString(
"%1%2" )
847 .arg( dk_path.right( dk_path.size() - 1 ) );
849 QFileInfo infoAbs( sAbsoluteDrumkitPath );
850 if ( infoAbs.isSymLink() ) {
851 sAbsoluteDrumkitPath = infoAbs.symLinkTarget();
872 return dk_path +
"." +
873 QDateTime::currentDateTime().toString(
"yyyy-MM-dd_hh-mm-ss" ) +
".bak";
879 return QDir(
patterns_dir() ).entryList( QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot );
889 return QDir( path ).entryList( QStringList(
PATTERN_FILTER ), QDir::Files | QDir::Readable | QDir::NoDotAndDotDot );
895 return QDir(
songs_dir() ).entryList( QStringList(
SONG_FILTER ), QDir::Files | QDir::Readable | QDir::NoDotAndDotDot );
901 foreach (
const QString& str,
song_list() ) {
911 return QDir(
songs_dir() ).exists( sg_name );
916 QFileInfo songFileInfo = QFileInfo( sSongPath );
918 if ( !songFileInfo.isAbsolute() ) {
919 ERRORLOG( QString(
"Error: Unable to handle path [%1]. Please provide an absolute file path!" )
920 .arg( sSongPath.toLocal8Bit().data() ));
924 if ( songFileInfo.exists() ) {
925 if ( !songFileInfo.isReadable() ) {
926 ERRORLOG( QString(
"Unable to handle path [%1]. You must have permissions to read the file!" )
927 .arg( sSongPath.toLocal8Bit().data() ));
930 if ( !songFileInfo.isWritable() ) {
931 WARNINGLOG( QString(
"You don't have permissions to write to the Song found in path [%1]. It will be opened as read-only (no autosave)." )
932 .arg( sSongPath.toLocal8Bit().data() ));
935 }
else if ( bCheckExistance ) {
936 ERRORLOG( QString(
"Provided song [%1] does not exist" ).arg( sSongPath ) );
940 if ( songFileInfo.suffix() !=
"h2song" ) {
941 ERRORLOG( QString(
"Unable to handle path [%1]. The provided file must have the suffix '.h2song'!" )
942 .arg( sSongPath.toLocal8Bit().data() ));
952 QString sValidName( sPath );
953 sValidName.replace(
" ",
"_" );
954 sValidName.remove( QRegExp(
"[^a-zA-Z0-9_-]" ) );
961 return QDir(
sys_theme_dir() ).entryList( QStringList(
THEME_FILTER ), QDir::Files | QDir::Readable | QDir::NoDotAndDotDot ) +
982 INFOLOG( QString(
"Internationalization dir : %1" ).arg(
i18n_dir() ) );
1003 if ( QFile( sFilename ).exists() ) {
1004 return QFileInfo( sFilename ).absoluteFilePath();
1006 else if ( ! bSilent ) {
1007 ___ERRORLOG( QString(
"File [%1] not found" ).arg( sFilename ) );
1014#ifdef H2CORE_HAVE_OSC
1016 if ( pHydrogen !=
nullptr &&
1017 pHydrogen->isUnderSessionManagement() ) {
1019 QFileInfo
info( sPath );
1020 if (
info.isRelative() ) {
1021 return QString(
"%1%2" )
1024 .arg( sPath.right( sPath.size() - 1 ) );
1052 const QStringList legacyDirSubfolders =
1053 legacyDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot,
1054 QDir::Name | QDir::Reversed );
1056 QStringList drumkitXSDs;
1057 for (
const auto& ffolder : legacyDirSubfolders ) {
1058 const QDir folder( legacyDir.filePath( ffolder ) );
1065 return std::move( drumkitXSDs );
1069#ifdef H2CORE_HAVE_APPIMAGE
1071 if ( sDrumkitPath.isEmpty() ) {
1072 ERRORLOG(
"Can not reroute empty drumkit paths" );
1079 const QString sAbsolutePath = QDir( sDrumkitPath ).absolutePath();
1080 QString sResult = sAbsolutePath;
1084 const QStringList systemPrefixes = {
"/tmp" };
1088 bool bIsForeignSystemKit =
false;
1089 for (
const auto& ssPrefix : systemPrefixes ) {
1090 if ( sAbsolutePath.startsWith( ssPrefix ) &&
1092 bIsForeignSystemKit =
true;
1096 if ( bIsForeignSystemKit ) {
1097 const QStringList pathComponents = sAbsolutePath.split(
"/" );
1098 if ( pathComponents.size() > 2 ) {
1099 const QString sNewPath = QString(
"%1%2/%3" )
1101 .arg( pathComponents[ pathComponents.size() - 2 ] )
1102 .arg( pathComponents[ pathComponents.size() - 1 ] );
1104 INFOLOG( QString(
"Rerouting system kit: [%1] -> [%2]" )
1105 .arg( sDrumkitPath )
1111 ERRORLOG( QString(
"Unable to replace drumkit path [%1]" )
1112 .arg( sDrumkitPath ) );
1118 return sDrumkitPath;
#define CLICK_SAMPLE
Sound of metronome beat.
#define DRUMKIT_DEFAULT_KIT
#define UNTITLED_PLAYLIST
static Logger * logger()
return the logger instance
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 QStringList song_list()
returns a list of existing songs
static QString __usr_cfg_path
the path to the user config file
static bool file_copy(const QString &src, const QString &dst, bool overwrite=false, bool bSilent=false)
copy a source file to a destination
static QString scripts_dir()
returns user scripts path
static const QString patterns_filter_name
static QString playlist_xsd_path()
returns the path to the playlist pattern XSD (xml schema definition) file
static QString validateFilePath(const QString &sPath)
Takes an arbitrary path, replaces white spaces by underscores and removes all characters apart from l...
static QString demos_dir()
returns system demos path
static QString song_path(const QString &sg_name)
returns user song path, add file extension
static QString ensure_session_compatibility(const QString &sPath)
If Hydrogen is under session management, we support for paths relative to the session folder.
static QString sys_data_path()
returns system data path
static bool dir_readable(const QString &path, bool silent=false)
returns true if the given path is a readable regular directory
static QString cache_dir()
returns user cache path
static const QString patterns_ext
static bool drumkit_exists(const QString &dk_name)
returns true if the drumkit exists within usable system or user drumkits
static bool check_usr_paths()
returns true if the user path is consistent
static const QString scripts_filter_name
static bool file_executable(const QString &path, bool silent=false)
returns true if the given path is an existing executable regular file
static bool isSongPathValid(const QString &sSongPath, bool bCheckExistance=false)
Checks the path pointing to a .h2song.
static bool drumkit_valid(const QString &dk_path)
returns true if the path contains a usable drumkit
static QString playlist_path(const QString &pl_name)
returns user playlist path, add file extension
static QStringList drumkit_list(const QString &path)
static bool check_permissions(const QString &path, const int perms, bool silent)
static QString usr_config_path()
returns user config path
static QString songs_dir()
returns user songs path
static QString pattern_xsd_path()
returns the path to the pattern XSD (xml schema definition) file
static const QString themes_ext
static QString playlists_dir()
returns user playlist path
static DrumkitType determineDrumkitType(const QString &sPath)
static QString usr_click_file_path()
returns click file path from user directory if exists, otherwise from system
static QString empty_sample_path()
returns system empty sample file path
static QStringList playlist_list()
returns a list of existing playlists
static QString absolute_path(const QString &sFilename, bool bSilent=false)
Convert a direct to an absolute path.
static QString plugins_dir()
returns user plugins path
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 QStringList theme_list()
static QString usr_drumkits_dir()
returns user drumkits path
static QString untitled_song_name()
returns untitled song name
static bool check_sys_paths()
returns true if the system path is consistent
static QStringList ladspa_paths()
returns user ladspa paths
static QStringList drumkit_xsd_legacy_paths()
static QStringList pattern_list()
returns a list of existing patterns
static QString sys_drumkits_dir()
returns system drumkits path
static bool rm(const QString &path, bool recursive=false, bool bSilent=false)
remove a path
static QString drumkit_backup_path(const QString &dk_path)
Create a backup path from a drumkit path.
static QStringList song_list_cleared()
returns a list of existing songs, excluding the autosaved one
static QString usr_theme_dir()
static QString drumkit_xsd_path()
returns the path to the drumkit XSD (xml schema definition) file
static QString doc_dir()
returns documentation path
static const QString scripts_ext
static QStringList usr_drumkit_list()
returns list of usable user drumkits ( see Filesystem::drumkit_list )
static QString repositories_cache_dir()
returns user repository cache path
static bool file_is_under_drumkit(const QString &fname)
Checks if the given filepath is under an existing user or system drumkit path, not the existence of t...
static QString prepare_sample_path(const QString &fname)
Returns the basename if the given path is under an existing user or system drumkit path,...
static bool rm_fr(const QString &path, bool bSilent=false)
recursively remove a path
static bool dir_writable(const QString &path, bool silent=false)
returns true if the given path is a writable regular directory
static int get_basename_idx_under_drumkit(const QString &fname)
Returns the index of the basename if the given path is under an existing user or system drumkit path,...
static QString img_dir()
returns gui image path
static QString rerouteDrumkitPath(const QString &sDrumkitPath)
Reroutes stored drumkit paths pointing to a temporary AppImage system data folder to the current AppI...
static bool file_exists(const QString &path, bool silent=false)
returns true if the given path is an existing regular file
static QString __usr_data_path
the path to the user files
static QString empty_song_path()
Provides the full path to the current empty song.
Lookup
Whenever a drumkit is loaded by name a collision between a user and a system drumkit carrying the sam...
@ system
Only search the system drumkits.
@ stacked
First, looks in the system drumkits and, afterwards, in the user drumkits.
@ user
Only search the user drumkits.
static QString drumkit_dir_search(const QString &dk_name, Lookup lookup)
returns the directory holding the named drumkit searching within user then system drumkits
static QString m_sPreferencesOverwritePath
If this variable is non-empty, its content will be used as an alternative to store and load the prefe...
static QString click_file_path()
Returns a string containing the path to the click.wav file used in the metronome.
static QStringList sys_drumkit_list()
returns list of usable system drumkits ( see Filesystem::drumkit_list )
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 playlists_filter_name
static QString sys_config_path()
returns system config path
static bool mkdir(const QString &path)
create a path
static QString drumkit_file(const QString &dk_path)
returns the path to the xml file within a supposed drumkit path
static QStringList pattern_drumkits()
returns a list of existing drumkit sub dir into the patterns directory
static QString __sys_data_path
Path to the system files set in Filesystem::bootstrap().
static bool bootstrap(Logger *logger, const QString &sys_path=nullptr)
check user and system filesystem usability
static QString pattern_path(const QString &dk_name, const QString &p_name)
returns user patterns path, add file extension
static QString __usr_log_path
the path to the log file
static bool write_to_file(const QString &dst, const QString &content)
writes to a file
static Logger * __logger
a pointer to the logger
static const QString playlist_ext
static QString i18n_dir()
returns internationalization path
static const QString songs_filter_name
static QString xsd_legacy_dir()
static QString tmp_dir()
returns temp path
DrumkitType
Determines were to find a kit and whether it is writable by the current user.
@ 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.
@ System
Kit was installed with Hydrogen, is automatically loaded, and most probably readonly.
@ SessionReadOnly
Kit was loaded via a NSM session, OSC command, or CLI option, only persist for the current Hydrogen s...
static void info()
send current settings information to logger with INFO severity
static QString usr_data_path()
returns user data path
static QString log_file_path()
returns the full path (including filename) of the logfile
static QString drumkit_usr_path(const QString &dk_name)
returns path for a drumkit within user drumkit path
static const QString drumkit_ext
static const QString themes_filter_name
static const QString songs_ext
static QString sys_theme_dir()
static bool path_usable(const QString &path, bool create=true, bool silent=false)
returns true if the path is a readable and writable regular directory, create if it not exists
static QString drumkit_xml()
Returns filename and extension of the expected drumkit file.
static QString drumkit_xsd()
returns the drumkit XSD (xml schema definition) name
static QString tmp_file_path(const QString &base)
touch a temporary file under tmp_dir() and return it's path.
static QString default_song_name()
Default option to offer the user when saving an empty song to disk.
static QString xsd_dir()
returns system xsd path
static bool song_exists(const QString &sg_name)
returns true if the song file exists
static QStringList __ladspa_paths
paths to laspa plugins
static QString untitled_playlist_file_name()
returns untitled playlist file name
static bool file_readable(const QString &path, bool silent=false)
returns true if the given path is an existing readable regular file
static bool dir_exists(const QString &path, bool silent=false)
returns true if the given path is a regular directory
static QString drumkit_default_kit()
static QString patterns_dir()
returns user patterns path
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
SoundLibraryDatabase * getSoundLibraryDatabase() const
Class for writing logs to the console.
std::shared_ptr< Drumkit > getDrumkit(const QString &sDrumkitPath, bool bLoad=true)
Retrieve a drumkit from the database.
static void printError(const QString &msg)
Custom function to print a colored error message.
static NsmClient * get_instance()
#define H2_SYS_PATH
Path of the configuration files of Hydrogen in the system.
#define H2_USR_PATH
Path of the configuration files of Hydrogen in the user home.
@ EVENT_UPDATE_SONG
Event triggering HydrogenApp::updateSongEvent() whenever the Song was changed outside of the GUI,...