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 ===========================================================================
24 // all other sound mixing is portable
28 #include <CoreAudio/AudioHardware.h>
33 // BUFFER_SIZE must be an even multiple of CHUNK_SIZE
34 #define CHUNK_SIZE 2048
35 #define BUFFER_SIZE 16384
37 static unsigned int submissionChunk;
38 static unsigned int maxMixedSamples;
39 static short *s_mixedSamples;
40 static int s_chunkCount; // number of chunks submitted
41 static qboolean s_isRunning;
43 static AudioDeviceID outputDeviceID;
44 static AudioStreamBasicDescription outputStreamBasicDescription;
53 OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
54 const AudioTimeStamp *inNow,
55 const AudioBufferList *inInputData,
56 const AudioTimeStamp *inInputTime,
57 AudioBufferList *outOutputData,
58 const AudioTimeStamp *inOutputTime,
63 unsigned int sampleIndex;
67 offset = (s_chunkCount * submissionChunk) % maxMixedSamples;
68 samples = s_mixedSamples + offset;
70 outBuffer = (float *)outOutputData->mBuffers[0].mData;
72 // If we have run out of samples, return silence
73 if (s_chunkCount * submissionChunk > shm->format.channels * paintedtime)
75 memset(outBuffer, 0, sizeof(*outBuffer) * submissionChunk);
79 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
80 scale = (1.0f / SHRT_MAX);
81 for (sampleIndex = 0; sampleIndex < submissionChunk; sampleIndex++)
82 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
84 s_chunkCount++; // this is the next buffer we will submit
95 qboolean SNDDMA_Init(void)
98 UInt32 propertySize, bufferByteCount;
103 Con_Printf("Initializing CoreAudio...\n");
105 // Get the output device
106 propertySize = sizeof(outputDeviceID);
107 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
110 Con_Printf("AudioHardwareGetProperty returned %d\n", status);
114 if (outputDeviceID == kAudioDeviceUnknown)
116 Con_Printf("AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n");
120 // Configure the output device
121 // TODO: support "-sndspeed", "-sndmono" and "-sndstereo"
122 propertySize = sizeof(bufferByteCount);
123 bufferByteCount = CHUNK_SIZE * sizeof(float);
124 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
127 Con_Printf("AudioDeviceSetProperty: returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE);
131 propertySize = sizeof(bufferByteCount);
132 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
135 Con_Printf("AudioDeviceGetProperty: returned %d when setting kAudioDevicePropertyBufferSize\n", status);
138 submissionChunk = bufferByteCount / sizeof(float);
139 Con_DPrintf(" Chunk size = %d samples\n", submissionChunk);
141 // Print out the device status
142 propertySize = sizeof(outputStreamBasicDescription);
143 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &outputStreamBasicDescription);
146 Con_Printf("AudioDeviceGetProperty: returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
149 Con_DPrintf(" Hardware format:\n");
150 Con_DPrintf(" %5d mSampleRate\n", (unsigned int)outputStreamBasicDescription.mSampleRate);
151 Con_DPrintf(" %c%c%c%c mFormatID\n",
152 (outputStreamBasicDescription.mFormatID & 0xff000000) >> 24,
153 (outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16,
154 (outputStreamBasicDescription.mFormatID & 0x0000ff00) >> 8,
155 (outputStreamBasicDescription.mFormatID & 0x000000ff) >> 0);
156 Con_DPrintf(" %5d mBytesPerPacket\n", outputStreamBasicDescription.mBytesPerPacket);
157 Con_DPrintf(" %5d mFramesPerPacket\n", outputStreamBasicDescription.mFramesPerPacket);
158 Con_DPrintf(" %5d mBytesPerFrame\n", outputStreamBasicDescription.mBytesPerFrame);
159 Con_DPrintf(" %5d mChannelsPerFrame\n", outputStreamBasicDescription.mChannelsPerFrame);
160 Con_DPrintf(" %5d mBitsPerChannel\n", outputStreamBasicDescription.mBitsPerChannel);
162 if(outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM)
164 Con_Printf("Default Audio Device doesn't support Linear PCM!\n");
168 // Start sound running
169 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
172 Con_Printf("AudioDeviceAddIOProc: returned %d\n", status);
176 maxMixedSamples = BUFFER_SIZE;
177 s_mixedSamples = (short *)Mem_Alloc (snd_mempool, sizeof(*s_mixedSamples) * maxMixedSamples);
178 Con_DPrintf(" Buffer size = %d samples (%d chunks)\n",
179 maxMixedSamples, (maxMixedSamples / submissionChunk));
181 // Tell the main app what we expect from it
182 memset ((void*)shm, 0, sizeof (*shm));
183 shm->format.speed = outputStreamBasicDescription.mSampleRate;
184 shm->format.channels = outputStreamBasicDescription.mChannelsPerFrame;
185 shm->format.width = 2;
186 shm->sampleframes = maxMixedSamples / shm->format.channels;
187 shm->samples = maxMixedSamples;
188 shm->buffer = (unsigned char *)s_mixedSamples;
191 // We haven't enqueued anything yet
194 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
197 Con_Printf("AudioDeviceStart: returned %d\n", status);
203 Con_Printf(" Initialization successful\n");
212 return the current sample position (in mono samples read)
213 inside the recirculating dma buffer, so the mixing code will know
214 how many sample are required to fill it up.
217 int SNDDMA_GetDMAPos(void)
219 return (s_chunkCount * submissionChunk) % shm->samples;
226 Reset the sound device for exiting
229 void SNDDMA_Shutdown(void)
236 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
239 Con_Printf("AudioDeviceStop: returned %d\n", status);
245 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
248 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", status);
252 Mem_Free(s_mixedSamples);
253 s_mixedSamples = NULL;
262 void SNDDMA_Submit(void)
264 // nothing to do (CoreAudio is callback-based)
272 void *S_LockBuffer(void)
274 // not necessary (just return the buffer)
283 void S_UnlockBuffer(void)