Play one sound after another with SDL_mixer? - c++

I have 4 sounds. I need play sound 1, when it finishes, automatically play sound 2; when sound 2 finishes, automatically play sound 3. Soun 3 finishes, play sound 4.... I'm using SDL Mixer 2.0, no SDL Sound...Is there a way?
int main() {
int frequencia = 22050;
Uint16 formato = AUDIO_S16SYS;
int canal = 2; // 1 mono; 2 = stereo;
int buffer = 4096;
Mix_OpenAudio(frequencia, formato, canal, buffer);
Mix_Chunk* sound_1;
Mix_Chunk* sound_2;
Mix_Chunk* sound_3;
Mix_Chunk* sound_4;
som_1 = Mix_LoadWAV("D:\\sound1.wav");
som_2 = Mix_LoadWAV("D:\\sound1.wav");
som_3 = Mix_LoadWAV("D:\\sound1.wav");
som_4 = Mix_LoadWAV("D:\\sound1.wav");
Mix_PlayChannel(-1, sound_1, 0);
Mix_PlayChannel(1, sound_2, 0);
Mix_PlayChannel(2, sound_3, 0);
Mix_PlayChannel(3, sound_4, 0);
return 0;
}

Check in a loop whether the channel is still playing using Mix_Playing(), and add a delay using SDL_Delay() to prevent the loop from consuming all available CPU time.
(In this example, I changed your first call to Mix_PlayChannel() from -1 to 1.)
Mix_PlayChannel(1, sound_1, 0);
while (Mix_Playing(1) != 0) {
SDL_Delay(200); // wait 200 milliseconds
}
Mix_PlayChannel(2, sound_2, 0);
while (Mix_Playing(2) != 0) {
SDL_Delay(200); // wait 200 milliseconds
}
// etc.
You should probably wrap that into a function instead so that you don't repeat what is basically the same code over and over again:
void PlayAndWait(int channel, Mix_Chunk* chunk, int loops)
{
channel = Mix_PlayChannel(channel, chunk, loops);
if (channel < 0) {
return; // error
}
while (Mix_Playing(channel) != 0) {
SDL_Delay(200);
}
}
// ...
PlayAndWait(-1, sound_1, 0);
PlayAndWait(1, sound_2, 0);
PlayAndWait(2, sound_3, 0);
PlayAndWait(3, sound_3, 0);

Related

SDL Lag / Super slow (SDL 2.0.14)

i have a game loop which measures the ms it takes to finish.
before entering the loop i call a "Init" function which looks like this:
void screen::Init()
{
if(SDL_Init(SDL_INIT_EVERYTHING) == 0)
{
m_window = SDL_CreateWindow("TITLE", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH*SIZE, HEIGHT*SIZE, NULL);
m_renderer = SDL_CreateRenderer(m_window, -1, NULL);
if(m_renderer)
SDL_SetRenderDrawColor(m_renderer, 0,0,0,0);
m_Texture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
}
}
The thing is that when i call the function one execution of the loop takes 70 to 90 ms!
if i leave the "init" function call out (so no window shown) its only 2-4 ms.
The loop does nothing other than calling one function which looks like the following:
void screen::handleEvents()
{
SDL_PollEvent(&m_event);
switch(m_event.type)
{
case SDL_QUIT:
screen->Clean();
break;
default:
break;
}
}
leaving the handleEvents function out results in 0-2ms, but the window crashes the moment i click on it.
not sure what im doing wrong but im pretty sure it shouldnt slow down to ~10 FPS lol
Here is the rest of the code:
#define SIZE 5
#define WIDTH 150
#define HEIGHT 150
int main(int argc, char* argv[])
{
screen* screen = new class screen;
screen->Init();
while(screen->running)
{
screen->execute();
}
return 0;
}
void screen::execute()
{
if(currline == 1 && Time == 0)
start = SDL_GetTicks();
if(Time > 0)
Time--;
else
counter();
handleEvents();
if(currline == 151 && Time == 0)
{
end = SDL_GetTicks();
printf("s: %8d e: %8d r: %d\n", start, end, end-start);
}
}
void screen::counter()
{
if(currline > 150)
currline = 255;
currline++;
Time = 500;
};
okay, found out why it was lagging:
i was calling handleevent everytime it executed a loop (which is a couple thousand times per second) instead of doing it inside this if statement:
if(currline == 151 && Time == 0)
{
end = SDL_GetTicks();
printf("s: %8d e: %8d r: %d\n", start, end, end-start);
}
the if statement is there to "restrict" the whole thing to a certain framerate :)
usually you would do a sleep at the end of the whole loop to get your 60 executions per second, but since i did it inside this if statment i had to put the function-call in there.

