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