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 * |
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 |
|
|
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 |
|
|
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 *************** |
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 *************** |
744 |
RIFF::List* pParent = pWaveList->GetParent(); |
RIFF::List* pParent = pWaveList->GetParent(); |
745 |
pParent->DeleteSubChunk(pWaveList); |
pParent->DeleteSubChunk(pWaveList); |
746 |
} |
} |
747 |
|
|
748 |
|
/** |
749 |
|
* Make a deep copy of the Sample object given by @a orig (without the |
750 |
|
* actual sample waveform data however) and assign it to this object. |
751 |
|
* |
752 |
|
* This is a special internal variant of CopyAssign() which only copies the |
753 |
|
* most mandatory member variables. It will be called by gig::Sample |
754 |
|
* descendent instead of CopyAssign() since gig::Sample has its own |
755 |
|
* implementation to access and copy the actual sample waveform data. |
756 |
|
* |
757 |
|
* @param orig - original Sample object to be copied from |
758 |
|
*/ |
759 |
|
void Sample::CopyAssignCore(const Sample* orig) { |
760 |
|
// handle base classes |
761 |
|
Resource::CopyAssign(orig); |
762 |
|
// handle actual own attributes of this class |
763 |
|
FormatTag = orig->FormatTag; |
764 |
|
Channels = orig->Channels; |
765 |
|
SamplesPerSecond = orig->SamplesPerSecond; |
766 |
|
AverageBytesPerSecond = orig->AverageBytesPerSecond; |
767 |
|
BlockAlign = orig->BlockAlign; |
768 |
|
BitDepth = orig->BitDepth; |
769 |
|
SamplesTotal = orig->SamplesTotal; |
770 |
|
FrameSize = orig->FrameSize; |
771 |
|
} |
772 |
|
|
773 |
|
/** |
774 |
|
* Make a deep copy of the Sample object given by @a orig and assign it to |
775 |
|
* this object. |
776 |
|
* |
777 |
|
* @param orig - original Sample object to be copied from |
778 |
|
*/ |
779 |
|
void Sample::CopyAssign(const Sample* orig) { |
780 |
|
CopyAssignCore(orig); |
781 |
|
|
782 |
|
// copy sample waveform data (reading directly from disc) |
783 |
|
Resize(orig->GetSize()); |
784 |
|
char* buf = (char*) LoadSampleData(); |
785 |
|
Sample* pOrig = (Sample*) orig; //HACK: circumventing the constness here for now |
786 |
|
const unsigned long restorePos = pOrig->pCkData->GetPos(); |
787 |
|
pOrig->SetPos(0); |
788 |
|
for (unsigned long todo = pOrig->GetSize(), i = 0; todo; ) { |
789 |
|
const int iReadAtOnce = 64*1024; |
790 |
|
unsigned long n = (iReadAtOnce < todo) ? iReadAtOnce : todo; |
791 |
|
n = pOrig->Read(&buf[i], n); |
792 |
|
if (!n) break; |
793 |
|
todo -= n; |
794 |
|
i += (n * pOrig->FrameSize); |
795 |
|
} |
796 |
|
pOrig->pCkData->SetPos(restorePos); |
797 |
|
} |
798 |
|
|
799 |
/** @brief Load sample data into RAM. |
/** @brief Load sample data into RAM. |
800 |
* |
* |
845 |
* @returns number of sample points or 0 if FormatTag != DLS_WAVE_FORMAT_PCM |
* @returns number of sample points or 0 if FormatTag != DLS_WAVE_FORMAT_PCM |
846 |
* @see FrameSize, FormatTag |
* @see FrameSize, FormatTag |
847 |
*/ |
*/ |
848 |
unsigned long Sample::GetSize() { |
unsigned long Sample::GetSize() const { |
849 |
if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; |
if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; |
850 |
return (pCkData) ? pCkData->GetSize() / FrameSize : 0; |
return (pCkData) ? pCkData->GetSize() / FrameSize : 0; |
851 |
} |
} |
1148 |
store32(&pData[4], Channel); |
store32(&pData[4], Channel); |
1149 |
store32(&pData[8], WavePoolTableIndex); |
store32(&pData[8], WavePoolTableIndex); |
1150 |
} |
} |
1151 |
|
|
1152 |
|
/** |
1153 |
|
* Make a (semi) deep copy of the Region object given by @a orig and assign |
1154 |
|
* it to this object. |
1155 |
|
* |
1156 |
|
* Note that the sample pointer referenced by @a orig is simply copied as |
1157 |
|
* memory address. Thus the respective sample is shared, not duplicated! |
1158 |
|
* |
1159 |
|
* @param orig - original Region object to be copied from |
1160 |
|
*/ |
1161 |
|
void Region::CopyAssign(const Region* orig) { |
1162 |
|
// handle base classes |
1163 |
|
Resource::CopyAssign(orig); |
1164 |
|
Articulator::CopyAssign(orig); |
1165 |
|
Sampler::CopyAssign(orig); |
1166 |
|
// handle actual own attributes of this class |
1167 |
|
// (the trivial ones) |
1168 |
|
VelocityRange = orig->VelocityRange; |
1169 |
|
KeyGroup = orig->KeyGroup; |
1170 |
|
Layer = orig->Layer; |
1171 |
|
SelfNonExclusive = orig->SelfNonExclusive; |
1172 |
|
PhaseMaster = orig->PhaseMaster; |
1173 |
|
PhaseGroup = orig->PhaseGroup; |
1174 |
|
MultiChannel = orig->MultiChannel; |
1175 |
|
Channel = orig->Channel; |
1176 |
|
// only take the raw sample reference if the two Region objects are |
1177 |
|
// part of the same file |
1178 |
|
if (GetParent()->GetParent() == orig->GetParent()->GetParent()) { |
1179 |
|
WavePoolTableIndex = orig->WavePoolTableIndex; |
1180 |
|
pSample = orig->pSample; |
1181 |
|
} else { |
1182 |
|
WavePoolTableIndex = -1; |
1183 |
|
pSample = NULL; |
1184 |
|
} |
1185 |
|
FormatOptionFlags = orig->FormatOptionFlags; |
1186 |
|
WaveLinkOptionFlags = orig->WaveLinkOptionFlags; |
1187 |
|
// handle the last, a bit sensible attribute |
1188 |
|
SetKeyRange(orig->KeyRange.low, orig->KeyRange.high); |
1189 |
|
} |
1190 |
|
|
1191 |
|
|
1192 |
// *************** Instrument *************** |
// *************** Instrument *************** |
1337 |
RIFF::List* pParent = pCkInstrument->GetParent(); |
RIFF::List* pParent = pCkInstrument->GetParent(); |
1338 |
pParent->DeleteSubChunk(pCkInstrument); |
pParent->DeleteSubChunk(pCkInstrument); |
1339 |
} |
} |
1340 |
|
|
1341 |
|
void Instrument::CopyAssignCore(const Instrument* orig) { |
1342 |
|
// handle base classes |
1343 |
|
Resource::CopyAssign(orig); |
1344 |
|
Articulator::CopyAssign(orig); |
1345 |
|
// handle actual own attributes of this class |
1346 |
|
// (the trivial ones) |
1347 |
|
IsDrum = orig->IsDrum; |
1348 |
|
MIDIBank = orig->MIDIBank; |
1349 |
|
MIDIBankCoarse = orig->MIDIBankCoarse; |
1350 |
|
MIDIBankFine = orig->MIDIBankFine; |
1351 |
|
MIDIProgram = orig->MIDIProgram; |
1352 |
|
} |
1353 |
|
|
1354 |
|
/** |
1355 |
|
* Make a (semi) deep copy of the Instrument object given by @a orig and assign |
1356 |
|
* it to this object. |
1357 |
|
* |
1358 |
|
* Note that all sample pointers referenced by @a orig are simply copied as |
1359 |
|
* memory address. Thus the respective samples are shared, not duplicated! |
1360 |
|
* |
1361 |
|
* @param orig - original Instrument object to be copied from |
1362 |
|
*/ |
1363 |
|
void Instrument::CopyAssign(const Instrument* orig) { |
1364 |
|
CopyAssignCore(orig); |
1365 |
|
// delete all regions first |
1366 |
|
while (Regions) DeleteRegion(GetFirstRegion()); |
1367 |
|
// now recreate and copy regions |
1368 |
|
{ |
1369 |
|
RegionList::const_iterator it = orig->pRegions->begin(); |
1370 |
|
for (int i = 0; i < orig->Regions; ++i, ++it) { |
1371 |
|
Region* dstRgn = AddRegion(); |
1372 |
|
//NOTE: Region does semi-deep copy ! |
1373 |
|
dstRgn->CopyAssign(*it); |
1374 |
|
} |
1375 |
|
} |
1376 |
|
} |
1377 |
|
|
1378 |
|
|
1379 |
// *************** File *************** |
// *************** File *************** |
1626 |
} |
} |
1627 |
|
|
1628 |
/** |
/** |
1629 |
|
* Returns extension file of given index. Extension files are used |
1630 |
|
* sometimes to circumvent the 2 GB file size limit of the RIFF format and |
1631 |
|
* of certain operating systems in general. In this case, instead of just |
1632 |
|
* using one file, the content is spread among several files with similar |
1633 |
|
* file name scheme. This is especially used by some GigaStudio sound |
1634 |
|
* libraries. |
1635 |
|
* |
1636 |
|
* @param index - index of extension file |
1637 |
|
* @returns sought extension file, NULL if index out of bounds |
1638 |
|
* @see GetFileName() |
1639 |
|
*/ |
1640 |
|
RIFF::File* File::GetExtensionFile(int index) { |
1641 |
|
if (index < 0 || index >= ExtensionFiles.size()) return NULL; |
1642 |
|
std::list<RIFF::File*>::iterator iter = ExtensionFiles.begin(); |
1643 |
|
for (int i = 0; iter != ExtensionFiles.end(); ++iter, ++i) |
1644 |
|
if (i == index) return *iter; |
1645 |
|
return NULL; |
1646 |
|
} |
1647 |
|
|
1648 |
|
/** @brief File name of this DLS file. |
1649 |
|
* |
1650 |
|
* This method returns the file name as it was provided when loading |
1651 |
|
* the respective DLS file. However in case the File object associates |
1652 |
|
* an empty, that is new DLS file, which was not yet saved to disk, |
1653 |
|
* this method will return an empty string. |
1654 |
|
* |
1655 |
|
* @see GetExtensionFile() |
1656 |
|
*/ |
1657 |
|
String File::GetFileName() { |
1658 |
|
return pRIFF->GetFileName(); |
1659 |
|
} |
1660 |
|
|
1661 |
|
/** |
1662 |
|
* You may call this method store a future file name, so you don't have to |
1663 |
|
* to pass it to the Save() call later on. |
1664 |
|
*/ |
1665 |
|
void File::SetFileName(const String& name) { |
1666 |
|
pRIFF->SetFileName(name); |
1667 |
|
} |
1668 |
|
|
1669 |
|
/** |
1670 |
* Apply all the DLS file's current instruments, samples and settings to |
* Apply all the DLS file's current instruments, samples and settings to |
1671 |
* the respective RIFF chunks. You have to call Save() to make changes |
* the respective RIFF chunks. You have to call Save() to make changes |
1672 |
* persistent. |
* persistent. |
1743 |
void File::Save(const String& Path) { |
void File::Save(const String& Path) { |
1744 |
UpdateChunks(); |
UpdateChunks(); |
1745 |
pRIFF->Save(Path); |
pRIFF->Save(Path); |
1746 |
__UpdateWavePoolTableChunk(); |
UpdateFileOffsets(); |
1747 |
} |
} |
1748 |
|
|
1749 |
/** @brief Save changes to same file. |
/** @brief Save changes to same file. |
1758 |
void File::Save() { |
void File::Save() { |
1759 |
UpdateChunks(); |
UpdateChunks(); |
1760 |
pRIFF->Save(); |
pRIFF->Save(); |
1761 |
|
UpdateFileOffsets(); |
1762 |
|
} |
1763 |
|
|
1764 |
|
/** @brief Updates all file offsets stored all over the file. |
1765 |
|
* |
1766 |
|
* This virtual method is called whenever the overall file layout has been |
1767 |
|
* changed (i.e. file or individual RIFF chunks have been resized). It is |
1768 |
|
* then the responsibility of this method to update all file offsets stored |
1769 |
|
* in the file format. For example samples are referenced by instruments by |
1770 |
|
* file offsets. The gig format also stores references to instrument |
1771 |
|
* scripts as file offsets, and thus it overrides this method to update |
1772 |
|
* those file offsets as well. |
1773 |
|
*/ |
1774 |
|
void File::UpdateFileOffsets() { |
1775 |
__UpdateWavePoolTableChunk(); |
__UpdateWavePoolTableChunk(); |
1776 |
} |
} |
1777 |
|
|