This commit is contained in:
Andrew Gundersen 2021-08-13 14:55:14 -05:00
commit d32989cc81
135 changed files with 17183 additions and 669 deletions

BIN
src/.DS_Store vendored

Binary file not shown.

BIN
src/a.out

Binary file not shown.

View file

@ -1,259 +0,0 @@
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <portaudio.h>
#include "audio_handler.h"
#include "frame_buffer.h"
bool sys_on;
bool recording = false;
int write_to_o_buff = 0;
bool initialized = false;
frame_buffer inputFrameBuffer;
frame_buffer outputFrameBuffer;
typedef short SAMPLE;
static int min_int (int a, int b) {
return (a < b) ? a : b;
}
#define SAMPLE_RATE (16000)
#define BUFFER_mSECS (30)
#define FRAMES_PER_BUFFER (160 * (BUFFER_mSECS/10))
/* input -> inputFrameBuffer */
static int input_callback( const void* input,
void* output,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* frameBuffer )
{
(void) output;
char *i_buf = (char *) input;
frame_buffer* fb = (frame_buffer*) frameBuffer;
if (!recording) {
rewrite_frames_fb( fb,
i_buf,
framesPerBuffer*sizeof(SAMPLE) );
} else {
rewrite_frames_fb( fb,
i_buf,
framesPerBuffer*sizeof(SAMPLE) );
}
return paContinue;
}
/* outputFrameBuffer -> output */
static int output_callback( const void *input,
void* output,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* frameBuffer )
{
(void) input;
char* o_buf = (char *) output;
frame_buffer* fb = (frame_buffer*) frameBuffer;
if (write_to_o_buff == 0) {
write_to_o_buff = 2;
if (fb->frames_used > 0) {
int frames_to_read = min_int( framesPerBuffer,
fb->frames_used );
/* read each frame in outputFrameBuffer and write to output */
for (int i = 0; i < frames_to_read; i++) {
*((SAMPLE *)o_buf) = *((SAMPLE *) read_frame_at_cursor_fb(fb));
o_buf += sizeof(SAMPLE);
}
} else {
*((SAMPLE *)o_buf) = 0;
}
write_to_o_buff = 0;
}
return paContinue;
}
static void setParameters( PaDeviceIndex device,
PaStreamParameters* parameters )
{
parameters->device = Pa_GetDefaultInputDevice();
parameters->channelCount = 1;
parameters->hostApiSpecificStreamInfo = NULL;
parameters->suggestedLatency = Pa_GetDeviceInfo(device)->defaultHighInputLatency;
}
static PaError set_stream( int type, // 0 = input, 1 = output
PaStream* stream,
PaDeviceIndex device,
PaStreamCallback* callback,
void* frameBuffer )
{
PaError err;
PaStreamParameters streamParameters;
setParameters(device, &streamParameters);
if (type == 0) {
err = Pa_OpenStream( &stream,
NULL,
&streamParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
callback,
frameBuffer );
} else {
err = Pa_OpenStream( &stream,
&streamParameters,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
callback,
frameBuffer );
}
if (err != paNoError) return err;
err = Pa_StartStream(stream);
return err;
}
void AH_initialize()
{
PaError err = paNoError;
PaDeviceIndex inputDevice;
PaDeviceIndex outputDevice;
PaDeviceIndex defaultOutputDevice;
PaDeviceIndex defaultInputDevice;
PaStream* inputStream = NULL;
PaStream* outputStream = NULL;
init_fb(&inputFrameBuffer);
init_fb(&outputFrameBuffer);
sys_on = true;
while (sys_on) {
Pa_Initialize();
defaultInputDevice = Pa_GetDefaultInputDevice();
defaultOutputDevice = Pa_GetDefaultOutputDevice();
printf("Input: %i, Output: %i\n", defaultInputDevice, defaultOutputDevice);
if (inputDevice != defaultInputDevice) {
inputDevice = defaultInputDevice;
err = set_stream( 0,
inputStream,
inputDevice,
&input_callback,
&inputFrameBuffer );
}
if (outputDevice != defaultOutputDevice) {
outputDevice = defaultOutputDevice;
err = set_stream( 1,
outputStream,
outputDevice,
&output_callback,
&outputFrameBuffer );
}
sleep(1);
}
Pa_AbortStream(inputStream);
Pa_AbortStream(outputStream);
delete_fb(&inputFrameBuffer);
delete_fb(&outputFrameBuffer);
Pa_Terminate();
initialized = false;
}
void AH_terminate()
{
printf("Terminating PA\n");
sys_on = false;
while(initialized) {
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 5000;
nanosleep(&t, &t);
}
}
void AH_fillInputBuffer()
{
recording = true;
}
void AH_resetInputBuffer(void* data, size_t* byte_length)
{
size_t len = sizeof(inputFrameBuffer.data);
char audioData[len];
data = audioData; // points to 0th el of audioData
byte_length = &len;
for(int i = 0; i < (int) len; i++) {
audioData[i] = inputFrameBuffer.data[i];
}
recording = false;
clear_fb(&inputFrameBuffer);
}
void AH_feedOutputBuffer(void* data, size_t* byte_length)
{
// can't intert into buffer while it's being read from
while (write_to_o_buff == 2) {
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 5000;
nanosleep(&t, &t);
}
write_to_o_buff = 1;
insert_frames_fb(&outputFrameBuffer, data, (int) &byte_length);
write_to_o_buff = 0;
}

View file

@ -1,16 +0,0 @@
#ifndef AUDIO_HANDLER
#define AUDIO_HANDLER
void AH_initialize ();
void AH_fillInputBuffer ();
void AH_resetInputBuffer(void* data, size_t* byte_length);
void AH_feedOutputBuffer(void* data, size_t* byte_length);
void AH_terminate ();
#endif

Binary file not shown.

View file

