mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-10-27 21:54:16 +00:00
New mod_portaudio (sponspred by eWorldCom http://www.eworldcom.hu/)
This updates mod_portaudio to use the new v19 api and also contains
major behavioural changes. This initial check-in should be tested to find
any obscure use cases that lead to crashes etc...
All of the old api interface commands are now depricated and any attempt to
use them should cause a polite warning asking you to try the new single "pa" command.
New Features:
*) Mulitiple calls with hold/call switching.
*) Inbound calls can play a ring file on specified device. (global and per call)
*) Optional hold music for backgrounded calls. (global and per call)
Example dialplan usage:
<extension name="2000">
<condition field="destination_number" expression="^2000$">
<!--if the next 3 lines are omitted the defaults will be used from portaudio.conf-->
<action application="set" data="pa_ring_file=/sounds/myring.wav"/>
<action application="set" data="pa_hold_file=/sounds/myhold.wav"/>
<action application="set" data="export_vars=pa_ring_file,pa_hold_file"/>
<action application="bridge" data="portaudio"/>
</condition>
</extension>
Example API interface usage:
call extension 1000
> pa call 1000
call extension 1001 putting the other call on hold
> pa call 1001
swap the calls between hold and active
> pa switch
view the current calls
> pa list
forground the call with id 1
> pa switch 1
background all calls
> pa switch none
send a dtmf string (1234) to the current call
> pa dtmf 1234
answer the oldest unanswered inbound call
> pa answer
answer the call with id 1
> pa answer 1
hangup the active call
> pa hangup
hangup the call with id 1
> pa hangup 1
get device info
> pa dump
print usage summary
> pa help
USAGE:
--------------------------------------------------------------------------------
pa help
pa dump
pa call <dest> [<dialplan> <cid_name> <cid_num> <rate>]
pa answer [<call_id>]
pa hangup [<call_id>]
pa list
pa switch [<call_id>|none]
pa_dtmf <digit string>
--------------------------------------------------------------------------------
The source of the portaudio v19 library will also be checked in for the
sake of the build system.
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3981 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
1 parent
0ac4fbc159
commit
39a9e94305
262 files changed
+149417
-1085
No files matched your search
@@ -1,10 +1,11 @@
|
||||
LDFLAGS += -lportaudio -L$(PREFIX)
|
||||
MYOBJS = pablio.o ringbuffer.o
|
||||
CFLAGS += -I.
|
||||
LDFLAGS += -lportaudio -L$(PREFIX)
|
||||
MYOBJS = pablio.o pa_ringbuffer.o
|
||||
|
||||
all: depends $(MODNAME).$(DYNAMIC_LIB_EXTEN)
|
||||
|
||||
depends:
|
||||
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install portaudio.tar.gz --prefix=$(PREFIX)
|
||||
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install portaudio --prefix=$(PREFIX) --with-pic
|
||||
%.o: %.c
|
||||
$(CC) -fPIC $(CFLAGS) -c -o $@ $<
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* $Id: pa_ringbuffer.c 1164 2006-12-21 15:34:50Z bjornroche $
|
||||
* Portable Audio I/O Library
|
||||
* Ring Buffer utility.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
* modified for SMP safety on Mac OS X by Bjorn Roche
|
||||
* modified for SMP safety on Linux by Leland Lucius
|
||||
* also, allowed for const where possible
|
||||
* Note that this is safe only for a single-thread reader and a
|
||||
* single-thread writer.
|
||||
*
|
||||
* This program uses the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.portaudio.com
|
||||
* Copyright (c) 1999-2000 Ross Bencina and 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/**
|
||||
@file
|
||||
@ingroup common_src
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pa_ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
/****************
|
||||
* First, we'll define some memory barrier primitives based on the system.
|
||||
* right now only OS X, FreeBSD, and Linux are supported. In addition to providing
|
||||
* memory barriers, these functions should ensure that data cached in registers
|
||||
* is written out to cache where it can be snooped by other CPUs. (ie, the volatile
|
||||
* keyword should not be required)
|
||||
*
|
||||
* the primitives that must be defined are:
|
||||
*
|
||||
* PaUtil_FullMemoryBarrier()
|
||||
* PaUtil_ReadMemoryBarrier()
|
||||
* PaUtil_WriteMemoryBarrier()
|
||||
*
|
||||
****************/
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
# include <libkern/OSAtomic.h>
|
||||
/* Here are the memory barrier functions. Mac OS X and FreeBSD only provide
|
||||
full memory barriers, so the three types of barriers are the same. */
|
||||
# define PaUtil_FullMemoryBarrier() OSMemoryBarrier()
|
||||
# define PaUtil_ReadMemoryBarrier() OSMemoryBarrier()
|
||||
# define PaUtil_WriteMemoryBarrier() OSMemoryBarrier()
|
||||
#elif defined(__GNUC__)
|
||||
/* GCC understands volatile asm and "memory" to mean it
|
||||
* should not reorder memory read/writes */
|
||||
# if defined( __PPC__ )
|
||||
# define PaUtil_FullMemoryBarrier() asm volatile("sync":::"memory")
|
||||
# define PaUtil_ReadMemoryBarrier() asm volatile("sync":::"memory")
|
||||
# define PaUtil_WriteMemoryBarrier() asm volatile("sync":::"memory")
|
||||
# elif defined( __i386__ ) || defined( __i486__ ) || defined( __i586__ ) || defined( __i686__ )
|
||||
# define PaUtil_FullMemoryBarrier() asm volatile("mfence":::"memory")
|
||||
# define PaUtil_ReadMemoryBarrier() asm volatile("lfence":::"memory")
|
||||
# define PaUtil_WriteMemoryBarrier() asm volatile("sfence":::"memory")
|
||||
# else
|
||||
# ifdef ALLOW_SMP_DANGERS
|
||||
# warning Memory barriers not defined on this system or system unknown
|
||||
# warning For SMP safety, you should fix this.
|
||||
# define PaUtil_FullMemoryBarrier()
|
||||
# define PaUtil_ReadMemoryBarrier()
|
||||
# define PaUtil_WriteMemoryBarrier()
|
||||
# else
|
||||
# error Memory barriers are not defined on this system. You can still compile by defining ALLOW_SMP_DANGERS, but SMP safety will not be guaranteed.
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# ifdef ALLOW_SMP_DANGERS
|
||||
# warning Memory barriers not defined on this system or system unknown
|
||||
# warning For SMP safety, you should fix this.
|
||||
# define PaUtil_FullMemoryBarrier()
|
||||
# define PaUtil_ReadMemoryBarrier()
|
||||
# define PaUtil_WriteMemoryBarrier()
|
||||
# else
|
||||
# error Memory barriers are not defined on this system. You can still compile by defining ALLOW_SMP_DANGERS, but SMP safety will not be guaranteed.
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/***************************************************************************
|
||||
* Initialize FIFO.
|
||||
* numBytes must be power of 2, returns -1 if not.
|
||||
*/
|
||||
long PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, long numBytes, void *dataPtr )
|
||||
{
|
||||
if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
|
||||
rbuf->bufferSize = numBytes;
|
||||
rbuf->buffer = (char *)dataPtr;
|
||||
PaUtil_FlushRingBuffer( rbuf );
|
||||
rbuf->bigMask = (numBytes*2)-1;
|
||||
rbuf->smallMask = (numBytes)-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Return number of bytes available for reading. */
|
||||
long PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
PaUtil_ReadMemoryBarrier();
|
||||
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
|
||||
}
|
||||
/***************************************************************************
|
||||
** Return number of bytes available for writing. */
|
||||
long PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
/* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */
|
||||
return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf));
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Clear buffer. Should only be called when buffer is NOT being read. */
|
||||
void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
rbuf->writeIndex = rbuf->readIndex = 0;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Get address of region(s) to which we can write data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 )
|
||||
{
|
||||
long index;
|
||||
long available = PaUtil_GetRingBufferWriteAvailable( rbuf );
|
||||
if( numBytes > available ) numBytes = available;
|
||||
/* Check to see if write is not contiguous. */
|
||||
index = rbuf->writeIndex & rbuf->smallMask;
|
||||
if( (index + numBytes) > rbuf->bufferSize )
|
||||
{
|
||||
/* Write data in two blocks that wrap the buffer. */
|
||||
long firstHalf = rbuf->bufferSize - index;
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = firstHalf;
|
||||
*dataPtr2 = &rbuf->buffer[0];
|
||||
*sizePtr2 = numBytes - firstHalf;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = numBytes;
|
||||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*/
|
||||
long PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, long numBytes )
|
||||
{
|
||||
/* we need to ensure that previous writes are seen before we update the write index */
|
||||
PaUtil_WriteMemoryBarrier();
|
||||
return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Get address of region(s) from which we can read data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 )
|
||||
{
|
||||
long index;
|
||||
long available = PaUtil_GetRingBufferReadAvailable( rbuf );
|
||||
if( numBytes > available ) numBytes = available;
|
||||
/* Check to see if read is not contiguous. */
|
||||
index = rbuf->readIndex & rbuf->smallMask;
|
||||
if( (index + numBytes) > rbuf->bufferSize )
|
||||
{
|
||||
/* Write data in two blocks that wrap the buffer. */
|
||||
long firstHalf = rbuf->bufferSize - index;
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = firstHalf;
|
||||
*dataPtr2 = &rbuf->buffer[0];
|
||||
*sizePtr2 = numBytes - firstHalf;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = numBytes;
|
||||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
/***************************************************************************
|
||||
*/
|
||||
long PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, long numBytes )
|
||||
{
|
||||
/* we need to ensure that previous writes are always seen before updating the index. */
|
||||
PaUtil_WriteMemoryBarrier();
|
||||
return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Return bytes written. */
|
||||
long PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, long numBytes )
|
||||
{
|
||||
long size1, size2, numWritten;
|
||||
void *data1, *data2;
|
||||
numWritten = PaUtil_GetRingBufferWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
|
||||
if( size2 > 0 )
|
||||
{
|
||||
|
||||
memcpy( data1, data, size1 );
|
||||
data = ((char *)data) + size1;
|
||||
memcpy( data2, data, size2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data1, data, size1 );
|
||||
}
|
||||
PaUtil_AdvanceRingBufferWriteIndex( rbuf, numWritten );
|
||||
return numWritten;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Return bytes read. */
|
||||
long PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, long numBytes )
|
||||
{
|
||||
long size1, size2, numRead;
|
||||
void *data1, *data2;
|
||||
numRead = PaUtil_GetRingBufferReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
|
||||
if( size2 > 0 )
|
||||
{
|
||||
memcpy( data, data1, size1 );
|
||||
data = ((char *)data) + size1;
|
||||
memcpy( data, data2, size2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data, data1, size1 );
|
||||
}
|
||||
PaUtil_AdvanceRingBufferReadIndex( rbuf, numRead );
|
||||
return numRead;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
#ifndef PA_RINGBUFFER_H
|
||||
#define PA_RINGBUFFER_H
|
||||
/*
|
||||
* $Id: pa_ringbuffer.h 1151 2006-11-29 02:11:16Z leland_lucius $
|
||||
* Portable Audio I/O Library
|
||||
* Ring Buffer utility.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
* modified for SMP safety on OS X by Bjorn Roche.
|
||||
* also allowed for const where possible.
|
||||
* Note that this is safe only for a single-thread reader
|
||||
* and a single-thread writer.
|
||||
*
|
||||
* This program is distributed with the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.portaudio.com
|
||||
* Copyright (c) 1999-2000 Ross Bencina and 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup common_src
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct PaUtilRingBuffer
|
||||
{
|
||||
long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by PaUtil_InitRingBuffer. */
|
||||
long writeIndex; /* Index of next writable byte. Set by PaUtil_AdvanceRingBufferWriteIndex. */
|
||||
long readIndex; /* Index of next readable byte. Set by PaUtil_AdvanceRingBufferReadIndex. */
|
||||
long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */
|
||||
long smallMask; /* Used for fitting indices to buffer. */
|
||||
char *buffer;
|
||||
}PaUtilRingBuffer;
|
||||
|
||||
/** Initialize Ring Buffer.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param numBytes The number of bytes in the buffer and must be power of 2.
|
||||
|
||||
@param dataPtr A pointer to a previously allocated area where the data
|
||||
will be maintained. It must be numBytes long.
|
||||
|
||||
@return -1 if numBytes is not a power of 2, otherwise 0.
|
||||
*/
|
||||
long PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, long numBytes, void *dataPtr );
|
||||
|
||||
/** Clear buffer. Should only be called when buffer is NOT being read.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
*/
|
||||
void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf );
|
||||
|
||||
/** Retrieve the number of bytes available in the ring buffer for writing.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@return The number of bytes available for writing.
|
||||
*/
|
||||
long PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf );
|
||||
|
||||
/** Retrieve the number of bytes available in the ring buffer for reading.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@return The number of bytes available for reading.
|
||||
*/
|
||||
long PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf );
|
||||
|
||||
/** Write data to the ring buffer.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param data The address of new data to write to the buffer.
|
||||
|
||||
@param numBytes The number of bytes to be written.
|
||||
|
||||
@return The number of bytes written.
|
||||
*/
|
||||
long PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, long numBytes );
|
||||
|
||||
/** Read data from the ring buffer.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param data The address where the data should be stored.
|
||||
|
||||
@param numBytes The number of bytes to be read.
|
||||
|
||||
@return The number of bytes read.
|
||||
*/
|
||||
long PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, long numBytes );
|
||||
|
||||
/** Get address of region(s) to which we can write data.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param numBytes The number of bytes desired.
|
||||
|
||||
@param dataPtr1 The address where the first (or only) region pointer will be
|
||||
stored.
|
||||
|
||||
@param sizePtr1 The address where the first (or only) region length will be
|
||||
stored.
|
||||
|
||||
@param dataPtr2 The address where the second region pointer will be stored if
|
||||
the first region is too small to satisfy numBytes.
|
||||
|
||||
@param sizePtr2 The address where the second region length will be stored if
|
||||
the first region is too small to satisfy numBytes.
|
||||
|
||||
@return The room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 );
|
||||
|
||||
/** Advance the write index to the next location to be written.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param numBytes The number of bytes to advance.
|
||||
|
||||
@return The new position.
|
||||
*/
|
||||
long PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, long numBytes );
|
||||
|
||||
/** Get address of region(s) from which we can write data.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param numBytes The number of bytes desired.
|
||||
|
||||
@param dataPtr1 The address where the first (or only) region pointer will be
|
||||
stored.
|
||||
|
||||
@param sizePtr1 The address where the first (or only) region length will be
|
||||
stored.
|
||||
|
||||
@param dataPtr2 The address where the second region pointer will be stored if
|
||||
the first region is too small to satisfy numBytes.
|
||||
|
||||
@param sizePtr2 The address where the second region length will be stored if
|
||||
the first region is too small to satisfy numBytes.
|
||||
|
||||
@return The number of bytes available for reading.
|
||||
*/
|
||||
long PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 );
|
||||
|
||||
/** Advance the read index to the next location to be read.
|
||||
|
||||
@param rbuf The ring buffer.
|
||||
|
||||
@param numBytes The number of bytes to advance.
|
||||
|
||||
@return The new position.
|
||||
*/
|
||||
long PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, long numBytes );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PA_RINGBUFFER_H */
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* $Id: pablio.c,v 1.1.1.1.4.4 2003/03/13 17:28:33 philburk Exp $
|
||||
* $Id: pablio.c 1151 2006-11-29 02:11:16Z leland_lucius $
|
||||
* pablio.c
|
||||
* Portable Audio Blocking Input/Output utility.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program uses the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* For more information see: http://www.portaudio.com
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
@@ -20,10 +20,6 @@
|
||||
* 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.
|
||||
@@ -31,307 +27,282 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
*/
|
||||
|
||||
/* History:
|
||||
* PLB021214 - check for valid stream in CloseAudioStream() to prevent hang.
|
||||
* add timeOutMSec to CloseAudioStream() to prevent hang.
|
||||
*/
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
#ifdef __ICC
|
||||
#pragma warning (disable:810 869 981 279 1469)
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
/************************************************************************/
|
||||
/******** Constants *****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/************************************************************************/
|
||||
/******** Constants *****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
|
||||
/************************************************************************/
|
||||
/******** Prototypes ****************************************************/
|
||||
/************************************************************************/
|
||||
static int blockingIOCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData);
|
||||
static PaError PABLIO_InitFIFO(RingBuffer * rbuf, long numFrames, long bytesPerFrame);
|
||||
static PaError PABLIO_TermFIFO(RingBuffer * rbuf);
|
||||
|
||||
/************************************************************************/
|
||||
/******** Prototypes ****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
static int blockingIOCallback( const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo* timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData );
|
||||
static PaError PABLIO_InitFIFO( PaUtilRingBuffer *rbuf, long numFrames, long bytesPerFrame );
|
||||
static PaError PABLIO_TermFIFO( PaUtilRingBuffer *rbuf );
|
||||
|
||||
/************************************************************************/
|
||||
/******** Functions *****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/******** Functions *****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
/* Called from PortAudio.
|
||||
* Read and write data only if there is room in FIFOs.
|
||||
*/
|
||||
static int blockingIOCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData)
|
||||
*/
|
||||
static int blockingIOCallback( const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo* timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData )
|
||||
{
|
||||
PABLIO_Stream * data = (PABLIO_Stream *) userData;
|
||||
long numBytes = data->bytesPerFrame * framesPerBuffer;
|
||||
(void) outTime;
|
||||
|
||||
/* This may get called with NULL inputBuffer during initial setup. */
|
||||
if (inputBuffer != NULL)
|
||||
{
|
||||
RingBuffer_Write(&data->inFIFO, inputBuffer, numBytes);
|
||||
}
|
||||
if (outputBuffer != NULL)
|
||||
{
|
||||
int i;
|
||||
int numRead = RingBuffer_Read(&data->outFIFO, outputBuffer, numBytes);
|
||||
|
||||
/* Zero out remainder of buffer if we run out of data. */
|
||||
for (i = numRead; i < numBytes; i++)
|
||||
{
|
||||
((char *) outputBuffer)[i] = 0;
|
||||
} }
|
||||
return 0;
|
||||
PABLIO_Stream *data = (PABLIO_Stream*)userData;
|
||||
long numBytes = data->bytesPerFrame * framesPerBuffer;
|
||||
|
||||
/* This may get called with NULL inputBuffer during initial setup. */
|
||||
if( inputBuffer != NULL )
|
||||
{
|
||||
PaUtil_WriteRingBuffer( &data->inFIFO, inputBuffer, numBytes );
|
||||
}
|
||||
if( outputBuffer != NULL )
|
||||
{
|
||||
int i;
|
||||
int numRead = PaUtil_ReadRingBuffer( &data->outFIFO, outputBuffer, numBytes );
|
||||
/* Zero out remainder of buffer if we run out of data. */
|
||||
for( i=numRead; i<numBytes; i++ )
|
||||
{
|
||||
((char *)outputBuffer)[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate buffer. */
|
||||
static PaError PABLIO_InitFIFO(RingBuffer * rbuf, long numFrames, long bytesPerFrame)
|
||||
/* Allocate buffer. */
|
||||
static PaError PABLIO_InitFIFO( PaUtilRingBuffer *rbuf, long numFrames, long bytesPerFrame )
|
||||
{
|
||||
long numBytes = numFrames * bytesPerFrame;
|
||||
char *buffer = (char *) malloc(numBytes);
|
||||
if (buffer == NULL)
|
||||
return paInsufficientMemory;
|
||||
memset(buffer, 0, numBytes);
|
||||
return (PaError) RingBuffer_Init(rbuf, numBytes, buffer);
|
||||
long numBytes = numFrames * bytesPerFrame;
|
||||
char *buffer = (char *) malloc( numBytes );
|
||||
if( buffer == NULL ) return paInsufficientMemory;
|
||||
memset( buffer, 0, numBytes );
|
||||
return (PaError) PaUtil_InitializeRingBuffer( rbuf, numBytes, buffer );
|
||||
}
|
||||
|
||||
|
||||
/* Free buffer. */
|
||||
static PaError PABLIO_TermFIFO(RingBuffer * rbuf)
|
||||
/* Free buffer. */
|
||||
static PaError PABLIO_TermFIFO( PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
if (rbuf->buffer)
|
||||
free(rbuf->buffer);
|
||||
rbuf->buffer = NULL;
|
||||
return paNoError;
|
||||
if( rbuf->buffer ) free( rbuf->buffer );
|
||||
rbuf->buffer = NULL;
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Write data to ring buffer.
|
||||
* Will not return until all the data has been written.
|
||||
*/
|
||||
long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames)
|
||||
*/
|
||||
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
|
||||
{
|
||||
long bytesWritten;
|
||||
char *p = (char *) data;
|
||||
long numBytes = aStream->bytesPerFrame * numFrames;
|
||||
while (numBytes > 0)
|
||||
{
|
||||
bytesWritten = RingBuffer_Write(&aStream->outFIFO, p, numBytes);
|
||||
numBytes -= bytesWritten;
|
||||
p += bytesWritten;
|
||||
if (numBytes > 0)
|
||||
Pa_Sleep(10);
|
||||
}
|
||||
return numFrames;
|
||||
long bytesWritten;
|
||||
char *p = (char *) data;
|
||||
long numBytes = aStream->bytesPerFrame * numFrames;
|
||||
while( numBytes > 0)
|
||||
{
|
||||
bytesWritten = PaUtil_WriteRingBuffer( &aStream->outFIFO, p, numBytes );
|
||||
numBytes -= bytesWritten;
|
||||
p += bytesWritten;
|
||||
if( numBytes > 0) Pa_Sleep(10);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Read data from ring buffer.
|
||||
* Will not return until all the data has been read.
|
||||
*/
|
||||
long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames)
|
||||
*/
|
||||
long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
|
||||
{
|
||||
long bytesRead;
|
||||
char *p = (char *) data;
|
||||
long numBytes = aStream->bytesPerFrame * numFrames;
|
||||
while (numBytes > 0)
|
||||
{
|
||||
bytesRead = RingBuffer_Read(&aStream->inFIFO, p, numBytes);
|
||||
numBytes -= bytesRead;
|
||||
p += bytesRead;
|
||||
if (numBytes > 0)
|
||||
Pa_Sleep(10);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
long bytesRead;
|
||||
char *p = (char *) data;
|
||||
long numBytes = aStream->bytesPerFrame * numFrames;
|
||||
|
||||
while( numBytes > 0)
|
||||
{
|
||||
bytesRead = PaUtil_ReadRingBuffer( &aStream->inFIFO, p, numBytes );
|
||||
numBytes -= bytesRead;
|
||||
p += bytesRead;
|
||||
if( numBytes > 0) Pa_Sleep(10);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that could be written to the stream without
|
||||
* having to wait.
|
||||
*/
|
||||
long GetAudioStreamWriteable(PABLIO_Stream * aStream)
|
||||
*/
|
||||
long GetAudioStreamWriteable( PABLIO_Stream *aStream )
|
||||
{
|
||||
int bytesEmpty = RingBuffer_GetWriteAvailable(&aStream->outFIFO);
|
||||
return bytesEmpty / aStream->bytesPerFrame;
|
||||
int bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO );
|
||||
return bytesEmpty / aStream->bytesPerFrame;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that are available to be read from the
|
||||
* stream without having to wait.
|
||||
*/
|
||||
long GetAudioStreamReadable(PABLIO_Stream * aStream)
|
||||
*/
|
||||
long GetAudioStreamReadable( PABLIO_Stream *aStream )
|
||||
{
|
||||
int bytesFull = RingBuffer_GetReadAvailable(&aStream->inFIFO);
|
||||
return bytesFull / aStream->bytesPerFrame;
|
||||
int bytesFull = PaUtil_GetRingBufferReadAvailable( &aStream->inFIFO );
|
||||
return bytesFull / aStream->bytesPerFrame;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************/
|
||||
static unsigned long RoundUpToNextPowerOf2(unsigned long n)
|
||||
/************************************************************/
|
||||
static unsigned long RoundUpToNextPowerOf2( unsigned long n )
|
||||
{
|
||||
long numBits = 0;
|
||||
if (((n - 1) & n) == 0)
|
||||
return n; /* Already Power of two. */
|
||||
while (n > 0)
|
||||
{
|
||||
n = n >> 1;
|
||||
numBits++;
|
||||
}
|
||||
return (1 << numBits);
|
||||
long numBits = 0;
|
||||
if( ((n-1) & n) == 0) return n; /* Already Power of two. */
|
||||
while( n > 0 )
|
||||
{
|
||||
n= n>>1;
|
||||
numBits++;
|
||||
}
|
||||
return (1<<numBits);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Opens a PortAudio stream with default characteristics.
|
||||
* Allocates PABLIO_Stream structure.
|
||||
*
|
||||
* flags parameter can be an ORed combination of:
|
||||
* PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
|
||||
* and either PABLIO_MONO or PABLIO_STEREO
|
||||
*/
|
||||
PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, double sampleRate, PaSampleFormat format, long flags, int indev,
|
||||
int outdev)
|
||||
*/
|
||||
PaError OpenAudioStream( PABLIO_Stream **rwblPtr,
|
||||
const PaStreamParameters *inputParameters,
|
||||
const PaStreamParameters *outputParameters,
|
||||
double sampleRate,
|
||||
PaStreamFlags streamFlags)
|
||||
{
|
||||
long bytesPerSample;
|
||||
long doRead = 0;
|
||||
long doWrite = 0;
|
||||
PaError err;
|
||||
PABLIO_Stream * aStream;
|
||||
long minNumBuffers;
|
||||
long numFrames;
|
||||
long bytesPerSample;
|
||||
long doRead = 0;
|
||||
long doWrite = 0;
|
||||
PaError err;
|
||||
PABLIO_Stream *aStream;
|
||||
long minNumBuffers;
|
||||
long numFrames;
|
||||
long numBytes;
|
||||
int channels = 1;
|
||||
|
||||
/* Allocate PABLIO_Stream structure for caller. */
|
||||
aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
|
||||
if( aStream == NULL ) return paInsufficientMemory;
|
||||
memset( aStream, 0, sizeof(PABLIO_Stream) );
|
||||
|
||||
/* Initialize PortAudio */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
if (inputParameters) {
|
||||
channels = inputParameters->channelCount;
|
||||
} else if (outputParameters) {
|
||||
channels = outputParameters->channelCount;
|
||||
}
|
||||
|
||||
numFrames = 4 * FRAMES_PER_BUFFER;
|
||||
numFrames = RoundUpToNextPowerOf2( numFrames );
|
||||
|
||||
bytesPerSample = 2;
|
||||
aStream->samplesPerFrame = channels;
|
||||
aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
|
||||
|
||||
/* Initialize Ring Buffers */
|
||||
|
||||
if (inputParameters) {
|
||||
err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
|
||||
if( err != paNoError ) goto error;
|
||||
}
|
||||
|
||||
if (outputParameters) {
|
||||
err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
|
||||
if( err != paNoError ) goto error;
|
||||
}
|
||||
|
||||
/* Make Write FIFO appear full initially. */
|
||||
numBytes = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO );
|
||||
PaUtil_AdvanceRingBufferWriteIndex( &aStream->outFIFO, numBytes );
|
||||
|
||||
/* Allocate PABLIO_Stream structure for caller. */
|
||||
aStream = (PABLIO_Stream *) malloc(sizeof(PABLIO_Stream));
|
||||
if (aStream == NULL)
|
||||
return paInsufficientMemory;
|
||||
memset(aStream, 0, sizeof(PABLIO_Stream));
|
||||
|
||||
/* Determine size of a sample. */
|
||||
bytesPerSample = Pa_GetSampleSize(format);
|
||||
if (bytesPerSample < 0)
|
||||
{
|
||||
err = (PaError) bytesPerSample;
|
||||
goto error;
|
||||
}
|
||||
aStream->samplesPerFrame = ((flags & PABLIO_MONO) != 0) ? 1 : 2;
|
||||
aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
|
||||
|
||||
/* Initialize PortAudio */
|
||||
err = Pa_Initialize();
|
||||
if (err != paNoError)
|
||||
goto error;
|
||||
|
||||
/* Warning: numFrames must be larger than amount of data processed per interrupt
|
||||
* inside PA to prevent glitches. Just to be safe, adjust size upwards.
|
||||
*/
|
||||
minNumBuffers = 2 * Pa_GetMinNumBuffers(FRAMES_PER_BUFFER, sampleRate);
|
||||
numFrames = minNumBuffers * FRAMES_PER_BUFFER;
|
||||
|
||||
/* The PortAudio callback runs in a high priority thread. But PABLIO
|
||||
* runs in a normal foreground thread. So we may have much worse
|
||||
* latency in PABLIO. So adjust latency to a safe level.
|
||||
*/
|
||||
{
|
||||
const int safeLatencyMSec = 200;
|
||||
int minLatencyMSec = (int) ((1000 * numFrames) / sampleRate);
|
||||
if (minLatencyMSec < safeLatencyMSec)
|
||||
{
|
||||
numFrames = (int) ((safeLatencyMSec * sampleRate) / 1000);
|
||||
}
|
||||
} numFrames = RoundUpToNextPowerOf2(numFrames);
|
||||
|
||||
/* Initialize Ring Buffers */
|
||||
doRead = ((flags & PABLIO_READ) != 0);
|
||||
doWrite = ((flags & PABLIO_WRITE) != 0);
|
||||
if (doRead)
|
||||
{
|
||||
err = PABLIO_InitFIFO(&aStream->inFIFO, numFrames, aStream->bytesPerFrame);
|
||||
if (err != paNoError)
|
||||
goto error;
|
||||
}
|
||||
if (doWrite)
|
||||
{
|
||||
long numBytes;
|
||||
err = PABLIO_InitFIFO(&aStream->outFIFO, numFrames, aStream->bytesPerFrame);
|
||||
if (err != paNoError)
|
||||
goto error;
|
||||
|
||||
/* Make Write FIFO appear full initially. */
|
||||
numBytes = RingBuffer_GetWriteAvailable(&aStream->outFIFO);
|
||||
RingBuffer_AdvanceWriteIndex(&aStream->outFIFO, numBytes);
|
||||
}
|
||||
|
||||
/* Open a PortAudio stream that we will use to communicate with the underlying
|
||||
* audio drivers. */
|
||||
err = Pa_OpenStream(&aStream->stream,
|
||||
(doRead ? (indev > -1) ? indev : Pa_GetDefaultInputDeviceID() : paNoDevice),
|
||||
(doRead ? aStream->samplesPerFrame : 0), format, NULL,
|
||||
(doWrite ? (outdev > -1) ? outdev : Pa_GetDefaultOutputDeviceID() : paNoDevice),
|
||||
(doWrite ? aStream->samplesPerFrame : 0), format, NULL, sampleRate, FRAMES_PER_BUFFER,
|
||||
minNumBuffers, paClipOff,
|
||||
/* we won't output out of range samples so don't bother clipping them */
|
||||
blockingIOCallback, aStream);
|
||||
if (err != paNoError)
|
||||
goto error;
|
||||
err = Pa_StartStream(aStream->stream);
|
||||
if (err != paNoError)
|
||||
goto error;
|
||||
*rwblPtr = aStream;
|
||||
return paNoError;
|
||||
error:CloseAudioStream(aStream);
|
||||
*rwblPtr = NULL;
|
||||
return err;
|
||||
|
||||
/* Open a PortAudio stream that we will use to communicate with the underlying
|
||||
* audio drivers. */
|
||||
err = Pa_OpenStream(
|
||||
&aStream->stream,
|
||||
inputParameters,
|
||||
outputParameters,
|
||||
sampleRate,
|
||||
FRAMES_PER_BUFFER,
|
||||
streamFlags,
|
||||
blockingIOCallback,
|
||||
aStream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( aStream->stream );
|
||||
if( err != paNoError ) goto error;
|
||||
*rwblPtr = aStream;
|
||||
return paNoError;
|
||||
|
||||
error:
|
||||
CloseAudioStream( aStream );
|
||||
*rwblPtr = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************/
|
||||
PaError CloseAudioStream(PABLIO_Stream * aStream)
|
||||
/************************************************************/
|
||||
PaError CloseAudioStream( PABLIO_Stream *aStream )
|
||||
{
|
||||
PaError err = paNoError;
|
||||
int bytesEmpty;
|
||||
int byteSize = aStream->outFIFO.bufferSize;
|
||||
if (aStream->stream != NULL) /* Make sure stream was opened. PLB021214 */
|
||||
{
|
||||
|
||||
/* If we are writing data, make sure we play everything written. */
|
||||
if (byteSize > 0)
|
||||
{
|
||||
int timeOutMSec = 2000;
|
||||
bytesEmpty = RingBuffer_GetWriteAvailable(&aStream->outFIFO);
|
||||
while ((bytesEmpty < byteSize) && (timeOutMSec > 0))
|
||||
{
|
||||
Pa_Sleep(20);
|
||||
timeOutMSec -= 20;
|
||||
bytesEmpty = RingBuffer_GetWriteAvailable(&aStream->outFIFO);
|
||||
}
|
||||
}
|
||||
err = Pa_StopStream(aStream->stream);
|
||||
if (err != paNoError)
|
||||
goto error;
|
||||
err = Pa_CloseStream(aStream->stream);
|
||||
}
|
||||
error:Pa_Terminate();
|
||||
PABLIO_TermFIFO(&aStream->inFIFO);
|
||||
PABLIO_TermFIFO(&aStream->outFIFO);
|
||||
free(aStream);
|
||||
return err;
|
||||
PaError err;
|
||||
int bytesEmpty;
|
||||
int byteSize = aStream->outFIFO.bufferSize;
|
||||
|
||||
/* If we are writing data, make sure we play everything written. */
|
||||
if( byteSize > 0 )
|
||||
{
|
||||
bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO );
|
||||
while( bytesEmpty < byteSize )
|
||||
{
|
||||
Pa_Sleep( 10 );
|
||||
bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO );
|
||||
}
|
||||
}
|
||||
|
||||
err = Pa_StopStream( aStream->stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( aStream->stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
|
||||
error:
|
||||
PABLIO_TermFIFO( &aStream->inFIFO );
|
||||
PABLIO_TermFIFO( &aStream->outFIFO );
|
||||
free( aStream );
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ extern "C"
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id: pablio.h,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $
|
||||
* $Id: pablio.h 1083 2006-08-23 07:30:49Z rossb $
|
||||
* PABLIO.h
|
||||
* Portable Audio Blocking read/write utility.
|
||||
*
|
||||
@@ -15,7 +15,7 @@ extern "C"
|
||||
*
|
||||
* Include file for PABLIO, the Portable Audio Blocking I/O Library.
|
||||
* PABLIO is built on top of PortAudio, the Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* For more information see: http://www.portaudio.com
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
@@ -29,10 +29,6 @@ extern "C"
|
||||
* 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.
|
||||
@@ -40,20 +36,32 @@ extern "C"
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pa_ringbuffer.h"
|
||||
#include "portaudio.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
RingBuffer inFIFO;
|
||||
RingBuffer outFIFO;
|
||||
PortAudioStream *stream;
|
||||
PaUtilRingBuffer inFIFO;
|
||||
PaUtilRingBuffer outFIFO;
|
||||
PaStream *stream;
|
||||
int bytesPerFrame;
|
||||
int samplesPerFrame;
|
||||
}
|
||||
@@ -98,8 +106,11 @@ long GetAudioStreamReadable( PABLIO_Stream *aStream );
|
||||
* PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
|
||||
* and either PABLIO_MONO or PABLIO_STEREO
|
||||
*/
|
||||
PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate,
|
||||
PaSampleFormat format, long flags, int indev, int outdev );
|
||||
PaError OpenAudioStream( PABLIO_Stream **rwblPtr,
|
||||
const PaStreamParameters *inputParameters,
|
||||
const PaStreamParameters *outputParameters,
|
||||
double sampleRate,
|
||||
PaStreamCallbackFlags statusFlags);
|
||||
|
||||
PaError CloseAudioStream( PABLIO_Stream *aStream );
|
||||
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* $Id: ringbuffer.c,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $
|
||||
* ringbuffer.c
|
||||
* Ring Buffer utility..
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program uses the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* Copyright (c) 1999-2000 Ross Bencina and 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.
|
||||
*
|
||||
*/
|
||||
#ifdef __ICC
|
||||
#pragma warning (disable:810 869 981 279 1469)
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
/***************************************************************************
|
||||
* Initialize FIFO.
|
||||
* numBytes must be power of 2, returns -1 if not.
|
||||
*/
|
||||
long RingBuffer_Init(RingBuffer * rbuf, long numBytes, void *dataPtr)
|
||||
{
|
||||
if (((numBytes - 1) & numBytes) != 0)
|
||||
return -1; /* Not Power of two. */
|
||||
rbuf->bufferSize = numBytes;
|
||||
rbuf->buffer = (char *) dataPtr;
|
||||
RingBuffer_Flush(rbuf);
|
||||
rbuf->bigMask = (numBytes * 2) - 1;
|
||||
rbuf->smallMask = (numBytes) - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Return number of bytes available for reading. */
|
||||
long RingBuffer_GetReadAvailable(RingBuffer * rbuf)
|
||||
{
|
||||
return ((rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Return number of bytes available for writing. */
|
||||
long RingBuffer_GetWriteAvailable(RingBuffer * rbuf)
|
||||
{
|
||||
return (rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Clear buffer. Should only be called when buffer is NOT being read. */
|
||||
void RingBuffer_Flush(RingBuffer * rbuf)
|
||||
{
|
||||
rbuf->writeIndex = rbuf->readIndex = 0;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Get address of region(s) to which we can write data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetWriteRegions(RingBuffer * rbuf, long numBytes, void **dataPtr1, long *sizePtr1, void **dataPtr2,
|
||||
long *sizePtr2)
|
||||
{
|
||||
long index;
|
||||
long available = RingBuffer_GetWriteAvailable(rbuf);
|
||||
if (numBytes > available)
|
||||
numBytes = available;
|
||||
|
||||
/* Check to see if write is not contiguous. */
|
||||
index = rbuf->writeIndex & rbuf->smallMask;
|
||||
if ((index + numBytes) > rbuf->bufferSize)
|
||||
{
|
||||
|
||||
/* Write data in two blocks that wrap the buffer. */
|
||||
long firstHalf = rbuf->bufferSize - index;
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = firstHalf;
|
||||
*dataPtr2 = &rbuf->buffer[0];
|
||||
*sizePtr2 = numBytes - firstHalf;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = numBytes;
|
||||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*/
|
||||
long RingBuffer_AdvanceWriteIndex(RingBuffer * rbuf, long numBytes)
|
||||
{
|
||||
return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Get address of region(s) from which we can read data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetReadRegions(RingBuffer * rbuf, long numBytes, void **dataPtr1, long *sizePtr1, void **dataPtr2,
|
||||
long *sizePtr2)
|
||||
{
|
||||
long index;
|
||||
long available = RingBuffer_GetReadAvailable(rbuf);
|
||||
if (numBytes > available)
|
||||
numBytes = available;
|
||||
|
||||
/* Check to see if read is not contiguous. */
|
||||
index = rbuf->readIndex & rbuf->smallMask;
|
||||
if ((index + numBytes) > rbuf->bufferSize)
|
||||
{
|
||||
|
||||
/* Write data in two blocks that wrap the buffer. */
|
||||
long firstHalf = rbuf->bufferSize - index;
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = firstHalf;
|
||||
*dataPtr2 = &rbuf->buffer[0];
|
||||
*sizePtr2 = numBytes - firstHalf;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = numBytes;
|
||||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*/
|
||||
long RingBuffer_AdvanceReadIndex(RingBuffer * rbuf, long numBytes)
|
||||
{
|
||||
return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Return bytes written. */
|
||||
long RingBuffer_Write(RingBuffer * rbuf, void *data, long numBytes)
|
||||
{
|
||||
long size1, size2, numWritten;
|
||||
void *data1, *data2;
|
||||
numWritten = RingBuffer_GetWriteRegions(rbuf, numBytes, &data1, &size1, &data2, &size2);
|
||||
if (size2 > 0)
|
||||
{
|
||||
memcpy(data1, data, size1);
|
||||
data = ((char *) data) + size1;
|
||||
memcpy(data2, data, size2);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
memcpy(data1, data, size1);
|
||||
}
|
||||
RingBuffer_AdvanceWriteIndex(rbuf, numWritten);
|
||||
return numWritten;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Return bytes read. */
|
||||
long RingBuffer_Read(RingBuffer * rbuf, void *data, long numBytes)
|
||||
{
|
||||
long size1, size2, numRead;
|
||||
void *data1, *data2;
|
||||
numRead = RingBuffer_GetReadRegions(rbuf, numBytes, &data1, &size1, &data2, &size2);
|
||||
if (size2 > 0)
|
||||
{
|
||||
memcpy(data, data1, size1);
|
||||
data = ((char *) data) + size1;
|
||||
memcpy(data, data2, size2);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
memcpy(data, data1, size1);
|
||||
}
|
||||
RingBuffer_AdvanceReadIndex(rbuf, numRead);
|
||||
return numRead;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
#ifndef _RINGBUFFER_H
|
||||
#define _RINGBUFFER_H
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id: ringbuffer.h,v 1.1.1.1.4.2 2003/04/28 17:45:34 philburk Exp $
|
||||
* ringbuffer.h
|
||||
* Ring Buffer utility..
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program is distributed with the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* Copyright (c) 1999-2000 Ross Bencina and 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.
|
||||
*
|
||||
*/
|
||||
#define PortAudioStream PaStream
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
|
||||
/* These are declared volatile because they are written by a different thread than the reader. */
|
||||
volatile long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
|
||||
volatile long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
|
||||
long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */
|
||||
long smallMask; /* Used for fitting indices to buffer. */
|
||||
char *buffer;
|
||||
}
|
||||
RingBuffer;
|
||||
/*
|
||||
* Initialize Ring Buffer.
|
||||
* numBytes must be power of 2, returns -1 if not.
|
||||
*/
|
||||
long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );
|
||||
|
||||
/* Clear buffer. Should only be called when buffer is NOT being read. */
|
||||
void RingBuffer_Flush( RingBuffer *rbuf );
|
||||
|
||||
/* Return number of bytes available for writing. */
|
||||
long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
|
||||
/* Return number of bytes available for read. */
|
||||
long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
|
||||
/* Return bytes written. */
|
||||
long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
|
||||
/* Return bytes read. */
|
||||
long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );
|
||||
|
||||
/* Get address of region(s) to which we can write data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 );
|
||||
long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );
|
||||
|
||||
/* Get address of region(s) from which we can read data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be read or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 );
|
||||
|
||||
long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _RINGBUFFER_H */
|
||||
Reference in new issue
Block a user