diff options
Diffstat (limited to 'src/gui/embedded/qsoundqss_qws.cpp')
-rw-r--r-- | src/gui/embedded/qsoundqss_qws.cpp | 1530 |
1 files changed, 0 insertions, 1530 deletions
diff --git a/src/gui/embedded/qsoundqss_qws.cpp b/src/gui/embedded/qsoundqss_qws.cpp deleted file mode 100644 index bd6da77d4c..0000000000 --- a/src/gui/embedded/qsoundqss_qws.cpp +++ /dev/null @@ -1,1530 +0,0 @@ -/**************************************************************************** -** -** 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 |