@ -1,120 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "frame_buffer.h"
#define BYTES_PER_FRAME (2)
#define INITIAL_FRAME_COUNT (480*100)
#define ALPHA (2)
void init_fb ( frame_buffer *fb ) {
fb->frames_used = 0;
fb->frames_free = INITIAL_FRAME_COUNT;
fb->data = (char *) malloc(BYTES_PER_FRAME*INITIAL_FRAME_COUNT);
fb->cursor = fb->data;
}
void delete_fb ( frame_buffer *fb ) {
fb->frames_used = 0;
fb->frames_free = 0;
fb->cursor = NULL;
free(fb->data);
}
void clear_fb ( frame_buffer *fb ) {
free(fb->data);
fb->data = (char *) malloc(BYTES_PER_FRAME*INITIAL_FRAME_COUNT);
fb->cursor = fb->data;
fb->frames_free = INITIAL_FRAME_COUNT;
fb->frames_used = 0;
}
void insert_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ) {
int frames = frames_len / BYTES_PER_FRAME;
int cursor_diff = fb->cursor - fb->data;
int data_len = (fb->frames_free+fb->frames_used)*BYTES_PER_FRAME;
if (frames > fb->frames_free) {
fb->data = realloc(fb->data, (data_len+frames_len)*ALPHA);
fb->cursor = fb->data+cursor_diff;
if (fb->frames_used*BYTES_PER_FRAME > data_len-cursor_diff) {
memmove(fb->data+data_len, fb->data, (fb->frames_used*BYTES_PER_FRAME)-(data_len-cursor_diff));
}
fb->frames_used += frames;
fb->frames_free = fb->frames_used;
}
data_len = (fb->frames_free+fb->frames_used)*BYTES_PER_FRAME;
if (fb->frames_used*BYTES_PER_FRAME > data_len-cursor_diff) {
memcpy(fb->data+((fb->frames_used*BYTES_PER_FRAME)-(data_len-cursor_diff)), frame_data, frames_len);
fb->frames_free -= frames;
fb->frames_used += frames;
return;
}
if (frames_len+(fb->frames_used*BYTES_PER_FRAME) > data_len-cursor_diff) {
int non_overlap = data_len - (cursor_diff+(fb->frames_used*BYTES_PER_FRAME));
int overlap = frames_len - non_overlap;
memcpy(fb->cursor+(fb->frames_used*BYTES_PER_FRAME), frame_data, non_overlap);
memcpy(fb->data, &(frame_data[non_overlap]), overlap);
fb->frames_free -= frames;
fb->frames_used += frames;
return;
}
memcpy(fb->cursor+(fb->frames_used*BYTES_PER_FRAME), frame_data, frames_len);
fb->frames_free -= frames;
fb->frames_used += frames;
}
void rewrite_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ) {
int frames = frames_len / BYTES_PER_FRAME;
if (frames > (fb->frames_used + fb->frames_free)) {
fb->data = realloc(fb->data, frames_len*ALPHA);
fb->frames_free = frames;
fb->frames_used = frames;
} else {
fb->frames_free = fb->frames_free + fb->frames_used - frames;
fb->frames_used = frames;
}
memcpy(fb->data, frame_data, frames_len);
fb->cursor = fb->data;
}
char* read_frame_at_cursor_fb ( frame_buffer *fb ) {
char *begin = fb->cursor;
int frames_free = fb->frames_free;
int frames_used = fb->frames_used;
char *data = fb->data;
if (frames_used == 0) {
return NULL;
}
fb->cursor += BYTES_PER_FRAME;
if (fb->cursor >= data + ((frames_free+frames_used)*BYTES_PER_FRAME)) {
fb->cursor = data;
}
fb->frames_free += 1;
fb->frames_used -= 1;
return begin;
}

View file

@ -1,24 +0,0 @@
#ifndef FRAME_BUFFER
#define FRAME_BUFFER
typedef struct {
int frames_free, frames_used;
char *data, *cursor;
} frame_buffer;
void init_fb ( frame_buffer *fb );
void delete_fb ( frame_buffer *fb );
void clear_fb ( frame_buffer *fb );
void insert_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len );
void rewrite_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len );
char* read_frame_at_cursor_fb ( frame_buffer *fb );
#endif

View file

@ -1,26 +0,0 @@
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =-L/usr/local/lib -L/usr/lib
LIBS=-lportaudio
_DEPS = audio_handler.h frame_buffer.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = audio_handler.o frame_buffer.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
nodeaudio: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LDIR) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~

25
src/na_audio_buffer.c Normal file
View file

@ -0,0 +1,25 @@
typedef struct
{
int frames_free, frames_used;
char *data, *cursor;
}
AudioBuffer;
AbError Ab_Init(AudioBuffer* ab, int size)
{
ab->lock = 0;
ab->bytes_free = size;
ab->data = (void *) malloc(size);
}
AbError Ab_Read(void* data, AudioBuffer* ab)
{
}
AbError Ab_Write(void* data, AudioBuffer* ab)
{
memcpy(ab->cursor, data, sizeof(data));
}

14
src/na_buffer.c Normal file
View file

@ -0,0 +1,14 @@
#include <stdlib.h>
#include "pa_ringbuffer.h"
void init_buffer(PaUtilRingBuffer* buffer, void** store)
{
*store = (void*) malloc(2 * 8192);
PaUtil_InitializeRingBuffer(buffer, 2, 8192, *store);
}
void free_buffer(PaUtilRingBuffer* buffer)
{
free(buffer->buffer);
}

8
src/na_buffer.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef NA_BUFFER
#define NA_BUFFER
void init_buffer(PaUtilRingBuffer* buffer, void** store);
void free_buffer(PaUtilRingBuffer* buffer);
#endif

159
src/na_callbacks.c Normal file
View file

