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-2014 by Christian Schoenebeck * |
* Copyright (C) 2003-2015 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 * |
53 |
|
|
54 |
namespace gig { |
namespace gig { |
55 |
|
|
|
// *************** progress_t *************** |
|
|
// * |
|
|
|
|
|
progress_t::progress_t() { |
|
|
callback = NULL; |
|
|
custom = NULL; |
|
|
__range_min = 0.0f; |
|
|
__range_max = 1.0f; |
|
|
} |
|
|
|
|
|
// private helper function to convert progress of a subprocess into the global progress |
|
|
static void __notify_progress(progress_t* pProgress, float subprogress) { |
|
|
if (pProgress && pProgress->callback) { |
|
|
const float totalrange = pProgress->__range_max - pProgress->__range_min; |
|
|
const float totalprogress = pProgress->__range_min + subprogress * totalrange; |
|
|
pProgress->factor = totalprogress; |
|
|
pProgress->callback(pProgress); // now actually notify about the progress |
|
|
} |
|
|
} |
|
|
|
|
|
// private helper function to divide a progress into subprogresses |
|
|
static void __divide_progress(progress_t* pParentProgress, progress_t* pSubProgress, float totalTasks, float currentTask) { |
|
|
if (pParentProgress && pParentProgress->callback) { |
|
|
const float totalrange = pParentProgress->__range_max - pParentProgress->__range_min; |
|
|
pSubProgress->callback = pParentProgress->callback; |
|
|
pSubProgress->custom = pParentProgress->custom; |
|
|
pSubProgress->__range_min = pParentProgress->__range_min + totalrange * currentTask / totalTasks; |
|
|
pSubProgress->__range_max = pSubProgress->__range_min + totalrange / totalTasks; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
56 |
// *************** Internal functions for sample decompression *************** |
// *************** Internal functions for sample decompression *************** |
57 |
// * |
// * |
58 |
|
|
496 |
* Usually there is absolutely no need to call this method explicitly. |
* Usually there is absolutely no need to call this method explicitly. |
497 |
* It will be called automatically when File::Save() was called. |
* It will be called automatically when File::Save() was called. |
498 |
* |
* |
499 |
|
* @param pProgress - callback function for progress notification |
500 |
* @throws DLS::Exception if FormatTag != DLS_WAVE_FORMAT_PCM or no sample data |
* @throws DLS::Exception if FormatTag != DLS_WAVE_FORMAT_PCM or no sample data |
501 |
* was provided yet |
* was provided yet |
502 |
* @throws gig::Exception if there is any invalid sample setting |
* @throws gig::Exception if there is any invalid sample setting |
503 |
*/ |
*/ |
504 |
void Sample::UpdateChunks() { |
void Sample::UpdateChunks(progress_t* pProgress) { |
505 |
// first update base class's chunks |
// first update base class's chunks |
506 |
DLS::Sample::UpdateChunks(); |
DLS::Sample::UpdateChunks(pProgress); |
507 |
|
|
508 |
// make sure 'smpl' chunk exists |
// make sure 'smpl' chunk exists |
509 |
pCkSmpl = pWaveList->GetSubChunk(CHUNK_ID_SMPL); |
pCkSmpl = pWaveList->GetSubChunk(CHUNK_ID_SMPL); |
1721 |
* |
* |
1722 |
* Usually there is absolutely no need to call this method explicitly. |
* Usually there is absolutely no need to call this method explicitly. |
1723 |
* It will be called automatically when File::Save() was called. |
* It will be called automatically when File::Save() was called. |
1724 |
|
* |
1725 |
|
* @param pProgress - callback function for progress notification |
1726 |
*/ |
*/ |
1727 |
void DimensionRegion::UpdateChunks() { |
void DimensionRegion::UpdateChunks(progress_t* pProgress) { |
1728 |
// first update base class's chunk |
// first update base class's chunk |
1729 |
DLS::Sampler::UpdateChunks(); |
DLS::Sampler::UpdateChunks(pProgress); |
1730 |
|
|
1731 |
RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP); |
RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP); |
1732 |
uint8_t* pData = (uint8_t*) wsmp->LoadChunkData(); |
uint8_t* pData = (uint8_t*) wsmp->LoadChunkData(); |
2997 |
* Usually there is absolutely no need to call this method explicitly. |
* Usually there is absolutely no need to call this method explicitly. |
2998 |
* It will be called automatically when File::Save() was called. |
* It will be called automatically when File::Save() was called. |
2999 |
* |
* |
3000 |
|
* @param pProgress - callback function for progress notification |
3001 |
* @throws gig::Exception if samples cannot be dereferenced |
* @throws gig::Exception if samples cannot be dereferenced |
3002 |
*/ |
*/ |
3003 |
void Region::UpdateChunks() { |
void Region::UpdateChunks(progress_t* pProgress) { |
3004 |
// in the gig format we don't care about the Region's sample reference |
// in the gig format we don't care about the Region's sample reference |
3005 |
// but we still have to provide some existing one to not corrupt the |
// but we still have to provide some existing one to not corrupt the |
3006 |
// file, so to avoid the latter we simply always assign the sample of |
// file, so to avoid the latter we simply always assign the sample of |
3008 |
pSample = pDimensionRegions[0]->pSample; |
pSample = pDimensionRegions[0]->pSample; |
3009 |
|
|
3010 |
// first update base class's chunks |
// first update base class's chunks |
3011 |
DLS::Region::UpdateChunks(); |
DLS::Region::UpdateChunks(pProgress); |
3012 |
|
|
3013 |
// update dimension region's chunks |
// update dimension region's chunks |
3014 |
for (int i = 0; i < DimensionRegions; i++) { |
for (int i = 0; i < DimensionRegions; i++) { |
3015 |
pDimensionRegions[i]->UpdateChunks(); |
pDimensionRegions[i]->UpdateChunks(pProgress); |
3016 |
} |
} |
3017 |
|
|
3018 |
File* pFile = (File*) GetParent()->GetParent(); |
File* pFile = (File*) GetParent()->GetParent(); |
4133 |
memcpy(&data[0], &text[0], text.size()); |
memcpy(&data[0], &text[0], text.size()); |
4134 |
} |
} |
4135 |
|
|
4136 |
void Script::UpdateChunks() { |
/** |
4137 |
|
* Apply this script to the respective RIFF chunks. You have to call |
4138 |
|
* File::Save() to make changes persistent. |
4139 |
|
* |
4140 |
|
* Usually there is absolutely no need to call this method explicitly. |
4141 |
|
* It will be called automatically when File::Save() was called. |
4142 |
|
* |
4143 |
|
* @param pProgress - callback function for progress notification |
4144 |
|
*/ |
4145 |
|
void Script::UpdateChunks(progress_t* pProgress) { |
4146 |
// recalculate CRC32 check sum |
// recalculate CRC32 check sum |
4147 |
__resetCRC(crc); |
__resetCRC(crc); |
4148 |
__calculateCRC(&data[0], data.size(), crc); |
__calculateCRC(&data[0], data.size(), crc); |
4232 |
} |
} |
4233 |
} |
} |
4234 |
|
|
4235 |
void ScriptGroup::UpdateChunks() { |
/** |
4236 |
|
* Apply this script group to the respective RIFF chunks. You have to call |
4237 |
|
* File::Save() to make changes persistent. |
4238 |
|
* |
4239 |
|
* Usually there is absolutely no need to call this method explicitly. |
4240 |
|
* It will be called automatically when File::Save() was called. |
4241 |
|
* |
4242 |
|
* @param pProgress - callback function for progress notification |
4243 |
|
*/ |
4244 |
|
void ScriptGroup::UpdateChunks(progress_t* pProgress) { |
4245 |
if (pScripts) { |
if (pScripts) { |
4246 |
if (!pList) |
if (!pList) |
4247 |
pList = pFile->pRIFF->GetSubList(LIST_TYPE_3LS)->AddSubList(LIST_TYPE_RTIS); |
pList = pFile->pRIFF->GetSubList(LIST_TYPE_3LS)->AddSubList(LIST_TYPE_RTIS); |
4252 |
for (std::list<Script*>::iterator it = pScripts->begin(); |
for (std::list<Script*>::iterator it = pScripts->begin(); |
4253 |
it != pScripts->end(); ++it) |
it != pScripts->end(); ++it) |
4254 |
{ |
{ |
4255 |
(*it)->UpdateChunks(); |
(*it)->UpdateChunks(pProgress); |
4256 |
} |
} |
4257 |
} |
} |
4258 |
} |
} |
4462 |
* Usually there is absolutely no need to call this method explicitly. |
* Usually there is absolutely no need to call this method explicitly. |
4463 |
* It will be called automatically when File::Save() was called. |
* It will be called automatically when File::Save() was called. |
4464 |
* |
* |
4465 |
|
* @param pProgress - callback function for progress notification |
4466 |
* @throws gig::Exception if samples cannot be dereferenced |
* @throws gig::Exception if samples cannot be dereferenced |
4467 |
*/ |
*/ |
4468 |
void Instrument::UpdateChunks() { |
void Instrument::UpdateChunks(progress_t* pProgress) { |
4469 |
// first update base classes' chunks |
// first update base classes' chunks |
4470 |
DLS::Instrument::UpdateChunks(); |
DLS::Instrument::UpdateChunks(pProgress); |
4471 |
|
|
4472 |
// update Regions' chunks |
// update Regions' chunks |
4473 |
{ |
{ |
4474 |
RegionList::iterator iter = pRegions->begin(); |
RegionList::iterator iter = pRegions->begin(); |
4475 |
RegionList::iterator end = pRegions->end(); |
RegionList::iterator end = pRegions->end(); |
4476 |
for (; iter != end; ++iter) |
for (; iter != end; ++iter) |
4477 |
(*iter)->UpdateChunks(); |
(*iter)->UpdateChunks(pProgress); |
4478 |
} |
} |
4479 |
|
|
4480 |
// make sure 'lart' RIFF list chunk exists |
// make sure 'lart' RIFF list chunk exists |
4551 |
|
|
4552 |
void Instrument::UpdateScriptFileOffsets() { |
void Instrument::UpdateScriptFileOffsets() { |
4553 |
// own gig format extensions |
// own gig format extensions |
4554 |
if (pScriptRefs) { |
if (pScriptRefs && pScriptRefs->size() > 0) { |
4555 |
RIFF::List* lst3LS = pCkInstrument->GetSubList(LIST_TYPE_3LS); |
RIFF::List* lst3LS = pCkInstrument->GetSubList(LIST_TYPE_3LS); |
4556 |
RIFF::Chunk* ckSCSL = lst3LS->GetSubChunk(CHUNK_ID_SCSL); |
RIFF::Chunk* ckSCSL = lst3LS->GetSubChunk(CHUNK_ID_SCSL); |
4557 |
const int slotCount = pScriptRefs->size(); |
const int slotCount = pScriptRefs->size(); |
4636 |
} |
} |
4637 |
|
|
4638 |
/** |
/** |
4639 |
|
* Move this instrument at the position before @arg dst. |
4640 |
|
* |
4641 |
|
* This method can be used to reorder the sequence of instruments in a |
4642 |
|
* .gig file. This might be helpful especially on large .gig files which |
4643 |
|
* contain a large number of instruments within the same .gig file. So |
4644 |
|
* grouping such instruments to similar ones, can help to keep track of them |
4645 |
|
* when working with such complex .gig files. |
4646 |
|
* |
4647 |
|
* When calling this method, this instrument will be removed from in its |
4648 |
|
* current position in the instruments list and moved to the requested |
4649 |
|
* target position provided by @param dst. You may also pass NULL as |
4650 |
|
* argument to this method, in that case this intrument will be moved to the |
4651 |
|
* very end of the .gig file's instrument list. |
4652 |
|
* |
4653 |
|
* You have to call Save() to make the order change persistent to the .gig |
4654 |
|
* file. |
4655 |
|
* |
4656 |
|
* Currently this method is limited to moving the instrument within the same |
4657 |
|
* .gig file. Trying to move it to another .gig file by calling this method |
4658 |
|
* will throw an exception. |
4659 |
|
* |
4660 |
|
* @param dst - destination instrument at which this instrument will be |
4661 |
|
* moved to, or pass NULL for moving to end of list |
4662 |
|
* @throw gig::Exception if this instrument and target instrument are not |
4663 |
|
* part of the same file |
4664 |
|
*/ |
4665 |
|
void Instrument::MoveTo(Instrument* dst) { |
4666 |
|
if (dst && GetParent() != dst->GetParent()) |
4667 |
|
throw Exception( |
4668 |
|
"gig::Instrument::MoveTo() can only be used for moving within " |
4669 |
|
"the same gig file." |
4670 |
|
); |
4671 |
|
|
4672 |
|
File* pFile = (File*) GetParent(); |
4673 |
|
|
4674 |
|
// move this instrument within the instrument list |
4675 |
|
{ |
4676 |
|
DLS::File::InstrumentList& list = *pFile->pInstruments; |
4677 |
|
|
4678 |
|
DLS::File::InstrumentList::iterator itFrom = |
4679 |
|
std::find(list.begin(), list.end(), static_cast<DLS::Instrument*>(this)); |
4680 |
|
|
4681 |
|
DLS::File::InstrumentList::iterator itTo = |
4682 |
|
std::find(list.begin(), list.end(), static_cast<DLS::Instrument*>(dst)); |
4683 |
|
|
4684 |
|
list.splice(itTo, list, itFrom); |
4685 |
|
} |
4686 |
|
|
4687 |
|
// move the instrument's actual list RIFF chunk appropriately |
4688 |
|
RIFF::List* lstCkInstruments = pFile->pRIFF->GetSubList(LIST_TYPE_LINS); |
4689 |
|
lstCkInstruments->MoveSubChunk( |
4690 |
|
this->pCkInstrument, |
4691 |
|
(dst) ? dst->pCkInstrument : NULL |
4692 |
|
); |
4693 |
|
} |
4694 |
|
|
4695 |
|
/** |
4696 |
* Returns a MIDI rule of the instrument. |
* Returns a MIDI rule of the instrument. |
4697 |
* |
* |
4698 |
* The list of MIDI rules, at least in gig v3, always contains at |
* The list of MIDI rules, at least in gig v3, always contains at |
5052 |
* |
* |
5053 |
* Usually there is absolutely no need to call this method explicitly. |
* Usually there is absolutely no need to call this method explicitly. |
5054 |
* It will be called automatically when File::Save() was called. |
* It will be called automatically when File::Save() was called. |
5055 |
|
* |
5056 |
|
* @param pProgress - callback function for progress notification |
5057 |
*/ |
*/ |
5058 |
void Group::UpdateChunks() { |
void Group::UpdateChunks(progress_t* pProgress) { |
5059 |
// make sure <3gri> and <3gnl> list chunks exist |
// make sure <3gri> and <3gnl> list chunks exist |
5060 |
RIFF::List* _3gri = pFile->pRIFF->GetSubList(LIST_TYPE_3GRI); |
RIFF::List* _3gri = pFile->pRIFF->GetSubList(LIST_TYPE_3GRI); |
5061 |
if (!_3gri) { |
if (!_3gri) { |
5831 |
* Usually there is absolutely no need to call this method explicitly. |
* Usually there is absolutely no need to call this method explicitly. |
5832 |
* It will be called automatically when File::Save() was called. |
* It will be called automatically when File::Save() was called. |
5833 |
* |
* |
5834 |
|
* @param pProgress - callback function for progress notification |
5835 |
* @throws Exception - on errors |
* @throws Exception - on errors |
5836 |
*/ |
*/ |
5837 |
void File::UpdateChunks() { |
void File::UpdateChunks(progress_t* pProgress) { |
5838 |
bool newFile = pRIFF->GetSubList(LIST_TYPE_INFO) == NULL; |
bool newFile = pRIFF->GetSubList(LIST_TYPE_INFO) == NULL; |
5839 |
|
|
5840 |
b64BitWavePoolOffsets = pVersion && pVersion->major == 3; |
b64BitWavePoolOffsets = pVersion && pVersion->major == 3; |
5857 |
for (std::list<ScriptGroup*>::iterator it = pScriptGroups->begin(); |
for (std::list<ScriptGroup*>::iterator it = pScriptGroups->begin(); |
5858 |
it != pScriptGroups->end(); ++it) |
it != pScriptGroups->end(); ++it) |
5859 |
{ |
{ |
5860 |
(*it)->UpdateChunks(); |
(*it)->UpdateChunks(pProgress); |
5861 |
} |
} |
5862 |
} |
} |
5863 |
} |
} |
5864 |
|
|
5865 |
// first update base class's chunks |
// first update base class's chunks |
5866 |
DLS::File::UpdateChunks(); |
DLS::File::UpdateChunks(pProgress); |
5867 |
|
|
5868 |
if (newFile) { |
if (newFile) { |
5869 |
// INFO was added by Resource::UpdateChunks - make sure it |
// INFO was added by Resource::UpdateChunks - make sure it |
5900 |
std::list<Group*>::iterator iter = pGroups->begin(); |
std::list<Group*>::iterator iter = pGroups->begin(); |
5901 |
std::list<Group*>::iterator end = pGroups->end(); |
std::list<Group*>::iterator end = pGroups->end(); |
5902 |
for (; iter != end; ++iter) { |
for (; iter != end; ++iter) { |
5903 |
(*iter)->UpdateChunks(); |
(*iter)->UpdateChunks(pProgress); |
5904 |
} |
} |
5905 |
} |
} |
5906 |
|
|