--- libgig/trunk/src/gig.cpp 2005/02/23 19:11:07 406 +++ libgig/trunk/src/gig.cpp 2006/07/22 14:22:01 902 @@ -23,13 +23,71 @@ #include "gig.h" +#include "helper.h" + +#include #include -namespace gig { namespace { +/// Initial size of the sample buffer which is used for decompression of +/// compressed sample wave streams - this value should always be bigger than +/// the biggest sample piece expected to be read by the sampler engine, +/// otherwise the buffer size will be raised at runtime and thus the buffer +/// reallocated which is time consuming and unefficient. +#define INITIAL_SAMPLE_BUFFER_SIZE 512000 // 512 kB + +/** (so far) every exponential paramater in the gig format has a basis of 1.000000008813822 */ +#define GIG_EXP_DECODE(x) (pow(1.000000008813822, x)) +#define GIG_EXP_ENCODE(x) (log(x) / log(1.000000008813822)) +#define GIG_PITCH_TRACK_EXTRACT(x) (!(x & 0x01)) +#define GIG_PITCH_TRACK_ENCODE(x) ((x) ? 0x00 : 0x01) +#define GIG_VCF_RESONANCE_CTRL_EXTRACT(x) ((x >> 4) & 0x03) +#define GIG_VCF_RESONANCE_CTRL_ENCODE(x) ((x & 0x03) << 4) +#define GIG_EG_CTR_ATTACK_INFLUENCE_EXTRACT(x) ((x >> 1) & 0x03) +#define GIG_EG_CTR_DECAY_INFLUENCE_EXTRACT(x) ((x >> 3) & 0x03) +#define GIG_EG_CTR_RELEASE_INFLUENCE_EXTRACT(x) ((x >> 5) & 0x03) +#define GIG_EG_CTR_ATTACK_INFLUENCE_ENCODE(x) ((x & 0x03) << 1) +#define GIG_EG_CTR_DECAY_INFLUENCE_ENCODE(x) ((x & 0x03) << 3) +#define GIG_EG_CTR_RELEASE_INFLUENCE_ENCODE(x) ((x & 0x03) << 5) + +namespace gig { + +// *************** 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; + } + } + -// *************** Internal functions for sample decopmression *************** +// *************** Internal functions for sample decompression *************** // * +namespace { + inline int get12lo(const unsigned char* pSrc) { const int x = pSrc[0] | (pSrc[1] & 0x0f) << 8; @@ -53,6 +111,13 @@ return x & 0x800000 ? x - 0x1000000 : x; } + inline void store24(unsigned char* pDst, int x) + { + pDst[0] = x; + pDst[1] = x >> 8; + pDst[2] = x >> 16; + } + void Decompress16(int compressionmode, const unsigned char* params, int srcStep, int dstStep, const unsigned char* pSrc, int16_t* pDst, @@ -92,45 +157,34 @@ } void Decompress24(int compressionmode, const unsigned char* params, - int dstStep, const unsigned char* pSrc, int16_t* pDst, + int dstStep, const unsigned char* pSrc, uint8_t* pDst, unsigned long currentframeoffset, - unsigned long copysamples) + unsigned long copysamples, int truncatedBits) { - // Note: The 24 bits are truncated to 16 bits for now. + int y, dy, ddy, dddy; - // Note: The calculation of the initial value of y is strange - // and not 100% correct. What should the first two parameters - // really be used for? Why are they two? The correct value for - // y seems to lie somewhere between the values of the first - // two parameters. - // - // Strange thing #2: The formula in SKIP_ONE gives values for - // y that are twice as high as they should be. That's why - // COPY_ONE shifts 9 steps instead of 8, and also why y is - // initialized with a sum instead of a mean value. - - int y, dy, ddy; - -#define GET_PARAMS(params) \ - y = (get24(params) + get24((params) + 3)); \ - dy = get24((params) + 6); \ - ddy = get24((params) + 9) +#define GET_PARAMS(params) \ + y = get24(params); \ + dy = y - get24((params) + 3); \ + ddy = get24((params) + 6); \ + dddy = get24((params) + 9) #define SKIP_ONE(x) \ - ddy -= (x); \ - dy -= ddy; \ - y -= dy + dddy -= (x); \ + ddy -= dddy; \ + dy = -dy - ddy; \ + y += dy #define COPY_ONE(x) \ SKIP_ONE(x); \ - *pDst = y >> 9; \ + store24(pDst, y << truncatedBits); \ pDst += dstStep switch (compressionmode) { case 2: // 24 bit uncompressed pSrc += currentframeoffset * 3; while (copysamples) { - *pDst = get24(pSrc) >> 8; + store24(pDst, get24(pSrc) << truncatedBits); pDst += dstStep; pSrc += 3; copysamples--; @@ -206,30 +260,68 @@ unsigned int Sample::Instances = 0; buffer_t Sample::InternalDecompressionBuffer; - Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset) : DLS::Sample((DLS::File*) pFile, waveList, WavePoolOffset) { + /** @brief Constructor. + * + * Load an existing sample or create a new one. A 'wave' list chunk must + * be given to this constructor. In case the given 'wave' list chunk + * contains a 'fmt', 'data' (and optionally a '3gix', 'smpl') chunk, the + * format and sample data will be loaded from there, otherwise default + * values will be used and those chunks will be created when + * File::Save() will be called later on. + * + * @param pFile - pointer to gig::File where this sample is + * located (or will be located) + * @param waveList - pointer to 'wave' list chunk which is (or + * will be) associated with this sample + * @param WavePoolOffset - offset of this sample data from wave pool + * ('wvpl') list chunk + * @param fileNo - number of an extension file where this sample + * is located, 0 otherwise + */ + Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset, unsigned long fileNo) : DLS::Sample((DLS::File*) pFile, waveList, WavePoolOffset) { Instances++; + FileNo = fileNo; - RIFF::Chunk* _3gix = waveList->GetSubChunk(CHUNK_ID_3GIX); - if (!_3gix) throw gig::Exception("Mandatory chunks in list chunk not found."); - SampleGroup = _3gix->ReadInt16(); - - RIFF::Chunk* smpl = waveList->GetSubChunk(CHUNK_ID_SMPL); - if (!smpl) throw gig::Exception("Mandatory chunks in list chunk not found."); - Manufacturer = smpl->ReadInt32(); - Product = smpl->ReadInt32(); - SamplePeriod = smpl->ReadInt32(); - MIDIUnityNote = smpl->ReadInt32(); - FineTune = smpl->ReadInt32(); - smpl->Read(&SMPTEFormat, 1, 4); - SMPTEOffset = smpl->ReadInt32(); - Loops = smpl->ReadInt32(); - smpl->ReadInt32(); // manufByt - LoopID = smpl->ReadInt32(); - smpl->Read(&LoopType, 1, 4); - LoopStart = smpl->ReadInt32(); - LoopEnd = smpl->ReadInt32(); - LoopFraction = smpl->ReadInt32(); - LoopPlayCount = smpl->ReadInt32(); + pCk3gix = waveList->GetSubChunk(CHUNK_ID_3GIX); + if (pCk3gix) { + SampleGroup = pCk3gix->ReadInt16(); + } else { // '3gix' chunk missing + // use default value(s) + SampleGroup = 0; + } + + pCkSmpl = waveList->GetSubChunk(CHUNK_ID_SMPL); + if (pCkSmpl) { + Manufacturer = pCkSmpl->ReadInt32(); + Product = pCkSmpl->ReadInt32(); + SamplePeriod = pCkSmpl->ReadInt32(); + MIDIUnityNote = pCkSmpl->ReadInt32(); + FineTune = pCkSmpl->ReadInt32(); + pCkSmpl->Read(&SMPTEFormat, 1, 4); + SMPTEOffset = pCkSmpl->ReadInt32(); + Loops = pCkSmpl->ReadInt32(); + pCkSmpl->ReadInt32(); // manufByt + LoopID = pCkSmpl->ReadInt32(); + pCkSmpl->Read(&LoopType, 1, 4); + LoopStart = pCkSmpl->ReadInt32(); + LoopEnd = pCkSmpl->ReadInt32(); + LoopFraction = pCkSmpl->ReadInt32(); + LoopPlayCount = pCkSmpl->ReadInt32(); + } else { // 'smpl' chunk missing + // use default values + Manufacturer = 0; + Product = 0; + SamplePeriod = 1 / SamplesPerSecond; + MIDIUnityNote = 64; + FineTune = 0; + SMPTEOffset = 0; + Loops = 0; + LoopID = 0; + LoopStart = 0; + LoopEnd = 0; + LoopFraction = 0; + LoopPlayCount = 0; + } FrameTable = NULL; SamplePos = 0; @@ -239,8 +331,17 @@ if (BitDepth > 24) throw gig::Exception("Only samples up to 24 bit supported"); - Compressed = (waveList->GetSubChunk(CHUNK_ID_EWAV)); + RIFF::Chunk* ewav = waveList->GetSubChunk(CHUNK_ID_EWAV); + Compressed = ewav; + Dithered = false; + TruncatedBits = 0; if (Compressed) { + uint32_t version = ewav->ReadInt32(); + if (version == 3 && BitDepth == 24) { + Dithered = ewav->ReadInt32(); + ewav->SetPos(Channels == 2 ? 84 : 64); + TruncatedBits = ewav->ReadInt32(); + } ScanCompressedSample(); } @@ -249,9 +350,56 @@ InternalDecompressionBuffer.pStart = new unsigned char[INITIAL_SAMPLE_BUFFER_SIZE]; InternalDecompressionBuffer.Size = INITIAL_SAMPLE_BUFFER_SIZE; } - FrameOffset = 0; // just for streaming compressed samples + FrameOffset = 0; // just for streaming compressed samples - LoopSize = LoopEnd - LoopStart; + LoopSize = LoopEnd - LoopStart + 1; + } + + /** + * Apply sample and its settings to the respective RIFF chunks. You have + * to call File::Save() to make changes persistent. + * + * Usually there is absolutely no need to call this method explicitly. + * It will be called automatically when File::Save() was called. + * + * @throws DLS::Exception if FormatTag != WAVE_FORMAT_PCM or no sample data + * was provided yet + * @throws gig::Exception if there is any invalid sample setting + */ + void Sample::UpdateChunks() { + // first update base class's chunks + DLS::Sample::UpdateChunks(); + + // make sure 'smpl' chunk exists + pCkSmpl = pWaveList->GetSubChunk(CHUNK_ID_SMPL); + if (!pCkSmpl) pCkSmpl = pWaveList->AddSubChunk(CHUNK_ID_SMPL, 60); + // update 'smpl' chunk + uint8_t* pData = (uint8_t*) pCkSmpl->LoadChunkData(); + SamplePeriod = 1 / SamplesPerSecond; + memcpy(&pData[0], &Manufacturer, 4); + memcpy(&pData[4], &Product, 4); + memcpy(&pData[8], &SamplePeriod, 4); + memcpy(&pData[12], &MIDIUnityNote, 4); + memcpy(&pData[16], &FineTune, 4); + memcpy(&pData[20], &SMPTEFormat, 4); + memcpy(&pData[24], &SMPTEOffset, 4); + memcpy(&pData[28], &Loops, 4); + + // we skip 'manufByt' for now (4 bytes) + + memcpy(&pData[36], &LoopID, 4); + memcpy(&pData[40], &LoopType, 4); + memcpy(&pData[44], &LoopStart, 4); + memcpy(&pData[48], &LoopEnd, 4); + memcpy(&pData[52], &LoopFraction, 4); + memcpy(&pData[56], &LoopPlayCount, 4); + + // make sure '3gix' chunk exists + pCk3gix = pWaveList->GetSubChunk(CHUNK_ID_3GIX); + if (!pCk3gix) pCk3gix = pWaveList->AddSubChunk(CHUNK_ID_3GIX, 4); + // update '3gix' chunk + pData = (uint8_t*) pCk3gix->LoadChunkData(); + memcpy(&pData[0], &SampleGroup, 2); } /// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). @@ -454,6 +602,41 @@ RAMCache.Size = 0; } + /** @brief Resize sample. + * + * Resizes the sample's wave form data, that is the actual size of + * sample wave data possible to be written for this sample. This call + * will return immediately and just schedule the resize operation. You + * should call File::Save() to actually perform the resize operation(s) + * "physically" to the file. As this can take a while on large files, it + * is recommended to call Resize() first on all samples which have to be + * resized and finally to call File::Save() to perform all those resize + * operations in one rush. + * + * The actual size (in bytes) is dependant to the current FrameSize + * value. You may want to set FrameSize before calling Resize(). + * + * Caution: You cannot directly write (i.e. with Write()) to + * enlarged samples before calling File::Save() as this might exceed the + * current sample's boundary! + * + * Also note: only WAVE_FORMAT_PCM is currently supported, that is + * FormatTag must be WAVE_FORMAT_PCM. Trying to resize samples with + * other formats will fail! + * + * @param iNewSize - new sample wave data size in sample points (must be + * greater than zero) + * @throws DLS::Excecption if FormatTag != WAVE_FORMAT_PCM + * or if \a iNewSize is less than 1 + * @throws gig::Exception if existing sample is compressed + * @see DLS::Sample::GetSize(), DLS::Sample::FrameSize, + * DLS::Sample::FormatTag, File::Save() + */ + void Sample::Resize(int iNewSize) { + if (Compressed) throw gig::Exception("There is no support for modifying compressed samples (yet)"); + DLS::Sample::Resize(iNewSize); + } + /** * Sets the position within the sample (in sample points, not in * bytes). Use this method and Read() if you don't want to load @@ -543,139 +726,146 @@ * @param SampleCount number of sample points to read * @param pPlaybackState will be used to store and reload the playback * state for the next ReadAndLoop() call + * @param pDimRgn dimension region with looping information * @param pExternalDecompressionBuffer (optional) external buffer to use for decompression * @returns number of successfully read sample points * @see CreateDecompressionBuffer() */ - unsigned long Sample::ReadAndLoop(void* pBuffer, unsigned long SampleCount, playback_state_t* pPlaybackState, buffer_t* pExternalDecompressionBuffer) { + unsigned long Sample::ReadAndLoop(void* pBuffer, unsigned long SampleCount, playback_state_t* pPlaybackState, + DimensionRegion* pDimRgn, buffer_t* pExternalDecompressionBuffer) { unsigned long samplestoread = SampleCount, totalreadsamples = 0, readsamples, samplestoloopend; uint8_t* pDst = (uint8_t*) pBuffer; SetPos(pPlaybackState->position); // recover position from the last time - if (this->Loops && GetPos() <= this->LoopEnd) { // honor looping if there are loop points defined + if (pDimRgn->SampleLoops) { // honor looping if there are loop points defined - switch (this->LoopType) { + const DLS::sample_loop_t& loop = pDimRgn->pSampleLoops[0]; + const uint32_t loopEnd = loop.LoopStart + loop.LoopLength; - case loop_type_bidirectional: { //TODO: not tested yet! - do { - // if not endless loop check if max. number of loop cycles have been passed - if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; - - if (!pPlaybackState->reverse) { // forward playback - do { - samplestoloopend = this->LoopEnd - GetPos(); - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); - samplestoread -= readsamples; - totalreadsamples += readsamples; - if (readsamples == samplestoloopend) { - pPlaybackState->reverse = true; - break; - } - } while (samplestoread && readsamples); - } - else { // backward playback + if (GetPos() <= loopEnd) { + switch (loop.LoopType) { - // as we can only read forward from disk, we have to - // determine the end position within the loop first, - // read forward from that 'end' and finally after - // reading, swap all sample frames so it reflects - // backward playback - - unsigned long swapareastart = totalreadsamples; - unsigned long loopoffset = GetPos() - this->LoopStart; - unsigned long samplestoreadinloop = Min(samplestoread, loopoffset); - unsigned long reverseplaybackend = GetPos() - samplestoreadinloop; - - SetPos(reverseplaybackend); - - // read samples for backward playback - do { - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoreadinloop, pExternalDecompressionBuffer); - samplestoreadinloop -= readsamples; - samplestoread -= readsamples; - totalreadsamples += readsamples; - } while (samplestoreadinloop && readsamples); + case loop_type_bidirectional: { //TODO: not tested yet! + do { + // if not endless loop check if max. number of loop cycles have been passed + if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; + + if (!pPlaybackState->reverse) { // forward playback + do { + samplestoloopend = loopEnd - GetPos(); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->reverse = true; + break; + } + } while (samplestoread && readsamples); + } + else { // backward playback - SetPos(reverseplaybackend); // pretend we really read backwards + // as we can only read forward from disk, we have to + // determine the end position within the loop first, + // read forward from that 'end' and finally after + // reading, swap all sample frames so it reflects + // backward playback + + unsigned long swapareastart = totalreadsamples; + unsigned long loopoffset = GetPos() - loop.LoopStart; + unsigned long samplestoreadinloop = Min(samplestoread, loopoffset); + unsigned long reverseplaybackend = GetPos() - samplestoreadinloop; + + SetPos(reverseplaybackend); + + // read samples for backward playback + do { + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoreadinloop, pExternalDecompressionBuffer); + samplestoreadinloop -= readsamples; + samplestoread -= readsamples; + totalreadsamples += readsamples; + } while (samplestoreadinloop && readsamples); + + SetPos(reverseplaybackend); // pretend we really read backwards + + if (reverseplaybackend == loop.LoopStart) { + pPlaybackState->loop_cycles_left--; + pPlaybackState->reverse = false; + } - if (reverseplaybackend == this->LoopStart) { - pPlaybackState->loop_cycles_left--; - pPlaybackState->reverse = false; + // reverse the sample frames for backward playback + SwapMemoryArea(&pDst[swapareastart * this->FrameSize], (totalreadsamples - swapareastart) * this->FrameSize, this->FrameSize); } + } while (samplestoread && readsamples); + break; + } - // reverse the sample frames for backward playback - SwapMemoryArea(&pDst[swapareastart * this->FrameSize], (totalreadsamples - swapareastart) * this->FrameSize, this->FrameSize); - } - } while (samplestoread && readsamples); - break; - } - - case loop_type_backward: { // TODO: not tested yet! - // forward playback (not entered the loop yet) - if (!pPlaybackState->reverse) do { - samplestoloopend = this->LoopEnd - GetPos(); - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); - samplestoread -= readsamples; - totalreadsamples += readsamples; - if (readsamples == samplestoloopend) { - pPlaybackState->reverse = true; - break; - } - } while (samplestoread && readsamples); + case loop_type_backward: { // TODO: not tested yet! + // forward playback (not entered the loop yet) + if (!pPlaybackState->reverse) do { + samplestoloopend = loopEnd - GetPos(); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->reverse = true; + break; + } + } while (samplestoread && readsamples); - if (!samplestoread) break; + if (!samplestoread) break; - // as we can only read forward from disk, we have to - // determine the end position within the loop first, - // read forward from that 'end' and finally after - // reading, swap all sample frames so it reflects - // backward playback - - unsigned long swapareastart = totalreadsamples; - unsigned long loopoffset = GetPos() - this->LoopStart; - unsigned long samplestoreadinloop = (this->LoopPlayCount) ? Min(samplestoread, pPlaybackState->loop_cycles_left * LoopSize - loopoffset) - : samplestoread; - unsigned long reverseplaybackend = this->LoopStart + Abs((loopoffset - samplestoreadinloop) % this->LoopSize); - - SetPos(reverseplaybackend); - - // read samples for backward playback - do { - // if not endless loop check if max. number of loop cycles have been passed - if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; - samplestoloopend = this->LoopEnd - GetPos(); - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoreadinloop, samplestoloopend), pExternalDecompressionBuffer); - samplestoreadinloop -= readsamples; - samplestoread -= readsamples; - totalreadsamples += readsamples; - if (readsamples == samplestoloopend) { - pPlaybackState->loop_cycles_left--; - SetPos(this->LoopStart); - } - } while (samplestoreadinloop && readsamples); + // as we can only read forward from disk, we have to + // determine the end position within the loop first, + // read forward from that 'end' and finally after + // reading, swap all sample frames so it reflects + // backward playback + + unsigned long swapareastart = totalreadsamples; + unsigned long loopoffset = GetPos() - loop.LoopStart; + unsigned long samplestoreadinloop = (this->LoopPlayCount) ? Min(samplestoread, pPlaybackState->loop_cycles_left * loop.LoopLength - loopoffset) + : samplestoread; + unsigned long reverseplaybackend = loop.LoopStart + Abs((loopoffset - samplestoreadinloop) % loop.LoopLength); + + SetPos(reverseplaybackend); + + // read samples for backward playback + do { + // if not endless loop check if max. number of loop cycles have been passed + if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; + samplestoloopend = loopEnd - GetPos(); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoreadinloop, samplestoloopend), pExternalDecompressionBuffer); + samplestoreadinloop -= readsamples; + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->loop_cycles_left--; + SetPos(loop.LoopStart); + } + } while (samplestoreadinloop && readsamples); - SetPos(reverseplaybackend); // pretend we really read backwards + SetPos(reverseplaybackend); // pretend we really read backwards - // reverse the sample frames for backward playback - SwapMemoryArea(&pDst[swapareastart * this->FrameSize], (totalreadsamples - swapareastart) * this->FrameSize, this->FrameSize); - break; - } + // reverse the sample frames for backward playback + SwapMemoryArea(&pDst[swapareastart * this->FrameSize], (totalreadsamples - swapareastart) * this->FrameSize, this->FrameSize); + break; + } - default: case loop_type_normal: { - do { - // if not endless loop check if max. number of loop cycles have been passed - if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; - samplestoloopend = this->LoopEnd - GetPos(); - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); - samplestoread -= readsamples; - totalreadsamples += readsamples; - if (readsamples == samplestoloopend) { - pPlaybackState->loop_cycles_left--; - SetPos(this->LoopStart); - } - } while (samplestoread && readsamples); - break; + default: case loop_type_normal: { + do { + // if not endless loop check if max. number of loop cycles have been passed + if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; + samplestoloopend = loopEnd - GetPos(); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->loop_cycles_left--; + SetPos(loop.LoopStart); + } + } while (samplestoread && readsamples); + break; + } } } } @@ -705,6 +895,10 @@ * have to use an external decompression buffer for EACH * streaming thread to avoid race conditions and crashes! * + * For 16 bit samples, the data in the buffer will be int16_t + * (using native endianness). For 24 bit, the buffer will + * contain three bytes per sample, little-endian. + * * @param pBuffer destination buffer * @param SampleCount number of sample points to read * @param pExternalDecompressionBuffer (optional) external buffer to use for decompression @@ -715,27 +909,7 @@ if (SampleCount == 0) return 0; if (!Compressed) { if (BitDepth == 24) { - // 24 bit sample. For now just truncate to 16 bit. - unsigned char* pSrc = (unsigned char*) ((pExternalDecompressionBuffer) ? pExternalDecompressionBuffer->pStart : this->InternalDecompressionBuffer.pStart); - int16_t* pDst = static_cast(pBuffer); - if (Channels == 2) { // Stereo - unsigned long readBytes = pCkData->Read(pSrc, SampleCount * 6, 1); - pSrc++; - for (unsigned long i = readBytes ; i > 0 ; i -= 3) { - *pDst++ = get16(pSrc); - pSrc += 3; - } - return (pDst - static_cast(pBuffer)) >> 1; - } - else { // Mono - unsigned long readBytes = pCkData->Read(pSrc, SampleCount * 3, 1); - pSrc++; - for (unsigned long i = readBytes ; i > 0 ; i -= 3) { - *pDst++ = get16(pSrc); - pSrc += 3; - } - return pDst - static_cast(pBuffer); - } + return pCkData->Read(pBuffer, SampleCount * FrameSize, 1) / FrameSize; } else { // 16 bit // (pCkData->Read does endian correction) @@ -765,6 +939,7 @@ unsigned char* pSrc = (unsigned char*) pDecompressionBuffer->pStart; int16_t* pDst = static_cast(pBuffer); + uint8_t* pDst24 = static_cast(pBuffer); remainingbytes = pCkData->Read(pSrc, assumedsize, 1); while (remainingsamples && remainingbytes) { @@ -846,14 +1021,16 @@ const unsigned char* const param_r = pSrc; if (mode_r != 2) pSrc += 12; - Decompress24(mode_l, param_l, 2, pSrc, pDst, skipsamples, copysamples); - Decompress24(mode_r, param_r, 2, pSrc + rightChannelOffset, pDst + 1, - skipsamples, copysamples); - pDst += copysamples << 1; + Decompress24(mode_l, param_l, 6, pSrc, pDst24, + skipsamples, copysamples, TruncatedBits); + Decompress24(mode_r, param_r, 6, pSrc + rightChannelOffset, pDst24 + 3, + skipsamples, copysamples, TruncatedBits); + pDst24 += copysamples * 6; } else { // Mono - Decompress24(mode_l, param_l, 1, pSrc, pDst, skipsamples, copysamples); - pDst += copysamples; + Decompress24(mode_l, param_l, 3, pSrc, pDst24, + skipsamples, copysamples, TruncatedBits); + pDst24 += copysamples * 3; } } else { // 16 bit @@ -895,6 +1072,29 @@ } } + /** @brief Write sample wave data. + * + * Writes \a SampleCount number of sample points from the buffer pointed + * by \a pBuffer and increments the position within the sample. Use this + * method to directly write the sample data to disk, i.e. if you don't + * want or cannot load the whole sample data into RAM. + * + * You have to Resize() the sample to the desired size and call + * File::Save() before using Write(). + * + * Note: there is currently no support for writing compressed samples. + * + * @param pBuffer - source buffer + * @param SampleCount - number of sample points to write + * @throws DLS::Exception if current sample size is too small + * @throws gig::Exception if sample is compressed + * @see DLS::LoadSampleData() + */ + unsigned long Sample::Write(void* pBuffer, unsigned long SampleCount) { + if (Compressed) throw gig::Exception("There is no support for writing compressed gig samples (yet)"); + return DLS::Sample::Write(pBuffer, SampleCount); + } + /** * Allocates a decompression buffer for streaming (compressed) samples * with Sample::Read(). If you are using more than one streaming thread @@ -959,174 +1159,564 @@ DimensionRegion::DimensionRegion(RIFF::List* _3ewl) : DLS::Sampler(_3ewl) { Instances++; + pSample = NULL; + memcpy(&Crossfade, &SamplerOptions, 4); if (!pVelocityTables) pVelocityTables = new VelocityTableMap; RIFF::Chunk* _3ewa = _3ewl->GetSubChunk(CHUNK_ID_3EWA); - _3ewa->ReadInt32(); // unknown, always 0x0000008C ? - LFO3Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - EG3Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - _3ewa->ReadInt16(); // unknown - LFO1InternalDepth = _3ewa->ReadUint16(); - _3ewa->ReadInt16(); // unknown - LFO3InternalDepth = _3ewa->ReadInt16(); - _3ewa->ReadInt16(); // unknown - LFO1ControlDepth = _3ewa->ReadUint16(); - _3ewa->ReadInt16(); // unknown - LFO3ControlDepth = _3ewa->ReadInt16(); - EG1Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - EG1Decay1 = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - _3ewa->ReadInt16(); // unknown - EG1Sustain = _3ewa->ReadUint16(); - EG1Release = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - EG1Controller = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); - uint8_t eg1ctrloptions = _3ewa->ReadUint8(); - EG1ControllerInvert = eg1ctrloptions & 0x01; - EG1ControllerAttackInfluence = GIG_EG_CTR_ATTACK_INFLUENCE_EXTRACT(eg1ctrloptions); - EG1ControllerDecayInfluence = GIG_EG_CTR_DECAY_INFLUENCE_EXTRACT(eg1ctrloptions); - EG1ControllerReleaseInfluence = GIG_EG_CTR_RELEASE_INFLUENCE_EXTRACT(eg1ctrloptions); - EG2Controller = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); - uint8_t eg2ctrloptions = _3ewa->ReadUint8(); - EG2ControllerInvert = eg2ctrloptions & 0x01; - EG2ControllerAttackInfluence = GIG_EG_CTR_ATTACK_INFLUENCE_EXTRACT(eg2ctrloptions); - EG2ControllerDecayInfluence = GIG_EG_CTR_DECAY_INFLUENCE_EXTRACT(eg2ctrloptions); - EG2ControllerReleaseInfluence = GIG_EG_CTR_RELEASE_INFLUENCE_EXTRACT(eg2ctrloptions); - LFO1Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - EG2Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - EG2Decay1 = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - _3ewa->ReadInt16(); // unknown - EG2Sustain = _3ewa->ReadUint16(); - EG2Release = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - _3ewa->ReadInt16(); // unknown - LFO2ControlDepth = _3ewa->ReadUint16(); - LFO2Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - _3ewa->ReadInt16(); // unknown - LFO2InternalDepth = _3ewa->ReadUint16(); - int32_t eg1decay2 = _3ewa->ReadInt32(); - EG1Decay2 = (double) GIG_EXP_DECODE(eg1decay2); - EG1InfiniteSustain = (eg1decay2 == 0x7fffffff); - _3ewa->ReadInt16(); // unknown - EG1PreAttack = _3ewa->ReadUint16(); - int32_t eg2decay2 = _3ewa->ReadInt32(); - EG2Decay2 = (double) GIG_EXP_DECODE(eg2decay2); - EG2InfiniteSustain = (eg2decay2 == 0x7fffffff); - _3ewa->ReadInt16(); // unknown - EG2PreAttack = _3ewa->ReadUint16(); - uint8_t velocityresponse = _3ewa->ReadUint8(); - if (velocityresponse < 5) { - VelocityResponseCurve = curve_type_nonlinear; - VelocityResponseDepth = velocityresponse; - } - else if (velocityresponse < 10) { - VelocityResponseCurve = curve_type_linear; - VelocityResponseDepth = velocityresponse - 5; - } - else if (velocityresponse < 15) { - VelocityResponseCurve = curve_type_special; - VelocityResponseDepth = velocityresponse - 10; + if (_3ewa) { // if '3ewa' chunk exists + _3ewa->ReadInt32(); // unknown, always 0x0000008C ? + LFO3Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + EG3Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + _3ewa->ReadInt16(); // unknown + LFO1InternalDepth = _3ewa->ReadUint16(); + _3ewa->ReadInt16(); // unknown + LFO3InternalDepth = _3ewa->ReadInt16(); + _3ewa->ReadInt16(); // unknown + LFO1ControlDepth = _3ewa->ReadUint16(); + _3ewa->ReadInt16(); // unknown + LFO3ControlDepth = _3ewa->ReadInt16(); + EG1Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + EG1Decay1 = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + _3ewa->ReadInt16(); // unknown + EG1Sustain = _3ewa->ReadUint16(); + EG1Release = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + EG1Controller = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); + uint8_t eg1ctrloptions = _3ewa->ReadUint8(); + EG1ControllerInvert = eg1ctrloptions & 0x01; + EG1ControllerAttackInfluence = GIG_EG_CTR_ATTACK_INFLUENCE_EXTRACT(eg1ctrloptions); + EG1ControllerDecayInfluence = GIG_EG_CTR_DECAY_INFLUENCE_EXTRACT(eg1ctrloptions); + EG1ControllerReleaseInfluence = GIG_EG_CTR_RELEASE_INFLUENCE_EXTRACT(eg1ctrloptions); + EG2Controller = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); + uint8_t eg2ctrloptions = _3ewa->ReadUint8(); + EG2ControllerInvert = eg2ctrloptions & 0x01; + EG2ControllerAttackInfluence = GIG_EG_CTR_ATTACK_INFLUENCE_EXTRACT(eg2ctrloptions); + EG2ControllerDecayInfluence = GIG_EG_CTR_DECAY_INFLUENCE_EXTRACT(eg2ctrloptions); + EG2ControllerReleaseInfluence = GIG_EG_CTR_RELEASE_INFLUENCE_EXTRACT(eg2ctrloptions); + LFO1Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + EG2Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + EG2Decay1 = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + _3ewa->ReadInt16(); // unknown + EG2Sustain = _3ewa->ReadUint16(); + EG2Release = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + _3ewa->ReadInt16(); // unknown + LFO2ControlDepth = _3ewa->ReadUint16(); + LFO2Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); + _3ewa->ReadInt16(); // unknown + LFO2InternalDepth = _3ewa->ReadUint16(); + int32_t eg1decay2 = _3ewa->ReadInt32(); + EG1Decay2 = (double) GIG_EXP_DECODE(eg1decay2); + EG1InfiniteSustain = (eg1decay2 == 0x7fffffff); + _3ewa->ReadInt16(); // unknown + EG1PreAttack = _3ewa->ReadUint16(); + int32_t eg2decay2 = _3ewa->ReadInt32(); + EG2Decay2 = (double) GIG_EXP_DECODE(eg2decay2); + EG2InfiniteSustain = (eg2decay2 == 0x7fffffff); + _3ewa->ReadInt16(); // unknown + EG2PreAttack = _3ewa->ReadUint16(); + uint8_t velocityresponse = _3ewa->ReadUint8(); + if (velocityresponse < 5) { + VelocityResponseCurve = curve_type_nonlinear; + VelocityResponseDepth = velocityresponse; + } else if (velocityresponse < 10) { + VelocityResponseCurve = curve_type_linear; + VelocityResponseDepth = velocityresponse - 5; + } else if (velocityresponse < 15) { + VelocityResponseCurve = curve_type_special; + VelocityResponseDepth = velocityresponse - 10; + } else { + VelocityResponseCurve = curve_type_unknown; + VelocityResponseDepth = 0; + } + uint8_t releasevelocityresponse = _3ewa->ReadUint8(); + if (releasevelocityresponse < 5) { + ReleaseVelocityResponseCurve = curve_type_nonlinear; + ReleaseVelocityResponseDepth = releasevelocityresponse; + } else if (releasevelocityresponse < 10) { + ReleaseVelocityResponseCurve = curve_type_linear; + ReleaseVelocityResponseDepth = releasevelocityresponse - 5; + } else if (releasevelocityresponse < 15) { + ReleaseVelocityResponseCurve = curve_type_special; + ReleaseVelocityResponseDepth = releasevelocityresponse - 10; + } else { + ReleaseVelocityResponseCurve = curve_type_unknown; + ReleaseVelocityResponseDepth = 0; + } + VelocityResponseCurveScaling = _3ewa->ReadUint8(); + AttenuationControllerThreshold = _3ewa->ReadInt8(); + _3ewa->ReadInt32(); // unknown + SampleStartOffset = (uint16_t) _3ewa->ReadInt16(); + _3ewa->ReadInt16(); // unknown + uint8_t pitchTrackDimensionBypass = _3ewa->ReadInt8(); + PitchTrack = GIG_PITCH_TRACK_EXTRACT(pitchTrackDimensionBypass); + if (pitchTrackDimensionBypass & 0x10) DimensionBypass = dim_bypass_ctrl_94; + else if (pitchTrackDimensionBypass & 0x20) DimensionBypass = dim_bypass_ctrl_95; + else DimensionBypass = dim_bypass_ctrl_none; + uint8_t pan = _3ewa->ReadUint8(); + Pan = (pan < 64) ? pan : -((int)pan - 63); // signed 7 bit -> signed 8 bit + SelfMask = _3ewa->ReadInt8() & 0x01; + _3ewa->ReadInt8(); // unknown + uint8_t lfo3ctrl = _3ewa->ReadUint8(); + LFO3Controller = static_cast(lfo3ctrl & 0x07); // lower 3 bits + LFO3Sync = lfo3ctrl & 0x20; // bit 5 + InvertAttenuationController = lfo3ctrl & 0x80; // bit 7 + AttenuationController = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); + uint8_t lfo2ctrl = _3ewa->ReadUint8(); + LFO2Controller = static_cast(lfo2ctrl & 0x07); // lower 3 bits + LFO2FlipPhase = lfo2ctrl & 0x80; // bit 7 + LFO2Sync = lfo2ctrl & 0x20; // bit 5 + bool extResonanceCtrl = lfo2ctrl & 0x40; // bit 6 + uint8_t lfo1ctrl = _3ewa->ReadUint8(); + LFO1Controller = static_cast(lfo1ctrl & 0x07); // lower 3 bits + LFO1FlipPhase = lfo1ctrl & 0x80; // bit 7 + LFO1Sync = lfo1ctrl & 0x40; // bit 6 + VCFResonanceController = (extResonanceCtrl) ? static_cast(GIG_VCF_RESONANCE_CTRL_EXTRACT(lfo1ctrl)) + : vcf_res_ctrl_none; + uint16_t eg3depth = _3ewa->ReadUint16(); + EG3Depth = (eg3depth <= 1200) ? eg3depth /* positives */ + : (-1) * (int16_t) ((eg3depth ^ 0xffff) + 1); /* binary complementary for negatives */ + _3ewa->ReadInt16(); // unknown + ChannelOffset = _3ewa->ReadUint8() / 4; + uint8_t regoptions = _3ewa->ReadUint8(); + MSDecode = regoptions & 0x01; // bit 0 + SustainDefeat = regoptions & 0x02; // bit 1 + _3ewa->ReadInt16(); // unknown + VelocityUpperLimit = _3ewa->ReadInt8(); + _3ewa->ReadInt8(); // unknown + _3ewa->ReadInt16(); // unknown + ReleaseTriggerDecay = _3ewa->ReadUint8(); // release trigger decay + _3ewa->ReadInt8(); // unknown + _3ewa->ReadInt8(); // unknown + EG1Hold = _3ewa->ReadUint8() & 0x80; // bit 7 + uint8_t vcfcutoff = _3ewa->ReadUint8(); + VCFEnabled = vcfcutoff & 0x80; // bit 7 + VCFCutoff = vcfcutoff & 0x7f; // lower 7 bits + VCFCutoffController = static_cast(_3ewa->ReadUint8()); + uint8_t vcfvelscale = _3ewa->ReadUint8(); + VCFCutoffControllerInvert = vcfvelscale & 0x80; // bit 7 + VCFVelocityScale = vcfvelscale & 0x7f; // lower 7 bits + _3ewa->ReadInt8(); // unknown + uint8_t vcfresonance = _3ewa->ReadUint8(); + VCFResonance = vcfresonance & 0x7f; // lower 7 bits + VCFResonanceDynamic = !(vcfresonance & 0x80); // bit 7 + uint8_t vcfbreakpoint = _3ewa->ReadUint8(); + VCFKeyboardTracking = vcfbreakpoint & 0x80; // bit 7 + VCFKeyboardTrackingBreakpoint = vcfbreakpoint & 0x7f; // lower 7 bits + uint8_t vcfvelocity = _3ewa->ReadUint8(); + VCFVelocityDynamicRange = vcfvelocity % 5; + VCFVelocityCurve = static_cast(vcfvelocity / 5); + VCFType = static_cast(_3ewa->ReadUint8()); + if (VCFType == vcf_type_lowpass) { + if (lfo3ctrl & 0x40) // bit 6 + VCFType = vcf_type_lowpassturbo; + } + } else { // '3ewa' chunk does not exist yet + // use default values + LFO3Frequency = 1.0; + EG3Attack = 0.0; + LFO1InternalDepth = 0; + LFO3InternalDepth = 0; + LFO1ControlDepth = 0; + LFO3ControlDepth = 0; + EG1Attack = 0.0; + EG1Decay1 = 0.0; + EG1Sustain = 0; + EG1Release = 0.0; + EG1Controller.type = eg1_ctrl_t::type_none; + EG1Controller.controller_number = 0; + EG1ControllerInvert = false; + EG1ControllerAttackInfluence = 0; + EG1ControllerDecayInfluence = 0; + EG1ControllerReleaseInfluence = 0; + EG2Controller.type = eg2_ctrl_t::type_none; + EG2Controller.controller_number = 0; + EG2ControllerInvert = false; + EG2ControllerAttackInfluence = 0; + EG2ControllerDecayInfluence = 0; + EG2ControllerReleaseInfluence = 0; + LFO1Frequency = 1.0; + EG2Attack = 0.0; + EG2Decay1 = 0.0; + EG2Sustain = 0; + EG2Release = 0.0; + LFO2ControlDepth = 0; + LFO2Frequency = 1.0; + LFO2InternalDepth = 0; + EG1Decay2 = 0.0; + EG1InfiniteSustain = false; + EG1PreAttack = 1000; + EG2Decay2 = 0.0; + EG2InfiniteSustain = false; + EG2PreAttack = 1000; + VelocityResponseCurve = curve_type_nonlinear; + VelocityResponseDepth = 3; + ReleaseVelocityResponseCurve = curve_type_nonlinear; + ReleaseVelocityResponseDepth = 3; + VelocityResponseCurveScaling = 32; + AttenuationControllerThreshold = 0; + SampleStartOffset = 0; + PitchTrack = true; + DimensionBypass = dim_bypass_ctrl_none; + Pan = 0; + SelfMask = true; + LFO3Controller = lfo3_ctrl_modwheel; + LFO3Sync = false; + InvertAttenuationController = false; + AttenuationController.type = attenuation_ctrl_t::type_none; + AttenuationController.controller_number = 0; + LFO2Controller = lfo2_ctrl_internal; + LFO2FlipPhase = false; + LFO2Sync = false; + LFO1Controller = lfo1_ctrl_internal; + LFO1FlipPhase = false; + LFO1Sync = false; + VCFResonanceController = vcf_res_ctrl_none; + EG3Depth = 0; + ChannelOffset = 0; + MSDecode = false; + SustainDefeat = false; + VelocityUpperLimit = 0; + ReleaseTriggerDecay = 0; + EG1Hold = false; + VCFEnabled = false; + VCFCutoff = 0; + VCFCutoffController = vcf_cutoff_ctrl_none; + VCFCutoffControllerInvert = false; + VCFVelocityScale = 0; + VCFResonance = 0; + VCFResonanceDynamic = false; + VCFKeyboardTracking = false; + VCFKeyboardTrackingBreakpoint = 0; + VCFVelocityDynamicRange = 0x04; + VCFVelocityCurve = curve_type_linear; + VCFType = vcf_type_lowpass; + } + + pVelocityAttenuationTable = GetVelocityTable(VelocityResponseCurve, + VelocityResponseDepth, + VelocityResponseCurveScaling); + + curve_type_t curveType = ReleaseVelocityResponseCurve; + uint8_t depth = ReleaseVelocityResponseDepth; + + // this models a strange behaviour or bug in GSt: two of the + // velocity response curves for release time are not used even + // if specified, instead another curve is chosen. + if ((curveType == curve_type_nonlinear && depth == 0) || + (curveType == curve_type_special && depth == 4)) { + curveType = curve_type_nonlinear; + depth = 3; + } + pVelocityReleaseTable = GetVelocityTable(curveType, depth, 0); + + curveType = VCFVelocityCurve; + depth = VCFVelocityDynamicRange; + + // even stranger GSt: two of the velocity response curves for + // filter cutoff are not used, instead another special curve + // is chosen. This curve is not used anywhere else. + if ((curveType == curve_type_nonlinear && depth == 0) || + (curveType == curve_type_special && depth == 4)) { + curveType = curve_type_special; + depth = 5; } - else { - VelocityResponseCurve = curve_type_unknown; - VelocityResponseDepth = 0; + pVelocityCutoffTable = GetVelocityTable(curveType, depth, + VCFCutoffController <= vcf_cutoff_ctrl_none2 ? VCFVelocityScale : 0); + + SampleAttenuation = pow(10.0, -Gain / (20.0 * 655360)); + VelocityTable = 0; + } + + /** + * Apply dimension region settings to the respective RIFF chunks. You + * have to call File::Save() to make changes persistent. + * + * Usually there is absolutely no need to call this method explicitly. + * It will be called automatically when File::Save() was called. + */ + void DimensionRegion::UpdateChunks() { + // first update base class's chunk + DLS::Sampler::UpdateChunks(); + + // make sure '3ewa' chunk exists + RIFF::Chunk* _3ewa = pParentList->GetSubChunk(CHUNK_ID_3EWA); + if (!_3ewa) _3ewa = pParentList->AddSubChunk(CHUNK_ID_3EWA, 140); + uint8_t* pData = (uint8_t*) _3ewa->LoadChunkData(); + + // update '3ewa' chunk with DimensionRegion's current settings + + const uint32_t unknown = 0x0000008C; // unknown, always 0x0000008C ? + memcpy(&pData[0], &unknown, 4); + + const int32_t lfo3freq = (int32_t) GIG_EXP_ENCODE(LFO3Frequency); + memcpy(&pData[4], &lfo3freq, 4); + + const int32_t eg3attack = (int32_t) GIG_EXP_ENCODE(EG3Attack); + memcpy(&pData[4], &eg3attack, 4); + + // next 2 bytes unknown + + memcpy(&pData[10], &LFO1InternalDepth, 2); + + // next 2 bytes unknown + + memcpy(&pData[14], &LFO3InternalDepth, 2); + + // next 2 bytes unknown + + memcpy(&pData[18], &LFO1ControlDepth, 2); + + // next 2 bytes unknown + + memcpy(&pData[22], &LFO3ControlDepth, 2); + + const int32_t eg1attack = (int32_t) GIG_EXP_ENCODE(EG1Attack); + memcpy(&pData[24], &eg1attack, 4); + + const int32_t eg1decay1 = (int32_t) GIG_EXP_ENCODE(EG1Decay1); + memcpy(&pData[28], &eg1decay1, 4); + + // next 2 bytes unknown + + memcpy(&pData[34], &EG1Sustain, 2); + + const int32_t eg1release = (int32_t) GIG_EXP_ENCODE(EG1Release); + memcpy(&pData[36], &eg1release, 4); + + const uint8_t eg1ctl = (uint8_t) EncodeLeverageController(EG1Controller); + memcpy(&pData[40], &eg1ctl, 1); + + const uint8_t eg1ctrloptions = + (EG1ControllerInvert) ? 0x01 : 0x00 | + GIG_EG_CTR_ATTACK_INFLUENCE_ENCODE(EG1ControllerAttackInfluence) | + GIG_EG_CTR_DECAY_INFLUENCE_ENCODE(EG1ControllerDecayInfluence) | + GIG_EG_CTR_RELEASE_INFLUENCE_ENCODE(EG1ControllerReleaseInfluence); + memcpy(&pData[41], &eg1ctrloptions, 1); + + const uint8_t eg2ctl = (uint8_t) EncodeLeverageController(EG2Controller); + memcpy(&pData[42], &eg2ctl, 1); + + const uint8_t eg2ctrloptions = + (EG2ControllerInvert) ? 0x01 : 0x00 | + GIG_EG_CTR_ATTACK_INFLUENCE_ENCODE(EG2ControllerAttackInfluence) | + GIG_EG_CTR_DECAY_INFLUENCE_ENCODE(EG2ControllerDecayInfluence) | + GIG_EG_CTR_RELEASE_INFLUENCE_ENCODE(EG2ControllerReleaseInfluence); + memcpy(&pData[43], &eg2ctrloptions, 1); + + const int32_t lfo1freq = (int32_t) GIG_EXP_ENCODE(LFO1Frequency); + memcpy(&pData[44], &lfo1freq, 4); + + const int32_t eg2attack = (int32_t) GIG_EXP_ENCODE(EG2Attack); + memcpy(&pData[48], &eg2attack, 4); + + const int32_t eg2decay1 = (int32_t) GIG_EXP_ENCODE(EG2Decay1); + memcpy(&pData[52], &eg2decay1, 4); + + // next 2 bytes unknown + + memcpy(&pData[58], &EG2Sustain, 2); + + const int32_t eg2release = (int32_t) GIG_EXP_ENCODE(EG2Release); + memcpy(&pData[60], &eg2release, 4); + + // next 2 bytes unknown + + memcpy(&pData[66], &LFO2ControlDepth, 2); + + const int32_t lfo2freq = (int32_t) GIG_EXP_ENCODE(LFO2Frequency); + memcpy(&pData[68], &lfo2freq, 4); + + // next 2 bytes unknown + + memcpy(&pData[72], &LFO2InternalDepth, 2); + + const int32_t eg1decay2 = (int32_t) (EG1InfiniteSustain) ? 0x7fffffff : (int32_t) GIG_EXP_ENCODE(EG1Decay2); + memcpy(&pData[74], &eg1decay2, 4); + + // next 2 bytes unknown + + memcpy(&pData[80], &EG1PreAttack, 2); + + const int32_t eg2decay2 = (int32_t) (EG2InfiniteSustain) ? 0x7fffffff : (int32_t) GIG_EXP_ENCODE(EG2Decay2); + memcpy(&pData[82], &eg2decay2, 4); + + // next 2 bytes unknown + + memcpy(&pData[88], &EG2PreAttack, 2); + + { + if (VelocityResponseDepth > 4) throw Exception("VelocityResponseDepth must be between 0 and 4"); + uint8_t velocityresponse = VelocityResponseDepth; + switch (VelocityResponseCurve) { + case curve_type_nonlinear: + break; + case curve_type_linear: + velocityresponse += 5; + break; + case curve_type_special: + velocityresponse += 10; + break; + case curve_type_unknown: + default: + throw Exception("Could not update DimensionRegion's chunk, unknown VelocityResponseCurve selected"); + } + memcpy(&pData[90], &velocityresponse, 1); } - uint8_t releasevelocityresponse = _3ewa->ReadUint8(); - if (releasevelocityresponse < 5) { - ReleaseVelocityResponseCurve = curve_type_nonlinear; - ReleaseVelocityResponseDepth = releasevelocityresponse; - } - else if (releasevelocityresponse < 10) { - ReleaseVelocityResponseCurve = curve_type_linear; - ReleaseVelocityResponseDepth = releasevelocityresponse - 5; - } - else if (releasevelocityresponse < 15) { - ReleaseVelocityResponseCurve = curve_type_special; - ReleaseVelocityResponseDepth = releasevelocityresponse - 10; + + { + if (ReleaseVelocityResponseDepth > 4) throw Exception("ReleaseVelocityResponseDepth must be between 0 and 4"); + uint8_t releasevelocityresponse = ReleaseVelocityResponseDepth; + switch (ReleaseVelocityResponseCurve) { + case curve_type_nonlinear: + break; + case curve_type_linear: + releasevelocityresponse += 5; + break; + case curve_type_special: + releasevelocityresponse += 10; + break; + case curve_type_unknown: + default: + throw Exception("Could not update DimensionRegion's chunk, unknown ReleaseVelocityResponseCurve selected"); + } + memcpy(&pData[91], &releasevelocityresponse, 1); } - else { - ReleaseVelocityResponseCurve = curve_type_unknown; - ReleaseVelocityResponseDepth = 0; + + memcpy(&pData[92], &VelocityResponseCurveScaling, 1); + + memcpy(&pData[93], &AttenuationControllerThreshold, 1); + + // next 4 bytes unknown + + memcpy(&pData[98], &SampleStartOffset, 2); + + // next 2 bytes unknown + + { + uint8_t pitchTrackDimensionBypass = GIG_PITCH_TRACK_ENCODE(PitchTrack); + switch (DimensionBypass) { + case dim_bypass_ctrl_94: + pitchTrackDimensionBypass |= 0x10; + break; + case dim_bypass_ctrl_95: + pitchTrackDimensionBypass |= 0x20; + break; + case dim_bypass_ctrl_none: + //FIXME: should we set anything here? + break; + default: + throw Exception("Could not update DimensionRegion's chunk, unknown DimensionBypass selected"); + } + memcpy(&pData[102], &pitchTrackDimensionBypass, 1); + } + + const uint8_t pan = (Pan >= 0) ? Pan : ((-Pan) + 63); // signed 8 bit -> signed 7 bit + memcpy(&pData[103], &pan, 1); + + const uint8_t selfmask = (SelfMask) ? 0x01 : 0x00; + memcpy(&pData[104], &selfmask, 1); + + // next byte unknown + + { + uint8_t lfo3ctrl = LFO3Controller & 0x07; // lower 3 bits + if (LFO3Sync) lfo3ctrl |= 0x20; // bit 5 + if (InvertAttenuationController) lfo3ctrl |= 0x80; // bit 7 + if (VCFType == vcf_type_lowpassturbo) lfo3ctrl |= 0x40; // bit 6 + memcpy(&pData[106], &lfo3ctrl, 1); + } + + const uint8_t attenctl = EncodeLeverageController(AttenuationController); + memcpy(&pData[107], &attenctl, 1); + + { + uint8_t lfo2ctrl = LFO2Controller & 0x07; // lower 3 bits + if (LFO2FlipPhase) lfo2ctrl |= 0x80; // bit 7 + if (LFO2Sync) lfo2ctrl |= 0x20; // bit 5 + if (VCFResonanceController != vcf_res_ctrl_none) lfo2ctrl |= 0x40; // bit 6 + memcpy(&pData[108], &lfo2ctrl, 1); } - VelocityResponseCurveScaling = _3ewa->ReadUint8(); - AttenuationControllerThreshold = _3ewa->ReadInt8(); - _3ewa->ReadInt32(); // unknown - SampleStartOffset = (uint16_t) _3ewa->ReadInt16(); - _3ewa->ReadInt16(); // unknown - uint8_t pitchTrackDimensionBypass = _3ewa->ReadInt8(); - PitchTrack = GIG_PITCH_TRACK_EXTRACT(pitchTrackDimensionBypass); - if (pitchTrackDimensionBypass & 0x10) DimensionBypass = dim_bypass_ctrl_94; - else if (pitchTrackDimensionBypass & 0x20) DimensionBypass = dim_bypass_ctrl_95; - else DimensionBypass = dim_bypass_ctrl_none; - uint8_t pan = _3ewa->ReadUint8(); - Pan = (pan < 64) ? pan : -((int)pan - 63); // signed 7 bit -> signed 8 bit - SelfMask = _3ewa->ReadInt8() & 0x01; - _3ewa->ReadInt8(); // unknown - uint8_t lfo3ctrl = _3ewa->ReadUint8(); - LFO3Controller = static_cast(lfo3ctrl & 0x07); // lower 3 bits - LFO3Sync = lfo3ctrl & 0x20; // bit 5 - InvertAttenuationController = lfo3ctrl & 0x80; // bit 7 - AttenuationController = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); - uint8_t lfo2ctrl = _3ewa->ReadUint8(); - LFO2Controller = static_cast(lfo2ctrl & 0x07); // lower 3 bits - LFO2FlipPhase = lfo2ctrl & 0x80; // bit 7 - LFO2Sync = lfo2ctrl & 0x20; // bit 5 - bool extResonanceCtrl = lfo2ctrl & 0x40; // bit 6 - uint8_t lfo1ctrl = _3ewa->ReadUint8(); - LFO1Controller = static_cast(lfo1ctrl & 0x07); // lower 3 bits - LFO1FlipPhase = lfo1ctrl & 0x80; // bit 7 - LFO1Sync = lfo1ctrl & 0x40; // bit 6 - VCFResonanceController = (extResonanceCtrl) ? static_cast(GIG_VCF_RESONANCE_CTRL_EXTRACT(lfo1ctrl)) - : vcf_res_ctrl_none; - uint16_t eg3depth = _3ewa->ReadUint16(); - EG3Depth = (eg3depth <= 1200) ? eg3depth /* positives */ - : (-1) * (int16_t) ((eg3depth ^ 0xffff) + 1); /* binary complementary for negatives */ - _3ewa->ReadInt16(); // unknown - ChannelOffset = _3ewa->ReadUint8() / 4; - uint8_t regoptions = _3ewa->ReadUint8(); - MSDecode = regoptions & 0x01; // bit 0 - SustainDefeat = regoptions & 0x02; // bit 1 - _3ewa->ReadInt16(); // unknown - VelocityUpperLimit = _3ewa->ReadInt8(); - _3ewa->ReadInt8(); // unknown - _3ewa->ReadInt16(); // unknown - ReleaseTriggerDecay = _3ewa->ReadUint8(); // release trigger decay - _3ewa->ReadInt8(); // unknown - _3ewa->ReadInt8(); // unknown - EG1Hold = _3ewa->ReadUint8() & 0x80; // bit 7 - uint8_t vcfcutoff = _3ewa->ReadUint8(); - VCFEnabled = vcfcutoff & 0x80; // bit 7 - VCFCutoff = vcfcutoff & 0x7f; // lower 7 bits - VCFCutoffController = static_cast(_3ewa->ReadUint8()); - VCFVelocityScale = _3ewa->ReadUint8(); - _3ewa->ReadInt8(); // unknown - uint8_t vcfresonance = _3ewa->ReadUint8(); - VCFResonance = vcfresonance & 0x7f; // lower 7 bits - VCFResonanceDynamic = !(vcfresonance & 0x80); // bit 7 - uint8_t vcfbreakpoint = _3ewa->ReadUint8(); - VCFKeyboardTracking = vcfbreakpoint & 0x80; // bit 7 - VCFKeyboardTrackingBreakpoint = vcfbreakpoint & 0x7f; // lower 7 bits - uint8_t vcfvelocity = _3ewa->ReadUint8(); - VCFVelocityDynamicRange = vcfvelocity % 5; - VCFVelocityCurve = static_cast(vcfvelocity / 5); - VCFType = static_cast(_3ewa->ReadUint8()); - if (VCFType == vcf_type_lowpass) { - if (lfo3ctrl & 0x40) // bit 6 - VCFType = vcf_type_lowpassturbo; + + { + uint8_t lfo1ctrl = LFO1Controller & 0x07; // lower 3 bits + if (LFO1FlipPhase) lfo1ctrl |= 0x80; // bit 7 + if (LFO1Sync) lfo1ctrl |= 0x40; // bit 6 + if (VCFResonanceController != vcf_res_ctrl_none) + lfo1ctrl |= GIG_VCF_RESONANCE_CTRL_ENCODE(VCFResonanceController); + memcpy(&pData[109], &lfo1ctrl, 1); + } + + const uint16_t eg3depth = (EG3Depth >= 0) ? EG3Depth + : uint16_t(((-EG3Depth) - 1) ^ 0xffff); /* binary complementary for negatives */ + memcpy(&pData[110], &eg3depth, 1); + + // next 2 bytes unknown + + const uint8_t channeloffset = ChannelOffset * 4; + memcpy(&pData[113], &channeloffset, 1); + + { + uint8_t regoptions = 0; + if (MSDecode) regoptions |= 0x01; // bit 0 + if (SustainDefeat) regoptions |= 0x02; // bit 1 + memcpy(&pData[114], ®options, 1); } - // get the corresponding velocity->volume table from the table map or create & calculate that table if it doesn't exist yet - uint32_t tableKey = (VelocityResponseCurve<<16) | (VelocityResponseDepth<<8) | VelocityResponseCurveScaling; + // next 2 bytes unknown + + memcpy(&pData[117], &VelocityUpperLimit, 1); + + // next 3 bytes unknown + + memcpy(&pData[121], &ReleaseTriggerDecay, 1); + + // next 2 bytes unknown + + const uint8_t eg1hold = (EG1Hold) ? 0x80 : 0x00; // bit 7 + memcpy(&pData[124], &eg1hold, 1); + + const uint8_t vcfcutoff = (VCFEnabled) ? 0x80 : 0x00 | /* bit 7 */ + (VCFCutoff) ? 0x7f : 0x00; /* lower 7 bits */ + memcpy(&pData[125], &vcfcutoff, 1); + + memcpy(&pData[126], &VCFCutoffController, 1); + + const uint8_t vcfvelscale = (VCFCutoffControllerInvert) ? 0x80 : 0x00 | /* bit 7 */ + (VCFVelocityScale) ? 0x7f : 0x00; /* lower 7 bits */ + memcpy(&pData[127], &vcfvelscale, 1); + + // next byte unknown + + const uint8_t vcfresonance = (VCFResonanceDynamic) ? 0x00 : 0x80 | /* bit 7 */ + (VCFResonance) ? 0x7f : 0x00; /* lower 7 bits */ + memcpy(&pData[129], &vcfresonance, 1); + + const uint8_t vcfbreakpoint = (VCFKeyboardTracking) ? 0x80 : 0x00 | /* bit 7 */ + (VCFKeyboardTrackingBreakpoint) ? 0x7f : 0x00; /* lower 7 bits */ + memcpy(&pData[130], &vcfbreakpoint, 1); + + const uint8_t vcfvelocity = VCFVelocityDynamicRange % 5 | + VCFVelocityCurve * 5; + memcpy(&pData[131], &vcfvelocity, 1); + + const uint8_t vcftype = (VCFType == vcf_type_lowpassturbo) ? vcf_type_lowpass : VCFType; + memcpy(&pData[132], &vcftype, 1); + } + + // get the corresponding velocity table from the table map or create & calculate that table if it doesn't exist yet + double* DimensionRegion::GetVelocityTable(curve_type_t curveType, uint8_t depth, uint8_t scaling) + { + double* table; + uint32_t tableKey = (curveType<<16) | (depth<<8) | scaling; if (pVelocityTables->count(tableKey)) { // if key exists - pVelocityAttenuationTable = (*pVelocityTables)[tableKey]; + table = (*pVelocityTables)[tableKey]; } else { - pVelocityAttenuationTable = - CreateVelocityTable(VelocityResponseCurve, - VelocityResponseDepth, - VelocityResponseCurveScaling); - (*pVelocityTables)[tableKey] = pVelocityAttenuationTable; // put the new table into the tables map + table = CreateVelocityTable(curveType, depth, scaling); + (*pVelocityTables)[tableKey] = table; // put the new table into the tables map } - - SampleAttenuation = pow(10.0, -Gain / (20.0 * 655360)); + return table; } leverage_ctrl_t DimensionRegion::DecodeLeverageController(_lev_ctrl_t EncodedController) { @@ -1247,6 +1837,101 @@ return decodedcontroller; } + DimensionRegion::_lev_ctrl_t DimensionRegion::EncodeLeverageController(leverage_ctrl_t DecodedController) { + _lev_ctrl_t encodedcontroller; + switch (DecodedController.type) { + // special controller + case leverage_ctrl_t::type_none: + encodedcontroller = _lev_ctrl_none; + break; + case leverage_ctrl_t::type_velocity: + encodedcontroller = _lev_ctrl_velocity; + break; + case leverage_ctrl_t::type_channelaftertouch: + encodedcontroller = _lev_ctrl_channelaftertouch; + break; + + // ordinary MIDI control change controller + case leverage_ctrl_t::type_controlchange: + switch (DecodedController.controller_number) { + case 1: + encodedcontroller = _lev_ctrl_modwheel; + break; + case 2: + encodedcontroller = _lev_ctrl_breath; + break; + case 4: + encodedcontroller = _lev_ctrl_foot; + break; + case 12: + encodedcontroller = _lev_ctrl_effect1; + break; + case 13: + encodedcontroller = _lev_ctrl_effect2; + break; + case 16: + encodedcontroller = _lev_ctrl_genpurpose1; + break; + case 17: + encodedcontroller = _lev_ctrl_genpurpose2; + break; + case 18: + encodedcontroller = _lev_ctrl_genpurpose3; + break; + case 19: + encodedcontroller = _lev_ctrl_genpurpose4; + break; + case 5: + encodedcontroller = _lev_ctrl_portamentotime; + break; + case 64: + encodedcontroller = _lev_ctrl_sustainpedal; + break; + case 65: + encodedcontroller = _lev_ctrl_portamento; + break; + case 66: + encodedcontroller = _lev_ctrl_sostenutopedal; + break; + case 67: + encodedcontroller = _lev_ctrl_softpedal; + break; + case 80: + encodedcontroller = _lev_ctrl_genpurpose5; + break; + case 81: + encodedcontroller = _lev_ctrl_genpurpose6; + break; + case 82: + encodedcontroller = _lev_ctrl_genpurpose7; + break; + case 83: + encodedcontroller = _lev_ctrl_genpurpose8; + break; + case 91: + encodedcontroller = _lev_ctrl_effect1depth; + break; + case 92: + encodedcontroller = _lev_ctrl_effect2depth; + break; + case 93: + encodedcontroller = _lev_ctrl_effect3depth; + break; + case 94: + encodedcontroller = _lev_ctrl_effect4depth; + break; + case 95: + encodedcontroller = _lev_ctrl_effect5depth; + break; + default: + throw gig::Exception("leverage controller number is not supported by the gig format"); + } + default: + throw gig::Exception("Unknown leverage controller type."); + } + return encodedcontroller; + } + DimensionRegion::~DimensionRegion() { Instances--; if (!Instances) { @@ -1260,6 +1945,7 @@ delete pVelocityTables; pVelocityTables = NULL; } + if (VelocityTable) delete[] VelocityTable; } /** @@ -1277,6 +1963,14 @@ return pVelocityAttenuationTable[MIDIKeyVelocity]; } + double DimensionRegion::GetVelocityRelease(uint8_t MIDIKeyVelocity) { + return pVelocityReleaseTable[MIDIKeyVelocity]; + } + + double DimensionRegion::GetVelocityCutoff(uint8_t MIDIKeyVelocity) { + return pVelocityCutoffTable[MIDIKeyVelocity]; + } + double* DimensionRegion::CreateVelocityTable(curve_type_t curveType, uint8_t depth, uint8_t scaling) { // line-segment approximations of the 15 velocity curves @@ -1310,9 +2004,13 @@ const int spe4[] = { 1, 4, 23, 5, 49, 13, 57, 17, 92, 57, 122, 127, 127, 127 }; + // this is only used by the VCF velocity curve + const int spe5[] = { 1, 2, 30, 5, 60, 19, 77, 70, 83, 85, 88, 106, + 91, 127, 127, 127 }; + const int* const curves[] = { non0, non1, non2, non3, non4, lin0, lin1, lin2, lin3, lin4, - spe0, spe1, spe2, spe3, spe4 }; + spe0, spe1, spe2, spe3, spe4, spe5 }; double* const table = new double[128]; @@ -1364,66 +2062,44 @@ for (int i = 0; i < dimensionBits; i++) { dimension_t dimension = static_cast(_3lnk->ReadUint8()); uint8_t bits = _3lnk->ReadUint8(); + _3lnk->ReadUint8(); // probably the position of the dimension + _3lnk->ReadUint8(); // unknown + uint8_t zones = _3lnk->ReadUint8(); // new for v3: number of zones doesn't have to be == pow(2,bits) if (dimension == dimension_none) { // inactive dimension pDimensionDefinitions[i].dimension = dimension_none; pDimensionDefinitions[i].bits = 0; pDimensionDefinitions[i].zones = 0; pDimensionDefinitions[i].split_type = split_type_bit; - pDimensionDefinitions[i].ranges = NULL; pDimensionDefinitions[i].zone_size = 0; } else { // active dimension pDimensionDefinitions[i].dimension = dimension; pDimensionDefinitions[i].bits = bits; - pDimensionDefinitions[i].zones = 0x01 << bits; // = pow(2,bits) + pDimensionDefinitions[i].zones = zones ? zones : 0x01 << bits; // = pow(2,bits) pDimensionDefinitions[i].split_type = (dimension == dimension_layer || dimension == dimension_samplechannel || - dimension == dimension_releasetrigger) ? split_type_bit - : split_type_normal; - pDimensionDefinitions[i].ranges = NULL; // it's not possible to check velocity dimensions for custom defined ranges at this point + dimension == dimension_releasetrigger || + dimension == dimension_keyboard || + dimension == dimension_roundrobin || + dimension == dimension_random) ? split_type_bit + : split_type_normal; pDimensionDefinitions[i].zone_size = - (pDimensionDefinitions[i].split_type == split_type_normal) ? 128 / pDimensionDefinitions[i].zones + (pDimensionDefinitions[i].split_type == split_type_normal) ? 128.0 / pDimensionDefinitions[i].zones : 0; Dimensions++; // if this is a layer dimension, remember the amount of layers if (dimension == dimension_layer) Layers = pDimensionDefinitions[i].zones; } - _3lnk->SetPos(6, RIFF::stream_curpos); // jump forward to next dimension definition + _3lnk->SetPos(3, RIFF::stream_curpos); // jump forward to next dimension definition } + for (int i = dimensionBits ; i < 8 ; i++) pDimensionDefinitions[i].bits = 0; - // check velocity dimension (if there is one) for custom defined zone ranges - for (uint i = 0; i < Dimensions; i++) { - dimension_def_t* pDimDef = pDimensionDefinitions + i; - if (pDimDef->dimension == dimension_velocity) { - if (pDimensionRegions[0]->VelocityUpperLimit == 0) { - // no custom defined ranges - pDimDef->split_type = split_type_normal; - pDimDef->ranges = NULL; - } - else { // custom defined ranges - pDimDef->split_type = split_type_customvelocity; - pDimDef->ranges = new range_t[pDimDef->zones]; - uint8_t bits[8] = { 0 }; - int previousUpperLimit = -1; - for (int velocityZone = 0; velocityZone < pDimDef->zones; velocityZone++) { - bits[i] = velocityZone; - DimensionRegion* pDimRegion = GetDimensionRegionByBit(bits); - - pDimDef->ranges[velocityZone].low = previousUpperLimit + 1; - pDimDef->ranges[velocityZone].high = pDimRegion->VelocityUpperLimit; - previousUpperLimit = pDimDef->ranges[velocityZone].high; - // fill velocity table - for (int i = pDimDef->ranges[velocityZone].low; i <= pDimDef->ranges[velocityZone].high; i++) { - VelocityTable[i] = velocityZone; - } - } - } - } - } + // if there's a velocity dimension and custom velocity zone splits are used, + // update the VelocityTables in the dimension regions + UpdateVelocityTable(); // jump to start of the wave pool indices (if not already there) - File* file = (File*) GetParent()->GetParent(); if (file->pVersion && file->pVersion->major == 3) _3lnk->SetPos(68); // version 3 has a different 3lnk structure else @@ -1432,10 +2108,77 @@ // load sample references for (uint i = 0; i < DimensionRegions; i++) { uint32_t wavepoolindex = _3lnk->ReadUint32(); - pDimensionRegions[i]->pSample = GetSampleFromWavePool(wavepoolindex); + if (file->pWavePoolTable) pDimensionRegions[i]->pSample = GetSampleFromWavePool(wavepoolindex); + } + } + + // make sure there is at least one dimension region + if (!DimensionRegions) { + RIFF::List* _3prg = rgnList->GetSubList(LIST_TYPE_3PRG); + if (!_3prg) _3prg = rgnList->AddSubList(LIST_TYPE_3PRG); + RIFF::List* _3ewl = _3prg->AddSubList(LIST_TYPE_3EWL); + pDimensionRegions[0] = new DimensionRegion(_3ewl); + DimensionRegions = 1; + } + } + + /** + * Apply Region settings and all its DimensionRegions to the respective + * RIFF chunks. You have to call File::Save() to make changes persistent. + * + * Usually there is absolutely no need to call this method explicitly. + * It will be called automatically when File::Save() was called. + * + * @throws gig::Exception if samples cannot be dereferenced + */ + void Region::UpdateChunks() { + // first update base class's chunks + DLS::Region::UpdateChunks(); + + // update dimension region's chunks + for (int i = 0; i < DimensionRegions; i++) { + pDimensionRegions[i]->UpdateChunks(); + } + + File* pFile = (File*) GetParent()->GetParent(); + const int iMaxDimensions = (pFile->pVersion && pFile->pVersion->major == 3) ? 8 : 5; + const int iMaxDimensionRegions = (pFile->pVersion && pFile->pVersion->major == 3) ? 256 : 32; + + // make sure '3lnk' chunk exists + RIFF::Chunk* _3lnk = pCkRegion->GetSubChunk(CHUNK_ID_3LNK); + if (!_3lnk) { + const int _3lnkChunkSize = (pFile->pVersion && pFile->pVersion->major == 3) ? 1092 : 172; + _3lnk = pCkRegion->AddSubChunk(CHUNK_ID_3LNK, _3lnkChunkSize); + } + + // update dimension definitions in '3lnk' chunk + uint8_t* pData = (uint8_t*) _3lnk->LoadChunkData(); + for (int i = 0; i < iMaxDimensions; i++) { + pData[i * 8] = (uint8_t) pDimensionDefinitions[i].dimension; + pData[i * 8 + 1] = pDimensionDefinitions[i].bits; + // next 2 bytes unknown + pData[i * 8 + 4] = pDimensionDefinitions[i].zones; + // next 3 bytes unknown + } + + // update wave pool table in '3lnk' chunk + const int iWavePoolOffset = (pFile->pVersion && pFile->pVersion->major == 3) ? 68 : 44; + for (uint i = 0; i < iMaxDimensionRegions; i++) { + int iWaveIndex = -1; + if (i < DimensionRegions) { + if (!pFile->pSamples || !pFile->pSamples->size()) throw gig::Exception("Could not update gig::Region, there are no samples"); + File::SampleList::iterator iter = pFile->pSamples->begin(); + File::SampleList::iterator end = pFile->pSamples->end(); + for (int index = 0; iter != end; ++iter, ++index) { + if (*iter == pDimensionRegions[i]->pSample) { + iWaveIndex = index; + break; + } + } + if (iWaveIndex < 0) throw gig::Exception("Could not update gig::Region, could not find DimensionRegion's sample"); } + memcpy(&pData[iWavePoolOffset + i * 4], &iWaveIndex, 4); } - else throw gig::Exception("Mandatory <3lnk> chunk not found."); } void Region::LoadDimensionRegions(RIFF::List* rgn) { @@ -1454,10 +2197,196 @@ } } - Region::~Region() { - for (uint i = 0; i < Dimensions; i++) { - if (pDimensionDefinitions[i].ranges) delete[] pDimensionDefinitions[i].ranges; + void Region::UpdateVelocityTable() { + // get velocity dimension's index + int veldim = -1; + for (int i = 0 ; i < Dimensions ; i++) { + if (pDimensionDefinitions[i].dimension == gig::dimension_velocity) { + veldim = i; + break; + } + } + if (veldim == -1) return; + + int step = 1; + for (int i = 0 ; i < veldim ; i++) step <<= pDimensionDefinitions[i].bits; + int skipveldim = (step << pDimensionDefinitions[veldim].bits) - step; + int end = step * pDimensionDefinitions[veldim].zones; + + // loop through all dimension regions for all dimensions except the velocity dimension + int dim[8] = { 0 }; + for (int i = 0 ; i < DimensionRegions ; i++) { + + if (pDimensionRegions[i]->VelocityUpperLimit) { + // create the velocity table + uint8_t* table = pDimensionRegions[i]->VelocityTable; + if (!table) { + table = new uint8_t[128]; + pDimensionRegions[i]->VelocityTable = table; + } + int tableidx = 0; + int velocityZone = 0; + for (int k = i ; k < end ; k += step) { + DimensionRegion *d = pDimensionRegions[k]; + for (; tableidx <= d->VelocityUpperLimit ; tableidx++) table[tableidx] = velocityZone; + velocityZone++; + } + } else { + if (pDimensionRegions[i]->VelocityTable) { + delete[] pDimensionRegions[i]->VelocityTable; + pDimensionRegions[i]->VelocityTable = 0; + } + } + + int j; + int shift = 0; + for (j = 0 ; j < Dimensions ; j++) { + if (j == veldim) i += skipveldim; // skip velocity dimension + else { + dim[j]++; + if (dim[j] < pDimensionDefinitions[j].zones) break; + else { + // skip unused dimension regions + dim[j] = 0; + i += ((1 << pDimensionDefinitions[j].bits) - + pDimensionDefinitions[j].zones) << shift; + } + } + shift += pDimensionDefinitions[j].bits; + } + if (j == Dimensions) break; + } + } + + /** @brief Einstein would have dreamed of it - create a new dimension. + * + * Creates a new dimension with the dimension definition given by + * \a pDimDef. The appropriate amount of DimensionRegions will be created. + * There is a hard limit of dimensions and total amount of "bits" all + * dimensions can have. This limit is dependant to what gig file format + * version this file refers to. The gig v2 (and lower) format has a + * dimension limit and total amount of bits limit of 5, whereas the gig v3 + * format has a limit of 8. + * + * @param pDimDef - defintion of the new dimension + * @throws gig::Exception if dimension of the same type exists already + * @throws gig::Exception if amount of dimensions or total amount of + * dimension bits limit is violated + */ + void Region::AddDimension(dimension_def_t* pDimDef) { + // check if max. amount of dimensions reached + File* file = (File*) GetParent()->GetParent(); + const int iMaxDimensions = (file->pVersion && file->pVersion->major == 3) ? 8 : 5; + if (Dimensions >= iMaxDimensions) + throw gig::Exception("Could not add new dimension, max. amount of " + ToString(iMaxDimensions) + " dimensions already reached"); + // check if max. amount of dimension bits reached + int iCurrentBits = 0; + for (int i = 0; i < Dimensions; i++) + iCurrentBits += pDimensionDefinitions[i].bits; + if (iCurrentBits >= iMaxDimensions) + throw gig::Exception("Could not add new dimension, max. amount of " + ToString(iMaxDimensions) + " dimension bits already reached"); + const int iNewBits = iCurrentBits + pDimDef->bits; + if (iNewBits > iMaxDimensions) + throw gig::Exception("Could not add new dimension, new dimension would exceed max. amount of " + ToString(iMaxDimensions) + " dimension bits"); + // check if there's already a dimensions of the same type + for (int i = 0; i < Dimensions; i++) + if (pDimensionDefinitions[i].dimension == pDimDef->dimension) + throw gig::Exception("Could not add new dimension, there is already a dimension of the same type"); + + // assign definition of new dimension + pDimensionDefinitions[Dimensions] = *pDimDef; + + // create new dimension region(s) for this new dimension + for (int i = 1 << iCurrentBits; i < 1 << iNewBits; i++) { + //TODO: maybe we should copy existing dimension regions if possible instead of simply creating new ones with default values + RIFF::List* pNewDimRgnListChunk = pCkRegion->AddSubList(LIST_TYPE_3EWL); + pDimensionRegions[i] = new DimensionRegion(pNewDimRgnListChunk); + DimensionRegions++; + } + + Dimensions++; + + // if this is a layer dimension, update 'Layers' attribute + if (pDimDef->dimension == dimension_layer) Layers = pDimDef->zones; + + UpdateVelocityTable(); + } + + /** @brief Delete an existing dimension. + * + * Deletes the dimension given by \a pDimDef and deletes all respective + * dimension regions, that is all dimension regions where the dimension's + * bit(s) part is greater than 0. In case of a 'sustain pedal' dimension + * for example this would delete all dimension regions for the case(s) + * where the sustain pedal is pressed down. + * + * @param pDimDef - dimension to delete + * @throws gig::Exception if given dimension cannot be found + */ + void Region::DeleteDimension(dimension_def_t* pDimDef) { + // get dimension's index + int iDimensionNr = -1; + for (int i = 0; i < Dimensions; i++) { + if (&pDimensionDefinitions[i] == pDimDef) { + iDimensionNr = i; + break; + } + } + if (iDimensionNr < 0) throw gig::Exception("Invalid dimension_def_t pointer"); + + // get amount of bits below the dimension to delete + int iLowerBits = 0; + for (int i = 0; i < iDimensionNr; i++) + iLowerBits += pDimensionDefinitions[i].bits; + + // get amount ot bits above the dimension to delete + int iUpperBits = 0; + for (int i = iDimensionNr + 1; i < Dimensions; i++) + iUpperBits += pDimensionDefinitions[i].bits; + + // delete dimension regions which belong to the given dimension + // (that is where the dimension's bit > 0) + for (int iUpperBit = 0; iUpperBit < 1 << iUpperBits; iUpperBit++) { + for (int iObsoleteBit = 1; iObsoleteBit < 1 << pDimensionDefinitions[iDimensionNr].bits; iObsoleteBit++) { + for (int iLowerBit = 0; iLowerBit < 1 << iLowerBits; iLowerBit++) { + int iToDelete = iUpperBit << (pDimensionDefinitions[iDimensionNr].bits + iLowerBits) | + iObsoleteBit << iLowerBits | + iLowerBit; + delete pDimensionRegions[iToDelete]; + pDimensionRegions[iToDelete] = NULL; + DimensionRegions--; + } + } + } + + // defrag pDimensionRegions array + // (that is remove the NULL spaces within the pDimensionRegions array) + for (int iFrom = 2, iTo = 1; iFrom < 256 && iTo < 256 - 1; iTo++) { + if (!pDimensionRegions[iTo]) { + if (iFrom <= iTo) iFrom = iTo + 1; + while (!pDimensionRegions[iFrom] && iFrom < 256) iFrom++; + if (iFrom < 256 && pDimensionRegions[iFrom]) { + pDimensionRegions[iTo] = pDimensionRegions[iFrom]; + pDimensionRegions[iFrom] = NULL; + } + } + } + + // 'remove' dimension definition + for (int i = iDimensionNr + 1; i < Dimensions; i++) { + pDimensionDefinitions[i - 1] = pDimensionDefinitions[i]; } + pDimensionDefinitions[Dimensions - 1].dimension = dimension_none; + pDimensionDefinitions[Dimensions - 1].bits = 0; + pDimensionDefinitions[Dimensions - 1].zones = 0; + + Dimensions--; + + // if this was a layer dimension, update 'Layers' attribute + if (pDimDef->dimension == dimension_layer) Layers = 1; + } + + Region::~Region() { for (int i = 0; i < 256; i++) { if (pDimensionRegions[i]) delete pDimensionRegions[i]; } @@ -1482,23 +2411,42 @@ * @see Dimensions */ DimensionRegion* Region::GetDimensionRegionByValue(const uint DimValues[8]) { - uint8_t bits[8] = { 0 }; + uint8_t bits; + int veldim = -1; + int velbitpos; + int bitpos = 0; + int dimregidx = 0; for (uint i = 0; i < Dimensions; i++) { - bits[i] = DimValues[i]; - switch (pDimensionDefinitions[i].split_type) { - case split_type_normal: - bits[i] /= pDimensionDefinitions[i].zone_size; - break; - case split_type_customvelocity: - bits[i] = VelocityTable[bits[i]]; - break; - case split_type_bit: // the value is already the sought dimension bit number - const uint8_t limiter_mask = (0xff << pDimensionDefinitions[i].bits) ^ 0xff; - bits[i] = bits[i] & limiter_mask; // just make sure the value don't uses more bits than allowed - break; - } + if (pDimensionDefinitions[i].dimension == dimension_velocity) { + // the velocity dimension must be handled after the other dimensions + veldim = i; + velbitpos = bitpos; + } else { + switch (pDimensionDefinitions[i].split_type) { + case split_type_normal: + bits = uint8_t(DimValues[i] / pDimensionDefinitions[i].zone_size); + break; + case split_type_bit: // the value is already the sought dimension bit number + const uint8_t limiter_mask = (0xff << pDimensionDefinitions[i].bits) ^ 0xff; + bits = DimValues[i] & limiter_mask; // just make sure the value doesn't use more bits than allowed + break; + } + dimregidx |= bits << bitpos; + } + bitpos += pDimensionDefinitions[i].bits; + } + DimensionRegion* dimreg = pDimensionRegions[dimregidx]; + if (veldim != -1) { + // (dimreg is now the dimension region for the lowest velocity) + if (dimreg->VelocityUpperLimit) // custom defined zone ranges + bits = dimreg->VelocityTable[DimValues[veldim]]; + else // normal split type + bits = uint8_t(DimValues[veldim] / pDimensionDefinitions[veldim].zone_size); + + dimregidx |= bits << velbitpos; + dimreg = pDimensionRegions[dimregidx]; } - return GetDimensionRegionByBit(bits); + return dimreg; } /** @@ -1535,13 +2483,16 @@ else return static_cast(pSample = GetSampleFromWavePool(WavePoolTableIndex)); } - Sample* Region::GetSampleFromWavePool(unsigned int WavePoolTableIndex) { + Sample* Region::GetSampleFromWavePool(unsigned int WavePoolTableIndex, progress_t* pProgress) { if ((int32_t)WavePoolTableIndex == -1) return NULL; File* file = (File*) GetParent()->GetParent(); + if (!file->pWavePoolTable) return NULL; unsigned long soughtoffset = file->pWavePoolTable[WavePoolTableIndex]; - Sample* sample = file->GetFirstSample(); + unsigned long soughtfileno = file->pWavePoolTableHi[WavePoolTableIndex]; + Sample* sample = file->GetFirstSample(pProgress); while (sample) { - if (sample->ulWavePoolOffset == soughtoffset) return static_cast(pSample = sample); + if (sample->ulWavePoolOffset == soughtoffset && + sample->FileNo == soughtfileno) return static_cast(pSample = sample); sample = file->GetNextSample(); } return NULL; @@ -1552,10 +2503,9 @@ // *************** Instrument *************** // * - Instrument::Instrument(File* pFile, RIFF::List* insList) : DLS::Instrument((DLS::File*)pFile, insList) { + Instrument::Instrument(File* pFile, RIFF::List* insList, progress_t* pProgress) : DLS::Instrument((DLS::File*)pFile, insList) { // Initialization for (int i = 0; i < 128; i++) RegionKeyTable[i] = NULL; - RegionIndex = -1; // Loading RIFF::List* lart = insList->GetSubList(LIST_TYPE_LART); @@ -1571,39 +2521,77 @@ DimensionKeyRange.low = dimkeystart >> 1; DimensionKeyRange.high = _3ewg->ReadUint8(); } - else throw gig::Exception("Mandatory <3ewg> chunk not found."); } - else throw gig::Exception("Mandatory list chunk not found."); + if (!pRegions) pRegions = new RegionList; RIFF::List* lrgn = insList->GetSubList(LIST_TYPE_LRGN); - if (!lrgn) throw gig::Exception("Mandatory chunks in chunk not found."); - pRegions = new Region*[Regions]; - for (uint i = 0; i < Regions; i++) pRegions[i] = NULL; - RIFF::List* rgn = lrgn->GetFirstSubList(); - unsigned int iRegion = 0; - while (rgn) { - if (rgn->GetListType() == LIST_TYPE_RGN) { - pRegions[iRegion] = new Region(this, rgn); - iRegion++; - } - rgn = lrgn->GetNextSubList(); - } - - // Creating Region Key Table for fast lookup - for (uint iReg = 0; iReg < Regions; iReg++) { - for (int iKey = pRegions[iReg]->KeyRange.low; iKey <= pRegions[iReg]->KeyRange.high; iKey++) { - RegionKeyTable[iKey] = pRegions[iReg]; + if (lrgn) { + RIFF::List* rgn = lrgn->GetFirstSubList(); + while (rgn) { + if (rgn->GetListType() == LIST_TYPE_RGN) { + __notify_progress(pProgress, (float) pRegions->size() / (float) Regions); + pRegions->push_back(new Region(this, rgn)); + } + rgn = lrgn->GetNextSubList(); } + // Creating Region Key Table for fast lookup + UpdateRegionKeyTable(); } + + __notify_progress(pProgress, 1.0f); // notify done } - Instrument::~Instrument() { - for (uint i = 0; i < Regions; i++) { - if (pRegions) { - if (pRegions[i]) delete (pRegions[i]); + void Instrument::UpdateRegionKeyTable() { + RegionList::iterator iter = pRegions->begin(); + RegionList::iterator end = pRegions->end(); + for (; iter != end; ++iter) { + gig::Region* pRegion = static_cast(*iter); + for (int iKey = pRegion->KeyRange.low; iKey <= pRegion->KeyRange.high; iKey++) { + RegionKeyTable[iKey] = pRegion; } } - if (pRegions) delete[] pRegions; + } + + Instrument::~Instrument() { + } + + /** + * Apply Instrument with all its Regions to the respective RIFF chunks. + * You have to call File::Save() to make changes persistent. + * + * Usually there is absolutely no need to call this method explicitly. + * It will be called automatically when File::Save() was called. + * + * @throws gig::Exception if samples cannot be dereferenced + */ + void Instrument::UpdateChunks() { + // first update base classes' chunks + DLS::Instrument::UpdateChunks(); + + // update Regions' chunks + { + RegionList::iterator iter = pRegions->begin(); + RegionList::iterator end = pRegions->end(); + for (; iter != end; ++iter) + (*iter)->UpdateChunks(); + } + + // make sure 'lart' RIFF list chunk exists + RIFF::List* lart = pCkInstrument->GetSubList(LIST_TYPE_LART); + if (!lart) lart = pCkInstrument->AddSubList(LIST_TYPE_LART); + // make sure '3ewg' RIFF chunk exists + RIFF::Chunk* _3ewg = lart->GetSubChunk(CHUNK_ID_3EWG); + if (!_3ewg) _3ewg = lart->AddSubChunk(CHUNK_ID_3EWG, 12); + // update '3ewg' RIFF chunk + uint8_t* pData = (uint8_t*) _3ewg->LoadChunkData(); + memcpy(&pData[0], &EffectSend, 2); + memcpy(&pData[2], &Attenuation, 4); + memcpy(&pData[6], &FineTune, 2); + memcpy(&pData[8], &PitchbendRange, 2); + const uint8_t dimkeystart = (PianoReleaseMode) ? 0x01 : 0x00 | + DimensionKeyRange.low << 1; + memcpy(&pData[10], &dimkeystart, 1); + memcpy(&pData[11], &DimensionKeyRange.high, 1); } /** @@ -1614,8 +2602,9 @@ * there is no Region defined for the given \a Key */ Region* Instrument::GetRegion(unsigned int Key) { - if (!pRegions || Key > 127) return NULL; + if (!pRegions || !pRegions->size() || Key > 127) return NULL; return RegionKeyTable[Key]; + /*for (int i = 0; i < Regions; i++) { if (Key <= pRegions[i]->KeyRange.high && Key >= pRegions[i]->KeyRange.low) return pRegions[i]; @@ -1631,9 +2620,9 @@ * @see GetNextRegion() */ Region* Instrument::GetFirstRegion() { - if (!Regions) return NULL; - RegionIndex = 1; - return pRegions[0]; + if (!pRegions) return NULL; + RegionsIterator = pRegions->begin(); + return static_cast( (RegionsIterator != pRegions->end()) ? *RegionsIterator : NULL ); } /** @@ -1645,8 +2634,30 @@ * @see GetFirstRegion() */ Region* Instrument::GetNextRegion() { - if (RegionIndex < 0 || uint32_t(RegionIndex) >= Regions) return NULL; - return pRegions[RegionIndex++]; + if (!pRegions) return NULL; + RegionsIterator++; + return static_cast( (RegionsIterator != pRegions->end()) ? *RegionsIterator : NULL ); + } + + Region* Instrument::AddRegion() { + // create new Region object (and its RIFF chunks) + RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN); + if (!lrgn) lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN); + RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN); + Region* pNewRegion = new Region(this, rgn); + pRegions->push_back(pNewRegion); + Regions = pRegions->size(); + // update Region key table for fast lookup + UpdateRegionKeyTable(); + // done + return pNewRegion; + } + + void Instrument::DeleteRegion(Region* pRegion) { + if (!pRegions) return; + DLS::Instrument::DeleteRegion((DLS::Region*) pRegion); + // update Region key table for fast lookup + UpdateRegionKeyTable(); } @@ -1654,37 +2665,14 @@ // *************** File *************** // * - File::File(RIFF::File* pRIFF) : DLS::File(pRIFF) { - pSamples = NULL; - pInstruments = NULL; + File::File() : DLS::File() { } - File::~File() { - // free samples - if (pSamples) { - SamplesIterator = pSamples->begin(); - while (SamplesIterator != pSamples->end() ) { - delete (*SamplesIterator); - SamplesIterator++; - } - pSamples->clear(); - delete pSamples; - - } - // free instruments - if (pInstruments) { - InstrumentsIterator = pInstruments->begin(); - while (InstrumentsIterator != pInstruments->end() ) { - delete (*InstrumentsIterator); - InstrumentsIterator++; - } - pInstruments->clear(); - delete pInstruments; - } + File::File(RIFF::File* pRIFF) : DLS::File(pRIFF) { } - Sample* File::GetFirstSample() { - if (!pSamples) LoadSamples(); + Sample* File::GetFirstSample(progress_t* pProgress) { + if (!pSamples) LoadSamples(pProgress); if (!pSamples) return NULL; SamplesIterator = pSamples->begin(); return static_cast( (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL ); @@ -1696,65 +2684,206 @@ return static_cast( (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL ); } + /** @brief Add a new sample. + * + * This will create a new Sample object for the gig file. You have to + * call Save() to make this persistent to the file. + * + * @returns pointer to new Sample object + */ + Sample* File::AddSample() { + if (!pSamples) LoadSamples(); + __ensureMandatoryChunksExist(); + RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL); + // create new Sample object and its respective 'wave' list chunk + RIFF::List* wave = wvpl->AddSubList(LIST_TYPE_WAVE); + Sample* pSample = new Sample(this, wave, 0 /*arbitrary value, we update offsets when we save*/); + pSamples->push_back(pSample); + return pSample; + } + + /** @brief Delete a sample. + * + * This will delete the given Sample object from the gig file. You have + * to call Save() to make this persistent to the file. + * + * @param pSample - sample to delete + * @throws gig::Exception if given sample could not be found + */ + void File::DeleteSample(Sample* pSample) { + if (!pSamples || !pSamples->size()) throw gig::Exception("Could not delete sample as there are no samples"); + SampleList::iterator iter = find(pSamples->begin(), pSamples->end(), (DLS::Sample*) pSample); + if (iter == pSamples->end()) throw gig::Exception("Could not delete sample, could not find given sample"); + pSamples->erase(iter); + delete pSample; + } + void File::LoadSamples() { - RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL); - if (wvpl) { - unsigned long wvplFileOffset = wvpl->GetFilePos(); - RIFF::List* wave = wvpl->GetFirstSubList(); - while (wave) { - if (wave->GetListType() == LIST_TYPE_WAVE) { - if (!pSamples) pSamples = new SampleList; - unsigned long waveFileOffset = wave->GetFilePos(); - pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset)); + LoadSamples(NULL); + } + + void File::LoadSamples(progress_t* pProgress) { + if (!pSamples) pSamples = new SampleList; + + RIFF::File* file = pRIFF; + + // just for progress calculation + int iSampleIndex = 0; + int iTotalSamples = WavePoolCount; + + // check if samples should be loaded from extension files + int lastFileNo = 0; + for (int i = 0 ; i < WavePoolCount ; i++) { + if (pWavePoolTableHi[i] > lastFileNo) lastFileNo = pWavePoolTableHi[i]; + } + String name(pRIFF->GetFileName()); + int nameLen = name.length(); + char suffix[6]; + if (nameLen > 4 && name.substr(nameLen - 4) == ".gig") nameLen -= 4; + + for (int fileNo = 0 ; ; ) { + RIFF::List* wvpl = file->GetSubList(LIST_TYPE_WVPL); + if (wvpl) { + unsigned long wvplFileOffset = wvpl->GetFilePos(); + RIFF::List* wave = wvpl->GetFirstSubList(); + while (wave) { + if (wave->GetListType() == LIST_TYPE_WAVE) { + // notify current progress + const float subprogress = (float) iSampleIndex / (float) iTotalSamples; + __notify_progress(pProgress, subprogress); + + unsigned long waveFileOffset = wave->GetFilePos(); + pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset, fileNo)); + + iSampleIndex++; + } + wave = wvpl->GetNextSubList(); } - wave = wvpl->GetNextSubList(); - } + + if (fileNo == lastFileNo) break; + + // open extension file (*.gx01, *.gx02, ...) + fileNo++; + sprintf(suffix, ".gx%02d", fileNo); + name.replace(nameLen, 5, suffix); + file = new RIFF::File(name); + ExtensionFiles.push_back(file); + } else break; } - else throw gig::Exception("Mandatory chunk not found."); + + __notify_progress(pProgress, 1.0); // notify done } Instrument* File::GetFirstInstrument() { if (!pInstruments) LoadInstruments(); if (!pInstruments) return NULL; InstrumentsIterator = pInstruments->begin(); - return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL; + return static_cast( (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL ); } Instrument* File::GetNextInstrument() { if (!pInstruments) return NULL; InstrumentsIterator++; - return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL; + return static_cast( (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL ); } /** * Returns the instrument with the given index. * + * @param index - number of the sought instrument (0..n) + * @param pProgress - optional: callback function for progress notification * @returns sought instrument or NULL if there's no such instrument */ - Instrument* File::GetInstrument(uint index) { - if (!pInstruments) LoadInstruments(); + Instrument* File::GetInstrument(uint index, progress_t* pProgress) { + if (!pInstruments) { + // TODO: hack - we simply load ALL samples here, it would have been done in the Region constructor anyway (ATM) + + // sample loading subtask + progress_t subprogress; + __divide_progress(pProgress, &subprogress, 3.0f, 0.0f); // randomly schedule 33% for this subtask + __notify_progress(&subprogress, 0.0f); + GetFirstSample(&subprogress); // now force all samples to be loaded + __notify_progress(&subprogress, 1.0f); + + // instrument loading subtask + if (pProgress && pProgress->callback) { + subprogress.__range_min = subprogress.__range_max; + subprogress.__range_max = pProgress->__range_max; // schedule remaining percentage for this subtask + } + __notify_progress(&subprogress, 0.0f); + LoadInstruments(&subprogress); + __notify_progress(&subprogress, 1.0f); + } if (!pInstruments) return NULL; InstrumentsIterator = pInstruments->begin(); for (uint i = 0; InstrumentsIterator != pInstruments->end(); i++) { - if (i == index) return *InstrumentsIterator; + if (i == index) return static_cast( *InstrumentsIterator ); InstrumentsIterator++; } return NULL; } + /** @brief Add a new instrument definition. + * + * This will create a new Instrument object for the gig file. You have + * to call Save() to make this persistent to the file. + * + * @returns pointer to new Instrument object + */ + Instrument* File::AddInstrument() { + if (!pInstruments) LoadInstruments(); + __ensureMandatoryChunksExist(); + RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS); + RIFF::List* lstInstr = lstInstruments->AddSubList(LIST_TYPE_INS); + Instrument* pInstrument = new Instrument(this, lstInstr); + pInstruments->push_back(pInstrument); + return pInstrument; + } + + /** @brief Delete an instrument. + * + * This will delete the given Instrument object from the gig file. You + * have to call Save() to make this persistent to the file. + * + * @param pInstrument - instrument to delete + * @throws gig::Excption if given instrument could not be found + */ + void File::DeleteInstrument(Instrument* pInstrument) { + if (!pInstruments) throw gig::Exception("Could not delete instrument as there are no instruments"); + InstrumentList::iterator iter = find(pInstruments->begin(), pInstruments->end(), (DLS::Instrument*) pInstrument); + if (iter == pInstruments->end()) throw gig::Exception("Could not delete instrument, could not find given instrument"); + pInstruments->erase(iter); + delete pInstrument; + } + void File::LoadInstruments() { + LoadInstruments(NULL); + } + + void File::LoadInstruments(progress_t* pProgress) { + if (!pInstruments) pInstruments = new InstrumentList; RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS); if (lstInstruments) { + int iInstrumentIndex = 0; RIFF::List* lstInstr = lstInstruments->GetFirstSubList(); while (lstInstr) { if (lstInstr->GetListType() == LIST_TYPE_INS) { - if (!pInstruments) pInstruments = new InstrumentList; - pInstruments->push_back(new Instrument(this, lstInstr)); + // notify current progress + const float localProgress = (float) iInstrumentIndex / (float) Instruments; + __notify_progress(pProgress, localProgress); + + // divide local progress into subprogress for loading current Instrument + progress_t subprogress; + __divide_progress(pProgress, &subprogress, Instruments, iInstrumentIndex); + + pInstruments->push_back(new Instrument(this, lstInstr, &subprogress)); + + iInstrumentIndex++; } lstInstr = lstInstruments->GetNextSubList(); } + __notify_progress(pProgress, 1.0); // notify done } - else throw gig::Exception("Mandatory list chunk not found."); } @@ -1769,4 +2898,25 @@ std::cout << "gig::Exception: " << Message << std::endl; } + +// *************** functions *************** +// * + + /** + * Returns the name of this C++ library. This is usually "libgig" of + * course. This call is equivalent to RIFF::libraryName() and + * DLS::libraryName(). + */ + String libraryName() { + return PACKAGE; + } + + /** + * Returns version of this C++ library. This call is equivalent to + * RIFF::libraryVersion() and DLS::libraryVersion(). + */ + String libraryVersion() { + return VERSION; + } + } // namespace gig