📄 drv_osx.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: drv_osx.c,v 1.5 2004/02/16 17:27:29 raph Exp $
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
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
- Altivec optimizations of the various vector transforms
- 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
#pragma mark INCLUDES
#include <CoreAudio/AudioHardware.h>
#pragma mark -
//________________________________________________________________________________________________DEFINES
#pragma mark 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
#pragma mark -
//_________________________________________________________________________________________________MACROS
#pragma mark 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) \
if (Player_Paused()) \
{ \
MUTEX_LOCK (vars); \
VC_SilenceBytes ((SBYTE*) (_buffer), (ULONG) (_size)); \
MUTEX_UNLOCK (vars); \
} \
else \
{ \
MUTEX_LOCK (vars); \
VC_WriteBytes ((SBYTE*) (_buffer), (ULONG) ((_size))); \
MUTEX_UNLOCK (vars); \
}
#pragma mark -
//_________________________________________________________________________________________________GLOBALS
#pragma mark 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 Boolean gIOProcIsInstalled = 0,
gDeviceHasStarted = 0,
gBufferMono2Stereo = 0;
static OSStatus (*gAudioIOProc) (AudioDeviceID, const AudioTimeStamp *,
const AudioBufferList *, const AudioTimeStamp *,
AudioBufferList *, const AudioTimeStamp *, void *);
#pragma mark -
//____________________________________________________________________________________FUNCTION_pROTOTYPES
#pragma mark 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 BOOL OSX_Init (void);
static void OSX_Exit (void);
static BOOL OSX_PlayStart (void);
static void OSX_PlayStop (void);
static void OSX_Update (void);
#pragma mark -
//_______________________________________________________________________________________OSX_FillBuffer()
#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 the buffer...
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 */
//__________________________________________________________________________________OSX_AudioIOProc8Bit()
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;
// fill her up, please ...
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;
}
//_________________________________________________________________________________OSX_AudioIOProc16Bit()
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;
// ... and check the oil too
pthread_cond_signal (&gBufferCondition);
pthread_mutex_unlock (&gBufferMutex);
#else
myInBuffer = (SInt16 *) 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_16BIT;
myOutBuffer+=2;
}
}
else
{
for (i = 0; i < SOUND_BUFFER_SIZE; i++)
{
*myOutBuffer++ = (*myInBuffer++) * SOUND_BUFFER_SCALE_16BIT;
}
}
return 0;
}
//_________________________________________________________________________________OSX_AudioIOProcFloat()
static OSStatus OSX_AudioIOProcFloat (AudioDeviceID inDevice,
const AudioTimeStamp *inNow,
const AudioBufferList *inInputData,
const AudioTimeStamp *inInputTime,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -