259 lines
6.4 KiB
C
259 lines
6.4 KiB
C
|
/*
|
||
|
|
||
|
Copyright (C) 2001-2002 A Nourai
|
||
|
Copyright (C) 2006 Jacek Piszczek (Mac OSX port)
|
||
|
|
||
|
This program 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 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program 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 included (GNU.txt) GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
*/
|
||
|
|
||
|
#include "quakedef.h"
|
||
|
#include "sound.h"
|
||
|
#include <CoreServices/CoreServices.h>
|
||
|
#include <AudioUnit/AudioUnit.h>
|
||
|
|
||
|
// Jacek:
|
||
|
// coreaudio is poorly documented so I'm not 100% sure the code below
|
||
|
// is correct :(
|
||
|
|
||
|
struct MacOSSound_Private
|
||
|
{
|
||
|
AudioUnit gOutputUnit;
|
||
|
unsigned int readpos;
|
||
|
};
|
||
|
|
||
|
static OSStatus AudioRender(void *inRefCon,
|
||
|
AudioUnitRenderActionFlags *ioActionFlags,
|
||
|
const AudioTimeStamp *inTimeStamp,
|
||
|
UInt32 inBusNumber,
|
||
|
UInt32 inNumberFrames,
|
||
|
AudioBufferList *ioData)
|
||
|
{
|
||
|
soundcardinfo_t *sc = inRefCon;
|
||
|
struct MacOSSound_Private *pdata = sc->handle;
|
||
|
|
||
|
int start = pdata->readpos;
|
||
|
int buffersize = sc->sn.samples * (sc->sn.samplebits/8);
|
||
|
int bytes = ioData->mBuffers[0].mDataByteSize;
|
||
|
int remaining;
|
||
|
|
||
|
start %= buffersize;
|
||
|
if (start + bytes > buffersize)
|
||
|
{
|
||
|
remaining = bytes;
|
||
|
bytes = buffersize - start;
|
||
|
remaining -= bytes;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
remaining = 0;
|
||
|
}
|
||
|
|
||
|
memcpy(ioData->mBuffers[0].mData, sc->sn.buffer + start, bytes);
|
||
|
memcpy((char*)ioData->mBuffers[0].mData+bytes, sc->sn.buffer, remaining);
|
||
|
|
||
|
pdata->readpos += inNumberFrames*sc->sn.numchannels * (sc->sn.samplebits/8);
|
||
|
|
||
|
return noErr;
|
||
|
}
|
||
|
|
||
|
static void MacOS_Shutdown(soundcardinfo_t *sc)
|
||
|
{
|
||
|
struct MacOSSound_Private *pdata = sc->handle;
|
||
|
sc->handle = NULL;
|
||
|
if (!pdata)
|
||
|
return;
|
||
|
|
||
|
// stop playback
|
||
|
AudioOutputUnitStop (pdata->gOutputUnit);
|
||
|
|
||
|
// release the unit
|
||
|
AudioUnitUninitialize (pdata->gOutputUnit);
|
||
|
|
||
|
// free the unit
|
||
|
CloseComponent (pdata->gOutputUnit);
|
||
|
|
||
|
// free the buffer memory
|
||
|
Z_Free(sc->sn.buffer);
|
||
|
Z_Free(pdata);
|
||
|
}
|
||
|
|
||
|
static unsigned int MacOS_GetDMAPos(soundcardinfo_t *sc)
|
||
|
{
|
||
|
struct MacOSSound_Private *pdata = sc->handle;
|
||
|
sc->sn.samplepos = pdata->readpos/(sc->sn.samplebits/8);
|
||
|
return sc->sn.samplepos;
|
||
|
}
|
||
|
|
||
|
static void MacOS_Submit(soundcardinfo_t *sc)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void *MacOS_Lock(soundcardinfo_t *sc)
|
||
|
{
|
||
|
return sc->sn.buffer;
|
||
|
}
|
||
|
|
||
|
static void MacOS_Unlock(soundcardinfo_t *sc, void *buffer)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void MacOS_SetWaterDistortion(soundcardinfo_t *sc, qboolean isunderwater)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static int MacOS_InitCard(soundcardinfo_t *sc, int cardnum)
|
||
|
{
|
||
|
ComponentResult err = noErr;
|
||
|
|
||
|
if (cardnum)
|
||
|
return 2; /* no more */
|
||
|
|
||
|
struct MacOSSound_Private *pdata = Z_Malloc(sizeof(*pdata));
|
||
|
if (!pdata)
|
||
|
return FALSE;
|
||
|
|
||
|
// Open the default output unit
|
||
|
ComponentDescription desc;
|
||
|
desc.componentType = kAudioUnitType_Output;
|
||
|
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||
|
desc.componentFlags = 0;
|
||
|
desc.componentFlagsMask = 0;
|
||
|
|
||
|
Component comp = FindNextComponent(NULL, &desc);
|
||
|
if (comp == NULL)
|
||
|
{
|
||
|
Con_Printf("FindNextComponent failed\n");
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
err = OpenAComponent(comp, &pdata->gOutputUnit);
|
||
|
if (comp == NULL)
|
||
|
{
|
||
|
Con_Printf("OpenAComponent failed\n");
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Set up a callback function to generate output to the output unit
|
||
|
AURenderCallbackStruct input;
|
||
|
input.inputProc = AudioRender;
|
||
|
input.inputProcRefCon = sc;
|
||
|
|
||
|
err = AudioUnitSetProperty ( pdata->gOutputUnit,
|
||
|
kAudioUnitProperty_SetRenderCallback,
|
||
|
kAudioUnitScope_Input,
|
||
|
0,
|
||
|
&input,
|
||
|
sizeof(input));
|
||
|
if (err)
|
||
|
{
|
||
|
Con_Printf("AudioUnitSetProperty failed\n");
|
||
|
CloseComponent(pdata->gOutputUnit);
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// describe our audio data
|
||
|
AudioStreamBasicDescription streamFormat;
|
||
|
streamFormat.mSampleRate = sc->sn.speed;
|
||
|
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
||
|
streamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian
|
||
|
| kLinearPCMFormatFlagIsPacked;
|
||
|
//| kAudioFormatFlagIsNonInterleaved;
|
||
|
streamFormat.mFramesPerPacket = 1;
|
||
|
streamFormat.mChannelsPerFrame = 2;
|
||
|
streamFormat.mBitsPerChannel = 16;
|
||
|
if (streamFormat.mBitsPerChannel >= 16)
|
||
|
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
||
|
else
|
||
|
streamFormat.mFormatFlags |= 0;
|
||
|
|
||
|
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * (streamFormat.mBitsPerChannel/8);
|
||
|
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
|
||
|
|
||
|
err = AudioUnitSetProperty (pdata->gOutputUnit,
|
||
|
kAudioUnitProperty_StreamFormat,
|
||
|
kAudioUnitScope_Input,
|
||
|
0,
|
||
|
&streamFormat,
|
||
|
sizeof(AudioStreamBasicDescription));
|
||
|
if (err)
|
||
|
{
|
||
|
Con_Printf("AudioUnitSetProperty failed\n");
|
||
|
CloseComponent(pdata->gOutputUnit);
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// set the shm structure
|
||
|
sc->sn.speed = streamFormat.mSampleRate;
|
||
|
sc->sn.samplebits = streamFormat.mBitsPerChannel;
|
||
|
sc->sn.numchannels = streamFormat.mChannelsPerFrame;
|
||
|
sc->sn.samples = 256 * 1024;
|
||
|
sc->sn.buffer = Z_Malloc(sc->sn.samples*sc->sn.samplebits/8);
|
||
|
|
||
|
int i;
|
||
|
for (i = 0; i < sc->sn.samples*sc->sn.samplebits/8; i++)
|
||
|
sc->sn.buffer[i] = rand();
|
||
|
|
||
|
if (sc->sn.buffer == 0)
|
||
|
{
|
||
|
Con_Printf("Malloc failed - cannot allocate sound buffer\n");
|
||
|
CloseComponent(pdata->gOutputUnit);
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Initialize unit
|
||
|
err = AudioUnitInitialize(pdata->gOutputUnit);
|
||
|
if (err)
|
||
|
{
|
||
|
Con_Printf("AudioOutputInitialize failed\n");
|
||
|
CloseComponent(pdata->gOutputUnit);
|
||
|
Z_Free(sc->sn.buffer);
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// start playing :)
|
||
|
err = AudioOutputUnitStart (pdata->gOutputUnit);
|
||
|
if (err)
|
||
|
{
|
||
|
Con_Printf("AudioOutputUnitStart failed\n");
|
||
|
AudioUnitUninitialize (pdata->gOutputUnit);
|
||
|
CloseComponent(pdata->gOutputUnit);
|
||
|
Z_Free(sc->sn.buffer);
|
||
|
Z_Free(pdata);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
sc->handle = pdata;
|
||
|
sc->Lock = MacOS_Lock;
|
||
|
sc->Unlock = MacOS_Unlock;
|
||
|
sc->SetWaterDistortion = MacOS_SetWaterDistortion;
|
||
|
sc->Submit = MacOS_Submit;
|
||
|
sc->GetDMAPos = MacOS_GetDMAPos;
|
||
|
sc->Shutdown = MacOS_Shutdown;
|
||
|
|
||
|
Con_Printf("Sound initialised\n");
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
sounddriver pMacOS_InitCard = &MacOS_InitCard;
|
||
|
|