babylon.audioEngine.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. module BABYLON {
  2. /**
  3. * This represents an audio engine and it is responsible
  4. * to play, synchronize and analyse sounds throughout the application.
  5. */
  6. export interface IAudioEngine extends IDisposable {
  7. /**
  8. * Gets whether the current host supports Web Audio and thus could create AudioContexts.
  9. */
  10. readonly canUseWebAudio: boolean;
  11. /**
  12. * Gets the current AudioContext if available.
  13. */
  14. readonly audioContext: Nullable<AudioContext>;
  15. /**
  16. * The master gain node defines the global audio volume of your audio engine.
  17. */
  18. readonly masterGain: GainNode;
  19. /**
  20. * Gets whether or not mp3 are supported by your browser.
  21. */
  22. readonly isMP3supported: boolean;
  23. /**
  24. * Gets whether or not ogg are supported by your browser.
  25. */
  26. readonly isOGGsupported: boolean;
  27. /**
  28. * Defines if Babylon should emit a warning if WebAudio is not supported.
  29. * @ignoreNaming
  30. */
  31. WarnedWebAudioUnsupported: boolean;
  32. }
  33. // Sets the default audio engine to Babylon JS.
  34. Engine.AudioEngineFactory = () => { return new AudioEngine(); };
  35. export class AudioEngine implements IAudioEngine{
  36. private _audioContext: Nullable<AudioContext> = null;
  37. private _audioContextInitialized = false;
  38. public canUseWebAudio: boolean = false;
  39. public masterGain: GainNode;
  40. private _connectedAnalyser: Nullable<Analyser>;
  41. public WarnedWebAudioUnsupported: boolean = false;
  42. public unlocked: boolean = false;
  43. public onAudioUnlocked: () => any;
  44. public isMP3supported: boolean = false;
  45. public isOGGsupported: boolean = false;
  46. public get audioContext(): Nullable<AudioContext> {
  47. if (!this._audioContextInitialized) {
  48. this._initializeAudioContext();
  49. }
  50. return this._audioContext;
  51. }
  52. constructor() {
  53. if (typeof window.AudioContext !== 'undefined' || typeof window.webkitAudioContext !== 'undefined') {
  54. window.AudioContext = window.AudioContext || window.webkitAudioContext;
  55. this.canUseWebAudio = true;
  56. }
  57. var audioElem = document.createElement('audio');
  58. try {
  59. if (audioElem && !!audioElem.canPlayType && audioElem.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/, '')) {
  60. this.isMP3supported = true;
  61. }
  62. }
  63. catch (e) {
  64. // protect error during capability check.
  65. }
  66. try {
  67. if (audioElem && !!audioElem.canPlayType && audioElem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '')) {
  68. this.isOGGsupported = true;
  69. }
  70. }
  71. catch (e) {
  72. // protect error during capability check.
  73. }
  74. if (/iPad|iPhone|iPod/.test(navigator.platform)) {
  75. this._unlockiOSaudio();
  76. }
  77. else {
  78. this.unlocked = true;
  79. }
  80. }
  81. private _unlockiOSaudio() {
  82. var unlockaudio = () => {
  83. if (!this.audioContext) {
  84. return;
  85. }
  86. var buffer = this.audioContext.createBuffer(1, 1, 22050);
  87. var source = this.audioContext.createBufferSource();
  88. source.buffer = buffer;
  89. source.connect(this.audioContext.destination);
  90. source.start(0);
  91. setTimeout(() => {
  92. if (((<any>source).playbackState === (<any>source).PLAYING_STATE || (<any>source).playbackState === (<any>source).FINISHED_STATE)) {
  93. this.unlocked = true;
  94. window.removeEventListener('touchend', unlockaudio, false);
  95. if (this.onAudioUnlocked) {
  96. this.onAudioUnlocked();
  97. }
  98. }
  99. }, 0);
  100. };
  101. window.addEventListener('touchend', unlockaudio, false);
  102. }
  103. private _initializeAudioContext() {
  104. try {
  105. if (this.canUseWebAudio) {
  106. this._audioContext = new AudioContext();
  107. // create a global volume gain node
  108. this.masterGain = this._audioContext.createGain();
  109. this.masterGain.gain.value = 1;
  110. this.masterGain.connect(this._audioContext.destination);
  111. this._audioContextInitialized = true;
  112. }
  113. }
  114. catch (e) {
  115. this.canUseWebAudio = false;
  116. Tools.Error("Web Audio: " + e.message);
  117. }
  118. }
  119. public dispose() {
  120. if (this.canUseWebAudio && this._audioContextInitialized) {
  121. if (this._connectedAnalyser && this._audioContext) {
  122. this._connectedAnalyser.stopDebugCanvas();
  123. this._connectedAnalyser.dispose();
  124. this.masterGain.disconnect();
  125. this.masterGain.connect(this._audioContext.destination);
  126. this._connectedAnalyser = null;
  127. }
  128. this.masterGain.gain.value = 1;
  129. }
  130. this.WarnedWebAudioUnsupported = false;
  131. }
  132. public getGlobalVolume(): number {
  133. if (this.canUseWebAudio && this._audioContextInitialized) {
  134. return this.masterGain.gain.value;
  135. }
  136. else {
  137. return -1;
  138. }
  139. }
  140. public setGlobalVolume(newVolume: number) {
  141. if (this.canUseWebAudio && this._audioContextInitialized) {
  142. this.masterGain.gain.value = newVolume;
  143. }
  144. }
  145. public connectToAnalyser(analyser: Analyser) {
  146. if (this._connectedAnalyser) {
  147. this._connectedAnalyser.stopDebugCanvas();
  148. }
  149. if (this.canUseWebAudio && this._audioContextInitialized && this._audioContext) {
  150. this._connectedAnalyser = analyser;
  151. this.masterGain.disconnect();
  152. this._connectedAnalyser.connectAudioNodes(this.masterGain, this._audioContext.destination);
  153. }
  154. }
  155. }
  156. }