summaryrefslogtreecommitdiffstats
path: root/src/gui/embedded/qsoundqss_qws.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/gui/embedded/qsoundqss_qws.cpp
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/gui/embedded/qsoundqss_qws.cpp')
-rw-r--r--src/gui/embedded/qsoundqss_qws.cpp1530
1 files changed, 1530 insertions, 0 deletions
diff --git a/src/gui/embedded/qsoundqss_qws.cpp b/src/gui/embedded/qsoundqss_qws.cpp
new file mode 100644
index 0000000000..bd6da77d4c
--- /dev/null
+++ b/src/gui/embedded/qsoundqss_qws.cpp
@@ -0,0 +1,1530 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsoundqss_qws.h"
+
+#ifndef QT_NO_SOUND
+#include <qbytearray.h>
+#include <qlist.h>
+#include <qsocketnotifier.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qevent.h>
+#include <qalgorithms.h>
+#include <qtimer.h>
+#include <qpointer.h>
+#include <qendian.h>
+#include <private/qcore_unix_p.h> // overrides QT_OPEN
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include <qdebug.h>
+
+#include <qvfbhdr.h>
+
+extern int errno;
+
+QT_BEGIN_NAMESPACE
+
+#define QT_QWS_SOUND_16BIT 1 // or 0, or undefined for always 0
+#define QT_QWS_SOUND_STEREO 1 // or 0, or undefined for always 0
+
+// Zaurus SL5000D doesn't seem to return any error if setting to 44000 and it fails,
+// however 44100 works, 44100 is more common that 44000.
+static int sound_speed = 44100;
+#ifndef QT_NO_QWS_SOUNDSERVER
+extern int qws_display_id;
+#endif
+
+static char *zeroMem = 0;
+
+struct QRiffChunk {
+ char id[4];
+ quint32 size;
+ char data[4/*size*/];
+};
+
+#if defined(QT_QWS_IPAQ)
+static const int sound_fragment_size = 12;
+#else
+static const int sound_fragment_size = 12;
+#endif
+static const int sound_buffer_size = 1 << sound_fragment_size;
+// nb. there will be an sound startup delay of
+// 2^sound_fragment_size / sound_speed seconds.
+// (eg. sound_fragment_size==12, sound_speed==44000 means 0.093s delay)
+
+#ifdef QT_QWS_SOUND_STEREO
+static int sound_stereo=QT_QWS_SOUND_STEREO;
+#else
+static const int sound_stereo=0;
+#endif
+#ifdef QT_QWS_SOUND_16BIT
+static bool sound_16bit=QT_QWS_SOUND_16BIT;
+#else
+static const bool sound_16bit=false;
+#endif
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+class QWSSoundServerClient : public QObject {
+ Q_OBJECT
+
+public:
+ QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent);
+ ~QWSSoundServerClient();
+
+public slots:
+ void sendSoundCompleted(int, int);
+ void sendDeviceReady(int, int);
+ void sendDeviceError(int, int, int);
+
+signals:
+ void play(int, int, const QString&);
+ void play(int, int, const QString&, int, int);
+ void playRaw(int, int, const QString&, int, int, int, int);
+
+ void pause(int, int);
+ void stop(int, int);
+ void resume(int, int);
+ void setVolume(int, int, int, int);
+ void setMute(int, int, bool);
+
+ void stopAll(int);
+
+ void playPriorityOnly(bool);
+
+ void setSilent( bool );
+
+private slots:
+ void tryReadCommand();
+
+private:
+ void sendClientMessage(QString msg);
+ int mCurrentID;
+ int left, right;
+ bool priExist;
+ static int lastId;
+ static int nextId() { return ++lastId; }
+ QPointer<QWS_SOCK_BASE> socket;
+};
+
+int QWSSoundServerClient::lastId = 0;
+
+QWSSoundServerClient::QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent) :
+ QObject( parent )
+{
+ socket = s;
+ priExist = false;
+ mCurrentID = nextId();
+ connect(socket,SIGNAL(readyRead()),
+ this,SLOT(tryReadCommand()));
+ connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
+}
+
+QWSSoundServerClient::~QWSSoundServerClient()
+{
+ if (priExist)
+ playPriorityOnly(false);
+ emit stopAll(mCurrentID);
+ if (socket)
+ socket->deleteLater();
+}
+
+static QString getStringTok(QString &in)
+{
+ int pos = in.indexOf(QLatin1Char(' '));
+ QString ret;
+ if (pos > 0) {
+ ret = in.left(pos);
+ in = in.mid(pos+1);
+ } else {
+ ret = in;
+ in = QString::null;
+ }
+ return ret;
+}
+
+static int getNumTok(QString &in)
+{
+ return getStringTok(in).toInt();
+}
+
+void QWSSoundServerClient::tryReadCommand()
+{
+ while ( socket->canReadLine() ) {
+ QString l = QString::fromAscii(socket->readLine());
+ l.truncate(l.length()-1); // chomp
+ QString functionName = getStringTok(l);
+ int soundid = getNumTok(l);
+ if (functionName == QLatin1String("PLAY")) {
+ emit play(mCurrentID, soundid, l);
+ } else if (functionName == QLatin1String("PLAYEXTEND")) {
+ int volume = getNumTok(l);
+ int flags = getNumTok(l);
+ emit play(mCurrentID, soundid, l, volume, flags);
+ } else if (functionName == QLatin1String("PLAYRAW")) {
+ int chs = getNumTok(l);
+ int freq = getNumTok(l);
+ int bitspersample = getNumTok(l);
+ int flags = getNumTok(l);
+ emit playRaw(mCurrentID, soundid, l, freq, chs, bitspersample, flags);
+ } else if (functionName == QLatin1String("PAUSE")) {
+ emit pause(mCurrentID, soundid);
+ } else if (functionName == QLatin1String("STOP")) {
+ emit stop(mCurrentID, soundid);
+ } else if (functionName == QLatin1String("RESUME")) {
+ emit resume(mCurrentID, soundid);
+ } else if (functionName == QLatin1String("SETVOLUME")) {
+ int left = getNumTok(l);
+ int right = getNumTok(l);
+ emit setVolume(mCurrentID, soundid, left, right);
+ } else if (functionName == QLatin1String("MUTE")) {
+ emit setMute(mCurrentID, soundid, true);
+ } else if (functionName == QLatin1String("UNMUTE")) {
+ emit setMute(mCurrentID, soundid, false);
+ } else if (functionName == QLatin1String("PRIORITYONLY")) {
+ bool sPri = soundid != 0;
+ if (sPri != priExist) {
+ priExist = sPri;
+ emit playPriorityOnly(sPri);
+ }
+ } else if(functionName == QLatin1String("SILENT")) {
+ emit setSilent( soundid != 0 );
+ }
+ }
+}
+
+void QWSSoundServerClient::sendClientMessage(QString msg)
+{
+#ifndef QT_NO_TEXTCODEC
+ QByteArray u = msg.toUtf8();
+#else
+ QByteArray u = msg.toLatin1();
+#endif
+ socket->write(u.data(), u.length());
+ socket->flush();
+}
+
+void QWSSoundServerClient::sendSoundCompleted(int gid, int sid)
+{
+ if (gid == mCurrentID)
+ sendClientMessage(QLatin1String("SOUNDCOMPLETED ")
+ + QString::number(sid) + QLatin1Char('\n'));
+}
+
+void QWSSoundServerClient::sendDeviceReady(int gid, int sid)
+{
+ if (gid == mCurrentID)
+ sendClientMessage(QLatin1String("DEVICEREADY ")
+ + QString::number(sid) + QLatin1Char('\n'));
+}
+
+void QWSSoundServerClient::sendDeviceError(int gid, int sid, int err)
+{
+ if (gid == mCurrentID)
+ sendClientMessage(QLatin1String("DEVICEERROR ")
+ + QString::number(sid) + QLatin1Char(' ')
+ + QString::number(err) + QLatin1Char('\n'));
+}
+#endif
+
+static const int maxVolume = 100;
+static const int runinLength = 2*sound_buffer_size;
+class QWSSoundServerProvider {
+public:
+ QWSSoundServerProvider(int w, int s)
+ : mWid(w), mSid(s), mMuted(false)
+ {
+ leftVolume = maxVolume>>1;
+ rightVolume = maxVolume>>1;
+ isPriority = false;
+ samples_due = 0;
+ max1 = max2 = out = 0;// = sound_buffer_size;
+ data = data1;
+ max = &max1;
+ sampleRunin = 0;
+ dev = -1;
+ }
+
+ virtual ~QWSSoundServerProvider() {
+ }
+
+ int groupId() const { return mWid; }
+ int soundId() const { return mSid; }
+
+ void startSampleRunin() {
+ // inteded to provide even audio return from mute/pause/dead samples.
+ //sampleRunin = runinLength; // or more?
+ }
+
+
+ void setVolume(int lv, int rv) {
+ leftVolume = qMin(maxVolume, qMax(0, lv));
+ rightVolume = qMin(maxVolume, qMax(0, rv));
+ }
+
+ void setMute(bool m) { mMuted = m; }
+ bool muted() { return mMuted; }
+
+ void setPriority(bool p) {
+ if (p != isPriority) {
+ isPriority = p; // currently meaningless.
+ }
+ }
+
+
+ static void setPlayPriorityOnly(bool p)
+ {
+ if (p)
+ priorityExists++;
+ else
+ priorityExists--;
+
+ if (priorityExists < 0)
+ qDebug("QSS: got more priority offs than ons");
+ }
+
+ // return -1 for file broken, give up.
+ // else return sampels ready for playing.
+ // argument is max samples server is looking for,
+ // in terms of current device status.
+ virtual int readySamples(int) = 0;
+
+ int getSample(int off, int bps) {
+
+ //
+ // 16-bit audio data is converted to native endian so that it can be scaled
+ // Yes, this is ugly on a BigEndian machine
+ // Perhaps it shouldn't be scaled at all
+ //
+ return (bps == 1) ? (data[out+off] - 128) * 128 : qToLittleEndian(((short*)data)[(out/2)+off]);
+ }
+
+ int add(int* mixl, int* mixr, int count)
+ {
+ int bytesPerSample = chunkdata.wBitsPerSample >> 3;
+
+ if ( mMuted ) {
+ sampleRunin -= qMin(sampleRunin,count);
+ while (count && (dev != -1)) {
+ if (out >= *max) {
+ // switch buffers
+ out = 0;
+ if (data == data1 && max2 != 0) {
+ data = data2;
+ max = &max2;
+ max1 = 0;
+ } else if (data == data2 && max1 != 0) {
+ data = data1;
+ max = &max1;
+ max2 = 0;
+ } else {
+ qDebug("QSS Read Error: both buffers empty");
+ return 0;
+ }
+ }
+ samples_due += sound_speed;
+ while (count && samples_due >= chunkdata.samplesPerSec) {
+ samples_due -= chunkdata.samplesPerSec;
+ count--;
+ }
+ out += bytesPerSample * chunkdata.channels;
+ }
+ return count;
+ }
+
+ // This shouldn't be the case
+ if ( !mixl || !mixr )
+ return 0;
+
+ int lVolNum = leftVolume, lVolDen = maxVolume;
+ int rVolNum = rightVolume, rVolDen = maxVolume;
+ if (priorityExists > 0 && !isPriority) {
+ lVolNum = 0; // later, make this gradually fade in and out.
+ lVolDen = 5;
+ rVolNum = 0;
+ rVolDen = 5;
+ }
+
+ while (count && (dev != -1)) {
+ if (out >= *max) {
+ // switch buffers
+ out = 0;
+ if (data == data1 && max2 != 0) {
+ data = data2;
+ max = &max2;
+ max1 = 0;
+ } else if (data == data2 && max1 != 0) {
+ data = data1;
+ max = &max1;
+ max2 = 0;
+ } else {
+ qDebug("QSS Read Error: both buffers empty");
+ return 0;
+ }
+ }
+ samples_due += sound_speed;
+ if (count && samples_due >= chunkdata.samplesPerSec) {
+ int l = getSample(0,bytesPerSample)*lVolNum/lVolDen;
+ int r = (chunkdata.channels == 2) ? getSample(1,bytesPerSample)*rVolNum/rVolDen : l;
+ if (!sound_stereo && chunkdata.channels == 2)
+ l += r;
+ if (sampleRunin) {
+ while (sampleRunin && count && samples_due >= chunkdata.samplesPerSec) {
+ mixl++;
+ if (sound_stereo)
+ mixr++;
+ samples_due -= chunkdata.samplesPerSec;
+ sampleRunin--;
+ count--;
+ }
+ }
+ while (count && samples_due >= chunkdata.samplesPerSec) {
+ *mixl++ += l;
+ if (sound_stereo)
+ *mixr++ += r;
+ samples_due -= chunkdata.samplesPerSec;
+ count--;
+ }
+ }
+
+ // optimize out manipulation of sample if downsampling and we skip it
+ out += bytesPerSample * chunkdata.channels;
+ }
+
+ return count;
+ }
+
+ virtual bool finished() const = 0;
+
+ bool equal(int wid, int sid)
+ {
+ return (wid == mWid && sid == mSid);
+ }
+
+protected:
+
+ char * prepareBuffer( int &size)
+ {
+ // keep reading as long as there is 50 % or more room in off buffer.
+ if (data == data1 && (max2<<1 < sound_buffer_size)) {
+ size=sound_buffer_size - max2;
+ return (char *)data2;
+ } else if (data == data2 && (max1<<1 < sound_buffer_size)) {
+ size=sound_buffer_size - max1;
+ return (char *)data1;
+ } else {
+ size = 0;
+ return 0;
+ }
+ }
+
+ void updateBuffer(int read)
+ {
+ // always reads to off buffer.
+ if (read >= 0) {
+ if (data == data2) {
+ max1 = read;
+ } else {
+ max2 = read;
+ }
+ }
+ }
+
+ int devSamples()
+ {
+ int possible = (((max1+max2-out) / ((chunkdata.wBitsPerSample>>3)*chunkdata.channels))
+ *sound_speed)/chunkdata.samplesPerSec;
+
+ return possible;
+ }
+
+
+ struct {
+ qint16 formatTag;
+ qint16 channels;
+ qint32 samplesPerSec;
+ qint32 avgBytesPerSec;
+ qint16 blockAlign;
+ qint16 wBitsPerSample;
+ } chunkdata;
+ int dev;
+ int samples_due;
+private:
+ int mWid;
+ int mSid;
+ int leftVolume;
+ int rightVolume;
+ bool isPriority;
+ static int priorityExists;
+ int *max;
+ uchar *data;
+ uchar data1[sound_buffer_size+4]; // +4 to handle badly aligned input data
+ uchar data2[sound_buffer_size+4]; // +4 to handle badly aligned input data
+ int out, max1, max2;
+ int sampleRunin;
+ bool mMuted;
+};
+
+int QWSSoundServerProvider::priorityExists = 0;
+
+class QWSSoundServerBucket : public QWSSoundServerProvider {
+public:
+ QWSSoundServerBucket(int d, int wid, int sid)
+ : QWSSoundServerProvider(wid, sid)
+ {
+ dev = d;
+ wavedata_remaining = -1;
+ mFinishedRead = false;
+ mInsufficientSamples = false;
+ }
+ ~QWSSoundServerBucket()
+ {
+ //dev->close();
+ ::close(dev);
+ }
+ bool finished() const
+ {
+ //return !max;
+ return mInsufficientSamples && mFinishedRead ;
+ }
+ int readySamples(int)
+ {
+ int size;
+ char *dest = prepareBuffer(size);
+ // may want to change this to something like
+ // if (data == data1 && max2<<1 < sound_buffer_size
+ // ||
+ // data == data2 && max1<<1 < sound_buffer_size)
+ // so will keep filling off buffer while there is +50% space left
+ if (size > 0 && dest != 0) {
+ while ( wavedata_remaining < 0 ) {
+ //max = 0;
+ wavedata_remaining = -1;
+ // Keep reading chunks...
+ const int n = sizeof(chunk)-sizeof(chunk.data);
+ int nr = ::read(dev, (void*)&chunk,n);
+ if ( nr != n ) {
+ // XXX check error? or don't we care?
+ wavedata_remaining = 0;
+ mFinishedRead = true;
+ } else if ( qstrncmp(chunk.id,"data",4) == 0 ) {
+ wavedata_remaining = qToLittleEndian( chunk.size );
+
+ //out = max = sound_buffer_size;
+
+ } else if ( qstrncmp(chunk.id,"RIFF",4) == 0 ) {
+ char d[4];
+ if ( read(dev, d, 4) != 4 ) {
+ // XXX check error? or don't we care?
+ //qDebug("couldn't read riff");
+ mInsufficientSamples = true;
+ mFinishedRead = true;
+ return 0;
+ } else if ( qstrncmp(d,"WAVE",4) != 0 ) {
+ // skip
+ if ( chunk.size > 1000000000 || lseek(dev,chunk.size-4, SEEK_CUR) == -1 ) {
+ //qDebug("oversized wav chunk");
+ mFinishedRead = true;
+ }
+ }
+ } else if ( qstrncmp(chunk.id,"fmt ",4) == 0 ) {
+ if ( ::read(dev,(char*)&chunkdata,sizeof(chunkdata)) != sizeof(chunkdata) ) {
+ // XXX check error? or don't we care?
+ //qDebug("couldn't ready chunkdata");
+ mFinishedRead = true;
+ }
+
+#define WAVE_FORMAT_PCM 1
+ else
+ {
+ /*
+ ** Endian Fix the chuck data
+ */
+ chunkdata.formatTag = qToLittleEndian( chunkdata.formatTag );
+ chunkdata.channels = qToLittleEndian( chunkdata.channels );
+ chunkdata.samplesPerSec = qToLittleEndian( chunkdata.samplesPerSec );
+ chunkdata.avgBytesPerSec = qToLittleEndian( chunkdata.avgBytesPerSec );
+ chunkdata.blockAlign = qToLittleEndian( chunkdata.blockAlign );
+ chunkdata.wBitsPerSample = qToLittleEndian( chunkdata.wBitsPerSample );
+ if ( chunkdata.formatTag != WAVE_FORMAT_PCM ) {
+ qWarning("WAV file: UNSUPPORTED FORMAT %d",chunkdata.formatTag);
+ mFinishedRead = true;
+ }
+ }
+ } else {
+ // ignored chunk
+ if ( chunk.size > 1000000000 || lseek(dev, chunk.size, SEEK_CUR) == -1) {
+ //qDebug("chunk size too big");
+ mFinishedRead = true;
+ }
+ }
+ }
+ // this looks wrong.
+ if (wavedata_remaining <= 0) {
+ mFinishedRead = true;
+ }
+
+ }
+ // may want to change this to something like
+ // if (data == data1 && max2<<1 < sound_buffer_size
+ // ||
+ // data == data2 && max1<<1 < sound_buffer_size)
+ // so will keep filling off buffer while there is +50% space left
+
+ if (wavedata_remaining) {
+ if (size > 0 && dest != 0) {
+ int read = ::read(dev, dest, qMin(size, wavedata_remaining));
+ // XXX check error? or don't we care?
+ wavedata_remaining -= read;
+ updateBuffer(read);
+ if (read <= 0) // data unexpectidly ended
+ mFinishedRead = true;
+ }
+ }
+ int possible = devSamples();
+ if (possible == 0)
+ mInsufficientSamples = true;
+ return possible;
+ }
+
+protected:
+ QRiffChunk chunk;
+ int wavedata_remaining;
+ bool mFinishedRead;
+ bool mInsufficientSamples;
+};
+
+class QWSSoundServerStream : public QWSSoundServerProvider {
+public:
+ QWSSoundServerStream(int d,int c, int f, int b,
+ int wid, int sid)
+ : QWSSoundServerProvider(wid, sid)
+ {
+ chunkdata.channels = c;
+ chunkdata.samplesPerSec = f;
+ chunkdata.wBitsPerSample = b;
+ dev = d;
+ //fcntl( dev, F_SETFL, O_NONBLOCK );
+ lasttime = 0;
+ }
+
+ ~QWSSoundServerStream()
+ {
+ if (dev != -1) {
+ ::close(dev);
+ dev = -1;
+ }
+ }
+
+ bool finished() const
+ {
+ return (dev == -1);
+ }
+
+
+ int readySamples(int)
+ {
+ int size;
+ char *dest = prepareBuffer(size);
+ if (size > 0 && dest != 0 && dev != -1) {
+
+ int read = ::read(dev, dest, size);
+ if (read < 0) {
+ switch(errno) {
+ case EAGAIN:
+ case EINTR:
+ // means read may yet succeed on the next attempt
+ break;
+ default:
+ // unexpected error, fail.
+ ::close(dev);
+ dev = -1;
+ }
+ } else if (read == 0) {
+ // 0 means writer has closed dev and/or
+ // file is at end.
+ ::close(dev);
+ dev = -1;
+ } else {
+ updateBuffer(read);
+ }
+ }
+ int possible = devSamples();
+ if (possible == 0)
+ startSampleRunin();
+ return possible;
+ }
+
+protected:
+ time_t lasttime;
+};
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent) :
+ QWSServerSocket(QT_VFB_SOUND_PIPE(qws_display_id), parent)
+{
+ connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
+}
+
+
+#ifdef QT3_SUPPORT
+QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent, const char *name) :
+ QWSServerSocket(QT_VFB_SOUND_PIPE(qws_display_id), parent)
+{
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
+}
+#endif
+
+void QWSSoundServerSocket::newConnection()
+{
+ while (QWS_SOCK_BASE *sock = nextPendingConnection()) {
+ QWSSoundServerClient* client = new QWSSoundServerClient(sock,this);
+
+ connect(client, SIGNAL(play(int,int,QString)),
+ this, SIGNAL(playFile(int,int,QString)));
+ connect(client, SIGNAL(play(int,int,QString,int,int)),
+ this, SIGNAL(playFile(int,int,QString,int,int)));
+ connect(client, SIGNAL(playRaw(int,int,QString,int,int,int,int)),
+ this, SIGNAL(playRawFile(int,int,QString,int,int,int,int)));
+
+ connect(client, SIGNAL(pause(int,int)),
+ this, SIGNAL(pauseFile(int,int)));
+ connect(client, SIGNAL(stop(int,int)),
+ this, SIGNAL(stopFile(int,int)));
+ connect(client, SIGNAL(playPriorityOnly(bool)),
+ this, SIGNAL(playPriorityOnly(bool)));
+ connect(client, SIGNAL(stopAll(int)),
+ this, SIGNAL(stopAll(int)));
+ connect(client, SIGNAL(resume(int,int)),
+ this, SIGNAL(resumeFile(int,int)));
+
+ connect(client, SIGNAL(setSilent(bool)),
+ this, SIGNAL(setSilent(bool)));
+
+ connect(client, SIGNAL(setMute(int,int,bool)),
+ this, SIGNAL(setMute(int,int,bool)));
+ connect(client, SIGNAL(setVolume(int,int,int,int)),
+ this, SIGNAL(setVolume(int,int,int,int)));
+
+ connect(this, SIGNAL(soundFileCompleted(int,int)),
+ client, SLOT(sendSoundCompleted(int,int)));
+ connect(this, SIGNAL(deviceReady(int,int)),
+ client, SLOT(sendDeviceReady(int,int)));
+ connect(this, SIGNAL(deviceError(int,int,int)),
+ client, SLOT(sendDeviceError(int,int,int)));
+ }
+}
+
+#endif
+
+class QWSSoundServerPrivate : public QObject {
+ Q_OBJECT
+
+public:
+ QWSSoundServerPrivate(QObject* parent=0, const char* name=0) :
+ QObject(parent)
+ {
+ timerId = 0;
+ if (name)
+ setObjectName(QString::fromAscii(name));
+#ifndef QT_NO_QWS_SOUNDSERVER
+ server = new QWSSoundServerSocket(this);
+
+ connect(server, SIGNAL(playFile(int,int,QString)),
+ this, SLOT(playFile(int,int,QString)));
+ connect(server, SIGNAL(playFile(int,int,QString,int,int)),
+ this, SLOT(playFile(int,int,QString,int,int)));
+ connect(server, SIGNAL(playRawFile(int,int,QString,int,int,int,int)),
+ this, SLOT(playRawFile(int,int,QString,int,int,int,int)));
+
+ connect(server, SIGNAL(pauseFile(int,int)),
+ this, SLOT(pauseFile(int,int)));
+ connect(server, SIGNAL(stopFile(int,int)),
+ this, SLOT(stopFile(int,int)));
+ connect(server, SIGNAL(stopAll(int)),
+ this, SLOT(stopAll(int)));
+ connect(server, SIGNAL(playPriorityOnly(bool)),
+ this, SLOT(playPriorityOnly(bool)));
+ connect(server, SIGNAL(resumeFile(int,int)),
+ this, SLOT(resumeFile(int,int)));
+
+ connect( server, SIGNAL(setSilent(bool)),
+ this, SLOT(setSilent(bool)));
+
+ connect(server, SIGNAL(setMute(int,int,bool)),
+ this, SLOT(setMute(int,int,bool)));
+ connect(server, SIGNAL(setVolume(int,int,int,int)),
+ this, SLOT(setVolume(int,int,int,int)));
+
+ connect(this, SIGNAL(soundFileCompleted(int,int)),
+ server, SIGNAL(soundFileCompleted(int,int)));
+ connect(this, SIGNAL(deviceReady(int,int)),
+ server, SIGNAL(deviceReady(int,int)));
+ connect(this, SIGNAL(deviceError(int,int,int)),
+ server, SIGNAL(deviceError(int,int,int)));
+
+#endif
+ silent = false;
+ fd = -1;
+ unwritten = 0;
+ can_GETOSPACE = true;
+ }
+
+ ~QWSSoundServerPrivate()
+ {
+ qDeleteAll(active);
+ qDeleteAll(inactive);
+ }
+
+signals:
+ void soundFileCompleted(int, int);
+ void deviceReady(int, int);
+ void deviceError(int, int, int);
+
+public slots:
+ void playRawFile(int wid, int sid, const QString &filename, int freq, int channels, int bitspersample, int flags);
+ void playFile(int wid, int sid, const QString& filename);
+ void playFile(int wid, int sid, const QString& filename, int v, int flags);
+ void checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p);
+ void pauseFile(int wid, int sid);
+ void resumeFile(int wid, int sid);
+ void stopFile(int wid, int sid);
+ void stopAll(int wid);
+ void setVolume(int wid, int sid, int lv, int rv);
+ void setMute(int wid, int sid, bool m);
+ void playPriorityOnly(bool p);
+ void sendCompletedSignals();
+ void feedDevice(int fd);
+ void setSilent( bool enabled );
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+private:
+ int openFile(int wid, int sid, const QString& filename);
+ bool openDevice();
+ void closeDevice()
+ {
+ if (fd >= 0) {
+ ::close(fd);
+ fd = -1;
+ }
+ }
+
+ QList<QWSSoundServerProvider*> active;
+ QList<QWSSoundServerProvider*> inactive;
+ struct PresetVolume {
+ int wid;
+ int sid;
+ int left;
+ int right;
+ bool mute;
+ };
+ QList<PresetVolume> volumes;
+ struct CompletedInfo {
+ CompletedInfo( ) : groupId( 0 ), soundId( 0 ) { }
+ CompletedInfo( int _groupId, int _soundId ) : groupId( _groupId ), soundId( _soundId ) { }
+ int groupId;
+ int soundId;
+ };
+ QList<CompletedInfo> completed;
+
+ bool silent;
+
+ int fd;
+ int unwritten;
+ int timerId;
+ char* cursor;
+ short data[sound_buffer_size*2];
+ bool can_GETOSPACE;
+#ifndef QT_NO_QWS_SOUNDSERVER
+ QWSSoundServerSocket *server;
+#endif
+};
+
+void QWSSoundServerPrivate::setSilent( bool enabled )
+{
+ // Close output device
+ closeDevice();
+ if( !unwritten && !active.count() ) {
+ sendCompletedSignals();
+ }
+ // Stop processing audio
+ killTimer( timerId );
+ silent = enabled;
+ // If audio remaining, open output device and continue processing
+ if( unwritten || active.count() ) {
+ openDevice();
+ }
+}
+
+void QWSSoundServerPrivate::timerEvent(QTimerEvent* event)
+{
+ // qDebug("QSS timer event");
+ if( event->timerId() == timerId ) {
+ if (fd >= 0)
+ feedDevice(fd);
+ if (fd < 0) {
+ killTimer(timerId);
+ timerId = 0;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::playRawFile(int wid, int sid, const QString &filename,
+ int freq, int channels, int bitspersample, int flags)
+{
+#ifdef QT_NO_QWS_SOUNDSERVER
+ Q_UNUSED(flags);
+#endif
+ int f = openFile(wid, sid, filename);
+ if ( f ) {
+ QWSSoundServerStream *b = new QWSSoundServerStream(f, channels, freq, bitspersample, wid, sid);
+ // check preset volumes.
+ checkPresetVolumes(wid, sid, b);
+#ifndef QT_NO_QWS_SOUNDSERVER
+ b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority);
+#endif
+ active.append(b);
+ emit deviceReady(wid, sid);
+ }
+}
+
+void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename)
+{
+ int f = openFile(wid, sid, filename);
+ if ( f ) {
+ QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid);
+ checkPresetVolumes(wid, sid, b);
+ active.append( b );
+ emit deviceReady(wid, sid);
+ }
+}
+
+void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename,
+ int v, int flags)
+{
+#ifdef QT_NO_QWS_SOUNDSERVER
+ Q_UNUSED(flags);
+#endif
+ int f = openFile(wid, sid, filename);
+ if ( f ) {
+ QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid);
+ checkPresetVolumes(wid, sid, b);
+ b->setVolume(v, v);
+#ifndef QT_NO_QWS_SOUNDSERVER
+ b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority);
+#endif
+ active.append(b);
+ emit deviceReady(wid, sid);
+ }
+}
+
+void QWSSoundServerPrivate::checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p)
+{
+ QList<PresetVolume>::Iterator it = volumes.begin();
+ while (it != volumes.end()) {
+ PresetVolume v = *it;
+ if (v.wid == wid && v.sid == sid) {
+ p->setVolume(v.left, v.right);
+ p->setMute(v.mute);
+ it = volumes.erase(it);
+ return;
+ } else {
+ ++it;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::pauseFile(int wid, int sid)
+{
+ QWSSoundServerProvider *bucket;
+ for (int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ // found bucket....
+ active.removeAt(i);
+ inactive.append(bucket);
+ return;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::resumeFile(int wid, int sid)
+{
+ QWSSoundServerProvider *bucket;
+ for (int i = 0; i < inactive.size(); ++i ) {
+ bucket = inactive.at(i);
+ if (bucket->equal(wid, sid)) {
+ // found bucket....
+ inactive.removeAt(i);
+ active.append(bucket);
+ return;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::stopFile(int wid, int sid)
+{
+ QWSSoundServerProvider *bucket;
+ for (int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ active.removeAt(i);
+ delete bucket;
+ return;
+ }
+ }
+ for (int i = 0; i < inactive.size(); ++i ) {
+ bucket = inactive.at(i);
+ if (bucket->equal(wid, sid)) {
+ inactive.removeAt(i);
+ delete bucket;
+ return;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::stopAll(int wid)
+{
+ QWSSoundServerProvider *bucket;
+ if (!active.isEmpty()) {
+ QList<QWSSoundServerProvider*>::Iterator it = active.begin();
+ while (it != active.end()) {
+ bucket = *it;
+ if (bucket->groupId() == wid) {
+ it = active.erase(it);
+ delete bucket;
+ } else {
+ ++it;
+ }
+ }
+ }
+ if (!inactive.isEmpty()) {
+ QList<QWSSoundServerProvider*>::Iterator it = inactive.begin();
+ while (it != inactive.end()) {
+ bucket = *it;
+ if (bucket->groupId() == wid) {
+ it = inactive.erase(it);
+ delete bucket;
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+void QWSSoundServerPrivate::setVolume(int wid, int sid, int lv, int rv)
+{
+ QWSSoundServerProvider *bucket;
+ for( int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ bucket->setVolume(lv,rv);
+ return;
+ }
+ }
+ // If gotten here, then it means wid/sid wasn't set up yet.
+ // first find and remove current preset volumes, then add this one.
+ QList<PresetVolume>::Iterator it = volumes.begin();
+ while (it != volumes.end()) {
+ PresetVolume v = *it;
+ if (v.wid == wid && v.sid == sid)
+ it = volumes.erase(it);
+ else
+ ++it;
+ }
+ // and then add this volume
+ PresetVolume nv;
+ nv.wid = wid;
+ nv.sid = sid;
+ nv.left = lv;
+ nv.right = rv;
+ nv.mute = false;
+ volumes.append(nv);
+}
+
+void QWSSoundServerPrivate::setMute(int wid, int sid, bool m)
+{
+ QWSSoundServerProvider *bucket;
+ for( int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ bucket->setMute(m);
+ return;
+ }
+ }
+ // if gotten here then setting is being applied before item
+ // is created.
+ QList<PresetVolume>::Iterator it = volumes.begin();
+ while (it != volumes.end()) {
+ PresetVolume v = *it;
+ if (v.wid == wid && v.sid == sid) {
+ (*it).mute = m;
+ return;
+ }
+ }
+ if (m) {
+ PresetVolume nv;
+ nv.wid = wid;
+ nv.sid = sid;
+ nv.left = maxVolume>>1;
+ nv.right = maxVolume>>1;
+ nv.mute = true;
+ volumes.append(nv);
+ }
+}
+
+void QWSSoundServerPrivate::playPriorityOnly(bool p)
+{
+ QWSSoundServerProvider::setPlayPriorityOnly(p);
+}
+
+void QWSSoundServerPrivate::sendCompletedSignals()
+{
+ while( !completed.isEmpty() ) {
+ emit soundFileCompleted( (*completed.begin()).groupId,
+ (*completed.begin()).soundId );
+ completed.erase( completed.begin() );
+ }
+}
+
+
+int QWSSoundServerPrivate::openFile(int wid, int sid, const QString& filename)
+{
+ stopFile(wid, sid); // close and re-open.
+ int f = QT_OPEN(QFile::encodeName(filename), O_RDONLY|O_NONBLOCK);
+ if (f == -1) {
+ // XXX check ferror, check reason.
+ qDebug("Failed opening \"%s\"",filename.toLatin1().data());
+#ifndef QT_NO_QWS_SOUNDSERVER
+ emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningFile );
+#endif
+ } else if ( openDevice() ) {
+ return f;
+ }
+#ifndef QT_NO_QWS_SOUNDSERVER
+ emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningAudioDevice );
+#endif
+ return 0;
+}
+
+bool QWSSoundServerPrivate::openDevice()
+{
+ if (fd < 0) {
+ if( silent ) {
+ fd = QT_OPEN( "/dev/null", O_WRONLY );
+ // Emulate write to audio device
+ int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))/sound_speed/2;
+ timerId = startTimer(delay);
+
+ return true;
+ }
+ //
+ // Don't block open right away.
+ //
+ bool openOkay = false;
+ if ((fd = QT_OPEN("/dev/dsp", O_WRONLY|O_NONBLOCK)) != -1) {
+ int flags = fcntl(fd, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ openOkay = (fcntl(fd, F_SETFL, flags) == 0);
+ }
+ if (!openOkay) {
+ qDebug("Failed opening audio device");
+ return false;
+ }
+
+ // Setup soundcard at 16 bit mono
+ int v;
+ //v=0x00010000+sound_fragment_size;
+ // um the media player did this instead.
+ v=0x10000 * 4 + sound_fragment_size;
+ if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v))
+ qWarning("Could not set fragments to %08x",v);
+#ifdef QT_QWS_SOUND_16BIT
+ //
+ // Use native endian
+ // Since we have manipulated the data volume the data
+ // is now in native format, even though its stored
+ // as little endian in the WAV file
+ //
+ v=AFMT_S16_NE; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v))
+ qWarning("Could not set format %d",v);
+ if (AFMT_S16_NE != v)
+ qDebug("Want format %d got %d", AFMT_S16_LE, v);
+#else
+ v=AFMT_U8; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v))
+ qWarning("Could not set format %d",v);
+ if (AFMT_U8 != v)
+ qDebug("Want format %d got %d", AFMT_U8, v);
+#endif
+ v=sound_stereo; if (ioctl(fd, SNDCTL_DSP_STEREO, &v))
+ qWarning("Could not set stereo %d",v);
+ if (sound_stereo != v)
+ qDebug("Want stereo %d got %d", sound_stereo, v);
+#ifdef QT_QWS_SOUND_STEREO
+ sound_stereo=v;
+#endif
+ v=sound_speed; if (ioctl(fd, SNDCTL_DSP_SPEED, &sound_speed))
+ qWarning("Could not set speed %d",v);
+ if (v != sound_speed)
+ qDebug("Want speed %d got %d", v, sound_speed);
+
+ int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))
+ /sound_speed/2;
+ // qDebug("QSS delay: %d", delay);
+ timerId = startTimer(delay);
+
+ //
+ // Check system volume
+ //
+ int mixerHandle = QT_OPEN( "/dev/mixer", O_RDWR|O_NONBLOCK );
+ if ( mixerHandle >= 0 ) {
+ int volume;
+ ioctl( mixerHandle, MIXER_READ(0), &volume );
+ close( mixerHandle );
+ if ( volume < 1<<(sound_stereo+sound_16bit) )
+ qDebug("Want sound at %d got %d",
+ 1<<(sound_stereo+sound_16bit), volume);
+ } else
+ qDebug( "get volume of audio device failed" );
+
+ }
+ return true;
+}
+
+void QWSSoundServerPrivate::feedDevice(int fd)
+{
+ if ( !unwritten && active.size() == 0 ) {
+ closeDevice();
+ sendCompletedSignals();
+ return;
+ } else {
+ sendCompletedSignals();
+ }
+
+ QWSSoundServerProvider* bucket;
+
+ // find out how much audio is possible
+ int available = sound_buffer_size;
+ QList<QWSSoundServerProvider*> running;
+ for (int i = 0; i < active.size(); ++i) {
+ bucket = active.at(i);
+ int ready = bucket->readySamples(available);
+ if (ready > 0) {
+ available = qMin(available, ready);
+ running.append(bucket);
+ }
+ }
+
+ audio_buf_info info;
+ if (can_GETOSPACE && ioctl(fd,SNDCTL_DSP_GETOSPACE,&info)) {
+ can_GETOSPACE = false;
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ }
+ if (!can_GETOSPACE)
+ info.fragments = 4; // #### configurable?
+ if (info.fragments > 0) {
+ if (!unwritten) {
+ int left[sound_buffer_size];
+ memset(left,0,available*sizeof(int));
+ int right[sound_buffer_size];
+ if ( sound_stereo )
+ memset(right,0,available*sizeof(int));
+
+ if (running.size() > 0) {
+ // should do volume mod here in regards to each bucket to avoid flattened/bad peaks.
+ for (int i = 0; i < running.size(); ++i ) {
+ bucket = running.at(i);
+ int unused = bucket->add(left,right,available);
+ if (unused > 0) {
+ // this error is quite serious, as
+ // it will really screw up mixing.
+ qDebug("provider lied about samples ready");
+ }
+ }
+ if ( sound_16bit ) {
+ short *d = (short*)data;
+ for (int i=0; i<available; i++) {
+ *d++ = (short)qMax(qMin(left[i],32767),-32768);
+ if ( sound_stereo )
+ *d++ = (short)qMax(qMin(right[i],32767),-32768);
+ }
+ } else {
+ signed char *d = (signed char *)data;
+ for (int i=0; i<available; i++) {
+ *d++ = (signed char)qMax(qMin(left[i]/256,127),-128)+128;
+ if ( sound_stereo )
+ *d++ = (signed char)qMax(qMin(right[i]/256,127),-128)+128;
+ }
+ }
+ unwritten = available*(sound_16bit+1)*(sound_stereo+1);
+ cursor = (char*)data;
+ }
+ }
+ // sound open, but nothing written. Should clear the buffer.
+
+ int w;
+ if (unwritten) {
+ w = ::write(fd,cursor,unwritten);
+
+ if (w < 0) {
+ if (can_GETOSPACE)
+ return;
+ w = 0;
+ }
+
+ cursor += w;
+ unwritten -= w;
+ } else {
+ // write some zeros to clear the buffer?
+ if (!zeroMem)
+ zeroMem = (char *)calloc(sound_buffer_size, sizeof(char));
+ w = ::write(fd, zeroMem, sound_buffer_size);
+ if (w < 0)
+ w = 0;
+ }
+ }
+
+ QList<QWSSoundServerProvider*>::Iterator it = active.begin();
+ while (it != active.end()) {
+ bucket = *it;
+ if (bucket->finished()) {
+ completed.append(CompletedInfo(bucket->groupId(), bucket->soundId()));
+ it = active.erase(it);
+ delete bucket;
+ } else {
+ ++it;
+ }
+ }
+}
+
+
+QWSSoundServer::QWSSoundServer(QObject* parent) :
+ QObject(parent)
+{
+ d = new QWSSoundServerPrivate(this);
+
+ connect( d, SIGNAL(soundFileCompleted(int,int)),
+ this, SLOT(translateSoundCompleted(int,int)) );
+}
+
+void QWSSoundServer::playFile( int sid, const QString& filename )
+{
+ //wid == 0, as it is the server initiating rather than a client
+ // if wid was passable, would accidently collide with server
+ // sockect's wids.
+ d->playFile(0, sid, filename);
+}
+
+void QWSSoundServer::pauseFile( int sid )
+{
+ d->pauseFile(0, sid);
+}
+
+void QWSSoundServer::stopFile( int sid )
+{
+ d->stopFile(0, sid);
+}
+
+void QWSSoundServer::resumeFile( int sid )
+{
+ d->resumeFile(0, sid);
+}
+
+QWSSoundServer::~QWSSoundServer()
+{
+ d->stopAll(0);
+}
+
+void QWSSoundServer::translateSoundCompleted( int, int sid )
+{
+ emit soundCompleted( sid );
+}
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+QWSSoundClient::QWSSoundClient(QObject* parent) :
+ QWSSocket(parent)
+{
+ connectToLocalFile(QT_VFB_SOUND_PIPE(qws_display_id));
+ QObject::connect(this,SIGNAL(readyRead()),
+ this,SLOT(tryReadCommand()));
+ if( state() == QWS_SOCK_BASE::ConnectedState ) QTimer::singleShot(1, this, SIGNAL(connected()));
+ else QTimer::singleShot(1, this, SLOT(emitConnectionRefused()));
+}
+
+QWSSoundClient::~QWSSoundClient( )
+{
+ flush();
+}
+
+void QWSSoundClient::reconnect()
+{
+ connectToLocalFile(QT_VFB_SOUND_PIPE(qws_display_id));
+ if( state() == QWS_SOCK_BASE::ConnectedState ) emit connected();
+ else emit error( QTcpSocket::ConnectionRefusedError );
+}
+
+void QWSSoundClient::sendServerMessage(QString msg)
+{
+#ifndef QT_NO_TEXTCODEC
+ QByteArray u = msg.toUtf8();
+#else
+ QByteArray u = msg.toLatin1();
+#endif
+ write(u.data(), u.length());
+ flush();
+}
+
+void QWSSoundClient::play( int id, const QString& filename )
+{
+ QFileInfo fi(filename);
+ sendServerMessage(QLatin1String("PLAY ")
+ + QString::number(id) + QLatin1Char(' ')
+ + fi.absoluteFilePath() + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::play( int id, const QString& filename, int volume, int flags)
+{
+ QFileInfo fi(filename);
+ sendServerMessage(QLatin1String("PLAYEXTEND ")
+ + QString::number(id) + QLatin1Char(' ')
+ + QString::number(volume) + QLatin1Char(' ')
+ + QString::number(flags) + QLatin1Char(' ')
+ + fi.absoluteFilePath() + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::pause( int id )
+{
+ sendServerMessage(QLatin1String("PAUSE ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::stop( int id )
+{
+ sendServerMessage(QLatin1String("STOP ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::resume( int id )
+{
+ sendServerMessage(QLatin1String("RESUME ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::playRaw( int id, const QString& filename,
+ int freq, int chs, int bitspersample, int flags)
+{
+ QFileInfo fi(filename);
+ sendServerMessage(QLatin1String("PLAYRAW ")
+ + QString::number(id) + QLatin1Char(' ')
+ + QString::number(chs) + QLatin1Char(' ')
+ + QString::number(freq) + QLatin1Char(' ')
+ + QString::number(bitspersample) + QLatin1Char(' ')
+ + QString::number(flags) + QLatin1Char(' ')
+ + fi.absoluteFilePath() + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::setMute( int id, bool m )
+{
+ sendServerMessage(QLatin1String(m ? "MUTE " : "UNMUTE ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::setVolume( int id, int leftVol, int rightVol )
+{
+ sendServerMessage(QLatin1String("SETVOLUME ")
+ + QString::number(id) + QLatin1Char(' ')
+ + QString::number(leftVol) + QLatin1Char(' ')
+ + QString::number(rightVol) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::playPriorityOnly( bool pri )
+{
+ sendServerMessage(QLatin1String("PRIORITYONLY ")
+ + QString::number(pri ? 1 : 0) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::setSilent( bool enable )
+{
+ sendServerMessage(QLatin1String("SILENT ")
+ + QString::number( enable ? 1 : 0 ) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::tryReadCommand()
+{
+ while ( canReadLine() ) {
+ QString l = QString::fromAscii(readLine());
+ l.truncate(l.length()-1); // chomp
+ QStringList token = l.split(QLatin1Char(' '));
+ if (token[0] == QLatin1String("SOUNDCOMPLETED")) {
+ emit soundCompleted(token[1].toInt());
+ } else if (token[0] == QLatin1String("DEVICEREADY")) {
+ emit deviceReady(token[1].toInt());
+ } else if (token[0] == QLatin1String("DEVICEERROR")) {
+ emit deviceError(token[1].toInt(),(DeviceErrors)token[2].toInt());
+ }
+ }
+}
+
+void QWSSoundClient::emitConnectionRefused()
+{
+ emit error( QTcpSocket::ConnectionRefusedError );
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "qsoundqss_qws.moc"
+
+#endif // QT_NO_SOUND