SFML: How to overload Packet operator>> with a SoundBuffer? - sfml

I am struggling with this one. I'd like to overload operator >> in order to send SoundBuffer to client , but Packet doesn't support Int16* which is type of a.getSamples();
sf::Packet& operator >>(sf::Packet& packet, SoundBuffer& a)
{
return packet >> a.getSamples() >> a.getSampleCount();
}
Thank in advance for help.

sf::SoundBuffer::getSamples() returns a pointer to the samples. You'll have to send/retrieve the individual samples rather than the pointer only.
I'd try something like this (untested):
sf::Packet& operator << (sf::Packet& packet, SoundBuffer& a)
{
std::size_t num = a.getSampleCount();
// Send the number of samples first
packet << num;
// Now send the actual samples (repeat as long as we have some left)
for (sf::Int16 *p = a.getSamples(); num; ++p, --num)
packet << p;
}
On the client side you'll just have to reverse the whole thing, then use sf::SoundBuffer::loadFromMemory() to load the data into the buffer.
Just keep in mind this is uncompressed and might be rather taxing on the connection. As such you might want to implement your own compression layer. You can find an example on the documentation page of sf::Packet.

Related

Qt, client - server relationship

I am newcomer in area of network and internet,therefore want to apologize for may be stupid question. I do not understand whether there are other ways to send data from client socket to server's axcept putting data into a stream using method QIODevice::write(QByteArray& ). If that is the only way how server should recognize what exactly data has been sent to it ? For example, we may have QString message as a usual input data, but also sometimes QString as the name of further receiver of future data. It is possible to describe all variants but the slot connected to readyRead() signal seems to be of enormous
size at this case.
Eventually, is there way to direct data to some exact server's functions ?
Qt Solutions has a library to make Qt servers easily:
Qt Solutions
And Json format it is a beautiful way to communicate
You need to define comman data type both side( client and server ). before you sent data packet you should write size of data packet to first four byte of data packet. at the server side check size of data receiving from client with first four bytes. and deserialize data which you how did you serialize at client side. I used this method a long time and there is any problem occured to today. i will give you sample code for you.
Client Side:
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
QDataStream in(&buffer);
in.setVersion(QDataStream::Qt_5_2);
in << int(0); // for packet size
in << int(3); // int may be this your data type or command
in << double(4); // double data
in << QString("asdsdffdggfh"); //
in << QVariant("");
in << .... // any data you can serialize which QDatastream accept
in.device()->seek(0); // seek packet fisrt byte
in << buffer.data().size(); // and write packet size
array = buffer.data();
this->socket->write(arr);
this->socket->waitForBytesWritten();
Server Side:
QDatastream in(socket);
//define this out of this scope and globally
int expectedByte = -1;
if( expectedByte < socket->bytesAvailable() && expectedByte == -1 )
{
in >> expectedByte;
}
if(expectedByte - socket->bytesAvailable()- (int)sizeof(int) != 0){
return;
}
// if code here, your packet received completely
int commandOrDataType;
in >> commandOrDataType;
double anyDoubleValue;
in >> anyDoubleValue;
QString anyStringValue;
in >> anyStringValue;
QVariant anyVariant;
in >> anyVariant;
// and whatever ...
// do something with above data
//you must set expectedByte = -1;
// if your proccessing doing any thing at this time there is no any data will be received while expectedByte != -1, but may be socket buffer will be filling. you should comfirm at the begining of this function
expectedByte = -1;
Hope this helpfully! :)

Read (many) values from QTcpSocket (fast)

