I very new concepts of audio processing and have a basic knowledge of C++, my end goal is to create a means to measure harmonic distortion on line-in audio; my thoughts are to write audio-in stream to a circular buffer, then read from the circular buffer to an FFT function; from the FFT data I can workout audio distortion.
So far I'm using Portaudio to write streamed audio to the circular buffer from it's streaming input callback; I've created a Thread that checks if there's any data in the circular buffer, if there is data, read it into a temporary buffer. As a means of a simple test, I'm merely echo'ing the number of elements to read from the circular buffer, this works but after a second or so, produces an incorrect value for 'read elements available' (18446744073709542400
) followed by a Segmentation error; sample output and my code below:
+Read available 0
Stream started
+Read available 8192
>>dataIndex: 8192
+Read available 2048
>>dataIndex: 10240
+Read available 0
+Read available 9216
>>dataIndex: 19456
+Read available 18446744073709542400
Segmentation fault: 11
Would appreciate any help in understanding the very large read value above (18446744073709542400
) and the reason for the Segmentation error; my thoughts are it could be because the Circular Buffer read is being done in a separate Thread; however the Circular Buffer used does state that its Thread safe.
main.cpp:
#include <array>
#include <stdio.h>
#include <thread>
#include "ringbuffer.hpp"
#include "portaudio.h"
/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (512)
#define NUM_SECONDS (1)
#define NUM_CHANNELS (2)
#define NUM_WRITES_PER_BUFFER (4)
#define DITHER_FLAG (paDitherOff)
//#define DITHER_FLAG (0)
/* Select sample format. */
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE (0.0f)
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE (128)
#define PRINTF_S_FORMAT "%d"
#endif
static unsigned long int rbs_min(unsigned long int a, unsigned long int b)
{
return (a < b) ? a : b;
}
typedef struct
{
unsigned long int frameIndex;
unsigned long int dataIndex;
unsigned long int maxFrameIndex;
int threadSyncFlag;
SAMPLE *sampleData;
const SAMPLE* buff;
Ringbuffer<const SAMPLE*, 65536> ringBuffer;
void *threadHandle;
}
paTestData;
static int cons(void* ptr) {
unsigned long int ra_i;
paTestData* pData = (paTestData*)ptr;
/* Mark thread started */
pData->threadSyncFlag = 0;
while(1) {
ra_i = pData->ringBuffer.readAvailable();
printf("+Read available %lu\n",ra_i);
if ( (pData->dataIndex <= 65536) )
{
if (! pData->ringBuffer.isEmpty()) {
pData->dataIndex += pData->ringBuffer.readBuff(&pData->buff,ra_i);
printf(">>dataIndex: %lu\n",pData->dataIndex);
}
} else
{
break;
}
Pa_Sleep(100);
}
pData->threadSyncFlag = 0;
return 0;
}
static unsigned NextPowerOf2(unsigned val)
{
val--;
val = (val >> 1) | val;
val = (val >> 2) | val;
val = (val >> 4) | val;
val = (val >> 8) | val;
val = (val >> 16) | val;
return ++val;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int recordCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
unsigned long int elementsWriteable = data->ringBuffer.writeAvailable();
unsigned long int elementsToWrite = rbs_min( elementsWriteable, (unsigned long int)(framesPerBuffer * NUM_CHANNELS) );
SAMPLE *rptr = (SAMPLE*)inputBuffer;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
(void) userData;
data->frameIndex += data->ringBuffer.writeBuff( &rptr, elementsToWrite);
return paContinue;
}
PaError pa_term(PaError err) {
Pa_Terminate();
if( err != paNoError )
{
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
err = 1; // Always return 0 or 1, but no other return codes.
}
return err;
}
int main(void);
int main(void) {
PaStreamParameters inputParameters,
outputParameters;
PaStream* stream;
PaError err = paNoError;
paTestData data = {0};
unsigned delayCntr;
unsigned long int totalFrames;
unsigned numSamples;
unsigned numBytes;
data.dataIndex = 0;
err = Pa_Initialize();
if( err != paNoError )
{
pa_term(err);
}
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (inputParameters.device == paNoDevice) {
printf("Error: No default input device.\n");
pa_term(paDeviceUnavailable);
}
inputParameters.channelCount = 2; /* stereo input */
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL, /* &outputParameters, */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
&data );
if( err != paNoError )
{
pa_term(err);
}
// Start stream logging thread
std::thread first (cons, &data);
err = Pa_StartStream( stream );
if( err != paNoError )
{
pa_term(err);
}
printf("Stream started\n");
while(!paNoError) {
Pa_Sleep(1);
}
err = Pa_CloseStream( stream );
if( err != paNoError )
{
pa_term(err);
}
}