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 25 #include "config.h" 26 27 #if ENABLE(WEB_AUDIO) 28 29 #include "modules/webaudio/AudioScheduledSourceNode.h" 30 31 #include "bindings/core/v8/ExceptionState.h" 32 #include "core/dom/CrossThreadTask.h" 33 #include "core/dom/ExceptionCode.h" 34 #include "modules/EventModules.h" 35 #include "modules/webaudio/AudioContext.h" 36 #include "platform/audio/AudioUtilities.h" 37 #include "wtf/MathExtras.h" 38 #include <algorithm> 39 40 namespace blink { 41 42 const double AudioScheduledSourceNode::UnknownTime = -1; 43 44 AudioScheduledSourceNode::AudioScheduledSourceNode(AudioContext* context, float sampleRate) 45 : AudioSourceNode(context, sampleRate) 46 , m_playbackState(UNSCHEDULED_STATE) 47 , m_startTime(0) 48 , m_endTime(UnknownTime) 49 , m_hasEndedListener(false) 50 { 51 } 52 53 void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize, 54 AudioBus* outputBus, 55 size_t& quantumFrameOffset, 56 size_t& nonSilentFramesToProcess) 57 { 58 ASSERT(outputBus); 59 if (!outputBus) 60 return; 61 62 ASSERT(quantumFrameSize == AudioNode::ProcessingSizeInFrames); 63 if (quantumFrameSize != AudioNode::ProcessingSizeInFrames) 64 return; 65 66 double sampleRate = this->sampleRate(); 67 68 // quantumStartFrame : Start frame of the current time quantum. 69 // quantumEndFrame : End frame of the current time quantum. 70 // startFrame : Start frame for this source. 71 // endFrame : End frame for this source. 72 size_t quantumStartFrame = context()->currentSampleFrame(); 73 size_t quantumEndFrame = quantumStartFrame + quantumFrameSize; 74 size_t startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate); 75 size_t endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate); 76 77 // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle. 78 if (m_endTime != UnknownTime && endFrame <= quantumStartFrame) 79 finish(); 80 81 if (m_playbackState == UNSCHEDULED_STATE || m_playbackState == FINISHED_STATE || startFrame >= quantumEndFrame) { 82 // Output silence. 83 outputBus->zero(); 84 nonSilentFramesToProcess = 0; 85 return; 86 } 87 88 // Check if it's time to start playing. 89 if (m_playbackState == SCHEDULED_STATE) { 90 // Increment the active source count only if we're transitioning from SCHEDULED_STATE to PLAYING_STATE. 91 m_playbackState = PLAYING_STATE; 92 } 93 94 quantumFrameOffset = startFrame > quantumStartFrame ? startFrame - quantumStartFrame : 0; 95 quantumFrameOffset = std::min(quantumFrameOffset, quantumFrameSize); // clamp to valid range 96 nonSilentFramesToProcess = quantumFrameSize - quantumFrameOffset; 97 98 if (!nonSilentFramesToProcess) { 99 // Output silence. 100 outputBus->zero(); 101 return; 102 } 103 104 // Handle silence before we start playing. 105 // Zero any initial frames representing silence leading up to a rendering start time in the middle of the quantum. 106 if (quantumFrameOffset) { 107 for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) 108 memset(outputBus->channel(i)->mutableData(), 0, sizeof(float) * quantumFrameOffset); 109 } 110 111 // Handle silence after we're done playing. 112 // If the end time is somewhere in the middle of this time quantum, then zero out the 113 // frames from the end time to the very end of the quantum. 114 if (m_endTime != UnknownTime && endFrame >= quantumStartFrame && endFrame < quantumEndFrame) { 115 size_t zeroStartFrame = endFrame - quantumStartFrame; 116 size_t framesToZero = quantumFrameSize - zeroStartFrame; 117 118 bool isSafe = zeroStartFrame < quantumFrameSize && framesToZero <= quantumFrameSize && zeroStartFrame + framesToZero <= quantumFrameSize; 119 ASSERT(isSafe); 120 121 if (isSafe) { 122 if (framesToZero > nonSilentFramesToProcess) 123 nonSilentFramesToProcess = 0; 124 else 125 nonSilentFramesToProcess -= framesToZero; 126 127 for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) 128 memset(outputBus->channel(i)->mutableData() + zeroStartFrame, 0, sizeof(float) * framesToZero); 129 } 130 131 finish(); 132 } 133 134 return; 135 } 136 137 void AudioScheduledSourceNode::start(double when, ExceptionState& exceptionState) 138 { 139 ASSERT(isMainThread()); 140 141 if (m_playbackState != UNSCHEDULED_STATE) { 142 exceptionState.throwDOMException( 143 InvalidStateError, 144 "cannot call start more than once."); 145 return; 146 } 147 148 if (!std::isfinite(when) || (when < 0)) { 149 exceptionState.throwDOMException( 150 InvalidStateError, 151 "Start time must be a finite non-negative number: " + String::number(when)); 152 return; 153 } 154 155 m_startTime = when; 156 m_playbackState = SCHEDULED_STATE; 157 } 158 159 void AudioScheduledSourceNode::stop(double when, ExceptionState& exceptionState) 160 { 161 ASSERT(isMainThread()); 162 163 if (m_playbackState == UNSCHEDULED_STATE) { 164 exceptionState.throwDOMException( 165 InvalidStateError, 166 "cannot call stop without calling start first."); 167 return; 168 } 169 170 if (!std::isfinite(when) || (when < 0)) { 171 exceptionState.throwDOMException( 172 InvalidStateError, 173 "Stop time must be a finite non-negative number: " + String::number(when)); 174 return; 175 } 176 177 // stop() can be called more than once, with the last call to stop taking effect, unless the 178 // source has already stopped due to earlier calls to stop. No exceptions are thrown in any 179 // case. 180 when = std::max(0.0, when); 181 m_endTime = when; 182 } 183 184 void AudioScheduledSourceNode::setOnended(PassRefPtr<EventListener> listener) 185 { 186 m_hasEndedListener = listener; 187 setAttributeEventListener(EventTypeNames::ended, listener); 188 } 189 190 void AudioScheduledSourceNode::finish() 191 { 192 if (m_playbackState != FINISHED_STATE) { 193 // Let the context dereference this AudioNode. 194 context()->notifyNodeFinishedProcessing(this); 195 m_playbackState = FINISHED_STATE; 196 } 197 198 if (m_hasEndedListener && context()->executionContext()) { 199 context()->executionContext()->postTask(createCrossThreadTask(&AudioScheduledSourceNode::notifyEnded, this)); 200 } 201 } 202 203 void AudioScheduledSourceNode::notifyEnded() 204 { 205 dispatchEvent(Event::create(EventTypeNames::ended)); 206 } 207 208 } // namespace blink 209 210 #endif // ENABLE(WEB_AUDIO) 211