Skip to content

Instantly share code, notes, and snippets.

@r1b
Created December 6, 2013 07:07
Show Gist options
  • Save r1b/7819755 to your computer and use it in GitHub Desktop.
Save r1b/7819755 to your computer and use it in GitHub Desktop.
Capture audio and store the last n seconds in a circular buffer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "sndfile.h"
#include "portaudio.h"
#define SAMPLE_RATE 44100
#define FRAMES_PER_BUFFER 1024
#define NUM_SECONDS 3
#define NUM_CHANNELS 2
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
volatile sig_atomic_t keep_going = 1;
// ring buffer
typedef struct {
char * buf;
int size;
} MYDATA;
/*
* Catch CTRL-C
*/
void catch_sigint(int sig){
if(sig == SIGINT)
keep_going = 0;
}
/*
* Portaudio callback; Keeps last NUM_SECONDS of audio in buffer
*/
static int addNewSamples(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData) {
int sizeAdded = framesPerBuffer * NUM_CHANNELS * sizeof(SAMPLE);
MYDATA * currentData = (MYDATA *)userData;
int sizeToMove = currentData->size - sizeAdded;
char * dataToKeep = currentData->buf + sizeAdded;
char * newData = currentData->buf + sizeToMove;
char * in = (char *)inputBuffer;
memmove(currentData->buf,dataToKeep,sizeToMove);
memmove(newData,in,sizeAdded);
return 0;
}
/*
* Interactions
*/
int main(void) {
PaStreamParameters inputParameters;
PaStream *stream;
MYDATA *mydata = malloc(sizeof(MYDATA));
PaError err;
const PaDeviceInfo *info;
int count;
int i;
int totalFrames;
int numSamples;
int numBytes;
signal(SIGINT,catch_sigint);
totalFrames = NUM_SECONDS * SAMPLE_RATE;
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
mydata->buf = (char *)malloc(numBytes);
mydata->size = numBytes;
memset(mydata->buf,0,numBytes);
err = Pa_Initialize();
if(err != paNoError){
Pa_Terminate();
fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
return -1;
}
count = Pa_GetDeviceCount();
if(count <= 0){
Pa_Terminate();
fprintf(stderr, "Error message: No devices!");
return -1;
}
for(i = 0;i<count;i++){
info = Pa_GetDeviceInfo(i);
printf("[%d] - %s\n",i,info->name);
}
printf("Select an input device: ");
int dev;
scanf("%d",&dev);
if(dev > i - 1 || dev < 0){
Pa_Terminate();
fprintf(stderr, "Bad device!\n");
return -1;
}
inputParameters.device = dev;
inputParameters.channelCount = NUM_CHANNELS;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
addNewSamples,
mydata);
if(err != paNoError){
Pa_Terminate();
fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
return -1;
}
err = Pa_StartStream( stream );
if(err != paNoError){
Pa_Terminate();
fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
return -1;
}
printf("Now recording");
fflush(stdout);
while(keep_going){
printf(".");
fflush(stdout);
sleep(1);
}
printf("\n");
err = Pa_CloseStream( stream );
if(err != paNoError){
Pa_Terminate();
fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
return -1;
}
//Convert to WAV and save to disk when done
SF_INFO sfinfo;
SNDFILE * sfd;
memset (&sfinfo, 0, sizeof (sfinfo)) ;
sfinfo.samplerate = SAMPLE_RATE;
sfinfo.channels = NUM_CHANNELS;
sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_16);
sfd = sf_open("recorded.wav",SFM_WRITE,&sfinfo);
sf_write_short(sfd,(short *)mydata->buf,numSamples);
sf_close(sfd);
Pa_Terminate();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment