|
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 |