@ -0,0 +1,159 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <node_api.h>
#include <portaudio.h>
#include "na_utils.h"
#include "na_callbacks.h"
#include "pa_ringbuffer.h"
/* JS callbacks */
napi_threadsafe_function js_on_data;
/* Calls js_emit_ts from the main thread and translates data to node */
static void call_js_on_data( napi_env env,
napi_value on_data,
void* context, /* the inputBuffer */
void* data )
{
(void) data;
napi_status status;
napi_value channel;
status = napi_create_string_utf8(env, "data", NAPI_AUTO_LENGTH, &channel);
PaUtilRingBuffer* rb = (PaUtilRingBuffer*) context;
ring_buffer_size_t framesToRead;
framesToRead = PaUtil_GetRingBufferReadAvailable(rb);
void* frames; /* read the data into here */
frames = malloc(framesToRead * 2);
PaUtil_ReadRingBuffer(rb, frames, framesToRead);
napi_value arrayBuffer;
napi_create_external_arraybuffer( env,
frames,
framesToRead * 2,
NULL,
NULL,
&arrayBuffer );
napi_value typedArray;
status = napi_create_typedarray( env,
napi_int16_array,
framesToRead,
arrayBuffer,
0,
&typedArray );
napi_value undefined;
status = napi_get_undefined(env, &undefined);
size_t argc = 2;
napi_value args[2] = { channel, typedArray };
status = napi_call_function(env, undefined, on_data, argc, &args[0], NULL);
}
/* Takes in a Node Event Emitter and creates JS threadsafe functions to be
* called from the PortAudio callbacks.
*/
void init_callbacks( napi_env env,
napi_value* js_emit,
PaUtilRingBuffer* inputBuffer )
{
napi_status status;
napi_value resource_name;
status = napi_create_string_utf8( env,
"Thread Safe Event Emitter",
NAPI_AUTO_LENGTH,
&resource_name );
/* Create thread-safe callback js_cb_safe */
status = napi_create_threadsafe_function( env,
*js_emit,
NULL,
resource_name,
0,
1,
NULL,
NULL,
inputBuffer, /* context */
call_js_on_data,
&js_on_data );
/* Optional: create another cb with js_emit here */
}
int input_callback( const void* input,
void* output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* data )
{
(void) output;
PaUtilRingBuffer* rb = (PaUtilRingBuffer*) data;
ring_buffer_size_t writableSpace;
writableSpace = PaUtil_GetRingBufferWriteAvailable(rb);
ring_buffer_size_t framesToWrite;
framesToWrite = rbs_min(writableSpace, frameCount);
PaUtil_WriteRingBuffer(rb, input, framesToWrite);
napi_status status;
status = napi_call_threadsafe_function( js_on_data,
NULL,
napi_tsfn_nonblocking );
return paContinue;
}
int output_callback( const void* input,
void* output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* data )
{
(void) input;
/* Reset output data first */
memset(output, 0, frameCount * 2);
PaUtilRingBuffer* rb = (PaUtilRingBuffer*) data;
ring_buffer_size_t framesInQueue;
framesInQueue = PaUtil_GetRingBufferReadAvailable(rb);
ring_buffer_size_t framesToRead;
framesToRead = rbs_min(framesInQueue, (ring_buffer_size_t) frameCount);
if (framesToRead != 0)
{
PaUtil_ReadRingBuffer(rb, output, framesToRead);
printf("Wrote %i frames\n", framesToRead);
}
return paContinue;
}
// short* sample = (short*) output;
// for (int i = 0; i < framesToRead * 2; ++i)
// {
// printf("%hu", sample[i]);
// }

29
src/na_callbacks.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef NA_CALLBACKS
#define NA_CALLBACKS
typedef struct
{
size_t len; /* in frames */
const void* data;
}
audio_info;
void init_callbacks( napi_env env,
napi_value* js_emit,
PaUtilRingBuffer* inputBuffer );
int input_callback( const void* input,
void* output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* data );
int output_callback( const void* input,
void* output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* data );
#endif

207
src/na_core.c Normal file
View file

