📄 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_THREADstatic 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];#elsestatic 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_THREADstatic 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_THREADstatic 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 + -