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;
47 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
48 const AudioTimeStamp *inNow,
49 const AudioBufferList *inInputData,
50 const AudioTimeStamp *inInputTime,
51 AudioBufferList *outOutputData,
52 const AudioTimeStamp *inOutputTime,
56 unsigned int frameCount, factor;
58 outBuffer = (float*)outOutputData->mBuffers[0].mData;
59 factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
62 // Lock the snd_renderbuffer
63 if (SndSys_LockRenderBuffer())
65 unsigned int maxFrames, sampleIndex, sampleCount;
66 unsigned int startOffset, endOffset;
68 const float scale = 1.0f / SHRT_MAX;
70 // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
71 maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
72 if (maxFrames >= submissionChunk)
73 frameCount = submissionChunk;
75 frameCount = maxFrames;
77 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
78 startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
79 endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
80 if (startOffset > endOffset) // if the buffer wraps
82 sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels;
83 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
84 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
85 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
87 outBuffer = &outBuffer[sampleCount];
88 sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
89 samples = (const short*)(&snd_renderbuffer->ring[0]);
90 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
91 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
95 sampleCount = frameCount * snd_renderbuffer->format.channels;
96 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
97 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
98 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
101 snd_renderbuffer->startframe += frameCount;
103 // Unlock the snd_renderbuffer
104 SndSys_UnlockRenderBuffer();
107 // If there was not enough samples, complete with silence samples
108 if (frameCount < submissionChunk)
110 unsigned int missingFrames;
112 missingFrames = submissionChunk - frameCount;
113 if (developer.integer >= 1000 && vid_activewindow)
114 Con_Printf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
115 memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
118 coreaudiotime += submissionChunk;
127 Create "snd_renderbuffer" with the proper sound format if the call is successful
128 May return a suggested format if the requested format isn't available
131 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
134 UInt32 propertySize, bufferByteCount;
135 AudioStreamBasicDescription streamDesc;
140 Con_Printf("Initializing CoreAudio...\n");
142 if(requested->width != 2)
144 // we can only do 16bit per sample for now
145 if(suggested != NULL)
147 memcpy (suggested, requested, sizeof (*suggested));
148 suggested->width = 2;
153 // Get the output device
154 propertySize = sizeof(outputDeviceID);
155 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
158 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", (int)status);
161 if (outputDeviceID == kAudioDeviceUnknown)
163 Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
167 // Configure the output device
168 propertySize = sizeof(bufferByteCount);
169 bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
170 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
173 Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", (int)status, CHUNK_SIZE);
177 propertySize = sizeof(bufferByteCount);
178 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
181 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", (int)status);
185 submissionChunk = bufferByteCount / sizeof(float);
186 if (submissionChunk % requested->channels != 0)
188 Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
191 submissionChunk /= requested->channels;
192 Con_DPrintf(" Chunk size = %d sample frames\n", submissionChunk);
194 // Print out the device status
195 propertySize = sizeof(streamDesc);
196 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
199 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
203 Con_DPrint (" Hardware format:\n");
204 Con_DPrintf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
205 Con_DPrintf(" %c%c%c%c mFormatID\n",
206 (char)(streamDesc.mFormatID >> 24),
207 (char)(streamDesc.mFormatID >> 16),
208 (char)(streamDesc.mFormatID >> 8),
209 (char)(streamDesc.mFormatID >> 0));
210 Con_DPrintf(" %5u mBytesPerPacket\n", (unsigned int)streamDesc.mBytesPerPacket);
211 Con_DPrintf(" %5u mFramesPerPacket\n", (unsigned int)streamDesc.mFramesPerPacket);
212 Con_DPrintf(" %5u mBytesPerFrame\n", (unsigned int)streamDesc.mBytesPerFrame);
213 Con_DPrintf(" %5u mChannelsPerFrame\n", (unsigned int)streamDesc.mChannelsPerFrame);
214 Con_DPrintf(" %5u mBitsPerChannel\n", (unsigned int)streamDesc.mBitsPerChannel);
216 // Suggest proper settings if they differ
217 if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate)
219 if (suggested != NULL)
221 memcpy (suggested, requested, sizeof (*suggested));
222 suggested->channels = streamDesc.mChannelsPerFrame;
223 suggested->speed = streamDesc.mSampleRate;
228 if(streamDesc.mFormatID != kAudioFormatLinearPCM)
230 Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
234 // Add the callback function
235 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
238 Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", (int)status);
242 // We haven't sent any sample frames yet
245 if (pthread_mutex_init(&coreaudio_mutex, NULL) != 0)
247 Con_Print("CoreAudio: can't create pthread mutex\n");
248 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
252 snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
254 // Start sound running
255 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
258 Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", (int)status);
259 pthread_mutex_destroy(&coreaudio_mutex);
260 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
265 Con_Print(" Initialization successful\n");
274 Stop the sound card, delete "snd_renderbuffer" and free its other resources
277 void SndSys_Shutdown(void)
284 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
287 Con_Printf("AudioDeviceStop: returned %d\n", (int)status);
292 pthread_mutex_destroy(&coreaudio_mutex);
294 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
297 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", (int)status);
301 if (snd_renderbuffer != NULL)
303 Mem_Free(snd_renderbuffer->ring);
304 Mem_Free(snd_renderbuffer);
305 snd_renderbuffer = NULL;
314 Submit the contents of "snd_renderbuffer" to the sound card
317 void SndSys_Submit (void)
319 // Nothing to do here (this sound module is callback-based)
327 Returns the number of sample frames consumed since the sound started
330 unsigned int SndSys_GetSoundTime (void)
332 return coreaudiotime;
338 SndSys_LockRenderBuffer
340 Get the exclusive lock on "snd_renderbuffer"
343 qboolean SndSys_LockRenderBuffer (void)
345 return (pthread_mutex_lock(&coreaudio_mutex) == 0);
351 SndSys_UnlockRenderBuffer
353 Release the exclusive lock on "snd_renderbuffer"
356 void SndSys_UnlockRenderBuffer (void)
358 pthread_mutex_unlock(&coreaudio_mutex);