--- libgig/trunk/src/gig.cpp 2004/10/13 20:05:42 282 +++ libgig/trunk/src/gig.cpp 2005/02/23 19:11:07 406 @@ -2,8 +2,8 @@ * * * libgig - C++ cross-platform Gigasampler format file loader library * * * - * Copyright (C) 2003, 2004 by Christian Schoenebeck * - * * + * Copyright (C) 2003-2005 by Christian Schoenebeck * + * * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -23,14 +23,188 @@ #include "gig.h" -namespace gig { +#include + +namespace gig { namespace { + +// *************** Internal functions for sample decopmression *************** +// * + + inline int get12lo(const unsigned char* pSrc) + { + const int x = pSrc[0] | (pSrc[1] & 0x0f) << 8; + return x & 0x800 ? x - 0x1000 : x; + } + + inline int get12hi(const unsigned char* pSrc) + { + const int x = pSrc[1] >> 4 | pSrc[2] << 4; + return x & 0x800 ? x - 0x1000 : x; + } + + inline int16_t get16(const unsigned char* pSrc) + { + return int16_t(pSrc[0] | pSrc[1] << 8); + } + + inline int get24(const unsigned char* pSrc) + { + const int x = pSrc[0] | pSrc[1] << 8 | pSrc[2] << 16; + return x & 0x800000 ? x - 0x1000000 : x; + } + + void Decompress16(int compressionmode, const unsigned char* params, + int srcStep, int dstStep, + const unsigned char* pSrc, int16_t* pDst, + unsigned long currentframeoffset, + unsigned long copysamples) + { + switch (compressionmode) { + case 0: // 16 bit uncompressed + pSrc += currentframeoffset * srcStep; + while (copysamples) { + *pDst = get16(pSrc); + pDst += dstStep; + pSrc += srcStep; + copysamples--; + } + break; + + case 1: // 16 bit compressed to 8 bit + int y = get16(params); + int dy = get16(params + 2); + while (currentframeoffset) { + dy -= int8_t(*pSrc); + y -= dy; + pSrc += srcStep; + currentframeoffset--; + } + while (copysamples) { + dy -= int8_t(*pSrc); + y -= dy; + *pDst = y; + pDst += dstStep; + pSrc += srcStep; + copysamples--; + } + break; + } + } + + void Decompress24(int compressionmode, const unsigned char* params, + int dstStep, const unsigned char* pSrc, int16_t* pDst, + unsigned long currentframeoffset, + unsigned long copysamples) + { + // Note: The 24 bits are truncated to 16 bits for now. + + // 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 SKIP_ONE(x) \ + ddy -= (x); \ + dy -= ddy; \ + y -= dy + +#define COPY_ONE(x) \ + SKIP_ONE(x); \ + *pDst = y >> 9; \ + pDst += dstStep + + switch (compressionmode) { + case 2: // 24 bit uncompressed + pSrc += currentframeoffset * 3; + while (copysamples) { + *pDst = get24(pSrc) >> 8; + pDst += dstStep; + pSrc += 3; + copysamples--; + } + break; + + case 3: // 24 bit compressed to 16 bit + GET_PARAMS(params); + while (currentframeoffset) { + SKIP_ONE(get16(pSrc)); + pSrc += 2; + currentframeoffset--; + } + while (copysamples) { + COPY_ONE(get16(pSrc)); + pSrc += 2; + copysamples--; + } + break; + + case 4: // 24 bit compressed to 12 bit + GET_PARAMS(params); + while (currentframeoffset > 1) { + SKIP_ONE(get12lo(pSrc)); + SKIP_ONE(get12hi(pSrc)); + pSrc += 3; + currentframeoffset -= 2; + } + if (currentframeoffset) { + SKIP_ONE(get12lo(pSrc)); + currentframeoffset--; + if (copysamples) { + COPY_ONE(get12hi(pSrc)); + pSrc += 3; + copysamples--; + } + } + while (copysamples > 1) { + COPY_ONE(get12lo(pSrc)); + COPY_ONE(get12hi(pSrc)); + pSrc += 3; + copysamples -= 2; + } + if (copysamples) { + COPY_ONE(get12lo(pSrc)); + } + break; + + case 5: // 24 bit compressed to 8 bit + GET_PARAMS(params); + while (currentframeoffset) { + SKIP_ONE(int8_t(*pSrc++)); + currentframeoffset--; + } + while (copysamples) { + COPY_ONE(int8_t(*pSrc++)); + copysamples--; + } + break; + } + } + + const int bytesPerFrame[] = { 4096, 2052, 768, 524, 396, 268 }; + const int bytesPerFrameNoHdr[] = { 4096, 2048, 768, 512, 384, 256 }; + const int headerSize[] = { 0, 4, 0, 12, 12, 12 }; + const int bitsPerSample[] = { 16, 8, 24, 16, 12, 8 }; +} + // *************** Sample *************** // * - unsigned int Sample::Instances = 0; - void* Sample::pDecompressionBuffer = NULL; - unsigned long Sample::DecompressionBufferSize = 0; + 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) { Instances++; @@ -49,7 +223,7 @@ smpl->Read(&SMPTEFormat, 1, 4); SMPTEOffset = smpl->ReadInt32(); Loops = smpl->ReadInt32(); - uint32_t manufByt = smpl->ReadInt32(); + smpl->ReadInt32(); // manufByt LoopID = smpl->ReadInt32(); smpl->Read(&LoopType, 1, 4); LoopStart = smpl->ReadInt32(); @@ -63,13 +237,17 @@ RAMCache.pStart = NULL; RAMCache.NullExtensionSize = 0; + if (BitDepth > 24) throw gig::Exception("Only samples up to 24 bit supported"); + Compressed = (waveList->GetSubChunk(CHUNK_ID_EWAV)); if (Compressed) { ScanCompressedSample(); - if (!pDecompressionBuffer) { - pDecompressionBuffer = new int8_t[INITIAL_SAMPLE_BUFFER_SIZE]; - DecompressionBufferSize = INITIAL_SAMPLE_BUFFER_SIZE; - } + } + + // we use a buffer for decompression and for truncating 24 bit samples to 16 bit + if ((Compressed || BitDepth == 24) && !InternalDecompressionBuffer.Size) { + InternalDecompressionBuffer.pStart = new unsigned char[INITIAL_SAMPLE_BUFFER_SIZE]; + InternalDecompressionBuffer.Size = INITIAL_SAMPLE_BUFFER_SIZE; } FrameOffset = 0; // just for streaming compressed samples @@ -82,30 +260,53 @@ this->SamplesTotal = 0; std::list frameOffsets; + SamplesPerFrame = BitDepth == 24 ? 256 : 2048; + WorstCaseFrameSize = SamplesPerFrame * FrameSize + Channels; // +Channels for compression flag + // Scanning pCkData->SetPos(0); - while (pCkData->GetState() == RIFF::stream_ready) { - frameOffsets.push_back(pCkData->GetPos()); - int16_t compressionmode = pCkData->ReadInt16(); - this->SamplesTotal += 2048; - switch (compressionmode) { - case 1: // left channel compressed - case 256: // right channel compressed - pCkData->SetPos(6148, RIFF::stream_curpos); + if (Channels == 2) { // Stereo + for (int i = 0 ; ; i++) { + // for 24 bit samples every 8:th frame offset is + // stored, to save some memory + if (BitDepth != 24 || (i & 7) == 0) frameOffsets.push_back(pCkData->GetPos()); + + const int mode_l = pCkData->ReadUint8(); + const int mode_r = pCkData->ReadUint8(); + if (mode_l > 5 || mode_r > 5) throw gig::Exception("Unknown compression mode"); + const unsigned long frameSize = bytesPerFrame[mode_l] + bytesPerFrame[mode_r]; + + if (pCkData->RemainingBytes() <= frameSize) { + SamplesInLastFrame = + ((pCkData->RemainingBytes() - headerSize[mode_l] - headerSize[mode_r]) << 3) / + (bitsPerSample[mode_l] + bitsPerSample[mode_r]); + SamplesTotal += SamplesInLastFrame; break; - case 257: // both channels compressed - pCkData->SetPos(4104, RIFF::stream_curpos); + } + SamplesTotal += SamplesPerFrame; + pCkData->SetPos(frameSize, RIFF::stream_curpos); + } + } + else { // Mono + for (int i = 0 ; ; i++) { + if (BitDepth != 24 || (i & 7) == 0) frameOffsets.push_back(pCkData->GetPos()); + + const int mode = pCkData->ReadUint8(); + if (mode > 5) throw gig::Exception("Unknown compression mode"); + const unsigned long frameSize = bytesPerFrame[mode]; + + if (pCkData->RemainingBytes() <= frameSize) { + SamplesInLastFrame = + ((pCkData->RemainingBytes() - headerSize[mode]) << 3) / bitsPerSample[mode]; + SamplesTotal += SamplesInLastFrame; break; - default: // both channels uncompressed - pCkData->SetPos(8192, RIFF::stream_curpos); + } + SamplesTotal += SamplesPerFrame; + pCkData->SetPos(frameSize, RIFF::stream_curpos); } } pCkData->SetPos(0); - //FIXME: only seen compressed samples with 16 bit stereo so far - this->FrameSize = 4; - this->BitDepth = 16; - // Build the frames table (which is used for fast resolving of a frame's chunk offset) if (FrameTable) delete[] FrameTable; FrameTable = new unsigned long[frameOffsets.size()]; @@ -141,9 +342,10 @@ * that will be returned to determine the actual cached samples, but note * that the size is given in bytes! You get the number of actually cached * samples by dividing it by the frame size of the sample: - * + * @code * buffer_t buf = pSample->LoadSampleData(acquired_samples); * long cachedsamples = buf.Size / pSample->FrameSize; + * @endcode * * @param SampleCount - number of sample points to load into RAM * @returns buffer_t structure with start address and size of @@ -189,10 +391,10 @@ * that will be returned to determine the actual cached samples, but note * that the size is given in bytes! You get the number of actually cached * samples by dividing it by the frame size of the sample: - * + * @code * buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(acquired_samples, null_samples); * long cachedsamples = buf.Size / pSample->FrameSize; - * + * @endcode * The method will add \a NullSamplesCount silence samples past the * official buffer end (this won't affect the 'Size' member of the * buffer_t structure, that means 'Size' always reflects the size of the @@ -323,25 +525,29 @@ * for the next time you call this method is stored in \a pPlaybackState. * You have to allocate and initialize the playback_state_t structure by * yourself before you use it to stream a sample: - * - * - * gig::playback_state_t playbackstate;
- * playbackstate.position = 0;
- * playbackstate.reverse = false;
- * playbackstate.loop_cycles_left = pSample->LoopPlayCount;
- *
- * + * @code + * gig::playback_state_t playbackstate; + * playbackstate.position = 0; + * playbackstate.reverse = false; + * playbackstate.loop_cycles_left = pSample->LoopPlayCount; + * @endcode * You don't have to take care of things like if there is actually a loop * defined or if the current read position is located within a loop area. * The method already handles such cases by itself. * + * Caution: If you are using more than one streaming thread, you + * have to use an external decompression buffer for EACH + * streaming thread to avoid race conditions and crashes! + * * @param pBuffer destination buffer * @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 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) { + unsigned long Sample::ReadAndLoop(void* pBuffer, unsigned long SampleCount, playback_state_t* pPlaybackState, buffer_t* pExternalDecompressionBuffer) { unsigned long samplestoread = SampleCount, totalreadsamples = 0, readsamples, samplestoloopend; uint8_t* pDst = (uint8_t*) pBuffer; @@ -359,7 +565,7 @@ if (!pPlaybackState->reverse) { // forward playback do { samplestoloopend = this->LoopEnd - GetPos(); - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend)); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); samplestoread -= readsamples; totalreadsamples += readsamples; if (readsamples == samplestoloopend) { @@ -385,7 +591,7 @@ // read samples for backward playback do { - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoreadinloop); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoreadinloop, pExternalDecompressionBuffer); samplestoreadinloop -= readsamples; samplestoread -= readsamples; totalreadsamples += readsamples; @@ -409,7 +615,7 @@ // forward playback (not entered the loop yet) if (!pPlaybackState->reverse) do { samplestoloopend = this->LoopEnd - GetPos(); - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend)); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); samplestoread -= readsamples; totalreadsamples += readsamples; if (readsamples == samplestoloopend) { @@ -439,7 +645,7 @@ // 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)); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoreadinloop, samplestoloopend), pExternalDecompressionBuffer); samplestoreadinloop -= readsamples; samplestoread -= readsamples; totalreadsamples += readsamples; @@ -461,7 +667,7 @@ // 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)); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend), pExternalDecompressionBuffer); samplestoread -= readsamples; totalreadsamples += readsamples; if (readsamples == samplestoloopend) { @@ -476,7 +682,7 @@ // read on without looping if (samplestoread) do { - readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoread); + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoread, pExternalDecompressionBuffer); samplestoread -= readsamples; totalreadsamples += readsamples; } while (readsamples && samplestoread); @@ -495,172 +701,249 @@ * and SetPos() if you don't want to load the sample into RAM, * thus for disk streaming. * + * Caution: If you are using more than one streaming thread, you + * have to use an external decompression buffer for EACH + * streaming thread to avoid race conditions and crashes! + * * @param pBuffer destination buffer * @param SampleCount number of sample points to read + * @param pExternalDecompressionBuffer (optional) external buffer to use for decompression * @returns number of successfully read sample points - * @see SetPos() + * @see SetPos(), CreateDecompressionBuffer() */ - unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount) { + unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount, buffer_t* pExternalDecompressionBuffer) { if (SampleCount == 0) return 0; - if (!Compressed) return pCkData->Read(pBuffer, SampleCount, FrameSize); //FIXME: channel inversion due to endian correction? - else { //FIXME: no support for mono compressed samples yet, are there any? + 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); + } + } + else { // 16 bit + // (pCkData->Read does endian correction) + return Channels == 2 ? pCkData->Read(pBuffer, SampleCount << 1, 2) >> 1 + : pCkData->Read(pBuffer, SampleCount, 2); + } + } + else { if (this->SamplePos >= this->SamplesTotal) return 0; - //TODO: efficiency: we simply assume here that all frames are compressed, maybe we should test for an average compression rate - // best case needed buffer size (all frames compressed) - unsigned long assumedsize = (SampleCount << 1) + // *2 (16 Bit, stereo, but assume all frames compressed) - (SampleCount >> 10) + // 10 bytes header per 2048 sample points - 8194, // at least one worst case sample frame + //TODO: efficiency: maybe we should test for an average compression rate + unsigned long assumedsize = GuessSize(SampleCount), remainingbytes = 0, // remaining bytes in the local buffer remainingsamples = SampleCount, - copysamples; - int currentframeoffset = this->FrameOffset; // offset in current sample frame since last Read() + copysamples, skipsamples, + currentframeoffset = this->FrameOffset; // offset in current sample frame since last Read() this->FrameOffset = 0; - if (assumedsize > this->DecompressionBufferSize) { - // local buffer reallocation - hope this won't happen - if (this->pDecompressionBuffer) delete[] (int8_t*) this->pDecompressionBuffer; - this->pDecompressionBuffer = new int8_t[assumedsize << 1]; // double of current needed size - this->DecompressionBufferSize = assumedsize; + buffer_t* pDecompressionBuffer = (pExternalDecompressionBuffer) ? pExternalDecompressionBuffer : &InternalDecompressionBuffer; + + // if decompression buffer too small, then reduce amount of samples to read + if (pDecompressionBuffer->Size < assumedsize) { + std::cerr << "gig::Read(): WARNING - decompression buffer size too small!" << std::endl; + SampleCount = WorstCaseMaxSamples(pDecompressionBuffer); + remainingsamples = SampleCount; + assumedsize = GuessSize(SampleCount); } - int16_t compressionmode, left, dleft, right, dright; - int8_t* pSrc = (int8_t*) this->pDecompressionBuffer; - int16_t* pDst = (int16_t*) pBuffer; + unsigned char* pSrc = (unsigned char*) pDecompressionBuffer->pStart; + int16_t* pDst = static_cast(pBuffer); remainingbytes = pCkData->Read(pSrc, assumedsize, 1); - while (remainingsamples) { - - // reload from disk to local buffer if needed - if (remainingbytes < 8194) { - if (pCkData->GetState() != RIFF::stream_ready) { - this->SamplePos = this->SamplesTotal; - return (SampleCount - remainingsamples); + while (remainingsamples && remainingbytes) { + unsigned long framesamples = SamplesPerFrame; + unsigned long framebytes, rightChannelOffset = 0, nextFrameOffset; + + int mode_l = *pSrc++, mode_r = 0; + + if (Channels == 2) { + mode_r = *pSrc++; + framebytes = bytesPerFrame[mode_l] + bytesPerFrame[mode_r] + 2; + rightChannelOffset = bytesPerFrameNoHdr[mode_l]; + nextFrameOffset = rightChannelOffset + bytesPerFrameNoHdr[mode_r]; + if (remainingbytes < framebytes) { // last frame in sample + framesamples = SamplesInLastFrame; + if (mode_l == 4 && (framesamples & 1)) { + rightChannelOffset = ((framesamples + 1) * bitsPerSample[mode_l]) >> 3; + } + else { + rightChannelOffset = (framesamples * bitsPerSample[mode_l]) >> 3; + } + } + } + else { + framebytes = bytesPerFrame[mode_l] + 1; + nextFrameOffset = bytesPerFrameNoHdr[mode_l]; + if (remainingbytes < framebytes) { + framesamples = SamplesInLastFrame; } - assumedsize = remainingsamples; - assumedsize = (assumedsize << 1) + // *2 (16 Bit, stereo, but assume all frames compressed) - (assumedsize >> 10) + // 10 bytes header per 2048 sample points - 8194; // at least one worst case sample frame - pCkData->SetPos(remainingbytes, RIFF::stream_backward); - if (pCkData->RemainingBytes() < assumedsize) assumedsize = pCkData->RemainingBytes(); - remainingbytes = pCkData->Read(this->pDecompressionBuffer, assumedsize, 1); - pSrc = (int8_t*) this->pDecompressionBuffer; } // determine how many samples in this frame to skip and read - if (remainingsamples >= 2048) { - copysamples = 2048 - currentframeoffset; - remainingsamples -= copysamples; + if (currentframeoffset + remainingsamples >= framesamples) { + if (currentframeoffset <= framesamples) { + copysamples = framesamples - currentframeoffset; + skipsamples = currentframeoffset; + } + else { + copysamples = 0; + skipsamples = framesamples; + } } else { + // This frame has enough data for pBuffer, but not + // all of the frame is needed. Set file position + // to start of this frame for next call to Read. copysamples = remainingsamples; - if (currentframeoffset + copysamples > 2048) { - copysamples = 2048 - currentframeoffset; - remainingsamples -= copysamples; - } - else { + skipsamples = currentframeoffset; + pCkData->SetPos(remainingbytes, RIFF::stream_backward); + this->FrameOffset = currentframeoffset + copysamples; + } + remainingsamples -= copysamples; + + if (remainingbytes > framebytes) { + remainingbytes -= framebytes; + if (remainingsamples == 0 && + currentframeoffset + copysamples == framesamples) { + // This frame has enough data for pBuffer, and + // all of the frame is needed. Set file + // position to start of next frame for next + // call to Read. FrameOffset is 0. pCkData->SetPos(remainingbytes, RIFF::stream_backward); - remainingsamples = 0; - this->FrameOffset = currentframeoffset + copysamples; } } + else remainingbytes = 0; - // decompress and copy current frame from local buffer to destination buffer - compressionmode = *(int16_t*)pSrc; pSrc+=2; - switch (compressionmode) { - case 1: // left channel compressed - remainingbytes -= 6150; // (left 8 bit, right 16 bit, +6 byte header) - if (!remainingsamples && copysamples == 2048) - pCkData->SetPos(remainingbytes, RIFF::stream_backward); - - left = *(int16_t*)pSrc; pSrc+=2; - dleft = *(int16_t*)pSrc; pSrc+=2; - while (currentframeoffset) { - dleft -= *pSrc; - left -= dleft; - pSrc+=3; // 8 bit left channel, skip uncompressed right channel (16 bit) - currentframeoffset--; - } - while (copysamples) { - dleft -= *pSrc; pSrc++; - left -= dleft; - *pDst = left; pDst++; - *pDst = *(int16_t*)pSrc; pDst++; pSrc+=2; - copysamples--; - } - break; - case 256: // right channel compressed - remainingbytes -= 6150; // (left 16 bit, right 8 bit, +6 byte header) - if (!remainingsamples && copysamples == 2048) - pCkData->SetPos(remainingbytes, RIFF::stream_backward); - - right = *(int16_t*)pSrc; pSrc+=2; - dright = *(int16_t*)pSrc; pSrc+=2; - if (currentframeoffset) { - pSrc+=2; // skip uncompressed left channel, now we can increment by 3 - while (currentframeoffset) { - dright -= *pSrc; - right -= dright; - pSrc+=3; // 8 bit right channel, skip uncompressed left channel (16 bit) - currentframeoffset--; - } - pSrc-=2; // back aligned to left channel + currentframeoffset -= skipsamples; + + if (copysamples == 0) { + // skip this frame + pSrc += framebytes - Channels; + } + else { + const unsigned char* const param_l = pSrc; + if (BitDepth == 24) { + if (mode_l != 2) pSrc += 12; + + if (Channels == 2) { // Stereo + 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; } - while (copysamples) { - *pDst = *(int16_t*)pSrc; pDst++; pSrc+=2; - dright -= *pSrc; pSrc++; - right -= dright; - *pDst = right; pDst++; - copysamples--; + else { // Mono + Decompress24(mode_l, param_l, 1, pSrc, pDst, skipsamples, copysamples); + pDst += copysamples; } - break; - case 257: // both channels compressed - remainingbytes -= 4106; // (left 8 bit, right 8 bit, +10 byte header) - if (!remainingsamples && copysamples == 2048) - pCkData->SetPos(remainingbytes, RIFF::stream_backward); - - left = *(int16_t*)pSrc; pSrc+=2; - dleft = *(int16_t*)pSrc; pSrc+=2; - right = *(int16_t*)pSrc; pSrc+=2; - dright = *(int16_t*)pSrc; pSrc+=2; - while (currentframeoffset) { - dleft -= *pSrc; pSrc++; - left -= dleft; - dright -= *pSrc; pSrc++; - right -= dright; - currentframeoffset--; + } + else { // 16 bit + if (mode_l) pSrc += 4; + + int step; + if (Channels == 2) { // Stereo + const unsigned char* const param_r = pSrc; + if (mode_r) pSrc += 4; + + step = (2 - mode_l) + (2 - mode_r); + Decompress16(mode_l, param_l, step, 2, pSrc, pDst, skipsamples, copysamples); + Decompress16(mode_r, param_r, step, 2, pSrc + (2 - mode_l), pDst + 1, + skipsamples, copysamples); + pDst += copysamples << 1; } - while (copysamples) { - dleft -= *pSrc; pSrc++; - left -= dleft; - dright -= *pSrc; pSrc++; - right -= dright; - *pDst = left; pDst++; - *pDst = right; pDst++; - copysamples--; + else { // Mono + step = 2 - mode_l; + Decompress16(mode_l, param_l, step, 1, pSrc, pDst, skipsamples, copysamples); + pDst += copysamples; } - break; - default: // both channels uncompressed - remainingbytes -= 8194; // (left 16 bit, right 16 bit, +2 byte header) - if (!remainingsamples && copysamples == 2048) - pCkData->SetPos(remainingbytes, RIFF::stream_backward); - - pSrc += currentframeoffset << 2; - currentframeoffset = 0; - memcpy(pDst, pSrc, copysamples << 2); - pDst += copysamples << 1; - pSrc += copysamples << 2; - break; + } + pSrc += nextFrameOffset; } - } + + // reload from disk to local buffer if needed + if (remainingsamples && remainingbytes < WorstCaseFrameSize && pCkData->GetState() == RIFF::stream_ready) { + assumedsize = GuessSize(remainingsamples); + pCkData->SetPos(remainingbytes, RIFF::stream_backward); + if (pCkData->RemainingBytes() < assumedsize) assumedsize = pCkData->RemainingBytes(); + remainingbytes = pCkData->Read(pDecompressionBuffer->pStart, assumedsize, 1); + pSrc = (unsigned char*) pDecompressionBuffer->pStart; + } + } // while + this->SamplePos += (SampleCount - remainingsamples); if (this->SamplePos > this->SamplesTotal) this->SamplePos = this->SamplesTotal; return (SampleCount - remainingsamples); } } + /** + * Allocates a decompression buffer for streaming (compressed) samples + * with Sample::Read(). If you are using more than one streaming thread + * in your application you HAVE to create a decompression buffer + * for EACH of your streaming threads and provide it with the + * Sample::Read() call in order to avoid race conditions and crashes. + * + * You should free the memory occupied by the allocated buffer(s) once + * you don't need one of your streaming threads anymore by calling + * DestroyDecompressionBuffer(). + * + * @param MaxReadSize - the maximum size (in sample points) you ever + * expect to read with one Read() call + * @returns allocated decompression buffer + * @see DestroyDecompressionBuffer() + */ + buffer_t Sample::CreateDecompressionBuffer(unsigned long MaxReadSize) { + buffer_t result; + const double worstCaseHeaderOverhead = + (256.0 /*frame size*/ + 12.0 /*header*/ + 2.0 /*compression type flag (stereo)*/) / 256.0; + result.Size = (unsigned long) (double(MaxReadSize) * 3.0 /*(24 Bit)*/ * 2.0 /*stereo*/ * worstCaseHeaderOverhead); + result.pStart = new int8_t[result.Size]; + result.NullExtensionSize = 0; + return result; + } + + /** + * Free decompression buffer, previously created with + * CreateDecompressionBuffer(). + * + * @param DecompressionBuffer - previously allocated decompression + * buffer to free + */ + void Sample::DestroyDecompressionBuffer(buffer_t& DecompressionBuffer) { + if (DecompressionBuffer.Size && DecompressionBuffer.pStart) { + delete[] (int8_t*) DecompressionBuffer.pStart; + DecompressionBuffer.pStart = NULL; + DecompressionBuffer.Size = 0; + DecompressionBuffer.NullExtensionSize = 0; + } + } + Sample::~Sample() { Instances--; - if (!Instances && pDecompressionBuffer) delete[] (int8_t*) pDecompressionBuffer; + if (!Instances && InternalDecompressionBuffer.Size) { + delete[] (unsigned char*) InternalDecompressionBuffer.pStart; + InternalDecompressionBuffer.pStart = NULL; + InternalDecompressionBuffer.Size = 0; + } if (FrameTable) delete[] FrameTable; if (RAMCache.pStart) delete[] (int8_t*) RAMCache.pStart; } @@ -781,10 +1064,6 @@ LFO3Controller = static_cast(lfo3ctrl & 0x07); // lower 3 bits LFO3Sync = lfo3ctrl & 0x20; // bit 5 InvertAttenuationController = lfo3ctrl & 0x80; // bit 7 - if (VCFType == vcf_type_lowpass) { - if (lfo3ctrl & 0x40) // bit 6 - VCFType = vcf_type_lowpassturbo; - } AttenuationController = DecodeLeverageController(static_cast<_lev_ctrl_t>(_3ewa->ReadUint8())); uint8_t lfo2ctrl = _3ewa->ReadUint8(); LFO2Controller = static_cast(lfo2ctrl & 0x07); // lower 3 bits @@ -829,6 +1108,10 @@ 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; + } // 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; @@ -836,38 +1119,14 @@ pVelocityAttenuationTable = (*pVelocityTables)[tableKey]; } else { - pVelocityAttenuationTable = new double[128]; - switch (VelocityResponseCurve) { // calculate the new table - case curve_type_nonlinear: - for (int velocity = 0; velocity < 128; velocity++) { - pVelocityAttenuationTable[velocity] = - GIG_VELOCITY_TRANSFORM_NONLINEAR(((double)velocity),((double)VelocityResponseDepth),((double)VelocityResponseCurveScaling)); - if (pVelocityAttenuationTable[velocity] > 1.0) pVelocityAttenuationTable[velocity] = 1.0; - else if (pVelocityAttenuationTable[velocity] < 1e-15) pVelocityAttenuationTable[velocity] = 0.0; - } - break; - case curve_type_linear: - for (int velocity = 0; velocity < 128; velocity++) { - pVelocityAttenuationTable[velocity] = - GIG_VELOCITY_TRANSFORM_LINEAR(((double)velocity),((double)VelocityResponseDepth),((double)VelocityResponseCurveScaling)); - if (pVelocityAttenuationTable[velocity] > 1.0) pVelocityAttenuationTable[velocity] = 1.0; - else if (pVelocityAttenuationTable[velocity] < 1e-15) pVelocityAttenuationTable[velocity] = 0.0; - } - break; - case curve_type_special: - for (int velocity = 0; velocity < 128; velocity++) { - pVelocityAttenuationTable[velocity] = - GIG_VELOCITY_TRANSFORM_SPECIAL(((double)velocity),((double)VelocityResponseDepth),((double)VelocityResponseCurveScaling)); - if (pVelocityAttenuationTable[velocity] > 1.0) pVelocityAttenuationTable[velocity] = 1.0; - else if (pVelocityAttenuationTable[velocity] < 1e-15) pVelocityAttenuationTable[velocity] = 0.0; - } - break; - case curve_type_unknown: - default: - throw gig::Exception("Unknown transform curve type."); - } + pVelocityAttenuationTable = + CreateVelocityTable(VelocityResponseCurve, + VelocityResponseDepth, + VelocityResponseCurveScaling); (*pVelocityTables)[tableKey] = pVelocityAttenuationTable; // put the new table into the tables map } + + SampleAttenuation = pow(10.0, -Gain / (20.0 * 655360)); } leverage_ctrl_t DimensionRegion::DecodeLeverageController(_lev_ctrl_t EncodedController) { @@ -1018,6 +1277,68 @@ return pVelocityAttenuationTable[MIDIKeyVelocity]; } + double* DimensionRegion::CreateVelocityTable(curve_type_t curveType, uint8_t depth, uint8_t scaling) { + + // line-segment approximations of the 15 velocity curves + + // linear + const int lin0[] = { 1, 1, 127, 127 }; + const int lin1[] = { 1, 21, 127, 127 }; + const int lin2[] = { 1, 45, 127, 127 }; + const int lin3[] = { 1, 74, 127, 127 }; + const int lin4[] = { 1, 127, 127, 127 }; + + // non-linear + const int non0[] = { 1, 4, 24, 5, 57, 17, 92, 57, 122, 127, 127, 127 }; + const int non1[] = { 1, 4, 46, 9, 93, 56, 118, 106, 123, 127, + 127, 127 }; + const int non2[] = { 1, 4, 46, 9, 57, 20, 102, 107, 107, 127, + 127, 127 }; + const int non3[] = { 1, 15, 10, 19, 67, 73, 80, 80, 90, 98, 98, 127, + 127, 127 }; + const int non4[] = { 1, 25, 33, 57, 82, 81, 92, 127, 127, 127 }; + + // special + const int spe0[] = { 1, 2, 76, 10, 90, 15, 95, 20, 99, 28, 103, 44, + 113, 127, 127, 127 }; + const int spe1[] = { 1, 2, 27, 5, 67, 18, 89, 29, 95, 35, 107, 67, + 118, 127, 127, 127 }; + const int spe2[] = { 1, 1, 33, 1, 53, 5, 61, 13, 69, 32, 79, 74, + 85, 90, 91, 127, 127, 127 }; + const int spe3[] = { 1, 32, 28, 35, 66, 48, 89, 59, 95, 65, 99, 73, + 117, 127, 127, 127 }; + const int spe4[] = { 1, 4, 23, 5, 49, 13, 57, 17, 92, 57, 122, 127, + 127, 127 }; + + const int* const curves[] = { non0, non1, non2, non3, non4, + lin0, lin1, lin2, lin3, lin4, + spe0, spe1, spe2, spe3, spe4 }; + + double* const table = new double[128]; + + const int* curve = curves[curveType * 5 + depth]; + const int s = scaling == 0 ? 20 : scaling; // 0 or 20 means no scaling + + table[0] = 0; + for (int x = 1 ; x < 128 ; x++) { + + if (x > curve[2]) curve += 2; + double y = curve[1] + (x - curve[0]) * + (double(curve[3] - curve[1]) / (curve[2] - curve[0])); + y = y / 127; + + // Scale up for s > 20, down for s < 20. When + // down-scaling, the curve still ends at 1.0. + if (s < 20 && y >= 0.5) + y = y / ((2 - 40.0 / s) * y + 40.0 / s - 1); + else + y = y * (s / 20.0); + if (y > 1) y = 1; + + table[x] = y; + } + return table; + } // *************** Region *************** @@ -1026,10 +1347,12 @@ Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : DLS::Region((DLS::Instrument*) pInstrument, rgnList) { // Initialization Dimensions = 0; - for (int i = 0; i < 32; i++) { + for (int i = 0; i < 256; i++) { pDimensionRegions[i] = NULL; } Layers = 1; + File* file = (File*) GetParent()->GetParent(); + int dimensionBits = (file->pVersion && file->pVersion->major == 3) ? 8 : 5; // Actual Loading @@ -1038,7 +1361,7 @@ RIFF::Chunk* _3lnk = rgnList->GetSubChunk(CHUNK_ID_3LNK); if (_3lnk) { DimensionRegions = _3lnk->ReadUint32(); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < dimensionBits; i++) { dimension_t dimension = static_cast(_3lnk->ReadUint8()); uint8_t bits = _3lnk->ReadUint8(); if (dimension == dimension_none) { // inactive dimension @@ -1081,11 +1404,11 @@ else { // custom defined ranges pDimDef->split_type = split_type_customvelocity; pDimDef->ranges = new range_t[pDimDef->zones]; - unsigned int bits[5] = {0,0,0,0,0}; + uint8_t bits[8] = { 0 }; int previousUpperLimit = -1; for (int velocityZone = 0; velocityZone < pDimDef->zones; velocityZone++) { bits[i] = velocityZone; - DimensionRegion* pDimRegion = GetDimensionRegionByBit(bits[4],bits[3],bits[2],bits[1],bits[0]); + DimensionRegion* pDimRegion = GetDimensionRegionByBit(bits); pDimDef->ranges[velocityZone].low = previousUpperLimit + 1; pDimDef->ranges[velocityZone].high = pDimRegion->VelocityUpperLimit; @@ -1099,8 +1422,14 @@ } } + // 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 + _3lnk->SetPos(44); + // load sample references - _3lnk->SetPos(44); // jump to start of the wave pool indices (if not already there) for (uint i = 0; i < DimensionRegions; i++) { uint32_t wavepoolindex = _3lnk->ReadUint32(); pDimensionRegions[i]->pSample = GetSampleFromWavePool(wavepoolindex); @@ -1129,7 +1458,7 @@ for (uint i = 0; i < Dimensions; i++) { if (pDimensionDefinitions[i].ranges) delete[] pDimensionDefinitions[i].ranges; } - for (int i = 0; i < 32; i++) { + for (int i = 0; i < 256; i++) { if (pDimensionRegions[i]) delete pDimensionRegions[i]; } } @@ -1147,18 +1476,15 @@ * left channel, 1 for right channel or 0 for layer 0, 1 for layer 1, * etc.). * - * @param Dim4Val MIDI controller value (0-127) for dimension 4 - * @param Dim3Val MIDI controller value (0-127) for dimension 3 - * @param Dim2Val MIDI controller value (0-127) for dimension 2 - * @param Dim1Val MIDI controller value (0-127) for dimension 1 - * @param Dim0Val MIDI controller value (0-127) for dimension 0 + * @param DimValues MIDI controller values (0-127) for dimension 0 to 7 * @returns adress to the DimensionRegion for the given situation * @see pDimensionDefinitions * @see Dimensions */ - DimensionRegion* Region::GetDimensionRegionByValue(uint Dim4Val, uint Dim3Val, uint Dim2Val, uint Dim1Val, uint Dim0Val) { - uint8_t bits[5] = {Dim0Val,Dim1Val,Dim2Val,Dim3Val,Dim4Val}; + DimensionRegion* Region::GetDimensionRegionByValue(const uint DimValues[8]) { + uint8_t bits[8] = { 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; @@ -1172,7 +1498,7 @@ break; } } - return GetDimensionRegionByBit(bits[4],bits[3],bits[2],bits[1],bits[0]); + return GetDimensionRegionByBit(bits); } /** @@ -1180,20 +1506,19 @@ * numbers (zone index). You usually use GetDimensionRegionByValue * instead of calling this method directly! * - * @param Dim4Bit Bit number for dimension 4 - * @param Dim3Bit Bit number for dimension 3 - * @param Dim2Bit Bit number for dimension 2 - * @param Dim1Bit Bit number for dimension 1 - * @param Dim0Bit Bit number for dimension 0 + * @param DimBits Bit numbers for dimension 0 to 7 * @returns adress to the DimensionRegion for the given dimension * bit numbers * @see GetDimensionRegionByValue() */ - DimensionRegion* Region::GetDimensionRegionByBit(uint8_t Dim4Bit, uint8_t Dim3Bit, uint8_t Dim2Bit, uint8_t Dim1Bit, uint8_t Dim0Bit) { - return *(pDimensionRegions + ((((((((Dim4Bit << pDimensionDefinitions[3].bits) | Dim3Bit) - << pDimensionDefinitions[2].bits) | Dim2Bit) - << pDimensionDefinitions[1].bits) | Dim1Bit) - << pDimensionDefinitions[0].bits) | Dim0Bit) ); + DimensionRegion* Region::GetDimensionRegionByBit(const uint8_t DimBits[8]) { + return pDimensionRegions[((((((DimBits[7] << pDimensionDefinitions[6].bits | DimBits[6]) + << pDimensionDefinitions[5].bits | DimBits[5]) + << pDimensionDefinitions[4].bits | DimBits[4]) + << pDimensionDefinitions[3].bits | DimBits[3]) + << pDimensionDefinitions[2].bits | DimBits[2]) + << pDimensionDefinitions[1].bits | DimBits[1]) + << pDimensionDefinitions[0].bits | DimBits[0]]; } /** @@ -1211,6 +1536,7 @@ } Sample* Region::GetSampleFromWavePool(unsigned int WavePoolTableIndex) { + if ((int32_t)WavePoolTableIndex == -1) return NULL; File* file = (File*) GetParent()->GetParent(); unsigned long soughtoffset = file->pWavePoolTable[WavePoolTableIndex]; Sample* sample = file->GetFirstSample(); @@ -1252,6 +1578,7 @@ 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) { @@ -1275,8 +1602,8 @@ if (pRegions) { if (pRegions[i]) delete (pRegions[i]); } - delete[] pRegions; } + if (pRegions) delete[] pRegions; } /** @@ -1318,7 +1645,7 @@ * @see GetFirstRegion() */ Region* Instrument::GetNextRegion() { - if (RegionIndex < 0 || RegionIndex >= Regions) return NULL; + if (RegionIndex < 0 || uint32_t(RegionIndex) >= Regions) return NULL; return pRegions[RegionIndex++]; } @@ -1332,6 +1659,30 @@ pInstruments = NULL; } + 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; + } + } + Sample* File::GetFirstSample() { if (!pSamples) LoadSamples(); if (!pSamples) return NULL;