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