Xcode app for macOS. This is how I setup to get audio from usb mic input. Worked a year ago, now doesn't. Why

Here is my audio init code. My app responds when queue buffers are ready, but all data in buffer is zero. Checking sound in system preferences shows that USB Audio CODEC in sound input dialog is active. AudioInit() is called right after app launches.
{
#pragma mark user data struct
typedef struct MyRecorder
{
AudioFileID recordFile;
SInt64 recordPacket;
Float32 *pSampledData;
MorseDecode *pMorseDecoder;
} MyRecorder;
#pragma mark utility functions
void CheckError(OSStatus error, const char *operation)
{
if(error == noErr) return;
char errorString[20];
// see if it appears to be a 4 char code
*(UInt32*)(errorString + 1) = CFSwapInt32HostToBig(error);
if (isprint(errorString[1]) && isprint(errorString[2]) &&
isprint(errorString[3]) && isprint(errorString[4]))
{
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
}
else
{
sprintf(errorString, "%d", (int)error);
}
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
}
OSStatus MyGetDefaultInputDeviceSampleRate(Float64 *outSampleRate)
{
OSStatus error;
AudioDeviceID deviceID = 0;
AudioObjectPropertyAddress propertyAddress;
UInt32 propertySize;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = 0;
propertySize = sizeof(AudioDeviceID);
error = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&propertyAddress,
0,
NULL,
&propertySize,
&deviceID);
if(error)
return error;
propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = 0;
propertySize = sizeof(Float64);
error = AudioObjectGetPropertyData(deviceID,
&propertyAddress,
0,
NULL,
&propertySize,
outSampleRate);
return error;
}
static int MyComputeRecordBufferSize(const AudioStreamBasicDescription *format,
AudioQueueRef queue,
float seconds)
{
int packets, frames, bytes;
frames = (int)ceil(seconds * format->mSampleRate);
if(format->mBytesPerFrame > 0)
{
bytes = frames * format->mBytesPerFrame;
}
else
{
UInt32 maxPacketSize;
if(format->mBytesPerPacket > 0)
{
// constant packet size
maxPacketSize = format->mBytesPerPacket;
}
else
{
// get the largest single packet size possible
UInt32 propertySize = sizeof(maxPacketSize);
CheckError(AudioQueueGetProperty(queue,
kAudioConverterPropertyMaximumOutputPacketSize,
&maxPacketSize,
&propertySize),
"Couldn't get queues max output packet size");
}
if(format->mFramesPerPacket > 0)
packets = frames / format->mFramesPerPacket;
else
// worst case scenario: 1 frame in a packet
packets = frames;
// sanity check
if(packets == 0)
packets = 1;
bytes = packets * maxPacketSize;
}
return bytes;
}
extern void bridgeToMainThread(MorseDecode *pDecode);
static int callBacks = 0;
// ---------------------------------------------
static void MyAQInputCallback(void *inUserData,
AudioQueueRef inQueue,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc)
{
MyRecorder *recorder = (MyRecorder*)inUserData;
Float32 *pAudioData = (Float32*)(inBuffer->mAudioData);
recorder->pMorseDecoder->pBuffer = pAudioData;
recorder->pMorseDecoder->bufferSize = inNumPackets;
bridgeToMainThread(recorder->pMorseDecoder);
CheckError(AudioQueueEnqueueBuffer(inQueue,
inBuffer,
0,
NULL),
"AudioQueueEnqueueBuffer failed");
printf("packets = %ld, bytes = %ld\n",(long)inNumPackets,(long)inBuffer->mAudioDataByteSize);
callBacks++;
//printf("\ncallBacks = %d\n",callBacks);
//if(callBacks == 0)
//audioStop();
}
static AudioQueueRef queue = {0};
static MyRecorder recorder = {0};
static AudioStreamBasicDescription recordFormat;
void audioInit()
{
// set up format
memset(&recordFormat,0,sizeof(recordFormat));
recordFormat.mFormatID = kAudioFormatLinearPCM;
recordFormat.mChannelsPerFrame = 2;
recordFormat.mBitsPerChannel = 32;
recordFormat.mBytesPerPacket = recordFormat.mBytesPerFrame = recordFormat.mChannelsPerFrame * sizeof(Float32);
recordFormat.mFramesPerPacket = 1;
//recordFormat.mFormatFlags = kAudioFormatFlagsCanonical;
recordFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
MyGetDefaultInputDeviceSampleRate(&recordFormat.mSampleRate);
UInt32 propSize = sizeof(recordFormat);
CheckError(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&recordFormat),
"AudioFormatProperty failed");
recorder.pMorseDecoder = MorseDecode::pInstance();
recorder.pMorseDecoder->m_sampleRate = recordFormat.mSampleRate;
// recorder.pMorseDecoder->setCircularBuffer();
//set up queue
CheckError(AudioQueueNewInput(&recordFormat,
MyAQInputCallback,
&recorder,
NULL,
kCFRunLoopCommonModes,
0,
&queue),
"AudioQueueNewInput failed");
UInt32 size = sizeof(recordFormat);
CheckError(AudioQueueGetProperty(queue,
kAudioConverterCurrentOutputStreamDescription,
&recordFormat,
&size), "Couldn't get queue's format");
// set up buffers and enqueue
const int kNumberRecordBuffers = 3;
int bufferByteSize = MyComputeRecordBufferSize(&recordFormat, queue, AUDIO_BUFFER_DURATION);
for(int bufferIndex = 0; bufferIndex < kNumberRecordBuffers; bufferIndex++)
{
AudioQueueBufferRef buffer;
CheckError(AudioQueueAllocateBuffer(queue,
bufferByteSize,
&buffer),
"AudioQueueAllocateBuffer failed");
CheckError(AudioQueueEnqueueBuffer(queue,
buffer,
0,
NULL),
"AudioQueueEnqueueBuffer failed");
}
}
void audioRun()
{
CheckError(AudioQueueStart(queue, NULL), "AudioQueueStart failed");
}
void audioStop()
{
CheckError(AudioQueuePause(queue), "AudioQueuePause failed");
}
}
This sounds like the new macOS 'microphone privacy' setting, which, if set to 'no access' for your app, will cause precisely this behaviour. So:
Open the System Preferences pane.
Click on 'Security and Privacy'.
Select the Privacy tab.
Click on 'Microphone' in the left-hand pane.
Locate your app in the right-hand pane and tick the checkbox next to it.
Then restart your app and test it.
Tedious, no?
Edit: As stated in the comments, you can't directly request microphone access, but you can detect whether it has been granted to your app or not by calling [AVCaptureDevice authorizationStatusForMediaType: AVMediaTypeAudio].

