low latency player

This commit is contained in:
Arvid E. Picciani 2007-01-10 22:45:28 +00:00 committed by Arvid Ephraim Picciani
parent f5a8801b7d
commit 80be88b612
8 changed files with 225 additions and 172 deletions

View File

@ -46,7 +46,9 @@ width="32" height="32" border="0" /></a></td>
<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#95e8927cc6986fe71853faae4b440852">eof</a> ()</td></tr>
<tr><td colspan="2"><br><h2>Public Member Functions</h2></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#da64cba9622acf00e9f835be71191421">QxtAVFile</a> (QString filename, int fliplen, QObject *parent=0)</td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#fc7c8f10ac9e4adf3fbd2fec3d1a494f">QxtAVFile</a> (QString filename, int fliplen, int flags=0, QObject *parent=0)</td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#cac3319a183995c8c7f25d6156833ab9">QxtAVFile</a> (QString filename, <a class="el" href="classQxtAudioPlayer.html">QxtAudioPlayer</a> *, int flags=0, QObject *parent=0)</td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#9a2f34df148cced0108370e82310dcdd">flip</a> (float *)</td></tr>
@ -64,6 +66,10 @@ double&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="cl
double&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#fd56df1972ba3b6da0522dfc0c719061">length</a> ()</td></tr>
<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">playback length of the file in seconds. <br></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="7a1d2b1184eee20305f2107aaeed972d"></a><!-- doxytag: member="QxtAVFile::reset" ref="7a1d2b1184eee20305f2107aaeed972d" args="()" -->
void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAVFile.html#7a1d2b1184eee20305f2107aaeed972d">reset</a> ()</td></tr>
<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">reset <br></td></tr>
</table>
<hr><a name="_details"></a><h2>Detailed Description</h2>
AV decoder.
@ -76,7 +82,7 @@ the buffer is interleaved (alternating right/left) and always stereo. the fliple
<p>
<hr><h2>Constructor &amp; Destructor Documentation</h2>
<a class="anchor" name="da64cba9622acf00e9f835be71191421"></a><!-- doxytag: member="QxtAVFile::QxtAVFile" ref="da64cba9622acf00e9f835be71191421" args="(QString filename, int fliplen, QObject *parent=0)" -->
<a class="anchor" name="fc7c8f10ac9e4adf3fbd2fec3d1a494f"></a><!-- doxytag: member="QxtAVFile::QxtAVFile" ref="fc7c8f10ac9e4adf3fbd2fec3d1a494f" args="(QString filename, int fliplen, int flags=0, QObject *parent=0)" -->
<div class="memitem">
<div class="memproto">
<table class="memname">
@ -92,6 +98,12 @@ the buffer is interleaved (alternating right/left) and always stereo. the fliple
<td class="paramtype">int&nbsp;</td>
<td class="paramname"> <em>fliplen</em>, </td>
</tr>
<tr>
<td class="paramkey"></td>
<td></td>
<td class="paramtype">int&nbsp;</td>
<td class="paramname"> <em>flags</em> = <code>0</code>, </td>
</tr>
<tr>
<td class="paramkey"></td>
<td></td>
@ -111,6 +123,47 @@ the buffer is interleaved (alternating right/left) and always stereo. the fliple
default constructor. pass filename and fliplen.
</div>
</div><p>
<a class="anchor" name="cac3319a183995c8c7f25d6156833ab9"></a><!-- doxytag: member="QxtAVFile::QxtAVFile" ref="cac3319a183995c8c7f25d6156833ab9" args="(QString filename, QxtAudioPlayer *, int flags=0, QObject *parent=0)" -->
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">QxtAVFile::QxtAVFile </td>
<td>(</td>
<td class="paramtype">QString&nbsp;</td>
<td class="paramname"> <em>filename</em>, </td>
</tr>
<tr>
<td class="paramkey"></td>
<td></td>
<td class="paramtype"><a class="el" href="classQxtAudioPlayer.html">QxtAudioPlayer</a> *&nbsp;</td>
<td class="paramname">, </td>
</tr>
<tr>
<td class="paramkey"></td>
<td></td>
<td class="paramtype">int&nbsp;</td>
<td class="paramname"> <em>flags</em> = <code>0</code>, </td>
</tr>
<tr>
<td class="paramkey"></td>
<td></td>
<td class="paramtype">QObject *&nbsp;</td>
<td class="paramname"> <em>parent</em> = <code>0</code></td><td>&nbsp;</td>
</tr>
<tr>
<td></td>
<td>)</td>
<td></td><td></td><td width="100%"></td>
</tr>
</table>
</div>
<div class="memdoc">
<p>
default constructor. if you just use <a class="el" href="classQxtAVFile.html">QxtAVFile</a> for playback with <a class="el" href="classQxtAudioPlayer.html">QxtAudioPlayer</a>, use this ctor to use the default fliplen
</div>
</div><p>
<hr><h2>Member Function Documentation</h2>
<a class="anchor" name="9a2f34df148cced0108370e82310dcdd"></a><!-- doxytag: member="QxtAVFile::flip" ref="9a2f34df148cced0108370e82310dcdd" args="(float *)" -->
<div class="memitem">
@ -169,7 +222,7 @@ Set it to indicate you want the output to be resampled to a specific samplerate.
<div class="memdoc">
<p>
End of File Signal. There is still data available in the pipe, this is just to inform you the file has been fully read
End of File Signal.
</div>
</div><p>
<address><hr />