@ -0,0 +1,207 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <node_api.h>
#include <portaudio.h>
#include "na_core.h"
#include "na_utils.h"
#include "na_buffer.h"
#include "na_callbacks.h"
#include "pa_ringbuffer.h"
PaStream* inputStream;
PaStream* outputStream;
PaUtilRingBuffer inputBuffer;
PaUtilRingBuffer outputBuffer;
void* inputBufferStore;
void* outputBufferStore;
/* Sample format is hard-coded paInt16 (unsigned short, 2 bytes) */
PaError Na_Initialize(napi_env env, napi_value* js_cb)
{
init_callbacks(env, js_cb, &inputBuffer);
PaError err;
err = Pa_Initialize();
return err;
}
PaError Na_GetDefaultInputDevice(PaDeviceIndex* deviceIndex)
{
PaError err;
err = Pa_RefreshDevices();
if (err != paNoError) return err;
*deviceIndex = Pa_GetDefaultInputDevice();
return err;
}
PaError Na_GetDefaultOutputDevice(PaDeviceIndex* deviceIndex)
{
PaError err;
err = Pa_RefreshDevices();
if (err != paNoError) return err;
*deviceIndex = Pa_GetDefaultOutputDevice();
return err;
}
PaError Na_OpenInputStream(int deviceIndex)
{
PaError err;
if ((err = Pa_IsStreamActive(inputStream) == 1))
{
err = paDeviceUnavailable;
}
if (err != paNoError) return err;
init_buffer(&inputBuffer, &inputBufferStore);
double sampleRate;
PaStreamParameters parameters;
sampleRate = setParameters(0, (PaDeviceIndex) deviceIndex, &parameters);
err = Pa_OpenStream( &inputStream,
&parameters,
NULL,
sampleRate,
paFramesPerBufferUnspecified,
paClipOff,
input_callback,
&inputBuffer );
if (err != paNoError) return err;
printf("NA_CORE:Na_OpenInputStream:starting stream...\n");
err = Pa_StartStream(inputStream);
return err;
}
PaError Na_OpenOutputStream(int deviceIndex)
{
PaError err;
if ((err = Pa_IsStreamActive(outputStream) == 1))
{
err = paDeviceUnavailable;
}
if (err != paNoError) return err;
init_buffer(&outputBuffer, &outputBufferStore);
double sampleRate;
PaStreamParameters parameters;
sampleRate = setParameters(1, (PaDeviceIndex) deviceIndex, &parameters);
err = Pa_OpenStream( &outputStream,
NULL,
&parameters,
sampleRate,
paFramesPerBufferUnspecified,
paClipOff,
output_callback,
&outputBuffer ); /* pass in outputBuffer here */
if (err != paNoError) return err;
printf("NA_CORE:Na_OpenOutputStream:starting stream...\n");
err = Pa_StartStream(outputStream);
return err;
}
static void WriteLargeDataToOutputBuffer(void* arg)
{
playback_data* pd = (playback_data*) arg;
ring_buffer_size_t writableSpace;
ring_buffer_size_t framesToWrite;
ring_buffer_size_t framesWritten;
// printing data works here...
while (pd->len > 0)
{
writableSpace = PaUtil_GetRingBufferWriteAvailable(&outputBuffer);
if (writableSpace != 0)
{
framesToWrite = rbs_min( writableSpace,
(ring_buffer_size_t) pd->len );
framesWritten = PaUtil_WriteRingBuffer( &outputBuffer,
pd->data,
framesToWrite );
pd->len -= framesWritten;
pd->data += framesWritten * 2; /* convert to bytes */
}
}
free(arg);
}
void Na_WriteToOutputBuffer(void* data, size_t size)
{
ring_buffer_size_t writableSpace;
writableSpace = PaUtil_GetRingBufferWriteAvailable(&outputBuffer);
// printing data works here...
/* If there is not enough space to write all the data at once, we must
* start a new thread in order to prevent blocking.
*/
size_t len = size / 2;
if ((size_t) writableSpace < len)
{
playback_data* pd = (playback_data*) malloc(sizeof(playback_data));
pd->data = data;
pd->len = len;
pthread_t w_th;
pthread_create(&w_th, NULL, (void*) WriteLargeDataToOutputBuffer, pd);
}
else
{
PaUtil_WriteRingBuffer(&outputBuffer, data, writableSpace);
}
}
PaError Na_Terminate()
{
printf("NACORE:Na_Terminate\n");
PaError err;
if ((err = Pa_IsStreamActive(inputStream) == 1))
{
err = Pa_AbortStream(inputStream);
}
if ((err = Pa_IsStreamActive(outputStream) == 1))
{
err = Pa_AbortStream(outputStream);
}
err = Pa_Terminate();
free_buffer(&inputBuffer);
free_buffer(&outputBuffer);
return err;
}
// /* Data being passed into the thread must be allocated to heap so that
// * it isn't deleted when this function returns.
// */
// playback_data* pd = (playback_data*) malloc(sizeof(playback_data));
// pd->data = (void*) malloc(len * 2);
// memcpy(pd->data, data, 2);
// pd->len = len;
// short* sample = (short*) pd->data;
// int count = (int) pd->len;
// for (int i = 0; i < count; i++)
// {
// printf("%hu", sample[i]);
// }

25
src/na_core.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef NA_CORE
#define NA_CORE
typedef struct
{
size_t len;
void* data;
}
playback_data;
PaError Na_Initialize(napi_env env, napi_value* js_cb);
PaError Na_GetDefaultInputDevice(PaDeviceIndex* deviceIndex);
PaError Na_GetDefaultOutputDevice(PaDeviceIndex* deviceIndex);
PaError Na_OpenInputStream(int deviceIndex);
PaError Na_OpenOutputStream(int deviceIndex);
void Na_WriteToOutputBuffer(void* data, size_t size);
PaError Na_Terminate();
#endif

173
src/na_front.c Normal file
View file

