/* =========================================================================== Return to Castle Wolfenstein multiplayer GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”). RTCW MP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW MP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW MP Source Code. If not, see . In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ // mac_snddma.c // all other sound mixing is portable #include #include #include #include "../client/snd_local.h" // For 'ri' #include "../renderer/tr_local.h" #import #import static unsigned int submissionChunk; static unsigned int maxMixedSamples; static short *s_mixedSamples; static int s_chunkCount; // number of chunks submitted static qboolean s_isRunning; static AudioDeviceID outputDeviceID; static AudioStreamBasicDescription outputStreamBasicDescription; /* =============== audioDeviceIOProc =============== */ 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, temp; offset = ( s_chunkCount * submissionChunk ) % maxMixedSamples; samples = s_mixedSamples + offset; assert( outOutputData->mNumberBuffers == 1 ); assert( outOutputData->mBuffers[0].mNumberChannels == 2 ); // assert(outOutputData->mBuffers[0].mDataByteSize == (dma.submission_chunk * sizeof(float))); outBuffer = (float *)outOutputData->mBuffers[0].mData; // If we have run out of samples, return silence if ( s_chunkCount * submissionChunk > dma.channels * s_paintedtime ) { memset( outBuffer, 0, sizeof( *outBuffer ) * dma.submission_chunk ); } else { scale = ( 1.0f / SHRT_MAX ); if ( outputStreamBasicDescription.mSampleRate == 44100 ) { for ( sampleIndex = 0; sampleIndex < dma.submission_chunk; sampleIndex++ ) { // Convert the samples from shorts to floats. Scale the floats to be [-1..1]. temp = samples[sampleIndex] * scale; outBuffer[( sampleIndex << 1 ) + 0] = temp; outBuffer[( sampleIndex << 1 ) + 1] = temp; } } else { for ( sampleIndex = 0; sampleIndex < dma.submission_chunk; sampleIndex++ ) { // Convert the samples from shorts to floats. Scale the floats to be [-1..1]. outBuffer[sampleIndex] = samples[sampleIndex] * scale; } } } s_chunkCount++; // this is the next buffer we will submit return 0; } /* =============== S_MakeTestPattern =============== */ void S_MakeTestPattern( void ) { int i; float v; int sample; for ( i = 0 ; i < dma.samples / 2 ; i++ ) { v = sin( M_PI * 2 * i / 64 ); sample = v * 0x4000; ( (short *)dma.buffer )[i * 2] = sample; ( (short *)dma.buffer )[i * 2 + 1] = sample; } } /* =============== SNDDMA_Init =============== */ qboolean SNDDMA_Init( void ) { cvar_t *bufferSize; cvar_t *chunkSize; OSStatus status; UInt32 propertySize, bufferByteCount; if ( s_isRunning ) { return qtrue; } chunkSize = ri.Cvar_Get( "s_chunksize", "512", CVAR_ARCHIVE ); bufferSize = ri.Cvar_Get( "s_buffersize", "16384", CVAR_ARCHIVE ); Com_Printf( " Chunk size = %d\n", chunkSize->integer ); Com_Printf( "Buffer size = %d\n", bufferSize->integer ); if ( !chunkSize->integer ) { ri.Error( ERR_FATAL, "s_chunksize must be non-zero\n" ); } if ( !bufferSize->integer ) { ri.Error( ERR_FATAL, "s_buffersize must be non-zero\n" ); } if ( chunkSize->integer >= bufferSize->integer ) { ri.Error( ERR_FATAL, "s_chunksize must be less than s_buffersize\n" ); } if ( bufferSize->integer % chunkSize->integer ) { ri.Error( ERR_FATAL, "s_buffersize must be an even multiple of s_chunksize\n" ); } // Get the output device propertySize = sizeof( outputDeviceID ); status = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID ); if ( status ) { Com_Printf( "AudioHardwareGetProperty returned %d\n", status ); return qfalse; } if ( outputDeviceID == kAudioDeviceUnknown ) { Com_Printf( "AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n" ); return qfalse; } // Configure the output device propertySize = sizeof( bufferByteCount ); bufferByteCount = chunkSize->integer * sizeof( float ); status = AudioDeviceSetProperty( outputDeviceID, NULL, 0, NO, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount ); if ( status ) { Com_Printf( "AudioDeviceSetProperty: returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, chunkSize->integer ); return qfalse; } propertySize = sizeof( bufferByteCount ); status = AudioDeviceGetProperty( outputDeviceID, 0, NO, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount ); if ( status ) { Com_Printf( "AudioDeviceGetProperty: returned %d when setting kAudioDevicePropertyBufferSize\n", status ); return qfalse; } // Print out the device status propertySize = sizeof( outputStreamBasicDescription ); status = AudioDeviceGetProperty( outputDeviceID, 0, NO, kAudioDevicePropertyStreamFormat, &propertySize, &outputStreamBasicDescription ); if ( status ) { Com_Printf( "AudioDeviceGetProperty: returned %d when getting kAudioDevicePropertyStreamFormat\n", status ); return qfalse; } Com_Printf( "Hardware format:\n" ); Com_Printf( " %f mSampleRate\n", outputStreamBasicDescription.mSampleRate ); Com_Printf( " %c%c%c%c mFormatID\n", ( outputStreamBasicDescription.mFormatID & 0xff000000 ) >> 24, ( outputStreamBasicDescription.mFormatID & 0x00ff0000 ) >> 16, ( outputStreamBasicDescription.mFormatID & 0x0000ff00 ) >> 8, ( outputStreamBasicDescription.mFormatID & 0x000000ff ) >> 0 ); Com_Printf( " %5d mBytesPerPacket\n", outputStreamBasicDescription.mBytesPerPacket ); Com_Printf( " %5d mFramesPerPacket\n", outputStreamBasicDescription.mFramesPerPacket ); Com_Printf( " %5d mBytesPerFrame\n", outputStreamBasicDescription.mBytesPerFrame ); Com_Printf( " %5d mChannelsPerFrame\n", outputStreamBasicDescription.mChannelsPerFrame ); Com_Printf( " %5d mBitsPerChannel\n", outputStreamBasicDescription.mBitsPerChannel ); if ( outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM ) { Com_Printf( "Default Audio Device doesn't support Linear PCM!" ); return qfalse; } // Start sound running status = AudioDeviceAddIOProc( outputDeviceID, audioDeviceIOProc, NULL ); if ( status ) { Com_Printf( "AudioDeviceAddIOProc: returned %d\n", status ); return qfalse; } submissionChunk = chunkSize->integer; if ( outputStreamBasicDescription.mSampleRate == 44100 ) { submissionChunk = chunkSize->integer / 2; } maxMixedSamples = bufferSize->integer; s_mixedSamples = calloc( 1, sizeof( *s_mixedSamples ) * maxMixedSamples ); Com_Printf( "Chunk Count = %d\n", ( maxMixedSamples / submissionChunk ) ); // Tell the main app what we expect from it dma.samples = maxMixedSamples; dma.submission_chunk = submissionChunk; dma.samplebits = 16; dma.buffer = (byte *)s_mixedSamples; dma.channels = outputStreamBasicDescription.mChannelsPerFrame; dma.speed = 22050; //(unsigned long)outputStreamBasicDescription.mSampleRate; // We haven't enqueued anything yet s_chunkCount = 0; status = AudioDeviceStart( outputDeviceID, audioDeviceIOProc ); if ( status ) { Com_Printf( "AudioDeviceStart: returned %d\n", status ); return qfalse; } s_isRunning = qtrue; return qtrue; } /* =============== SNDDMA_GetBufferDuration =============== */ float SNDDMA_GetBufferDuration( void ) { return (float)dma.samples / (float)( dma.channels * dma.speed ); } /* =============== SNDDMA_GetDMAPos =============== */ int SNDDMA_GetDMAPos( void ) { return s_chunkCount * dma.submission_chunk; } /* =============== SNDDMA_Shutdown =============== */ void SNDDMA_Shutdown( void ) { OSStatus status; if ( !s_isRunning ) { return; } status = AudioDeviceStop( outputDeviceID, audioDeviceIOProc ); if ( status ) { Com_Printf( "AudioDeviceStop: returned %d\n", status ); return; } s_isRunning = qfalse; status = AudioDeviceRemoveIOProc( outputDeviceID, audioDeviceIOProc ); if ( status ) { Com_Printf( "AudioDeviceRemoveIOProc: returned %d\n", status ); return; } free( s_mixedSamples ); s_mixedSamples = NULL; dma.samples = NULL; } /* =============== SNDDMA_BeginPainting =============== */ void SNDDMA_BeginPainting( void ) { } /* =============== SNDDMA_Submit =============== */ void SNDDMA_Submit( void ) { }