2 |
* * |
* * |
3 |
* libgig - C++ cross-platform Gigasampler format file access library * |
* libgig - C++ cross-platform Gigasampler format file access library * |
4 |
* * |
* * |
5 |
* Copyright (C) 2003-2007 by Christian Schoenebeck * |
* Copyright (C) 2003-2013 by Christian Schoenebeck * |
6 |
* <cuse@users.sourceforge.net> * |
* <cuse@users.sourceforge.net> * |
7 |
* * |
* * |
8 |
* This library is free software; you can redistribute it and/or modify * |
* This library is free software; you can redistribute it and/or modify * |
23 |
|
|
24 |
#include "DLS.h" |
#include "DLS.h" |
25 |
|
|
26 |
|
#include <algorithm> |
27 |
#include <time.h> |
#include <time.h> |
28 |
|
|
29 |
#ifdef __APPLE__ |
#ifdef __APPLE__ |
227 |
} |
} |
228 |
} |
} |
229 |
} |
} |
230 |
|
|
231 |
|
/** |
232 |
|
* Not yet implemented in this version, since the .gig format does |
233 |
|
* not need to copy DLS articulators and so far nobody used pure |
234 |
|
* DLS instrument AFAIK. |
235 |
|
*/ |
236 |
|
void Articulator::CopyAssign(const Articulator* orig) { |
237 |
|
//TODO: implement deep copy assignment for this class |
238 |
|
} |
239 |
|
|
240 |
|
|
241 |
|
|
249 |
* @param list - pointer to a list chunk which contains an INFO list chunk |
* @param list - pointer to a list chunk which contains an INFO list chunk |
250 |
*/ |
*/ |
251 |
Info::Info(RIFF::List* list) { |
Info::Info(RIFF::List* list) { |
252 |
FixedStringLengths = NULL; |
pFixedStringLengths = NULL; |
253 |
pResourceListChunk = list; |
pResourceListChunk = list; |
254 |
if (list) { |
if (list) { |
255 |
RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO); |
RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO); |
278 |
Info::~Info() { |
Info::~Info() { |
279 |
} |
} |
280 |
|
|
281 |
|
/** |
282 |
|
* Forces specific Info fields to be of a fixed length when being saved |
283 |
|
* to a file. By default the respective RIFF chunk of an Info field |
284 |
|
* will have a size analogue to its actual string length. With this |
285 |
|
* method however this behavior can be overridden, allowing to force an |
286 |
|
* arbitrary fixed size individually for each Info field. |
287 |
|
* |
288 |
|
* This method is used as a workaround for the gig format, not for DLS. |
289 |
|
* |
290 |
|
* @param lengths - NULL terminated array of string_length_t elements |
291 |
|
*/ |
292 |
|
void Info::SetFixedStringLengths(const string_length_t* lengths) { |
293 |
|
pFixedStringLengths = lengths; |
294 |
|
} |
295 |
|
|
296 |
/** @brief Load given INFO field. |
/** @brief Load given INFO field. |
297 |
* |
* |
298 |
* Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO |
* Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO |
320 |
*/ |
*/ |
321 |
void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) { |
void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) { |
322 |
int size = 0; |
int size = 0; |
323 |
if (FixedStringLengths) { |
if (pFixedStringLengths) { |
324 |
for (int i = 0 ; FixedStringLengths[i].length ; i++) { |
for (int i = 0 ; pFixedStringLengths[i].length ; i++) { |
325 |
if (FixedStringLengths[i].chunkId == ChunkID) { |
if (pFixedStringLengths[i].chunkId == ChunkID) { |
326 |
size = FixedStringLengths[i].length; |
size = pFixedStringLengths[i].length; |
327 |
break; |
break; |
328 |
} |
} |
329 |
} |
} |
392 |
SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String("")); |
SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String("")); |
393 |
SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String("")); |
SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String("")); |
394 |
} |
} |
395 |
|
|
396 |
|
/** |
397 |
|
* Make a deep copy of the Info object given by @a orig and assign it to |
398 |
|
* this object. |
399 |
|
* |
400 |
|
* @param orig - original Info object to be copied from |
401 |
|
*/ |
402 |
|
void Info::CopyAssign(const Info* orig) { |
403 |
|
Name = orig->Name; |
404 |
|
ArchivalLocation = orig->ArchivalLocation; |
405 |
|
CreationDate = orig->CreationDate; |
406 |
|
Comments = orig->Comments; |
407 |
|
Product = orig->Product; |
408 |
|
Copyright = orig->Copyright; |
409 |
|
Artists = orig->Artists; |
410 |
|
Genre = orig->Genre; |
411 |
|
Keywords = orig->Keywords; |
412 |
|
Engineer = orig->Engineer; |
413 |
|
Technician = orig->Technician; |
414 |
|
Software = orig->Software; |
415 |
|
Medium = orig->Medium; |
416 |
|
Source = orig->Source; |
417 |
|
SourceForm = orig->SourceForm; |
418 |
|
Commissioned = orig->Commissioned; |
419 |
|
Subject = orig->Subject; |
420 |
|
//FIXME: hmm, is copying this pointer a good idea? |
421 |
|
pFixedStringLengths = orig->pFixedStringLengths; |
422 |
|
} |
423 |
|
|
424 |
|
|
425 |
|
|
494 |
UUID uuid; |
UUID uuid; |
495 |
UuidCreate(&uuid); |
UuidCreate(&uuid); |
496 |
pDLSID->ulData1 = uuid.Data1; |
pDLSID->ulData1 = uuid.Data1; |
497 |
pDLSID->usData1 = uuid.Data2; |
pDLSID->usData2 = uuid.Data2; |
498 |
pDLSID->usData2 = uuid.Data3; |
pDLSID->usData3 = uuid.Data3; |
499 |
memcpy(pDLSID->abData, uuid.Data4, 8); |
memcpy(pDLSID->abData, uuid.Data4, 8); |
500 |
|
|
501 |
#elif defined(__APPLE__) |
#elif defined(__APPLE__) |
524 |
#endif |
#endif |
525 |
#endif |
#endif |
526 |
} |
} |
527 |
|
|
528 |
|
/** |
529 |
|
* Make a deep copy of the Resource object given by @a orig and assign it |
530 |
|
* to this object. |
531 |
|
* |
532 |
|
* @param orig - original Resource object to be copied from |
533 |
|
*/ |
534 |
|
void Resource::CopyAssign(const Resource* orig) { |
535 |
|
pInfo->CopyAssign(orig->pInfo); |
536 |
|
} |
537 |
|
|
538 |
|
|
539 |
// *************** Sampler *************** |
// *************** Sampler *************** |
550 |
SamplerOptions = wsmp->ReadUint32(); |
SamplerOptions = wsmp->ReadUint32(); |
551 |
SampleLoops = wsmp->ReadUint32(); |
SampleLoops = wsmp->ReadUint32(); |
552 |
} else { // 'wsmp' chunk missing |
} else { // 'wsmp' chunk missing |
553 |
uiHeaderSize = 0; |
uiHeaderSize = 20; |
554 |
UnityNote = 64; |
UnityNote = 60; |
555 |
FineTune = 0; // +- 0 cents |
FineTune = 0; // +- 0 cents |
556 |
Gain = 0; // 0 dB |
Gain = 0; // 0 dB |
557 |
SamplerOptions = F_WSMP_NO_COMPRESSION; |
SamplerOptions = F_WSMP_NO_COMPRESSION; |
575 |
if (pSampleLoops) delete[] pSampleLoops; |
if (pSampleLoops) delete[] pSampleLoops; |
576 |
} |
} |
577 |
|
|
578 |
|
void Sampler::SetGain(int32_t gain) { |
579 |
|
Gain = gain; |
580 |
|
} |
581 |
|
|
582 |
/** |
/** |
583 |
* Apply all sample player options to the respective RIFF chunk. You |
* Apply all sample player options to the respective RIFF chunk. You |
584 |
* have to call File::Save() to make changes persistent. |
* have to call File::Save() to make changes persistent. |
586 |
void Sampler::UpdateChunks() { |
void Sampler::UpdateChunks() { |
587 |
// make sure 'wsmp' chunk exists |
// make sure 'wsmp' chunk exists |
588 |
RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP); |
RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP); |
589 |
|
int wsmpSize = uiHeaderSize + SampleLoops * 16; |
590 |
if (!wsmp) { |
if (!wsmp) { |
591 |
uiHeaderSize = 20; |
wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, wsmpSize); |
592 |
wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, uiHeaderSize + SampleLoops * 16); |
} else if (wsmp->GetSize() != wsmpSize) { |
593 |
|
wsmp->Resize(wsmpSize); |
594 |
} |
} |
595 |
uint8_t* pData = (uint8_t*) wsmp->LoadChunkData(); |
uint8_t* pData = (uint8_t*) wsmp->LoadChunkData(); |
596 |
// update headers size |
// update headers size |
647 |
// copy old loops array (skipping given loop) |
// copy old loops array (skipping given loop) |
648 |
for (int i = 0, o = 0; i < SampleLoops; i++) { |
for (int i = 0, o = 0; i < SampleLoops; i++) { |
649 |
if (&pSampleLoops[i] == pLoopDef) continue; |
if (&pSampleLoops[i] == pLoopDef) continue; |
650 |
if (o == SampleLoops - 1) |
if (o == SampleLoops - 1) { |
651 |
|
delete[] pNewLoops; |
652 |
throw Exception("Could not delete Sample Loop, because it does not exist"); |
throw Exception("Could not delete Sample Loop, because it does not exist"); |
653 |
|
} |
654 |
pNewLoops[o] = pSampleLoops[i]; |
pNewLoops[o] = pSampleLoops[i]; |
655 |
o++; |
o++; |
656 |
} |
} |
659 |
pSampleLoops = pNewLoops; |
pSampleLoops = pNewLoops; |
660 |
SampleLoops--; |
SampleLoops--; |
661 |
} |
} |
662 |
|
|
663 |
|
/** |
664 |
|
* Make a deep copy of the Sampler object given by @a orig and assign it |
665 |
|
* to this object. |
666 |
|
* |
667 |
|
* @param orig - original Sampler object to be copied from |
668 |
|
*/ |
669 |
|
void Sampler::CopyAssign(const Sampler* orig) { |
670 |
|
// copy trivial scalars |
671 |
|
UnityNote = orig->UnityNote; |
672 |
|
FineTune = orig->FineTune; |
673 |
|
Gain = orig->Gain; |
674 |
|
NoSampleDepthTruncation = orig->NoSampleDepthTruncation; |
675 |
|
NoSampleCompression = orig->NoSampleCompression; |
676 |
|
SamplerOptions = orig->SamplerOptions; |
677 |
|
|
678 |
|
// copy sample loops |
679 |
|
if (SampleLoops) delete[] pSampleLoops; |
680 |
|
pSampleLoops = new sample_loop_t[orig->SampleLoops]; |
681 |
|
memcpy(pSampleLoops, orig->pSampleLoops, orig->SampleLoops * sizeof(sample_loop_t)); |
682 |
|
SampleLoops = orig->SampleLoops; |
683 |
|
} |
684 |
|
|
685 |
|
|
686 |
// *************** Sample *************** |
// *************** Sample *************** |
1005 |
} |
} |
1006 |
|
|
1007 |
/** |
/** |
1008 |
|
* Modifies the key range of this Region and makes sure the respective |
1009 |
|
* chunks are in correct order. |
1010 |
|
* |
1011 |
|
* @param Low - lower end of key range |
1012 |
|
* @param High - upper end of key range |
1013 |
|
*/ |
1014 |
|
void Region::SetKeyRange(uint16_t Low, uint16_t High) { |
1015 |
|
KeyRange.low = Low; |
1016 |
|
KeyRange.high = High; |
1017 |
|
|
1018 |
|
// make sure regions are already loaded |
1019 |
|
Instrument* pInstrument = (Instrument*) GetParent(); |
1020 |
|
if (!pInstrument->pRegions) pInstrument->LoadRegions(); |
1021 |
|
if (!pInstrument->pRegions) return; |
1022 |
|
|
1023 |
|
// find the r which is the first one to the right of this region |
1024 |
|
// at its new position |
1025 |
|
Region* r = NULL; |
1026 |
|
Region* prev_region = NULL; |
1027 |
|
for ( |
1028 |
|
Instrument::RegionList::iterator iter = pInstrument->pRegions->begin(); |
1029 |
|
iter != pInstrument->pRegions->end(); iter++ |
1030 |
|
) { |
1031 |
|
if ((*iter)->KeyRange.low > this->KeyRange.low) { |
1032 |
|
r = *iter; |
1033 |
|
break; |
1034 |
|
} |
1035 |
|
prev_region = *iter; |
1036 |
|
} |
1037 |
|
|
1038 |
|
// place this region before r if it's not already there |
1039 |
|
if (prev_region != this) pInstrument->MoveRegion(this, r); |
1040 |
|
} |
1041 |
|
|
1042 |
|
/** |
1043 |
* Apply Region settings to the respective RIFF chunks. You have to |
* Apply Region settings to the respective RIFF chunks. You have to |
1044 |
* call File::Save() to make changes persistent. |
* call File::Save() to make changes persistent. |
1045 |
* |
* |
1090 |
} |
} |
1091 |
} |
} |
1092 |
} |
} |
|
if (index < 0) throw Exception("Could not save Region, could not find Region's sample"); |
|
1093 |
WavePoolTableIndex = index; |
WavePoolTableIndex = index; |
1094 |
// update 'wlnk' chunk |
// update 'wlnk' chunk |
1095 |
store16(&pData[0], WaveLinkOptionFlags); |
store16(&pData[0], WaveLinkOptionFlags); |
1097 |
store32(&pData[4], Channel); |
store32(&pData[4], Channel); |
1098 |
store32(&pData[8], WavePoolTableIndex); |
store32(&pData[8], WavePoolTableIndex); |
1099 |
} |
} |
1100 |
|
|
1101 |
|
/** |
1102 |
|
* Make a (semi) deep copy of the Region object given by @a orig and assign |
1103 |
|
* it to this object. |
1104 |
|
* |
1105 |
|
* Note that the sample pointer referenced by @a orig is simply copied as |
1106 |
|
* memory address. Thus the respective sample is shared, not duplicated! |
1107 |
|
* |
1108 |
|
* @param orig - original Region object to be copied from |
1109 |
|
*/ |
1110 |
|
void Region::CopyAssign(const Region* orig) { |
1111 |
|
// handle base classes |
1112 |
|
Resource::CopyAssign(orig); |
1113 |
|
Articulator::CopyAssign(orig); |
1114 |
|
Sampler::CopyAssign(orig); |
1115 |
|
// handle actual own attributes of this class |
1116 |
|
// (the trivial ones) |
1117 |
|
VelocityRange = orig->VelocityRange; |
1118 |
|
KeyGroup = orig->KeyGroup; |
1119 |
|
Layer = orig->Layer; |
1120 |
|
SelfNonExclusive = orig->SelfNonExclusive; |
1121 |
|
PhaseMaster = orig->PhaseMaster; |
1122 |
|
PhaseGroup = orig->PhaseGroup; |
1123 |
|
MultiChannel = orig->MultiChannel; |
1124 |
|
Channel = orig->Channel; |
1125 |
|
WavePoolTableIndex = orig->WavePoolTableIndex; |
1126 |
|
pSample = orig->pSample; |
1127 |
|
FormatOptionFlags = orig->FormatOptionFlags; |
1128 |
|
WaveLinkOptionFlags = orig->WaveLinkOptionFlags; |
1129 |
|
// handle the last, a bit sensible attribute |
1130 |
|
SetKeyRange(orig->KeyRange.low, orig->KeyRange.high); |
1131 |
|
} |
1132 |
|
|
1133 |
|
|
1134 |
// *************** Instrument *************** |
// *************** Instrument *************** |
1279 |
RIFF::List* pParent = pCkInstrument->GetParent(); |
RIFF::List* pParent = pCkInstrument->GetParent(); |
1280 |
pParent->DeleteSubChunk(pCkInstrument); |
pParent->DeleteSubChunk(pCkInstrument); |
1281 |
} |
} |
1282 |
|
|
1283 |
|
void Instrument::CopyAssignCore(const Instrument* orig) { |
1284 |
|
// handle base classes |
1285 |
|
Resource::CopyAssign(orig); |
1286 |
|
Articulator::CopyAssign(orig); |
1287 |
|
// handle actual own attributes of this class |
1288 |
|
// (the trivial ones) |
1289 |
|
IsDrum = orig->IsDrum; |
1290 |
|
MIDIBank = orig->MIDIBank; |
1291 |
|
MIDIBankCoarse = orig->MIDIBankCoarse; |
1292 |
|
MIDIBankFine = orig->MIDIBankFine; |
1293 |
|
MIDIProgram = orig->MIDIProgram; |
1294 |
|
} |
1295 |
|
|
1296 |
|
/** |
1297 |
|
* Make a (semi) deep copy of the Instrument object given by @a orig and assign |
1298 |
|
* it to this object. |
1299 |
|
* |
1300 |
|
* Note that all sample pointers referenced by @a orig are simply copied as |
1301 |
|
* memory address. Thus the respective samples are shared, not duplicated! |
1302 |
|
* |
1303 |
|
* @param orig - original Instrument object to be copied from |
1304 |
|
*/ |
1305 |
|
void Instrument::CopyAssign(const Instrument* orig) { |
1306 |
|
CopyAssignCore(orig); |
1307 |
|
// delete all regions first |
1308 |
|
while (Regions) DeleteRegion(GetFirstRegion()); |
1309 |
|
// now recreate and copy regions |
1310 |
|
{ |
1311 |
|
RegionList::const_iterator it = orig->pRegions->begin(); |
1312 |
|
for (int i = 0; i < orig->Regions; ++i, ++it) { |
1313 |
|
Region* dstRgn = AddRegion(); |
1314 |
|
//NOTE: Region does semi-deep copy ! |
1315 |
|
dstRgn->CopyAssign(*it); |
1316 |
|
} |
1317 |
|
} |
1318 |
|
} |
1319 |
|
|
1320 |
|
|
1321 |
// *************** File *************** |
// *************** File *************** |
1568 |
} |
} |
1569 |
|
|
1570 |
/** |
/** |
1571 |
|
* Returns extension file of given index. Extension files are used |
1572 |
|
* sometimes to circumvent the 2 GB file size limit of the RIFF format and |
1573 |
|
* of certain operating systems in general. In this case, instead of just |
1574 |
|
* using one file, the content is spread among several files with similar |
1575 |
|
* file name scheme. This is especially used by some GigaStudio sound |
1576 |
|
* libraries. |
1577 |
|
* |
1578 |
|
* @param index - index of extension file |
1579 |
|
* @returns sought extension file, NULL if index out of bounds |
1580 |
|
* @see GetFileName() |
1581 |
|
*/ |
1582 |
|
RIFF::File* File::GetExtensionFile(int index) { |
1583 |
|
if (index < 0 || index >= ExtensionFiles.size()) return NULL; |
1584 |
|
std::list<RIFF::File*>::iterator iter = ExtensionFiles.begin(); |
1585 |
|
for (int i = 0; iter != ExtensionFiles.end(); ++iter, ++i) |
1586 |
|
if (i == index) return *iter; |
1587 |
|
return NULL; |
1588 |
|
} |
1589 |
|
|
1590 |
|
/** @brief File name of this DLS file. |
1591 |
|
* |
1592 |
|
* This method returns the file name as it was provided when loading |
1593 |
|
* the respective DLS file. However in case the File object associates |
1594 |
|
* an empty, that is new DLS file, which was not yet saved to disk, |
1595 |
|
* this method will return an empty string. |
1596 |
|
* |
1597 |
|
* @see GetExtensionFile() |
1598 |
|
*/ |
1599 |
|
String File::GetFileName() { |
1600 |
|
return pRIFF->GetFileName(); |
1601 |
|
} |
1602 |
|
|
1603 |
|
/** |
1604 |
* Apply all the DLS file's current instruments, samples and settings to |
* Apply all the DLS file's current instruments, samples and settings to |
1605 |
* the respective RIFF chunks. You have to call Save() to make changes |
* the respective RIFF chunks. You have to call Save() to make changes |
1606 |
* persistent. |
* persistent. |