0
|
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 "audionode.h"
|
|
19 |
#include "audiograph.h"
|
|
20 |
#include "audioconnection.h"
|
|
21 |
#include "medianode.h"
|
|
22 |
|
|
23 |
QT_BEGIN_NAMESPACE
|
|
24 |
|
|
25 |
namespace Phonon
|
|
26 |
{
|
|
27 |
namespace QT7
|
|
28 |
{
|
|
29 |
|
|
30 |
AudioNode::AudioNode(int maxInputBusses, int maxOutputBusses)
|
|
31 |
{
|
|
32 |
m_auNode = 0;
|
|
33 |
m_audioUnit = 0;
|
|
34 |
m_audioGraph = 0;
|
|
35 |
m_maxInputBusses = maxInputBusses;
|
|
36 |
m_maxOutputBusses = maxOutputBusses;
|
|
37 |
m_lastConnectionIn = 0;
|
|
38 |
}
|
|
39 |
|
|
40 |
AudioNode::~AudioNode()
|
|
41 |
{
|
|
42 |
setGraph(0);
|
|
43 |
}
|
|
44 |
|
|
45 |
void AudioNode::setGraph(AudioGraph *audioGraph)
|
|
46 |
{
|
|
47 |
if (m_audioGraph == audioGraph)
|
|
48 |
return;
|
|
49 |
|
|
50 |
DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "is setting graph:" << int(audioGraph))
|
|
51 |
if (m_auNode){
|
|
52 |
AUGraphRemoveNode(m_audioGraph->audioGraphRef(), m_auNode);
|
|
53 |
m_auNode = 0;
|
|
54 |
}
|
|
55 |
|
|
56 |
m_audioUnit = 0;
|
|
57 |
m_lastConnectionIn = 0;
|
|
58 |
m_audioGraph = audioGraph;
|
|
59 |
}
|
|
60 |
|
|
61 |
void AudioNode::createAndConnectAUNodes()
|
|
62 |
{
|
|
63 |
if (m_auNode)
|
|
64 |
return;
|
|
65 |
|
|
66 |
ComponentDescription description = getAudioNodeDescription();
|
|
67 |
DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "creates AUNode"
|
|
68 |
<< QString(!FindNextComponent(0, &description) ? "ERROR: COMPONENT NOT FOUND!" : "OK!"))
|
|
69 |
|
|
70 |
OSStatus err = noErr;
|
|
71 |
|
|
72 |
// The proper function to call here is AUGraphAddNode() but the type has
|
|
73 |
// changed between 10.5 and 10.6. it's still OK to call this function, but
|
|
74 |
// if we want to use the proper thing we need to move over to
|
|
75 |
// AudioComponentDescription everywhere, which is very similar to the
|
|
76 |
// ComponentDescription, but a different size. however,
|
|
77 |
// AudioComponentDescription only exists on 10.6+. More fun than we need to
|
|
78 |
// deal with at the moment, so we'll take the "deprecated" warning instead.
|
|
79 |
err = AUGraphNewNode(m_audioGraph->audioGraphRef(), &description, 0, 0, &m_auNode);
|
|
80 |
BACKEND_ASSERT2(err != kAUGraphErr_OutputNodeErr, "A MediaObject can only be connected to one audio output device.", FATAL_ERROR)
|
|
81 |
BACKEND_ASSERT2(err == noErr, "Could not create new AUNode.", FATAL_ERROR)
|
|
82 |
}
|
|
83 |
|
|
84 |
AUNode AudioNode::getInputAUNode()
|
|
85 |
{
|
|
86 |
return m_auNode;
|
|
87 |
}
|
|
88 |
|
|
89 |
AUNode AudioNode::getOutputAUNode()
|
|
90 |
{
|
|
91 |
return m_auNode;
|
|
92 |
}
|
|
93 |
|
|
94 |
void AudioNode::createAudioUnits()
|
|
95 |
{
|
|
96 |
if (m_audioUnit)
|
|
97 |
return;
|
|
98 |
|
|
99 |
DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "creates AudioUnit")
|
|
100 |
OSStatus err = AUGraphGetNodeInfo(m_audioGraph->audioGraphRef(), m_auNode, 0, 0, 0, &m_audioUnit);
|
|
101 |
BACKEND_ASSERT2(err == noErr, "Could not get audio unit from audio node.", FATAL_ERROR)
|
|
102 |
initializeAudioUnit();
|
|
103 |
}
|
|
104 |
|
|
105 |
ComponentDescription AudioNode::getAudioNodeDescription() const
|
|
106 |
{
|
|
107 |
// Override if needed.
|
|
108 |
ComponentDescription cd;
|
|
109 |
Q_UNUSED(cd);
|
|
110 |
return cd;
|
|
111 |
}
|
|
112 |
|
|
113 |
bool AudioNode::setStreamHelp(AudioConnection *c, int bus, OSType scope, bool fromSource)
|
|
114 |
{
|
|
115 |
if (fromSource){
|
|
116 |
OSStatus err = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope,
|
|
117 |
bus, &c->m_sourceStreamDescription, sizeof(AudioStreamBasicDescription));
|
|
118 |
if (err != noErr){
|
|
119 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " - failed setting stream format")
|
|
120 |
return false;
|
|
121 |
}
|
|
122 |
AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_AudioChannelLayout, scope,
|
|
123 |
bus, c->m_sourceChannelLayout, c->m_sourceChannelLayoutSize);
|
|
124 |
} else {
|
|
125 |
OSStatus err = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope,
|
|
126 |
bus, &c->m_sinkStreamDescription, sizeof(AudioStreamBasicDescription));
|
|
127 |
if (err != noErr){
|
|
128 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " - failed setting stream format")
|
|
129 |
return false;
|
|
130 |
}
|
|
131 |
AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_AudioChannelLayout, scope,
|
|
132 |
bus, c->m_sinkChannelLayout, c->m_sourceChannelLayoutSize);
|
|
133 |
}
|
|
134 |
return true;
|
|
135 |
}
|
|
136 |
|
|
137 |
bool AudioNode::setStreamSpecification(AudioConnection *connection, ConnectionSide side)
|
|
138 |
{
|
|
139 |
if (side == Source){
|
|
140 |
// This object am source of connection:
|
|
141 |
if (connection->m_hasSourceSpecification){
|
|
142 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification out"
|
|
143 |
<< connection->m_sourceOutputBus << "from connection source")
|
|
144 |
return setStreamHelp(connection, connection->m_sourceOutputBus, kAudioUnitScope_Output, true);
|
|
145 |
} else {
|
|
146 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "did not set stream specification out")
|
|
147 |
}
|
|
148 |
} else {
|
|
149 |
if (connection->m_hasSinkSpecification){
|
|
150 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification"
|
|
151 |
<< connection->m_sinkInputBus << "from connection sink")
|
|
152 |
return setStreamHelp(connection, connection->m_sinkInputBus, kAudioUnitScope_Input, false);
|
|
153 |
} else if (connection->m_hasSourceSpecification){
|
|
154 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification"
|
|
155 |
<< connection->m_sinkInputBus << "from connection source")
|
|
156 |
return setStreamHelp(connection, connection->m_sinkInputBus, kAudioUnitScope_Input, true);
|
|
157 |
} else {
|
|
158 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "did not set stream specification in")
|
|
159 |
}
|
|
160 |
}
|
|
161 |
return true;
|
|
162 |
}
|
|
163 |
|
|
164 |
bool AudioNode::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side)
|
|
165 |
{
|
|
166 |
if (side == Source){
|
|
167 |
// As default, use the last description to describe the source:
|
|
168 |
if (m_lastConnectionIn->m_hasSinkSpecification){
|
|
169 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is source, and fills in stream spec using last connection sink.")
|
|
170 |
connection->m_sourceStreamDescription = m_lastConnectionIn->m_sinkStreamDescription;
|
|
171 |
connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_lastConnectionIn->m_sinkChannelLayoutSize);
|
|
172 |
memcpy(connection->m_sourceChannelLayout, m_lastConnectionIn->m_sinkChannelLayout, m_lastConnectionIn->m_sinkChannelLayoutSize);
|
|
173 |
connection->m_sourceChannelLayoutSize = m_lastConnectionIn->m_sinkChannelLayoutSize;
|
|
174 |
connection->m_hasSourceSpecification = true;
|
|
175 |
} else if (m_lastConnectionIn->m_hasSourceSpecification){
|
|
176 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is source, and fills in stream spec using last connection source.")
|
|
177 |
connection->m_sourceStreamDescription = m_lastConnectionIn->m_sourceStreamDescription;
|
|
178 |
connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_lastConnectionIn->m_sourceChannelLayoutSize);
|
|
179 |
memcpy(connection->m_sourceChannelLayout, m_lastConnectionIn->m_sourceChannelLayout, m_lastConnectionIn->m_sourceChannelLayoutSize);
|
|
180 |
connection->m_sourceChannelLayoutSize = m_lastConnectionIn->m_sourceChannelLayoutSize;
|
|
181 |
connection->m_hasSourceSpecification = true;
|
|
182 |
} else {
|
|
183 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " __WARNING__: could not get stream specification...")
|
|
184 |
}
|
|
185 |
} else {
|
|
186 |
DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is sink, skips filling in stream.")
|
|
187 |
if (!connection->isSinkOnly())
|
|
188 |
m_lastConnectionIn = connection;
|
|
189 |
}
|
|
190 |
return true;
|
|
191 |
}
|
|
192 |
|
|
193 |
/**
|
|
194 |
Let timeProperty be one of e.g
|
|
195 |
{kAudioUnitProperty_Latency, kAudioUnitProperty_TailTime,
|
|
196 |
kAudioOutputUnitProperty_StartTime, kAudioUnitProperty_CurrentPlayTime}
|
|
197 |
*/
|
|
198 |
Float64 AudioNode::getTimeInSamples(int timeProperty)
|
|
199 |
{
|
|
200 |
if (!m_audioUnit)
|
|
201 |
return 0;
|
|
202 |
|
|
203 |
AudioTimeStamp timeStamp;
|
|
204 |
UInt32 size = sizeof(timeStamp);
|
|
205 |
memset(&timeStamp, 0, sizeof(timeStamp));
|
|
206 |
OSStatus err = AudioUnitGetProperty(m_audioUnit,
|
|
207 |
timeProperty, kAudioUnitScope_Global,
|
|
208 |
0, &timeStamp, &size);
|
|
209 |
if (err != noErr)
|
|
210 |
return 0;
|
|
211 |
return timeStamp.mSampleTime;
|
|
212 |
}
|
|
213 |
|
|
214 |
void AudioNode::notify(const MediaNodeEvent *event)
|
|
215 |
{
|
|
216 |
switch(event->type()){
|
|
217 |
case MediaNodeEvent::AudioGraphAboutToBeDeleted:
|
|
218 |
setGraph(0);
|
|
219 |
break;
|
|
220 |
case MediaNodeEvent::NewAudioGraph:
|
|
221 |
setGraph(static_cast<AudioGraph *>(event->data()));
|
|
222 |
break;
|
|
223 |
default:
|
|
224 |
break;
|
|
225 |
}
|
|
226 |
|
|
227 |
mediaNodeEvent(event);
|
|
228 |
}
|
|
229 |
|
|
230 |
void AudioNode::mediaNodeEvent(const MediaNodeEvent */*event*/)
|
|
231 |
{
|
|
232 |
// Override if needed
|
|
233 |
}
|
|
234 |
|
|
235 |
void AudioNode::initializeAudioUnit()
|
|
236 |
{
|
|
237 |
// Override if needed.
|
|
238 |
}
|
|
239 |
|
|
240 |
}} //namespace Phonon::QT7
|
|
241 |
|
|
242 |
QT_END_NAMESPACE
|