mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-28 21:40:46 +00:00
403 lines
13 KiB
C
403 lines
13 KiB
C
|
/*
|
||
|
* pa_conversions.c
|
||
|
* portaudio
|
||
|
*
|
||
|
* Created by Phil Burk on Mon Mar 18 2002.
|
||
|
*
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include "portaudio.h"
|
||
|
#include "pa_host.h"
|
||
|
|
||
|
#define CLIP( val, min, max ) { val = ((val) < (min)) ? min : (((val) < (max)) ? (max) : (val)); }
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int16(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
short *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
short samp = (short) (*sourceBuffer * (32767.0f));
|
||
|
*targetBuffer = samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int16_Clip(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
short *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
long samp = (long) (*sourceBuffer * (32767.0f));
|
||
|
CLIP( samp, -0x8000, 0x7FFF );
|
||
|
*targetBuffer = (short) samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int16_ClipDither(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
short *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
// use smaller scaler to prevent overflow when we add the dither
|
||
|
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
|
||
|
float dithered = (*sourceBuffer * (32766.0f)) + dither;
|
||
|
long samp = (long) dithered;
|
||
|
CLIP( samp, -0x8000, 0x7FFF );
|
||
|
*targetBuffer = (short) samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int16_Dither(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
short *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
// use smaller scaler to prevent overflow when we add the dither
|
||
|
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
|
||
|
float dithered = (*sourceBuffer * (32766.0f)) + dither;
|
||
|
*targetBuffer = (short) dithered;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Int16_Float32(
|
||
|
short *sourceBuffer, int sourceStride,
|
||
|
float *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
float samp = *sourceBuffer * (1.0f / 32768.0f);
|
||
|
*targetBuffer = samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int8(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
char *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
char samp = (char) (*sourceBuffer * (127.0));
|
||
|
*targetBuffer = samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int8_Clip(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
char *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
long samp = *sourceBuffer * 127.0f;
|
||
|
CLIP( samp, -0x80, 0x7F );
|
||
|
*targetBuffer = (char) samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int8_ClipDither(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
char *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
// use smaller scaler to prevent overflow when we add the dither
|
||
|
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
|
||
|
float dithered = (*sourceBuffer * (126.0f)) + dither;
|
||
|
long samp = (long) dithered;
|
||
|
CLIP( samp, -0x80, 0x7F );
|
||
|
*targetBuffer = (char) samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_Int8_Dither(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
char *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
// use smaller scaler to prevent overflow when we add the dither
|
||
|
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; //FIXME
|
||
|
float dithered = (*sourceBuffer * (126.0f)) + dither;
|
||
|
long samp = (long) dithered;
|
||
|
*targetBuffer = (char) samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Int8_Float32(
|
||
|
char *sourceBuffer, int sourceStride,
|
||
|
float *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
float samp = *sourceBuffer * (1.0f / 128.0f);
|
||
|
*targetBuffer = samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_Float32_UInt8(
|
||
|
float *sourceBuffer, int sourceStride,
|
||
|
unsigned char *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
unsigned char samp = 128 + (unsigned char) (*sourceBuffer * (127.0));
|
||
|
*targetBuffer = samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static void PaConvert_UInt8_Float32(
|
||
|
unsigned char *sourceBuffer, int sourceStride,
|
||
|
float *targetBuffer, int targetStride,
|
||
|
int numSamples )
|
||
|
{
|
||
|
int i;
|
||
|
for( i=0; i<numSamples; i++ )
|
||
|
{
|
||
|
float samp = (*sourceBuffer - 128) * (1.0f / 128.0f);
|
||
|
*targetBuffer = samp;
|
||
|
sourceBuffer += sourceStride;
|
||
|
targetBuffer += targetStride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
static PortAudioConverter *PaConvert_SelectProc( PaSampleFormat sourceFormat,
|
||
|
PaSampleFormat targetFormat, int ifClip, int ifDither )
|
||
|
{
|
||
|
PortAudioConverter *proc = NULL;
|
||
|
switch( sourceFormat )
|
||
|
{
|
||
|
case paUInt8:
|
||
|
switch( targetFormat )
|
||
|
{
|
||
|
case paFloat32:
|
||
|
proc = (PortAudioConverter *) PaConvert_UInt8_Float32;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case paInt8:
|
||
|
switch( targetFormat )
|
||
|
{
|
||
|
case paFloat32:
|
||
|
proc = (PortAudioConverter *) PaConvert_Int8_Float32;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case paInt16:
|
||
|
switch( targetFormat )
|
||
|
{
|
||
|
case paFloat32:
|
||
|
proc = (PortAudioConverter *) PaConvert_Int16_Float32;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case paFloat32:
|
||
|
switch( targetFormat )
|
||
|
{
|
||
|
case paUInt8:
|
||
|
proc = (PortAudioConverter *) PaConvert_Float32_UInt8;
|
||
|
break;
|
||
|
case paInt8:
|
||
|
if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_ClipDither;
|
||
|
else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Clip;
|
||
|
else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Dither;
|
||
|
else proc = (PortAudioConverter *) PaConvert_Float32_Int8;
|
||
|
break;
|
||
|
case paInt16:
|
||
|
if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_ClipDither;
|
||
|
else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Clip;
|
||
|
else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Dither;
|
||
|
else proc = (PortAudioConverter *) PaConvert_Float32_Int16;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return proc;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
PaError PaConvert_SetupInput( internalPortAudioStream *past,
|
||
|
PaSampleFormat nativeInputSampleFormat )
|
||
|
{
|
||
|
past->past_NativeInputSampleFormat = nativeInputSampleFormat;
|
||
|
past->past_InputConversionSourceStride = 1;
|
||
|
past->past_InputConversionTargetStride = 1;
|
||
|
|
||
|
if( nativeInputSampleFormat != past->past_InputSampleFormat )
|
||
|
{
|
||
|
int ifDither = (past->past_Flags & paDitherOff) == 0;
|
||
|
past->past_InputConversionProc = PaConvert_SelectProc( nativeInputSampleFormat,
|
||
|
past->past_InputSampleFormat, 0, ifDither );
|
||
|
if( past->past_InputConversionProc == NULL ) return paSampleFormatNotSupported;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
past->past_InputConversionProc = NULL; /* no conversion necessary */
|
||
|
}
|
||
|
|
||
|
return paNoError;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
PaError PaConvert_SetupOutput( internalPortAudioStream *past,
|
||
|
PaSampleFormat nativeOutputSampleFormat )
|
||
|
{
|
||
|
|
||
|
past->past_NativeOutputSampleFormat = nativeOutputSampleFormat;
|
||
|
past->past_OutputConversionSourceStride = 1;
|
||
|
past->past_OutputConversionTargetStride = 1;
|
||
|
|
||
|
if( nativeOutputSampleFormat != past->past_OutputSampleFormat )
|
||
|
{
|
||
|
int ifDither = (past->past_Flags & paDitherOff) == 0;
|
||
|
int ifClip = (past->past_Flags & paClipOff) == 0;
|
||
|
|
||
|
past->past_OutputConversionProc = PaConvert_SelectProc( past->past_OutputSampleFormat,
|
||
|
nativeOutputSampleFormat, ifClip, ifDither );
|
||
|
if( past->past_OutputConversionProc == NULL ) return paSampleFormatNotSupported;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
past->past_OutputConversionProc = NULL; /* no conversion necessary */
|
||
|
}
|
||
|
|
||
|
return paNoError;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************
|
||
|
** Called by host code.
|
||
|
** Convert input from native format to user format,
|
||
|
** call user code,
|
||
|
** then convert output to native format.
|
||
|
** Returns result from user callback.
|
||
|
*/
|
||
|
long PaConvert_Process( internalPortAudioStream *past,
|
||
|
void *nativeInputBuffer,
|
||
|
void *nativeOutputBuffer )
|
||
|
{
|
||
|
int userResult;
|
||
|
void *inputBuffer = NULL;
|
||
|
void *outputBuffer = NULL;
|
||
|
|
||
|
/* Get native input data. */
|
||
|
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
||
|
{
|
||
|
if( past->past_InputSampleFormat == past->past_NativeInputSampleFormat )
|
||
|
{
|
||
|
/* Already in native format so just read directly from native buffer. */
|
||
|
inputBuffer = nativeInputBuffer;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
inputBuffer = past->past_InputBuffer;
|
||
|
/* Convert input data to user format. */
|
||
|
(*past->past_InputConversionProc)(nativeInputBuffer, past->past_InputConversionSourceStride,
|
||
|
inputBuffer, past->past_InputConversionTargetStride,
|
||
|
past->past_FramesPerUserBuffer * past->past_NumInputChannels );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Are we doing output? */
|
||
|
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
||
|
{
|
||
|
outputBuffer = (past->past_OutputConversionProc == NULL) ?
|
||
|
nativeOutputBuffer : past->past_OutputBuffer;
|
||
|
}
|
||
|
/*
|
||
|
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
|
||
|
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
|
||
|
*/
|
||
|
/* Call user callback routine. */
|
||
|
userResult = past->past_Callback(
|
||
|
inputBuffer,
|
||
|
outputBuffer,
|
||
|
past->past_FramesPerUserBuffer,
|
||
|
past->past_FrameCount,
|
||
|
past->past_UserData );
|
||
|
|
||
|
/* Advance frame counter for timestamp. */
|
||
|
past->past_FrameCount += past->past_FramesPerUserBuffer; // FIXME - should this be in here?
|
||
|
|
||
|
/* Convert to native format if necessary. */
|
||
|
if( (past->past_OutputConversionProc != NULL ) && (outputBuffer != NULL) )
|
||
|
{
|
||
|
(*past->past_OutputConversionProc)( outputBuffer, past->past_OutputConversionSourceStride,
|
||
|
nativeOutputBuffer, past->past_OutputConversionTargetStride,
|
||
|
past->past_FramesPerUserBuffer * past->past_NumOutputChannels );
|
||
|
}
|
||
|
|
||
|
return userResult;
|
||
|
}
|