home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #30 / NN_1992_30.iso / spool / comp / sys / sgi / 18121 < prev    next >
Encoding:
Text File  |  1992-12-16  |  11.6 KB  |  316 lines

  1. Path: sparky!uunet!usc!cs.utexas.edu!sun-barr!ames!sgi!fido!prophet.esd.sgi.com!gints
  2. From: gints@prophet.esd.sgi.com (Gints Klimanis)
  3. Newsgroups: comp.sys.sgi
  4. Subject: FOR paul@comback
  5. Date: 17 Dec 1992 00:46:46 GMT
  6. Organization: Silicon Graphics, Inc.
  7. Lines: 304
  8. Distribution: world
  9. Message-ID: <1goilmINNa9l@fido.asd.sgi.com>
  10. NNTP-Posting-Host: prophet.esd.sgi.com
  11.  
  12. Hi !
  13.  
  14. Your mail bounces.
  15. /* 
  16. NOTE: this is not actual apanel code.  Send questions to:
  17.  
  18.         Gints Klimanis
  19.         gints@sgi.com
  20.  
  21. apanel method is a dual peak-metering scheme.  VU meter simulation
  22. (signal average and signal peak) didn't "look" right, 
  23. because the average values never "pushed" up the peak meters.
  24.  
  25. Most meters on DAT machines are dual peak meters.  The short and long
  26. meter intervals are a fraction of a second and one second, respectively.  
  27. An meter update rate (and short time interval) in excess of 15 renders/sec.
  28. looks crisp.  If the meter intervals are measured in signal samples 
  29. rather than by a timer, the interval lengths must track the sampling
  30. rate so signals in lower sampling rates are not monitored with lower
  31. meter update rates.
  32.  
  33. The apanel meters are driven by a lookup into a logarithmic table.  A 
  34. data cache miss to index this table 20 times/second is acceptable in 
  35. comparison to the CPU required to render the meters.
  36.  
  37. Apanel meters go red when the absolute value of the signal equals or 
  38. exceeds 32767, the 0dB headroom level for a sixteen-bit resolution signal.
  39. Since a logarithmic scale exaggerates the lower values, apanel draws
  40. from -60dB to 0dB.  This prevents quiescent noise (also future system dither) 
  41. from  external sources from flickering the lower segments.
  42.  
  43. Logarithmic metering mandates DC removal  Apanel uses a simple run length 
  44. complementary low pass filter which computes the signal average value.  
  45. Since this filter  is linear phase, the filter output
  46. may be subtracted from the meter peak values to determine the actual meter
  47. level.  Subtracting the low pass filter from the input is the complement
  48. of a low-pass filter operation:  a high pass filter.  Worry not about
  49. time alignment during the subtraction, since we can assume that the DC
  50. level does not change over the short time interval.  The DC level must
  51. be computed in real-time rather than at system login/power up since the level 
  52. varies from machine to machine, with temperature and the line/mic input
  53. attenuation.   Thus, each of the stereo input requires a separate filter.
  54. The mic and line inputs have different DC offsets.
  55.  
  56. The length of the filter should be long enough to determine the average
  57. signal level but short enough to minimize the settling time.  You can observe
  58. a meter flicker caused by this filter settling by selecting line or 
  59. mic input, input attenution faders at lowest screen position and switching 
  60. between the two input sources.
  61.  
  62. The filter accumulators and the run length buffer must be cleared when
  63. the signal breaks.  We do this for changes in input sources 
  64. and sampling rates.  An additional opportunity is when meters are toggled
  65. on and off. This provides recovery when different devices (with potentially
  66. different DC offests) are connected to the IRIS audio inputs.
  67. */
  68. #include "audio.h"
  69.  
  70. /* linear to log scale for input level meters */
  71. float        levelToDecibelTable[32768];    /* can be reduced in size */
  72.  
  73. /* run length buffers used to compute DC offset in meter compute routines */
  74. /* important to keep length of run buffer small.  Need to keep it
  75. large enough for an accurate measurement at the high sampling rates.
  76. However, large values take a long time to settle when changing input
  77. levels (thus changing the offset) and sampling rates.
  78. Since it currently does not scale with sampling rate, the settling time
  79. is longer at the lower sampling rates. Seems to be ok.
  80. */
  81. short        runLengthBufferL[RUN_LENGTH_BUFFER_LENGTH];    
  82. short        runLengthBufferR[RUN_LENGTH_BUFFER_LENGTH];    
  83. #define RUN_LENGTH_BUFFER_INDEX_MODULO    RUN_LENGTH_BUFFER_LENGTH-1
  84. unsigned long    runLengthBufferIndex;
  85.  
  86. long        dcOffsetL, dcOffsetR;    /* DC filter filter accumulators */
  87.  
  88. float        meterUpdateRate = 18.0;    /* # short time meter updates / sec
  89. unsigned int    shortBlockLength;    /* length of block used for
  90.                             short time peak meter */
  91.  
  92. #define REFERENCE_LEVEL 32767    /* largest value considered from 
  93.                     2's complement 16-bit data */
  94.  
  95. /* stuff for meter short and long interval storage */
  96. long shortBlockMinL = shortBlockMinR = 0;
  97. long shortBlockMaxL = shortBlockMaxR = 0;
  98. long shortBlockCounter = 0;
  99. long longBlockPeakL = longBlockPeakR = 0;    
  100.                     
  101. /* **********************************************************************
  102.  *
  103.  * ComputeShortTimeMeterInterval:    compute new short time interval 
  104.  *                    (in samples) and clear filter
  105.  * ********************************************************************** */
  106.     void 
  107. ComputeShortTimeMeterInterval()
  108. {
  109. long    pvBuffer[2];
  110.  
  111. /* clear meter registers */
  112.     shortBlockMinL = shortBlockMinR = 0;
  113.     shortBlockMaxL = shortBlockMaxR = 0;
  114.     longBlockPeakL = longBlockPeakR = 0;
  115.     shortBlockCounter = 0;
  116.  
  117. /* shortBlockLength is inversely proportional to sampling rate */
  118. /* this code asks audio hardware for sampling rate.  Otherwise,
  119.     substitute your known sampling rate */
  120.     pvBuffer[0] = AL_INPUT_RATE;
  121.     pvBuffer[1] = 0;
  122.     ALgetparams(AL_DEFAULT_DEVICE, pvBuffer, 2);
  123. /* compute short block length meter update rate is 50 Hz (WHAT !! BUT
  124. SEEMS TO BE RIGHT) */
  125.     shortBlockLength = (unsigned int) ((((float)
  126. pvBuffer[1])/meterUpdateRate) + 0.5);
  127.  
  128. /* 
  129.  * initialize some filter variables.  This code should be used
  130.  *  to reset the run length filters when the signal continuity is violated.
  131.  *  Apanel runs similar stuff at start up and every time metering is enabled
  132.  */
  133. /* clear run length buffer and set ptr to start at beginning */
  134.     for (i = 0; i < RUN_LENGTH_BUFFER_LENGTH; i++)
  135.     {
  136.     runLengthBufferL[i] = 0;
  137.     runLengthBufferR[i] = 0;
  138.     }
  139.     runLengthBufferIndex = 0;
  140.  
  141. /* clear filter accumulators */
  142. dcOffsetL = 0;
  143. dcOffsetR = 0;
  144. }    /* ------------------ end ComputeShortTimeMeterInterval() --------------- */
  145.  
  146. /* **********************************************************************
  147.  *
  148.  * ComputeLevelToMeterLookUpTable:    fill meter logarithmic lookup
  149.  *                    table.  These values range between
  150.  *                    0.0 .. 1.0.  
  151.  * ********************************************************************** */
  152.     void 
  153. ComputeLevelToMeterLookUpTable()
  154. {
  155.     int        i;
  156.     int        minimumLinearLevel = 33;
  157.     int        maximumLinearLevel = 32767;
  158.     float   topdBLevel, bottomdBLevel;
  159.  
  160. /* Any values below -60 dB (value 33of32767 linear) are displayed as 0.0 */
  161. /* -60dB = 20*log10(maximumLinearLevel/maximumLinearLevel) */
  162.     /* nuke out minimumLinearLevel positions */
  163.     for (i = 0; i <= minimumLinearLevel; i++)
  164.     {
  165.     levelToDecibelTable[i] = 0.0;
  166.     }
  167.  
  168.     /* comppute values only between the bottom and top decibel values */
  169.     topdBLevel = 20.0*log10(2.0*((float) maximumLinearLevel));
  170.     bottomdBLevel = 20.0*log10(2.0*((float) minimumLinearLevel));
  171.     for (; i < maximumLinearLevel; i++)
  172.     {
  173.     levelToDecibelTable[i] = topdBLevel - bottomdBLevel - (-20.0 *
  174. log10(float(i) / ((float) REFERENCE_LEVEL)));
  175.     levelToDecibelTable[i] /= topdBLevel - bottomdBLevel;
  176.     }
  177.     levelToDecibelTable[maximumLinearLevel] = 1.0;
  178. }    /* ------------------ end ComputeLevelToMeterLookUpTable()
  179. --------------- */
  180.  
  181. /* **********************************************************************
  182.  *
  183.  * ComputeDualPeakMeters:    compute positions of short and long
  184.  *                interval peak meters
  185.  * ********************************************************************** */
  186. void 
  187. ComputeDualPeakMeters()
  188. {
  189.     register long h;
  190.  
  191.     register short *inBufferPtr;    /* make it point to stereo, time
  192.                     interleaved buffer of your choice */
  193.  
  194.      for (numStereoSamplesLeft = INPUT_BUFFER_LENGTH; numStereoSamplesLeft;
  195. h += 2, inBufferPtr += 2) 
  196.         {
  197.         /* 
  198.          * compute run length filters.  This will give us a
  199.          *    majorly low pass filtered version of input signals.
  200.          *    This value is also the DC offset.  The DC offset is then
  201.          *        subtracted from the input signal.  This could very
  202.          *        well be a high pass filter if the signal and filter
  203.          *        output were time aligned.  However, they are not. 
  204.          *    This operation works on the assumption that the low pass
  205.          *    filter output value changes VERY slowly over time. 
  206.          *    
  207.          *    Also need to compute for each channel.  DC offset also
  208.          *        seems to vary a little with the input attenuation.
  209.          */
  210.         /* subtract off value at end of circular runlength buffer */
  211.         /* and write new input into buffers */
  212.         dcOffsetL -= (long) runLengthBufferL[runLengthBufferIndex];
  213.         runLengthBufferL[runLengthBufferIndex] = inBufferPtr[0];
  214.  
  215.         dcOffsetR -= (long) runLengthBufferR[runLengthBufferIndex];
  216.         runLengthBufferR[runLengthBufferIndex] = inBufferPtr[1];
  217.  
  218.         /* read input values from input buffer */
  219.         long inputLeft = (long) inBufferPtr[0];
  220.         long inputRight = (long) inBufferPtr[1];
  221.  
  222.         /* advance and bound buffer index */
  223.         runLengthBufferIndex++;
  224.         runLengthBufferIndex &= RUN_LENGTH_BUFFER_INDEX_MODULO;
  225.         /* accumulate input to signed 32bits:  CAREFUL.  short interval
  226.         should be short enough to avoid overflow */
  227.         dcOffsetL += inputLeft;
  228.         dcOffsetR += inputRight;
  229.  
  230.         /* collect maximum negative and positive values of 
  231.             left/right input samples */
  232. /* should work to subtract DC inside slower update loop.  Do not subtract
  233.     off absolute value */
  234.         if (inputLeft > shortBlockMaxL) 
  235.         {
  236.         shortBlockMaxL = inputLeft;
  237.         }
  238.         else if (inputLeft < shortBlockMinL)
  239.         {
  240.         shortBlockMinL = inputLeft;
  241.         }
  242.         if (inputRight > shortBlockMaxR) 
  243.         {
  244.         shortBlockMaxR = inputRight;
  245.         }
  246.         else if (inputRight < shortBlockMinR)
  247.         {
  248.         shortBlockMinR = inputRight;
  249.         }
  250.     
  251.         /* 
  252.          * update meters every shortBlockLength using dual
  253.          * peak metering scheme.  Short timepeak interval is 
  254.          * 1/20 second.  Long time peak interval is 1 second.
  255.          * The long time peak values are maintained in
  256.          *    the meter drawing routine 
  257.          */
  258.         /* IMPORTANT:  >= operation is safeguard for dynamic changes in
  259.             shortBlockLength.  Such changes occur when the sampling
  260.             rate is changed. */
  261.         if ((shortBlockCounter++ >= shortBlockLength))
  262.         {
  263.         /* only need to remove DC offset at rate which short block
  264.         peak is updated */
  265.         /* choose greatest short block absolute value */
  266.         if (shortBlockMaxL < -shortBlockMinL)
  267.             shortBlockMaxL = -shortBlockMinL;
  268.  
  269.         if (shortBlockMaxR < -shortBlockMinR)
  270.             shortBlockMaxR = -shortBlockMinR;
  271.  
  272.         /* saturate short block max values */
  273.         if (shortBlockMaxL > REFERENCE_LEVEL)
  274.             {
  275.             shortBlockMaxL = REFERENCE_LEVEL;
  276.             }
  277.         if (shortBlockMaxR > REFERENCE_LEVEL)
  278.             {
  279.             shortBlockMaxR = REFERENCE_LEVEL;
  280.             }
  281.  
  282.         /* use short block max values to update long block max values */
  283.         if (shortBlockMaxL > longBlockPeakL) 
  284.             {
  285.             longBlockPeakL = shortBlockMaxL;
  286.             }
  287.         if (shortBlockMaxR > longBlockPeakR) 
  288.             {
  289.             longBlockPeakR = shortBlockMaxR;
  290.             }
  291.  
  292.         /* look up meter value in decibel table and render on screen */
  293.  
  294.         /* AT THIS POINT, WE HAVE THE METER VALUE TO BE DISPLAYED */
  295.  
  296.         /* the long block is actually
  297.         lMeter->newLevel(levelToDecibelTable[shortBlockMaxL], 
  298.             levelToDecibelTable[longBlockPeakL]);
  299.         rMeter->newLevel(levelToDecibelTable[shortBlockMaxR], 
  300.             levelToDecibelTable[longBlockPeakR]);
  301.         /* clear long block register every second.  Add your code to
  302.          ensure they are not cleared every loop iteration. Simple
  303.         to make it a multiple of the short block interval. */
  304.         longBlockPeakL = longBlockPeakR = 0;    
  305.  
  306.         /* clear short peak registers and short block counter */
  307.         shortBlockMinL = shortBlockMinR = 0;
  308.         shortBlockMaxL = shortBlockMaxR = 0;
  309.         shortBlockCounter = 0;
  310.         }
  311.     }
  312.     }
  313.  
  314.  
  315.  
  316.