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 (18 years, 4 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

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