FFmpeg - resampled audio with much noise

I'm not familiar with auido resampling. I tried to resample auido streams from two videos. The first one's output was close to the original but with noise, the other one was almost full of noise.
Information for the first one
128 kb/s, 48.0kHz, 2 channels, AACLC
Information for the second one
384 kb/s, 48.0 kHz, 6channels, AACLC
I found that, when I set the sample size 16, the frist one worked quit good but still with noise. The other one worked too bad but still had sound. What and how to determine the output sample size? Although I used channels * av_get_bytes_per_sample((AVSampleFormat)output_fmt) as the output sample size because I wanted it to be the same as the original, it had no sound at all.
MyResampling.cpp
bool MyResample::open(AVCodecParameters* par) {
if (!par) {
std::cout << "par is null" << std::endl;
return false;
}
audio_context = swr_alloc_set_opts(
audio_context, av_get_default_channel_layout(2), (AVSampleFormat)output_fmt,
par->sample_rate, av_get_default_channel_layout(par->channels), (AVSampleFormat)par->format, par->sample_rate,
0, 0);
avcodec_parameters_free(&par);
int ret = swr_init(audio_context);
if (ret != 0) {
std::cout << "failed to open audio codec" << std::endl;
}
return true;
}
int MyResample::resample(AVFrame* frame, unsigned char* output)
{
if (!frame)
return 0;
if (!output)
av_frame_free(&frame);
uint8_t* data[2] = { 0 };
data[0] = output;
int ret = swr_convert(audio_context, data, frame->nb_samples, (const uint8_t**)frame->data, frame->nb_samples);
//int size = ret * frame->channels * av_get_bytes_per_sample((AVSampleFormat)output_fmt);
int size = av_samples_get_buffer_size(nullptr, frame->channels, frame->nb_samples, (AVSampleFormat)output_fmt, 1);
if (ret < 0)
return ret;
return size;
}
MyAudioPlayer.cpp
bool open()
{
close();
QAudioFormat fmt;
fmt.setSampleRate(sample_rate); // from audioStream->codecpar->sample_rate
fmt.setSampleSize(16); //
fmt.setChannelCount(channels); // from audioStream->codecpar->channels
fmt.setCodec("audio/pcm");
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setSampleType(QAudioFormat::UnSignedInt);
output = new QAudioOutput(fmt);
io = output->start();
if (io)
return true;
return false;
}
bool write(const unsigned char* data, int data_size)
{
if (!data || data_size <= 0)
return false;
if (!output || !io)
{
return false;
}
int size = io->write((char*)data, data_size);
if (data_size != size)
return false;
return true;
}
main.cpp
MyAudioPlayer::open();
unsigned char* pcm = new unsigned char[1024 * 1024];
if (demux.get_media_type() == 1) { // audio
audio_decode.sendPacket(pkt);
AVFrame* frame = audio_decode.receiveFrame();
int len = resample.resample(frame, pcm);
while (len > 0) {
if (MyAudioPlayer::check_space() >= len) {
MyAudioPlayer::write(pcm, len);
break;
}
msleep(1);
}
}
If you have troubles with the final quality and noise probably you are misunderstanding the proper way to perform a resampling or there is a bug in your configuration.
Take a look into this example: libswresample-example.
I am not familiar with the FFmpeg API because to do resampling I tend to use libsamplerate.
Regarding old example, those are the steps to perform a basic resample with FFMPEG:
Start by configuring your resampling context:
//Set up resampling context
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swr, "in_sample_rate", 44100, 0);
av_opt_set_int(swr, "out_sample_rate", 22050, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
swr_init(swr);
Depending on your input data types and the format you expect as an output, you will need to specify the right format. This is the equivalence in C++ standard:
----------------------------------------
| *AV_SAMPLE_FMT_S16* | `std::int16_t` |
| *AV_SAMPLE_FMT_S32* | `std::int32_t` |
| *AV_SAMPLE_FMT_FLT* | `float` |
| *AV_SAMPLE_FMT_DBL | `double` |
| *AV_SAMPLE_FMT_U8P* | `std::uint8_t` |
| ... | |
Get your data from whatever place in the right format and estimate your sampling count.
After that, you can perform the resampling in few steps:
Estimate the number of output samples
uint8_t* out_samples;
int out_num_samples = av_rescale_rnd(swr_get_delay(swr, in_samplerate) + in_num_samples, out_samplerate, in_samplerate, AV_ROUND_UP);
Allocate the memory for the output file
av_samples_alloc(&out_samples, NULL, out_num_channels, out_num_samples, AV_SAMPLE_FMT_FLT, 0);
Convert the input data into the expected output format
out_num_samples = swr_convert(swr, &out_samples, out_num_samples, &in_samples, in_num_samples);
Do not forget to free your memory
av_freep(&out_samples);
swr_free(&swr);
If you have noise, probably the input formats and output formats are not the proper one or the resampling quality is low.
For instance, do not panic if you get fewer samples than what you expected. It is the common behavior because of the way the filtering works. To get the remaining trailing you can perform the step 5 with NULL as input, which will flush the internal data.

Is it possible to get good FPS using Raspberry Pi camera v4l2 in c++?

I'm trying to stream video on a Raspberry Pi using the official V4L2 driver with the Raspberry Pi camera, from C++ on raspbian (2015-02 release), and I'm having low FPS issues.
Currently I'm just creating a window and copying the buffer to the screen (which takes about 30ms) whereas the select() takes about 140ms (for a total of 5-6 fps). I also tried sleeping for 100ms and it decreases the select() time by a similar amount (resulting in the same fps). CPU load is about 5-15%.
I also tried changing the driver fps from console (or system()) but it only works downwards (for example, if I set the driver fps to 1fps, I'll get 1fps but if I set it to 90fps I still get 5-6fps, even though the driver confirms setting it to 90fps).
Also, when querying FPS modes for the used resolution I get 90fps.
I included the parts of the code related to V4L2 (code omitted between different parts) :
//////////////////
// Open device
//////////////////
mFD = open(mDevName, O_RDWR | O_NONBLOCK, 0);
if (mFD == -1) ErrnoExit("Open device failed");
//////////////////
// Setup format
//////////////////
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Xioctl(VIDIOC_G_FMT, &fmt);
mImgWidth = fmt.fmt.pix.width;
mImgHeight = fmt.fmt.pix.height;
cout << "width=" << mImgWidth << " height=" << mImgHeight << "\nbytesperline=" << fmt.fmt.pix.bytesperline << " sizeimage=" << fmt.fmt.pix.sizeimage << "\n";
// For some reason querying the format always sets pixelformat to JPEG
// no matter the input, so set it back to YUYV
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (Xioctl(VIDIOC_S_FMT, &fmt) == -1)
{
cout << "Set video format failed : " << strerror(errno) << "\n";
}
//////////////////
// Setup streaming
//////////////////
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 20;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == Xioctl(VIDIOC_REQBUFS, &req))
{
ErrnoExit("Reqbufs");
}
if (req.count < 2)
throw "Not enough buffer memory !";
mNBuffers = req.count;
mBuffers = new CBuffer[mNBuffers];
if (!mBuffers) throw "Out of memory !";
for (unsigned int i = 0; i < mNBuffers; i++)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == Xioctl(VIDIOC_QUERYBUF, &buf))
ErrnoExit("Querybuf");
mBuffers[i].mLength = buf.length;
mBuffers[i].pStart = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, mFD, buf.m.offset);
if (mBuffers[i].pStart == MAP_FAILED)
ErrnoExit("mmap");
}
//////////////////
// Start streaming
//////////////////
unsigned int i;
enum v4l2_buf_type type;
struct v4l2_buffer buf;
for (i = 0; i < mNBuffers; i++)
{
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == Xioctl(VIDIOC_QBUF, &buf))
ErrnoExit("QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1==Xioctl(VIDIOC_STREAMON, &type))
ErrnoExit("STREAMON");
And the last two parts in the main loop :
//////////////////
// Get frame
//////////////////
FD_ZERO(&fds);
FD_SET(mFD, &fds);
tv.tv_sec = 3;
tv.tv_usec = 0;
struct timespec t0, t1;
clock_gettime(CLOCK_REALTIME, &t0);
// This line takes about 140ms which I don't get
r = select(mFD + 1, &fds, NULL, NULL, &tv);
clock_gettime(CLOCK_REALTIME, &t1);
cout << "select time : " << ((float)(t1.tv_sec - t0.tv_sec))*1000.0f + ((float)(t1.tv_nsec - t0.tv_nsec))/1000000.0f << "\n";
if (-1 == r)
{
if (EINTR == errno)
continue;
ErrnoExit("select");
}
if (r == 0)
throw "Select timeout\n";
// Read the frame
//~ struct v4l2_buffer buf;
memset(&mCurBuf, 0, sizeof(mCurBuf));
mCurBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mCurBuf.memory = V4L2_MEMORY_MMAP;
// DQBUF about 2ms
if (-1 == Xioctl(VIDIOC_DQBUF, &mCurBuf))
{
if (errno == EAGAIN) continue;
ErrnoExit("DQBUF");
}
clock_gettime(CLOCK_REALTIME, &mCaptureTime);
// Manage frame in mBuffers[buf.index]
mCurBufIndex = mCurBuf.index;
break;
}
//////////////////
// Release frame
//////////////////
if (-1 == Xioctl(VIDIOC_QBUF, &mCurBuf))
ErrnoExit("VIDIOC_QBUF during mainloop");
I have been looking at the various methods of using the picamera and I'm hardly an expert, but it would seem that the default camera settings are what's holding you back. There are many modes and switches. I don't know if they are exposed through ioctls or how yet, I just started. But I had to use a program called v4l-ctl to get things ready for the mode I wanted. A deep look at that source and some code lifting should let you achieve greatness. Oh, and I doubt the select call is an issue, it's simply waiting on the descriptor which is slow to become readable. Depending on mode, etc. there can be a mandatory wait for autoexposure, etc.
Edit: I meant to say "a default setting" as you've changed some. There are also rules not codified in the driver.
The pixelformat matters. I encountered the similar low-fps problem, and I spent some time testing using my program in Go and C++ using V4L2 API. What I found is, Rpi Cam Module has good accelaration with H.264/MJPG pixelformat. I can easily get 60fps at 640*480, same as non-compressed formats like YUYV/RGB. However JPEG runs very slow. I can only get 4fps even at 320*240. And I also found the current is higher (>700mA) with JPEG compare to 500mA with H.264/MJPG.

