You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
9.0 KiB
316 lines
9.0 KiB
/*
|
|
Copyright (C) 2005 Remko Troncon
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <portaudio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "portaudiocard.h"
|
|
#include "msossread.h"
|
|
#include "msosswrite.h"
|
|
|
|
// Settings
|
|
#define BUFFER_SIZE 2048
|
|
|
|
// PortAudio settings
|
|
#define FRAMES_PER_BUFFER 256
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
int readBuffer(char* buffer, char** buffer_read_p, char*buffer_write, char* buffer_end, char* target_buffer, int target_len)
|
|
{
|
|
char *end, *tmp, *buffer_read = *buffer_read_p;
|
|
size_t remaining, len;
|
|
int read = 0;
|
|
|
|
// First phase
|
|
tmp = buffer_read + target_len;
|
|
if (buffer_write < buffer_read) {
|
|
if (tmp > buffer_end) {
|
|
end = buffer_end;
|
|
remaining = tmp - buffer_end;
|
|
}
|
|
else {
|
|
end = tmp;
|
|
remaining = 0;
|
|
}
|
|
}
|
|
else {
|
|
end = (tmp >= buffer_write ? buffer_write : tmp);
|
|
remaining = 0;
|
|
}
|
|
//printf("end: %p\n",end);
|
|
|
|
// Copy the data
|
|
len = end - buffer_read;
|
|
memcpy(target_buffer, buffer_read, len);
|
|
buffer_read += len;
|
|
target_buffer += len;
|
|
read += len;
|
|
|
|
// Second phase
|
|
if (remaining > 0) {
|
|
buffer_read = buffer;
|
|
tmp = buffer_read + remaining;
|
|
len = (tmp > buffer_write ? buffer_write : tmp) - buffer_read;
|
|
memcpy(target_buffer, buffer_read, len);
|
|
buffer_read += len;
|
|
read += len;
|
|
}
|
|
|
|
// Finish up
|
|
*buffer_read_p = buffer_read;
|
|
|
|
return read;
|
|
}
|
|
|
|
int writeBuffer(char* buffer, char* buffer_read, char** buffer_write_p, char* buffer_end, char* source_buffer, int source_len)
|
|
{
|
|
char *end, *tmp, *buffer_write = *buffer_write_p;
|
|
size_t remaining, len;
|
|
int written = 0;
|
|
|
|
// First phase
|
|
tmp = buffer_write + source_len;
|
|
if (buffer_write >= buffer_read) {
|
|
if (tmp > buffer_end) {
|
|
end = buffer_end;
|
|
remaining = tmp - buffer_end;
|
|
}
|
|
else {
|
|
end = tmp;
|
|
remaining = 0;
|
|
}
|
|
}
|
|
else {
|
|
if (tmp > buffer_read) {
|
|
printf("Warning: Dropping frame(s) %p %p\n", tmp, buffer_read);
|
|
end = buffer_read;
|
|
remaining = 0;
|
|
}
|
|
else {
|
|
end = tmp;
|
|
remaining = 0;
|
|
}
|
|
}
|
|
|
|
len = end - buffer_write;
|
|
memcpy(buffer_write, source_buffer, len);
|
|
buffer_write += len;
|
|
source_buffer += len;
|
|
written += len;
|
|
|
|
// Second phase
|
|
if (remaining > 0) {
|
|
buffer_write = buffer;
|
|
tmp = buffer_write + remaining;
|
|
if (tmp > buffer_read) {
|
|
printf("Warning: Dropping frame(s) %p %p\n", tmp, buffer_read);
|
|
end = buffer_read;
|
|
}
|
|
else {
|
|
end = tmp;
|
|
}
|
|
|
|
len = end - buffer_write;
|
|
memcpy(buffer_write, source_buffer, len);
|
|
buffer_write += len;
|
|
written += len;
|
|
}
|
|
|
|
// Finish up
|
|
*buffer_write_p = buffer_write;
|
|
return written;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *card_p )
|
|
{
|
|
PortAudioCard* card = (PortAudioCard*) card_p;
|
|
|
|
size_t len = framesPerBuffer * Pa_GetSampleSize(paInt16);
|
|
//printf("PA::readBuffer begin %p %p %p %p %d\n",card->out_buffer,card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, len);
|
|
readBuffer(card->out_buffer,&card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, outputBuffer, len);
|
|
//printf("PA::readBuffer end %p %p %p %p %d\n",card->out_buffer,card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, len);
|
|
writeBuffer(card->in_buffer,card->in_buffer_read,&card->in_buffer_write,card->in_buffer_end, inputBuffer, len);
|
|
return 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
int portaudio_card_probe(PortAudioCard *obj, int bits, int stereo, int rate)
|
|
{
|
|
return FRAMES_PER_BUFFER * (SND_CARD(obj)->stereo ? 2 : 1) * Pa_GetSampleSize(paInt16);
|
|
}
|
|
|
|
|
|
int portaudio_card_open_r(PortAudioCard *obj,int bits,int stereo,int rate)
|
|
{
|
|
fprintf(stderr,"Opening PortAudio card\n");
|
|
|
|
int err;
|
|
err = Pa_OpenDefaultStream(&obj->stream, 1, 1, paInt16, rate, FRAMES_PER_BUFFER, 0, portAudioCallback, obj);
|
|
if (err != paNoError) {
|
|
fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err));
|
|
return -1;
|
|
}
|
|
|
|
err = Pa_StartStream(obj->stream);
|
|
if (err != paNoError) {
|
|
fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err));
|
|
Pa_CloseStream(obj->stream);
|
|
obj->stream = NULL;
|
|
return -1;
|
|
}
|
|
|
|
SND_CARD(obj)->bits = 16;
|
|
SND_CARD(obj)->stereo = 0;
|
|
SND_CARD(obj)->rate = rate;
|
|
// Should this be multiplied by Pa_GetMinNumBuffers(FRAMES_PER_BUFFER,sampleRate) ?
|
|
SND_CARD(obj)->bsize = FRAMES_PER_BUFFER * (SND_CARD(obj)->stereo ? 2 : 1) * Pa_GetSampleSize(paInt16);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
void portaudio_card_close_r(PortAudioCard *obj)
|
|
{
|
|
fprintf(stderr, "Closing PortAudio card\n");
|
|
if (obj->stream) {
|
|
Pa_StopStream(obj->stream);
|
|
Pa_CloseStream(obj->stream);
|
|
obj->stream = NULL;
|
|
}
|
|
}
|
|
|
|
int portaudio_card_open_w(PortAudioCard *obj,int bits,int stereo,int rate)
|
|
{
|
|
}
|
|
|
|
void portaudio_card_close_w(PortAudioCard *obj)
|
|
{
|
|
}
|
|
|
|
void portaudio_card_destroy(PortAudioCard *obj)
|
|
{
|
|
snd_card_uninit(SND_CARD(obj));
|
|
free(obj->in_buffer);
|
|
free(obj->out_buffer);
|
|
}
|
|
|
|
gboolean portaudio_card_can_read(PortAudioCard *obj)
|
|
{
|
|
return obj->in_buffer_read != obj->in_buffer_write;
|
|
}
|
|
|
|
int portaudio_card_read(PortAudioCard *obj,char *buf,int size)
|
|
{
|
|
//printf("read begin %p %p %p %p %d\n",obj->in_buffer,obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, size);
|
|
return readBuffer(obj->in_buffer,&obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, buf, size);
|
|
//printf("read end %p %p %p %p %d\n",obj->in_buffer,obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, size);
|
|
}
|
|
|
|
int portaudio_card_write(PortAudioCard *obj,char *buf,int size)
|
|
{
|
|
//printf("writeBuffer begin %p %p %p %p %d\n",obj->out_buffer,obj->out_buffer_read,obj->out_buffer_write,obj->out_buffer_end, size);
|
|
return writeBuffer(obj->out_buffer,obj->out_buffer_read,&obj->out_buffer_write,obj->out_buffer_end, buf, size);
|
|
//printf("writeBuffer end %p %p %p %p %d\n",obj->out_buffer,obj->out_buffer_read,obj->out_buffer_write,obj->out_buffer_end, size);
|
|
}
|
|
|
|
void portaudio_card_set_level(PortAudioCard *obj,gint way,gint a)
|
|
{
|
|
}
|
|
|
|
gint portaudio_card_get_level(PortAudioCard *obj,gint way)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void portaudio_card_set_source(PortAudioCard *obj,int source)
|
|
{
|
|
}
|
|
|
|
MSFilter *portaudio_card_create_read_filter(PortAudioCard *card)
|
|
{
|
|
MSFilter *f=ms_oss_read_new();
|
|
ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index);
|
|
return f;
|
|
}
|
|
|
|
MSFilter *portaudio_card_create_write_filter(PortAudioCard *card)
|
|
{
|
|
MSFilter *f=ms_oss_write_new();
|
|
ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index);
|
|
return f;
|
|
}
|
|
|
|
|
|
SndCard* portaudio_card_new()
|
|
{
|
|
// Basic stuff
|
|
PortAudioCard* obj= g_new0(PortAudioCard,1);
|
|
SndCard* base= SND_CARD(obj);
|
|
snd_card_init(base);
|
|
base->card_name=g_strdup_printf("PortAudio Card");
|
|
base->_probe=(SndCardOpenFunc)portaudio_card_probe;
|
|
base->_open_r=(SndCardOpenFunc)portaudio_card_open_r;
|
|
base->_open_w=(SndCardOpenFunc)portaudio_card_open_w;
|
|
base->_can_read=(SndCardPollFunc)portaudio_card_can_read;
|
|
base->_read=(SndCardIOFunc)portaudio_card_read;
|
|
base->_write=(SndCardIOFunc)portaudio_card_write;
|
|
base->_close_r=(SndCardCloseFunc)portaudio_card_close_r;
|
|
base->_close_w=(SndCardCloseFunc)portaudio_card_close_w;
|
|
base->_set_rec_source=(SndCardMixerSetRecSourceFunc)portaudio_card_set_source;
|
|
base->_set_level=(SndCardMixerSetLevelFunc)portaudio_card_set_level;
|
|
base->_get_level=(SndCardMixerGetLevelFunc)portaudio_card_get_level;
|
|
base->_destroy=(SndCardDestroyFunc)portaudio_card_destroy;
|
|
base->_create_read_filter=(SndCardCreateFilterFunc)portaudio_card_create_read_filter;
|
|
base->_create_write_filter=(SndCardCreateFilterFunc)portaudio_card_create_write_filter;
|
|
|
|
// Initialize stream
|
|
obj->stream = NULL;
|
|
|
|
// Initialize buffers
|
|
obj->out_buffer = (char*) malloc(sizeof(char)*BUFFER_SIZE);
|
|
obj->out_buffer_read = obj->out_buffer_write = obj->out_buffer;
|
|
obj->out_buffer_end = obj->out_buffer + BUFFER_SIZE;
|
|
obj->in_buffer = (char*) malloc(sizeof(char)*BUFFER_SIZE);
|
|
obj->in_buffer_read = obj->in_buffer_write = obj->in_buffer;
|
|
obj->in_buffer_end = obj->in_buffer + BUFFER_SIZE;
|
|
|
|
return base;
|
|
}
|
|
|
|
gint portaudio_card_manager_init(SndCardManager *manager, gint tabindex)
|
|
{
|
|
// Initialize portaudio lib
|
|
int err = Pa_Initialize();
|
|
if (err != paNoError) {
|
|
fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err));
|
|
return 0;
|
|
}
|
|
|
|
// Create new card
|
|
manager->cards[0]=portaudio_card_new();
|
|
manager->cards[0]->index=0;
|
|
|
|
return 1;
|
|
}
|