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 ===========================================================================
26 #include <CoreAudio/AudioHardware.h>
32 #define CHUNK_SIZE 1024
34 static unsigned int submissionChunk = 0; // in sample frames
35 static unsigned int coreaudiotime = 0; // based on the number of chunks submitted so far
36 static qboolean s_isRunning = false;
37 static pthread_mutex_t coreaudio_mutex;
38 static AudioDeviceID outputDeviceID = kAudioDeviceUnknown;
46 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
47 const AudioTimeStamp *inNow,
48 const AudioBufferList *inInputData,
49 const AudioTimeStamp *inInputTime,
50 AudioBufferList *outOutputData,
51 const AudioTimeStamp *inOutputTime,
55 unsigned int frameCount, factor;
57 outBuffer = (float*)outOutputData->mBuffers[0].mData;
58 factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
61 // Lock the snd_renderbuffer
62 if (SndSys_LockRenderBuffer())
64 unsigned int maxFrames, sampleIndex, sampleCount;
65 unsigned int startOffset, endOffset;
67 const float scale = 1.0f / SHRT_MAX;
69 // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
70 maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
71 if (maxFrames >= submissionChunk)
72 frameCount = submissionChunk;
74 frameCount = maxFrames;
76 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
77 startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
78 endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
79 if (startOffset > endOffset) // if the buffer wraps
81 sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels;
82 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
83 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
84 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
86 outBuffer = &outBuffer[sampleCount];
87 sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
88 samples = (const short*)(&snd_renderbuffer->ring[0]);
89 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
90 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
94 sampleCount = frameCount * snd_renderbuffer->format.channels;
95 samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
96 for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
97 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
100 snd_renderbuffer->startframe += frameCount;
102 // Unlock the snd_renderbuffer
103 SndSys_UnlockRenderBuffer();
106 // If there was not enough samples, complete with silence samples
107 if (frameCount < submissionChunk)
109 unsigned int missingFrames;
111 missingFrames = submissionChunk - frameCount;
112 if (developer.integer >= 1000 && vid_activewindow)
113 Con_Printf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
114 memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
117 coreaudiotime += submissionChunk;
126 Create "snd_renderbuffer" with the proper sound format if the call is successful
127 May return a suggested format if the requested format isn't available
130 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
133 UInt32 propertySize, bufferByteCount;
134 AudioStreamBasicDescription streamDesc;
139 Con_Printf("Initializing CoreAudio...\n");
141 if (suggested != NULL)
142 memcpy (suggested, requested, sizeof (suggested));
144 // Get the device status and suggest any appropriate changes to format
145 propertySize = sizeof(streamDesc);
146 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
149 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
152 // Suggest proper settings if they differ
153 if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate || requested->width != streamDesc.mBitsPerChannel/8)
155 if (suggested != NULL)
157 suggested->channels = streamDesc.mChannelsPerFrame;
158 suggested->speed = streamDesc.mSampleRate;
159 suggested->width = streamDesc.mBitsPerChannel/8;
164 // Get the output device
165 propertySize = sizeof(outputDeviceID);
166 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
169 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", status);
172 if (outputDeviceID == kAudioDeviceUnknown)
174 Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
178 // Configure the output device
179 propertySize = sizeof(bufferByteCount);
180 bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
181 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
184 Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE);
188 propertySize = sizeof(bufferByteCount);
189 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
192 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", status);
196 submissionChunk = bufferByteCount / sizeof(float);
197 if (submissionChunk % requested->channels != 0)
199 Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
202 submissionChunk /= requested->channels;
203 Con_DPrintf(" Chunk size = %d sample frames\n", submissionChunk);
205 // Print out the device status
206 propertySize = sizeof(streamDesc);
207 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
210 Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
213 Con_DPrint (" Hardware format:\n");
214 Con_DPrintf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
215 Con_DPrintf(" %c%c%c%c mFormatID\n",
216 (streamDesc.mFormatID & 0xff000000) >> 24,
217 (streamDesc.mFormatID & 0x00ff0000) >> 16,
218 (streamDesc.mFormatID & 0x0000ff00) >> 8,
219 (streamDesc.mFormatID & 0x000000ff) >> 0);
220 Con_DPrintf(" %5d mBytesPerPacket\n", streamDesc.mBytesPerPacket);
221 Con_DPrintf(" %5d mFramesPerPacket\n", streamDesc.mFramesPerPacket);
222 Con_DPrintf(" %5d mBytesPerFrame\n", streamDesc.mBytesPerFrame);
223 Con_DPrintf(" %5d mChannelsPerFrame\n", streamDesc.mChannelsPerFrame);
224 Con_DPrintf(" %5d mBitsPerChannel\n", streamDesc.mBitsPerChannel);
226 if(streamDesc.mFormatID != kAudioFormatLinearPCM)
228 Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
232 // Add the callback function
233 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
236 Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", status);
240 // We haven't sent any sample frames yet
243 if (pthread_mutex_init(&coreaudio_mutex, NULL) != 0)
245 Con_Print("CoreAudio: can't create pthread mutex\n");
246 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
250 snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
252 // Start sound running
253 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
256 Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", status);
257 pthread_mutex_destroy(&coreaudio_mutex);
258 AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
263 Con_Print(" Initialization successful\n");
272 Stop the sound card, delete "snd_renderbuffer" and free its other resources
275 void SndSys_Shutdown(void)
282 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
285 Con_Printf("AudioDeviceStop: returned %d\n", status);
290 pthread_mutex_destroy(&coreaudio_mutex);
292 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
295 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", status);
299 if (snd_renderbuffer != NULL)
301 Mem_Free(snd_renderbuffer->ring);
302 Mem_Free(snd_renderbuffer);
303 snd_renderbuffer = NULL;
312 Submit the contents of "snd_renderbuffer" to the sound card
315 void SndSys_Submit (void)
317 // Nothing to do here (this sound module is callback-based)
325 Returns the number of sample frames consumed since the sound started
328 unsigned int SndSys_GetSoundTime (void)
330 return coreaudiotime;
336 SndSys_LockRenderBuffer
338 Get the exclusive lock on "snd_renderbuffer"
341 qboolean SndSys_LockRenderBuffer (void)
343 return (pthread_mutex_lock(&coreaudio_mutex) == 0);
349 SndSys_UnlockRenderBuffer
351 Release the exclusive lock on "snd_renderbuffer"
354 void SndSys_UnlockRenderBuffer (void)
356 pthread_mutex_unlock(&coreaudio_mutex);