Serial Port communication with Arduino and C++

I am having a problem with a Serial Port communication between Arduino Nano and C++, even though the problem is in C++ side. Basically I want to send integers (or long,...) from the Arduino to a C++ program to be processed.
First I did a test sending information from the Arduino to the computer using Matlab. The Arduino code is pretty simple:
int i = 0;
void setup() {
// start serial port at 9600 bps:
Serial.begin(9600);
establishContact();
}
void loop() {
Serial.println(i);
i=i+1;
delay(10);
}
void establishContact() {
while (Serial.available() <= 0) {
Serial.println('A', BYTE);
delay(10);
}
}
The Matlab side is also simple:
clc;
clear all;
numSec=2;
t=[];
v=[];
s1 = serial('COM3'); % define serial port
s1.BaudRate=9600; % define baud rate
set(s1, 'terminator', 'LF'); % define the terminator for println
fopen(s1);
try % use try catch to ensure fclose
% signal the arduino to start collection
w=fscanf(s1,'%s'); % must define the input % d or %s, etc.
if (w=='A')
display(['Collecting data']);
fprintf(s1,'%s\n','A'); % establishContact just wants
% something in the buffer
end
i=0;
t0=tic;
while (toc(t0)<=numSec)
i=i+1;
t(i)=toc(t0);
t(i)=t(i)-t(1);
v(i)=fscanf(s1,'%d');
end
fclose(s1);
plot(t,v,'*r')
catch me
fclose(s1);
end
My goal is, with C++, do the same that is done in Matlab using fscanf(s1, '%d').
Here is the current code that I am using (C++ code):
void main()
{
HANDLE hSerial;
hSerial = CreateFile(TEXT("COM3"),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,//FILE_FLAG_OVERLAPPED,
NULL);
if ( hSerial == INVALID_HANDLE_VALUE)
{
printf("Error initializing handler");
}
else
{
// Set the parameters of the handler to the serial port.
DCB dcb = {0};
dcb.DCBlength = sizeof(dcb);
if ( !GetCommState(hSerial, &dcb) )
{
printf("Error setting parameters");
}
FillMemory(&dcb, sizeof(dcb), 0);
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if ( !SetCommState(hSerial, &dcb) )
{
// error setting serial port state.
}
// Tell the program not to wait for data to show up
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 0;//20;
timeouts.ReadTotalTimeoutConstant = 0;//20;
timeouts.ReadTotalTimeoutMultiplier = 0;//50;
timeouts.WriteTotalTimeoutConstant = 0;//100;
timeouts.WriteTotalTimeoutMultiplier = 0;//100;
if ( !SetCommTimeouts(hSerial, &timeouts) )
{
printf("Error setting the timeouts");
}
char szBuff[5] = "";
DWORD dwBytesRead = 0;
int i = 0;
char test[] = "B\n";
int maxSamples = 10;
DWORD dwCommStatus;
WriteFile(hSerial, test, 2, &dwBytesRead, NULL);
SetCommMask(hSerial,EV_RXCHAR);
while (i < maxSamples)
{
WaitCommEvent (hSerial, &dwCommStatus, 0);
if (dwCommStatus & EV_RXCHAR)
{
memset(szBuff,0,sizeof(szBuff));
ReadFile(hSerial, LPVOID(szBuff), 4, &dwBytesRead, NULL);
cout<<szBuff;
printf(" - %d - \n", atoi(szBuff));
}
i++;
}
scanf("%d", &i);
CloseHandle(hSerial);
}
}
The goal of my code would be something like num = ReadSerialCOM(hSerial, "%d");
My current C++ code reads the information from the buffer, but there is not an accepted end of line, which implies that my numbers (integers) are received cut.
Eg:
I send 8889 from the Arduino, which places it in the COM port. And the command ReadFile saves '88' into szBuff. At the next iteration '89\n' is saved into sZBuff. Basically I want to avoid to post-process sZBuff to concat '88' and '89\n'.
Anyone?
Thanks!
If I understand your question correctly, one way to avoid having to 'post-process' is to move the pointer passed to ReadFile to the end of the available data, so the ReadFile call is appending to the buffer, instead of overwriting.
Essentially, you would have two pointers. One to the buffer, the other to the end of the data in the buffer. So when your program starts, both pointers will be the same. Now, you read the first 2 bytes. You increment the end-of-data pointer by 2. You do another read, but instead of szBuff, you pass a pointer to the end of the previously read data. You read the next three bytes and you have the complete entry in szBuff.
If you need to wait until some delimiter to mark the end of an entry is received, you could just search the received data for it. If it's not there, you keep reading until you find it. If it is there, you can just return.
// Fill the buffer with 0
char szBuff[256] = {0};
// We have no data in the buffer, so the end of data points to the beginning
// of the buffer.
char* szEndOfData = szBuff;
while (i < maxSamples)
{
WaitCommEvent (hSerial, &dwCommStatus, 0);
if (dwCommStatus & EV_RXCHAR)
{
// Append up to 4 bytes from the serial port to the buffer
ReadFile(hSerial, LPVOID(szEndOfData), 4, &dwBytesRead, NULL);
// Increment the end of data pointer, so it points to the end of the
// data available in the buffer.
szEndOfData += dwBytesRead;
cout<<szBuff;
printf(" - %d - \n", atoi(szBuff));
}
i++;
}
// Output, assuming what you mentioned happens:
// - 88 -
// - 8889 -
If this approach is acceptable to you, it will require a bit more work. For example, you would have to ensure you don't overflow your buffer. When you remove data from the buffer, you'll have to move all of the data after the removed segment to the beginning, and fix the end of data pointer. Alternatively, you could use a circular buffer.
As Hans Passant and dauphic pointed, it doesn't seem to be a general solution for my question. I am writing, though, the code that I was trying to avoid, just in case somebody finds it useful or face the same problem that I had:
int i = 0;
DWORD dwBytesRead = 0;
DWORD dwCommStatus = 0;
char szBuff[2] = "";
int maxRead = 20;
int sizeNum = 1;
int *num = (int*)malloc(maxRead*sizeof(int));
char *currNum;
char *pastNum;
// Write something into the Serial Port to start receive
// information from the Arduino
WriteFile(hSerial, (LPCVOID)"A\0", 1, &dwBytesRead, NULL);
SetCommMask(hSerial, EV_RXCHAR);
// Start reading from the Serial Port
while ( i < maxRead )
{
WaitCommEvent (hSerial, &dwCommStatus, 0);
if (dwCommStatus & EV_RXCHAR) // if a char is received in the serial port
{
ReadFile(hSerial, LPVOID(szBuff), 1, &dwBytesRead, NULL);
if ( szBuff[0] > 47 && szBuff[0] < 58 )
{
sizeNum++;
if (sizeNum ==2)
{
currNum = (char*)malloc(sizeNum*sizeof(char));
strcpy(currNum, szBuff);
} else
{
if (pastNum != NULL)
free(pastNum);
pastNum = currNum;
currNum = (char*)malloc(sizeNum*sizeof(char));
strcpy(currNum, pastNum);
strcpy(currNum+(sizeNum-2)*sizeof(char), szBuff);
}
cout << szBuff<<endl;
} else if (szBuff[0] == '\n' && sizeNum > 1) // end of number
{
num[i] = atoi(currNum);
i++;
sizeNum = 1;
if (currNum!=NULL)
free(currNum);
}
}
}