I'm using a measurement device which sends (binary) float values using a tcp socket with up to 70 kHz.
My goal is to read these values as fast as possible and use them in other parts of my program.
Till now I'm able to extract value by value using a QTcpSocket and QDataStream:
First I create the socket and connect the stream to it
mysock = new QTcpSocket(this);
mysock->connectToHost(ip, port);
QDataStream stream(mysock);
stream.setByteOrder(QDataStream::LittleEndian);
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
Then I read from the socket and write the stream data to my float value
while(true) //only for test purpose (dont stop reading)
if (mysock->waitForReadyRead())
{
while (mysock->bytesAvailable() >= 6)
{
QByteArray a = mysock->read(6); //each value sent is 6 bytes long
stream.skipRawData(2); //first 2 bytes don't belong to the number
float result;
stream >> result;
//qDebug()<<result;
}
}
When I measure the iteration frequency of the while(true) loop I'm able to achieve about 30 kHz.
Reading multiple values per read I can reach up to 70 Khz. (Not taking other calculations into account which might slow me down)
My questions are:
If I read multiple values at once, how do I extract these values from the QDataStream? I need a 6 bytes spacing with only 4 bytes containing the value.
Answer: In my case there is 2 bytes (trash) followed by a known number of values, for example 4 bytes for a float, 4 bytes for another float, 2 bytes for an uint16.
stream >> trashuint16 >> resultfloat1 >> resultfloat2 >> resultuint16
Expands 1: I can configure my device to send different values of different type (int, float) which need to be written to different variables.
Answer: Same.
Is there a more efficient way to read many values from a QTcpSocket?
Answer: Anwered in the comments.
Update (to answer some questions):
Max rate in Bytes: 70 kHz x 6 Byte (for one value) = 420 kB/s (Doesnt seem that much :))
Update 2
New Question: When i start a transaction (using stream.startTransaction) I would like to know whats inside that stream in binary code.
I dont understand how QDataStream::startTransaction works. How many bytes will be read? what happens with the data I dont extract using >>?
I've tried the following:
if (mysock->waitForReadyRead())
{
stream.startTransaction();
char *c = new char[40];
stream.readRawData(c, 40); //I want to know whats really inside
QByteArray a(c);
qDebug() << a <<stream.status();
if (!stream.commitTransaction())
break;
}
Doing this again and again, I'll sometimes get status = -1 (read too much) and sometimes not. How do I get the "size" of the stream?
Your code has couple mistakes.
You are doing direct reading from socket when in the same time you are using QDataStream. This can break stuff.
Also your code is assuming that your application will receive data in same chunks as it was sent by other end. You do not have such warranty! It may happen that you will receive chunk data which are ending in middle of your frame. It works just by pure luck or you are ignoring some bugs of your application.
This should go like this:
while(true)
if (mysock->waitForReadyRead()) // IMO doing such loop is terrible approach
// but this is Out of the scope of question, so ignoring that
{
while (true)
{
stream.startTransaction();
float result;
qint32 somedata
stream >> somedata >> result; // I do not know binary format your application is using
if (!in.commitTransaction())
break;
AddDataToModel(result, somedata);
}
}
Edit:
From comment:
Please correct me if I'm wrong, but if I want 2 bytes to be discarded I need to do "stream >> someint(2 byte) >> somefloat(4 byte)"? How can I handle many values in stream?
qint16 toBeDiscarded;
float value;
// note stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
// is needed to read float as 32 bit floating point number
stream >> toBeDiscarded >> value;
ProcessValue(value);

Sending a flexible Amount of Data over Network by using Asio (Boost)

