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 "core/dom/CrossThreadTask.h" 32 #include "modules/webaudio/AudioContext.h" 33 #include "platform/Task.h" 34 #include "platform/audio/AudioBus.h" 35 #include "platform/audio/HRTFDatabaseLoader.h" 36 #include "public/platform/Platform.h" 37 #include <algorithm> 38 39 namespace blink { 40 41 const size_t renderQuantumSize = 128; 42 43 OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext* context, AudioBuffer* renderTarget) 44 : AudioDestinationNode(context, renderTarget->sampleRate()) 45 , m_renderTarget(renderTarget) 46 , m_startedRendering(false) 47 { 48 m_renderBus = AudioBus::create(renderTarget->numberOfChannels(), renderQuantumSize); 49 } 50 51 OfflineAudioDestinationNode::~OfflineAudioDestinationNode() 52 { 53 ASSERT(!isInitialized()); 54 } 55 56 void OfflineAudioDestinationNode::dispose() 57 { 58 uninitialize(); 59 AudioDestinationNode::dispose(); 60 } 61 62 void OfflineAudioDestinationNode::initialize() 63 { 64 if (isInitialized()) 65 return; 66 67 AudioNode::initialize(); 68 } 69 70 void OfflineAudioDestinationNode::uninitialize() 71 { 72 if (!isInitialized()) 73 return; 74 75 if (m_renderThread) 76 m_renderThread.clear(); 77 78 AudioNode::uninitialize(); 79 } 80 81 void OfflineAudioDestinationNode::startRendering() 82 { 83 ASSERT(isMainThread()); 84 ASSERT(m_renderTarget.get()); 85 if (!m_renderTarget.get()) 86 return; 87 88 if (!m_startedRendering) { 89 m_startedRendering = true; 90 m_renderThread = adoptPtr(blink::Platform::current()->createThread("Offline Audio Renderer")); 91 m_renderThread->postTask(new Task(bind(&OfflineAudioDestinationNode::offlineRender, this))); 92 } 93 } 94 95 void OfflineAudioDestinationNode::offlineRender() 96 { 97 ASSERT(!isMainThread()); 98 ASSERT(m_renderBus.get()); 99 if (!m_renderBus.get()) 100 return; 101 102 bool isAudioContextInitialized = context()->isInitialized(); 103 ASSERT(isAudioContextInitialized); 104 if (!isAudioContextInitialized) 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 // Break up the render target into smaller "render quantize" sized pieces. 118 // Render until we're finished. 119 size_t framesToProcess = m_renderTarget->length(); 120 unsigned numberOfChannels = m_renderTarget->numberOfChannels(); 121 122 unsigned n = 0; 123 while (framesToProcess > 0) { 124 // Render one render quantum. 125 render(0, m_renderBus.get(), renderQuantumSize); 126 127 size_t framesAvailableToCopy = std::min(framesToProcess, renderQuantumSize); 128 129 for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) { 130 const float* source = m_renderBus->channel(channelIndex)->data(); 131 float* destination = m_renderTarget->getChannelData(channelIndex)->data(); 132 memcpy(destination + n, source, sizeof(float) * framesAvailableToCopy); 133 } 134 135 n += framesAvailableToCopy; 136 framesToProcess -= framesAvailableToCopy; 137 } 138 139 // Our work is done. Let the AudioContext know. 140 if (context()->executionContext()) 141 context()->executionContext()->postTask(createCrossThreadTask(&OfflineAudioDestinationNode::notifyComplete, this)); 142 } 143 144 void OfflineAudioDestinationNode::notifyComplete() 145 { 146 context()->fireCompletionEvent(); 147 } 148 149 void OfflineAudioDestinationNode::trace(Visitor* visitor) 150 { 151 visitor->trace(m_renderTarget); 152 AudioDestinationNode::trace(visitor); 153 } 154 155 } // namespace blink 156 157 #endif // ENABLE(WEB_AUDIO) 158