View File

@ -43,7 +43,8 @@ width="32" height="32" border="0" /></a></td>
<table border="0" cellpadding="0" cellspacing="0">
<tr><td></td></tr>
<tr><td colspan="2"><br><h2>Public Slots</h2></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">static void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="classQxtAudioPlayer.html#29a6ab8036a27218930caf460dd28b1a">play</a> (QString url)</td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="7eb277952135e05f564372163e44f935"></a><!-- doxytag: member="QxtAudioPlayer::play" ref="7eb277952135e05f564372163e44f935" args="(QxtAVFile *file)" -->
void&nbsp;</td><td class="memItemRight" valign="bottom"><b>play</b> (<a class="el" href="classQxtAVFile.html">QxtAVFile</a> *file)</td></tr>
<tr><td colspan="2"><br><h2>Public Member Functions</h2></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="7b74eb59ec3dc3d192257bd9bf664e25"></a><!-- doxytag: member="QxtAudioPlayer::QxtAudioPlayer" ref="7b74eb59ec3dc3d192257bd9bf664e25" args="(QObject *parent=0)" -->
@ -53,32 +54,12 @@ width="32" height="32" border="0" /></a></td>
<hr><a name="_details"></a><h2>Detailed Description</h2>
simple player using the <a class="el" href="classQxtAVFile.html">QxtAVFile</a> and portaudio
<p>
sometimes you just want to play a sound in your application. this is not meant to be a full blown media player and does not provide any functions for it.
sometimes you just want to play a sound in your application. this is not meant to be a full blown media player and does not provide any functions for it.<p>
example: <div class="fragment"><pre class="fragment"><a class="code" href="classQxtAudioPlayer.html">QxtAudioPlayer</a> player;
<a class="code" href="classQxtAVFile.html">QxtAVFile</a> file(<span class="stringliteral">"test.wav"</span>,&amp;player,Qxt::preload);
player.<a class="code" href="classQxtAudioPlayer.html#7eb277952135e05f564372163e44f935">play</a>(&amp;file);
</pre></div>
<p>
<hr><h2>Member Function Documentation</h2>
<a class="anchor" name="29a6ab8036a27218930caf460dd28b1a"></a><!-- doxytag: member="QxtAudioPlayer::play" ref="29a6ab8036a27218930caf460dd28b1a" args="(QString url)" -->
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">void QxtAudioPlayer::play </td>
<td>(</td>
<td class="paramtype">QString&nbsp;</td>
<td class="paramname"> <em>url</em> </td>
<td>&nbsp;)&nbsp;</td>
<td width="100%"><code> [static, slot]</code></td>
</tr>
</table>
</div>
<div class="memdoc">
<p>
usage:<p>
<div class="fragment"><pre class="fragment"> <a class="code" href="classQxtAudioPlayer.html#29a6ab8036a27218930caf460dd28b1a">QxtAudioPlayer::play</a>(<span class="stringliteral">"sound/ding.wav"</span>);
</pre></div> obviously you do not need to construct an object<p>
calling this slot while another file is playing will interupt the current playback
</div>
</div><p>
<address><hr />
<div align="center">

View File

