home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #30 / NN_1992_30.iso / spool / comp / sys / sgi / 18122 < prev    next >
Encoding:
Internet Message Format  |  1992-12-16  |  12.8 KB

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