You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LocalStatsCollector.js 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * Provides statistics for the local stream.
  3. */
  4. var RTCBrowserType = require('../RTC/RTCBrowserType');
  5. /**
  6. * Size of the webaudio analyzer buffer.
  7. * @type {number}
  8. */
  9. var WEBAUDIO_ANALYZER_FFT_SIZE = 2048;
  10. /**
  11. * Value of the webaudio analyzer smoothing time parameter.
  12. * @type {number}
  13. */
  14. var WEBAUDIO_ANALYZER_SMOOTING_TIME = 0.8;
  15. window.AudioContext = window.AudioContext || window.webkitAudioContext;
  16. var context = null;
  17. if(window.AudioContext) {
  18. context = new AudioContext();
  19. // XXX Not all browsers define a suspend method on AudioContext. As the
  20. // invocation is at the (ES6 module) global execution level, it breaks the
  21. // loading of the lib-jitsi-meet library in such browsers and, consequently,
  22. // the loading of the very Web app that uses the lib-jitsi-meet library. For
  23. // example, Google Chrome 40 on Android does not define the method but we
  24. // still want to be able to load the lib-jitsi-meet library there and
  25. // display a page which notifies the user that the Web app is not supported
  26. // there.
  27. context.suspend && context.suspend();
  28. }
  29. /**
  30. * Converts time domain data array to audio level.
  31. * @param samples the time domain data array.
  32. * @returns {number} the audio level
  33. */
  34. function timeDomainDataToAudioLevel(samples) {
  35. var maxVolume = 0;
  36. var length = samples.length;
  37. for (var i = 0; i < length; i++) {
  38. if (maxVolume < samples[i]) {
  39. maxVolume = samples[i];
  40. }
  41. }
  42. return parseFloat(((maxVolume - 127) / 128).toFixed(3));
  43. }
  44. /**
  45. * Animates audio level change
  46. * @param newLevel the new audio level
  47. * @param lastLevel the last audio level
  48. * @returns {Number} the audio level to be set
  49. */
  50. function animateLevel(newLevel, lastLevel) {
  51. var value = 0;
  52. var diff = lastLevel - newLevel;
  53. if(diff > 0.2) {
  54. value = lastLevel - 0.2;
  55. } else if(diff < -0.4) {
  56. value = lastLevel + 0.4;
  57. } else {
  58. value = newLevel;
  59. }
  60. return parseFloat(value.toFixed(3));
  61. }
  62. /**
  63. * <tt>LocalStatsCollector</tt> calculates statistics for the local stream.
  64. *
  65. * @param stream the local stream
  66. * @param interval stats refresh interval given in ms.
  67. * @param callback function that receives the audio levels.
  68. * @constructor
  69. */
  70. function LocalStatsCollector(stream, interval, callback) {
  71. this.stream = stream;
  72. this.intervalId = null;
  73. this.intervalMilis = interval;
  74. this.audioLevel = 0;
  75. this.callback = callback;
  76. }
  77. /**
  78. * Starts the collecting the statistics.
  79. */
  80. LocalStatsCollector.prototype.start = function () {
  81. if (!context ||
  82. RTCBrowserType.isTemasysPluginUsed()) {
  83. return;
  84. }
  85. context.resume();
  86. var analyser = context.createAnalyser();
  87. analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;
  88. analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;
  89. var source = context.createMediaStreamSource(this.stream);
  90. source.connect(analyser);
  91. var self = this;
  92. this.intervalId = setInterval(
  93. function () {
  94. var array = new Uint8Array(analyser.frequencyBinCount);
  95. analyser.getByteTimeDomainData(array);
  96. var audioLevel = timeDomainDataToAudioLevel(array);
  97. if (audioLevel != self.audioLevel) {
  98. self.audioLevel = animateLevel(audioLevel, self.audioLevel);
  99. self.callback(self.audioLevel);
  100. }
  101. },
  102. this.intervalMilis
  103. );
  104. };
  105. /**
  106. * Stops collecting the statistics.
  107. */
  108. LocalStatsCollector.prototype.stop = function () {
  109. if (this.intervalId) {
  110. clearInterval(this.intervalId);
  111. this.intervalId = null;
  112. }
  113. };
  114. module.exports = LocalStatsCollector;