--- libgig/trunk/src/DLS.cpp 2007/05/27 13:54:24 1209 +++ libgig/trunk/src/DLS.cpp 2013/01/07 23:23:58 2394 @@ -2,7 +2,7 @@ * * * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2007 by Christian Schoenebeck * + * Copyright (C) 2003-2013 by Christian Schoenebeck * * * * * * This library is free software; you can redistribute it and/or modify * @@ -23,6 +23,7 @@ #include "DLS.h" +#include #include #ifdef __APPLE__ @@ -226,6 +227,15 @@ } } } + + /** + * Not yet implemented in this version, since the .gig format does + * not need to copy DLS articulators and so far nobody used pure + * DLS instrument AFAIK. + */ + void Articulator::CopyAssign(const Articulator* orig) { + //TODO: implement deep copy assignment for this class + } @@ -239,7 +249,7 @@ * @param list - pointer to a list chunk which contains an INFO list chunk */ Info::Info(RIFF::List* list) { - FixedStringLengths = NULL; + pFixedStringLengths = NULL; pResourceListChunk = list; if (list) { RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO); @@ -268,6 +278,21 @@ Info::~Info() { } + /** + * Forces specific Info fields to be of a fixed length when being saved + * to a file. By default the respective RIFF chunk of an Info field + * will have a size analogue to its actual string length. With this + * method however this behavior can be overridden, allowing to force an + * arbitrary fixed size individually for each Info field. + * + * This method is used as a workaround for the gig format, not for DLS. + * + * @param lengths - NULL terminated array of string_length_t elements + */ + void Info::SetFixedStringLengths(const string_length_t* lengths) { + pFixedStringLengths = lengths; + } + /** @brief Load given INFO field. * * Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO @@ -295,10 +320,10 @@ */ void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) { int size = 0; - if (FixedStringLengths) { - for (int i = 0 ; FixedStringLengths[i].length ; i++) { - if (FixedStringLengths[i].chunkId == ChunkID) { - size = FixedStringLengths[i].length; + if (pFixedStringLengths) { + for (int i = 0 ; pFixedStringLengths[i].length ; i++) { + if (pFixedStringLengths[i].chunkId == ChunkID) { + size = pFixedStringLengths[i].length; break; } } @@ -367,6 +392,34 @@ SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String("")); SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String("")); } + + /** + * Make a deep copy of the Info object given by @a orig and assign it to + * this object. + * + * @param orig - original Info object to be copied from + */ + void Info::CopyAssign(const Info* orig) { + Name = orig->Name; + ArchivalLocation = orig->ArchivalLocation; + CreationDate = orig->CreationDate; + Comments = orig->Comments; + Product = orig->Product; + Copyright = orig->Copyright; + Artists = orig->Artists; + Genre = orig->Genre; + Keywords = orig->Keywords; + Engineer = orig->Engineer; + Technician = orig->Technician; + Software = orig->Software; + Medium = orig->Medium; + Source = orig->Source; + SourceForm = orig->SourceForm; + Commissioned = orig->Commissioned; + Subject = orig->Subject; + //FIXME: hmm, is copying this pointer a good idea? + pFixedStringLengths = orig->pFixedStringLengths; + } @@ -441,8 +494,8 @@ UUID uuid; UuidCreate(&uuid); pDLSID->ulData1 = uuid.Data1; - pDLSID->usData1 = uuid.Data2; - pDLSID->usData2 = uuid.Data3; + pDLSID->usData2 = uuid.Data2; + pDLSID->usData3 = uuid.Data3; memcpy(pDLSID->abData, uuid.Data4, 8); #elif defined(__APPLE__) @@ -471,6 +524,16 @@ #endif #endif } + + /** + * Make a deep copy of the Resource object given by @a orig and assign it + * to this object. + * + * @param orig - original Resource object to be copied from + */ + void Resource::CopyAssign(const Resource* orig) { + pInfo->CopyAssign(orig->pInfo); + } // *************** Sampler *************** @@ -487,8 +550,8 @@ SamplerOptions = wsmp->ReadUint32(); SampleLoops = wsmp->ReadUint32(); } else { // 'wsmp' chunk missing - uiHeaderSize = 0; - UnityNote = 64; + uiHeaderSize = 20; + UnityNote = 60; FineTune = 0; // +- 0 cents Gain = 0; // 0 dB SamplerOptions = F_WSMP_NO_COMPRESSION; @@ -512,6 +575,10 @@ if (pSampleLoops) delete[] pSampleLoops; } + void Sampler::SetGain(int32_t gain) { + Gain = gain; + } + /** * Apply all sample player options to the respective RIFF chunk. You * have to call File::Save() to make changes persistent. @@ -519,9 +586,11 @@ void Sampler::UpdateChunks() { // make sure 'wsmp' chunk exists RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP); + int wsmpSize = uiHeaderSize + SampleLoops * 16; if (!wsmp) { - uiHeaderSize = 20; - wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, uiHeaderSize + SampleLoops * 16); + wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, wsmpSize); + } else if (wsmp->GetSize() != wsmpSize) { + wsmp->Resize(wsmpSize); } uint8_t* pData = (uint8_t*) wsmp->LoadChunkData(); // update headers size @@ -578,8 +647,10 @@ // copy old loops array (skipping given loop) for (int i = 0, o = 0; i < SampleLoops; i++) { if (&pSampleLoops[i] == pLoopDef) continue; - if (o == SampleLoops - 1) + if (o == SampleLoops - 1) { + delete[] pNewLoops; throw Exception("Could not delete Sample Loop, because it does not exist"); + } pNewLoops[o] = pSampleLoops[i]; o++; } @@ -588,7 +659,28 @@ pSampleLoops = pNewLoops; SampleLoops--; } - + + /** + * Make a deep copy of the Sampler object given by @a orig and assign it + * to this object. + * + * @param orig - original Sampler object to be copied from + */ + void Sampler::CopyAssign(const Sampler* orig) { + // copy trivial scalars + UnityNote = orig->UnityNote; + FineTune = orig->FineTune; + Gain = orig->Gain; + NoSampleDepthTruncation = orig->NoSampleDepthTruncation; + NoSampleCompression = orig->NoSampleCompression; + SamplerOptions = orig->SamplerOptions; + + // copy sample loops + if (SampleLoops) delete[] pSampleLoops; + pSampleLoops = new sample_loop_t[orig->SampleLoops]; + memcpy(pSampleLoops, orig->pSampleLoops, orig->SampleLoops * sizeof(sample_loop_t)); + SampleLoops = orig->SampleLoops; + } // *************** Sample *************** @@ -913,6 +1005,41 @@ } /** + * Modifies the key range of this Region and makes sure the respective + * chunks are in correct order. + * + * @param Low - lower end of key range + * @param High - upper end of key range + */ + void Region::SetKeyRange(uint16_t Low, uint16_t High) { + KeyRange.low = Low; + KeyRange.high = High; + + // make sure regions are already loaded + Instrument* pInstrument = (Instrument*) GetParent(); + if (!pInstrument->pRegions) pInstrument->LoadRegions(); + if (!pInstrument->pRegions) return; + + // find the r which is the first one to the right of this region + // at its new position + Region* r = NULL; + Region* prev_region = NULL; + for ( + Instrument::RegionList::iterator iter = pInstrument->pRegions->begin(); + iter != pInstrument->pRegions->end(); iter++ + ) { + if ((*iter)->KeyRange.low > this->KeyRange.low) { + r = *iter; + break; + } + prev_region = *iter; + } + + // place this region before r if it's not already there + if (prev_region != this) pInstrument->MoveRegion(this, r); + } + + /** * Apply Region settings to the respective RIFF chunks. You have to * call File::Save() to make changes persistent. * @@ -963,7 +1090,6 @@ } } } - if (index < 0) throw Exception("Could not save Region, could not find Region's sample"); WavePoolTableIndex = index; // update 'wlnk' chunk store16(&pData[0], WaveLinkOptionFlags); @@ -971,7 +1097,38 @@ store32(&pData[4], Channel); store32(&pData[8], WavePoolTableIndex); } - + + /** + * Make a (semi) deep copy of the Region object given by @a orig and assign + * it to this object. + * + * Note that the sample pointer referenced by @a orig is simply copied as + * memory address. Thus the respective sample is shared, not duplicated! + * + * @param orig - original Region object to be copied from + */ + void Region::CopyAssign(const Region* orig) { + // handle base classes + Resource::CopyAssign(orig); + Articulator::CopyAssign(orig); + Sampler::CopyAssign(orig); + // handle actual own attributes of this class + // (the trivial ones) + VelocityRange = orig->VelocityRange; + KeyGroup = orig->KeyGroup; + Layer = orig->Layer; + SelfNonExclusive = orig->SelfNonExclusive; + PhaseMaster = orig->PhaseMaster; + PhaseGroup = orig->PhaseGroup; + MultiChannel = orig->MultiChannel; + Channel = orig->Channel; + WavePoolTableIndex = orig->WavePoolTableIndex; + pSample = orig->pSample; + FormatOptionFlags = orig->FormatOptionFlags; + WaveLinkOptionFlags = orig->WaveLinkOptionFlags; + // handle the last, a bit sensible attribute + SetKeyRange(orig->KeyRange.low, orig->KeyRange.high); + } // *************** Instrument *************** @@ -1122,7 +1279,43 @@ RIFF::List* pParent = pCkInstrument->GetParent(); pParent->DeleteSubChunk(pCkInstrument); } - + + void Instrument::CopyAssignCore(const Instrument* orig) { + // handle base classes + Resource::CopyAssign(orig); + Articulator::CopyAssign(orig); + // handle actual own attributes of this class + // (the trivial ones) + IsDrum = orig->IsDrum; + MIDIBank = orig->MIDIBank; + MIDIBankCoarse = orig->MIDIBankCoarse; + MIDIBankFine = orig->MIDIBankFine; + MIDIProgram = orig->MIDIProgram; + } + + /** + * Make a (semi) deep copy of the Instrument object given by @a orig and assign + * it to this object. + * + * Note that all sample pointers referenced by @a orig are simply copied as + * memory address. Thus the respective samples are shared, not duplicated! + * + * @param orig - original Instrument object to be copied from + */ + void Instrument::CopyAssign(const Instrument* orig) { + CopyAssignCore(orig); + // delete all regions first + while (Regions) DeleteRegion(GetFirstRegion()); + // now recreate and copy regions + { + RegionList::const_iterator it = orig->pRegions->begin(); + for (int i = 0; i < orig->Regions; ++i, ++it) { + Region* dstRgn = AddRegion(); + //NOTE: Region does semi-deep copy ! + dstRgn->CopyAssign(*it); + } + } + } // *************** File *************** @@ -1375,6 +1568,39 @@ } /** + * Returns extension file of given index. Extension files are used + * sometimes to circumvent the 2 GB file size limit of the RIFF format and + * of certain operating systems in general. In this case, instead of just + * using one file, the content is spread among several files with similar + * file name scheme. This is especially used by some GigaStudio sound + * libraries. + * + * @param index - index of extension file + * @returns sought extension file, NULL if index out of bounds + * @see GetFileName() + */ + RIFF::File* File::GetExtensionFile(int index) { + if (index < 0 || index >= ExtensionFiles.size()) return NULL; + std::list::iterator iter = ExtensionFiles.begin(); + for (int i = 0; iter != ExtensionFiles.end(); ++iter, ++i) + if (i == index) return *iter; + return NULL; + } + + /** @brief File name of this DLS file. + * + * This method returns the file name as it was provided when loading + * the respective DLS file. However in case the File object associates + * an empty, that is new DLS file, which was not yet saved to disk, + * this method will return an empty string. + * + * @see GetExtensionFile() + */ + String File::GetFileName() { + return pRIFF->GetFileName(); + } + + /** * Apply all the DLS file's current instruments, samples and settings to * the respective RIFF chunks. You have to call Save() to make changes * persistent.