1
/*  This file is part of the KDE project.
2
3
    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5
    This library is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU Lesser General Public License as published by
7
    the Free Software Foundation, either version 2.1 or 3 of the License.
8
9
    This library is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
    GNU Lesser General Public License for more details.
13
14
    You should have received a copy of the GNU Lesser General Public License
15
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
#include "audiograph.h"
19
#include "quicktimeaudioplayer.h"
20
#include "medianode.h"
21
22
QT_BEGIN_NAMESPACE
23
24
namespace Phonon
25
{
26
namespace QT7
27
{
28
29
AudioGraph::AudioGraph(MediaNode *root) : MediaNode(AudioGraphNode, 0, root), m_root(root)
30
{
31
    m_audioGraphRef = 0;
32
    m_initialized = false;
33
    m_startedLogically = false;
34
    m_graphCannotPlay = false;
35
    m_paused = false;
36
}
37
38
AudioGraph::~AudioGraph()
39
{
40
    deleteGraph();
41
}
42
43
void AudioGraph::startAllOverFromScratch()
44
{
45
    MediaNodeEvent event(MediaNodeEvent::AudioGraphAboutToBeDeleted, this);
46
    m_root->notify(&event);
47
    deleteGraph();
48
}
49
50
void AudioGraph::deleteGraph()
51
{
52
    if (m_audioGraphRef){
53
    	AUGraphStop(m_audioGraphRef);
54
	    AUGraphUninitialize(m_audioGraphRef);
55
        AUGraphClose(m_audioGraphRef);
56
        DisposeAUGraph(m_audioGraphRef);
57
        m_initialized = false;
58
        m_graphCannotPlay = false;
59
        DEBUG_AUDIO_GRAPH("Graph ref in" << int(this) << "is deleted")
60
    }
61
}
62
63
MediaNode *AudioGraph::root()
64
{
65
    return m_root;
66
}
67
68
AUGraph AudioGraph::audioGraphRef()
69
{
70
    return m_audioGraphRef;
71
}
72
73
void AudioGraph::setStatusCannotPlay()
74
{
75
    DEBUG_AUDIO_GRAPH("Graph" << int(this) << "received 'cannot play' request")
76
    if (!m_graphCannotPlay){
77
        stop();
78
        m_graphCannotPlay = true;
79
        MediaNodeEvent e(MediaNodeEvent::AudioGraphCannotPlay, this);
80
        m_root->notify(&e);
81
    }
82
}
83
84
void AudioGraph::rebuildGraph()
85
{
86
    DEBUG_AUDIO_GRAPH("Graph" << int(this) << "is rebuilding")
87
    startAllOverFromScratch();
88
    if (!openAndInit()){
89
        setStatusCannotPlay();
90
    } else { 
91
        tryStartGraph();
92
        m_graphCannotPlay = false;
93
    }   
94
}
95
96
bool AudioGraph::graphCannotPlay()
97
{
98
    return m_graphCannotPlay;
99
}
100
101
void AudioGraph::updateStreamSpecifications()
102
{
103
    if (!m_initialized){
104
        if (m_graphCannotPlay)
105
            rebuildGraph();
106
        return;
107
    }
108
109
    AudioConnection rootConnection(m_root);
110
    bool updateOk = updateStreamSpecificationRecursive(&rootConnection);
111
    if (!updateOk){
112
        DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.")
113
        rebuildGraph();
114
    }
115
}
116
117
bool AudioGraph::updateStreamSpecificationRecursive(AudioConnection *connection)
118
{
119
    bool updateOk = connection->updateStreamSpecification();
120
    if (!updateOk)
121
        return false;
122
123
    for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){
124
        if (!updateStreamSpecificationRecursive(connection->m_sink->m_audioSinkList[i]))
125
            return false;
126
    }
127
    return true;
128
}
129
130
bool AudioGraph::openAndInit()
131
{
132
	OSStatus err;
133
	err = NewAUGraph(&m_audioGraphRef);
134
    BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false)
135
136
    MediaNodeEvent eventNew(MediaNodeEvent::NewAudioGraph, this);
137
    m_root->notify(&eventNew);
138
139
    AudioConnection rootConnection(m_root);
140
    createAndConnectAuNodesRecursive(&rootConnection);
141
	err = AUGraphOpen(m_audioGraphRef);
142
    BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false)
143
144
    if (!createAudioUnitsRecursive(&rootConnection))
145
        return false;
146
147
	err = AUGraphInitialize(m_audioGraphRef);
148
    BACKEND_ASSERT3(err == noErr, "Could not initialize audio graph.", NORMAL_ERROR, false)
149
150
    m_initialized = true;
151
    MediaNodeEvent eventInit(MediaNodeEvent::AudioGraphInitialized, this);
152
    m_root->notify(&eventInit);
153
    return true;
154
}
155
156
void AudioGraph::createAndConnectAuNodesRecursive(AudioConnection *connection)
157
{
158
    connection->m_sink->m_audioNode->createAndConnectAUNodes();
159
    for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){
160
        AudioConnection *c = connection->m_sink->m_audioSinkList[i];
161
        createAndConnectAuNodesRecursive(c);
162
        bool ok = c->connect(this);
163
        BACKEND_ASSERT2(ok, "Could not connect an audio nodes pair in the audio graph.", NORMAL_ERROR)
164
    }
165
}
166
167
bool AudioGraph::createAudioUnitsRecursive(AudioConnection *connection)
168
{
169
    connection->m_sink->m_audioNode->createAudioUnits();
170
    bool ok = connection->updateStreamSpecification();
171
    if (!ok)
172
        return false;
173
    for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){
174
        if (!createAudioUnitsRecursive(connection->m_sink->m_audioSinkList[i]))
175
            return false;
176
    }
177
    return true;
178
}
179
180
void AudioGraph::tryStartGraph()
181
{
182
    // The graph will only start if the background AUGraph
183
    // is valid. Therefore we just try. If it fails, user
184
    // actions like connect etc. migh make the graph valid
185
    // at a later point.
186
    if (m_startedLogically && !isRunning()){
187
        OSStatus err = AUGraphStart(m_audioGraphRef);
188
        if (err == noErr)
189
            DEBUG_AUDIO_GRAPH("Graph" << int(this) << "started")
190
        else
191
            DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not start")
192
    }
193
}
194
195
bool AudioGraph::isRunning()
196
{
197
    Boolean running = false;
198
    AUGraphIsRunning(m_audioGraphRef, &running);
199
    return running;
200
}
201
202
void AudioGraph::setPaused(bool pause)
203
{
204
    // This function should only make
205
    // a difference if the graph is
206
    // running before pausing.
207
    if (pause){
208
        if (isRunning()){
209
            stop();
210
            m_paused = true;
211
        }
212
    } else if (m_paused){
213
        start();
214
        m_paused = false;
215
    }
216
}
217
218
void AudioGraph::connectLate(AudioConnection *connection)
219
{
220
    MediaNodeEvent event(MediaNodeEvent::NewAudioGraph, this);
221
    connection->m_sink->notify(&event);
222
223
    if (!m_initialized)
224
        return;
225
226
    DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "create and connect audio sink after init:" << int(connection->m_sink->m_audioNode))
227
    AudioConnection startConnection(connection->m_source);
228
    createAndConnectAuNodesRecursive(&startConnection);
229
    
230
    if (!createAudioUnitsRecursive(&startConnection)){
231
        DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.")
232
        rebuildGraph();
233
    }  
234
}
235
236
void AudioGraph::disconnectLate(AudioConnection *connection)
237
{
238
    if (!m_initialized)
239
        return;
240
241
    DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "disconnect audio sink after init:" << int(connection->m_sink->m_audioNode))
242
243
    if (!connection->disconnect(this)){
244
        DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not disconnect audio sink. Rebuild.")
245
        rebuildGraph();
246
    }
247
}
248
249
void AudioGraph::update()
250
{
251
    if (m_startedLogically){
252
        if (m_initialized){
253
            // Quick solution:
254
            AUGraphUpdate(m_audioGraphRef, 0);
255
            tryStartGraph();            
256
        } else
257
            rebuildGraph();
258
    }
259
}
260
261
int AudioGraph::nodeCount()
262
{
263
    if (!m_audioGraphRef)
264
        return 0;
265
    UInt32 count;
266
    AUGraphGetNodeCount(m_audioGraphRef, &count);
267
    return int(count);
268
}
269
270
void AudioGraph::prepare()
271
{
272
    if (!m_initialized)
273
        rebuildGraph();
274
}
275
276
void AudioGraph::start()
277
{
278
    // Start does not mean 'start to play
279
    // music'. It means 'prepare to receive
280
    // audio from the player units'.
281
    DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to start (cannot play:" << m_graphCannotPlay << ")")
282
    m_startedLogically = true;
283
    
284
    if (m_graphCannotPlay)
285
        return;
286
        
287
    if (!m_initialized)
288
        rebuildGraph();
289
290
    if (!m_graphCannotPlay)
291
        tryStartGraph();
292
}
293
294
void AudioGraph::stop()
295
{
296
    DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to stop")
297
    if (m_audioGraphRef)
298
	    AUGraphStop(m_audioGraphRef);
299
    m_startedLogically = false;
300
}
301
302
void AudioGraph::notify(const MediaNodeEvent *event, bool propagate)
303
{
304
    switch (event->type()){
305
        case MediaNodeEvent::StartConnectionChange:
306
            if (m_graphCannotPlay)
307
                startAllOverFromScratch();
308
            break;
309
        case MediaNodeEvent::EndConnectionChange:
310
            update();
311
            break;
312
       default:
313
            break;
314
    }
315
    m_root->notify(event, propagate);
316
}
317
318
}} // namespace Phonon::QT7
319
320
QT_END_NAMESPACE