Initial revision
This commit is contained in:
commit
c98797d938
76 changed files with 22645 additions and 0 deletions
46
Makefile.linux
Normal file
46
Makefile.linux
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# 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
|
||||
|
||||
|
||||
19
fixdir.bat
Executable file
19
fixdir.bat
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
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 ..\
|
||||
BIN
pa_asio/Callback_adaptation_.pdf
Normal file
BIN
pa_asio/Callback_adaptation_.pdf
Normal file
Binary file not shown.
BIN
pa_asio/Pa_ASIO.pdf
Normal file
BIN
pa_asio/Pa_ASIO.pdf
Normal file
Binary file not shown.
3005
pa_asio/pa_asio.cpp
Normal file
3005
pa_asio/pa_asio.cpp
Normal file
File diff suppressed because it is too large
Load diff
538
pa_beos/PlaybackNode.cc
Normal file
538
pa_beos/PlaybackNode.cc
Normal file
|
|
@ -0,0 +1,538 @@
|
|||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* BeOS Media Kit Implementation by Joshua Haberman
|
||||
*
|
||||
* Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* Significant portions of this file are based on sample code from Be. The
|
||||
* Be Sample Code Licence follows:
|
||||
*
|
||||
* Copyright 1991-1999, Be Incorporated.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <be/media/BufferGroup.h>
|
||||
#include <be/media/Buffer.h>
|
||||
#include <be/media/TimeSource.h>
|
||||
|
||||
#include "PlaybackNode.h"
|
||||
|
||||
#define PRINT(x) { printf x; fflush(stdout); }
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBUG(x) PRINT(x)
|
||||
#else
|
||||
#define DBUG(x)
|
||||
#endif
|
||||
|
||||
|
||||
PaPlaybackNode::PaPlaybackNode(uint32 channels, float frame_rate, uint32 frames_per_buffer,
|
||||
PortAudioCallback* callback, void *user_data) :
|
||||
BMediaNode("PortAudio input node"),
|
||||
BBufferProducer(B_MEDIA_RAW_AUDIO),
|
||||
BMediaEventLooper(),
|
||||
mAborted(false),
|
||||
mRunning(false),
|
||||
mBufferGroup(NULL),
|
||||
mDownstreamLatency(0),
|
||||
mStartTime(0),
|
||||
mCallback(callback),
|
||||
mUserData(user_data),
|
||||
mFramesPerBuffer(frames_per_buffer)
|
||||
{
|
||||
DBUG(("Constructor called.\n"));
|
||||
|
||||
mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
|
||||
mPreferredFormat.u.raw_audio.channel_count = channels;
|
||||
mPreferredFormat.u.raw_audio.frame_rate = frame_rate;
|
||||
mPreferredFormat.u.raw_audio.byte_order =
|
||||
(B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
|
||||
mPreferredFormat.u.raw_audio.buffer_size =
|
||||
media_raw_audio_format::wildcard.buffer_size;
|
||||
|
||||
mOutput.destination = media_destination::null;
|
||||
mOutput.format = mPreferredFormat;
|
||||
|
||||
/* The amount of time it takes for this node to produce a buffer when
|
||||
* asked. Essentially, it is how long the user's callback takes to run.
|
||||
* We set this to be the length of the sound data each buffer of the
|
||||
* requested size can hold. */
|
||||
//mInternalLatency = (bigtime_t)(1000000 * frames_per_buffer / frame_rate);
|
||||
|
||||
/* ACK! it seems that the mixer (at least on my machine) demands that IT
|
||||
* specify the buffer size, so for now I'll just make a generic guess here */
|
||||
mInternalLatency = 1000000 / 20;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PaPlaybackNode::~PaPlaybackNode()
|
||||
{
|
||||
DBUG(("Destructor called.\n"));
|
||||
Quit(); /* Stop the BMediaEventLooper thread */
|
||||
}
|
||||
|
||||
|
||||
/*************************
|
||||
*
|
||||
* Local methods
|
||||
*
|
||||
*/
|
||||
|
||||
bool PaPlaybackNode::IsRunning()
|
||||
{
|
||||
return mRunning;
|
||||
}
|
||||
|
||||
|
||||
PaTimestamp PaPlaybackNode::GetStreamTime()
|
||||
{
|
||||
BTimeSource *timeSource = TimeSource();
|
||||
PaTimestamp time = (timeSource->Now() - mStartTime) *
|
||||
mPreferredFormat.u.raw_audio.frame_rate / 1000000;
|
||||
return time;
|
||||
}
|
||||
|
||||
|
||||
void PaPlaybackNode::SetSampleFormat(PaSampleFormat inFormat,
|
||||
PaSampleFormat outFormat)
|
||||
{
|
||||
uint32 beOutFormat;
|
||||
|
||||
switch(outFormat)
|
||||
{
|
||||
case paFloat32:
|
||||
beOutFormat = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
mOutputSampleWidth = 4;
|
||||
break;
|
||||
|
||||
case paInt16:
|
||||
beOutFormat = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
mOutputSampleWidth = 2;
|
||||
break;
|
||||
|
||||
case paInt32:
|
||||
beOutFormat = media_raw_audio_format::B_AUDIO_INT;
|
||||
mOutputSampleWidth = 4;
|
||||
break;
|
||||
|
||||
case paInt8:
|
||||
beOutFormat = media_raw_audio_format::B_AUDIO_CHAR;
|
||||
mOutputSampleWidth = 1;
|
||||
break;
|
||||
|
||||
case paUInt8:
|
||||
beOutFormat = media_raw_audio_format::B_AUDIO_UCHAR;
|
||||
mOutputSampleWidth = 1;
|
||||
break;
|
||||
|
||||
case paInt24:
|
||||
case paPackedInt24:
|
||||
case paCustomFormat:
|
||||
DBUG(("Unsupported output format: %x\n", outFormat));
|
||||
break;
|
||||
|
||||
default:
|
||||
DBUG(("Unknown output format: %x\n", outFormat));
|
||||
}
|
||||
|
||||
mPreferredFormat.u.raw_audio.format = beOutFormat;
|
||||
mFramesPerBuffer * mPreferredFormat.u.raw_audio.channel_count * mOutputSampleWidth;
|
||||
}
|
||||
|
||||
BBuffer *PaPlaybackNode::FillNextBuffer(bigtime_t time)
|
||||
{
|
||||
/* Get a buffer from the buffer group */
|
||||
BBuffer *buf = mBufferGroup->RequestBuffer(
|
||||
mOutput.format.u.raw_audio.buffer_size, BufferDuration());
|
||||
unsigned long frames = mOutput.format.u.raw_audio.buffer_size /
|
||||
mOutputSampleWidth / mOutput.format.u.raw_audio.channel_count;
|
||||
bigtime_t start_time;
|
||||
int ret;
|
||||
|
||||
if( !buf )
|
||||
{
|
||||
DBUG(("Unable to allocate a buffer\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start_time = mStartTime +
|
||||
(bigtime_t)((double)mSamplesSent /
|
||||
(double)mOutput.format.u.raw_audio.frame_rate /
|
||||
(double)mOutput.format.u.raw_audio.channel_count *
|
||||
1000000.0);
|
||||
|
||||
/* Now call the user callback to get the data */
|
||||
ret = mCallback(NULL, /* Input buffer */
|
||||
buf->Data(), /* Output buffer */
|
||||
frames, /* Frames per buffer */
|
||||
mSamplesSent / mOutput.format.u.raw_audio.channel_count, /* timestamp */
|
||||
mUserData);
|
||||
|
||||
if( ret )
|
||||
mAborted = true;
|
||||
|
||||
media_header *hdr = buf->Header();
|
||||
|
||||
hdr->type = B_MEDIA_RAW_AUDIO;
|
||||
hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
|
||||
hdr->time_source = TimeSource()->ID();
|
||||
hdr->start_time = start_time;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************
|
||||
*
|
||||
* BMediaNode methods
|
||||
*
|
||||
*/
|
||||
|
||||
BMediaAddOn *PaPlaybackNode::AddOn( int32 * ) const
|
||||
{
|
||||
DBUG(("AddOn() called.\n"));
|
||||
return NULL; /* we don't provide service to outside applications */
|
||||
}
|
||||
|
||||
|
||||
status_t PaPlaybackNode::HandleMessage( int32 message, const void *data,
|
||||
size_t size )
|
||||
{
|
||||
DBUG(("HandleMessage() called.\n"));
|
||||
return B_ERROR; /* we don't define any custom messages */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************
|
||||
*
|
||||
* BMediaEventLooper methods
|
||||
*
|
||||
*/
|
||||
|
||||
void PaPlaybackNode::NodeRegistered()
|
||||
{
|
||||
DBUG(("NodeRegistered() called.\n"));
|
||||
|
||||
/* Start the BMediaEventLooper thread */
|
||||
SetPriority(B_REAL_TIME_PRIORITY);
|
||||
Run();
|
||||
|
||||
/* set up as much information about our output as we can */
|
||||
mOutput.source.port = ControlPort();
|
||||
mOutput.source.id = 0;
|
||||
mOutput.node = Node();
|
||||
::strcpy(mOutput.name, "PortAudio Playback");
|
||||
}
|
||||
|
||||
|
||||
void PaPlaybackNode::HandleEvent( const media_timed_event *event,
|
||||
bigtime_t lateness, bool realTimeEvent )
|
||||
{
|
||||
// DBUG(("HandleEvent() called.\n"));
|
||||
status_t err;
|
||||
|
||||
switch(event->type)
|
||||
{
|
||||
case BTimedEventQueue::B_START:
|
||||
DBUG((" Handling a B_START event\n"));
|
||||
if( RunState() != B_STARTED )
|
||||
{
|
||||
mStartTime = event->event_time + EventLatency();
|
||||
mSamplesSent = 0;
|
||||
mAborted = false;
|
||||
mRunning = true;
|
||||
media_timed_event firstEvent( mStartTime,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER );
|
||||
EventQueue()->AddEvent( firstEvent );
|
||||
}
|
||||
break;
|
||||
|
||||
case BTimedEventQueue::B_STOP:
|
||||
DBUG((" Handling a B_STOP event\n"));
|
||||
mRunning = false;
|
||||
EventQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER );
|
||||
break;
|
||||
|
||||
case BTimedEventQueue::B_HANDLE_BUFFER:
|
||||
//DBUG((" Handling a B_HANDLE_BUFFER event\n"));
|
||||
|
||||
/* make sure we're started and connected */
|
||||
if( RunState() != BMediaEventLooper::B_STARTED ||
|
||||
mOutput.destination == media_destination::null )
|
||||
break;
|
||||
|
||||
BBuffer *buffer = FillNextBuffer(event->event_time);
|
||||
|
||||
/* make sure we weren't aborted while this routine was running.
|
||||
* this can happen in one of two ways: either the callback returned
|
||||
* nonzero (in which case mAborted is set in FillNextBuffer() ) or
|
||||
* the client called AbortStream */
|
||||
if( mAborted )
|
||||
{
|
||||
if( buffer )
|
||||
buffer->Recycle();
|
||||
Stop(0, true);
|
||||
break;
|
||||
}
|
||||
|
||||
if( buffer )
|
||||
{
|
||||
err = SendBuffer(buffer, mOutput.destination);
|
||||
if( err != B_OK )
|
||||
buffer->Recycle();
|
||||
}
|
||||
|
||||
mSamplesSent += mOutput.format.u.raw_audio.buffer_size / mOutputSampleWidth;
|
||||
|
||||
/* Now schedule the next buffer event, so we can send another
|
||||
* buffer when this one runs out. We calculate when it should
|
||||
* happen by calculating when the data we just sent will finish
|
||||
* playing.
|
||||
*
|
||||
* NOTE, however, that the event will actually get generated
|
||||
* earlier than we specify, to account for the latency it will
|
||||
* take to produce the buffer. It uses the latency value we
|
||||
* specified in SetEventLatency() to determine just how early
|
||||
* to generate it. */
|
||||
|
||||
/* totalPerformanceTime includes the time represented by the buffer
|
||||
* we just sent */
|
||||
bigtime_t totalPerformanceTime = (bigtime_t)((double)mSamplesSent /
|
||||
(double)mOutput.format.u.raw_audio.channel_count /
|
||||
(double)mOutput.format.u.raw_audio.frame_rate * 1000000.0);
|
||||
|
||||
bigtime_t nextEventTime = mStartTime + totalPerformanceTime;
|
||||
|
||||
media_timed_event nextBufferEvent(nextEventTime,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
EventQueue()->AddEvent(nextBufferEvent);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************
|
||||
*
|
||||
* BBufferProducer methods
|
||||
*
|
||||
*/
|
||||
|
||||
status_t PaPlaybackNode::FormatSuggestionRequested( media_type type,
|
||||
int32 /*quality*/, media_format* format )
|
||||
{
|
||||
/* the caller wants to know this node's preferred format and provides
|
||||
* a suggestion, asking if we support it */
|
||||
DBUG(("FormatSuggestionRequested() called.\n"));
|
||||
|
||||
if(!format)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*format = mPreferredFormat;
|
||||
|
||||
/* we only support raw audio (a wildcard is okay too) */
|
||||
if ( type == B_MEDIA_UNKNOWN_TYPE || type == B_MEDIA_RAW_AUDIO )
|
||||
return B_OK;
|
||||
else
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
|
||||
status_t PaPlaybackNode::FormatProposal( const media_source& output,
|
||||
media_format* format )
|
||||
{
|
||||
/* This is similar to FormatSuggestionRequested(), but it is actually part
|
||||
* of the negotiation process. We're given the opportunity to specify any
|
||||
* properties that are wildcards (ie. properties that the other node doesn't
|
||||
* care one way or another about) */
|
||||
DBUG(("FormatProposal() called.\n"));
|
||||
|
||||
/* Make sure this proposal really applies to our output */
|
||||
if( output != mOutput.source )
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
/* We return two things: whether we support the proposed format, and our own
|
||||
* preferred format */
|
||||
*format = mPreferredFormat;
|
||||
|
||||
if( format->type == B_MEDIA_UNKNOWN_TYPE || format->type == B_MEDIA_RAW_AUDIO )
|
||||
return B_OK;
|
||||
else
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
|
||||
status_t PaPlaybackNode::FormatChangeRequested( const media_source& source,
|
||||
const media_destination& destination, media_format* io_format, int32* )
|
||||
{
|
||||
/* we refuse to change formats, supporting only 1 */
|
||||
DBUG(("FormatChangeRequested() called.\n"));
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t PaPlaybackNode::GetNextOutput( int32* cookie, media_output* out_output )
|
||||
{
|
||||
/* this is where we allow other to enumerate our outputs -- the cookie is
|
||||
* an integer we can use to keep track of where we are in enumeration. */
|
||||
DBUG(("GetNextOutput() called.\n"));
|
||||
|
||||
if( *cookie == 0 )
|
||||
{
|
||||
*out_output = mOutput;
|
||||
*cookie = 1;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
|
||||
status_t PaPlaybackNode::DisposeOutputCookie( int32 cookie )
|
||||
{
|
||||
DBUG(("DisposeOutputCookie() called.\n"));
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void PaPlaybackNode::LateNoticeReceived( const media_source& what,
|
||||
bigtime_t how_much, bigtime_t performance_time )
|
||||
{
|
||||
/* This function is called as notification that a buffer we sent wasn't
|
||||
* received by the time we stamped it with -- it got there late. Basically,
|
||||
* it means we underestimated our own latency, so we should increase it */
|
||||
DBUG(("LateNoticeReceived() called.\n"));
|
||||
|
||||
if( what != mOutput.source )
|
||||
return;
|
||||
|
||||
if( RunMode() == B_INCREASE_LATENCY )
|
||||
{
|
||||
mInternalLatency += how_much;
|
||||
SetEventLatency( mDownstreamLatency + mInternalLatency );
|
||||
DBUG(("Increasing latency to %Ld\n", mDownstreamLatency + mInternalLatency));
|
||||
}
|
||||
else
|
||||
DBUG(("I don't know what to do with this notice!"));
|
||||
}
|
||||
|
||||
|
||||
void PaPlaybackNode::EnableOutput( const media_source& what, bool enabled,
|
||||
int32* )
|
||||
{
|
||||
DBUG(("EnableOutput() called.\n"));
|
||||
/* stub -- we don't support this yet */
|
||||
}
|
||||
|
||||
|
||||
status_t PaPlaybackNode::PrepareToConnect( const media_source& what,
|
||||
const media_destination& where, media_format* format,
|
||||
media_source* out_source, char* out_name )
|
||||
{
|
||||
/* the final stage of format negotiations. here we _must_ make specific any
|
||||
* remaining wildcards */
|
||||
DBUG(("PrepareToConnect() called.\n"));
|
||||
|
||||
/* make sure this really refers to our source */
|
||||
if( what != mOutput.source )
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
/* make sure we're not already connected */
|
||||
if( mOutput.destination != media_destination::null )
|
||||
return B_MEDIA_ALREADY_CONNECTED;
|
||||
|
||||
if( format->type != B_MEDIA_RAW_AUDIO )
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
|
||||
if( format->u.raw_audio.format != mPreferredFormat.u.raw_audio.format )
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
|
||||
if( format->u.raw_audio.buffer_size ==
|
||||
media_raw_audio_format::wildcard.buffer_size )
|
||||
{
|
||||
DBUG(("We were left to decide buffer size: choosing 2048"));
|
||||
format->u.raw_audio.buffer_size = 2048;
|
||||
}
|
||||
else
|
||||
DBUG(("Using consumer specified buffer size of %lu.\n",
|
||||
format->u.raw_audio.buffer_size));
|
||||
|
||||
/* Reserve the connection, return the information */
|
||||
mOutput.destination = where;
|
||||
mOutput.format = *format;
|
||||
*out_source = mOutput.source;
|
||||
strncpy( out_name, mOutput.name, B_MEDIA_NAME_LENGTH );
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void PaPlaybackNode::Connect(status_t error, const media_source& source,
|
||||
const media_destination& destination, const media_format& format, char* io_name)
|
||||
{
|
||||
DBUG(("Connect() called.\n"));
|
||||
|
||||
108
pa_beos/PlaybackNode.h
Normal file
108
pa_beos/PlaybackNode.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* BeOS Media Kit Implementation by Joshua Haberman
|
||||
*
|
||||
* Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <be/media/MediaRoster.h>
|
||||
#include <be/media/MediaEventLooper.h>
|
||||
#include <be/media/BufferProducer.h>
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
class PaPlaybackNode :
|
||||
public BBufferProducer,
|
||||
public BMediaEventLooper
|
||||
{
|
||||
|
||||
public:
|
||||
PaPlaybackNode( uint32 channels, float frame_rate, uint32 frames_per_buffer,
|
||||
PortAudioCallback *callback, void *user_data );
|
||||
~PaPlaybackNode();
|
||||
|
||||
|
||||
/* Local methods ******************************************/
|
||||
|
||||
BBuffer *FillNextBuffer(bigtime_t time);
|
||||
void SetSampleFormat(PaSampleFormat inFormat, PaSampleFormat outFormat);
|
||||
bool IsRunning();
|
||||
PaTimestamp GetStreamTime();
|
||||
|
||||
/* BMediaNode methods *************************************/
|
||||
|
||||
BMediaAddOn* AddOn( int32 * ) const;
|
||||
status_t HandleMessage( int32 message, const void *data, size_t size );
|
||||
|
||||
/* BMediaEventLooper methods ******************************/
|
||||
|
||||
void HandleEvent( const media_timed_event *event, bigtime_t lateness,
|
||||
bool realTimeEvent );
|
||||
void NodeRegistered();
|
||||
|
||||
/* BBufferProducer methods ********************************/
|
||||
|
||||
status_t FormatSuggestionRequested( media_type type, int32 quality,
|
||||
media_format* format );
|
||||
status_t FormatProposal( const media_source& output, media_format* format );
|
||||
status_t FormatChangeRequested( const media_source& source,
|
||||
const media_destination& destination, media_format* io_format, int32* );
|
||||
|
||||
status_t GetNextOutput( int32* cookie, media_output* out_output );
|
||||
status_t DisposeOutputCookie( int32 cookie );
|
||||
|
||||
void LateNoticeReceived( const media_source& what, bigtime_t how_much,
|
||||
bigtime_t performance_time );
|
||||
void EnableOutput( const media_source& what, bool enabled, int32* _deprecated_ );
|
||||
|
||||
status_t PrepareToConnect( const media_source& what,
|
||||
const media_destination& where, media_format* format,
|
||||
media_source* out_source, char* out_name );
|
||||
void Connect(status_t error, const media_source& source,
|
||||
const media_destination& destination, const media_format& format,
|
||||
char* io_name);
|
||||
void Disconnect(const media_source& what, const media_destination& where);
|
||||
|
||||
status_t SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup);
|
||||
|
||||
bool mAborted;
|
||||
|
||||
private:
|
||||
media_output mOutput;
|
||||
media_format mPreferredFormat;
|
||||
uint32 mOutputSampleWidth, mFramesPerBuffer;
|
||||
BBufferGroup *mBufferGroup;
|
||||
bigtime_t mDownstreamLatency, mInternalLatency, mStartTime;
|
||||
uint64 mSamplesSent;
|
||||
PortAudioCallback *mCallback;
|
||||
void *mUserData;
|
||||
bool mRunning;
|
||||
|
||||
};
|
||||
|
||||
441
pa_beos/pa_beos_mk.cc
Normal file
441
pa_beos/pa_beos_mk.cc
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* BeOS Media Kit Implementation by Joshua Haberman
|
||||
*
|
||||
* Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <be/app/Application.h>
|
||||
#include <be/kernel/OS.h>
|
||||
#include <be/media/RealtimeAlloc.h>
|
||||
#include <be/media/MediaRoster.h>
|
||||
#include <be/media/TimeSource.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_host.h"
|
||||
|
||||
#include "PlaybackNode.h"
|
||||
|
||||
#define PRINT(x) { printf x; fflush(stdout); }
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBUG(x) PRINT(x)
|
||||
#else
|
||||
#define DBUG(x)
|
||||
#endif
|
||||
|
||||
typedef struct PaHostSoundControl
|
||||
{
|
||||
/* These members are common to all modes of operation */
|
||||
media_node pahsc_TimeSource; /* the sound card's DAC. */
|
||||
media_format pahsc_Format;
|
||||
|
||||
/* These methods are specific to playing mode */
|
||||
media_node pahsc_OutputNode; /* output to the mixer */
|
||||
media_node pahsc_InputNode; /* reads data from user callback -- PA specific */
|
||||
|
||||
media_input pahsc_MixerInput; /* input jack on the soundcard's mixer. */
|
||||
media_output pahsc_PaOutput; /* output jack from the PA node */
|
||||
|
||||
PaPlaybackNode *pahsc_InputNodeInstance;
|
||||
|
||||
}
|
||||
PaHostSoundControl;
|
||||
|
||||
/*************************************************************************/
|
||||
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
|
||||
{
|
||||
/* stub */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaDeviceID Pa_GetDefaultInputDeviceID( void )
|
||||
{
|
||||
/* stub */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
|
||||
{
|
||||
/* stub */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
int Pa_CountDevices()
|
||||
{
|
||||
/* stub */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_Init( void )
|
||||
{
|
||||
/* we have to create this in order to use BMediaRoster. I hope it doesn't
|
||||
* cause problems */
|
||||
be_app = new BApplication("application/x-vnd.portaudio-app");
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
PaError PaHost_Term( void )
|
||||
{
|
||||
delete be_app;
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StreamActive( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
|
||||
DBUG(("IsRunning returning: %s\n",
|
||||
pahsc->pahsc_InputNodeInstance->IsRunning() ? "true" : "false"));
|
||||
|
||||
return (PaError)pahsc->pahsc_InputNodeInstance->IsRunning();
|
||||
}
|
||||
|
||||
PaError PaHost_StartOutput( internalPortAudioStream *past )
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StartInput( internalPortAudioStream *past )
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StartEngine( internalPortAudioStream *past )
|
||||
{
|
||||
bigtime_t very_soon, start_latency;
|
||||
status_t err;
|
||||
BMediaRoster *roster = BMediaRoster::Roster(&err);
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
|
||||
|
||||
/* for some reason, err indicates an error (though nothing it wrong)
|
||||
* when the DBUG macro in pa_lib.c is enabled. It's reproducably
|
||||
* linked. Weird. */
|
||||
if( !roster /* || err != B_OK */ )
|
||||
{
|
||||
DBUG(("No media server! err=%d, roster=%x\n", err, roster));
|
||||
return paHostError;
|
||||
}
|
||||
|
||||
/* tell the node when to start -- since there aren't any other nodes
|
||||
* starting that we have to wait for, just tell it to start now
|
||||
*/
|
||||
|
||||
BTimeSource *timeSource = roster->MakeTimeSourceFor(pahsc->pahsc_TimeSource);
|
||||
very_soon = timeSource->PerformanceTimeFor( BTimeSource::RealTime() );
|
||||
timeSource->Release();
|
||||
|
||||
/* Add the latency of starting the network of nodes */
|
||||
err = roster->GetStartLatencyFor( pahsc->pahsc_TimeSource, &start_latency );
|
||||
very_soon += start_latency;
|
||||
|
||||
err = roster->StartNode( pahsc->pahsc_InputNode, very_soon );
|
||||
/* No need to start the mixer -- it's always running */
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
|
||||
{
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
|
||||
BMediaRoster *roster = BMediaRoster::Roster();
|
||||
|
||||
if( !roster )
|
||||
{
|
||||
DBUG(("No media roster!\n"));
|
||||
return paHostError;
|
||||
}
|
||||
|
||||
if( !pahsc )
|
||||
return paHostError;
|
||||
|
||||
/* this crashes, and I don't know why yet */
|
||||
// if( abort )
|
||||
// pahsc->pahsc_InputNodeInstance->mAborted = true;
|
||||
|
||||
roster->StopNode(pahsc->pahsc_InputNode, 0, /* immediate = */ true);
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_OpenStream( internalPortAudioStream *past )
|
||||
{
|
||||
status_t err;
|
||||
BMediaRoster *roster = BMediaRoster::Roster(&err);
|
||||
PaHostSoundControl *pahsc;
|
||||
|
||||
/* Allocate and initialize host data. */
|
||||
pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
|
||||
if( pahsc == NULL )
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
memset( pahsc, 0, sizeof(PaHostSoundControl) );
|
||||
past->past_DeviceData = (void *) pahsc;
|
||||
|
||||
if( !roster /* || err != B_OK */ )
|
||||
{
|
||||
/* no media server! */
|
||||
DBUG(("No media server.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ( past->past_NumInputChannels > 0 && past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
/* filter -- not implemented yet */
|
||||
goto error;
|
||||
}
|
||||
else if ( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
/* recorder -- not implemented yet */
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* player ****************************************************************/
|
||||
|
||||
status_t err;
|
||||
int32 num;
|
||||
|
||||
/* First we need to create the three components (like components in a stereo
|
||||
* system). The mixer component is our interface to the sound card, data
|
||||
* we write there will get played. The BePA_InputNode component is the node
|
||||
* which represents communication with the PA client (it is what calls the
|
||||
* client's callbacks). The time source component is the sound card's DAC,
|
||||
* which allows us to slave the other components to it instead of the system
|
||||
* clock. */
|
||||
err = roster->GetAudioMixer( &pahsc->pahsc_OutputNode );
|
||||
if( err != B_OK )
|
||||
{
|
||||
DBUG(("Couldn't get default mixer.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = roster->GetTimeSource( &pahsc->pahsc_TimeSource );
|
||||
if( err != B_OK )
|
||||
{
|
||||
DBUG(("Couldn't get time source.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
pahsc->pahsc_InputNodeInstance = new PaPlaybackNode(2, 44100,
|
||||
past->past_FramesPerUserBuffer, past->past_Callback, past->past_UserData );
|
||||
pahsc->pahsc_InputNodeInstance->SetSampleFormat(0,
|
||||
past->past_OutputSampleFormat);
|
||||
err = roster->RegisterNode( pahsc->pahsc_InputNodeInstance );
|
||||
if( err != B_OK )
|
||||
{
|
||||
DBUG(("Unable to register node.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
roster->GetNodeFor( pahsc->pahsc_InputNodeInstance->Node().node,
|
||||
&pahsc->pahsc_InputNode );
|
||||
if( err != B_OK )
|
||||
{
|
||||
DBUG(("Unable to get input node.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Now we have three components (nodes) sitting next to each other. The
|
||||
* next step is to look at them and find their inputs and outputs so we can
|
||||
* wire them together. */
|
||||
err = roster->GetFreeInputsFor( pahsc->pahsc_OutputNode,
|
||||
&pahsc->pahsc_MixerInput, 1, &num, B_MEDIA_RAW_AUDIO );
|
||||
if( err != B_OK || num < 1 )
|
||||
{
|
||||
DBUG(("Couldn't get the mixer input.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = roster->GetFreeOutputsFor( pahsc->pahsc_InputNode,
|
||||
&pahsc->pahsc_PaOutput, 1, &num, B_MEDIA_RAW_AUDIO );
|
||||
if( err != B_OK || num < 1 )
|
||||
{
|
||||
DBUG(("Couldn't get PortAudio output.\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
/* We've found the input and output -- the final step is to run a wire
|
||||
* between them so they are connected. */
|
||||
|
||||
/* try to make the mixer input adapt to what PA sends it */
|
||||
pahsc->pahsc_Format = pahsc->pahsc_PaOutput.format;
|
||||
roster->Connect( pahsc->pahsc_PaOutput.source,
|
||||
pahsc->pahsc_MixerInput.destination, &pahsc->pahsc_Format,
|
||||
&pahsc->pahsc_PaOutput, &pahsc->pahsc_MixerInput );
|
||||
|
||||
|
||||
/* Actually, there's one final step -- tell them all to sync to the
|
||||
* sound card's DAC */
|
||||
roster->SetTimeSourceFor( pahsc->pahsc_InputNode.node,
|
||||
pahsc->pahsc_TimeSource.node );
|
||||
roster->SetTimeSourceFor( pahsc->pahsc_OutputNode.node,
|
||||
pahsc->pahsc_TimeSource.node );
|
||||
|
||||
}
|
||||
|
||||
return paNoError;
|
||||
|
||||
error:
|
||||
PaHost_CloseStream( past );
|
||||
return paHostError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError PaHost_CloseStream( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
|
||||
status_t err;
|
||||
BMediaRoster *roster = BMediaRoster::Roster(&err);
|
||||
|
||||
if( !roster )
|
||||
{
|
||||
DBUG(("Couldn't get media roster\n"));
|
||||
return paHostError;
|
||||
}
|
||||
|
||||
if( !pahsc )
|
||||
return paHostError;
|
||||
|
||||
/* Disconnect all the connections we made when opening the stream */
|
||||
|
||||
roster->Disconnect(pahsc->pahsc_InputNode.node, pahsc->pahsc_PaOutput.source,
|
||||
pahsc->pahsc_OutputNode.node, pahsc->pahsc_MixerInput.destination);
|
||||
|
||||
DBUG(("Calling ReleaseNode()"));
|
||||
roster->ReleaseNode(pahsc->pahsc_InputNode);
|
||||
|
||||
/* deleting the node shouldn't be necessary -- it is reference counted, and will
|
||||
* delete itself when its references drop to zero. the call to ReleaseNode()
|
||||
* above should decrease its reference count */
|
||||
pahsc->pahsc_InputNodeInstance = NULL;
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
|
||||
{
|
||||
internalPortAudioStream *past = (internalPortAudioStream *) stream;
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
|
||||
|
||||
return pahsc->pahsc_InputNodeInstance->GetStreamTime();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
void Pa_Sleep( long msec )
|
||||
{
|
||||
/* snooze() takes microseconds */
|
||||
snooze( msec * 1000 );
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Allocate memory that can be accessed in real-time.
|
||||
* This may need to be held in physical memory so that it is not
|
||||
* paged to virtual memory.
|
||||
* This call MUST be balanced with a call to PaHost_FreeFastMemory().
|
||||
* Memory will be set to zero.
|
||||
*/
|
||||
void *PaHost_AllocateFastMemory( long numBytes )
|
||||
{
|
||||
/* BeOS supports non-pagable memory through pools -- a pool is an area
|
||||
* of physical memory that is locked. It would be best to pre-allocate
|
||||
* that pool and then hand out memory from it, but we don't know in
|
||||
* advance how much we'll need. So for now, we'll allocate a pool
|
||||
* for every request we get, storing a pointer to the pool at the
|
||||
* beginning of the allocated memory */
|
||||
rtm_pool *pool;
|
||||
void *addr;
|
||||
long size = numBytes + sizeof(rtm_pool *);
|
||||
static int counter = 0;
|
||||
char pool_name[100];
|
||||
|
||||
/* Every pool needs a unique name. */
|
||||
sprintf(pool_name, "PaPoolNumber%d", counter++);
|
||||
|
||||
if( rtm_create_pool( &pool, size, pool_name ) != B_OK )
|
||||
return 0;
|
||||
|
||||
addr = rtm_alloc( pool, size );
|
||||
if( addr == NULL )
|
||||
return 0;
|
||||
|
||||
memset( addr, 0, numBytes );
|
||||
*((rtm_pool **)addr) = pool; // store the pointer to the pool
|
||||
addr = (rtm_pool **)addr + 1; // and return the next location in memory
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Free memory that could be accessed in real-time.
|
||||
* This call MUST be balanced with a call to PaHost_AllocateFastMemory().
|
||||
*/
|
||||
void PaHost_FreeFastMemory( void *addr, long numBytes )
|
||||
{
|
||||
rtm_pool *pool;
|
||||
|
||||
if( addr == NULL )
|
||||
return;
|
||||
|
||||
addr = (rtm_pool **)addr - 1;
|
||||
pool = *((rtm_pool **)addr);
|
||||
|
||||
rtm_free( addr );
|
||||
rtm_delete_pool( pool );
|
||||
}
|
||||
142
pa_common/pa_host.h
Normal file
142
pa_common/pa_host.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#ifndef PA_HOST_H
|
||||
#define PA_HOST_H
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
* Host dependant internal API for PortAudio
|
||||
*
|
||||
* Author: Phil Burk <philburk@softsynth.com>
|
||||
*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.softsynth.com/portaudio/
|
||||
* DirectSound and Macintosh Implementation
|
||||
* Copyright (c) 1999-2000 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 __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef SUPPORT_AUDIO_CAPTURE
|
||||
#define SUPPORT_AUDIO_CAPTURE (1)
|
||||
#endif
|
||||
|
||||
#ifndef int32
|
||||
typedef long int32;
|
||||
#endif
|
||||
#ifndef uint32
|
||||
typedef unsigned long uint32;
|
||||
#endif
|
||||
#ifndef int16
|
||||
typedef short int16;
|
||||
#endif
|
||||
#ifndef uint16
|
||||
typedef unsigned short uint16;
|
||||
#endif
|
||||
|
||||
#define PA_MAGIC (0x18273645)
|
||||
|
||||
/************************************************************************************/
|
||||
/****************** Structures ******************************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
typedef struct internalPortAudioStream
|
||||
{
|
||||
uint32 past_Magic; /* ID for struct to catch bugs. */
|
||||
/* User specified information. */
|
||||
uint32 past_FramesPerUserBuffer;
|
||||
uint32 past_NumUserBuffers;
|
||||
double past_SampleRate; /* Closest supported sample rate. */
|
||||
int past_NumInputChannels;
|
||||
int past_NumOutputChannels;
|
||||
PaDeviceID past_InputDeviceID;
|
||||
PaDeviceID past_OutputDeviceID;
|
||||
PaSampleFormat past_InputSampleFormat;
|
||||
PaSampleFormat past_OutputSampleFormat;
|
||||
void *past_DeviceData;
|
||||
PortAudioCallback *past_Callback;
|
||||
void *past_UserData;
|
||||
uint32 past_Flags;
|
||||
/* Flags for communicating between foreground and background. */
|
||||
volatile int past_IsActive; /* Background is still playing. */
|
||||
volatile int past_StopSoon; /* Background should keep playing when buffers empty. */
|
||||
volatile int past_StopNow; /* Background should stop playing now. */
|
||||
/* These buffers are used when the native format does not match the user format. */
|
||||
void *past_InputBuffer;
|
||||
uint32 past_InputBufferSize;
|
||||
void *past_OutputBuffer;
|
||||
uint32 past_OutputBufferSize;
|
||||
/* Measurements */
|
||||
uint32 past_NumCallbacks;
|
||||
PaTimestamp past_FrameCount; /* Frames output to buffer. */
|
||||
/* For measuring CPU utilization. */
|
||||
double past_AverageInsideCount;
|
||||
double past_AverageTotalCount;
|
||||
double past_Usage;
|
||||
int past_IfLastExitValid;
|
||||
}
|
||||
internalPortAudioStream;
|
||||
|
||||
/************************************************************************************/
|
||||
/****************** Prototypes ******************************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
PaError PaHost_Init( void );
|
||||
PaError PaHost_Term( void );
|
||||
|
||||
PaError PaHost_OpenStream( internalPortAudioStream *past );
|
||||
PaError PaHost_CloseStream( internalPortAudioStream *past );
|
||||
|
||||
PaError PaHost_StartOutput( internalPortAudioStream *past );
|
||||
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort );
|
||||
PaError PaHost_StartInput( internalPortAudioStream *past );
|
||||
PaError PaHost_StopInput( internalPortAudioStream *past, int abort );
|
||||
PaError PaHost_StartEngine( internalPortAudioStream *past );
|
||||
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort );
|
||||
PaError PaHost_StreamActive( internalPortAudioStream *past );
|
||||
|
||||
long Pa_CallConvertInt16( internalPortAudioStream *past,
|
||||
short *nativeInputBuffer,
|
||||
short *nativeOutputBuffer );
|
||||
|
||||
long Pa_CallConvertFloat32( internalPortAudioStream *past,
|
||||
float *nativeInputBuffer,
|
||||
float *nativeOutputBuffer );
|
||||
|
||||
void *PaHost_AllocateFastMemory( long numBytes );
|
||||
void PaHost_FreeFastMemory( void *addr, long numBytes );
|
||||
|
||||
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
|
||||
double *closestFrameRatePtr );
|
||||
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable,
|
||||
int numRates, double frameRate );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PA_HOST_H */
|
||||
836
pa_common/pa_lib.c
Normal file
836
pa_common/pa_lib.c
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* Host Independant Layer
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2000 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:
|
||||
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
|
||||
PLB20010820 - fix dither and shift for recording PaUInt8 format
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
|
||||
#ifdef _WIN32
|
||||
#ifndef __MWERKS__
|
||||
#include <memory.h>
|
||||
#endif /* __MWERKS__ */
|
||||
#else /* !_WIN32 */
|
||||
#include <memory.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_host.h"
|
||||
#include "pa_trace.h"
|
||||
|
||||
/* The reason we might NOT want to validate the rate before opening the stream
|
||||
* is because many DirectSound drivers lie about the rates they actually support.
|
||||
*/
|
||||
#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
|
||||
|
||||
/*
|
||||
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
|
||||
*/
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0)
|
||||
#define TRUE (!FALSE)
|
||||
#endif
|
||||
|
||||
#define PRINT(x) { printf x; fflush(stdout); }
|
||||
#define ERR_RPT(x) PRINT(x)
|
||||
#define DBUG(x) /* PRINT(x) */
|
||||
#define DBUGX(x) /* PRINT(x) */
|
||||
|
||||
static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
|
||||
|
||||
static PaError Pa_KillStream( PortAudioStream *stream, int abort );
|
||||
|
||||
/***********************************************************************/
|
||||
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
|
||||
{
|
||||
double err, minErr = allowableError;
|
||||
int i, bestFit = -1;
|
||||
|
||||
for( i=0; i<numRates; i++ )
|
||||
{
|
||||
err = fabs( frameRate - rateTable[i] );
|
||||
if( err < minErr )
|
||||
{
|
||||
minErr = err;
|
||||
bestFit = i;
|
||||
}
|
||||
}
|
||||
return bestFit;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
** Make sure sample rate is legal and also convert to enumeration for driver.
|
||||
*/
|
||||
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
|
||||
double *closestFrameRatePtr )
|
||||
{
|
||||
long bestRateIndex;
|
||||
const PaDeviceInfo *pdi;
|
||||
pdi = Pa_GetDeviceInfo( id );
|
||||
if( pdi == NULL )
|
||||
{
|
||||
return paInvalidDeviceId;
|
||||
}
|
||||
|
||||
if( pdi->numSampleRates == -1 )
|
||||
{
|
||||
/* Is it out of range? */
|
||||
if( (requestedFrameRate < pdi->sampleRates[0]) ||
|
||||
(requestedFrameRate > pdi->sampleRates[1]) )
|
||||
{
|
||||
return paInvalidSampleRate;
|
||||
}
|
||||
|
||||
*closestFrameRatePtr = requestedFrameRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
|
||||
if( bestRateIndex < 0 ) return paInvalidSampleRate;
|
||||
*closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
|
||||
}
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_OpenStream(
|
||||
PortAudioStream** streamPtrPtr,
|
||||
PaDeviceID inputDeviceID,
|
||||
int numInputChannels,
|
||||
PaSampleFormat inputSampleFormat,
|
||||
void *inputDriverInfo,
|
||||
PaDeviceID outputDeviceID,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat outputSampleFormat,
|
||||
void *outputDriverInfo,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
unsigned long streamFlags,
|
||||
PortAudioCallback *callback,
|
||||
void *userData )
|
||||
{
|
||||
internalPortAudioStream *past = NULL;
|
||||
PaError result = paNoError;
|
||||
int bitsPerInputSample;
|
||||
int bitsPerOutputSample;
|
||||
/* Print passed parameters. */
|
||||
DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
|
||||
streamPtrPtr, inputDeviceID, numInputChannels,
|
||||
inputSampleFormat, inputDriverInfo ));
|
||||
DBUG((" %d, %d, %d, %p, /* output */\n",
|
||||
outputDeviceID, numOutputChannels,
|
||||
outputSampleFormat, outputDriverInfo ));
|
||||
DBUG((" %g, %d, %d, 0x%x, , %p )\n",
|
||||
sampleRate, framesPerBuffer, numberOfBuffers,
|
||||
streamFlags, userData ));
|
||||
|
||||
/* Check for parameter errors. */
|
||||
if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
|
||||
if( streamPtrPtr == NULL ) return paBadStreamPtr;
|
||||
if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
|
||||
if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
|
||||
if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
|
||||
if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
|
||||
{
|
||||
return paInvalidDeviceId;
|
||||
}
|
||||
if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
|
||||
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
if( inputDeviceID >= 0 )
|
||||
{
|
||||
PaError size = Pa_GetSampleSize( inputSampleFormat );
|
||||
if( size < 0 ) return size;
|
||||
bitsPerInputSample = 8 * size;
|
||||
if( (numInputChannels <= 0) ) return paInvalidChannelCount;
|
||||
}
|
||||
#else
|
||||
if( inputDeviceID >= 0 )
|
||||
{
|
||||
return paInvalidChannelCount;
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
else
|
||||
{
|
||||
if( numInputChannels > 0 ) return paInvalidChannelCount;
|
||||
bitsPerInputSample = 0;
|
||||
}
|
||||
|
||||
if( outputDeviceID >= 0 )
|
||||
{
|
||||
PaError size = Pa_GetSampleSize( outputSampleFormat );
|
||||
if( size < 0 ) return size;
|
||||
bitsPerOutputSample = 8 * size;
|
||||
if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( numOutputChannels > 0 ) return paInvalidChannelCount;
|
||||
bitsPerOutputSample = 0;
|
||||
}
|
||||
|
||||
if( callback == NULL ) return paNullCallback;
|
||||
|
||||
/* Allocate and clear stream structure. */
|
||||
past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
|
||||
if( past == NULL ) return paInsufficientMemory;
|
||||
memset( past, 0, sizeof(internalPortAudioStream) );
|
||||
AddTraceMessage("Pa_OpenStream: past", (long) past );
|
||||
|
||||
past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
|
||||
past->past_FramesPerUserBuffer = framesPerBuffer;
|
||||
past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
|
||||
past->past_Callback = callback;
|
||||
past->past_UserData = userData;
|
||||
past->past_OutputSampleFormat = outputSampleFormat;
|
||||
past->past_InputSampleFormat = inputSampleFormat;
|
||||
past->past_OutputDeviceID = outputDeviceID;
|
||||
past->past_InputDeviceID = inputDeviceID;
|
||||
past->past_NumInputChannels = numInputChannels;
|
||||
past->past_NumOutputChannels = numOutputChannels;
|
||||
past->past_Flags = streamFlags;
|
||||
|
||||
/* Check for absurd sample rates. */
|
||||
if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
|
||||
{
|
||||
result = paInvalidSampleRate;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Allocate buffers that may be used for format conversion from user to native buffers. */
|
||||
if( numInputChannels > 0 )
|
||||
{
|
||||
|
||||
#if PA_VALIDATE_RATE
|
||||
result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
|
||||
if( result < 0 )
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
#else
|
||||
past->past_SampleRate = sampleRate;
|
||||
#endif
|
||||
/* Allocate single Input buffer. */
|
||||
past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
|
||||
past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
|
||||
if( past->past_InputBuffer == NULL )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
past->past_InputBuffer = NULL;
|
||||
}
|
||||
|
||||
/* Allocate single Output buffer. */
|
||||
if( numOutputChannels > 0 )
|
||||
{
|
||||
#if PA_VALIDATE_RATE
|
||||
result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
|
||||
if( result < 0 )
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
#else
|
||||
past->past_SampleRate = sampleRate;
|
||||
#endif
|
||||
past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
|
||||
past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
|
||||
if( past->past_OutputBuffer == NULL )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
past->past_OutputBuffer = NULL;
|
||||
}
|
||||
|
||||
result = PaHost_OpenStream( past );
|
||||
if( result < 0 ) goto cleanup;
|
||||
|
||||
*streamPtrPtr = (void *) past;
|
||||
|
||||
return result;
|
||||
|
||||
cleanup:
|
||||
if( past != NULL ) Pa_CloseStream( past );
|
||||
*streamPtrPtr = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
|
||||
int numInputChannels,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat sampleFormat,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PortAudioCallback *callback,
|
||||
void *userData )
|
||||
{
|
||||
return Pa_OpenStream(
|
||||
stream,
|
||||
((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
|
||||
numInputChannels, sampleFormat, NULL,
|
||||
((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
|
||||
numOutputChannels, sampleFormat, NULL,
|
||||
sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_CloseStream( PortAudioStream* stream)
|
||||
{
|
||||
PaError result;
|
||||
internalPortAudioStream *past;
|
||||
|
||||
DBUG(("Pa_CloseStream()\n"));
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
|
||||
Pa_AbortStream( past );
|
||||
result = PaHost_CloseStream( past );
|
||||
|
||||
if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
|
||||
if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
|
||||
PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_StartStream( PortAudioStream *stream )
|
||||
{
|
||||
PaError result = paHostError;
|
||||
internalPortAudioStream *past;
|
||||
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
|
||||
past->past_FrameCount = 0.0;
|
||||
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StartInput( past );
|
||||
DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
}
|
||||
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StartOutput( past );
|
||||
DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
}
|
||||
|
||||
result = PaHost_StartEngine( past );
|
||||
DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
|
||||
return paNoError;
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_StopStream( PortAudioStream *stream )
|
||||
{
|
||||
return Pa_KillStream( stream, 0 );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_AbortStream( PortAudioStream *stream )
|
||||
{
|
||||
return Pa_KillStream( stream, 1 );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
static PaError Pa_KillStream( PortAudioStream *stream, int abort )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
internalPortAudioStream *past;
|
||||
|
||||
DBUG(("Pa_StopStream().\n"));
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
|
||||
if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
|
||||
{
|
||||
result = PaHost_StopEngine( past, abort );
|
||||
DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
}
|
||||
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StopInput( past, abort );
|
||||
DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
|
||||
if( result != paNoError ) goto error;
|
||||
}
|
||||
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StopOutput( past, abort );
|
||||
DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
|
||||
if( result != paNoError ) goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
past->past_Usage = 0;
|
||||
past->past_IfLastExitValid = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_StreamActive( PortAudioStream *stream )
|
||||
{
|
||||
internalPortAudioStream *past;
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
return PaHost_StreamActive( past );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
const char *Pa_GetErrorText( PaError errnum )
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
switch(errnum)
|
||||
{
|
||||
case paNoError: msg = "Success"; break;
|
||||
case paHostError: msg = "Host error."; break;
|
||||
case paInvalidChannelCount: msg = "Invalid number of channels."; break;
|
||||
case paInvalidSampleRate: msg = "Invalid sample rate."; break;
|
||||
case paInvalidDeviceId: msg = "Invalid device ID."; break;
|
||||
case paInvalidFlag: msg = "Invalid flag."; break;
|
||||
case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
|
||||
case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
|
||||
case paInsufficientMemory: msg = "Insufficient memory."; break;
|
||||
case paBufferTooBig: msg = "Buffer too big."; break;
|
||||
case paBufferTooSmall: msg = "Buffer too small."; break;
|
||||
case paNullCallback: msg = "No callback routine specified."; break;
|
||||
case paBadStreamPtr: msg = "Invalid stream pointer."; break;
|
||||
case paTimedOut : msg = "Wait Timed Out."; break;
|
||||
case paInternalError: msg = "Internal PortAudio Error."; break;
|
||||
case paDeviceUnavailable: msg = "Device Unavailable."; break;
|
||||
default: msg = "Illegal error number."; break;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
Get CPU Load as a fraction of total CPU time.
|
||||
A value of 0.5 would imply that PortAudio and the sound generating
|
||||
callback was consuming roughly 50% of the available CPU time.
|
||||
The amount may vary depending on CPU load.
|
||||
This function may be called from the callback function.
|
||||
*/
|
||||
double Pa_GetCPULoad( PortAudioStream* stream)
|
||||
{
|
||||
internalPortAudioStream *past;
|
||||
if( stream == NULL ) return (double) paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
return past->past_Usage;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
** Calculate 2 LSB dither signal with a triangular distribution.
|
||||
** Ranged properly for adding to a 32 bit integer prior to >>15.
|
||||
*/
|
||||
#define DITHER_BITS (15)
|
||||
#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
|
||||
static long Pa_TriangularDither( void )
|
||||
{
|
||||
static unsigned long previous = 0;
|
||||
static unsigned long randSeed1 = 22222;
|
||||
static unsigned long randSeed2 = 5555555;
|
||||
long current, highPass;
|
||||
/* Generate two random numbers. */
|
||||
randSeed1 = (randSeed1 * 196314165) + 907633515;
|
||||
randSeed2 = (randSeed2 * 196314165) + 907633515;
|
||||
/* Generate triangular distribution about 0. */
|
||||
current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
|
||||
/* High pass filter to reduce audibility. */
|
||||
highPass = current - previous;
|
||||
previous = current;
|
||||
return highPass;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Called by host code.
|
||||
** Convert input from Int16, call user code, then convert output
|
||||
** to Int16 format for native use.
|
||||
** Assumes host native format is paInt16.
|
||||
** Returns result from user callback.
|
||||
*/
|
||||
long Pa_CallConvertInt16( internalPortAudioStream *past,
|
||||
short *nativeInputBuffer,
|
||||
short *nativeOutputBuffer )
|
||||
{
|
||||
long temp;
|
||||
long bytesEmpty = 0;
|
||||
long bytesFilled = 0;
|
||||
int userResult;
|
||||
unsigned int i;
|
||||
void *inputBuffer = NULL;
|
||||
void *outputBuffer = NULL;
|
||||
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
/* Get native data from DirectSound. */
|
||||
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
||||
{
|
||||
/* Convert from native format to PA format. */
|
||||
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
|
||||
switch(past->past_InputSampleFormat)
|
||||
{
|
||||
|
||||
case paFloat32:
|
||||
{
|
||||
float *inBufPtr = (float *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt32:
|
||||
{
|
||||
/* Convert 16 bit data to 32 bit integers */
|
||||
int *inBufPtr = (int *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = nativeInputBuffer[i] << 16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt16:
|
||||
{
|
||||
/* Already in correct format so don't copy. */
|
||||
inputBuffer = nativeInputBuffer;
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt8:
|
||||
{
|
||||
/* Convert 16 bit data to 8 bit chars */
|
||||
char *inBufPtr = (char *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
temp = nativeInputBuffer[i];
|
||||
temp += Pa_TriangularDither() >> 8; /* PLB20010820 */
|
||||
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
inBufPtr[i] = (char)(temp >> 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paUInt8:
|
||||
{
|
||||
/* Convert 16 bit data to 8 bit unsigned chars */
|
||||
unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If you dither then you have to clip because dithering could push the signal out of range! */
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
temp = nativeInputBuffer[i];
|
||||
temp += Pa_TriangularDither() >> 8; /* PLB20010820 */
|
||||
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
|
||||
/* Are we doing output time? */
|
||||
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
||||
{
|
||||
/* May already be in native format so just write directly to native buffer. */
|
||||
outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
|
||||
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 );
|
||||
|
||||
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
|
||||
|
||||
/* Convert to native format if necessary. */
|
||||
if( outputBuffer != NULL )
|
||||
{
|
||||
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
|
||||
switch(past->past_OutputSampleFormat)
|
||||
{
|
||||
case paFloat32:
|
||||
{
|
||||
float *outBufPtr = (float *) past->past_OutputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
if( past->past_Flags & paClipOff ) /* NOTHING */
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
|
||||
}
|
||||
}
|
||||
else /* CLIP */
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
temp = (long)(outBufPtr[i] * 32767.0f);
|
||||
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If you dither then you have to clip because dithering could push the signal out of range! */
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
float dither = Pa_TriangularDither()*DITHER_SCALE;
|
||||
float dithered = (outBufPtr[i] * (32767.0f)) + dither;
|
||||
temp = (long) (dithered);
|
||||
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt32:
|
||||
{
|
||||
int *outBufPtr = (int *) past->past_OutputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
/* Shift one bit down before dithering so that we have room for overflow from add. */
|
||||
temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
|
||||
temp = temp >> 15;
|
||||
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt8:
|
||||
{
|
||||
char *outBufPtr = (char *) past->past_OutputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paUInt8:
|
||||
{
|
||||
unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return userResult;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Called by host code.
|
||||
** Convert input from Float32, call user code, then convert output
|
||||
** to Float32 format for native use.
|
||||
** Assumes host native format is Float32.
|
||||
** Returns result from user callback.
|
||||
** FIXME - Unimplemented for formats other than paFloat32!!!!
|
||||
*/
|
||||
long Pa_CallConvertFloat32( internalPortAudioStream *past,
|
||||
float *nativeInputBuffer,
|
||||
float *nativeOutputBuffer )
|
||||
{
|
||||
long bytesEmpty = 0;
|
||||
long bytesFilled = 0;
|
||||
int userResult;
|
||||
void *inputBuffer = NULL;
|
||||
void *outputBuffer = NULL;
|
||||
|
||||
/* Get native data from DirectSound. */
|
||||
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
||||
{
|
||||
inputBuffer = nativeInputBuffer; /* FIXME */
|
||||
}
|
||||
|
||||
/* Are we doing output time? */
|
||||
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
||||
{
|
||||
/* May already be in native format so just write directly to native buffer. */
|
||||
outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
|
||||
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 );
|
||||
|
||||
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
|
||||
|
||||
/* Convert to native format if necessary. */ /* FIXME */
|
||||
return userResult;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_Initialize( void )
|
||||
{
|
||||
if( gInitCount++ > 0 ) return paNoError;
|
||||
ResetTraceMessages();
|
||||
return PaHost_Init();
|
||||
}
|
||||
|
||||
PaError Pa_Terminate( void )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
|
||||
if( gInitCount == 0 ) return paNoError;
|
||||
else if( --gInitCount == 0 )
|
||||
{
|
||||
result = PaHost_Term();
|
||||
DumpTraceMessages();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
PaError Pa_GetSampleSize( PaSampleFormat format )
|
||||
{
|
||||
int size;
|
||||
switch(format )
|
||||
{
|
||||
|
||||
case paUInt8:
|
||||
case paInt8:
|
||||
size = 1;
|
||||
break;
|
||||
|
||||
case paInt16:
|
||||
size = 2;
|
||||
break;
|
||||
|
||||
case paPackedInt24:
|
||||
size = 3;
|
||||
break;
|
||||
|
||||
case paFloat32:
|
||||
case paInt32:
|
||||
case paInt24:
|
||||
size = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
size = paSampleFormatNotSupported;
|
||||
break;
|
||||
}
|
||||
return (PaError) size;
|
||||
}
|
||||
|
||||
|
||||
83
pa_common/pa_trace.c
Normal file
83
pa_common/pa_trace.c
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library Trace Facility
|
||||
* Store trace information in real-time for later printing.
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2000 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "pa_trace.h"
|
||||
|
||||
#if TRACE_REALTIME_EVENTS
|
||||
|
||||
static char *traceTextArray[MAX_TRACE_RECORDS];
|
||||
static int traceIntArray[MAX_TRACE_RECORDS];
|
||||
static int traceIndex = 0;
|
||||
static int traceBlock = 0;
|
||||
|
||||
/*********************************************************************/
|
||||
void ResetTraceMessages()
|
||||
{
|
||||
traceIndex = 0;
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
void DumpTraceMessages()
|
||||
{
|
||||
int i;
|
||||
int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;
|
||||
|
||||
printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
|
||||
for( i=0; i<numDump; i++ )
|
||||
{
|
||||
printf("%3d: %s = 0x%08X\n",
|
||||
i, traceTextArray[i], traceIntArray[i] );
|
||||
}
|
||||
ResetTraceMessages();
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
void AddTraceMessage( char *msg, int data )
|
||||
{
|
||||
if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
|
||||
{
|
||||
traceBlock = 1;
|
||||
/* DumpTraceMessages(); */
|
||||
}
|
||||
else if( traceIndex < MAX_TRACE_RECORDS )
|
||||
{
|
||||
traceTextArray[traceIndex] = msg;
|
||||
traceIntArray[traceIndex] = data;
|
||||
traceIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
67
pa_common/pa_trace.h
Normal file
67
pa_common/pa_trace.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef PA_TRACE_H
|
||||
#define PA_TRACE_H
|
||||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library Trace Facility
|
||||
* Store trace information in real-time for later printing.
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2000 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 TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
|
||||
#define MAX_TRACE_RECORDS (2048)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
/************************************************************************************/
|
||||
/****************** Prototypes ******************************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
#if TRACE_REALTIME_EVENTS
|
||||
|
||||
void DumpTraceMessages();
|
||||
void ResetTraceMessages();
|
||||
void AddTraceMessage( char *msg, int data );
|
||||
|
||||
#else
|
||||
|
||||
#define AddTraceMessage(msg,data) /* noop */
|
||||
#define ResetTraceMessages() /* noop */
|
||||
#define DumpTraceMessages() /* noop */
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* PA_TRACE_H */
|
||||
432
pa_common/portaudio.h
Normal file
432
pa_common/portaudio.h
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
#ifndef PORT_AUDIO_H
|
||||
#define PORT_AUDIO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PortAudio API Header File
|
||||
* Latest version available at: 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.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef int PaError;
|
||||
typedef enum {
|
||||
paNoError = 0,
|
||||
|
||||
paHostError = -10000,
|
||||
paInvalidChannelCount,
|
||||
paInvalidSampleRate,
|
||||
paInvalidDeviceId,
|
||||
paInvalidFlag,
|
||||
paSampleFormatNotSupported,
|
||||
paBadIODeviceCombination,
|
||||
paInsufficientMemory,
|
||||
paBufferTooBig,
|
||||
paBufferTooSmall,
|
||||
paNullCallback,
|
||||
paBadStreamPtr,
|
||||
paTimedOut,
|
||||
paInternalError,
|
||||
paDeviceUnavailable
|
||||
} PaErrorNum;
|
||||
|
||||
/*
|
||||
Pa_Initialize() is the library initialisation function - call this before
|
||||
using the library.
|
||||
*/
|
||||
|
||||
PaError Pa_Initialize( void );
|
||||
|
||||
/*
|
||||
Pa_Terminate() is the library termination function - call this after
|
||||
using the library.
|
||||
*/
|
||||
|
||||
PaError Pa_Terminate( void );
|
||||
|
||||
/*
|
||||
Return host specific error.
|
||||
This can be called after receiving a paHostError.
|
||||
*/
|
||||
long Pa_GetHostError( void );
|
||||
|
||||
/*
|
||||
Translate the error number into a human readable message.
|
||||
*/
|
||||
const char *Pa_GetErrorText( PaError errnum );
|
||||
|
||||
/*
|
||||
Sample formats
|
||||
|
||||
These are formats used to pass sound data between the callback and the
|
||||
stream. Each device has a "native" format which may be used when optimum
|
||||
efficiency or control over conversion is required.
|
||||
|
||||
Formats marked "always available" are supported (emulated) by all devices.
|
||||
|
||||
The floating point representation uses +1.0 and -1.0 as the respective
|
||||
maximum and minimum.
|
||||
|
||||
*/
|
||||
|
||||
typedef unsigned long PaSampleFormat;
|
||||
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
|
||||
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
|
||||
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
|
||||
#define paInt24 ((PaSampleFormat) (1<<3))
|
||||
#define paPackedInt24 ((PaSampleFormat) (1<<4))
|
||||
#define paInt8 ((PaSampleFormat) (1<<5))
|
||||
#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
|
||||
#define paCustomFormat ((PaSampleFormat) (1<<16))
|
||||
|
||||
/*
|
||||
Device enumeration mechanism.
|
||||
|
||||
Device ids range from 0 to Pa_CountDevices()-1.
|
||||
|
||||
Devices may support input, output or both. Device 0 is always the "default"
|
||||
device and should support at least stereo in and out if that is available
|
||||
on the taget platform _even_ if this involves kludging an input/output
|
||||
device on platforms that usually separate input from output. Other platform
|
||||
specific devices are specified by positive device ids.
|
||||
*/
|
||||
|
||||
typedef int PaDeviceID;
|
||||
#define paNoDevice -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int structVersion;
|
||||
const char *name;
|
||||
int maxInputChannels;
|
||||
int maxOutputChannels;
|
||||
/* Number of discrete rates, or -1 if range supported. */
|
||||
int numSampleRates;
|
||||
/* Array of supported sample rates, or {min,max} if range supported. */
|
||||
const double *sampleRates;
|
||||
PaSampleFormat nativeSampleFormats;
|
||||
}
|
||||
PaDeviceInfo;
|
||||
|
||||
|
||||
int Pa_CountDevices();
|
||||
/*
|
||||
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
|
||||
|
||||
Return the default device ID or paNoDevice if there is no devices.
|
||||
The result can be passed to Pa_OpenStream().
|
||||
|
||||
On the PC, the user can specify a default device by
|
||||
setting an environment variable. For example, to use device #1.
|
||||
|
||||
set PA_RECOMMENDED_OUTPUT_DEVICE=1
|
||||
|
||||
The user should first determine the available device ID by using
|
||||
the supplied application "pa_devs".
|
||||
*/
|
||||
PaDeviceID Pa_GetDefaultInputDeviceID( void );
|
||||
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
|
||||
|
||||
/*
|
||||
PaTimestamp is used to represent a continuous sample clock with arbitrary
|
||||
start time useful for syncronisation. The type is used in the outTime
|
||||
argument to the callback function and the result of Pa_StreamTime()
|
||||
*/
|
||||
|
||||
typedef double PaTimestamp;
|
||||
|
||||
/*
|
||||
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
|
||||
referring to the device specified by id.
|
||||
If id is out of range the function returns NULL.
|
||||
|
||||
The returned structure is owned by the PortAudio implementation and must
|
||||
not be manipulated or freed. The pointer is only guaranteed to be valid
|
||||
between calls to Pa_Initialize() and Pa_Terminate().
|
||||
*/
|
||||
|
||||
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID devID );
|
||||
|
||||
/*
|
||||
PortAudioCallback is implemented by clients of the portable audio api.
|
||||
|
||||
inputBuffer and outputBuffer are arrays of interleaved samples,
|
||||
the format, packing and number of channels used by the buffers are
|
||||
determined by parameters to Pa_OpenStream() (see below).
|
||||
|
||||
framesPerBuffer is the number of sample frames to be processed by the callback.
|
||||
|
||||
outTime is the time in samples when the buffer(s) processed by
|
||||
this callback will begin being played at the audio output.
|
||||
See also Pa_StreamTime()
|
||||
|
||||
userData is the value of a user supplied pointer passed to Pa_OpenStream()
|
||||
intended for storing synthesis data etc.
|
||||
|
||||
return value:
|
||||
The callback can return a nonzero value to stop the stream. This may be
|
||||
useful in applications such as soundfile players where a specific duration
|
||||
of output is required. However, it is not necessary to utilise this mechanism
|
||||
as StopStream() will also terminate the stream. A callback returning a
|
||||
nonzero value must fill the entire outputBuffer.
|
||||
|
||||
NOTE: None of the other stream functions may be called from within the
|
||||
callback function except for Pa_GetCPULoad().
|
||||
|
||||
*/
|
||||
|
||||
typedef int (PortAudioCallback)(
|
||||
void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
|
||||
|
||||
/*
|
||||
Stream flags
|
||||
|
||||
These flags may be supplied (ored together) in the streamFlags argument to
|
||||
the Pa_OpenStream() function.
|
||||
|
||||
[ suggestions? ]
|
||||
*/
|
||||
|
||||
#define paNoFlag (0)
|
||||
#define paClipOff (1<<0) /* disable defult clipping of out of range samples */
|
||||
#define paDitherOff (1<<1) /* disable default dithering */
|
||||
#define paPlatformSpecificFlags (0x00010000)
|
||||
typedef unsigned long PaStreamFlags;
|
||||
|
||||
/*
|
||||
A single PortAudioStream provides multiple channels of real-time
|
||||
input and output audio streaming to a client application.
|
||||
Pointers to PortAudioStream objects are passed between PortAudio functions.
|
||||
*/
|
||||
|
||||
typedef void PortAudioStream;
|
||||
#define PaStream PortAudioStream
|
||||
|
||||
/*
|
||||
Pa_OpenStream() opens a stream for either input, output or both.
|
||||
|
||||
stream is the address of a PortAudioStream pointer which will receive
|
||||
a pointer to the newly opened stream.
|
||||
|
||||
inputDevice is the id of the device used for input (see PaDeviceID above.)
|
||||
inputDevice may be paNoDevice to indicate that an input device is not required.
|
||||
|
||||
numInputChannels is the number of channels of sound to be delivered to the
|
||||
callback. It can range from 1 to the value of maxInputChannels in the
|
||||
device input record for the device specified in the inputDevice parameter.
|
||||
If inputDevice is paNoDevice numInputChannels is ignored.
|
||||
|
||||
inputSampleFormat is the format of inputBuffer provided to the callback
|
||||
function. inputSampleFormat may be any of the formats described by the
|
||||
PaSampleFormat enumeration (see above). PortAudio guarantees support for
|
||||
the sound devices native formats (nativeSampleFormats in the device info
|
||||
record) and additionally 16 and 32 bit integer and 32 bit floating point
|
||||
formats. Support for other formats is implementation defined.
|
||||
|
||||
inputDriverInfo is a pointer to an optional driver specific data structure
|
||||
containing additional information for device setup or stream processing.
|
||||
inputDriverInfo is never required for correct operation. If not used
|
||||
inputDriverInfo should be NULL.
|
||||
|
||||
outputDevice is the id of the device used for output (see PaDeviceID above.)
|
||||
outputDevice may be paNoDevice to indicate that an output device is not required.
|
||||
|
||||
numOutputChannels is the number of channels of sound to be supplied by the
|
||||
callback. See the definition of numInputChannels above for more details.
|
||||
|
||||
outputSampleFormat is the sample format of the outputBuffer filled by the
|
||||
callback function. See the definition of inputSampleFormat above for more
|
||||
details.
|
||||
|
||||
outputDriverInfo is a pointer to an optional driver specific data structure
|
||||
containing additional information for device setup or stream processing.
|
||||
outputDriverInfo is never required for correct operation. If not used
|
||||
outputDriverInfo should be NULL.
|
||||
|
||||
sampleRate is the desired sampleRate for input and output
|
||||
|
||||
framesPerBuffer is the length in sample frames of all internal sample buffers
|
||||
used for communication with platform specific audio routines. Wherever
|
||||
possible this corresponds to the framesPerBuffer parameter passed to the
|
||||
callback function.
|
||||
|
||||
numberOfBuffers is the number of buffers used for
|
||||
multibuffered communication with the platform specific audio
|
||||
routines. If you pass zero, then an optimum value will be
|
||||
chosen for you internally. This parameter is provided only
|
||||
as a guide - and does not imply that an implementation must
|
||||
use multibuffered i/o when reliable double buffering is
|
||||
available (such as SndPlayDoubleBuffer() on the Macintosh.)
|
||||
|
||||
streamFlags may contain a combination of flags ORed together.
|
||||
These flags modify the behavior of the
|
||||
streaming process. Some flags may only be relevant to certain buffer formats.
|
||||
|
||||
callback is a pointer to a client supplied function that is responsible
|
||||
for processing and filling input and output buffers (see above for details.)
|
||||
|
||||
userData is a client supplied pointer which is passed to the callback
|
||||
function. It could for example, contain a pointer to instance data necessary
|
||||
for processing the audio buffers.
|
||||
|
||||
return value:
|
||||
Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
|
||||
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
|
||||
If a call to Pa_OpenStream() fails a nonzero error code is returned (see
|
||||
PAError above) and the value of stream is invalid.
|
||||
|
||||
*/
|
||||
|
||||
PaError Pa_OpenStream( PortAudioStream** stream,
|
||||
PaDeviceID inputDevice,
|
||||
int numInputChannels,
|
||||
PaSampleFormat inputSampleFormat,
|
||||
void *inputDriverInfo,
|
||||
PaDeviceID outputDevice,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat outputSampleFormat,
|
||||
void *outputDriverInfo,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PaStreamFlags streamFlags,
|
||||
PortAudioCallback *callback,
|
||||
void *userData );
|
||||
|
||||
|
||||
/*
|
||||
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
|
||||
opens the default input and/or ouput devices. Most parameters have
|
||||
identical meaning to their Pa_OpenStream() counterparts, with the following
|
||||
exceptions:
|
||||
|
||||
If either numInputChannels or numOutputChannels is 0 the respective device
|
||||
is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
|
||||
|
||||
sampleFormat applies to both the input and output buffers.
|
||||
*/
|
||||
|
||||
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
|
||||
int numInputChannels,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat sampleFormat,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PortAudioCallback *callback,
|
||||
void *userData );
|
||||
|
||||
/*
|
||||
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
|
||||
*/
|
||||
|
||||
PaError Pa_CloseStream( PortAudioStream* );
|
||||
|
||||
/*
|
||||
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
|
||||
Pa_StopStream() waits until all pending audio buffers have been played.
|
||||
Pa_AbortStream() stops playing immediately without waiting for pending
|
||||
buffers to complete.
|
||||
*/
|
||||
|
||||
PaError Pa_StartStream( PortAudioStream *stream );
|
||||
|
||||
PaError Pa_StopStream( PortAudioStream *stream );
|
||||
|
||||
PaError Pa_AbortStream( PortAudioStream *stream );
|
||||
|
||||
/*
|
||||
Pa_StreamActive() returns one when the stream is playing audio,
|
||||
zero when not playing, or a negative error number if the
|
||||
stream is invalid.
|
||||
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
|
||||
but may also become inactive if the callback returns a non-zero value.
|
||||
In the latter case, the stream is considered inactive after the last
|
||||
buffer has finished playing.
|
||||
*/
|
||||
|
||||
PaError Pa_StreamActive( PortAudioStream *stream );
|
||||
|
||||
/*
|
||||
Pa_StreamTime() returns the current output time for the stream in samples.
|
||||
This time may be used as a time reference (for example syncronising audio to
|
||||
MIDI).
|
||||
*/
|
||||
|
||||
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
|
||||
|
||||
/*
|
||||
The "CPU Load" is a fraction of total CPU time consumed by the
|
||||
stream's audio processing.
|
||||
A value of 0.5 would imply that PortAudio and the sound generating
|
||||
callback was consuming roughly 50% of the available CPU time.
|
||||
This function may be called from the callback function or the application.
|
||||
*/
|
||||
double Pa_GetCPULoad( PortAudioStream* stream );
|
||||
|
||||
/*
|
||||
Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
|
||||
the current host based on minimum latency.
|
||||
On the PC, for the DirectSound implementation, latency can be optionally set
|
||||
by user by setting an environment variable.
|
||||
For example, to set latency to 200 msec, put:
|
||||
|
||||
set PA_MIN_LATENCY_MSEC=200
|
||||
|
||||
in the AUTOEXEC.BAT file and reboot.
|
||||
If the environment variable is not set, then the latency will be determined
|
||||
based on the OS. Windows NT has higher latency than Win95.
|
||||
*/
|
||||
|
||||
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
|
||||
|
||||
/*
|
||||
Sleep for at least 'msec' milliseconds.
|
||||
You may sleep longer than the requested time so don't rely
|
||||
on this for accurate musical timing.
|
||||
*/
|
||||
void Pa_Sleep( long msec );
|
||||
|
||||
/*
|
||||
Return size in bytes of a single sample in a given PaSampleFormat
|
||||
or paSampleFormatNotSupported.
|
||||
*/
|
||||
PaError Pa_GetSampleSize( PaSampleFormat format );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PORT_AUDIO_H */
|
||||
184
pa_dll_switch/PaDllEntry.h
Normal file
184
pa_dll_switch/PaDllEntry.h
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
|
||||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PortAudio DLL Header File
|
||||
* Latest version available at: 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// changed by zplane.developement in order to generate a DLL
|
||||
|
||||
#ifndef __PADLLENTRY_HEADER_INCLUDED__
|
||||
|
||||
#define __PADLLENTRY_HEADER_INCLUDED__
|
||||
|
||||
typedef int PaError;
|
||||
typedef enum {
|
||||
paNoError = 0,
|
||||
|
||||
paHostError = -10000,
|
||||
paInvalidChannelCount,
|
||||
paInvalidSampleRate,
|
||||
paInvalidDeviceId,
|
||||
paInvalidFlag,
|
||||
paSampleFormatNotSupported,
|
||||
paBadIODeviceCombination,
|
||||
paInsufficientMemory,
|
||||
paBufferTooBig,
|
||||
paBufferTooSmall,
|
||||
paNullCallback,
|
||||
paBadStreamPtr,
|
||||
paTimedOut,
|
||||
paInternalError
|
||||
} PaErrorNum;
|
||||
|
||||
typedef unsigned long PaSampleFormat;
|
||||
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
|
||||
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
|
||||
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
|
||||
#define paInt24 ((PaSampleFormat) (1<<3))
|
||||
#define paPackedInt24 ((PaSampleFormat) (1<<4))
|
||||
#define paInt8 ((PaSampleFormat) (1<<5))
|
||||
#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
|
||||
#define paCustomFormat ((PaSampleFormat) (1<<16))
|
||||
|
||||
|
||||
typedef int PaDeviceID;
|
||||
#define paNoDevice -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int structVersion;
|
||||
const char *name;
|
||||
int maxInputChannels;
|
||||
int maxOutputChannels;
|
||||
/* Number of discrete rates, or -1 if range supported. */
|
||||
int numSampleRates;
|
||||
/* Array of supported sample rates, or {min,max} if range supported. */
|
||||
const double *sampleRates;
|
||||
PaSampleFormat nativeSampleFormats;
|
||||
}
|
||||
PaDeviceInfo;
|
||||
|
||||
|
||||
typedef double PaTimestamp;
|
||||
|
||||
|
||||
typedef int (PortAudioCallback)(
|
||||
void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
|
||||
|
||||
#define paNoFlag (0)
|
||||
#define paClipOff (1<<0) /* disable default clipping of out of range samples */
|
||||
#define paDitherOff (1<<1) /* disable default dithering */
|
||||
#define paPlatformSpecificFlags (0x00010000)
|
||||
typedef unsigned long PaStreamFlags;
|
||||
|
||||
typedef void PortAudioStream;
|
||||
#define PaStream PortAudioStream
|
||||
|
||||
extern PaError (__cdecl* Pa_Initialize)( void );
|
||||
|
||||
|
||||
|
||||
extern PaError (__cdecl* Pa_Terminate)( void );
|
||||
|
||||
|
||||
extern long (__cdecl* Pa_GetHostError)( void );
|
||||
|
||||
|
||||
extern const char* (__cdecl* Pa_GetErrorText)( PaError );
|
||||
|
||||
|
||||
|
||||
extern int (__cdecl* Pa_CountDevices)(void);
|
||||
|
||||
extern PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
|
||||
|
||||
extern PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
|
||||
|
||||
|
||||
extern const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
|
||||
|
||||
|
||||
|
||||
extern PaError (__cdecl* Pa_OpenStream)(
|
||||
PortAudioStream ** ,
|
||||
PaDeviceID ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
void *,
|
||||
PaDeviceID ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
void *,
|
||||
double ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
PortAudioCallback *,
|
||||
void * );
|
||||
|
||||
|
||||
|
||||
extern PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
|
||||
int numInputChannels,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat sampleFormat,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PortAudioCallback *callback,
|
||||
void *userData );
|
||||
|
||||
|
||||
extern PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
|
||||
|
||||
|
||||
extern PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
|
||||
|
||||
extern PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
|
||||
|
||||
extern PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
|
||||
|
||||
extern PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
|
||||
|
||||
extern PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
|
||||
|
||||
extern double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
|
||||
|
||||
extern int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
|
||||
|
||||
extern void (__cdecl* Pa_Sleep)( long msec );
|
||||
|
||||
extern PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
|
||||
|
||||
#endif // __PADLLENTRY_HEADER_INCLUDED__
|
||||
|
||||
BIN
pa_dll_switch/letter_from_tim_010817.txt
Normal file
BIN
pa_dll_switch/letter_from_tim_010817.txt
Normal file
Binary file not shown.
203
pa_dll_switch/loadPA_DLL.cpp
Normal file
203
pa_dll_switch/loadPA_DLL.cpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
HINSTANCE pPaDll;
|
||||
|
||||
/*
|
||||
the function pointers to the PortAudio DLLs
|
||||
*/
|
||||
|
||||
PaError (__cdecl* Pa_Initialize)( void );
|
||||
|
||||
|
||||
|
||||
PaError (__cdecl* Pa_Terminate)( void );
|
||||
|
||||
|
||||
long (__cdecl* Pa_GetHostError)( void );
|
||||
|
||||
|
||||
const char* (__cdecl* Pa_GetErrorText)( PaError );
|
||||
|
||||
|
||||
int (__cdecl* Pa_CountDevices)(void);
|
||||
|
||||
PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
|
||||
|
||||
PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
|
||||
|
||||
|
||||
const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
|
||||
|
||||
|
||||
|
||||
PaError (__cdecl* Pa_OpenStream)(
|
||||
PortAudioStream ** ,
|
||||
PaDeviceID ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
void *,
|
||||
PaDeviceID ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
void *,
|
||||
double ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
PortAudioCallback *,
|
||||
void * );
|
||||
|
||||
|
||||
|
||||
PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
|
||||
int numInputChannels,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat sampleFormat,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PortAudioCallback *callback,
|
||||
void *userData );
|
||||
|
||||
|
||||
PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
|
||||
|
||||
|
||||
PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
|
||||
|
||||
PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
|
||||
|
||||
PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
|
||||
|
||||
PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
|
||||
|
||||
PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
|
||||
|
||||
double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
|
||||
|
||||
int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
|
||||
|
||||
void (__cdecl* Pa_Sleep)( long msec );
|
||||
|
||||
PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
...
|
||||
|
||||
ZERROR AudioEngine::DirectXSupport(ZBOOL bSupDX)
|
||||
{
|
||||
if (bSupDX)
|
||||
if (CheckForDirectXSupport())
|
||||
bSupportDirectX = _TRUE;
|
||||
else
|
||||
return _NO_SOUND;
|
||||
else
|
||||
bSupportDirectX = _FALSE;
|
||||
return _NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ZBOOL AudioEngine::CheckForDirectXSupport()
|
||||
{
|
||||
HMODULE pTestDXLib;
|
||||
FARPROC pFunctionality;
|
||||
|
||||
pTestDXLib=LoadLibrary("DSOUND");
|
||||
if (pTestDXLib!=NULL) // check if there is a DirectSound
|
||||
{
|
||||
pFunctionality = GetProcAddress(pTestDXLib, (char*) 7);
|
||||
if (pFunctionality!=NULL)
|
||||
{
|
||||
FreeLibrary(pTestDXLib);
|
||||
return _TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeLibrary(pTestDXLib);
|
||||
return _FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
return _FALSE;
|
||||
}
|
||||
|
||||
|
||||
ZERROR AudioEngine::LoadPALib()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (bSupportDirectX)
|
||||
pPaDll = LoadLibrary("PA_DXD");
|
||||
else
|
||||
pPaDll = LoadLibrary("PA_MMED");
|
||||
#else
|
||||
if (bSupportDirectX)
|
||||
pPaDll = LoadLibrary("PA_DX");
|
||||
else
|
||||
pPaDll = LoadLibrary("PA_MME");
|
||||
#endif
|
||||
if (pPaDll!=NULL)
|
||||
{
|
||||
|
||||
Pa_Initialize = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Initialize");
|
||||
Pa_Terminate = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Terminate");
|
||||
Pa_GetHostError = (long (__cdecl* )( void )) GetProcAddress(pPaDll,"Pa_GetHostError");
|
||||
Pa_GetErrorText = (const char* (__cdecl* )( PaError )) GetProcAddress(pPaDll,"Pa_GetErrorText");
|
||||
Pa_CountDevices = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_CountDevices");
|
||||
Pa_GetDefaultInputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultInputDeviceID");
|
||||
Pa_GetDefaultOutputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultOutputDeviceID");
|
||||
Pa_GetDeviceInfo = (const PaDeviceInfo* (__cdecl* )( PaDeviceID)) GetProcAddress(pPaDll,"Pa_GetDeviceInfo");
|
||||
Pa_OpenStream = ( PaError (__cdecl* )(
|
||||
PortAudioStream ** ,
|
||||
PaDeviceID ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
void *,
|
||||
PaDeviceID ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
void *,
|
||||
double ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
PortAudioCallback *,
|
||||
void * )) GetProcAddress(pPaDll,"Pa_OpenStream");
|
||||
|
||||
Pa_OpenDefaultStream = (PaError (__cdecl* )( PortAudioStream** ,
|
||||
int ,
|
||||
int ,
|
||||
PaSampleFormat ,
|
||||
double ,
|
||||
unsigned long ,
|
||||
unsigned long ,
|
||||
PortAudioCallback *,
|
||||
void * )) GetProcAddress(pPaDll,"Pa_OpenDefaultStream");
|
||||
Pa_CloseStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_CloseStream");
|
||||
Pa_StartStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StartStream");
|
||||
Pa_StopStream = (PaError (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_StopStream");
|
||||
Pa_AbortStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_AbortStream");
|
||||
Pa_StreamActive = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StreamActive");
|
||||
Pa_StreamTime = (PaTimestamp (__cdecl* )( PortAudioStream *))GetProcAddress(pPaDll,"Pa_StreamTime");
|
||||
Pa_GetCPULoad = (double (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_GetCPULoad");
|
||||
Pa_GetMinNumBuffers = (int (__cdecl* )( int , double )) GetProcAddress(pPaDll,"Pa_GetMinNumBuffers");
|
||||
Pa_Sleep = (void (__cdecl* )( long )) GetProcAddress(pPaDll,"Pa_Sleep");
|
||||
Pa_GetSampleSize = (PaError (__cdecl* )( PaSampleFormat )) GetProcAddress(pPaDll,"Pa_GetSampleSize");
|
||||
|
||||
return _NO_ERROR;
|
||||
}
|
||||
else
|
||||
return _DLL_NOT_FOUND;
|
||||
}
|
||||
|
||||
ZERROR AudioEngine::UnLoadPALib()
|
||||
{
|
||||
if (pPaDll!=NULL)
|
||||
FreeLibrary(pPaDll);
|
||||
return _NO_ERROR;
|
||||
}
|
||||
|
||||
...
|
||||
827
pa_dll_switch/pa_lib.c
Normal file
827
pa_dll_switch/pa_lib.c
Normal file
|
|
@ -0,0 +1,827 @@
|
|||
/*
|
||||
* Portable Audio I/O Library
|
||||
* Host Independant Layer
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2000 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:
|
||||
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
|
||||
#ifdef _WIN32
|
||||
#ifndef __MWERKS__
|
||||
#include <memory.h>
|
||||
#endif /* __MWERKS__ */
|
||||
#else /* !_WIN32 */
|
||||
#include <memory.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_host.h"
|
||||
#include "pa_trace.h"
|
||||
|
||||
/* The reason we might NOT want to validate the rate before opening the stream
|
||||
* is because many DirectSound drivers lie about the rates they actually support.
|
||||
*/
|
||||
#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
|
||||
|
||||
/*
|
||||
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
|
||||
*/
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0)
|
||||
#define TRUE (!FALSE)
|
||||
#endif
|
||||
|
||||
#define PRINT(x) { printf x; fflush(stdout); }
|
||||
#define ERR_RPT(x) PRINT(x)
|
||||
#define DBUG(x) /* PRINT(x) */
|
||||
#define DBUGX(x) /* PRINT(x) */
|
||||
|
||||
static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
|
||||
|
||||
static PaError Pa_KillStream( PortAudioStream *stream, int abort );
|
||||
|
||||
/***********************************************************************/
|
||||
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
|
||||
{
|
||||
double err, minErr = allowableError;
|
||||
int i, bestFit = -1;
|
||||
|
||||
for( i=0; i<numRates; i++ )
|
||||
{
|
||||
err = fabs( frameRate - rateTable[i] );
|
||||
if( err < minErr )
|
||||
{
|
||||
minErr = err;
|
||||
bestFit = i;
|
||||
}
|
||||
}
|
||||
return bestFit;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
** Make sure sample rate is legal and also convert to enumeration for driver.
|
||||
*/
|
||||
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
|
||||
double *closestFrameRatePtr )
|
||||
{
|
||||
long bestRateIndex;
|
||||
const PaDeviceInfo *pdi;
|
||||
pdi = Pa_GetDeviceInfo( id );
|
||||
if( pdi == NULL ) return paInvalidDeviceId;
|
||||
|
||||
if( pdi->numSampleRates == -1 )
|
||||
{
|
||||
/* Is it out of range? */
|
||||
if( (requestedFrameRate < pdi->sampleRates[0]) ||
|
||||
(requestedFrameRate > pdi->sampleRates[1]) )
|
||||
{
|
||||
return paInvalidSampleRate;
|
||||
}
|
||||
|
||||
*closestFrameRatePtr = requestedFrameRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
|
||||
if( bestRateIndex < 0 ) return paInvalidSampleRate;
|
||||
*closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
|
||||
}
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_OpenStream(
|
||||
PortAudioStream** streamPtrPtr,
|
||||
PaDeviceID inputDeviceID,
|
||||
int numInputChannels,
|
||||
PaSampleFormat inputSampleFormat,
|
||||
void *inputDriverInfo,
|
||||
PaDeviceID outputDeviceID,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat outputSampleFormat,
|
||||
void *outputDriverInfo,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
unsigned long streamFlags,
|
||||
PortAudioCallback *callback,
|
||||
void *userData )
|
||||
{
|
||||
internalPortAudioStream *past = NULL;
|
||||
PaError result = paNoError;
|
||||
int bitsPerInputSample;
|
||||
int bitsPerOutputSample;
|
||||
/* Print passed parameters. */
|
||||
DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
|
||||
streamPtrPtr, inputDeviceID, numInputChannels,
|
||||
inputSampleFormat, inputDriverInfo ));
|
||||
DBUG((" %d, %d, %d, %p, /* output */\n",
|
||||
outputDeviceID, numOutputChannels,
|
||||
outputSampleFormat, outputDriverInfo ));
|
||||
DBUG((" %g, %d, %d, 0x%x, , %p )\n",
|
||||
sampleRate, framesPerBuffer, numberOfBuffers,
|
||||
streamFlags, userData ));
|
||||
|
||||
/* Check for parameter errors. */
|
||||
if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
|
||||
if( streamPtrPtr == NULL ) return paBadStreamPtr;
|
||||
if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
|
||||
if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
|
||||
if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
|
||||
if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId;
|
||||
if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
|
||||
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
if( inputDeviceID >= 0 )
|
||||
{
|
||||
PaError size = Pa_GetSampleSize( inputSampleFormat );
|
||||
if( size < 0 ) return size;
|
||||
bitsPerInputSample = 8 * size;
|
||||
if( (numInputChannels <= 0) ) return paInvalidChannelCount;
|
||||
}
|
||||
#else
|
||||
if( inputDeviceID >= 0 )
|
||||
{
|
||||
return paInvalidChannelCount;
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
else
|
||||
{
|
||||
if( numInputChannels > 0 ) return paInvalidChannelCount;
|
||||
bitsPerInputSample = 0;
|
||||
}
|
||||
|
||||
if( outputDeviceID >= 0 )
|
||||
{
|
||||
PaError size = Pa_GetSampleSize( outputSampleFormat );
|
||||
if( size < 0 ) return size;
|
||||
bitsPerOutputSample = 8 * size;
|
||||
if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( numOutputChannels > 0 ) return paInvalidChannelCount;
|
||||
bitsPerOutputSample = 0;
|
||||
}
|
||||
|
||||
if( callback == NULL ) return paNullCallback;
|
||||
|
||||
/* Allocate and clear stream structure. */
|
||||
past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
|
||||
if( past == NULL ) return paInsufficientMemory;
|
||||
memset( past, 0, sizeof(internalPortAudioStream) );
|
||||
AddTraceMessage("Pa_OpenStream: past", (long) past );
|
||||
|
||||
past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
|
||||
past->past_FramesPerUserBuffer = framesPerBuffer;
|
||||
past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
|
||||
past->past_Callback = callback;
|
||||
past->past_UserData = userData;
|
||||
past->past_OutputSampleFormat = outputSampleFormat;
|
||||
past->past_InputSampleFormat = inputSampleFormat;
|
||||
past->past_OutputDeviceID = outputDeviceID;
|
||||
past->past_InputDeviceID = inputDeviceID;
|
||||
past->past_NumInputChannels = numInputChannels;
|
||||
past->past_NumOutputChannels = numOutputChannels;
|
||||
past->past_Flags = streamFlags;
|
||||
|
||||
/* Check for absurd sample rates. */
|
||||
if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
|
||||
{
|
||||
result = paInvalidSampleRate;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Allocate buffers that may be used for format conversion from user to native buffers. */
|
||||
if( numInputChannels > 0 )
|
||||
{
|
||||
|
||||
#if PA_VALIDATE_RATE
|
||||
result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
|
||||
if( result < 0 )
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
#else
|
||||
past->past_SampleRate = sampleRate;
|
||||
#endif
|
||||
/* Allocate single Input buffer. */
|
||||
past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
|
||||
past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
|
||||
if( past->past_InputBuffer == NULL )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
past->past_InputBuffer = NULL;
|
||||
}
|
||||
|
||||
/* Allocate single Output buffer. */
|
||||
if( numOutputChannels > 0 )
|
||||
{
|
||||
#if PA_VALIDATE_RATE
|
||||
result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
|
||||
if( result < 0 )
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
#else
|
||||
past->past_SampleRate = sampleRate;
|
||||
#endif
|
||||
past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
|
||||
past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
|
||||
if( past->past_OutputBuffer == NULL )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
past->past_OutputBuffer = NULL;
|
||||
}
|
||||
|
||||
result = PaHost_OpenStream( past );
|
||||
if( result < 0 ) goto cleanup;
|
||||
|
||||
*streamPtrPtr = (void *) past;
|
||||
|
||||
return result;
|
||||
|
||||
cleanup:
|
||||
if( past != NULL ) Pa_CloseStream( past );
|
||||
*streamPtrPtr = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
|
||||
int numInputChannels,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat sampleFormat,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PortAudioCallback *callback,
|
||||
void *userData )
|
||||
{
|
||||
return Pa_OpenStream(
|
||||
stream,
|
||||
((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
|
||||
numInputChannels, sampleFormat, NULL,
|
||||
((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
|
||||
numOutputChannels, sampleFormat, NULL,
|
||||
sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_CloseStream( PortAudioStream* stream)
|
||||
{
|
||||
PaError result;
|
||||
internalPortAudioStream *past;
|
||||
|
||||
DBUG(("Pa_CloseStream()\n"));
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
|
||||
Pa_AbortStream( past );
|
||||
result = PaHost_CloseStream( past );
|
||||
|
||||
if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
|
||||
if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
|
||||
PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_StartStream( PortAudioStream *stream )
|
||||
{
|
||||
PaError result = paHostError;
|
||||
internalPortAudioStream *past;
|
||||
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
|
||||
past->past_FrameCount = 0.0;
|
||||
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StartInput( past );
|
||||
DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
}
|
||||
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StartOutput( past );
|
||||
DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
}
|
||||
|
||||
result = PaHost_StartEngine( past );
|
||||
DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
|
||||
return paNoError;
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_StopStream( PortAudioStream *stream )
|
||||
{
|
||||
return Pa_KillStream( stream, 0 );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_AbortStream( PortAudioStream *stream )
|
||||
{
|
||||
return Pa_KillStream( stream, 1 );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
static PaError Pa_KillStream( PortAudioStream *stream, int abort )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
internalPortAudioStream *past;
|
||||
|
||||
DBUG(("Pa_StopStream().\n"));
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
|
||||
if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
|
||||
{
|
||||
result = PaHost_StopEngine( past, abort );
|
||||
DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
|
||||
if( result < 0 ) goto error;
|
||||
}
|
||||
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StopInput( past, abort );
|
||||
DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
|
||||
if( result != paNoError ) goto error;
|
||||
}
|
||||
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
result = PaHost_StopOutput( past, abort );
|
||||
DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
|
||||
if( result != paNoError ) goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
past->past_Usage = 0;
|
||||
past->past_IfLastExitValid = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_StreamActive( PortAudioStream *stream )
|
||||
{
|
||||
internalPortAudioStream *past;
|
||||
if( stream == NULL ) return paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
return PaHost_StreamActive( past );
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API const char *Pa_GetErrorText( PaError errnum )
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
switch(errnum)
|
||||
{
|
||||
case paNoError: msg = "Success"; break;
|
||||
case paHostError: msg = "Host error."; break;
|
||||
case paInvalidChannelCount: msg = "Invalid number of channels."; break;
|
||||
case paInvalidSampleRate: msg = "Invalid sample rate."; break;
|
||||
case paInvalidDeviceId: msg = "Invalid device ID."; break;
|
||||
case paInvalidFlag: msg = "Invalid flag."; break;
|
||||
case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
|
||||
case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
|
||||
case paInsufficientMemory: msg = "Insufficient memory."; break;
|
||||
case paBufferTooBig: msg = "Buffer too big."; break;
|
||||
case paBufferTooSmall: msg = "Buffer too small."; break;
|
||||
case paNullCallback: msg = "No callback routine specified."; break;
|
||||
case paBadStreamPtr: msg = "Invalid stream pointer."; break;
|
||||
case paTimedOut : msg = "Wait Timed Out."; break;
|
||||
case paInternalError: msg = "Internal PortAudio Error."; break;
|
||||
default: msg = "Illegal error number."; break;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
Get CPU Load as a fraction of total CPU time.
|
||||
A value of 0.5 would imply that PortAudio and the sound generating
|
||||
callback was consuming roughly 50% of the available CPU time.
|
||||
The amount may vary depending on CPU load.
|
||||
This function may be called from the callback function.
|
||||
*/
|
||||
DLL_API double Pa_GetCPULoad( PortAudioStream* stream)
|
||||
{
|
||||
internalPortAudioStream *past;
|
||||
if( stream == NULL ) return (double) paBadStreamPtr;
|
||||
past = (internalPortAudioStream *) stream;
|
||||
return past->past_Usage;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
** Calculate 2 LSB dither signal with a triangular distribution.
|
||||
** Ranged properly for adding to a 32 bit integer prior to >>15.
|
||||
*/
|
||||
#define DITHER_BITS (15)
|
||||
#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
|
||||
static long Pa_TriangularDither( void )
|
||||
{
|
||||
static unsigned long previous = 0;
|
||||
static unsigned long randSeed1 = 22222;
|
||||
static unsigned long randSeed2 = 5555555;
|
||||
long current, highPass;
|
||||
/* Generate two random numbers. */
|
||||
randSeed1 = (randSeed1 * 196314165) + 907633515;
|
||||
randSeed2 = (randSeed2 * 196314165) + 907633515;
|
||||
/* Generate triangular distribution about 0. */
|
||||
current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
|
||||
/* High pass filter to reduce audibility. */
|
||||
highPass = current - previous;
|
||||
previous = current;
|
||||
return highPass;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Called by host code.
|
||||
** Convert input from Int16, call user code, then convert output
|
||||
** to Int16 format for native use.
|
||||
** Assumes host native format is paInt16.
|
||||
** Returns result from user callback.
|
||||
*/
|
||||
long Pa_CallConvertInt16( internalPortAudioStream *past,
|
||||
short *nativeInputBuffer,
|
||||
short *nativeOutputBuffer )
|
||||
{
|
||||
long temp;
|
||||
long bytesEmpty = 0;
|
||||
long bytesFilled = 0;
|
||||
int userResult;
|
||||
unsigned int i;
|
||||
void *inputBuffer = NULL;
|
||||
void *outputBuffer = NULL;
|
||||
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
/* Get native data from DirectSound. */
|
||||
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
||||
{
|
||||
/* Convert from native format to PA format. */
|
||||
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
|
||||
switch(past->past_InputSampleFormat)
|
||||
{
|
||||
|
||||
case paFloat32:
|
||||
{
|
||||
float *inBufPtr = (float *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt32:
|
||||
{
|
||||
/* Convert 16 bit data to 32 bit integers */
|
||||
int *inBufPtr = (int *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = nativeInputBuffer[i] << 16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt16:
|
||||
{
|
||||
/* Already in correct format so don't copy. */
|
||||
inputBuffer = nativeInputBuffer;
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt8:
|
||||
{
|
||||
/* Convert 16 bit data to 8 bit chars */
|
||||
char *inBufPtr = (char *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
temp = nativeInputBuffer[i];
|
||||
temp += Pa_TriangularDither() >> 7;
|
||||
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
inBufPtr[i] = (char)(temp >> 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paUInt8:
|
||||
{
|
||||
/* Convert 16 bit data to 8 bit unsigned chars */
|
||||
unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
|
||||
inputBuffer = past->past_InputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If you dither then you have to clip because dithering could push the signal out of range! */
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
temp = nativeInputBuffer[i];
|
||||
temp += Pa_TriangularDither() >> 7;
|
||||
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
inBufPtr[i] = (unsigned char)(temp + 0x80);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
|
||||
/* Are we doing output time? */
|
||||
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
||||
{
|
||||
/* May already be in native format so just write directly to native buffer. */
|
||||
outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
|
||||
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 );
|
||||
|
||||
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
|
||||
|
||||
/* Convert to native format if necessary. */
|
||||
if( outputBuffer != NULL )
|
||||
{
|
||||
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
|
||||
switch(past->past_OutputSampleFormat)
|
||||
{
|
||||
case paFloat32:
|
||||
{
|
||||
float *outBufPtr = (float *) past->past_OutputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
if( past->past_Flags & paClipOff ) /* NOTHING */
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
|
||||
}
|
||||
}
|
||||
else /* CLIP */
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
temp = (long)(outBufPtr[i] * 32767.0f);
|
||||
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If you dither then you have to clip because dithering could push the signal out of range! */
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
float dither = Pa_TriangularDither()*DITHER_SCALE;
|
||||
float dithered = (outBufPtr[i] * (32767.0f)) + dither;
|
||||
temp = (long) (dithered);
|
||||
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt32:
|
||||
{
|
||||
int *outBufPtr = (int *) past->past_OutputBuffer;
|
||||
if( past->past_Flags & paDitherOff )
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
/* Shift one bit down before dithering so that we have room for overflow from add. */
|
||||
temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
|
||||
temp = temp >> 15;
|
||||
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paInt8:
|
||||
{
|
||||
char *outBufPtr = (char *) past->past_OutputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case paUInt8:
|
||||
{
|
||||
unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
|
||||
for( i=0; i<samplesPerBuffer; i++ )
|
||||
{
|
||||
*nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return userResult;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Called by host code.
|
||||
** Convert input from Float32, call user code, then convert output
|
||||
** to Float32 format for native use.
|
||||
** Assumes host native format is Float32.
|
||||
** Returns result from user callback.
|
||||
** FIXME - Unimplemented for formats other than paFloat32!!!!
|
||||
*/
|
||||
long Pa_CallConvertFloat32( internalPortAudioStream *past,
|
||||
float *nativeInputBuffer,
|
||||
float *nativeOutputBuffer )
|
||||
{
|
||||
long bytesEmpty = 0;
|
||||
long bytesFilled = 0;
|
||||
int userResult;
|
||||
void *inputBuffer = NULL;
|
||||
void *outputBuffer = NULL;
|
||||
|
||||
/* Get native data from DirectSound. */
|
||||
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
||||
{
|
||||
inputBuffer = nativeInputBuffer; // FIXME
|
||||
}
|
||||
|
||||
/* Are we doing output time? */
|
||||
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
||||
{
|
||||
/* May already be in native format so just write directly to native buffer. */
|
||||
outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
|
||||
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 );
|
||||
|
||||
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
|
||||
|
||||
/* Convert to native format if necessary. */ // FIXME
|
||||
return userResult;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_Initialize( void )
|
||||
{
|
||||
if( gInitCount++ > 0 ) return paNoError;
|
||||
ResetTraceMessages();
|
||||
return PaHost_Init();
|
||||
}
|
||||
|
||||
DLL_API PaError Pa_Terminate( void )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
|
||||
if( gInitCount == 0 ) return paNoError;
|
||||
else if( --gInitCount == 0 )
|
||||
{
|
||||
result = PaHost_Term();
|
||||
DumpTraceMessages();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
DLL_API PaError Pa_GetSampleSize( PaSampleFormat format )
|
||||
{
|
||||
int size;
|
||||
switch(format )
|
||||
{
|
||||
|
||||
case paUInt8:
|
||||
case paInt8:
|
||||
size = 1;
|
||||
break;
|
||||
|
||||
case paInt16:
|
||||
size = 2;
|
||||
break;
|
||||
|
||||
case paPackedInt24:
|
||||
size = 3;
|
||||
break;
|
||||
|
||||
case paFloat32:
|
||||
case paInt32:
|
||||
case paInt24:
|
||||
size = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
size = paSampleFormatNotSupported;
|
||||
break;
|
||||
}
|
||||
return (PaError) size;
|
||||
}
|
||||
|
||||
|
||||
439
pa_dll_switch/portaudio.h
Normal file
439
pa_dll_switch/portaudio.h
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
#ifndef PORT_AUDIO_H
|
||||
#define PORT_AUDIO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PortAudio API Header File
|
||||
* Latest version available at: 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// added by zplane.developement in order to generate a DLL
|
||||
|
||||
#if defined(PA_MME_EXPORTS) || defined(PA_DX_EXPORTS)
|
||||
#define DLL_API __declspec( dllexport )
|
||||
#elif defined(_LIB) || defined(_STATIC_LINK) || defined(_STATIC_APP)
|
||||
#define DLL_API
|
||||
#else
|
||||
#define DLL_API __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
|
||||
typedef int PaError;
|
||||
typedef enum {
|
||||
paNoError = 0,
|
||||
|
||||
paHostError = -10000,
|
||||
paInvalidChannelCount,
|
||||
paInvalidSampleRate,
|
||||
paInvalidDeviceId,
|
||||
paInvalidFlag,
|
||||
paSampleFormatNotSupported,
|
||||
paBadIODeviceCombination,
|
||||
paInsufficientMemory,
|
||||
paBufferTooBig,
|
||||
paBufferTooSmall,
|
||||
paNullCallback,
|
||||
paBadStreamPtr,
|
||||
paTimedOut,
|
||||
paInternalError
|
||||
} PaErrorNum;
|
||||
|
||||
/*
|
||||
Pa_Initialize() is the library initialisation function - call this before
|
||||
using the library.
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_Initialize( void );
|
||||
|
||||
/*
|
||||
Pa_Terminate() is the library termination function - call this after
|
||||
using the library.
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_Terminate( void );
|
||||
|
||||
/*
|
||||
Return host specific error.
|
||||
This can be called after receiving a paHostError.
|
||||
*/
|
||||
DLL_API long Pa_GetHostError( void );
|
||||
|
||||
/*
|
||||
Translate the error number into a human readable message.
|
||||
*/
|
||||
DLL_API const char *Pa_GetErrorText( PaError errnum );
|
||||
|
||||
/*
|
||||
Sample formats
|
||||
|
||||
These are formats used to pass sound data between the callback and the
|
||||
stream. Each device has a "native" format which may be used when optimum
|
||||
efficiency or control over conversion is required.
|
||||
|
||||
Formats marked "always available" are supported (emulated) by all devices.
|
||||
|
||||
The floating point representation uses +1.0 and -1.0 as the respective
|
||||
maximum and minimum.
|
||||
|
||||
*/
|
||||
|
||||
typedef unsigned long PaSampleFormat;
|
||||
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
|
||||
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
|
||||
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
|
||||
#define paInt24 ((PaSampleFormat) (1<<3))
|
||||
#define paPackedInt24 ((PaSampleFormat) (1<<4))
|
||||
#define paInt8 ((PaSampleFormat) (1<<5))
|
||||
#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
|
||||
#define paCustomFormat ((PaSampleFormat) (1<<16))
|
||||
|
||||
/*
|
||||
Device enumeration mechanism.
|
||||
|
||||
Device ids range from 0 to Pa_CountDevices()-1.
|
||||
|
||||
Devices may support input, output or both. Device 0 is always the "default"
|
||||
device and should support at least stereo in and out if that is available
|
||||
on the taget platform _even_ if this involves kludging an input/output
|
||||
device on platforms that usually separate input from output. Other platform
|
||||
specific devices are specified by positive device ids.
|
||||
*/
|
||||
|
||||
typedef int PaDeviceID;
|
||||
#define paNoDevice -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int structVersion;
|
||||
const char *name;
|
||||
int maxInputChannels;
|
||||
int maxOutputChannels;
|
||||
/* Number of discrete rates, or -1 if range supported. */
|
||||
int numSampleRates;
|
||||
/* Array of supported sample rates, or {min,max} if range supported. */
|
||||
const double *sampleRates;
|
||||
PaSampleFormat nativeSampleFormats;
|
||||
}
|
||||
PaDeviceInfo;
|
||||
|
||||
|
||||
DLL_API int Pa_CountDevices();
|
||||
/*
|
||||
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
|
||||
|
||||
Return the default device ID or paNoDevice if there is no devices.
|
||||
The result can be passed to Pa_OpenStream().
|
||||
|
||||
On the PC, the user can specify a default device by
|
||||
setting an environment variable. For example, to use device #1.
|
||||
|
||||
set PA_RECOMMENDED_OUTPUT_DEVICE=1
|
||||
|
||||
The user should first determine the available device ID by using
|
||||
the supplied application "pa_devs".
|
||||
*/
|
||||
DLL_API PaDeviceID Pa_GetDefaultInputDeviceID( void );
|
||||
DLL_API PaDeviceID Pa_GetDefaultOutputDeviceID( void );
|
||||
|
||||
/*
|
||||
PaTimestamp is used to represent a continuous sample clock with arbitrary
|
||||
start time useful for syncronisation. The type is used in the outTime
|
||||
argument to the callback function and the result of Pa_StreamTime()
|
||||
*/
|
||||
|
||||
typedef double PaTimestamp;
|
||||
|
||||
/*
|
||||
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
|
||||
referring to the device specified by id.
|
||||
If id is out of range the function returns NULL.
|
||||
|
||||
The returned structure is owned by the PortAudio implementation and must
|
||||
not be manipulated or freed. The pointer is guaranteed to be valid until
|
||||
between calls to Pa_Initialize() and Pa_Terminate().
|
||||
*/
|
||||
|
||||
DLL_API const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id );
|
||||
|
||||
/*
|
||||
PortAudioCallback is implemented by clients of the portable audio api.
|
||||
|
||||
inputBuffer and outputBuffer are arrays of interleaved samples,
|
||||
the format, packing and number of channels used by the buffers are
|
||||
determined by parameters to Pa_OpenStream() (see below).
|
||||
|
||||
framesPerBuffer is the number of sample frames to be processed by the callback.
|
||||
|
||||
outTime is the time in samples when the buffer(s) processed by
|
||||
this callback will begin being played at the audio output.
|
||||
See also Pa_StreamTime()
|
||||
|
||||
userData is the value of a user supplied pointer passed to Pa_OpenStream()
|
||||
intended for storing synthesis data etc.
|
||||
|
||||
return value:
|
||||
The callback can return a nonzero value to stop the stream. This may be
|
||||
useful in applications such as soundfile players where a specific duration
|
||||
of output is required. However, it is not necessary to utilise this mechanism
|
||||
as StopStream() will also terminate the stream. A callback returning a
|
||||
nonzero value must fill the entire outputBuffer.
|
||||
|
||||
NOTE: None of the other stream functions may be called from within the
|
||||
callback function except for Pa_GetCPULoad().
|
||||
|
||||
*/
|
||||
|
||||
typedef int (PortAudioCallback)(
|
||||
void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
|
||||
|
||||
/*
|
||||
Stream flags
|
||||
|
||||
These flags may be supplied (ored together) in the streamFlags argument to
|
||||
the Pa_OpenStream() function.
|
||||
|
||||
[ suggestions? ]
|
||||
*/
|
||||
|
||||
#define paNoFlag (0)
|
||||
#define paClipOff (1<<0) /* disable defult clipping of out of range samples */
|
||||
#define paDitherOff (1<<1) /* disable default dithering */
|
||||
#define paPlatformSpecificFlags (0x00010000)
|
||||
typedef unsigned long PaStreamFlags;
|
||||
|
||||
/*
|
||||
A single PortAudioStream provides multiple channels of real-time
|
||||
input and output audio streaming to a client application.
|
||||
Pointers to PortAudioStream objects are passed between PortAudio functions.
|
||||
*/
|
||||
|
||||
typedef void PortAudioStream;
|
||||
#define PaStream PortAudioStream
|
||||
|
||||
/*
|
||||
Pa_OpenStream() opens a stream for either input, output or both.
|
||||
|
||||
stream is the address of a PortAudioStream pointer which will receive
|
||||
a pointer to the newly opened stream.
|
||||
|
||||
inputDevice is the id of the device used for input (see PaDeviceID above.)
|
||||
inputDevice may be paNoDevice to indicate that an input device is not required.
|
||||
|
||||
numInputChannels is the number of channels of sound to be delivered to the
|
||||
callback. It can range from 1 to the value of maxInputChannels in the
|
||||
device input record for the device specified in the inputDevice parameter.
|
||||
If inputDevice is paNoDevice numInputChannels is ignored.
|
||||
|
||||
inputSampleFormat is the format of inputBuffer provided to the callback
|
||||
function. inputSampleFormat may be any of the formats described by the
|
||||
PaSampleFormat enumeration (see above). PortAudio guarantees support for
|
||||
the sound devices native formats (nativeSampleFormats in the device info
|
||||
record) and additionally 16 and 32 bit integer and 32 bit floating point
|
||||
formats. Support for other formats is implementation defined.
|
||||
|
||||
inputDriverInfo is a pointer to an optional driver specific data structure
|
||||
containing additional information for device setup or stream processing.
|
||||
inputDriverInfo is never required for correct operation. If not used
|
||||
inputDriverInfo should be NULL.
|
||||
|
||||
outputDevice is the id of the device used for output (see PaDeviceID above.)
|
||||
outputDevice may be paNoDevice to indicate that an output device is not required.
|
||||
|
||||
numOutputChannels is the number of channels of sound to be supplied by the
|
||||
callback. See the definition of numInputChannels above for more details.
|
||||
|
||||
outputSampleFormat is the sample format of the outputBuffer filled by the
|
||||
callback function. See the definition of inputSampleFormat above for more
|
||||
details.
|
||||
|
||||
outputDriverInfo is a pointer to an optional driver specific data structure
|
||||
containing additional information for device setup or stream processing.
|
||||
outputDriverInfo is never required for correct operation. If not used
|
||||
outputDriverInfo should be NULL.
|
||||
|
||||
sampleRate is the desired sampleRate for input and output
|
||||
|
||||
framesPerBuffer is the length in sample frames of all internal sample buffers
|
||||
used for communication with platform specific audio routines. Wherever
|
||||
possible this corresponds to the framesPerBuffer parameter passed to the
|
||||
callback function.
|
||||
|
||||
numberOfBuffers is the number of buffers used for multibuffered
|
||||
communication with the platform specific audio routines. This parameter is
|
||||
provided only as a guide - and does not imply that an implementation must
|
||||
use multibuffered i/o when reliable double buffering is available (such as
|
||||
SndPlayDoubleBuffer() on the Macintosh.)
|
||||
|
||||
streamFlags may contain a combination of flags ORed together.
|
||||
These flags modify the behavior of the
|
||||
streaming process. Some flags may only be relevant to certain buffer formats.
|
||||
|
||||
callback is a pointer to a client supplied function that is responsible
|
||||
for processing and filling input and output buffers (see above for details.)
|
||||
|
||||
userData is a client supplied pointer which is passed to the callback
|
||||
function. It could for example, contain a pointer to instance data necessary
|
||||
for processing the audio buffers.
|
||||
|
||||
return value:
|
||||
Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
|
||||
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
|
||||
If a call to Pa_OpenStream() fails a nonzero error code is returned (see
|
||||
PAError above) and the value of stream is invalid.
|
||||
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_OpenStream( PortAudioStream** stream,
|
||||
PaDeviceID inputDevice,
|
||||
int numInputChannels,
|
||||
PaSampleFormat inputSampleFormat,
|
||||
void *inputDriverInfo,
|
||||
PaDeviceID outputDevice,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat outputSampleFormat,
|
||||
void *outputDriverInfo,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PaStreamFlags streamFlags,
|
||||
PortAudioCallback *callback,
|
||||
void *userData );
|
||||
|
||||
|
||||
/*
|
||||
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
|
||||
opens the default input and/or ouput devices. Most parameters have
|
||||
identical meaning to their Pa_OpenStream() counterparts, with the following
|
||||
exceptions:
|
||||
|
||||
If either numInputChannels or numOutputChannels is 0 the respective device
|
||||
is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
|
||||
|
||||
sampleFormat applies to both the input and output buffers.
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
|
||||
int numInputChannels,
|
||||
int numOutputChannels,
|
||||
PaSampleFormat sampleFormat,
|
||||
double sampleRate,
|
||||
unsigned long framesPerBuffer,
|
||||
unsigned long numberOfBuffers,
|
||||
PortAudioCallback *callback,
|
||||
void *userData );
|
||||
|
||||
/*
|
||||
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_CloseStream( PortAudioStream* );
|
||||
|
||||
/*
|
||||
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
|
||||
When Pa_StopStream() returns, all pending audio buffers have been played.
|
||||
Pa_AbortStream() stops playing immediately without waiting for pending
|
||||
buffers to complete.
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_StartStream( PortAudioStream *stream );
|
||||
|
||||
DLL_API PaError Pa_StopStream( PortAudioStream *stream );
|
||||
|
||||
DLL_API PaError Pa_AbortStream( PortAudioStream *stream );
|
||||
|
||||
/*
|
||||
Pa_StreamActive() returns one when the stream is playing audio,
|
||||
zero when not playing, or a negative error number if the
|
||||
stream is invalid.
|
||||
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
|
||||
but may also become inactive if the callback returns a non-zero value.
|
||||
In the latter case, the stream is considered inactive after the last
|
||||
buffer has finished playing.
|
||||
*/
|
||||
|
||||
DLL_API PaError Pa_StreamActive( PortAudioStream *stream );
|
||||
|
||||
/*
|
||||
Pa_StreamTime() returns the current output time for the stream in samples.
|
||||
This time may be used as a time reference (for example syncronising audio to
|
||||
MIDI).
|
||||
*/
|
||||
|
||||
DLL_API PaTimestamp Pa_StreamTime( PortAudioStream *stream );
|
||||
|
||||
/*
|
||||
The "CPU Load" is a fraction of total CPU time consumed by the
|
||||
stream's audio processing.
|
||||
A value of 0.5 would imply that PortAudio and the sound generating
|
||||
callback was consuming roughly 50% of the available CPU time.
|
||||
This function may be called from the callback function or the application.
|
||||
*/
|
||||
DLL_API double Pa_GetCPULoad( PortAudioStream* stream );
|
||||
|
||||
/*
|
||||
Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
|
||||
the current host based on minimum latency.
|
||||
On the PC, for the DirectSound implementation, latency can be optionally set
|
||||
by user by setting an environment variable.
|
||||
For example, to set latency to 200 msec, put:
|
||||
|
||||
set PA_MIN_LATENCY_MSEC=200
|
||||
|
||||
in the AUTOEXEC.BAT file and reboot.
|
||||
If the environment variable is not set, then the latency will be determined
|
||||
based on the OS. Windows NT has higher latency than Win95.
|
||||
*/
|
||||
|
||||
DLL_API int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
|
||||
|
||||
/*
|
||||
Sleep for at least 'msec' milliseconds.
|
||||
You may sleep longer than the requested time so don't rely
|
||||
on this for accurate musical timing.
|
||||
*/
|
||||
DLL_API void Pa_Sleep( long msec );
|
||||
|
||||
/*
|
||||
Return size in bytes of a single sample in a given PaSampleFormat
|
||||
or paSampleFormatNotSupported.
|
||||
*/
|
||||
DLL_API PaError Pa_GetSampleSize( PaSampleFormat format );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PORT_AUDIO_H */
|
||||
1567
pa_mac/pa_mac.c
Normal file
1567
pa_mac/pa_mac.c
Normal file
File diff suppressed because it is too large
Load diff
1159
pa_mac_core/pa_mac_core.c
Normal file
1159
pa_mac_core/pa_mac_core.c
Normal file
File diff suppressed because it is too large
Load diff
52
pa_sgi/Makefile
Normal file
52
pa_sgi/Makefile
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Make PortAudio for Silicon Graphics IRIX (6.2)
|
||||
# Pieter suurmond, august 17, 2001. (v15 pa_sgi sub-version #0.04)
|
||||
|
||||
# Instead of "-lpthread", as with linux,
|
||||
# just the audio- and math-library for SGI:
|
||||
LIBS = -laudio -lm
|
||||
|
||||
CDEFINES = -I../pa_common
|
||||
|
||||
# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64,
|
||||
# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging.
|
||||
# And use for example -O2 or -O3 for better optimization:
|
||||
CFLAGS = -O2
|
||||
PASRC = ../pa_common/pa_lib.c pa_sgi.c
|
||||
PAINC = ../pa_common/portaudio.h
|
||||
|
||||
# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2).
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK
|
||||
TESTC = $(PASRC) ../pa_tests/patest_record.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK but needs more than 4 buffers to do without glitches.
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_saw.c # Seems OK (does it gracefully exit?).
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore.
|
||||
|
||||
# Tests that do not yet work.
|
||||
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 1 output channel(s) on AL default
|
||||
#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 2 output channel(s) on AL default
|
||||
#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # THIS CRASHED MY WHOLE IRIX SYSTEM after "ENTER"! :-(
|
||||
# PROCESS IN ITSELF RUNS OK, WITH LARGER BUFFSIZES THOUGH.
|
||||
# OH MAYBE IT WAS BECAUSE DEBUGGING WAS ON???
|
||||
# HMMM I'M NOT GONNA RUN THAT AGAIN... WAY TOO DANGEROUS!
|
||||
TESTH = $(PAINC)
|
||||
|
||||
all: patest
|
||||
|
||||
#(cc may be changed to gcc)
|
||||
patest: $(TESTC) $(TESTH) Makefile
|
||||
cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
|
||||
|
||||
run: patest
|
||||
./patest
|
||||
|
||||
991
pa_sgi/pa_sgi.c
Normal file
991
pa_sgi/pa_sgi.c
Normal file
|
|
@ -0,0 +1,991 @@
|
|||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* SGI IRIX implementation by Pieter Suurmond, aug 17, 2001.
|
||||
*
|
||||
* Copyright (c) 1999-2000 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.
|
||||
*/
|
||||
/*
|
||||
Modfication History:
|
||||
8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
|
||||
8/17/2001 - (v15 pa_sgi sub-version #0.04), wow, much is working now! Send this to Phil & Ross.
|
||||
ID-stuff is nicer here now than in the linux_oss-alpha-version, v15. Was able to get
|
||||
rid of a number of globals (like "sNumDevices" for instance).
|
||||
TODO:
|
||||
- Test under IRIX 6.5.
|
||||
- Maybe use pthread instead of (platform-specific) poll/select/pollfd-calls.
|
||||
(As Gary Scavone more or less suggested(?)... But can I get enough process-control then?)
|
||||
- Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
|
||||
and maybe also the other natively supported formats? (might increase performance)
|
||||
- Not sure whether CPU UTILIZATION MEASUREMENT (from OSS/linux) really works. Changed nothing yet,
|
||||
seems ok, but I've not yet tested it thoroughly. (maybe utilization-code may be made _unix_common_ then?)
|
||||
- Make sure that the fuzz-test doesn't let all IRIX crash (especially with higher buff-sizes).
|
||||
- The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4.
|
||||
REFERENCES:
|
||||
- IRIX 6.2 man pages regarding SGI AL library.
|
||||
- IRIS Digital MediaProgramming Guide (online books as well as man-pages come with IRIX 6.2 and
|
||||
may not be publically available on the internet).
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* Standard libraries. */
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "portaudio.h" /* Portaudio headers. */
|
||||
#include "pa_host.h"
|
||||
#include "pa_trace.h"
|
||||
|
||||
#include <sys/time.h> /* System specific (IRIX 6.2). */
|
||||
#include <sys/types.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/schedctl.h>
|
||||
#include <fcntl.h> /* fcntl.h needed. */
|
||||
#include <unistd.h> /* For streams, ioctl(), etc. */
|
||||
#include <signal.h>
|
||||
#include <ulocks.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
#include <dmedia/audio.h>
|
||||
|
||||
/*--------------------------------------------*/
|
||||
#define PRINT(x) { printf x; fflush(stdout); }
|
||||
#define ERR_RPT(x) PRINT(x)
|
||||
#define DBUG(x) PRINT(x)
|
||||
#define DBUGX(x) /* PRINT(x) */
|
||||
|
||||
#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */
|
||||
#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */
|
||||
|
||||
typedef struct internalPortAudioDevice /* IRIX specific device info: */
|
||||
{
|
||||
PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */
|
||||
long pad_ALdevice; /* SGI-number! */
|
||||
double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
|
||||
char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */
|
||||
PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */
|
||||
/* int structVersion; */
|
||||
/* const char* name; */
|
||||
/* int maxInputChannels, maxOutputChannels; */
|
||||
/* int numSampleRates; Num rates, or -1 if range supprtd. */
|
||||
/* const double* sampleRates; Array of supported sample rates, */
|
||||
/* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */
|
||||
struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */
|
||||
}
|
||||
internalPortAudioDevice;
|
||||
|
||||
typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */
|
||||
{
|
||||
ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */
|
||||
pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */
|
||||
int pahsc_threadPID; /* Sproc()-result, written by PaHost_StartEngine(). */
|
||||
short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */
|
||||
*pahsc_NativeOutputBuffer;
|
||||
unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */
|
||||
pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */
|
||||
unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */
|
||||
pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */
|
||||
struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */
|
||||
pahsc_LastExitTime;
|
||||
long pahsc_InsideCountSum,
|
||||
pahsc_TotalCountSum;
|
||||
}
|
||||
PaHostSoundControl;
|
||||
|
||||
/*----------------------------- Shared Data ------------------------------------------------------*/
|
||||
static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
|
||||
static int sPaHostError = 0; /* Maybe more than one process writing errs!? */
|
||||
|
||||
/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
|
||||
/* (copied from source pa_linux_oss/pa_linux_oss.c) */
|
||||
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
|
||||
{
|
||||
struct itimerval itimer;
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return;
|
||||
/* Query system timer for usage analysis and to prevent overuse of CPU. */
|
||||
getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
|
||||
}
|
||||
|
||||
static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
|
||||
{
|
||||
long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
|
||||
long usecs = secs * 1000000;
|
||||
usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
|
||||
return usecs;
|
||||
}
|
||||
|
||||
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
|
||||
{
|
||||
struct itimerval currentTime;
|
||||
long insideCount;
|
||||
long totalCount; /* Measure CPU utilization during this callback. */
|
||||
|
||||
#define LOWPASS_COEFFICIENT_0 (0.95)
|
||||
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
|
||||
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if (pahsc == NULL)
|
||||
return;
|
||||
if (getitimer( ITIMER_REAL, ¤tTime ) == 0 )
|
||||
{
|
||||
if (past->past_IfLastExitValid)
|
||||
{
|
||||
insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, ¤tTime );
|
||||
pahsc->pahsc_InsideCountSum += insideCount;
|
||||
totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, ¤tTime );
|
||||
pahsc->pahsc_TotalCountSum += totalCount;
|
||||
/* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
|
||||
/* Low pass filter the result because sometimes we get called several times in a row. */
|
||||
/* That can cause the TotalCount to be very low which can cause the usage to appear */
|
||||
/* unnaturally high. So we must filter numerator and denominator separately!!! */
|
||||
if (pahsc->pahsc_InsideCountSum > 0)
|
||||
{
|
||||
past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
|
||||
(LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
|
||||
past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
|
||||
(LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
|
||||
past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
|
||||
pahsc->pahsc_InsideCountSum = 0;
|
||||
pahsc->pahsc_TotalCountSum = 0;
|
||||
}
|
||||
}
|
||||
past->past_IfLastExitValid = 1;
|
||||
}
|
||||
pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
|
||||
pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
|
||||
setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
|
||||
past->past_IfLastExitValid = 1;
|
||||
} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------------------*/
|
||||
PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
|
||||
{ /* call to report via ERR_RPT(), yields a PaError-num. */
|
||||
const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */
|
||||
switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
|
||||
{
|
||||
case AL_BAD_OUT_OF_MEM:
|
||||
ERR_RPT(("%sout of memory.\n", a));
|
||||
return paInsufficientMemory; /* Known PaError. */
|
||||
case AL_BAD_CONFIG:
|
||||
ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_CHANNELS:
|
||||
ERR_RPT(("%schannels not 1,2 or 4.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_NO_PORTS:
|
||||
ERR_RPT(("%sout of audio ports.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_DEVICE:
|
||||
ERR_RPT(("%swrong device number.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_DEVICE_ACCESS:
|
||||
ERR_RPT(("%swrong device access.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_DIRECTION:
|
||||
ERR_RPT(("%sinvalid direction.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_SAMPFMT:
|
||||
ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_FLOATMAX:
|
||||
ERR_RPT(("%smax float value is zero.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_WIDTH:
|
||||
ERR_RPT(("%sunsupported samplewidth.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_QSIZE:
|
||||
ERR_RPT(("%sinvalid queue size.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_PVBUFFER:
|
||||
ERR_RPT(("%sPVbuffer null.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_BUFFERLENGTH_NEG:
|
||||
ERR_RPT(("%snegative bufferlength.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_BUFFERLENGTH_ODD:
|
||||
ERR_RPT(("%sodd bufferlength.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
case AL_BAD_PARAM:
|
||||
ERR_RPT(("%sparameter not valid for device.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
default:
|
||||
ERR_RPT(("%sunknown error.\n", a));
|
||||
return paHostError; /* Generic PaError. */
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------------------*/
|
||||
/* Tries to set various rates and formats and fill in the device info structure. */
|
||||
static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */
|
||||
PaDeviceID id, /* (DefaultI|ODeviceID()) */
|
||||
char* name, /* (for example "SGI AL") */
|
||||
internalPortAudioDevice* pad) /* Result written to pad. */
|
||||
{
|
||||
int format;
|
||||
long min, max; /* To catch hardware characteristics. */
|
||||
ALseterrorhandler(0); /* 0 = turn off the default error handler. */
|
||||
/*--------------------------------------------------------------------------------------*/
|
||||
pad->pad_ALdevice = ALdev; /* Set the AL device number. */
|
||||
pad->pad_DeviceID = id; /* Set the PA device number. */
|
||||
if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */
|
||||
{
|
||||
ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
|
||||
return paHostError;
|
||||
}
|
||||
strcpy(pad->pad_DeviceName, name); /* Write name-string. */
|
||||
pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */
|
||||
/*--------------------------------- natively supported sample formats: -----------------*/
|
||||
pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
|
||||
/* Then also choose other CallConvertXX()! */
|
||||
/*--------------------------------- number of available i/o channels: ------------------*/
|
||||
if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
|
||||
return translateSGIerror();
|
||||
pad->pad_Info.maxInputChannels = max;
|
||||
DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
|
||||
if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
|
||||
return translateSGIerror();
|
||||
pad->pad_Info.maxOutputChannels = max;
|
||||
DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
|
||||
/*--------------------------------- supported samplerates: ----------------------*/
|
||||
pad->pad_Info.numSampleRates = 7;
|
||||
pad->pad_Info.sampleRates = pad->pad_SampleRates;
|
||||
pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */
|
||||
pad->pad_SampleRates[1] = (double)AL_RATE_11025;
|
||||
pad->pad_SampleRates[2] = (double)AL_RATE_16000;
|
||||
pad->pad_SampleRates[3] = (double)AL_RATE_22050;
|
||||
pad->pad_SampleRates[4] = (double)AL_RATE_32000;
|
||||
pad->pad_SampleRates[5] = (double)AL_RATE_44100;
|
||||
pad->pad_SampleRates[6] = (double)AL_RATE_48000;
|
||||
if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */
|
||||
return translateSGIerror(); /* double -> long. */
|
||||
if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */
|
||||
goto weird;
|
||||
if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */
|
||||
return translateSGIerror();
|
||||
if (max != (long)(0.5 + pad->pad_SampleRates[6]))
|
||||
{
|
||||
weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
|
||||
return paHostError; /* Or make it a warning and just carry on... */
|
||||
}
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------------*/
|
||||
int Pa_CountDevices() /* Name of this function suggests it only counts and */
|
||||
{ /* is NOT destructive, it however resets whole PA ! */
|
||||
int numDevices = 0; /* Let 's not do that here. */
|
||||
internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */
|
||||
#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */
|
||||
if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */
|
||||
Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */
|
||||
#endif /* friendly to clients that forgot to initialize PA. */
|
||||
while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */
|
||||
{
|
||||
numDevices++;
|
||||
currentDevice = currentDevice->pad_Next;
|
||||
}
|
||||
return numDevices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
|
||||
{
|
||||
int numDevices = 0;
|
||||
internalPortAudioDevice *res = (internalPortAudioDevice*)NULL;
|
||||
internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */
|
||||
while (pad) /* pad may be NULL, that's ok, return 0. */
|
||||
{ /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */
|
||||
if (pad->pad_DeviceID == id) /* This the device we were looking for? */
|
||||
res = pad; /* But keep on(!) counting so we don't */
|
||||
numDevices++; /* have to call Pa_CountDevices() later. */
|
||||
pad = pad->pad_Next; /* Advance to the next device or NULL. */
|
||||
} /* No assumptions about order of ID's in */
|
||||
if (!res) /* the list. */
|
||||
ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
|
||||
if ((id < 0) || (id >= numDevices))
|
||||
{
|
||||
ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
|
||||
#if 1 /* Be strict, even when found, */
|
||||
res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */
|
||||
#endif
|
||||
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
|
||||
{
|
||||
PaDeviceInfo* res = (PaDeviceInfo*)NULL;
|
||||
internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */
|
||||
if (pad)
|
||||
res = &pad->pad_Info; /* Not finding the specified ID is not */
|
||||
if (!res) /* the same as &pad->pad_Info == NULL. */
|
||||
ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
|
||||
return res; /* So (maybe) a second/third ERR_RPT(). */
|
||||
}
|
||||
|
||||
/*------------------------------------------------*/
|
||||
PaDeviceID Pa_GetDefaultInputDeviceID(void)
|
||||
{
|
||||
return 0; /* 0 is the default device ID. */
|
||||
}
|
||||
/*------------------------------------------------*/
|
||||
PaDeviceID Pa_GetDefaultOutputDeviceID(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------------*/
|
||||
/* Build linked list with all available audio devices on this SGI machine. */
|
||||
PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */
|
||||
{
|
||||
internalPortAudioDevice* pad;
|
||||
PaError r = paNoError;
|
||||
int audioLibFileID;
|
||||
|
||||
if (sDeviceList) /* Allow re-init, only warn, no err. */
|
||||
{
|
||||
ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
|
||||
return r;
|
||||
}
|
||||
/*------------- ADD THE SGI DEFAULT DEVICES TO THE LIST: --------------------------------------*/
|
||||
audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */
|
||||
if (audioLibFileID < 0) /* IO port. On failure, machine */
|
||||
{ /* has no audio ability. */
|
||||
ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
|
||||
return paHostError;
|
||||
}
|
||||
close(audioLibFileID); /* Allocate fast mem to hold device info. */
|
||||
pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
|
||||
if (pad == NULL)
|
||||
return paInsufficientMemory;
|
||||
memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */
|
||||
r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */
|
||||
Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
|
||||
"AL default", /* A suitable name. */
|
||||
pad); /* Write args and queried info into pad. */
|
||||
if (r != paNoError)
|
||||
{
|
||||
ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
|
||||
PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */
|
||||
}
|
||||
else
|
||||
sDeviceList = pad; /* First element in linked list (pad->pad_Next is already NULL). */
|
||||
/*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LIST: --------------*/
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
return r;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
usema_t *SendSema, /* These variables are shared between the audio */
|
||||
*RcvSema; /* handling process and the main process. */
|
||||
|
||||
/*--------------------------------------------------------------------------------------------*/
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b)) /* MIN()-function is used below. */
|
||||
#define kPollSEMA 0 /* To index the pollfd-array, reads nicer than just */
|
||||
#define kPollOUT 1 /* numbers. */
|
||||
#define kPollIN 2
|
||||
void Pa_SgiAudioProcess(void *v) /* This function is sproc-ed by PaHost_StartEngine() */
|
||||
{ /* as a separate thread. (Argument must be void*). */
|
||||
short evtLoop=1; /* Reset by parent indirectly, or at local errors. */
|
||||
PaError result;
|
||||
struct pollfd PollFD[3]; /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */
|
||||
internalPortAudioStream *past = (internalPortAudioStream*)v; /* Copy void-ptr-argument.*/
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl*)past->past_DeviceData;
|
||||
if (pahsc == NULL)
|
||||
{
|
||||
sPaHostError = paInternalError; /* The only way is to signal error to shared area?! */
|
||||
goto skipALL; /* Sproc-ed threads MAY NOT RETURN paInternalError. */
|
||||
}
|
||||
past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */
|
||||
DBUG(("entering thread.\n"));
|
||||
PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN); /* ALgetfd returns -1 on failures */
|
||||
PollFD[kPollIN].events = POLLOUT; /* such as ALport not there. */
|
||||
/* .events = POLLOUT ??? ok? */
|
||||
PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT);
|
||||
PollFD[kPollOUT].events = POLLOUT; /* .events = POLLOUT is OK. */
|
||||
schedctl(NDPRI, NDPHIMIN); /* Sets non-degrading priority for this process. */
|
||||
PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777); /* To communicate with parent. */
|
||||
PollFD[kPollSEMA].events = POLLIN; /* .events = POLLIN is OK. */
|
||||
uspsema(SendSema); /* Blocks until the next (first)signal is received???? */
|
||||
do
|
||||
{
|
||||
/*---------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ----------*/
|
||||
if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */
|
||||
/* For input port, fill point is number of locations in the sample queue that must be */
|
||||
/* filled in order to trigger a return from select(). (or poll()) */
|
||||
/* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/
|
||||
if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputBuffer))
|
||||
{ /* Same amount as transferred per time. */
|
||||
ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n"));
|
||||
sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
|
||||
goto skipIO;
|
||||
}
|
||||
if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */
|
||||
/* For output port, fill point is number of locations that must be free in order to */
|
||||
/* wake up from select(). (or poll()) */
|
||||
if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputBuffer))
|
||||
{
|
||||
ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n"));
|
||||
sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
|
||||
goto skipIO;
|
||||
} /* poll() with timeout=-1 makes it block until a requested */
|
||||
poll(PollFD, 3, -1); /* event occurs or until call is interrupted. If fd-value in */
|
||||
/* array <0, events is ignored and revents is set to 0. */
|
||||
/*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/
|
||||
if (PollFD[kPollSEMA].revents & POLLIN)
|
||||
{
|
||||
if (past->past_StopNow || past->past_StopSoon)
|
||||
evtLoop = 0;
|
||||
uspsema(SendSema); /* Decrements count of previously allocated semaphore SendSema. If count */
|
||||
/* is then negative, semaphore will logically block calling process until */
|
||||
/* count is incremented due to an usvsema call made by another process. */
|
||||
usvsema(RcvSema); /* Increments the count associated with RcvSema. If there are any */
|
||||
/* processes queued waiting for the semaphore the first one is awakened. */
|
||||
/* Here usvsema sends an acknowledgement to the "SendAudioCommand()". */
|
||||
if (past->past_StopNow)
|
||||
goto skipIO; /* Else go on for a little while... */
|
||||
}
|
||||
/*------------------------------------- FREE-EVENT FROM INPUT BUFFER: ----------------------------*/
|
||||
if (PollFD[kPollIN].revents & POLLOUT) /* Don't need to check (pahsc->pahsc_NativeInputBuffer): */
|
||||
{ /* if buffer was not there, ALport not there, no events! */
|
||||
if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer,
|
||||
pahsc->pahsc_SamplesPerInputBuffer))
|
||||
{ /* Here again: number of samples instead of number of frames. */
|
||||
ERR_RPT(("ALreadsamps() failed.\n"));
|
||||
sPaHostError = paInternalError;
|
||||
goto skipIO; /* (Using exit(-1) would be a bit rude.) */
|
||||
}
|
||||
}
|
||||
/*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/
|
||||
if ((PollFD[kPollIN].revents & POLLOUT) || (PollFD[kPollOUT].revents & POLLOUT))
|
||||
{ /* To be sure we that really DID input-transfer or gonna DO output-transfer, and that it is */
|
||||
/* not just "sema"- (i.e. user)-event, or some other system-event that awakened the poll(). */
|
||||
Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */
|
||||
result = Pa_CallConvertInt16(past, /* user data and call user routine. */
|
||||
pahsc->pahsc_NativeInputBuffer,
|
||||
pahsc->pahsc_NativeOutputBuffer);
|
||||
Pa_EndUsageCalculation(past);
|
||||
if (result)
|
||||
{
|
||||
DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result));
|
||||
goto skipIO; /* But it is apparently NOT an error! */
|
||||
} /* Just letting the userCallBack stop us. */
|
||||
}
|
||||
/*------------------------------------- FILLED-EVENT FROM OUTPUT BUFFER: -------------------------*/
|
||||
if (PollFD[kPollOUT].revents & POLLOUT) /* Don't need to check (pahsc->pahsc_NativeOutputBuffer) */
|
||||
{ /* because if filedescriptor not there, no event for it. */
|
||||
if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer,
|
||||
pahsc->pahsc_SamplesPerOutputBuffer))
|
||||
{
|
||||
ERR_RPT(("ALwritesamps() failed.\n"));
|
||||
sPaHostError = paInternalError; /* Better use SEMAS also for messaging back to parent! */
|
||||
goto skipIO;
|
||||
}
|
||||
}
|
||||
/*------------------------------------------------------------------------------------------------*/
|
||||
}
|
||||
while (evtLoop); /* Set at init-time, only reset indirectly via semaphore, */
|
||||
skipIO: /* or in case of error, only within this loop. */
|
||||
DBUG(("leaving thread.\n")); /* Don't we need to cleanup semaphore-stuff / thread itself? */
|
||||
skipALL: /* (make threadPID in the struct -1 again, for instance?) */
|
||||
past->past_IsActive = 0;
|
||||
pahsc->pahsc_threadPID == -1; /* Signal back, so StopEngine() for example won't call KILL().... hmmm doesn't work yet! */
|
||||
/* SHIT: when this process ends, it also KILLS of the parent!!! because sigint = kill?? */
|
||||
exit(0); /* Unix process. SIGCLD is sent to parent (if prctl wa called with 0 by parent). */
|
||||
} /* if prctl was called with 9 by parent, then when this process ends, it also */
|
||||
/* KILLS off the parent!!! */
|
||||
|
||||
/*--------------------------------------------------------------------------------------*/
|
||||
PaError PaHost_OpenStream(internalPortAudioStream *past)
|
||||
{
|
||||
PaError result = paNoError;
|
||||
PaHostSoundControl *pahsc;
|
||||
unsigned int minNumBuffers;
|
||||
internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */
|
||||
ALconfig sgiALconfig = NULL; /* IRIX-datatype. */
|
||||
long pvbuf[8]; /* To get/set hardware configs. */
|
||||
long sr, ALqsize;
|
||||
DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */
|
||||
if (!past)
|
||||
{
|
||||
ERR_RPT(("Streampointer NULL!\n"));
|
||||
result = paBadStreamPtr; goto done;
|
||||
}
|
||||
pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
|
||||
if (pahsc == NULL)
|
||||
{
|
||||
ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */
|
||||
result = paInsufficientMemory; goto done; /* code (nothing will be freed). */
|
||||
}
|
||||
memset(pahsc, 0, sizeof(PaHostSoundControl)); /* Hmmm, not too elegant, besides, the */
|
||||
pahsc->pahsc_threadPID = -1; /* pahsc_threadPID must be inited to */
|
||||
past->past_DeviceData = (void*)pahsc; /* -1 instead of 0! ?? */
|
||||
/*--------------------------------------------------- Allocate native buffers: --------*/
|
||||
pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
|
||||
past->past_NumInputChannels; /* audio-thread. */
|
||||
pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short);
|
||||
if (past->past_NumInputChannels > 0) /* Assumes short = 16 bits! */
|
||||
{
|
||||
pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
|
||||
if( pahsc->pahsc_NativeInputBuffer == NULL )
|
||||
{
|
||||
ERR_RPT(("(normal yet) Memory allocation failed.\n"));
|
||||
result = paInsufficientMemory;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
|
||||
past->past_NumOutputChannels; /* audio-thread. */
|
||||
pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short);
|
||||
if (past->past_NumOutputChannels > 0) /* Assumes short = 16 bits! */
|
||||
{
|
||||
pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
|
||||
if (pahsc->pahsc_NativeOutputBuffer == NULL)
|
||||
{
|
||||
ERR_RPT(("(normal yet) Memory allocation failed.\n"));
|
||||
result = paInsufficientMemory; goto done;
|
||||
}
|
||||
}
|
||||
/*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
|
||||
ALseterrorhandler(0); /* 0 = turn off the default error handler. */
|
||||
pvbuf[0] = AL_INPUT_RATE;
|
||||
pvbuf[2] = AL_INPUT_COUNT;
|
||||
pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */
|
||||
pvbuf[6] = AL_OUTPUT_COUNT;
|
||||
sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */
|
||||
if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */
|
||||
{ /* sponding native AL-number(s). */
|
||||
padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
|
||||
if (!padIN)
|
||||
{
|
||||
ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
|
||||
result = paHostError; goto done;
|
||||
}
|
||||
if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
|
||||
goto sgiError; /* the same AL-device, the AL-library might */
|
||||
if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */
|
||||
{ /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */
|
||||
if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */
|
||||
{
|
||||
ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
|
||||
another process is currently using input at %ld kHz.\n", sr, pvbuf[1]));
|
||||
result = paHostError; goto done;
|
||||
}
|
||||
pvbuf[1] = sr; /* Then set input-rate. */
|
||||
if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
|
||||
goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
|
||||
}
|
||||
}
|
||||
if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels<=0! */
|
||||
{ /* We use padOUT/IN later on, or at least 1 of both. */
|
||||
padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
|
||||
if (!padOUT)
|
||||
{
|
||||
ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
|
||||
result = paHostError; goto done;
|
||||
}
|
||||
if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
|
||||
goto sgiError;
|
||||
if ((past->past_NumOutputChannels > 0) && (pvbuf[5] != sr))
|
||||
{ /* Output needed and rate different from current harware-rate. */
|
||||
if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */
|
||||
{
|
||||
ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
|
||||
another process is currently using output at %ld kHz.\n", sr, pvbuf[5]));
|
||||
result = paHostError; goto done; /* Will free again the inputbuffer */
|
||||
} /* that was just created above. */
|
||||
pvbuf[5] = sr; /* Then set output-rate. */
|
||||
if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
|
||||
goto sgiError;
|
||||
}
|
||||
}
|
||||
/*------------------------------------------ Construct an audio-port-configuration ----------*/
|
||||
sgiALconfig = ALnewconfig(); /* Change the SGI-AL-default-settings. */
|
||||
if (sgiALconfig == (ALconfig)0) /* sgiALconfig is released here after use! */
|
||||
goto sgiError; /* See that sgiALconfig is NOT released! */
|
||||
if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */
|
||||
goto sgiError;
|
||||
if (ALsetwidth (sgiALconfig, AL_SAMPLE_16)) /* Only meaningful when sample format for */
|
||||
goto sgiError; /* config is set to two's complement format. */
|
||||
/************************ Future versions might (dynamically) switch to 32-bit floats? *******
|
||||
if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.)
|
||||
goto sgiError;
|
||||
if (ALsetfloatmax (sgiALconfig, 1.0)) Only meaningful when sample format for config
|
||||
goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
|
||||
/*---------- ?? --------------------*/
|
||||
/* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
|
||||
minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate);
|
||||
past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
|
||||
minNumBuffers : past->past_NumUserBuffers;
|
||||
/*------------------------------------------------ Set internal AL queuesize (in samples) ----*/
|
||||
if (pahsc->pahsc_SamplesPerInputBuffer >= pahsc->pahsc_SamplesPerOutputBuffer)
|
||||
ALqsize = (long)pahsc->pahsc_SamplesPerInputBuffer;
|
||||
else /* Take the largest of the two amounts. */
|
||||
ALqsize = (long)pahsc->pahsc_SamplesPerOutputBuffer;
|
||||
ALqsize *= 4; /* 4 times as large as amount per transfer! */
|
||||
if (ALsetqueuesize(sgiALconfig, ALqsize)) /* Or should we use past_NumUserBuffers here? */
|
||||
goto sgiError; /* Using 2 times may give glitches... */
|
||||
/* Have to work on ALsetqueuesize() above, fillpoints in the thread are ok now. */
|
||||
|
||||
/* Do ALsetchannels() later, apart per input and/or output. */
|
||||
/*----------------------------------------------- OPEN 1 OR 2 AL-DEVICES: --------------------*/
|
||||
if (past->past_OutputDeviceID == past->past_InputDeviceID) /* Who SETS these devive-numbers? */
|
||||
{
|
||||
if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
|
||||
{
|
||||
DBUG(("PaHost_OpenStream: opening both input and output channels.\n"));
|
||||
/*------------------------- open output port: ----------------------------------*/
|
||||
if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
|
||||
goto sgiError; /* Returns 0 on success, -1 on failure. */
|
||||
pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
|
||||
if (pahsc->pahsc_ALportOUT == (ALport)0)
|
||||
goto sgiError;
|
||||
/*------------------------- open input port: -----------------------------------*/
|
||||
if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
|
||||
goto sgiError; /* Returns 0 on success, -1 on failure. */
|
||||
pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
|
||||
if (pahsc->pahsc_ALportIN == (ALport)0)
|
||||
goto sgiError; /* For some reason the "patest_wire.c"-test crashes! */
|
||||
} /* Probably due to too small buffersizes?.... */
|
||||
else
|
||||
{
|
||||
ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
|
||||
result = paHostError;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else /* (OutputDeviceID != InputDeviceID) */
|
||||
{
|
||||
if (past->past_NumOutputChannels > 0) /* WRITE-ONLY: */
|
||||
{
|
||||
/*------------------------- open output port: ----------------------------------*/
|
||||
DBUG(("PaHost_OpenStream: opening %d output channel(s) on %s.\n",
|
||||
past->past_NumOutputChannels, padOUT->pad_DeviceName));
|
||||
if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
|
||||
goto sgiError; /* Returns 0 on success, -1 on failure. */
|
||||
pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
|
||||
if (pahsc->pahsc_ALportOUT == (ALport)0)
|
||||
goto sgiError;
|
||||
}
|
||||
if (past->past_NumInputChannels > 0) /* READ-ONLY: */
|
||||
{
|
||||
/*------------------------- open input port: -----------------------------------*/
|
||||
DBUG(("PaHost_OpenStream: opening %d input channel(s) on %s.\n",
|
||||
past->past_NumInputChannels, padIN->pad_DeviceName));
|
||||
if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
|
||||
goto sgiError; /* Returns 0 on success, -1 on failure. */
|
||||
pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
|
||||
if (pahsc->pahsc_ALportIN == (ALport)0)
|
||||
goto sgiError;
|
||||
}
|
||||
}
|
||||
DBUG(("PaHost_OpenStream() succeeded.\n"));
|
||||
goto done; /* (no errors occured) */
|
||||
sgiError:
|
||||
result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
|
||||
done:
|
||||
if (sgiALconfig)
|
||||
ALfreeconfig(sgiALconfig); /* We don't need that struct anymore. */
|
||||
if (result != paNoError)
|
||||
PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */
|
||||
return result;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*/
|
||||
PaError PaHost_StartOutput(internalPortAudioStream *past)
|
||||
{
|
||||
return paNoError; /* Hmm, not implemented yet? */
|
||||
}
|
||||
PaError PaHost_StartInput(internalPortAudioStream *past)
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*/
|
||||
PaError PaHost_StartEngine(internalPortAudioStream *past)
|
||||
{
|
||||
PaHostSoundControl *pahsc;
|
||||
usptr_t *arena;
|
||||
if (!past) /* Test argument. */
|
||||
{
|
||||
ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
|
||||
return paBadStreamPtr;
|
||||
}
|
||||
pahsc = (PaHostSoundControl*)past->past_DeviceData;
|
||||
if (!pahsc)
|
||||
{
|
||||
ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n"));
|
||||
return paHostError;
|
||||
}
|
||||
past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */
|
||||
past->past_StopNow = 0; /* Why don't we check pahsc for NULL? */
|
||||
past->past_IsActive = 1;
|
||||
|
||||
/* Although the pthread_create() function, as well as <pthread.h>, may be */
|
||||
/* available in IRIX, use sproc() on SGI to create audio-background-thread. */
|
||||
/* (Linux/oss uses pthread_create() instead of __clone() because: */
|
||||
/* - pthread_create also works for other UNIX systems like Solaris, */
|
||||
/* - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().) */
|
||||
|
||||
usconfig(CONF_ARENATYPE, US_SHAREDONLY); /* (From SGI-AL-examples, file */
|
||||
arena = usinit(tmpnam(0)); /* motifexample.c, function */
|
||||
SendSema = usnewpollsema(arena, 0); /* InitializeAudioProcess().) */
|
||||
RcvSema = usnewsema(arena, 1); /* 1= common mutual exclusion semaphore, where 1 and only 1 process
|
||||
will be permitted through a semaphore at a time. Values > 1
|
||||
imply that up to val resources may be simultaneously used, but requests
|
||||
for more than val resources cause the calling process to block until a
|
||||
resource comes free (by a process holding a resource performing a
|
||||
usvsema(). IS THIS usnewsema() TOO PLATFORM SPECIFIC? */
|
||||
prctl(PR_SETEXITSIG, 0);/* No not (void*)9, but 0, which doesn't kill the parent! */
|
||||
/* PR_SETEXITSIG controls whether all members of a share group will be
|
||||
signaled if any one of them leaves the share group
|
||||
(either via exit() or exec()). If second
|
||||
argument, interpreted as an int is 0, then normal IRIX
|
||||
process termination rules apply, namely that the parent
|
||||
is sent a SIGCLD upon death of child, but no indication
|
||||
of death of parent is given. If the second argument is
|
||||
a valid signal number then if any member
|
||||
of a share group leaves the share group, a signal is
|
||||
sent to ALL surviving members of the share group. */
|
||||
/* SPAWN AUDIO-CHILD: */
|
||||
pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */
|
||||
PR_SALL, /* new process, or -1. */
|
||||
(void*)past); /* Pass past as optional */
|
||||
if (pahsc->pahsc_threadPID == -1) /* third void-ptr-arg. */
|
||||
{
|
||||
ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
|
||||
sPaHostError = oserror(); /* Pass native error-number to shared area. */
|
||||
return paHostError; /* But return the generic error-number. */
|
||||
}
|
||||
return paNoError; /* Hmmm, errno may come from other threads in same group! */
|
||||
} /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */
|
||||
|
||||
/*------------------------------------------------------------------------------*/
|
||||
PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
|
||||
{
|
||||
int hres;
|
||||
long timeOut;
|
||||
PaError result = paNoError;
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl*)past->past_DeviceData;
|
||||
|
||||
if (pahsc == NULL)
|
||||
return result; /* paNoError */
|
||||
past->past_StopSoon = 1; /* Tell background thread to stop generating */
|
||||
if (abort) /* more and to let current data play out. If */
|
||||
past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */
|
||||
|
||||
/*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/
|
||||
usvsema(SendSema); /* Increments count associated with SendSema. */
|
||||
/* Wait for the response. */
|
||||
uspsema(RcvSema); /* Decrements count of previously allocated */
|
||||
/* semaphore specified by RcvSema. */
|
||||
/* Must be sure sub-process is really there,... otherwise above semas block?! */
|
||||
if (pahsc->pahsc_threadPID != -1) /* Did we really init it to -1 somewhere? */
|
||||
{
|
||||
/* How to JOIN a "sproc"-thread to recover memory resources????? */
|
||||
/* hres = pthread_join( pahsc->pahsc_ThreadPID, NULL ); */
|
||||
DBUG(("PaHost_StopEngine() is about to KILL(SIGKILL) audio-thread.\n"));
|
||||
if (kill(pahsc->pahsc_threadPID, SIGKILL)) /* Or SIGTERM or SIGQUIT(core) */
|
||||
{ /* Returns -1 in case of error. */
|
||||
result = paHostError;
|
||||
sPaHostError = oserror(); /* Hmmm, other threads may also write here! */
|
||||
ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n"));
|
||||
}
|
||||
else
|
||||
pahsc->pahsc_threadPID = -1; /* Notify that we've killed this thread. */
|
||||
}
|
||||
past->past_IsActive = 0; /* Even when kill() failed and pahsc_threadPID still there??? */
|
||||
return result;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------*/
|
||||
PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
|
||||
{
|
||||
return paNoError; /* Not implemented yet? */
|
||||
}
|
||||
PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
PaError PaHost_CloseStream(internalPortAudioStream *past)
|
||||
{
|
||||
PaHostSoundControl *pahsc;
|
||||
PaError result = paNoError;
|
||||
|
||||
if (past == NULL)
|
||||
return paBadStreamPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if (pahsc == NULL) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
|
||||
return result; /* This test prevents from freeing NULL-pointers. */
|
||||
|
||||
if (pahsc->pahsc_ALportIN)
|
||||
{
|
||||
if (ALcloseport(pahsc->pahsc_ALportIN))
|
||||
result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
|
||||
else /* But go on anyway... to release other stuff... */
|
||||
pahsc->pahsc_ALportIN = (ALport)0;
|
||||
}
|
||||
if (pahsc->pahsc_ALportOUT)
|
||||
{
|
||||
if (ALcloseport(pahsc->pahsc_ALportOUT))
|
||||
result = translateSGIerror();
|
||||
else
|
||||
pahsc->pahsc_ALportOUT = (ALport)0;
|
||||
}
|
||||
if (pahsc->pahsc_NativeInputBuffer)
|
||||
{
|
||||
free(pahsc->pahsc_NativeInputBuffer);
|
||||
pahsc->pahsc_NativeInputBuffer = NULL;
|
||||
}
|
||||
if (pahsc->pahsc_NativeOutputBuffer)
|
||||
{
|
||||
free(pahsc->pahsc_NativeOutputBuffer);
|
||||
pahsc->pahsc_NativeOutputBuffer = NULL;
|
||||
}
|
||||
PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl)); /* Not just free(pahsc) because */
|
||||
past->past_DeviceData = NULL; /* PaHost_OpenStream(); allocated FAST (thus mpinned?) MEM. */
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Determine minimum number of buffers required for this host based
|
||||
** on minimum latency. Latency can be optionally set by user by setting
|
||||
** an environment variable. For example, to set latency to 200 msec, put:
|
||||
** set PA_MIN_LATENCY_MSEC=200
|
||||
** in the AUTOEXEC.BAT file and reboot.
|
||||
** If the environment variable is not set, then the latency will be
|
||||
** determined based on the OS. Windows NT has higher latency than Win95.
|
||||
*/
|
||||
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
|
||||
|
||||
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */
|
||||
/* Do we HAVE to do it this way under IRIX???.... */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
PaError PaHost_Term(void) /* Frees all of the linked devices. */
|
||||
{ /* Called by Pa_Terminate() from pa_lib.c. */
|
||||
internalPortAudioDevice *pad = sDeviceList,
|
||||
*nxt;
|
||||
while (pad)
|
||||
{
|
||||
DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
|
||||
nxt = pad->pad_Next;
|
||||
PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
|
||||
pad = nxt; /* PaHost_Init allocated this FAST MEM.*/
|
||||
}
|
||||
sDeviceList = (internalPortAudioDevice*)NULL;
|
||||
return 0; /* Got rid of sNumDevices=0; */
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */
|
||||
{
|
||||
#if 0
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = msec / 1000;
|
||||
timeout.tv_usec = (msec % 1000) * 1000;
|
||||
select(0, NULL, NULL, NULL, &timeout);
|
||||
#else
|
||||
long usecs = msec * 1000;
|
||||
usleep( usecs );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------*/
|
||||
/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
|
||||
/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
|
||||
/* a call to PaHost_FreeFastMemory(). */
|
||||
void *PaHost_AllocateFastMemory(long numBytes)
|
||||
{
|
||||
void *addr = malloc(numBytes); /* mpin() reads into memory all pages over the given */
|
||||
if (addr) /* range and locks the pages into memory. A counter */
|
||||
{ /* is incremented each time the page is locked. The */
|
||||
if (mpin(addr, numBytes)) /* superuser can lock as many pages as it wishes, */
|
||||
{ /* others are limited to the configurable PLOCK_MA. */
|
||||
ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n"));
|
||||
#if 1
|
||||
free(addr); /* You MAY cut out these 2 lines to be less strict, */
|
||||
addr = NULL; /* you then only get the warning but PA goes on... */
|
||||
#endif /* Only problem then may be corresponding munpin() */
|
||||
} /* call at PaHost_FreeFastMemory(), below. */
|
||||
memset(addr, 0, numBytes); /* Locks established with mlock are not inherited by */
|
||||
} /* a child process after a fork. Furthermore, IRIX- */
|
||||
return addr; /* man-pages warn against mixing both mpin and mlock */
|
||||
} /* in 1 piece of code, so stick to mpin()/munpin() ! */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------------------*/
|
||||
/* Free memory that could be accessed in real-time. This call MUST be balanced with a */
|
||||
/* call to PaHost_AllocateFastMemory(). */
|
||||
void PaHost_FreeFastMemory(void *addr, long numBytes)
|
||||
{
|
||||
if (addr)
|
||||
{
|
||||
if (munpin(addr, numBytes)) /* Will munpin() fail when it was never mpinned? */
|
||||
ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n"));
|
||||
free(addr); /* But go on, try to release it, just warn... */
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------*/
|
||||
PaError PaHost_StreamActive( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc;
|
||||
if (past == NULL)
|
||||
return paBadStreamPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if (pahsc == NULL)
|
||||
return paInternalError;
|
||||
return (PaError)(past->past_IsActive != 0);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
|
||||
{
|
||||
internalPortAudioStream *past = (internalPortAudioStream *) stream;
|
||||
/* FIXME - return actual frames played, not frames generated.
|
||||
** Need to query the output device somehow.
|
||||
*/
|
||||
return past->past_FrameCount;
|
||||
}
|
||||
183
pa_tests/debug_dual.c
Normal file
183
pa_tests/debug_dual.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* $Id$
|
||||
* debug_dual.c
|
||||
* Try to open TWO streams on separate cards.
|
||||
* Play a sine sweep using the Portable Audio api for several seconds.
|
||||
* Hacked test for debugging PA.
|
||||
*
|
||||
* Author: Phil Burk <philburk@softsynth.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define DEV_ID_1 (13)
|
||||
#define DEV_ID_2 (15)
|
||||
#define NUM_SECONDS (8)
|
||||
#define SLEEP_DUR (800)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#if 0
|
||||
#define MIN_LATENCY_MSEC (200)
|
||||
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#else
|
||||
#define NUM_BUFFERS (0)
|
||||
#endif
|
||||
#define MIN_FREQ (100.0f)
|
||||
#define MAX_FREQ (4000.0f)
|
||||
#define FREQ_SCALAR (1.00002f)
|
||||
#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (400)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
|
||||
float phase_increment;
|
||||
float left_phase;
|
||||
float right_phase;
|
||||
}
|
||||
paTestData;
|
||||
/* Convert phase between and 1.0 to sine value
|
||||
* using linear interpolation.
|
||||
*/
|
||||
float LookupSine( paTestData *data, float phase );
|
||||
float LookupSine( paTestData *data, float phase )
|
||||
{
|
||||
float fIndex = phase*TABLE_SIZE;
|
||||
int index = (int) fIndex;
|
||||
float fract = fIndex - index;
|
||||
float lo = data->sine[index];
|
||||
float hi = data->sine[index+1];
|
||||
float val = lo + fract*(hi-lo);
|
||||
return val;
|
||||
}
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = LookupSine(data, data->left_phase); /* left */
|
||||
*out++ = LookupSine(data, data->right_phase); /* right */
|
||||
data->left_phase += data->phase_increment;
|
||||
if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
|
||||
data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
|
||||
if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
|
||||
/* sweep frequency then start over. */
|
||||
data->phase_increment *= FREQ_SCALAR;
|
||||
if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID,
|
||||
paTestData *data );
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream1, *stream2;
|
||||
PaError err;
|
||||
paTestData DATA1, DATA2;
|
||||
printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS );
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = TestStart( &stream1, DEV_ID_1, &DATA1 );
|
||||
if( err != paNoError ) goto error;
|
||||
err = TestStart( &stream2, DEV_ID_2, &DATA2 );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Hit ENTER\n");
|
||||
getchar();
|
||||
err = Pa_StopStream( stream1 );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StopStream( stream2 );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
int i;
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data->sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data->sine[TABLE_SIZE] = data->sine[0]; // set guard point
|
||||
data->left_phase = data->right_phase = 0.0;
|
||||
data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
|
||||
printf("PortAudio Test: output device = %d\n", devID );
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
devID,
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
*streamPtr = stream;
|
||||
return 0;
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
179
pa_tests/debug_multi_in.c
Normal file
179
pa_tests/debug_multi_in.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* $Id$
|
||||
* debug_multi_in.c
|
||||
* Pass output from each of multiple channels
|
||||
* to a stereo output using the Portable Audio api.
|
||||
* Hacked test for debugging PA.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "portaudio.h"
|
||||
//#define INPUT_DEVICE_NAME ("EWS88 MT Interleaved Rec")
|
||||
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
|
||||
//#define OUTPUT_DEVICE (18)
|
||||
#define SAMPLE_RATE (22050)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#define MIN_LATENCY_MSEC (400)
|
||||
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
int liveChannel;
|
||||
int numChannels;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
float *in = (float*)inputBuffer;
|
||||
int i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
if( in == NULL ) return 0;
|
||||
for( i=0; i<(int)framesPerBuffer; i++ )
|
||||
{
|
||||
/* Copy one channel of input to output. */
|
||||
*out++ = in[data->liveChannel];
|
||||
*out++ = in[data->liveChannel];
|
||||
in += data->numChannels;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int PaFindDeviceByName( const char *name )
|
||||
{
|
||||
int i;
|
||||
int numDevices;
|
||||
const PaDeviceInfo *pdi;
|
||||
int len = strlen( name );
|
||||
PaDeviceID result = paNoDevice;
|
||||
numDevices = Pa_CountDevices();
|
||||
for( i=0; i<numDevices; i++ )
|
||||
{
|
||||
pdi = Pa_GetDeviceInfo( i );
|
||||
if( strncmp( name, pdi->name, len ) == 0 )
|
||||
{
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
PaDeviceID inputDevice;
|
||||
const PaDeviceInfo *pdi;
|
||||
printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS );
|
||||
data.liveChannel = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
#ifdef INPUT_DEVICE_NAME
|
||||
printf("Try to use device: %s\n", INPUT_DEVICE_NAME );
|
||||
inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME);
|
||||
if( inputDevice == paNoDevice )
|
||||
{
|
||||
printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME );
|
||||
inputDevice = Pa_GetDefaultInputDeviceID();
|
||||
}
|
||||
#else
|
||||
printf("Using default input device.\n");
|
||||
inputDevice = Pa_GetDefaultInputDeviceID();
|
||||
#endif
|
||||
pdi = Pa_GetDeviceInfo( inputDevice );
|
||||
if( pdi == NULL )
|
||||
{
|
||||
printf("Could not get device info!\n");
|
||||
goto error;
|
||||
}
|
||||
data.numChannels = pdi->maxInputChannels;
|
||||
printf("Input Device name is %s\n", pdi->name );
|
||||
printf("Input Device has %d channels.\n", pdi->maxInputChannels);
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
inputDevice,
|
||||
pdi->maxInputChannels,
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
OUTPUT_DEVICE,
|
||||
2,
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
data.liveChannel = 0;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
for( i=0; i<data.numChannels; i++ )
|
||||
{
|
||||
data.liveChannel = i;
|
||||
printf("Channel %d being sent to output. Hit ENTER for next channel.", i );
|
||||
fflush(stdout);
|
||||
getchar();
|
||||
}
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
165
pa_tests/debug_multi_out.c
Normal file
165
pa_tests/debug_multi_out.c
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* $Id$
|
||||
* debug_multi.c
|
||||
* Play a sine wave on each of multiple channels,
|
||||
* using the Portable Audio api.
|
||||
* Hacked test for debugging PA.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_CHANNELS (8)
|
||||
// #define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
|
||||
#define OUTPUT_DEVICE (18)
|
||||
#define NUM_SECONDS (NUM_CHANNELS*4)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_CHANNEL (SAMPLE_RATE/2)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#define MIN_LATENCY_MSEC (400)
|
||||
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (800)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int phase;
|
||||
int liveChannel;
|
||||
int count;
|
||||
unsigned int sampsToGo;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
int i, j;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
if( data->sampsToGo < framesPerBuffer )
|
||||
{
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<(int)framesPerBuffer; i++ )
|
||||
{
|
||||
for( j=0; j<NUM_CHANNELS; j++ )
|
||||
{
|
||||
/* Output sine wave only on live channel. */
|
||||
*out++ = (j==data->liveChannel) ? data->sine[data->phase] : 0.0f;
|
||||
/* Play each channel at a higher frequency. */
|
||||
data->phase += 1 + data->liveChannel;
|
||||
if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
|
||||
}
|
||||
/* Switch channels every so often. */
|
||||
if( --data->count <= 0 )
|
||||
{
|
||||
data->count = FRAMES_PER_CHANNEL;
|
||||
data->liveChannel += 1;
|
||||
if( data->liveChannel >= NUM_CHANNELS ) data->liveChannel = 0;
|
||||
}
|
||||
}
|
||||
data->sampsToGo -= framesPerBuffer;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalSamps;
|
||||
printf("PortAudio Test: output sine wave. %d buffers\n", NUM_BUFFERS );
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.phase = 0;
|
||||
data.count = FRAMES_PER_CHANNEL;
|
||||
data.liveChannel = 0;
|
||||
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, /* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
OUTPUT_DEVICE,
|
||||
NUM_CHANNELS,
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Is callback being called?\n");
|
||||
for( i=0; i<NUM_SECONDS; i++ )
|
||||
{
|
||||
printf("data.sampsToGo = %d\n", data.sampsToGo );
|
||||
Pa_Sleep( 1000 );
|
||||
}
|
||||
/* Stop sound until ENTER hit. */
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_CloseStream( stream );
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
297
pa_tests/debug_record.c
Normal file
297
pa_tests/debug_record.c
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* $Id$
|
||||
* debug_record.c
|
||||
* Record input into an array.
|
||||
* Save array to a file.
|
||||
* Based on patest_record.c but with various ugly debug hacks thrown in.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include "portaudio.h"
|
||||
#define SAMPLE_RATE (22050)
|
||||
#define NUM_SECONDS (10)
|
||||
#define SLEEP_DUR_MSEC (200)
|
||||
#define REC_BUF_FRAMES (1<<10)
|
||||
#define NUM_REC_BUFS (0)
|
||||
#if 0
|
||||
#define PA_SAMPLE_TYPE paFloat32
|
||||
typedef float SAMPLE;
|
||||
#else
|
||||
#define PA_SAMPLE_TYPE paInt16
|
||||
typedef short SAMPLE;
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
long frameIndex; /* Index into sample array. */
|
||||
long maxFrameIndex;
|
||||
long samplesPerFrame;
|
||||
SAMPLE *recordedSamples;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int recordCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
SAMPLE *rptr = (SAMPLE*)inputBuffer;
|
||||
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
|
||||
long framesToCalc;
|
||||
unsigned long i;
|
||||
int finished;
|
||||
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
|
||||
|
||||
(void) outputBuffer; /* Prevent unused variable warnings. */
|
||||
(void) outTime;
|
||||
|
||||
if( framesLeft < framesPerBuffer )
|
||||
{
|
||||
framesToCalc = framesLeft;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
framesToCalc = framesPerBuffer;
|
||||
finished = 0;
|
||||
}
|
||||
if( inputBuffer == NULL )
|
||||
{
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*wptr++ = 0; /* left */
|
||||
*wptr++ = 0; /* right */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*wptr++ = *rptr++; /* left */
|
||||
*wptr++ = *rptr++; /* right */
|
||||
}
|
||||
}
|
||||
data->frameIndex += framesToCalc;
|
||||
return finished;
|
||||
}
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int playCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
|
||||
SAMPLE *wptr = (SAMPLE*)outputBuffer;
|
||||
unsigned long i;
|
||||
int finished;
|
||||
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
|
||||
if( outputBuffer == NULL ) return 0;
|
||||
(void) inputBuffer; /* Prevent unused variable warnings. */
|
||||
(void) outTime;
|
||||
|
||||
if( framesLeft < framesPerBuffer )
|
||||
{
|
||||
/* final buffer... */
|
||||
for( i=0; i<framesLeft; i++ )
|
||||
{
|
||||
*wptr++ = *rptr++; /* left */
|
||||
*wptr++ = *rptr++; /* right */
|
||||
}
|
||||
for( ; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*wptr++ = 0; /* left */
|
||||
*wptr++ = 0; /* right */
|
||||
}
|
||||
data->frameIndex += framesLeft;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*wptr++ = *rptr++; /* left */
|
||||
*wptr++ = *rptr++; /* right */
|
||||
}
|
||||
data->frameIndex += framesPerBuffer;
|
||||
finished = 0;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
long totalFrames;
|
||||
long numSamples;
|
||||
long numBytes;
|
||||
long i;
|
||||
printf("patest_record.c\n"); fflush(stdout);
|
||||
|
||||
data.frameIndex = 0;
|
||||
data.samplesPerFrame = 2;
|
||||
data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE;
|
||||
|
||||
printf("totalFrames = %d\n", totalFrames ); fflush(stdout);
|
||||
numSamples = totalFrames * data.samplesPerFrame;
|
||||
numBytes = numSamples * sizeof(SAMPLE);
|
||||
data.recordedSamples = (SAMPLE *) malloc( numBytes );
|
||||
if( data.recordedSamples == NULL )
|
||||
{
|
||||
printf("Could not allocate record array.\n");
|
||||
exit(1);
|
||||
}
|
||||
for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Record some audio. */
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_GetDefaultInputDeviceID(),
|
||||
data.samplesPerFrame, /* stereo input */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
paNoDevice,
|
||||
0,
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
REC_BUF_FRAMES, /* frames per buffer */
|
||||
NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
recordCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Now recording!\n"); fflush(stdout);
|
||||
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
|
||||
{
|
||||
if( Pa_StreamActive( stream ) <= 0)
|
||||
{
|
||||
printf("Stream inactive!\n");
|
||||
break;
|
||||
}
|
||||
if( data.maxFrameIndex <= data.frameIndex )
|
||||
{
|
||||
printf("Buffer recording complete.\n");
|
||||
break;
|
||||
}
|
||||
Pa_Sleep(100);
|
||||
printf("index = %d\n", data.frameIndex );
|
||||
}
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
/* Playback recorded data. */
|
||||
data.frameIndex = 0;
|
||||
printf("Begin playback.\n"); fflush(stdout);
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* NO input */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(),
|
||||
data.samplesPerFrame, /* stereo output */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
1024, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
playCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Waiting for playback to finish.\n"); fflush(stdout);
|
||||
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
|
||||
{
|
||||
Pa_Sleep(100);
|
||||
printf("index = %d\n", data.frameIndex );
|
||||
}
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Done.\n"); fflush(stdout);
|
||||
{
|
||||
SAMPLE max = 0;
|
||||
SAMPLE posVal;
|
||||
int i;
|
||||
for( i=0; i<numSamples; i++ )
|
||||
{
|
||||
posVal = data.recordedSamples[i];
|
||||
if( posVal < 0 ) posVal = -posVal;
|
||||
if( posVal > max ) max = posVal;
|
||||
}
|
||||
printf("Largest recorded sample = %d\n", max );
|
||||
}
|
||||
/* Write recorded data to a file. */
|
||||
#if 0
|
||||
{
|
||||
FILE *fid;
|
||||
fid = fopen("recorded.raw", "wb");
|
||||
if( fid == NULL )
|
||||
{
|
||||
printf("Could not open file.");
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite( data.recordedSamples, data.samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
|
||||
fclose( fid );
|
||||
printf("Wrote data to 'recorded.raw'\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free( data.recordedSamples );
|
||||
Pa_Terminate();
|
||||
return 0;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
if( err == paHostError )
|
||||
{
|
||||
fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
192
pa_tests/debug_sine.c
Normal file
192
pa_tests/debug_sine.c
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* $Id$
|
||||
* debug_sine.c
|
||||
* Play a sine sweep using the Portable Audio api for several seconds.
|
||||
* Hacked test for debugging PA.
|
||||
*
|
||||
* Author: Phil Burk <philburk@softsynth.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
|
||||
//#define OUTPUT_DEVICE (11)
|
||||
#define NUM_SECONDS (8)
|
||||
#define SLEEP_DUR (800)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#if 1
|
||||
#define MIN_LATENCY_MSEC (200)
|
||||
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#else
|
||||
#define NUM_BUFFERS (0)
|
||||
#endif
|
||||
#define MIN_FREQ (100.0f)
|
||||
#define MAX_FREQ (4000.0f)
|
||||
#define FREQ_SCALAR (1.00002f)
|
||||
#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (400)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
|
||||
float phase_increment;
|
||||
float left_phase;
|
||||
float right_phase;
|
||||
unsigned int framesToGo;
|
||||
}
|
||||
paTestData;
|
||||
/* Convert phase between and 1.0 to sine value
|
||||
* using linear interpolation.
|
||||
*/
|
||||
float LookupSine( paTestData *data, float phase );
|
||||
float LookupSine( paTestData *data, float phase )
|
||||
{
|
||||
float fIndex = phase*TABLE_SIZE;
|
||||
int index = (int) fIndex;
|
||||
float fract = fIndex - index;
|
||||
float lo = data->sine[index];
|
||||
float hi = data->sine[index+1];
|
||||
float val = lo + fract*(hi-lo);
|
||||
return val;
|
||||
}
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
int framesToCalc;
|
||||
int i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
if( data->framesToGo < framesPerBuffer )
|
||||
{
|
||||
framesToCalc = data->framesToGo;
|
||||
data->framesToGo = 0;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
framesToCalc = framesPerBuffer;
|
||||
data->framesToGo -= framesPerBuffer;
|
||||
}
|
||||
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*out++ = LookupSine(data, data->left_phase); /* left */
|
||||
*out++ = LookupSine(data, data->right_phase); /* right */
|
||||
data->left_phase += data->phase_increment;
|
||||
if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
|
||||
data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
|
||||
if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
|
||||
/* sweep frequency then start over. */
|
||||
data->phase_increment *= FREQ_SCALAR;
|
||||
if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<(int)framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalSamps;
|
||||
printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
|
||||
data.left_phase = data.right_phase = 0.0;
|
||||
data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
|
||||
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
printf("totalSamps = %d\n", totalSamps );
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
OUTPUT_DEVICE,
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Is callback being called?\n");
|
||||
for( i=0; i<((NUM_SECONDS+1)*1000); i+=SLEEP_DUR )
|
||||
{
|
||||
printf("data.framesToGo = %d\n", data.framesToGo ); fflush(stdout);
|
||||
Pa_Sleep( SLEEP_DUR );
|
||||
}
|
||||
/* Stop sound until ENTER hit. */
|
||||
printf("Call Pa_StopStream()\n");
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
138
pa_tests/debug_sine_wait.c
Normal file
138
pa_tests/debug_sine_wait.c
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* $Id$
|
||||
* debug_sine_wait.c
|
||||
* Play a sine wave using the Portable Audio api until we hit ENTER.
|
||||
*
|
||||
* Authors:
|
||||
* Ross Bencina <rossb@audiomulch.com>
|
||||
* Phil Burk <philburk@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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
|
||||
#define NUM_SECONDS (20)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#define NUM_BUFFERS (16)
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
}
|
||||
paTestData;
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Hit ENTER to stop.\n");
|
||||
gets(stdin);
|
||||
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
114
pa_tests/debug_test1.c
Normal file
114
pa_tests/debug_test1.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* $Id$
|
||||
patest1.c
|
||||
Ring modulate the audio input with a 441hz sine wave for 20 seconds
|
||||
using the Portable Audio api
|
||||
Author: Ross Bencina <rossb@audiomulch.com>
|
||||
Modifications:
|
||||
April 5th, 2001 - PLB - Check for NULL inputBuffer.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
float sine[100];
|
||||
int phase;
|
||||
int sampsToGo;
|
||||
}
|
||||
patest1data;
|
||||
static int patest1Callback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long bufferFrames,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
patest1data *data = (patest1data*)userData;
|
||||
float *in = (float*)inputBuffer;
|
||||
float *out = (float*)outputBuffer;
|
||||
int framesToCalc = bufferFrames;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
if(inputBuffer == NULL) return 0;
|
||||
if( data->sampsToGo < bufferFrames )
|
||||
{
|
||||
finished = 1;
|
||||
}
|
||||
for( i=0; i<bufferFrames; i++ )
|
||||
{
|
||||
*out++ = *in++;
|
||||
*out++ = *in++;
|
||||
if( data->phase >= 100 )
|
||||
data->phase = 0;
|
||||
}
|
||||
data->sampsToGo -= bufferFrames;
|
||||
/* zero remainder of final buffer if not already done */
|
||||
for( ; i<bufferFrames; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
int main(int argc, char* argv[]);
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
PaStream *stream;
|
||||
PaError err;
|
||||
patest1data data;
|
||||
int i;
|
||||
int inputDevice = Pa_GetDefaultInputDeviceID();
|
||||
int outputDevice = Pa_GetDefaultOutputDeviceID();
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<100; i++ )
|
||||
data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
|
||||
data.phase = 0;
|
||||
data.sampsToGo = 44100 * 4; // 20 seconds
|
||||
/* initialise portaudio subsytem */
|
||||
Pa_Initialize();
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
inputDevice,
|
||||
2, /* stereo input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
outputDevice,
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
44100.,
|
||||
// 22050, /* half second buffers */
|
||||
// 4, /* four buffers */
|
||||
512, /* half second buffers */
|
||||
0, /* four buffers */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patest1Callback,
|
||||
&data );
|
||||
if( err == paNoError )
|
||||
{
|
||||
err = Pa_StartStream( stream );
|
||||
// printf( "Press any key to end.\n" );
|
||||
// getc( stdin ); //wait for input before exiting
|
||||
// Pa_AbortStream( stream );
|
||||
|
||||
printf( "Waiting for stream to complete...\n" );
|
||||
|
||||
while( Pa_StreamActive( stream ) )
|
||||
Pa_Sleep(1000); /* sleep until playback has finished */
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "An error occured while opening the portaudio stream\n" );
|
||||
if( err == paHostError )
|
||||
fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
|
||||
else
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
}
|
||||
Pa_Terminate();
|
||||
printf( "bye\n" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
98
pa_tests/pa_devs.c
Normal file
98
pa_tests/pa_devs.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* $Id$
|
||||
* pa_devs.c
|
||||
* List available devices.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i,j;
|
||||
int numDevices;
|
||||
const PaDeviceInfo *pdi;
|
||||
PaError err;
|
||||
Pa_Initialize();
|
||||
numDevices = Pa_CountDevices();
|
||||
if( numDevices < 0 )
|
||||
{
|
||||
printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
|
||||
err = numDevices;
|
||||
goto error;
|
||||
}
|
||||
printf("Number of devices = %d\n", numDevices );
|
||||
for( i=0; i<numDevices; i++ )
|
||||
{
|
||||
pdi = Pa_GetDeviceInfo( i );
|
||||
printf("---------------------------------------------- #%d", i );
|
||||
if( i == Pa_GetDefaultInputDeviceID() ) printf(" DefaultInput");
|
||||
if( i == Pa_GetDefaultOutputDeviceID() ) printf(" DefaultOutput");
|
||||
printf("\nName = %s\n", pdi->name );
|
||||
printf("Max Inputs = %d", pdi->maxInputChannels );
|
||||
printf(", Max Outputs = %d\n", pdi->maxOutputChannels );
|
||||
if( pdi->numSampleRates == -1 )
|
||||
{
|
||||
printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Sample Rates =");
|
||||
for( j=0; j<pdi->numSampleRates; j++ )
|
||||
{
|
||||
printf(" %8.2f,", pdi->sampleRates[j] );
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("Native Sample Formats = ");
|
||||
if( pdi->nativeSampleFormats & paInt8 ) printf("paInt8, ");
|
||||
if( pdi->nativeSampleFormats & paUInt8 ) printf("paUInt8, ");
|
||||
if( pdi->nativeSampleFormats & paInt16 ) printf("paInt16, ");
|
||||
if( pdi->nativeSampleFormats & paInt32 ) printf("paInt32, ");
|
||||
if( pdi->nativeSampleFormats & paFloat32 ) printf("paFloat32, ");
|
||||
if( pdi->nativeSampleFormats & paInt24 ) printf("paInt24, ");
|
||||
if( pdi->nativeSampleFormats & paPackedInt24 ) printf("paPackedInt24, ");
|
||||
printf("\n");
|
||||
}
|
||||
Pa_Terminate();
|
||||
|
||||
printf("----------------------------------------------\n");
|
||||
return 0;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
155
pa_tests/pa_fuzz.c
Normal file
155
pa_tests/pa_fuzz.c
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* $Id$
|
||||
* pa_fuzz.c
|
||||
* Distort input like a fuzz boz.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
/*
|
||||
** Note that many of the older ISA sound cards on PCs do NOT support
|
||||
** full duplex audio (simultaneous record and playback).
|
||||
** And some only support full duplex at lower sample rates.
|
||||
*/
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define PA_SAMPLE_TYPE paFloat32
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
|
||||
typedef float SAMPLE;
|
||||
|
||||
float CubicAmplifier( float input );
|
||||
static int fuzzCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
|
||||
/* Non-linear amplifier with soft distortion curve. */
|
||||
float CubicAmplifier( float input )
|
||||
{
|
||||
float output, temp;
|
||||
if( input < 0.0 )
|
||||
{
|
||||
temp = input + 1.0f;
|
||||
output = (temp * temp * temp) - 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = input - 1.0f;
|
||||
output = (temp * temp * temp) + 1.0f;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x))))
|
||||
|
||||
static int gNumNoInputs = 0;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int fuzzCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
SAMPLE *out = (SAMPLE*)outputBuffer;
|
||||
SAMPLE *in = (SAMPLE*)inputBuffer;
|
||||
unsigned int i;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) userData;
|
||||
|
||||
if( inputBuffer == NULL )
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left - silent */
|
||||
*out++ = 0; /* right - silent */
|
||||
}
|
||||
gNumNoInputs += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = FUZZ(*in++); /* left - distorted */
|
||||
*out++ = *in++; /* right - clean */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_GetDefaultInputDeviceID(), /* default output device */
|
||||
2, /* stereo input */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
0, // paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
fuzzCallback,
|
||||
NULL );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
printf("Hit ENTER to stop program.\n");
|
||||
getchar();
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
printf("Finished. gNumNoInputs = %d\n", gNumNoInputs );
|
||||
Pa_Terminate();
|
||||
return 0;
|
||||
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
164
pa_tests/pa_minlat.c
Normal file
164
pa_tests/pa_minlat.c
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* $Id$
|
||||
* paminlat.c
|
||||
* Experiment with different numbers of buffers to determine the
|
||||
* minimum latency for a computer.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define DEFAULT_BUFFER_SIZE (128)
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
}
|
||||
paTestData;
|
||||
/* Very simple synthesis routine to generate two sine waves. */
|
||||
static int paminlatCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned int i;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void main( int argc, char **argv );
|
||||
void main( int argc, char **argv )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int go;
|
||||
int numBuffers = 0;
|
||||
int minBuffers = 0;
|
||||
int framesPerBuffer;
|
||||
double sampleRate = 44100.0;
|
||||
char str[256];
|
||||
printf("paminlat - Determine minimum latency for your computer.\n");
|
||||
printf(" usage: paminlat {framesPerBuffer}\n");
|
||||
printf(" for example: paminlat 256\n");
|
||||
printf("Adjust your stereo until you hear a smooth tone in each speaker.\n");
|
||||
printf("Then try to find the smallest number of buffers that still sounds smooth.\n");
|
||||
printf("Note that the sound will stop momentarily when you change the number of buffers.\n");
|
||||
/* Get bufferSize from command line. */
|
||||
framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE;
|
||||
printf("Frames per buffer = %d\n", framesPerBuffer );
|
||||
/* Initialise sinusoidal wavetable. */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
/* Ask PortAudio for the recommended minimum number of buffers. */
|
||||
numBuffers = minBuffers = Pa_GetMinNumBuffers( framesPerBuffer, sampleRate );
|
||||
printf("NumBuffers set to %d based on a call to Pa_GetMinNumBuffers()\n", numBuffers );
|
||||
/* Try different numBuffers in a loop. */
|
||||
go = 1;
|
||||
while( go )
|
||||
{
|
||||
|
||||
printf("Latency = framesPerBuffer * numBuffers = %d * %d = %d frames = %d msecs.\n",
|
||||
framesPerBuffer, numBuffers, framesPerBuffer*numBuffers,
|
||||
(int)((1000 * framesPerBuffer * numBuffers) / sampleRate) );
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
sampleRate,
|
||||
framesPerBuffer,/* 46 msec buffers */
|
||||
numBuffers, /* number of buffers */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
paminlatCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
if( stream == NULL ) goto error;
|
||||
/* Start audio. */
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
/* Ask user for a new number of buffers. */
|
||||
printf("\nMove windows around to see if the sound glitches.\n");
|
||||
printf("NumBuffers currently %d, enter new number, or 'q' to quit: ", numBuffers );
|
||||
gets( str );
|
||||
if( str[0] == 'q' ) go = 0;
|
||||
else
|
||||
{
|
||||
numBuffers = atol( str );
|
||||
if( numBuffers < minBuffers )
|
||||
{
|
||||
printf( "numBuffers below minimum of %d! Set to minimum!!!\n", minBuffers );
|
||||
numBuffers = minBuffers;
|
||||
}
|
||||
}
|
||||
/* Stop sound until ENTER hit. */
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
}
|
||||
printf("A good setting for latency would be somewhat higher than\n");
|
||||
printf("the minimum latency that worked.\n");
|
||||
printf("PortAudio: Test finished.\n");
|
||||
Pa_Terminate();
|
||||
return;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
}
|
||||
317
pa_tests/paqa_devs.c
Normal file
317
pa_tests/paqa_devs.c
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* $Id$
|
||||
* paqa_devs.c
|
||||
* Self Testing Quality Assurance app for PortAudio
|
||||
* Try to open each device and run through all the
|
||||
* possible configurations.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "pa_trace.h"
|
||||
/****************************************** Definitions ***********/
|
||||
#define MODE_INPUT (0)
|
||||
#define MODE_OUTPUT (1)
|
||||
typedef struct PaQaData
|
||||
{
|
||||
unsigned long framesLeft;
|
||||
int numChannels;
|
||||
int bytesPerSample;
|
||||
int mode;
|
||||
short sawPhase;
|
||||
PaSampleFormat format;
|
||||
}
|
||||
PaQaData;
|
||||
/****************************************** Prototypes ***********/
|
||||
static void TestDevices( int mode );
|
||||
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
|
||||
int numChannels );
|
||||
static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
|
||||
int numChannels, PaSampleFormat format );
|
||||
static int QaCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
/****************************************** Globals ***********/
|
||||
static int gNumPassed = 0;
|
||||
static int gNumFailed = 0;
|
||||
/****************************************** Macros ***********/
|
||||
/* Print ERROR if it fails. Tally success or failure. */
|
||||
/* Odd do-while wrapper seems to be needed for some compilers. */
|
||||
#define EXPECT(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if ((_exp)) {\
|
||||
/* printf("SUCCESS for %s\n", #_exp ); */ \
|
||||
gNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("ERROR - 0x%x - %s for %s\n", result, \
|
||||
((result == 0) ? "-" : Pa_GetErrorText(result)), \
|
||||
#_exp ); \
|
||||
gNumFailed++; \
|
||||
goto error; \
|
||||
} \
|
||||
} while(0)
|
||||
/*******************************************************************/
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int QaCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
unsigned long i;
|
||||
short phase;
|
||||
PaQaData *data = (PaQaData *) userData;
|
||||
(void) inputBuffer;
|
||||
(void) outTime;
|
||||
|
||||
/* Play simle sawtooth wave. */
|
||||
if( data->mode == MODE_OUTPUT )
|
||||
{
|
||||
phase = data->sawPhase;
|
||||
switch( data->format )
|
||||
{
|
||||
case paFloat32:
|
||||
{
|
||||
float *out = (float *) outputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
phase += 0x123;
|
||||
*out++ = (float) (phase * (1.0 / 32768.0));
|
||||
if( data->numChannels == 2 )
|
||||
{
|
||||
*out++ = (float) (phase * (1.0 / 32768.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case paInt32:
|
||||
{
|
||||
int *out = (int *) outputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
phase += 0x123;
|
||||
*out++ = ((int) phase ) << 16;
|
||||
if( data->numChannels == 2 )
|
||||
{
|
||||
*out++ = ((int) phase ) << 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case paInt16:
|
||||
{
|
||||
short *out = (short *) outputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
phase += 0x123;
|
||||
*out++ = phase;
|
||||
if( data->numChannels == 2 )
|
||||
{
|
||||
*out++ = phase;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
unsigned char *out = (unsigned char *) outputBuffer;
|
||||
unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
|
||||
for( i=0; i<numBytes; i++ )
|
||||
{
|
||||
*out++ = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
data->sawPhase = phase;
|
||||
}
|
||||
/* Are we through yet? */
|
||||
if( data->framesLeft > framesPerBuffer )
|
||||
{
|
||||
AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
|
||||
data->framesLeft -= framesPerBuffer;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
|
||||
data->framesLeft = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PaError result;
|
||||
EXPECT( ((result=Pa_Initialize()) == 0) );
|
||||
printf("Test OUTPUT ---------------\n");
|
||||
TestDevices( MODE_OUTPUT );
|
||||
printf("Test INPUT ---------------\n");
|
||||
TestDevices( MODE_INPUT );
|
||||
error:
|
||||
Pa_Terminate();
|
||||
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
|
||||
}
|
||||
/*******************************************************************
|
||||
* Try each output device, through its full range of capabilities. */
|
||||
static void TestDevices( int mode )
|
||||
{
|
||||
int id,jc,kr;
|
||||
int maxChannels;
|
||||
const PaDeviceInfo *pdi;
|
||||
int numDevices = Pa_CountDevices();
|
||||
/* Iterate through all devices. */
|
||||
for( id=0; id<numDevices; id++ )
|
||||
{
|
||||
pdi = Pa_GetDeviceInfo( id );
|
||||
/* Try 1 to maxChannels on each device. */
|
||||
maxChannels = ( mode == MODE_INPUT ) ? pdi->maxInputChannels : pdi->maxOutputChannels;
|
||||
for( jc=1; jc<=maxChannels; jc++ )
|
||||
{
|
||||
printf("Name = %s\n", pdi->name );
|
||||
/* Try each legal sample rate. */
|
||||
if( pdi->numSampleRates == -1 )
|
||||
{
|
||||
double low, high;
|
||||
low = pdi->sampleRates[0];
|
||||
high = pdi->sampleRates[1];
|
||||
if( low < 8000.0 ) low = 8000.0;
|
||||
TestFormats( mode, id, low, jc );
|
||||
#define TESTSR(sr) {if(((sr)>=low) && ((sr)<=high)) TestFormats( mode, id, (sr), jc ); }
|
||||
|
||||
TESTSR(11025.0);
|
||||
TESTSR(22050.0);
|
||||
TESTSR(44100.0);
|
||||
TestFormats( mode, id, high, jc );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( kr=0; kr<pdi->numSampleRates; kr++ )
|
||||
{
|
||||
TestFormats( mode, id, pdi->sampleRates[kr], jc );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*******************************************************************/
|
||||
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
|
||||
int numChannels )
|
||||
{
|
||||
TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); /* */
|
||||
TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); /* */
|
||||
TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); /* */
|
||||
/* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */
|
||||
}
|
||||
/*******************************************************************/
|
||||
static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
|
||||
int numChannels, PaSampleFormat format )
|
||||
{
|
||||
PortAudioStream *stream = NULL;
|
||||
PaError result;
|
||||
PaQaData myData;
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %d -------\n",
|
||||
( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
|
||||
deviceID, sampleRate, numChannels, format);
|
||||
/* Setup data for synthesis thread. */
|
||||
myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */
|
||||
myData.numChannels = numChannels;
|
||||
myData.mode = mode;
|
||||
myData.format = format;
|
||||
switch( format )
|
||||
{
|
||||
case paFloat32:
|
||||
case paInt32:
|
||||
case paInt24:
|
||||
myData.bytesPerSample = 4;
|
||||
break;
|
||||
case paPackedInt24:
|
||||
myData.bytesPerSample = 3;
|
||||
break;
|
||||
default:
|
||||
myData.bytesPerSample = 2;
|
||||
break;
|
||||
}
|
||||
EXPECT( ((result = Pa_OpenStream(
|
||||
&stream,
|
||||
( mode == MODE_INPUT ) ? deviceID : paNoDevice,
|
||||
( mode == MODE_INPUT ) ? numChannels : 0,
|
||||
format,
|
||||
NULL,
|
||||
( mode == MODE_OUTPUT ) ? deviceID : paNoDevice,
|
||||
( mode == MODE_OUTPUT ) ? numChannels : 0,
|
||||
format,
|
||||
NULL,
|
||||
sampleRate,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == 0) );
|
||||
if( stream )
|
||||
{
|
||||
PaTimestamp oldStamp, newStamp;
|
||||
unsigned long oldFrames;
|
||||
int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400;
|
||||
int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
|
||||
int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate);
|
||||
if( msec < minDelay ) msec = minDelay;
|
||||
printf("msec = %d\n", msec); /**/
|
||||
EXPECT( ((result=Pa_StartStream( stream )) == 0) );
|
||||
/* Check to make sure PortAudio is advancing timeStamp. */
|
||||
result = paNoError;
|
||||
oldStamp = Pa_StreamTime(stream);
|
||||
Pa_Sleep(msec);
|
||||
newStamp = Pa_StreamTime(stream);
|
||||
printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/
|
||||
EXPECT( (oldStamp < newStamp) );
|
||||
/* Check to make sure callback is decrementing framesLeft. */
|
||||
oldFrames = myData.framesLeft;
|
||||
Pa_Sleep(msec);
|
||||
printf("oldFrames = %d, myData.framesLeft = %d\n", oldFrames, myData.framesLeft ); /**/
|
||||
EXPECT( (oldFrames > myData.framesLeft) );
|
||||
EXPECT( ((result=Pa_CloseStream( stream )) == 0) );
|
||||
stream = NULL;
|
||||
}
|
||||
error:
|
||||
if( stream != NULL ) Pa_CloseStream( stream );
|
||||
return result;
|
||||
}
|
||||
305
pa_tests/paqa_errs.c
Normal file
305
pa_tests/paqa_errs.c
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* $Id$
|
||||
* paqa_devs.c
|
||||
* Self Testing Quality Assurance app for PortAudio
|
||||
* Do lots of bad things to test error reporting.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
/****************************************** Definitions ***********/
|
||||
#define MODE_INPUT (0)
|
||||
#define MODE_OUTPUT (1)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#define SAMPLE_RATE (44100.0)
|
||||
#define NUM_BUFFERS (0)
|
||||
typedef struct PaQaData
|
||||
{
|
||||
unsigned long framesLeft;
|
||||
int numChannels;
|
||||
int bytesPerSample;
|
||||
int mode;
|
||||
}
|
||||
PaQaData;
|
||||
/****************************************** Prototypes ***********/
|
||||
static void TestDevices( int mode );
|
||||
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
|
||||
int numChannels );
|
||||
static int TestBadOpens( void );
|
||||
static int TestBadActions( void );
|
||||
static int QaCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
/****************************************** Globals ***********/
|
||||
static int gNumPassed = 0;
|
||||
static int gNumFailed = 0;
|
||||
/****************************************** Macros ***********/
|
||||
/* Print ERROR if it fails. Tally success or failure. */
|
||||
/* Odd do-while wrapper seems to be needed for some compilers. */
|
||||
#define EXPECT(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if ((_exp)) {\
|
||||
gNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
|
||||
gNumFailed++; \
|
||||
goto error; \
|
||||
} \
|
||||
} while(0)
|
||||
#define HOPEFOR(_exp) \
|
||||
do \
|
||||
{ \
|
||||
if ((_exp)) {\
|
||||
gNumPassed++; \
|
||||
} \
|
||||
else { \
|
||||
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
|
||||
gNumFailed++; \
|
||||
} \
|
||||
} while(0)
|
||||
/*******************************************************************/
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int QaCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
unsigned long i;
|
||||
unsigned char *out = (unsigned char *) outputBuffer;
|
||||
PaQaData *data = (PaQaData *) userData;
|
||||
(void) inputBuffer; /* Prevent "unused variable" warnings. */
|
||||
(void) outTime;
|
||||
|
||||
/* Zero out buffer so we don't hear terrible noise. */
|
||||
if( data->mode == MODE_OUTPUT )
|
||||
{
|
||||
unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
|
||||
for( i=0; i<numBytes; i++ )
|
||||
{
|
||||
*out++ = 0;
|
||||
}
|
||||
}
|
||||
/* Are we through yet? */
|
||||
if( data->framesLeft > framesPerBuffer )
|
||||
{
|
||||
data->framesLeft -= framesPerBuffer;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->framesLeft = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PaError result;
|
||||
EXPECT( ((result=Pa_Initialize()) == 0) );
|
||||
TestBadOpens();
|
||||
TestBadActions();
|
||||
error:
|
||||
Pa_Terminate();
|
||||
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
static int TestBadOpens( void )
|
||||
{
|
||||
PortAudioStream *stream = NULL;
|
||||
PaError result;
|
||||
PaQaData myData;
|
||||
/* Setup data for synthesis thread. */
|
||||
myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
|
||||
myData.numChannels = 1;
|
||||
myData.mode = MODE_OUTPUT;
|
||||
HOPEFOR( (/* No devices specified. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidDeviceId) );
|
||||
HOPEFOR( ( /* Out of range input device specified. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_CountDevices(), 0, paFloat32, NULL,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidDeviceId) );
|
||||
|
||||
HOPEFOR( ( /* Out of range output device specified. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_CountDevices(), 0, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidDeviceId) );
|
||||
HOPEFOR( ( /* Zero input channels. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_GetDefaultInputDeviceID(), 0, paFloat32, NULL,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidChannelCount) );
|
||||
HOPEFOR( ( /* Zero output channels. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 0, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidChannelCount) );
|
||||
HOPEFOR( ( /* Nonzero input channels but no device. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL,
|
||||
paNoDevice, 2, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidChannelCount) );
|
||||
|
||||
HOPEFOR( ( /* Nonzero output channels but no device. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 2, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidChannelCount) );
|
||||
HOPEFOR( ( /* NULL stream pointer. */
|
||||
(result = Pa_OpenStream(
|
||||
NULL,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paBadStreamPtr) );
|
||||
HOPEFOR( ( /* Low sample rate. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
1.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidSampleRate) );
|
||||
HOPEFOR( ( /* High sample rate. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
10000000.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidSampleRate) );
|
||||
HOPEFOR( ( /* NULL callback. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
NULL,
|
||||
&myData )
|
||||
) == paNullCallback) );
|
||||
HOPEFOR( ( /* Bad flag. */
|
||||
(result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
(1<<3),
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == paInvalidFlag) );
|
||||
if( stream != NULL ) Pa_CloseStream( stream );
|
||||
return result;
|
||||
}
|
||||
/*******************************************************************/
|
||||
static int TestBadActions( void )
|
||||
{
|
||||
PortAudioStream *stream = NULL;
|
||||
PaError result;
|
||||
PaQaData myData;
|
||||
/* Setup data for synthesis thread. */
|
||||
myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
|
||||
myData.numChannels = 1;
|
||||
myData.mode = MODE_OUTPUT;
|
||||
/* Default output. */
|
||||
EXPECT( ((result = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice, 0, paFloat32, NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
|
||||
paClipOff,
|
||||
QaCallback,
|
||||
&myData )
|
||||
) == 0) );
|
||||
HOPEFOR( ((result = Pa_StartStream( NULL )) == paBadStreamPtr) );
|
||||
HOPEFOR( ((result = Pa_StopStream( NULL )) == paBadStreamPtr) );
|
||||
HOPEFOR( ((result = Pa_StreamActive( NULL )) == paBadStreamPtr) );
|
||||
HOPEFOR( ((result = Pa_CloseStream( NULL )) == paBadStreamPtr) );
|
||||
HOPEFOR( ((result = (PaError)Pa_StreamTime( NULL )) != 0) );
|
||||
HOPEFOR( ((result = (PaError)Pa_GetCPULoad( NULL )) != 0) );
|
||||
error:
|
||||
if( stream != NULL ) Pa_CloseStream( stream );
|
||||
return result;
|
||||
}
|
||||
114
pa_tests/patest1.c
Normal file
114
pa_tests/patest1.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
$Id$
|
||||
patest1.c
|
||||
Ring modulate the audio input with a sine wave for 20 seconds
|
||||
using the Portable Audio api
|
||||
Author: Ross Bencina <rossb@audiomulch.com>
|
||||
Modifications:
|
||||
April 5th, 2001 - PLB - Check for NULL inputBuffer.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
float sine[100];
|
||||
int phase;
|
||||
int sampsToGo;
|
||||
}
|
||||
patest1data;
|
||||
static int patest1Callback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long bufferFrames,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
patest1data *data = (patest1data*)userData;
|
||||
float *in = (float*)inputBuffer;
|
||||
float *out = (float*)outputBuffer;
|
||||
int framesToCalc = bufferFrames;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
/* Check to see if any input data is available. */
|
||||
if(inputBuffer == NULL) return 0;
|
||||
if( data->sampsToGo < bufferFrames )
|
||||
{
|
||||
framesToCalc = data->sampsToGo;
|
||||
finished = 1;
|
||||
}
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*out++ = *in++ * data->sine[data->phase]; /* left */
|
||||
*out++ = *in++ * data->sine[data->phase++]; /* right */
|
||||
if( data->phase >= 100 )
|
||||
data->phase = 0;
|
||||
}
|
||||
data->sampsToGo -= framesToCalc;
|
||||
/* zero remainder of final buffer if not already done */
|
||||
for( ; i<bufferFrames; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
int main(int argc, char* argv[]);
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
PaStream *stream;
|
||||
PaError err;
|
||||
patest1data data;
|
||||
int i;
|
||||
int inputDevice = Pa_GetDefaultInputDeviceID();
|
||||
int outputDevice = Pa_GetDefaultOutputDeviceID();
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<100; i++ )
|
||||
data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
|
||||
data.phase = 0;
|
||||
data.sampsToGo = 44100 * 20; // 20 seconds
|
||||
/* initialise portaudio subsytem */
|
||||
Pa_Initialize();
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
inputDevice,
|
||||
2, /* stereo input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
outputDevice,
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
44100.,
|
||||
512, /* small buffers */
|
||||
0, /* let PA determine number of buffers */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patest1Callback,
|
||||
&data );
|
||||
if( err == paNoError )
|
||||
{
|
||||
err = Pa_StartStream( stream );
|
||||
printf( "Press any key to end.\n" );
|
||||
getc( stdin ); //wait for input before exiting
|
||||
Pa_AbortStream( stream );
|
||||
|
||||
printf( "Waiting for stream to complete...\n" );
|
||||
|
||||
while( Pa_StreamActive( stream ) )
|
||||
Pa_Sleep(1000); /* sleep until playback has finished */
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "An error occured while opening the portaudio stream\n" );
|
||||
if( err == paHostError )
|
||||
fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
|
||||
else
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
}
|
||||
Pa_Terminate();
|
||||
printf( "bye\n" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
181
pa_tests/patest_buffer.c
Normal file
181
pa_tests/patest_buffer.c
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_buffer.c
|
||||
* Test opening streams with different buffer sizes.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (1)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
|
||||
#define BUFFER_TABLE 9
|
||||
long buffer_table[] = {200,256,500,512,600, 723, 1000, 1024, 2345};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
unsigned int sampsToGo;
|
||||
}
|
||||
paTestData;
|
||||
PaError TestOnce( int buffersize );
|
||||
|
||||
static int patest1Callback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patest1Callback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
short *out = (short*)outputBuffer;
|
||||
unsigned int i;
|
||||
int finished = 0;
|
||||
(void) inputBuffer; /* Prevent "unused variable" warnings. */
|
||||
(void) outTime;
|
||||
|
||||
if( data->sampsToGo < framesPerBuffer )
|
||||
{
|
||||
/* final buffer... */
|
||||
|
||||
for( i=0; i<data->sampsToGo; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
data->sampsToGo -= framesPerBuffer;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
PaError err;
|
||||
printf("Test opening streams with different buffer sizes\n\n");
|
||||
|
||||
for (i = 0 ; i < BUFFER_TABLE; i++)
|
||||
{
|
||||
printf("Buffer size %d\n", buffer_table[i]);
|
||||
err = TestOnce(buffer_table[i]);
|
||||
if( err < 0 ) return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PaError TestOnce( int buffersize )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalSamps;
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paInt16, /* sample format */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paInt16, /* sample format */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
buffersize, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patest1Callback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Waiting for sound to finish.\n");
|
||||
Pa_Sleep(1000);
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
return paNoError;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
156
pa_tests/patest_clip.c
Normal file
156
pa_tests/patest_clip.c
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_clip.c
|
||||
* Play a sine wave using the Portable Audio api for several seconds
|
||||
* at an amplitude that would require clipping.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (4)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct paTestData
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
float amplitude;
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
}
|
||||
paTestData;
|
||||
PaError PlaySine( paTestData *data, unsigned long flags, float amplitude );
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int sineCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
float amplitude = data->amplitude;
|
||||
unsigned int i;
|
||||
(void) inputBuffer; /* Prevent "unused variable" warnings. */
|
||||
(void) outTime;
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = amplitude * data->sine[data->left_phase]; /* left */
|
||||
*out++ = amplitude * data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PaError err;
|
||||
paTestData DATA;
|
||||
int i;
|
||||
printf("PortAudio Test: output sine wave with and without clipping.\n");
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
printf("\nHalf amplitude. Should sound like sine wave.\n"); fflush(stdout);
|
||||
err = PlaySine( &DATA, paClipOff | paDitherOff, 0.5f );
|
||||
if( err < 0 ) goto error;
|
||||
printf("\nFull amplitude. Should sound like sine wave.\n"); fflush(stdout);
|
||||
err = PlaySine( &DATA, paClipOff | paDitherOff, 0.999f );
|
||||
if( err < 0 ) goto error;
|
||||
printf("\nOver range with clipping and dithering turned OFF. Should sound very nasty.\n");
|
||||
fflush(stdout);
|
||||
err = PlaySine( &DATA, paClipOff | paDitherOff, 1.1f );
|
||||
if( err < 0 ) goto error;
|
||||
printf("\nOver range with clipping and dithering turned ON. Should sound smoother than previous.\n");
|
||||
fflush(stdout);
|
||||
err = PlaySine( &DATA, paNoFlag, 1.1f );
|
||||
if( err < 0 ) goto error;
|
||||
printf("\nOver range with paClipOff but dithering ON.\n"
|
||||
"That forces clipping ON so it should sound the same as previous.\n");
|
||||
fflush(stdout);
|
||||
err = PlaySine( &DATA, paClipOff, 1.1f );
|
||||
if( err < 0 ) goto error;
|
||||
return 0;
|
||||
error:
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return 1;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
PaError PlaySine( paTestData *data, unsigned long flags, float amplitude )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
data->left_phase = data->right_phase = 0;
|
||||
data->amplitude = amplitude;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
1024,
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
flags, /* we won't output out of range samples so don't bother clipping them */
|
||||
sineCallback,
|
||||
data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Sleep( NUM_SECONDS * 1000 );
|
||||
printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
return paNoError;
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
151
pa_tests/patest_dither.c
Normal file
151
pa_tests/patest_dither.c
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_dither.c
|
||||
* Attempt to hear difference between dithered and non-dithered signal.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (4)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct paTestData
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
float amplitude;
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
}
|
||||
paTestData;
|
||||
PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude );
|
||||
static int sineCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int sineCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
float amplitude = data->amplitude;
|
||||
unsigned int i;
|
||||
(void) outTime;
|
||||
(void) inputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = amplitude * data->sine[data->left_phase]; /* left */
|
||||
*out++ = amplitude * data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PaError err;
|
||||
paTestData DATA;
|
||||
int i;
|
||||
float amplitude = 32.0 / (1<<15);
|
||||
printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n");
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
printf("\nNo treatment..\n"); fflush(stdout);
|
||||
err = PlaySine( &DATA, paClipOff | paDitherOff, amplitude );
|
||||
if( err < 0 ) goto error;
|
||||
printf("\nClip.\n");
|
||||
fflush(stdout);
|
||||
err = PlaySine( &DATA, paDitherOff, amplitude );
|
||||
if( err < 0 ) goto error;
|
||||
printf("\nClip and Dither.\n");
|
||||
fflush(stdout);
|
||||
err = PlaySine( &DATA, paNoFlag, amplitude );
|
||||
if( err < 0 ) goto error;
|
||||
return 0;
|
||||
error:
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
data->left_phase = data->right_phase = 0;
|
||||
data->amplitude = amplitude;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
1024,
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
flags, /* we won't output out of range samples so don't bother clipping them */
|
||||
sineCallback,
|
||||
(void *)data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Sleep( NUM_SECONDS * 1000 );
|
||||
printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
return paNoError;
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
171
pa_tests/patest_latency.c
Normal file
171
pa_tests/patest_latency.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Hear the latency caused by bug buffers.
|
||||
* Play a sine wave and change frequency based on letter input.
|
||||
*
|
||||
* Author: Phil Burk <philburk@softsynth.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#if 1
|
||||
#define MIN_LATENCY_MSEC (2000)
|
||||
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#else
|
||||
#define NUM_BUFFERS (0)
|
||||
#endif
|
||||
#define MIN_FREQ (100.0f)
|
||||
#define CalcPhaseIncrement(freq) ((freq)/SAMPLE_RATE)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (400)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
|
||||
float phase_increment;
|
||||
float left_phase;
|
||||
float right_phase;
|
||||
}
|
||||
paTestData;
|
||||
float LookupSine( paTestData *data, float phase );
|
||||
/* Convert phase between and 1.0 to sine value
|
||||
* using linear interpolation.
|
||||
*/
|
||||
float LookupSine( paTestData *data, float phase )
|
||||
{
|
||||
float fIndex = phase*TABLE_SIZE;
|
||||
int index = (int) fIndex;
|
||||
float fract = fIndex - index;
|
||||
float lo = data->sine[index];
|
||||
float hi = data->sine[index+1];
|
||||
float val = lo + fract*(hi-lo);
|
||||
return val;
|
||||
}
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
int i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = LookupSine(data, data->left_phase); /* left */
|
||||
*out++ = LookupSine(data, data->right_phase); /* right */
|
||||
data->left_phase += data->phase_increment;
|
||||
if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
|
||||
data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
|
||||
if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int done = 0;
|
||||
printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
|
||||
data.left_phase = data.right_phase = 0.0;
|
||||
data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
OUTPUT_DEVICE,
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Play ASCII keyboard. Hit 'q' to stop. (Use RETURN key on Mac)\n");
|
||||
while ( !done )
|
||||
{
|
||||
float freq;
|
||||
int index;
|
||||
char c;
|
||||
do
|
||||
{
|
||||
c = getchar();
|
||||
}
|
||||
while( c < ' '); /* Strip white space and control chars. */
|
||||
|
||||
if( c == 'q' ) done = 1;
|
||||
index = c % 26;
|
||||
freq = MIN_FREQ + (index * 40.0);
|
||||
data.phase_increment = CalcPhaseIncrement(freq);
|
||||
}
|
||||
printf("Call Pa_StopStream()\n");
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
163
pa_tests/patest_leftright.c
Normal file
163
pa_tests/patest_leftright.c
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_leftright.c
|
||||
* Play different tone sine waves that alternate between left and right channel.
|
||||
* The low tone should be on the left channel.
|
||||
*
|
||||
* Authors:
|
||||
* Ross Bencina <rossb@audiomulch.com>
|
||||
* Phil Burk <philburk@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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (8)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (512)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
int toggle;
|
||||
int countDown;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
if( data->toggle )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
}
|
||||
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
|
||||
if( data->countDown < 0 )
|
||||
{
|
||||
data->countDown = SAMPLE_RATE;
|
||||
data->toggle = !data->toggle;
|
||||
}
|
||||
data->countDown -= framesPerBuffer;
|
||||
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int timeout;
|
||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
data.countDown = SAMPLE_RATE;
|
||||
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Play for several seconds.\n");
|
||||
timeout = NUM_SECONDS * 4;
|
||||
while( timeout > 0 )
|
||||
{
|
||||
printf("Countdown = %d, Toggle = %d\n", data.countDown, data.toggle );
|
||||
fflush( stdout );
|
||||
Pa_Sleep( 1000/4 );
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
124
pa_tests/patest_longsine.c
Normal file
124
pa_tests/patest_longsine.c
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_longsine.c
|
||||
* Play a sine wave using the Portable Audio api forever.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define SAMPLE_RATE (44100)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned int i;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
printf("PortAudio Test: output sine wave.\n");
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
256, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Hit ENTER to stop program.\n");
|
||||
getchar();
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
194
pa_tests/patest_many.c
Normal file
194
pa_tests/patest_many.c
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_many.c
|
||||
* Start and stop the PortAudio Driver multiple times.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (1)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
short sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
unsigned int sampsToGo;
|
||||
}
|
||||
paTestData;
|
||||
PaError TestOnce( void );
|
||||
static int patest1Callback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patest1Callback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
short *out = (short*)outputBuffer;
|
||||
unsigned int i;
|
||||
int finished = 0;
|
||||
(void) inputBuffer; /* Prevent "unused variable" warnings. */
|
||||
(void) outTime;
|
||||
|
||||
if( data->sampsToGo < framesPerBuffer )
|
||||
{
|
||||
/* final buffer... */
|
||||
|
||||
for( i=0; i<data->sampsToGo; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
data->sampsToGo -= framesPerBuffer;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
#ifdef MACINTOSH
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
PaError err;
|
||||
int numLoops = 10;
|
||||
printf("Loop %d times.\n", numLoops );
|
||||
for( i=0; i<numLoops; i++ )
|
||||
{
|
||||
printf("Loop %d out of %d.\n", i+1, numLoops );
|
||||
err = TestOnce();
|
||||
if( err < 0 ) return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
int main(int argc, char **argv);
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
PaError err;
|
||||
int i, numLoops = 10;
|
||||
if( argc > 1 )
|
||||
{
|
||||
numLoops = atoi(argv[1]);
|
||||
}
|
||||
for( i=0; i<numLoops; i++ )
|
||||
{
|
||||
printf("Loop %d out of %d.\n", i+1, numLoops );
|
||||
err = TestOnce();
|
||||
if( err < 0 ) return 1;
|
||||
}
|
||||
printf("Test complete.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
PaError TestOnce( void )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalSamps;
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paInt16, /* sample format */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paInt16, /* sample format */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
1024, /* frames per buffer */
|
||||
8, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patest1Callback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Waiting for sound to finish.\n");
|
||||
Pa_Sleep(1000);
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
return paNoError;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
155
pa_tests/patest_maxsines.c
Normal file
155
pa_tests/patest_maxsines.c
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_maxsines.c
|
||||
* How many sine waves can we calculate and play in less than 80% CPU Load.
|
||||
*
|
||||
* Authors:
|
||||
* Ross Bencina <rossb@audiomulch.com>
|
||||
* Phil Burk <philburk@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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
|
||||
#define MAX_SINES (500)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (512)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TWOPI (M_PI * 2.0)
|
||||
|
||||
typedef struct paTestData
|
||||
{
|
||||
int numSines;
|
||||
double phases[MAX_SINES];
|
||||
}
|
||||
paTestData;
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned long i;
|
||||
int j;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
float output = 0.0;
|
||||
double phaseInc = 0.02;
|
||||
double phase;
|
||||
for( j=0; j<data->numSines; j++ )
|
||||
{
|
||||
/* Advance phase of next oscillator. */
|
||||
phase = data->phases[j];
|
||||
phase += phaseInc;
|
||||
if( phase > TWOPI ) phase -= TWOPI;
|
||||
|
||||
phaseInc *= 1.02;
|
||||
if( phaseInc > 0.5 ) phaseInc *= 0.5;
|
||||
|
||||
/* This is not a very efficient way to calc sines. */
|
||||
output += (float) sin( phase );
|
||||
data->phases[j] = phase;
|
||||
}
|
||||
|
||||
|
||||
*out++ = (float) (output / data->numSines);
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data = {0};
|
||||
double load;
|
||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
/* initialise sinusoidal wavetable */
|
||||
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
1, /* mono output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
do
|
||||
{
|
||||
data.numSines++;
|
||||
Pa_Sleep( 200 );
|
||||
|
||||
load = Pa_GetCPULoad( stream );
|
||||
printf("numSines = %d, CPU load = %f\n", data.numSines, load );
|
||||
}
|
||||
while( load < 0.8 );
|
||||
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
245
pa_tests/patest_pink.c
Normal file
245
pa_tests/patest_pink.c
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* $Id$
|
||||
patest_pink.c
|
||||
Generate Pink Noise using Gardner method.
|
||||
Optimization suggested by James McCartney uses a tree
|
||||
to select which random value to replace.
|
||||
x x x x x x x x x x x x x x x x
|
||||
x x x x x x x x
|
||||
x x x x
|
||||
x x
|
||||
x
|
||||
Tree is generated by counting trailing zeros in an increasing index.
|
||||
When the index is zero, no random number is selected.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define PINK_MAX_RANDOM_ROWS (30)
|
||||
#define PINK_RANDOM_BITS (24)
|
||||
#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
|
||||
typedef struct
|
||||
{
|
||||
long pink_Rows[PINK_MAX_RANDOM_ROWS];
|
||||
long pink_RunningSum; /* Used to optimize summing of generators. */
|
||||
int pink_Index; /* Incremented each sample. */
|
||||
int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
|
||||
float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
|
||||
}
|
||||
PinkNoise;
|
||||
/* Prototypes */
|
||||
static unsigned long GenerateRandomNumber( void );
|
||||
void InitializePinkNoise( PinkNoise *pink, int numRows );
|
||||
float GeneratePinkNoise( PinkNoise *pink );
|
||||
/************************************************************/
|
||||
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
|
||||
static unsigned long GenerateRandomNumber( void )
|
||||
{
|
||||
/* Change this seed for different random sequences. */
|
||||
static unsigned long randSeed = 22222;
|
||||
randSeed = (randSeed * 196314165) + 907633515;
|
||||
return randSeed;
|
||||
}
|
||||
/************************************************************/
|
||||
/* Setup PinkNoise structure for N rows of generators. */
|
||||
void InitializePinkNoise( PinkNoise *pink, int numRows )
|
||||
{
|
||||
int i;
|
||||
long pmax;
|
||||
pink->pink_Index = 0;
|
||||
pink->pink_IndexMask = (1<<numRows) - 1;
|
||||
/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
|
||||
pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
|
||||
pink->pink_Scalar = 1.0f / pmax;
|
||||
/* Initialize rows. */
|
||||
for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
|
||||
pink->pink_RunningSum = 0;
|
||||
}
|
||||
#define PINK_MEASURE
|
||||
#ifdef PINK_MEASURE
|
||||
float pinkMax = -999.0;
|
||||
float pinkMin = 999.0;
|
||||
#endif
|
||||
/* Generate Pink noise values between -1.0 and +1.0 */
|
||||
float GeneratePinkNoise( PinkNoise *pink )
|
||||
{
|
||||
long newRandom;
|
||||
long sum;
|
||||
float output;
|
||||
/* Increment and mask index. */
|
||||
pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
|
||||
/* If index is zero, don't update any random values. */
|
||||
if( pink->pink_Index != 0 )
|
||||
{
|
||||
/* Determine how many trailing zeros in PinkIndex. */
|
||||
/* This algorithm will hang if n==0 so test first. */
|
||||
int numZeros = 0;
|
||||
int n = pink->pink_Index;
|
||||
while( (n & 1) == 0 )
|
||||
{
|
||||
n = n >> 1;
|
||||
numZeros++;
|
||||
}
|
||||
/* Replace the indexed ROWS random value.
|
||||
* Subtract and add back to RunningSum instead of adding all the random
|
||||
* values together. Only one changes each time.
|
||||
*/
|
||||
pink->pink_RunningSum -= pink->pink_Rows[numZeros];
|
||||
newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
|
||||
pink->pink_RunningSum += newRandom;
|
||||
pink->pink_Rows[numZeros] = newRandom;
|
||||
}
|
||||
|
||||
/* Add extra white noise value. */
|
||||
newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
|
||||
sum = pink->pink_RunningSum + newRandom;
|
||||
/* Scale to range of -1.0 to 0.9999. */
|
||||
output = pink->pink_Scalar * sum;
|
||||
#ifdef PINK_MEASURE
|
||||
/* Check Min/Max */
|
||||
if( output > pinkMax ) pinkMax = output;
|
||||
else if( output < pinkMin ) pinkMin = output;
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
/*******************************************************************/
|
||||
#define PINK_TEST
|
||||
#ifdef PINK_TEST
|
||||
/* Context for callback routine. */
|
||||
typedef struct
|
||||
{
|
||||
PinkNoise leftPink;
|
||||
PinkNoise rightPink;
|
||||
unsigned int sampsToGo;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
int finished;
|
||||
int i;
|
||||
int numFrames;
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
(void) inputBuffer; /* Prevent "unused variable" warnings. */
|
||||
(void) outTime;
|
||||
|
||||
/* Are we almost at end. */
|
||||
if( data->sampsToGo < framesPerBuffer )
|
||||
{
|
||||
numFrames = data->sampsToGo;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
numFrames = framesPerBuffer;
|
||||
finished = 0;
|
||||
}
|
||||
for( i=0; i<numFrames; i++ )
|
||||
{
|
||||
*out++ = GeneratePinkNoise( &data->leftPink );
|
||||
*out++ = GeneratePinkNoise( &data->rightPink );
|
||||
}
|
||||
data->sampsToGo -= numFrames;
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int totalSamps;
|
||||
/* Initialize two pink noise signals with different numbers of rows. */
|
||||
InitializePinkNoise( &data.leftPink, 12 );
|
||||
InitializePinkNoise( &data.rightPink, 16 );
|
||||
/* Look at a few values. */
|
||||
{
|
||||
int i;
|
||||
float pink;
|
||||
for( i=0; i<20; i++ )
|
||||
{
|
||||
pink = GeneratePinkNoise( &data.leftPink );
|
||||
printf("Pink = %f\n", pink );
|
||||
}
|
||||
}
|
||||
data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
/* Open a stereo PortAudio stream so we can hear the result. */
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
44100.,
|
||||
2048, /* 46 msec buffers */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Waiting for sound to finish.\n");
|
||||
while( Pa_StreamActive( stream ) )
|
||||
{
|
||||
Pa_Sleep(100); /* SPIN! */
|
||||
}
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
#ifdef PINK_MEASURE
|
||||
printf("Pink min = %f, max = %f\n", pinkMin, pinkMax );
|
||||
#endif
|
||||
Pa_Terminate();
|
||||
return 0;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return 0;
|
||||
}
|
||||
#endif /* PINK_TEST */
|
||||
303
pa_tests/patest_record.c
Normal file
303
pa_tests/patest_record.c
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_record.c
|
||||
* Record input into an array.
|
||||
* Save array to a file.
|
||||
* Playback recorded data.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "portaudio.h"
|
||||
|
||||
/* #define SAMPLE_RATE (17932) /* Test failure to open with this value. */
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define NUM_SECONDS (5)
|
||||
|
||||
/* Select sample format. */
|
||||
#if 0
|
||||
#define PA_SAMPLE_TYPE paFloat32
|
||||
typedef float SAMPLE;
|
||||
#elif 0
|
||||
#define PA_SAMPLE_TYPE paInt16
|
||||
typedef short SAMPLE;
|
||||
#else
|
||||
#define PA_SAMPLE_TYPE paUInt8
|
||||
typedef unsigned char SAMPLE;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int frameIndex; /* Index into sample array. */
|
||||
int maxFrameIndex;
|
||||
int samplesPerFrame;
|
||||
SAMPLE *recordedSamples;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int recordCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
SAMPLE *rptr = (SAMPLE*)inputBuffer;
|
||||
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
|
||||
long framesToCalc;
|
||||
long i;
|
||||
int finished;
|
||||
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
|
||||
|
||||
(void) outputBuffer; /* Prevent unused variable warnings. */
|
||||
(void) outTime;
|
||||
|
||||
if( framesLeft < framesPerBuffer )
|
||||
{
|
||||
framesToCalc = framesLeft;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
framesToCalc = framesPerBuffer;
|
||||
finished = 0;
|
||||
}
|
||||
if( inputBuffer == NULL )
|
||||
{
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*wptr++ = 0; /* left */
|
||||
*wptr++ = 0; /* right */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*wptr++ = *rptr++; /* left */
|
||||
*wptr++ = *rptr++; /* right */
|
||||
}
|
||||
}
|
||||
data->frameIndex += framesToCalc;
|
||||
return finished;
|
||||
}
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int playCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
|
||||
SAMPLE *wptr = (SAMPLE*)outputBuffer;
|
||||
unsigned int i;
|
||||
int finished;
|
||||
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
|
||||
(void) inputBuffer; /* Prevent unused variable warnings. */
|
||||
(void) outTime;
|
||||
|
||||
if( framesLeft < framesPerBuffer )
|
||||
{
|
||||
/* final buffer... */
|
||||
for( i=0; i<framesLeft; i++ )
|
||||
{
|
||||
*wptr++ = *rptr++; /* left */
|
||||
*wptr++ = *rptr++; /* right */
|
||||
}
|
||||
for( ; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*wptr++ = 0; /* left */
|
||||
*wptr++ = 0; /* right */
|
||||
}
|
||||
data->frameIndex += framesLeft;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*wptr++ = *rptr++; /* left */
|
||||
*wptr++ = *rptr++; /* right */
|
||||
}
|
||||
data->frameIndex += framesPerBuffer;
|
||||
finished = 0;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalFrames;
|
||||
int numSamples;
|
||||
int numBytes;
|
||||
SAMPLE max, average, val;
|
||||
printf("patest_record.c\n"); fflush(stdout);
|
||||
|
||||
data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
|
||||
data.frameIndex = 0;
|
||||
data.samplesPerFrame = 2;
|
||||
numSamples = totalFrames * data.samplesPerFrame;
|
||||
|
||||
numBytes = numSamples * sizeof(SAMPLE);
|
||||
data.recordedSamples = (SAMPLE *) malloc( numBytes );
|
||||
if( data.recordedSamples == NULL )
|
||||
{
|
||||
printf("Could not allocate record array.\n");
|
||||
exit(1);
|
||||
}
|
||||
for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
|
||||
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Record some audio. -------------------------------------------- */
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_GetDefaultInputDeviceID(),
|
||||
data.samplesPerFrame, /* stereo input */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
paNoDevice,
|
||||
0,
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
1024, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
recordCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Now recording!!\n"); fflush(stdout);
|
||||
|
||||
while( Pa_StreamActive( stream ) )
|
||||
{
|
||||
Pa_Sleep(1000);
|
||||
printf("index = %d\n", data.frameIndex ); fflush(stdout);
|
||||
}
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Measure maximum peak amplitude. */
|
||||
max = 0;
|
||||
average = 0;
|
||||
for( i=0; i<numSamples; i++ )
|
||||
{
|
||||
val = data.recordedSamples[i];
|
||||
if( val < 0 ) val = -val; /* ABS */
|
||||
if( val > max )
|
||||
{
|
||||
max = val;
|
||||
}
|
||||
average += val;
|
||||
}
|
||||
printf("sample max amplitude = %d\n", max );
|
||||
average = average / numSamples;
|
||||
printf("sample average = %d\n", average );
|
||||
|
||||
/* Write recorded data to a file. */
|
||||
#if 0
|
||||
{
|
||||
FILE *fid;
|
||||
fid = fopen("recorded.raw", "wb");
|
||||
if( fid == NULL )
|
||||
{
|
||||
printf("Could not open file.");
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite( data.recordedSamples, data.samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
|
||||
fclose( fid );
|
||||
printf("Wrote data to 'recorded.raw'\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Playback recorded data. -------------------------------------------- */
|
||||
data.frameIndex = 0;
|
||||
printf("Begin playback.\n"); fflush(stdout);
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,
|
||||
0, /* NO input */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(),
|
||||
data.samplesPerFrame, /* stereo output */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
1024, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
playCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
if( stream )
|
||||
{
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Waiting for playback to finish.\n"); fflush(stdout);
|
||||
|
||||
while( Pa_StreamActive( stream ) ) Pa_Sleep(100);
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Done.\n"); fflush(stdout);
|
||||
}
|
||||
free( data.recordedSamples );
|
||||
|
||||
Pa_Terminate();
|
||||
return 0;
|
||||
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
41
pa_tests/patest_ringmix.c
Normal file
41
pa_tests/patest_ringmix.c
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* $Id$ */
|
||||
|
||||
#include "stdio.h"
|
||||
#include "portaudio.h"
|
||||
/* This will be called asynchronously by the PortAudio engine. */
|
||||
static int myCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
|
||||
{
|
||||
float *out = (float *) outputBuffer;
|
||||
float *in = (float *) inputBuffer;
|
||||
float leftInput, rightInput;
|
||||
unsigned int i;
|
||||
if( inputBuffer == NULL ) return 0;
|
||||
/* Read input buffer, process data, and fill output buffer. */
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
leftInput = *in++; /* Get interleaved samples from input buffer. */
|
||||
rightInput = *in++;
|
||||
*out++ = leftInput * rightInput; /* ring modulation */
|
||||
*out++ = 0.5f * (leftInput + rightInput); /* mix */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Open a PortAudioStream to input and output audio data. */
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
Pa_Initialize();
|
||||
Pa_OpenDefaultStream(
|
||||
&stream,
|
||||
2, 2, /* stereo input and output */
|
||||
paFloat32, 44100.0,
|
||||
64, 0, /* 64 frames per buffer, let PA determine numBuffers */
|
||||
myCallback, NULL );
|
||||
Pa_StartStream( stream );
|
||||
Pa_Sleep( 10000 ); /* Sleep for 10 seconds while processing. */
|
||||
Pa_StopStream( stream );
|
||||
Pa_CloseStream( stream );
|
||||
Pa_Terminate();
|
||||
return 0;
|
||||
}
|
||||
118
pa_tests/patest_saw.c
Normal file
118
pa_tests/patest_saw.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_saw.c
|
||||
* Play a simple sawtooth wave.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (4)
|
||||
#define SAMPLE_RATE (44100)
|
||||
typedef struct
|
||||
{
|
||||
float left_phase;
|
||||
float right_phase;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
/* Cast data passed through stream to our structure. */
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned int i;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->left_phase; /* left */
|
||||
*out++ = data->right_phase; /* right */
|
||||
/* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
|
||||
data->left_phase += 0.01f;
|
||||
/* When signal reaches top, drop back down. */
|
||||
if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
|
||||
/* higher pitch so we can distinguish left and right. */
|
||||
data->right_phase += 0.03f;
|
||||
if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
static paTestData data;
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
printf("PortAudio Test: output sawtooth wave.\n");
|
||||
/* Initialize our data for use by callback. */
|
||||
data.left_phase = data.right_phase = 0.0;
|
||||
/* Initialize library before making any other calls. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
/* Open an audio I/O stream. */
|
||||
err = Pa_OpenDefaultStream(
|
||||
&stream,
|
||||
0, /* no input channels */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
SAMPLE_RATE,
|
||||
256, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
/* Sleep for several seconds. */
|
||||
Pa_Sleep(NUM_SECONDS*1000);
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
138
pa_tests/patest_sine.c
Normal file
138
pa_tests/patest_sine.c
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* $Id$
|
||||
* $Id$
|
||||
* patest_sine.c
|
||||
* Play a sine wave using the Portable Audio api for several seconds.
|
||||
*
|
||||
* Authors:
|
||||
* Ross Bencina <rossb@audiomulch.com>
|
||||
* Phil Burk <philburk@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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
|
||||
#define NUM_SECONDS (5)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
}
|
||||
paTestData;
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Play for %d seconds.\n", NUM_SECONDS );
|
||||
Pa_Sleep( NUM_SECONDS * 1000 );
|
||||
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
184
pa_tests/patest_sine8.c
Normal file
184
pa_tests/patest_sine8.c
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_sine8.c
|
||||
* Play a sine wave using the Portable Audio api for several seconds.
|
||||
* Test 8 bit data.
|
||||
*
|
||||
* Author: Ross Bencina <rossb@audiomulch.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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (8)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define TEST_UNSIGNED (1)
|
||||
#if TEST_UNSIGNED
|
||||
#define TEST_FORMAT paUInt8
|
||||
#else
|
||||
#define TEST_FORMAT paInt8
|
||||
#endif
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
#if TEST_UNSIGNED
|
||||
unsigned char sine[TABLE_SIZE];
|
||||
#else
|
||||
char sine[TABLE_SIZE];
|
||||
#endif
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
unsigned int framesToGo;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
char *out = (char*)outputBuffer;
|
||||
int i;
|
||||
int framesToCalc;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
if( data->framesToGo < framesPerBuffer )
|
||||
{
|
||||
framesToCalc = data->framesToGo;
|
||||
data->framesToGo = 0;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
framesToCalc = framesPerBuffer;
|
||||
data->framesToGo -= framesPerBuffer;
|
||||
}
|
||||
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<(int)framesPerBuffer; i++ )
|
||||
{
|
||||
#if TEST_UNSIGNED
|
||||
*out++ = (unsigned char) 0x80; /* left */
|
||||
*out++ = (unsigned char) 0x80; /* right */
|
||||
#else
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
#endif
|
||||
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalSamps;
|
||||
#if TEST_UNSIGNED
|
||||
printf("PortAudio Test: output UNsigned 8 bit sine wave.\n");
|
||||
#else
|
||||
printf("PortAudio Test: output signed 8 bit sine wave.\n");
|
||||
#endif
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (char) (127.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
|
||||
#if TEST_UNSIGNED
|
||||
data.sine[i] += (unsigned char) 0x80;
|
||||
#endif
|
||||
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
TEST_FORMAT,
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
TEST_FORMAT,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
256, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
/* Watch until sound is halfway finished. */
|
||||
while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
|
||||
/* Stop sound until ENTER hit. */
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Pause for 2 seconds.\n");
|
||||
Pa_Sleep( 2000 );
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Waiting for sound to finish.\n");
|
||||
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
198
pa_tests/patest_sine_formats.c
Normal file
198
pa_tests/patest_sine_formats.c
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_sine_formats.c
|
||||
* Play a sine wave using the Portable Audio api for several seconds.
|
||||
* Test various data formats.
|
||||
*
|
||||
* Author: Phil Burk
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
|
||||
#define NUM_SECONDS (20)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#define LEFT_FREQ (SAMPLE_RATE/256.0) /* So we hit 1.0 */
|
||||
#define RIGHT_FREQ (500.0)
|
||||
#define AMPLITUDE (1.0)
|
||||
|
||||
/* Select ONE format for testing. */
|
||||
#define TEST_UINT8 (0)
|
||||
#define TEST_INT8 (0)
|
||||
#define TEST_INT16 (1)
|
||||
#define TEST_FLOAT32 (0)
|
||||
|
||||
#if TEST_UINT8
|
||||
#define TEST_FORMAT paUInt8
|
||||
typedef unsigned char SAMPLE_t;
|
||||
#define SAMPLE_ZERO (0x80)
|
||||
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
|
||||
#define FORMAT_NAME "Unsigned 8 Bit"
|
||||
#endif
|
||||
|
||||
#if TEST_INT8
|
||||
#define TEST_FORMAT paInt8
|
||||
typedef char SAMPLE_t;
|
||||
#define SAMPLE_ZERO (0)
|
||||
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
|
||||
#define FORMAT_NAME "Signed 8 Bit"
|
||||
#endif
|
||||
|
||||
#if TEST_INT16
|
||||
#define TEST_FORMAT paInt16
|
||||
typedef short SAMPLE_t;
|
||||
#define SAMPLE_ZERO (0)
|
||||
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767.0 * (x)))
|
||||
#define FORMAT_NAME "Signed 16 Bit"
|
||||
#endif
|
||||
|
||||
#if TEST_FLOAT32
|
||||
#define TEST_FORMAT paFloat32
|
||||
typedef float SAMPLE_t;
|
||||
#define SAMPLE_ZERO (0.0)
|
||||
#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x))
|
||||
#define FORMAT_NAME "Float 32 Bit"
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double left_phase;
|
||||
double right_phase;
|
||||
unsigned int framesToGo;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
SAMPLE_t *out = (SAMPLE_t *)outputBuffer;
|
||||
int i;
|
||||
int framesToCalc;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
if( data->framesToGo < framesPerBuffer )
|
||||
{
|
||||
framesToCalc = data->framesToGo;
|
||||
data->framesToGo = 0;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
framesToCalc = framesPerBuffer;
|
||||
data->framesToGo -= framesPerBuffer;
|
||||
}
|
||||
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
data->left_phase += (LEFT_FREQ / SAMPLE_RATE);
|
||||
if( data->left_phase > 1.0) data->left_phase -= 1.0;
|
||||
*out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. )));
|
||||
|
||||
data->right_phase += (RIGHT_FREQ / SAMPLE_RATE);
|
||||
if( data->right_phase > 1.0) data->right_phase -= 1.0;
|
||||
*out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. )));
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<(int)framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = SAMPLE_ZERO; /* left */
|
||||
*out++ = SAMPLE_ZERO; /* right */
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int totalSamps;
|
||||
|
||||
printf("PortAudio Test: output " FORMAT_NAME "\n");
|
||||
|
||||
|
||||
data.left_phase = data.right_phase = 0.0;
|
||||
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
TEST_FORMAT,
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
TEST_FORMAT,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS );
|
||||
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
|
||||
printf("PortAudio Test Finished: " FORMAT_NAME "\n");
|
||||
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
165
pa_tests/patest_sine_time.c
Normal file
165
pa_tests/patest_sine_time.c
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_sine_time.c
|
||||
* Play a sine wave using the Portable Audio api for several seconds.
|
||||
* Pausing in the middle.
|
||||
* use the Pa_StreamTime() and Pa_StreamActive() calls.
|
||||
*
|
||||
* Authors:
|
||||
* Ross Bencina <rossb@audiomulch.com>
|
||||
* Phil Burk <philburk@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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_SECONDS (8)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
int framesToGo;
|
||||
}
|
||||
paTestData;
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
int i;
|
||||
int framesToCalc;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
if( data->framesToGo < framesPerBuffer )
|
||||
{
|
||||
framesToCalc = data->framesToGo;
|
||||
data->framesToGo = 0;
|
||||
finished = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
framesToCalc = framesPerBuffer;
|
||||
data->framesToGo -= framesPerBuffer;
|
||||
}
|
||||
|
||||
for( i=0; i<framesToCalc; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<(int)framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
int totalSamps;
|
||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = 0;
|
||||
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
/* Watch until sound is halfway finished. */
|
||||
printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout);
|
||||
while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
|
||||
/* Stop sound until ENTER hit. */
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Pause for 2 seconds.\n"); fflush(stdout);
|
||||
Pa_Sleep( 2000 );
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Play until sound is finished.\n"); fflush(stdout);
|
||||
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
285
pa_tests/patest_stop.c
Normal file
285
pa_tests/patest_stop.c
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_stop.c
|
||||
*
|
||||
* Test the three ways of stopping audio:
|
||||
* calling Pa_StopStream(),
|
||||
* calling Pa_AbortStream(),
|
||||
* and returning a 1 from the callback function.
|
||||
*
|
||||
* A long latency is set up so that you can hear the difference.
|
||||
* Then a simple 8 note sequence is repeated twice.
|
||||
* The program will print what you should hear.
|
||||
*
|
||||
* Author: Phil Burk <philburk@softsynth.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
|
||||
#define SLEEP_DUR (200)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#define LATENCY_MSEC (3000)
|
||||
#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#define FRAMES_PER_NOTE (SAMPLE_RATE/2)
|
||||
#define MAX_REPEATS (2)
|
||||
#define FUNDAMENTAL (400.0f / SAMPLE_RATE)
|
||||
#define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f)
|
||||
#define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f)
|
||||
#define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f)
|
||||
#define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f)
|
||||
#define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f)
|
||||
#define MODE_FINISH (0)
|
||||
#define MODE_STOP (1)
|
||||
#define MODE_ABORT (2)
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
#define TABLE_SIZE (400)
|
||||
typedef struct
|
||||
{
|
||||
float waveform[TABLE_SIZE + 1]; // add one for guard point for interpolation
|
||||
float phase_increment;
|
||||
float phase;
|
||||
float *tune;
|
||||
int notesPerTune;
|
||||
int frameCounter;
|
||||
int noteCounter;
|
||||
int repeatCounter;
|
||||
PaTimestamp outTime;
|
||||
int stopMode;
|
||||
int done;
|
||||
}
|
||||
paTestData;
|
||||
/************* Prototypes *****************************/
|
||||
int TestStopMode( paTestData *data );
|
||||
float LookupWaveform( paTestData *data, float phase );
|
||||
/******************************************************
|
||||
* Convert phase between 0.0 and 1.0 to waveform value
|
||||
* using linear interpolation.
|
||||
*/
|
||||
float LookupWaveform( paTestData *data, float phase )
|
||||
{
|
||||
float fIndex = phase*TABLE_SIZE;
|
||||
int index = (int) fIndex;
|
||||
float fract = fIndex - index;
|
||||
float lo = data->waveform[index];
|
||||
float hi = data->waveform[index+1];
|
||||
float val = lo + fract*(hi-lo);
|
||||
return val;
|
||||
}
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
float value;
|
||||
unsigned int i = 0;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
|
||||
data->outTime = outTime;
|
||||
if( !data->done )
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
/* Are we done with this note? */
|
||||
if( data->frameCounter >= FRAMES_PER_NOTE )
|
||||
{
|
||||
data->noteCounter += 1;
|
||||
data->frameCounter = 0;
|
||||
/* Are we done with this tune? */
|
||||
if( data->noteCounter >= data->notesPerTune )
|
||||
{
|
||||
data->noteCounter = 0;
|
||||
data->repeatCounter += 1;
|
||||
/* Are we totally done? */
|
||||
if( data->repeatCounter >= MAX_REPEATS )
|
||||
{
|
||||
data->done = 1;
|
||||
if( data->stopMode == MODE_FINISH )
|
||||
{
|
||||
finished = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
data->phase_increment = data->tune[data->noteCounter];
|
||||
}
|
||||
value = LookupWaveform(data, data->phase);
|
||||
*out++ = value; /* left */
|
||||
*out++ = value; /* right */
|
||||
data->phase += data->phase_increment;
|
||||
if( data->phase >= 1.0f ) data->phase -= 1.0f;
|
||||
|
||||
data->frameCounter += 1;
|
||||
}
|
||||
}
|
||||
/* zero remainder of final buffer */
|
||||
for( ; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
paTestData DATA;
|
||||
int i;
|
||||
float simpleTune[] = { NOTE_0, NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_3, NOTE_2, NOTE_1 };
|
||||
printf("PortAudio Test: play song and test stopping. ask for %d buffers\n", NUM_BUFFERS );
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
DATA.waveform[i] = (float) (
|
||||
(0.2 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. )) +
|
||||
(0.2 * sin( ((double)(3*i)/(double)TABLE_SIZE) * M_PI * 2. )) +
|
||||
(0.1 * sin( ((double)(5*i)/(double)TABLE_SIZE) * M_PI * 2. ))
|
||||
);
|
||||
}
|
||||
DATA.waveform[TABLE_SIZE] = DATA.waveform[0]; // set guard point
|
||||
DATA.tune = &simpleTune[0];
|
||||
DATA.notesPerTune = sizeof(simpleTune) / sizeof(float);
|
||||
printf("Test MODE_FINISH - callback returns 1.\n");
|
||||
printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
|
||||
DATA.stopMode = MODE_FINISH;
|
||||
if( TestStopMode( &DATA ) != paNoError )
|
||||
{
|
||||
printf("Test of MODE_FINISH failed!\n");
|
||||
goto error;
|
||||
}
|
||||
printf("Test MODE_STOP - stop when song is done.\n");
|
||||
printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
|
||||
DATA.stopMode = MODE_STOP;
|
||||
if( TestStopMode( &DATA ) != paNoError )
|
||||
{
|
||||
printf("Test of MODE_STOP failed!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
printf("Test MODE_ABORT - abort immediately.\n");
|
||||
printf("Should hear last repetition cut short by %d msec.\n", LATENCY_MSEC);
|
||||
DATA.stopMode = MODE_ABORT;
|
||||
if( TestStopMode( &DATA ) != paNoError )
|
||||
{
|
||||
printf("Test of MODE_ABORT failed!\n");
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TestStopMode( paTestData *data )
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
data->done = 0;
|
||||
data->phase = 0.0;
|
||||
data->frameCounter = 0;
|
||||
data->noteCounter = 0;
|
||||
data->repeatCounter = 0;
|
||||
data->phase_increment = data->tune[data->noteCounter];
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
OUTPUT_DEVICE,
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
if( data->stopMode == MODE_FINISH )
|
||||
{
|
||||
while( Pa_StreamActive( stream ) )
|
||||
{
|
||||
/*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
|
||||
data->noteCounter, data->repeatCounter );
|
||||
fflush(stdout); /**/
|
||||
Pa_Sleep( SLEEP_DUR );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( data->repeatCounter < MAX_REPEATS )
|
||||
{
|
||||
/*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
|
||||
data->noteCounter, data->repeatCounter );
|
||||
fflush(stdout); /**/
|
||||
Pa_Sleep( SLEEP_DUR );
|
||||
}
|
||||
}
|
||||
if( data->stopMode == MODE_ABORT )
|
||||
{
|
||||
printf("Call Pa_AbortStream()\n");
|
||||
err = Pa_AbortStream( stream );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Call Pa_StopStream()\n");
|
||||
err = Pa_StopStream( stream );
|
||||
}
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Call Pa_CloseStream()\n"); fflush(stdout);
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
227
pa_tests/patest_sync.c
Normal file
227
pa_tests/patest_sync.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_sync.c
|
||||
* Test time stamping and synchronization of audio and video.
|
||||
* A high latency is used so we can hear the difference in time.
|
||||
* Random durations are used so we know we are hearing the right beep
|
||||
* and not the one before or after.
|
||||
*
|
||||
* Sequence of events:
|
||||
* Foreground requests a beep.
|
||||
* Background randomly schedules a beep.
|
||||
* Foreground waits for the beep to be heard based on Pa_StreamTime().
|
||||
* Foreground outputs video (printf) in sync with audio.
|
||||
* Repeat.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#define NUM_BEEPS (6)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
#define BEEP_DURATION (1000)
|
||||
#define LATENCY_MSEC (2000)
|
||||
#define SLEEP_MSEC (10)
|
||||
#define TIMEOUT_MSEC ((3 * LATENCY_MSEC) / (2 * SLEEP_MSEC))
|
||||
#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
|
||||
#define STATE_BKG_IDLE (0)
|
||||
#define STATE_BKG_PENDING (1)
|
||||
#define STATE_BKG_BEEPING (2)
|
||||
typedef struct
|
||||
{
|
||||
float left_phase;
|
||||
float right_phase;
|
||||
int state;
|
||||
int requestBeep; /* Set by foreground, cleared by background. */
|
||||
PaTimestamp beepTime;
|
||||
int beepCount;
|
||||
}
|
||||
paTestData;
|
||||
static unsigned long GenerateRandomNumber( void );
|
||||
/************************************************************/
|
||||
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
|
||||
static unsigned long GenerateRandomNumber( void )
|
||||
{
|
||||
static unsigned long randSeed = 22222; /* Change this for different random sequences. */
|
||||
randSeed = (randSeed * 196314165) + 907633515;
|
||||
return randSeed;
|
||||
}
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
/* Cast data passed through stream to our structure. */
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned int i;
|
||||
(void) inputBuffer;
|
||||
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
switch( data->state )
|
||||
{
|
||||
case STATE_BKG_IDLE:
|
||||
/* Schedule beep at some random time in the future. */
|
||||
if( data->requestBeep )
|
||||
{
|
||||
int random = GenerateRandomNumber() >> 14;
|
||||
data->beepTime = outTime + (i + random + (SAMPLE_RATE/4));
|
||||
data->state = STATE_BKG_PENDING;
|
||||
data->requestBeep = 0;
|
||||
data->left_phase = data->right_phase = 0.0;
|
||||
}
|
||||
*out++ = 0.0; /* left */
|
||||
*out++ = 0.0; /* right */
|
||||
break;
|
||||
case STATE_BKG_PENDING:
|
||||
if( (outTime + i) >= data->beepTime )
|
||||
{
|
||||
data->state = STATE_BKG_BEEPING;
|
||||
data->beepCount = BEEP_DURATION;
|
||||
}
|
||||
*out++ = 0.0; /* left */
|
||||
*out++ = 0.0; /* right */
|
||||
break;
|
||||
case STATE_BKG_BEEPING:
|
||||
if( data->beepCount <= 0 )
|
||||
{
|
||||
data->state = STATE_BKG_IDLE;
|
||||
*out++ = 0.0; /* left */
|
||||
*out++ = 0.0; /* right */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Play sawtooth wave. */
|
||||
*out++ = data->left_phase; /* left */
|
||||
*out++ = data->right_phase; /* right */
|
||||
/* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
|
||||
data->left_phase += 0.01f;
|
||||
/* When signal reaches top, drop back down. */
|
||||
if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
|
||||
/* higher pitch so we can distinguish left and right. */
|
||||
data->right_phase += 0.03f;
|
||||
if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
|
||||
}
|
||||
data->beepCount -= 1;
|
||||
break;
|
||||
default:
|
||||
data->state = STATE_BKG_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData DATA;
|
||||
int i, timeout;
|
||||
PaTimestamp previousTime;
|
||||
printf("PortAudio Test: you should see BEEP at the same time you hear it.\n");
|
||||
printf("Wait for a few seconds random delay between BEEPs.\n");
|
||||
printf("BEEP %d times.\n", NUM_BEEPS );
|
||||
/* Initialize our DATA for use by callback. */
|
||||
DATA.left_phase = DATA.right_phase = 0.0;
|
||||
DATA.state = STATE_BKG_IDLE;
|
||||
DATA.requestBeep = 0;
|
||||
/* Initialize library before making any other calls. */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
/* Open an audio I/O stream. */
|
||||
err = Pa_OpenDefaultStream(
|
||||
&stream,
|
||||
0, /* no input channels */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
NUM_BUFFERS,
|
||||
patestCallback,
|
||||
&DATA );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
previousTime = Pa_StreamTime( stream );
|
||||
for( i=0; i<NUM_BEEPS; i++ )
|
||||
{
|
||||
/* Request a beep from background. */
|
||||
DATA.requestBeep = 1;
|
||||
/* Wait for background to acknowledge request. */
|
||||
timeout = TIMEOUT_MSEC;
|
||||
while( (DATA.requestBeep == 1) && (timeout-- > 0 ) ) Pa_Sleep(SLEEP_MSEC);
|
||||
if( timeout <= 0 )
|
||||
{
|
||||
fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" );
|
||||
goto error;
|
||||
}
|
||||
/* Wait for scheduled beep time. */
|
||||
timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC);
|
||||
while( (Pa_StreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) )
|
||||
{
|
||||
Pa_Sleep(SLEEP_MSEC);
|
||||
}
|
||||
if( timeout <= 0 )
|
||||
{
|
||||
fprintf( stderr, "Timed out waiting for time. Now = %g, Beep for %g.\n",
|
||||
Pa_StreamTime( stream ), DATA.beepTime );
|
||||
goto error;
|
||||
}
|
||||
/* Beep should be sounding now so print synchronized BEEP. */
|
||||
printf("BEEP");
|
||||
fflush(stdout);
|
||||
printf(" at %d, delta = %d\n",
|
||||
(long) DATA.beepTime, (long) (DATA.beepTime - previousTime) );
|
||||
fflush(stdout);
|
||||
previousTime = DATA.beepTime;
|
||||
}
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
151
pa_tests/patest_underflow.c
Normal file
151
pa_tests/patest_underflow.c
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_underflow.c
|
||||
* Simulate an output buffer underflow condition.
|
||||
* Tests whether the stream can be stopped when underflowing buffers.
|
||||
*
|
||||
* Authors:
|
||||
* Ross Bencina <rossb@audiomulch.com>
|
||||
* Phil Burk <philburk@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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
|
||||
#define NUM_SECONDS (20)
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (2048)
|
||||
#define MSEC_PER_BUFFER ( (FRAMES_PER_BUFFER * 1000) / SAMPLE_RATE )
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265)
|
||||
#endif
|
||||
|
||||
#define TABLE_SIZE (200)
|
||||
typedef struct
|
||||
{
|
||||
float sine[TABLE_SIZE];
|
||||
int left_phase;
|
||||
int right_phase;
|
||||
int sleepTime;
|
||||
}
|
||||
paTestData;
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int patestCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
float *out = (float*)outputBuffer;
|
||||
unsigned long i;
|
||||
int finished = 0;
|
||||
(void) outTime; /* Prevent unused variable warnings. */
|
||||
(void) inputBuffer;
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = data->sine[data->left_phase]; /* left */
|
||||
*out++ = data->sine[data->right_phase]; /* right */
|
||||
data->left_phase += 1;
|
||||
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
|
||||
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
|
||||
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
|
||||
}
|
||||
|
||||
/* Cause underflow to occur. */
|
||||
if( data->sleepTime > 0 ) Pa_Sleep( data->sleepTime );
|
||||
data->sleepTime += 1;
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
paTestData data;
|
||||
int i;
|
||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
/* initialise sinusoidal wavetable */
|
||||
for( i=0; i<TABLE_SIZE; i++ )
|
||||
{
|
||||
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
||||
}
|
||||
data.left_phase = data.right_phase = data.sleepTime = 0;
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
paNoDevice,/* default input device */
|
||||
0, /* no input */
|
||||
paFloat32, /* 32 bit floating point input */
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
paFloat32, /* 32 bit floating point output */
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
patestCallback,
|
||||
&data );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
while( data.sleepTime < (2 * MSEC_PER_BUFFER) )
|
||||
{
|
||||
printf("SleepTime = %d\n", data.sleepTime );
|
||||
Pa_Sleep( data.sleepTime );
|
||||
}
|
||||
|
||||
printf("Try to stop stream.\n");
|
||||
err = Pa_StopStream( stream ); /* */
|
||||
err = Pa_AbortStream( stream ); /* */
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Test finished.\n");
|
||||
return err;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
139
pa_tests/patest_wire.c
Normal file
139
pa_tests/patest_wire.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* $Id$
|
||||
* patest_wire.c
|
||||
*
|
||||
* Pass input directly to output.
|
||||
* Note that some HW devices, for example many ISA audio cards
|
||||
* on PCs, do NOT support full duplex! For a PC, you normally need
|
||||
* a PCI based audio card such as the SBLive.
|
||||
*
|
||||
* Author: Phil Burk http://www.softsynth.com
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
/*
|
||||
** Note that many of the older ISA sound cards on PCs do NOT support
|
||||
** full duplex audio (simultaneous record and playback).
|
||||
** And some only support full duplex at lower sample rates.
|
||||
*/
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define FRAMES_PER_BUFFER (64)
|
||||
#if 1
|
||||
#define PA_SAMPLE_TYPE paFloat32
|
||||
typedef float SAMPLE;
|
||||
#else
|
||||
#define PA_SAMPLE_TYPE paInt16
|
||||
typedef short SAMPLE;
|
||||
#endif
|
||||
static int wireCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
|
||||
/* This routine will be called by the PortAudio engine when audio is needed.
|
||||
** It may be called at interrupt level on some machines so don't do anything
|
||||
** that could mess up the system like calling malloc() or free().
|
||||
*/
|
||||
static int wireCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
SAMPLE *out = (SAMPLE*)outputBuffer;
|
||||
SAMPLE *in = (SAMPLE*)inputBuffer;
|
||||
unsigned int i;
|
||||
int finished = 0;
|
||||
(void) outTime;
|
||||
|
||||
/* This may get called with NULL inputBuffer during initial setup. */
|
||||
if( inputBuffer == NULL )
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = 0; /* left */
|
||||
*out++ = 0; /* right */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i=0; i<framesPerBuffer; i++ )
|
||||
{
|
||||
*out++ = *in++; /* left */
|
||||
*out++ = *in++; /* right */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
PortAudioStream *stream;
|
||||
PaError err;
|
||||
int numDevices = Pa_CountDevices();
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
printf("PortAudio Test: input device = %d\n", Pa_GetDefaultInputDeviceID() );
|
||||
printf("PortAudio Test: output device = %d\n", Pa_GetDefaultOutputDeviceID() );
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
Pa_GetDefaultInputDeviceID(), /* default output device */
|
||||
2, /* stereo input */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
Pa_GetDefaultOutputDeviceID(), /* default output device */
|
||||
2, /* stereo output */
|
||||
PA_SAMPLE_TYPE,
|
||||
NULL,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER, /* frames per buffer */
|
||||
0, /* number of buffers, if zero then use default minimum */
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
wireCallback,
|
||||
NULL ); /* no data */
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
printf("Full duplex sound test in progress.\n");
|
||||
printf("Hit ENTER to exit test.\n");
|
||||
getchar();
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) goto error;
|
||||
Pa_Terminate();
|
||||
printf("Full duplex sound test complete.\n"); fflush(stdout);
|
||||
return 0;
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
38
pa_unix_oss/Makefile
Normal file
38
pa_unix_oss/Makefile
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Make PortAudio for Linux
|
||||
|
||||
LIBS = -lm -lpthread
|
||||
|
||||
CDEFINES = -I../pa_common
|
||||
CFLAGS = -g
|
||||
PASRC = ../pa_common/pa_lib.c pa_unix_oss.c
|
||||
PAINC = ../pa_common/portaudio.h
|
||||
|
||||
# Tests that work.
|
||||
TESTC = $(PASRC) ../pa_tests/patest_sine.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_maxsines.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_stop.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sync.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_pink.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_clip.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_dither.c
|
||||
#TESTC = $(PASRC) ../pa_tests/pa_devs.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_many.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_record.c
|
||||
#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_wire.c
|
||||
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
|
||||
|
||||
# Tests that do not yet work.
|
||||
|
||||
TESTH = $(PAINC)
|
||||
|
||||
all: patest
|
||||
|
||||
patest: $(TESTC) $(TESTH) Makefile
|
||||
gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
|
||||
|
||||
run: patest
|
||||
./patest
|
||||
|
||||
36
pa_unix_oss/Makefile_freebsd
Normal file
36
pa_unix_oss/Makefile_freebsd
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Make PortAudio for FreeBSD
|
||||
|
||||
LIBS = -lm -pthread
|
||||
|
||||
CDEFINES = -I../pa_common
|
||||
CFLAGS = -g
|
||||
PASRC = ../pa_common/pa_lib.c pa_freebsd.c
|
||||
PAINC = ../pa_common/portaudio.h
|
||||
|
||||
# Tests that work.
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sine.c
|
||||
TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_stop.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_sync.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_pink.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_clip.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_dither.c
|
||||
#TESTC = $(PASRC) ../pa_tests/pa_devs.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_many.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_record.c
|
||||
#TESTC = $(PASRC) ../pa_tests/patest_wire.c
|
||||
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
|
||||
|
||||
# Tests that do not yet work.
|
||||
|
||||
TESTH = $(PAINC)
|
||||
|
||||
all: patest
|
||||
|
||||
patest: $(TESTC) $(TESTH) Makefile
|
||||
gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
|
||||
|
||||
run: patest
|
||||
./patest
|
||||
|
||||
BIN
pa_unix_oss/low_latency_tip.txt
Normal file
BIN
pa_unix_oss/low_latency_tip.txt
Normal file
Binary file not shown.
1072
pa_unix_oss/pa_unix_oss.c
Normal file
1072
pa_unix_oss/pa_unix_oss.c
Normal file
File diff suppressed because it is too large
Load diff
114
pa_unix_oss/recplay.c
Normal file
114
pa_unix_oss/recplay.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* recplay.c
|
||||
* Phil Burk
|
||||
* Minimal record and playback test.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef __STDC__
|
||||
/* #include <getopt.h> */
|
||||
#endif /* __STDC__ */
|
||||
#include <fcntl.h>
|
||||
#ifdef __STDC__
|
||||
#include <string.h>
|
||||
#else /* __STDC__ */
|
||||
#include <strings.h>
|
||||
#endif /* __STDC__ */
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#define NUM_BYTES (64*1024)
|
||||
#define BLOCK_SIZE (4*1024)
|
||||
|
||||
#define AUDIO "/dev/dsp"
|
||||
|
||||
char buffer[NUM_BYTES];
|
||||
|
||||
int audioDev = 0;
|
||||
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int numLeft;
|
||||
char *ptr;
|
||||
int num;
|
||||
int samplesize;
|
||||
|
||||
/********** RECORD ********************/
|
||||
/* Open audio device. */
|
||||
audioDev = open (AUDIO, O_RDONLY, 0);
|
||||
if (audioDev == -1)
|
||||
{
|
||||
perror (AUDIO);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
/* Set to 16 bit samples. */
|
||||
samplesize = 16;
|
||||
ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
|
||||
if (samplesize != 16)
|
||||
{
|
||||
perror("Unable to set the sample size.");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Record in blocks */
|
||||
printf("Begin recording.\n");
|
||||
numLeft = NUM_BYTES;
|
||||
ptr = buffer;
|
||||
while( numLeft >= BLOCK_SIZE )
|
||||
{
|
||||
if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 )
|
||||
{
|
||||
perror (AUDIO);
|
||||
exit (-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Read %d bytes\n", num);
|
||||
ptr += num;
|
||||
numLeft -= num;
|
||||
}
|
||||
}
|
||||
|
||||
close( audioDev );
|
||||
|
||||
/********** PLAYBACK ********************/
|
||||
/* Open audio device for writing. */
|
||||
audioDev = open (AUDIO, O_WRONLY, 0);
|
||||
if (audioDev == -1)
|
||||
{
|
||||
perror (AUDIO);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
/* Set to 16 bit samples. */
|
||||
samplesize = 16;
|
||||
ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
|
||||
if (samplesize != 16)
|
||||
{
|
||||
perror("Unable to set the sample size.");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Play in blocks */
|
||||
printf("Begin playing.\n");
|
||||
numLeft = NUM_BYTES;
|
||||
ptr = buffer;
|
||||
while( numLeft >= BLOCK_SIZE )
|
||||
{
|
||||
if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 )
|
||||
{
|
||||
perror (AUDIO);
|
||||
exit (-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Wrote %d bytes\n", num);
|
||||
ptr += num;
|
||||
numLeft -= num;
|
||||
}
|
||||
}
|
||||
|
||||
close( audioDev );
|
||||
}
|
||||
466
pa_win_ds/dsound_wrapper.c
Normal file
466
pa_win_ds/dsound_wrapper.c
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Simplified DirectSound interface.
|
||||
*
|
||||
* Author: Phil Burk & Robert Marsanyi
|
||||
*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* For more information see: http://www.softsynth.com/portaudio/
|
||||
* DirectSound Implementation
|
||||
* Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#define INITGUID // Needed to build IID_IDirectSoundNotify. See objbase.h for info.
|
||||
#include <objbase.h>
|
||||
#include <unknwn.h>
|
||||
#include "dsound_wrapper.h"
|
||||
#include "pa_trace.h"
|
||||
|
||||
/************************************************************************************/
|
||||
void DSW_Term( DSoundWrapper *dsw )
|
||||
{
|
||||
// Cleanup the sound buffers
|
||||
if (dsw->dsw_OutputBuffer)
|
||||
{
|
||||
IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
|
||||
IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
|
||||
dsw->dsw_OutputBuffer = NULL;
|
||||
}
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
if (dsw->dsw_InputBuffer)
|
||||
{
|
||||
IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
|
||||
IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
|
||||
dsw->dsw_InputBuffer = NULL;
|
||||
}
|
||||
if (dsw->dsw_pDirectSoundCapture)
|
||||
{
|
||||
IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
|
||||
dsw->dsw_pDirectSoundCapture = NULL;
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
if (dsw->dsw_pDirectSound)
|
||||
{
|
||||
IDirectSound_Release( dsw->dsw_pDirectSound );
|
||||
dsw->dsw_pDirectSound = NULL;
|
||||
}
|
||||
}
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_Init( DSoundWrapper *dsw )
|
||||
{
|
||||
memset( dsw, 0, sizeof(DSoundWrapper) );
|
||||
return 0;
|
||||
}
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
|
||||
{
|
||||
// Create the DS object
|
||||
HRESULT hr = DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
|
||||
if( hr != DS_OK ) return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
|
||||
{
|
||||
DWORD dwDataLen;
|
||||
DWORD playCursor;
|
||||
HRESULT result;
|
||||
LPDIRECTSOUNDBUFFER pPrimaryBuffer;
|
||||
HWND hWnd;
|
||||
HRESULT hr;
|
||||
WAVEFORMATEX wfFormat;
|
||||
DSBUFFERDESC primaryDesc;
|
||||
DSBUFFERDESC secondaryDesc;
|
||||
unsigned char* pDSBuffData;
|
||||
LARGE_INTEGER counterFrequency;
|
||||
dsw->dsw_OutputSize = bytesPerBuffer;
|
||||
dsw->dsw_OutputRunning = FALSE;
|
||||
dsw->dsw_OutputUnderflows = 0;
|
||||
dsw->dsw_FramesWritten = 0;
|
||||
dsw->dsw_BytesPerFrame = nChannels * sizeof(short);
|
||||
// We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
|
||||
// applications's window. Also if that window is closed before the Buffer is closed
|
||||
// then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
|
||||
// So we will use GetDesktopWindow() which was suggested by Miller Puckette.
|
||||
// hWnd = GetForegroundWindow();
|
||||
hWnd = GetDesktopWindow();
|
||||
// Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
|
||||
// Exclusize also prevents unexpected sounds from other apps during a performance.
|
||||
if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
|
||||
hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Create primary buffer and set format just so we can specify our custom format.
|
||||
// Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
|
||||
// Setup the primary buffer description
|
||||
ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
|
||||
primaryDesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
|
||||
primaryDesc.dwBufferBytes = 0;
|
||||
primaryDesc.lpwfxFormat = NULL;
|
||||
// Create the buffer
|
||||
if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
|
||||
&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
|
||||
// Define the buffer format
|
||||
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfFormat.nChannels = nChannels;
|
||||
wfFormat.nSamplesPerSec = nFrameRate;
|
||||
wfFormat.wBitsPerSample = 8 * sizeof(short);
|
||||
wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
|
||||
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
|
||||
wfFormat.cbSize = 0; /* No extended format info. */
|
||||
// Set the primary buffer's format
|
||||
if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
|
||||
// ----------------------------------------------------------------------
|
||||
// Setup the secondary buffer description
|
||||
ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
|
||||
secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||
secondaryDesc.dwBufferBytes = bytesPerBuffer;
|
||||
secondaryDesc.lpwfxFormat = &wfFormat;
|
||||
// Create the secondary buffer
|
||||
if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
|
||||
&secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
|
||||
// Lock the DS buffer
|
||||
if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
|
||||
&dwDataLen, NULL, 0, 0)) != DS_OK) return result;
|
||||
// Zero the DS buffer
|
||||
ZeroMemory(pDSBuffData, dwDataLen);
|
||||
// Unlock the DS buffer
|
||||
if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
|
||||
if( QueryPerformanceFrequency( &counterFrequency ) )
|
||||
{
|
||||
int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
|
||||
dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
|
||||
AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart );
|
||||
}
|
||||
else
|
||||
{
|
||||
dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
|
||||
}
|
||||
// Let DSound set the starting write position because if we set it to zero, it looks like the
|
||||
// buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame;
|
||||
/* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
|
||||
return DS_OK;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_StartOutput( DSoundWrapper *dsw )
|
||||
{
|
||||
HRESULT hr;
|
||||
QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
|
||||
dsw->dsw_LastPlayCursor = 0;
|
||||
dsw->dsw_FramesPlayed = 0;
|
||||
hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
// Start the buffer playback in a loop.
|
||||
if( dsw->dsw_OutputBuffer != NULL )
|
||||
{
|
||||
hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
dsw->dsw_OutputRunning = TRUE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_StopOutput( DSoundWrapper *dsw )
|
||||
{
|
||||
// Stop the buffer playback
|
||||
if( dsw->dsw_OutputBuffer != NULL )
|
||||
{
|
||||
dsw->dsw_OutputRunning = FALSE;
|
||||
return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD playCursor;
|
||||
DWORD writeCursor;
|
||||
long numBytesEmpty;
|
||||
long playWriteGap;
|
||||
// Query to see how much room is in buffer.
|
||||
// Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing
|
||||
// under WinNT. The Microsoft documentation says we can pass NULL but apparently not.
|
||||
// Thanks to Max Rheiner for the fix.
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
AddTraceMessage("playCursor", playCursor);
|
||||
AddTraceMessage("dsw_WriteOffset", dsw->dsw_WriteOffset);
|
||||
// Determine size of gap between playIndex and WriteIndex that we cannot write into.
|
||||
playWriteGap = writeCursor - playCursor;
|
||||
if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
|
||||
/* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
|
||||
/* Attempt to detect playCursor wrap-around and correct it. */
|
||||
if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
|
||||
{
|
||||
/* How much time has elapsed since last check. */
|
||||
LARGE_INTEGER currentTime;
|
||||
LARGE_INTEGER elapsedTime;
|
||||
long bytesPlayed;
|
||||
long bytesExpected;
|
||||
long buffersWrapped;
|
||||
QueryPerformanceCounter( ¤tTime );
|
||||
elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
|
||||
dsw->dsw_LastPlayTime = currentTime;
|
||||
/* How many bytes does DirectSound say have been played. */
|
||||
bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
|
||||
if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
|
||||
dsw->dsw_LastPlayCursor = playCursor;
|
||||
/* Calculate how many bytes we would have expected to been played by now. */
|
||||
bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
|
||||
buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
|
||||
if( buffersWrapped > 0 )
|
||||
{
|
||||
AddTraceMessage("playCursor wrapped! bytesPlayed", bytesPlayed );
|
||||
AddTraceMessage("playCursor wrapped! bytesExpected", bytesExpected );
|
||||
playCursor += (buffersWrapped * dsw->dsw_OutputSize);
|
||||
bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
|
||||
}
|
||||
/* Maintain frame output cursor. */
|
||||
dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerFrame);
|
||||
}
|
||||
numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
|
||||
if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
|
||||
/* Have we underflowed? */
|
||||
if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
|
||||
{
|
||||
if( dsw->dsw_OutputRunning )
|
||||
{
|
||||
dsw->dsw_OutputUnderflows += 1;
|
||||
AddTraceMessage("underflow detected! numBytesEmpty", numBytesEmpty );
|
||||
}
|
||||
dsw->dsw_WriteOffset = writeCursor;
|
||||
numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
|
||||
}
|
||||
*bytesEmpty = numBytesEmpty;
|
||||
return hr;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
|
||||
{
|
||||
HRESULT hr;
|
||||
LPBYTE lpbuf1 = NULL;
|
||||
LPBYTE lpbuf2 = NULL;
|
||||
DWORD dwsize1 = 0;
|
||||
DWORD dwsize2 = 0;
|
||||
long bytesEmpty;
|
||||
hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
|
||||
if (hr != DS_OK) return hr;
|
||||
if( bytesEmpty == 0 ) return DS_OK;
|
||||
// Lock free space in the DS
|
||||
hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
|
||||
(void **) &lpbuf2, &dwsize2, 0);
|
||||
if (hr == DS_OK)
|
||||
{
|
||||
// Copy the buffer into the DS
|
||||
ZeroMemory(lpbuf1, dwsize1);
|
||||
if(lpbuf2 != NULL)
|
||||
{
|
||||
ZeroMemory(lpbuf2, dwsize2);
|
||||
}
|
||||
// Update our buffer offset and unlock sound buffer
|
||||
dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
|
||||
IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
|
||||
dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerFrame;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
|
||||
{
|
||||
HRESULT hr;
|
||||
LPBYTE lpbuf1 = NULL;
|
||||
LPBYTE lpbuf2 = NULL;
|
||||
DWORD dwsize1 = 0;
|
||||
DWORD dwsize2 = 0;
|
||||
// Lock free space in the DS
|
||||
hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
|
||||
(void **) &lpbuf2, &dwsize2, 0);
|
||||
if (hr == DS_OK)
|
||||
{
|
||||
// Copy the buffer into the DS
|
||||
CopyMemory(lpbuf1, buf, dwsize1);
|
||||
if(lpbuf2 != NULL)
|
||||
{
|
||||
CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
|
||||
}
|
||||
// Update our buffer offset and unlock sound buffer
|
||||
dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
|
||||
IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
|
||||
dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerFrame;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
|
||||
{
|
||||
DWORD status;
|
||||
if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
|
||||
return( DSERR_INVALIDPARAM );
|
||||
else
|
||||
return( status );
|
||||
}
|
||||
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
/* These routines are used to support audio input.
|
||||
* Do NOT compile these calls when using NT4 because it does
|
||||
* not support the entry points.
|
||||
*/
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
|
||||
{
|
||||
HRESULT hr = DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
|
||||
if( hr != DS_OK ) return hr;
|
||||
return hr;
|
||||
}
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
|
||||
{
|
||||
DSCBUFFERDESC captureDesc;
|
||||
WAVEFORMATEX wfFormat;
|
||||
HRESULT result;
|
||||
// Define the buffer format
|
||||
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfFormat.nChannels = nChannels;
|
||||
wfFormat.nSamplesPerSec = nFrameRate;
|
||||
wfFormat.wBitsPerSample = 8 * sizeof(short);
|
||||
wfFormat.nBlockAlign = wfFormat.nChannels * (wfFormat.wBitsPerSample / 8);
|
||||
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
|
||||
wfFormat.cbSize = 0; /* No extended format info. */
|
||||
dsw->dsw_InputSize = bytesPerBuffer;
|
||||
// ----------------------------------------------------------------------
|
||||
// Setup the secondary buffer description
|
||||
ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
|
||||
captureDesc.dwSize = sizeof(DSCBUFFERDESC);
|
||||
captureDesc.dwFlags = 0;
|
||||
captureDesc.dwBufferBytes = bytesPerBuffer;
|
||||
captureDesc.lpwfxFormat = &wfFormat;
|
||||
// Create the capture buffer
|
||||
if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
|
||||
&captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
|
||||
dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
|
||||
return DS_OK;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_StartInput( DSoundWrapper *dsw )
|
||||
{
|
||||
// Start the buffer playback
|
||||
if( dsw->dsw_InputBuffer != NULL )
|
||||
{
|
||||
return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_StopInput( DSoundWrapper *dsw )
|
||||
{
|
||||
// Stop the buffer playback
|
||||
if( dsw->dsw_InputBuffer != NULL )
|
||||
{
|
||||
return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD capturePos;
|
||||
DWORD readPos;
|
||||
long filled;
|
||||
// Query to see how much data is in buffer.
|
||||
// We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
|
||||
// so let's pass a pointer just to be safe.
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
filled = readPos - dsw->dsw_ReadOffset;
|
||||
if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
|
||||
*bytesFilled = filled;
|
||||
return hr;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
|
||||
{
|
||||
HRESULT hr;
|
||||
LPBYTE lpbuf1 = NULL;
|
||||
LPBYTE lpbuf2 = NULL;
|
||||
DWORD dwsize1 = 0;
|
||||
DWORD dwsize2 = 0;
|
||||
// Lock free space in the DS
|
||||
hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
|
||||
(void **) &lpbuf2, &dwsize2, 0);
|
||||
if (hr == DS_OK)
|
||||
{
|
||||
// Copy from DS to the buffer
|
||||
CopyMemory( buf, lpbuf1, dwsize1);
|
||||
if(lpbuf2 != NULL)
|
||||
{
|
||||
CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
|
||||
}
|
||||
// Update our buffer offset and unlock sound buffer
|
||||
dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
|
||||
IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
106
pa_win_ds/dsound_wrapper.h
Normal file
106
pa_win_ds/dsound_wrapper.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef __DSOUND_WRAPPER_H
|
||||
#define __DSOUND_WRAPPER_H
|
||||
/*
|
||||
* $Id$
|
||||
* Simplified DirectSound interface.
|
||||
*
|
||||
* Author: Phil Burk & Robert Marsanyi
|
||||
*
|
||||
* For PortAudio Portable Real-Time Audio Library
|
||||
* For more information see: http://www.softsynth.com/portaudio/
|
||||
* DirectSound Implementation
|
||||
* Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <DSound.h>
|
||||
#if !defined(BOOL)
|
||||
#define BOOL short
|
||||
#endif
|
||||
#ifndef SUPPORT_AUDIO_CAPTURE
|
||||
#define SUPPORT_AUDIO_CAPTURE (1)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define DSW_NUM_POSITIONS (4)
|
||||
#define DSW_NUM_EVENTS (5)
|
||||
#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Output */
|
||||
LPDIRECTSOUND dsw_pDirectSound;
|
||||
LPDIRECTSOUNDBUFFER dsw_OutputBuffer;
|
||||
DWORD dsw_WriteOffset; /* last write position */
|
||||
INT dsw_OutputSize;
|
||||
INT dsw_BytesPerFrame;
|
||||
/* Try to detect play buffer underflows. */
|
||||
LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
|
||||
LARGE_INTEGER dsw_LastPlayTime;
|
||||
UINT dsw_LastPlayCursor;
|
||||
UINT dsw_OutputUnderflows;
|
||||
BOOL dsw_OutputRunning;
|
||||
/* use double which lets us can play for several thousand years with enough precision */
|
||||
double dsw_FramesWritten;
|
||||
double dsw_FramesPlayed;
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
/* Input */
|
||||
LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer;
|
||||
UINT dsw_ReadOffset; /* last read position */
|
||||
UINT dsw_InputSize;
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
|
||||
}
|
||||
DSoundWrapper;
|
||||
HRESULT DSW_Init( DSoundWrapper *dsw );
|
||||
void DSW_Term( DSoundWrapper *dsw );
|
||||
HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
|
||||
int nChannels, int bufSize );
|
||||
HRESULT DSW_StartOutput( DSoundWrapper *dsw );
|
||||
HRESULT DSW_StopOutput( DSoundWrapper *dsw );
|
||||
DWORD DSW_GetOutputStatus( DSoundWrapper *dsw );
|
||||
HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
|
||||
HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
|
||||
HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
|
||||
HRESULT DSW_Enumerate( DSoundWrapper *dsw );
|
||||
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
|
||||
int nChannels, int bufSize );
|
||||
HRESULT DSW_StartInput( DSoundWrapper *dsw );
|
||||
HRESULT DSW_StopInput( DSoundWrapper *dsw );
|
||||
HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
|
||||
HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __DSOUND_WRAPPER_H */
|
||||
983
pa_win_ds/pa_dsound.c
Normal file
983
pa_win_ds/pa_dsound.c
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
/*
|
||||
* $Id$
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.softsynth.com/portaudio/
|
||||
* DirectSound Implementation
|
||||
*
|
||||
* Copyright (c) 1999-2000 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.
|
||||
*
|
||||
*/
|
||||
/* Modifications
|
||||
* 7/19/01 Mike Berry - casts for compiling with __MWERKS__ CodeWarrior
|
||||
* 9/27/01 Phil Burk - use number of frames instead of real-time for CPULoad calculation.
|
||||
*/
|
||||
/* Compiler flags:
|
||||
SUPPORT_AUDIO_CAPTURE - define this flag if you want to SUPPORT_AUDIO_CAPTURE
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef __MWERKS__
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#endif //__MWERKS__
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "pa_host.h"
|
||||
#include "pa_trace.h"
|
||||
#include "dsound_wrapper.h"
|
||||
#define PRINT(x) { printf x; fflush(stdout); }
|
||||
#define ERR_RPT(x) PRINT(x)
|
||||
#define DBUG(x) /* PRINT(x) */
|
||||
#define DBUGX(x) /* PRINT(x) */
|
||||
#define PA_USE_HIGH_LATENCY (0)
|
||||
#if PA_USE_HIGH_LATENCY
|
||||
#define PA_WIN_9X_LATENCY (500)
|
||||
#define PA_WIN_NT_LATENCY (600)
|
||||
#else
|
||||
#define PA_WIN_9X_LATENCY (140)
|
||||
#define PA_WIN_NT_LATENCY (280)
|
||||
#endif
|
||||
/* Trigger an underflow for testing purposes. Should normally be (0). */
|
||||
#define PA_SIMULATE_UNDERFLOW (0)
|
||||
#if PA_SIMULATE_UNDERFLOW
|
||||
static gUnderCallbackCounter = 0;
|
||||
#define UNDER_START_GAP (10)
|
||||
#define UNDER_STOP_GAP (UNDER_START_GAP + 4)
|
||||
#endif
|
||||
/************************************************* Definitions ********/
|
||||
typedef struct internalPortAudioStream internalPortAudioStream;
|
||||
typedef struct internalPortAudioDevice
|
||||
{
|
||||
GUID pad_GUID;
|
||||
GUID *pad_lpGUID;
|
||||
double pad_SampleRates[10]; /* for pointing to from pad_Info FIXME?!*/
|
||||
PaDeviceInfo pad_Info;
|
||||
}
|
||||
internalPortAudioDevice;
|
||||
/* Define structure to contain all DirectSound and Windows specific data. */
|
||||
typedef struct PaHostSoundControl
|
||||
{
|
||||
DSoundWrapper pahsc_DSoundWrapper;
|
||||
MMRESULT pahsc_TimerID;
|
||||
BOOL pahsc_IfInsideCallback; /* Test for reentrancy. */
|
||||
short *pahsc_NativeBuffer;
|
||||
unsigned int pahsc_BytesPerBuffer; /* native buffer size in bytes */
|
||||
double pahsc_ValidFramesWritten;
|
||||
int pahsc_FramesPerDSBuffer;
|
||||
/* For measuring CPU utilization. */
|
||||
LARGE_INTEGER pahsc_EntryCount;
|
||||
double pahsc_InverseTicksPerUserBuffer;
|
||||
}
|
||||
PaHostSoundControl;
|
||||
/************************************************* Shared Data ********/
|
||||
/* FIXME - put Mutex around this shared data. */
|
||||
static int sNumDevices = 0;
|
||||
static int sDeviceIndex = 0;
|
||||
static internalPortAudioDevice *sDevices = NULL;
|
||||
static int sDefaultInputDeviceID = paNoDevice;
|
||||
static int sDefaultOutputDeviceID = paNoDevice;
|
||||
static int sEnumerationError;
|
||||
static int sPaHostError = 0;
|
||||
/************************************************* Prototypes **********/
|
||||
static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
|
||||
static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID,
|
||||
LPCTSTR lpszDesc,
|
||||
LPCTSTR lpszDrvName,
|
||||
LPVOID lpContext );
|
||||
static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID,
|
||||
LPCTSTR lpszDesc,
|
||||
LPCTSTR lpszDrvName,
|
||||
LPVOID lpContext );
|
||||
static Pa_QueryDevices( void );
|
||||
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
|
||||
DWORD dwUser, DWORD dw1, DWORD dw2);
|
||||
/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
|
||||
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return;
|
||||
/* Query system timer for usage analysis and to prevent overuse of CPU. */
|
||||
QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
|
||||
}
|
||||
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
|
||||
{
|
||||
LARGE_INTEGER CurrentCount = { 0, 0 };
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return;
|
||||
/*
|
||||
** Measure CPU utilization during this callback. Note that this calculation
|
||||
** assumes that we had the processor the whole time.
|
||||
*/
|
||||
#define LOWPASS_COEFFICIENT_0 (0.9)
|
||||
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
|
||||
if( QueryPerformanceCounter( &CurrentCount ) )
|
||||
{
|
||||
LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
|
||||
double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerUserBuffer;
|
||||
past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
|
||||
(LOWPASS_COEFFICIENT_1 * newUsage);
|
||||
}
|
||||
}
|
||||
/****************************************** END CPU UTILIZATION *******/
|
||||
static PaError Pa_QueryDevices( void )
|
||||
{
|
||||
int numBytes;
|
||||
sDefaultInputDeviceID = paNoDevice;
|
||||
sDefaultOutputDeviceID = paNoDevice;
|
||||
/* Enumerate once just to count devices. */
|
||||
sNumDevices = 0; // for default device
|
||||
DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL );
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL );
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
/* Allocate structures to hold device info. */
|
||||
numBytes = sNumDevices * sizeof(internalPortAudioDevice);
|
||||
sDevices = (internalPortAudioDevice *)PaHost_AllocateFastMemory( numBytes ); /* MEM */
|
||||
if( sDevices == NULL ) return paInsufficientMemory;
|
||||
/* Enumerate again to fill in structures. */
|
||||
sDeviceIndex = 0;
|
||||
sEnumerationError = 0;
|
||||
DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)0 );
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
if( sEnumerationError != paNoError ) return sEnumerationError;
|
||||
sEnumerationError = 0;
|
||||
DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)1 );
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
return sEnumerationError;
|
||||
}
|
||||
/************************************************************************************/
|
||||
long Pa_GetHostError()
|
||||
{
|
||||
return sPaHostError;
|
||||
}
|
||||
/************************************************************************************
|
||||
** Just count devices so we know how much memory to allocate.
|
||||
*/
|
||||
static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID,
|
||||
LPCTSTR lpszDesc,
|
||||
LPCTSTR lpszDrvName,
|
||||
LPVOID lpContext )
|
||||
{
|
||||
sNumDevices++;
|
||||
return TRUE;
|
||||
}
|
||||
/************************************************************************************
|
||||
** Extract capabilities info from each device.
|
||||
*/
|
||||
static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID,
|
||||
LPCTSTR lpszDesc,
|
||||
LPCTSTR lpszDrvName,
|
||||
LPVOID lpContext )
|
||||
{
|
||||
HRESULT hr;
|
||||
LPDIRECTSOUND lpDirectSound;
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
int isInput = (int) lpContext; /* Passed from Pa_CountDevices() */
|
||||
internalPortAudioDevice *pad;
|
||||
|
||||
if( sDeviceIndex >= sNumDevices )
|
||||
{
|
||||
sEnumerationError = paInternalError;
|
||||
return FALSE;
|
||||
}
|
||||
pad = &sDevices[sDeviceIndex];
|
||||
/* Copy GUID to static array. Set pointer. */
|
||||
if( lpGUID == NULL )
|
||||
{
|
||||
pad->pad_lpGUID = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( &pad->pad_GUID, lpGUID, sizeof(GUID) );
|
||||
pad->pad_lpGUID = &pad->pad_GUID;
|
||||
}
|
||||
pad->pad_Info.sampleRates = pad->pad_SampleRates; /* Point to array. */
|
||||
/* Allocate room for descriptive name. */
|
||||
if( lpszDesc != NULL )
|
||||
{
|
||||
int len = strlen(lpszDesc);
|
||||
pad->pad_Info.name = (char *)malloc( len+1 );
|
||||
if( pad->pad_Info.name == NULL )
|
||||
{
|
||||
sEnumerationError = paInsufficientMemory;
|
||||
return FALSE;
|
||||
}
|
||||
memcpy( (void *) pad->pad_Info.name, lpszDesc, len+1 );
|
||||
}
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
if( isInput )
|
||||
{
|
||||
/********** Input ******************************/
|
||||
DSCCAPS caps;
|
||||
if( lpGUID == NULL ) sDefaultInputDeviceID = sDeviceIndex;
|
||||
hr = DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
pad->pad_Info.maxInputChannels = 0;
|
||||
DBUG(("Cannot create Capture for %s. Result = 0x%x\n", lpszDesc, hr ));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Query device characteristics. */
|
||||
caps.dwSize = sizeof(caps);
|
||||
IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
|
||||
/* printf("caps.dwFormats = 0x%x\n", caps.dwFormats ); */
|
||||
pad->pad_Info.maxInputChannels = caps.dwChannels;
|
||||
/* Determine sample rates from flags. */
|
||||
if( caps.dwChannels == 2 )
|
||||
{
|
||||
int index = 0;
|
||||
if( caps.dwFormats & WAVE_FORMAT_1S16) pad->pad_SampleRates[index++] = 11025.0;
|
||||
if( caps.dwFormats & WAVE_FORMAT_2S16) pad->pad_SampleRates[index++] = 22050.0;
|
||||
if( caps.dwFormats & WAVE_FORMAT_4S16) pad->pad_SampleRates[index++] = 44100.0;
|
||||
pad->pad_Info.numSampleRates = index;
|
||||
}
|
||||
else if( caps.dwChannels == 1 )
|
||||
{
|
||||
int index = 0;
|
||||
if( caps.dwFormats & WAVE_FORMAT_1M16) pad->pad_SampleRates[index++] = 11025.0;
|
||||
if( caps.dwFormats & WAVE_FORMAT_2M16) pad->pad_SampleRates[index++] = 22050.0;
|
||||
if( caps.dwFormats & WAVE_FORMAT_4M16) pad->pad_SampleRates[index++] = 44100.0;
|
||||
pad->pad_Info.numSampleRates = index;
|
||||
}
|
||||
else pad->pad_Info.numSampleRates = 0;
|
||||
IDirectSoundCapture_Release( lpDirectSoundCapture );
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
|
||||
{
|
||||
/********** Output ******************************/
|
||||
DSCAPS caps;
|
||||
if( lpGUID == NULL ) sDefaultOutputDeviceID = sDeviceIndex;
|
||||
/* Create interfaces for each object. */
|
||||
hr = DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
pad->pad_Info.maxOutputChannels = 0;
|
||||
DBUG(("Cannot create dsound for %s. Result = 0x%x\n", lpszDesc, hr ));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Query device characteristics. */
|
||||
caps.dwSize = sizeof(caps);
|
||||
IDirectSound_GetCaps( lpDirectSound, &caps );
|
||||
pad->pad_Info.maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
|
||||
/* Get sample rates. */
|
||||
pad->pad_SampleRates[0] = (double) caps.dwMinSecondarySampleRate;
|
||||
pad->pad_SampleRates[1] = (double) caps.dwMaxSecondarySampleRate;
|
||||
if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) pad->pad_Info.numSampleRates = -1;
|
||||
else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
|
||||
{
|
||||
if( caps.dwMinSecondarySampleRate == 0 )
|
||||
{
|
||||
/*
|
||||
** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
|
||||
** But it supports continuous sampling.
|
||||
** So fake range of rates, and hope it really supports it.
|
||||
*/
|
||||
pad->pad_SampleRates[0] = 11025.0f;
|
||||
pad->pad_SampleRates[1] = 48000.0f;
|
||||
pad->pad_Info.numSampleRates = -1; /* continuous range */
|
||||
|
||||
DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
|
||||
}
|
||||
else
|
||||
{
|
||||
pad->pad_Info.numSampleRates = 1;
|
||||
}
|
||||
}
|
||||
else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
|
||||
{
|
||||
/* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
|
||||
** But we know that they really support a range of rates!
|
||||
** So when we see a ridiculous set of rates, assume it is a range.
|
||||
*/
|
||||
pad->pad_Info.numSampleRates = -1;
|
||||
DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
|
||||
}
|
||||
else pad->pad_Info.numSampleRates = 2;
|
||||
IDirectSound_Release( lpDirectSound );
|
||||
}
|
||||
}
|
||||
pad->pad_Info.nativeSampleFormats = paInt16;
|
||||
sDeviceIndex++;
|
||||
return( TRUE );
|
||||
}
|
||||
/*************************************************************************/
|
||||
int Pa_CountDevices()
|
||||
{
|
||||
if( sNumDevices <= 0 ) Pa_Initialize();
|
||||
return sNumDevices;
|
||||
}
|
||||
static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
|
||||
{
|
||||
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
|
||||
return &sDevices[id];
|
||||
}
|
||||
/*************************************************************************/
|
||||
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
|
||||
{
|
||||
internalPortAudioDevice *pad;
|
||||
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
|
||||
pad = Pa_GetInternalDevice( id );
|
||||
return &pad->pad_Info ;
|
||||
}
|
||||
static PaError Pa_MaybeQueryDevices( void )
|
||||
{
|
||||
if( sNumDevices == 0 )
|
||||
{
|
||||
return Pa_QueryDevices();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*************************************************************************
|
||||
** Returns recommended device ID.
|
||||
** On the PC, the recommended device can be specified by the user by
|
||||
** setting an environment variable. For example, to use device #1.
|
||||
**
|
||||
** set PA_RECOMMENDED_OUTPUT_DEVICE=1
|
||||
**
|
||||
** The user should first determine the available device ID by using
|
||||
** the supplied application "pa_devs".
|
||||
*/
|
||||
#define PA_ENV_BUF_SIZE (32)
|
||||
#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE")
|
||||
#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE")
|
||||
static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
|
||||
{
|
||||
DWORD hresult;
|
||||
char envbuf[PA_ENV_BUF_SIZE];
|
||||
PaDeviceID recommendedID = paNoDevice;
|
||||
/* Let user determine default device by setting environment variable. */
|
||||
hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
|
||||
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
|
||||
{
|
||||
recommendedID = atoi( envbuf );
|
||||
}
|
||||
return recommendedID;
|
||||
}
|
||||
PaDeviceID Pa_GetDefaultInputDeviceID( void )
|
||||
{
|
||||
PaError result;
|
||||
result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
|
||||
if( result < 0 )
|
||||
{
|
||||
result = Pa_MaybeQueryDevices();
|
||||
if( result < 0 ) return result;
|
||||
result = sDefaultInputDeviceID;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
|
||||
{
|
||||
PaError result;
|
||||
result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
|
||||
if( result < 0 )
|
||||
{
|
||||
result = Pa_MaybeQueryDevices();
|
||||
if( result < 0 ) return result;
|
||||
result = sDefaultOutputDeviceID;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**********************************************************************
|
||||
** Make sure that we have queried the device capabilities.
|
||||
*/
|
||||
PaError PaHost_Init( void )
|
||||
{
|
||||
#if PA_SIMULATE_UNDERFLOW
|
||||
PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
|
||||
#endif
|
||||
return Pa_MaybeQueryDevices();
|
||||
}
|
||||
static PaError Pa_TimeSlice( internalPortAudioStream *past )
|
||||
{
|
||||
PaError result = 0;
|
||||
long bytesEmpty = 0;
|
||||
long bytesFilled = 0;
|
||||
long bytesToXfer = 0;
|
||||
long numChunks;
|
||||
HRESULT hresult;
|
||||
PaHostSoundControl *pahsc;
|
||||
short *nativeBufPtr;
|
||||
past->past_NumCallbacks += 1;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return paInternalError;
|
||||
/* How much input data is available? */
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled );
|
||||
bytesToXfer = bytesFilled;
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
/* How much output room is available? */
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty );
|
||||
bytesToXfer = bytesEmpty;
|
||||
}
|
||||
AddTraceMessage( "bytesEmpty ", bytesEmpty );
|
||||
/* Choose smallest value if both are active. */
|
||||
if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) )
|
||||
{
|
||||
bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty;
|
||||
}
|
||||
/* printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n",
|
||||
bytesFilled, bytesEmpty, bytesToXfer);
|
||||
*/
|
||||
/* Quantize to multiples of a buffer. */
|
||||
numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer;
|
||||
if( numChunks > (long)(past->past_NumUserBuffers/2) )
|
||||
{
|
||||
numChunks = (long)past->past_NumUserBuffers/2;
|
||||
}
|
||||
else if( numChunks < 0 )
|
||||
{
|
||||
numChunks = 0;
|
||||
}
|
||||
AddTraceMessage( "numChunks ", numChunks );
|
||||
nativeBufPtr = pahsc->pahsc_NativeBuffer;
|
||||
if( numChunks > 0 )
|
||||
{
|
||||
while( numChunks-- > 0 )
|
||||
{
|
||||
/* Measure usage based on time to process one user buffer. */
|
||||
Pa_StartUsageCalculation( past );
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
/* Get native data from DirectSound. */
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
|
||||
if( hresult < 0 )
|
||||
{
|
||||
ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult));
|
||||
sPaHostError = hresult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
/* Convert 16 bit native data to user data and call user routine. */
|
||||
result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr );
|
||||
if( result != 0) break;
|
||||
/* Pass native data to DirectSound. */
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
/* static short DEBUGHACK = 0;
|
||||
DEBUGHACK += 0x0049;
|
||||
nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */
|
||||
hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
|
||||
if( hresult < 0 )
|
||||
{
|
||||
ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult));
|
||||
sPaHostError = hresult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Pa_EndUsageCalculation( past );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/*******************************************************************/
|
||||
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
|
||||
{
|
||||
internalPortAudioStream *past;
|
||||
PaHostSoundControl *pahsc;
|
||||
#if PA_SIMULATE_UNDERFLOW
|
||||
gUnderCallbackCounter++;
|
||||
if( (gUnderCallbackCounter >= UNDER_START_GAP) &&
|
||||
(gUnderCallbackCounter <= UNDER_STOP_GAP) )
|
||||
{
|
||||
if( gUnderCallbackCounter == UNDER_START_GAP)
|
||||
{
|
||||
AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
|
||||
}
|
||||
if( gUnderCallbackCounter == UNDER_STOP_GAP)
|
||||
{
|
||||
AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
past = (internalPortAudioStream *) dwUser;
|
||||
if( past == NULL ) return;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return;
|
||||
if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive )
|
||||
{
|
||||
if( past->past_StopNow )
|
||||
{
|
||||
past->past_IsActive = 0;
|
||||
}
|
||||
else if( past->past_StopSoon )
|
||||
{
|
||||
DSoundWrapper *dsw = &pahsc->pahsc_DSoundWrapper;
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
DSW_ZeroEmptySpace( dsw );
|
||||
AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten );
|
||||
AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed );
|
||||
/* clear past_IsActive when all sound played */
|
||||
if( dsw->dsw_FramesPlayed >= past->past_FrameCount )
|
||||
{
|
||||
past->past_IsActive = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
past->past_IsActive = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pahsc->pahsc_IfInsideCallback = 1;
|
||||
if( Pa_TimeSlice( past ) != 0) /* Call time slice independant of timing method. */
|
||||
{
|
||||
past->past_StopSoon = 1;
|
||||
}
|
||||
pahsc->pahsc_IfInsideCallback = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*******************************************************************/
|
||||
PaError PaHost_OpenStream( internalPortAudioStream *past )
|
||||
{
|
||||
HRESULT hr;
|
||||
PaError result = paNoError;
|
||||
PaHostSoundControl *pahsc;
|
||||
int numBytes, maxChannels;
|
||||
unsigned int minNumBuffers;
|
||||
internalPortAudioDevice *pad;
|
||||
DSoundWrapper *dsw;
|
||||
/* Allocate and initialize host data. */
|
||||
pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */
|
||||
if( pahsc == NULL )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto error;
|
||||
}
|
||||
memset( pahsc, 0, sizeof(PaHostSoundControl) );
|
||||
past->past_DeviceData = (void *) pahsc;
|
||||
pahsc->pahsc_TimerID = 0;
|
||||
dsw = &pahsc->pahsc_DSoundWrapper;
|
||||
DSW_Init( dsw );
|
||||
/* Allocate native buffer. */
|
||||
maxChannels = ( past->past_NumOutputChannels > past->past_NumInputChannels ) ?
|
||||
past->past_NumOutputChannels : past->past_NumInputChannels;
|
||||
pahsc->pahsc_BytesPerBuffer = past->past_FramesPerUserBuffer * maxChannels * sizeof(short);
|
||||
if( maxChannels > 0 )
|
||||
{
|
||||
pahsc->pahsc_NativeBuffer = (short *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerBuffer); /* MEM */
|
||||
if( pahsc->pahsc_NativeBuffer == NULL )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = paInvalidChannelCount;
|
||||
goto error;
|
||||
}
|
||||
|
||||
DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer ));
|
||||
minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
|
||||
past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
|
||||
numBytes = pahsc->pahsc_BytesPerBuffer * past->past_NumUserBuffers;
|
||||
if( numBytes < DSBSIZE_MIN )
|
||||
{
|
||||
result = paBufferTooSmall;
|
||||
goto error;
|
||||
}
|
||||
if( numBytes > DSBSIZE_MAX )
|
||||
{
|
||||
result = paBufferTooBig;
|
||||
goto error;
|
||||
}
|
||||
pahsc->pahsc_FramesPerDSBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers;
|
||||
{
|
||||
int msecLatency = (int) ((pahsc->pahsc_FramesPerDSBuffer * 1000) / past->past_SampleRate);
|
||||
PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", pahsc->pahsc_FramesPerDSBuffer, msecLatency ));
|
||||
}
|
||||
/* ------------------ OUTPUT */
|
||||
if( (past->past_OutputDeviceID >= 0) && (past->past_NumOutputChannels > 0) )
|
||||
{
|
||||
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
|
||||
pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
|
||||
hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL );
|
||||
/* If this fails, then try each output device until we find one that works. */
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
int i;
|
||||
ERR_RPT(("Creation of requested Audio Output device '%s' failed.\n",
|
||||
((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
|
||||
for( i=0; i<Pa_CountDevices(); i++ )
|
||||
{
|
||||
pad = Pa_GetInternalDevice( i );
|
||||
if( pad->pad_Info.maxOutputChannels >= past->past_NumOutputChannels )
|
||||
{
|
||||
DBUG(("Try device '%s' instead.\n", pad->pad_Info.name ));
|
||||
hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL );
|
||||
if( hr == DS_OK )
|
||||
{
|
||||
ERR_RPT(("Using device '%s' instead.\n", pad->pad_Info.name ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
|
||||
result = paHostError;
|
||||
sPaHostError = hr;
|
||||
goto error;
|
||||
}
|
||||
hr = DSW_InitOutputBuffer( dsw,
|
||||
(unsigned long) (past->past_SampleRate + 0.5),
|
||||
past->past_NumOutputChannels, numBytes );
|
||||
DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
result = paHostError;
|
||||
sPaHostError = hr;
|
||||
goto error;
|
||||
}
|
||||
past->past_FrameCount = pahsc->pahsc_DSoundWrapper.dsw_FramesWritten;
|
||||
}
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
/* ------------------ INPUT */
|
||||
if( (past->past_InputDeviceID >= 0) && (past->past_NumInputChannels > 0) )
|
||||
{
|
||||
pad = Pa_GetInternalDevice( past->past_InputDeviceID );
|
||||
hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
|
||||
/* If this fails, then try each input device until we find one that works. */
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
int i;
|
||||
ERR_RPT(("Creation of requested Audio Capture device '%s' failed.\n",
|
||||
((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
|
||||
for( i=0; i<Pa_CountDevices(); i++ )
|
||||
{
|
||||
pad = Pa_GetInternalDevice( i );
|
||||
if( pad->pad_Info.maxInputChannels >= past->past_NumInputChannels )
|
||||
{
|
||||
PRINT(("Try device '%s' instead.\n", pad->pad_Info.name ));
|
||||
hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
|
||||
if( hr == DS_OK ) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
|
||||
result = paHostError;
|
||||
sPaHostError = hr;
|
||||
goto error;
|
||||
}
|
||||
hr = DSW_InitInputBuffer( dsw,
|
||||
(unsigned long) (past->past_SampleRate + 0.5),
|
||||
past->past_NumInputChannels, numBytes );
|
||||
DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
|
||||
result = paHostError;
|
||||
sPaHostError = hr;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
/* Calculate scalar used in CPULoad calculation. */
|
||||
{
|
||||
LARGE_INTEGER frequency;
|
||||
if( QueryPerformanceFrequency( &frequency ) == 0 )
|
||||
{
|
||||
pahsc->pahsc_InverseTicksPerUserBuffer = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pahsc->pahsc_InverseTicksPerUserBuffer = past->past_SampleRate /
|
||||
( (double)frequency.QuadPart * past->past_FramesPerUserBuffer );
|
||||
DBUG(("pahsc_InverseTicksPerUserBuffer = %g\n", pahsc->pahsc_InverseTicksPerUserBuffer ));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
error:
|
||||
PaHost_CloseStream( past );
|
||||
return result;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StartOutput( internalPortAudioStream *past )
|
||||
{
|
||||
HRESULT hr;
|
||||
PaHostSoundControl *pahsc;
|
||||
PaError result = paNoError;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
/* Give user callback a chance to pre-fill buffer. */
|
||||
result = Pa_TimeSlice( past );
|
||||
if( result != paNoError ) return result; // FIXME - what if finished?
|
||||
hr = DSW_StartOutput( &pahsc->pahsc_DSoundWrapper );
|
||||
DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
result = paHostError;
|
||||
sPaHostError = hr;
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StartInput( internalPortAudioStream *past )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
HRESULT hr;
|
||||
PaHostSoundControl *pahsc;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
hr = DSW_StartInput( &pahsc->pahsc_DSoundWrapper );
|
||||
DBUG(("Pa_StartStream: DSW_StartInput returned = 0x%X.\n", hr));
|
||||
if( hr != DS_OK )
|
||||
{
|
||||
result = paHostError;
|
||||
sPaHostError = hr;
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
return result;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StartEngine( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc;
|
||||
PaError result = paNoError;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
past->past_StopNow = 0;
|
||||
past->past_StopSoon = 0;
|
||||
past->past_IsActive = 1;
|
||||
/* Create timer that will wake us up so we can fill the DSound buffer. */
|
||||
{
|
||||
int msecPerBuffer;
|
||||
int resolution;
|
||||
int bufsPerInterrupt = past->past_NumUserBuffers/4;
|
||||
if( bufsPerInterrupt < 1 ) bufsPerInterrupt = 1;
|
||||
msecPerBuffer = 1000 * (bufsPerInterrupt * past->past_FramesPerUserBuffer) / (int) past->past_SampleRate;
|
||||
if( msecPerBuffer < 10 ) msecPerBuffer = 10;
|
||||
else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
|
||||
resolution = msecPerBuffer/4;
|
||||
pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
|
||||
(DWORD) past, TIME_PERIODIC );
|
||||
}
|
||||
if( pahsc->pahsc_TimerID == 0 )
|
||||
{
|
||||
past->past_IsActive = 0;
|
||||
result = paHostError;
|
||||
sPaHostError = 0;
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
|
||||
{
|
||||
int timeoutMsec;
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return paNoError;
|
||||
if( abort ) past->past_StopNow = 1;
|
||||
past->past_StopSoon = 1;
|
||||
/* Set timeout at 20% beyond maximum time we might wait. */
|
||||
timeoutMsec = (int) (1200.0 * pahsc->pahsc_FramesPerDSBuffer / past->past_SampleRate);
|
||||
while( past->past_IsActive && (timeoutMsec > 0) )
|
||||
{
|
||||
Sleep(10);
|
||||
timeoutMsec -= 10;
|
||||
}
|
||||
if( pahsc->pahsc_TimerID != 0 )
|
||||
{
|
||||
timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */
|
||||
pahsc->pahsc_TimerID = 0;
|
||||
}
|
||||
return paNoError;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
|
||||
{
|
||||
#if SUPPORT_AUDIO_CAPTURE
|
||||
HRESULT hr;
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return paNoError;
|
||||
(void) abort;
|
||||
hr = DSW_StopInput( &pahsc->pahsc_DSoundWrapper );
|
||||
DBUG(("DSW_StopInput() result is %x\n", hr));
|
||||
#endif /* SUPPORT_AUDIO_CAPTURE */
|
||||
return paNoError;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
|
||||
{
|
||||
HRESULT hr;
|
||||
PaHostSoundControl *pahsc;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return paNoError;
|
||||
(void) abort;
|
||||
hr = DSW_StopOutput( &pahsc->pahsc_DSoundWrapper );
|
||||
DBUG(("DSW_StopOutput() result is %x\n", hr));
|
||||
return paNoError;
|
||||
}
|
||||
/*******************************************************************/
|
||||
PaError PaHost_CloseStream( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc;
|
||||
if( past == NULL ) return paBadStreamPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return paNoError;
|
||||
DSW_Term( &pahsc->pahsc_DSoundWrapper );
|
||||
if( pahsc->pahsc_NativeBuffer )
|
||||
{
|
||||
PaHost_FreeFastMemory( pahsc->pahsc_NativeBuffer, pahsc->pahsc_BytesPerBuffer ); /* MEM */
|
||||
pahsc->pahsc_NativeBuffer = NULL;
|
||||
}
|
||||
PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */
|
||||
past->past_DeviceData = NULL;
|
||||
return paNoError;
|
||||
}
|
||||
/*************************************************************************
|
||||
** Determine minimum number of buffers required for this host based
|
||||
** on minimum latency. Latency can be optionally set by user by setting
|
||||
** an environment variable. For example, to set latency to 200 msec, put:
|
||||
**
|
||||
** set PA_MIN_LATENCY_MSEC=200
|
||||
**
|
||||
** in the AUTOEXEC.BAT file and reboot.
|
||||
** If the environment variable is not set, then the latency will be determined
|
||||
** based on the OS. Windows NT has higher latency than Win95.
|
||||
*/
|
||||
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
|
||||
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
|
||||
{
|
||||
char envbuf[PA_ENV_BUF_SIZE];
|
||||
DWORD hostVersion;
|
||||
DWORD hresult;
|
||||
int minLatencyMsec = 0;
|
||||
double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
|
||||
int minBuffers;
|
||||
/* Let user determine minimal latency by setting environment variable. */
|
||||
hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
|
||||
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
|
||||
{
|
||||
minLatencyMsec = atoi( envbuf );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set minimal latency based on whether NT or Win95.
|
||||
* NT has higher latency.
|
||||
*/
|
||||
hostVersion = GetVersion();
|
||||
/* High bit clear if NT */
|
||||
minLatencyMsec = ( (hostVersion & 0x80000000) == 0 ) ? PA_WIN_NT_LATENCY : PA_WIN_9X_LATENCY ;
|
||||
#if PA_USE_HIGH_LATENCY
|
||||
PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
|
||||
#endif
|
||||
|
||||
}
|
||||
minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
|
||||
if( minBuffers < 2 ) minBuffers = 2;
|
||||
return minBuffers;
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaError PaHost_Term( void )
|
||||
{
|
||||
int i;
|
||||
/* Free names allocated during enumeration. */
|
||||
for( i=0; i<sNumDevices; i++ )
|
||||
{
|
||||
if( sDevices[i].pad_Info.name != NULL )
|
||||
{
|
||||
free( (void *) sDevices[i].pad_Info.name );
|
||||
sDevices[i].pad_Info.name = NULL;
|
||||
}
|
||||
}
|
||||
if( sDevices != NULL )
|
||||
{
|
||||
PaHost_FreeFastMemory( sDevices, sNumDevices * sizeof(internalPortAudioDevice) ); /* MEM */
|
||||
sDevices = NULL;
|
||||
sNumDevices = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void Pa_Sleep( long msec )
|
||||
{
|
||||
Sleep( msec );
|
||||
}
|
||||
/*************************************************************************
|
||||
* Allocate memory that can be accessed in real-time.
|
||||
* This may need to be held in physical memory so that it is not
|
||||
* paged to virtual memory.
|
||||
* This call MUST be balanced with a call to PaHost_FreeFastMemory().
|
||||
* Memory will be set to zero.
|
||||
*/
|
||||
void *PaHost_AllocateFastMemory( long numBytes )
|
||||
{
|
||||
void *addr = GlobalAlloc( GPTR, numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
|
||||
return addr;
|
||||
}
|
||||
/*************************************************************************
|
||||
* Free memory that could be accessed in real-time.
|
||||
* This call MUST be balanced with a call to PaHost_AllocateFastMemory().
|
||||
*/
|
||||
void PaHost_FreeFastMemory( void *addr, long numBytes )
|
||||
{
|
||||
if( addr != NULL ) GlobalFree( addr ); /* MEM */
|
||||
}
|
||||
/***********************************************************************/
|
||||
PaError PaHost_StreamActive( internalPortAudioStream *past )
|
||||
{
|
||||
PaHostSoundControl *pahsc;
|
||||
if( past == NULL ) return paBadStreamPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return paInternalError;
|
||||
return (PaError) (past->past_IsActive);
|
||||
}
|
||||
/*************************************************************************/
|
||||
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
|
||||
{
|
||||
DSoundWrapper *dsw;
|
||||
internalPortAudioStream *past = (internalPortAudioStream *) stream;
|
||||
PaHostSoundControl *pahsc;
|
||||
if( past == NULL ) return paBadStreamPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
dsw = &pahsc->pahsc_DSoundWrapper;
|
||||
return dsw->dsw_FramesPlayed;
|
||||
}
|
||||
28
pa_win_ds/portaudio.def
Normal file
28
pa_win_ds/portaudio.def
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
LIBRARY PortAudio
|
||||
DESCRIPTION 'PortAudio Portable interface to audio HW'
|
||||
|
||||
EXPORTS
|
||||
; Explicit exports can go here
|
||||
Pa_Initialize @1
|
||||
Pa_Terminate @2
|
||||
Pa_GetHostError @3
|
||||
Pa_GetErrorText @4
|
||||
Pa_CountDevices @5
|
||||
Pa_GetDefaultInputDeviceID @6
|
||||
Pa_GetDefaultOutputDeviceID @7
|
||||
Pa_GetDeviceInfo @8
|
||||
Pa_OpenStream @9
|
||||
Pa_OpenDefaultStream @10
|
||||
Pa_CloseStream @11
|
||||
Pa_StartStream @12
|
||||
Pa_StopStream @13
|
||||
Pa_StreamActive @14
|
||||
Pa_StreamTime @15
|
||||
Pa_GetCPULoad @16
|
||||
Pa_GetMinNumBuffers @17
|
||||
Pa_Sleep @18
|
||||
|
||||
;123456789012345678901234567890123456
|
||||
;000000000111111111122222222223333333
|
||||
|
||||
|
||||
34
pa_win_wmme/Makefile.cygwin
Normal file
34
pa_win_wmme/Makefile.cygwin
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
# Makefile for PortAudio on cygwin
|
||||
# Contributed by Bill Eldridge on 6/13/2001
|
||||
|
||||
ARCH= pa_win_wmme
|
||||
|
||||
TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
|
||||
|
||||
.c.o:
|
||||
-gcc -c -I./pa_common $< -o $*.o
|
||||
-gcc $*.o -o $*.exe -L/usr/local/lib -L$(ARCH) -lportaudio.dll -lwinmm
|
||||
|
||||
all: sharedlib tests
|
||||
|
||||
sharedlib: ./pa_common/pa_lib.c
|
||||
gcc -c -I./pa_common pa_common/pa_lib.c -o pa_common/pa_lib.o
|
||||
gcc -c -I./pa_common pa_win_wmme/pa_win_wmme.c -o pa_win_wmme/pa_win_wmme.o
|
||||
dlltool --export-all --output-def pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o
|
||||
gcc -shared -Wl,--enable-auto-image-base -o pa_win_wmme/portaudio.dll -Wl,--out-implib=pa_win_wmme/libportaudio.dll.a pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/lib/w32api -lwinmm
|
||||
cp pa_win_wmme/portaudio.dll /usr/local/bin
|
||||
|
||||
tests: $(TESTS:.c=.o)
|
||||
|
||||
sine:
|
||||
gcc -c -I./pa_common pa_tests/patest_sine.c -o pa_tests/patest_sine.o
|
||||
gcc pa_tests/patest_sine.o -o pa_tests/patest_sine.exe -L/usr/local/lib -lportaudio.dll -lwinmm
|
||||
|
||||
clean:
|
||||
-rm ./pa_tests/*.exe
|
||||
|
||||
nothing:
|
||||
gcc pa_tests/patest_sine.o -L/usr/lib/w32api -L./pa_win_wmme -lportaudio.dll -lwinmm
|
||||
|
||||
|
||||
1493
pa_win_wmme/pa_win_wmme.c
Normal file
1493
pa_win_wmme/pa_win_wmme.c
Normal file
File diff suppressed because it is too large
Load diff
39
pablio/README.txt
Normal file
39
pablio/README.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
README for PABLIO
|
||||
Portable Audio Blocking I/O Library
|
||||
Author: Phil Burk
|
||||
|
||||
PABLIO is a simplified interface to PortAudio that provide
|
||||
read/write style blocking I/O.
|
||||
|
||||
Please see the .DOC file for documentation.
|
||||
|
||||
/*
|
||||
* More information on PortAudio at: http://www.portaudio.com
|
||||
* Copyright (c) 1999-2000 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
307
pablio/pablio.c
Normal file
307
pablio/pablio.c
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* $Id$
|
||||
* 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/
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "pablio.h"
|
||||
#include <string.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 );
|
||||
|
||||
/************************************************************************/
|
||||
/******** 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 )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* Allocate buffer. */
|
||||
static PaError PABLIO_InitFIFO( RingBuffer *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 );
|
||||
}
|
||||
|
||||
/* Free buffer. */
|
||||
static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
|
||||
{
|
||||
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 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;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that could be written to the stream without
|
||||
* having to wait.
|
||||
*/
|
||||
long GetAudioStreamWriteable( PABLIO_Stream *aStream )
|
||||
{
|
||||
int bytesEmpty = RingBuffer_GetWriteAvailable( &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 )
|
||||
{
|
||||
int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
|
||||
return bytesFull / aStream->bytesPerFrame;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
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);
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* 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 )
|
||||
{
|
||||
long bytesPerSample;
|
||||
long doRead = 0;
|
||||
long doWrite = 0;
|
||||
PaError err;
|
||||
PABLIO_Stream *aStream;
|
||||
long minNumBuffers;
|
||||
long numFrames;
|
||||
|
||||
/* 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;
|
||||
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 ? Pa_GetDefaultInputDeviceID() : paNoDevice),
|
||||
(doRead ? aStream->samplesPerFrame : 0 ),
|
||||
format,
|
||||
NULL,
|
||||
(doWrite ? 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;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
PaError CloseAudioStream( PABLIO_Stream *aStream )
|
||||
{
|
||||
PaError err;
|
||||
int bytesEmpty;
|
||||
int byteSize = aStream->outFIFO.bufferSize;
|
||||
|
||||
/* If we are writing data, make sure we play everything written. */
|
||||
if( byteSize > 0 )
|
||||
{
|
||||
bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
|
||||
while( bytesEmpty < byteSize )
|
||||
{
|
||||
Pa_Sleep( 10 );
|
||||
bytesEmpty = RingBuffer_GetWriteAvailable( &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;
|
||||
}
|
||||
35
pablio/pablio.def
Normal file
35
pablio/pablio.def
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
LIBRARY PABLIO
|
||||
DESCRIPTION 'PABLIO Portable Audio Blocking I/O'
|
||||
|
||||
EXPORTS
|
||||
; Explicit exports can go here
|
||||
Pa_Initialize @1
|
||||
Pa_Terminate @2
|
||||
Pa_GetHostError @3
|
||||
Pa_GetErrorText @4
|
||||
Pa_CountDevices @5
|
||||
Pa_GetDefaultInputDeviceID @6
|
||||
Pa_GetDefaultOutputDeviceID @7
|
||||
Pa_GetDeviceInfo @8
|
||||
Pa_OpenStream @9
|
||||
Pa_OpenDefaultStream @10
|
||||
Pa_CloseStream @11
|
||||
Pa_StartStream @12
|
||||
Pa_StopStream @13
|
||||
Pa_StreamActive @14
|
||||
Pa_StreamTime @15
|
||||
Pa_GetCPULoad @16
|
||||
Pa_GetMinNumBuffers @17
|
||||
Pa_Sleep @18
|
||||
|
||||
OpenAudioStream @19
|
||||
CloseAudioStream @20
|
||||
WriteAudioStream @21
|
||||
ReadAudioStream @22
|
||||
|
||||
Pa_GetSampleSize @23
|
||||
|
||||
;123456789012345678901234567890123456
|
||||
;000000000111111111122222222223333333
|
||||
|
||||
|
||||
109
pablio/pablio.h
Normal file
109
pablio/pablio.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#ifndef _PABLIO_H
|
||||
#define _PABLIO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
* PABLIO.h
|
||||
* Portable Audio Blocking read/write utility.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com/portaudio/
|
||||
*
|
||||
* 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/
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
RingBuffer inFIFO;
|
||||
RingBuffer outFIFO;
|
||||
PortAudioStream *stream;
|
||||
int bytesPerFrame;
|
||||
int samplesPerFrame;
|
||||
}
|
||||
PABLIO_Stream;
|
||||
|
||||
/* Values for flags for OpenAudioStream(). */
|
||||
#define PABLIO_READ (1<<0)
|
||||
#define PABLIO_WRITE (1<<1)
|
||||
#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE)
|
||||
#define PABLIO_MONO (1<<2)
|
||||
#define PABLIO_STEREO (1<<3)
|
||||
|
||||
/************************************************************
|
||||
* Write data to ring buffer.
|
||||
* Will not return until all the data has been written.
|
||||
*/
|
||||
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long 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 );
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that could be written to the stream without
|
||||
* having to wait.
|
||||
*/
|
||||
long GetAudioStreamWriteable( PABLIO_Stream *aStream );
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that are available to be read from the
|
||||
* stream without having to wait.
|
||||
*/
|
||||
long GetAudioStreamReadable( PABLIO_Stream *aStream );
|
||||
|
||||
/************************************************************
|
||||
* 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 **aStreamPtr, double sampleRate,
|
||||
PaSampleFormat format, long flags );
|
||||
|
||||
PaError CloseAudioStream( PABLIO_Stream *aStream );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _PABLIO_H */
|
||||
199
pablio/ringbuffer.c
Normal file
199
pablio/ringbuffer.c
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* $Id$
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#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;
|
||||
}
|
||||
101
pablio/ringbuffer.h
Normal file
101
pablio/ringbuffer.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#ifndef _RINGBUFFER_H
|
||||
#define _RINGBUFFER_H
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#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. */
|
||||
long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
|
||||
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 written 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 */
|
||||
99
pablio/test_rw.c
Normal file
99
pablio/test_rw.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* $Id$
|
||||
* test_rw.c
|
||||
* Read input from one stream and write it to another.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com/portaudio/
|
||||
*
|
||||
* This program uses 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/
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "pablio.h"
|
||||
|
||||
/*
|
||||
** Note that many of the older ISA sound cards on PCs do NOT support
|
||||
** full duplex audio (simultaneous record and playback).
|
||||
** And some only support full duplex at lower sample rates.
|
||||
*/
|
||||
#define SAMPLE_RATE (22050)
|
||||
#define NUM_SECONDS (15)
|
||||
#define SAMPLES_PER_FRAME (2)
|
||||
|
||||
/* Select whether we will use floats or shorts. */
|
||||
#if 1
|
||||
#define SAMPLE_TYPE paFloat32
|
||||
typedef float SAMPLE;
|
||||
#else
|
||||
#define SAMPLE_TYPE paInt16
|
||||
typedef short SAMPLE;
|
||||
#endif
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
SAMPLE samples[SAMPLES_PER_FRAME];
|
||||
PaError err;
|
||||
PABLIO_Stream *aStream;
|
||||
|
||||
printf("Full duplex sound test using PortAudio and RingBuffers\n");
|
||||
fflush(stdout);
|
||||
|
||||
/* Open simplified blocking I/O layer on top of PortAudio. */
|
||||
err = OpenAudioStream( &aStream, SAMPLE_RATE, SAMPLE_TYPE,
|
||||
(PABLIO_READ_WRITE | PABLIO_STEREO) );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Process samples in the foreground. */
|
||||
for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
|
||||
{
|
||||
/* Read one frame of data into sample array from audio input. */
|
||||
ReadAudioStream( aStream, samples, 1 );
|
||||
/* Write that same frame of data to output. */
|
||||
/* samples[1] = (i&256) * (1.0f/256.0f); /* sawtooth */
|
||||
WriteAudioStream( aStream, samples, 1 );
|
||||
}
|
||||
|
||||
CloseAudioStream( aStream );
|
||||
|
||||
printf("Full duplex sound test complete.\n" );
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
Pa_Terminate();
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
123
pablio/test_rw_echo.c
Normal file
123
pablio/test_rw_echo.c
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* $Id$
|
||||
* test_rw_echo.c
|
||||
* Echo delayed input to output.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com/portaudio/
|
||||
*
|
||||
* This program uses PABLIO, the Portable Audio Blocking I/O Library.
|
||||
* PABLIO is built on top of PortAudio, the Portable Audio Library.
|
||||
*
|
||||
* Note that if you need low latency, you should not use PABLIO.
|
||||
* Use the PA_OpenStream callback technique which is lower level
|
||||
* than PABLIO.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Note that many of the older ISA sound cards on PCs do NOT support
|
||||
** full duplex audio (simultaneous record and playback).
|
||||
** And some only support full duplex at lower sample rates.
|
||||
*/
|
||||
#define SAMPLE_RATE (22050)
|
||||
#define NUM_SECONDS (20)
|
||||
#define SAMPLES_PER_FRAME (2)
|
||||
|
||||
/* Select whether we will use floats or shorts. */
|
||||
#if 1
|
||||
#define SAMPLE_TYPE paFloat32
|
||||
typedef float SAMPLE;
|
||||
#else
|
||||
#define SAMPLE_TYPE paInt16
|
||||
typedef short SAMPLE;
|
||||
#endif
|
||||
|
||||
#define NUM_ECHO_FRAMES (2*SAMPLE_RATE)
|
||||
SAMPLE samples[NUM_ECHO_FRAMES][SAMPLES_PER_FRAME] = {0.0};
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
PaError err;
|
||||
PABLIO_Stream *aInStream;
|
||||
PABLIO_Stream *aOutStream;
|
||||
int index;
|
||||
|
||||
printf("Full duplex sound test using PABLIO\n");
|
||||
fflush(stdout);
|
||||
|
||||
/* Open simplified blocking I/O layer on top of PortAudio. */
|
||||
/* Open input first so it can start to fill buffers. */
|
||||
err = OpenAudioStream( &aInStream, SAMPLE_RATE, SAMPLE_TYPE,
|
||||
(PABLIO_READ | PABLIO_STEREO) );
|
||||
if( err != paNoError ) goto error;
|
||||
/* printf("opened input\n"); fflush(stdout); /**/
|
||||
|
||||
err = OpenAudioStream( &aOutStream, SAMPLE_RATE, SAMPLE_TYPE,
|
||||
(PABLIO_WRITE | PABLIO_STEREO) );
|
||||
if( err != paNoError ) goto error;
|
||||
/* printf("opened output\n"); fflush(stdout); /**/
|
||||
|
||||
/* Process samples in the foreground. */
|
||||
index = 0;
|
||||
for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
|
||||
{
|
||||
/* Write old frame of data to output. */
|
||||
/* samples[index][1] = (i&256) * (1.0f/256.0f); /* sawtooth */
|
||||
WriteAudioStream( aOutStream, &samples[index][0], 1 );
|
||||
|
||||
/* Read one frame of data into sample array for later output. */
|
||||
ReadAudioStream( aInStream, &samples[index][0], 1 );
|
||||
index += 1;
|
||||
if( index >= NUM_ECHO_FRAMES ) index = 0;
|
||||
|
||||
if( (i & 0xFFFF) == 0 ) printf("i = %d\n", i ); fflush(stdout); /**/
|
||||
}
|
||||
|
||||
CloseAudioStream( aOutStream );
|
||||
CloseAudioStream( aInStream );
|
||||
|
||||
printf("R/W echo sound test complete.\n" );
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fprintf( stderr, "An error occured while using PortAudio\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
108
pablio/test_w_saw.c
Normal file
108
pablio/test_w_saw.c
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* $Id$
|
||||
* test_w_saw.c
|
||||
* Generate stereo sawtooth waveforms.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program uses 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/
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define SAMPLE_RATE (44100)
|
||||
#define NUM_SECONDS (6)
|
||||
#define SAMPLES_PER_FRAME (2)
|
||||
|
||||
#define FREQUENCY (220.0f)
|
||||
#define PHASE_INCREMENT (2.0f * FREQUENCY / SAMPLE_RATE)
|
||||
#define FRAMES_PER_BLOCK (100)
|
||||
|
||||
float samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
|
||||
float phases[SAMPLES_PER_FRAME];
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i,j;
|
||||
PaError err;
|
||||
PABLIO_Stream *aOutStream;
|
||||
|
||||
printf("Generate sawtooth waves using PABLIO.\n");
|
||||
fflush(stdout);
|
||||
|
||||
/* Open simplified blocking I/O layer on top of PortAudio. */
|
||||
err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32,
|
||||
(PABLIO_WRITE | PABLIO_STEREO) );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Initialize oscillator phases. */
|
||||
phases[0] = 0.0;
|
||||
phases[1] = 0.0;
|
||||
|
||||
for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
|
||||
{
|
||||
/* Generate sawtooth waveforms in a block for efficiency. */
|
||||
for( j=0; j<FRAMES_PER_BLOCK; j++ )
|
||||
{
|
||||
/* Generate a sawtooth wave by incrementing a variable. */
|
||||
phases[0] += PHASE_INCREMENT;
|
||||
/* The signal range is -1.0 to +1.0 so wrap around if we go over. */
|
||||
if( phases[0] > 1.0f ) phases[0] -= 2.0f;
|
||||
samples[j][0] = phases[0];
|
||||
|
||||
/* On the second channel, generate a sawtooth wave a fifth higher. */
|
||||
phases[1] += PHASE_INCREMENT * (3.0f / 2.0f);
|
||||
if( phases[1] > 1.0f ) phases[1] -= 2.0f;
|
||||
samples[j][1] = phases[1];
|
||||
}
|
||||
|
||||
/* Write samples to output. */
|
||||
WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
|
||||
}
|
||||
|
||||
CloseAudioStream( aOutStream );
|
||||
|
||||
printf("Sawtooth sound test complete.\n" );
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fprintf( stderr, "An error occured while using PABLIO\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
106
pablio/test_w_saw8.c
Normal file
106
pablio/test_w_saw8.c
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* $Id$
|
||||
* test_w_saw8.c
|
||||
* Generate stereo 8 bit sawtooth waveforms.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program uses 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/
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define SAMPLE_RATE (22050)
|
||||
#define NUM_SECONDS (6)
|
||||
#define SAMPLES_PER_FRAME (2)
|
||||
|
||||
|
||||
#define FRAMES_PER_BLOCK (100)
|
||||
|
||||
unsigned char samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
|
||||
unsigned char phases[SAMPLES_PER_FRAME];
|
||||
|
||||
/*******************************************************************/
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
int i,j;
|
||||
PaError err;
|
||||
PABLIO_Stream *aOutStream;
|
||||
|
||||
printf("Generate unsigned 8 bit sawtooth waves using PABLIO.\n");
|
||||
fflush(stdout);
|
||||
|
||||
/* Open simplified blocking I/O layer on top of PortAudio. */
|
||||
err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paUInt8,
|
||||
(PABLIO_WRITE | PABLIO_STEREO) );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Initialize oscillator phases to "ground" level for paUInt8. */
|
||||
phases[0] = 128;
|
||||
phases[1] = 128;
|
||||
|
||||
for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
|
||||
{
|
||||
/* Generate sawtooth waveforms in a block for efficiency. */
|
||||
for( j=0; j<FRAMES_PER_BLOCK; j++ )
|
||||
{
|
||||
/* Generate a sawtooth wave by incrementing a variable. */
|
||||
phases[0] += 1;
|
||||
/* We don't have to do anything special to wrap when using paUint8 because
|
||||
* 8 bit arithmetic automatically wraps. */
|
||||
samples[j][0] = phases[0];
|
||||
|
||||
/* On the second channel, generate a higher sawtooth wave. */
|
||||
phases[1] += 3;
|
||||
samples[j][1] = phases[1];
|
||||
}
|
||||
|
||||
/* Write samples to output. */
|
||||
WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
|
||||
}
|
||||
|
||||
CloseAudioStream( aOutStream );
|
||||
|
||||
printf("Sawtooth sound test complete.\n" );
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fprintf( stderr, "An error occured while using PABLIO\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return -1;
|
||||
}
|
||||
Loading…
Reference in a new issue