Getting flite to output audio with PortAudio - c

I am trying to get the flite speech synthesis library to work on my Mac, but my sound architecture isn't supported within the flite library. To fix that problem, I am using PortAudio to playback the synthesized audio; so I had to do a little bit of hacking within the audio.c file to get flite to use that library. I managed to get everything compiling just fine after mucking around in the GNU AutoTools for a while, but then I run the program and get this output:
$ ./flite -t "test"
frameIndex: 0
maxFrameIndex: 0
numChannels: 1
numSamples: 7225
sampleRate: 8000
=== Now playing back. ===
Waiting for playback to finish.
frameIndex in callback: -2008986336
maxFrameIndex in callback: 32655
numChannels in callback: 152579008
numSamples in callback: 0
sampleRate in callback: 0
Segmentation fault: 11
$ ./flite -t "test"
frameIndex: 0
maxFrameIndex: 0
numChannels: 1
numSamples: 7225
sampleRate: 8000
=== Now playing back. ===
Waiting for playback to finish.
frameIndex in callback: -71217888
maxFrameIndex in callback: 32712
numChannels in callback: 232979392
numSamples in callback: 0
sampleRate in callback: 0
Segmentation fault: 11
Here is the relevant code from the audio.c file, which is called when I supply the command line argument -t. I marked the area of interest where the segmentation fault occurs in the playCallback() function after a bit of debugging.
static int playCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
cst_wave *data = (cst_wave*)userData;
short *rptr = &data->samples[data->frameIndex * data->num_channels];
short *wptr = (short*)outputBuffer;
unsigned int i;
int finished;
unsigned int framesLeft = cst_wave_maxFrameIndex(data) - cst_wave_frameIndex(data);
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
(void) userData;
printf("frameIndex in callback: %d\n", cst_wave_frameIndex(data));
printf("maxFrameIndex in callback: %d\n", cst_wave_maxFrameIndex(data));
printf("numChannels in callback: %d\n", cst_wave_num_channels(data));
printf("numSamples in callback: %d\n", cst_wave_num_samples(data));
printf("sampleRate in callback: %d\n\n", cst_wave_sample_rate(data));
if( framesLeft < framesPerBuffer )
{
/* final buffer... */
for( i=0; i<framesLeft; i++ )
{
*wptr++ = *rptr++; /* left */
if( cst_wave_num_channels(data) == 2 ) *wptr++ = *rptr++; /* right */
}
for( ; i<framesPerBuffer; i++ )
{
*wptr++ = 0; /* left */
if( cst_wave_num_channels(data) == 2) *wptr++ = 0; /* right */
}
data->frameIndex += framesLeft;
finished = paComplete;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*wptr++ = *rptr++; /* left */
if( cst_wave_num_channels(data) == 2 ) *wptr++ = *rptr++; /* right */
}
cst_wave_set_frameIndex(data, framesPerBuffer);
finished = paContinue;
}
return finished;
}
int play_wave(cst_wave *w)
{
PaStream* stream;
PaStreamParameters outputParameters;
cst_wave_set_frameIndex(w, 0);
cst_wave_set_maxFrameIndex(w, (cst_wave_num_samples(w) / cst_wave_sample_rate(w)) * cst_wave_num_channels(w) * sizeof(short));
int err = 0;
err = Pa_Initialize();
outputParameters.device = Pa_GetDefaultOutputDevice();
if (outputParameters.device == paNoDevice)
{
fprintf(stderr,"Error: No default output device.\n");
return -5;
}
printf("frameIndex: %d\n", cst_wave_frameIndex(w));
printf("maxFrameIndex: %d\n", cst_wave_maxFrameIndex(w));
printf("numChannels: %d\n", cst_wave_num_channels(w));
printf("numSamples: %d\n", cst_wave_num_samples(w));
printf("sampleRate: %d\n", cst_wave_sample_rate(w));
outputParameters.channelCount = cst_wave_num_channels(w);
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
puts("=== Now playing back. ===");
err = Pa_OpenStream(&stream,
NULL, /* no input */
&outputParameters,
cst_wave_sample_rate(w),
512,
paClipOff,
playCallback,
&w);
if( stream )
{
err = Pa_StartStream( stream );
if( err != paNoError ) goto done;
puts("Waiting for playback to finish.");
while((err = Pa_IsStreamActive(stream)) == 1) Pa_Sleep(100);
if( err < 0 ) goto done;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto done;
puts("Done.");
}
done:
Pa_Terminate();
free(cst_wave_samples(w));
}
Because it is relevant, I also slightly modified the cst_wave structure in cst_wave.h so that it contains my data I have to store, as well as adding a few #defines to the ones that were already present:
typedef struct cst_wave_struct {
const char *type;
int frameIndex;
int maxFrameIndex;
int sample_rate;
int num_samples;
int num_channels;
short *samples;
} cst_wave;
#define cst_wave_num_samples(w) (w?w->num_samples:0)
#define cst_wave_num_channels(w) (w?w->num_channels:0)
#define cst_wave_sample_rate(w) (w?w->sample_rate:0)
#define cst_wave_samples(w) (w->samples)
#define cst_wave_frameIndex(w) (w->frameIndex)
#define cst_wave_maxFrameIndex(w) (w->maxFrameIndex)
#define cst_wave_set_num_samples(w,s) w->num_samples=s
#define cst_wave_set_num_channels(w,s) w->num_channels=s
#define cst_wave_set_sample_rate(w,s) w->sample_rate=s
#define cst_wave_set_frameIndex(w,s) w->frameIndex=s
#define cst_wave_set_maxFrameIndex(w,s) w->maxFrameIndex=s
Update 1:
Following the advice of #Rohan now gives me this output:
$ ./bin/flite -t "test"
frameIndex: 0
maxFrameIndex: 0
numChannels: 1
numSamples: 7225
sampleRate: 8000
=== Now playing back. ===
Waiting for playback to finish.
frameIndex in callback: 0
maxFrameIndex in callback: 0
numChannels in callback: 1
numSamples in callback: 7225
sampleRate in callback: 8000
Done.
flite(68929,0x7fff71c0d310) malloc: *** error for object 0x7fd6e2809800: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
To fix that, I removed the free(cst_wave_samples(w));. Now the program executes normally with no visible errors, but there is still no audio output on my Mac. Any suggestions?

