initial commit
This commit is contained in:
commit
1d75db5bde
33 changed files with 2327 additions and 0 deletions
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
src/a.out
Executable file
BIN
src/a.out
Executable file
Binary file not shown.
259
src/audio_handler.c
Normal file
259
src/audio_handler.c
Normal 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
16
src/audio_handler.h
Normal 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
BIN
src/audio_handler.o
Normal file
Binary file not shown.
120
src/frame_buffer.c
Normal file
120
src/frame_buffer.c
Normal 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
24
src/frame_buffer.h
Normal 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
BIN
src/frame_buffer.o
Normal file
Binary file not shown.
26
src/makefile
Normal file
26
src/makefile
Normal 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
BIN
src/nodeaudio
Executable file
Binary file not shown.
90
src/nodeaudio.c
Normal file
90
src/nodeaudio.c
Normal 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
44
src/test.c
Normal 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;
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue