--- libgig/trunk/src/gig.cpp 2003/11/16 17:47:00 11
+++ libgig/trunk/src/gig.cpp 2003/12/26 16:15:31 24
@@ -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,10 @@
}
}
FrameOffset = 0; // just for streaming compressed samples
+
+ LoopStart /= FrameSize; // convert to sample points
+ LoopEnd /= FrameSize; // convert to sample points
+ LoopSize = LoopEnd - LoopStart;
}
/// Scans compressed samples for mandatory informations (e.g. actual number of total sample points).
@@ -310,6 +315,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,6 +503,7 @@
* @see SetPos()
*/
unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount) {
+ 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;
@@ -491,8 +672,14 @@
// *************** 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 ?
@@ -644,6 +831,75 @@
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+1),(double)(VelocityResponseDepth+1),(double)VelocityResponseCurveScaling);
+ if (pVelocityAttenuationTable[velocity] > 1.0) pVelocityAttenuationTable[velocity] = 1.0;
+ else if (pVelocityAttenuationTable[velocity] < 0.0) 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+1),(double)VelocityResponseCurveScaling);
+ if (pVelocityAttenuationTable[velocity] > 1.0) pVelocityAttenuationTable[velocity] = 1.0;
+ else if (pVelocityAttenuationTable[velocity] < 0.0) pVelocityAttenuationTable[velocity] = 0.0;
+ }
+ break;
+ case curve_type_special:
+ for (int velocity = 0; velocity < 128; velocity++) {
+ pVelocityAttenuationTable[velocity] =
+ GIG_VELOCITY_TRANSFORM_SPECIAL((double)(velocity+1),(double)(VelocityResponseDepth+1),(double)VelocityResponseCurveScaling);
+ if (pVelocityAttenuationTable[velocity] > 1.0) pVelocityAttenuationTable[velocity] = 1.0;
+ else if (pVelocityAttenuationTable[velocity] < 0.0) 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
+ }
+ }
+
+ 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 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];
}
@@ -995,6 +1251,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) {