v1
This commit is contained in:
parent
1d75db5bde
commit
d32989cc81
135 changed files with 17183 additions and 669 deletions
BIN
src/.DS_Store
vendored
BIN
src/.DS_Store
vendored
Binary file not shown.
BIN
src/a.out
BIN
src/a.out
Binary file not shown.
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
26
src/makefile
26
src/makefile
|
|
@ -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
25
src/na_audio_buffer.c
Normal 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
14
src/na_buffer.c
Normal 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
8
src/na_buffer.h
Normal 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
159
src/na_callbacks.c
Normal 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
29
src/na_callbacks.h
Normal 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
207
src/na_core.c
Normal 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, ¶meters);
|
||||
|
||||
err = Pa_OpenStream( &inputStream,
|
||||
¶meters,
|
||||
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, ¶meters);
|
||||
|
||||
err = Pa_OpenStream( &outputStream,
|
||||
NULL,
|
||||
¶meters,
|
||||
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
25
src/na_core.h
Normal 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
173
src/na_front.c
Normal 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
41
src/na_utils.c
Normal 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
12
src/na_utils.h
Normal 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
|
||||
BIN
src/nodeaudio
BIN
src/nodeaudio
Binary file not shown.
|
|
@ -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
128
src/pa_memorybarrier.h
Normal 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
237
src/pa_ringbuffer.c
Normal 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
236
src/pa_ringbuffer.h
Normal 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
107
src/pa_types.h
Normal 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 */
|
||||
44
src/test.c
44
src/test.c
|
|
@ -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
BIN
src/test.o
Normal file
Binary file not shown.
Loading…
Reference in a new issue