X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_coreaudio.c;h=ce8fff5bcf7d02073d025d78cc6b9bb20c929f9d;hp=10214b900b9ecc50bc3684f53d1b34487a33cc48;hb=35b7d263c31d6576b488a3df4c1b06ad5acb3ecd;hpb=6824d8ddc8a43cae0609be5bbe8bee01fa1a4225 diff --git a/snd_coreaudio.c b/snd_coreaudio.c index 10214b90..ce8fff5b 100644 --- a/snd_coreaudio.c +++ b/snd_coreaudio.c @@ -20,111 +20,172 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -// snd_coreaudio.c -// all other sound mixing is portable +#include "quakedef.h" #include +#include #include -#include "quakedef.h" #include "snd_main.h" -// BUFFER_SIZE must be an even multiple of CHUNK_SIZE -#define CHUNK_SIZE 2048 -#define BUFFER_SIZE 16384 -static unsigned int submissionChunk; -static unsigned int maxMixedSamples; -static short *s_mixedSamples; -static int s_chunkCount; // number of chunks submitted -static qboolean s_isRunning; +#define CHUNK_SIZE 1024 -static AudioDeviceID outputDeviceID; -static AudioStreamBasicDescription outputStreamBasicDescription; +static unsigned int submissionChunk = 0; // in sample frames +static unsigned int coreaudiotime = 0; // based on the number of chunks submitted so far +static qboolean s_isRunning = false; +static pthread_mutex_t coreaudio_mutex; +static AudioDeviceID outputDeviceID = kAudioDeviceUnknown; +static short *mixbuffer = NULL; /* -=============== +==================== audioDeviceIOProc -=============== +==================== */ - -OSStatus audioDeviceIOProc(AudioDeviceID inDevice, - const AudioTimeStamp *inNow, - const AudioBufferList *inInputData, - const AudioTimeStamp *inInputTime, - AudioBufferList *outOutputData, - const AudioTimeStamp *inOutputTime, - void *inClientData) +static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *inClientData) { - int offset; - short *samples; - unsigned int sampleIndex; float *outBuffer; - float scale; - - offset = (s_chunkCount * submissionChunk) % maxMixedSamples; - samples = s_mixedSamples + offset; + unsigned int frameCount, factor, sampleIndex; + float scale = 1.0f / SHRT_MAX; - outBuffer = (float *)outOutputData->mBuffers[0].mData; + outBuffer = (float*)outOutputData->mBuffers[0].mData; + factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width; + frameCount = 0; + if (snd_blocked) + scale = 0; - // If we have run out of samples, return silence - if (s_chunkCount * submissionChunk > shm->format.channels * paintedtime) + // Lock the snd_renderbuffer + if (SndSys_LockRenderBuffer()) { - memset(outBuffer, 0, sizeof(*outBuffer) * submissionChunk); + unsigned int maxFrames, sampleCount; + unsigned int startOffset, endOffset; + const short *samples; + + if (snd_usethreadedmixing) + { + S_MixToBuffer(mixbuffer, submissionChunk); + sampleCount = submissionChunk * snd_renderbuffer->format.channels; + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = mixbuffer[sampleIndex] * scale; + // unlock the mutex now + SndSys_UnlockRenderBuffer(); + return 0; + } + + // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer + maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe; + if (maxFrames >= submissionChunk) + frameCount = submissionChunk; + else + frameCount = maxFrames; + + // Convert the samples from shorts to floats. Scale the floats to be [-1..1]. + startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes; + endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes; + if (startOffset > endOffset) // if the buffer wraps + { + sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels; + samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]); + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = samples[sampleIndex] * scale; + + outBuffer = &outBuffer[sampleCount]; + sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount; + samples = (const short*)(&snd_renderbuffer->ring[0]); + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = samples[sampleIndex] * scale; + } + else + { + sampleCount = frameCount * snd_renderbuffer->format.channels; + samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]); + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = samples[sampleIndex] * scale; + } + + snd_renderbuffer->startframe += frameCount; + + // unlock the mutex now + SndSys_UnlockRenderBuffer(); } - else + + // If there was not enough samples, complete with silence samples + if (frameCount < submissionChunk) { - // Convert the samples from shorts to floats. Scale the floats to be [-1..1]. - scale = (1.0f / SHRT_MAX); - for (sampleIndex = 0; sampleIndex < submissionChunk; sampleIndex++) - outBuffer[sampleIndex] = samples[sampleIndex] * scale; + unsigned int missingFrames; - s_chunkCount++; // this is the next buffer we will submit + missingFrames = submissionChunk - frameCount; + if (developer_insane.integer && vid_activewindow) + Con_DPrintf("audioDeviceIOProc: %u sample frames missing\n", missingFrames); + memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0])); } + coreaudiotime += submissionChunk; return 0; } + /* -=============== -SNDDMA_Init -=============== +==================== +SndSys_Init + +Create "snd_renderbuffer" with the proper sound format if the call is successful +May return a suggested format if the requested format isn't available +==================== */ -qboolean SNDDMA_Init(void) +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { OSStatus status; UInt32 propertySize, bufferByteCount; + AudioStreamBasicDescription streamDesc; if (s_isRunning) return true; Con_Printf("Initializing CoreAudio...\n"); + snd_threaded = false; + + if(requested->width != 2) + { + // we can only do 16bit per sample for now + if(suggested != NULL) + { + memcpy (suggested, requested, sizeof (*suggested)); + suggested->width = 2; + } + return false; + } // Get the output device propertySize = sizeof(outputDeviceID); status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID); if (status) { - Con_Printf("AudioHardwareGetProperty returned %d\n", status); + Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", (int)status); return false; } - if (outputDeviceID == kAudioDeviceUnknown) { - Con_Printf("AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n"); + Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n"); return false; } // Configure the output device - // TODO: support "-sndspeed", "-sndmono" and "-sndstereo" propertySize = sizeof(bufferByteCount); - bufferByteCount = CHUNK_SIZE * sizeof(float); + bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels; status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount); if (status) { - Con_Printf("AudioDeviceSetProperty: returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE); + Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", (int)status, CHUNK_SIZE); return false; } @@ -132,100 +193,112 @@ qboolean SNDDMA_Init(void) status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount); if (status) { - Con_Printf("AudioDeviceGetProperty: returned %d when setting kAudioDevicePropertyBufferSize\n", status); + Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", (int)status); return false; } - submissionChunk = bufferByteCount / sizeof(float); - Con_DPrintf(" Chunk size = %d samples\n", submissionChunk); - // Print out the device status - propertySize = sizeof(outputStreamBasicDescription); - status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &outputStreamBasicDescription); - if (status) - { - Con_Printf("AudioDeviceGetProperty: returned %d when getting kAudioDevicePropertyStreamFormat\n", status); - return false; - } - Con_DPrintf(" Hardware format:\n"); - Con_DPrintf(" %5d mSampleRate\n", (unsigned int)outputStreamBasicDescription.mSampleRate); - Con_DPrintf(" %c%c%c%c mFormatID\n", - (outputStreamBasicDescription.mFormatID & 0xff000000) >> 24, - (outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16, - (outputStreamBasicDescription.mFormatID & 0x0000ff00) >> 8, - (outputStreamBasicDescription.mFormatID & 0x000000ff) >> 0); - Con_DPrintf(" %5d mBytesPerPacket\n", outputStreamBasicDescription.mBytesPerPacket); - Con_DPrintf(" %5d mFramesPerPacket\n", outputStreamBasicDescription.mFramesPerPacket); - Con_DPrintf(" %5d mBytesPerFrame\n", outputStreamBasicDescription.mBytesPerFrame); - Con_DPrintf(" %5d mChannelsPerFrame\n", outputStreamBasicDescription.mChannelsPerFrame); - Con_DPrintf(" %5d mBitsPerChannel\n", outputStreamBasicDescription.mBitsPerChannel); - - if(outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM) + submissionChunk = bufferByteCount / sizeof(float); + if (submissionChunk % requested->channels != 0) { - Con_Printf("Default Audio Device doesn't support Linear PCM!\n"); + Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n"); return false; } + submissionChunk /= requested->channels; + Con_Printf(" Chunk size = %d sample frames\n", submissionChunk); - // Start sound running - status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL); + // Print out the device status + propertySize = sizeof(streamDesc); + status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc); if (status) { - Con_Printf("AudioDeviceAddIOProc: returned %d\n", status); + Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status); return false; } - maxMixedSamples = BUFFER_SIZE; - s_mixedSamples = (short *)Mem_Alloc (snd_mempool, sizeof(*s_mixedSamples) * maxMixedSamples); - Con_DPrintf(" Buffer size = %d samples (%d chunks)\n", - maxMixedSamples, (maxMixedSamples / submissionChunk)); - - // Tell the main app what we expect from it - memset ((void*)shm, 0, sizeof (*shm)); - shm->format.speed = outputStreamBasicDescription.mSampleRate; - shm->format.channels = outputStreamBasicDescription.mChannelsPerFrame; - shm->format.width = 2; - shm->samples = maxMixedSamples; - shm->buffer = (unsigned char *)s_mixedSamples; - shm->samplepos = 0; - - // We haven't enqueued anything yet - s_chunkCount = 0; - - status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc); - if (status) + Con_Print (" Hardware format:\n"); + Con_Printf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate); + Con_Printf(" %c%c%c%c mFormatID\n", + (char)(streamDesc.mFormatID >> 24), + (char)(streamDesc.mFormatID >> 16), + (char)(streamDesc.mFormatID >> 8), + (char)(streamDesc.mFormatID >> 0)); + Con_Printf(" %5u mBytesPerPacket\n", (unsigned int)streamDesc.mBytesPerPacket); + Con_Printf(" %5u mFramesPerPacket\n", (unsigned int)streamDesc.mFramesPerPacket); + Con_Printf(" %5u mBytesPerFrame\n", (unsigned int)streamDesc.mBytesPerFrame); + Con_Printf(" %5u mChannelsPerFrame\n", (unsigned int)streamDesc.mChannelsPerFrame); + Con_Printf(" %5u mBitsPerChannel\n", (unsigned int)streamDesc.mBitsPerChannel); + + // Suggest proper settings if they differ + if (requested->channels != streamDesc.mChannelsPerFrame || requested->speed != streamDesc.mSampleRate) { - Con_Printf("AudioDeviceStart: returned %d\n", status); + if (suggested != NULL) + { + memcpy (suggested, requested, sizeof (*suggested)); + suggested->channels = streamDesc.mChannelsPerFrame; + suggested->speed = streamDesc.mSampleRate; + } return false; } - s_isRunning = true; - - Con_Printf(" Initialization successful\n"); - - return true; + if(streamDesc.mFormatID == kAudioFormatLinearPCM) + { + // Add the callback function + status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL); + if (!status) + { + // We haven't sent any sample frames yet + coreaudiotime = 0; + if (pthread_mutex_init(&coreaudio_mutex, NULL) == 0) + { + if ((snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL))) + { + if ((mixbuffer = Mem_Alloc(snd_mempool, CHUNK_SIZE * sizeof(*mixbuffer) * requested->channels))) + { + // Start sound running + status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc); + if (!status) + { + s_isRunning = true; + snd_threaded = true; + Con_Print(" Initialization successful\n"); + return true; + } + else + Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", (int)status); + Mem_Free(mixbuffer); + mixbuffer = NULL; + } + else + Con_Print("CoreAudio: can't allocate memory for mixbuffer\n"); + Mem_Free(snd_renderbuffer->ring); + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } + else + Con_Print("CoreAudio: can't allocate memory for ringbuffer\n"); + pthread_mutex_destroy(&coreaudio_mutex); + } + else + Con_Print("CoreAudio: can't create pthread mutex\n"); + AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc); + } + else + Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", (int)status); + } + else + Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n"); + return false; } -/* -=============== -SNDDMA_GetDMAPos - -return the current sample position (in mono samples read) -inside the recirculating dma buffer, so the mixing code will know -how many sample are required to fill it up. -=============== -*/ -int SNDDMA_GetDMAPos(void) -{ - return (s_chunkCount * submissionChunk) % shm->samples; -} /* -=============== -SNDDMA_Shutdown +==================== +SndSys_Shutdown -Reset the sound device for exiting -=============== +Stop the sound card, delete "snd_renderbuffer" and free its other resources +==================== */ -void SNDDMA_Shutdown(void) +void SndSys_Shutdown(void) { OSStatus status; @@ -235,51 +308,92 @@ void SNDDMA_Shutdown(void) status = AudioDeviceStop(outputDeviceID, audioDeviceIOProc); if (status) { - Con_Printf("AudioDeviceStop: returned %d\n", status); + Con_Printf("AudioDeviceStop: returned %d\n", (int)status); return; } - s_isRunning = false; + pthread_mutex_destroy(&coreaudio_mutex); + status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc); if (status) { - Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", status); + Con_Printf("AudioDeviceRemoveIOProc: returned %d\n", (int)status); return; } - Mem_Free(s_mixedSamples); - s_mixedSamples = NULL; - shm->buffer = NULL; + if (snd_renderbuffer != NULL) + { + Mem_Free(snd_renderbuffer->ring); + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } + + if (mixbuffer != NULL) + Mem_Free(mixbuffer); + mixbuffer = NULL; } + /* -=============== -SNDDMA_Submit -=============== +==================== +SndSys_Submit + +Submit the contents of "snd_renderbuffer" to the sound card +==================== */ -void SNDDMA_Submit(void) +void SndSys_Submit (void) { - // nothing to do (CoreAudio is callback-based) + // Nothing to do here (this sound module is callback-based) } + /* -=============== -S_LockBuffer -=============== +==================== +SndSys_GetSoundTime + +Returns the number of sample frames consumed since the sound started +==================== */ -void *S_LockBuffer(void) +unsigned int SndSys_GetSoundTime (void) { - // not necessary (just return the buffer) - return shm->buffer; + return coreaudiotime; } + +/* +==================== +SndSys_LockRenderBuffer + +Get the exclusive lock on "snd_renderbuffer" +==================== +*/ +qboolean SndSys_LockRenderBuffer (void) +{ + return (pthread_mutex_lock(&coreaudio_mutex) == 0); +} + + /* -=============== -S_UnlockBuffer -=============== +==================== +SndSys_UnlockRenderBuffer + +Release the exclusive lock on "snd_renderbuffer" +==================== +*/ +void SndSys_UnlockRenderBuffer (void) +{ + pthread_mutex_unlock(&coreaudio_mutex); +} + +/* +==================== +SndSys_SendKeyEvents + +Send keyboard events originating from the sound system (e.g. MIDI) +==================== */ -void S_UnlockBuffer(void) +void SndSys_SendKeyEvents(void) { - // not necessary + // not supported }