aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2008-11-20 11:10:02 +0000
committertrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2008-11-20 11:10:02 +0000
commita030a982fc4194e81b21a36c1fb0a505f8e2e28b (patch)
treefdbcaa5f5bc3901078b9dcd9d20782c30c3c6899
parenta008a77ed5aecdc1f1f000cff1cb7a5e03b18e3c (diff)
downloadqmmp-a030a982fc4194e81b21a36c1fb0a505f8e2e28b.tar.gz
qmmp-a030a982fc4194e81b21a36c1fb0a505f8e2e28b.tar.bz2
qmmp-a030a982fc4194e81b21a36c1fb0a505f8e2e28b.zip
output api changes
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@622 90c681e8-e032-0410-971d-27865f9a5e38
-rw-r--r--src/plugins/Output/alsa/outputalsa.cpp449
-rw-r--r--src/plugins/Output/alsa/outputalsa.h36
-rw-r--r--src/plugins/Output/pulseaudio/outputpulseaudio.cpp158
-rw-r--r--src/plugins/Output/pulseaudio/outputpulseaudio.h26
-rw-r--r--src/qmmp/output.cpp127
-rw-r--r--src/qmmp/output.h22
-rw-r--r--src/qmmp/soundcore.cpp1
7 files changed, 345 insertions, 474 deletions
diff --git a/src/plugins/Output/alsa/outputalsa.cpp b/src/plugins/Output/alsa/outputalsa.cpp
index 6e277663c..0a73431f6 100644
--- a/src/plugins/Output/alsa/outputalsa.cpp
+++ b/src/plugins/Output/alsa/outputalsa.cpp
@@ -38,16 +38,16 @@
#include "outputalsa.h"
OutputALSA::OutputALSA(QObject * parent)
- : Output(parent), m_inited(FALSE), m_pause(FALSE), m_play(FALSE),
- m_userStop(FALSE), m_totalWritten(0), m_currentSeconds(-1),
- m_bps(-1), m_channels(-1), m_precision(-1)
+ : Output(parent), m_inited(FALSE)
{
- m_frequency = 0;
QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
QString dev_name = settings.value("ALSA/device","default").toString();
m_use_mmap = settings.value("ALSA/use_mmap", FALSE).toBool();
pcm_name = strdup(dev_name.toAscii().data());
pcm_handle = 0;
+ m_prebuf = 0;
+ m_prebuf_size = 0;
+ m_prebuf_fill = 0;
}
OutputALSA::~OutputALSA()
@@ -56,168 +56,136 @@ OutputALSA::~OutputALSA()
free (pcm_name);
}
-void OutputALSA::stop()
-{
- m_userStop = TRUE;
-}
-
-void OutputALSA::status()
+void OutputALSA::configure(quint32 freq, int chan, int prec)
{
- long ct = (m_totalWritten - latency()) / m_bps;
+ // we need to configure
- if (ct < 0)
- ct = 0;
+ uint rate = freq; /* Sample rate */
+ uint exact_rate = freq; /* Sample rate returned by */
- if (ct > m_currentSeconds)
+ /* load settings from config */
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ uint buffer_time = settings.value("buffer_time",500).toUInt()*1000;
+ uint period_time = settings.value("period_time",100).toUInt()*1000;
+ settings.endGroup();
+
+ snd_pcm_hw_params_t *hwparams = 0;
+ snd_pcm_sw_params_t *swparams = 0;
+ int err; //alsa error code
+
+ //hw params
+ snd_pcm_hw_params_alloca(&hwparams);
+ if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
{
- m_currentSeconds = ct;
- dispatch(m_currentSeconds, m_totalWritten, m_rate,
- m_frequency, m_precision, m_channels);
+ qWarning("OutputALSA: Can not read configuration for PCM device: %s", snd_strerror(err));
+ return;
}
-}
-
-qint64 OutputALSA::written()
-{
- return m_totalWritten;
-}
-
-void OutputALSA::seek(qint64 pos)
-{
- m_totalWritten = (pos * m_bps);
- m_currentSeconds = -1;
-}
-
-void OutputALSA::configure(quint32 freq, int chan, int prec)
-{
- // we need to configure
- if (freq != m_frequency || chan != m_channels || prec != m_precision)
- {
- m_frequency = freq;
- m_channels = chan;
- m_precision = prec;
- m_bps = freq * chan * (prec / 8);
- uint rate = m_frequency; /* Sample rate */
- uint exact_rate = m_frequency; /* Sample rate returned by */
-
- /* load settings from config */
- QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
- settings.beginGroup("ALSA");
- uint buffer_time = settings.value("buffer_time",500).toUInt()*1000;
- uint period_time = settings.value("period_time",100).toUInt()*1000;
- settings.endGroup();
-
- snd_pcm_hw_params_t *hwparams = 0;
- snd_pcm_sw_params_t *swparams = 0;
- int err; //alsa error code
-
- //hw params
- snd_pcm_hw_params_alloca(&hwparams);
- if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
+ if (m_use_mmap)
+ {
+ if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)
{
- qWarning("OutputALSA: Can not read configuration for PCM device: %s", snd_strerror(err));
- return;
+ qWarning("OutputALSA: Error setting mmap access: %s", snd_strerror(err));
+ m_use_mmap = FALSE;
}
- if (m_use_mmap)
- {
- if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)
- {
- qWarning("OutputALSA: Error setting mmap access: %s", snd_strerror(err));
- m_use_mmap = FALSE;
- }
- }
- if (!m_use_mmap)
- {
- if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
- {
- qWarning("OutputALSA: Error setting access: %s", snd_strerror(err));
- return;
- }
- }
- snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
- switch (prec)
- {
- case 8:
- format = SND_PCM_FORMAT_S8;
- break;
- case 16:
- format = SND_PCM_FORMAT_S16_LE;
- break;
- case 24:
- format = SND_PCM_FORMAT_S24_LE;
- break;
- case 32:
- format = SND_PCM_FORMAT_S32_LE;
- break;
- default:
- qWarning("OutputALSA: unsupported format detected");
- return;
- }
- if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format)) < 0)
+ }
+ if (!m_use_mmap)
+ {
+ if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
- qDebug("OutputALSA: Error setting format: %s", snd_strerror(err));
+ qWarning("OutputALSA: Error setting access: %s", snd_strerror(err));
return;
}
- exact_rate = rate;// = 11000;
- qDebug("OutputALSA: frequency=%d, channels=%d", rate, chan);
+ }
+ snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
+ switch (prec)
+ {
+ case 8:
+ format = SND_PCM_FORMAT_S8;
+ break;
+ case 16:
+ format = SND_PCM_FORMAT_S16_LE;
+ break;
+ case 24:
+ format = SND_PCM_FORMAT_S24_LE;
+ break;
+ case 32:
+ format = SND_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ qWarning("OutputALSA: unsupported format detected");
+ return;
+ }
+ if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format)) < 0)
+ {
+ qDebug("OutputALSA: Error setting format: %s", snd_strerror(err));
+ return;
+ }
+ exact_rate = rate;// = 11000;
+ qDebug("OutputALSA: frequency=%d, channels=%d", rate, chan);
- if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0)
- {
- qWarning("OutputALSA: Error setting rate: %s", snd_strerror(err));
- return;
- }
- if (rate != exact_rate)
- {
- qWarning("OutputALSA: The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.", rate, exact_rate);
- }
- uint c = m_channels;
- if ((err = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
- {
- qWarning("OutputALSA: Error setting channels: %s", snd_strerror(err));
- return;
- }
- if ((err = snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &period_time ,0)) < 0)
- {
- qWarning("OutputALSA: Error setting period time: %s", snd_strerror(err));
- return;
- }
- if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffer_time ,0)) < 0)
- {
- qWarning("OutputALSA: Error setting buffer time: %s", snd_strerror(err));
- return;
- }
- if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
- {
- qWarning("OutputALSA: Error setting HW params: %s", snd_strerror(err));
- return;
- }
- //read some alsa parameters
- snd_pcm_uframes_t buffer_size = 0;
- snd_pcm_uframes_t period_size = 0;
- if ((err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0)
- {
- qWarning("OutputALSA: Error reading buffer size: %s", snd_strerror(err));
- return;
- }
- if ((err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0)) < 0)
- {
- qWarning("OutputALSA: Error reading period size: %s", snd_strerror(err));
- return;
- }
- //swparams
- snd_pcm_sw_params_alloca(&swparams);
- snd_pcm_sw_params_current(pcm_handle, swparams);
- if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,
- buffer_size - period_size)) < 0)
- qWarning("OutputALSA: Error setting threshold: %s", snd_strerror(err));
- if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0)
- {
- qWarning("OutputALSA: Error setting SW params: %s", snd_strerror(err));
- return;
- }
- //setup needed values
- m_bits_per_frame = snd_pcm_format_physical_width(format) * chan;
- m_chunk_size = period_size;
+ if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0)
+ {
+ qWarning("OutputALSA: Error setting rate: %s", snd_strerror(err));
+ return;
+ }
+ if (rate != exact_rate)
+ {
+ qWarning("OutputALSA: The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.", rate, exact_rate);
+ }
+ uint c = chan;
+ if ((err = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
+ {
+ qWarning("OutputALSA: Error setting channels: %s", snd_strerror(err));
+ return;
+ }
+ if ((err = snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &period_time ,0)) < 0)
+ {
+ qWarning("OutputALSA: Error setting period time: %s", snd_strerror(err));
+ return;
+ }
+ if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffer_time ,0)) < 0)
+ {
+ qWarning("OutputALSA: Error setting buffer time: %s", snd_strerror(err));
+ return;
+ }
+ if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
+ {
+ qWarning("OutputALSA: Error setting HW params: %s", snd_strerror(err));
+ return;
}
+ //read some alsa parameters
+ snd_pcm_uframes_t buffer_size = 0;
+ snd_pcm_uframes_t period_size = 0;
+ if ((err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0)
+ {
+ qWarning("OutputALSA: Error reading buffer size: %s", snd_strerror(err));
+ return;
+ }
+ if ((err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0)) < 0)
+ {
+ qWarning("OutputALSA: Error reading period size: %s", snd_strerror(err));
+ return;
+ }
+ //swparams
+ snd_pcm_sw_params_alloca(&swparams);
+ snd_pcm_sw_params_current(pcm_handle, swparams);
+ if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,
+ buffer_size - period_size)) < 0)
+ qWarning("OutputALSA: Error setting threshold: %s", snd_strerror(err));
+ if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0)
+ {
+ qWarning("OutputALSA: Error setting SW params: %s", snd_strerror(err));
+ return;
+ }
+ //setup needed values
+ m_bits_per_frame = snd_pcm_format_physical_width(format) * chan;
+ m_chunk_size = period_size;
+ //}
+ Output::configure(freq, chan, prec); //apply configuration
+ //create alsa prebuffer;
+ m_prebuf_size = Buffer::size() + m_bits_per_frame * m_chunk_size / 8;
+ m_prebuf = (uchar *)malloc(m_prebuf_size);
}
void OutputALSA::reset()
@@ -234,25 +202,13 @@ void OutputALSA::reset()
}
}
-
-void OutputALSA::pause()
-{
- if (!m_play)
- return;
- m_pause = !m_pause;
- Qmmp::State state = m_pause ? Qmmp::Paused: Qmmp::Playing;
- dispatch(state);
-}
-
bool OutputALSA::initialize()
{
- m_inited = m_pause = m_play = m_userStop = FALSE;
+ m_inited = FALSE;
if (pcm_handle)
return FALSE;
- m_currentSeconds = -1;
- m_totalWritten = 0;
if (snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0)
{
qWarning ("OutputALSA: Error opening PCM device %s", pcm_name);
@@ -277,125 +233,52 @@ qint64 OutputALSA::latency()
return used;
}
-void OutputALSA::run()
+qint64 OutputALSA::writeAudio(unsigned char *data, qint64 maxSize)
{
-
- mutex()->lock ();
- if (! m_inited)
+ //increase buffer size if needed
+ if (m_prebuf_size < m_prebuf_fill + maxSize)
{
- mutex()->unlock();
- return;
+ m_prebuf_size = m_prebuf_fill + maxSize;
+ m_prebuf = (uchar*) realloc(m_prebuf, m_prebuf_size);
}
+ memcpy(m_prebuf + m_prebuf_fill, data, maxSize);
+ m_prebuf_fill += maxSize;
- m_play = TRUE;
-
- mutex()->unlock();
-
- Buffer *b = 0;
- bool done = FALSE;
- long m = 0;
- snd_pcm_uframes_t l;
-
- long prebuffer_size = Buffer::size() + m_bits_per_frame * m_chunk_size / 8;
+ snd_pcm_uframes_t l = snd_pcm_bytes_to_frames(pcm_handle, m_prebuf_fill);
- unsigned char *prebuffer = (unsigned uchar *)malloc(prebuffer_size);
- ulong prebuffer_fill = 0;
-
- dispatch(Qmmp::Playing);
-
- while (!done)
+ while (l >= m_chunk_size)
{
- mutex()->lock ();
- recycler()->mutex()->lock ();
-
- done = m_userStop;
-
- while (!done && (recycler()->empty() || m_pause))
+ snd_pcm_wait(pcm_handle, 10);
+ long m;
+ if ((m = alsa_write(m_prebuf, m_chunk_size)) >= 0)
{
- mutex()->unlock();
- recycler()->cond()->wakeOne();
- recycler()->cond()->wait(recycler()->mutex());
- mutex()->lock ();
- done = m_userStop;
- }
- status();
-
- if (! b)
- {
- b = recycler()->next();
- if (b->rate)
- m_rate = b->rate;
- }
-
- recycler()->cond()->wakeOne();
- recycler()->mutex()->unlock();
-
- if (b)
- {
- if ((ulong)prebuffer_size < prebuffer_fill + b->nbytes)
- {
- prebuffer_size = prebuffer_fill + b->nbytes;
- prebuffer = (unsigned char*) realloc(prebuffer, prebuffer_size);
- }
-
-
- memcpy(prebuffer + prebuffer_fill, b->data, b->nbytes);
- prebuffer_fill += b->nbytes;
-
- l = snd_pcm_bytes_to_frames(pcm_handle, prebuffer_fill);
-
- while (l >= m_chunk_size)
- {
- snd_pcm_wait(pcm_handle, 10);
- if ((m = alsa_write(prebuffer, m_chunk_size)) >= 0)
- {
- l -= m;
- m = snd_pcm_frames_to_bytes(pcm_handle, m); // convert frames to bytes
- prebuffer_fill -= m;
- memcpy(prebuffer, prebuffer + m, prebuffer_fill); //move data to begin
- m_totalWritten += m;
- status();
- dispatchVisual(b, m_totalWritten, m_channels, m_precision);
- }
- else
- break;
- }
+ l -= m;
+ m = snd_pcm_frames_to_bytes(pcm_handle, m); // convert frames to bytes
+ m_prebuf_fill -= m;
+ memcpy(m_prebuf, m_prebuf + m, m_prebuf_fill); //move data to begin
}
- //force buffer change
- recycler()->mutex()->lock ();
- recycler()->done();
- recycler()->mutex()->unlock();
- b = 0;
- mutex()->unlock();
+ else
+ return -1;
}
+ return maxSize;
+}
- mutex()->lock ();
- //write remaining data
- if (prebuffer_fill > 0 && recycler()->empty())
+void OutputALSA::flush()
+{
+ snd_pcm_uframes_t l = snd_pcm_bytes_to_frames(pcm_handle, m_prebuf_fill);
+ long m;
+ while (l > 0)
{
- l = snd_pcm_bytes_to_frames(pcm_handle, prebuffer_fill);
-
- while (l > 0)
+ if ((m = alsa_write(m_prebuf, l)) >= 0)
{
- if ((m = alsa_write(prebuffer, l)) >= 0)
- {
- l -= m;
- m = snd_pcm_frames_to_bytes(pcm_handle, m); // convert frames to bytes
- prebuffer_fill -= m;
- memcpy(prebuffer, prebuffer + m, prebuffer_fill);
- m_totalWritten += m;
- status();
- }
- else
- break;
+ l -= m;
+ m = snd_pcm_frames_to_bytes(pcm_handle, m); // convert frames to bytes
+ m_prebuf_fill -= m;
+ memcpy(m_prebuf, m_prebuf + m, m_prebuf_fill);
}
+ else
+ break;
}
- m_play = FALSE;
- dispatch(Qmmp::Stopped);
- free(prebuffer);
- prebuffer = 0;
- mutex()->unlock();
-
}
long OutputALSA::alsa_write(unsigned char *data, long size)
@@ -408,18 +291,18 @@ long OutputALSA::alsa_write(unsigned char *data, long size)
if (m == -EAGAIN)
{
- mutex()->unlock();
+ //mutex()->unlock();
snd_pcm_wait(pcm_handle, 500);
- mutex()->lock ();
+ //mutex()->lock ();
return 0;
}
else if (m >= 0)
{
if (m < size)
{
- mutex()->unlock();
+ //mutex()->unlock();
snd_pcm_wait(pcm_handle, 500);
- mutex()->lock ();
+ //mutex()->lock ();
}
return m;
}
@@ -433,6 +316,7 @@ long OutputALSA::alsa_write(unsigned char *data, long size)
/* TODO: reopen the device */
return -1;
}
+ return 0;
}
else if (m == -ESTRPIPE)
{
@@ -450,6 +334,7 @@ long OutputALSA::alsa_write(unsigned char *data, long size)
return -1;
}
}
+ return 0;
}
return -1;
}
@@ -459,21 +344,15 @@ void OutputALSA::uninitialize()
if (!m_inited)
return;
m_inited = FALSE;
- m_pause = FALSE;
- m_play = FALSE;
- m_userStop = FALSE;
- m_totalWritten = 0;
- m_currentSeconds = -1;
- m_bps = -1;
- m_frequency = -1;
- m_channels = -1;
- m_precision = -1;
if (pcm_handle)
{
qDebug("OutputALSA: closing pcm_handle");
snd_pcm_close(pcm_handle);
pcm_handle = 0;
}
+ if (m_prebuf)
+ free(m_prebuf);
+ m_prebuf = 0;
}
/* ****** MIXER ******* */
diff --git a/src/plugins/Output/alsa/outputalsa.h b/src/plugins/Output/alsa/outputalsa.h
index 9bd6e0e4a..3ddf4278c 100644
--- a/src/plugins/Output/alsa/outputalsa.h
+++ b/src/plugins/Output/alsa/outputalsa.h
@@ -42,46 +42,30 @@ public:
~OutputALSA();
bool initialize();
- bool isInitialized() const
- {
- return m_inited;
- }
- void uninitialize();
void configure(quint32, int, int);
- void stop();
- void pause();
- qint64 written();
qint64 latency();
- void seek(qint64);
private:
- // thread run function
- void run();
+ //output api
+ qint64 writeAudio(unsigned char *data, qint64 maxSize);
+ void flush();
// helper functions
void reset();
- void status();
long alsa_write(unsigned char *data, long size);
+ void uninitialize();
- bool m_inited, m_pause, m_play, m_userStop;
- qint64 m_totalWritten, m_currentSeconds, m_bps;
- int m_rate, m_channels, m_precision;
- quint32 m_frequency;
+ bool m_inited;
+ bool m_use_mmap;
//alsa
snd_pcm_t *pcm_handle;
char *pcm_name;
- //alsa
snd_pcm_uframes_t m_chunk_size;
size_t m_bits_per_frame;
-
- //alsa mixer
- /*int setupMixer(QString card, QString device);
- void parseMixerName(char *str, char **name, int *index);
- int getMixer(snd_mixer_t **mixer, QString card);
- snd_mixer_elem_t* getMixerElem(snd_mixer_t *mixer, char *name, int index);
- snd_mixer_t *mixer;
- snd_mixer_elem_t *pcm_element;*/
- bool m_use_mmap;
+ //prebuffer
+ uchar *m_prebuf;
+ qint64 m_prebuf_size;
+ qint64 m_prebuf_fill;
};
class VolumeControlALSA : public VolumeControl
diff --git a/src/plugins/Output/pulseaudio/outputpulseaudio.cpp b/src/plugins/Output/pulseaudio/outputpulseaudio.cpp
index daef291eb..422c49bb4 100644
--- a/src/plugins/Output/pulseaudio/outputpulseaudio.cpp
+++ b/src/plugins/Output/pulseaudio/outputpulseaudio.cpp
@@ -22,7 +22,8 @@
#include <QApplication>
#include <QtGlobal>
-extern "C" {
+extern "C"
+{
#include <pulse/error.h>
}
@@ -36,9 +37,7 @@ extern "C" {
#include "outputpulseaudio.h"
OutputPulseAudio::OutputPulseAudio(QObject * parent)
- : Output(parent), m_inited(FALSE), m_pause(FALSE), m_play(FALSE),
- m_userStop(FALSE), m_totalWritten(0), m_currentSeconds(-1),
- m_bps(-1), m_frequency(-1), m_channels(-1), m_precision(-1)
+ : Output(parent)
{
m_connection = 0;
}
@@ -48,44 +47,8 @@ OutputPulseAudio::~OutputPulseAudio()
uninitialize();
}
-void OutputPulseAudio::stop()
-{
- m_userStop = TRUE;
-}
-
-void OutputPulseAudio::status()
-{
- long ct = (m_totalWritten - latency()) / m_bps;
-
- if (ct < 0)
- ct = 0;
-
- if (ct > m_currentSeconds)
- {
- m_currentSeconds = ct;
- dispatch(m_currentSeconds, m_totalWritten, m_rate,
- m_frequency, m_precision, m_channels);
- }
-}
-
-qint64 OutputPulseAudio::written()
-{
- return m_totalWritten;
-}
-
-void OutputPulseAudio::seek(qint64 pos)
-{
- m_totalWritten = (pos * m_bps);
- m_currentSeconds = -1;
-}
-
void OutputPulseAudio::configure(quint32 freq, int chan, int prec)
{
- m_frequency = freq;
- m_channels = chan;
- m_precision = prec;
- m_bps = freq * chan * (prec / 8);
-
pa_sample_spec ss;
ss.format = PA_SAMPLE_S16LE;
ss.channels = chan;
@@ -104,127 +67,50 @@ void OutputPulseAudio::configure(quint32 freq, int chan, int prec)
if (!m_connection)
{
qWarning("OutputPulseAudio: pa_simple_new() failed: %s", pa_strerror(error));
- m_inited = FALSE;
return;
}
qDebug("OutputPulseAudio: frequency=%d, channels=%d", uint(freq), chan);
-}
-
-void OutputPulseAudio::pause()
-{
- if (!m_play)
- return;
- m_pause = (m_pause) ? FALSE : TRUE;
- Qmmp::State state = m_pause ? Qmmp::Paused: Qmmp::Playing;
- dispatch(state);
+ Output::configure(freq, chan, prec);
}
bool OutputPulseAudio::initialize()
{
- m_inited = m_pause = m_play = m_userStop = FALSE;
- m_currentSeconds = -1;
- m_inited = TRUE;
return TRUE;
}
qint64 OutputPulseAudio::latency()
{
- long used = 0;
-
- return used;
+ if (!m_connection)
+ return 0;
+ int error;
+ return pa_simple_get_latency(m_connection, &error);
}
-void OutputPulseAudio::run()
+qint64 OutputPulseAudio::writeAudio(unsigned char *data, qint64 maxSize)
{
-
- mutex()->lock ();
- if (! m_inited)
+ int error;
+ if (!m_connection)
+ return -1;
+ if (pa_simple_write(m_connection, data, maxSize, &error) < 0)
{
mutex()->unlock();
- return;
+ qWarning("OutputPulseAudio: pa_simple_write() failed: %s", pa_strerror(error));
+ return -1;
}
+ return maxSize;
+}
- m_play = TRUE;
-
- mutex()->unlock();
-
- Buffer *b = 0;
- bool done = FALSE;
+void OutputPulseAudio::flush()
+{
int error;
-
- dispatch(Qmmp::Playing);
-
- while (! done)
- {
- mutex()->lock ();
- recycler()->mutex()->lock ();
-
- done = m_userStop;
-
- while (! done && (recycler()->empty() || m_pause))
- {
- mutex()->unlock();
- recycler()->cond()->wakeOne();
- recycler()->cond()->wait(recycler()->mutex());
- mutex()->lock ();
- done = m_userStop;
- status();
- }
-
- if (! b)
- {
- b = recycler()->next();
- if (b->rate)
- m_rate = b->rate;
- }
-
- recycler()->cond()->wakeOne();
- recycler()->mutex()->unlock();
-
- if (b)
- {
- if(pa_simple_write(m_connection, b->data, b->nbytes, &error) < 0)
- {
- mutex()->unlock();
- qWarning("OutputPulseAudio: pa_simple_write() failed: %s", pa_strerror(error));
- break;
- }
-
- dispatchVisual(b, m_totalWritten, m_channels, m_precision);
- status();
- m_totalWritten += b->nbytes;
- mutex()->unlock();
- }
- // force buffer change
- recycler()->mutex()->lock ();
- recycler()->done();
- recycler()->mutex()->unlock();
- b = 0;
- }
-
- mutex()->lock ();
- m_play = FALSE;
- dispatch(Qmmp::Stopped);
- mutex()->unlock();
-
+ if (m_connection)
+ pa_simple_flush(m_connection, &error);
}
void OutputPulseAudio::uninitialize()
{
- if (!m_inited)
- return;
- m_inited = FALSE;
- m_pause = FALSE;
- m_play = FALSE;
- m_userStop = FALSE;
- m_totalWritten = 0;
- m_currentSeconds = -1;
- m_bps = -1;
- m_frequency = -1;
- m_channels = -1;
- m_precision = -1;
- if (!m_connection)
+ if (m_connection)
{
qDebug("OutputPulseAudio: closing connection");
pa_simple_free(m_connection);
diff --git a/src/plugins/Output/pulseaudio/outputpulseaudio.h b/src/plugins/Output/pulseaudio/outputpulseaudio.h
index 704d95d27..4a2712998 100644
--- a/src/plugins/Output/pulseaudio/outputpulseaudio.h
+++ b/src/plugins/Output/pulseaudio/outputpulseaudio.h
@@ -22,8 +22,9 @@
#define OUTPUTPULSEAUDIO_H
#include <QObject>
-extern "C" {
- #include <pulse/simple.h>
+extern "C"
+{
+#include <pulse/simple.h>
}
#include <qmmp/output.h>
@@ -33,34 +34,25 @@ extern "C" {
*/
class OutputPulseAudio : public Output
{
-Q_OBJECT
+ Q_OBJECT
public:
OutputPulseAudio(QObject * parent = 0);
~OutputPulseAudio();
bool initialize();
- bool isInitialized() const { return m_inited; }
- void uninitialize();
void configure(quint32, int, int);
- void stop();
- void pause();
- qint64 written();
qint64 latency();
- void seek(qint64);
private:
- // thread run function
- void run();
+ //output api
+ qint64 writeAudio(unsigned char *data, qint64 maxSize);
+ void flush();
- // helper function
+ // helper functions
void status();
+ void uninitialize();
- bool m_inited, m_pause, m_play, m_userStop;
- qint64 m_totalWritten, m_currentSeconds, m_bps;
- quint32 m_frequency;
- int m_rate, m_channels, m_precision;
pa_simple *m_connection;
-
};
diff --git a/src/qmmp/output.cpp b/src/qmmp/output.cpp
index 75f87c4ba..6626be6ce 100644
--- a/src/qmmp/output.cpp
+++ b/src/qmmp/output.cpp
@@ -11,6 +11,7 @@
#include <QTimer>
#include "constants.h"
+#include "buffer.h"
#include "output.h"
#include <stdio.h>
@@ -18,6 +19,43 @@
Output::Output (QObject* parent) : QThread (parent), m_recycler (stackSize())
{
m_handler = 0;
+ m_frequency = 0;
+ m_channels = 0;
+ m_kbps = 0;
+ m_totalWritten = 0;
+ m_currentSeconds = -1;
+ m_bytesPerSecond = 0;
+}
+
+void Output::configure(quint32 freq, int chan, int prec)
+{
+ m_frequency = freq;
+ m_channels = chan;
+ m_precision = prec;
+ m_bytesPerSecond = freq * chan * (prec / 8);
+}
+
+void Output::pause()
+{
+ m_pause = !m_pause;
+ Qmmp::State state = m_pause ? Qmmp::Paused: Qmmp::Playing;
+ dispatch(state);
+}
+
+void Output::stop()
+{
+ m_userStop = TRUE;
+}
+
+qint64 Output::written()
+{
+ return m_totalWritten;
+}
+
+void Output::seek(qint64 pos)
+{
+ m_totalWritten = pos * m_bytesPerSecond;
+ m_currentSeconds = -1;
}
Recycler *Output::recycler()
@@ -81,6 +119,87 @@ void Output::dispatch(const Qmmp::State &state)
clearVisuals();
}
+void Output::run()
+{
+ if (!m_bytesPerSecond)
+ {
+ qWarning("Output: invalid audio parameters");
+ return;
+ }
+
+ m_userStop = FALSE;
+ m_pause = FALSE;
+ bool done = FALSE;
+ Buffer *b = 0;
+ qint64 l = 0;
+
+ dispatch(Qmmp::Playing);
+
+ while (!done)
+ {
+ mutex()->lock ();
+ recycler()->mutex()->lock ();
+
+ done = m_userStop;
+
+ while (!done && (recycler()->empty() || m_pause))
+ {
+ mutex()->unlock();
+ recycler()->cond()->wakeOne();
+ recycler()->cond()->wait(recycler()->mutex());
+ mutex()->lock ();
+ done = m_userStop;
+ }
+ status();
+
+ if (!b)
+ {
+ b = recycler()->next();
+ dispatchVisual(b, m_totalWritten, m_channels, m_precision);
+ if (b->rate)
+ m_kbps = b->rate;
+ }
+
+ recycler()->cond()->wakeOne();
+ recycler()->mutex()->unlock();
+ mutex()->unlock();
+ if (b)
+ {
+ if ((l = writeAudio(b->data, b->nbytes)) > 0)
+ m_totalWritten += b->nbytes;
+ else
+ break;
+ }
+ mutex()->lock();
+ //force buffer change
+ recycler()->mutex()->lock ();
+ recycler()->done();
+ recycler()->mutex()->unlock();
+ b = 0;
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+ //write remaining data
+ flush();
+ dispatch(Qmmp::Stopped);
+ mutex()->unlock();
+}
+
+void Output::status()
+{
+ long ct = (m_totalWritten - latency()) / m_bytesPerSecond;
+
+ if (ct < 0)
+ ct = 0;
+
+ if (ct > m_currentSeconds)
+ {
+ m_currentSeconds = ct;
+ dispatch(m_currentSeconds, m_totalWritten, m_kbps,
+ m_frequency, m_precision, m_channels);
+ }
+}
// static methods
@@ -91,7 +210,7 @@ QTimer *Output::m_timer = 0;
void Output::checkFactories()
{
- if ( ! m_factories )
+ if (!m_factories)
{
m_files.clear();
m_factories = new QList<OutputFactory *>;
@@ -170,7 +289,11 @@ OutputFactory *Output::currentFactory()
{
checkFactories();
QSettings settings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat);
- QString name = settings.value("Output/current_plugin", "alsa").toString(); //TODO freebsd support
+#ifdef Q_OS_LINUX
+ QString name = settings.value("Output/current_plugin", "alsa").toString();
+#else
+ QString name = settings.value("Output/current_plugin", "oss").toString();
+#endif
foreach(OutputFactory *factory, *m_factories)
{
if (factory->properties().shortName == name)
diff --git a/src/qmmp/output.h b/src/qmmp/output.h
index b0ff92313..389c29695 100644
--- a/src/qmmp/output.h
+++ b/src/qmmp/output.h
@@ -34,16 +34,14 @@ public:
~Output();
// abstract
- virtual bool isInitialized() const = 0;
virtual bool initialize() = 0;
- virtual void uninitialize() = 0;
- virtual void configure(quint32, int, int) = 0;
- virtual void pause() = 0;
- virtual void stop() = 0;
- virtual qint64 written() = 0;
virtual qint64 latency() = 0;
- virtual void seek(qint64) = 0;
+ virtual void configure(quint32, int, int);
+ void pause();
+ void stop();
+ qint64 written();
+ void seek(qint64);
Recycler *recycler();
QMutex *mutex();
void setStateHandler(StateHandler *handler);
@@ -66,10 +64,20 @@ protected:
void dispatchVisual(Buffer *, unsigned long, int, int);
void clearVisuals();
+ virtual qint64 writeAudio(unsigned char *data, qint64 maxSize) = 0;
+ virtual void flush() = 0;
+
private:
+ void run(); //thread run function
+ void status();
QMutex m_mutex;
Recycler m_recycler;
StateHandler *m_handler;
+ quint32 m_frequency;
+ int m_channels, m_precision, m_kbps;
+ qint64 m_bytesPerSecond;
+ bool m_userStop, m_pause;
+ qint64 m_totalWritten, m_currentSeconds;
static void checkFactories();
//TODO use QMap instead
diff --git a/src/qmmp/soundcore.cpp b/src/qmmp/soundcore.cpp
index c56250dd9..7ad0bc050 100644
--- a/src/qmmp/soundcore.cpp
+++ b/src/qmmp/soundcore.cpp
@@ -76,7 +76,6 @@ SoundCore::~SoundCore()
bool SoundCore::play(const QString &source)
{
stop();
- qDebug("SoundCore: url=%s", qPrintable(source));
m_source = source;
if (m_handler->state() != Qmmp::Stopped) //clear error state
m_handler->dispatch(Qmmp::Stopped);