@ -0,0 +1,173 @@
#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <node_api.h>
#include <portaudio.h>
#include "na_core.h"
#define DECLARE_NAPI_METHOD(name, func) { name, 0, func, 0, 0, 0, napi_default, 0 }
napi_value Initialize(napi_env env, napi_callback_info info)
{
printf("NAPI::Initialize\n");
size_t argc = 1;
napi_value args[1];
napi_status status;
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
PaError err;
napi_value js_cb = args[0];
err = Na_Initialize(env, &js_cb);
if (err != paNoError)
{
napi_throw_error(env, NULL, Pa_GetErrorText(err));
}
return NULL;
}
napi_value GetDefaultInputDevice(napi_env env, napi_callback_info info)
{
printf("NAPI::GetDefaultInputDevice\n");
(void) info;
int deviceIndex;
PaError err;
err = Na_GetDefaultInputDevice(&deviceIndex);
if (err != paNoError)
{
napi_throw_error(env, NULL, Pa_GetErrorText(err));
}
napi_status status;
napi_value js_deviceIndex;
status = napi_create_int32(env, deviceIndex, &js_deviceIndex);
return js_deviceIndex;
}
napi_value GetDefaultOutputDevice(napi_env env, napi_callback_info info)
{
printf("NAPI::GetDefaultOutputDevice\n");
(void) info;
int deviceIndex;
PaError err;
err = Na_GetDefaultOutputDevice(&deviceIndex);
if (err != paNoError)
{
napi_throw_error(env, NULL, Pa_GetErrorText(err));
}
napi_status status;
napi_value js_deviceIndex;
status = napi_create_int32(env, deviceIndex, &js_deviceIndex);
return js_deviceIndex;
}
napi_value OpenInputStream(napi_env env, napi_callback_info info)
{
printf("NAPI::OpenInputStream\n");
size_t argc = 1; /* parse args */
napi_value args[1];
napi_status status;
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
int deviceIndex;
status = napi_get_value_int32( env,
args[0],
&deviceIndex );
PaError err = paNoError;
err = Na_OpenInputStream(deviceIndex);
if (err != paNoError)
{
napi_throw_error(env, NULL, Pa_GetErrorText(err));
}
return NULL;
}
napi_value OpenOutputStream(napi_env env, napi_callback_info info)
{
printf("NAPI::OpenOutputStream\n");
size_t argc = 1; /* parse args */
napi_value args[1];
napi_status status;
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
int deviceIndex;
status = napi_get_value_int32( env,
args[0],
&deviceIndex );
PaError err = paNoError;
err = Na_OpenOutputStream(deviceIndex);
if (err != paNoError)
{
napi_throw_error(env, NULL, Pa_GetErrorText(err));
}
return NULL;
}
napi_value WriteToOutputStream(napi_env env, napi_callback_info info)
{
printf("NAPI::WriteToOutputStream\n");
size_t argc = 1; /* parse args */
napi_value args[1];
napi_status status;
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
size_t size;
void* data;
status = napi_get_arraybuffer_info(env, args[0], &data, &size);
Na_WriteToOutputBuffer(data, size);
return NULL;
}
napi_value Terminate(napi_env env, napi_callback_info info)
{
printf("NAPI::Terminate\n");
(void) info;
PaError err = paNoError;
err = Na_Terminate();
if (err != paNoError)
{
napi_throw_error(env, NULL, Pa_GetErrorText(err));
}
return NULL;
}
napi_value Init(napi_env env, napi_value exports)
{
napi_status status;
napi_property_descriptor desc[] = {
DECLARE_NAPI_METHOD("Initialize", Initialize),
DECLARE_NAPI_METHOD("Terminate", Terminate),
DECLARE_NAPI_METHOD("GetDefaultInputDevice", GetDefaultInputDevice),
DECLARE_NAPI_METHOD("GetDefaultOutputDevice", GetDefaultOutputDevice),
DECLARE_NAPI_METHOD("OpenInputStream", OpenInputStream),
DECLARE_NAPI_METHOD("OpenOutputStream", OpenOutputStream),
DECLARE_NAPI_METHOD("WriteToOutputStream", WriteToOutputStream),
};
status = napi_define_properties(env, exports, 7, desc);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

41
src/na_utils.c Normal file
View file

@ -0,0 +1,41 @@
#include <stdio.h>
#include <portaudio.h>
#include "na_utils.h"
double setParameters( int type,
PaDeviceIndex deviceIndex,
PaStreamParameters* parameters )
{
const PaDeviceInfo* deviceInfo;
printf("Setting parameters for new stream:\n");
deviceInfo = Pa_GetDeviceInfo( deviceIndex );
parameters->device = deviceIndex;
if ( type == 0 )
{
printf(" Type: input\n");
parameters->channelCount = 1;
printf(" Channels: %i\n", parameters->channelCount);
}
else
{
printf(" Type: output\n");
parameters->channelCount = 1;
printf(" Channels: %i\n", parameters->channelCount);
}
parameters->sampleFormat = paInt16;
parameters->hostApiSpecificStreamInfo = NULL;
parameters->suggestedLatency = deviceInfo->defaultHighInputLatency;
printf(" Device: %s\n", deviceInfo->name);
printf(" SampleRate: %i\n", (int) deviceInfo->defaultSampleRate);
return deviceInfo->defaultSampleRate;
}
ring_buffer_size_t rbs_min(ring_buffer_size_t a, ring_buffer_size_t b)
{
return (a < b) ? a : b;
}

12
src/na_utils.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef NA_UTILS
#define NA_UTILS
#include "pa_ringbuffer.h"
double setParameters( int type,
PaDeviceIndex deviceIndex,
PaStreamParameters* parameters );
ring_buffer_size_t rbs_min(ring_buffer_size_t a, ring_buffer_size_t b);
#endif

Binary file not shown.

View file

@ -1,90 +0,0 @@
#include <assert.h>
#include <node_api.h>
#include <stdio.h>
#include "audio_handler.h"
#define DECLARE_NAPI_METHOD(name, func) { name, 0, func, 0, 0, 0, napi_default, 0 }
napi_value Initialize(napi_env env, napi_callback_info info)
{
(void) info;
AH_initialize();
return NULL;
}
napi_value FillInputBuffer(napi_env env, napi_callback_info info)
{
(void) info;
AH_fillInputBuffer();
return NULL;
}
napi_value ReturnInputBuffer(napi_env env, napi_callback_info info)
{
(void) info;
napi_status status;
void* data;
size_t byte_length;
AH_resetInputBuffer(&data, &byte_length);
napi_value buffer;
status = napi_create_arraybuffer( env,
byte_length,
&data,
&buffer );
assert(status == napi_ok);
return buffer;
}
static napi_value FeedOutputBuffer(napi_env env, napi_callback_info info)
{
napi_status status;
void* data;
size_t byte_length;
napi_value arraybuffer;
size_t argc = 1; /* parse args */
napi_value args[1];
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
assert(status == napi_ok);
arraybuffer = args[0];
status = napi_get_arraybuffer_info( env,
arraybuffer,
&data,
&byte_length );
assert(status == napi_ok);
AH_feedOutputBuffer(data, &byte_length);
return NULL;
}
napi_value Init(napi_env env, napi_value exports)
{
napi_status status;
napi_property_descriptor desc[] = {
DECLARE_NAPI_METHOD("fill_input_buffer", FillInputBuffer),
DECLARE_NAPI_METHOD("return_input_buffer", ReturnInputBuffer),
DECLARE_NAPI_METHOD("feed_output_buffer", FeedOutputBuffer)
};
status = napi_define_properties(env, exports, 1, desc);
AH_initialize();
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

128
src/pa_memorybarrier.h Normal file
View file

@ -0,0 +1,128 @@
/*
* $Id: pa_memorybarrier.h 1240 2007-07-17 13:05:07Z bjornroche $
* Portable Audio I/O Library
* Memory barrier utilities
*
* Author: Bjorn Roche, XO Audio, LLC
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/**
@file pa_memorybarrier.h
@ingroup common_src
*/
/****************
* Some memory barrier primitives based on the system.
* right now only OS X, FreeBSD, and Linux are supported. In addition to providing
* memory barriers, these functions should ensure that data cached in registers
* is written out to cache where it can be snooped by other CPUs. (ie, the volatile
* keyword should not be required)
*
* the primitives that must be defined are:
*
* PaUtil_FullMemoryBarrier()
* PaUtil_ReadMemoryBarrier()
* PaUtil_WriteMemoryBarrier()
*
****************/
#if defined(__APPLE__)
# include <libkern/OSAtomic.h>
/* Here are the memory barrier functions. Mac OS X only provides
full memory barriers, so the three types of barriers are the same,
however, these barriers are superior to compiler-based ones. */
# define PaUtil_FullMemoryBarrier() OSMemoryBarrier()
# define PaUtil_ReadMemoryBarrier() OSMemoryBarrier()
# define PaUtil_WriteMemoryBarrier() OSMemoryBarrier()
#elif defined(__GNUC__)
/* GCC >= 4.1 has built-in intrinsics. We'll use those */
# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
# define PaUtil_FullMemoryBarrier() __sync_synchronize()
# define PaUtil_ReadMemoryBarrier() __sync_synchronize()
# define PaUtil_WriteMemoryBarrier() __sync_synchronize()
/* as a fallback, GCC understands volatile asm and "memory" to mean it
* should not reorder memory read/writes */
/* Note that it is not clear that any compiler actually defines __PPC__,
* it can probably removed safely. */
# elif defined( __ppc__ ) || defined( __powerpc__) || defined( __PPC__ )
# define PaUtil_FullMemoryBarrier() asm volatile("sync":::"memory")
# define PaUtil_ReadMemoryBarrier() asm volatile("sync":::"memory")
# define PaUtil_WriteMemoryBarrier() asm volatile("sync":::"memory")
# elif defined( __i386__ ) || defined( __i486__ ) || defined( __i586__ ) || \
defined( __i686__ ) || defined( __x86_64__ )
# define PaUtil_FullMemoryBarrier() asm volatile("mfence":::"memory")
# define PaUtil_ReadMemoryBarrier() asm volatile("lfence":::"memory")
# define PaUtil_WriteMemoryBarrier() asm volatile("sfence":::"memory")
# else
# ifdef ALLOW_SMP_DANGERS
# warning Memory barriers not defined on this system or system unknown
# warning For SMP safety, you should fix this.
# define PaUtil_FullMemoryBarrier()
# define PaUtil_ReadMemoryBarrier()
# define PaUtil_WriteMemoryBarrier()
# else
# error Memory barriers are not defined on this system. You can still compile by defining ALLOW_SMP_DANGERS, but SMP safety will not be guaranteed.
# endif
# endif
#elif (_MSC_VER >= 1400) && !defined(_WIN32_WCE)
# include <intrin.h>
# pragma intrinsic(_ReadWriteBarrier)
# pragma intrinsic(_ReadBarrier)
# pragma intrinsic(_WriteBarrier)
/* note that MSVC intrinsics _ReadWriteBarrier(), _ReadBarrier(), _WriteBarrier() are just compiler barriers *not* memory barriers */
# define PaUtil_FullMemoryBarrier() _ReadWriteBarrier()
# define PaUtil_ReadMemoryBarrier() _ReadBarrier()
# define PaUtil_WriteMemoryBarrier() _WriteBarrier()
#elif defined(_WIN32_WCE)
# define PaUtil_FullMemoryBarrier()
# define PaUtil_ReadMemoryBarrier()
# define PaUtil_WriteMemoryBarrier()
#elif defined(_MSC_VER) || defined(__BORLANDC__)
# define PaUtil_FullMemoryBarrier() _asm { lock add [esp], 0 }
# define PaUtil_ReadMemoryBarrier() _asm { lock add [esp], 0 }
# define PaUtil_WriteMemoryBarrier() _asm { lock add [esp], 0 }
#else
# ifdef ALLOW_SMP_DANGERS
# warning Memory barriers not defined on this system or system unknown
# warning For SMP safety, you should fix this.
# define PaUtil_FullMemoryBarrier()
# define PaUtil_ReadMemoryBarrier()
# define PaUtil_WriteMemoryBarrier()
# else
# error Memory barriers are not defined on this system. You can still compile by defining ALLOW_SMP_DANGERS, but SMP safety will not be guaranteed.
# endif
#endif

237
src/pa_ringbuffer.c Normal file
View file

@ -0,0 +1,237 @@
/*
* $Id$
* Portable Audio I/O Library
* Ring Buffer utility.
*
* Author: Phil Burk, http://www.softsynth.com
* modified for SMP safety on Mac OS X by Bjorn Roche
* modified for SMP safety on Linux by Leland Lucius
* also, allowed for const where possible
* modified for multiple-byte-sized data elements by Sven Fischer
*
* Note that this is safe only for a single-thread reader and a
* single-thread writer.
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/**
@file
@ingroup common_src
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "pa_ringbuffer.h"
#include <string.h>
#include "pa_memorybarrier.h"
/***************************************************************************
* Initialize FIFO.
* elementCount must be power of 2, returns -1 if not.
*/
ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr )
{
if( ((elementCount-1) & elementCount) != 0) return -1; /* Not Power of two. */
rbuf->bufferSize = elementCount;
rbuf->buffer = (char *)dataPtr;
PaUtil_FlushRingBuffer( rbuf );
rbuf->bigMask = (elementCount*2)-1;
rbuf->smallMask = (elementCount)-1;
rbuf->elementSizeBytes = elementSizeBytes;
return 0;
}
/***************************************************************************
** Return number of elements available for reading. */
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf )
{
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
}
/***************************************************************************
** Return number of elements available for writing. */
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf )
{
return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf));
}
/***************************************************************************
** Clear buffer. Should only be called when buffer is NOT being read or written. */
void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf )
{
rbuf->writeIndex = rbuf->readIndex = 0;
}
/***************************************************************************
** Get address of region(s) to which we can write data.
** If the region is contiguous, size2 will be zero.
** If non-contiguous, size2 will be the size of second region.
** Returns room available to be written or elementCount, whichever is smaller.
*/
ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount,
void **dataPtr1, ring_buffer_size_t *sizePtr1,
void **dataPtr2, ring_buffer_size_t *sizePtr2 )
{
ring_buffer_size_t index;
ring_buffer_size_t available = PaUtil_GetRingBufferWriteAvailable( rbuf );
if( elementCount > available ) elementCount = available;
/* Check to see if write is not contiguous. */
index = rbuf->writeIndex & rbuf->smallMask;
if( (index + elementCount) > rbuf->bufferSize )
{
/* Write data in two blocks that wrap the buffer. */
ring_buffer_size_t firstHalf = rbuf->bufferSize - index;
*dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
*sizePtr1 = firstHalf;
*dataPtr2 = &rbuf->buffer[0];
*sizePtr2 = elementCount - firstHalf;
}
else
{
*dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
*sizePtr1 = elementCount;
*dataPtr2 = NULL;
*sizePtr2 = 0;
}
if( available )
PaUtil_FullMemoryBarrier(); /* (write-after-read) => full barrier */
return elementCount;
}
/***************************************************************************
*/
ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount )
{
/* ensure that previous writes are seen before we update the write index
(write after write)
*/
PaUtil_WriteMemoryBarrier();
return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & 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 read or elementCount, whichever is smaller.
*/
ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount,
void **dataPtr1, ring_buffer_size_t *sizePtr1,
void **dataPtr2, ring_buffer_size_t *sizePtr2 )
{
ring_buffer_size_t index;
ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); /* doesn't use memory barrier */
if( elementCount > available ) elementCount = available;
/* Check to see if read is not contiguous. */
index = rbuf->readIndex & rbuf->smallMask;
if( (index + elementCount) > rbuf->bufferSize )
{
/* Write data in two blocks that wrap the buffer. */
ring_buffer_size_t firstHalf = rbuf->bufferSize - index;
*dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
*sizePtr1 = firstHalf;
*dataPtr2 = &rbuf->buffer[0];
*sizePtr2 = elementCount - firstHalf;
}
else
{
*dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
*sizePtr1 = elementCount;
*dataPtr2 = NULL;
*sizePtr2 = 0;
}
if( available )
PaUtil_ReadMemoryBarrier(); /* (read-after-read) => read barrier */
return elementCount;
}
/***************************************************************************
*/
ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount )
{
/* ensure that previous reads (copies out of the ring buffer) are always completed before updating (writing) the read index.
(write-after-read) => full barrier
*/
PaUtil_FullMemoryBarrier();
return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask;
}
/***************************************************************************
** Return elements written. */
ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount )
{
ring_buffer_size_t size1, size2, numWritten;
void *data1, *data2;
numWritten = PaUtil_GetRingBufferWriteRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 );
if( size2 > 0 )
{
memcpy( data1, data, size1*rbuf->elementSizeBytes );
data = ((char *)data) + size1*rbuf->elementSizeBytes;
memcpy( data2, data, size2*rbuf->elementSizeBytes );
}
else
{
memcpy( data1, data, size1*rbuf->elementSizeBytes );
}
PaUtil_AdvanceRingBufferWriteIndex( rbuf, numWritten );
return numWritten;
}
/***************************************************************************
** Return elements read. */
ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount )
{
ring_buffer_size_t size1, size2, numRead;
void *data1, *data2;
numRead = PaUtil_GetRingBufferReadRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 );
if( size2 > 0 )
{
memcpy( data, data1, size1*rbuf->elementSizeBytes );
data = ((char *)data) + size1*rbuf->elementSizeBytes;
memcpy( data, data2, size2*rbuf->elementSizeBytes );
}
else
{
memcpy( data, data1, size1*rbuf->elementSizeBytes );
}
PaUtil_AdvanceRingBufferReadIndex( rbuf, numRead );
return numRead;
}

