--- timidity/portaudio_a.c.neoclust 2004-05-18 05:59:34.000000000 +0200 +++ timidity/portaudio_a.c 2006-02-04 02:10:31.000000000 +0100 @@ -17,11 +17,13 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - portaudio_a.c by Avatar <avatar@deva.net> + portaudio_a.c by skeishi <s_keishi@mutt.freemail.ne.jp> based on esd_a.c Functions to play sound through EsounD */ +#define PORTAUDIO_V19 1 + #ifdef HAVE_CONFIG_H #include "config.h" @@ -63,8 +65,9 @@ static int output_data(char *buf, int32 nbytes); static int acntl(int request, void *arg); -static int framesPerBuffer=256; +static int framesPerBuffer=128; static int stereo=2; +static int conv16_32 =0; static int data_nbyte; static int numBuffers; static unsigned int framesPerInBuffer; @@ -72,13 +75,22 @@ //static int firsttime; static int pa_active=0; static int first=1; + +#if PORTAUDIO_V19 +PaDeviceIndex DeviceIndex; +const PaDeviceInfo *DeviceInfo; +PaStreamParameters StreamParameters; +PaStream *stream; +PaError err; +#else PaDeviceID DeviceID; const PaDeviceInfo *DeviceInfo; PortAudioStream *stream; PaError err; +#endif typedef struct { char buf[DATA_BLOCK_SIZE*2]; - int32 samplesToGo; + uint32 samplesToGo; char *bufpoint; char *bufepoint; } padata_t; @@ -93,7 +105,7 @@ PlayMode portaudio_asio_play_mode = { (SAMPLE_RATE), PE_16BIT|PE_SIGNED, - PF_PCM_STREAM|PF_BUFF_FRAGM_OPT/*|PF_CAN_TRACE*/, + PF_PCM_STREAM|PF_BUFF_FRAGM_OPT|PF_CAN_TRACE, -1, {32}, /* PF_BUFF_FRAGM_OPT is need for TWSYNTH */ "PortAudio(ASIO)", 'o', @@ -106,7 +118,7 @@ PlayMode portaudio_win_ds_play_mode = { (SAMPLE_RATE), PE_16BIT|PE_SIGNED, - PF_PCM_STREAM|PF_BUFF_FRAGM_OPT/*|PF_CAN_TRACE*/, + PF_PCM_STREAM|PF_BUFF_FRAGM_OPT|PF_CAN_TRACE, -1, {32}, /* PF_BUFF_FRAGM_OPT is need for TWSYNTH */ "PortAudio(DirectSound)", 'P', @@ -119,7 +131,7 @@ PlayMode portaudio_win_wmme_play_mode = { (SAMPLE_RATE), PE_16BIT|PE_SIGNED, - PF_PCM_STREAM|PF_BUFF_FRAGM_OPT/*|PF_CAN_TRACE*/, + PF_PCM_STREAM|PF_BUFF_FRAGM_OPT|PF_CAN_TRACE, -1, {32}, /* PF_BUFF_FRAGM_OPT is need for TWSYNTH */ "PortAudio(WMME)", 'p', @@ -139,7 +151,7 @@ PlayMode dpm = { (SAMPLE_RATE), PE_16BIT|PE_SIGNED, - PF_PCM_STREAM|PF_BUFF_FRAGM_OPT/*|PF_CAN_TRACE*/, + PF_PCM_STREAM|PF_BUFF_FRAGM_OPT|PF_CAN_TRACE, -1, {32}, /* PF_BUFF_FRAGM_OPT is need for TWSYNTH */ "Portaudio Driver", 'p', @@ -151,45 +163,90 @@ }; #endif - +#if PORTAUDIO_V19 +int paCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +#else int paCallback( void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - - PaTimestamp outTime, void *userData ) - + PaTimestamp outTime, + void *userData ) +#endif { unsigned int i; int finished = 0; /* Cast data passed through stream to our structure type. */ - // pa_data_t pa_data = (pa_data_t*)userData; - char *out = (char*)outputBuffer; - if(pa_data.samplesToGo < framesPerBuffer*data_nbyte*stereo ){ - for(i=0;i<pa_data.samplesToGo;i++){ - *out++ = *(pa_data.bufpoint)++; - if( pa_data.buf+bytesPerInBuffer*2 <= pa_data.bufpoint ){ - pa_data.bufpoint=pa_data.buf; + unsigned long datalength = framesPerBuffer*data_nbyte*stereo; + char * buflimit = pa_data.buf+bytesPerInBuffer*2; + + if(conv16_32){ + if(pa_data.samplesToGo < datalength ){ + for(i=0;i<pa_data.samplesToGo/2;i++){ + *out++ = 0; + *out++ = 0; + *out++ = *(pa_data.bufpoint)++; + *out++ = *(pa_data.bufpoint)++; + if( buflimit <= pa_data.bufpoint ){ + pa_data.bufpoint=pa_data.buf; + } } + pa_data.samplesToGo=0; + for(;i<datalength/2;i++){ + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = 0; + } + finished = 0; + }else{ + for(i=0;i<datalength/2;i++){ + *out++ = 0; + *out++ = 0; + *out++=*(pa_data.bufpoint)++; + *out++=*(pa_data.bufpoint)++; + if( buflimit <= pa_data.bufpoint ){ + pa_data.bufpoint=pa_data.buf; + } + } + pa_data.samplesToGo -= datalength; } - pa_data.samplesToGo=0; - for(;i<framesPerBuffer*data_nbyte*stereo;i++){ - *out++=0; - } - finished = 0; }else{ - for(i=0;i<framesPerBuffer*data_nbyte*stereo;i++){ - *out++=*(pa_data.bufpoint)++; - if( pa_data.buf+bytesPerInBuffer*2 <= pa_data.bufpoint ){ - pa_data.bufpoint=pa_data.buf; + if(pa_data.samplesToGo < datalength ){ + if(pa_data.bufpoint+datalength <= buflimit){ + memcpy(out, pa_data.bufpoint, datalength); + pa_data.bufpoint += datalength; + }else{ + int32 send; + send = buflimit-pa_data.bufpoint; + memcpy(out, pa_data.bufpoint, send); + out +=send; + memcpy(out, pa_data.buf, datalength -send); + pa_data.bufpoint = pa_data.buf+datalength -send; + } + memset(out, 0x0, datalength-pa_data.samplesToGo); + finished = 0; + }else{ + if(pa_data.bufpoint + datalength <= buflimit){ + memcpy(out, pa_data.bufpoint, datalength); + pa_data.bufpoint += datalength; + }else{ + int32 send; + send = buflimit-pa_data.bufpoint; + memcpy(out, pa_data.bufpoint, send); + out += send; + memcpy(out, pa_data.buf, datalength -send); + pa_data.bufpoint = pa_data.buf+datalength -send; } + pa_data.samplesToGo -= datalength; } - pa_data.samplesToGo -= framesPerBuffer*data_nbyte*stereo; } - return finished ; } @@ -234,16 +291,83 @@ } } #endif + /* if call twice Pa_OpenStream causes paDeviceUnavailable error */ + if(pa_active == 1) return 0; if(pa_active == 0){ err = Pa_Initialize(); if( err != paNoError ) goto error; pa_active = 1; } - if(first == 1){ - atexit(close_output); - first = 0; + +#ifdef PORTAUDIO_V19 + DeviceIndex = Pa_GetDefaultOutputDevice(); + if(DeviceIndex==paNoDevice) goto error; + DeviceInfo = Pa_GetDeviceInfo( DeviceIndex); + if(DeviceInfo==NULL) goto error; + + if (dpm.encoding & PE_24BIT) { + SampleFormat = paInt24; + }else if (dpm.encoding & PE_16BIT) { + SampleFormat = paInt16; + }else { + SampleFormat = paInt8; } + stereo = (dpm.encoding & PE_MONO) ? 1 : 2; + data_nbyte = (dpm.encoding & PE_16BIT) ? 2 : 1; + data_nbyte = (dpm.encoding & PE_24BIT) ? 3 : data_nbyte; + + pa_data.samplesToGo = 0; + pa_data.bufpoint = pa_data.buf; + pa_data.bufepoint = pa_data.buf; +// firsttime = 1; + numBuffers = 1; //Pa_GetMinNumBuffers( framesPerBuffer, dpm.rate ); + framesPerInBuffer = numBuffers * framesPerBuffer; + if (framesPerInBuffer < 4096) framesPerInBuffer = 4096; + bytesPerInBuffer = framesPerInBuffer * data_nbyte * stereo; + + /* set StreamParameters */ + StreamParameters.device = DeviceIndex; + StreamParameters.channelCount = stereo; + if(ctl->id_character != 'r' && ctl->id_character != 'A' && ctl->id_character != 'W' && ctl->id_character != 'P'){ + StreamParameters.suggestedLatency = DeviceInfo->defaultHighOutputLatency; + }else{ + StreamParameters.suggestedLatency = DeviceInfo->defaultLowOutputLatency; + } + StreamParameters.hostApiSpecificStreamInfo = NULL; + + if( SampleFormat == paInt16){ + StreamParameters.sampleFormat = paInt16; + if( paFormatIsSupported != Pa_IsFormatSupported( NULL , + &StreamParameters,(double) dpm.rate )){ + StreamParameters.sampleFormat = paInt32; + conv16_32 = 1; + } else { + StreamParameters.sampleFormat = paInt16; + conv16_32 = 0; + } + }else{ + StreamParameters.sampleFormat = SampleFormat; + } + err = Pa_IsFormatSupported( NULL , + &StreamParameters, + (double) dpm.rate ); + if ( err != paNoError) goto error; + err = Pa_OpenStream( + & stream, /* passes back stream pointer */ + NULL, /* inputStreamParameters */ + &StreamParameters, /* outputStreamParameters */ + (double) dpm.rate, /* sample rate */ + paFramesPerBufferUnspecified, /* frames per buffer */ + paFramesPerBufferUnspecified, /* PaStreamFlags */ + paCallback, /* specify our custom callback */ + &pa_data /* pass our data through to callback */ + ); +// Pa_Sleeep(1); + if ( err != paNoError) goto error; + return 0; + +#else DeviceID = Pa_GetDefaultOutputDeviceID(); if(DeviceID==paNoDevice) goto error; DeviceInfo = Pa_GetDeviceInfo( DeviceID); @@ -252,13 +376,21 @@ exclude_enc = PE_ULAW | PE_ALAW | PE_BYTESWAP; include_enc = PE_SIGNED; - if (!(nativeSampleFormats & paInt16)) {exclude_enc |= PE_16BIT;} + if (!(nativeSampleFormats & paInt16) && !(nativeSampleFormats & paInt32)) {exclude_enc |= PE_16BIT;} if (!(nativeSampleFormats & paInt24)) {exclude_enc |= PE_24BIT;} dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc); - if (dpm.encoding & PE_24BIT) {SampleFormat = paInt24;} - else if (dpm.encoding & PE_16BIT) {SampleFormat = paInt16;} - else {SampleFormat = paInt8;} + if (dpm.encoding & PE_24BIT) { + SampleFormat = paInt24; + }else if (dpm.encoding & PE_16BIT) { + if(nativeSampleFormats & paInt16) SampleFormat = paInt16; + else{ + SampleFormat = paInt32; + conv16_32 = 1; + } + }else { + SampleFormat = paInt8; + } stereo = (dpm.encoding & PE_MONO) ? 1 : 2; data_nbyte = (dpm.encoding & PE_16BIT) ? 2 : 1; @@ -300,6 +432,8 @@ if ( err != paNoError && err != paHostError) goto error; return 0; +#endif + error: Pa_Terminate(); pa_active = 0; #ifdef AU_PORTAUDIO_DLL @@ -316,11 +450,17 @@ // if(pa_data.samplesToGo > DATA_BLOCK_SIZE){ // Sleep( (pa_data.samplesToGo - DATA_BLOCK_SIZE)/dpm.rate/4 ); // } - for(i=0;i<nbytes;i++){ - *(pa_data.bufepoint)++ = *buf++ ; - if( pa_data.buf+bytesPerInBuffer*2 <= pa_data.bufepoint ){ - pa_data.bufepoint=pa_data.buf; - } + if (pa_data.buf+bytesPerInBuffer*2 >= pa_data.bufepoint + nbytes){ + memcpy(pa_data.bufepoint, buf, nbytes); + pa_data.bufepoint += nbytes; + //buf += nbytes; + }else{ + int32 send = pa_data.buf+bytesPerInBuffer*2 - pa_data.bufepoint; + memcpy(pa_data.bufepoint, buf, send); + buf += send; + memcpy(pa_data.buf, buf, nbytes - send); + pa_data.bufepoint = pa_data.buf + nbytes - send; + //buf += nbytes-send; } pa_data.samplesToGo += nbytes; @@ -332,12 +472,18 @@ firsttime=0; } */ +#if PORTAUDIO_V19 + if( 0==Pa_IsStreamActive(stream)){ +#else if( 0==Pa_StreamActive(stream)){ +#endif err = Pa_StartStream( stream ); if( err != paNoError ) goto error; } - while(pa_data.samplesToGo > bytesPerInBuffer){ Pa_Sleep(1);}; + + if(ctl->id_character != 'r' && ctl->id_character != 'A' && ctl->id_character != 'W' && ctl->id_character != 'P') + while((pa_active==1) && (pa_data.samplesToGo > bytesPerInBuffer)){ Pa_Sleep(1);}; // Pa_Sleep( (pa_data.samplesToGo - bytesPerInBuffer)/dpm.rate * 1000); return 0; @@ -350,7 +496,11 @@ static void close_output(void) { if( pa_active==0) return; +#if PORTAUDIO_V19 + if(Pa_IsStreamActive(stream)){ +#else if(Pa_StreamActive(stream)){ +#endif Pa_Sleep( bytesPerInBuffer/dpm.rate*1000 ); } err = Pa_StopStream( stream ); @@ -382,26 +532,26 @@ case PM_REQ_GETQSIZ: *(int *)arg = bytesPerInBuffer*2; return 0; - break; + //break; case PM_REQ_GETFILLABLE: *(int *)arg = bytesPerInBuffer*2-pa_data.samplesToGo; return 0; - break; + //break; case PM_REQ_GETFILLED: *(int *)arg = pa_data.samplesToGo; return 0; - break; + //break; case PM_REQ_DISCARD: Pa_StopStream( stream ); close_output(); open_output(); return 0; - break; + //break; case PM_REQ_FLUSH: close_output(); open_output(); return 0; - break; + //break; case PM_REQ_RATE: /* NOT WORK */ { int i; @@ -418,7 +568,7 @@ return -1; } } - break; + //break; } return -1; }