@ -160,8 +160,6 @@ Here is a list of all documented struct and union fields with links to the struc
: <a class="el" href="classQxtRPCPeer.html#e3fd8e63aceea84eef2a70a8fa50418d">QxtRPCPeer</a>
<li>peerError()
: <a class="el" href="classQxtRPCPeer.html#cd89d8b304d38cfff4fef3e0c513e1da">QxtRPCPeer</a>
<li>play()
: <a class="el" href="classQxtAudioPlayer.html#29a6ab8036a27218930caf460dd28b1a">QxtAudioPlayer</a>
</ul>
<h3><a class="anchor" name="index_q">- q -</a></h3><ul>
<li>qMakeTripple
@ -185,7 +183,7 @@ Here is a list of all documented struct and union fields with links to the struc
<li>Qxt9Tuple
: <a class="el" href="classQxtTuple.html#17446ec67b52e60a1ca648fa863fe240">QxtTuple&lt; TYPELIST &gt;</a>
<li>QxtAVFile()
: <a class="el" href="classQxtAVFile.html#da64cba9622acf00e9f835be71191421">QxtAVFile</a>
: <a class="el" href="classQxtAVFile.html#cac3319a183995c8c7f25d6156833ab9">QxtAVFile</a>
<li>QxtLongTuple
: <a class="el" href="classQxtTuple.html#8e0bb5049828a909bfd4935bdf9221f9">QxtTuple&lt; TYPELIST &gt;</a>
<li>qxtNull
@ -205,6 +203,8 @@ Here is a list of all documented struct and union fields with links to the struc
, <a class="el" href="classQxtTrippleList.html#9cac4c23fec929ba15809689c166b830">QxtTrippleList&lt; T, K, L &gt;</a>
<li>resample()
: <a class="el" href="classQxtAVFile.html#6212b0878e52ce9af097a65aa46df076">QxtAVFile</a>
<li>reset()
: <a class="el" href="classQxtAVFile.html#7a1d2b1184eee20305f2107aaeed972d">QxtAVFile</a>
<li>rowCount()
: <a class="el" href="classQxtSqlPackageModel.html#f9be109f83c28eb658f5cd21e32b8081">QxtSqlPackageModel</a>
<li>rpcType()

View File

@ -158,12 +158,10 @@ width="32" height="32" border="0" /></a></td>
: <a class="el" href="classQxtRPCPeer.html#e3fd8e63aceea84eef2a70a8fa50418d">QxtRPCPeer</a>
<li>peerError()
: <a class="el" href="classQxtRPCPeer.html#cd89d8b304d38cfff4fef3e0c513e1da">QxtRPCPeer</a>
<li>play()
: <a class="el" href="classQxtAudioPlayer.html#29a6ab8036a27218930caf460dd28b1a">QxtAudioPlayer</a>
</ul>
<h3><a class="anchor" name="index_q">- q -</a></h3><ul>
<li>QxtAVFile()
: <a class="el" href="classQxtAVFile.html#da64cba9622acf00e9f835be71191421">QxtAVFile</a>
: <a class="el" href="classQxtAVFile.html#fc7c8f10ac9e4adf3fbd2fec3d1a494f">QxtAVFile</a>
<li>QxtRPCPeer()
: <a class="el" href="classQxtRPCPeer.html#2ca9ef835583cbbb61dae031effea560">QxtRPCPeer</a>
<li>QxtSignalWaiter()
@ -179,6 +177,8 @@ width="32" height="32" border="0" /></a></td>
, <a class="el" href="classQxtTrippleList.html#9cac4c23fec929ba15809689c166b830">QxtTrippleList&lt; T, K, L &gt;</a>
<li>resample()
: <a class="el" href="classQxtAVFile.html#6212b0878e52ce9af097a65aa46df076">QxtAVFile</a>
<li>reset()
: <a class="el" href="classQxtAVFile.html#7a1d2b1184eee20305f2107aaeed972d">QxtAVFile</a>
<li>rowCount()
: <a class="el" href="classQxtSqlPackageModel.html#f9be109f83c28eb658f5cd21e32b8081">QxtSqlPackageModel</a>
<li>rpcType()

View File