236
src/pa_ringbuffer.h Normal file
View file

@ -0,0 +1,236 @@
#ifndef PA_RINGBUFFER_H
#define PA_RINGBUFFER_H
/*
* $Id$
* Portable Audio I/O Library
* Ring Buffer utility.
*
* Author: Phil Burk, http://www.softsynth.com
* modified for SMP safety on OS X by Bjorn Roche.
* also allowed for const where possible.
* modified for multiple-byte-sized data elements by Sven Fischer
*
* Note that this is safe only for a single-thread reader
* and a single-thread writer.
*
* This program is distributed with the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/** @file
@ingroup common_src
@brief Single-reader single-writer lock-free ring buffer
PaUtilRingBuffer is a ring buffer used to transport samples between
different execution contexts (threads, OS callbacks, interrupt handlers)
without requiring the use of any locks. This only works when there is
a single reader and a single writer (ie. one thread or callback writes
to the ring buffer, another thread or callback reads from it).
The PaUtilRingBuffer structure manages a ring buffer containing N
elements, where N must be a power of two. An element may be any size
(specified in bytes).
The memory area used to store the buffer elements must be allocated by
the client prior to calling PaUtil_InitializeRingBuffer() and must outlive
the use of the ring buffer.
@note The ring buffer functions are not normally exposed in the PortAudio libraries.
If you want to call them then you will need to add pa_ringbuffer.c to your application source code.
*/
#if defined(__APPLE__)
#include <sys/types.h>
typedef int32_t ring_buffer_size_t;
#elif defined( __GNUC__ )
typedef long ring_buffer_size_t;
#elif (_MSC_VER >= 1400)
typedef long ring_buffer_size_t;
#elif defined(_MSC_VER) || defined(__BORLANDC__)
typedef long ring_buffer_size_t;
#else
typedef long ring_buffer_size_t;
#endif
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct PaUtilRingBuffer
{
ring_buffer_size_t bufferSize; /**< Number of elements in FIFO. Power of 2. Set by PaUtil_InitRingBuffer. */
volatile ring_buffer_size_t writeIndex; /**< Index of next writable element. Set by PaUtil_AdvanceRingBufferWriteIndex. */
volatile ring_buffer_size_t readIndex; /**< Index of next readable element. Set by PaUtil_AdvanceRingBufferReadIndex. */
ring_buffer_size_t bigMask; /**< Used for wrapping indices with extra bit to distinguish full/empty. */
ring_buffer_size_t smallMask; /**< Used for fitting indices to buffer. */
ring_buffer_size_t elementSizeBytes; /**< Number of bytes per element. */
char *buffer; /**< Pointer to the buffer containing the actual data. */
}PaUtilRingBuffer;
/** Initialize Ring Buffer to empty state ready to have elements written to it.
@param rbuf The ring buffer.
@param elementSizeBytes The size of a single data element in bytes.
@param elementCount The number of elements in the buffer (must be a power of 2).
@param dataPtr A pointer to a previously allocated area where the data
will be maintained. It must be elementCount*elementSizeBytes long.
@return -1 if elementCount is not a power of 2, otherwise 0.
*/
ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr );
/** Reset buffer to empty. Should only be called when buffer is NOT being read or written.
@param rbuf The ring buffer.
*/
void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf );
/** Retrieve the number of elements available in the ring buffer for writing.
@param rbuf The ring buffer.
@return The number of elements available for writing.
*/
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf );
/** Retrieve the number of elements available in the ring buffer for reading.
@param rbuf The ring buffer.
@return The number of elements available for reading.
*/
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf );
/** Write data to the ring buffer.
@param rbuf The ring buffer.
@param data The address of new data to write to the buffer.
@param elementCount The number of elements to be written.
@return The number of elements written.
*/
ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount );
/** Read data from the ring buffer.
@param rbuf The ring buffer.
@param data The address where the data should be stored.
@param elementCount The number of elements to be read.
@return The number of elements read.
*/
ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount );
/** Get address of region(s) to which we can write data.
@param rbuf The ring buffer.
@param elementCount The number of elements desired.
@param dataPtr1 The address where the first (or only) region pointer will be
stored.
@param sizePtr1 The address where the first (or only) region length will be
stored.
@param dataPtr2 The address where the second region pointer will be stored if
the first region is too small to satisfy elementCount.
@param sizePtr2 The address where the second region length will be stored if
the first region is too small to satisfy elementCount.
@return The room available to be written or elementCount, whichever is smaller.
*/
ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount,
void **dataPtr1, ring_buffer_size_t *sizePtr1,
void **dataPtr2, ring_buffer_size_t *sizePtr2 );
/** Advance the write index to the next location to be written.
@param rbuf The ring buffer.
@param elementCount The number of elements to advance.
@return The new position.
*/
ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount );
/** Get address of region(s) from which we can read data.
@param rbuf The ring buffer.
@param elementCount The number of elements desired.
@param dataPtr1 The address where the first (or only) region pointer will be
stored.
@param sizePtr1 The address where the first (or only) region length will be
stored.
@param dataPtr2 The address where the second region pointer will be stored if
the first region is too small to satisfy elementCount.
@param sizePtr2 The address where the second region length will be stored if
the first region is too small to satisfy elementCount.
@return The number of elements available for reading.
*/
ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount,
void **dataPtr1, ring_buffer_size_t *sizePtr1,
void **dataPtr2, ring_buffer_size_t *sizePtr2 );
/** Advance the read index to the next location to be read.
@param rbuf The ring buffer.
@param elementCount The number of elements to advance.
@return The new position.
*/
ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_RINGBUFFER_H */

