Wave Format - negative data size - format

I'm working with Wave files. I'm reading all the data but in some files data size (specified amount of bytes to be read) is negative, which breaks the reading mechanism. Are wave files constant in terms of byte offsets? Is x offset always the same value?

Since the unit of datasize in the .wav header is unsigned, and since you're getting a signed value, it suggests you're not reading the value from the wave header correctly...

Are wave files constant in terms of byte offsets? Is x offset always
the same value?
No, at least the data section is not always found at the same offset. Some simple programs assume that the sound samples start at a fixed offset (44) but this is not always the case.
The only way to reliably read a wave file is to look for the data section and once you find it, the data size field will be at offset +4 relative to the data section.

If it helps this is what I use for wav decoding/encoding (I coded some years ago):
//---------------------------------------------------------------------------
//--- RIFF WAVE format: 1.01 ------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _RIFF_h
#define _RIFF_h
//---------------------------------------------------------------------------
// 8bit PCM is unsigned
// 16bit PCM is signed 2'os complement little endian (big endian is RIFX)
//---------------------------------------------------------------------------
struct _wave_chunk
{
DWORD ids;
DWORD len;
_wave_chunk(){ ids=' '; len=0; }
_wave_chunk(_wave_chunk& a){ *this=a; }; ~_wave_chunk(){}; _wave_chunk* operator = (const _wave_chunk *a) { *this=*a; return this; }; /*_wave_chunk* operator = (const _wave_chunk &a) { ...copy... return this; };*/
};
struct _wave_hdr
{
DWORD ids; // "RIFF"
DWORD len;
DWORD tps; // "WAVE"
_wave_hdr(){ ids='FFIR'; len=0; tps='EVAW'; }
_wave_hdr(_wave_hdr& a){ *this=a; }; ~_wave_hdr(){}; _wave_hdr* operator = (const _wave_hdr *a) { *this=*a; return this; }; /*_wave_hdr* operator = (const _wave_hdr &a) { ...copy... return this; };*/
};
struct _wave_fmt
{
DWORD ids; // "fmt "
DWORD len; // 16,18,40
WORD format; // 1 = PCM linear quantization
/* 0x0001 WAVE_FORMAT_PCM PCM
0x0003 WAVE_FORMAT_IEEE_FLOAT IEEE float
0x0006 WAVE_FORMAT_ALAW 8-bit ITU-T G.711 A-law
0x0007 WAVE_FORMAT_MULAW 8-bit ITU-T G.711 µ-law
0xFFFE WAVE_FORMAT_EXTENSIBLE Determined by SubFormat */
WORD chanels;
DWORD samplerate;
DWORD byterate;
WORD blockalign;
WORD bits;
WORD ext_len; // extension length 0,22
WORD ext_validbits;
DWORD ext_channelmask;
BYTE ext_subformat[16];
_wave_fmt(){ ids=' tmf'; len=16; format=1; chanels=1; samplerate=44100; bits=8; ext_len=0; ext_validbits=0; ext_channelmask=0; for (int i=0;i<16;i++) ext_subformat[i]=0; compute(); }
_wave_fmt(_wave_fmt& a){ *this=a; }; ~_wave_fmt(){}; _wave_fmt* operator = (const _wave_fmt *a) { *this=*a; return this; }; /*_wave_fmt* operator = (const _wave_fmt &a) { ...copy... return this; };*/
void compute()
{
byterate=(chanels*samplerate*bits)/8;
blockalign=(chanels*bits)/8;
}
};
struct _wave_dat
{
DWORD ids; // "data"
DWORD len;
_wave_dat(){ ids='atad'; len=0; }
_wave_dat(_wave_dat& a){ *this=a; }; ~_wave_dat(){}; _wave_dat* operator = (const _wave_dat *a) { *this=*a; return this; }; /*_wave_dat* operator = (const _wave_dat &a) { ...copy... return this; };*/
};
//---------------------------------------------------------------------------
class wave
{
public:
AnsiString name;
int hnd;
bool readonly;
_wave_hdr hdr;
_wave_fmt fmt;
_wave_dat dat;
wave();
~wave();
void create(AnsiString _name);
void write(BYTE *data,DWORD size);
bool open(AnsiString _name);
DWORD read(BYTE *data,DWORD size);
void close();
};
//---------------------------------------------------------------------------
wave::wave()
{
name=0;
hnd=-1;
readonly=true;
}
//---------------------------------------------------------------------------
wave::~wave()
{
close();
}
//---------------------------------------------------------------------------
void wave::create(AnsiString _name)
{
close();
readonly=true;
// hdr=_wave_hdr();
// fmt=_wave_fmt();
// dat=_wave_dat();
hdr.len=sizeof(hdr)-8;
dat.len=0;
fmt.compute();
name=_name;
hnd=FileCreate(name);
if (hnd<0) return;
FileWrite(hnd,&hdr,sizeof(hdr));
FileWrite(hnd,&fmt,fmt.len+8);
FileWrite(hnd,&dat,sizeof(dat));
readonly=false;
}
//---------------------------------------------------------------------------
bool wave::open(AnsiString _name)
{
close();
readonly=true;
name=_name;
hnd=FileOpen(name,fmOpenRead);
if (hnd<0) return false;
if (FileRead(hnd,&hdr,sizeof(hdr))<sizeof(hdr)){ close(); return false; }
if (hdr.ids!='FFIR') return false;
if (hdr.tps!='EVAW') return false;
_wave_chunk chk;
DWORD sz=sizeof(chk),l;
for(;;)
{
if (FileRead(hnd,&chk,sz)<sz){ close(); return false; }
if (chk.ids==' tmf')
{
fmt.ids=chk.ids;
fmt.len=chk.len;
if (FileRead(hnd,((BYTE*)&fmt)+sz,chk.len)<chk.len){ close(); return false; }
}
else if (chk.ids=='atad')
{
dat.ids=chk.ids;
dat.len=chk.len;
return true;
}
else FileSeek(hnd,int(chk.len),1);
}
}
//---------------------------------------------------------------------------
void wave::write(BYTE *data,DWORD size)
{
if (hnd<0) return;
hdr.len+=size;
dat.len+=size;
if (!readonly) FileWrite(hnd,data,size);
}
//---------------------------------------------------------------------------
DWORD wave::read(BYTE *data,DWORD size)
{
if (hnd<0) return 0;
return FileRead(hnd,data,size);
}
//---------------------------------------------------------------------------
void wave::close()
{
name="";
if (hnd<0) return;
FileSeek(hnd,0,0);
if (!readonly) FileWrite(hnd,&hdr,sizeof(hdr));
FileClose(hnd);
hnd=-1;
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
it does not support all the formats but should be enough to compare and repair your code resp. extract the addressing ... The code is C++/VCL based so you need to port the binary file access and AnsiString datatype to your environment ...
However negative value suggest you are using signed integers again (just like in your other question) so use unsigned datatype instead ... The offsets in RIFF Wave files are never negative !!!
[Edit1] I updated the code for newer formats.
After adding some extensions (see format info from first xbug's comment under OP) to the Wave format the format fmt chunk can have variable size now. Also they added few other chunks (related to compression) so in order to read reliably you need to pass all the RIFF chunks based on their chunk size until you hit the data chunk your data samples should follow...
My original code was for format 1.0 (as mention it was coded years ago) and did not load newer wave files properly (thx to dsp_user for spotting it). I updated it with the new formating so it should be safe to use again.

Related

Can I send a stream from blob data in a database to PlaySound (MMSystem) rather than supplying a file name?

I need to be able to supply a stand alone system for a medical application that will be distributed free of charge to home users of blood pressure monitors, it is being designed to run off a memory stick and taken to doctors, pharmacists, hospitals, I would like to have a database file of spoken instructions for data entry fields for those who are not computer savvy.
The system is being developed to capture a large amount of observed symptomatic data from the user in the home which can then be taken to a doctor to open in the system and see the observation data as taken at the time for actual review and diagnosis, it is intended for those who are interested in helping themselves by letting their doctors or any other health practitioner understand by knowing what the patient experienced during the pain, feelings and other important information a doctor may rely on that they have identified.
At the moment I have
String file = frmMDI->dlg->InitialDir + "\\sounds\\" + "ObservationDateTimeField.wav";
speech->Send(file);
What this does is speak out the instruction for what is expected in the field when it has received the focus, it works fine as it is but would like to get the wav from the database so there is minimal files being distributed via usb.
in the OnEnter method, speech is a custom control that manages my needs, simply the PlaySound(...) requirement for .wav files, other controls work out how and when to send instruction.
Edit: a
add screen shot of observations window
Screen capture of main observation entry
I would like to have this as open source but I have no idea how to get it there, it is a development of ideas spanning 20 years, my code may be left wanting but it all works and has done so for a long time.
How do I go about it, if it can be done?
I have not had time to try too many things like sending a stream instead of a file, playsound works fine from a .wav sound file
Edit: Thanks to Spektre for time and code, very much appreciated.
PlaySound() cannot play audio from a stream. It can play from a file, a block of memory, or an EXE/DLL resource.
I would not recommend storing your WAV audio in database blobs. Use files or resources instead. But, if you must use a database, you can use the TDataSet::CreateBlobStream() method to get a read-only TStream to access the blob data, and then you can Read() that data into an allocated memory buffer, or CopyFrom() it into a TMemoryStream, and then you can have PlaySound() play from the memory of that buffer/stream.
If you really need to play streaming audio, you will have to use waveOutOpen()/waveOutPrepareHeader()/waveOutWrite() directly, or use more modern APIs like DirectSound or XAudio2 instead.
from my experience WAVEIN/WAVEOUT is the best sound api on win for this purpose... DirectSound is(was) buggy and very big latency so I stopped using it for good years ago...
Here my ancient C++ lib I originally wrote for my oscilloscope,spectral analyzator,signal generator apps but using it everywhere even in my ZXSpectrum emulator:
waveout.h:
//---------------------------------------------------------------------------
//--- WAVE IN/OUT class ver: 4.02 -------------------------------------------
//---------------------------------------------------------------------------
#ifndef _waveout_h
#define _waveout_h
//---------------------------------------------------------------------------
#include <mmsystem.h>
#include "fifo.h"
#include "lock.h"
//---------------------------------------------------------------------------
void CALLBACK wave_in_event(HWAVEIN hw,UINT msg,DWORD inst,DWORD p1,DWORD p2);
void CALLBACK wave_out_event(HWAVEOUT hw,UINT msg,DWORD inst,DWORD p1,DWORD p2);
//---------------------------------------------------------------------------
class wave_in
{
public:
bool _init,_fifo;
FIFO<BYTE> fifo;
WAVEHDR *hdr;
HWAVEIN hw;
WAVEFORMATEX buff_format;
DWORD buff_size;
DWORD freq; WORD chanels,bits,samples,buffers;
BYTE **buff;
int adr,num;
void CALLBACK (*event)(HWAVEIN hw,UINT msg,DWORD inst,DWORD p1,DWORD p2);
// void (*onreceive)(wave_in *wi,BYTE *data,int size);
void (__closure *onreceive)(wave_in *wi,BYTE *data,int size);
wave_in()
{
hdr=NULL;
buff=NULL;
buffers=0;
buff_size=0;
_init=false;
adr=0;
num=0;
event=wave_in_event;
onreceive=NULL;
}
~wave_in()
{
_free();
}
wave_in(wave_in& a) { *this=a; }
wave_in* operator = (const wave_in *a) { *this=*a; return this; }
//wave_in* operator = (const wave_in &a) { ...copy... return this; }
void _free()
{
adr=0;
num=0;
if (_init) { waveInClose(hw); _init=false; }
#ifdef _mmap_h
if (buff) for (int i=0;i<buffers;i++) if (buff[i]) mmap_del(buff[i]);
if (buff) mmap_del(buff);
if (hdr ) mmap_del(hdr );
#endif
if (buff)
{
for (int i=0;i<buffers;i++) if (buff[i]) delete[] buff[i];
delete[] buff;
buff=NULL;
}
if (hdr) { delete[] hdr; hdr=NULL; }
}
void init(DWORD _freq,WORD _chanels,WORD _bits,WORD _samples,WORD _buffers=5)
{
int i,ret;
_free();
buffers=_buffers;
if (buffers<1) buffers=1;
hdr=new WAVEHDR[buffers];
buff=new BYTE*[buffers];
#ifdef _mmap_h
if (hdr ) mmap_new(hdr ,buffers*sizeof(WAVEHDR));
if (buff) mmap_new(buff,buffers*sizeof(BYTE*));
#endif
freq =_freq;
chanels =_chanels;
bits =_bits;
samples =_samples;
buff_size=(chanels*bits*samples)>>3;
samples=(buff_size<<3)/(chanels*bits);
for (i=0;i<buffers;i++) buff[i]=new BYTE[buff_size];
#ifdef _mmap_h
for (i=0;i<buffers;i++) if (buff[i]) mmap_new(buff[i],buff_size);
#endif
buff_format.wFormatTag =WAVE_FORMAT_PCM; // set buffer format
buff_format.nChannels =chanels;
buff_format.nSamplesPerSec =freq;
buff_format.wBitsPerSample =bits;
buff_format.cbSize =0;
buff_format.nAvgBytesPerSec =(freq*chanels*bits)>>3;
buff_format.nBlockAlign =(chanels*bits)>>3;
if (event) ret=waveInOpen(&hw,WAVE_MAPPER,&buff_format,(DWORD_PTR)event,(DWORD_PTR)this,CALLBACK_FUNCTION);
else ret=waveInOpen(&hw,WAVE_MAPPER,&buff_format,0,0,CALLBACK_NULL);
if (ret!=MMSYSERR_NOERROR)
{
// waveInGetErrorText(ret,err,255);
return;
}
WAVEHDR hdr0;
hdr0.dwBufferLength=buff_size;
hdr0.dwUser=0;
hdr0.dwFlags=0;
hdr0.dwLoops=0;
hdr0.lpNext=NULL;
for (i=0;i<buffers;i++)
{
hdr[i]=hdr0;
hdr[i].lpData=buff[i];
}
_init=true;
}
void add()
{
if (!_init) return;
int i,ret;
BYTE *p;
p=buff[adr];
ret=waveInPrepareHeader(hw,&hdr[adr],sizeof(WAVEHDR));
if (ret!=MMSYSERR_NOERROR)
{
// waveInGetErrorText(ret,err,255);
return;
}
waveInAddBuffer(hw,&hdr[adr],sizeof(WAVEHDR));
if (ret!=MMSYSERR_NOERROR)
{
// waveInGetErrorText(ret,err,255);
return;
}
adr++;
if (adr>=buffers) adr=0;
num++;
}
void start()
{
if (!_init) return;
while (num<buffers) add();
waveInStart(hw);
}
void stop()
{
if (!_init) return;
waveInStop(hw);
}
};
//---------------------------------------------------------------------------
class wave_out:public multi_lock
{
public:
bool _init,_fifo;
FIFO<BYTE> fifo;
WAVEHDR *hdr;
HWAVEOUT hw;
WAVEFORMATEX buff_format;
DWORD buff_size;
DWORD freq; WORD chanels,bits,samples,buffers;
BYTE **buff;
int adr,num,err;
void CALLBACK (*event)(HWAVEOUT hw,UINT msg,DWORD inst,DWORD p1,DWORD p2);
wave_out()
{
hdr=NULL;
buff=NULL;
buffers=0;
buff_size=0;
_init=false;
adr=0;
num=0;
err=0;
event=wave_out_event;
}
~wave_out()
{
_free();
}
wave_out(wave_out& a) { *this=a; }
wave_out* operator = (const wave_out *a) { *this=*a; return this; }
//wave_out* operator = (const wave_out &a) { ...copy... return this; }
void _free()
{
adr=0;
num=0;
if (_init) { waveOutClose(hw); _init=false; }
#ifdef _mmap_h
if (buff) for (int i=0;i<buffers;i++) if (buff[i]) mmap_del(buff[i]);
if (buff) mmap_del(buff);
if (hdr ) mmap_del(hdr );
#endif
if (buff)
{
for (int i=0;i<buffers;i++) if (buff[i]) delete[] buff[i];
delete[] buff;
buff=NULL;
}
if (hdr) { delete[] hdr; hdr=NULL; }
}
void init(DWORD _freq,WORD _chanels,WORD _bits,WORD _samples,WORD _buffers=10)
{
int i,ret;
_free();
buffers=_buffers;
if (buffers<1) buffers=1;
hdr=new WAVEHDR[buffers];
buff=new BYTE*[buffers];
#ifdef _mmap_h
if (hdr ) mmap_new(hdr ,buffers*sizeof(WAVEHDR));
if (buff) mmap_new(buff,buffers*sizeof(BYTE*));
#endif
freq =_freq;
chanels =_chanels;
bits =_bits;
samples =_samples;
buff_size=(chanels*bits*samples)>>3;
samples=(buff_size<<3)/(chanels*bits);
for (i=0;i<buffers;i++) buff[i]=new BYTE[buff_size];
#ifdef _mmap_h
for (i=0;i<buffers;i++) if (buff[i]) mmap_new(buff[i],buff_size);
#endif
buff_format.wFormatTag =WAVE_FORMAT_PCM; // set buffer format
buff_format.nChannels =chanels;
buff_format.nSamplesPerSec =freq;
buff_format.wBitsPerSample =bits;
buff_format.cbSize =0;
buff_format.nAvgBytesPerSec =(freq*chanels*bits)>>3;
buff_format.nBlockAlign =(chanels*bits)>>3;
if (event) ret=waveOutOpen(&hw,WAVE_MAPPER,&buff_format,(DWORD_PTR)event,(DWORD_PTR)this,CALLBACK_FUNCTION);
else ret=waveOutOpen(&hw,WAVE_MAPPER,&buff_format,0,0,CALLBACK_NULL);
if (ret!=MMSYSERR_NOERROR)
{
// waveOutGetErrorText(ret,err,255);
return;
}
WAVEHDR hdr0;
hdr0.dwBufferLength=buff_size;
hdr0.dwUser=0;
hdr0.dwFlags=WHDR_INQUEUE;
hdr0.dwLoops=0;
hdr0.lpNext=NULL;
for (i=0;i<buffers;i++)
{
hdr[i]=hdr0;
hdr[i].lpData=buff[i];
}
_init=true;
}
void send(BYTE *data)
{
if (!_init) return;
lock();
if (num>buffers)
{
err++;
adr=0;
num=0;
}
DWORD i;
int ret;
BYTE *p;
p=buff[adr];
for (i=0;i<buff_size;i++) p[i]=data[i];
ret=waveOutPrepareHeader(hw,&hdr[adr],sizeof(WAVEHDR));
if (ret!=MMSYSERR_NOERROR)
{
// waveOutGetErrorText(ret,err,255);
unlock();
return;
}
waveOutWrite(hw,&hdr[adr],sizeof(WAVEHDR));
if (ret!=MMSYSERR_NOERROR)
{
// waveOutGetErrorText(ret,err,255);
unlock();
return;
}
adr++;
if (adr>=buffers) adr=0;
num++;
unlock();
}
void stop()
{
waveOutReset(hw);
}
};
//---------------------------------------------------------------------------
void CALLBACK wave_in_event(HWAVEIN hw,UINT msg,DWORD inst,DWORD p1,DWORD p2)
{
wave_in *w=(wave_in*)(void*)(DWORD_PTR)inst;
if (w==NULL) return;
if (msg==WIM_OPEN); // open wave HW
if (msg==WIM_DATA) // wave data send done
{
int adr0=w->adr-w->num;
while (adr0>=w->buffers) adr0-=w->buffers;
while (adr0< 0) adr0+=w->buffers;
if (w->onreceive) w->onreceive(w,w->buff[adr0],w->buff_size);
w->num--;
}
if (msg==WIM_CLOSE); // close wave HW
}
//---------------------------------------------------------------------------
void CALLBACK wave_out_event(HWAVEOUT hw,UINT msg,DWORD inst,DWORD p1,DWORD p2)
{
wave_out *w=(wave_out*)(void*)(DWORD_PTR)inst;
if (w==NULL) return;
w->lock();
if (msg==WOM_OPEN); // open wave HW
if (msg==WOM_DONE) w->num--; // wave data send done
if (msg==WOM_CLOSE); // close wave HW
w->unlock();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
support file lock.h:
//---------------------------------------------------------------------------
//--- Multithread lock class ver 1.00 ---------------------------------------
//---------------------------------------------------------------------------
#ifndef _lock_h
#define _lock_h
//---------------------------------------------------------------------------
class single_lock
{
public:
CRITICAL_SECTION hnd;
single_lock() { InitializeCriticalSectionAndSpinCount(&hnd,0x00000400); }
~single_lock() { DeleteCriticalSection(&hnd); }
single_lock(single_lock& a) { *this=a; }
single_lock* operator = (const single_lock *a) { *this=*a; return this; }
// single_lock* operator = (const single_lock &a) { **** }
// thread safe functions
inline void lock() { EnterCriticalSection(&hnd); }
inline void unlock() { LeaveCriticalSection(&hnd); }
};
//---------------------------------------------------------------------------
const int _multi_lock_size=16; // max number of simultanious access
class multi_lock
{
public:
CRITICAL_SECTION hnd;
CRITICAL_SECTION dat[_multi_lock_size];
DWORD adr0,adr1,siz;
multi_lock() { InitializeCriticalSectionAndSpinCount(&hnd,0x00000400); for(int i=0;i<_multi_lock_size;i++) InitializeCriticalSectionAndSpinCount(&dat[i],0x00000400); adr0=0; adr1=0; siz=0; }
~multi_lock() { DeleteCriticalSection(&hnd); for(int i=0;i<_multi_lock_size;i++) DeleteCriticalSection(&dat[i]); }
multi_lock(multi_lock& a) { *this=a; }
multi_lock* operator = (const multi_lock *a) { *this=*a; return this; }
// multi_lock* operator = (const multi_lock &a) { **** }
// thread safe functions
inline void lock()
{
EnterCriticalSection(&hnd);
if (siz<_multi_lock_size)
{
siz++;
EnterCriticalSection(&dat[adr1]);
adr1++; if (adr1>=_multi_lock_size) adr1=0;
}
// else error();
LeaveCriticalSection(&hnd);
}
inline void unlock()
{
EnterCriticalSection(&hnd);
if (siz>0)
{
siz--;
LeaveCriticalSection(&dat[adr0]);
adr0++; if (adr0>=_multi_lock_size) adr0=0;
}
// else error();
LeaveCriticalSection(&hnd);
}
};
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
support file FIFO.h:
//---------------------------------------------------------------------------
//--- FIFO template class ver 2.08 ------------------------------------------
//---------------------------------------------------------------------------
#ifndef _fifo_h
#define _fifo_h
//---------------------------------------------------------------------------
//static bool _enable_fifo_debug=false;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
template <class T> class FIFO
{
public:
T *dat;
int adr0,adr1,size;
CRITICAL_SECTION lock;
FIFO() { dat=NULL; InitializeCriticalSectionAndSpinCount(&lock,0x00000400); alloc(16); }
~FIFO() { _free(); DeleteCriticalSection(&lock); }
FIFO(FIFO& a) { *this=a; }
FIFO* operator = (const FIFO *a){ *this=*a; return this; }
FIFO* operator = (const FIFO &a){ EnterCriticalSection(&a.lock); EnterCriticalSection(&lock); _alloc(a.size); adr0=a.adr0; adr1=a.adr1; for (int i=0;i<size;i++) dat[i]=a.dat[i]; LeaveCriticalSection(&lock); LeaveCriticalSection(&a.lock); return this; }
// already locked functions
inline int _adr_inc(int a) volatile { a++; if (a>=size) a=0; return a; }
inline int _adr_dec(int a) volatile { if (a!=adr0) a--; if (a<0) a=size-1; return a; }
inline void _alloc(int _size)volatile { if (dat) delete[] dat; dat=NULL; size=_size; adr0=0; adr1=0; dat=new T[size]; if (dat==NULL) size=0; _reset(); }
inline void _free() volatile { if (dat) delete[] dat; dat=NULL; size= 0; adr0=0; adr1=0; }
inline void _reset() volatile { adr0=0; adr1=0; }
inline void _in(T x) volatile { if (_is_full()) return; dat[adr1]=x; adr1=_adr_inc(adr1); }
inline T _out() volatile { if (_is_empty()){ T null; return null; } T x=dat[adr0]; adr0=_adr_inc(adr0); return x; }
inline T _peek_first() volatile { if (_is_empty()){ T null; return null; } T x=dat[adr0]; return x; }
inline T _peek_last() volatile { if (_is_empty()){ T null; return null; } int a=_adr_dec(adr1); T x=dat[a]; return x; }
inline bool _is_empty() volatile { bool ret=(adr0==adr1); return ret; }
inline bool _is_full() volatile { int a=_adr_inc(adr1); bool ret=(a==adr0); return ret; }
inline int _get_size() volatile { if (_is_empty()) return 0; if (_is_full()) return size; if (adr0<adr1) return adr1-adr0; else return size+adr1-adr0; }
// thread safe functions
void _lock() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); }
void _unlock() volatile { LeaveCriticalSection((CRITICAL_SECTION*)&lock); }
void alloc(int _size) volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); _alloc(_size); LeaveCriticalSection((CRITICAL_SECTION*)&lock); }
void reset() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); _reset(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); }
void in(T x) volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); _in(x); LeaveCriticalSection((CRITICAL_SECTION*)&lock); }
T out() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); T x=_out(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); return x; }
T peek_first() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); T x=_peek_first(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); return x; }
T peek_last() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); T x=_peek_last(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); return x; }
bool is_empty() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); bool x=_is_empty(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); return x; }
bool is_full() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); bool x=_is_full(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); return x; }
int get_size() volatile { EnterCriticalSection((CRITICAL_SECTION*)&lock); int x=_get_size(); LeaveCriticalSection((CRITICAL_SECTION*)&lock); return x; }
};
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
here simple usage (extracted from my generator hope I did not forget anything):
// globals and init
const int _size=20*1024;
wave_out wo;
WORD buffer[_size]; // your PCM sound data matching the init operands
wo.init(44100,2,16,_size,10); // sampling_freq,channels,bits,buffer size,buffers
// this in some timer or thread (fast enbough)
while (wo.num<4)
{
// here prepare buff[] data
wo.send((BYTE*)buff);
}
// force to stop on app exit or when needed
wo.stop();
The stuff was compiled on BDS2006 C++ Builder so in case you use newer compiler you might need to tweak some stuff...had not time will to port this to new compiler yet so if the case see this.
wave_out is for sound playback and wave_in is for recording both are using preferred windows sound device selected in control panel of Windows. To enable smooth playback just make sure that at least 4 buffers are filled in the sound que while (wo.num<4)... otherwise sound glitches might occur
You might also need to decode WAV files so here another ancient lib of mine RIFF.h:
//---------------------------------------------------------------------------
//--- RIFF WAVE format: 1.01 ------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _RIFF_h
#define _RIFF_h
//---------------------------------------------------------------------------
// 8bit PCM is unsigned
// 16bit PCM is signed 2'os complement little endian (big endian is RIFX)
//---------------------------------------------------------------------------
struct _wave_chunk
{
DWORD ids;
DWORD len;
_wave_chunk(){ ids=' '; len=0; }
_wave_chunk(_wave_chunk& a){ *this=a; }; ~_wave_chunk(){}; _wave_chunk* operator = (const _wave_chunk *a) { *this=*a; return this; }; /*_wave_chunk* operator = (const _wave_chunk &a) { ...copy... return this; };*/
};
struct _wave_hdr
{
DWORD ids; // "RIFF"
DWORD len;
DWORD tps; // "WAVE"
_wave_hdr(){ ids='FFIR'; len=0; tps='EVAW'; }
_wave_hdr(_wave_hdr& a){ *this=a; }; ~_wave_hdr(){}; _wave_hdr* operator = (const _wave_hdr *a) { *this=*a; return this; }; /*_wave_hdr* operator = (const _wave_hdr &a) { ...copy... return this; };*/
};
struct _wave_fmt
{
DWORD ids; // "fmt "
DWORD len; // 16,18,40
WORD format; // 1 = PCM linear quantization
/* 0x0001 WAVE_FORMAT_PCM PCM
0x0003 WAVE_FORMAT_IEEE_FLOAT IEEE float
0x0006 WAVE_FORMAT_ALAW 8-bit ITU-T G.711 A-law
0x0007 WAVE_FORMAT_MULAW 8-bit ITU-T G.711 µ-law
0xFFFE WAVE_FORMAT_EXTENSIBLE Determined by SubFormat */
WORD chanels;
DWORD samplerate;
DWORD byterate;
WORD blockalign;
WORD bits;
WORD ext_len; // extension length 0,22
WORD ext_validbits;
DWORD ext_channelmask;
BYTE ext_subformat[16];
_wave_fmt(){ ids=' tmf'; len=16; format=1; chanels=1; samplerate=44100; bits=8; ext_len=0; ext_validbits=0; ext_channelmask=0; for (int i=0;i<16;i++) ext_subformat[i]=0; compute(); }
_wave_fmt(_wave_fmt& a){ *this=a; }; ~_wave_fmt(){}; _wave_fmt* operator = (const _wave_fmt *a) { *this=*a; return this; }; /*_wave_fmt* operator = (const _wave_fmt &a) { ...copy... return this; };*/
void compute()
{
byterate=(chanels*samplerate*bits)/8;
blockalign=(chanels*bits)/8;
}
};
struct _wave_dat
{
DWORD ids; // "data"
DWORD len;
_wave_dat(){ ids='atad'; len=0; }
_wave_dat(_wave_dat& a){ *this=a; }; ~_wave_dat(){}; _wave_dat* operator = (const _wave_dat *a) { *this=*a; return this; }; /*_wave_dat* operator = (const _wave_dat &a) { ...copy... return this; };*/
};
//---------------------------------------------------------------------------
class wave
{
public:
AnsiString name;
int hnd;
bool readonly;
_wave_hdr hdr;
_wave_fmt fmt;
_wave_dat dat;
wave();
~wave();
void create(AnsiString _name);
void write(BYTE *data,DWORD size);
bool open(AnsiString _name);
DWORD read(BYTE *data,DWORD size);
void close();
};
//---------------------------------------------------------------------------
wave::wave()
{
name=0;
hnd=-1;
readonly=true;
}
//---------------------------------------------------------------------------
wave::~wave()
{
close();
}
//---------------------------------------------------------------------------
void wave::create(AnsiString _name)
{
close();
readonly=true;
// hdr=_wave_hdr();
// fmt=_wave_fmt();
// dat=_wave_dat();
hdr.len=sizeof(hdr)-8;
dat.len=0;
fmt.compute();
name=_name;
hnd=FileCreate(name);
if (hnd<0) return;
FileWrite(hnd,&hdr,sizeof(hdr));
FileWrite(hnd,&fmt,fmt.len+8);
FileWrite(hnd,&dat,sizeof(dat));
readonly=false;
}
//---------------------------------------------------------------------------
bool wave::open(AnsiString _name)
{
close();
readonly=true;
name=_name;
hnd=FileOpen(name,fmOpenRead);
if (hnd<0) return false;
if (FileRead(hnd,&hdr,sizeof(hdr))<sizeof(hdr)){ close(); return false; }
if (hdr.ids!='FFIR') return false;
if (hdr.tps!='EVAW') return false;
_wave_chunk chk;
DWORD sz=sizeof(chk),l;
for(;;)
{
if (FileRead(hnd,&chk,sz)<sz){ close(); return false; }
if (chk.ids==' tmf')
{
fmt.ids=chk.ids;
fmt.len=chk.len;
if (FileRead(hnd,((BYTE*)&fmt)+sz,chk.len)<chk.len){ close(); return false; }
}
else if (chk.ids=='atad')
{
dat.ids=chk.ids;
dat.len=chk.len;
return true;
}
else FileSeek(hnd,int(chk.len),1);
}
}
//---------------------------------------------------------------------------
void wave::write(BYTE *data,DWORD size)
{
if (hnd<0) return;
hdr.len+=size;
dat.len+=size;
if (!readonly) FileWrite(hnd,data,size);
}
//---------------------------------------------------------------------------
DWORD wave::read(BYTE *data,DWORD size)
{
if (hnd<0) return 0;
return FileRead(hnd,data,size);
}
//---------------------------------------------------------------------------
void wave::close()
{
name="";
if (hnd<0) return;
FileSeek(hnd,0,0);
if (!readonly) FileWrite(hnd,&hdr,sizeof(hdr));
FileClose(hnd);
hnd=-1;
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
And usage:
// globals
wave wav;
wave_out wo;
BYTE *buff;
// init
wav.open("tetris2.wav"); // any file ...
wo.init(wav.fmt.samplerate,wav.fmt.chanels,wav.fmt.bits,dt*wav.fmt.samplerate/1000,10);
buff=new BYTE[wo.buff_size];
// timer 20ms ...
while (wo.num<4)
{
wav.read(buff,wo.buff_size);
wo.send(buff);
}
// exit
wo.stop();
wav.close();
delete[] buff;
the wav.read(...) returns number of BYTEs read so once it hit less than buffer size or even equals to zero it means you already on end of wavefile ... Now as you will have the wav in memory just rewrite the file access to memory access ...

std::atomic on struct bit-fields

I'm modifying some existing open source library and there is a struct (say named as Node) containing bit-fields, e.g.
struct Node {
std::atomic<uint32_t> size:30;
std::atomic<uint32_t> isnull:1;
};
To fit my needs, these fields need to be atomic so I was expecting to use std::atomic for this and faced compile time error:
bit-field 'size' has non-integral type 'std::atomic<uint32_t>'
According to documentation, there is a restricted set of types which can be used for std::atomic
Can anyone advise/have idea on how to get functionality of atomic fields with the minimum impact to the existing source code?
Thanks in advance!
I used an unsigned short as an example below.
This is less ideal, but you could sacrifice 8 bits and insert a std::atomic_flag in the bit field with a union. Unfortunately, std::atomic_flag type is a std::atomic_bool type.
This structure can be spin locked manually every time you access it. However, the code should have minimal performance degradation (unlike creating, locking, unlocking, destroying with a std::mutex and std::unique_lock).
This code may waste about 10-30 clock cycles to enable low cost multi-threading.
PS. Make sure the reserved 8 bits below are not messed up by the endian structure of the processor. You may have to define at the end for big-endian processors. I only tested this code on an Intel CPU (always little-endian).
#include <iostream>
#include <atomic>
#include <thread>
union Data
{
std::atomic_flag access = ATOMIC_FLAG_INIT; // one byte
struct
{
typedef unsigned short ushort;
ushort reserved : 8;
ushort count : 4;
ushort ready : 1;
ushort unused : 3;
} bits;
};
class SpinLock
{
public:
inline SpinLock(std::atomic_flag &access, bool locked=true)
: mAccess(access)
{
if(locked) lock();
}
inline ~SpinLock()
{
unlock();
}
inline void lock()
{
while (mAccess.test_and_set(std::memory_order_acquire))
{
}
}
// each attempt will take about 10-30 clock cycles
inline bool try_lock(unsigned int attempts=0)
{
while(mAccess.test_and_set(std::memory_order_acquire))
{
if (! attempts) return false;
-- attempts;
}
return true;
}
inline void unlock()
{
mAccess.clear(std::memory_order_release);
}
private:
std::atomic_flag &mAccess;
};
void aFn(int &i, Data &d)
{
SpinLock lock(d.access, false);
// manually locking/unlocking can be tighter
lock.lock();
if (d.bits.ready)
{
++d.bits.count;
}
d.bits.ready ^= true; // alternate each time
lock.unlock();
}
int main(void)
{
Data f;
f.bits.count = 0;
f.bits.ready = true;
std::thread *p[8];
for (int i = 0; i < 8; ++ i)
{
p[i] = new std::thread([&f] (int i) { aFn(i, f); }, i);
}
for (int i = 0; i < 8; ++i)
{
p[i]->join();
delete p[i];
}
std::cout << "size: " << sizeof(f) << std::endl;
std::cout << "count: " << f.bits.count << std::endl;
}
The result is as expected...
size: 2
count: 4

Windows Thumbnail Handler Never Gets Called

I'm writing a thumbnail handler for a custom file type. When I register it, it returns with a success message. The "thumbnail" is shown as completely blank, but it does not say "no thumbnail available". The problem is, the actual thumbnail handler never gets called. I know this, because I put a series of statements in my handler under handler::initialize, handler::queryinterface and handler::getthumbnail. I have gone through most of this documentation., but all I can find is that I need to use initialize and getthumbnail. Here is my code:
Header:
#pragma once
#include <windows.h>
#include <thumbcache.h> // For IThumbnailProvider
#include <wincodec.h> // Windows Imaging Codecs
#include <fstream>
#include <iostream>
#pragma comment(lib, "windowscodecs.lib")
class ThumbnailProvider :
public IInitializeWithStream,
public IThumbnailProvider
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IInitializeWithStream
IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);
// IThumbnailProvider
IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha);
ThumbnailProvider();
protected:
~ ThumbnailProvider();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
IStream *m_pStream;
std::ofstream output;
void stripImageFrom (IStream *stream, HBITMAP *phbmp);
};
Body:
#include " ThumbnailProvider.h"
#include <Shlwapi.h>
#include <Wincrypt.h> // For CryptStringToBinary.
#include <msxml6.h>
#include <atlimage.h>
#include <fstream>
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "msxml6.lib")
extern HINSTANCE g_hInst;
extern long g_cDllRef;
ThumbnailProvider:: ThumbnailProvider() : m_cRef(1), m_pStream(NULL)
{
std::ofstream st;
st.open("C:\\Users\\labs\\Desktop\\Output\\out.txt", std::ios_base::app);
st << "Made provider";
st.close();
InterlockedIncrement(&g_cDllRef);
}
ThumbnailProvider::~ ThumbnailProvider()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
// Query to the interface the component supported.
IFACEMETHODIMP ThumbnailProvider::QueryInterface(REFIID riid, void **ppv)
{
std::ofstream st;
st.open("C:\\Users\\labs\\Desktop\\Output\\out.txt", std::ios_base::app);
st << "Querying interface";
st.close();
static const QITAB qit[] =
{
QITABENT( ThumbnailProvider, IThumbnailProvider),
QITABENT( ThumbnailProvider, IInitializeWithStream),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
// Increase the reference count for an interface on an object.
IFACEMETHODIMP_(ULONG) ThumbnailProvider::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
// Decrease the reference count for an interface on an object.
IFACEMETHODIMP_(ULONG) ThumbnailProvider::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializeWithStream
// Initializes the thumbnail handler with a stream.
IFACEMETHODIMP ThumbnailProvider::Initialize(IStream *pStream, DWORD grfMode)
{
std::ofstream st;
st.open("C:\\Users\\labs\\Desktop\\Output\\out.txt", std::ios_base::app);
st << "Got to initialization";
st.close();
// A handler instance should be initialized only once in its lifetime.
HRESULT hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
if (m_pStream == NULL)
{
// Take a reference to the stream if it has not been initialized yet.
hr = pStream->QueryInterface(&m_pStream);
}
return hr;
}
#pragma endregion
#pragma region IThumbnailProvider
// Gets a thumbnail image and alpha type. The GetThumbnail is called with the
// largest desired size of the image, in pixels. Although the parameter is
// called cx, this is used as the maximum size of both the x and y dimensions.
// If the retrieved thumbnail is not square, then the longer axis is limited
// by cx and the aspect ratio of the original image respected. On exit,
// GetThumbnail provides a handle to the retrieved image. It also provides a
// value that indicates the color at of the image and whether it has
// valid alpha in ation.
IFACEMETHODIMP ThumbnailProvider::GetThumbnail(UINT cx, HBITMAP *phbmp,
WTS_ALPHATYPE *pdwAlpha) {
std::ofstream st;
st.open("C:\\Users\\labs\\Desktop\\Output\\out.txt", std::ios_base::app);
st << "Getting thumbnail";
st.close();
ThumbnailProvider::stripImageFrom (m_pStream, phbmp);
cx = 1024 * 1024;
*pdwAlpha = WTSAT_UNKNOWN;
return S_OK;
}
#pragma endregion
#pragma region Helper Functions
// The PNG signature is 137 80 78 71 13 10 26 10. This does not make sense to do backward. I will do it forward.
void ThumbnailProvider::stripImageFrom (IStream *stream, HBITMAP *phbmp) {
unsigned long numBytes = 0;
unsigned long *numBytesPtr = &numBytes;
char *chptr = nullptr;
byte vals[8] = { 0 };
STATSTG *stat = nullptr;
DWORD temp = NULL;
stream->Stat(stat, temp);
unsigned long long length = stat->cbSize.QuadPart;
unsigned long long i;
for (i = 0; i<length; i++) {
stream->Read(chptr, 1, numBytesPtr);
if (*chptr == 137) {
vals[0] = 1;
}
else if (*chptr == 80 && vals[0]) {
vals[1] = 1;
}
else if (*chptr == 78 && vals[1]) {
vals[2] = 1;
}
else if (*chptr == 71 && vals[2]) {
vals[3] = 1;
}
else if (*chptr == 13 && vals[3]) {
vals[4] = 1;
}
else if (*chptr == 10 && vals[4] && !vals[5]) {
vals[5] = 1;
}
else if (*chptr == 26 && vals[5]) {
vals[6] = 1;
}
else if (*chptr == 10 && vals[6]) {
vals[7] = 1;
i -= 7;
break;
}
else {
memset(vals, 0, 8 * sizeof(vals[0]));
}
}
if (vals[7]) {
IStream *imgstream = nullptr;
stream->Read(imgstream, length-i, numBytesPtr);
CImage *img = nullptr;
img->Load(imgstream);
*phbmp = *img;
}
}
#pragma endregion
Everything else is just edited from one of Microsoft's examples, so I am pretty sure that is ok. The example is here.
If you're having this problem, make sure you're building for the right architecture. That solved it for me.

bitwise manipulation via operator[]

I'm writing a simple bitset wrapper to easily and efficiently set, clear and read bits from an 8-bit integer. I would do these three operations via operator[], but I'm stucked, and honestly I'm not sure it is possible without losing performances (really important for my purposes).
#include <stdint.h>
#include <iostream>
class BitMask {
private:
uint8_t mask;
public:
inline void set(int bit) { mask |= 1 << bit; } // deprecated
inline void clear(int bit) { mask &= ~(1 << bit); } // deprecated
inline int read(int bit) { return (mask >> bit) & 1; } // deprecated
bool operator[](int bit) const { return (mask >> bit) & 1; } // new way to read
??? // new way to write
friend std::ostream& operator<<(std::ostream& os, const BitMask& bitmask) {
for (int bit = 0; bit < 8; ++bit)
os << ((bitmask.mask >> bit) & 1 ? "1" : "0");
return os;
}
};
int main() {
BitMask bitmask1;
bitmask1.set(3);
bitmask1.clear(3);
bitmask1.read(3);
std::cout << bitmask1;
BitMask bitmask2;
bitmask2[3] = 1; // set
bitmask2[3] = 0; // clear
bitmask2[3]; // read
std::cout << bitmask2;
}
Any idea?
One way (the only way?) is to return a proxy object from your operator[] which will hold index for your bit, this way you will assign new value to your proxy object which will alter appropriate BitMask bit. For example see here: Vector, proxy class and dot operator in C++
As for a performance - it all depends how compiler will optimize your code, if your proxy class would have only inline methods then it should be fast.
Below is example how to fix your code:
#include <stdint.h>
#include <iostream>
class BitMask {
private:
uint8_t mask;
public:
inline void set(int bit) { mask |= 1 << bit; } // deprecated
inline void clear(int bit) { mask &= ~(1 << bit); } // deprecated
inline int read(int bit) const { return (mask >> bit) & 1; } // deprecated
struct proxy_bit
{
BitMask& bitmask;
int index;
proxy_bit(BitMask& p_bitmask, int p_index) : bitmask(p_bitmask), index(p_index) {}
proxy_bit& operator=(int rhs) {
if (rhs)
bitmask.set(index);
else
bitmask.clear(index);
return *this;
}
operator int() {
return bitmask.read(index);
}
};
proxy_bit operator[](int bit) { return proxy_bit(*this, bit); } // new way to read
int operator[](int bit) const { return read(bit); } // new way to read
friend std::ostream& operator<<(std::ostream& os, const BitMask& bitmask) {
for (int bit = 0; bit < 8; ++bit)
os << ((bitmask.mask >> bit) & 1 ? "1" : "0");
return os;
}
};
int main() {
BitMask bitmask1;
bitmask1.set(3);
bitmask1.clear(3);
bitmask1.read(3);
std::cout << bitmask1 << std::endl;
BitMask bitmask2;
bitmask2[3] = 1; // set
bitmask2[3] = 0; // clear
bitmask2[3]; // read
std::cout << bitmask2 << std::endl;
const BitMask bitmask3;
if (bitmask3[3]) {}
//bitmask3[3] = 1; // compile error - OK!
}

how to use boost::asio::async_read_until with an external memory address as a buffer

async_read_until expects a basic_streambuf into which the data will be read. I don't want to allocate additional memory, but using a memory address (from a specified interface that I'm not allowed to change) as the target buffer.
Is it possible to create a streambuf with an external memory address or do I need to write a wrapper-class?
Finally solved the issue by writing my own async_read_until_delim class which expects a memory-pointer and a maximum value of bytes to read. It's as close as possible to the original boost implementation, but has a few adjustments which should lead to a more performant execution.
namespace {
template<typename read_handler>
class async_read_until_delim
{
public:
async_read_until_delim(tcp::socket& socket, void* buffer, std::size_t max_read_size_in_bytes,
char delim, read_handler& handler)
: m_socket(socket), m_cur(static_cast<char*>(buffer)),
m_end(static_cast<char*>(buffer) + max_read_size_in_bytes), m_delim(delim),
m_handler(handler), m_pos(0)
{
read_some();
}
async_read_until_delim(async_read_until_delim const& other)
: m_socket(other.m_socket), m_cur(other.m_cur), m_end(other.m_end), m_delim(other.m_delim),
m_handler(other.m_handler), m_pos(other.m_pos)
{
}
void operator()(boost::system::error_code const& error, std::size_t bytes_transferred)
{
if (!error)
{
if (std::find(m_cur, m_end, m_delim) != m_end)
{
m_handler(error, m_pos + bytes_transferred);
return;
}
else if (m_cur == m_end)
{
m_handler(boost::asio::error::not_found, -1);
return;
}
m_cur += bytes_transferred;
m_pos += bytes_transferred;
read_some();
}
else
m_handler(error, m_pos);
}
private:
void read_some()
{
m_socket.async_read_some(
boost::asio::buffer(m_cur, m_end - m_cur), async_read_until_delim(*this));
}
tcp::socket& m_socket;
char *m_cur,
*m_end;
char m_delim;
read_handler m_handler;
std::size_t m_pos;
};
template<typename read_handler>
inline void do_async_read_until_delim(tcp::socket& socket, void* buffer, std::size_t max_read_size_in_bytes,
char delim, read_handler& handler)
{
async_read_until_delim<read_handler>(socket, buffer, max_read_size_in_bytes, delim, handler);
}
} /* anonymous namespace */
So, I hope it will be usefull for someone too.

Resources