/* -*- c-file-style: "gnu" -*- TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo Copyright (C) 1995 Tuukka Toivonen ALSA 0.[56] support by Katsuhiro Ueno 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. darwin_a.c Functions to play sound on the Darwin audio driver by T.Nogami */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #ifndef NO_STRING_H #include #else #include #endif #include #include "timidity.h" #include "common.h" #include "output.h" #include "controls.h" #include "timer.h" #include "instrum.h" #include "playmidi.h" #include "miditrace.h" static int open_output(void); /* 0=success, 1=warning, -1=fatal error */ static void close_output(void); static int output_data(char *buf, int32 in_bytes); static int acntl(int request, void *arg); static int total_bytes; float output_volume = 1.0; int mac_buf_using_num; /* export the playback mode */ /*********************************************************************/ #define dpm darwin_play_mode PlayMode dpm = { DEFAULT_RATE, PE_16BIT|PE_SIGNED, PF_PCM_STREAM|PF_CAN_TRACE, -1, {0}, /* default: get all the buffer fragments you can */ "Mac OS X pcm device", 'd', "", open_output, close_output, output_data, acntl }; #define FailWithAction(cond, action, handler) \ if (cond) { \ { action; } \ goto handler; \ } //#define DEBUG_DARWIN_A(x) /*nothing*/ #define DEBUG_DARWIN_A(x) ctl->cmsg x; /*********************************************************************/ typedef struct { Boolean soundPlaying; AudioDeviceID device; // the default device UInt32 deviceBufferSize; // bufferSize returned by kAudioDevicePropertyBufferSize AudioStreamBasicDescription deviceFormat; // info about the default device #define BUFNUM 256 #define BUFLEN 4096 char buffer[BUFNUM][BUFLEN]; //1 buffer size = 4096bytes unsigned short buffer_len[BUFNUM]; volatile int currBuf, nextBuf; int samples, get_samples; } appGlobals, *appGlobalsPtr, **appGlobalsHandle; static appGlobals globals; /*********************************************************************/ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* dummy ) { int next_curr; int trans_len; if( globals.currBuf==globals.nextBuf ){ //end of play outOutputData->mNumberBuffers=0; memset(outOutputData->mBuffers[0].mData, 0, globals.deviceBufferSize); globals.soundPlaying=0; return 0; } trans_len = globals.buffer_len[globals.currBuf]; if( output_volume==1.0 ){ memcpy( outOutputData->mBuffers[0].mData, &globals.buffer[globals.currBuf][0], trans_len); // move data into output data buffer }else{ float *src = (float*)&globals.buffer[globals.currBuf][0], *dst = outOutputData->mBuffers[0].mData; int i, quant = trans_len/sizeof(float); for(i=0; imBuffers[0].mDataByteSize = trans_len; outOutputData->mNumberBuffers=1; globals.samples += trans_len / globals.deviceFormat.mBytesPerPacket; next_curr = globals.currBuf+1; next_curr %= BUFNUM; mac_buf_using_num--; globals.currBuf = next_curr; return 0; //no err } /*********************************************************************/ static void init_variable() { globals.samples = globals.get_samples = 0; globals.currBuf = globals.nextBuf = 0; globals.soundPlaying = 0; mac_buf_using_num = 0; } /*********************************************************************/ /*return value == 0 sucess * == -1 fails */ static int open_output(void) { OSStatus err = 0; //no err UInt32 count, bufferSize; AudioDeviceID device = kAudioDeviceUnknown; AudioStreamBasicDescription format; // get the default output device for the HAL count = sizeof(globals.device); // it is required to pass the size of the data to be returned err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &device); if (err != 0) goto Bail; // get the buffersize that the default device uses for IO count = sizeof(globals.deviceBufferSize); // it is required to pass the size of the data to be returned err = AudioDeviceGetProperty(device, 0, 0, kAudioDevicePropertyBufferSize, &count, &bufferSize); if (err != 0) goto Bail; if( globals.deviceBufferSize>BUFLEN ){ fprintf(stderr, "globals.deviceBufferSize NG: %ld\n", globals.deviceBufferSize); exit(1); } // get a description of the data format used by the default device count = sizeof(globals.deviceFormat); // it is required to pass the size of the data to be returned err = AudioDeviceGetProperty(device, 0, 0, kAudioDevicePropertyStreamFormat, &count, &format); if (err != 0) goto Bail; FailWithAction(format.mFormatID != kAudioFormatLinearPCM, err = -1, Bail); // bail if the format is not linear pcm // everything is ok so fill in these globals globals.device = device; globals.deviceBufferSize = bufferSize; globals.deviceFormat = format; init_variable(); err = AudioDeviceAddIOProc(globals.device, appIOProc, 0 ); // setup our device with an IO proc if (err != 0) goto Bail; globals.deviceFormat.mSampleRate = dpm.rate; #if 0 globals.deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; globals.deviceFormat.mBytesPerPacket = 4; globals.deviceFormat.mBytesPerFrame = 4; globals.deviceFormat.mBitsPerChannel = 0x10; err = AudioDeviceSetProperty(device, &inWhen, 0, 0, kAudioDevicePropertyStreamFormat, count, &globals.deviceFormat); if (err != 0) goto Bail; #endif #if 0 fprintf(stderr, "deviceBufferSize = %d\n", globals.deviceBufferSize); fprintf(stderr, "mSampleRate = %g\n", globals.deviceFormat.mSampleRate); fprintf(stderr, "mFormatID = 0x%08x\n", globals.deviceFormat.mFormatID); fprintf(stderr, "mFormatFlags = 0x%08x\n", globals.deviceFormat.mFormatFlags); fprintf(stderr, "mBytesPerPacket = 0x%08x\n", globals.deviceFormat.mBytesPerPacket); fprintf(stderr, "mBytesPerFrame = 0x%08x\n", globals.deviceFormat.mBytesPerFrame); fprintf(stderr, "mBitsPerChannel = 0x%08x\n", globals.deviceFormat.mBitsPerChannel); #endif Bail: return (err); } /*********************************************************************/ static int output_data(char *buf, int32 in_bytes) { OSStatus err = 0; int next_nextbuf, out_bytes; int inBytesPerQuant, max_quant, max_outbytes, out_quant, i; float maxLevel; //quant : 1 value //packet : 1 pair of quant(stereo data) if (dpm.encoding & PE_16BIT){ inBytesPerQuant = 2; maxLevel=32768.0; }else if(dpm.encoding & PE_24BIT){ inBytesPerQuant = 3; maxLevel=8388608.0; }else{ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Sorry, not support 8bit sound."); exit(1); } max_outbytes = globals.deviceBufferSize; max_quant = max_outbytes / sizeof(float); redo: out_quant = in_bytes/inBytesPerQuant; out_bytes= out_quant * sizeof(float); if( out_bytes > max_outbytes ){ out_bytes = max_outbytes; } out_quant = out_bytes/sizeof(float); out_quant &=0xfffffffe; //trunc to eaven number for stereo out_bytes = out_quant*sizeof(float); next_nextbuf = globals.nextBuf+1; next_nextbuf %= BUFNUM; while( globals.currBuf==next_nextbuf ){ //queue full usleep(100000); //0.1sec } switch( inBytesPerQuant ){ case 2: for( i=0; i>8) / maxLevel; } break; } globals.buffer_len[globals.nextBuf] = out_bytes; if( globals.soundPlaying == 0){ err = AudioDeviceStart(globals.device, appIOProc); if (err != 0) goto Bail; // start playing sound through the device globals.soundPlaying = 1; // set the playing status global to true } globals.nextBuf = next_nextbuf; in_bytes -= out_quant*inBytesPerQuant; buf += out_quant*inBytesPerQuant; mac_buf_using_num++; globals.get_samples += out_bytes/globals.deviceFormat.mBytesPerPacket; if( in_bytes ){ goto redo; } Bail: return (err); } /*********************************************************************/ static void close_output(void) { OSStatus err = 0; err = AudioDeviceStop(globals.device, appIOProc); // stop playing sound through the device if (err != 0) goto Bail; err = AudioDeviceRemoveIOProc(globals.device, appIOProc); // remove the IO proc from the device if (err != 0) goto Bail; globals.soundPlaying = 0; // set the playing status global to false Bail:; } /*********************************************************************/ static int acntl(int request, void *arg) { switch (request){ case PM_REQ_GETFRAGSIZ: if( dpm.encoding & PE_24BIT ){ *((int *)arg) = 3072; }else{ *((int *)arg) = BUFLEN; } return 0; case PM_REQ_GETSAMPLES: *((int *)arg) = globals.samples; return 0; case PM_REQ_DISCARD: AudioDeviceStop(globals.device, appIOProc); init_variable(); return 0; case PM_REQ_PLAY_START: init_variable(); return 0; case PM_REQ_FLUSH: case PM_REQ_OUTPUT_FINISH: while( globals.soundPlaying ){ trace_loop(); usleep(1000); } init_variable(); return 0; default: break; } return -1; }