ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/AudioBackEnd.cpp
Revision: 1.1
Committed: 2006-07-28T13:15:18Z (17 years, 10 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-19, HEAD
Log Message:
Working audio output by Daniel Sumorok.
Not quite the way I wanted to do it but it will do for now.
(on a real Mac, the real audio hardware should be able to pull/grab the data
 from our buffers - an extra thread with its own set of buffers is wasteful!)

File Contents

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