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 ===========================================================================
28 #include <CoreAudio/AudioHardware.h>
33 #define CHUNK_SIZE 1024
35 static unsigned int submissionChunk = 0; // in sample frames
36 static unsigned int coreaudiotime = 0; // based on the number of chunks submitted so far
37 static qboolean s_isRunning = false;
38 static pthread_mutex_t coreaudio_mutex;
39 static AudioDeviceID outputDeviceID = kAudioDeviceUnknown;
40 static short *mixbuffer = NULL;
48 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
49 const AudioTimeStamp *inNow,
50 const AudioBufferList *inInputData,
51 const AudioTimeStamp *inInputTime,
52 AudioBufferList *outOutputData,
53 const AudioTimeStamp *inOutputTime,
57 unsigned int frameCount, factor, sampleIndex;
58 const float scale = 1.0f / SHRT_MAX;
60 outBuffer = (float*)outOutputData->mBuffers[0].mData;
61 factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
64 // Lock the snd_renderbuffer
65 if (SndSys_LockRenderBuffer())
67 unsigned int maxFrames, sampleCount;
68 unsigned int startOffset, endOffset;
71 if (snd_usethreadedmixing)
73 S_MixToBuffer(mixbuffer, submissionChunk);
74 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
75 outBuffer[sampleIndex] = mixbuffer[sampleIndex] * scale;
76 // unlock the mutex now
77 SndSys_UnlockRenderBuffer();
81 // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
82 maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
83 if (maxFrames >= submissionChunk)
84 frameCount = submissionChunk;
86 frameCount = maxFrames;
88 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
89 startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
90 endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
91 if (startOffset > endOffset) // if the buffer wraps
93 sampleCount = (snd_renderbuffer->maxframes - startOffset) * 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;
98 outBuffer = &outBuffer[sampleCount];
99 sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
100 samples = (const short*)(&snd_renderbuffer->ring[0]);
101 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
102 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
106 sampleCount = frameCount * snd_renderbuffer->format.channels;
107 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
108 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
109 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
112 snd_renderbuffer->startframe += frameCount;
114 // unlock the mutex now
115 SndSys_UnlockRenderBuffer();
118 // If there was not enough samples, complete with silence samples
119 if (frameCount < submissionChunk)
121 unsigned int missingFrames;
123 missingFrames = submissionChunk - frameCount;
124 if (developer.integer >= 1000 && vid_activewindow)
125 Con_Printf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
126 memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
129 coreaudiotime += submissionChunk;
138 Create "snd_renderbuffer" with the proper sound format if the call is successful
139 May return a suggested format if the requested format isn't available
142 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
145 UInt32 propertySize, bufferByteCount;
146 AudioStreamBasicDescription streamDesc;
151 Con_Printf("Initializing CoreAudio...\n");
152 snd_threaded = false;
154 if(requested->width != 2)
156 // we can only do 16bit per sample for now
157 if(suggested != NULL)
159 memcpy (suggested, requested, sizeof (*suggested));
160 suggested->width = 2;
165 // Get the output device
166 propertySize = sizeof(outputDeviceID);
167 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
170 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", (int)status);
173 if (outputDeviceID == kAudioDeviceUnknown)
175 Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
179 // Configure the output device
180 propertySize = sizeof(bufferByteCount);
181 bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
182 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
185 Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", (int)status, CHUNK_SIZE);
189 propertySize = sizeof(bufferByteCount);
190 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
193 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", (int)status);
197 submissionChunk = bufferByteCount / sizeof(float);
198 if (submissionChunk % requested->channels != 0)
200 Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
203 submissionChunk /= requested->channels;
204 Con_Printf(" Chunk size = %d sample frames\n", submissionChunk);
206 // Print out the device status
207 propertySize = sizeof(streamDesc);
208 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
211 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
215 Con_Print (" Hardware format:\n");
216 Con_Printf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
217 Con_Printf(" %c%c%c%c mFormatID\n",
218 (char)(streamDesc.mFormatID >> 24),
219 (char)(streamDesc.mFormatID >> 16),
220 (char)(streamDesc.mFormatID >> 8),
221 (char)(streamDesc.mFormatID >> 0));
222 Con_Printf(" %5u mBytesPerPacket\n", (unsigned int)streamDesc.mBytesPerPacket);
223 Con_Printf(" %5u mFramesPerPacket\n", (unsigned int)streamDesc.mFramesPerPacket);
224 Con_Printf(" %5u mBytesPerFrame\n", (unsigned int)streamDesc.mBytesPerFrame);
225 Con_Printf(" %5u mChannelsPerFrame\n", (unsigned int)streamDesc.mChannelsPerFrame);
226 Con_Printf(" %5u mBitsPerChannel\n", (unsigned int)streamDesc.mBitsPerChannel);
228 // Suggest proper settings if they differ
229 if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate)
231 if (suggested != NULL)
233 memcpy (suggested, requested, sizeof (*suggested));
234 suggested->channels = streamDesc.mChannelsPerFrame;
235 suggested->speed = streamDesc.mSampleRate;
240 if(streamDesc.mFormatID != kAudioFormatLinearPCM)
242 // Add the callback function
243 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
246 // We haven't sent any sample frames yet
248 if (pthread_mutex_init(&coreaudio_mutex, NULL) == 0)
250 if ((snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL)))
252 if ((mixbuffer = Mem_Alloc(snd_mempool, CHUNK_SIZE * sizeof(*mixbuffer) * requested->channels)))
254 // Start sound running
255 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
260 Con_Print(" Initialization successful\n");
264 Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", (int)status);
269 Con_Print("CoreAudio: can't allocate memory for mixbuffer\n");
270 Mem_Free(snd_renderbuffer->ring);
271 Mem_Free(snd_renderbuffer);
272 snd_renderbuffer = NULL;
275 Con_Print("CoreAudio: can't allocate memory for ringbuffer\n");
276 pthread_mutex_destroy(&coreaudio_mutex);
279 Con_Print("CoreAudio: can't create pthread mutex\n");
280 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
283 Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", (int)status);
286 Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
295 Stop the sound card, delete "snd_renderbuffer" and free its other resources
298 void SndSys_Shutdown(void)
305 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
308 Con_Printf("AudioDeviceStop: returned %d\n", (int)status);
313 pthread_mutex_destroy(&coreaudio_mutex);
315 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
318 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", (int)status);
322 if (snd_renderbuffer != NULL)
324 Mem_Free(snd_renderbuffer->ring);
325 Mem_Free(snd_renderbuffer);
326 snd_renderbuffer = NULL;
329 if (mixbuffer != NULL)
339 Submit the contents of "snd_renderbuffer" to the sound card
342 void SndSys_Submit (void)
344 // Nothing to do here (this sound module is callback-based)
352 Returns the number of sample frames consumed since the sound started
355 unsigned int SndSys_GetSoundTime (void)
357 return coreaudiotime;
363 SndSys_LockRenderBuffer
365 Get the exclusive lock on "snd_renderbuffer"
368 qboolean SndSys_LockRenderBuffer (void)
370 return (pthread_mutex_lock(&coreaudio_mutex) == 0);
376 SndSys_UnlockRenderBuffer
378 Release the exclusive lock on "snd_renderbuffer"
381 void SndSys_UnlockRenderBuffer (void)
383 pthread_mutex_unlock(&coreaudio_mutex);