107
src/pa_types.h Normal file
View file

@ -0,0 +1,107 @@
#ifndef PA_TYPES_H
#define PA_TYPES_H
/*
* Portable Audio I/O Library
* integer type definitions
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2006 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/** @file
@ingroup common_src
@brief Definition of 16 and 32 bit integer types (PaInt16, PaInt32 etc)
SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
when it is used. Otherwise we default to the common 32 bit values, if your
platform doesn't use configure, and doesn't use the default values below
you will need to explicitly define these symbols in your make file.
A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
file are correct.
*/
#ifndef SIZEOF_SHORT
#define SIZEOF_SHORT 2
#endif
#ifndef SIZEOF_INT
#define SIZEOF_INT 4
#endif
#ifndef SIZEOF_LONG
#define SIZEOF_LONG 4
#endif
#if SIZEOF_SHORT == 2
typedef signed short PaInt16;
typedef unsigned short PaUint16;
#elif SIZEOF_INT == 2
typedef signed int PaInt16;
typedef unsigned int PaUint16;
#else
#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
#endif
#if SIZEOF_SHORT == 4
typedef signed short PaInt32;
typedef unsigned short PaUint32;
#elif SIZEOF_INT == 4
typedef signed int PaInt32;
typedef unsigned int PaUint32;
#elif SIZEOF_LONG == 4
typedef signed long PaInt32;
typedef unsigned long PaUint32;
#else
#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
#endif
/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
ensure that PortAudio was configured correctly, and raises an assertion if
they don't match the expected values. <assert.h> must be included in the
context in which this macro is used.
*/
#define PA_VALIDATE_TYPE_SIZES \
{ \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
}
#endif /* PA_TYPES_H */

View file

@ -1,44 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
// #include "audio_handler.h"
#include <portaudio.h>
// int main() {
// printf("PA init test.\n");
// pthread_t in_th;
// printf("Creating audio thread.\n");
// pthread_create(&in_th, NULL, (void *) AH_initialize, NULL);
// sleep(10);
// AH_terminate();
// return 0;
// }
int main ()
{
printf("Initializing portaudio\n");
Pa_Initialize();
for (int i = 0; i < 10; ++i)
{
PaHostApiIndex hostApi = Pa_GetDefaultHostApi();
const PaHostApiInfo* hostApiInfo = Pa_GetHostApiInfo(hostApi);
printf("Default input id: %i\n", hostApiInfo->defaultInputDevice);
sleep(2);
}
Pa_Terminate();
return 0;
}

BIN
src/test.o Normal file

Binary file not shown.