Source: syngen/audio/mixer/auxiliary/reverb.js

  1. /**
  2. * Provides a auxiliary send for global reverb processing.
  3. * Because `ConvolverNode`s are quite intensive, implementations are encouraged to leverage this to provide a single global reverb.
  4. * @augments syngen.utility.pubsub
  5. * @namespace
  6. * @see syngen.audio.mixer.send.reverb
  7. */
  8. syngen.audio.mixer.auxiliary.reverb = (() => {
  9. const context = syngen.audio.context(),
  10. delay = context.createDelay(),
  11. input = context.createGain(),
  12. output = syngen.audio.mixer.createBus(),
  13. pubsub = syngen.utility.pubsub.create()
  14. let active = true,
  15. convolver = context.createConvolver(),
  16. highpass,
  17. lowpass
  18. convolver.buffer = syngen.audio.buffer.impulse.small()
  19. delay.delayTime.value = 1/64
  20. input.connect(delay)
  21. createFilters()
  22. convolver.connect(output)
  23. function createFilters(highpassFrequency = syngen.const.minFrequency, lowpassFrequency = syngen.const.maxFrequency) {
  24. highpass = context.createBiquadFilter()
  25. highpass.type = 'highpass'
  26. highpass.frequency.value = highpassFrequency
  27. lowpass = context.createBiquadFilter()
  28. lowpass.type = 'lowpass'
  29. lowpass.frequency.value = lowpassFrequency
  30. delay.connect(highpass)
  31. highpass.connect(lowpass)
  32. lowpass.connect(convolver)
  33. }
  34. function destroyFilters() {
  35. delay.disconnect()
  36. lowpass.disconnect()
  37. lowpass = null
  38. highpass.disconnect()
  39. highpass = null
  40. }
  41. return syngen.utility.pubsub.decorate({
  42. /**
  43. * Creates a `GainNode` that's connected to the reverb input.
  44. * @memberof syngen.audio.mixer.auxiliary.reverb
  45. * @returns {GainNode}
  46. */
  47. createSend: () => {
  48. const gain = context.createGain()
  49. gain.connect(input)
  50. return gain
  51. },
  52. /**
  53. * Returns whether the processing is active.
  54. * @memberof syngen.audio.mixer.auxiliary.reverb
  55. * @returns {Boolean}
  56. */
  57. isActive: () => active,
  58. /**
  59. * Returns the output node for the send.
  60. * @deprecated
  61. * @memberof syngen.audio.mixer.auxiliary.reverb
  62. * @returns {GainNode}
  63. */
  64. output: () => output,
  65. /**
  66. * Exposes the parameters associated with reverb processing.
  67. * @memberof syngen.audio.mixer.auxiliary.reverb
  68. * @property {AudioParam} delay
  69. * @property {AudioParam} gain
  70. * @property {Object} highpass
  71. * @property {AudioParam} highpass.frequency
  72. * @property {Object} lowpass
  73. * @property {AudioParam} lowpass.frequency
  74. */
  75. param: {
  76. delay: delay.delayTime,
  77. gain: output.gain,
  78. highpass: {
  79. frequency: highpass.frequency,
  80. },
  81. lowpass: {
  82. frequency: lowpass.frequency,
  83. },
  84. },
  85. /**
  86. * Occasionally the filters can enter an unstable or bad state.
  87. * This provides a solution for replacing them with stable filters.
  88. * @memberof syngen.audio.mixer.auxiliary.reverb
  89. * @see syngen.audio.mixer.rebuildFilters
  90. */
  91. rebuildFilters: function () {
  92. const highpassFrequency = highpass.frequency.value,
  93. lowpassFrequency = lowpass.frequency.value
  94. destroyFilters()
  95. createFilters(highpassFrequency, lowpassFrequency)
  96. this.param.highpass.frequency = highpass.frequency
  97. this.param.lowpass.frequency = lowpass.frequency
  98. return this
  99. },
  100. /**
  101. * Sets the active state.
  102. * Implementations can disable processing for a performance boost.
  103. * @fires syngen.audio.mixer.auxiliary.reverb#event:activate
  104. * @fires syngen.audio.mixer.auxiliary.reverb#event:deactivate
  105. * @memberof syngen.audio.mixer.auxiliary.reverb
  106. * @param {Boolean} state
  107. */
  108. setActive: function (state) {
  109. if (active == state) {
  110. return this
  111. }
  112. active = Boolean(state)
  113. if (active) {
  114. /**
  115. * Fired whenever the send is activated.
  116. * @event syngen.audio.mixer.auxiliary.reverb#event:activate
  117. */
  118. pubsub.emit('activate')
  119. input.connect(delay)
  120. } else {
  121. /**
  122. * Fired whenever the send is deactivated.
  123. * @event syngen.audio.mixer.auxiliary.reverb#event:deactivate
  124. */
  125. pubsub.emit('deactivate')
  126. input.disconnect(delay)
  127. }
  128. return this
  129. },
  130. /**
  131. * Sets the impulse buffer for the inner `ConvolverNode`.
  132. * To prevent pops and clicks, the tail of the previous buffer persists until it fades out.
  133. * @memberof syngen.audio.mixer.auxiliary.reverb
  134. * @param {BufferSource} buffer
  135. */
  136. setImpulse: function (buffer) {
  137. input.disconnect()
  138. convolver = context.createConvolver()
  139. convolver.buffer = buffer
  140. convolver.connect(output)
  141. if (active) {
  142. input.connect(delay)
  143. }
  144. return this
  145. },
  146. }, pubsub)
  147. })()