analyser.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import { Engine } from "../Engines/engine.js";
  2. import { Tools } from "../Misc/tools.js";
  3. import { EngineStore } from "../Engines/engineStore.js";
  4. /**
  5. * Class used to work with sound analyzer using fast fourier transform (FFT)
  6. * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic
  7. */
  8. export class Analyser {
  9. /**
  10. * Creates a new analyser
  11. * @param scene defines hosting scene
  12. */
  13. constructor(scene) {
  14. /**
  15. * Gets or sets the smoothing
  16. * @ignorenaming
  17. */
  18. this.SMOOTHING = 0.75;
  19. /**
  20. * Gets or sets the FFT table size
  21. * @ignorenaming
  22. */
  23. this.FFT_SIZE = 512;
  24. /**
  25. * Gets or sets the bar graph amplitude
  26. * @ignorenaming
  27. */
  28. this.BARGRAPHAMPLITUDE = 256;
  29. /**
  30. * Gets or sets the position of the debug canvas
  31. * @ignorenaming
  32. */
  33. this.DEBUGCANVASPOS = { x: 20, y: 20 };
  34. /**
  35. * Gets or sets the debug canvas size
  36. * @ignorenaming
  37. */
  38. this.DEBUGCANVASSIZE = { width: 320, height: 200 };
  39. scene = scene || EngineStore.LastCreatedScene;
  40. if (!scene) {
  41. return;
  42. }
  43. this._scene = scene;
  44. if (!Engine.audioEngine) {
  45. Tools.Warn("No audio engine initialized, failed to create an audio analyser");
  46. return;
  47. }
  48. this._audioEngine = Engine.audioEngine;
  49. if (this._audioEngine.canUseWebAudio && this._audioEngine.audioContext) {
  50. this._webAudioAnalyser = this._audioEngine.audioContext.createAnalyser();
  51. this._webAudioAnalyser.minDecibels = -140;
  52. this._webAudioAnalyser.maxDecibels = 0;
  53. this._byteFreqs = new Uint8Array(this._webAudioAnalyser.frequencyBinCount);
  54. this._byteTime = new Uint8Array(this._webAudioAnalyser.frequencyBinCount);
  55. this._floatFreqs = new Float32Array(this._webAudioAnalyser.frequencyBinCount);
  56. }
  57. }
  58. /**
  59. * Get the number of data values you will have to play with for the visualization
  60. * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/frequencyBinCount
  61. * @returns a number
  62. */
  63. getFrequencyBinCount() {
  64. if (this._audioEngine.canUseWebAudio) {
  65. return this._webAudioAnalyser.frequencyBinCount;
  66. }
  67. else {
  68. return 0;
  69. }
  70. }
  71. /**
  72. * Gets the current frequency data as a byte array
  73. * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteFrequencyData
  74. * @returns a Uint8Array
  75. */
  76. getByteFrequencyData() {
  77. if (this._audioEngine.canUseWebAudio) {
  78. this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
  79. this._webAudioAnalyser.fftSize = this.FFT_SIZE;
  80. this._webAudioAnalyser.getByteFrequencyData(this._byteFreqs);
  81. }
  82. return this._byteFreqs;
  83. }
  84. /**
  85. * Gets the current waveform as a byte array
  86. * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteTimeDomainData
  87. * @returns a Uint8Array
  88. */
  89. getByteTimeDomainData() {
  90. if (this._audioEngine.canUseWebAudio) {
  91. this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
  92. this._webAudioAnalyser.fftSize = this.FFT_SIZE;
  93. this._webAudioAnalyser.getByteTimeDomainData(this._byteTime);
  94. }
  95. return this._byteTime;
  96. }
  97. /**
  98. * Gets the current frequency data as a float array
  99. * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteFrequencyData
  100. * @returns a Float32Array
  101. */
  102. getFloatFrequencyData() {
  103. if (this._audioEngine.canUseWebAudio) {
  104. this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
  105. this._webAudioAnalyser.fftSize = this.FFT_SIZE;
  106. this._webAudioAnalyser.getFloatFrequencyData(this._floatFreqs);
  107. }
  108. return this._floatFreqs;
  109. }
  110. /**
  111. * Renders the debug canvas
  112. */
  113. drawDebugCanvas() {
  114. if (this._audioEngine.canUseWebAudio) {
  115. if (!this._debugCanvas) {
  116. this._debugCanvas = document.createElement("canvas");
  117. this._debugCanvas.width = this.DEBUGCANVASSIZE.width;
  118. this._debugCanvas.height = this.DEBUGCANVASSIZE.height;
  119. this._debugCanvas.style.position = "absolute";
  120. this._debugCanvas.style.top = this.DEBUGCANVASPOS.y + "px";
  121. this._debugCanvas.style.left = this.DEBUGCANVASPOS.x + "px";
  122. this._debugCanvasContext = this._debugCanvas.getContext("2d");
  123. document.body.appendChild(this._debugCanvas);
  124. this._registerFunc = () => {
  125. this.drawDebugCanvas();
  126. };
  127. this._scene.registerBeforeRender(this._registerFunc);
  128. }
  129. if (this._registerFunc && this._debugCanvasContext) {
  130. const workingArray = this.getByteFrequencyData();
  131. this._debugCanvasContext.fillStyle = "rgb(0, 0, 0)";
  132. this._debugCanvasContext.fillRect(0, 0, this.DEBUGCANVASSIZE.width, this.DEBUGCANVASSIZE.height);
  133. // Draw the frequency domain chart.
  134. for (let i = 0; i < this.getFrequencyBinCount(); i++) {
  135. const value = workingArray[i];
  136. const percent = value / this.BARGRAPHAMPLITUDE;
  137. const height = this.DEBUGCANVASSIZE.height * percent;
  138. const offset = this.DEBUGCANVASSIZE.height - height - 1;
  139. const barWidth = this.DEBUGCANVASSIZE.width / this.getFrequencyBinCount();
  140. const hue = (i / this.getFrequencyBinCount()) * 360;
  141. this._debugCanvasContext.fillStyle = "hsl(" + hue + ", 100%, 50%)";
  142. this._debugCanvasContext.fillRect(i * barWidth, offset, barWidth, height);
  143. }
  144. }
  145. }
  146. }
  147. /**
  148. * Stops rendering the debug canvas and removes it
  149. */
  150. stopDebugCanvas() {
  151. if (this._debugCanvas) {
  152. if (this._registerFunc) {
  153. this._scene.unregisterBeforeRender(this._registerFunc);
  154. this._registerFunc = null;
  155. }
  156. document.body.removeChild(this._debugCanvas);
  157. this._debugCanvas = null;
  158. this._debugCanvasContext = null;
  159. }
  160. }
  161. /**
  162. * Connects two audio nodes
  163. * @param inputAudioNode defines first node to connect
  164. * @param outputAudioNode defines second node to connect
  165. */
  166. connectAudioNodes(inputAudioNode, outputAudioNode) {
  167. if (this._audioEngine.canUseWebAudio) {
  168. inputAudioNode.connect(this._webAudioAnalyser);
  169. this._webAudioAnalyser.connect(outputAudioNode);
  170. }
  171. }
  172. /**
  173. * Releases all associated resources
  174. */
  175. dispose() {
  176. if (this._audioEngine.canUseWebAudio) {
  177. this._webAudioAnalyser.disconnect();
  178. }
  179. }
  180. }
  181. //# sourceMappingURL=analyser.js.map