home *** CD-ROM | disk | FTP | other *** search
- // copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
- // see COPYRIGHT for reuse legalities
- //
-
- // So here's the idea. Instead of the WWSampleList having a list of
- // WWSamples, it now has a list of objects which conform to the
- // <WWSample> protocol. There are two objects that do so: WWSample (duh!)
- // and WWSampleComponentList. The first time we are asked to add a sample,
- // we look at the sample and and see if we already have a sample at that timestamp.
- // - If we don't, we merely insert the sample in the list.
- // - else we do already have a sample, we look to see how many generators
- // this sample has.
- // - If it only has one generator (i.e. generatorCount == 1)
- // then we see if the the generatorName is the same as the new sample.
- // - if it is, we merely replace the old sample with the new one, freeing
- // the old one (which may or may not free its data...).
- // - else the new sample at this timestamp has a
- // different generator than the old one, *and* the old sample has only
- // one generator, we need to be a bit more fancy. We malloc up a
- // WWSampleComponentList, move our old sample to it, then move our new
- // sample to it, and then replace our old sample in the WWSampleList with
- // our new WWSampleComponentList.
- // - else it has more than one generator, which means that we're looking
- // at a WWSampleComponentsList. At that point, we do a set... on it,
- // and we're golden.
-
-
-
- #import "WWSampleList.h"
-
- #import "WWSample.h"
- #import "WWSampleComponentList.h"
- #import "math.h"
- #import "Protocol_WWRenderable.h"
- #import "Protocol_WWSample.h"
-
- @implementation WWSampleList
-
- - init
- {
- [super init];
-
- epsilon = .001;
- lerpSamples = YES;
- cacheLerps = YES;
-
- return self;
-
- }
-
- - initCount:(unsigned)numSlots
- {
- [super initCount:numSlots];
-
- epsilon = .001;
- lerpSamples = YES;
- cacheLerps = YES;
-
- return self;
-
- }
-
- - awake
- {
- [super awake];
-
- epsilon = .001;
-
- return self;
-
- }
-
- // we want to cover our freeObjects method here, because when building
- // a list, we might actually have multiple samples that are using the
- // same data. This probably shouldn't happen, but the caching stuff
- // might have it happen... Anyway, if it does happen, it will be with
- // adjacent samples, so we only really need to hold on to the last sample
- // data we free'ed. If the one we're about to free has the same id as
- // the one we just free'ed, mark that sample not to free its data and
- // then free it. If it isn't the one we just free
- - freeObjects
- {
- int i, howMany = [self count];
- id data, lastSampleData = nil;
- id <WWSample> sample;
-
-
- //NXLogError("freeing objects for WWSampleList %p", self);
- for (i = 0; i < howMany; i++)
- { sample = (id <WWSample>)[self objectAt:i];
- data = [sample data];
- if (lastSampleData == data) // yikes! it has the same data as what I just free'ed
- { [sample setFreeData:NO]; // please don't free your data; it's already been free'ed
- }
- else
- { lastSampleData = data;
- }
- [sample free];
- }
- return self;
- }
-
- - (double)epsilon { return epsilon; }
-
- - setEpsilon:(double)newEpsilon { epsilon = newEpsilon; return self; }
-
- - (BOOL)lerpSamples { return lerpSamples; }
- - setLerpSamples:(BOOL)flag { lerpSamples = flag; return self; }
-
- - (BOOL)cacheLerps { return cacheLerps; }
- - setSetCacheLerps:(BOOL)flag { cacheLerps = flag; return self; }
-
- // there really should be several different schemes for inserting samples.
- // One way is that a new sample overrides an old one at the same timestamp,
- // i.e. the old one is freed immediately. Another way is that the
-
- - dumpList
- {
- int howMany = [self count],
- i = howMany;
- id <WWSample> aSample = nil;
-
-
- NXLogError("WWSampleList contents:\n");
- for (i = 0; i < howMany; i++)
- { aSample = [self objectAt:i];
- NXLogError("(%d): %f %s with data %s\n", i, [aSample timestamp], [[aSample class] name], [[[aSample data] class] name]);
- }
- return self;
- }
-
- - addSample:(WWSample *)theSample
- {
- int howMany = [self count],
- i = howMany;
- BOOL found = NO;
- id <WWSample> aSample = nil;
- double delta;
- const char *previousGen, *currentGen;
-
-
- //NXLogError("before adding new sample:\n");
- //[self dumpList];
-
- // this routine adds the sample to the list in the appropriate, ordered spot.
- // it also ensures that if there is a sample with the same timestamp as this
- // new one, it frees the old one and inserts this in the appropriate spot.
- // The naive insertion assumption is that samples get put in sequentially,
- // so we start from the back of the list and move towards the front...
- while (!found && i)
- { i--;
- aSample = [self objectAt:i];
- delta = fabs((double)([aSample timestamp] - [theSample timestamp]));
- if (delta < epsilon)
- { // okay, we're there. Now we have to find out if we already have
- // a sample with the same generator.
- if ([aSample generatorCount] > 1)
- { // it's already a list, just go ahead and set it with
- // the contents of the new sample. It's smart enough
- // to do the right thing.
- //NXLogError("adding new sample to WWSampleComponentList");
- [(WWSampleComponentList *)aSample addObject:theSample];
- found = YES;
- }
- else
- { // okay, this guy is a single sample. We have to see if it has
- // the same generator as the new sample. If it does, just replace
- // the old one with the new one. If, on the other hand, they
- // have different generators, we need to malloc up a WWSampleComponentList,
- // stick the old sample in there, and then stick the new one in,
- // then replace the old sample here with the newly alloced list
- previousGen = [aSample generatorName];
- currentGen = [theSample generatorName];
- if (!previousGen)
- { if (!currentGen)
- { // they're both NULL; replace old with new
- //NXLogError("single sample: both generators NULL; replacing old with new");
- [self replaceObject:aSample with:theSample];
- [aSample free];
- found = YES;
- }
- else // previous was NULL, new one is !NULL, malloc up a list
- { // actually, make sure current isn't just a \0
- if (!*currentGen)
- { // hah! one's NULL, but the other is ""
- //NXLogError("single sample: one's NULL, and the other is \"\"; replacing old with new");
- [self replaceObject:aSample with:theSample];
- [aSample free];
- found = YES;
- }
- }
- {
- }
- }
- else
- { if (currentGen) // they're both !NULL
- { if (!strcmp(previousGen, currentGen))
- { // neither are NULL and they're the same
- //NXLogError("single sample: both generators not NULL and they're the same; replacing old with new");
- [self replaceObject:aSample with:theSample];
- [aSample free];
- found = YES;
- }
- else
- { // neither NULL, not the same, malloc up a list
- }
- }
- else // previous was !NULL, new one is NULL, malloc up a list
- { // actually, make sure previous isn't just a \0
- if (!*previousGen)
- { // hah! one's NULL, but the other is ""
- //NXLogError("single sample: one's NULL, and the other is \"\"; replacing old with new");
- [self replaceObject:aSample with:theSample];
- [aSample free];
- found = YES;
- }
- }
- }
- if (!found)
- { WWSampleComponentList *newList = [[WWSampleComponentList alloc] initCount:2];
-
- //NXLogError("didn't find it; malloc'ing new WWSampleComponentList and adding the two, then replacing the old one with this list");
- [newList addObject:aSample];
- [newList addObject:theSample];
- [self replaceObject:aSample with:newList];
- found = YES;
- }
- }
- }
- else // okay, it's not the same; is it later?
- { if ([theSample timestamp] > [aSample timestamp])
- { //NXLogError("punting and adding new sample at %d", i+1);
- [self insertObject:theSample at:(i + 1)];
- found = YES;
- }
- }
- }
- if (!found) // stick it in the front
- { //NXLogError("sticking the sample in the front");
- [self insertObject:theSample at:0];
- }
-
- //NXLogError("after adding new sample:\n");
- //[self dumpList];
-
- return self;
- }
-
- // if we get a sample that is interpolated, and it's not the same as
- // the one it's interpolated from, add it to the list if we've got the
- // "cacheLerps" flag set
-
- - (BOOL)isLerpable { return NO; }
-
- - (id <WWSample>)sampleAtOrJustBefore:(float)theTimestamp
- {
- id <WWSample> aSample, theSample = nil;
- int howMany = [self count],
- i = howMany;
- BOOL found = NO;
- double delta;
- id <WWSample> nextSample;
- float u;
- WWSample *newSample;
-
-
- // okay, the samples are ordered sequentially, so we'll step back
- // through time, trying to find a sample at the time we're looking at.
- // We first see if we've gone under the time, at which point we realize
- // that we need to stop and get the sample there. If
-
- while (!found && i)
- { i--;
- aSample = [self objectAt:i];
- if ([aSample timestamp] < theTimestamp)
- { delta = fabs((double)(theTimestamp - [aSample timestamp]));
- //NXLogError("theTimestamp == %f\n", theTimestamp);
- //NXLogError("[aSample timestamp] == %f\n", [aSample timestamp]);
- //NXLogError("delta == %lf\n", delta);
- if ( !lerpSamples
- || (i == (howMany - 1))
- || (delta < epsilon)) // we're not lerping or this is the last sample in the list or we're close enough
- { theSample = aSample;
- found = YES;
- }
- else // at this point, need to manufacture a new one by calling lerpWith:by: using this sample and the next
- { // or if this sample isn't linearly interpolatable, or if we're at the end of the list, we just hand back this sample.
- if ( (([[aSample data] respondsTo:@selector(isLerpable)]) && (![[aSample data] isLerpable])) || ((i+1) == howMany) )
- { theSample = aSample;
- }
- else // okay, looks like we've got to lerp it
- { //NXLogError("\ttheTimestamp == %f\n", theTimestamp);
- //NXLogError("\t[aSample timestamp] == %f\n", [aSample timestamp]);
- //NXLogError("\tdelta == %lf\n", delta);
- nextSample = [self objectAt:(i+1)];
- u = (theTimestamp - [aSample timestamp])/([nextSample timestamp] - [aSample timestamp]);
- newSample = [[[WWSample alloc] init] setData:[[aSample data] lerpWith:[nextSample data] by:u]
- timestamp:theTimestamp
- generator:"WWSampleList" weight:1.0];
- if (cacheLerps)
- { [self addSample:newSample];
- }
- theSample = newSample;
- }
- found = YES;
- }
- }
- else // need to check if it's close enough
- { delta = fabs((double)([aSample timestamp] - theTimestamp));
- if (delta < epsilon)
- { theSample = aSample;
- found = YES;
- }
- else // nope; keep trying...
- {
- }
- }
- }
- return theSample;
- }
-
- // The sample list needs to be able to manufacture interpolated samples...
-
-
- - (float)lastSampleIsAt {return [[self lastObject] timestamp]; }
-
- - (unsigned long int)maxSampleBandwidth
- {
- int i, howMany = [self count];
- unsigned
- long int sampleBandwidth, maxSampleBandwidth = 0;
-
-
- for (i = 0; i < howMany; i++)
- { sampleBandwidth = [[self objectAt:i] maxSampleBandwidth];
- if (sampleBandwidth > maxSampleBandwidth)
- { maxSampleBandwidth = sampleBandwidth;
- }
- }
- return maxSampleBandwidth;
- }
-
- @end
-