diff options
| author | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2008-11-20 11:10:02 +0000 |
|---|---|---|
| committer | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2008-11-20 11:10:02 +0000 |
| commit | a030a982fc4194e81b21a36c1fb0a505f8e2e28b (patch) | |
| tree | fdbcaa5f5bc3901078b9dcd9d20782c30c3c6899 /src | |
| parent | a008a77ed5aecdc1f1f000cff1cb7a5e03b18e3c (diff) | |
| download | qmmp-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
Diffstat (limited to 'src')
| -rw-r--r-- | src/plugins/Output/alsa/outputalsa.cpp | 449 | ||||
| -rw-r--r-- | src/plugins/Output/alsa/outputalsa.h | 36 | ||||
| -rw-r--r-- | src/plugins/Output/pulseaudio/outputpulseaudio.cpp | 158 | ||||
| -rw-r--r-- | src/plugins/Output/pulseaudio/outputpulseaudio.h | 26 | ||||
| -rw-r--r-- | src/qmmp/output.cpp | 127 | ||||
| -rw-r--r-- | src/qmmp/output.h | 22 | ||||
| -rw-r--r-- | src/qmmp/soundcore.cpp | 1 |
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); |
