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.

AudioMixer.js 2.5KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { getLogger } from '@jitsi/logger';
  2. import { createAudioContext } from './WebAudioUtils';
  3. const logger = getLogger(__filename);
  4. /**
  5. * The AudioMixer, as the name implies, mixes a number of MediaStreams containing audio tracks into a single
  6. * MediaStream.
  7. */
  8. export default class AudioMixer {
  9. /**
  10. * Create AudioMixer instance.
  11. */
  12. constructor() {
  13. this._started = false;
  14. this._streamsToMix = [];
  15. this._streamMSSArray = [];
  16. }
  17. /**
  18. * Add audio MediaStream to be mixed, if the stream doesn't contain any audio tracks it will be ignored.
  19. *
  20. * @param {MediaStream} stream - MediaStream to be mixed.
  21. */
  22. addMediaStream(stream) {
  23. if (!stream.getAudioTracks()) {
  24. logger.warn('Added MediaStream doesn\'t contain audio tracks.');
  25. }
  26. this._streamsToMix.push(stream);
  27. }
  28. /**
  29. * At this point a WebAudio ChannelMergerNode is created and and the two associated MediaStreams are connected to
  30. * it; the resulting mixed MediaStream is returned.
  31. *
  32. * @returns {MediaStream} - MediaStream containing added streams mixed together, or null if no MediaStream
  33. * is added.
  34. */
  35. start() {
  36. // If the mixer was already started just return the existing mixed stream.
  37. if (this._started) {
  38. return this._mixedMSD.stream;
  39. }
  40. this._audioContext = createAudioContext();
  41. if (!this._streamsToMix.length) {
  42. logger.warn('No MediaStream\'s added to AudioMixer, nothing will happen.');
  43. return null;
  44. }
  45. this._started = true;
  46. this._mixedMSD = this._audioContext.createMediaStreamDestination();
  47. for (const stream of this._streamsToMix) {
  48. const streamMSS = this._audioContext.createMediaStreamSource(stream);
  49. streamMSS.connect(this._mixedMSD);
  50. // Maintain a list of MediaStreamAudioSourceNode so we can disconnect them on reset.
  51. this._streamMSSArray.push(streamMSS);
  52. }
  53. return this._mixedMSD.stream;
  54. }
  55. /**
  56. * Disconnect MediaStreamAudioSourceNode and clear references.
  57. *
  58. * @returns {void}
  59. */
  60. reset() {
  61. this._started = false;
  62. this._streamsToMix = [];
  63. // Clean up created MediaStreamAudioSourceNode.
  64. for (const streamMSS of this._streamMSSArray) {
  65. streamMSS.disconnect();
  66. }
  67. this._streamMSSArray = [];
  68. if (this._audioContext) {
  69. this._audioContext = undefined;
  70. }
  71. }
  72. }