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