It looks to me like the problem is probably elsewhere.
The routine where you've added the comment is really pretty trivial when all is said and done. It's basically just copying a buffer full of data from one place to another, and if the data doesn't fill the input buffer, zero-filling the remainder. If I were writing the code, I'd probably do something more along these general lines:
const unsigned frame_size = sizeof(short) * data->num_channels;
char *source = &data->samples[data->frameIndex * data->num_channels];
char *dest = outputBuffer;
unsigned framesLeft = data->maxFrameIndex - data->frameIndex;
unsigned framesEmpty = framesPerBuffer - framesLeft;
memcpy(source, dest, framesLeft * frame_size);
memset(dest+framesLeft * frame_size, 0, framesEmpty * frame_size);
data->frameIndex += framesPerBuffer;
Although rather clumsily written, the if/else in the question just skips doing the memset part at all if the size that needs to be filled is zero.
So, this copies a buffer full of data from one place to another, and zero-fills any remainder. If you're getting a segfault, whatever's allocating the destination buffer apparently hasn't allocated enough space there. Without doing some looking, it's impossible to guess whether the allocation happens in Pa_Initialize, Pa_OpenStream, Pa_StartStream, or possibly somewhere else--and most likely you care less about the code that actually does the allocation than the code that computes how much space to allocate (which might be in one of the above, or somewhere else completely).

In your play_wave function you are calling:
err = Pa_OpenStream(&stream,
NULL, /* no input */
&outputParameters,
cst_wave_sample_rate(w),
512,
paClipOff,
playCallback,
&w);
Here as last parameter you are passing &w, so you are passing cst_wave ** as w is defined as cst_wave *w.
But in playCallback() you are using it as
cst_wave *data = (cst_wave*)userData;
So in this function you are incorrectly accessing cst_wave ** as cst_wave *. So at some point you will access the invalid memory while using some member of w.
Also, this is the reason you are getting the incorrect output for other parameters e.g. frameIndex, maxFrameIndex etc. as your output shows.
Solution is just pass w to Pa_OpenStream() function rather than &w.
Your next problem is that you aren't setting your maxFrameIndex correctly. As you stated in the comments, this shouldn't be 0. In order to set it properly, you should have something like this:
cst_wave_set_maxFrameIndex(w, cst_wave_num_samples(w) * cst_wave_num_channels(w));
Lastly, it looks like your callback could be messing things up a bit. Here is a better and more efficient way to write it:
static int playCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
cst_wave *data = (cst_wave*)userData;
short *rptr = &data->samples[data->frameIndex * data->num_channels];
short *wptr = (short*)outputBuffer;
int finished;
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
(void) userData;
if( framesLeft < framesPerBuffer )
{
/* final buffer... */
memcpy(wptr, rptr, sizeof(*wptr) * data->num_channels * framesLeft);
memset(wptr, sizeof(*wptr) * data->num_channels * framesPerBuffer, 0);
data->frameIndex += framesLeft;
finished = paComplete;
}
else
{
memcpy(wptr, rptr, sizeof(*wptr) * data->num_channels * framesPerBuffer);
data->frameIndex += framesPerBuffer;
finished = paContinue;
}
return finished;
}

You're in luck. I was able to compile both PortAudio and flite on my own Mac, and solve your problem.
You have several issues other than those mentioned before, all of which I have addressed in the code dump below.
Minor: You don't use consistently your own API for cst_wave.
Minor: I prefer to enclose my while and if blocks with {} always. This has a habit of preventing mysterious bugs.
Max Frames was being set to zero. That's because in (cst_wave_num_samples(w) / cst_wave_sample_rate(w)) * cst_wave_num_channels(w) * sizeof(short), you were dividing by the sample rate, which was greater than your number of samples. Given that integer division is left-associative and truncating, yadda yadda yadda zero.
Max Frames is still wrong, as a frame includes all channel samples. The number of frames is thus agnostic to both number of channels and the size of the samples themselves. Allowing myself to guess that flite misuses sample to mean frame, your max frame index is just cst_wave_num_samples(w). Else it will be cst_wave_num_samples(w) / cst_wave_num_channels(w).
PortAudio's documentation states you should call Pa_StopStream(stream) after the stream becomes inactive, whether or not you were waiting until it became so.
I simplified the callback, and corrected it for
Minor: Consistent use of your API
MAJOR: Ehm... cst_wave_set_frameIndex(data, framesPerBuffer); is definitely wrong. You're pinning yourself at frame index 512 instead of incrementing! That's because you asked for 512 frames per buffer when opening the stream and you're not incrementing the frame index by framesPerBuffer, you're setting the frame index to framesPerBuffer. You hadn't made it that far because your maxFrameIndex was 0 anyways so you were exiting. I fixed it so that the frame index increments - with your API of course.
Here is the code, which I took the freedom of documenting and cleaning until it approached my standards of elegance. Enjoy!
#include <stdio.h>
#include <string.h>
/**
* Audio play callback.
*
* Follows the PaStreamCallback signature, wherein:
*
* #param input and
* #param output are either arrays of interleaved samples or; if
* non-interleaved samples were requested using the
* paNonInterleaved sample format flag, an array of buffer
* pointers, one non-interleaved buffer for each channel.
* #param frameCount The number of sample frames to be processed by the
* stream callback.
* #param timeInfo Timestamps indicating the ADC capture time of the first
* sample in the input buffer, the DAC output time of the
* first sample in the output buffer and the time the
* callback was invoked. See PaStreamCallbackTimeInfo and
* Pa_GetStreamTime()
* #param statusFlags Flags indicating whether input and/or output buffers
* have been inserted or will be dropped to overcome
* underflow or overflow conditions.
* #param userData The value of a user supplied pointer passed to
* Pa_OpenStream() intended for storing synthesis data
* etc.
*/
static int playCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData){
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
(void) userData;
/**
* Compute current processing state.
*/
cst_wave* data;
short* rptr;
short* wptr;
unsigned int framesLeft, /* Number of frames of data remaining within the stream ***as a whole*** */
frames, /* Number of frames of data to be written for this buffer. */
framesPad, /* Number of frames of padding required within the final buffer. */
samples, /* Number of samples of data to be written for this buffer. */
samplesPad, /* Number of samples of padding required within the final buffer. */
numBytes, /* Number of bytes of data to be written for this buffer. */
numBytesPad;/* Number of bytes of padding required within the final buffer. */
int finalBuffer;/* Stores whether or not this is the final buffer. */
data = (cst_wave*)userData;
rptr = &data->samples[cst_wave_frameIndex (data) *
cst_wave_num_channels(data)];
wptr = (short*)outputBuffer;
framesLeft = cst_wave_maxFrameIndex(data) - cst_wave_frameIndex(data);
finalBuffer = framesLeft <= framesPerBuffer;
frames = finalBuffer ? framesLeft : framesPerBuffer;
framesPad = framesPerBuffer - frames;
samples = frames * cst_wave_num_channels(data);
samplesPad = framesPad * cst_wave_num_channels(data);
numBytes = samples * sizeof(short);
numBytesPad = samplesPad * sizeof(short);
/**
* Debug code. Comment out in production.
*/
printf("framesLeft in callback: %u\n", framesLeft);
printf("framesPerBuffer in callback: %lu\n", framesPerBuffer);
printf("frames in callback: %u\n", frames);
printf("frameIndex in callback: %d\n", cst_wave_frameIndex(data));
printf("maxFrameIndex in callback: %d\n", cst_wave_maxFrameIndex(data));
printf("numChannels in callback: %d\n", cst_wave_num_channels(data));
printf("numSamples in callback: %d\n", cst_wave_num_samples(data));
printf("sampleRate in callback: %d\n\n", cst_wave_sample_rate(data));
/**
* Output data. We handle the final buffer specially, padding it with zeros.
*/
memcpy(wptr, rptr, numBytes);
wptr += samples;
rptr += samples;
cst_wave_set_frameIndex(data, cst_wave_frameIndex(data) + frames);
memset(wptr, 0, numBytesPad);
wptr += samplesPad;
rptr += samplesPad;
/**
* Return a completion or continue code depending on whether this was the
* final buffer or not respectively.
*/
return finalBuffer ? paComplete : paContinue;
}
/**
* Play wave function.
*
* Plays the given cst_wave data as audio, blocking until this is done.
*/
int play_wave(cst_wave *w){
PaStream* stream;
PaStreamParameters outputParameters;
int err;
/**
* Initialize custom fields in cst_wave struct.
*/
cst_wave_set_frameIndex(w, 0);
cst_wave_set_maxFrameIndex(w, (cst_wave_num_samples(w)));
// / cst_wave_sample_rate(w) * cst_wave_num_channels(w) * sizeof(short)
/**
* Initialize Port Audio device and stream parameters.
*/
err = Pa_Initialize();
outputParameters.device = Pa_GetDefaultOutputDevice();
if (outputParameters.device == paNoDevice){
fprintf(stderr,"Error: No default output device.\n");
return -5;
}
printf("frameIndex: %d\n", cst_wave_frameIndex(w));
printf("maxFrameIndex: %d\n", cst_wave_maxFrameIndex(w));
printf("numChannels: %d\n", cst_wave_num_channels(w));
printf("numSamples: %d\n", cst_wave_num_samples(w));
printf("sampleRate: %d\n", cst_wave_sample_rate(w));
outputParameters.channelCount = cst_wave_num_channels(w);
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/**
* Open the stream for playback.
*/
puts("=== Now playing back. ===");
err = Pa_OpenStream(&stream,
NULL, /* no input */
&outputParameters,
cst_wave_sample_rate(w),
512,
paClipOff,
playCallback,
w);
if(stream){
/**
* Start the stream.
*/
err = Pa_StartStream(stream);
if(err != paNoError){
goto done;
}
/**
* Block while it plays.
*/
puts("Waiting for playback to finish.");
while((err = Pa_IsStreamActive(stream)) == 1){
Pa_Sleep(100);
}
if(err < 0){
goto done;
}
/**
* Stop and close the stream. Both are necessary.
*/
Pa_StopStream(stream);
err = Pa_CloseStream(stream);
if(err != paNoError){
goto done;
}
puts("Done.");
}
/**
* Terminate and leave.
*/
done:
Pa_Terminate();
return 0;
}

