Commit a7b6e2f7 authored by phil's avatar phil

Initial revision


git-svn-id: https://subversion.assembla.com/svn/portaudio/portaudio/trunk@90 0f58301d-fd10-0410-b4af-bbb618454e57
parent fbddd655
# Make PortAudio for Linux
# Updated 2001/08/25 Bill Eldridge bill@rfa.org
# Updated 2001/10/16, philburk@softsynth.com, s/unix_oss/unix_oss/
# A pretty bare makefile, that figures out all the test files
# and compiles them against the library in the pa_unix_oss directory.
# Do "make all" and then when happy, "make libinstall"
# (if not happy, "make clean")
# The ldconfig stuff in libinstall is the wrong way to do it -
# someone tell me the right way, please
LIBS = -lm -lpthread
CDEFINES = -I../pa_common
CFLAGS = -g
LIBINST = /usr/local/lib
TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
LIBFILES:= ./pa_common/pa_lib.c ./pa_unix_oss/pa_unix_oss.c
.c.o:
-gcc -c -I./pa_common $< -o $*.o
-gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio
all: sharedlib tests
sharedlib: $(LIBFILES:.c=.o)
gcc -shared -o ./pa_unix_oss/libportaudio.so ./pa_common/pa_lib.o ./pa_unix_oss/pa_unix_oss.o
libinstall: ./pa_unix_oss/libportaudio.so
@cp -f ./pa_unix_oss/libportaudio.so $(LIBINST)
@/sbin/ldconfig -v
tests: $(TESTS:.c=.o)
clean:
-@rm -f $(TESTS:.c=.o)
-@rm -f $(TESTS:.c=)
-@rm -f $(LIBFILES:.c=.o)
-@rm -f ./pa_unix_oss/libportaudio.so
rem Use Astyle to fix style in 'C' files
cd %1%
fixlines -p *.c
fixlines -p *.cpp
fixlines -p *.cc
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.c
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cpp
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cc
del *.orig
@rem convert line terminators to Unix style LFs
fixlines -u *.c
fixlines -u *.cpp
fixlines -u *.cc
fixlines -u *.h
del *.bak
cd ..\
/*
* $Id$
* Portable Audio I/O Library for ASIO Drivers
*
* Author: Stephane Letz
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 2000-2001 Stephane Letz, Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Modification History
08-03-01 First version : Stephane Letz
08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
the stream is stopped : Stephane Letz
08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
the stream is stopped : Stephane Letz
10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
11-06-01 Rename functions : Stephane Letz
11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
TO DO :
- Check Pa_StopSteam and Pa_AbortStream
- Optimization for Input only or Ouput only (really necessary ??)
- Opening of several streams
*/
#include <stdio.h>
#include <assert.h>
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
#include "asiosys.h"
#include "asio.h"
#include "asiodrivers.h"
#if MAC
#include <Math64.h>
#else
#include <math.h>
#include <windows.h>
#include <mmsystem.h>
#endif
enum {
// number of input and outputs supported by the host application
// you can change these to higher or lower values
kMaxInputChannels = 32,
kMaxOutputChannels = 32
};
/* ASIO specific device information. */
typedef struct internalPortAudioDevice
{
PaDeviceInfo pad_Info;
} internalPortAudioDevice;
/* ASIO driver internal data storage */
typedef struct PaHostSoundControl
{
// ASIOInit()
ASIODriverInfo pahsc_driverInfo;
// ASIOGetChannels()
int32 pahsc_NumInputChannels;
int32 pahsc_NumOutputChannels;
// ASIOGetBufferSize() - sizes in frames per buffer
int32 pahsc_minSize;
int32 pahsc_maxSize;
int32 pahsc_preferredSize;
int32 pahsc_granularity;
// ASIOGetSampleRate()
ASIOSampleRate pahsc_sampleRate;
// ASIOOutputReady()
bool pahsc_postOutput;
// ASIOGetLatencies ()
int32 pahsc_inputLatency;
int32 pahsc_outputLatency;
// ASIOCreateBuffers ()
ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
// ASIOGetChannelInfo()
ASIOChannelInfo pahsc_channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
// The above two arrays share the same indexing, as the data in them are linked together
// Information from ASIOGetSamplePosition()
// data is converted to double floats for easier use, however 64 bit integer can be used, too
double nanoSeconds;
double samples;
double tcSamples; // time code samples
// bufferSwitchTimeInfo()
ASIOTime tInfo; // time info state
unsigned long sysRefTime; // system reference time, when bufferSwitch() was called
// Signal the end of processing in this example
bool stopped;
ASIOCallbacks pahsc_asioCallbacks;
int32 pahsc_userInputBufferFrameOffset; // Position in Input user buffer
int32 pahsc_userOutputBufferFrameOffset; // Position in Output user buffer
int32 pahsc_hostOutputBufferFrameOffset; // Position in Output ASIO buffer
int32 past_FramesPerHostBuffer; // Number of frames in ASIO buffer
int32 pahsc_InputBufferOffset; // Number of null frames for input buffer alignement
int32 pahsc_OutputBufferOffset; // Number of null frames for ouput buffer alignement
#if MAC
UInt64 pahsc_EntryCount;
UInt64 pahsc_LastExitCount;
#elif WINDOWS
LARGE_INTEGER pahsc_EntryCount;
LARGE_INTEGER pahsc_LastExitCount;
#endif
PaTimestamp pahsc_NumFramesDone;
internalPortAudioStream *past;
} PaHostSoundControl;
//----------------------------------------------------------
// name of the ASIO device to be used
#if WINDOWS
//#define ASIO_DRIVER_NAME "ASIO Multimedia Driver"
#define ASIO_DRIVER_NAME "ASIO Sample"
#elif MAC
//#define ASIO_DRIVER_NAME "Apple Sound Manager"
#define ASIO_DRIVER_NAME "ASIO Delta1010"
//#define ASIO_DRIVER_NAME "DigiDesign DirectIO"
#endif
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) /**/
/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
#define CARBON_COMPATIBLE (0)
#define PA_MAX_DEVICE_INFO (32)
#define MIN_INT8 (-0x80)
#define MAX_INT8 (0x7F)
#define MIN_INT8_FP ((float)-0x80)
#define MAX_INT8_FP ((float)0x7F)
#define MIN_INT16_FP ((float)-0x8000)
#define MAX_INT16_FP ((float)0x7FFF)
#define MIN_INT16 (-0x8000)
#define MAX_INT16 (0x7FFF)
#define MAX_INT32_FP (2147483520.0f) /* 0x0x7FFFFF80 - seems safe */
/************************************************************************************/
/****************** Data ************************************************************/
/************************************************************************************/
static int sNumDevices = 0;
static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
static int32 sPaHostError = 0;
static int sDefaultOutputDeviceID = 0;
static int sDefaultInputDeviceID = 0;
PaHostSoundControl asioDriverInfo = {0};
#ifdef MAC
static bool swap = true;
#elif WINDOWS
static bool swap = false;
#endif
// Prototypes
void bufferSwitch(long index, ASIOBool processNow);
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
void sampleRateChanged(ASIOSampleRate sRate);
long asioMessages(long selector, long value, void* message, double* opt);
static void Pa_StartUsageCalculation( internalPortAudioStream *past );
static void Pa_EndUsageCalculation( internalPortAudioStream *past );
void Pa_ASIO_Convert_Inter_Input(
ASIOBufferInfo* nativeBuffer,
void* inputBuffer,
short NumInputChannels,
short NumOuputChannels,
short framePerBuffer,
short hostFrameOffset,
short userFrameOffset,
ASIOSampleType nativeFormat,
PaSampleFormat paFormat,
PaStreamFlags flags,
short index);
void Pa_ASIO_Convert_Inter_Output(
ASIOBufferInfo* nativeBuffer,
void* outputBuffer,
short NumInputChannels,
short NumOuputChannels,
short framePerBuffer,
short hostFrameOffset,
short userFrameOffset,
ASIOSampleType nativeFormat,
PaSampleFormat paFormat,
PaStreamFlags flags,
short index);
void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer,
ASIOSampleType nativeFormat,
short NumInputChannels,
short NumOuputChannels,
int index,
int hostFrameOffset,
int frames);
void Pa_ASIO_Callback_Input(long index);
void Pa_ASIO_Callback_Output(long index, long framePerBuffer);
void Pa_ASIO_Callback_End();
void Pa_ASIO_Clear_User_Buffers();
// Some external references
extern AsioDrivers* asioDrivers ;
bool loadAsioDriver(char *name);
unsigned long get_sys_reference_time();
// For callback debugging on Mac : to be removed in the final version
/*
#define MidiSharePPC_68k
#include <MidiShare.h>
void SendVal (long val)
{
MidiEvPtr ev = MidiNewEv(typeTempo);
if (ev) {
Tempo(ev) = val;
MidiSendIm(0,ev);
}
}
void MidiPrintText ( char * s)
{
MidiEvPtr e;
long c = 0;
if (e = MidiNewEv(typeTextual)){
for (c = 0 ; *s ; s++,c++) MidiAddField (e ,*s);
MidiSendIm (0, e);
}
}
*/
/************************************************************************************/
/****************** Macro ************************************************************/
/************************************************************************************/
#define SwapLong(v) ((((v)>>24)&0xFF)|(((v)>>8)&0xFF00)|(((v)&0xFF00)<<8)|(((v)&0xFF)<<24)) ;
#define SwapShort(v) ((((v)>>8)&0xFF)|(((v)&0xFF)<<8)) ;
#define ClipShort(v) (((v)<MIN_INT16)?MIN_INT16:(((v)>MAX_INT16)?MAX_INT16:(v)))
#define ClipChar(v) (((v)<MIN_INT8)?MIN_INT8:(((v)>MAX_INT8)?MAX_INT8:(v)))
#define ClipFloat(v) (((v)<-1.0f)?-1.0f:(((v)>1.0f)?1.0f:(v)))
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef max
#define max(a,b) ((a)>=(b)?(a):(b))
#endif
// Utilities for alignement buffer size computation
int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);}
int PPCM (int a, int b) {return (a*b) / PGCD (a,b);}
// Takes the size of host buffer and user buffer : returns the number of frames needed for buffer alignement
int Pa_ASIO_CalcFrameShift (int M, int N)
{
int res = 0;
for (int i = M; i < PPCM (M,N) ; i+=M) { res = max (res, i%N); }
return res;
}
// We have the following relation :
// Pa_ASIO_CalcFrameShift (M,N) + M = Pa_ASIO_CalcFrameShift (N,M) + N
/* ASIO sample type to PortAudio sample type conversion */
static PaSampleFormat Pa_ASIO_Convert_SampleFormat(ASIOSampleType type)
{
switch (type) {
case ASIOSTInt16MSB:
case ASIOSTInt16LSB:
case ASIOSTInt32MSB16:
case ASIOSTInt32LSB16:
return paInt16;
case ASIOSTFloat32MSB:
case ASIOSTFloat32LSB:
case ASIOSTFloat64MSB:
case ASIOSTFloat64LSB:
return paFloat32;
case ASIOSTInt32MSB:
case ASIOSTInt32LSB:
case ASIOSTInt32MSB18:
case ASIOSTInt32MSB20:
case ASIOSTInt32MSB24:
case ASIOSTInt32LSB18:
case ASIOSTInt32LSB20:
case ASIOSTInt32LSB24:
return paInt32;
case ASIOSTInt24MSB:
case ASIOSTInt24LSB:
return paInt24;
default:
return paCustomFormat;
}
}
/* Allocate ASIO buffers, initialise channels */
static ASIOError Pa_ASIO_CreateBuffers (PaHostSoundControl *asioDriverInfo, long InputChannels,
long OutputChannels, long framesPerBuffer)
{
ASIOError err;
int i;
ASIOBufferInfo *info = asioDriverInfo->bufferInfos;
// Check parameters
if ((InputChannels > kMaxInputChannels) || (OutputChannels > kMaxInputChannels)) return ASE_InvalidParameter;
for(i = 0; i < InputChannels; i++, info++){
info->isInput = ASIOTrue;
info->channelNum = i;
info->buffers[0] = info->buffers[1] = 0;
}
for(i = 0; i < OutputChannels; i++, info++){
info->isInput = ASIOFalse;
info->channelNum = i;
info->buffers[0] = info->buffers[1] = 0;
}
// Set up the asioCallback structure and create the ASIO data buffer
asioDriverInfo->pahsc_asioCallbacks.bufferSwitch = &bufferSwitch;
asioDriverInfo->pahsc_asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioDriverInfo->pahsc_asioCallbacks.asioMessage = &asioMessages;
asioDriverInfo->pahsc_asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
DBUG(("PortAudio : ASIOCreateBuffers with size = %ld \n", framesPerBuffer));
err = ASIOCreateBuffers( asioDriverInfo->bufferInfos, InputChannels+OutputChannels,
framesPerBuffer, &asioDriverInfo->pahsc_asioCallbacks);
if (err != ASE_OK) return err;
// Initialise buffers
for (i = 0; i < InputChannels + OutputChannels; i++)
{
asioDriverInfo->pahsc_channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
asioDriverInfo->pahsc_channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
err = ASIOGetChannelInfo(&asioDriverInfo->pahsc_channelInfos[i]);
if (err != ASE_OK) break;
}
err = ASIOGetLatencies(&asioDriverInfo->pahsc_inputLatency, &asioDriverInfo->pahsc_outputLatency);
DBUG(("PortAudio : InputLatency = %ld latency = %ld msec \n",
asioDriverInfo->pahsc_inputLatency,
(long)((asioDriverInfo->pahsc_inputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
DBUG(("PortAudio : OuputLatency = %ld latency = %ld msec \n",
asioDriverInfo->pahsc_outputLatency,
(long)((asioDriverInfo->pahsc_outputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
return err;
}
/*
Query ASIO driver info :
First we get all available ASIO drivers located in the ASIO folder,
then try to load each one. For each loaded driver, get all needed informations.
*/
static PaError Pa_ASIO_QueryDeviceInfo( internalPortAudioDevice * ipad )
{
#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */
#define NUM_CUSTOMSAMPLINGRATES 9 /* must be the same number of elements as in the array below */
#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
ASIOSampleRate possibleSampleRates[]
= {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0};
ASIOChannelInfo channelInfos;
long InputChannels,OutputChannels;
double *sampleRates;
char* names[PA_MAX_DEVICE_INFO] ;
PaDeviceInfo *dev;
int i;
int numDrivers;
ASIOError asioError;
/* Allocate names */
for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) names[i] = (char*)PaHost_AllocateFastMemory(32);
/* MUST BE CHECKED : to force fragments loading on Mac */
loadAsioDriver("dummy");
/* Get names of all available ASIO drivers */
asioDrivers->getDriverNames(names,PA_MAX_DEVICE_INFO);
/* Check all available ASIO drivers */
#if MAC
numDrivers = asioDrivers->getNumFragments();
#elif WINDOWS
numDrivers = asioDrivers->asioGetNumDev();
#endif
DBUG(("PaASIO_QueryDeviceInfo: numDrivers = %d\n", numDrivers ));
for (int driver = 0 ; driver < numDrivers ; driver++)
{
#if WINDOWS
asioDriverInfo.pahsc_driverInfo.asioVersion = 2; // FIXME - is this right? PLB
asioDriverInfo.pahsc_driverInfo.sysRef = GetDesktopWindow(); // FIXME - is this right? PLB
#endif
/* If the driver can be loaded : */
if ( !loadAsioDriver(names[driver]) )
{
DBUG(("PaASIO_QueryDeviceInfo could not loadAsioDriver %s\n", names[driver]));
}
else if( (asioError = ASIOInit(&asioDriverInfo.pahsc_driverInfo)) != ASE_OK )
{
DBUG(("PaASIO_QueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, names[driver]));
}
else if( (ASIOGetChannels(&InputChannels, &OutputChannels) != ASE_OK))
{
DBUG(("PaASIO_QueryDeviceInfo could not ASIOGetChannels for %s\n", names[driver]));
}
else
{
/* Gets the name */
dev = &(ipad[driver].pad_Info);
dev->name = names[driver];
names[driver] = 0;
/* Gets Input and Output channels number */
dev->maxInputChannels = InputChannels;
dev->maxOutputChannels = OutputChannels;
DBUG(("PaASIO_QueryDeviceInfo: InputChannels = %d\n", InputChannels ));
DBUG(("PaASIO_QueryDeviceInfo: OutputChannels = %d\n", OutputChannels ));
/* Make room in case device supports all rates. */
sampleRates = (double*)PaHost_AllocateFastMemory( MAX_NUMSAMPLINGRATES * sizeof(double));
dev->sampleRates = sampleRates;
dev->numSampleRates = 0;
/* Loop through the possible sampling rates and check each to see if the device supports it. */
for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) {
if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) {
DBUG(("PortAudio : possible sample rate = %d\n", (long)possibleSampleRates[index]));
dev->numSampleRates+=1;
*sampleRates = possibleSampleRates[index];
sampleRates++;
}
}
/* We assume that all channels have the same SampleType, so check the first */
channelInfos.channel = 0;
channelInfos.isInput = 1;
ASIOGetChannelInfo(&channelInfos);
dev->nativeSampleFormats = Pa_ASIO_Convert_SampleFormat(channelInfos.type);
/* unload the driver */
ASIODisposeBuffers();
ASIOExit();
sNumDevices++;
}
}
/* free only unused names */
for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) if (names[i]) PaHost_FreeFastMemory(names[i],32);
return paNoError;
}
//----------------------------------------------------------------------------------
// TAKEN FROM THE ASIO SDK:
void sampleRateChanged(ASIOSampleRate sRate)
{
// do whatever you need to do if the sample rate changed
// usually this only happens during external sync.
// Audio processing is not stopped by the driver, actual sample rate
// might not have even changed, maybe only the sample rate status of an
// AES/EBU or S/PDIF digital input at the audio device.
// You might have to update time/sample related conversion routines, etc.
}
//----------------------------------------------------------------------------------
// TAKEN FROM THE ASIO SDK:
long asioMessages(long selector, long value, void* message, double* opt)
{