/*
===========================================================================
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 ) {
}