I got a client and a server application which will send each other data by using the Asio (Standalone) library. Both applications consists of two (logical) parts:
A high level part: dealing with complex objects e.g. users, permissions,...
A low level part: sending data over network between client and server
Let's assume the complex objects are already serialized by using Protocoll Buffers and the low level part of the application receives the data as std::string from the high level part. I would like to use this function from Protocoll Buffers for this job:
bool SerializeToString(string* output) const;: serializes the message
and stores the bytes in the given string. Note that the bytes are
binary, not text; we only use the string class as a convenient
container.
And say I transfer this data with async_write on the client side:
size_t dataLength = strlen(data);
//writes a certain number of bytes of data to a stream.
asio::async_write(mSocket,
asio::buffer(data, dataLength),
std::bind(&Client::writeCallback, this,
std::placeholders::_1,
std::placeholders::_2));
How can I read this data on the server side? I don't know how much data I will have to read. Therefore this will not work (length is unknown):
asio::async_read(mSocket,
asio::buffer(mResponse, length),
std::bind(&Server::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
What is the best way to solve this problem? I could think of two solutions:
Append a 'special' character at the end of data and read until I reach this 'end of data signal'. The problem is, what if this character appears in data somehow? I don't know how Protocoll Buffers serializes my data.
Send a binary string with size_of_data + data instead of data. But I don't know how to serialize the size in an platform independent way, add it to the binary data and extract it again.
Edit: Maybe I could use this:
uint64_t length = strlen(data);
uint64_t nwlength = htonl(length);
uint8_t len[8];
len[0] = nwlength >> 56;
len[1] = nwlength >> 48;
len[2] = nwlength >> 40;
len[3] = nwlength >> 32;
len[4] = nwlength >> 24;
len[5] = nwlength >> 16;
len[6] = nwlength >> 8;
len[7] = nwlength >> 0;
std::string test(len);
mRequest = data;
mRequest.insert(0, test);
and send mRequest to the server? Any traps or caveats with this code?
How could I read the length on server side and the content afterwards?
Maybe like this:
void Server::readHeader(){
asio::async_read(mSocket,
asio::buffer(header, HEADER_LENGTH),
std::bind(&Server::readHeaderCallback, this,
std::placeholders::_1,
std::placeholders::_2),
asio::transfer_exactly(HEADER_LENGTH));
}
void Server::readHeaderCallback(const asio::error_code& error,
size_t bytes_transferred){
if(!error && decodeHeader(header, mResponseLength)){
//reading header finished, now read the content
readContent();
}
else{
if(error) std::cout << "Read failed: " << error.message() << "\n";
else std::cout << "decodeHeader failed \n";
}
}
void Server::readContent(){
asio::async_read(mSocket,
asio::buffer(mResponse, mResponseLength),
std::bind(&Server::readContentCallback, this,
std::placeholders::_1,
std::placeholders::_2),
asio::transfer_exactly(mResponseLength));
}
void Server::readContentCallback(const asio::error_code& error,
size_t bytes_transferred){
if (!error){
//handle content
}
else{
//#todo remove this cout
std::cout << "Read failed: " << error.message() << "\n";
}
}
Please note that I try to use transfer_exactly. Will this work?
When sending variable length messages over a stream-based protocol, there are generally three solutions to indicate message boundaries:
Use a delimiter to specify message boundaries. The async_read_until() operations provide a convenient way to read variable length delimited messages. When using a delimiter, one needs to consider the potential of a delimiter collision, where the delimiter appears within the contents of a message, but does not indicate a boundary. There are various techniques to handle delimiter collisions, such as escape characters or escape sequences.
Use a fixed-length header with a variable-length body protocol. The header will provide meta-information about the message, such as the length of the body. The official Asio chat example demonstrates one way to handle fixed-length header and variable-length body protocols.
If binary data is being sent, then one will need to consider handling byte-ordering. The hton() and ntoh() family of functions can help with byte-ordering. For example, consider a protocol that defines the field as two bytes in network-byte-order (big-endian) and a client reads the field as a uint16_t. If the value 10 is sent, and a little-endian machine reads it without converting from network-order to local-order, then the client will read the value as 2560. The Asio chat example avoids handling endianness by encoding the body length to string instead of a binary form.
Use the connection's end-of-file to indicate the end of a message. While this makes sending and receiving messages easy, it limits the sender to only one message per connection. To send an additional message, one would need to established another connection.
A few observations about the code:
The Protocol Buffers' SerializeToString() function serializes a message to a binary form. One should avoid using text based functions, such as strlen(), on the serialized string. For instance, strlen() may incorrectly determine the length, as it will treat the first byte with a value of 0 as the terminating null byte, even if that byte is part of the encoded value.
When providing an explicitly sized buffer to an operation via asio::buffer(buffer, n), the default completion condition of transfer_all will function the same as transfer_exactly(n). As such, the duplicate use of variables can be removed:
asio::async_read(mSocket,
asio::buffer(header, HEADER_LENGTH),
std::bind(&Server::readHeaderCallback, this,
std::placeholders::_1,
std::placeholders::_2));
The htonl() overloads support uint16_t and uint_32t, not uint64_t.
Asio supports scatter/gather operations, allowing a receive operation to scatter-read into multiple buffers, and transmit operations can gather-write from multiple buffers. As such, one does not necessarily need to have both the fixed-length header and message-body contained with a single buffer.
std::string body_buffer;
body.SerializeToString(&body_buffer);
std::string header_buffer = encode_header(body_buffer.size());
// Use "gather-write" to send both the header and data in a
// single write operation.
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(header_buffer));
buffers.push_back(boost::asio::buffer(body_buffer));
boost::asio::write(socket_, buffers);
client must call
socket.shutdown(asio::ip::tcp::socket::shutdown_both);
socket.close();
on the server size read until EOF detected
std::string reveive_complete_message(tcp::socket& sock)
{
std::string json_msg;
asio::error_code error;
char buf[255];
while (1)
{
//read some data up to buffer size
size_t len = sock.read_some(asio::buffer(buf), error);
//store the received buffer and increment the total return message
std::string str(buf, len);
json_msg += str;
if (error == asio::error::eof)
{
//EOF received, the connection was closed by client
break;
}
else if (error)
{
throw asio::system_error(error);
}
}
return json_msg;
}

Audio over socket c++

I am capturing some audio from my microphone using SFML.
The data is being stored in samples of type Int16*.
Int16* samples;
My question is. What should I do to this samples to stream it over a socket to be played in another place? I ask in relation of data type. Do I need to convert this Int16 array to another type? Or can I just send this Int16* as it is?
EDIT
void BroadcastRecorder::loadBufferFromSamples()
{
//m_samples is of type vector<Int16*>
if (!m_samples.empty()){
m_buffer.loadFromSamples(&m_samples[0], m_samples.size(), 1, getSampleRate());
m_samples.clear();
}
}
void Broadcaster::Send()
{
//load the buffer with the samples
if(!m_recorder->empty()){
m_recorder->loadBufferFromSamples();
const sf::SoundBuffer& buffer = m_recorder->getBuffer();
size_t dataLength = m_recorder->GetSamplesSize();
wxSocketClient * socket = new wxSocketClient(wxSOCKET_NOWAIT);
socket->Notify(false);
// ------------- DATA----------------------
wxString data = "";
wxString strToPrepend(_("--myboundary\r\nContent-Type: audio/wav\r\n"));
wxString strToAppend(_("\r\n--myboundary\r\n"));
// ------------- HEADER -----------------------
wxString header = "";
header.append("POST ");
header.append("/cgi-bin/operator/transmit");
header.append(" HTTP/1.0\r\n");
header.append("Content-Type: multipart/form-data; boundary=--myboundary\r\n");
header.append("Content-Length: " + wxString::Format(wxT("%i"),(dataLength + strToPrepend.Len() + strToAppend.Len()) ) + "\r\n");
header.append("Authorization: Basic keykeykeykey\r\n");
header.append("\r\n");
//-------------- CONNECTION ---------------
wxString host = _("192.168.50.11");
wxIPV4address * address = new wxIPV4address();
address->Hostname(host);
address->Service(8084);
if (socket->Connect(*address)){
//Write header
socket->Write(header.c_str(),header.Len());
//Write data
socket->Write(strToPrepend.c_str(),strToPrepend.Len());
const sf::Int16* samples = buffer.getSamples();
const char* bytesData = reinterpret_cast<const char*>(samples);
socket->Write(bytesData,dataLength);
socket->Write(strToAppend.c_str(),strToAppend.Len());
socket->Close();
}
delete socket;
delete address;
}
}
I am getting only some noises between gaps.
BTW. The audio is being sent to an IP camera p2 connector.
The data format is just the way your application treats them. After all you send raw bytes over a socket. And you can do it with anything you want
Int16 data;
const char* pBytesOfData = (const char*) &data;
int size = sizeof (Int16);
send( socket, pBytesOfdata, size, flags);
When the bytes arrive on the second end it is up to you to interpret them correctly. Probably you will want again treat them as Int16. You need to have a protocol (common way of communication) to do it right (maybe send size of the data at the begining of the transmission, etc).
You can also take a look on libraries that ease serialization: Boost.Asio and Boost.Serialization.
Firstly, You need to create and bind a socket. Then you have to send the data stored in "samples" to another peer by using socket API. For using socket API to send the data, you need to convert this data to char*. As send API of socket takes input of data you need to send as char*. For more information about sending you can go through this link. This is for windows. For Unix you can check the manpage for send API for unix.
Int16* is a pointer. The samples you get should also have an associated length. Your data will likely be between addresses: [samples, samples + length) (where samples is the address to the first sample).
To play the samples remotely (actual code will depend on what APIs you use):
open socket
in a loop
get samples from your microphone
transmit the data over socket
on the server, you will have to read samples in a loop and send them to whatever sound output API you use.
Sockets work with bytes, so in the end you will send bytes. As long as the way you interpret these bytes on the receiving side matches the data you sent, you can send whatever you want in those bytes.
In this case sending the samples directly without conversion seems the most trivial thing to do, but you will probably need to send the size of the sample before, most likely in a fixed length format, for example:
[size on 4 bytes][sample on `size` bytes]
[] [] [] [][] [] [] [] [] []

Retrieve correct data with two consecutive calls to boost::asio::read

I am currently implementing a network protocol with Boost Asio. The domain classes already exist and I am able to
write packets to a std::istream
and read packets from a std::ostream.
A Network Packet contains a Network Packet Header. The header starts with the Packet Length field, which has a size of two bytes (std::uint16_t).
I am using TCP/IPv4 as the transport layer, therefore I try to implement the following:
Read the length of the packet to know its total length. This means reading exactly two bytes from the socket.
Read the rest of the packet. This means reading exactly kActualPacketLength - sizeof(PacketLengthFieldType) bytes from the socket.
Concat both read binary data.
Therefore I need at least two calls to boost::asio::read (I am starting synchronously!).
I am able to read a packet with one call to boost::asio::read if I hard-code the expected length:
Packet const ReadPacketFromSocket() {
boost::asio::streambuf stream_buffer;
boost::asio::streambuf::mutable_buffers_type buffer{
stream_buffer.prepare(Packet::KRecommendedMaximumSize)};
std::size_t const kBytesTransferred{boost::asio::read(
this->socket_,
buffer,
// TODO: Remove hard-coded value.
boost::asio::transfer_exactly(21))};
stream_buffer.commit(kBytesTransferred);
std::istream input_stream(&stream_buffer);
PacketReader const kPacketReader{MessageReader::GetInstance()};
return kPacketReader.Read(input_stream);
}
This reads the complete packet data at once and returns a Packet instance. This works, so the concept is working.
So far so good. Now my problem:
If I make two consecutive calls to boost::asio::read with the same boost::asio::streambuf I can't get it to work.
Here is the code:
Packet const ReadPacketFromSocket() {
std::uint16_t constexpr kPacketLengthFieldSize{2};
boost::asio::streambuf stream_buffer;
boost::asio::streambuf::mutable_buffers_type buffer{
stream_buffer.prepare(Packet::KRecommendedMaximumSize)};
std::size_t const kBytesTransferred{boost::asio::read(
// The stream from which the data is to be read.
this->socket_,
// One or more buffers into which the data will be read.
buffer,
// The function object to be called to determine whether the read
// operation is complete.
boost::asio::transfer_exactly(kPacketLengthFieldSize))};
// The received data is "committed" (moved) from the output sequence to the
// input sequence.
stream_buffer.commit(kBytesTransferred);
BOOST_LOG_TRIVIAL(debug) << "bytes transferred: " << kBytesTransferred;
BOOST_LOG_TRIVIAL(debug) << "size of stream_buffer: " << stream_buffer.size();
std::uint16_t packet_size;
// This does seem to modify the streambuf!
std::istream istream(&stream_buffer);
istream.read(reinterpret_cast<char *>(&packet_size), sizeof(packet_size));
BOOST_LOG_TRIVIAL(debug) << "size of stream_buffer: " << stream_buffer.size();
BOOST_LOG_TRIVIAL(debug) << "data of stream_buffer: " << std::to_string(packet_size);
std::size_t const kBytesTransferred2{
boost::asio::read(
this->socket_,
buffer,
boost::asio::transfer_exactly(packet_size - kPacketLengthFieldSize))};
stream_buffer.commit(kBytesTransferred2);
BOOST_LOG_TRIVIAL(debug) << "bytes transferred: " << kBytesTransferred2;
BOOST_LOG_TRIVIAL(debug) << "size of stream_buffer: " << stream_buffer.size();
// Create an input stream with the data from the stream buffer.
std::istream input_stream(&stream_buffer);
PacketReader const kPacketReader{MessageReader::GetInstance()};
return kPacketReader.Read(input_stream);
}
I have the following problems:
Reading the packet length from the boost::asio::streambuf after the first socket read seems to remove the data from the boost::asio::streambuf.
If I use two distinct boost::asio::streambuf instances I do not know how to "concat" / "append" them.
At the end of the day I need a std::istream with the correct data obtained from the socket.
Can someone please guide me into the correct direction? I've tried to make this work for several hours now...
Maybe this approach isn't the best, so I am open to suggestions to improve my design.
Thanks!
I believe the behaviour is by design.
To concatenate the buffers, you can use BUfferSequences (using make_buffers) and use buffer iterators, or you can stream the second into the first:
boost::asio::streambuf a, b;
std::ostream as(&a);
as << &b;
Now you can throw away b as it's pending data have been appended to a
See it Live on Coliru
Before I forget, I want to summarize my current solution, which doesn't use a boost::asio::streambuf, since it seems to be impossible to read from it without modifying it. Instead I use a std::vector<std::uint8_t> (ByteVector) as the data holder for the buffers.
The following source code contains my current solution:
Packet const ReadPacketFromSocket() {
ByteVector const kPacketLengthData{this->ReadPacketLengthFromSocket()};
PacketHeader::PacketLengthType kPacketLength{
static_cast<PacketHeader::PacketLengthType>(
(kPacketLengthData[1] << 8) | kPacketLengthData[0])};
ByteVector rest_packet_data(Packet::KRecommendedMaximumSize);
boost::asio::read(
this->socket_,
boost::asio::buffer(rest_packet_data),
boost::asio::transfer_exactly(
kPacketLength - sizeof(PacketHeader::PacketLengthType)));
ByteVector data{
VectorUtils::GetInstance().Concatenate(
kPacketLengthData,
rest_packet_data)};
// Create an input stream from the vector.
std::stringstream input_stream;
input_stream.rdbuf()->pubsetbuf(
reinterpret_cast<char *>(&data[0]), data.size());
PacketReader const kPacketReader{MessageReader::GetInstance()};
return kPacketReader.Read(input_stream);
}
ByteVector ReadPacketLengthFromSocket() {
ByteVector data_holder(sizeof(PacketHeader::PacketLengthType));
boost::asio::read(
this->socket_,
boost::asio::buffer(data_holder),
boost::asio::transfer_exactly(sizeof(PacketHeader::PacketLengthType)));
return data_holder;
}
This works like a charm, I have successfully exchanged packets with messages from my domain model between two processes using this approach.
But: This solution feels wrong, since I have to do lots of conversions. Maybe someone else can provide me with a cleaner approach? What do you think about my solution?