source: trunk/src/gui/embedded/qsoundqss_qws.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 43.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsoundqss_qws.h"
43
44#ifndef QT_NO_SOUND
45#include <qbytearray.h>
46#include <qlist.h>
47#include <qsocketnotifier.h>
48#include <qfile.h>
49#include <qfileinfo.h>
50#include <qstringlist.h>
51#include <qevent.h>
52#include <qalgorithms.h>
53#include <qtimer.h>
54#include <qpointer.h>
55#include <qendian.h>
56#include <private/qcore_unix_p.h> // overrides QT_OPEN
57
58#include <unistd.h>
59#include <stdlib.h>
60#include <fcntl.h>
61#include <errno.h>
62#include <time.h>
63#include <sys/types.h>
64#include <sys/stat.h>
65#include <sys/ioctl.h>
66#include <sys/soundcard.h>
67
68#include <qdebug.h>
69
70#include <qvfbhdr.h>
71
72extern int errno;
73
74QT_BEGIN_NAMESPACE
75
76#define QT_QWS_SOUND_16BIT 1 // or 0, or undefined for always 0
77#define QT_QWS_SOUND_STEREO 1 // or 0, or undefined for always 0
78
79// Zaurus SL5000D doesn't seem to return any error if setting to 44000 and it fails,
80// however 44100 works, 44100 is more common that 44000.
81static int sound_speed = 44100;
82#ifndef QT_NO_QWS_SOUNDSERVER
83extern int qws_display_id;
84#endif
85
86static char *zeroMem = 0;
87
88struct QRiffChunk {
89 char id[4];
90 quint32 size;
91 char data[4/*size*/];
92};
93
94#if defined(QT_QWS_IPAQ)
95static const int sound_fragment_size = 12;
96#else
97static const int sound_fragment_size = 12;
98#endif
99static const int sound_buffer_size = 1 << sound_fragment_size;
100// nb. there will be an sound startup delay of
101// 2^sound_fragment_size / sound_speed seconds.
102// (eg. sound_fragment_size==12, sound_speed==44000 means 0.093s delay)
103
104#ifdef QT_QWS_SOUND_STEREO
105static int sound_stereo=QT_QWS_SOUND_STEREO;
106#else
107static const int sound_stereo=0;
108#endif
109#ifdef QT_QWS_SOUND_16BIT
110static bool sound_16bit=QT_QWS_SOUND_16BIT;
111#else
112static const bool sound_16bit=false;
113#endif
114
115#ifndef QT_NO_QWS_SOUNDSERVER
116class QWSSoundServerClient : public QObject {
117 Q_OBJECT
118
119public:
120 QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent);
121 ~QWSSoundServerClient();
122
123public slots:
124 void sendSoundCompleted(int, int);
125 void sendDeviceReady(int, int);
126 void sendDeviceError(int, int, int);
127
128signals:
129 void play(int, int, const QString&);
130 void play(int, int, const QString&, int, int);
131 void playRaw(int, int, const QString&, int, int, int, int);
132
133 void pause(int, int);
134 void stop(int, int);
135 void resume(int, int);
136 void setVolume(int, int, int, int);
137 void setMute(int, int, bool);
138
139 void stopAll(int);
140
141 void playPriorityOnly(bool);
142
143 void setSilent( bool );
144
145private slots:
146 void tryReadCommand();
147
148private:
149 void sendClientMessage(QString msg);
150 int mCurrentID;
151 int left, right;
152 bool priExist;
153 static int lastId;
154 static int nextId() { return ++lastId; }
155 QPointer<QWS_SOCK_BASE> socket;
156};
157
158int QWSSoundServerClient::lastId = 0;
159
160QWSSoundServerClient::QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent) :
161 QObject( parent )
162{
163 socket = s;
164 priExist = false;
165 mCurrentID = nextId();
166 connect(socket,SIGNAL(readyRead()),
167 this,SLOT(tryReadCommand()));
168 connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
169}
170
171QWSSoundServerClient::~QWSSoundServerClient()
172{
173 if (priExist)
174 playPriorityOnly(false);
175 emit stopAll(mCurrentID);
176 if (socket)
177 socket->deleteLater();
178}
179
180static QString getStringTok(QString &in)
181{
182 int pos = in.indexOf(QLatin1Char(' '));
183 QString ret;
184 if (pos > 0) {
185 ret = in.left(pos);
186 in = in.mid(pos+1);
187 } else {
188 ret = in;
189 in = QString::null;
190 }
191 return ret;
192}
193
194static int getNumTok(QString &in)
195{
196 return getStringTok(in).toInt();
197}
198
199void QWSSoundServerClient::tryReadCommand()
200{
201 while ( socket->canReadLine() ) {
202 QString l = QString::fromAscii(socket->readLine());
203 l.truncate(l.length()-1); // chomp
204 QString functionName = getStringTok(l);
205 int soundid = getNumTok(l);
206 if (functionName == QLatin1String("PLAY")) {
207 emit play(mCurrentID, soundid, l);
208 } else if (functionName == QLatin1String("PLAYEXTEND")) {
209 int volume = getNumTok(l);
210 int flags = getNumTok(l);
211 emit play(mCurrentID, soundid, l, volume, flags);
212 } else if (functionName == QLatin1String("PLAYRAW")) {
213 int chs = getNumTok(l);
214 int freq = getNumTok(l);
215 int bitspersample = getNumTok(l);
216 int flags = getNumTok(l);
217 emit playRaw(mCurrentID, soundid, l, freq, chs, bitspersample, flags);
218 } else if (functionName == QLatin1String("PAUSE")) {
219 emit pause(mCurrentID, soundid);
220 } else if (functionName == QLatin1String("STOP")) {
221 emit stop(mCurrentID, soundid);
222 } else if (functionName == QLatin1String("RESUME")) {
223 emit resume(mCurrentID, soundid);
224 } else if (functionName == QLatin1String("SETVOLUME")) {
225 int left = getNumTok(l);
226 int right = getNumTok(l);
227 emit setVolume(mCurrentID, soundid, left, right);
228 } else if (functionName == QLatin1String("MUTE")) {
229 emit setMute(mCurrentID, soundid, true);
230 } else if (functionName == QLatin1String("UNMUTE")) {
231 emit setMute(mCurrentID, soundid, false);
232 } else if (functionName == QLatin1String("PRIORITYONLY")) {
233 bool sPri = soundid != 0;
234 if (sPri != priExist) {
235 priExist = sPri;
236 emit playPriorityOnly(sPri);
237 }
238 } else if(functionName == QLatin1String("SILENT")) {
239 emit setSilent( soundid != 0 );
240 }
241 }
242}
243
244void QWSSoundServerClient::sendClientMessage(QString msg)
245{
246#ifndef QT_NO_TEXTCODEC
247 QByteArray u = msg.toUtf8();
248#else
249 QByteArray u = msg.toLatin1();
250#endif
251 socket->write(u.data(), u.length());
252 socket->flush();
253}
254
255void QWSSoundServerClient::sendSoundCompleted(int gid, int sid)
256{
257 if (gid == mCurrentID)
258 sendClientMessage(QLatin1String("SOUNDCOMPLETED ")
259 + QString::number(sid) + QLatin1Char('\n'));
260}
261
262void QWSSoundServerClient::sendDeviceReady(int gid, int sid)
263{
264 if (gid == mCurrentID)
265 sendClientMessage(QLatin1String("DEVICEREADY ")
266 + QString::number(sid) + QLatin1Char('\n'));
267}
268
269void QWSSoundServerClient::sendDeviceError(int gid, int sid, int err)
270{
271 if (gid == mCurrentID)
272 sendClientMessage(QLatin1String("DEVICEERROR ")
273 + QString::number(sid) + QLatin1Char(' ')
274 + QString::number(err) + QLatin1Char('\n'));
275}
276#endif
277
278static const int maxVolume = 100;
279static const int runinLength = 2*sound_buffer_size;
280class QWSSoundServerProvider {
281public:
282 QWSSoundServerProvider(int w, int s)
283 : mWid(w), mSid(s), mMuted(false)
284 {
285 leftVolume = maxVolume>>1;
286 rightVolume = maxVolume>>1;
287 isPriority = false;
288 samples_due = 0;
289 max1 = max2 = out = 0;// = sound_buffer_size;
290 data = data1;
291 max = &max1;
292 sampleRunin = 0;
293 dev = -1;
294 }
295
296 virtual ~QWSSoundServerProvider() {
297 }
298
299 int groupId() const { return mWid; }
300 int soundId() const { return mSid; }
301
302 void startSampleRunin() {
303 // inteded to provide even audio return from mute/pause/dead samples.
304 //sampleRunin = runinLength; // or more?
305 }
306
307
308 void setVolume(int lv, int rv) {
309 leftVolume = qMin(maxVolume, qMax(0, lv));
310 rightVolume = qMin(maxVolume, qMax(0, rv));
311 }
312
313 void setMute(bool m) { mMuted = m; }
314 bool muted() { return mMuted; }
315
316 void setPriority(bool p) {
317 if (p != isPriority) {
318 isPriority = p; // currently meaningless.
319 }
320 }
321
322
323 static void setPlayPriorityOnly(bool p)
324 {
325 if (p)
326 priorityExists++;
327 else
328 priorityExists--;
329
330 if (priorityExists < 0)
331 qDebug("QSS: got more priority offs than ons");
332 }
333
334 // return -1 for file broken, give up.
335 // else return sampels ready for playing.
336 // argument is max samples server is looking for,
337 // in terms of current device status.
338 virtual int readySamples(int) = 0;
339
340 int getSample(int off, int bps) {
341
342 //
343 // 16-bit audio data is converted to native endian so that it can be scaled
344 // Yes, this is ugly on a BigEndian machine
345 // Perhaps it shouldn't be scaled at all
346 //
347 return (bps == 1) ? (data[out+off] - 128) * 128 : qToLittleEndian(((short*)data)[(out/2)+off]);
348 }
349
350 int add(int* mixl, int* mixr, int count)
351 {
352 int bytesPerSample = chunkdata.wBitsPerSample >> 3;
353
354 if ( mMuted ) {
355 sampleRunin -= qMin(sampleRunin,count);
356 while (count && (dev != -1)) {
357 if (out >= *max) {
358 // switch buffers
359 out = 0;
360 if (data == data1 && max2 != 0) {
361 data = data2;
362 max = &max2;
363 max1 = 0;
364 } else if (data == data2 && max1 != 0) {
365 data = data1;
366 max = &max1;
367 max2 = 0;
368 } else {
369 qDebug("QSS Read Error: both buffers empty");
370 return 0;
371 }
372 }
373 samples_due += sound_speed;
374 while (count && samples_due >= chunkdata.samplesPerSec) {
375 samples_due -= chunkdata.samplesPerSec;
376 count--;
377 }
378 out += bytesPerSample * chunkdata.channels;
379 }
380 return count;
381 }
382
383 // This shouldn't be the case
384 if ( !mixl || !mixr )
385 return 0;
386
387 int lVolNum = leftVolume, lVolDen = maxVolume;
388 int rVolNum = rightVolume, rVolDen = maxVolume;
389 if (priorityExists > 0 && !isPriority) {
390 lVolNum = 0; // later, make this gradually fade in and out.
391 lVolDen = 5;
392 rVolNum = 0;
393 rVolDen = 5;
394 }
395
396 while (count && (dev != -1)) {
397 if (out >= *max) {
398 // switch buffers
399 out = 0;
400 if (data == data1 && max2 != 0) {
401 data = data2;
402 max = &max2;
403 max1 = 0;
404 } else if (data == data2 && max1 != 0) {
405 data = data1;
406 max = &max1;
407 max2 = 0;
408 } else {
409 qDebug("QSS Read Error: both buffers empty");
410 return 0;
411 }
412 }
413 samples_due += sound_speed;
414 if (count && samples_due >= chunkdata.samplesPerSec) {
415 int l = getSample(0,bytesPerSample)*lVolNum/lVolDen;
416 int r = (chunkdata.channels == 2) ? getSample(1,bytesPerSample)*rVolNum/rVolDen : l;
417 if (!sound_stereo && chunkdata.channels == 2)
418 l += r;
419 if (sampleRunin) {
420 while (sampleRunin && count && samples_due >= chunkdata.samplesPerSec) {
421 mixl++;
422 if (sound_stereo)
423 mixr++;
424 samples_due -= chunkdata.samplesPerSec;
425 sampleRunin--;
426 count--;
427 }
428 }
429 while (count && samples_due >= chunkdata.samplesPerSec) {
430 *mixl++ += l;
431 if (sound_stereo)
432 *mixr++ += r;
433 samples_due -= chunkdata.samplesPerSec;
434 count--;
435 }
436 }
437
438 // optimize out manipulation of sample if downsampling and we skip it
439 out += bytesPerSample * chunkdata.channels;
440 }
441
442 return count;
443 }
444
445 virtual bool finished() const = 0;
446
447 bool equal(int wid, int sid)
448 {
449 return (wid == mWid && sid == mSid);
450 }
451
452protected:
453
454 char * prepareBuffer( int &size)
455 {
456 // keep reading as long as there is 50 % or more room in off buffer.
457 if (data == data1 && (max2<<1 < sound_buffer_size)) {
458 size=sound_buffer_size - max2;
459 return (char *)data2;
460 } else if (data == data2 && (max1<<1 < sound_buffer_size)) {
461 size=sound_buffer_size - max1;
462 return (char *)data1;
463 } else {
464 size = 0;
465 return 0;
466 }
467 }
468
469 void updateBuffer(int read)
470 {
471 // always reads to off buffer.
472 if (read >= 0) {
473 if (data == data2) {
474 max1 = read;
475 } else {
476 max2 = read;
477 }
478 }
479 }
480
481 int devSamples()
482 {
483 int possible = (((max1+max2-out) / ((chunkdata.wBitsPerSample>>3)*chunkdata.channels))
484 *sound_speed)/chunkdata.samplesPerSec;
485
486 return possible;
487 }
488
489
490 struct {
491 qint16 formatTag;
492 qint16 channels;
493 qint32 samplesPerSec;
494 qint32 avgBytesPerSec;
495 qint16 blockAlign;
496 qint16 wBitsPerSample;
497 } chunkdata;
498 int dev;
499 int samples_due;
500private:
501 int mWid;
502 int mSid;
503 int leftVolume;
504 int rightVolume;
505 bool isPriority;
506 static int priorityExists;
507 int *max;
508 uchar *data;
509 uchar data1[sound_buffer_size+4]; // +4 to handle badly aligned input data
510 uchar data2[sound_buffer_size+4]; // +4 to handle badly aligned input data
511 int out, max1, max2;
512 int sampleRunin;
513 bool mMuted;
514};
515
516int QWSSoundServerProvider::priorityExists = 0;
517
518class QWSSoundServerBucket : public QWSSoundServerProvider {
519public:
520 QWSSoundServerBucket(int d, int wid, int sid)
521 : QWSSoundServerProvider(wid, sid)
522 {
523 dev = d;
524 wavedata_remaining = -1;
525 mFinishedRead = false;
526 mInsufficientSamples = false;
527 }
528 ~QWSSoundServerBucket()
529 {
530 //dev->close();
531 ::close(dev);
532 }
533 bool finished() const
534 {
535 //return !max;
536 return mInsufficientSamples && mFinishedRead ;
537 }
538 int readySamples(int)
539 {
540 int size;
541 char *dest = prepareBuffer(size);
542 // may want to change this to something like
543 // if (data == data1 && max2<<1 < sound_buffer_size
544 // ||
545 // data == data2 && max1<<1 < sound_buffer_size)
546 // so will keep filling off buffer while there is +50% space left
547 if (size > 0 && dest != 0) {
548 while ( wavedata_remaining < 0 ) {
549 //max = 0;
550 wavedata_remaining = -1;
551 // Keep reading chunks...
552 const int n = sizeof(chunk)-sizeof(chunk.data);
553 int nr = ::read(dev, (void*)&chunk,n);
554 if ( nr != n ) {
555 // XXX check error? or don't we care?
556 wavedata_remaining = 0;
557 mFinishedRead = true;
558 } else if ( qstrncmp(chunk.id,"data",4) == 0 ) {
559 wavedata_remaining = qToLittleEndian( chunk.size );
560
561 //out = max = sound_buffer_size;
562
563 } else if ( qstrncmp(chunk.id,"RIFF",4) == 0 ) {
564 char d[4];
565 if ( read(dev, d, 4) != 4 ) {
566 // XXX check error? or don't we care?
567 //qDebug("couldn't read riff");
568 mInsufficientSamples = true;
569 mFinishedRead = true;
570 return 0;
571 } else if ( qstrncmp(d,"WAVE",4) != 0 ) {
572 // skip
573 if ( chunk.size > 1000000000 || lseek(dev,chunk.size-4, SEEK_CUR) == -1 ) {
574 //qDebug("oversized wav chunk");
575 mFinishedRead = true;
576 }
577 }
578 } else if ( qstrncmp(chunk.id,"fmt ",4) == 0 ) {
579 if ( ::read(dev,(char*)&chunkdata,sizeof(chunkdata)) != sizeof(chunkdata) ) {
580 // XXX check error? or don't we care?
581 //qDebug("couldn't ready chunkdata");
582 mFinishedRead = true;
583 }
584
585#define WAVE_FORMAT_PCM 1
586 else
587 {
588 /*
589 ** Endian Fix the chuck data
590 */
591 chunkdata.formatTag = qToLittleEndian( chunkdata.formatTag );
592 chunkdata.channels = qToLittleEndian( chunkdata.channels );
593 chunkdata.samplesPerSec = qToLittleEndian( chunkdata.samplesPerSec );
594 chunkdata.avgBytesPerSec = qToLittleEndian( chunkdata.avgBytesPerSec );
595 chunkdata.blockAlign = qToLittleEndian( chunkdata.blockAlign );
596 chunkdata.wBitsPerSample = qToLittleEndian( chunkdata.wBitsPerSample );
597 if ( chunkdata.formatTag != WAVE_FORMAT_PCM ) {
598 qWarning("WAV file: UNSUPPORTED FORMAT %d",chunkdata.formatTag);
599 mFinishedRead = true;
600 }
601 }
602 } else {
603 // ignored chunk
604 if ( chunk.size > 1000000000 || lseek(dev, chunk.size, SEEK_CUR) == -1) {
605 //qDebug("chunk size too big");
606 mFinishedRead = true;
607 }
608 }
609 }
610 // this looks wrong.
611 if (wavedata_remaining <= 0) {
612 mFinishedRead = true;
613 }
614
615 }
616 // may want to change this to something like
617 // if (data == data1 && max2<<1 < sound_buffer_size
618 // ||
619 // data == data2 && max1<<1 < sound_buffer_size)
620 // so will keep filling off buffer while there is +50% space left
621
622 if (wavedata_remaining) {
623 if (size > 0 && dest != 0) {
624 int read = ::read(dev, dest, qMin(size, wavedata_remaining));
625 // XXX check error? or don't we care?
626 wavedata_remaining -= read;
627 updateBuffer(read);
628 if (read <= 0) // data unexpectidly ended
629 mFinishedRead = true;
630 }
631 }
632 int possible = devSamples();
633 if (possible == 0)
634 mInsufficientSamples = true;
635 return possible;
636 }
637
638protected:
639 QRiffChunk chunk;
640 int wavedata_remaining;
641 bool mFinishedRead;
642 bool mInsufficientSamples;
643};
644
645class QWSSoundServerStream : public QWSSoundServerProvider {
646public:
647 QWSSoundServerStream(int d,int c, int f, int b,
648 int wid, int sid)
649 : QWSSoundServerProvider(wid, sid)
650 {
651 chunkdata.channels = c;
652 chunkdata.samplesPerSec = f;
653 chunkdata.wBitsPerSample = b;
654 dev = d;
655 //fcntl( dev, F_SETFL, O_NONBLOCK );
656 lasttime = 0;
657 }
658
659 ~QWSSoundServerStream()
660 {
661 if (dev != -1) {
662 ::close(dev);
663 dev = -1;
664 }
665 }
666
667 bool finished() const
668 {
669 return (dev == -1);
670 }
671
672
673 int readySamples(int)
674 {
675 int size;
676 char *dest = prepareBuffer(size);
677 if (size > 0 && dest != 0 && dev != -1) {
678
679 int read = ::read(dev, dest, size);
680 if (read < 0) {
681 switch(errno) {
682 case EAGAIN:
683 case EINTR:
684 // means read may yet succeed on the next attempt
685 break;
686 default:
687 // unexpected error, fail.
688 ::close(dev);
689 dev = -1;
690 }
691 } else if (read == 0) {
692 // 0 means writer has closed dev and/or
693 // file is at end.
694 ::close(dev);
695 dev = -1;
696 } else {
697 updateBuffer(read);
698 }
699 }
700 int possible = devSamples();
701 if (possible == 0)
702 startSampleRunin();
703 return possible;
704 }
705
706protected:
707 time_t lasttime;
708};
709
710#ifndef QT_NO_QWS_SOUNDSERVER
711QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent) :
712 QWSServerSocket(QT_VFB_SOUND_PIPE(qws_display_id), parent)
713{
714 connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
715}
716
717
718#ifdef QT3_SUPPORT
719QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent, const char *name) :
720 QWSServerSocket(QT_VFB_SOUND_PIPE(qws_display_id), parent)
721{
722 if (name)
723 setObjectName(QString::fromAscii(name));
724 connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
725}
726#endif
727
728void QWSSoundServerSocket::newConnection()
729{
730 while (QWS_SOCK_BASE *sock = nextPendingConnection()) {
731 QWSSoundServerClient* client = new QWSSoundServerClient(sock,this);
732
733 connect(client, SIGNAL(play(int,int,QString)),
734 this, SIGNAL(playFile(int,int,QString)));
735 connect(client, SIGNAL(play(int,int,QString,int,int)),
736 this, SIGNAL(playFile(int,int,QString,int,int)));
737 connect(client, SIGNAL(playRaw(int,int,QString,int,int,int,int)),
738 this, SIGNAL(playRawFile(int,int,QString,int,int,int,int)));
739
740 connect(client, SIGNAL(pause(int,int)),
741 this, SIGNAL(pauseFile(int,int)));
742 connect(client, SIGNAL(stop(int,int)),
743 this, SIGNAL(stopFile(int,int)));
744 connect(client, SIGNAL(playPriorityOnly(bool)),
745 this, SIGNAL(playPriorityOnly(bool)));
746 connect(client, SIGNAL(stopAll(int)),
747 this, SIGNAL(stopAll(int)));
748 connect(client, SIGNAL(resume(int,int)),
749 this, SIGNAL(resumeFile(int,int)));
750
751 connect(client, SIGNAL(setSilent(bool)),
752 this, SIGNAL(setSilent(bool)));
753
754 connect(client, SIGNAL(setMute(int,int,bool)),
755 this, SIGNAL(setMute(int,int,bool)));
756 connect(client, SIGNAL(setVolume(int,int,int,int)),
757 this, SIGNAL(setVolume(int,int,int,int)));
758
759 connect(this, SIGNAL(soundFileCompleted(int,int)),
760 client, SLOT(sendSoundCompleted(int,int)));
761 connect(this, SIGNAL(deviceReady(int,int)),
762 client, SLOT(sendDeviceReady(int,int)));
763 connect(this, SIGNAL(deviceError(int,int,int)),
764 client, SLOT(sendDeviceError(int,int,int)));
765 }
766}
767
768#endif
769
770class QWSSoundServerPrivate : public QObject {
771 Q_OBJECT
772
773public:
774 QWSSoundServerPrivate(QObject* parent=0, const char* name=0) :
775 QObject(parent)
776 {
777 timerId = 0;
778 if (name)
779 setObjectName(QString::fromAscii(name));
780#ifndef QT_NO_QWS_SOUNDSERVER
781 server = new QWSSoundServerSocket(this);
782
783 connect(server, SIGNAL(playFile(int,int,QString)),
784 this, SLOT(playFile(int,int,QString)));
785 connect(server, SIGNAL(playFile(int,int,QString,int,int)),
786 this, SLOT(playFile(int,int,QString,int,int)));
787 connect(server, SIGNAL(playRawFile(int,int,QString,int,int,int,int)),
788 this, SLOT(playRawFile(int,int,QString,int,int,int,int)));
789
790 connect(server, SIGNAL(pauseFile(int,int)),
791 this, SLOT(pauseFile(int,int)));
792 connect(server, SIGNAL(stopFile(int,int)),
793 this, SLOT(stopFile(int,int)));
794 connect(server, SIGNAL(stopAll(int)),
795 this, SLOT(stopAll(int)));
796 connect(server, SIGNAL(playPriorityOnly(bool)),
797 this, SLOT(playPriorityOnly(bool)));
798 connect(server, SIGNAL(resumeFile(int,int)),
799 this, SLOT(resumeFile(int,int)));
800
801 connect( server, SIGNAL(setSilent(bool)),
802 this, SLOT(setSilent(bool)));
803
804 connect(server, SIGNAL(setMute(int,int,bool)),
805 this, SLOT(setMute(int,int,bool)));
806 connect(server, SIGNAL(setVolume(int,int,int,int)),
807 this, SLOT(setVolume(int,int,int,int)));
808
809 connect(this, SIGNAL(soundFileCompleted(int,int)),
810 server, SIGNAL(soundFileCompleted(int,int)));
811 connect(this, SIGNAL(deviceReady(int,int)),
812 server, SIGNAL(deviceReady(int,int)));
813 connect(this, SIGNAL(deviceError(int,int,int)),
814 server, SIGNAL(deviceError(int,int,int)));
815
816#endif
817 silent = false;
818 fd = -1;
819 unwritten = 0;
820 can_GETOSPACE = true;
821 }
822
823 ~QWSSoundServerPrivate()
824 {
825 qDeleteAll(active);
826 qDeleteAll(inactive);
827 }
828
829signals:
830 void soundFileCompleted(int, int);
831 void deviceReady(int, int);
832 void deviceError(int, int, int);
833
834public slots:
835 void playRawFile(int wid, int sid, const QString &filename, int freq, int channels, int bitspersample, int flags);
836 void playFile(int wid, int sid, const QString& filename);
837 void playFile(int wid, int sid, const QString& filename, int v, int flags);
838 void checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p);
839 void pauseFile(int wid, int sid);
840 void resumeFile(int wid, int sid);
841 void stopFile(int wid, int sid);
842 void stopAll(int wid);
843 void setVolume(int wid, int sid, int lv, int rv);
844 void setMute(int wid, int sid, bool m);
845 void playPriorityOnly(bool p);
846 void sendCompletedSignals();
847 void feedDevice(int fd);
848 void setSilent( bool enabled );
849
850protected:
851 void timerEvent(QTimerEvent* event);
852
853private:
854 int openFile(int wid, int sid, const QString& filename);
855 bool openDevice();
856 void closeDevice()
857 {
858 if (fd >= 0) {
859 ::close(fd);
860 fd = -1;
861 }
862 }
863
864 QList<QWSSoundServerProvider*> active;
865 QList<QWSSoundServerProvider*> inactive;
866 struct PresetVolume {
867 int wid;
868 int sid;
869 int left;
870 int right;
871 bool mute;
872 };
873 QList<PresetVolume> volumes;
874 struct CompletedInfo {
875 CompletedInfo( ) : groupId( 0 ), soundId( 0 ) { }
876 CompletedInfo( int _groupId, int _soundId ) : groupId( _groupId ), soundId( _soundId ) { }
877 int groupId;
878 int soundId;
879 };
880 QList<CompletedInfo> completed;
881
882 bool silent;
883
884 int fd;
885 int unwritten;
886 int timerId;
887 char* cursor;
888 short data[sound_buffer_size*2];
889 bool can_GETOSPACE;
890#ifndef QT_NO_QWS_SOUNDSERVER
891 QWSSoundServerSocket *server;
892#endif
893};
894
895void QWSSoundServerPrivate::setSilent( bool enabled )
896{
897 // Close output device
898 closeDevice();
899 if( !unwritten && !active.count() ) {
900 sendCompletedSignals();
901 }
902 // Stop processing audio
903 killTimer( timerId );
904 silent = enabled;
905 // If audio remaining, open output device and continue processing
906 if( unwritten || active.count() ) {
907 openDevice();
908 }
909}
910
911void QWSSoundServerPrivate::timerEvent(QTimerEvent* event)
912{
913 // qDebug("QSS timer event");
914 if( event->timerId() == timerId ) {
915 if (fd >= 0)
916 feedDevice(fd);
917 if (fd < 0) {
918 killTimer(timerId);
919 timerId = 0;
920 }
921 }
922}
923
924void QWSSoundServerPrivate::playRawFile(int wid, int sid, const QString &filename,
925 int freq, int channels, int bitspersample, int flags)
926{
927#ifdef QT_NO_QWS_SOUNDSERVER
928 Q_UNUSED(flags);
929#endif
930 int f = openFile(wid, sid, filename);
931 if ( f ) {
932 QWSSoundServerStream *b = new QWSSoundServerStream(f, channels, freq, bitspersample, wid, sid);
933 // check preset volumes.
934 checkPresetVolumes(wid, sid, b);
935#ifndef QT_NO_QWS_SOUNDSERVER
936 b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority);
937#endif
938 active.append(b);
939 emit deviceReady(wid, sid);
940 }
941}
942
943void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename)
944{
945 int f = openFile(wid, sid, filename);
946 if ( f ) {
947 QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid);
948 checkPresetVolumes(wid, sid, b);
949 active.append( b );
950 emit deviceReady(wid, sid);
951 }
952}
953
954void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename,
955 int v, int flags)
956{
957#ifdef QT_NO_QWS_SOUNDSERVER
958 Q_UNUSED(flags);
959#endif
960 int f = openFile(wid, sid, filename);
961 if ( f ) {
962 QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid);
963 checkPresetVolumes(wid, sid, b);
964 b->setVolume(v, v);
965#ifndef QT_NO_QWS_SOUNDSERVER
966 b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority);
967#endif
968 active.append(b);
969 emit deviceReady(wid, sid);
970 }
971}
972
973void QWSSoundServerPrivate::checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p)
974{
975 QList<PresetVolume>::Iterator it = volumes.begin();
976 while (it != volumes.end()) {
977 PresetVolume v = *it;
978 if (v.wid == wid && v.sid == sid) {
979 p->setVolume(v.left, v.right);
980 p->setMute(v.mute);
981 it = volumes.erase(it);
982 return;
983 } else {
984 ++it;
985 }
986 }
987}
988
989void QWSSoundServerPrivate::pauseFile(int wid, int sid)
990{
991 QWSSoundServerProvider *bucket;
992 for (int i = 0; i < active.size(); ++i ) {
993 bucket = active.at(i);
994 if (bucket->equal(wid, sid)) {
995 // found bucket....
996 active.removeAt(i);
997 inactive.append(bucket);
998 return;
999 }
1000 }
1001}
1002
1003void QWSSoundServerPrivate::resumeFile(int wid, int sid)
1004{
1005 QWSSoundServerProvider *bucket;
1006 for (int i = 0; i < inactive.size(); ++i ) {
1007 bucket = inactive.at(i);
1008 if (bucket->equal(wid, sid)) {
1009 // found bucket....
1010 inactive.removeAt(i);
1011 active.append(bucket);
1012 return;
1013 }
1014 }
1015}
1016
1017void QWSSoundServerPrivate::stopFile(int wid, int sid)
1018{
1019 QWSSoundServerProvider *bucket;
1020 for (int i = 0; i < active.size(); ++i ) {
1021 bucket = active.at(i);
1022 if (bucket->equal(wid, sid)) {
1023 active.removeAt(i);
1024 delete bucket;
1025 return;
1026 }
1027 }
1028 for (int i = 0; i < inactive.size(); ++i ) {
1029 bucket = inactive.at(i);
1030 if (bucket->equal(wid, sid)) {
1031 inactive.removeAt(i);
1032 delete bucket;
1033 return;
1034 }
1035 }
1036}
1037
1038void QWSSoundServerPrivate::stopAll(int wid)
1039{
1040 QWSSoundServerProvider *bucket;
1041 if (!active.isEmpty()) {
1042 QList<QWSSoundServerProvider*>::Iterator it = active.begin();
1043 while (it != active.end()) {
1044 bucket = *it;
1045 if (bucket->groupId() == wid) {
1046 it = active.erase(it);
1047 delete bucket;
1048 } else {
1049 ++it;
1050 }
1051 }
1052 }
1053 if (!inactive.isEmpty()) {
1054 QList<QWSSoundServerProvider*>::Iterator it = inactive.begin();
1055 while (it != inactive.end()) {
1056 bucket = *it;
1057 if (bucket->groupId() == wid) {
1058 it = inactive.erase(it);
1059 delete bucket;
1060 } else {
1061 ++it;
1062 }
1063 }
1064 }
1065}
1066
1067void QWSSoundServerPrivate::setVolume(int wid, int sid, int lv, int rv)
1068{
1069 QWSSoundServerProvider *bucket;
1070 for( int i = 0; i < active.size(); ++i ) {
1071 bucket = active.at(i);
1072 if (bucket->equal(wid, sid)) {
1073 bucket->setVolume(lv,rv);
1074 return;
1075 }
1076 }
1077 // If gotten here, then it means wid/sid wasn't set up yet.
1078 // first find and remove current preset volumes, then add this one.
1079 QList<PresetVolume>::Iterator it = volumes.begin();
1080 while (it != volumes.end()) {
1081 PresetVolume v = *it;
1082 if (v.wid == wid && v.sid == sid)
1083 it = volumes.erase(it);
1084 else
1085 ++it;
1086 }
1087 // and then add this volume
1088 PresetVolume nv;
1089 nv.wid = wid;
1090 nv.sid = sid;
1091 nv.left = lv;
1092 nv.right = rv;
1093 nv.mute = false;
1094 volumes.append(nv);
1095}
1096
1097void QWSSoundServerPrivate::setMute(int wid, int sid, bool m)
1098{
1099 QWSSoundServerProvider *bucket;
1100 for( int i = 0; i < active.size(); ++i ) {
1101 bucket = active.at(i);
1102 if (bucket->equal(wid, sid)) {
1103 bucket->setMute(m);
1104 return;
1105 }
1106 }
1107 // if gotten here then setting is being applied before item
1108 // is created.
1109 QList<PresetVolume>::Iterator it = volumes.begin();
1110 while (it != volumes.end()) {
1111 PresetVolume v = *it;
1112 if (v.wid == wid && v.sid == sid) {
1113 (*it).mute = m;
1114 return;
1115 }
1116 }
1117 if (m) {
1118 PresetVolume nv;
1119 nv.wid = wid;
1120 nv.sid = sid;
1121 nv.left = maxVolume>>1;
1122 nv.right = maxVolume>>1;
1123 nv.mute = true;
1124 volumes.append(nv);
1125 }
1126}
1127
1128void QWSSoundServerPrivate::playPriorityOnly(bool p)
1129{
1130 QWSSoundServerProvider::setPlayPriorityOnly(p);
1131}
1132
1133void QWSSoundServerPrivate::sendCompletedSignals()
1134{
1135 while( !completed.isEmpty() ) {
1136 emit soundFileCompleted( (*completed.begin()).groupId,
1137 (*completed.begin()).soundId );
1138 completed.erase( completed.begin() );
1139 }
1140}
1141
1142
1143int QWSSoundServerPrivate::openFile(int wid, int sid, const QString& filename)
1144{
1145 stopFile(wid, sid); // close and re-open.
1146 int f = QT_OPEN(QFile::encodeName(filename), O_RDONLY|O_NONBLOCK);
1147 if (f == -1) {
1148 // XXX check ferror, check reason.
1149 qDebug("Failed opening \"%s\"",filename.toLatin1().data());
1150#ifndef QT_NO_QWS_SOUNDSERVER
1151 emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningFile );
1152#endif
1153 } else if ( openDevice() ) {
1154 return f;
1155 }
1156#ifndef QT_NO_QWS_SOUNDSERVER
1157 emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningAudioDevice );
1158#endif
1159 return 0;
1160}
1161
1162bool QWSSoundServerPrivate::openDevice()
1163{
1164 if (fd < 0) {
1165 if( silent ) {
1166 fd = QT_OPEN( "/dev/null", O_WRONLY );
1167 // Emulate write to audio device
1168 int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))/sound_speed/2;
1169 timerId = startTimer(delay);
1170
1171 return true;
1172 }
1173 //
1174 // Don't block open right away.
1175 //
1176 bool openOkay = false;
1177 if ((fd = QT_OPEN("/dev/dsp", O_WRONLY|O_NONBLOCK)) != -1) {
1178 int flags = fcntl(fd, F_GETFL);
1179 flags &= ~O_NONBLOCK;
1180 openOkay = (fcntl(fd, F_SETFL, flags) == 0);
1181 }
1182 if (!openOkay) {
1183 qDebug("Failed opening audio device");
1184 return false;
1185 }
1186
1187 // Setup soundcard at 16 bit mono
1188 int v;
1189 //v=0x00010000+sound_fragment_size;
1190 // um the media player did this instead.
1191 v=0x10000 * 4 + sound_fragment_size;
1192 if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v))
1193 qWarning("Could not set fragments to %08x",v);
1194#ifdef QT_QWS_SOUND_16BIT
1195 //
1196 // Use native endian
1197 // Since we have manipulated the data volume the data
1198 // is now in native format, even though its stored
1199 // as little endian in the WAV file
1200 //
1201 v=AFMT_S16_NE; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v))
1202 qWarning("Could not set format %d",v);
1203 if (AFMT_S16_NE != v)
1204 qDebug("Want format %d got %d", AFMT_S16_LE, v);
1205#else
1206 v=AFMT_U8; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v))
1207 qWarning("Could not set format %d",v);
1208 if (AFMT_U8 != v)
1209 qDebug("Want format %d got %d", AFMT_U8, v);
1210#endif
1211 v=sound_stereo; if (ioctl(fd, SNDCTL_DSP_STEREO, &v))
1212 qWarning("Could not set stereo %d",v);
1213 if (sound_stereo != v)
1214 qDebug("Want stereo %d got %d", sound_stereo, v);
1215#ifdef QT_QWS_SOUND_STEREO
1216 sound_stereo=v;
1217#endif
1218 v=sound_speed; if (ioctl(fd, SNDCTL_DSP_SPEED, &sound_speed))
1219 qWarning("Could not set speed %d",v);
1220 if (v != sound_speed)
1221 qDebug("Want speed %d got %d", v, sound_speed);
1222
1223 int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))
1224 /sound_speed/2;
1225 // qDebug("QSS delay: %d", delay);
1226 timerId = startTimer(delay);
1227
1228 //
1229 // Check system volume
1230 //
1231 int mixerHandle = QT_OPEN( "/dev/mixer", O_RDWR|O_NONBLOCK );
1232 if ( mixerHandle >= 0 ) {
1233 int volume;
1234 ioctl( mixerHandle, MIXER_READ(0), &volume );
1235 close( mixerHandle );
1236 if ( volume < 1<<(sound_stereo+sound_16bit) )
1237 qDebug("Want sound at %d got %d",
1238 1<<(sound_stereo+sound_16bit), volume);
1239 } else
1240 qDebug( "get volume of audio device failed" );
1241
1242 }
1243 return true;
1244}
1245
1246void QWSSoundServerPrivate::feedDevice(int fd)
1247{
1248 if ( !unwritten && active.size() == 0 ) {
1249 closeDevice();
1250 sendCompletedSignals();
1251 return;
1252 } else {
1253 sendCompletedSignals();
1254 }
1255
1256 QWSSoundServerProvider* bucket;
1257
1258 // find out how much audio is possible
1259 int available = sound_buffer_size;
1260 QList<QWSSoundServerProvider*> running;
1261 for (int i = 0; i < active.size(); ++i) {
1262 bucket = active.at(i);
1263 int ready = bucket->readySamples(available);
1264 if (ready > 0) {
1265 available = qMin(available, ready);
1266 running.append(bucket);
1267 }
1268 }
1269
1270 audio_buf_info info;
1271 if (can_GETOSPACE && ioctl(fd,SNDCTL_DSP_GETOSPACE,&info)) {
1272 can_GETOSPACE = false;
1273 fcntl(fd, F_SETFL, O_NONBLOCK);
1274 }
1275 if (!can_GETOSPACE)
1276 info.fragments = 4; // #### configurable?
1277 if (info.fragments > 0) {
1278 if (!unwritten) {
1279 int left[sound_buffer_size];
1280 memset(left,0,available*sizeof(int));
1281 int right[sound_buffer_size];
1282 if ( sound_stereo )
1283 memset(right,0,available*sizeof(int));
1284
1285 if (running.size() > 0) {
1286 // should do volume mod here in regards to each bucket to avoid flattened/bad peaks.
1287 for (int i = 0; i < running.size(); ++i ) {
1288 bucket = running.at(i);
1289 int unused = bucket->add(left,right,available);
1290 if (unused > 0) {
1291 // this error is quite serious, as
1292 // it will really screw up mixing.
1293 qDebug("provider lied about samples ready");
1294 }
1295 }
1296 if ( sound_16bit ) {
1297 short *d = (short*)data;
1298 for (int i=0; i<available; i++) {
1299 *d++ = (short)qMax(qMin(left[i],32767),-32768);
1300 if ( sound_stereo )
1301 *d++ = (short)qMax(qMin(right[i],32767),-32768);
1302 }
1303 } else {
1304 signed char *d = (signed char *)data;
1305 for (int i=0; i<available; i++) {
1306 *d++ = (signed char)qMax(qMin(left[i]/256,127),-128)+128;
1307 if ( sound_stereo )
1308 *d++ = (signed char)qMax(qMin(right[i]/256,127),-128)+128;
1309 }
1310 }
1311 unwritten = available*(sound_16bit+1)*(sound_stereo+1);
1312 cursor = (char*)data;
1313 }
1314 }
1315 // sound open, but nothing written. Should clear the buffer.
1316
1317 int w;
1318 if (unwritten) {
1319 w = ::write(fd,cursor,unwritten);
1320
1321 if (w < 0) {
1322 if (can_GETOSPACE)
1323 return;
1324 w = 0;
1325 }
1326
1327 cursor += w;
1328 unwritten -= w;
1329 } else {
1330 // write some zeros to clear the buffer?
1331 if (!zeroMem)
1332 zeroMem = (char *)calloc(sound_buffer_size, sizeof(char));
1333 w = ::write(fd, zeroMem, sound_buffer_size);
1334 if (w < 0)
1335 w = 0;
1336 }
1337 }
1338
1339 QList<QWSSoundServerProvider*>::Iterator it = active.begin();
1340 while (it != active.end()) {
1341 bucket = *it;
1342 if (bucket->finished()) {
1343 completed.append(CompletedInfo(bucket->groupId(), bucket->soundId()));
1344 it = active.erase(it);
1345 delete bucket;
1346 } else {
1347 ++it;
1348 }
1349 }
1350}
1351
1352
1353QWSSoundServer::QWSSoundServer(QObject* parent) :
1354 QObject(parent)
1355{
1356 d = new QWSSoundServerPrivate(this);
1357
1358 connect( d, SIGNAL(soundFileCompleted(int,int)),
1359 this, SLOT(translateSoundCompleted(int,int)) );
1360}
1361
1362void QWSSoundServer::playFile( int sid, const QString& filename )
1363{
1364 //wid == 0, as it is the server initiating rather than a client
1365 // if wid was passable, would accidently collide with server
1366 // sockect's wids.
1367 d->playFile(0, sid, filename);
1368}
1369
1370void QWSSoundServer::pauseFile( int sid )
1371{
1372 d->pauseFile(0, sid);
1373}
1374
1375void QWSSoundServer::stopFile( int sid )
1376{
1377 d->stopFile(0, sid);
1378}
1379
1380void QWSSoundServer::resumeFile( int sid )
1381{
1382 d->resumeFile(0, sid);
1383}
1384
1385QWSSoundServer::~QWSSoundServer()
1386{
1387 d->stopAll(0);
1388}
1389
1390void QWSSoundServer::translateSoundCompleted( int, int sid )
1391{
1392 emit soundCompleted( sid );
1393}
1394
1395#ifndef QT_NO_QWS_SOUNDSERVER
1396QWSSoundClient::QWSSoundClient(QObject* parent) :
1397 QWSSocket(parent)
1398{
1399 connectToLocalFile(QT_VFB_SOUND_PIPE(qws_display_id));
1400 QObject::connect(this,SIGNAL(readyRead()),
1401 this,SLOT(tryReadCommand()));
1402 if( state() == QWS_SOCK_BASE::ConnectedState ) QTimer::singleShot(1, this, SIGNAL(connected()));
1403 else QTimer::singleShot(1, this, SLOT(emitConnectionRefused()));
1404}
1405
1406QWSSoundClient::~QWSSoundClient( )
1407{
1408 flush();
1409}
1410
1411void QWSSoundClient::reconnect()
1412{
1413 connectToLocalFile(QT_VFB_SOUND_PIPE(qws_display_id));
1414 if( state() == QWS_SOCK_BASE::ConnectedState ) emit connected();
1415 else emit error( QTcpSocket::ConnectionRefusedError );
1416}
1417
1418void QWSSoundClient::sendServerMessage(QString msg)
1419{
1420#ifndef QT_NO_TEXTCODEC
1421 QByteArray u = msg.toUtf8();
1422#else
1423 QByteArray u = msg.toLatin1();
1424#endif
1425 write(u.data(), u.length());
1426 flush();
1427}
1428
1429void QWSSoundClient::play( int id, const QString& filename )
1430{
1431 QFileInfo fi(filename);
1432 sendServerMessage(QLatin1String("PLAY ")
1433 + QString::number(id) + QLatin1Char(' ')
1434 + fi.absoluteFilePath() + QLatin1Char('\n'));
1435}
1436
1437void QWSSoundClient::play( int id, const QString& filename, int volume, int flags)
1438{
1439 QFileInfo fi(filename);
1440 sendServerMessage(QLatin1String("PLAYEXTEND ")
1441 + QString::number(id) + QLatin1Char(' ')
1442 + QString::number(volume) + QLatin1Char(' ')
1443 + QString::number(flags) + QLatin1Char(' ')
1444 + fi.absoluteFilePath() + QLatin1Char('\n'));
1445}
1446
1447void QWSSoundClient::pause( int id )
1448{
1449 sendServerMessage(QLatin1String("PAUSE ")
1450 + QString::number(id) + QLatin1Char('\n'));
1451}
1452
1453void QWSSoundClient::stop( int id )
1454{
1455 sendServerMessage(QLatin1String("STOP ")
1456 + QString::number(id) + QLatin1Char('\n'));
1457}
1458
1459void QWSSoundClient::resume( int id )
1460{
1461 sendServerMessage(QLatin1String("RESUME ")
1462 + QString::number(id) + QLatin1Char('\n'));
1463}
1464
1465void QWSSoundClient::playRaw( int id, const QString& filename,
1466 int freq, int chs, int bitspersample, int flags)
1467{
1468 QFileInfo fi(filename);
1469 sendServerMessage(QLatin1String("PLAYRAW ")
1470 + QString::number(id) + QLatin1Char(' ')
1471 + QString::number(chs) + QLatin1Char(' ')
1472 + QString::number(freq) + QLatin1Char(' ')
1473 + QString::number(bitspersample) + QLatin1Char(' ')
1474 + QString::number(flags) + QLatin1Char(' ')
1475 + fi.absoluteFilePath() + QLatin1Char('\n'));
1476}
1477
1478void QWSSoundClient::setMute( int id, bool m )
1479{
1480 sendServerMessage(QLatin1String(m ? "MUTE " : "UNMUTE ")
1481 + QString::number(id) + QLatin1Char('\n'));
1482}
1483
1484void QWSSoundClient::setVolume( int id, int leftVol, int rightVol )
1485{
1486 sendServerMessage(QLatin1String("SETVOLUME ")
1487 + QString::number(id) + QLatin1Char(' ')
1488 + QString::number(leftVol) + QLatin1Char(' ')
1489 + QString::number(rightVol) + QLatin1Char('\n'));
1490}
1491
1492void QWSSoundClient::playPriorityOnly( bool pri )
1493{
1494 sendServerMessage(QLatin1String("PRIORITYONLY ")
1495 + QString::number(pri ? 1 : 0) + QLatin1Char('\n'));
1496}
1497
1498void QWSSoundClient::setSilent( bool enable )
1499{
1500 sendServerMessage(QLatin1String("SILENT ")
1501 + QString::number( enable ? 1 : 0 ) + QLatin1Char('\n'));
1502}
1503
1504void QWSSoundClient::tryReadCommand()
1505{
1506 while ( canReadLine() ) {
1507 QString l = QString::fromAscii(readLine());
1508 l.truncate(l.length()-1); // chomp
1509 QStringList token = l.split(QLatin1Char(' '));
1510 if (token[0] == QLatin1String("SOUNDCOMPLETED")) {
1511 emit soundCompleted(token[1].toInt());
1512 } else if (token[0] == QLatin1String("DEVICEREADY")) {
1513 emit deviceReady(token[1].toInt());
1514 } else if (token[0] == QLatin1String("DEVICEERROR")) {
1515 emit deviceError(token[1].toInt(),(DeviceErrors)token[2].toInt());
1516 }
1517 }
1518}
1519
1520void QWSSoundClient::emitConnectionRefused()
1521{
1522 emit error( QTcpSocket::ConnectionRefusedError );
1523}
1524#endif
1525
1526QT_END_NAMESPACE
1527
1528#include "qsoundqss_qws.moc"
1529
1530#endif // QT_NO_SOUND
Note: See TracBrowser for help on using the repository browser.