AudioScheduledSourceNode.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (C) 2012, Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  17. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  20. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "config.h"
  25. #if ENABLE(WEB_AUDIO)
  26. #include "AudioScheduledSourceNode.h"
  27. #include "AudioContext.h"
  28. #include "AudioUtilities.h"
  29. #include "Event.h"
  30. #include "ScriptController.h"
  31. #include <algorithm>
  32. #include <wtf/MathExtras.h>
  33. using namespace std;
  34. namespace WebCore {
  35. const double AudioScheduledSourceNode::UnknownTime = -1;
  36. AudioScheduledSourceNode::AudioScheduledSourceNode(AudioContext* context, float sampleRate)
  37. : AudioNode(context, sampleRate)
  38. , m_playbackState(UNSCHEDULED_STATE)
  39. , m_startTime(0)
  40. , m_endTime(UnknownTime)
  41. , m_hasEndedListener(false)
  42. {
  43. }
  44. void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
  45. AudioBus* outputBus,
  46. size_t& quantumFrameOffset,
  47. size_t& nonSilentFramesToProcess)
  48. {
  49. ASSERT(outputBus);
  50. if (!outputBus)
  51. return;
  52. ASSERT(quantumFrameSize == AudioNode::ProcessingSizeInFrames);
  53. if (quantumFrameSize != AudioNode::ProcessingSizeInFrames)
  54. return;
  55. double sampleRate = this->sampleRate();
  56. // quantumStartFrame : Start frame of the current time quantum.
  57. // quantumEndFrame : End frame of the current time quantum.
  58. // startFrame : Start frame for this source.
  59. // endFrame : End frame for this source.
  60. size_t quantumStartFrame = context()->currentSampleFrame();
  61. size_t quantumEndFrame = quantumStartFrame + quantumFrameSize;
  62. size_t startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate);
  63. size_t endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate);
  64. // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle.
  65. if (m_endTime != UnknownTime && endFrame <= quantumStartFrame)
  66. finish();
  67. if (m_playbackState == UNSCHEDULED_STATE || m_playbackState == FINISHED_STATE || startFrame >= quantumEndFrame) {
  68. // Output silence.
  69. outputBus->zero();
  70. nonSilentFramesToProcess = 0;
  71. return;
  72. }
  73. // Check if it's time to start playing.
  74. if (m_playbackState == SCHEDULED_STATE) {
  75. // Increment the active source count only if we're transitioning from SCHEDULED_STATE to PLAYING_STATE.
  76. m_playbackState = PLAYING_STATE;
  77. context()->incrementActiveSourceCount();
  78. }
  79. quantumFrameOffset = startFrame > quantumStartFrame ? startFrame - quantumStartFrame : 0;
  80. quantumFrameOffset = min(quantumFrameOffset, quantumFrameSize); // clamp to valid range
  81. nonSilentFramesToProcess = quantumFrameSize - quantumFrameOffset;
  82. if (!nonSilentFramesToProcess) {
  83. // Output silence.
  84. outputBus->zero();
  85. return;
  86. }
  87. // Handle silence before we start playing.
  88. // Zero any initial frames representing silence leading up to a rendering start time in the middle of the quantum.
  89. if (quantumFrameOffset) {
  90. for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
  91. memset(outputBus->channel(i)->mutableData(), 0, sizeof(float) * quantumFrameOffset);
  92. }
  93. // Handle silence after we're done playing.
  94. // If the end time is somewhere in the middle of this time quantum, then zero out the
  95. // frames from the end time to the very end of the quantum.
  96. if (m_endTime != UnknownTime && endFrame >= quantumStartFrame && endFrame < quantumEndFrame) {
  97. size_t zeroStartFrame = endFrame - quantumStartFrame;
  98. size_t framesToZero = quantumFrameSize - zeroStartFrame;
  99. bool isSafe = zeroStartFrame < quantumFrameSize && framesToZero <= quantumFrameSize && zeroStartFrame + framesToZero <= quantumFrameSize;
  100. ASSERT(isSafe);
  101. if (isSafe) {
  102. if (framesToZero > nonSilentFramesToProcess)
  103. nonSilentFramesToProcess = 0;
  104. else
  105. nonSilentFramesToProcess -= framesToZero;
  106. for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
  107. memset(outputBus->channel(i)->mutableData() + zeroStartFrame, 0, sizeof(float) * framesToZero);
  108. }
  109. finish();
  110. }
  111. return;
  112. }
  113. void AudioScheduledSourceNode::start(double when)
  114. {
  115. ASSERT(isMainThread());
  116. if (ScriptController::processingUserGesture())
  117. context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
  118. if (m_playbackState != UNSCHEDULED_STATE)
  119. return;
  120. m_startTime = when;
  121. m_playbackState = SCHEDULED_STATE;
  122. }
  123. void AudioScheduledSourceNode::stop(double when)
  124. {
  125. ASSERT(isMainThread());
  126. if (!(m_playbackState == SCHEDULED_STATE || m_playbackState == PLAYING_STATE))
  127. return;
  128. when = max(0.0, when);
  129. m_endTime = when;
  130. }
  131. #if ENABLE(LEGACY_WEB_AUDIO)
  132. void AudioScheduledSourceNode::noteOn(double when)
  133. {
  134. start(when);
  135. }
  136. void AudioScheduledSourceNode::noteOff(double when)
  137. {
  138. stop(when);
  139. }
  140. #endif
  141. void AudioScheduledSourceNode::setOnended(PassRefPtr<EventListener> listener)
  142. {
  143. m_hasEndedListener = listener;
  144. setAttributeEventListener(eventNames().endedEvent, listener);
  145. }
  146. void AudioScheduledSourceNode::finish()
  147. {
  148. if (m_playbackState != FINISHED_STATE) {
  149. // Let the context dereference this AudioNode.
  150. context()->notifyNodeFinishedProcessing(this);
  151. m_playbackState = FINISHED_STATE;
  152. context()->decrementActiveSourceCount();
  153. }
  154. if (m_hasEndedListener)
  155. callOnMainThread(&AudioScheduledSourceNode::notifyEndedDispatch, this);
  156. }
  157. void AudioScheduledSourceNode::notifyEndedDispatch(void* userData)
  158. {
  159. static_cast<AudioScheduledSourceNode*>(userData)->notifyEnded();
  160. }
  161. void AudioScheduledSourceNode::notifyEnded()
  162. {
  163. EventListener* listener = onended();
  164. if (!listener)
  165. return;
  166. RefPtr<Event> event = Event::create(eventNames().endedEvent, FALSE, FALSE);
  167. event->setTarget(this);
  168. listener->handleEvent(context()->scriptExecutionContext(), event.get());
  169. }
  170. } // namespace WebCore
  171. #endif // ENABLE(WEB_AUDIO)