454 |
} |
} |
455 |
|
|
456 |
/** |
/** |
457 |
|
* Make a (semi) deep copy of the Sample object given by @a orig (without |
458 |
|
* the actual waveform data) and assign it to this object. |
459 |
|
* |
460 |
|
* Discussion: copying .gig samples is a bit tricky. It requires three |
461 |
|
* steps: |
462 |
|
* 1. Copy sample's meta informations (done by CopyAssignMeta()) including |
463 |
|
* its new sample waveform data size. |
464 |
|
* 2. Saving the file (done by File::Save()) so that it gains correct size |
465 |
|
* and layout for writing the actual wave form data directly to disc |
466 |
|
* in next step. |
467 |
|
* 3. Copy the waveform data with disk streaming (done by CopyAssignWave()). |
468 |
|
* |
469 |
|
* @param orig - original Sample object to be copied from |
470 |
|
*/ |
471 |
|
void Sample::CopyAssignMeta(const Sample* orig) { |
472 |
|
// handle base classes |
473 |
|
DLS::Sample::CopyAssignCore(orig); |
474 |
|
|
475 |
|
// handle actual own attributes of this class |
476 |
|
Manufacturer = orig->Manufacturer; |
477 |
|
Product = orig->Product; |
478 |
|
SamplePeriod = orig->SamplePeriod; |
479 |
|
MIDIUnityNote = orig->MIDIUnityNote; |
480 |
|
FineTune = orig->FineTune; |
481 |
|
SMPTEFormat = orig->SMPTEFormat; |
482 |
|
SMPTEOffset = orig->SMPTEOffset; |
483 |
|
Loops = orig->Loops; |
484 |
|
LoopID = orig->LoopID; |
485 |
|
LoopType = orig->LoopType; |
486 |
|
LoopStart = orig->LoopStart; |
487 |
|
LoopEnd = orig->LoopEnd; |
488 |
|
LoopSize = orig->LoopSize; |
489 |
|
LoopFraction = orig->LoopFraction; |
490 |
|
LoopPlayCount = orig->LoopPlayCount; |
491 |
|
|
492 |
|
// schedule resizing this sample to the given sample's size |
493 |
|
Resize(orig->GetSize()); |
494 |
|
} |
495 |
|
|
496 |
|
/** |
497 |
|
* Should be called after CopyAssignMeta() and File::Save() sequence. |
498 |
|
* Read more about it in the discussion of CopyAssignMeta(). This method |
499 |
|
* copies the actual waveform data by disk streaming. |
500 |
|
* |
501 |
|
* @e CAUTION: this method is currently not thread safe! During this |
502 |
|
* operation the sample must not be used for other purposes by other |
503 |
|
* threads! |
504 |
|
* |
505 |
|
* @param orig - original Sample object to be copied from |
506 |
|
*/ |
507 |
|
void Sample::CopyAssignWave(const Sample* orig) { |
508 |
|
const int iReadAtOnce = 32*1024; |
509 |
|
char* buf = new char[iReadAtOnce * orig->FrameSize]; |
510 |
|
Sample* pOrig = (Sample*) orig; //HACK: remove constness for now |
511 |
|
unsigned long restorePos = pOrig->GetPos(); |
512 |
|
pOrig->SetPos(0); |
513 |
|
SetPos(0); |
514 |
|
for (unsigned long n = pOrig->Read(buf, iReadAtOnce); n; |
515 |
|
n = pOrig->Read(buf, iReadAtOnce)) |
516 |
|
{ |
517 |
|
Write(buf, n); |
518 |
|
} |
519 |
|
pOrig->SetPos(restorePos); |
520 |
|
delete [] buf; |
521 |
|
} |
522 |
|
|
523 |
|
/** |
524 |
* Apply sample and its settings to the respective RIFF chunks. You have |
* Apply sample and its settings to the respective RIFF chunks. You have |
525 |
* to call File::Save() to make changes persistent. |
* to call File::Save() to make changes persistent. |
526 |
* |
* |
581 |
// update '3gix' chunk |
// update '3gix' chunk |
582 |
pData = (uint8_t*) pCk3gix->LoadChunkData(); |
pData = (uint8_t*) pCk3gix->LoadChunkData(); |
583 |
store16(&pData[0], iSampleGroup); |
store16(&pData[0], iSampleGroup); |
584 |
|
|
585 |
|
// if the library user toggled the "Compressed" attribute from true to |
586 |
|
// false, then the EWAV chunk associated with compressed samples needs |
587 |
|
// to be deleted |
588 |
|
RIFF::Chunk* ewav = pWaveList->GetSubChunk(CHUNK_ID_EWAV); |
589 |
|
if (ewav && !Compressed) { |
590 |
|
pWaveList->DeleteSubChunk(ewav); |
591 |
|
} |
592 |
} |
} |
593 |
|
|
594 |
/// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). |
/// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). |
883 |
/** |
/** |
884 |
* Returns the current position in the sample (in sample points). |
* Returns the current position in the sample (in sample points). |
885 |
*/ |
*/ |
886 |
unsigned long Sample::GetPos() { |
unsigned long Sample::GetPos() const { |
887 |
if (Compressed) return SamplePos; |
if (Compressed) return SamplePos; |
888 |
else return pCkData->GetPos() / FrameSize; |
else return pCkData->GetPos() / FrameSize; |
889 |
} |
} |
1683 |
* @param orig - original DimensionRegion object to be copied from |
* @param orig - original DimensionRegion object to be copied from |
1684 |
*/ |
*/ |
1685 |
void DimensionRegion::CopyAssign(const DimensionRegion* orig) { |
void DimensionRegion::CopyAssign(const DimensionRegion* orig) { |
1686 |
|
CopyAssign(orig, NULL); |
1687 |
|
} |
1688 |
|
|
1689 |
|
/** |
1690 |
|
* Make a (semi) deep copy of the DimensionRegion object given by @a orig |
1691 |
|
* and assign it to this object. |
1692 |
|
* |
1693 |
|
* @param orig - original DimensionRegion object to be copied from |
1694 |
|
* @param mSamples - crosslink map between the foreign file's samples and |
1695 |
|
* this file's samples |
1696 |
|
*/ |
1697 |
|
void DimensionRegion::CopyAssign(const DimensionRegion* orig, const std::map<Sample*,Sample*>* mSamples) { |
1698 |
// delete all allocated data first |
// delete all allocated data first |
1699 |
if (VelocityTable) delete [] VelocityTable; |
if (VelocityTable) delete [] VelocityTable; |
1700 |
if (pSampleLoops) delete [] pSampleLoops; |
if (pSampleLoops) delete [] pSampleLoops; |
1702 |
// backup parent list pointer |
// backup parent list pointer |
1703 |
RIFF::List* p = pParentList; |
RIFF::List* p = pParentList; |
1704 |
|
|
1705 |
|
gig::Sample* pOriginalSample = pSample; |
1706 |
|
gig::Region* pOriginalRegion = pRegion; |
1707 |
|
|
1708 |
//NOTE: copy code copied from assignment constructor above, see comment there as well |
//NOTE: copy code copied from assignment constructor above, see comment there as well |
1709 |
|
|
1710 |
*this = *orig; // default memberwise shallow copy of all parameters |
*this = *orig; // default memberwise shallow copy of all parameters |
1711 |
pParentList = p; // restore the chunk pointer |
pParentList = p; // restore the chunk pointer |
1712 |
|
|
1713 |
|
// only take the raw sample reference & parent region reference if the |
1714 |
|
// two DimensionRegion objects are part of the same file |
1715 |
|
if (pOriginalRegion->GetParent()->GetParent() != orig->pRegion->GetParent()->GetParent()) { |
1716 |
|
pRegion = pOriginalRegion; |
1717 |
|
pSample = pOriginalSample; |
1718 |
|
} |
1719 |
|
|
1720 |
|
if (mSamples && mSamples->count(orig->pSample)) { |
1721 |
|
pSample = mSamples->find(orig->pSample)->second; |
1722 |
|
} |
1723 |
|
|
1724 |
// deep copy of owned structures |
// deep copy of owned structures |
1725 |
if (orig->VelocityTable) { |
if (orig->VelocityTable) { |
3073 |
* @param orig - original Region object to be copied from |
* @param orig - original Region object to be copied from |
3074 |
*/ |
*/ |
3075 |
void Region::CopyAssign(const Region* orig) { |
void Region::CopyAssign(const Region* orig) { |
3076 |
|
CopyAssign(orig, NULL); |
3077 |
|
} |
3078 |
|
|
3079 |
|
/** |
3080 |
|
* Make a (semi) deep copy of the Region object given by @a orig and |
3081 |
|
* assign it to this object |
3082 |
|
* |
3083 |
|
* @param mSamples - crosslink map between the foreign file's samples and |
3084 |
|
* this file's samples |
3085 |
|
*/ |
3086 |
|
void Region::CopyAssign(const Region* orig, const std::map<Sample*,Sample*>* mSamples) { |
3087 |
// handle base classes |
// handle base classes |
3088 |
DLS::Region::CopyAssign(orig); |
DLS::Region::CopyAssign(orig); |
3089 |
|
|
3090 |
|
if (mSamples && mSamples->count((gig::Sample*)orig->pSample)) { |
3091 |
|
pSample = mSamples->find((gig::Sample*)orig->pSample)->second; |
3092 |
|
} |
3093 |
|
|
3094 |
// handle own member variables |
// handle own member variables |
3095 |
for (int i = Dimensions - 1; i >= 0; --i) { |
for (int i = Dimensions - 1; i >= 0; --i) { |
3096 |
DeleteDimension(&pDimensionDefinitions[i]); |
DeleteDimension(&pDimensionDefinitions[i]); |
3105 |
for (int i = 0; i < 256; i++) { |
for (int i = 0; i < 256; i++) { |
3106 |
if (pDimensionRegions[i] && orig->pDimensionRegions[i]) { |
if (pDimensionRegions[i] && orig->pDimensionRegions[i]) { |
3107 |
pDimensionRegions[i]->CopyAssign( |
pDimensionRegions[i]->CopyAssign( |
3108 |
orig->pDimensionRegions[i] |
orig->pDimensionRegions[i], |
3109 |
|
mSamples |
3110 |
); |
); |
3111 |
} |
} |
3112 |
} |
} |
3578 |
* @param orig - original Instrument object to be copied from |
* @param orig - original Instrument object to be copied from |
3579 |
*/ |
*/ |
3580 |
void Instrument::CopyAssign(const Instrument* orig) { |
void Instrument::CopyAssign(const Instrument* orig) { |
3581 |
|
CopyAssign(orig, NULL); |
3582 |
|
} |
3583 |
|
|
3584 |
|
/** |
3585 |
|
* Make a (semi) deep copy of the Instrument object given by @a orig |
3586 |
|
* and assign it to this object. |
3587 |
|
* |
3588 |
|
* @param orig - original Instrument object to be copied from |
3589 |
|
* @param mSamples - crosslink map between the foreign file's samples and |
3590 |
|
* this file's samples |
3591 |
|
*/ |
3592 |
|
void Instrument::CopyAssign(const Instrument* orig, const std::map<Sample*,Sample*>* mSamples) { |
3593 |
// handle base class |
// handle base class |
3594 |
// (without copying DLS region stuff) |
// (without copying DLS region stuff) |
3595 |
DLS::Instrument::CopyAssignCore(orig); |
DLS::Instrument::CopyAssignCore(orig); |
3618 |
Region* dstRgn = AddRegion(); |
Region* dstRgn = AddRegion(); |
3619 |
//NOTE: Region does semi-deep copy ! |
//NOTE: Region does semi-deep copy ! |
3620 |
dstRgn->CopyAssign( |
dstRgn->CopyAssign( |
3621 |
static_cast<gig::Region*>(*it) |
static_cast<gig::Region*>(*it), |
3622 |
|
mSamples |
3623 |
); |
); |
3624 |
} |
} |
3625 |
} |
} |
3828 |
SamplesIterator++; |
SamplesIterator++; |
3829 |
return static_cast<gig::Sample*>( (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL ); |
return static_cast<gig::Sample*>( (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL ); |
3830 |
} |
} |
3831 |
|
|
3832 |
|
/** |
3833 |
|
* Returns Sample object of @a index. |
3834 |
|
* |
3835 |
|
* @returns sample object or NULL if index is out of bounds |
3836 |
|
*/ |
3837 |
|
Sample* File::GetSample(uint index) { |
3838 |
|
if (!pSamples) LoadSamples(); |
3839 |
|
if (!pSamples) return NULL; |
3840 |
|
DLS::File::SampleList::iterator it = pSamples->begin(); |
3841 |
|
for (int i = 0; i < index; ++i) { |
3842 |
|
++it; |
3843 |
|
if (it == pSamples->end()) return NULL; |
3844 |
|
} |
3845 |
|
if (it == pSamples->end()) return NULL; |
3846 |
|
return static_cast<gig::Sample*>( *it ); |
3847 |
|
} |
3848 |
|
|
3849 |
/** @brief Add a new sample. |
/** @brief Add a new sample. |
3850 |
* |
* |
4063 |
instr->CopyAssign(orig); |
instr->CopyAssign(orig); |
4064 |
return instr; |
return instr; |
4065 |
} |
} |
4066 |
|
|
4067 |
|
/** @brief Add content of another existing file. |
4068 |
|
* |
4069 |
|
* Duplicates the samples, groups and instruments of the original file |
4070 |
|
* given by @a pFile and adds them to @c this File. In case @c this File is |
4071 |
|
* a new one that you haven't saved before, then you have to call |
4072 |
|
* SetFileName() before calling AddContentOf(), because this method will |
4073 |
|
* automatically save this file during operation, which is required for |
4074 |
|
* writing the sample waveform data by disk streaming. |
4075 |
|
* |
4076 |
|
* @param pFile - original file whose's content shall be copied from |
4077 |
|
*/ |
4078 |
|
void File::AddContentOf(File* pFile) { |
4079 |
|
static int iCallCount = -1; |
4080 |
|
iCallCount++; |
4081 |
|
std::map<Group*,Group*> mGroups; |
4082 |
|
std::map<Sample*,Sample*> mSamples; |
4083 |
|
|
4084 |
|
// clone sample groups |
4085 |
|
for (int i = 0; pFile->GetGroup(i); ++i) { |
4086 |
|
Group* g = AddGroup(); |
4087 |
|
g->Name = |
4088 |
|
"COPY" + ToString(iCallCount) + "_" + pFile->GetGroup(i)->Name; |
4089 |
|
mGroups[pFile->GetGroup(i)] = g; |
4090 |
|
} |
4091 |
|
|
4092 |
|
// clone samples (not waveform data here yet) |
4093 |
|
for (int i = 0; pFile->GetSample(i); ++i) { |
4094 |
|
Sample* s = AddSample(); |
4095 |
|
s->CopyAssignMeta(pFile->GetSample(i)); |
4096 |
|
mGroups[pFile->GetSample(i)->GetGroup()]->AddSample(s); |
4097 |
|
mSamples[pFile->GetSample(i)] = s; |
4098 |
|
} |
4099 |
|
|
4100 |
|
//BUG: For some reason this method only works with this additional |
4101 |
|
// Save() call in between here. |
4102 |
|
// |
4103 |
|
// Important: The correct one of the 2 Save() methods has to be called |
4104 |
|
// here, depending on whether the file is completely new or has been |
4105 |
|
// saved to disk already, otherwise it will result in data corruption. |
4106 |
|
if (pRIFF->IsNew()) |
4107 |
|
Save(GetFileName()); |
4108 |
|
else |
4109 |
|
Save(); |
4110 |
|
|
4111 |
|
// clone instruments |
4112 |
|
// (passing the crosslink table here for the cloned samples) |
4113 |
|
for (int i = 0; pFile->GetInstrument(i); ++i) { |
4114 |
|
Instrument* instr = AddInstrument(); |
4115 |
|
instr->CopyAssign(pFile->GetInstrument(i), &mSamples); |
4116 |
|
} |
4117 |
|
|
4118 |
|
// Mandatory: file needs to be saved to disk at this point, so this |
4119 |
|
// file has the correct size and data layout for writing the samples' |
4120 |
|
// waveform data to disk. |
4121 |
|
Save(); |
4122 |
|
|
4123 |
|
// clone samples' waveform data |
4124 |
|
// (using direct read & write disk streaming) |
4125 |
|
for (int i = 0; pFile->GetSample(i); ++i) { |
4126 |
|
mSamples[pFile->GetSample(i)]->CopyAssignWave(pFile->GetSample(i)); |
4127 |
|
} |
4128 |
|
} |
4129 |
|
|
4130 |
/** @brief Delete an instrument. |
/** @brief Delete an instrument. |
4131 |
* |
* |