hydrogen 1.2.3
Drumkit.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
23#include <core/Basics/Drumkit.h>
24#include <core/config.h>
25#ifdef H2CORE_HAVE_LIBARCHIVE
26#include <archive.h>
27#include <archive_entry.h>
28#else
29#ifndef WIN32
30#include <fcntl.h>
31#include <errno.h>
32#include <zlib.h>
33#include <libtar.h>
34#endif
35#endif
36
37#include <core/Basics/Sample.h>
43
44#include <core/Helpers/Xml.h>
45#include <core/Helpers/Legacy.h>
46
47#include <core/Hydrogen.h>
49
50namespace H2Core
51{
52
53Drumkit::Drumkit() : __samples_loaded( false ),
54 __instruments( nullptr ),
55 __name( "empty" ),
56 __author( "undefined author" ),
57 __info( "No information available." ),
58 __license( License() ),
59 __imageLicense( License() )
60{
61 QDir usrDrumkitPath( Filesystem::usr_drumkits_dir() );
62 __path = usrDrumkitPath.filePath( __name );
63 __components = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
64 __instruments = std::make_shared<InstrumentList>();
65}
66
67Drumkit::Drumkit( std::shared_ptr<Drumkit> other ) :
68 Object(),
69 __path( other->get_path() ),
70 __name( other->get_name() ),
71 __author( other->get_author() ),
72 __info( other->get_info() ),
73 __license( other->get_license() ),
74 __image( other->get_image() ),
75 __imageLicense( other->get_image_license() ),
76 __samples_loaded( other->samples_loaded() )
77{
78 __instruments = std::make_shared<InstrumentList>( other->get_instruments() );
79
80 __components = std::make_shared<std::vector<std::shared_ptr<DrumkitComponent>>>();
81 for ( const auto& pComponent : *other->get_components() ) {
82 __components->push_back( std::make_shared<DrumkitComponent>( pComponent ) );
83 }
84}
85
89
90std::shared_ptr<Drumkit> Drumkit::load( const QString& sDrumkitPath, bool bUpgrade, bool bSilent )
91{
92 if ( ! Filesystem::drumkit_valid( sDrumkitPath ) ) {
93 ERRORLOG( QString( "[%1] is not valid drumkit folder" ).arg( sDrumkitPath ) );
94 return nullptr;
95 }
96
97 QString sDrumkitFile = Filesystem::drumkit_file( sDrumkitPath );
98
99 bool bReadingSuccessful = true;
100
101 XMLDoc doc;
102 if ( !doc.read( sDrumkitFile, Filesystem::drumkit_xsd_path(), true ) ) {
103 // Drumkit does not comply with the XSD schema
104 // definition. It's probably an old one. load_from() will try
105 // to handle it regardlessly but we should upgrade it in order
106 // to avoid this in future loads.
107 doc.read( sDrumkitFile, nullptr, bSilent );
108
109 bReadingSuccessful = false;
110 }
111
112 XMLNode root = doc.firstChildElement( "drumkit_info" );
113 if ( root.isNull() ) {
114 ERRORLOG( "drumkit_info node not found" );
115 return nullptr;
116 }
117
118 auto pDrumkit =
119 Drumkit::load_from( &root, sDrumkitFile.left( sDrumkitFile.lastIndexOf( "/" ) ),
120 bSilent );
121
122 if ( pDrumkit == nullptr ) {
123 ERRORLOG( QString( "Unable to load drumkit [%1]" ).arg( sDrumkitFile ) );
124 return nullptr;
125 }
126
127 if ( ! bReadingSuccessful && bUpgrade ) {
128 upgrade_drumkit( pDrumkit, sDrumkitPath );
129 }
130
131 return pDrumkit;
132}
133
134std::shared_ptr<Drumkit> Drumkit::load_from( XMLNode* node, const QString& sDrumkitPath, bool bSilent )
135{
136 QString sDrumkitName = node->read_string( "name", "", false, false, bSilent );
137 if ( sDrumkitName.isEmpty() ) {
138 ERRORLOG( "Drumkit has no name, abort" );
139 return nullptr;
140 }
141
142 std::shared_ptr<Drumkit> pDrumkit = std::make_shared<Drumkit>();
143
144 pDrumkit->__path = sDrumkitPath;
145 pDrumkit->__name = sDrumkitName;
146 pDrumkit->__author = node->read_string( "author", "undefined author",
147 true, true, true );
148 pDrumkit->__info = node->read_string( "info", "No information available.",
149 true, true, bSilent );
150
151 License license( node->read_string( "license", "undefined license",
152 true, true, bSilent ),
153 pDrumkit->__author );
154 pDrumkit->set_license( license );
155
156 // As of 2022 we have no drumkits featuring an image in
157 // stock. Thus, verbosity of this one will be turned of in order
158 // to make to log more concise.
159 pDrumkit->set_image( node->read_string( "image", "",
160 true, true, true ) );
161 License imageLicense( node->read_string( "imageLicense", "undefined license",
162 true, true, true ),
163 pDrumkit->__author );
164 pDrumkit->set_image_license( imageLicense );
165
166 XMLNode componentListNode = node->firstChildElement( "componentList" );
167 if ( ! componentListNode.isNull() ) {
168 XMLNode componentNode = componentListNode.firstChildElement( "drumkitComponent" );
169 while ( ! componentNode.isNull() ) {
170 auto pDrumkitComponent = DrumkitComponent::load_from( &componentNode );
171 if ( pDrumkitComponent != nullptr ) {
172 pDrumkit->get_components()->push_back(pDrumkitComponent);
173 }
174
175 componentNode = componentNode.nextSiblingElement( "drumkitComponent" );
176 }
177 } else {
178 WARNINGLOG( "componentList node not found" );
179 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0, "Main" );
180 pDrumkit->get_components()->push_back(pDrumkitComponent);
181 }
182
183 auto pInstrumentList = InstrumentList::load_from( node,
184 sDrumkitPath,
185 sDrumkitName,
186 license, false );
187 // Required to assure backward compatibility.
188 if ( pInstrumentList == nullptr ) {
189 WARNINGLOG( "instrument list could not be loaded. Using empty one." );
190 pInstrumentList = std::make_shared<InstrumentList>();
191 }
192
193 pDrumkit->set_instruments( pInstrumentList );
194
195 // Instead of making the *::load_from() functions more complex by
196 // passing the license down to each sample, we will make the
197 // drumkit assign its license to each sample in here.
198 pDrumkit->propagateLicense();
199
200 return pDrumkit;
201
202}
203
205{
206 INFOLOG( QString( "Loading drumkit %1 instrument samples" ).arg( __name ) );
207 if( !__samples_loaded ) {
208 __instruments->load_samples();
209 __samples_loaded = true;
210 }
211}
212
213License Drumkit::loadLicenseFrom( const QString& sDrumkitDir, bool bSilent )
214{
215 XMLDoc doc;
216 if ( Drumkit::loadDoc( sDrumkitDir, &doc, bSilent ) ) {
217 XMLNode root = doc.firstChildElement( "drumkit_info" );
218
219 QString sAuthor = root.read_string( "author", "undefined author",
220 true, true, bSilent );
221 QString sLicenseString = root.read_string( "license", "undefined license",
222 false, true, bSilent );
223 if ( sLicenseString.isNull() ) {
224 ERRORLOG( QString( "Unable to retrieve license information from [%1]" )
225 .arg( sDrumkitDir ) );
226 return std::move( License() );
227 }
228
229 return std::move( License( sLicenseString, sAuthor ) );
230 }
231
232 return std::move( License() );
233}
234
235bool Drumkit::loadDoc( const QString& sDrumkitDir, XMLDoc* pDoc, bool bSilent ) {
236
237 if ( ! Filesystem::drumkit_valid( sDrumkitDir ) ) {
238 ERRORLOG( QString( "[%1] is not valid drumkit folder" ).arg( sDrumkitDir ) );
239 return false;
240 }
241
242 const QString sDrumkitPath = Filesystem::drumkit_file( sDrumkitDir );
243
244 if( ! pDoc->read( sDrumkitPath, Filesystem::drumkit_xsd_path(), true ) ) {
245 if ( ! bSilent ) {
246 WARNINGLOG( QString( "[%1] does not validate against drumkit schema. Trying to retrieve its name nevertheless.")
247 .arg( sDrumkitPath ) );
248 }
249
250 if ( ! pDoc->read( sDrumkitPath, nullptr, bSilent ) ) {
251 ERRORLOG( QString( "Unable to load drumkit name for [%1]" )
252 .arg( sDrumkitPath ) );
253 return false;
254 }
255 }
256
257 XMLNode root = pDoc->firstChildElement( "drumkit_info" );
258 if ( root.isNull() ) {
259 ERRORLOG( QString( "Unable to load drumkit name for [%1]. 'drumkit_info' node not found" )
260 .arg( sDrumkitPath ) );
261 return false;
262 }
263
264 return true;
265}
266
267void Drumkit::upgrade_drumkit(std::shared_ptr<Drumkit> pDrumkit, const QString& sDrumkitPath, bool bSilent )
268{
269 if ( pDrumkit != nullptr ) {
270 const QString sDrumkitFile = Filesystem::drumkit_file( sDrumkitPath );
271 if ( ! Filesystem::file_exists( sDrumkitFile, true ) ) {
272 ERRORLOG( QString( "No drumkit.xml found in folder [%1]" ).arg( sDrumkitPath ) );
273 return;
274 }
275
276 if ( ! Filesystem::dir_writable( sDrumkitPath, true ) ) {
277 ERRORLOG( QString( "Drumkit in [%1] is out of date but can not be upgraded since path is not writable (please copy it to your user's home instead)" ).arg( sDrumkitPath ) );
278 return;
279 }
280 if ( ! bSilent ) {
281 INFOLOG( QString( "Upgrading drumkit [%1]" ).arg( sDrumkitPath ) );
282 }
283
284 QString sBackupFile = Filesystem::drumkit_backup_path( sDrumkitFile );
285 Filesystem::file_copy( sDrumkitFile, sBackupFile,
286 false /* do not overwrite existing
287 files */,
288 bSilent );
289
290 pDrumkit->save( sDrumkitPath, -1, true, bSilent );
291 }
292}
293
295{
296 INFOLOG( QString( "Unloading drumkit %1 instrument samples" ).arg( __name ) );
297 if( __samples_loaded ) {
298 __instruments->unload_samples();
299 __samples_loaded = false;
300 }
301}
302
303QString Drumkit::getFolderName() const {
305}
306
307QString Drumkit::getExportName( const QString& sComponentName, bool bRecentVersion ) const {
308 QString sExportName = getFolderName();
309 if ( ! sComponentName.isEmpty() ) {
310 sExportName.append( "_" +
311 Filesystem::validateFilePath( sComponentName ) );
312 if ( ! bRecentVersion ) {
313 sExportName.append( "_legacy" );
314 }
315 }
316
317 return sExportName;
318}
319
320bool Drumkit::save( const QString& sDrumkitPath, int nComponentID, bool bRecentVersion, bool bSilent )
321{
322 QString sDrumkitFolder( sDrumkitPath );
323 if ( sDrumkitPath.isEmpty() ) {
324 sDrumkitFolder = __path;
325 }
326 else {
327 // We expect the path to a folder in sDrumkitPath. But in case
328 // the user or developer provided the path to the drumkit.xml
329 // file within this folder we don't play dumb as such things
330 // happen and are plausible when just looking at the
331 // function's signature
332 QFileInfo fi( sDrumkitPath );
333 if ( fi.isFile() && fi.fileName() == Filesystem::drumkit_xml() ) {
334 WARNINGLOG( QString( "Please provide the path to the drumkit folder instead to the drumkit.xml file within: [%1]" )
335 .arg( sDrumkitPath ) );
336 sDrumkitFolder = fi.dir().absolutePath();
337 }
338 }
339
340 if ( ! Filesystem::dir_exists( sDrumkitFolder, true ) &&
341 ! Filesystem::mkdir( sDrumkitFolder ) ) {
342 ERRORLOG( QString( "Unable to export drumkit [%1] to [%2]. Could not create drumkit folder." )
343 .arg( __name ).arg( sDrumkitFolder ) );
344 return false;
345 }
346
347 if ( Filesystem::dir_exists( sDrumkitFolder, bSilent ) &&
348 ! Filesystem::dir_writable( sDrumkitFolder, bSilent ) ) {
349 ERRORLOG( QString( "Unable to export drumkit [%1] to [%2]. Drumkit folder not writable." )
350 .arg( __name ).arg( sDrumkitFolder ) );
351 return false;
352 }
353
354 if ( ! bSilent ) {
355 INFOLOG( QString( "Saving drumkit [%1] into [%2]" )
356 .arg( __name ).arg( sDrumkitFolder ) );
357 }
358
359 // Save external files
360 if ( ! save_samples( sDrumkitFolder, bSilent ) ) {
361 ERRORLOG( QString( "Unable to save samples of drumkit [%1] to [%2]. Abort." )
362 .arg( __name ).arg( sDrumkitFolder ) );
363 return false;
364 }
365
366 if ( ! save_image( sDrumkitFolder, bSilent ) ) {
367 ERRORLOG( QString( "Unable to save image of drumkit [%1] to [%2]. Abort." )
368 .arg( __name ).arg( sDrumkitFolder ) );
369 return false;
370 }
371
372 // Ensure all instruments and associated samples will hold the
373 // same license as the overall drumkit and are associated to
374 // it. (Not important for saving itself but for consistency and
375 // using the drumkit later on).
377
378 // Save drumkit.xml
379 XMLDoc doc;
380 XMLNode root = doc.set_root( "drumkit_info", "drumkit" );
381
382 // In order to comply with the GPL license we have to add a
383 // license notice to the file.
384 if ( __license.getType() == License::GPL ) {
385 root.appendChild( doc.createComment( License::getGPLLicenseNotice( __author ) ) );
386 }
387
388 save_to( &root, nComponentID, bRecentVersion, bSilent );
389 return doc.write( Filesystem::drumkit_file( sDrumkitFolder ) );
390}
391
392void Drumkit::save_to( XMLNode* node, int component_id, bool bRecentVersion, bool bSilent ) const
393{
394 node->write_string( "name", __name );
395 node->write_string( "author", __author );
396 node->write_string( "info", __info );
397 node->write_string( "license", __license.getLicenseString() );
398 node->write_string( "image", __image );
399 node->write_string( "imageLicense", __imageLicense.getLicenseString() );
400
401 // Only drumkits used for Hydrogen v0.9.7 or higher are allowed to
402 // have components. If the user decides to export the kit to
403 // legacy version, the components will be omitted and Instrument
404 // layers corresponding to component_id will be exported.
405 if ( bRecentVersion ) {
406 XMLNode components_node = node->createNode( "componentList" );
407 if ( component_id == -1 && __components->size() > 0 ) {
408 for ( const auto& pComponent : *__components ){
409 pComponent->save_to( &components_node );
410 }
411 }
412 else {
413 bool bComponentFound = false;
414
415 if ( component_id != -1 ) {
416 for ( const auto& pComponent : *__components ){
417 if ( pComponent != nullptr &&
418 pComponent->get_id() == component_id ) {
419 bComponentFound = true;
420 pComponent->save_to( &components_node );
421 }
422 }
423 } else {
424 WARNINGLOG( "Drumkit has no components. Storing an empty one as fallback." );
425 }
426
427 if ( ! bComponentFound ) {
428 if ( component_id != -1 ) {
429 ERRORLOG( QString( "Unable to retrieve DrumkitComponent [%1]. Storing an empty one as fallback." )
430 .arg( component_id ) );
431 }
432 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( 0, "Main" );
433 pDrumkitComponent->save_to( &components_node );
434 }
435 }
436 } else {
437 // Legacy export
438 if ( component_id == -1 ) {
439 ERRORLOG( "Exporting the full drumkit with all components is allowed when targeting the legacy versions >= 0.9.6" );
440 return;
441 }
442 }
443
444 if ( __instruments != nullptr && __instruments->size() > 0 ) {
445 __instruments->save_to( node, component_id, bRecentVersion, false );
446 } else {
447 WARNINGLOG( "Drumkit has no instruments. Storing an InstrumentList with a single empty Instrument as fallback." );
448 auto pInstrumentList = std::make_shared<InstrumentList>();
449 auto pInstrument = std::make_shared<Instrument>();
450 pInstrumentList->insert( 0, pInstrument );
451 pInstrumentList->save_to( node, component_id, bRecentVersion );
452 }
453}
454
455bool Drumkit::save_samples( const QString& sDrumkitFolder, bool bSilent ) const
456{
457 if ( ! bSilent ) {
458 INFOLOG( QString( "Saving drumkit [%1] samples into [%2]" )
459 .arg( __name ).arg( sDrumkitFolder ) );
460 }
461
462 auto pInstrList = get_instruments();
463 for ( int i = 0; i < pInstrList->size(); i++ ) {
464 auto pInstrument = ( *pInstrList )[i];
465 for ( const auto& pComponent : *pInstrument->get_components() ) {
466
467 for ( int n = 0; n < InstrumentComponent::getMaxLayers(); n++ ) {
468 auto pLayer = pComponent->get_layer( n );
469 if ( pLayer != nullptr && pLayer->get_sample() != nullptr ) {
470 QString src = pLayer->get_sample()->get_filepath();
471 QString dst = sDrumkitFolder + "/" + pLayer->get_sample()->get_filename();
472
473 if ( src != dst ) {
474 QString original_dst = dst;
475
476 // If the destination path does not have an extension and there is a dot in the path, hell will break loose. QFileInfo maybe?
477 int insertPosition = original_dst.length();
478 if ( original_dst.lastIndexOf(".") > 0 ) {
479 insertPosition = original_dst.lastIndexOf(".");
480 }
481
482 pLayer->get_sample()->set_filename( dst );
483
484 if( ! Filesystem::file_copy( src, dst, bSilent ) ) {
485 return false;
486 }
487 }
488 }
489 }
490 }
491 }
492
493 return true;
494}
495
496bool Drumkit::save_image( const QString& sDrumkitDir, bool bSilent ) const
497{
498 if ( ! __image.isEmpty() && sDrumkitDir != __path ) {
499 QString src = __path + "/" + __image;
500 QString dst = sDrumkitDir + "/" + __image;
501 if ( Filesystem::file_exists( src, bSilent ) ) {
502 if ( ! Filesystem::file_copy( src, dst, bSilent ) ) {
503 ERRORLOG( QString( "Error copying %1 to %2").arg( src ).arg( dst ) );
504 return false;
505 }
506 }
507 }
508 return true;
509}
510
511void Drumkit::set_instruments( std::shared_ptr<InstrumentList> instruments )
512{
513 __instruments = instruments;
514}
515
516void Drumkit::set_components( std::shared_ptr<std::vector<std::shared_ptr<DrumkitComponent>>> components )
517{
518 __components = components;
519}
520
522
523 for ( const auto& ppInstrument : *__instruments ) {
524 if ( ppInstrument != nullptr ) {
525
526 ppInstrument->set_drumkit_path( __path );
527 ppInstrument->set_drumkit_name( __name );
528 for ( const auto& ppInstrumentComponent : *ppInstrument->get_components() ) {
529 if ( ppInstrumentComponent != nullptr ) {
530 for ( const auto& ppInstrumentLayer : *ppInstrumentComponent ) {
531 if ( ppInstrumentLayer != nullptr ) {
532 auto pSample = ppInstrumentLayer->get_sample();
533 if ( pSample != nullptr ) {
534 pSample->setLicense( get_license() );
535 }
536 }
537 }
538 }
539 }
540 }
541 }
542}
543
544std::vector<std::shared_ptr<InstrumentList::Content>> Drumkit::summarizeContent() const {
545 return __instruments->summarizeContent( __components );
546}
547
548bool Drumkit::remove( const QString& sDrumkitDir )
549{
550 if( ! Filesystem::drumkit_valid( sDrumkitDir ) ) {
551 ERRORLOG( QString( "%1 is not valid drumkit folder" ).arg( sDrumkitDir ) );
552 return false;
553 }
554
555 INFOLOG( QString( "Removing drumkit: %1" ).arg( sDrumkitDir ) );
556 if ( ! Filesystem::rm( sDrumkitDir, true ) ) {
557 ERRORLOG( QString( "Unable to remove drumkit: %1" ).arg( sDrumkitDir ) );
558 return false;
559 }
560
562 return true;
563}
564
565bool Drumkit::install( const QString& sSourcePath, const QString& sTargetPath, bool bSilent )
566{
567 if ( sTargetPath.isEmpty() ) {
568 if ( ! bSilent ) {
569 _INFOLOG( QString( "Install drumkit [%1]" ).arg( sSourcePath ) );
570 }
571
572 } else {
573 if ( ! Filesystem::path_usable( sTargetPath, true, false ) ) {
574 return false;
575 }
576
577 if ( ! bSilent ) {
578 _INFOLOG( QString( "Extract drumkit from [%1] to [%2]" )
579 .arg( sSourcePath ).arg( sTargetPath ) );
580 }
581 }
582
583#ifdef H2CORE_HAVE_LIBARCHIVE
584 int r;
585 struct archive* arch;
586 struct archive_entry* entry;
587
588 arch = archive_read_new();
589
590#if ARCHIVE_VERSION_NUMBER < 3000000
591 archive_read_support_compression_all( arch );
592#else
593 archive_read_support_filter_all( arch );
594#endif
595
596 archive_read_support_format_all( arch );
597
598#if ARCHIVE_VERSION_NUMBER < 3000000
599 if ( archive_read_open_file( arch, sSourcePath.toLocal8Bit(), 10240 ) ) {
600#else
601 if ( archive_read_open_filename( arch, sSourcePath.toLocal8Bit(), 10240 ) ) {
602#endif
603 _ERRORLOG( QString( "archive_read_open_file() [%1] %2" )
604 .arg( archive_errno( arch ) )
605 .arg( archive_error_string( arch ) ) );
606 archive_read_close( arch );
607
608#if ARCHIVE_VERSION_NUMBER < 3000000
609 archive_read_finish( arch );
610#else
611 archive_read_free( arch );
612#endif
613
614 return false;
615 }
616 bool ret = true;
617
618 QString dk_dir;
619 if ( ! sTargetPath.isEmpty() ) {
620 dk_dir = sTargetPath + "/";
621 } else {
622 dk_dir = Filesystem::usr_drumkits_dir() + "/";
623 }
624
625 while ( ( r = archive_read_next_header( arch, &entry ) ) != ARCHIVE_EOF ) {
626 if ( r != ARCHIVE_OK ) {
627 _ERRORLOG( QString( "archive_read_next_header() [%1] %2" )
628 .arg( archive_errno( arch ) )
629 .arg( archive_error_string( arch ) ) );
630 ret = false;
631 break;
632 }
633 QString np = dk_dir + archive_entry_pathname( entry );
634
635 QByteArray newpath = np.toLocal8Bit();
636
637 archive_entry_set_pathname( entry, newpath.data() );
638 r = archive_read_extract( arch, entry, 0 );
639 if ( r == ARCHIVE_WARN ) {
640 _WARNINGLOG( QString( "archive_read_extract() [%1] %2" )
641 .arg( archive_errno( arch ) )
642 .arg( archive_error_string( arch ) ) );
643 } else if ( r != ARCHIVE_OK ) {
644 _ERRORLOG( QString( "archive_read_extract() [%1] %2" )
645 .arg( archive_errno( arch ) )
646 .arg( archive_error_string( arch ) ) );
647 ret = false;
648 break;
649 }
650 }
651 archive_read_close( arch );
652
653#if ARCHIVE_VERSION_NUMBER < 3000000
654 archive_read_finish( arch );
655#else
656 archive_read_free( arch );
657#endif
658
659 return ret;
660#else // H2CORE_HAVE_LIBARCHIVE
661#ifndef WIN32
662 // GUNZIP
663
664 QString gzd_name = sSourcePath.left( sSourcePath.indexOf( "." ) ) + ".tar";
665 FILE* gzd_file = fopen( gzd_name.toLocal8Bit(), "wb" );
666 gzFile gzip_file = gzopen( sSourcePath.toLocal8Bit(), "rb" );
667 if ( !gzip_file ) {
668 _ERRORLOG( QString( "Error reading drumkit file: %1" )
669 .arg( sSourcePath ) );
670 gzclose( gzip_file );
671 fclose( gzd_file );
672 return false;
673 }
674 uchar buf[4096];
675 while ( gzread( gzip_file, buf, 4096 ) > 0 ) {
676 fwrite( buf, sizeof( uchar ), 4096, gzd_file );
677 }
678 gzclose( gzip_file );
679 fclose( gzd_file );
680 // UNTAR
681 TAR* tar_file;
682
683 QByteArray tar_path = gzd_name.toLocal8Bit();
684
685 if ( tar_open( &tar_file, tar_path.data(), NULL, O_RDONLY, 0, TAR_GNU ) == -1 ) {
686 _ERRORLOG( QString( "tar_open(): %1" ).arg( QString::fromLocal8Bit( strerror( errno ) ) ) );
687 return false;
688 }
689 bool ret = true;
690 char dst_dir[1024];
691
692 QString dk_dir;
693 if ( ! sTargetPath.isEmpty() ) {
694 dk_dir = sTargetPath + "/";
695 } else {
696 dk_dir = Filesystem::usr_drumkits_dir() + "/";
697 }
698
699 strncpy( dst_dir, dk_dir.toLocal8Bit(), 1024 );
700 if ( tar_extract_all( tar_file, dst_dir ) != 0 ) {
701 _ERRORLOG( QString( "tar_extract_all(): %1" )
702 .arg( QString::fromLocal8Bit( strerror( errno ) ) ) );
703 ret = false;
704 }
705 if ( tar_close( tar_file ) != 0 ) {
706 _ERRORLOG( QString( "tar_close(): %1" )
707 .arg( QString::fromLocal8Bit( strerror( errno ) ) ) );
708 ret = false;
709 }
710 return ret;
711#else // WIN32
712 _ERRORLOG( "WIN32 NOT IMPLEMENTED" );
713 return false;
714#endif
715#endif
716}
717
718bool Drumkit::exportTo( const QString& sTargetDir, const QString& sComponentName, bool bRecentVersion, bool bSilent ) {
719
720 if ( ! Filesystem::path_usable( sTargetDir, true, false ) ) {
721 ERRORLOG( QString( "Provided destination folder [%1] is not valid" )
722 .arg( sTargetDir ) );
723 return false;
724 }
725
726 if ( ! bRecentVersion && sComponentName.isEmpty() ) {
727 ERRORLOG( "A DrumkiComponent name is required to exported a drumkit in a format similar to the one prior to version 0.9.7" );
728 return false;
729 }
730
731 // When performing an export of a single component, the resulting
732 // file will be <DRUMKIT_NAME>_<COMPONENT_NAME>.h2drumkit. This
733 // itself is nice because the user can not choose the name of the
734 // resulting file and it would not be possible to store the export
735 // of multiple components in a single folder otherwise. But if all
736 // those different .h2drumkit would be extracted into the same
737 // folder there would be easily confusion or maybe even loss of
738 // data. We thus temporary rename the drumkit within this
739 // function.
740 // If a legacy export is asked for (!bRecentVersion) the suffix
741 // "_legacy" will be appended as well in order to provide unique
742 // filenames for all export options of a drumkit that can be
743 // selected in the GUI.
744 QString sOldDrumkitName = __name;
745 QString sDrumkitName = getExportName( sComponentName, bRecentVersion );
746
747 QString sTargetName = sTargetDir + "/" + sDrumkitName +
749
750 if ( ! bSilent ) {
751 QString sMsg( "Export ");
752
753 if ( sComponentName.isEmpty() && bRecentVersion ) {
754 sMsg.append( "drumkit " );
755 } else {
756 sMsg.append( QString( "component: [%1] " ).arg( sComponentName ) );
757 }
758
759 sMsg.append( QString( "to [%1] " ).arg( sTargetName ) );
760
761 if ( bRecentVersion ) {
762 sMsg.append( "using the most recent format" );
763 } else {
764 sMsg.append( "using the legacy format supported by Hydrogen versions <= 0.9.6" );
765 }
766
767 INFOLOG( sMsg );
768 }
769
770 // Unique temporary folder to save intermediate drumkit.xml and
771 // component files. The uniqueness is required in case several
772 // threads or instances of Hydrogen do export a drumkit at once.
773 QTemporaryDir tmpFolder( Filesystem::tmp_dir() + "/XXXXXX" );
774 if ( ! sComponentName.isEmpty() ) {
775 tmpFolder.setAutoRemove( false );
776 }
777
778 // In case we just export a single component, we store a pruned
779 // version of the drumkit with all other DrumkitComponents removed
780 // from the Instruments in a temporary folder and use this one as
781 // a basis for further compression.
782 int nComponentID = -1;
783 if ( ! sComponentName.isEmpty() ) {
784 for ( auto pComponent : *__components ) {
785 if( pComponent->get_name().compare( sComponentName ) == 0) {
786 nComponentID = pComponent->get_id();
787 set_name( sDrumkitName );
788 break;
789 }
790 }
791 if ( nComponentID == -1 ) {
792 ERRORLOG( QString( "Component [%1] could not be found in current Drumkit [%2]" )
793 .arg( sComponentName )
794 .arg( toQString( "", true ) ) );
795 set_name( sOldDrumkitName );
796 return false;
797 }
798 if ( ! save( tmpFolder.path(), nComponentID, bRecentVersion, bSilent ) ) {
799 ERRORLOG( QString( "Unable to save backup drumkit to [%1] using component ID [%2]" )
800 .arg( tmpFolder.path() ).arg( nComponentID ) );
801 }
802 }
803
804 if ( ! Filesystem::dir_readable( __path, true ) ) {
805 ERRORLOG( QString( "Unabled to access folder associated with drumkit [%1]" )
806 .arg( __path ) );
807 set_name( sOldDrumkitName );
808 return false;
809 }
810
811 QDir sourceDir( __path );
812
813 QStringList sourceFilesList = sourceDir.entryList( QDir::Files );
814 // In case just a single component is exported, we only add
815 // samples associated with it to the .h2drumkit file.
816 QStringList filesUsed;
817
818 // List of formats libsndfile is able to import (see
819 // https://libsndfile.github.io/libsndfile/api.html#open).
820 // This list is used to decide what will happen to a file on a
821 // single-component export in case the file is not associated with
822 // a sample of an instrument belonging to the exported
823 // component. If its suffix is contained in this list, the file is
824 // considered to be part of an instrument we like to drop. If not,
825 // it might be a metafile, like LICENSE, README, or the kit's
826 // image.
827 // The list does not have to be comprehensive as a "leakage" of
828 // audio files in the resulting .h2drumkit is not a big problem.
829 QStringList suffixBlacklist;
830 suffixBlacklist << "wav" << "flac" << "aifc" << "aif" << "aiff" << "au"
831 << "caf" << "w64" << "ogg" << "pcm" << "l16" << "vob"
832 << "mp1" << "mp2" << "mp3";
833
834 bool bSampleFound;
835
836 for ( const auto& ssFile : sourceFilesList ) {
837 if( ssFile.compare( Filesystem::drumkit_xml() ) == 0 &&
838 nComponentID != -1 ) {
839 filesUsed << Filesystem::drumkit_file( tmpFolder.path() );
840 } else {
841
842 bSampleFound = false;
843 for( const auto& pInstr : *( get_instruments() ) ){
844 if( pInstr != nullptr ) {
845 for ( auto const& pComponent : *( pInstr->get_components() ) ) {
846 if ( pComponent != nullptr &&
847 ( nComponentID == -1 ||
848 pComponent->get_drumkit_componentID() == nComponentID ) ) {
849 for( int n = 0; n < InstrumentComponent::getMaxLayers(); n++ ) {
850 const auto pLayer = pComponent->get_layer( n );
851 if( pLayer != nullptr && pLayer->get_sample() != nullptr ) {
852 if( pLayer->get_sample()->get_filename().compare( ssFile ) == 0 ) {
853 filesUsed << sourceDir.filePath( ssFile );
854 bSampleFound = true;
855 break;
856 }
857 }
858 }
859 }
860 }
861 }
862 }
863
864 // Should we drop the file?
865 if ( ! bSampleFound ) {
866 QFileInfo ffileInfo( sourceDir.filePath( ssFile ) );
867 if ( ! suffixBlacklist.contains( ffileInfo.suffix(),
868 Qt::CaseInsensitive ) ) {
869
870 // We do not want to export any old backups
871 // created during the upgrade process of the
872 // drumkits.
873 if ( ! ( ssFile.contains( Filesystem::drumkit_xml() ) &&
874 ssFile.contains( ".bak" ) ) ) {
875 filesUsed << sourceDir.filePath( ssFile );
876 }
877 }
878 }
879 }
880 }
881
882#if defined(H2CORE_HAVE_LIBARCHIVE)
883
884 struct archive *a;
885 struct archive_entry *entry;
886 struct stat st;
887 char buff[8192];
888 int len;
889 FILE *f;
890
891 a = archive_write_new();
892
893 #if ARCHIVE_VERSION_NUMBER < 3000000
894 archive_write_set_compression_gzip(a);
895 #else
896 archive_write_add_filter_gzip(a);
897 #endif
898
899 archive_write_set_format_pax_restricted(a);
900
901 int ret = archive_write_open_filename(a, sTargetName.toUtf8().constData());
902 if ( ret != ARCHIVE_OK ) {
903 ERRORLOG( QString("Couldn't create archive [%0]" )
904 .arg( sTargetName ) );
905 set_name( sOldDrumkitName );
906 return false;
907 }
908
909 bool bFoundFileInRightComponent;
910 for ( const auto& sFilename : filesUsed ) {
911 QFileInfo ffileInfo( sFilename );
912 QString sTargetFilename = sDrumkitName + "/" + ffileInfo.fileName();
913
914 // Small sanity check since the libarchive code won't fail
915 // gracefully but segfaults if the provided file does not
916 // exist.
917 if ( ! Filesystem::file_readable( sFilename, true ) ) {
918 ERRORLOG( QString( "Unable to export drumkit. File [%1] does not exists or is not readable." )
919 .arg( sFilename ) );
920 set_name( sOldDrumkitName );
921 return false;
922 }
923
924 stat( sFilename.toUtf8().constData(), &st );
925 entry = archive_entry_new();
926 archive_entry_set_pathname(entry, sTargetFilename.toUtf8().constData());
927 archive_entry_set_size(entry, st.st_size);
928 archive_entry_set_filetype(entry, AE_IFREG);
929 archive_entry_set_perm(entry, 0644);
930 archive_write_header(a, entry);
931 f = fopen( sFilename.toUtf8().constData(), "rb" );
932 len = fread(buff, sizeof(char), sizeof(buff), f);
933 while ( len > 0 ) {
934 archive_write_data(a, buff, len);
935 len = fread(buff, sizeof(char), sizeof(buff), f);
936 }
937 fclose(f);
938 archive_entry_free(entry);
939 }
940 archive_write_close(a);
941
942 #if ARCHIVE_VERSION_NUMBER < 3000000
943 archive_write_finish(a);
944 #else
945 archive_write_free(a);
946 #endif
947
948 sourceFilesList.clear();
949
950 // Only clean up the temp folder when everything was
951 // working. Else, it's probably worth inspecting its content (and
952 // the system will clean it up anyway).
953 Filesystem::rm( tmpFolder.path(), true, true );
954
955 set_name( sOldDrumkitName );
956
957 return true;
958#else // No LIBARCHIVE
959
960#ifndef WIN32
961 if ( nComponentID != -1 ) {
962 // In order to add components name to the folder name we have
963 // to copy _all_ files to a temporary folder holding the same
964 // name. This is unarguably a quite expensive operation. But
965 // exporting is only down sparsely and almost all versions of
966 // Hydrogen should come with libarchive support anyway. On the
967 // other hand, being consistent and prevent confusion and loss
968 // of data beats sparsely excessive copying.
969 QString sDirName = getFolderName();
970
971 QDir sTmpSourceDir( tmpFolder.path() + "/" + sDirName );
972 if ( sTmpSourceDir.exists() ) {
973 sTmpSourceDir.removeRecursively();
974 }
975 if ( ! Filesystem::path_usable( tmpFolder.path() + "/" + sDirName,
976 true, true ) ) {
977 ERRORLOG( QString( "Unable to create tmp folder [%1]" )
978 .arg( tmpFolder.path() + "/" + sDirName ) );
979 set_name( sOldDrumkitName );
980 return false;
981 }
982
983 QString sNewFilePath;
984 QStringList copiedFiles;
985 for ( const auto& ssFile : filesUsed ) {
986 QString sNewFilePath( ssFile );
987 sNewFilePath.replace( sNewFilePath.left( sNewFilePath.lastIndexOf( "/" ) ),
988 tmpFolder.path() + "/" + sDirName );
989 if ( ! Filesystem::file_copy( ssFile, sNewFilePath, true, true ) ) {
990 ERRORLOG( QString( "Unable to copy file [%1] to [%2]." )
991 .arg( ssFile ).arg( sNewFilePath ) );
992 set_name( sOldDrumkitName );
993 return false;
994 }
995
996 copiedFiles << sNewFilePath;
997 }
998
999 filesUsed = copiedFiles;
1000 sourceDir = QDir( tmpFolder.path() + "/" + sDirName );
1001 }
1002
1003 // Since there is no way to alter the target names of the files
1004 // provided to command line `tar` and we want the output to be
1005 // identically to the only created used libarchive, we need to do
1006 // some string replacement in here. If not, the unpack to
1007 // ./home/USER_NAME_RUNNING_THE_EXPORT/.hydrogen/data/drumkits/DRUMKIT_NAME/
1008 // but we instead want it to unpack to ./DRUMKIT_NAME/
1009 filesUsed = filesUsed.replaceInStrings( sourceDir.absolutePath(),
1010 sourceDir.dirName() );
1011
1012 QString sCmd = QString( "tar czf %1 -C %2 -- \"%3\"" )
1013 .arg( sTargetName )
1014 .arg( sourceDir.absolutePath().left( sourceDir.absolutePath().lastIndexOf( "/" ) ) )
1015 .arg( filesUsed.join( "\" \"" ) );
1016 int nRet = std::system( sCmd.toLocal8Bit() );
1017
1018 if ( nRet != 0 ) {
1019 ERRORLOG( QString( "Unable to export drumkit using system command:\n%1" )
1020 .arg( sCmd ) );
1021 set_name( sOldDrumkitName );
1022 return false;
1023 }
1024
1025 // Only clean up the temp folder when everything was
1026 // working. Else, it's probably worth inspecting its content (and
1027 // the system will clean it up anyway).
1028 Filesystem::rm( tmpFolder.path(), true, true );
1029
1030 set_name( sOldDrumkitName );
1031
1032 return true;
1033#else // WIN32
1034 ERRORLOG( "Operation not supported on Windows" );
1035
1036 return false;
1037#endif
1038#endif // LIBARCHIVE
1039
1040}
1041
1042QString Drumkit::toQString( const QString& sPrefix, bool bShort ) const {
1043 QString s = Base::sPrintIndention;
1044 QString sOutput;
1045 if ( ! bShort ) {
1046 sOutput = QString( "%1[Drumkit]\n" ).arg( sPrefix )
1047 .append( QString( "%1%2path: %3\n" ).arg( sPrefix ).arg( s ).arg( __path ) )
1048 .append( QString( "%1%2name: %3\n" ).arg( sPrefix ).arg( s ).arg( __name ) )
1049 .append( QString( "%1%2author: %3\n" ).arg( sPrefix ).arg( s ).arg( __author ) )
1050 .append( QString( "%1%2info: %3\n" ).arg( sPrefix ).arg( s ).arg( __info ) )
1051 .append( QString( "%1%2license: %3\n" ).arg( sPrefix ).arg( s ).arg( __license.toQString() ) )
1052 .append( QString( "%1%2image: %3\n" ).arg( sPrefix ).arg( s ).arg( __image ) )
1053 .append( QString( "%1%2imageLicense: %3\n" ).arg( sPrefix ).arg( s ).arg( __imageLicense.toQString() ) )
1054 .append( QString( "%1%2samples_loaded: %3\n" ).arg( sPrefix ).arg( s ).arg( __samples_loaded ) )
1055 .append( QString( "%1" ).arg( __instruments->toQString( sPrefix + s, bShort ) ) )
1056 .append( QString( "%1%2components:\n" ).arg( sPrefix ).arg( s ) );
1057 for ( auto cc : *__components ) {
1058 if ( cc != nullptr ) {
1059 sOutput.append( QString( "%1" ).arg( cc->toQString( sPrefix + s + s, bShort ) ) );
1060 }
1061 }
1062 } else {
1063
1064 sOutput = QString( "[Drumkit]" )
1065 .append( QString( " path: %1" ).arg( __path ) )
1066 .append( QString( ", name: %1" ).arg( __name ) )
1067 .append( QString( ", author: %1" ).arg( __author ) )
1068 .append( QString( ", info: %1" ).arg( __info ) )
1069 .append( QString( ", license: %1" ).arg( __license.toQString() ) )
1070 .append( QString( ", image: %1" ).arg( __image ) )
1071 .append( QString( ", imageLicense: %1" ).arg( __imageLicense.toQString() ) )
1072 .append( QString( ", samples_loaded: %1" ).arg( __samples_loaded ) )
1073 .append( QString( ", [%1]" ).arg( __instruments->toQString( sPrefix + s, bShort ) ) )
1074 .append( QString( ", components: [ " ) );
1075 for ( auto cc : *__components ) {
1076 if ( cc != nullptr ) {
1077 sOutput.append( QString( "[%1]" ).arg( cc->toQString( sPrefix + s + s, bShort ).replace( "\n", " " ) ) );
1078 }
1079 }
1080 sOutput.append( "]\n" );
1081 }
1082
1083 return sOutput;
1084}
1085
1086};
1087
1088/* vim: set softtabstop=4 noexpandtab: */
#define _WARNINGLOG(x)
Definition Object.h:244
#define INFOLOG(x)
Definition Object.h:237
#define _INFOLOG(x)
Definition Object.h:243
#define WARNINGLOG(x)
Definition Object.h:238
#define ERRORLOG(x)
Definition Object.h:239
#define _ERRORLOG(x)
Definition Object.h:245
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
static std::shared_ptr< DrumkitComponent > load_from(XMLNode *node)
QString __name
drumkit name
Definition Drumkit.h:245
static bool loadDoc(const QString &sDrumkitDir, XMLDoc *pDoc, bool bSilent=false)
Loads the drumkit stored in sDrumkitDir into pDoc and takes care of all the error handling.
Definition Drumkit.cpp:235
std::shared_ptr< InstrumentList > get_instruments() const
returns __instruments
Definition Drumkit.h:304
void set_name(const QString &name)
__name setter
Definition Drumkit.h:319
const License & get_license() const
__license accessor
Definition Drumkit.h:356
void save_to(XMLNode *node, int component_id=-1, bool bRecentVersion=true, bool bSilent=false) const
Definition Drumkit.cpp:392
static std::shared_ptr< Drumkit > load_from(XMLNode *node, const QString &dk_path, bool bSilent=false)
load a drumkit from an XMLNode
Definition Drumkit.cpp:134
bool save_image(const QString &dk_dir, bool bSilent=false) const
save the drumkit image into the new directory
Definition Drumkit.cpp:496
std::shared_ptr< std::vector< std::shared_ptr< DrumkitComponent > > > __components
list of drumkit component
Definition Drumkit.h:254
void propagateLicense()
Assign the license stored in #m_license to all samples contained in the kit.
Definition Drumkit.cpp:521
License __imageLicense
drumkit image license
Definition Drumkit.h:250
~Drumkit()
drumkit destructor, delete __instruments
Definition Drumkit.cpp:86
static void upgrade_drumkit(std::shared_ptr< Drumkit > pDrumkit, const QString &dk_path, bool bSilent=false)
Saves the current drumkit to dk_path, but makes a backup.
Definition Drumkit.cpp:267
std::shared_ptr< InstrumentList > __instruments
the list of instruments
Definition Drumkit.h:253
QString __info
drumkit free text
Definition Drumkit.h:247
bool save_samples(const QString &dk_dir, bool bSilent=false) const
save a drumkit instruments samples into a directory
Definition Drumkit.cpp:455
void load_samples()
Calls the InstrumentList::load_samples() member function of __instruments.
Definition Drumkit.cpp:204
QString __author
drumkit author
Definition Drumkit.h:246
static bool remove(const QString &sDrumkitDir)
remove a drumkit from the disk
Definition Drumkit.cpp:548
std::vector< std::shared_ptr< InstrumentList::Content > > summarizeContent() const
Returns vector of lists containing instrument name, component name, file name, the license of all ass...
Definition Drumkit.cpp:544
QString __image
drumkit image filename
Definition Drumkit.h:249
static std::shared_ptr< Drumkit > load(const QString &dk_dir, bool bUpgrade=true, bool bSilent=false)
Load drumkit information from a directory.
Definition Drumkit.cpp:90
static License loadLicenseFrom(const QString &sDrumkitDir, bool bSilent=false)
Loads the license information of a drumkit contained in directory sDrumkitDir.
Definition Drumkit.cpp:213
QString getExportName(const QString &sComponentName, bool bRecentVersion) const
Returns the base name used when exporting the drumkit.
Definition Drumkit.cpp:307
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Drumkit.cpp:1042
QString __path
absolute drumkit path
Definition Drumkit.h:244
bool exportTo(const QString &sTargetDir, const QString &sComponentName="", bool bRecentVersion=true, bool bSilent=false)
Compresses the drumkit into a .h2drumkit file.
Definition Drumkit.cpp:718
void unload_samples()
Calls the InstrumentList::unload_samples() member function of __instruments.
Definition Drumkit.cpp:294
static bool install(const QString &sSourcePath, const QString &sTargetPath="", bool bSilent=false)
Extract a .h2drumkit file.
Definition Drumkit.cpp:565
Drumkit()
drumkit constructor, does nothing
Definition Drumkit.cpp:53
bool __samples_loaded
true if the instrument samples are loaded
Definition Drumkit.h:252
bool save(const QString &sDrumkitPath="", int nComponentID=-1, bool bRecentVersion=true, bool bSilent=false)
Save a drumkit to disk.
Definition Drumkit.cpp:320
QString getFolderName() const
Returns a version of __name stripped of all whitespaces and other characters which would prevent its ...
Definition Drumkit.cpp:303
void set_components(std::shared_ptr< std::vector< std::shared_ptr< DrumkitComponent > > > components)
Definition Drumkit.cpp:516
void set_instruments(std::shared_ptr< InstrumentList > instruments)
set __instruments, delete existing one
Definition Drumkit.cpp:511
License __license
drumkit license description
Definition Drumkit.h:248
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 validateFilePath(const QString &sPath)
Takes an arbitrary path, replaces white spaces by underscores and removes all characters apart from l...
static bool dir_readable(const QString &path, bool silent=false)
returns true if the given path is a readable regular directory
static bool drumkit_valid(const QString &dk_path)
returns true if the path contains a usable drumkit
static QString usr_drumkits_dir()
returns user 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 QString drumkit_xsd_path()
returns the path to the drumkit XSD (xml schema definition) file
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 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 QString tmp_dir()
returns temp path
static const QString drumkit_ext
Definition Filesystem.h:91
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 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 Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
SoundLibraryDatabase * getSoundLibraryDatabase() const
Definition Hydrogen.h:94
static std::shared_ptr< InstrumentList > load_from(XMLNode *node, const QString &sDrumkitPath, const QString &sDrumkitName, const License &license=License(), bool bSilent=false)
load an instrument list from an XMLNode
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
Definition License.h:48
@ GPL
Not a desirable license for audio data but introduced here specifically since it is already used by a...
Definition License.h:68
LicenseType getType() const
Definition License.h:143
QString getLicenseString() const
Definition License.h:146
static QString getGPLLicenseNotice(const QString &sAuthor)
Definition License.h:194
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition License.cpp:151
void updateDrumkits(bool bTriggerEvent=true)
XMLDoc is a subclass of QDomDocument with read and write methods.
Definition Xml.h:182
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
Definition Xml.cpp:391
bool read(const QString &filepath, const QString &schemapath=nullptr, bool bSilent=false)
read the content of an xml file
Definition Xml.cpp:296
bool write(const QString &filepath)
write itself into a file
Definition Xml.cpp:370
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
QString read_string(const QString &node, const QString &default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a string stored into a child node
Definition Xml.cpp:95
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:63
void write_string(const QString &node, const QString &value)
write a string into a child node
Definition Xml.cpp:269