aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/pa_linux_alsa
diff options
context:
space:
mode:
authorGuenter Geiger <ggeiger@users.sourceforge.net>2003-05-09 16:04:00 +0000
committerGuenter Geiger <ggeiger@users.sourceforge.net>2003-05-09 16:04:00 +0000
commit9c0e19a3be2288db79e2502e5fa450c3e20a668d (patch)
treeca97ce615e037a533304fc4660dcf372ca3b9cd6 /pd/portaudio/pa_linux_alsa
parentef50dd62804d54af7da18d8bd8413c0dccd729b8 (diff)
This commit was generated by cvs2svn to compensate for changes in r610,
which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=611
Diffstat (limited to 'pd/portaudio/pa_linux_alsa')
-rw-r--r--pd/portaudio/pa_linux_alsa/blocking_calls.c61
-rw-r--r--pd/portaudio/pa_linux_alsa/blocking_calls.obin0 -> 1180 bytes
-rw-r--r--pd/portaudio/pa_linux_alsa/callback_thread.c374
-rw-r--r--pd/portaudio/pa_linux_alsa/callback_thread.obin0 -> 4180 bytes
-rw-r--r--pd/portaudio/pa_linux_alsa/pa_linux_alsa.c989
-rw-r--r--pd/portaudio/pa_linux_alsa/pa_linux_alsa.h45
-rw-r--r--pd/portaudio/pa_linux_alsa/pa_linux_alsa.obin0 -> 10728 bytes
7 files changed, 1469 insertions, 0 deletions
diff --git a/pd/portaudio/pa_linux_alsa/blocking_calls.c b/pd/portaudio/pa_linux_alsa/blocking_calls.c
new file mode 100644
index 00000000..6304b117
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/blocking_calls.c
@@ -0,0 +1,61 @@
+
+#include "pa_stream.h"
+
+#include "pa_linux_alsa.h"
+
+PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ /* TODO: handle failure, xruns */
+
+ if( stream->capture_interleaved )
+ {
+ snd_pcm_mmap_readi( stream->pcm_capture, buffer, frames );
+ }
+ else
+ {
+ snd_pcm_mmap_readn( stream->pcm_capture, (void**)buffer, frames );
+ }
+
+ return paNoError;
+}
+
+
+PaError WriteStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ if( stream->playback_interleaved )
+ {
+ snd_pcm_mmap_writei( stream->pcm_playback, buffer, frames );
+ }
+ else
+ {
+ snd_pcm_mmap_writen( stream->pcm_playback, (void**)buffer, frames );
+ }
+
+ return paNoError;
+}
+
+
+unsigned long GetStreamReadAvailable( PaStream* s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ return snd_pcm_avail_update( stream->pcm_capture );
+}
+
+
+unsigned long GetStreamWriteAvailable( PaStream* s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ return snd_pcm_avail_update( stream->pcm_playback );
+}
+
+
diff --git a/pd/portaudio/pa_linux_alsa/blocking_calls.o b/pd/portaudio/pa_linux_alsa/blocking_calls.o
new file mode 100644
index 00000000..d4bc2108
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/blocking_calls.o
Binary files differ
diff --git a/pd/portaudio/pa_linux_alsa/callback_thread.c b/pd/portaudio/pa_linux_alsa/callback_thread.c
new file mode 100644
index 00000000..483557b6
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/callback_thread.c
@@ -0,0 +1,374 @@
+
+#include <sys/poll.h>
+#include <limits.h>
+#include <math.h> /* abs() */
+
+#include <alsa/asoundlib.h>
+
+#include "pa_linux_alsa.h"
+
+#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
+
+static int wait( PaAlsaStream *stream )
+{
+ int need_capture;
+ int need_playback;
+ int capture_avail = INT_MAX;
+ int playback_avail = INT_MAX;
+ int common_avail;
+
+ if( stream->pcm_capture )
+ need_capture = 1;
+ else
+ need_capture = 0;
+
+ if( stream->pcm_playback )
+ need_playback = 1;
+ else
+ need_playback = 0;
+
+ while( need_capture || need_playback )
+ {
+ int playback_pfd_offset=0;
+ int total_fds = 0;
+
+ /* if the main thread has requested that we stop, do so now */
+ pthread_testcancel();
+
+ /*printf("still polling...\n");
+ if( need_capture )
+ printf("need capture.\n");
+ if( need_playback )
+ printf("need playback.\n"); */
+
+ /* get the fds, packing all applicable fds into a single array,
+ * so we can check them all with a single poll() call */
+
+ if( need_capture )
+ {
+ snd_pcm_poll_descriptors( stream->pcm_capture, stream->pfds,
+ stream->capture_nfds );
+ total_fds += stream->capture_nfds;
+ }
+
+ if( need_playback )
+ {
+ playback_pfd_offset = total_fds;
+ snd_pcm_poll_descriptors( stream->pcm_playback,
+ stream->pfds + playback_pfd_offset,
+ stream->playback_nfds );
+ total_fds += stream->playback_nfds;
+ }
+
+ /* now poll on the combination of playback and capture fds.
+ * TODO: handle interrupt and/or failure */
+ poll( stream->pfds, total_fds, 1000 );
+
+ /* check the return status of our pfds */
+ if( need_capture )
+ {
+ short revents;
+ snd_pcm_poll_descriptors_revents( stream->pcm_capture, stream->pfds,
+ stream->capture_nfds, &revents );
+ if( revents == POLLIN )
+ need_capture = 0;
+ }
+
+ if( need_playback )
+ {
+ short revents;
+ snd_pcm_poll_descriptors_revents( stream->pcm_playback,
+ stream->pfds + playback_pfd_offset,
+ stream->playback_nfds, &revents );
+ //if( revents & POLLOUT )
+ //if( revents & POLLERR )
+ // printf("polling error!");
+ if( revents == POLLOUT )
+ need_playback = 0;
+ }
+ }
+
+ /* we have now established that there are buffers ready to be
+ * operated on. Now determine how many frames are available. */
+ if( stream->pcm_capture )
+ capture_avail = snd_pcm_avail_update( stream->pcm_capture );
+
+ if( stream->pcm_playback )
+ playback_avail = snd_pcm_avail_update( stream->pcm_playback );
+
+ common_avail = MIN(capture_avail, playback_avail);
+ common_avail -= common_avail % stream->frames_per_period;
+
+ return common_avail;
+}
+
+static int setup_buffers( PaAlsaStream *stream, int frames_avail )
+{
+ int i;
+ int capture_frames_avail = INT_MAX;
+ int playback_frames_avail = INT_MAX;
+ int common_frames_avail;
+
+ if( stream->pcm_capture )
+ {
+ const snd_pcm_channel_area_t *capture_areas;
+ const snd_pcm_channel_area_t *area;
+ snd_pcm_uframes_t frames = frames_avail;
+
+ /* I do not understand this code fragment yet, it is copied out of the
+ * alsa-devel archives... */
+ snd_pcm_mmap_begin( stream->pcm_capture, &capture_areas,
+ &stream->capture_offset, &frames);
+
+ if( stream->capture_interleaved )
+ {
+ void *interleaved_capture_buffer;
+ area = &capture_areas[0];
+ interleaved_capture_buffer = area->addr +
+ (area->first + area->step * stream->capture_offset) / 8;
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+ 0, /* starting at channel 0 */
+ interleaved_capture_buffer,
+ 0 /* default numInputChannels */
+ );
+ }
+ else
+ {
+ /* noninterleaved */
+ void *noninterleaved_capture_buffers[1000];
+ for( i = 0; i < stream->capture_channels; i++ )
+ {
+ area = &capture_areas[i];
+ noninterleaved_capture_buffers[i] = area->addr +
+ (area->first + area->step * stream->capture_offset) / 8;
+ PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor,
+ i,
+ noninterleaved_capture_buffers[i]);
+ }
+ }
+
+ capture_frames_avail = frames;
+ }
+
+ if( stream->pcm_playback )
+ {
+ const snd_pcm_channel_area_t *playback_areas;
+ const snd_pcm_channel_area_t *area;
+ snd_pcm_uframes_t frames = frames_avail;
+
+ /* I do not understand this code fragment yet, it is copied out of the
+ * alsa-devel archives... */
+ snd_pcm_mmap_begin( stream->pcm_playback, &playback_areas,
+ &stream->playback_offset, &frames);
+
+ if( stream->playback_interleaved )
+ {
+ void *interleaved_playback_buffer;
+ area = &playback_areas[0];
+ interleaved_playback_buffer = area->addr +
+ (area->first + area->step * stream->playback_offset) / 8;
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+ 0, /* starting at channel 0 */
+ interleaved_playback_buffer,
+ 0 /* default numInputChannels */
+ );
+ }
+ else
+ {
+ /* noninterleaved */
+ void *noninterleaved_playback_buffers[1000];
+ for( i = 0; i < stream->playback_channels; i++ )
+ {
+ area = &playback_areas[i];
+ noninterleaved_playback_buffers[i] = area->addr +
+ (area->first + area->step * stream->playback_offset) / 8;
+ PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor,
+ i,
+ noninterleaved_playback_buffers[i]);
+ }
+ }
+
+ playback_frames_avail = frames;
+ }
+
+
+ common_frames_avail = MIN(capture_frames_avail, playback_frames_avail);
+ common_frames_avail -= common_frames_avail % stream->frames_per_period;
+ //printf( "%d capture frames available\n", capture_frames_avail );
+ //printf( "%d frames playback available\n", playback_frames_avail );
+ //printf( "%d frames available\n", common_frames_avail );
+
+ if( stream->pcm_capture )
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, common_frames_avail );
+
+ if( stream->pcm_playback )
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, common_frames_avail );
+
+ return common_frames_avail;
+}
+
+void *CallbackThread( void *userData )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)userData;
+
+ if( stream->pcm_capture )
+ snd_pcm_start( stream->pcm_capture );
+ if( stream->pcm_playback )
+ snd_pcm_start( stream->pcm_playback );
+
+ while(1)
+ {
+ int frames_avail;
+ int frames_got;
+
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
+ int callbackResult;
+ int framesProcessed;
+
+ pthread_testcancel();
+ {
+ /* calculate time info */
+ snd_timestamp_t capture_timestamp;
+ snd_timestamp_t playback_timestamp;
+ snd_pcm_status_t *capture_status;
+ snd_pcm_status_t *playback_status;
+ snd_pcm_status_alloca( &capture_status );
+ snd_pcm_status_alloca( &playback_status );
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_status( stream->pcm_capture, capture_status );
+ snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
+ }
+ if( stream->pcm_playback )
+ {
+ snd_pcm_status( stream->pcm_playback, playback_status );
+ snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
+ }
+
+ /* Hmm, we potentially have both a playback and a capture timestamp.
+ * Hopefully they are the same... */
+ if( stream->pcm_capture && stream->pcm_playback )
+ {
+ float capture_time = capture_timestamp.tv_sec +
+ ((float)capture_timestamp.tv_usec/1000000);
+ float playback_time= playback_timestamp.tv_sec +
+ ((float)playback_timestamp.tv_usec/1000000);
+ if( fabsf(capture_time-playback_time) > 0.01 )
+ printf("Capture time and playback time differ by %f\n", fabsf(capture_time-playback_time));
+ timeInfo.currentTime = capture_time;
+ }
+ else if( stream->pcm_playback )
+ {
+ timeInfo.currentTime = playback_timestamp.tv_sec +
+ ((float)playback_timestamp.tv_usec/1000000);
+ }
+ else
+ {
+ timeInfo.currentTime = capture_timestamp.tv_sec +
+ ((float)capture_timestamp.tv_usec/1000000);
+ }
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_sframes_t capture_delay = snd_pcm_status_get_delay( capture_status );
+ timeInfo.inputBufferAdcTime = timeInfo.currentTime -
+ (float)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_sframes_t playback_delay = snd_pcm_status_get_delay( playback_status );
+ timeInfo.outputBufferDacTime = timeInfo.currentTime +
+ (float)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
+ }
+ }
+
+
+ /*
+ IMPLEMENT ME:
+ - handle buffer slips
+ */
+
+ /*
+ depending on whether the host buffers are interleaved, non-interleaved
+ or a mixture, you will want to call PaUtil_ProcessInterleavedBuffers(),
+ PaUtil_ProcessNonInterleavedBuffers() or PaUtil_ProcessBuffers() here.
+ */
+
+ frames_avail = wait( stream );
+ //printf( "%d frames available\n", frames_avail );
+
+ /* Now we know the soundcard is ready to produce/receive at least
+ * one period. We just need to get the buffers for the client
+ * to read/write. */
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo );
+
+ frames_got = setup_buffers( stream, frames_avail );
+
+ if( frames_avail == frames_got )
+ ;//printf("good, they were both %d\n", frames_avail );
+ else
+ printf("damn, they were different: avail: %d, got: %d\n", frames_avail, frames_got );
+
+ /* this calls the callback */
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
+ &callbackResult );
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+ /* inform ALSA how many frames we wrote */
+
+ if( stream->pcm_capture )
+ snd_pcm_mmap_commit( stream->pcm_capture, stream->capture_offset, frames_avail );
+
+ if( stream->pcm_playback )
+ snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames_avail );
+
+
+ /*
+ If you need to byte swap outputBuffer, you can do it here using
+ routines in pa_byteswappers.h
+ */
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ stream->callback_finished = 1;
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_drop( stream->pcm_capture );
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_drop( stream->pcm_playback );
+ }
+ pthread_exit(NULL);
+ }
+ else
+ {
+ stream->callback_finished = 1;
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_drain( stream->pcm_capture );
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_drain( stream->pcm_playback );
+ }
+ pthread_exit(NULL);
+ }
+
+ }
+}
+
diff --git a/pd/portaudio/pa_linux_alsa/callback_thread.o b/pd/portaudio/pa_linux_alsa/callback_thread.o
new file mode 100644
index 00000000..a242d490
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/callback_thread.o
Binary files differ
diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c
new file mode 100644
index 00000000..9582b5b8
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c
@@ -0,0 +1,989 @@
+/*
+ * $Id: pa_linux_alsa.c,v 1.1.2.3 2003/02/01 21:55:03 joshua Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * ALSA implementation by Joshua Haberman
+ *
+ * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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 <sys/poll.h>
+#include <pthread.h>
+
+#include <string.h> /* strlen() */
+#include <limits.h>
+
+#include <alsa/asoundlib.h>
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_linux_alsa.h"
+
+/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation commonHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ PaHostApiIndex hostApiIndex;
+}
+PaAlsaHostApiRepresentation;
+
+
+/* prototypes for functions declared in this file */
+
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *callback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
+
+/* blocking calls are in blocking_calls.c */
+extern PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+extern PaError WriteStream( PaStream* stream, void *buffer, unsigned long frames );
+extern signed long GetStreamReadAvailable( PaStream* stream );
+extern signed long GetStreamWriteAvailable( PaStream* stream );
+
+/* all callback-related functions are in callback_thread.c */
+extern void *CallbackThread( void *userData );
+
+
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, deviceCount;
+ PaAlsaHostApiRepresentation *skeletonHostApi;
+
+ skeletonHostApi = (PaAlsaHostApiRepresentation*)
+ PaUtil_AllocateMemory( sizeof(PaAlsaHostApiRepresentation) );
+ if( !skeletonHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !skeletonHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ skeletonHostApi->hostApiIndex = hostApiIndex;
+ *hostApi = (PaUtilHostApiRepresentation*)skeletonHostApi;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paALSA;
+ (*hostApi)->info.name = "ALSA implementation";
+
+ BuildDeviceList( skeletonHostApi );
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+
+ PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream,
+ IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyReadWrite, PaUtil_DummyReadWrite,
+ PaUtil_DummyGetAvailable,
+ PaUtil_DummyGetAvailable );
+
+ PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream,
+ IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream,
+ GetStreamReadAvailable,
+ GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( skeletonHostApi )
+ {
+ if( skeletonHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( skeletonHostApi );
+ }
+ return result;
+}
+
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
+{
+ PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
+ PaDeviceInfo *deviceInfoArray;
+ int deviceCount = 0;
+ int card_idx;
+ int device_idx;
+ snd_ctl_t *ctl;
+ snd_ctl_card_info_t *card_info;
+
+ /* count the devices by enumerating all the card numbers */
+
+ /* snd_card_next() modifies the integer passed to it to be:
+ * the index of the first card if the parameter is -1
+ * the index of the next card if the parameter is the index of a card
+ * -1 if there are no more cards
+ *
+ * The function itself returns 0 if it succeeded. */
+ card_idx = -1;
+ while( snd_card_next( &card_idx ) == 0 && card_idx >= 0 )
+ {
+ deviceCount++;
+ }
+
+ /* allocate deviceInfo memory based on the number of devices */
+
+ commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ alsaApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+ if( !commonApi->deviceInfos )
+ {
+ return paInsufficientMemory;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ alsaApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+ if( !deviceInfoArray )
+ {
+ return paInsufficientMemory;
+ }
+
+ /* now loop over the list of devices again, filling in the deviceInfo for each */
+ card_idx = -1;
+ device_idx = 0;
+ while( snd_card_next( &card_idx ) == 0 && card_idx >= 0 )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[device_idx];
+ char *deviceName;
+ char alsaDeviceName[50];
+ const char *cardName;
+
+ commonApi->deviceInfos[device_idx++] = deviceInfo;
+
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = alsaApi->hostApiIndex;
+
+ sprintf( alsaDeviceName, "hw:%d", card_idx );
+ snd_ctl_open( &ctl, alsaDeviceName, 0 );
+ snd_ctl_card_info_malloc( &card_info );
+ snd_ctl_card_info( ctl, card_info );
+ cardName = snd_ctl_card_info_get_id( card_info );
+
+ deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+ strlen(cardName) + 1 );
+ if( !deviceName )
+ {
+ return paInsufficientMemory;
+ }
+ strcpy( deviceName, cardName );
+ deviceInfo->name = deviceName;
+
+ snd_ctl_card_info_free( card_info );
+
+ /* to determine max. channels, we must open the device and query the
+ * hardware parameter configuration space */
+ {
+ snd_pcm_t *pcm_handle;
+ snd_pcm_hw_params_t *hw_params;
+ int dir;
+
+ snd_pcm_hw_params_malloc( &hw_params );
+
+ /* get max channels for capture */
+
+ if( snd_pcm_open( &pcm_handle, alsaDeviceName, SND_PCM_STREAM_CAPTURE, 0 ) < 0 )
+ {
+ deviceInfo->maxInputChannels = 0;
+ }
+ else
+ {
+ snd_pcm_hw_params_any( pcm_handle, hw_params );
+ deviceInfo->maxInputChannels = snd_pcm_hw_params_get_channels_max( hw_params );
+ /* TODO: I'm not really sure what to do here */
+ //deviceInfo->defaultLowInputLatency = snd_pcm_hw_params_get_period_size_min( hw_params, &dir );
+ //deviceInfo->defaultHighInputLatency = snd_pcm_hw_params_get_period_size_max( hw_params, &dir );
+ deviceInfo->defaultLowInputLatency = 128. / 44100;
+ deviceInfo->defaultHighInputLatency = 16384. / 44100;
+ snd_pcm_close( pcm_handle );
+ }
+
+ /* get max channels for playback */
+ if( snd_pcm_open( &pcm_handle, alsaDeviceName, SND_PCM_STREAM_PLAYBACK, 0 ) < 0 )
+ {
+ deviceInfo->maxOutputChannels = 0;
+ }
+ else
+ {
+ snd_pcm_hw_params_any( pcm_handle, hw_params );
+ deviceInfo->maxOutputChannels = snd_pcm_hw_params_get_channels_max( hw_params );
+ /* TODO: I'm not really sure what to do here */
+ //deviceInfo->defaultLowOutputLatency = snd_pcm_hw_params_get_period_size_min( hw_params, &dir );
+ //deviceInfo->defaultHighOutputLatency = snd_pcm_hw_params_get_period_size_max( hw_params, &dir );
+ deviceInfo->defaultLowOutputLatency = 128. / 44100;
+ deviceInfo->defaultHighOutputLatency = 16384. / 44100;
+ snd_pcm_close( pcm_handle );
+ }
+
+ snd_pcm_hw_params_free( hw_params );
+ }
+
+ deviceInfo->defaultSampleRate = 44100.; /* IMPLEMENT ME */
+ }
+
+ commonApi->info.deviceCount = deviceCount;
+ commonApi->info.defaultInputDevice = 0;
+ commonApi->info.defaultOutputDevice = 0;
+
+ return paNoError;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaAlsaHostApiRepresentation *skeletonHostApi;
+ skeletonHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+ /*
+ IMPLEMENT ME:
+ - clean up any resourced not handled by the allocation group
+ */
+
+ if( skeletonHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( skeletonHostApi );
+}
+
+
+/* Given an open stream, what sample formats are available? */
+
+static PaSampleFormat GetAvailableFormats( snd_pcm_t *stream )
+{
+ PaSampleFormat available = 0;
+ snd_pcm_hw_params_t *hw_params;
+ snd_pcm_hw_params_alloca( &hw_params );
+
+ snd_pcm_hw_params_any( stream, hw_params );
+
+ if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_FLOAT ) == 0)
+ available |= paFloat32;
+
+ if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S16 ) == 0)
+ available |= paInt16;
+
+ if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S24 ) == 0)
+ available |= paInt24;
+
+ if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S32 ) == 0)
+ available |= paInt32;
+
+ if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S8 ) == 0)
+ available |= paInt8;
+
+ if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_U8 ) == 0)
+ available |= paUInt8;
+
+ return available;
+}
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError ConfigureStream( snd_pcm_t *stream, int channels,
+ int interleaved, unsigned long rate,
+ PaSampleFormat pa_format, int framesPerBuffer )
+{
+#define ENSURE(functioncall) \
+ if( (functioncall) < 0 ) { \
+ printf("Error executing ALSA call, line %d\n", __LINE__); \
+ return 1; \
+ } \
+ else { \
+ printf("ALSA call at line %d succeeded\n", __LINE__ ); \
+ }
+
+ snd_pcm_access_t access_mode;
+ snd_pcm_format_t alsa_format;
+
+ /* configuration consists of setting all of ALSA's parameters.
+ * These parameters come in two flavors: hardware parameters
+ * and software paramters. Hardware parameters will affect
+ * the way the device is initialized, software parameters
+ * affect the way ALSA interacts with me, the user-level client. */
+
+ snd_pcm_hw_params_t *hw_params;
+ snd_pcm_sw_params_t *sw_params;
+
+ snd_pcm_hw_params_alloca( &hw_params );
+
+ /* ... fill up the configuration space with all possibile
+ * combinations of parameters this device will accept */
+ ENSURE( snd_pcm_hw_params_any( stream, hw_params ) );
+
+ if( interleaved )
+ access_mode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ else
+ access_mode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+
+ ENSURE( snd_pcm_hw_params_set_access( stream, hw_params, access_mode ) );
+
+ /* set the format based on what the user selected */
+ switch( pa_format )
+ {
+ case paFloat32:
+ alsa_format = SND_PCM_FORMAT_FLOAT;
+ break;
+
+ case paInt16:
+ alsa_format = SND_PCM_FORMAT_S16;
+ break;
+
+ case paInt24:
+ alsa_format = SND_PCM_FORMAT_S24;
+ break;
+
+ case paInt32:
+ alsa_format = SND_PCM_FORMAT_S32;
+ break;
+
+ case paInt8:
+ alsa_format = SND_PCM_FORMAT_S8;
+ break;
+
+ case paUInt8:
+ alsa_format = SND_PCM_FORMAT_U8;
+ break;
+
+ default:
+ printf("Unknown PortAudio format %d\n", (int)pa_format );
+ return 1;
+ }
+ //printf("PortAudio format: %d\n", pa_format);
+ printf("ALSA format: %d\n", alsa_format);
+ ENSURE( snd_pcm_hw_params_set_format( stream, hw_params, alsa_format ) );
+
+ /* ... set the sample rate */
+ ENSURE( snd_pcm_hw_params_set_rate( stream, hw_params, rate, 0 ) );
+
+ /* ... set the number of channels */
+ ENSURE( snd_pcm_hw_params_set_channels( stream, hw_params, channels ) );
+
+ /* ... set the number of periods to 2, which is essentially double buffering.
+ * this makes the latency the number of samples per buffer, which is the best
+ * it can be */
+ ENSURE( snd_pcm_hw_params_set_periods ( stream, hw_params, 2, 0 ) );
+
+ /* ... set the period size, which is essentially the hardware buffer size */
+ if( framesPerBuffer != 0 )
+ {
+ ENSURE( snd_pcm_hw_params_set_period_size( stream, hw_params,
+ framesPerBuffer, 0 ) );
+ }
+ else
+ {
+ ENSURE( snd_pcm_hw_params_set_period_size( stream, hw_params,
+ 2048, 0 ) );
+ }
+
+
+ /* Set the parameters! */
+ ENSURE( snd_pcm_hw_params( stream, hw_params ) );
+
+ return 0;
+#undef ENSURE
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *callback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaAlsaHostApiRepresentation *skeletonHostApi =
+ (PaAlsaHostApiRepresentation*)hostApi;
+ PaAlsaStream *stream = 0;
+ PaSampleFormat hostInputSampleFormat=0, hostOutputSampleFormat=0;
+ int numInputChannels, numOutputChannels;
+ PaSampleFormat inputSampleFormat=0, outputSampleFormat=0;
+ unsigned long framesPerHostBuffer = framesPerBuffer;
+
+ if( framesPerHostBuffer == paFramesPerBufferUnspecified )
+ {
+ // TODO: have some reason
+ framesPerHostBuffer = 2048;
+ }
+
+ if( inputParameters )
+ {
+ numInputChannels = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification.
+ [JH] this could be supported in the future, to allow ALSA device strings
+ like hw:0 */
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support numInputChannels */
+ if( numInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ numInputChannels = 0;
+ }
+
+ if( outputParameters )
+ {
+ numOutputChannels = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification
+ [JH] this could be supported in the future, to allow ALSA device strings
+ like hw:0 */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support numInputChannels */
+ if( numOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ numOutputChannels = 0;
+ }
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+ /* allocate and do basic initialization of the stream structure */
+
+ stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) );
+ if( !stream )
+ {
+ printf("memory point 2\n");
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ stream->pcm_capture = NULL;
+ stream->pcm_playback = NULL;
+ stream->callback_mode = (callback != 0);
+ stream->callback_finished = 0;
+
+ if( callback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &skeletonHostApi->callbackStreamInterface,
+ callback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &skeletonHostApi->blockingStreamInterface,
+ callback, userData );
+ }
+
+
+ stream->streamRepresentation.streamInfo.inputLatency = framesPerHostBuffer;
+ stream->streamRepresentation.streamInfo.outputLatency = framesPerHostBuffer;
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ /* open the devices now, so we can obtain info about the available formats */
+
+ if( numInputChannels > 0 )
+ {
+ char inputDeviceName[50];
+
+ sprintf( inputDeviceName, "hw:CARD=%s", hostApi->deviceInfos[inputParameters->device]->name );
+ if( snd_pcm_open( &stream->pcm_capture, inputDeviceName, SND_PCM_STREAM_CAPTURE, 0 ) < 0 )
+ {
+ result = paBadIODeviceCombination;
+ goto error;
+ }
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( GetAvailableFormats(stream->pcm_capture),
+ inputSampleFormat );
+ }
+
+ if( numOutputChannels > 0 )
+ {
+ char outputDeviceName[50];
+
+ sprintf( outputDeviceName, "hw:CARD=%s", hostApi->deviceInfos[outputParameters->device]->name );
+ if( snd_pcm_open( &stream->pcm_playback, outputDeviceName, SND_PCM_STREAM_PLAYBACK, 0 ) < 0 )
+ {
+ result = paBadIODeviceCombination;
+ goto error;
+ }
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( GetAvailableFormats(stream->pcm_playback),
+ outputSampleFormat );
+ stream->playback_hostsampleformat = hostOutputSampleFormat;
+ }
+
+
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ numInputChannels, inputSampleFormat, hostInputSampleFormat,
+ numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer, framesPerHostBuffer,
+ paUtilFixedHostBufferSize, callback, userData );
+ if( result != paNoError )
+ goto error;
+
+ /* configure the streams */
+
+ if( numInputChannels > 0 )
+ {
+ int interleaved;
+ PaSampleFormat plain_format = hostInputSampleFormat & ~paNonInterleaved;
+
+ if( inputSampleFormat & paNonInterleaved )
+ interleaved = 0;
+ else
+ interleaved = 1;
+
+ if( ConfigureStream( stream->pcm_capture, numInputChannels, interleaved,
+ sampleRate, plain_format, framesPerHostBuffer ) != 0 )
+ {
+ result = paBadIODeviceCombination;
+ goto error;
+ }
+
+ stream->capture_interleaved = interleaved;
+ }
+
+ if( numOutputChannels > 0 )
+ {
+ int interleaved;
+ PaSampleFormat plain_format = hostOutputSampleFormat & ~paNonInterleaved;
+
+ if( outputSampleFormat & paNonInterleaved )
+ interleaved = 0;
+ else
+ interleaved = 1;
+
+ if( ConfigureStream( stream->pcm_playback, numOutputChannels, interleaved,
+ sampleRate, plain_format, framesPerHostBuffer ) != 0 )
+ {
+ result = paBadIODeviceCombination;
+ goto error;
+ }
+
+ stream->playback_interleaved = interleaved;
+ }
+
+ stream->capture_nfds = 0;
+ stream->playback_nfds = 0;
+
+ if( stream->pcm_capture )
+ stream->capture_nfds = snd_pcm_poll_descriptors_count( stream->pcm_capture );
+
+ if( stream->pcm_playback )
+ stream->playback_nfds = snd_pcm_poll_descriptors_count( stream->pcm_playback );
+
+ /* TODO: free this properly */
+ printf("trying to allocate %d bytes of memory\n", (stream->capture_nfds + stream->playback_nfds + 1) * sizeof(struct pollfd) );
+ stream->pfds = (struct pollfd*)PaUtil_AllocateMemory( (stream->capture_nfds +
+ stream->playback_nfds + 1) *
+ sizeof(struct pollfd) );
+ if( !stream->pfds )
+ {
+ printf("bad memory point 1\n");
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ stream->frames_per_period = framesPerHostBuffer;
+ stream->capture_channels = numInputChannels;
+ stream->playback_channels = numOutputChannels;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_close( stream->pcm_capture );
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_close( stream->pcm_playback );
+ }
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ /* TODO: support errorText */
+#define ENSURE(x) \
+ { \
+ int error_ret; \
+ error_ret = (x); \
+ if( error_ret != 0 ) { \
+ PaHostErrorInfo err; \
+ err.errorCode = error_ret; \
+ err.hostApiType = paALSA; \
+ printf("call at %d failed\n", __LINE__); \
+ return paUnanticipatedHostError; \
+ } \
+ else \
+ printf("call at line %d succeeded\n", __LINE__); \
+ }
+
+ if( stream->pcm_capture )
+ {
+ ENSURE( snd_pcm_prepare( stream->pcm_capture ) );
+ }
+
+ if( stream->pcm_playback )
+ {
+ const snd_pcm_channel_area_t *playback_areas, *area;
+ snd_pcm_uframes_t offset, frames;
+ int sample_size = Pa_GetSampleSize( stream->playback_hostsampleformat );
+ printf("Sample size: %d\n", sample_size );
+ ENSURE( snd_pcm_prepare( stream->pcm_playback ) );
+ frames = snd_pcm_avail_update( stream->pcm_playback );
+ printf("frames: %d\n", (int)frames );
+ printf("channels: %d\n", stream->playback_channels );
+
+ snd_pcm_mmap_begin( stream->pcm_playback, &playback_areas, &offset, &frames );
+
+ /* Insert silence */
+ if( stream->playback_interleaved )
+ {
+ void *playback_buffer;
+ area = &playback_areas[0];
+ playback_buffer = area->addr + (area->first + area->step * offset) / 8;
+ memset( playback_buffer, 0,
+ frames * stream->playback_channels * sample_size );
+ }
+ else
+ {
+ int i;
+ for( i = 0; i < stream->playback_channels; i++ )
+ {
+ void *channel_buffer;
+ area = &playback_areas[i];
+ channel_buffer = area->addr + (area->first + area->step * offset) / 8;
+ memset( channel_buffer, 0, frames * sample_size );
+ }
+ }
+
+ snd_pcm_mmap_commit( stream->pcm_playback, offset, frames );
+ }
+
+ if( stream->callback_mode )
+ {
+ ENSURE( pthread_create( &stream->callback_thread, NULL, &CallbackThread, stream ) );
+
+ /* we'll do the snd_pcm_start() in the callback thread */
+ }
+ else
+ {
+ if( stream->pcm_capture )
+ snd_pcm_start( stream->pcm_capture );
+ if( stream->pcm_playback )
+ snd_pcm_start( stream->pcm_playback );
+ }
+
+ /* On my machine, the pcm stream will not transition to the RUNNING
+ * state for a while after snd_pcm_start is called. The PortAudio
+ * client needs to be able to depend on Pa_IsStreamActive() returning
+ * true the second after this function returns. So I sleep briefly here.
+ *
+ * I don't like this one bit.
+ */
+ Pa_Sleep( 100 );
+
+ stream->callback_finished = 0;
+
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ /* First deal with the callback thread, cancelling and/or joining
+ * it if necessary
+ */
+
+ if( stream->callback_mode && stream->callback_finished )
+ {
+ /* We are running in callback mode but the callback thread has
+ * already been cancelled by the return value from the user's
+ * callback function. Therefore we don't need to cancel the
+ * thread, but we do want to wait for it. */
+ pthread_join( stream->callback_thread, NULL );
+ }
+ else if( stream->callback_mode )
+ {
+ /* We are running in callback mode, and the callback thread
+ * is still running. Cancel it and wait for it to be done. */
+ pthread_cancel( stream->callback_thread );
+ pthread_join( stream->callback_thread, NULL );
+ }
+
+ /* Stop the ALSA streams if necessary */
+
+ if( stream->callback_mode && stream->callback_finished )
+ {
+ /* If we are in the callback_finished state the callback thread
+ * already stopped the streams. So there is nothing to do here.
+ */
+ }
+ else
+ {
+ if( stream->pcm_capture )
+ {
+ snd_pcm_drain( stream->pcm_capture );
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_drain( stream->pcm_playback );
+ }
+ }
+
+ stream->callback_finished = 0;
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ /* First deal with the callback thread, cancelling and/or joining
+ * it if necessary
+ */
+
+ if( stream->callback_mode && stream->callback_finished )
+ {
+ /* We are running in callback mode but the callback thread has
+ * already been cancelled by the return value from the user's
+ * callback function. Therefore we don't need to cancel the
+ * thread, but we do want to wait for it. */
+ pthread_join( stream->callback_thread, NULL );
+ }
+ else if( stream->callback_mode )
+ {
+ /* We are running in callback mode, and the callback thread
+ * is still running. Cancel it and wait for it to be done. */
+ pthread_cancel( stream->callback_thread );
+ pthread_join( stream->callback_thread, NULL );
+ }
+
+ /* Stop the ALSA streams if necessary */
+
+ if( stream->callback_mode && stream->callback_finished )
+ {
+ /* If we are in the callback_finished state the callback thread
+ * already stopped the streams. So there is nothing to do here.
+ */
+ }
+ else
+ {
+ if( stream->pcm_capture )
+ {
+ snd_pcm_drop( stream->pcm_capture );
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_drop( stream->pcm_playback );
+ }
+ }
+
+ stream->callback_finished = 0;
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ if( IsStreamActive(s) || stream->callback_finished )
+ return 0;
+ else
+ return 1;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_state_t capture_state = snd_pcm_state( stream->pcm_capture );
+
+ if( capture_state == SND_PCM_STATE_RUNNING /*||
+ capture_state == SND_PCM_STATE_PREPARED*/ )
+ return 1;
+ }
+
+ if( stream->pcm_playback )
+ {
+ snd_pcm_state_t playback_state = snd_pcm_state( stream->pcm_playback );
+
+ if( playback_state == SND_PCM_STATE_RUNNING /*||
+ playback_state == SND_PCM_STATE_PREPARED*/ )
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ snd_output_t *output;
+ snd_timestamp_t timestamp;
+ snd_pcm_status_t *status;
+ snd_pcm_status_alloca( &status );
+
+ /* TODO: what if we have both? does it really matter? */
+
+ /* TODO: if running in callback mode, this will mean
+ * libasound routines are being called form multiple threads.
+ * need to verify that libasound is thread-safe. */
+
+ if( stream->pcm_capture )
+ {
+ snd_pcm_status( stream->pcm_capture, status );
+ }
+ else if( stream->pcm_playback )
+ {
+ snd_pcm_status( stream->pcm_playback, status );
+ }
+
+ snd_pcm_status_get_tstamp( status, &timestamp );
+
+ return timestamp.tv_sec + ((float)timestamp.tv_usec/1000000);
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h
new file mode 100644
index 00000000..62c9512c
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h
@@ -0,0 +1,45 @@
+
+#include <alsa/asoundlib.h>
+
+#include <pthread.h>
+
+#include "pa_util.h"
+#include "pa_process.h"
+#include "pa_cpuload.h"
+#include "pa_stream.h"
+
+typedef struct PaAlsaStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ snd_pcm_t *pcm_capture;
+ snd_pcm_t *pcm_playback;
+
+ int callback_finished; /* bool: are we in the "callback finished" state? */
+
+ int frames_per_period;
+ int playback_hostsampleformat;
+
+ int capture_channels;
+ int playback_channels;
+
+ int capture_interleaved; /* bool: is capture interleaved? */
+ int playback_interleaved; /* bool: is playback interleaved? */
+
+ int callback_mode; /* bool: are we running in callback mode? */
+ pthread_t callback_thread;
+
+ /* the callback thread uses these to poll the sound device, waiting
+ * for data to be ready/available */
+ unsigned int capture_nfds;
+ unsigned int playback_nfds;
+ struct pollfd *pfds;
+
+ /* these aren't really stream state, the callback uses them */
+ snd_pcm_uframes_t capture_offset;
+ snd_pcm_uframes_t playback_offset;
+}
+PaAlsaStream;
+
diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.o b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.o
new file mode 100644
index 00000000..f79af61d
--- /dev/null
+++ b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.o
Binary files differ