hydrogen 1.2.3
NsmClient.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
25#include "core/EventQueue.h"
26#include "core/Hydrogen.h"
27#include "core/Basics/Drumkit.h"
31#include "core/Basics/Sample.h"
32#include "core/Basics/Song.h"
34#include "core/NsmClient.h"
36
37#include <QDir>
38#include <QFile>
39#include <QFileInfo>
40#include <pthread.h>
41#include <unistd.h>
42
43#if defined(H2CORE_HAVE_OSC) || _DOXYGEN_
44
46bool NsmClient::bNsmShutdown = false;
47
48
50 : m_pNsm( nullptr ),
51 m_bUnderSessionManagement( false ),
52 m_NsmThread( 0 ),
53 m_sSessionFolderPath( "" ),
54 m_bIsNewSession( false )
55{
56}
57
59{
60 __instance = nullptr;
61}
62
64{
65 if( __instance == nullptr ) {
67 }
68}
69
70int NsmClient::OpenCallback( const char *name,
71 const char *displayName,
72 const char *clientID,
73 char **outMsg,
74 void *userData ) {
75
76 auto pHydrogen = H2Core::Hydrogen::get_instance();
78 auto pController = pHydrogen->getCoreActionController();
79
80 if ( !name ) {
81 NsmClient::printError( "No `name` supplied in NSM open callback!" );
82 return ERR_LAUNCH_FAILED;
83 }
84
85 // Cause there is no newline in the output of nsmd shown
86 // beforehand.
87 std::cout << std::endl;
88
89 // NSM sends a unique string, like - if the displayName ==
90 // Hydrogen - "Hydrogen.nJKUV". In order to make the whole
91 // Hydrogen session reproducible, a folder will be created, which
92 // will contain the song file, a copy of the current preferences,
93 // and a symbolic link to the drumkit.
94 QDir sessionFolder( name );
95 if ( !sessionFolder.exists() ) {
96 if ( !sessionFolder.mkpath( name ) ) {
97 NsmClient::printError( "Folder could not created." );
98 }
99 }
100
102
104
105 const QFileInfo sessionPath( name );
106 const QString sSongPath = QString( "%1/%2%3" )
107 .arg( name )
108 .arg( sessionPath.fileName() )
110
111 const QFileInfo songFileInfo = QFileInfo( sSongPath );
112
113 // When restarting the JACK client (during song loading) the
114 // clientID will be used as the name of the freshly created
115 // instance.
116 if ( pPref != nullptr ){
117 if ( clientID ){
118 // Setup JACK here, client_id gets the JACK client name
119 pPref->setNsmClientId( QString( clientID ) );
120 } else {
121 NsmClient::printError( "No `clientID` supplied in NSM open callback!" );
122 return ERR_LAUNCH_FAILED;
123 }
124 } else {
125 NsmClient::printError( "Preferences instance is not ready yet!" );
126 return ERR_NOT_NOW;
127 }
128
129 bool bEmptySongOpened = false;
130 std::shared_ptr<H2Core::Song> pSong = nullptr;
131 if ( songFileInfo.exists() ) {
132
133 // Song loading itself does not add the drumkit found to the
134 // SoundLibraryDatabase (in order to avoid problem with cyclic
135 // dependencies between drumkits).
136 loadDrumkit();
137
138 pSong = H2Core::Song::load( sSongPath );
139 if ( pSong == nullptr ) {
140 NsmClient::printError( QString( "Unable to open existing Song [%1]." )
141 .arg( sSongPath ) );
142 return ERR_LAUNCH_FAILED;
143 }
144
145 } else {
146
148 if ( pSong == nullptr ) {
149 NsmClient::printError( "Unable to open new Song." );
150 return ERR_LAUNCH_FAILED;
151 }
152 pSong->setFilename( sSongPath );
153 bEmptySongOpened = true;
154
155 // Mark empty song modified in order to emphasis that an
156 // initial song save is required to generate the song file and
157 // link the associated drumkit in the session folder.
158 pSong->setIsModified( true );
160
161 // The drumkit of the new song will linked into the session
162 // folder during the next song save.
163 pHydrogen->setSessionDrumkitNeedsRelinking( true );
164 }
165
166 if ( ! pController->openSong( pSong, false /*relinking*/ ) ) {
167 NsmClient::printError( "Unable to handle opening action!" );
168 return ERR_LAUNCH_FAILED;
169 }
170
171 NsmClient::printMessage( "Song loaded!" );
172
173 return ERR_OK;
174}
175
176void NsmClient::copyPreferences( const char* name ) {
177
179 auto pHydrogen = H2Core::Hydrogen::get_instance();
180 auto pCoreActionController = pHydrogen->getCoreActionController();
181
182 QFile preferences( H2Core::Filesystem::usr_config_path() );
183 if ( !preferences.exists() ) {
184 preferences.setFileName( H2Core::Filesystem::sys_config_path() );
185 }
186
187 const QString sNewPreferencesPath = QString( "%1/%2" )
188 .arg( name )
189 .arg( QFileInfo( H2Core::Filesystem::usr_config_path() )
190 .fileName() );
191
192 // Store the path in a session variable of the Preferences
193 // singleton, which allows overwriting the default path used
194 // throughout the application.
196
197 const QFileInfo newPreferencesFileInfo( sNewPreferencesPath );
198 if ( newPreferencesFileInfo.exists() ){
199 // If there's already a preference file present from a
200 // previous session, we load it instead of overwriting it.
201 pPref->loadPreferences( false );
202
203 } else {
204 if ( !preferences.copy( sNewPreferencesPath ) ) {
205 NsmClient::printError( QString( "Unable to copy preferences to [%1]" )
206 .arg( sNewPreferencesPath ) );
207 } else {
208 NsmClient::printMessage( QString( "Preferences copied to [%1]" )
209 .arg( sNewPreferencesPath ) );
210 // The copied preferences file is already loaded.
211 }
212 }
213
214 pCoreActionController->updatePreferences();
215
216 NsmClient::printMessage( "Preferences loaded!" );
217}
218
220
221 const auto pHydrogen = H2Core::Hydrogen::get_instance();
222 const QString sSessionFolder = NsmClient::get_instance()->getSessionFolderPath();
223 const QString sLinkedDrumkitPath = QString( "%1/%2" )
224 .arg( sSessionFolder ).arg( "drumkit" );
225 const QFileInfo linkedDrumkitPathInfo( sLinkedDrumkitPath );
226
227 // Check whether the linked folder is valid.
228 if ( linkedDrumkitPathInfo.isSymLink() ||
229 linkedDrumkitPathInfo.isDir() ) {
230
231 auto pDrumkit =
232 pHydrogen->getSoundLibraryDatabase()->getDrumkit( sLinkedDrumkitPath );
233 if ( pDrumkit == nullptr ) {
234 ERRORLOG( "Unable to load drumkit from session folder" );
235 }
236 }
237 else {
238 ERRORLOG( "No valid drumkit found in session folder" );
239 }
240}
241
242void NsmClient::linkDrumkit( std::shared_ptr<H2Core::Song> pSong ) {
243
244 const auto pHydrogen = H2Core::Hydrogen::get_instance();
245
246 bool bRelinkDrumkit = true;
247
248 const QString sDrumkitName = pSong->getLastLoadedDrumkitName();
249 const QString sDrumkitAbsPath = pSong->getLastLoadedDrumkitPath();
250
251 const QString sSessionFolder = NsmClient::get_instance()->getSessionFolderPath();
252
253 // Sanity check in order to avoid circular linking.
254 if ( sDrumkitAbsPath.contains( sSessionFolder, Qt::CaseInsensitive ) ) {
255 NsmClient::printError( QString( "Last loaded drumkit [%1] with absolute path [%2] is located within the session folder [%3]. Linking skipped." )
256 .arg( sDrumkitName )
257 .arg( sDrumkitAbsPath )
258 .arg( sSessionFolder ) );
259 return;
260 }
261
262 const QString sLinkedDrumkitPath = QString( "%1/%2" )
263 .arg( sSessionFolder ).arg( "drumkit" );
264 const QFileInfo linkedDrumkitPathInfo( sLinkedDrumkitPath );
265
266 // Check whether the linked folder is still valid.
267 if ( linkedDrumkitPathInfo.isSymLink() ||
268 linkedDrumkitPathInfo.isDir() ) {
269
270 // In case of a symbolic link, the target it is pointing to
271 // has to be resolved. If drumkit is a real folder, we will
272 // search for a drumkit.xml therein.
273 QString sLinkedDrumkitPath;
274 if ( linkedDrumkitPathInfo.isSymLink() ) {
275 sLinkedDrumkitPath = QString( "%1" )
276 .arg( linkedDrumkitPathInfo.symLinkTarget() );
277 } else {
278 sLinkedDrumkitPath = QString( "%1" )
279 .arg( sLinkedDrumkitPath );
280 }
281
282 if ( H2Core::Filesystem::drumkit_valid( sLinkedDrumkitPath ) ) {
283
284 QString sLinkedDrumkitName( "seemsLikeTheKitCouldNotBeRetrievedFromTheDatabase" );
285 auto pSoundLibraryDatabase = pHydrogen->getSoundLibraryDatabase();
286 if ( pSoundLibraryDatabase != nullptr ) {
287 auto pDrumkit = pSoundLibraryDatabase->getDrumkit( sLinkedDrumkitPath );
288 if ( pDrumkit != nullptr ) {
289 sLinkedDrumkitName = pDrumkit->get_name();
290 }
291 }
292
293 if ( sLinkedDrumkitName == sDrumkitName ) {
294 bRelinkDrumkit = false;
295 }
296 }
297 else {
298 NsmClient::printError( "Symlink does not point to valid drumkit." );
299 }
300 }
301
302 // The symbolic link either does not exist, is not valid, or does
303 // point to the wrong location. Remove it and create a fresh one.
304 if ( bRelinkDrumkit ){
305 NsmClient::printMessage( "Relinking drumkit" );
306 QFile linkedDrumkitFile( sLinkedDrumkitPath );
307
308 if ( linkedDrumkitFile.exists() ) {
309 if ( linkedDrumkitPathInfo.isDir() &&
310 ! linkedDrumkitPathInfo.isSymLink() ) {
311 // Move the folder so we don't use the precious old
312 // drumkit. But in order to use it again, it has to be
313 // renamed to 'drumkit' manually again.
314 QDir oldDrumkitFolder( sLinkedDrumkitPath );
315 if ( ! oldDrumkitFolder.rename( sLinkedDrumkitPath,
316 QString( "%1/drumkit_old" )
317 .arg( sSessionFolder ) ) ) {
318 NsmClient::printError( QString( "Unable to rename drumkit folder [%1]." )
319 .arg( sLinkedDrumkitPath ) );
320 return;
321 }
322 } else {
323 if ( ! linkedDrumkitFile.remove() ) {
324 NsmClient::printError( QString( "Unable to remove symlink to drumkit [%1]." )
325 .arg( sLinkedDrumkitPath ) );
326 return;
327 }
328 }
329 }
330
331 if ( sDrumkitAbsPath.isEmpty() ) {
332 // Something went wrong. We skip the linking.
333 NsmClient::printError( QString( "No drumkit named [%1] could be found." )
334 .arg( sDrumkitName ) );
335 } else {
336
337 // Actual linking.
338 QFile targetPath( sDrumkitAbsPath );
339 if ( !targetPath.link( sLinkedDrumkitPath ) ) {
340 NsmClient::printError( QString( "Unable to link drumkit [%1] to [%2]." )
341 .arg( sLinkedDrumkitPath )
342 .arg( sDrumkitAbsPath ) );
343 }
344 }
345 }
346
347 // Replace the temporary reference to the "global" drumkit to the
348 // (freshly) linked/found one in the session folder.
349 NsmClient::replaceDrumkitPath( pSong, "./drumkit" );
350
351 pHydrogen->setSessionDrumkitNeedsRelinking( false );
352}
353
354int NsmClient::dereferenceDrumkit( std::shared_ptr<H2Core::Song> pSong ) {
355 auto pHydrogen = H2Core::Hydrogen::get_instance();
356
357 if ( pSong == nullptr ) {
358 ERRORLOG( "no song set" );
359 return -1;
360 }
361
362 const QString sLastLoadedDrumkitPath = pSong->getLastLoadedDrumkitPath();
363 const QString sLastLoadedDrumkitName = pSong->getLastLoadedDrumkitName();
364
365 if ( ! sLastLoadedDrumkitPath.contains( NsmClient::get_instance()->
367 Qt::CaseInsensitive ) ) {
368 // Regular path. We do not have to alter it.
369 return 0;
370 }
371
372 const QFileInfo lastLoadedDrumkitInfo( sLastLoadedDrumkitPath );
373 if ( lastLoadedDrumkitInfo.isSymLink() ) {
374
375 QString sDeferencedDrumkit = lastLoadedDrumkitInfo.symLinkTarget();
376
377 NsmClient::printMessage( QString( "Dereferencing linked drumkit to [%1]" )
378 .arg( sDeferencedDrumkit ) );
379 NsmClient::replaceDrumkitPath( pSong, sDeferencedDrumkit );
380 }
381 else if ( lastLoadedDrumkitInfo.isDir() ) {
382 // Drumkit is not linked into the session folder but present
383 // within a directory (probably because the session was
384 // transfered from another device to recovered from a
385 // backup).
386 //
387 // This is a little bit tricky as we do not want to install
388 // the kit into the user's data folder on our own (loss of
389 // data etc.). If a kit containing the same name is present,
390 // we will assume the kits do match. That's nowhere near
391 // perfect but we are dealing with an edge-case of an
392 // edge-case in here anyway. If it not exists, we will prompt
393 // a warning dialog (via the GUI) asking the user to install
394 // it herself.
395 bool bDrumkitFound = false;
396 for ( const auto& pDrumkitEntry :
397 pHydrogen->getSoundLibraryDatabase()->getDrumkitDatabase() ) {
398
399 auto pDrumkit = pDrumkitEntry.second;
400 if ( pDrumkit != nullptr ) {
401 if ( pDrumkit->get_name() == sLastLoadedDrumkitName ) {
402 NsmClient::replaceDrumkitPath( pSong, pDrumkitEntry.first );
403 bDrumkitFound = true;
404 break;
405
406 }
407 }
408 }
409
410 if ( ! bDrumkitFound ) {
411 ERRORLOG( QString( "Drumkit used in session folder [%1] is not present on the current system. It has to be installed first in order to use the exported song" )
412 .arg( sLastLoadedDrumkitName ) );
414 return -2;
415 }
416 else {
417 INFOLOG( QString( "Drumkit used in session folder [%1] was dereferenced to [%2]" )
418 .arg( sLastLoadedDrumkitName )
419 .arg( pSong->getLastLoadedDrumkitPath() ) );
420 }
421 }
422 else {
423 ERRORLOG( "This should not happen" );
424 return -1;
425 }
426 return 0;
427}
428
429void NsmClient::replaceDrumkitPath( std::shared_ptr<H2Core::Song> pSong, const QString& sDrumkitPath ) {
430 auto pHydrogen = H2Core::Hydrogen::get_instance();
431
432 // We are only replacing the paths corresponding to the drumkit
433 // which is either about to be linked into the session folder or
434 // the one which is supposed to replace the linked one.
435 const QString sDrumkitToBeReplaced = pSong->getLastLoadedDrumkitPath();
436
437 pSong->setLastLoadedDrumkitPath( sDrumkitPath );
438
439 for ( auto pInstrument : *pSong->getInstrumentList() ) {
440 if ( pInstrument != nullptr &&
441 pInstrument->get_drumkit_path() == sDrumkitToBeReplaced ) {
442
443 pInstrument->set_drumkit_path( sDrumkitPath );
444
445 // Use full paths in case the drumkit in sDrumkitPath is
446 // not located in either the user's or system's drumkit
447 // folder or just use the filenames (and load the
448 // relatively) otherwise.
449 for ( auto pComponent : *pInstrument->get_components() ) {
450 if ( pComponent != nullptr ) {
451 for ( auto pInstrumentLayer : *pComponent ) {
452 if ( pInstrumentLayer != nullptr ) {
453 auto pSample = pInstrumentLayer->get_sample();
454 if ( pSample != nullptr ) {
455 QString sNewPath = QString( "%1/%2" )
456 .arg( sDrumkitPath )
457 .arg( pSample->get_filename() );
458
459 pSample->set_filepath( H2Core::Filesystem::prepare_sample_path( sNewPath ) );
460 }
461 }
462 }
463 }
464 }
465 }
466 }
467}
468
469void NsmClient::printError( const QString& msg ) {
470 std::cerr << "[\033[30mHydrogen\033[0m]\033[31m "
471 << "Error: " << msg.toLocal8Bit().data() << "\033[0m" << std::endl;
472}
473void NsmClient::printMessage( const QString& msg ) {
474 std::cerr << "[\033[30mHydrogen\033[0m]\033[32m "
475 << msg.toLocal8Bit().data() << "\033[0m" << std::endl;
476}
477
478int NsmClient::SaveCallback( char** outMsg, void* userData ) {
479
481
482 if ( ! pController->saveSong() ) {
483 NsmClient::printError( "Unable to save Song!" );
484 return ERR_GENERAL;
485 }
486 if ( ! pController->savePreferences() ) {
487 NsmClient::printError( "Unable to save Preferences!" );
488 return ERR_GENERAL;
489 }
490
491 NsmClient::printMessage( "Song and Preferences saved!" );
492
493 return ERR_OK;
494}
495
496void* NsmClient::ProcessEvent(void* data) {
497 nsm_client_t* pNsm = (nsm_client_t*) data;
498
499 while( !NsmClient::bNsmShutdown && pNsm ){
500 nsm_check_wait( pNsm, 1000 );
501 }
502
503 return nullptr;
504}
505
507{
509}
510
512{
513 /*
514 * Make first contact with NSM server.
515 */
516
517 nsm_client_t* pNsm = nullptr;
518
520 QString H2ProcessName = pPref->getH2ProcessName();
521 QByteArray byteArray = H2ProcessName.toLatin1();
522
523 const char *nsm_url = getenv( "NSM_URL" );
524
525 if ( nsm_url )
526 {
527 pNsm = nsm_new();
528
529 // Store the nsm client in a private member variable for later
530 // access.
531 m_pNsm = pNsm;
532
533 if ( pNsm )
534 {
535 nsm_set_open_callback( pNsm, NsmClient::OpenCallback, (void*) nullptr );
536 nsm_set_save_callback( pNsm, NsmClient::SaveCallback, (void*) nullptr );
537
538 if ( nsm_init( pNsm, nsm_url ) == 0 )
539 {
540 // Technically Hydrogen will be under session
541 // management after the nsm_send_announce and
542 // nsm_check_wait function are called. But since the
543 // NsmClient::OpenCallback() will be called by the NSM server
544 // immediately after receiving the announce and some
545 // of the functions called thereafter do check whether
546 // H2 is under session management, the variable will
547 // be set here.
549
550 nsm_send_announce( pNsm, "Hydrogen", ":dirty:switch:", byteArray.data() );
551
552 if ( pthread_create( &m_NsmThread, nullptr, NsmClient::ProcessEvent, pNsm ) ) {
553 ___ERRORLOG("Error creating NSM thread\n ");
555 return;
556 }
557
558 // Wait until first the Song and afterwards the audio
559 // driver was set (asynchronously by the
560 // NsmClient::OpenCallback() function).
562 const int nNumberOfChecks = 10;
563 int nCheck = 0;
564
565 while ( true ) {
566 if ( pHydrogen->getSong() != nullptr ) {
567 break;
568 }
569 // Don't wait indefinitely.
570 if ( nCheck > nNumberOfChecks ) {
571 break;
572 }
573 nCheck++;
574 sleep( 1 );
575 }
576
577 } else {
578 ___ERRORLOG("failed, freeing NSM client");
579 nsm_free( pNsm );
580 pNsm = nullptr;
581 m_pNsm = nullptr;
582 }
583 }
584 }
585 else
586 {
587 ___WARNINGLOG("No NSM URL available: no NSM management\n");
588 }
589}
590
591void NsmClient::sendDirtyState( const bool bIsDirty ) {
592
593 if ( m_pNsm != nullptr ) {
594 if ( bIsDirty ) {
596 } else {
598 }
599 }
600}
601
602#endif /* H2CORE_HAVE_OSC */
603
#define sleep(SECONDS)
NSM_EXPORT nsm_client_t * nsm_new(void)
Definition Nsm.h:155
NSM_EXPORT void nsm_check_wait(nsm_client_t *nsm, int timeout)
Definition Nsm.h:249
NSM_EXPORT void nsm_set_save_callback(nsm_client_t *nsm, nsm_save_callback *save_callback, void *userdata)
Definition Nsm.h:308
NSM_EXPORT int nsm_init(nsm_client_t *nsm, const char *nsm_url)
Definition Nsm.h:493
void * nsm_client_t
Definition Nsm.h:86
NSM_EXPORT void nsm_send_is_clean(nsm_client_t *nsm)
Definition Nsm.h:192
NSM_EXPORT void nsm_send_announce(nsm_client_t *nsm, const char *app_name, const char *capabilities, const char *process_name)
Definition Nsm.h:215
@ ERR_OK
Definition Nsm.h:128
@ ERR_NOT_NOW
Definition Nsm.h:136
@ ERR_LAUNCH_FAILED
Definition Nsm.h:132
@ ERR_GENERAL
Definition Nsm.h:129
NSM_EXPORT void nsm_set_open_callback(nsm_client_t *nsm, nsm_open_callback *open_callback, void *userdata)
Definition Nsm.h:300
NSM_EXPORT void nsm_send_is_dirty(nsm_client_t *nsm)
Definition Nsm.h:184
NSM_EXPORT void nsm_free(nsm_client_t *nsm)
Definition Nsm.h:281
#define ___WARNINGLOG(x)
Definition Object.h:256
#define INFOLOG(x)
Definition Object.h:237
#define ERRORLOG(x)
Definition Object.h:239
#define ___ERRORLOG(x)
Definition Object.h:257
static bool drumkit_valid(const QString &dk_path)
returns true if the path contains a usable drumkit
static void setPreferencesOverwritePath(const QString &sPath)
Definition Filesystem.h:543
static QString usr_config_path()
returns user config path
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 QString sys_config_path()
returns system config path
static const QString songs_ext
Definition Filesystem.h:87
Hydrogen Audio Engine.
Definition Hydrogen.h:54
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
CoreActionController * getCoreActionController() const
Definition Hydrogen.h:639
Manager for User Preferences File (singleton)
Definition Preferences.h:78
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString getH2ProcessName()
static std::shared_ptr< Song > load(const QString &sFilename, bool bSilent=false)
Load a song from file.
Definition Song.cpp:187
static std::shared_ptr< Song > getEmptySong()
Definition Song.cpp:953
Non session manager client implementation.
Definition NsmClient.h:58
void setIsNewSession(bool bNew)
Definition NsmClient.h:345
void setSessionFolderPath(const QString &sPath)
Definition NsmClient.h:338
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...
~NsmClient()
Destructor.
Definition NsmClient.cpp:58
void sendDirtyState(const bool isDirty)
Informs the NSM server whether the current H2Core::Song is modified or not.
static int OpenCallback(const char *name, const char *displayName, const char *clientID, char **outMsg, void *userData)
Callback function for the NSM server to tell Hydrogen to open a H2Core::Song.
Definition NsmClient.cpp:70
static void printError(const QString &msg)
Custom function to print a colored error message.
static NsmClient * get_instance()
Definition NsmClient.h:84
static void loadDrumkit()
Checks whether there is a drumkit present in the session folder and loads it into the H2Core::SoundLi...
static NsmClient * __instance
Object holding the current NsmClient singleton.
Definition NsmClient.h:67
static void create_instance()
If __instance equals nullptr, a new NsmClient singleton will be created and stored in it.
Definition NsmClient.cpp:63
void shutdown()
Causes the NSM client to not process events anymore.
static void copyPreferences(const char *name)
Part of OpenCallback() responsible for copying and loading the preferences.
QString getSessionFolderPath() const
Definition NsmClient.h:335
nsm_client_t * m_pNsm
Stores the current instance of the NSM client.
Definition NsmClient.h:211
static void linkDrumkit(std::shared_ptr< H2Core::Song > pSong)
Responsible for linking a drumkit on user or system level into the session folder and updating all co...
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...
static bool bNsmShutdown
Indicates whether the NsmClient::NsmProcessEvent() function should continue processing events.
Definition NsmClient.h:328
void createInitialClient()
Actual setup, initialization, and registration of the NSM client.
static int SaveCallback(char **outMsg, void *userData)
Callback function for the NSM server to tell Hydrogen to save the current session.
static void printMessage(const QString &msg)
Custom function to print a colored message.
bool m_bUnderSessionManagement
To determine whether Hydrogen is under NON session management, it is not sufficient to check whether ...
Definition NsmClient.h:220
pthread_t m_NsmThread
Thread the NSM client will run in.
Definition NsmClient.h:71
NsmClient()
Private constructor to allow construction only via create_instance().
Definition NsmClient.cpp:49
static void * ProcessEvent(void *data)
Event handling function of the NSM client.