Related

nanopb/protobuf - how to force max size serialization/encoding

Documentation for pb_ostream_from_buffer says
After writing, you can check stream.bytes_written to find out how much
valid data there is in the buffer. This should be passed as the
message length on decoding side.
So ideally, when I send the serialized data I need to also send the bytes_written as a parameter separate from the buffer.
The problem is that my interface only allows me to send one variable: the buffer.
QUESTION
How do I specify always serialize the struct with no optimizations so that bufsize in
pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
can be a constant (i.e. the macro that specifies the maximum size) instead of needing to pass stream.bytes_written?
According to the Protocol Buffers encoding specification there are variable size types (like int32, int64, string, etc) and fixed size types (like fixed32, fixed64, double, etc). Now, this variable size encoding is more than just an optimization, it's a part of the design and specification. So disabling this "optimization" by the means of Protocol Buffers is only possible if your data consists exclusively of fixed length types and has no repeated fields as long as the number of repetitions is not fixed. I presume that this is not the case, since you're asking this question. So the short answer is no, it's not possible by means of the library because it would violate the encoding specification.
But in my opinion the desired effect could be easily achieved by encoding the size into the buffer with little CPU and RAM overhead. I presume you know the maximum size of the message generated by nanopb, we denote it by MAX_MSG_SIZE. We call this message the payload message. Suppose that this MAX_MSG_SIZE can be represented by some integer type, which we denote by wrapped_size_t (e.g. uint16_t).
The idea is simple:
allocate the buffer slightly larger than MAX_MSG_SIZE;
write the payload message generated by nanopb at some offset into the allocated buffer;
use this offset to encode the size of the payload message at the beginning of the buffer;
transmit the whole buffer having the fixed size equal to MAX_MSG_SIZE + sizeof(wrapped_size_t) to the receiver;
upon reception decode the size of the payload message and pass both the decoded size and the payload message to pb_istream_from_buffer.
I attach the code to illustrate the idea. I used an example from nanopb repository:
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "simple.pb.h"
//#define COMMON_ENDIANNES
#ifdef COMMON_ENDIANNES
#define encode_size encode_size_ce
#define decode_size decode_size_ce
#else
#define encode_size encode_size_le
#define decode_size decode_size_le
#endif
typedef uint16_t wrapped_size_t;
/* Maximum size of the message returned by bytes_written */
const size_t MAX_MSG_SIZE = 11;
/* Size of the field storing the actual size of the message
* (as returned by bytes_written) */
const size_t SIZE_FIELD = sizeof(wrapped_size_t);
/* Fixed wrapped message size */
const size_t FIXED_MSG_SIZE = MAX_MSG_SIZE + sizeof(wrapped_size_t);
void print_usage(char *prog);
/* Get the address of the payload buffer from the transmitted buffer */
uint8_t* payload_buffer(uint8_t *buffer);
/* Encode the payload size into the transmitted buffer (common endiannes) */
void encode_size_ce(uint8_t *buffer, size_t size);
/* Decode the payload size into the transmitted buffer (common endiannes) */
wrapped_size_t decode_size_ce(uint8_t *buffer);
/* Encode the payload size into the transmitted buffer (little endian) */
void encode_size_le(uint8_t *buffer, size_t size);
/* Decode the payload size into the transmitted buffer (little endian) */
size_t decode_size_le(uint8_t *buffer);
int main(int argc, char* argv[])
{
/* This is the buffer where we will store our message. */
uint8_t buffer[MAX_MSG_SIZE + sizeof(wrapped_size_t)];
bool status;
if(argc > 2 || (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))))
{
print_usage(argv[0]);
return 1;
}
/* Encode our message */
{
/* Allocate space on the stack to store the message data.
*
* Nanopb generates simple struct definitions for all the messages.
* - check out the contents of simple.pb.h!
* It is a good idea to always initialize your structures
* so that you do not have garbage data from RAM in there.
*/
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that will write to our buffer. */
pb_ostream_t stream = pb_ostream_from_buffer(payload_buffer(buffer),
MAX_MSG_SIZE);
if(argc > 1)
sscanf(argv[1], "%" SCNd32, &message.lucky_number);
else
{
printf("Input lucky number: ");
scanf("%" SCNd32, &message.lucky_number);
}
/* Encode the payload message */
status = pb_encode(&stream, SimpleMessage_fields, &message);
/* Wrap the payload, i.e. add the size to the buffer */
encode_size(buffer, stream.bytes_written);
/* Then just check for any errors.. */
if (!status)
{
printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
}
/* Now we could transmit the message over network, store it in a file, etc.
* Note, the transmitted message has a fixed length equal to FIXED_MSG_SIZE
* and is stored in buffer
*/
/* But for the sake of simplicity we will just decode it immediately. */
{
/* Allocate space for the decoded message. */
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that reads from the buffer. */
pb_istream_t stream = pb_istream_from_buffer(payload_buffer(buffer),
decode_size(buffer));
/* Now we are ready to decode the message. */
status = pb_decode(&stream, SimpleMessage_fields, &message);
/* Check for errors... */
if (!status)
{
printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
/* Print the data contained in the message. */
printf("Your lucky number was %d; payload length was %d.\n",
(int)message.lucky_number, (int)decode_size(buffer));
}
return 0;
}
void print_usage(char *prog)
{
printf("usage: %s [<lucky_number>]\n", prog);
}
uint8_t* payload_buffer(uint8_t *buffer)
{
return buffer + SIZE_FIELD;
}
void encode_size_ce(uint8_t *buffer, size_t size)
{
*(wrapped_size_t*)buffer = size;
}
wrapped_size_t decode_size_ce(uint8_t *buffer)
{
return *(wrapped_size_t*)buffer;
}
void encode_size_le(uint8_t *buffer, size_t size)
{
int i;
for(i = 0; i < sizeof(wrapped_size_t); ++i)
{
buffer[i] = size;
size >>= 8;
}
}
size_t decode_size_le(uint8_t *buffer)
{
int i;
size_t ret = 0;
for(i = sizeof(wrapped_size_t) - 1; i >= 0; --i)
ret = buffer[i] + (ret << 8);
return ret;
}
UPD Ok, if, for some reason, you still wish to stick to the original GPB encoding there's another option available: fill the unused part of the buffer (i.e. the part after the last byte written by nanopb) with some valid data which will be ignored. For instance, you can reserve a field number which doesn't mark any field in your *.proto file but is used to mark the data which will be discarded by the GPB decoder. Let's denote this reserved field number as RESERVED_FIELD_NUMBER. This is used for backward compatibility but you can use it for your purpose as well. Let's call this filling-in the buffer with the dummy data sealing (perhaps there's a better term). This method also requires that you have at least 2 free bytes available to you after pb_encode.
So the idea of sealing is even simpler:
calculate how many buffer bytes is left unfilled after pb_encode;
mark the rest of the buffer as array of bytes with RESERVED_FIELD_NUMBER.
I attach the updated code, the main function is bool seal_buffer(uint8_t *buffer, size_t size), call it after pb_encode to seal the buffer and you're done. Currently, it has a limitation of sealing no more than 2 ** 28 + 4 bytes, but it could be easily updated to overcome this limitation.
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "simple.pb.h"
/* Reserved field_number shouldn't be used for field numbering. We use it
* to mark the data which will be ignored upon reception by GPB parser.
* This number should be 1 to 15 to fit into a single byte. */
const uint8_t RESERVED_FIELD_NUMBER = 15;
/* Maximum size of the message returned by bytes_written (payload size) */
const size_t MAX_MSG_SIZE = 200;
/* Size of the transmitted message (reserve 2 bytes for minimal sealing) */
const size_t FIXED_MSG_SIZE = MAX_MSG_SIZE + 2;
void print_usage(char *prog);
/* Sealing the buffer means filling it in with data which is valid
* in the sense that a GPB parser accepts it as valid but ignores it */
bool seal_buffer(uint8_t *buffer, size_t size);
int main(int argc, char* argv[])
{
/* This is the buffer where we will store our message. */
uint8_t buffer[FIXED_MSG_SIZE];
bool status;
if(argc > 2 || (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))))
{
print_usage(argv[0]);
return 1;
}
/* Encode our message */
{
/* Allocate space on the stack to store the message data.
*
* Nanopb generates simple struct definitions for all the messages.
* - check out the contents of simple.pb.h!
* It is a good idea to always initialize your structures
* so that you do not have garbage data from RAM in there.
*/
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that will write to our buffer. */
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if(argc > 1)
sscanf(argv[1], "%" SCNd32, &message.lucky_number);
else
{
printf("Input lucky number: ");
scanf("%" SCNd32, &message.lucky_number);
}
/* Now we are ready to encode the message! */
status = pb_encode(&stream, SimpleMessage_fields, &message);
/* Then just check for any errors.. */
if (!status)
{
fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
/* Now the main part - making the buffer fixed-size */
assert(stream.bytes_written + 2 <= FIXED_MSG_SIZE);
if(!seal_buffer(buffer + stream.bytes_written,
FIXED_MSG_SIZE - stream.bytes_written))
{
fprintf(stderr, "Failed sealing the buffer "
"(filling in with valid but ignored data)\n");
return 1;
}
}
/* Now we could transmit the message over network, store it in a file or
* wrap it to a pigeon's leg.
*/
/* But because we are lazy, we will just decode it immediately. */
{
/* Allocate space for the decoded message. */
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that reads from the buffer. */
pb_istream_t stream = pb_istream_from_buffer(buffer, FIXED_MSG_SIZE);
/* Now we are ready to decode the message. */
status = pb_decode(&stream, SimpleMessage_fields, &message);
/* Check for errors... */
if (!status)
{
fprintf(stderr, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
/* Print the data contained in the message. */
printf("Your lucky number was %d.\n", (int)message.lucky_number);
}
return 0;
}
void print_usage(char *prog)
{
printf("usage: %s [<lucky_number>]\n", prog);
}
bool seal_buffer(uint8_t *buffer, size_t size)
{
size_t i;
if(size == 1)
{
fprintf( stderr, "Cannot seal the buffer, at least 2 bytes are needed\n");
return false;
}
assert(size - 5 < 1<<28);
if(size - 5 >= 1<<28)
{
fprintf( stderr, "Representing the size exceeding 2 ** 28 + 4, "
"although it's not difficult, is not yet implemented\n");
return false;
}
buffer[0] = (15 << 3) + 2;
/* encode the size */
if(size - 2 < 1<<7)
buffer[1] = size - 2;
else
{
/* Size is large enough to fit into 7 bits (1 byte).
* For simplicity we represent the remaining size by 4 bytes (28 bits).
* Note that 1 byte is used for encoding field_number and wire_type,
* plus 4 bytes for the size encoding, therefore the "remaining size"
* is equal to (size - 5)
*/
size -= 5;
for(i = 0; i < 4; ++i)
{
buffer[i + 1] = i < 3? (size & 0x7f) | 0x80: size & 0x7f;
size >>= 7;
}
}
return true;
}

LWIP ECHO SERVER: How to increase the buffer size in itoa function?

I am working with Xilinx Ethernetlite (LWIP) design. I am able to transfer the data from KC board to PC (Hercules) through Ethernet only if buf =32. But my actual buffer size is 1024. How to increase the buffer size from 32 to 1024
I am unable to make sure whether the mistake is in code or in hercules. To read the values (integer) in hercules i am doing this function.
initially, From hercules i will send the Hello command to Board,then board with accept that request. After that, board will output the data (integer values) to Hercules.
C code for itoa
char* itoa(int val, int base)
{
static char buf[32] = {0}; //buf size
int i = 30;
for(; val && i ; --i, val /= base)
buf[i] = "0123456789abcdef"[val % base];
return &buf[i+1];
}
Modified code
#define DAQ_FIFO_DEPTH 128
int transfer_data()
{
return 0;
}
err_t tcp_write_u32_string(struct tcp_pcb *pcb, unsigned char prefix, u32_t value)
{
unsigned char buf[11]; /* enough room for prefix and value. */
err_t result;
u16_t len;
unsigned char *p = buf + sizeof buf;
do {
/* ASCII encoding: '0' = 48, '1' = 49, ..., '9' = 57. */
*(--p) = 48 + (value % 10u);
value /= 10;
} while (value);
if (prefix)
*(--p) = prefix;
len = buf + sizeof buf - p;
if (tcp_sndbuf(pcb) < len)
{
result = tcp_output(pcb);
if (result != ERR_OK)
return result;
}
return tcp_write(pcb, p, len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
}
err_t send_list(struct tcp_pcb *pcb, const u32_t data[], u16_t len)
{
static const char newline[2] = { 13, 10 }; /* ASCII \r\n */
err_t result;
if (len > 0) {
u16_t i;
result = tcp_write_u32_string(pcb, 0, data[0]);
if (result != ERR_OK)
return result;
for (i = 1; i < len; i++)
{
/* ASCII comma is code 44. (Use 32 for space, or 9 for tab.) */
result = tcp_write_u32_string(pcb, 44, data[i]);
if (result != ERR_OK)
return result;
}
}
result = tcp_write(pcb, newline, 2, 0);
if (result)
return result;
return tcp_output(pcb);
}
int application_connection(void *arg, struct tcp_pcb *conn, err_t err)
{
struct netif *netif = arg; /* Because of tcp_arg(, netif). */
u32_t data[DAQ_FIFO_DEPTH];
u32_t i, n;
if (err != ERR_OK) {
tcp_abort(conn);
return ERR_ABRT;
}
err = daq_setup();
if (err != ERR_OK)
{
tcp_abort(conn);
return ERR_ABRT;
}
while (1)
{
xemacif_input(netif);
tcp_tmr();
tcp_output(conn);
n = daq_acquire(data, DAQ_FIFO_DEPTH);
if (n > DAQ_FIFO_DEPTH)
break;
if (tcp_write(conn, data, n * sizeof data[0], TCP_WRITE_FLAG_COPY) != ERR_OK)
break;
}
// daq_close();
/* Close the TCP connection. */
if (tcp_close(conn) == ERR_OK)
return ERR_OK;
/* Close failed. Abort it, then. */
tcp_abort(conn);
return ERR_ABRT;
}
int application_main(struct netif *netif, unsigned int port)
{
struct tcp_pcb *pcb;
err_t err;
pcb = tcp_new();
if (!pcb) {
/* Out of memory error */
return -1;
}
err = tcp_bind(pcb, IP_ADDR_ANY, port);
if (err != ERR_OK) {
/* TCP error */
return -1;
}
pcb = tcp_listen_with_backlog(pcb, 1);
if (!pcb) {
/* Out of memory. */
return -1;
}
tcp_arg(pcb, netif);
tcp_accept(pcb, application_connection);
while (1)
xemacif_input(netif);
}
Hercules output
enter image description here
So, this is a continuation from the discussion at Xilinx forums?
The itoa() function converts an unsigned integer (stored in an int) into the first 30 or so characters in the buffer buf.
The recv_callback() function makes little to no sense.
The call to aurora_rx_main() is documented as a "FUNCTION CALL", which is rather less than helpful (because we have no idea what it does), and even its return value is completely ignored.
The first for loop dumps the contents of the first 100 u32's in DestinationBuffer[] for debugging purposes, so that code is unrelated to the task at hand. However, we don't know who or what filled DestinationBuffer. It might or might not have been filled by the aurora_rx_main() call; we're not told either way.
(The tcp_*() functions seem to follow the API described in lwIP Wiki at Wikia.)
If the p parameter is NULL, then tcp_close(tcpb) is called, followed by a tcp_recv(tcpb, NULL) call. This makes the least sense of all: why try to receive anything (and why the NULL parameter) after a close?
The next part is similarly baffling. It looks like the if test checks if the TCP send buffer is over 1024 bytes in size. If not, the p buffer is freed. Otherwise, the for loop tries to convert each u32 in DestinationBuffer to a string, write that string to the TCP buffer; however, rather than the proper api flags, it uses the constant 1, and does not even check if appending to the TCP send buffer works.
In summary, this looks like a pile of copy-pasted code that does nothing sensible. Increasing the buffer size in itoa function is not only unnecessary (an u32, even when converted to an int, will always fit within 12 characters (excluding either the minus sign, or the nul byte at end, so make that 13 characters total), but completely unrelated to the problem it is supposed to fix.
The root problem is that the code is horrible. Modifying it is like putting car body filler over a piece of old chewing gum, in an effort to "fix" it. The proper fix is to rip out that junk code altogether, and use something better instead.
Edit: The OP states that they're a new programmer, so the comments above should be taken as a direct, honest opinion of the code shown, and not about OP themselves. Let's see if we can help OP produce better code.
First, the itoa() function shown is silly. Assuming the intent is really to send back the u32_ts in the DestinationBuffer as decimal strings, it is much better to implement a helper function for doing the conversion. Since the value is to be preceded with a comma (or some other separator), we can add that trivially as well. Since it will be sent using tcp_write(), we'll combine the functionality:
err_t tcp_write_u32_string(struct tcp_pcb *pcb,
unsigned char prefix, /* 0 for none */
u32_t value)
{
/* Because 0 <= u32_t <= 4294967295, the value itself is at most 10 digits long. */
unsigned char buf[11]; /* enough room for prefix and value. */
err_t result;
u16_t len;
unsigned char *p = buf + sizeof buf;
/* Construct the value first, from right to left. */
do {
/* ASCII encoding: '0' = 48, '1' = 49, ..., '9' = 57. */
*(--p) = 48 + (value % 10u);
value /= 10;
} while (value);
/* Prepend the prefix, if any. */
if (prefix)
*(--p) = prefix;
/* Calculate the length of this part. */
len = buf + sizeof buf - p;
/* If the TCP buffer does not have enough free space, flush it. */
if (tcp_sendbuf(pcb) < len) {
result = tcp_output(pcb);
if (result != ERR_OK)
return result;
}
/* Append the buffer to the TCP send buffer.
We also assume the packet is not done yet. */
return tcp_write(pcb, p, len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
}
so that to send num u32_ts from a specified array as decimal strings, with a newline at end, you could use
err_t send_list(struct tcp_pcb *pcb,
const u32_t data[],
u16_t len)
{
static const char newline[2] = { 13, 10 }; /* ASCII \r\n */
err_t result;
if (len > 0) {
u16_t i;
/* The first number has no prefix. */
result = tcp_write_u32_string(pcb, 0, data[0]);
if (result != ERR_OK)
return result;
/* The following numbers have a comma prefix. */
for (i = 1; i < len; i++) {
/* ASCII comma is code 44. (Use 32 for space, or 9 for tab.) */
result = tcp_write_u32_string(pcb, 44, data[i]);
if (result != ERR_OK)
return result;
}
}
/* We add a final newline.
Note that this one can be referenced,
and it does complete what we wanted to send thus far. */
result = tcp_write(pcb, newline, 2, 0);
if (result)
return result;
/* and flush the buffer, so the packet gets sent right now. */
return tcp_output(pcb);
}
Now, I haven't written C for Xilinx or used the lwIP stack at all, so the above code is written blind. Yet, I'm pretty confident it works (barring any typos or thinkos; if you find any, report them in a comment, and I'll verify and fix).
The two buffers (buf and newline) are declared static, so that although they're only visible within their respective functions, their value is valid in the global scope.
Because TCP is a stream protocol, it is not necessary to fit each response to a single packet. Other than the 11-character (for each number and its prefixing character) and the 2-character (newline) buffers, the only large buffer you need is the TCP send buffer (the maximum transmission unit or maximum segment size as I'm not sure how lwIP uses the buffer internally), typically between 536 and 1518 bytes.
The two above functions try to split packets between numbers, but that's just because it's easier than to try and fill each packet exactly. If the next (comma and) value fit in the buffer, then it is added to the buffer; otherwise the buffer is flushed first, and then the next (comma and) value added to the buffer.
From the recipient side, you should obtain a nice, readable stream using e.g. netcat. (I have no idea if Hercules is an application, or just the name of your local machine.) Because TCP is a stream protocol, the recipient cannot (reliably) detect where the packet boundaries were (unlike in, say, UDP datagrams). In practice, a TCP connection is just two streams of data, each going one way, and the split into packets is just a protocol detail application programmers don't need to worry about. For lwIP, because it is such a low-level library, a little bit of care need to be taken, but as one can see from the above code, it's really not that much.
In a comment, OP explained that they are not very experienced, and that the overall intent is to have the device accept a TCP connection, and stream data (samples acquired by a separate acquisition board) as unsigned 32-bit integers via the connection.
Because I would love to have one of those FPGA boards (I have several tasks I could see if I could offload to an FPGA), but no resources to get one, I shall try to outline the entire application here. Do note that the only information I have to go on, is the 2018 version of Xilinx OS and Libraries Document Collection (UG643) (PDF). It looks like OP wants to use the raw API, for high performance.
Converting the samples to text is silly, especially if high performance is desired. We should just use raw binary, and whatever endianness the KC705 uses. (I didn't see it at a quick glance from the documentation, but I suspect it is little-endian).
According to the documentation, the raw API main() is something similar to following:
int main(void)
{
/* MAC address. Use an unique one. */
unsigned char mac[6] = { 0x00, 0x0A, 0x35, 0x00, 0x01, 0x02 };
struct netif *netif = NULL;
ip_addr_t ipaddr, netmask, gateway;
/* Define IP address, netmask, and gateway. */
IP4_ADDR(&ipaddr, 192, 168, 1, 1);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gateway, 0, 0, 0, 0);
/* Initialize lwIP networking stack. */
lwip_init();
/* Add this networking interface, and make it the default one */
if (!xemac_add(netif, &ipaddr, &netmask, &gateway, mac, EMAC_BASEADDR)) {
printf("Error adding network interface\n\r");
return -1;
}
netif_set_default(netif);
platform_enable_interrupts();
/* Bring the network interface up (activate it) */
netif_set_up(netif);
/* Our application listens on port 7. */
return application_main(netif, 7);
}
In the documentation examples, rather than return application_main(netif);, you'll see a call to start_application(), and then an infinite loop that regularly calls xemacif_input(netif) instead. It just means that out application_main() must call xemacif_input(netif) regularly, to be able to receive data. (The lwIP documentation says that we should also call sys_check_timeouts() or tcp_tmr() at regular intervals.)
Note that I've omitted error reporting printfs, and that rather than recovering from errors gracefully, this will just return (from main()); I'm not certain whether this causes the KC705 to restart or what.
int application_main(struct netif *netif, unsigned int port)
{
struct tcp_pcb *pcb;
err_t err;
pcb = tcp_new();
if (!pcb) {
/* Out of memory error */
return -1;
}
/* Listen for incoming connections on the specified port. */
err = tcp_bind(pcb, IP_ADDR_ANY, port);
if (err != ERR_OK) {
/* TCP error */
return -1;
}
pcb = tcp_listen_with_backlog(pcb, 1);
if (!pcb) {
/* Out of memory. */
return -1;
}
/* The accept callback function gets the network interface
structure as the extra parameter. */
tcp_arg(pcb, netif);
/* For each incoming connection, call application_connection(). */
tcp_accept(pcb, application_connection);
/* In the mean time, process incoming data. */
while (1)
xemacif_input(netif);
}
For each TCP connection to the port, we get a call to application_connection(). This is the function that sets up the data acquisition board, and transfers the data for as long as the recipient wants it.
/* How many DAQ samples to process in each batch.
* Should be around the DAQ FIFO depth or so, I think. */
#define DAQ_FIFO_DEPTH 128
err_t application_connection(void *arg, struct tcp_pcb *conn, err_t err)
{
struct netif *netif = arg; /* Because of tcp_arg(, netif). */
u32_t data[DAQ_FIFO_DEPTH];
u32_t i, n;
/* Drop the connection if there was an error. */
if (err != ERR_OK) {
tcp_abort(conn);
return ERR_ABRT;
}
/* Setup the data aquisition. */
err = daq_setup();
if (err != ERR_OK) {
tcp_abort(conn);
return ERR_ABRT;
}
/* Data acquisition to TCP loop. */
while (1) {
/* Keep the networking stack running. */
xemacif_input(netif);
tcp_tmr();
/* Tell the networking stack to output what it can. */
tcp_output(conn);
/* Acquire up to DAQ_FIFO_DEPTH samples. */
n = daq_acquire(data, DAQ_FIFO_DEPTH);
if (n > DAQ_FIFO_DEPTH)
break;
/* Write data as-is to the tcp buffer. */
if (tcp_write(conn, data, n * sizeof data[0], TCP_WRITE_FLAG_COPY) != ERR_OK)
break;
}
/* Stop data acquisition. */
daq_close();
/* Close the TCP connection. */
if (tcp_close(conn) == ERR_OK)
return ERR_OK;
/* Close failed. Abort it, then. */
tcp_abort(conn);
return ERR_ABRT;
}
There are three more functions to implement: daq_setup(), which should setup the data acquisition and FIFOs; daq_acquire(u32_t *data, u32_t count) that stores up to count samples to data[], and returns the actual number of samples stored -- it would be best if it just drained the FIFO, rather than waited for new samples to arrive --, and finally daq_close(), that stops the data acquisition.
I believe they should be something like this:
XLlFifo daq_fifo;
err_t daq_setup(void)
{
XLlFifo_Config *config = NULL;
config = XLlFifo_LookupConfig(DAQ_FIFO_ID);
if (!config)
return ERR_RTE;
if (XLlFifo_CfgInitialize(&daq_fifo, config, config->BaseAddress) != XST_SUCCESS)
return ERR_RTE;
}
u32_t daq_acquire(u32_t *data, u32_t max)
{
u32_t len, have;
have = XLlFifo_iRxGetLen(&daq_fifo);
if (have < 1)
return 0;
else
if (have < max)
max = have;
for (len = 0; len < max; len++)
data[len] = XLlFifo_RxGetWork(&daq_fifo);
return len;
}
err_t daq_close(void)
{
/* How to stop the FIFO? Do we need to? */
}
That's about it.

How can I retrieve a stack trace of the currently running function's stack?

For troubleshooting reason, I would like to be able to retreive and print the caller stack of the currently running function.
I have tried the following:
/*******************************************************************************
* *
* * xxxTracePrint - stack trace print function
* *
* * RETURNS: OK or ERROR
* */
static void xxxTracePrint
(
INSTR *caller,
int func,
int nargs,
int *args
)
{
char buf [250];
int ix;
int len = 0;
len += sprintf (&buf [len], "%#10x: %#10x (", (int)caller, func);
for (ix = 0; ix < nargs; ix++) {
if (ix != 0)
len += sprintf (&buf [len], ", ");
len += sprintf (&buf [len], "%#x", args [ix]);
}
len += sprintf (&buf [len], ")\n");
printf (buf);
}
/*******************************************************************************
* *
* * xxxTrace - stack trace
* *
* * RETURNS: OK or ERROR
* */
int xxxTrace(int tcb)
{
REG_SET regs;
if (tcb == 0)
return (ERROR);
taskRegsGet (tcb, &regs);
trcStack (&regs, (FUNCPTR) xxxTracePrint, tcb);
return (OK);
}
void DbgTest(void)
{
xxxTrace(taskIdSelf());
}
but I get:
JPAX-DP> DbgTest
trcStack aborted: error in top frame
value = 0 = 0x0
Is this even possible? How can I do this? I saw, for taskRegsGet(), they say:
This routine only works well if the task is known to be in a stable,
non-executing state. Self-examination, for instance, is not advisable,
as results are unpredictable.
But what other method should I apply?
The compiler is diab and cpu arch powerpc
You mentioned that taskRegsGet() mentions that it is not advisable to call from a currently running task. However I have seen someone using taskDelay(1) with the comment 'force context save'. I cannot take credit for it, nor do I know how reliable it is, or what side-effects it may have, but it might help to get correct information about the current task:
taskDelay (1); /* Force context save */
taskRegsGet (0, &regs); /* 0 task-id for myself */
trcStack (&regs, NULL, 0); /* NULL function pointer for default print fcn, 0 task-id for myself */
If your compiler is GCC and the calling conventions of your architecture permit it (x86 being the first that comes to mind), I'd recommend using __builtin_return_address( unsigned int level ). More info can be found here:
https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html.

PortAudio real-time audio processing for continuous input stream

I am using PortAudio to implement a real-time audio processing.
My primary task is to acquire data from mic continuously and provide 100 samples for processing (each FRAME = 100 samples at a time) to some other processing thread.
Here is my callback collecting 100 samples each time on a continuous basis -
static int paStreamCallback( const void* input, void* output,
unsigned long samplesPerFrame,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData ) {
paTestData *data = (paTestData*) userData;
const SAMPLE *readPtr = (const SAMPLE*)input; // Casting input read to valid input type SAMPLE (float)
unsigned long totalSamples = TOTAL_NUM_OF_SAMPLES ; // totalSamples = 100 here
(void) output;
(void) timeInfo;
(void) statusFlags;
static int count = 1;
if(data->sampleIndex < count * samplesPerFrame){
data->recordedSample[data->sampleIndex] = *readPtr;
data->sampleIndex++;
}
else if(data->sampleIndex == count * samplesPerFrame){
processSampleFrame(data->recordedSample);
count++;
finished = paContinue;
}
return finished;
}
My `main function -
int main(void){
// Some Code here
data.sampleIndex = 0;
data.frameIndex = 1;
numBytes = TOTAL_NUM_OF_SAMPLES * sizeof(SAMPLE);
data.recordedSample = (SAMPLE*)malloc(numBytes);
for(i=0; i < TOTAL_NUM_OF_SAMPLES; i++)
data.recordedSample[i] = 0;
// Open Stream Initialization
err = Pa_StartStream(stream);
/* Recording audio here..Speak into the MIC */
printf("\nRecording...\n");
fflush(stdout);
while((err = Pa_IsStreamActive(stream)) == 1)
Pa_Sleep(10);
if(err < 0)
goto done;
err = Pa_CloseStream(stream);
// Some more code here
}
Sending each Frame of 100 samples to processSampleFrame.
void processSampleFrame(SAMPLE *singleFrame){
// Free buffer of this frame
// Processing sample frame here
}
The challenge is that I need to implement a way in which the time processSampleFrame is processing the samples, my callBack should be active and keep acquiring the next Frame of 100 samples and (may be more depending upon the processing time of processSampleFrame).
Also the buffer should able to free itself of the frame so sooner it has passed it to processSampleFrame.
EDIT 2 :
I tried implementing with PaUtilRingBuffer that PortAudio provides. Here is my callback.
printf("Inside callback\n");
paTestData *data = (paTestData*) userData;
ring_buffer_size_t elementsWritable = PaUtil_GetRingBufferWriteAvailable(&data->ringBuffer);
ring_buffer_size_t elementsToWrite = rbs_min(elementsWritable, (ring_buffer_size_t)(samplesPerFrame * numChannels));
const SAMPLE *readPtr = (const SAMPLE*)input;
printf("Sample ReadPtr = %.8f\n", *readPtr);
(void) output; // Preventing unused variable warnings
(void) timeInfo;
(void) statusFlags;
data->frameIndex += PaUtil_WriteRingBuffer(&data->ringBuffer, readPtr, elementsToWrite);
return paContinue;
And main :
int main(void){
paTestData data; // Object of paTestData structure
fflush(stdout);
data.frameIndex = 1;
long numBytes = TOTAL_NUM_OF_SAMPLES * LIMIT;
data.ringBufferData = (SAMPLE*)PaUtil_AllocateMemory(numBytes);
if(PaUtil_InitializeRingBuffer(&data.ringBuffer, sizeof(SAMPLE), ELEMENTS_TO_WRITE, data.ringBufferData) < 0){
printf("Failed to initialise Ring Buffer.\n");
goto done;
err = Pa_StartStream(stream);
/* Recording audio here..Speak into the MIC */
printf("\nRecording samples\n");
fflush(stdout);
while((err = Pa_IsStreamActive(stream)) == 1)
Pa_Sleep(10);
if(err < 0)
goto done;
err = Pa_CloseStream(stream);
// Error Handling here
}
PaTestData Structure :
typedef struct{
SAMPLE *ringBufferData;
PaUtilRingBuffer ringBuffer;
unsigned int frameIndex;
}
paTestData;
I am facing the same issue of seg-fault after successful acquisition for the allocated space because of not being able to use any free in the callback as suggested by PortAudio documentation.
Where can I free the buffer of the allocated frame given to the processing thread. May be a method of obtaining a thread-synchronization can be really useful here. Your help is appreciated.
Example code of processing audio input:
#define FRAME_SIZE 1024
#define CIRCULAR_BUFFER_SIZE (FRAME_SIZE * 4)
float buffer[CIRCULAR_BUFFER_SIZE];
typedef struct {
int read, write;
float vol;
} paData;
static int paStreamCallback(const void* input, void* output,
unsigned long samplesPerFrame,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData) {
paData *data = (paData *)userData;
// Write input buffer to our buffer: (memcpy function is fine, just a for loop of writing data
memcpy(&buffer[write], input, samplesPerFrame); // Assuming samplesPerFrame = FRAME_SIZE
// Increment write/wraparound
write = (write + FRAME_SIZE) % CIRCULAR_BUFFER_SIZE;
// dummy algorithm for processing blocks:
processAlgorithm(&buffer[read]);
// Write output
float *dummy_buffer = &buffer[read]; // just for easy increment
for (int i = 0; i < samplesPerFrame; i++) {
// Mix audio input and processed input; 50% mix
output[i] = input[i] * 0.5 + dummy_buffer[i] * 0.5;
}
// Increment read/wraparound
read = (read + FRAME_SIZE) % CIRCULAR_BUFFER_SIZE;
return paContinue;
}
int main(void) {
// Initialize code here; any memory allocation needs to be done here! default buffers to 0 (silence)
// initialize paData too; write = 0, read = 3072
// read is 3072 because I'm thinking about this like a delay system.
}
Take this with a grain of salt; Obviously better ways of doing this but it can be used as a starting point.

C: playing audio loops in linux

I have a buffer of int16_t with some audio PCM data in it. I need to play the buffer repetitively from a point a to a point b, so that you hear an infinite audio loop.
I found that the easiest way to play sound is by using libao, but I agree with any other method.
This is my code:
int play(int a, int b, char *buf);
int main()
{
int16_t *buf; /*my buffer*/
int a, b;
/* a and b are the indexes of the buffer;
* because libao wants a buffer of char,
* and buf points to of int16_t, I'll pass
* the value a and b multiplied with 2.
*/
[ยทยทยท]
play(2*a, 2*b, (char *) buf);
return 0;
}
int play(int a, int b, char *buf)
{
ao_device *device;
ao_sample_format format;
int default_driver;
/* -- Initialize -- */
fprintf(stderr, "libao example program\n");
ao_initialize();
/* -- Setup for default driver -- */
default_driver = ao_default_driver_id();
memset(&format, 0, sizeof(format));
format.bits = 16;
format.channels = 1;
format.rate = 44100;
format.byte_format = AO_FMT_LITTLE;
/* -- Open driver -- */
device = ao_open_live(default_driver, &format, NULL /* no options */);
if (device == NULL) {
fprintf(stderr, "Error opening device.\n");
exit(1);
}
/* -- Play the infinite loop -- */
for (;;){
ao_play(device, buf+a, b-a+1);
/*buf+a is the start of the loop, b-a+1 the number of byte to play--edited*/
}
/* -- Close and shutdown -- */
ao_close(device);
ao_shutdown();
return 0;
}
The problem is that I hear a period of silence between the end and the start of the loop. Because I'm using this code to testing other code, I absolutely need to know if it could be caused by an incorrect use of libao.
Yes, it absolutely could be caused by incorrect use of libao. Please remove the +1 from the ao_play() call, like so:
ao_play(device, buf+a, b-a);

Resources