2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
5 This file is part of Quake III Arena source code.
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Foobar; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
25 #include <CoreAudio/AudioHardware.h>
31 #define CHUNK_SIZE 1024
33 static unsigned int submissionChunk = 0; // in sample frames
34 static unsigned int coreaudiotime = 0; // based on the number of chunks submitted so far
35 static qboolean s_isRunning = false;
36 static pthread_mutex_t coreaudio_mutex;
37 static AudioDeviceID outputDeviceID = kAudioDeviceUnknown;
45 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
46 const AudioTimeStamp *inNow,
47 const AudioBufferList *inInputData,
48 const AudioTimeStamp *inInputTime,
49 AudioBufferList *outOutputData,
50 const AudioTimeStamp *inOutputTime,
54 unsigned int frameCount, factor;
56 outBuffer = (float*)outOutputData->mBuffers[0].mData;
57 factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
60 // Lock the snd_renderbuffer
61 if (SndSys_LockRenderBuffer())
63 unsigned int maxFrames, sampleIndex, sampleCount;
64 unsigned int startOffset, endOffset;
66 const float scale = 1.0f / SHRT_MAX;
68 // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
69 maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
70 if (maxFrames >= submissionChunk)
71 frameCount = submissionChunk;
73 frameCount = maxFrames;
75 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
76 startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
77 endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
78 if (startOffset > endOffset) // if the buffer wraps
80 sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels;
81 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
82 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
83 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
85 outBuffer = &outBuffer[sampleCount];
86 sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
87 samples = (const short*)(&snd_renderbuffer->ring[0]);
88 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
89 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
93 sampleCount = frameCount * snd_renderbuffer->format.channels;
94 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
95 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
96 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
99 snd_renderbuffer->startframe += frameCount;
101 // Unlock the snd_renderbuffer
102 SndSys_UnlockRenderBuffer();
105 // If there was not enough samples, complete with silence samples
106 if (frameCount < submissionChunk)
108 unsigned int missingFrames;
110 missingFrames = submissionChunk - frameCount;
111 Con_DPrintf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
112 memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
115 coreaudiotime += submissionChunk;
124 Create "snd_renderbuffer" with the proper sound format if the call is successful
125 May return a suggested format if the requested format isn't available
128 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
131 UInt32 propertySize, bufferByteCount;
132 AudioStreamBasicDescription streamDesc;
137 Con_Printf("Initializing CoreAudio...\n");
139 // We only accept 16-bit samples for the moment
140 if (requested->width != 2)
142 // Suggest a 16-bit format instead
143 if (suggested != NULL)
145 memcpy (suggested, requested, sizeof (suggested));
146 suggested->width = 2;
152 // Get the output device
153 propertySize = sizeof(outputDeviceID);
154 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
157 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", status);
160 if (outputDeviceID == kAudioDeviceUnknown)
162 Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
166 // Configure the output device
167 propertySize = sizeof(bufferByteCount);
168 bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
169 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
172 Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE);
176 propertySize = sizeof(bufferByteCount);
177 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
180 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", status);
184 submissionChunk = bufferByteCount / sizeof(float);
185 if (submissionChunk % requested->channels != 0)
187 Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
190 submissionChunk /= requested->channels;
191 Con_DPrintf(" Chunk size = %d sample frames\n", submissionChunk);
193 // Print out the device status
194 propertySize = sizeof(streamDesc);
195 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
198 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
201 Con_DPrint (" Hardware format:\n");
202 Con_DPrintf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
203 Con_DPrintf(" %c%c%c%c mFormatID\n",
204 (streamDesc.mFormatID & 0xff000000) >> 24,
205 (streamDesc.mFormatID & 0x00ff0000) >> 16,
206 (streamDesc.mFormatID & 0x0000ff00) >> 8,
207 (streamDesc.mFormatID & 0x000000ff) >> 0);
208 Con_DPrintf(" %5d mBytesPerPacket\n", streamDesc.mBytesPerPacket);
209 Con_DPrintf(" %5d mFramesPerPacket\n", streamDesc.mFramesPerPacket);
210 Con_DPrintf(" %5d mBytesPerFrame\n", streamDesc.mBytesPerFrame);
211 Con_DPrintf(" %5d mChannelsPerFrame\n", streamDesc.mChannelsPerFrame);
212 Con_DPrintf(" %5d mBitsPerChannel\n", streamDesc.mBitsPerChannel);
214 if(streamDesc.mFormatID != kAudioFormatLinearPCM)
216 Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
220 // Add the callback function
221 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
224 Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", status);
228 // We haven't sent any sample frames yet
231 if (pthread_mutex_init(&coreaudio_mutex, NULL) != 0)
233 Con_Print("CoreAudio: can't create pthread mutex\n");
234 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
238 snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
240 // Start sound running
241 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
244 Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", status);
245 pthread_mutex_destroy(&coreaudio_mutex);
246 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
251 Con_Print(" Initialization successful\n");
260 Stop the sound card, delete "snd_renderbuffer" and free its other resources
263 void SndSys_Shutdown(void)
270 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
273 Con_Printf("AudioDeviceStop: returned %d\n", status);
278 pthread_mutex_destroy(&coreaudio_mutex);
280 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
283 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", status);
287 if (snd_renderbuffer != NULL)
289 Mem_Free(snd_renderbuffer->ring);
290 Mem_Free(snd_renderbuffer);
291 snd_renderbuffer = NULL;
300 Submit the contents of "snd_renderbuffer" to the sound card
303 void SndSys_Submit (void)
305 // Nothing to do here (this sound module is callback-based)
313 Returns the number of sample frames consumed since the sound started
316 unsigned int SndSys_GetSoundTime (void)
318 return coreaudiotime;
324 SndSys_LockRenderBuffer
326 Get the exclusive lock on "snd_renderbuffer"
329 qboolean SndSys_LockRenderBuffer (void)
331 return (pthread_mutex_lock(&coreaudio_mutex) == 0);
337 SndSys_UnlockRenderBuffer
339 Release the exclusive lock on "snd_renderbuffer"
342 void SndSys_UnlockRenderBuffer (void)
344 pthread_mutex_unlock(&coreaudio_mutex);