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