As I am new to PortAudio, I tried an example program from the internet. The program is able to record the input of a microphone through a callback function.
I want to get every single sample of the recorded audio represented as a numeric value (e.g. float). I cannot figure out where the recorded data of the mic is stored.
This is the callback function:
static int recordCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
paTestData *data = (paTestData*)userData;
const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void)outputBuffer; /* Prevent unused variable warnings. */
(void)timeInfo;
(void)statusFlags;
(void)userData;
if (framesLeft < framesPerBuffer)
{
framesToCalc = framesLeft;
finished = paComplete;
}
else
{
framesToCalc = framesPerBuffer;
finished = paContinue;
}
if (inputBuffer == NULL)
{
for (i = 0; i<framesToCalc; i++)
{
*wptr++ = SAMPLE_SILENCE; /* left */
if (NUM_CHANNELS == 2) *wptr++ = SAMPLE_SILENCE; /* right */
}
}
else
{
cout << endl << "SAMPLE" << endl;
for (i = 0; i<framesToCalc; i++)
{
*wptr++ = *rptr++; /* left */
//cout << rptr<<endl;
if (NUM_CHANNELS == 2) *wptr++ = *rptr++; /* right */
}
}
data->frameIndex += framesToCalc;
return finished;
}
The audio input stream is initialized here:
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);
The incoming data is stored in the inputBuffer pointer of the callback. Depending on the inputParameters used when calling Pa_OpenStream one should be able to cast the input buffer into an array of corresponding data type (e.g. if paFloat32 is used as sample format, then the buffer can be interpreted as const float*).
It is recommended that in the callback function you copy the incoming data into another buffer for further processing (outside of the callback).
Multiple channels samples are interleaved in the buffer. For example for stereo input inputBuffer[0] is the first sample for the left channer, inputBuffr[1] is the first sample to the right channel, inputBuffer[2] is the 2nd sample for the left channel, etc. Total number of samples is provided via framesPerBuffer argument of the callback.
Related
I'm trying to make a C++ application to transmit audio via a VoIP protocol between 2 clients (using UDP).
I'm working with Portaudio C library and I have issues to encapsulate this lib. In order to send the recorded audio to another client, I'd like to get sound samples as it is recorded (real time).
For the moment I can only record sound, and then, play what I recorded.
I'm not comfortable at all with this library and any help would be appreciated.
Here's what I've done so far.
Audio.cpp -> Callback methods:
static int PaRecordCallback(const void *input, void *output, unsigned long frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
Audio *audio = reinterpret_cast<Audio *>(userData);
return audio->RecordCallback(input, output, frameCount, timeInfo, statusFlags);
}
static int PaPlayCallback(const void *input, void *output, unsigned long frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
Audio *audio = reinterpret_cast<Audio *>(userData);
return audio->PlayCallback(input, output, frameCount, timeInfo, statusFlags);
}
int Audio::RecordCallback(const void *input, void *output, unsigned long &frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags &statusFlags)
{
std::cout << "Frame index:\t\t" << _recordedFrameIndex << std::endl << "Max frame index:\t" << _maxFrameIndex << std::endl << "--------------" << std::endl;
const SAMPLE *rptr = static_cast<const SAMPLE *>(input);
SAMPLE *wptr = &_recordedSamples[_recordedFrameIndex * NUM_CHANNELS];
unsigned long framesLeft = _maxFrameIndex - _recordedFrameIndex;
unsigned long framesToCalc;
int finished;
if (framesLeft < frameCount) {
framesToCalc = framesLeft;
finished = paComplete;
} else {
framesToCalc = frameCount;
finished = paContinue;
}
for (unsigned long i = 0; i < framesToCalc; i++) {
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
*wptr++ = *rptr++;
}
_recordedFrameIndex += framesToCalc;
return finished;
}
int Audio::PlayCallback(const void *input, void *output, unsigned long &frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags &statusFlags)
{
SAMPLE *rptr = &_recordedSamples[_playedFrameIndex * NUM_CHANNELS];
SAMPLE *wptr = static_cast<SAMPLE *>(output);
unsigned long framesLeft = _maxFrameIndex - _playedFrameIndex;
unsigned int i;
int finished;
if (framesLeft < frameCount) {
for (i = 0; i < framesLeft; i++) {
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
*wptr++ = *rptr++;
}
for (; i < frameCount; i++) {
*wptr++ = 0;
if (NUM_CHANNELS == 2)
*wptr++ = 0;
}
_playedFrameIndex += framesLeft;
finished = paComplete;
} else {
for (i = 0; i < frameCount; i++) {
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
*wptr++ = *rptr++;
}
_playedFrameIndex += frameCount;
finished = paContinue;
}
return finished;
}
Audio.cpp -> Record and Play methods:
void Audio::Record()
{
if (!_recordStream) {
OpenRecordStream();
_recordedFrameIndex = 0;
_err = Pa_StartStream(_recordStream);
if (_err != paNoError)
AudioError("Audio::Record -> Pa_StartStream()");
std::cout << "Audio record stream started." << std::endl;
std::cout << "Recording ..." << std::endl;
_recording = true;
fflush(stdout);
} else if (_recording)
Pa_Sleep(1000);
}
void Audio::Play()
{
if (!_playStream) {
OpenPlayStream();
_playedFrameIndex = 0;
_err = Pa_StartStream(_playStream);
if (_err != paNoError)
AudioError("Audio::Play -> Pa_StartStream()");
std::cout << "Audio play stream started." << std::endl;
std::cout << "Playing ..." << std::endl;
_playing = true;
fflush(stdout);
} else if (_playing)
Pa_Sleep(500);
}
Audio.hpp -> Class audio:
#include <portaudio.h>
typedef int16_t SAMPLE;
#define PA_SAMPLE_TYPE paInt16
#define PRINTF_S_FORMAT "%.8f"
#define SAMPLE_RATE 44100
#define SAMPLE_SILENCE 0.0f
#define FRAMES_PER_BUFFER 1
#define NUM_SECONDS 5
#define NUM_CHANNELS 2
#define DITHER_FLAG 0
#define WRITE_TO_FILE 0
#define SAMPLE_SIZE NUM_SECONDS * SAMPLE_RATE * NUM_CHANNELS
class Audio
{
public:
Audio();
~Audio();
void Record();
void Play();
void OpenRecordStream();
void OpenPlayStream();
void CloseRecordStream();
void ClosePlayStream();
const bool &isRecording() const;
const bool &isPlaying() const;
const PaStream *GetRecordStream() const;
const PaStream *GetPlayStream() const;
void GetSamples(SAMPLE *);
void SetSamples(SAMPLE *);
int RecordCallback(const void *, void *, unsigned long &, \
const PaStreamCallbackTimeInfo *, PaStreamCallbackFlags &);
int PlayCallback(const void *, void *, unsigned long &, \
const PaStreamCallbackTimeInfo *, PaStreamCallbackFlags &);
bool _recording;
bool _playing;
protected:
private:
// Functions:
void AudioError(const std::string &);
// Variables:
PaError _err;
PaStream *_playStream;
PaStream *_recordStream;
SAMPLE *_samplesToPlay;
SAMPLE *_recordedSamples;
unsigned long _recordedFrameIndex;
unsigned long _playedFrameIndex;
unsigned long _maxFrameIndex;
PaStreamParameters _inputParameters;
PaStreamParameters _outputParameters;
};
I apologize if it's very long, but I want you to have all the necessary informations to understand my problem.
I don't ask questions often, so I really need some help here.
Thank you.
You need to use a full duplex callback to record and play in real time so you catch the input buffer in chunks for your recordings (when you want to record) and send the recordings (or even the incoming sound) to the output buffer, also in chunks.
The chunksize normally would be from 64 to 4096 frames, and each frame normally would contain 2 samples (channel L and channel R)
The full duplex callback is a kind of circular buffer that brings you x frames when the input buffer is filled by the ADC and where you fill the output buffer with x frames for being ready for when the DAC asks for it.
I'm trying to receive audio from the soundcard via RtAudio Api. It has a callback function that gets called once the audio has enough bytes received and the user can then copy the data to a custom object. This custom object can be sent to the callback via pointer. My class that encapsulates RtAudio looks like this:
class Audio {
private:
AudioConfiguration config;
AudioData data;
RtAudio* rt;
// the void* d is the casted AudioData-object
static int input( void*, void* inputBuffer, unsigned int bufferSize, double, RtAudioStreamStatus status, void* d );
void openStream( RtAudio::StreamParameters& params, RtAudio::StreamOptions& options, AudioConfiguration& config );
bool isDeviceOk();
public:
// ctor & dtor
Audio( AudioConfiguration& c );
~Audio();
// copy ctor & assignment
Audio( const Audio& other );
Audio& operator=( const Audio& a );
// move ctor & assignment
Audio( Audio&& other );
Audio& operator=( Audio&& a);
AudioConfiguration& getConfiguration();
AudioData& getData();
void start();
void stop();
};
This is the implementation of the static function that gets called from inside the audio thread
int Audio::input( void*, void* inputBuffer, unsigned int bufferSize, double, RtAudioStreamStatus status, void* d ){
if( status == RTAUDIO_INPUT_OVERFLOW )
std::cout << "Audio Thread: Input overflow detected." << std::endl;
//std::cout << "Audio Thread: Received input from soundcard" << std::endl;
float* in = static_cast<float*>( inputBuffer );
AudioData* data = static_cast<AudioData*>( d );
boost::lock_guard<boost::mutex> lock{data->getMutex()};
unsigned int i = 0;
while( i < bufferSize ){
data->getBuffer().push_back( *in );
in++;
i++;
}
return 0;
}
The custom object that I share between the threads is of the class AudioData, which looks like this:
class AudioData {
private:
boost::circular_buffer<float> buffer;
boost::mutex mutex;
public:
AudioData();
~AudioData();
boost::circular_buffer<float>& getBuffer();
boost::mutex& getMutex();
};
The audio-object gets embedded in a Recorder-Object, which then reads the buffer in the AudioData member variable of Audio.
typedef boost::container::vector<boost::circular_buffer<float>> Buffers;
class Recorder {
private:
Audio audio;
Buffers buffers;
/*
* Reads n samples per channel from audio buffers to NUM_CHANNELS distinct buffers
* When done, it returns the number of samples read into each channel
* Why? We want to keep audio buffer to be locked as minimal time as possible
*/
unsigned int read( unsigned int samples );
/*
* Deletes n samples from channel buffer
* When done, it returns the number of samples deleted
*/
unsigned int deleteBegin( unsigned int ch, unsigned int samples );
/*
* Detects the trigger on TRIGGER_CHANNEL
* Returns true, if trigger was found and its position
*/
bool detectTrigger( unsigned int* pos );
public:
Recorder( AudioConfiguration& c );
Recorder( Audio&& a );
boost::container::vector<float> record( RecorderConfiguration& config );
};
The function record(..) looks like this:
boost::container::vector<float> Recorder::record( RecorderConfiguration& config ){
AudioConfiguration audioConfig = audio.getConfiguration();
unsigned int length = ( audioConfig.fs * config.length ) / 1000;
boost::container::vector<float> recording;
recording.resize( length );
// Tell Audio to start recording
audio.start();
// State
unsigned int times = 0; // Count averages
unsigned int left = length; // Samples left on current average
bool triggered = false; // Trigger has been read
while( true ){
// Read into local buffer
unsigned int samplesToRead = length / 10;
unsigned int samplesRead = read( samplesToRead );
// if not enough samples, wait for more
if( samplesRead < 100 )
continue;
// Actual logic
unsigned int triggerPos = 0;
if( !triggered && detectTrigger( &triggerPos ) ){
std::cout << "Recorder: Trigger detected." << std::endl;
triggered = true;
// delete everything that comes before trigger on both channels
for( unsigned int i = 0 ; i < NUM_CHANNELS ; i++ ){
deleteBegin( i, triggerPos - 1);
}
}
// Copy from buffer if trigger was found beforehand
if( triggered ){
boost::circular_buffer<float>& buffer = buffers[ EEG_CHANNEL ];
unsigned int samplesToCopy = buffer.size();
if( samplesToCopy > left )
samplesToCopy = left;
for( unsigned int i = 0 ; i < samplesToCopy ; i++ ){
recording[ length - left ] = recording[ left - left ] + buffer.front();
buffer.pop_front();
left--;
}
}
// current average done
if( left <= 0 ){
// increment times
times++;
// check if recording is done
if( times >= config.times )
break;
// if not
else {
triggered = false;
left = length;
}
}
}
// Stop receiving input from audio
audio.stop();
return recording;
}
I read that the heap is the place to hold data that is shared between threads, but in the example by rtaudio they use a global variable that gets allocated on the stack for pushing the data to Link. So I am a little bit confused. Help would be gladly accepted!
Edit: When i debug my app. I can see that the input function of the audio-thread gets called and it writes to the buffer. Also the record function works as expected. Only the buffer (of AudioData) does not seem to have any data in it...
Edit2: Here is the code where I register the callback in the rtaudio api.
void Audio::openStream( RtAudio::StreamParameters& params, RtAudio::StreamOptions& options, AudioConfiguration& config ){
try {
rt->openStream( nullptr, ¶ms, RTAUDIO_FLOAT32, config.fs, &config.bufferSize, &this->input, &data, &options, nullptr );
} catch( RtAudioError& e ){
std::cout << "Audio::openStream(): Cannot open stream." << std::endl;
throw e;
}
}
I am writing a C++ code and I am struggling with something rather simple:
I have declared an array
uint8_t *received_data as global variable in my code.
Then I allocate its memory inside a function:
void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) {
if(params->type == 3){
for(int i = 0; i < params->advertisingDataLen; i++){
if(params->advertisingData[i] == 0x16){
if(params->advertisingData[i+1] == 0x34 && params->advertisingData[i+2] == 0x23){
received_data_size = params->advertisingDataLen - (i + 3);
received_data = new uint8_t[received_data_size];
for(int index = i+3; index < params->advertisingDataLen; index++){
received_data[index] = params->advertisingData[index];
//printf("%02x ", received_data[index]);//params->advertisingData[index]);
//printf("\r\n");
}
}
}
}
}
}
Note that the commented printf's are printing the data I receive correctly.
But then in my main when I try the same printf I receive garbage most of the times and some times I receive the last three elements of the array in the first three places and then garbage.
My main is:
int main(void)
{
BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
ble.init(bleInitComplete);
bool state = true;
while(true){
ble.waitForEvent();
measurement[2]++;
printf("In the loop \n");
for(int i = 0; i < received_data_size; i++){
printf("%02x ", received_data[i]);//params->advertisingData[index]);
printf("\r\n");
}
delete[] received_data;
}
}
The whole code as of now is:
#include "mbed.h"
#include "ble/BLE.h"
/* Optional: Device Name, add for human read-ability */
const static char DEVICE_NAME[] = "G4";
uint16_t uuid16_list[] = {0x2334};
/* You have up to 26 bytes of advertising data to use. */
const static uint8_t AdvData[] = {0x01,0x02,0x03,0x04,0x05}; /* Example of hex data */
uint8_t meas = 0;
uint8_t received_data_size;
static uint8_t *received_data;
uint8_t measurement[] = {0x34,0x23, meas};
/* Optional: Restart advertising when peer disconnects */
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
BLE::Instance().gap().startAdvertising();
}
/**
* This function is called when the ble initialization process has failed
*/
void onBleInitError(BLE &ble, ble_error_t error)
{
/* Avoid compiler warnings */
(void) ble;
(void) error;
/* Initialization error handling should go here */
}
void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) {
if(params->type == 3){
for(int i = 0; i < params->advertisingDataLen; i++){
if(params->advertisingData[i] == 0x16){
if(params->advertisingData[i+1] == 0x34 && params->advertisingData[i+2] == 0x23){
received_data_size = params->advertisingDataLen - (i + 3);
received_data = new uint8_t[received_data_size];
for(int index = i+3; index < params->advertisingDataLen; index++){
received_data[index] = params->advertisingData[index];
//printf("%02x ", received_data[index]);//params->advertisingData[index]);
//printf("\r\n");
}
}
}
}
}
}
/**
* Callback triggered when the ble initialization process has finished
*/
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
BLE& ble = params->ble;
ble_error_t error = params->error;
if (error != BLE_ERROR_NONE) {
/* In case of error, forward the error handling to onBleInitError */
onBleInitError(ble, error);
return;
}
/* Ensure that it is the default instance of BLE */
if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
return;
}
/* Set device name characteristic data */
ble.gap().setDeviceName((const uint8_t *) DEVICE_NAME);
/* Optional: add callback for disconnection */
ble.gap().onDisconnection(disconnectionCallback);
/* Sacrifice 3B of 31B to Advertising Flags */
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE );
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
/* Sacrifice 2B of 31B to AdvType overhead, rest goes to AdvData array you define */
//ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData));
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, measurement, sizeof(measurement));
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
/* Optional: Add name to device */
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
/* Set advertising interval. Longer interval == longer battery life */
ble.gap().setAdvertisingInterval(500); /* 100ms */
/* Start advertising */
//ble.gap().startAdvertising();
/*Start Scanning*/
ble.gap().setScanParams(500 /* scan interval */, 200 /* scan window */);
ble.gap().startScan(advertisementCallback);
}
int main(void)
{
BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
ble.init(bleInitComplete);
bool state = true;
while(true){
ble.waitForEvent();
measurement[2]++;
printf("In the loop \n");
for(int i = 0; i < received_data_size; i++){
printf("%02x ", received_data[i]);//params->advertisingData[index]);
printf("\r\n");
}
delete[] received_data;
}
}
BLE stands for bluetooth low energy. The code is based on examples found at mbed.org
I guess I am missing something, but I am not sure what exactly. Thank you very much for your help.
First of all, without taking a further look at the rest of the code, the first three bytes of your global received_data will never be written. Take a look into this particular part of your advertisementCallback():
for(int index = i+3; index < params->advertisingDataLen; index++){
received_data[index] = params->advertisingData[index];
So the index used for writing to received_data always starts at an offset of 3 - never less - , while it should start at 0. Add a dedicated index variable for this purpose. In your main(), you created a loop starting at 0:
for(int i = 0; i < received_data_size; i++){
printf("%02x ", received_data[i]);
Therefore the first three bytes of your debug output will always contain random data.
I am struggling with outputting user selected audio tones with portaudio. I have based a function on the paex_sine.cpp and modified it somewhat based on this post multi-audio-tones-to-sound-card-using-portaudio
I am able to generate a sine wave on the fly by placing the sin(n * FREQ * 2 * PI / SAMPLE_RATE) calculation in paCallBackMethod() but the frequency isn't correct, and doesn't change based on user input.
Here is my function code.
void CDeepWaveDlg::generateSound(float freq)
{
pitch = tone;
offset = freq;
class Sine
{
public:
Sine() : stream(0), left_phase(0), right_phase(0)
{
}
bool open(PaDeviceIndex index)
{
PaStreamParameters outputParameters;
outputParameters.device = index;
if (outputParameters.device == paNoDevice) {
return false;
}
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
PaError err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
&Sine::paCallback,
this /* Using 'this' for userData so we can cast to Sine* in paCallback method */
);
if (err != paNoError)
{
/* Failed to open stream to device !!! */
return false;
}
err = Pa_SetStreamFinishedCallback(stream, &Sine::paStreamFinished);
if (err != paNoError)
{
Pa_CloseStream(stream);
stream = 0;
return false;
}
return true;
}
bool close()
{
if (stream == 0)
return false;
PaError err = Pa_CloseStream(stream);
stream = 0;
return (err == paNoError);
}
bool start()
{
if (stream == 0)
return false;
PaError err = Pa_StartStream(stream);
return (err == paNoError);
}
bool stop()
{
if (stream == 0)
return false;
PaError err = Pa_StopStream(stream);
return (err == paNoError);
}
private:
/* The instance callback, where we have access to every method/variable in object of class Sine */
int paCallbackMethod(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags)
{
float *out = (float*)outputBuffer;
unsigned long i;
(void)timeInfo; /* Prevent unused variable warnings. */
(void)statusFlags;
(void)inputBuffer;
for (i = 0; i<framesPerBuffer; i++)
{
float v = sin(i * pitch * 2.0 * M_PI /(float)SAMPLE_RATE);
float v2 = sin(i * (pitch + (float)offset) * 2.0 * M_PI /(float)SAMPLE_RATE);
*out++ = v;
*out++ = v2;
}
return paContinue;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may 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 paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
/* Here we cast userData to Sine* type so we can call the instance method paCallbackMethod, we can do that since
we called Pa_OpenStream with 'this' for userData */
return ((Sine*)userData)->paCallbackMethod(inputBuffer, outputBuffer,
framesPerBuffer,
timeInfo,
statusFlags);
}
void paStreamFinishedMethod()
{
printf("Stream Completed: %s\n", message);
}
/*
* This routine is called by portaudio when playback is done.
*/
static void paStreamFinished(void* userData)
{
return ((Sine*)userData)->paStreamFinishedMethod();
}
PaStream *stream;
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
char message[20];
};
PaError err;
Sine sine;
err = Pa_Initialize();
if (err != paNoError) goto error;
if (sine.open(Pa_GetDefaultOutputDevice()))
{
if (sine.start())
{
Pa_Sleep(NUM_SECONDS * 1000);
sine.stop();
}
sine.close();
}
Pa_Terminate();
The only way I am able to get close to the desired pitch is to increase frames per buffer, which is currently 64, but I still run into the problem of not changing frequency based on user selection.
This problem is definitely beyond my skill level, but I am hoping someone can help me understand what's going on here.Thanks in advance for any help.
I am working on firmware of an ATMEL sensor board (accelerometer and gyro)and trying to read the data in a platform in Ubuntu.
Currently the firmware is like this:
Ubuntu sends a character "D" and the firmware in response sends back 20 bytes of data that ends in "\n" then ubuntu uses serialport_read_until(fd, buff, '\n') and assumes that buff[0] is byte zero and so on.The frequency of acquisition is 200hz.
BUT using this method sometimes I receive corrupted values and it is not working well. Also there are many "Unable to write on serial port" error in ubuntu.
I have found an example code from ATMEL for the firmware and there the data is sent in different packages and continuously (without waiting for the computer to ask for it) the structure is like this:
void adv_data_send_3(uint8_t stream_num, uint32_t timestamp,
int32_t value0, int32_t value1, int32_t value2)
{
/* Define packet format with 3 data fields */
struct {
adv_data_start_t start; /* Starting fields of packet */
adv_data_field_t field [3]; /* 3 data fields */
adv_data_end_t end; /* Ending fields of packet */
} packet;
/* Construct packet */
packet.start.header1 = ADV_PKT_HEADER_1;
packet.start.header2 = ADV_PKT_HEADER_2;
packet.start.length = cpu_to_le16(sizeof(packet));
packet.start.type = ADV_PKT_DATA;
packet.start.stream_num = stream_num;
packet.start.time_stamp = cpu_to_le32(timestamp);
packet.field[0].value = cpu_to_le32(value0);
packet.field[1].value = cpu_to_le32(value1);
packet.field[2].value = cpu_to_le32(value2);
packet.end.crc = 0x00; /* Not used */
packet.end.mark = ADV_PKT_END;
/* Write packet */
adv_write_buf((uint8_t *)&packet, sizeof(packet));
}
but I don't know how I can continuously read the data that is sent in a structure like above.
Sorry if it is a trivial question. I am not a programmer but I need to solve this and I could not find a solution (that I can understand!) after searching for a couple of days.
The reading function I use in linux:
int serialport_read_until(int fd, unsigned char* buf, char until){
char b[1];
int i=0;
do {
int n = read(fd, b, 1); // read a char at a time
if( n==-1) return -1; // couldn't read
if( n==0 ) {
usleep( 1 * 1000 ); // wait 1 msec try again
continue;
}
buf[i] = b[0]; i++;
} while( b[0] != until );
buf[i] = 0; // null terminate the string
return 0;}
The new Reading Func:
// Read the header part
adv_data_start_t start;
serial_read_buf(fd, reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
serial_read_buf(fd, data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_field_t* fields = reinterpret_cast<adv_data_field_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
std::cout << "Field #" << (i + 1) << " = " << fields[i].value << '\n';
The data packets that are sent from the firmware:
typedef struct {
uint8_t header1; // header bytes - always 0xFF5A
uint8_t header2; // header bytes - always 0xFF5A
uint16_t length; // packet length (bytes)
uint32_t time_stamp; // time stamp (tick count)
} adv_data_start_t;
typedef struct {
int32_t value; // data field value (3 VALUES)
} adv_data_field_t;
typedef struct {
uint8_t crc; // 8-bit checksum
uint8_t mark; // 1-byte end-of-packet marker
uint16_t mark2; // 2-byte end-of-packet marker (Added to avoid data structure alignment problem)
} adv_data_end_t;
Well you have the length of the packet in the packet "header", so read the header fields (the start structure) in one read, and in a second read you read the data and the end.
If the start and end parts are the same for all packets (which I guess they are), you can easily figure out the amount of data fields after the second read.
Something like this:
// Read the header part
adv_data_start_t start;
adv_read_buf(reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
adv_read_buf(data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_end_t* fields = reinterpret_cast<adv_data_end_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
std::cout << "Field #" << (i + 1) << " = " << fields[i] << '\n';
Possible read_buf implementation:
// Read `bufsize` bytes into `buffer` from a file descriptor
// Will block until `bufsize` bytes has been read
// Returns -1 on error, or `bufsize` on success
int serial_read_buf(int fd, uint8_t* buffer, const size_t bufsize)
{
uint8_t* current = buffer;
size_t remaining = bufsize
while (remaining > 0)
{
ssize_t ret = read(fd, current, remaining);
if (ret == -1)
return -1; // Error
else if (ret == 0)
{
// Note: For some descriptors, this means end-of-file or
// connection closed.
usleep(1000);
}
else
{
current += ret; // Advance read-point in buffer
remaining -= ret; // Less data remaining to read
}
}
return bufsize;
}