diff options
Diffstat (limited to 'src/plugins/Output/oss/outputoss.cpp')
| -rw-r--r-- | src/plugins/Output/oss/outputoss.cpp | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/src/plugins/Output/oss/outputoss.cpp b/src/plugins/Output/oss/outputoss.cpp new file mode 100644 index 000000000..9c52ac777 --- /dev/null +++ b/src/plugins/Output/oss/outputoss.cpp @@ -0,0 +1,505 @@ +/*************************************************************************** + * Copyright (C) 2007 by Uriy Zhuravlev stalkerg@gmail.com * + * * + * Copyright (c) 2000-2001 Brad Hughes bhughes@trolltech.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <QApplication> + +#include "outputoss.h" +#include "constants.h" +#include "buffer.h" +#include "visual.h" + +#include <stdio.h> +#include <string.h> +#include <QtGlobal> +#include <QSettings> +#include <QDir> + +#include <iostream> + +//extern Q_EXPORT QApplication* qApp; + + +void OutputOSS::stop() +{ + m_userStop = TRUE; +} + +void OutputOSS::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); + } +} + +long OutputOSS::written() +{ + return m_totalWritten; +} + +void OutputOSS::seek(long pos) +{ + recycler()->mutex()->lock(); + recycler()->clear(); + recycler()->mutex()->unlock(); + + m_totalWritten = (pos * m_bps); + m_currentSeconds = -1; +} + + +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#if defined(__FreeBSD__) +# include <sys/soundcard.h> +#elif defined(__linux__) +# include <linux/soundcard.h> +#elif defined(__bsdi__) +# include <sys/soundcard.h> +#endif + + +OutputOSS::OutputOSS(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), + do_select(TRUE), + m_audio_fd(-1), m_mixer_fd(-1) +{ +QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); +m_master = true; +m_audio_device = settings.value("OSS/device","/dev/dsp").toString(); +m_mixer_device = settings.value("OSS/mixer_device","/dev/mixer").toString(); +openMixer(); +} + +OutputOSS::~OutputOSS() +{ + if (m_audio_fd > 0) + { + close(m_audio_fd); + m_audio_fd = -1; + } + if (m_mixer_fd > 0) + { + close(m_mixer_fd); + m_mixer_fd = -1; + } +} + +void OutputOSS::configure(long freq, int chan, int prec, int rate) +{ + // we need to configure + if (freq != m_frequency || chan != m_channels || prec != m_precision) { + // we have already configured, but are changing settings... + // reset the device + resetDSP(); + + m_frequency = freq; + m_channels = chan; + m_precision = prec; + + m_bps = freq * chan * (prec / 8); + + int p; + switch(prec) { + default: + case 16: +#if defined(AFMT_S16_NE) + p = AFMT_S16_NE; +#else + p = AFMT_S16_LE; +#endif + break; + + case 8: + p = AFMT_S8; + break; + + } + + ioctl(m_audio_fd, SNDCTL_DSP_SETFMT, &p); + ioctl(m_audio_fd, SNDCTL_DSP_SAMPLESIZE, &prec); + int stereo = (chan > 1) ? 1 : 0; + ioctl(m_audio_fd, SNDCTL_DSP_STEREO, &stereo); + ioctl(m_audio_fd, SNDCTL_DSP_SPEED, &freq); + } + + m_rate = rate; +} + + +void OutputOSS::reset() +{ + if (m_audio_fd > 0) + { + close(m_audio_fd); + m_audio_fd = -1; + } + + m_audio_fd = open(m_audio_device.toAscii(), O_WRONLY, 0); + + if (m_audio_fd < 0) + { + error(QString("OSSOutput: failed to open output device '%1'"). + arg(m_audio_device)); + return; + } + + int flags; + if ((flags = fcntl(m_audio_fd, F_GETFL, 0)) > 0) + { + flags &= O_NDELAY; + fcntl(m_audio_fd, F_SETFL, flags); + } + + fd_set afd; + FD_ZERO(&afd); + FD_SET(m_audio_fd, &afd); + struct timeval tv; + tv.tv_sec = 0l; + tv.tv_usec = 50000l; + do_select = (select(m_audio_fd + 1, 0, &afd, 0, &tv) > 0); + + if (m_audio_fd > 0) + { + close(m_mixer_fd); + m_mixer_fd = -1; + } + openMixer(); +} + +void OutputOSS::openMixer() +{ + if (m_mixer_fd != -1) + return; + + if ((m_mixer_fd = open(m_mixer_device.toAscii(), O_RDWR)) == -1) + { + return; + } + if (m_audio_fd < 0) + { + error(QString("OSSOutput: failed to open mixer device '%1'"). + arg(m_mixer_device)); + return; + } +} + +void OutputOSS::pause() +{ + m_pause = (m_pause) ? FALSE : TRUE; +} + +void OutputOSS::post() +{ + if (m_audio_fd < 1) + return; + + int unused; + ioctl(m_audio_fd, SNDCTL_DSP_POST, &unused); +} + +void OutputOSS::sync() +{ + if (m_audio_fd < 1) + return; + + int unused; + ioctl(m_audio_fd, SNDCTL_DSP_SYNC, &unused); +} + + +void OutputOSS::resetDSP() +{ + if (m_audio_fd < 1) + return; + + int unused; + ioctl(m_audio_fd, SNDCTL_DSP_RESET, &unused); +} + + +bool OutputOSS::initialize() +{ + m_inited = m_pause = m_play = m_userStop = FALSE; + + + reset(); + if (m_audio_fd < 0) + return FALSE; + if (m_mixer_fd < 0) + return FALSE; + + + m_currentSeconds = -1; + m_totalWritten = 0; + stat = OutputState::Stopped; + + m_inited = TRUE; + return TRUE; +} + +void OutputOSS::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; + resetDSP(); + if (m_audio_fd > 0) + { + close(m_audio_fd); + m_audio_fd = -1; + } + if (m_audio_fd > 0) + { + close(m_mixer_fd); + m_mixer_fd = -1; + } + + qDebug("OutputOSS: uninitialize"); + dispatch(OutputState::Stopped); +} + +long OutputOSS::latency() +{ + ulong used = 0; + + if (! m_pause) + { + if (ioctl(m_audio_fd, SNDCTL_DSP_GETODELAY, &used) == -1) + used = 0; + } + + return used; +} + +void OutputOSS::run() +{ + mutex()->lock(); + + if (! m_inited) + { + mutex()->unlock(); + + return; + } + + m_play = TRUE; + + mutex()->unlock(); + + fd_set afd; + struct timeval tv; + Buffer *b = 0; + bool done = FALSE; + unsigned long n = 0, m = 0, l = 0; + + dispatch(OutputState::Playing); + + FD_ZERO(&afd); + + while (! done) { + mutex()->lock(); + + recycler()->mutex()->lock(); + + done = m_userStop; + + while (! done && (recycler()->empty() || m_pause)) { + post(); + + mutex()->unlock(); + + { + stat = m_pause ? OutputState::Paused : OutputState::Buffering; + OutputState e((OutputState::Type) stat); + dispatch(e); + } + + 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(); + + FD_ZERO(&afd); + FD_SET(m_audio_fd, &afd); + // nice long poll timeout + tv.tv_sec = 5l; + tv.tv_usec = 0l; + + if (b && + (! do_select || (select(m_audio_fd + 1, 0, &afd, 0, &tv) > 0 && + FD_ISSET(m_audio_fd, &afd)))) { + l = qMin(int(2048), int(b->nbytes - n)); + if (l > 0) { + m = write(m_audio_fd, b->data + n, l); + n += m; + + status(); + dispatchVisual(b, m_totalWritten, m_channels, m_precision); + } else { + // force buffer change + n = b->nbytes; + m = 0; + } + } + + m_totalWritten += m; + + if (n == b->nbytes) { + recycler()->mutex()->lock(); + recycler()->done(); + recycler()->mutex()->unlock(); + + b = 0; + n = 0; + } + + mutex()->unlock(); + } + + mutex()->lock(); + + if (! m_userStop) + sync(); + resetDSP(); + + m_play = FALSE; + + dispatch(OutputState::Stopped); + mutex()->unlock(); +} + + +void OutputOSS::setVolume(int l, int r) +{ + int v, devs; + long cmd; + + ioctl(m_mixer_fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (m_master == false)) + cmd = SOUND_MIXER_WRITE_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (m_master == true)) + cmd = SOUND_MIXER_WRITE_VOLUME; + else + { + //close(mifd); + return; + } + v = (r << 8) | l; + ioctl(m_mixer_fd, cmd, &v); +} + +void OutputOSS::volume(int *ll,int *rr) +{ + *ll = 0; + *rr = 0; + int cmd; + int v, devs; + + ioctl(m_mixer_fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (m_master == 0)) + cmd = SOUND_MIXER_READ_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (m_master == 1)) + cmd = SOUND_MIXER_READ_VOLUME; + else + return; + + ioctl(m_mixer_fd, cmd, &v); + *ll = (v & 0xFF00) >> 8; + *rr = (v & 0x00FF); + + *ll = (*ll > 100) ? 100 : *ll; + *rr = (*rr > 100) ? 100 : *rr; + *ll = (*ll < 0) ? 0 : *ll; + *rr = (*rr < 0) ? 0 : *rr; +} + + +void OutputOSS::checkVolume() +{ + long ll = 0, lr = 0, cmd; + int v, devs; + + /* + * We dont show any errors if this fails, as this is called + * rather often + */ +// if (!open_mixer_device()) { + ioctl(m_mixer_fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (m_master == 0)) + cmd = SOUND_MIXER_READ_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (m_master == 1)) + cmd = SOUND_MIXER_READ_VOLUME; + else + return; + + ioctl(m_mixer_fd, cmd, &v); + ll = (v & 0xFF00) >> 8; + lr = (v & 0x00FF); + + ll = (ll > 100) ? 100 : ll; + lr = (lr > 100) ? 100 : lr; + ll = (ll < 0) ? 0 : ll; + lr = (lr < 0) ? 0 : lr; + if (bl!=ll || br!=lr) + { + bl = ll; + br = lr; + dispatchVolume(ll,lr); + } +// } +} + + |
