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