📄 qsoundqss_qws.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#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 <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>extern int errno;#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_SOUNDSERVERextern int qws_display_id;#define SOUND_PIPE "/tmp/.qt_soundserver-%1"#endifstatic 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;#elsestatic const int sound_fragment_size = 12;#endifstatic 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_STEREOstatic int sound_stereo=QT_QWS_SOUND_STEREO;#elsestatic const int sound_stereo=0;#endif#ifdef QT_QWS_SOUND_16BITstatic bool sound_16bit=QT_QWS_SOUND_16BIT;#elsestatic const bool sound_16bit=false;#endif#ifndef QT_NO_QWS_SOUNDSERVERclass QWSSoundServerClient : public QObject { Q_OBJECTpublic: 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'));}#endifstatic 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) { return (bps == 1) ? (data[out+off] - 128) * 128 : ((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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -