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