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 float scale = 1.0f / SHRT_MAX;
60 outBuffer = (float*)outOutputData->mBuffers[0].mData;
61 factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
66 // Lock the snd_renderbuffer
67 if (SndSys_LockRenderBuffer())
69 unsigned int maxFrames, sampleCount;
70 unsigned int startOffset, endOffset;
73 if (snd_usethreadedmixing)
75 S_MixToBuffer(mixbuffer, submissionChunk);
76 sampleCount = submissionChunk * snd_renderbuffer->format.channels;
77 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
78 outBuffer[sampleIndex] = mixbuffer[sampleIndex] * scale;
79 // unlock the mutex now
80 SndSys_UnlockRenderBuffer();
84 // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
85 maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
86 if (maxFrames >= submissionChunk)
87 frameCount = submissionChunk;
89 frameCount = maxFrames;
91 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
92 startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
93 endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
94 if (startOffset > endOffset) // if the buffer wraps
96 sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels;
97 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
98 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
99 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
101 outBuffer = &outBuffer[sampleCount];
102 sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
103 samples = (const short*)(&snd_renderbuffer->ring[0]);
104 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
105 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
109 sampleCount = frameCount * snd_renderbuffer->format.channels;
110 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
111 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
112 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
115 snd_renderbuffer->startframe += frameCount;
117 // unlock the mutex now
118 SndSys_UnlockRenderBuffer();
121 // If there was not enough samples, complete with silence samples
122 if (frameCount < submissionChunk)
124 unsigned int missingFrames;
126 missingFrames = submissionChunk - frameCount;
127 if (developer_insane.integer && vid_activewindow)
128 Con_DPrintf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
129 memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
132 coreaudiotime += submissionChunk;
141 Create "snd_renderbuffer" with the proper sound format if the call is successful
142 May return a suggested format if the requested format isn't available
145 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
148 UInt32 propertySize, bufferByteCount;
149 AudioStreamBasicDescription streamDesc;
154 Con_Printf("Initializing CoreAudio...\n");
155 snd_threaded = false;
157 if(requested->width != 2)
159 // we can only do 16bit per sample for now
160 if(suggested != NULL)
162 memcpy (suggested, requested, sizeof (*suggested));
163 suggested->width = 2;
168 // Get the output device
169 propertySize = sizeof(outputDeviceID);
170 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
173 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", (int)status);
176 if (outputDeviceID == kAudioDeviceUnknown)
178 Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
182 // Configure the output device
183 propertySize = sizeof(bufferByteCount);
184 bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
185 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
188 Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", (int)status, CHUNK_SIZE);
192 propertySize = sizeof(bufferByteCount);
193 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
196 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", (int)status);
200 submissionChunk = bufferByteCount / sizeof(float);
201 if (submissionChunk % requested->channels != 0)
203 Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
206 submissionChunk /= requested->channels;
207 Con_Printf(" Chunk size = %d sample frames\n", submissionChunk);
209 // Print out the device status
210 propertySize = sizeof(streamDesc);
211 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
214 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
218 Con_Print (" Hardware format:\n");
219 Con_Printf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
220 Con_Printf(" %c%c%c%c mFormatID\n",
221 (char)(streamDesc.mFormatID >> 24),
222 (char)(streamDesc.mFormatID >> 16),
223 (char)(streamDesc.mFormatID >> 8),
224 (char)(streamDesc.mFormatID >> 0));
225 Con_Printf(" %5u mBytesPerPacket\n", (unsigned int)streamDesc.mBytesPerPacket);
226 Con_Printf(" %5u mFramesPerPacket\n", (unsigned int)streamDesc.mFramesPerPacket);
227 Con_Printf(" %5u mBytesPerFrame\n", (unsigned int)streamDesc.mBytesPerFrame);
228 Con_Printf(" %5u mChannelsPerFrame\n", (unsigned int)streamDesc.mChannelsPerFrame);
229 Con_Printf(" %5u mBitsPerChannel\n", (unsigned int)streamDesc.mBitsPerChannel);
231 // Suggest proper settings if they differ
232 if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate)
234 if (suggested != NULL)
236 memcpy (suggested, requested, sizeof (*suggested));
237 suggested->channels = streamDesc.mChannelsPerFrame;
238 suggested->speed = streamDesc.mSampleRate;
243 if(streamDesc.mFormatID == kAudioFormatLinearPCM)
245 // Add the callback function
246 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
249 // We haven't sent any sample frames yet
251 if (pthread_mutex_init(&coreaudio_mutex, NULL) == 0)
253 if ((snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL)))
255 if ((mixbuffer = Mem_Alloc(snd_mempool, CHUNK_SIZE * sizeof(*mixbuffer) * requested->channels)))
257 // Start sound running
258 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
263 Con_Print(" Initialization successful\n");
267 Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", (int)status);
272 Con_Print("CoreAudio: can't allocate memory for mixbuffer\n");
273 Mem_Free(snd_renderbuffer->ring);
274 Mem_Free(snd_renderbuffer);
275 snd_renderbuffer = NULL;
278 Con_Print("CoreAudio: can't allocate memory for ringbuffer\n");
279 pthread_mutex_destroy(&coreaudio_mutex);
282 Con_Print("CoreAudio: can't create pthread mutex\n");
283 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
286 Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", (int)status);
289 Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
298 Stop the sound card, delete "snd_renderbuffer" and free its other resources
301 void SndSys_Shutdown(void)
308 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
311 Con_Printf("AudioDeviceStop: returned %d\n", (int)status);
316 pthread_mutex_destroy(&coreaudio_mutex);
318 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
321 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", (int)status);
325 if (snd_renderbuffer != NULL)
327 Mem_Free(snd_renderbuffer->ring);
328 Mem_Free(snd_renderbuffer);
329 snd_renderbuffer = NULL;
332 if (mixbuffer != NULL)
342 Submit the contents of "snd_renderbuffer" to the sound card
345 void SndSys_Submit (void)
347 // Nothing to do here (this sound module is callback-based)
355 Returns the number of sample frames consumed since the sound started
358 unsigned int SndSys_GetSoundTime (void)
360 return coreaudiotime;
366 SndSys_LockRenderBuffer
368 Get the exclusive lock on "snd_renderbuffer"
371 qboolean SndSys_LockRenderBuffer (void)
373 return (pthread_mutex_lock(&coreaudio_mutex) == 0);
379 SndSys_UnlockRenderBuffer
381 Release the exclusive lock on "snd_renderbuffer"
384 void SndSys_UnlockRenderBuffer (void)
386 pthread_mutex_unlock(&coreaudio_mutex);
393 Send keyboard events originating from the sound system (e.g. MIDI)
396 void SndSys_SendKeyEvents(void)