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;
46 extern mempool_t *snd_mempool;
55 OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
56 const AudioTimeStamp *inNow,
57 const AudioBufferList *inInputData,
58 const AudioTimeStamp *inInputTime,
59 AudioBufferList *outOutputData,
60 const AudioTimeStamp *inOutputTime,
65 unsigned int sampleIndex;
69 offset = (s_chunkCount * submissionChunk) % maxMixedSamples;
70 samples = s_mixedSamples + offset;
72 outBuffer = (float *)outOutputData->mBuffers[0].mData;
74 // If we have run out of samples, return silence
75 if (s_chunkCount * submissionChunk > shm->format.channels * paintedtime)
77 memset(outBuffer, 0, sizeof(*outBuffer) * submissionChunk);
81 // Convert the samples from shorts to floats. Scale the floats to be [-1..1].
82 scale = (1.0f / SHRT_MAX);
83 for (sampleIndex = 0; sampleIndex < submissionChunk; sampleIndex++)
84 outBuffer[sampleIndex] = samples[sampleIndex] * scale;
86 s_chunkCount++; // this is the next buffer we will submit
97 qboolean SNDDMA_Init(void)
100 UInt32 propertySize, bufferByteCount;
105 Con_Printf("Initializing CoreAudio...\n");
107 // Get the output device
108 propertySize = sizeof(outputDeviceID);
109 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
112 Con_Printf("AudioHardwareGetProperty returned %d\n", status);
116 if (outputDeviceID == kAudioDeviceUnknown)
118 Con_Printf("AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n");
122 // Configure the output device
123 // TODO: support "-sndspeed", "-sndmono" and "-sndstereo"
124 propertySize = sizeof(bufferByteCount);
125 bufferByteCount = CHUNK_SIZE * sizeof(float);
126 status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
129 Con_Printf("AudioDeviceSetProperty: returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE);
133 propertySize = sizeof(bufferByteCount);
134 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
137 Con_Printf("AudioDeviceGetProperty: returned %d when setting kAudioDevicePropertyBufferSize\n", status);
140 submissionChunk = bufferByteCount / sizeof(float);
141 Con_DPrintf(" Chunk size = %d samples\n", submissionChunk);
143 // Print out the device status
144 propertySize = sizeof(outputStreamBasicDescription);
145 status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &outputStreamBasicDescription);
148 Con_Printf("AudioDeviceGetProperty: returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
151 Con_DPrintf(" Hardware format:\n");
152 Con_DPrintf(" %5d mSampleRate\n", (unsigned int)outputStreamBasicDescription.mSampleRate);
153 Con_DPrintf(" %c%c%c%c mFormatID\n",
154 (outputStreamBasicDescription.mFormatID & 0xff000000) >> 24,
155 (outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16,
156 (outputStreamBasicDescription.mFormatID & 0x0000ff00) >> 8,
157 (outputStreamBasicDescription.mFormatID & 0x000000ff) >> 0);
158 Con_DPrintf(" %5d mBytesPerPacket\n", outputStreamBasicDescription.mBytesPerPacket);
159 Con_DPrintf(" %5d mFramesPerPacket\n", outputStreamBasicDescription.mFramesPerPacket);
160 Con_DPrintf(" %5d mBytesPerFrame\n", outputStreamBasicDescription.mBytesPerFrame);
161 Con_DPrintf(" %5d mChannelsPerFrame\n", outputStreamBasicDescription.mChannelsPerFrame);
162 Con_DPrintf(" %5d mBitsPerChannel\n", outputStreamBasicDescription.mBitsPerChannel);
164 if(outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM)
166 Con_Printf("Default Audio Device doesn't support Linear PCM!\n");
170 // Start sound running
171 status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
174 Con_Printf("AudioDeviceAddIOProc: returned %d\n", status);
178 maxMixedSamples = BUFFER_SIZE;
179 s_mixedSamples = Mem_Alloc (snd_mempool, sizeof(*s_mixedSamples) * maxMixedSamples);
180 Con_DPrintf(" Buffer size = %d samples (%d chunks)\n",
181 maxMixedSamples, (maxMixedSamples / submissionChunk));
183 // Tell the main app what we expect from it
184 memset ((void*)shm, 0, sizeof (*shm));
185 shm->format.speed = outputStreamBasicDescription.mSampleRate;
186 shm->format.channels = outputStreamBasicDescription.mChannelsPerFrame;
187 shm->format.width = 2;
188 shm->samples = maxMixedSamples;
189 shm->buffer = (qbyte *)s_mixedSamples;
192 // We haven't enqueued anything yet
195 status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
198 Con_Printf("AudioDeviceStart: returned %d\n", status);
204 Con_Printf(" Initialization successful\n");
213 return the current sample position (in mono samples read)
214 inside the recirculating dma buffer, so the mixing code will know
215 how many sample are required to fill it up.
218 int SNDDMA_GetDMAPos(void)
220 return (s_chunkCount * submissionChunk) % shm->samples;
227 Reset the sound device for exiting
230 void SNDDMA_Shutdown(void)
237 status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc);
240 Con_Printf("AudioDeviceStop: returned %d\n", status);
246 status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
249 Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", status);
253 Mem_Free(s_mixedSamples);
254 s_mixedSamples = NULL;
263 void SNDDMA_Submit(void)
265 // nothing to do (CoreAudio is callback-based)
273 void *S_LockBuffer(void)
275 // not necessary (just return the buffer)
284 void S_UnlockBuffer(void)