initial commit

This commit is contained in:
Andrew Gundersen 2021-07-26 14:08:25 -05:00
commit 1d75db5bde
33 changed files with 2327 additions and 0 deletions

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/a.out Executable file

Binary file not shown.

259
src/audio_handler.c Normal file
View file

@ -0,0 +1,259 @@
#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;
}

16
src/audio_handler.h Normal file
View file

@ -0,0 +1,16 @@
#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

BIN
src/audio_handler.o Normal file

Binary file not shown.

120
src/frame_buffer.c Normal file
View file

@ -0,0 +1,120 @@
#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;
}

24
src/frame_buffer.h Normal file
View file

@ -0,0 +1,24 @@
#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

BIN
src/frame_buffer.o Normal file

Binary file not shown.

26
src/makefile Normal file
View file

@ -0,0 +1,26 @@
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)/*~

BIN
src/nodeaudio Executable file

Binary file not shown.

90
src/nodeaudio.c Normal file
View file

@ -0,0 +1,90 @@
#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)

44
src/test.c Normal file
View file

@ -0,0 +1,44 @@
#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;
}