mirror of
https://github.com/kbeckmann/game-and-watch-retro-go.git
synced 2025-12-16 13:15:55 +01:00
724 lines
20 KiB
C
724 lines
20 KiB
C
/* MikMod sound library
|
|
(c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
|
|
complete list.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Library 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
|
|
GNU Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
*/
|
|
|
|
/*==============================================================================
|
|
|
|
$Id$
|
|
|
|
Driver for output via CoreAudio [MacOS X and Darwin].
|
|
|
|
==============================================================================*/
|
|
|
|
/*
|
|
|
|
Written by Axel Wefers <awe@fruitz-of-dojo.de>
|
|
|
|
Notes:
|
|
- if HAVE_PTHREAD (config.h) is defined, an extra thread will be created to fill the buffers.
|
|
- if HAVE_PTHREAD is defined, a double buffered method will be used.
|
|
- if an unsupported frequency is selected [md_mixfreq], the native device frequency is used.
|
|
- if mono playback is selected and is not supported by the device, we will emulate mono
|
|
playback.
|
|
- if stereo/surround playback is selected and is not supported by the device, DMODE_STEREO
|
|
will be deactivated automagically.
|
|
|
|
Bug fixes by Anders F Bjoerklund <afb@algonet.se>
|
|
|
|
Changes:
|
|
- cleared up in the macro jungle, to see what was going on in the code
|
|
- separated "has ability to use pthreads" from "wish to use pthreads"
|
|
- moved pthread_cond_wait inside the mutex lock, to avoid a deadlock [!]
|
|
- added more than one back buffer, currently left at eight or something
|
|
- gave up on whole thread idea, since it stutters if you rescale a window
|
|
- moved a #pragma mark and added DRV_OSX/MISSING, for non-Darwin compiles
|
|
- added support for float-point buffers, to avoid the conversion and copying
|
|
- Altivec optimizations of the various vector transforms (S.Denis)
|
|
|
|
Future ideas: (TODO)
|
|
|
|
- support possibly partially filled buffers from libmikmod
|
|
- clean up the rest of the code and lose even more macros
|
|
- use hardware preferred native size for the sample buffers
|
|
- provide a PPC64 version of the library, for PowerMac G5
|
|
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "mikmod_internals.h"
|
|
|
|
#ifdef DRV_OSX
|
|
|
|
/* INCLUDES */
|
|
#include <mach-o/arch.h>
|
|
#include <sys/sysctl.h>
|
|
#include <CoreAudio/AudioHardware.h>
|
|
|
|
/* DEFINES */
|
|
#define SOUND_BUFFER_SCALE_8BIT (1.0f / 128.0f) /* CoreAudio requires float input. */
|
|
#define SOUND_BUFFER_SCALE_16BIT (1.0f / 32768.0f) /* CoreAudio requires float input. */
|
|
|
|
#define SOUND_BUFFER_SIZE 4096 /* The buffersize libmikmod will use. */
|
|
#define USE_FILL_THREAD 0 /* Use an extra thread to fill the buffers? */
|
|
|
|
#ifndef HAVE_PTHREAD
|
|
#undef USE_FILL_THREAD
|
|
#define USE_FILL_THREAD 0 /* must have pthread supports to use thread */
|
|
#endif
|
|
|
|
#define NUMBER_BACK_BUFFERS 8 /* Number of back buffers for the thread */
|
|
#define DEBUG_TRACE_THREADS 0
|
|
|
|
/* MACROS */
|
|
#define CHECK_ERROR(ERRNO, RESULT) \
|
|
if (RESULT != kAudioHardwareNoError) { \
|
|
_mm_errno = ERRNO; \
|
|
return 1; \
|
|
}
|
|
|
|
#define SET_PROPS() if (AudioDeviceSetProperty(gSoundDeviceID, NULL, 0, 0, \
|
|
kAudioDevicePropertyStreamFormat, \
|
|
myPropertySize, \
|
|
&mySoundBasicDescription)) { \
|
|
CHECK_ERROR (MMERR_OSX_BAD_PROPERTY, \
|
|
AudioDeviceGetProperty(gSoundDeviceID, 0, 0, \
|
|
kAudioDevicePropertyStreamFormat, \
|
|
&myPropertySize, \
|
|
&mySoundBasicDescription)); \
|
|
}
|
|
|
|
#define SET_STEREO() switch (mySoundBasicDescription.mChannelsPerFrame) { \
|
|
case 1: \
|
|
md_mode &= ~DMODE_STEREO; \
|
|
gBufferMono2Stereo = 0; \
|
|
break; \
|
|
case 2: \
|
|
if (md_mode & DMODE_STEREO) gBufferMono2Stereo = 0; \
|
|
else gBufferMono2Stereo = 1; \
|
|
break; \
|
|
default: \
|
|
_mm_errno = MMERR_OSX_SET_STEREO; \
|
|
return 1; \
|
|
}
|
|
|
|
#define FILL_BUFFER(_buffer,_size) \
|
|
MUTEX_LOCK (vars); \
|
|
if (Player_Paused_internal()) \
|
|
VC_SilenceBytes ((SBYTE*) (_buffer), (ULONG) (_size)); \
|
|
else VC_WriteBytes ((SBYTE*) (_buffer), (ULONG) (_size)); \
|
|
MUTEX_UNLOCK (vars);
|
|
|
|
/* GLOBALS */
|
|
#if USE_FILL_THREAD
|
|
|
|
static pthread_t gBufferFillThread;
|
|
static pthread_mutex_t gBufferMutex;
|
|
static pthread_cond_t gBufferCondition;
|
|
static Boolean gExitBufferFillThread = 0;
|
|
|
|
static int gCurrentPlayBuffer;
|
|
static int gCurrentFillBuffer;
|
|
static unsigned char *gSoundBackBuffer[NUMBER_BACK_BUFFERS];
|
|
|
|
#else
|
|
|
|
static unsigned char *gSoundBuffer = NULL;
|
|
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
static AudioDeviceID gSoundDeviceID;
|
|
static UInt32 gInBufferSize;
|
|
static UInt32 gHardwareBufferSize = 0;
|
|
static Boolean gIOProcIsInstalled = 0,
|
|
gDeviceHasStarted = 0,
|
|
gBufferMono2Stereo = 0;
|
|
|
|
static OSStatus (*gAudioIOProc) (AudioDeviceID,
|
|
const AudioTimeStamp *, const AudioBufferList *,
|
|
const AudioTimeStamp *, AudioBufferList *,
|
|
const AudioTimeStamp *, void *);
|
|
|
|
/* FUNCTION PROTOTYPES */
|
|
#if USE_FILL_THREAD
|
|
|
|
static void * OSX_FillBuffer (void *);
|
|
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
static OSStatus OSX_AudioIOProc8Bit (AudioDeviceID,
|
|
const AudioTimeStamp *, const AudioBufferList *,
|
|
const AudioTimeStamp *, AudioBufferList *,
|
|
const AudioTimeStamp *, void *);
|
|
static OSStatus OSX_AudioIOProc16Bit (AudioDeviceID,
|
|
const AudioTimeStamp *, const AudioBufferList *,
|
|
const AudioTimeStamp *, AudioBufferList *,
|
|
const AudioTimeStamp *, void *);
|
|
static OSStatus OSX_AudioIOProcFloat (AudioDeviceID,
|
|
const AudioTimeStamp *, const AudioBufferList *,
|
|
const AudioTimeStamp *, AudioBufferList *,
|
|
const AudioTimeStamp *, void *);
|
|
|
|
static BOOL OSX_IsPresent (void);
|
|
static int OSX_Init (void);
|
|
static void OSX_Exit (void);
|
|
static int OSX_PlayStart (void);
|
|
static void OSX_PlayStop (void);
|
|
static void OSX_Update (void);
|
|
|
|
|
|
#if USE_FILL_THREAD
|
|
static void *OSX_FillBuffer (void *theID)
|
|
{
|
|
unsigned char *buffer;
|
|
int done;
|
|
|
|
while (1)
|
|
{
|
|
done = 0;
|
|
|
|
while (!done)
|
|
{
|
|
/* shall the thread exit? */
|
|
if (gExitBufferFillThread) pthread_exit (NULL);
|
|
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
|
|
if ((gCurrentFillBuffer+1) % NUMBER_BACK_BUFFERS != gCurrentPlayBuffer) {
|
|
#if DEBUG_TRACE_THREADS
|
|
fprintf(stderr,"filling buffer #%d\n", gCurrentFillBuffer);
|
|
#endif
|
|
buffer = gSoundBackBuffer[gCurrentFillBuffer];
|
|
if (++gCurrentFillBuffer >= NUMBER_BACK_BUFFERS)
|
|
gCurrentFillBuffer = 0;
|
|
FILL_BUFFER (buffer, gInBufferSize);
|
|
}
|
|
else {
|
|
/* we are caught up now, give it a rest */
|
|
done = 1;
|
|
}
|
|
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
}
|
|
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
/* wait for the next buffer-fill request */
|
|
pthread_cond_wait (&gBufferCondition, &gBufferMutex);
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
}
|
|
|
|
return theID;
|
|
}
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
static OSStatus OSX_AudioIOProc8Bit (AudioDeviceID inDevice,
|
|
const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
|
|
const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
|
|
const AudioTimeStamp *inOutputTime, void *inClientData)
|
|
{
|
|
register float *myOutBuffer = (float *) outOutputData->mBuffers[0].mData;
|
|
register UInt8 *myInBuffer;
|
|
register UInt32 i;
|
|
|
|
#if USE_FILL_THREAD
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
|
|
#if DEBUG_TRACE_THREADS
|
|
fprintf(stderr,"playing buffer #%d\n", gCurrentPlayBuffer);
|
|
#endif
|
|
myInBuffer = (UInt8 *) gSoundBackBuffer[gCurrentPlayBuffer];
|
|
if (++gCurrentPlayBuffer >= NUMBER_BACK_BUFFERS)
|
|
gCurrentPlayBuffer = 0;
|
|
pthread_cond_signal (&gBufferCondition);
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
#else
|
|
myInBuffer = (UInt8 *) gSoundBuffer;
|
|
FILL_BUFFER(gSoundBuffer, gInBufferSize);
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
if (gBufferMono2Stereo) {
|
|
for (i = 0; i < SOUND_BUFFER_SIZE >> 1; i++) {
|
|
myOutBuffer[1] = myOutBuffer[0] = (*myInBuffer++) * SOUND_BUFFER_SCALE_8BIT;
|
|
myOutBuffer+=2;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < SOUND_BUFFER_SIZE; i++) {
|
|
*myOutBuffer++ = (*myInBuffer++) * SOUND_BUFFER_SCALE_8BIT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_SSE2
|
|
/* FIXME: SSE2-specific code here? */
|
|
#endif /* HAVE_SSE2 */
|
|
|
|
#ifdef HAVE_ALTIVEC
|
|
/* note: AltiVec code needs to be in a function of its own,
|
|
* since the compiler will generate vrsave instructions */
|
|
|
|
#ifdef __GNUC__
|
|
__attribute__((noinline))
|
|
#endif
|
|
static void OSX_AudioIOProc16Bit_Altivec(SInt16 *myInBuffer, float *myOutBuffer)
|
|
{
|
|
register UInt32 i;
|
|
|
|
float f = SOUND_BUFFER_SCALE_16BIT;
|
|
const vector float gain = vec_load_ps1(&f); /* multiplier */
|
|
const vector float mix = vec_setzero();
|
|
|
|
if (gBufferMono2Stereo) {
|
|
int j = 0;
|
|
/* TEST: OK */
|
|
for (i = 0; i < SOUND_BUFFER_SIZE; i += 8, j += 16) {
|
|
vector short int v0 = vec_ld(0, myInBuffer + i); /* Load 8 shorts */
|
|
vector float v1 = vec_ctf((vector signed int)vec_unpackh(v0), 0); /* convert to float */
|
|
vector float v2 = vec_ctf((vector signed int)vec_unpackl(v0), 0); /* convert to float */
|
|
vector float v3 = vec_madd(v1, gain, mix); /* scale */
|
|
vector float v4 = vec_madd(v2, gain, mix); /* scale */
|
|
|
|
vector float v5 = vec_mergel(v3, v3); /* v3(0,0,1,1); */
|
|
vector float v6 = vec_mergeh(v3, v3); /* v3(2,2,3,3); */
|
|
vector float v7 = vec_mergel(v4, v4); /* v4(0,0,1,1); */
|
|
vector float v8 = vec_mergeh(v4, v4); /* v4(2,2,3,3); */
|
|
|
|
vec_st(v5, 0, myOutBuffer + j); /* Store 4 floats */
|
|
vec_st(v6, 0, myOutBuffer + 4 + j); /* Store 4 floats */
|
|
vec_st(v7, 0, myOutBuffer + 8 + j); /* Store 4 floats */
|
|
vec_st(v8, 0, myOutBuffer + 12 + j); /* Store 4 floats */
|
|
}
|
|
}
|
|
else {
|
|
/* TEST: OK */
|
|
for (i = 0; i < SOUND_BUFFER_SIZE; i += 8) {
|
|
vector short int v0 = vec_ld(0, myInBuffer + i); /* Load 8 shorts */
|
|
vector float v1 = vec_ctf((vector signed int)vec_unpackh(v0), 0); /* convert to float */
|
|
vector float v2 = vec_ctf((vector signed int)vec_unpackl(v0), 0); /* convert to float */
|
|
vector float v3 = vec_madd(v1, gain, mix); /* scale */
|
|
vector float v4 = vec_madd(v2, gain, mix); /* scale */
|
|
vec_st(v3, 0, myOutBuffer + i); /* Store 4 floats */
|
|
vec_st(v4, 0, myOutBuffer + 4 + i); /* Store 4 floats */
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_ALTIVEC */
|
|
|
|
static OSStatus OSX_AudioIOProc16Bit (AudioDeviceID inDevice,
|
|
const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
|
|
const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
|
|
const AudioTimeStamp *inOutputTime, void *inClientData)
|
|
{
|
|
register float *myOutBuffer = (float *) outOutputData->mBuffers[0].mData;
|
|
register SInt16 *myInBuffer;
|
|
register UInt32 i;
|
|
|
|
#if USE_FILL_THREAD
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
|
|
#if DEBUG_TRACE_THREADS
|
|
fprintf(stderr,"playing buffer #%d\n", gCurrentPlayBuffer);
|
|
#endif
|
|
|
|
myInBuffer = (SInt16 *) gSoundBackBuffer[gCurrentPlayBuffer];
|
|
if (++gCurrentPlayBuffer >= NUMBER_BACK_BUFFERS)
|
|
gCurrentPlayBuffer = 0;
|
|
pthread_cond_signal (&gBufferCondition);
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
#else
|
|
myInBuffer = (SInt16 *) gSoundBuffer;
|
|
FILL_BUFFER(gSoundBuffer, gInBufferSize);
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
#ifdef HAVE_ALTIVEC
|
|
if (md_mode & DMODE_SIMDMIXER) {
|
|
#if __MWERKS__
|
|
#pragma dont_inline on
|
|
#endif
|
|
OSX_AudioIOProc16Bit_Altivec(myInBuffer,myOutBuffer);
|
|
#if __MWERKS__
|
|
#pragma dont_inline reset
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (gBufferMono2Stereo) {
|
|
for (i = 0; i < SOUND_BUFFER_SIZE >> 1; i++) {
|
|
myOutBuffer[1] = myOutBuffer[0] = (*myInBuffer++) * SOUND_BUFFER_SCALE_16BIT;
|
|
myOutBuffer+=2;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < SOUND_BUFFER_SIZE; i++) {
|
|
*myOutBuffer++ = (*myInBuffer++) * SOUND_BUFFER_SCALE_16BIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static OSStatus OSX_AudioIOProcFloat (AudioDeviceID inDevice,
|
|
const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
|
|
const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
|
|
const AudioTimeStamp *inOutputTime, void *inClientData)
|
|
{
|
|
register float *myOutBuffer = (float *) outOutputData->mBuffers[0].mData;
|
|
register float *myInBuffer;
|
|
register UInt32 i;
|
|
|
|
#if USE_FILL_THREAD
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
|
|
#if DEBUG_TRACE_THREADS
|
|
fprintf(stderr,"playing buffer #%d\n", gCurrentPlayBuffer);
|
|
#endif
|
|
|
|
myInBuffer = (float *) gSoundBackBuffer[gCurrentPlayBuffer];
|
|
if (++gCurrentPlayBuffer >= NUMBER_BACK_BUFFERS)
|
|
gCurrentPlayBuffer = 0;
|
|
pthread_cond_signal (&gBufferCondition);
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
#else
|
|
/* avoid copy, if no conversion needed */
|
|
myInBuffer = (gBufferMono2Stereo) ? (float *) gSoundBuffer : myOutBuffer;
|
|
FILL_BUFFER( myInBuffer, gInBufferSize);
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
if (gBufferMono2Stereo) {
|
|
for (i = 0; i < SOUND_BUFFER_SIZE >> 1; i++) {
|
|
myOutBuffer[1] = myOutBuffer[0] = *myInBuffer++;
|
|
myOutBuffer+=2;
|
|
}
|
|
}
|
|
else if (myInBuffer != myOutBuffer) {
|
|
for (i = 0; i < SOUND_BUFFER_SIZE; i++) {
|
|
*myOutBuffer++ = *myInBuffer++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_ALTIVEC
|
|
static BOOL OSX_HasAltivec (void)
|
|
{
|
|
int result = 0;
|
|
int selectors[2] = { CTL_HW, HW_VECTORUNIT };
|
|
size_t length = sizeof(result);
|
|
sysctl(selectors, 2, &result, &length, NULL, 0);
|
|
return !!result;
|
|
}
|
|
#endif
|
|
|
|
static BOOL OSX_IsPresent (void)
|
|
{
|
|
/* weak_import and check syms? meh.. */
|
|
return 1;
|
|
}
|
|
|
|
static int OSX_Init (void)
|
|
{
|
|
AudioStreamBasicDescription mySoundBasicDescription;
|
|
UInt32 myPropertySize, myBufferByteCount;
|
|
#if USE_FILL_THREAD
|
|
int i;
|
|
#endif
|
|
|
|
/* get the device */
|
|
myPropertySize = sizeof (gSoundDeviceID);
|
|
CHECK_ERROR(MMERR_DETECTING_DEVICE,
|
|
AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice,
|
|
&myPropertySize, &gSoundDeviceID)
|
|
);
|
|
if (gSoundDeviceID == kAudioDeviceUnknown) {
|
|
_mm_errno = MMERR_OSX_UNKNOWN_DEVICE;
|
|
return 1;
|
|
}
|
|
|
|
/* get the device format */
|
|
myPropertySize = sizeof (mySoundBasicDescription);
|
|
CHECK_ERROR(MMERR_OSX_BAD_PROPERTY,
|
|
AudioDeviceGetProperty (gSoundDeviceID, 0, 0, kAudioDevicePropertyStreamFormat,
|
|
&myPropertySize, &mySoundBasicDescription)
|
|
);
|
|
|
|
/* set up basic md_mode, just to be secure */
|
|
md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
|
|
|
|
#ifdef HAVE_ALTIVEC
|
|
/* check for Altivec support */
|
|
if (OSX_HasAltivec()) {
|
|
md_mode |= DMODE_SIMDMIXER;
|
|
}
|
|
#endif
|
|
|
|
/* try the selected mix frequency, if failure, fall back to native frequency */
|
|
if (mySoundBasicDescription.mSampleRate != md_mixfreq) {
|
|
mySoundBasicDescription.mSampleRate = md_mixfreq;
|
|
SET_PROPS ();
|
|
md_mixfreq = mySoundBasicDescription.mSampleRate;
|
|
}
|
|
|
|
/* try selected channels, if failure select native channels */
|
|
switch (md_mode & DMODE_STEREO) {
|
|
case 0:
|
|
if (mySoundBasicDescription.mChannelsPerFrame != 1) {
|
|
mySoundBasicDescription.mChannelsPerFrame = 1;
|
|
SET_PROPS ();
|
|
SET_STEREO ();
|
|
}
|
|
break;
|
|
case 1:
|
|
if (mySoundBasicDescription.mChannelsPerFrame != 2) {
|
|
mySoundBasicDescription.mChannelsPerFrame = 2;
|
|
SET_PROPS();
|
|
SET_STEREO();
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* linear PCM is required */
|
|
if (mySoundBasicDescription.mFormatID != kAudioFormatLinearPCM) {
|
|
_mm_errno = MMERR_OSX_UNSUPPORTED_FORMAT;
|
|
return 1;
|
|
}
|
|
|
|
/* prepare the buffers */
|
|
if (gBufferMono2Stereo) {
|
|
gInBufferSize = SOUND_BUFFER_SIZE >> 1;
|
|
}
|
|
else {
|
|
gInBufferSize = SOUND_BUFFER_SIZE;
|
|
}
|
|
|
|
if (md_mode & DMODE_FLOAT) {
|
|
gInBufferSize *= sizeof(float);
|
|
gAudioIOProc = OSX_AudioIOProcFloat;
|
|
}
|
|
else if (md_mode & DMODE_16BITS) {
|
|
gInBufferSize *= sizeof(SInt16);
|
|
gAudioIOProc = OSX_AudioIOProc16Bit;
|
|
}
|
|
else {
|
|
gInBufferSize *= sizeof(UInt8);
|
|
gAudioIOProc = OSX_AudioIOProc8Bit;
|
|
}
|
|
|
|
/* save the original buffer size */
|
|
myPropertySize = sizeof (gHardwareBufferSize);
|
|
AudioDeviceGetProperty (gSoundDeviceID, 0, 0, kAudioDevicePropertyBufferSize,
|
|
&myPropertySize, &gHardwareBufferSize);
|
|
|
|
myBufferByteCount = SOUND_BUFFER_SIZE * sizeof(float);
|
|
CHECK_ERROR(MMERR_OSX_BUFFER_ALLOC,
|
|
AudioDeviceSetProperty (gSoundDeviceID, NULL, 0, 0, kAudioDevicePropertyBufferSize,
|
|
sizeof(myBufferByteCount), &myBufferByteCount)
|
|
);
|
|
|
|
/* add our audio IO procedure. */
|
|
CHECK_ERROR(MMERR_OSX_ADD_IO_PROC,
|
|
AudioDeviceAddIOProc (gSoundDeviceID, gAudioIOProc, NULL)
|
|
);
|
|
gIOProcIsInstalled = 1;
|
|
|
|
#if !USE_FILL_THREAD
|
|
/* get the buffer memory */
|
|
if ((gSoundBuffer = (unsigned char *) MikMod_amalloc(gInBufferSize)) == NULL) {
|
|
_mm_errno = MMERR_OUT_OF_MEMORY;
|
|
return 1;
|
|
}
|
|
#else
|
|
/* some thread init */
|
|
if (pthread_mutex_init (&gBufferMutex, NULL) ||
|
|
pthread_cond_init (&gBufferCondition, NULL)) {
|
|
_mm_errno = MMERR_OSX_PTHREAD;
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < NUMBER_BACK_BUFFERS; i++) {
|
|
if ((gSoundBackBuffer[i] = (unsigned char *) MikMod_amalloc(gInBufferSize)) == NULL) {
|
|
_mm_errno = MMERR_OUT_OF_MEMORY;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
gCurrentPlayBuffer = 0;
|
|
gCurrentFillBuffer = 0;
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
/* init mikmod */
|
|
return VC_Init ();
|
|
}
|
|
|
|
static void OSX_Exit (void)
|
|
{
|
|
#if USE_FILL_THREAD
|
|
int i;
|
|
#endif
|
|
|
|
if (gDeviceHasStarted) {
|
|
/* stop the audio device */
|
|
AudioDeviceStop (gSoundDeviceID, gAudioIOProc);
|
|
gDeviceHasStarted = 0;
|
|
#if USE_FILL_THREAD
|
|
/* finish the fill buffer thread off */
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
gExitBufferFillThread = 1;
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
pthread_cond_signal(&gBufferCondition);
|
|
pthread_join (gBufferFillThread, NULL);
|
|
}
|
|
|
|
/* destroy other thread related stuff */
|
|
pthread_mutex_destroy (&gBufferMutex);
|
|
pthread_cond_destroy (&gBufferCondition);
|
|
#else
|
|
}
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
/* remove the audio IO proc */
|
|
if (gIOProcIsInstalled) {
|
|
AudioDeviceRemoveIOProc (gSoundDeviceID, gAudioIOProc);
|
|
gIOProcIsInstalled = 0;
|
|
}
|
|
|
|
if (gHardwareBufferSize) {
|
|
AudioDeviceSetProperty (gSoundDeviceID, NULL, 0, 0, kAudioDevicePropertyBufferSize,
|
|
sizeof(gHardwareBufferSize), &gHardwareBufferSize);
|
|
gHardwareBufferSize = 0;
|
|
}
|
|
|
|
#if !USE_FILL_THREAD
|
|
/* free up the sound buffer */
|
|
MikMod_afree (gSoundBuffer);
|
|
gSoundBuffer = NULL;
|
|
#else
|
|
for ( i = 0; i < NUMBER_BACK_BUFFERS; i++ ) {
|
|
/* free up the back buffer */
|
|
MikMod_afree (gSoundBackBuffer[i]);
|
|
gSoundBackBuffer[i] = NULL;
|
|
}
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
VC_Exit ();
|
|
}
|
|
|
|
/* OSX_PlayStart() */
|
|
static int OSX_PlayStart (void)
|
|
{
|
|
/* start virtch */
|
|
if (VC_PlayStart ()) {
|
|
return 1;
|
|
}
|
|
|
|
/* just for security: audio device already playing? */
|
|
if (gDeviceHasStarted) return 0;
|
|
|
|
#if USE_FILL_THREAD
|
|
/* start the buffer fill thread */
|
|
gExitBufferFillThread = 0;
|
|
if (pthread_create(&gBufferFillThread, NULL, OSX_FillBuffer, NULL)) {
|
|
_mm_errno = MMERR_OSX_PTHREAD;
|
|
return 1;
|
|
}
|
|
#endif /* USE_FILL_THREAD */
|
|
|
|
/* start the audio IO Proc */
|
|
if (AudioDeviceStart (gSoundDeviceID, gAudioIOProc)) {
|
|
_mm_errno = MMERR_OSX_DEVICE_START;
|
|
return 1;
|
|
}
|
|
gDeviceHasStarted = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void OSX_PlayStop (void)
|
|
{
|
|
if (gDeviceHasStarted) {
|
|
/* stop the audio IO Proc */
|
|
AudioDeviceStop (gSoundDeviceID, gAudioIOProc);
|
|
gDeviceHasStarted = 0;
|
|
|
|
#if USE_FILL_THREAD
|
|
/* finish the fill buffer thread off */
|
|
pthread_mutex_lock (&gBufferMutex);
|
|
gExitBufferFillThread = 1;
|
|
pthread_mutex_unlock (&gBufferMutex);
|
|
pthread_cond_signal (&gBufferCondition);
|
|
pthread_join (gBufferFillThread, NULL);
|
|
#endif /* USE_FILL_THREAD */
|
|
}
|
|
|
|
/* tell virtch that playback has stopped. */
|
|
VC_PlayStop ();
|
|
}
|
|
|
|
static void OSX_Update (void)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
MIKMODAPI MDRIVER drv_osx={
|
|
NULL,
|
|
"CoreAudio Driver",
|
|
"CoreAudio Driver v2.1",
|
|
0,255,
|
|
"osx",
|
|
NULL,
|
|
NULL,
|
|
OSX_IsPresent,
|
|
VC_SampleLoad,
|
|
VC_SampleUnload,
|
|
VC_SampleSpace,
|
|
VC_SampleLength,
|
|
OSX_Init,
|
|
OSX_Exit,
|
|
NULL,
|
|
VC_SetNumVoices,
|
|
OSX_PlayStart,
|
|
OSX_PlayStop,
|
|
OSX_Update,
|
|
NULL,
|
|
VC_VoiceSetVolume,
|
|
VC_VoiceGetVolume,
|
|
VC_VoiceSetFrequency,
|
|
VC_VoiceGetFrequency,
|
|
VC_VoiceSetPanning,
|
|
VC_VoiceGetPanning,
|
|
VC_VoicePlay,
|
|
VC_VoiceStop,
|
|
VC_VoiceStopped,
|
|
VC_VoiceGetPosition,
|
|
VC_VoiceRealVolume
|
|
};
|
|
|
|
#else
|
|
|
|
MISSING(drv_osx);
|
|
|
|
#endif /* DRV_OSX */
|