1 |
nigel |
1.1 |
/* |
2 |
|
|
* |
3 |
|
|
* This is based on Apple example software AudioBackEnd.cpp |
4 |
|
|
* |
5 |
|
|
* Copyright © 2004 Apple Computer, Inc., All Rights Reserved |
6 |
|
|
* Original Apple code modified by Daniel Sumorok |
7 |
|
|
* |
8 |
|
|
* This program is free software; you can redistribute it and/or modify |
9 |
|
|
* it under the terms of the GNU General Public License as published by |
10 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
11 |
|
|
* (at your option) any later version. |
12 |
|
|
* |
13 |
|
|
* This program is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
|
|
* GNU General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* You should have received a copy of the GNU General Public License |
19 |
|
|
* along with this program; if not, write to the Free Software |
20 |
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 |
|
|
*/ |
22 |
|
|
|
23 |
|
|
#include "AudioBackEnd.h" |
24 |
|
|
|
25 |
|
|
#pragma mark ---Public Methods--- |
26 |
|
|
|
27 |
|
|
AudioBackEnd::AudioBackEnd(int bitsPerSample, int numChannels, int sampleRate): |
28 |
|
|
mBitsPerSample(bitsPerSample), |
29 |
|
|
mSampleRate(sampleRate), |
30 |
|
|
mNumChannels(numChannels), |
31 |
|
|
mCallback(NULL), |
32 |
|
|
mCallbackArg(NULL), |
33 |
|
|
mBufferSizeFrames(0), |
34 |
|
|
mFramesProcessed(0), |
35 |
|
|
mAudioBuffer(NULL), |
36 |
|
|
mAudioBufferWriteIndex(0), |
37 |
|
|
mAudioBufferReadIndex(0), |
38 |
|
|
mBytesPerFrame(0), |
39 |
|
|
mAudioBufferSize(0) { |
40 |
|
|
OSStatus err = noErr; |
41 |
|
|
err = Init(); |
42 |
|
|
if(err) { |
43 |
|
|
fprintf(stderr,"AudioBackEnd ERROR: Cannot Init AudioBackEnd"); |
44 |
|
|
exit(1); |
45 |
|
|
} |
46 |
|
|
} |
47 |
|
|
|
48 |
|
|
AudioBackEnd::~AudioBackEnd() { //clean up |
49 |
|
|
Stop(); |
50 |
|
|
|
51 |
|
|
AUGraphClose(mGraph); |
52 |
|
|
DisposeAUGraph(mGraph); |
53 |
|
|
|
54 |
|
|
if(mAudioBuffer != NULL) { |
55 |
|
|
delete mAudioBuffer; |
56 |
|
|
mAudioBuffer = NULL; |
57 |
|
|
mAudioBufferSize = 0; |
58 |
|
|
} |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
OSStatus AudioBackEnd::Init() { |
62 |
|
|
OSStatus err = noErr; |
63 |
|
|
|
64 |
|
|
err = SetupGraph(); |
65 |
|
|
checkErr(err); |
66 |
|
|
|
67 |
|
|
err = SetupBuffers(); |
68 |
|
|
checkErr(err); |
69 |
|
|
|
70 |
|
|
err = AUGraphInitialize(mGraph); |
71 |
|
|
checkErr(err); |
72 |
|
|
|
73 |
|
|
return err; |
74 |
|
|
} |
75 |
|
|
|
76 |
|
|
#pragma mark --- Operation--- |
77 |
|
|
|
78 |
|
|
OSStatus AudioBackEnd::Start() |
79 |
|
|
{ |
80 |
|
|
OSStatus err = noErr; |
81 |
|
|
if(!IsRunning()) { |
82 |
|
|
mFramesProcessed = 0; |
83 |
|
|
mAudioBufferWriteIndex = 0; |
84 |
|
|
mAudioBufferReadIndex = 0; |
85 |
|
|
|
86 |
|
|
err = AUGraphStart(mGraph); |
87 |
|
|
} |
88 |
|
|
return err; |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
OSStatus AudioBackEnd::Stop() { |
92 |
|
|
OSStatus err = noErr; |
93 |
|
|
|
94 |
|
|
if(IsRunning()) { |
95 |
|
|
err = AUGraphStop(mGraph); |
96 |
|
|
} |
97 |
|
|
return err; |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
Boolean AudioBackEnd::IsRunning() { |
101 |
|
|
OSStatus err = noErr; |
102 |
|
|
Boolean graphRunning; |
103 |
|
|
|
104 |
|
|
err = AUGraphIsRunning(mGraph,&graphRunning); |
105 |
|
|
|
106 |
|
|
return (graphRunning); |
107 |
|
|
} |
108 |
|
|
|
109 |
|
|
#pragma mark - |
110 |
|
|
#pragma mark --Private methods--- |
111 |
|
|
OSStatus AudioBackEnd::SetupGraph() { |
112 |
|
|
OSStatus err = noErr; |
113 |
|
|
AURenderCallbackStruct output; |
114 |
|
|
UInt32 size; |
115 |
|
|
ComponentDescription outDesc; |
116 |
|
|
AudioDeviceID out; |
117 |
|
|
|
118 |
|
|
//Make a New Graph |
119 |
|
|
err = NewAUGraph(&mGraph); |
120 |
|
|
checkErr(err); |
121 |
|
|
|
122 |
|
|
//Open the Graph, AudioUnits are opened but not initialized |
123 |
|
|
err = AUGraphOpen(mGraph); |
124 |
|
|
checkErr(err); |
125 |
|
|
|
126 |
|
|
outDesc.componentType = kAudioUnitType_Output; |
127 |
|
|
outDesc.componentSubType = kAudioUnitSubType_DefaultOutput; |
128 |
|
|
outDesc.componentManufacturer = kAudioUnitManufacturer_Apple; |
129 |
|
|
outDesc.componentFlags = 0; |
130 |
|
|
outDesc.componentFlagsMask = 0; |
131 |
|
|
|
132 |
|
|
////////////////////////// |
133 |
|
|
///MAKE NODES |
134 |
|
|
//This creates a node in the graph that is an AudioUnit, using |
135 |
|
|
//the supplied ComponentDescription to find and open that unit |
136 |
|
|
err = AUGraphNewNode(mGraph, &outDesc, 0, NULL, &mOutputNode); |
137 |
|
|
checkErr(err); |
138 |
|
|
|
139 |
|
|
//Get Audio Units from AUGraph node |
140 |
|
|
err = AUGraphGetNodeInfo(mGraph, mOutputNode, NULL, NULL, NULL, &mOutputUnit); |
141 |
|
|
checkErr(err); |
142 |
|
|
|
143 |
|
|
err = AUGraphUpdate(mGraph, NULL); |
144 |
|
|
checkErr(err); |
145 |
|
|
|
146 |
|
|
size = sizeof(AudioDeviceID); |
147 |
|
|
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, |
148 |
|
|
&size, &out); |
149 |
|
|
checkErr(err); |
150 |
|
|
mOutputDevice.Init(out, false); |
151 |
|
|
|
152 |
|
|
//Set the Current Device to the Default Output Unit. |
153 |
|
|
err = AudioUnitSetProperty(mOutputUnit, |
154 |
|
|
kAudioOutputUnitProperty_CurrentDevice, |
155 |
|
|
kAudioUnitScope_Global, |
156 |
|
|
0, |
157 |
|
|
&out, |
158 |
|
|
sizeof(out)); |
159 |
|
|
checkErr(err); |
160 |
|
|
|
161 |
|
|
output.inputProc = OutputProc; |
162 |
|
|
output.inputProcRefCon = this; |
163 |
|
|
|
164 |
|
|
err = AudioUnitSetProperty(mOutputUnit, |
165 |
|
|
kAudioUnitProperty_SetRenderCallback, |
166 |
|
|
kAudioUnitScope_Input, |
167 |
|
|
0, |
168 |
|
|
&output, |
169 |
|
|
sizeof(output)); |
170 |
|
|
checkErr(err); |
171 |
|
|
return err; |
172 |
|
|
} |
173 |
|
|
|
174 |
|
|
//Allocate Audio Buffer List(s) to hold the data from input. |
175 |
|
|
OSStatus AudioBackEnd::SetupBuffers() { |
176 |
|
|
OSStatus err = noErr; |
177 |
|
|
UInt32 safetyOffset; |
178 |
|
|
AudioStreamBasicDescription asbd; |
179 |
|
|
UInt32 propertySize; |
180 |
|
|
|
181 |
|
|
propertySize = sizeof(mBufferSizeFrames); |
182 |
|
|
err = AudioUnitGetProperty(mOutputUnit, kAudioDevicePropertyBufferFrameSize, |
183 |
|
|
kAudioUnitScope_Global, 0, &mBufferSizeFrames, |
184 |
|
|
&propertySize); |
185 |
|
|
|
186 |
|
|
propertySize = sizeof(safetyOffset); |
187 |
|
|
safetyOffset = 0; |
188 |
|
|
err = AudioUnitGetProperty(mOutputUnit, kAudioDevicePropertySafetyOffset, |
189 |
|
|
kAudioUnitScope_Global, 0, &safetyOffset, |
190 |
|
|
&propertySize); |
191 |
|
|
|
192 |
|
|
|
193 |
|
|
asbd.mFormatID = 0x6c70636d; // 'lpcm' |
194 |
|
|
asbd.mFormatFlags = (kAudioFormatFlagIsSignedInteger | |
195 |
|
|
kAudioFormatFlagIsBigEndian | |
196 |
|
|
kAudioFormatFlagIsPacked); |
197 |
|
|
asbd.mChannelsPerFrame = mNumChannels; |
198 |
|
|
asbd.mSampleRate = mSampleRate; |
199 |
|
|
|
200 |
|
|
if(asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger) { |
201 |
|
|
asbd.mBitsPerChannel = mBitsPerSample; |
202 |
|
|
} else if(asbd.mFormatFlags & kAudioFormatFlagIsFloat) { |
203 |
|
|
asbd.mBitsPerChannel = 32; |
204 |
|
|
} else { |
205 |
|
|
asbd.mBitsPerChannel = 0; |
206 |
|
|
} |
207 |
|
|
|
208 |
|
|
asbd.mFramesPerPacket = 1; |
209 |
|
|
asbd.mBytesPerFrame = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame; |
210 |
|
|
asbd.mBytesPerPacket = asbd.mBytesPerFrame * asbd.mFramesPerPacket; |
211 |
|
|
|
212 |
|
|
asbd.mReserved = 0; |
213 |
|
|
|
214 |
|
|
mBytesPerFrame = asbd.mBytesPerFrame; |
215 |
|
|
if((mBytesPerFrame & (mBytesPerFrame - 1)) != 0) { |
216 |
|
|
printf("Audio buffer size must be a power of two!\n"); |
217 |
|
|
return -1; |
218 |
|
|
} |
219 |
|
|
|
220 |
|
|
propertySize = sizeof(asbd); |
221 |
|
|
err = AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, |
222 |
|
|
kAudioUnitScope_Input, 0, &asbd, propertySize); |
223 |
|
|
checkErr(err); |
224 |
|
|
|
225 |
|
|
if(mAudioBuffer != NULL) { |
226 |
|
|
delete mAudioBuffer; |
227 |
|
|
mAudioBufferSize = 0; |
228 |
|
|
} |
229 |
|
|
|
230 |
|
|
mAudioBufferSize = mBytesPerFrame * mBufferSizeFrames * 2; |
231 |
|
|
mAudioBuffer = new UInt8[mAudioBufferSize]; |
232 |
|
|
bzero(mAudioBuffer, mAudioBufferSize); |
233 |
|
|
return err; |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
#pragma mark - |
237 |
|
|
#pragma mark -- IO Procs -- |
238 |
|
|
OSStatus AudioBackEnd::OutputProc(void *inRefCon, |
239 |
|
|
AudioUnitRenderActionFlags *ioActionFlags, |
240 |
|
|
const AudioTimeStamp *TimeStamp, |
241 |
|
|
UInt32 inBusNumber, |
242 |
|
|
UInt32 inNumberFrames, |
243 |
|
|
AudioBufferList * ioData) { |
244 |
|
|
OSStatus err = noErr; |
245 |
|
|
AudioBackEnd *This = (AudioBackEnd *)inRefCon; |
246 |
|
|
UInt8 *dstPtr; |
247 |
|
|
UInt32 bytesToCopy; |
248 |
|
|
UInt32 bytesUntilEnd; |
249 |
|
|
|
250 |
|
|
This->mFramesProcessed += inNumberFrames; |
251 |
|
|
|
252 |
|
|
dstPtr = (UInt8 *)ioData->mBuffers[0].mData; |
253 |
|
|
if(This->mAudioBuffer == NULL) { |
254 |
|
|
bzero(dstPtr, inNumberFrames * This->mBytesPerFrame); |
255 |
|
|
return noErr; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
bytesToCopy = inNumberFrames * This->mBytesPerFrame; |
259 |
|
|
bytesUntilEnd = This->mAudioBufferSize - This->mAudioBufferReadIndex; |
260 |
|
|
if(bytesUntilEnd < bytesToCopy) { |
261 |
|
|
memcpy(dstPtr, &This->mAudioBuffer[This->mAudioBufferReadIndex], |
262 |
|
|
bytesUntilEnd); |
263 |
|
|
memcpy(dstPtr, This->mAudioBuffer, bytesToCopy - bytesUntilEnd); |
264 |
|
|
|
265 |
|
|
This->mAudioBufferReadIndex = bytesToCopy - bytesUntilEnd; |
266 |
|
|
} else { |
267 |
|
|
memcpy(dstPtr, &This->mAudioBuffer[This->mAudioBufferReadIndex], |
268 |
|
|
bytesToCopy); |
269 |
|
|
This->mAudioBufferReadIndex += bytesToCopy; |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
|
273 |
|
|
while(This->mFramesProcessed >= This->mBufferSizeFrames) { |
274 |
|
|
This->mFramesProcessed -= This->mBufferSizeFrames; |
275 |
|
|
if(This->mCallback != NULL) { |
276 |
|
|
This->mCallback(This->mCallbackArg); |
277 |
|
|
} |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
return err; |
281 |
|
|
} |
282 |
|
|
|
283 |
|
|
void AudioBackEnd::setCallback(playthruCallback func, void *arg) { |
284 |
|
|
mCallback = func; |
285 |
|
|
mCallbackArg = arg; |
286 |
|
|
} |
287 |
|
|
|
288 |
|
|
UInt32 AudioBackEnd::BufferSizeFrames() { |
289 |
|
|
return mBufferSizeFrames; |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
int AudioBackEnd::sendAudioBuffer(void *buffer, int numFrames) { |
293 |
|
|
UInt8 *dstBuffer; |
294 |
|
|
int totalBytes; |
295 |
|
|
|
296 |
|
|
mAudioBufferWriteIndex += (mAudioBufferSize / 2); |
297 |
|
|
mAudioBufferWriteIndex &= (mAudioBufferSize - 1); |
298 |
|
|
|
299 |
|
|
dstBuffer = &mAudioBuffer[mAudioBufferWriteIndex]; |
300 |
|
|
totalBytes = mBytesPerFrame * numFrames; |
301 |
|
|
memcpy(dstBuffer, buffer, totalBytes); |
302 |
|
|
|
303 |
|
|
dstBuffer += totalBytes; |
304 |
|
|
bzero(dstBuffer, (mBufferSizeFrames * mBytesPerFrame) - totalBytes); |
305 |
|
|
|
306 |
|
|
return numFrames; |
307 |
|
|
} |