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 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
77 outBuffer[sampleIndex] = mixbuffer[sampleIndex] * scale;
78 // unlock the mutex now
79 SndSys_UnlockRenderBuffer();
83 // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
84 maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
85 if (maxFrames >= submissionChunk)
86 frameCount = submissionChunk;
88 frameCount = maxFrames;
90 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
91 startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
92 endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
93 if (startOffset > endOffset) // if the buffer wraps
95 sampleCount = (snd_renderbuffer->maxframes - startOffset) * 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;
100 outBuffer = &outBuffer[sampleCount];
101 sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
102 samples = (const short*)(&snd_renderbuffer->ring[0]);
103 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
104 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
108 sampleCount = frameCount * snd_renderbuffer->format.channels;
109 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
110 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
111 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
114 snd_renderbuffer->startframe += frameCount;
116 // unlock the mutex now
117 SndSys_UnlockRenderBuffer();
120 // If there was not enough samples, complete with silence samples
121 if (frameCount < submissionChunk)
123 unsigned int missingFrames;
125 missingFrames = submissionChunk - frameCount;
126 if (developer.integer >= 1000 && vid_activewindow)
127 Con_Printf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
128 memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
131 coreaudiotime += submissionChunk;
140 Create "snd_renderbuffer" with the proper sound format if the call is successful
141 May return a suggested format if the requested format isn't available
144 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
147 UInt32 propertySize, bufferByteCount;
148 AudioStreamBasicDescription streamDesc;
153 Con_Printf("Initializing CoreAudio...\n");
154 snd_threaded = false;
156 if(requested->width != 2)
158 // we can only do 16bit per sample for now
159 if(suggested != NULL)
161 memcpy (suggested, requested, sizeof (*suggested));
162 suggested->width = 2;
167 // Get the output device
168 propertySize = sizeof(outputDeviceID);
169 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
172 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", (int)status);
175 if (outputDeviceID == kAudioDeviceUnknown)
177 Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
181 // Configure the output device
182 propertySize = sizeof(bufferByteCount);
183 bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
184 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
187 Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", (int)status, CHUNK_SIZE);
191 propertySize = sizeof(bufferByteCount);
192 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
195 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", (int)status);
199 submissionChunk = bufferByteCount / sizeof(float);
200 if (submissionChunk % requested->channels != 0)
202 Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
205 submissionChunk /= requested->channels;
206 Con_Printf(" Chunk size = %d sample frames\n", submissionChunk);
208 // Print out the device status
209 propertySize = sizeof(streamDesc);
210 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
213 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
217 Con_Print (" Hardware format:\n");
218 Con_Printf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
219 Con_Printf(" %c%c%c%c mFormatID\n",
220 (char)(streamDesc.mFormatID >> 24),
221 (char)(streamDesc.mFormatID >> 16),
222 (char)(streamDesc.mFormatID >> 8),
223 (char)(streamDesc.mFormatID >> 0));
224 Con_Printf(" %5u mBytesPerPacket\n", (unsigned int)streamDesc.mBytesPerPacket);
225 Con_Printf(" %5u mFramesPerPacket\n", (unsigned int)streamDesc.mFramesPerPacket);
226 Con_Printf(" %5u mBytesPerFrame\n", (unsigned int)streamDesc.mBytesPerFrame);
227 Con_Printf(" %5u mChannelsPerFrame\n", (unsigned int)streamDesc.mChannelsPerFrame);
228 Con_Printf(" %5u mBitsPerChannel\n", (unsigned int)streamDesc.mBitsPerChannel);
230 // Suggest proper settings if they differ
231 if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate)
233 if (suggested != NULL)
235 memcpy (suggested, requested, sizeof (*suggested));
236 suggested->channels = streamDesc.mChannelsPerFrame;
237 suggested->speed = streamDesc.mSampleRate;
242 if(streamDesc.mFormatID == kAudioFormatLinearPCM)
244 // Add the callback function
245 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
248 // We haven't sent any sample frames yet
250 if (pthread_mutex_init(&coreaudio_mutex, NULL) == 0)
252 if ((snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL)))
254 if ((mixbuffer = Mem_Alloc(snd_mempool, CHUNK_SIZE * sizeof(*mixbuffer) * requested->channels)))
256 // Start sound running
257 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
262 // FIXME: This causes crashes and weird problems, why doesn't it work?
265 Con_Print(" Initialization successful\n");
269 Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", (int)status);
274 Con_Print("CoreAudio: can't allocate memory for mixbuffer\n");
275 Mem_Free(snd_renderbuffer->ring);
276 Mem_Free(snd_renderbuffer);
277 snd_renderbuffer = NULL;
280 Con_Print("CoreAudio: can't allocate memory for ringbuffer\n");
281 pthread_mutex_destroy(&coreaudio_mutex);
284 Con_Print("CoreAudio: can't create pthread mutex\n");
285 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
288 Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", (int)status);
291 Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
300 Stop the sound card, delete "snd_renderbuffer" and free its other resources
303 void SndSys_Shutdown(void)
310 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
313 Con_Printf("AudioDeviceStop: returned %d\n", (int)status);
318 pthread_mutex_destroy(&coreaudio_mutex);
320 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
323 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", (int)status);
327 if (snd_renderbuffer != NULL)
329 Mem_Free(snd_renderbuffer->ring);
330 Mem_Free(snd_renderbuffer);
331 snd_renderbuffer = NULL;
334 if (mixbuffer != NULL)
344 Submit the contents of "snd_renderbuffer" to the sound card
347 void SndSys_Submit (void)
349 // Nothing to do here (this sound module is callback-based)
357 Returns the number of sample frames consumed since the sound started
360 unsigned int SndSys_GetSoundTime (void)
362 return coreaudiotime;
368 SndSys_LockRenderBuffer
370 Get the exclusive lock on "snd_renderbuffer"
373 qboolean SndSys_LockRenderBuffer (void)
375 return (pthread_mutex_lock(&coreaudio_mutex) == 0);
381 SndSys_UnlockRenderBuffer
383 Release the exclusive lock on "snd_renderbuffer"
386 void SndSys_UnlockRenderBuffer (void)
388 pthread_mutex_unlock(&coreaudio_mutex);