ao_macosx.c

来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 449 行

C
449
字号
/* * *  ao_macosx.c * *      Original Copyright (C) Timothy J. Wood - Aug 2000 * *  This file is part of libao, a cross-platform library.  See *  README for a history of this source code. * *  libao 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, or (at your option) *  any later version. * *  libao 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 libao; if not, write to the Free Software Foundation, Inc., *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *//* * The MacOS X CoreAudio framework doesn't mesh as simply as some * simpler frameworks do.  This is due to the fact that CoreAudio pulls * audio samples rather than having them pushed at it (which is nice * when you are wanting to do good buffering of audio).  *//* Change log: *  * 14/5-2003: Ported to MPlayer libao2 by Dan Christiansen * *            AC-3 and MPEG audio passthrough is possible, but I don't have *            access to a sound card that supports it. */#include <CoreServices/CoreServices.h>#include <AudioUnit/AudioUnit.h>#include <AudioToolbox/AudioToolbox.h>#include <mplaylib.h>#include "mplaylib.h"#include <inttypes.h>#include <pthread.h>#include "config.h"#include "mp_msg.h"#include "audio_out.h"#include "audio_out_internal.h"#include "libaf/af_format.h"static ao_info_t info =  {    "Darwin/Mac OS X native audio output",    "macosx",    "Timothy J. Wood & Dan Christiansen & Chris Roccati",    ""  };LIBAO_EXTERN(macosx)/* Prefix for all mp_msg() calls */#define ao_msg(a, b, c...) mp_msg(a, b, "AO: [macosx] " c)/* This is large, but best (maybe it should be even larger). * CoreAudio supposedly has an internal latency in the order of 2ms */#define NUM_BUFS 32typedef struct ao_macosx_s{  /* AudioUnit */  AudioUnit theOutputUnit;  int packetSize;  int paused;  /* Ring-buffer */  /* does not need explicit synchronization, but needs to allocate   * (num_chunks + 1) * chunk_size memory to store num_chunks * chunk_size   * data */  unsigned char *buffer;  unsigned int buffer_len; ///< must always be (num_chunks + 1) * chunk_size  unsigned int num_chunks;  unsigned int chunk_size;    unsigned int buf_read_pos;  unsigned int buf_write_pos;} ao_macosx_t;static ao_macosx_t *ao = NULL;/** * \brief return number of free bytes in the buffer *    may only be called by mplayer's thread * \return minimum number of free bytes in buffer, value may change between *    two immediately following calls, and the real number of free bytes *    might actually be larger! */static int buf_free(void) {  int free = ao->buf_read_pos - ao->buf_write_pos - ao->chunk_size;  if (free < 0) free += ao->buffer_len;  return free;}/** * \brief return number of buffered bytes *    may only be called by playback thread * \return minimum number of buffered bytes, value may change between *    two immediately following calls, and the real number of buffered bytes *    might actually be larger! */static int buf_used(void) {  int used = ao->buf_write_pos - ao->buf_read_pos;  if (used < 0) used += ao->buffer_len;  return used;}/** * \brief add data to ringbuffer */static int write_buffer(unsigned char* data, int len){  int first_len = ao->buffer_len - ao->buf_write_pos;  int free = buf_free();  if (len > free) len = free;  if (first_len > len) first_len = len;  // till end of buffer  memcpy (&ao->buffer[ao->buf_write_pos], data, first_len);  if (len > first_len) { // we have to wrap around    // remaining part from beginning of buffer    memcpy (ao->buffer, &data[first_len], len - first_len);  }  ao->buf_write_pos = (ao->buf_write_pos + len) % ao->buffer_len;  return len;}/** * \brief remove data from ringbuffer */static int read_buffer(unsigned char* data,int len){  int first_len = ao->buffer_len - ao->buf_read_pos;  int buffered = buf_used();  if (len > buffered) len = buffered;  if (first_len > len) first_len = len;  // till end of buffer  memcpy (data, &ao->buffer[ao->buf_read_pos], first_len);  if (len > first_len) { // we have to wrap around    // remaining part from beginning of buffer    memcpy (&data[first_len], ao->buffer, len - first_len);  }  ao->buf_read_pos = (ao->buf_read_pos + len) % ao->buffer_len;  return len;}OSStatus theRenderProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData){int amt=buf_used();int req=(inNumFrames)*ao->packetSize;	if(amt>req) 		amt=req;	if(amt)		read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt);	else audio_pause();	ioData->mBuffers[0].mDataByteSize = amt; 	return noErr;}static int control(int cmd,void *arg){ao_control_vol_t *control_vol;OSStatus err;Float32 vol;	switch (cmd) {	case AOCONTROL_GET_VOLUME:		control_vol = (ao_control_vol_t*)arg;		err = AudioUnitGetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &vol);		if(err==0) {			// printf("GET VOL=%f\n", vol);			control_vol->left=control_vol->right=vol*100.0/4.0;			return CONTROL_TRUE;		}		else {			return CONTROL_FALSE;		}	case AOCONTROL_SET_VOLUME:		control_vol = (ao_control_vol_t*)arg;				vol=(control_vol->left+control_vol->right)*4.0/200.0;		err = AudioUnitSetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0);		if(err==0) {			// printf("SET VOL=%f\n", vol);			return CONTROL_TRUE;		}		else {			return CONTROL_FALSE;		}	  /* Everything is currently unimplemented */	default:	  return CONTROL_FALSE;	}	}static void print_format(const char* str,AudioStreamBasicDescription *f){    uint32_t flags=(uint32_t) f->mFormatFlags;    ao_msg(MSGT_AO,MSGL_V, "%s %7.1fHz %dbit [%c%c%c%c] %s %s %s%s%s%s\n",	    str, f->mSampleRate, f->mBitsPerChannel,	    (int)(f->mFormatID & 0xff000000) >> 24,	    (int)(f->mFormatID & 0x00ff0000) >> 16,	    (int)(f->mFormatID & 0x0000ff00) >>  8,	    (int)(f->mFormatID & 0x000000ff) >>  0,	    (flags&kAudioFormatFlagIsFloat) ? "float" : "int",	    (flags&kAudioFormatFlagIsBigEndian) ? "BE" : "LE",	    (flags&kAudioFormatFlagIsSignedInteger) ? "S" : "U",	    (flags&kAudioFormatFlagIsPacked) ? " packed" : "",	    (flags&kAudioFormatFlagIsAlignedHigh) ? " aligned" : "",	    (flags&kAudioFormatFlagIsNonInterleaved) ? " ni" : "" );    ao_msg(MSGT_AO,MSGL_DBG2, "%5d mBytesPerPacket\n",	    (int)f->mBytesPerPacket);    ao_msg(MSGT_AO,MSGL_DBG2, "%5d mFramesPerPacket\n",	    (int)f->mFramesPerPacket);    ao_msg(MSGT_AO,MSGL_DBG2, "%5d mBytesPerFrame\n",	    (int)f->mBytesPerFrame);    ao_msg(MSGT_AO,MSGL_DBG2, "%5d mChannelsPerFrame\n",	    (int)f->mChannelsPerFrame);}static int init(int rate,int channels,int format,int flags){AudioStreamBasicDescription inDesc;ComponentDescription desc; Component comp; AURenderCallbackStruct renderCallback;OSStatus err;UInt32 size, maxFrames;int aoIsCreated = ao != NULL;	if (!aoIsCreated)	ao = malloc(sizeof(ao_macosx_t));	// Build Description for the input format	inDesc.mSampleRate=rate;	inDesc.mFormatID=kAudioFormatLinearPCM;	inDesc.mChannelsPerFrame=channels;	switch(format&AF_FORMAT_BITS_MASK){	case AF_FORMAT_8BIT:		inDesc.mBitsPerChannel=8;		break;	case AF_FORMAT_16BIT:		inDesc.mBitsPerChannel=16;		break;	case AF_FORMAT_24BIT:		inDesc.mBitsPerChannel=24;		break;	case AF_FORMAT_32BIT:		inDesc.mBitsPerChannel=32;		break;	default:		ao_msg(MSGT_AO, MSGL_WARN, "Unsupported format (0x%08x)\n", format);		return CONTROL_FALSE;		break;	}    if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) {	// float		inDesc.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked;    }    else if((format&AF_FORMAT_SIGN_MASK)==AF_FORMAT_SI) {	// signed int		inDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;    }    else {	// unsigned int		inDesc.mFormatFlags = kAudioFormatFlagIsPacked;    }      if((format&AF_FORMAT_END_MASK)==AF_FORMAT_BE)		inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;    inDesc.mFramesPerPacket = 1;    ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8);    print_format("source: ",&inDesc);	if (!aoIsCreated) {	desc.componentType = kAudioUnitType_Output;	desc.componentSubType = kAudioUnitSubType_DefaultOutput;	desc.componentManufacturer = kAudioUnitManufacturer_Apple;	desc.componentFlags = 0;	desc.componentFlagsMask = 0;					comp = FindNextComponent(NULL, &desc);  //Finds an component that meets the desc spec's	if (comp == NULL) {		ao_msg(MSGT_AO, MSGL_WARN, "Unable to find Output Unit component\n");		return CONTROL_FALSE;	}			err = OpenAComponent(comp, &(ao->theOutputUnit));  //gains access to the services provided by the component	if (err) {		ao_msg(MSGT_AO, MSGL_WARN, "Unable to open Output Unit component (err=%d)\n", err);		return CONTROL_FALSE;	}	// Initialize AudioUnit 	err = AudioUnitInitialize(ao->theOutputUnit);	if (err) {		ao_msg(MSGT_AO, MSGL_WARN, "Unable to initialize Output Unit component (err=%d)\n", err);		return CONTROL_FALSE;	}	}	size =  sizeof(AudioStreamBasicDescription);	err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &inDesc, size);	if (err) {		ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the input format (err=%d)\n", err);		return CONTROL_FALSE;	}	size = sizeof(UInt32);	err = AudioUnitGetProperty(ao->theOutputUnit, kAudioDevicePropertyBufferSize, kAudioUnitScope_Input, 0, &maxFrames, &size);		if (err)	{		ao_msg(MSGT_AO,MSGL_WARN, "AudioUnitGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)err);		return CONTROL_FALSE;	}	ao->chunk_size = maxFrames;//*inDesc.mBytesPerFrame;    	ao_data.samplerate = inDesc.mSampleRate;	ao_data.channels = inDesc.mChannelsPerFrame;    ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame;    ao_data.outburst = ao->chunk_size;	ao_data.buffersize = ao_data.bps;	ao->num_chunks = (ao_data.bps+ao->chunk_size-1)/ao->chunk_size;    ao->buffer_len = (ao->num_chunks + 1) * ao->chunk_size;    ao->buffer = aoIsCreated ? realloc(ao->buffer,(ao->num_chunks + 1)*ao->chunk_size)							: calloc(ao->num_chunks + 1, ao->chunk_size);		ao_msg(MSGT_AO,MSGL_V, "using %5d chunks of %d bytes (buffer len %d bytes)\n", (int)ao->num_chunks, (int)ao->chunk_size, (int)ao->buffer_len);    renderCallback.inputProc = theRenderProc;    renderCallback.inputProcRefCon = 0;    err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct));	if (err) {		ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the render callback (err=%d)\n", err);		return CONTROL_FALSE;	}	reset();        return CONTROL_OK;}static int play(void* output_samples,int num_bytes,int flags){  int wrote=write_buffer(output_samples, num_bytes);	audio_resume();  return wrote;}/* set variables and buffer to initial state */static void reset(void){  audio_pause();  /* reset ring-buffer state */  ao->buf_read_pos=0;  ao->buf_write_pos=0;    return;}/* return available space */static int get_space(void){  return buf_free();}/* return delay until audio is played */static float get_delay(void){  int buffered = ao->buffer_len - ao->chunk_size - buf_free(); // could be less  // inaccurate, should also contain the data buffered e.g. by the OS  return (float)(buffered)/(float)ao_data.bps;}/* unload plugin and deregister from coreaudio */static void uninit(int immed){  if (!immed) {    long long timeleft=(1000000LL*buf_used())/ao_data.bps;    ao_msg(MSGT_AO,MSGL_DBG2, "%d bytes left @%d bps (%ld usec)\n", buf_used(), ao_data.bps, (int)timeleft);    usec_sleep((int)timeleft);  }  AudioOutputUnitStop(ao->theOutputUnit);  AudioUnitUninitialize(ao->theOutputUnit);  CloseComponent(ao->theOutputUnit);  free(ao->buffer);  free(ao);  ao = NULL;}/* stop playing, keep buffers (for pause) */static void audio_pause(void){  OSErr status=noErr;  /* stop callback */  status=AudioOutputUnitStop(ao->theOutputUnit);  if (status)    ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStop returned %d\n",	   (int)status);  ao->paused=1;}/* resume playing, after audio_pause() */static void audio_resume(void){  if(ao->paused) {  OSErr status=noErr;	  /* start callback */  status=AudioOutputUnitStart(ao->theOutputUnit);  if (status)    ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStart returned %d\n",	   (int)status);	  ao->paused=0;  }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?