--- libgig/trunk/src/gig.cpp 2003/10/25 20:15:04 2 +++ libgig/trunk/src/gig.cpp 2004/09/15 13:49:21 241 @@ -2,8 +2,8 @@ * * * libgig - C++ cross-platform Gigasampler format file loader library * * * - * Copyright (C) 2003 by Christian Schoenebeck * - * * + * Copyright (C) 2003, 2004 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 * @@ -45,10 +45,11 @@ Product = smpl->ReadInt32(); SamplePeriod = smpl->ReadInt32(); MIDIUnityNote = smpl->ReadInt32(); - MIDIPitchFraction = smpl->ReadInt32(); + FineTune = smpl->ReadInt32(); smpl->Read(&SMPTEFormat, 1, 4); SMPTEOffset = smpl->ReadInt32(); Loops = smpl->ReadInt32(); + uint32_t manufByt = smpl->ReadInt32(); LoopID = smpl->ReadInt32(); smpl->Read(&LoopType, 1, 4); LoopStart = smpl->ReadInt32(); @@ -71,6 +72,8 @@ } } FrameOffset = 0; // just for streaming compressed samples + + LoopSize = LoopEnd - LoopStart; } /// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). @@ -310,6 +313,181 @@ } /** + * Reads \a SampleCount number of sample points from the position stored + * in \a pPlaybackState into the buffer pointed by \a pBuffer and moves + * the position within the sample respectively, this method honors the + * looping informations of the sample (if any). The sample wave stream + * will be decompressed on the fly if using a compressed sample. Use this + * method if you don't want to load the sample into RAM, thus for disk + * streaming. All this methods needs to know to proceed with streaming + * 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;
+ *
+ * + * 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. + * + * @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 + * @returns number of successfully read sample points + */ + unsigned long Sample::ReadAndLoop(void* pBuffer, unsigned long SampleCount, playback_state_t* pPlaybackState) { + 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 + + switch (this->LoopType) { + + 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)); + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->reverse = true; + break; + } + } while (samplestoread && readsamples); + } + else { // backward playback + + // 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); + samplestoreadinloop -= readsamples; + samplestoread -= readsamples; + totalreadsamples += readsamples; + } while (samplestoreadinloop && readsamples); + + SetPos(reverseplaybackend); // pretend we really read backwards + + 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; + } + + 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)); + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->reverse = true; + break; + } + } while (samplestoread && readsamples); + + 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)); + samplestoreadinloop -= readsamples; + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->loop_cycles_left--; + SetPos(this->LoopStart); + } + } while (samplestoreadinloop && readsamples); + + 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; + } + + 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)); + samplestoread -= readsamples; + totalreadsamples += readsamples; + if (readsamples == samplestoloopend) { + pPlaybackState->loop_cycles_left--; + SetPos(this->LoopStart); + } + } while (samplestoread && readsamples); + break; + } + } + } + + // read on without looping + if (samplestoread) do { + readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoread); + samplestoread -= readsamples; + totalreadsamples += readsamples; + } while (readsamples && samplestoread); + + // store current position + pPlaybackState->position = GetPos(); + + return totalreadsamples; + } + + /** * Reads \a SampleCount number of sample points from the current * position into the buffer pointed by \a pBuffer and increments the * position within the sample. The sample wave stream will be @@ -323,8 +501,10 @@ * @see SetPos() */ unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount) { - if (!Compressed) return pCkData->Read(pBuffer, SampleCount, FrameSize); + 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 (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) @@ -353,8 +533,7 @@ // reload from disk to local buffer if needed if (remainingbytes < 8194) { if (pCkData->GetState() != RIFF::stream_ready) { - this->SamplePos += (SampleCount - remainingsamples); - //if (this->SamplePos > this->SamplesTotal) this->SamplePos = this->SamplesTotal; + this->SamplePos = this->SamplesTotal; return (SampleCount - remainingsamples); } assumedsize = remainingsamples; @@ -474,7 +653,7 @@ } } this->SamplePos += (SampleCount - remainingsamples); - //if (this->SamplePos > this->SamplesTotal) this->SamplePos = this->SamplesTotal; + if (this->SamplePos > this->SamplesTotal) this->SamplePos = this->SamplesTotal; return (SampleCount - remainingsamples); } } @@ -491,11 +670,17 @@ // *************** DimensionRegion *************** // * + uint DimensionRegion::Instances = 0; + DimensionRegion::VelocityTableMap* DimensionRegion::pVelocityTables = NULL; + DimensionRegion::DimensionRegion(RIFF::List* _3ewl) : DLS::Sampler(_3ewl) { + Instances++; + memcpy(&Crossfade, &SamplerOptions, 4); + if (!pVelocityTables) pVelocityTables = new VelocityTableMap; RIFF::Chunk* _3ewa = _3ewl->GetSubChunk(CHUNK_ID_3EWA); - _3ewa->ReadInt32(); // unknown, allways 0x0000008C ? + _3ewa->ReadInt32(); // unknown, always 0x0000008C ? LFO3Frequency = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); EG3Attack = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); _3ewa->ReadInt16(); // unknown @@ -511,13 +696,13 @@ _3ewa->ReadInt16(); // unknown EG1Sustain = _3ewa->ReadUint16(); EG1Release = (double) GIG_EXP_DECODE(_3ewa->ReadInt32()); - EG1Controller = static_cast(_3ewa->ReadUint8()); + 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 = static_cast(_3ewa->ReadUint8()); + 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); @@ -579,7 +764,7 @@ ReleaseVelocityResponseDepth = 0; } VelocityResponseCurveScaling = _3ewa->ReadUint8(); - AttenuationControlTreshold = _3ewa->ReadInt8(); + AttenuationControllerThreshold = _3ewa->ReadInt8(); _3ewa->ReadInt32(); // unknown SampleStartOffset = (uint16_t) _3ewa->ReadInt16(); _3ewa->ReadInt16(); // unknown @@ -595,12 +780,12 @@ uint8_t lfo3ctrl = _3ewa->ReadUint8(); LFO3Controller = static_cast(lfo3ctrl & 0x07); // lower 3 bits LFO3Sync = lfo3ctrl & 0x20; // bit 5 - InvertAttenuationControl = lfo3ctrl & 0x80; // bit 7 + InvertAttenuationController = lfo3ctrl & 0x80; // bit 7 if (VCFType == vcf_type_lowpass) { if (lfo3ctrl & 0x40) // bit 6 VCFType = vcf_type_lowpassturbo; } - AttenuationControl = static_cast(_3ewa->ReadUint8()); + 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 @@ -644,6 +829,193 @@ VCFVelocityDynamicRange = vcfvelocity % 5; VCFVelocityCurve = static_cast(vcfvelocity / 5); VCFType = static_cast(_3ewa->ReadUint8()); + + // 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; + if (pVelocityTables->count(tableKey)) { // if key exists + 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."); + } + (*pVelocityTables)[tableKey] = pVelocityAttenuationTable; // put the new table into the tables map + } + } + + leverage_ctrl_t DimensionRegion::DecodeLeverageController(_lev_ctrl_t EncodedController) { + leverage_ctrl_t decodedcontroller; + switch (EncodedController) { + // special controller + case _lev_ctrl_none: + decodedcontroller.type = leverage_ctrl_t::type_none; + decodedcontroller.controller_number = 0; + break; + case _lev_ctrl_velocity: + decodedcontroller.type = leverage_ctrl_t::type_velocity; + decodedcontroller.controller_number = 0; + break; + case _lev_ctrl_channelaftertouch: + decodedcontroller.type = leverage_ctrl_t::type_channelaftertouch; + decodedcontroller.controller_number = 0; + break; + + // ordinary MIDI control change controller + case _lev_ctrl_modwheel: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 1; + break; + case _lev_ctrl_breath: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 2; + break; + case _lev_ctrl_foot: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 4; + break; + case _lev_ctrl_effect1: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 12; + break; + case _lev_ctrl_effect2: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 13; + break; + case _lev_ctrl_genpurpose1: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 16; + break; + case _lev_ctrl_genpurpose2: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 17; + break; + case _lev_ctrl_genpurpose3: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 18; + break; + case _lev_ctrl_genpurpose4: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 19; + break; + case _lev_ctrl_portamentotime: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 5; + break; + case _lev_ctrl_sustainpedal: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 64; + break; + case _lev_ctrl_portamento: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 65; + break; + case _lev_ctrl_sostenutopedal: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 66; + break; + case _lev_ctrl_softpedal: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 67; + break; + case _lev_ctrl_genpurpose5: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 80; + break; + case _lev_ctrl_genpurpose6: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 81; + break; + case _lev_ctrl_genpurpose7: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 82; + break; + case _lev_ctrl_genpurpose8: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 83; + break; + case _lev_ctrl_effect1depth: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 91; + break; + case _lev_ctrl_effect2depth: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 92; + break; + case _lev_ctrl_effect3depth: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 93; + break; + case _lev_ctrl_effect4depth: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 94; + break; + case _lev_ctrl_effect5depth: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 95; + break; + + // unknown controller type + default: + throw gig::Exception("Unknown leverage controller type."); + } + return decodedcontroller; + } + + DimensionRegion::~DimensionRegion() { + Instances--; + if (!Instances) { + // delete the velocity->volume tables + VelocityTableMap::iterator iter; + for (iter = pVelocityTables->begin(); iter != pVelocityTables->end(); iter++) { + double* pTable = iter->second; + if (pTable) delete[] pTable; + } + pVelocityTables->clear(); + delete pVelocityTables; + pVelocityTables = NULL; + } + } + + /** + * Returns the correct amplitude factor for the given \a MIDIKeyVelocity. + * All involved parameters (VelocityResponseCurve, VelocityResponseDepth + * and VelocityResponseCurveScaling) involved are taken into account to + * calculate the amplitude factor. Use this method when a key was + * triggered to get the volume with which the sample should be played + * back. + * + * @param MIDIKeyVelocity MIDI velocity value of the triggered key (between 0 and 127) + * @returns amplitude factor (between 0.0 and 1.0) + */ + double DimensionRegion::GetVelocityAttenuation(uint8_t MIDIKeyVelocity) { + return pVelocityAttenuationTable[MIDIKeyVelocity]; } @@ -681,8 +1053,9 @@ pDimensionDefinitions[i].bits = bits; pDimensionDefinitions[i].zones = 0x01 << bits; // = pow(2,bits) pDimensionDefinitions[i].split_type = (dimension == dimension_layer || - dimension == dimension_samplechannel) ? split_type_bit - : split_type_normal; + 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 pDimensionDefinitions[i].zone_size = (pDimensionDefinitions[i].split_type == split_type_normal) ? 128 / pDimensionDefinitions[i].zones @@ -780,7 +1153,7 @@ * @see Dimensions */ DimensionRegion* Region::GetDimensionRegionByValue(uint Dim4Val, uint Dim3Val, uint Dim2Val, uint Dim1Val, uint Dim0Val) { - unsigned int bits[5] = {Dim0Val,Dim1Val,Dim2Val,Dim3Val,Dim4Val}; + uint8_t bits[5] = {Dim0Val,Dim1Val,Dim2Val,Dim3Val,Dim4Val}; for (uint i = 0; i < Dimensions; i++) { switch (pDimensionDefinitions[i].split_type) { case split_type_normal: @@ -789,7 +1162,10 @@ case split_type_customvelocity: bits[i] = VelocityTable[bits[i]]; break; - // else the value is already the sought dimension bit number + 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; } } return GetDimensionRegionByBit(bits[4],bits[3],bits[2],bits[1],bits[0]); @@ -995,6 +1371,22 @@ return (InstrumentsIterator != pInstruments->end()) ? *InstrumentsIterator : NULL; } + /** + * Returns the instrument with the given index. + * + * @returns sought instrument or NULL if there's no such instrument + */ + Instrument* File::GetInstrument(uint index) { + if (!pInstruments) LoadInstruments(); + if (!pInstruments) return NULL; + InstrumentsIterator = pInstruments->begin(); + for (uint i = 0; InstrumentsIterator != pInstruments->end(); i++) { + if (i == index) return *InstrumentsIterator; + InstrumentsIterator++; + } + return NULL; + } + void File::LoadInstruments() { RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS); if (lstInstruments) {