Created
December 6, 2013 07:07
-
-
Save r1b/7819755 to your computer and use it in GitHub Desktop.
Capture audio and store the last n seconds in a circular buffer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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