Hello,
I'm using sf::SoundRecorder / sf::SoundStream to make a VOIP client. The recording and the networking (using Qt for the later) are working just fine (except that the SoundRecorder crash on exit, go figure), but I'm running into a weird behavior when I'm subclassing sf::SoundStream.
Beforehand, my code:
Sound.h
#ifndef DEF_SOUND
#define DEF_SOUND
/* Some includes */
class Sound : public QObject, public sf::SoundStream
{
Q_OBJECT
public:
Sound(size_t sampleRate);
protected:
virtual bool OnGetData(Chunk& Data);
public slots:
void queue(const IntVector & samples);
private:
sf::Mutex m_bmux;
QQueue<IntVector > m_buff;
size_t m_sampleRate;
};
#endif
Sound.cpp
#include "Sound.h"
#include <QDebug>
#define D() qDebug() << __FILE__ << ";" << __LINE__
Sound::Sound(size_t sampleRate):sf::SoundStream()
{
m_sampleRate=sampleRate;
Initialize(1, m_sampleRate);
Play();
}
bool Sound::OnGetData(Chunk& Data)
{
m_bmux.Lock();
if(m_buff.size()<1)
{
m_bmux.Unlock();
return true;
}
IntVector bu = m_buff.dequeue();
m_bmux.Unlock();
size_t bfs=bu.size();
sf::Int16* bf=new sf::Int16[bfs]; //Possible memory leak? SFML documentation is unclear about that...
for(size_t i=0;i<bfs;++i)
bf[i]=bu[i];
Data.Samples = bf;
Data.NbSamples = bfs;
D() << "B";
return true;
}
void Sound::queue(const IntVector & samples)
{
m_bmux.Lock();
if(m_buff.size() < 150)
m_buff.enqueue(samples);
else
D() << "Buffer full!";
m_bmux.Unlock();
Play();
D() << "A";
}
IntVector is a QVector<sf::Int16> typedef.
All the Qt containers behave like STL's here. A queue's a queue, and a "vector" is a wrapper over a C-array.
I call queue() every once in a while (each time I receive a frame). For debug purpose it's at the moment directly connected to a sf::SoundRecorder.
The minor part of the problem:
in OnGetData(), do I loose the ownership of the data array I put into the chunk? Both the documentation and the tutorial are very vague about it.
The MAJOR part of the problem:
I expected the console output to be ABABAB..., give or take.
What I get, however, is "ABBBAAAAAAAAAA...". OnGetData() just stop being called, yet the GetState() function still return Playing.
I tried delaying the Play() call, calling it only when there are at least 3 frames in the buffer. This time I got "AAABBBAABBAABBAABBAAAAAAAAAAAAAAAAAA...", so it's kind of better, but it's still broken.
I tried calling Initialize(), Stop(), etc. pretty much everywhere in the code, obviously it just doesn't work this way.
I'm compiling with Mingw32, on a 64bits Windows 7. I used to work with OpenAL directly, but it was a pain to debug and to maintain, this is why I switched to SFML 1.6 for the VOIP.
Anyone got a suggestion on what's wrong with what I've done here?
Thanks,
Gig
PS: I apologize for my English, it is not my mothertongue.
Okay, so I got a few updates.
I made a new code from scratch in a new project, still with SFML 1.6, and I still got this strange behavior.
I migrated to SFML 2.0, changed a few things in the code because of the new name conventions, and this is the code I got in the end:
Main.cpp
#include <QCoreApplication>
#include "Player.h"
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
Player play(44100);
play.play();
return app.exec();
}
Player.h
#ifndef PLAYER_H
#define PLAYER_H
#include <QObject>
#include <SFML2/Audio.hpp>
#include <QDebug>
#define D() qDebug() << __FILE__ << ";" << __LINE__
class Player : public QObject, public sf::SoundStream
{
public:
Player(size_t sampleRate);
protected:
virtual bool onGetData(Chunk& Data);
virtual void onSeek(sf::Time) {}
};
#endif // PLAYER_H
Player.cpp
#include "Player.h"
Player::Player(size_t sampleRate)
{
initialize(1, sampleRate);
}
bool Player::onGetData(Chunk& Data)
{
D() << "B";
return true;
}
Aaaand it still don't work. I got three "B", and then nothing, onGetData() isn't called anymore. Is it because I don't give any data the first three times? It just doesn't make any sense.
Hum, so if I don't receive anything from the network, I have to create a few zero-ed frames? Why not, it should be an easy fix. I'll try it ASAP and get back with what I've found.
However, what I don't get is that I couldn't restart the SoundStream once it went down because of an empty frame with "Play".
Anyway, this behavior should be explained in the documentation or the tutorial (something like "Warning: the Chunk must NOT be empty, or else!").
Thank you again Laurent.
EDIT: It seems to be working just fine now:
bool Player::onGetData(Chunk& Data)
{
sf::Int16* buff=new sf::Int16[800];
Data.samples=buff;
Data.sampleCount=800;
D() << "B";
return true;
}
Now I got a segfault on exit though (Ctrl-C from the terminal is quite violent thought, I hope a 'softer' exit will fix it)