@ -41,9 +41,13 @@ static soundtouch::SoundTouch resampler;
//-------------------------------------------------------------
QxtAVFile::QxtAVFile(QString filename,QxtAudioPlayer*,int flags,QObject *parent)
{
QxtAVFile::QxtAVFile(filename,2048,flags,parent);
}
QxtAVFile::QxtAVFile(QString filename,int fliplen,QObject *parent):QThread(parent)
QxtAVFile::QxtAVFile(QString filename,int fliplen,int flags,QObject *parent):QThread(parent)
{
///defaults
AORatio=1.0;
@ -54,7 +58,8 @@ QxtAVFile::QxtAVFile(QString filename,int fliplen,QObject *parent):QThread(paren
resample_m=0;
playbacktime=0.0;
eof_f=false;
flags_d=flags;
blocked =false;
/// \bug buffsize must be at least 2048
assert(fliplen>=2048);
assert(!filename.isEmpty());
@ -92,11 +97,19 @@ QxtAVFile::QxtAVFile(QString filename,int fliplen,QObject *parent):QThread(paren
OUT1= new float[fliplen_m+MINIMAL_SRC_LEN];
OUT2= new float[fliplen_m+MINIMAL_SRC_LEN];
for (unsigned int i=0;i<fliplen_m;i++)
if (flags & Qxt::preload)
{
refill(OUT1);
refill(OUT2);
}
else
{
OUT1[i]=0;
OUT2[i]=0;
for (unsigned int i=0;i<fliplen_m;i++)
{
OUT1[i]=0;
OUT2[i]=0;
}
}
DSRC=new float[fliplen_m*8];
@ -180,6 +193,12 @@ double QxtAVFile::length()
//-------------------------------------------------------------
int QxtAVFile::flip(float* out)
{
if (blocked)
{
for (unsigned int i=0;i<fliplen_m;i++)
*out++=0.0f;
return fliplen_m;
}
if (eof_f && HF==WF){emit(eof());qWarning("called flip after eof, doing nothing.");return -1;}
@ -190,7 +209,6 @@ int QxtAVFile::flip(float* out)
{
decoderlock_b=OUT2;
memcpy(out,OUT1,fliplen_m*sizeof(float));
WF=false;
}
else
@ -288,12 +306,12 @@ void QxtAVFile::run()
{
forever
{
if (eof_f)return;
if (decoderlock_b==NULL)
{msleep(10);continue;}
HF=(decoderlock_b==OUT2);
refill(decoderlock_b);
decoderlock_b=NULL;
if (eof_f)return;
}
}
@ -383,8 +401,47 @@ void QxtAVFile::refill(float * WRITE)
//-------------------------------------------------------------
void QxtAVFile::reset()
{
blocked=true;
eof_f=true;
decoderlock_b=NULL;
wait();
resampler.clear();
seek(0.0);
playbacktime=0.0;
DSRC_LEN=0;
if (flags_d & Qxt::preload)
{
refill(OUT1);
refill(OUT2);
}
else
{
for (unsigned int i=0;i<fliplen_m;i++)
{
OUT1[i]=0;
OUT2[i]=0;
}
}
WF=false;
HF=true;
eof_f=false;
decoderlock_b=NULL;
start();
blocked=false;
}

View File

@ -34,9 +34,18 @@ the fliplean is NOT meant as per channel. it is the amount of data for all chann
\bug currently fliplen must be minimal 1024*2 in order to get the decoder working.
*/
namespace Qxt
{
enum QxtAVFileFlags
{
preload,
fullLoad
};
};
class QxtAudioPlayer;
class QxtAVFile : public QThread
{
Q_OBJECT
@ -45,7 +54,12 @@ class QxtAVFile : public QThread
/**default constructor.
pass filename and fliplen.
*/
QxtAVFile(QString filename,int fliplen,QObject *parent=0);
QxtAVFile(QString filename,int fliplen,int flags=0,QObject *parent=0);
/**default constructor.
if you just use QxtAVFile for playback with QxtAudioPlayer, use this ctor to use the default fliplen
*/
QxtAVFile(QString filename,QxtAudioPlayer*, int flags=0,QObject *parent=0);
~QxtAVFile();
@ -77,6 +91,10 @@ class QxtAVFile : public QThread
///playback length of the file in seconds.
double length();
///reset
void reset();
signals:
/**
End of File Signal.
@ -131,6 +149,11 @@ class QxtAVFile : public QThread
///indicate that the next buffer is the last
bool eof_f;///flip
int flags_d;
///canot call flip
bool blocked;
};

View File

@ -10,7 +10,6 @@ released under the Terms of LGPL (see the LICENSE file)
#define FRAMES_PER_BUFFER 1024
static QxtAVFile * avfile=NULL;
@ -40,114 +39,63 @@ static int PortaudioCallback(const void *, void *outputBuffer,unsigned long fram
class AUDIOOUT
{
public:
AUDIOOUT()
{
///init portaudio
Q_ASSERT(Pa_Initialize()==paNoError);
/// make sure the default audio out is available.for the sake of simplicity we do not check for other outputs then oss on unix
Q_ASSERT(Pa_GetDeviceInfo( Pa_GetDefaultOutputDevice()));
///open the default stream
Q_ASSERT(Pa_OpenDefaultStream (
&stream,
0, ///no input channels
2, ///stereo output
paFloat32, ///32 bit floating output
Pa_GetDeviceInfo( Pa_GetDefaultOutputDevice() )->defaultSampleRate,
FRAMES_PER_BUFFER,
PortaudioCallback,
&avfile
)==paNoError);
///start playback
Q_ASSERT(Pa_StartStream( stream )==paNoError);
}
~AUDIOOUT()
{
///cleanup
Pa_StopStream( stream );
Pa_CloseStream( stream );
Pa_Terminate();
}
PaStream *stream;
static PaStream *stream;
};
static AUDIOOUT * audiout=NULL;
static QxtAudioPlayerStaticEofObjectHolder * eofo=NULL;
QxtAudioPlayer::QxtAudioPlayer(QObject * parent):QObject(parent){}
void QxtAudioPlayer::play(QString url)
QxtAudioPlayer::QxtAudioPlayer(QObject * parent):QObject(parent)
{
if (!eofo)
eofo=new QxtAudioPlayerStaticEofObjectHolder();
if (!audiout)
audiout = new AUDIOOUT();
if (avfile)
{
///set null before delete to avoid crash of callback
QxtAVFile * B=avfile;
avfile=NULL;
delete (B);
}
///intialise QxtAVFile. take care of the *2 QxtAVFile wants the amount of samples to push whereas portaudio means the amount per channel
avfile = new QxtAVFile(url,FRAMES_PER_BUFFER*2);
connect(avfile,SIGNAL(eof()),eofo,SLOT(f__eof()));
///tell avfile to resample its output to the soundcards samplerate
avfile->resample((int)Pa_GetDeviceInfo( Pa_GetDefaultOutputDevice() )->defaultSampleRate);
///init portaudio
Q_ASSERT(Pa_Initialize()==paNoError);
/// make sure the default audio out is available.for the sake of simplicity we do not check for other outputs then oss on unix
Q_ASSERT(Pa_GetDeviceInfo( Pa_GetDefaultOutputDevice()));
///open the default stream
Q_ASSERT(Pa_OpenDefaultStream (
&stream,
0, ///no input channels
2, ///stereo output
paFloat32, ///32 bit floating output
Pa_GetDeviceInfo( Pa_GetDefaultOutputDevice() )->defaultSampleRate,
FRAMES_PER_BUFFER,
PortaudioCallback,
&avfile
)==paNoError);
///start playback
Q_ASSERT(Pa_StartStream( stream )==paNoError);
}
void QxtAudioPlayerStaticEofObjectHolder::f__eof()
{
Pa_StopStream(audiout->stream );
QxtAVFile * B=avfile;
avfile=NULL;
if (B) delete (B);
}
void QxtAudioPlayer::close()
{
if (audiout) delete(audiout);
if (avfile) delete(avfile);
}
QxtAudioPlayer::~QxtAudioPlayer()
{
if (eofo) delete(eofo);
if (audiout) delete(audiout);
if (avfile) delete(avfile);
}
void QxtAudioPlayer::play(QxtAVFile * file)
{
if(avfile)avfile->reset();
avfile=NULL;
connect(file,SIGNAL(eof()),this,SLOT(feof()));
///tell avfile to resample its output to the soundcards samplerate
file->resample((int)Pa_GetDeviceInfo( Pa_GetDefaultOutputDevice() )->defaultSampleRate);
avfile=file;
}
void QxtAudioPlayer::feof()
{
// if(!(QxtAVFile* )sender())return;
if(avfile)avfile->reset();
avfile=NULL;
}

View File

@ -5,6 +5,7 @@ released under the Terms of LGPL (see the LICENSE file)
*******************************************************************/
#include <QObject>
#include <QString>
#include <QxtAVFile.h>
/**
@ -16,18 +17,17 @@ released under the Terms of LGPL (see the LICENSE file)
sometimes you just want to play a sound in your application.
this is not meant to be a full blown media player and does not provide any functions for it.
example:
\code
QxtAudioPlayer player;
QxtAVFile file("test.wav",&player,Qxt::preload);
player.play(&file);
\endcode
*/
class QxtAudioPlayerStaticEofObjectHolder: public QObject
{
Q_OBJECT
public slots:
void f__eof();
};
class QxtAVFile;
class QxtAudioPlayer : public QObject
{
@ -38,27 +38,18 @@ class QxtAudioPlayer : public QObject
public slots:
/**
usage:
\code
QxtAudioPlayer::play("sound/ding.wav");
\endcode
obviously you do not need to construct an object
calling this slot while another file is playing will interupt the current playback
*/
static void play(QString url);
/**
if you do not need sound a longer time, you should close the sound out.
It will automaticly be reopened the next play();
*/
static void close();
void play(QxtAVFile * file);
private slots:
void feof();
};