home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / Wave / WavesWorld / Source / IBPalettes / WW3DKit / WWSampleList.m < prev    next >
Encoding:
Text File  |  1995-03-22  |  12.2 KB  |  344 lines

  1. // copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
  2. // see COPYRIGHT for reuse legalities
  3. //
  4.  
  5. // So here's the idea. Instead of the WWSampleList having a list of
  6. // WWSamples, it now has a list of objects which conform to the
  7. // <WWSample> protocol.  There are two objects that do so: WWSample (duh!)
  8. // and WWSampleComponentList.  The first time we are asked to add a sample, 
  9. // we look at the sample and and see if we already have a sample at that timestamp.  
  10. // - If we don't, we merely insert the sample in the list.
  11. // - else we do already have a sample, we look to see how many generators
  12. //   this sample has.  
  13. //   - If it only has one generator (i.e. generatorCount == 1)
  14. //     then we see if the the generatorName is the same as the new sample.  
  15. //     - if it is, we merely replace the old sample with the new one, freeing 
  16. //       the old one (which may or may not free its data...).
  17. //     - else the new sample at this timestamp has a 
  18. //       different generator than the old one, *and* the old sample has only
  19. //       one generator, we need to be a bit more fancy.  We malloc up a
  20. //       WWSampleComponentList, move our old sample to it, then move our new
  21. //       sample to it, and then replace our old sample in the WWSampleList with
  22. //       our new WWSampleComponentList.
  23. //   - else it has more than one generator, which means that we're looking
  24. //     at a WWSampleComponentsList.  At that point, we do a set... on it,
  25. //     and we're golden. 
  26.  
  27.  
  28.  
  29. #import "WWSampleList.h"
  30.  
  31. #import "WWSample.h"
  32. #import "WWSampleComponentList.h"
  33. #import "math.h"
  34. #import "Protocol_WWRenderable.h"
  35. #import "Protocol_WWSample.h"
  36.  
  37. @implementation WWSampleList
  38.  
  39. - init
  40. {
  41.   [super init];
  42.  
  43.   epsilon = .001;
  44.   lerpSamples = YES;
  45.   cacheLerps = YES;
  46.  
  47.   return self;
  48.  
  49. }
  50.  
  51. - initCount:(unsigned)numSlots
  52. {
  53.   [super initCount:numSlots];
  54.  
  55.   epsilon = .001;
  56.   lerpSamples = YES;
  57.   cacheLerps = YES;
  58.  
  59.   return self;
  60.  
  61. }
  62.  
  63. - awake
  64. {
  65.   [super awake];
  66.  
  67.   epsilon = .001;
  68.  
  69.   return self;
  70.  
  71. }
  72.  
  73. // we want to cover our freeObjects method here, because when building
  74. // a list, we might actually have multiple samples that are using the
  75. // same data.  This probably shouldn't happen, but the caching stuff
  76. // might have it happen... Anyway, if it does happen, it will be with
  77. // adjacent samples, so we only really need to hold on to the last sample
  78. // data we free'ed.  If the one we're about to free has the same id as
  79. // the one we just free'ed, mark that sample not to free its data and
  80. // then free it.  If it isn't the one we just free
  81. - freeObjects
  82. {
  83.   int            i, howMany = [self count];
  84.   id             data, lastSampleData = nil;
  85.   id <WWSample>  sample;
  86.  
  87.  
  88.   //NXLogError("freeing objects for WWSampleList %p", self);
  89.   for (i = 0; i < howMany; i++)
  90.   {  sample = (id <WWSample>)[self objectAt:i];
  91.      data = [sample data];
  92.      if (lastSampleData == data) // yikes! it has the same data as what I just free'ed
  93.      {  [sample setFreeData:NO];  // please don't free your data; it's already been free'ed
  94.      }
  95.      else
  96.      {  lastSampleData = data;
  97.      }
  98.      [sample free];
  99.   }
  100.   return self;
  101. }
  102.  
  103. - (double)epsilon { return epsilon; }
  104.  
  105. - setEpsilon:(double)newEpsilon { epsilon = newEpsilon; return self; }
  106.  
  107. - (BOOL)lerpSamples { return lerpSamples; }
  108. - setLerpSamples:(BOOL)flag { lerpSamples = flag;  return self; }
  109.  
  110. - (BOOL)cacheLerps { return cacheLerps; }
  111. - setSetCacheLerps:(BOOL)flag { cacheLerps = flag;  return self; }
  112.  
  113. // there really should be several different schemes for inserting samples.
  114. // One way is that a new sample overrides an old one at the same timestamp,
  115. // i.e. the old one is freed immediately.  Another way is that the 
  116.  
  117. - dumpList
  118. {
  119.    int            howMany = [self count],
  120.                   i = howMany;
  121.    id <WWSample>  aSample = nil;
  122.  
  123.  
  124.    NXLogError("WWSampleList contents:\n");
  125.    for (i = 0; i < howMany; i++)
  126.    {  aSample = [self objectAt:i];
  127.       NXLogError("(%d): %f %s with data %s\n", i, [aSample timestamp], [[aSample class] name], [[[aSample data] class] name]);
  128.    }
  129.    return self;
  130. }
  131.  
  132. - addSample:(WWSample *)theSample
  133. {
  134.    int            howMany = [self count],
  135.                   i = howMany;
  136.    BOOL           found = NO;
  137.    id <WWSample>  aSample = nil;
  138.    double         delta;
  139.    const char     *previousGen, *currentGen;
  140.  
  141.  
  142.    //NXLogError("before adding new sample:\n");
  143.    //[self dumpList];
  144.  
  145.    // this routine adds the sample to the list in the appropriate, ordered spot.
  146.    // it also ensures that if there is a sample with the same timestamp as this
  147.    // new one, it frees the old one and inserts this in the appropriate spot.
  148.    // The naive insertion assumption is that samples get put in sequentially,
  149.    // so we start from the back of the list and move towards the front...
  150.    while (!found && i)
  151.    {  i--;
  152.       aSample = [self objectAt:i];
  153.       delta = fabs((double)([aSample timestamp] - [theSample timestamp]));
  154.       if (delta < epsilon)
  155.       {  // okay, we're there.  Now we have to find out if we already have 
  156.          // a sample with the same generator. 
  157.          if ([aSample generatorCount] > 1)
  158.          {  // it's already a list, just go ahead and set it with 
  159.             // the contents of the new sample.  It's smart enough
  160.             // to do the right thing.
  161.             //NXLogError("adding new sample to WWSampleComponentList");
  162.             [(WWSampleComponentList *)aSample addObject:theSample];
  163.             found = YES;
  164.          }
  165.          else
  166.          {  // okay, this guy is a single sample.  We have to see if it has
  167.             // the same generator as the new sample.  If it does, just replace
  168.             // the old one with the new one.  If, on the other hand, they
  169.             // have different generators, we need to malloc up a WWSampleComponentList,
  170.             // stick the old sample in there, and then stick the new one in,
  171.             // then replace the old sample here with the newly alloced list
  172.             previousGen = [aSample generatorName];
  173.             currentGen = [theSample generatorName];
  174.             if (!previousGen)
  175.             {  if (!currentGen)
  176.                {  // they're both NULL; replace old with new
  177.                   //NXLogError("single sample: both generators NULL; replacing old with new");
  178.                   [self replaceObject:aSample with:theSample];
  179.                   [aSample free];
  180.                   found = YES;
  181.            }
  182.                else // previous was NULL, new one is !NULL, malloc up a list
  183.            {  // actually, make sure current isn't just a \0
  184.                   if (!*currentGen)
  185.           {  // hah! one's NULL, but the other is ""
  186.                      //NXLogError("single sample: one's NULL, and the other is \"\"; replacing old with new");
  187.                      [self replaceObject:aSample with:theSample];
  188.                      [aSample free];
  189.                      found = YES;
  190.                   }
  191.            }
  192.            {
  193.            }
  194.             }
  195.             else
  196.             {  if (currentGen)  // they're both !NULL
  197.                {  if (!strcmp(previousGen, currentGen))
  198.           {  // neither are NULL and they're the same
  199.                      //NXLogError("single sample: both generators not NULL and they're the same; replacing old with new");
  200.                      [self replaceObject:aSample with:theSample];
  201.                      [aSample free];
  202.                      found = YES;
  203.                   }
  204.                   else
  205.                   {  // neither NULL, not the same, malloc up a list
  206.                   }
  207.            }
  208.                else // previous was !NULL, new one is NULL, malloc up a list
  209.            {  // actually, make sure previous isn't just a \0
  210.                   if (!*previousGen)
  211.           {  // hah! one's NULL, but the other is ""
  212.                      //NXLogError("single sample: one's NULL, and the other is \"\"; replacing old with new");
  213.                      [self replaceObject:aSample with:theSample];
  214.                      [aSample free];
  215.                      found = YES;
  216.                   }
  217.            }
  218.             }
  219.             if (!found)
  220.             {  WWSampleComponentList  *newList = [[WWSampleComponentList alloc] initCount:2];
  221.  
  222.                //NXLogError("didn't find it; malloc'ing new WWSampleComponentList and adding the two, then replacing the old one with this list");
  223.                [newList addObject:aSample];
  224.                [newList addObject:theSample];
  225.                [self replaceObject:aSample with:newList];
  226.                found = YES;
  227.             }
  228.          }
  229.       }
  230.       else  // okay, it's not the same; is it later?
  231.       {  if ([theSample timestamp] > [aSample timestamp])
  232.          {  //NXLogError("punting and adding new sample at %d", i+1);
  233.             [self insertObject:theSample at:(i + 1)];
  234.             found = YES;
  235.          }
  236.       }     
  237.    }
  238.    if (!found)  // stick it in the front
  239.    {  //NXLogError("sticking the sample in the front");
  240.       [self insertObject:theSample at:0];
  241.    }
  242.  
  243.    //NXLogError("after adding new sample:\n");
  244.    //[self dumpList];
  245.  
  246.    return self;
  247. }
  248.  
  249. // if we get a sample that is interpolated, and it's not the same as
  250. // the one it's interpolated from, add it to the list if we've got the
  251. // "cacheLerps" flag set
  252.  
  253. - (BOOL)isLerpable { return NO; }
  254.  
  255. - (id <WWSample>)sampleAtOrJustBefore:(float)theTimestamp
  256. {
  257.    id <WWSample>  aSample, theSample = nil;
  258.    int            howMany = [self count],
  259.                   i = howMany;
  260.    BOOL           found = NO;
  261.    double         delta;
  262.    id <WWSample>  nextSample;
  263.    float          u;
  264.    WWSample       *newSample;
  265.  
  266.  
  267.    // okay, the samples are ordered sequentially, so we'll step back
  268.    // through time, trying to find a sample at the time we're looking at.
  269.    // We first see if we've gone under the time, at which point we realize
  270.    // that we need to stop and get the sample there.  If 
  271.  
  272.    while (!found && i)
  273.    {  i--;
  274.       aSample = [self objectAt:i];
  275.       if ([aSample timestamp] < theTimestamp)
  276.       {  delta = fabs((double)(theTimestamp - [aSample timestamp]));
  277.      //NXLogError("theTimestamp == %f\n", theTimestamp);
  278.      //NXLogError("[aSample timestamp] == %f\n", [aSample timestamp]);
  279.      //NXLogError("delta == %lf\n", delta);
  280.          if (   !lerpSamples 
  281.              || (i == (howMany - 1)) 
  282.              || (delta < epsilon)) // we're not lerping or this is the last sample in the list or we're close enough
  283.      {  theSample = aSample;
  284.             found = YES;
  285.       }
  286.          else // at this point, need to manufacture a new one by calling lerpWith:by: using this sample and the next
  287.      {  // or if this sample isn't linearly interpolatable, or if we're at the end of the list, we just hand back this sample.
  288.             if ( (([[aSample data] respondsTo:@selector(isLerpable)]) && (![[aSample data] isLerpable])) || ((i+1) == howMany) )
  289.         {  theSample = aSample;
  290.          }      
  291.             else  // okay, looks like we've got to lerp it
  292.         {  //NXLogError("\ttheTimestamp == %f\n", theTimestamp);
  293.            //NXLogError("\t[aSample timestamp] == %f\n", [aSample timestamp]);
  294.            //NXLogError("\tdelta == %lf\n", delta);
  295.                nextSample = [self objectAt:(i+1)];
  296.                u = (theTimestamp - [aSample timestamp])/([nextSample timestamp] - [aSample timestamp]);         
  297.                newSample = [[[WWSample alloc] init] setData:[[aSample data] lerpWith:[nextSample data] by:u]
  298.                                                        timestamp:theTimestamp 
  299.                                                        generator:"WWSampleList" weight:1.0];
  300.                if (cacheLerps)
  301.                {  [self addSample:newSample];
  302.                }
  303.                theSample = newSample;
  304.             }
  305.             found = YES;
  306.       }
  307.       }
  308.       else  // need to check if it's close enough
  309.       {  delta = fabs((double)([aSample timestamp] - theTimestamp));
  310.          if (delta < epsilon)
  311.      {  theSample = aSample;
  312.             found = YES;
  313.       }
  314.          else  // nope; keep trying...
  315.      {
  316.          }
  317.       }
  318.    }
  319.    return theSample;
  320. }
  321.  
  322. // The sample list needs to be able to manufacture interpolated samples...
  323.  
  324.  
  325. - (float)lastSampleIsAt {return [[self lastObject] timestamp]; }
  326.  
  327. - (unsigned long int)maxSampleBandwidth
  328.    int   i, howMany = [self count];
  329.    unsigned 
  330.        long int sampleBandwidth, maxSampleBandwidth = 0; 
  331.  
  332.  
  333.    for (i = 0; i < howMany; i++)
  334.    {  sampleBandwidth = [[self objectAt:i] maxSampleBandwidth];
  335.       if (sampleBandwidth > maxSampleBandwidth)
  336.       {  maxSampleBandwidth = sampleBandwidth;
  337.       }
  338.    }
  339.    return maxSampleBandwidth;
  340. }
  341.  
  342. @end
  343.