1/* 2 * Copyright (C) 2011, 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 "OfflineAudioDestinationNode.h" 30 31#include "AudioBus.h" 32#include "AudioContext.h" 33#include "HRTFDatabaseLoader.h" 34#include <algorithm> 35#include <wtf/MainThread.h> 36 37using namespace std; 38 39namespace WebCore { 40 41const size_t renderQuantumSize = 128; 42 43OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext* context, AudioBuffer* renderTarget) 44 : AudioDestinationNode(context, renderTarget->sampleRate()) 45 , m_renderTarget(renderTarget) 46 , m_renderThread(0) 47 , m_startedRendering(false) 48{ 49 m_renderBus = AudioBus::create(renderTarget->numberOfChannels(), renderQuantumSize); 50} 51 52OfflineAudioDestinationNode::~OfflineAudioDestinationNode() 53{ 54 uninitialize(); 55} 56 57void OfflineAudioDestinationNode::initialize() 58{ 59 if (isInitialized()) 60 return; 61 62 AudioNode::initialize(); 63} 64 65void OfflineAudioDestinationNode::uninitialize() 66{ 67 if (!isInitialized()) 68 return; 69 70 if (m_renderThread) { 71 waitForThreadCompletion(m_renderThread); 72 m_renderThread = 0; 73 } 74 75 AudioNode::uninitialize(); 76} 77 78void OfflineAudioDestinationNode::startRendering() 79{ 80 ASSERT(isMainThread()); 81 ASSERT(m_renderTarget.get()); 82 if (!m_renderTarget.get()) 83 return; 84 85 if (!m_startedRendering) { 86 m_startedRendering = true; 87 ref(); // See corresponding deref() call in notifyCompleteDispatch(). 88 m_renderThread = createThread(OfflineAudioDestinationNode::offlineRenderEntry, this, "offline renderer"); 89 } 90} 91 92// Do offline rendering in this thread. 93void OfflineAudioDestinationNode::offlineRenderEntry(void* threadData) 94{ 95 OfflineAudioDestinationNode* destinationNode = reinterpret_cast<OfflineAudioDestinationNode*>(threadData); 96 ASSERT(destinationNode); 97 destinationNode->offlineRender(); 98} 99 100void OfflineAudioDestinationNode::offlineRender() 101{ 102 ASSERT(!isMainThread()); 103 ASSERT(m_renderBus.get()); 104 if (!m_renderBus.get()) 105 return; 106 107 bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numberOfChannels(); 108 ASSERT(channelsMatch); 109 if (!channelsMatch) 110 return; 111 112 bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; 113 ASSERT(isRenderBusAllocated); 114 if (!isRenderBusAllocated) 115 return; 116 117 // Synchronize with HRTFDatabaseLoader. 118 // The database must be loaded before we can proceed. 119 HRTFDatabaseLoader* loader = context()->hrtfDatabaseLoader(); 120 ASSERT(loader); 121 if (!loader) 122 return; 123 124 loader->waitForLoaderThreadCompletion(); 125 126 // Break up the render target into smaller "render quantize" sized pieces. 127 // Render until we're finished. 128 size_t framesToProcess = m_renderTarget->length(); 129 unsigned numberOfChannels = m_renderTarget->numberOfChannels(); 130 131 unsigned n = 0; 132 while (framesToProcess > 0) { 133 // Render one render quantum. 134 render(0, m_renderBus.get(), renderQuantumSize); 135 136 size_t framesAvailableToCopy = min(framesToProcess, renderQuantumSize); 137 138 for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) { 139 const float* source = m_renderBus->channel(channelIndex)->data(); 140 float* destination = m_renderTarget->getChannelData(channelIndex)->data(); 141 memcpy(destination + n, source, sizeof(float) * framesAvailableToCopy); 142 } 143 144 n += framesAvailableToCopy; 145 framesToProcess -= framesAvailableToCopy; 146 } 147 148 // Our work is done. Let the AudioContext know. 149 callOnMainThread(notifyCompleteDispatch, this); 150} 151 152void OfflineAudioDestinationNode::notifyCompleteDispatch(void* userData) 153{ 154 OfflineAudioDestinationNode* destinationNode = static_cast<OfflineAudioDestinationNode*>(userData); 155 ASSERT(destinationNode); 156 if (!destinationNode) 157 return; 158 159 destinationNode->notifyComplete(); 160 destinationNode->deref(); 161} 162 163void OfflineAudioDestinationNode::notifyComplete() 164{ 165 context()->fireCompletionEvent(); 166} 167 168} // namespace WebCore 169 170#endif // ENABLE(WEB_AUDIO) 171