1 /*  This file is part of the KDE project
2     Copyright (C) 2006 Alexander Kern <alex.kern@gmx.de>
3 
4     based on example for Phonon Architecture, Matthias Kretz <kretz@kde.org>
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Library General Public
8     License version 2 as published by the Free Software Foundation.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public License
16     along with this library; see the file COPYING.LIB.  If not, write to
17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18     Boston, MA 02110-1301, USA.
19 */
20 
21 #include "audio_phonon.h"
22 #include "audio.h"
23 
24 #include <QByteArray>
25 #include <QDataStream>
26 #include <QTimer>
27 #include <QWaitCondition>
28 #include <QMutex>
29 
30 #include <phonon/audiooutput.h>
31 #include <phonon/audiopath.h>
32 #include <phonon/mediaobject.h>
33 
LibWMPcmPlayer()34 LibWMPcmPlayer::LibWMPcmPlayer() : AbstractMediaStream(NULL),
35     m_media(NULL),
36     m_cmd(WM_CDM_UNKNOWN),
37     m_blk(NULL)
38 {
39     Phonon::AudioOutput* m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
40     Phonon::AudioPath* m_path = new Phonon::AudioPath(this);
41     m_path->addOutput(m_output);
42     m_media = new Phonon::MediaObject(this);
43     m_media->addAudioPath(m_path);
44     m_media->setCurrentSource(this);
45     setStreamSeekable(false);
46     setStreamSize(0xffffffff);
47 
48     connect(this, SIGNAL(cmdChanged(int)), this, SLOT(executeCmd(int)));
49     connect(this, SIGNAL(nextBuffer(cdda_block*)), this, SLOT(playBuffer(cdda_block*)));
50     connect(m_media, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
51         this, SLOT(stateChanged(Phonon::State,Phonon::State)));
52 
53     DEBUGLOG("writeHeader\n");
54     writeData( wavHeader() );
55     DEBUGLOG("writeHeader end\n");
56 }
57 
~LibWMPcmPlayer()58 LibWMPcmPlayer::~LibWMPcmPlayer()
59 {
60     stop();
61 }
62 
wavHeader() const63 QByteArray LibWMPcmPlayer::wavHeader() const
64 {
65     QByteArray data;
66     QDataStream stream( &data, QIODevice::WriteOnly );
67     stream.setByteOrder( QDataStream::LittleEndian );
68     stream
69         << 0x46464952 //"RIFF"
70         << static_cast<quint32>( 0x7FFFFFFF )
71         << 0x45564157 //"WAVE"
72         << 0x20746D66 //"fmt "           //Subchunk1ID
73         << static_cast<quint32>( 16 )    //Subchunk1Size
74         << static_cast<quint16>( 1 )     //AudioFormat
75         << static_cast<quint16>( 2 )     //NumChannels
76         << static_cast<quint32>( 44100 ) //SampleRate
77         << static_cast<quint32>( 2*2*44100 )//ByteRate
78         << static_cast<quint16>( 2*2 )   //BlockAlign
79         << static_cast<quint16>( 16 )    //BitsPerSample
80         << 0x61746164 //"data"                   //Subchunk2ID
81         << static_cast<quint32>( 0x7FFFFFFF-36 )//Subchunk2Size
82         ;
83 
84     return data;
85 }
86 
reset()87 void LibWMPcmPlayer::reset()
88 {
89     setStreamSeekable(false);
90     setStreamSize(0xffffffff);
91     DEBUGLOG("writeHeader\n");
92     writeData( wavHeader() );
93     DEBUGLOG("writeHeader end\n");
94 }
95 
needData()96 void LibWMPcmPlayer::needData()
97 {
98     DEBUGLOG("needData\n");
99     m_mutex.lock();
100     m_readyToPlay.wakeAll();
101     m_mutex.unlock();
102 
103 }
104 
setNextBuffer(struct cdda_block * blk)105 void LibWMPcmPlayer::setNextBuffer(struct cdda_block *blk)
106 {
107     Q_EMIT nextBuffer(blk);
108     m_mutex.lock();
109     m_readyToPlay.wait(&m_mutex);
110     m_mutex.unlock();
111 }
112 
playBuffer(struct cdda_block * blk)113 void LibWMPcmPlayer::playBuffer(struct cdda_block *blk)
114 {
115     if(m_cmd != WM_CDM_PLAYING) {
116         Q_EMIT cmdChanged(WM_CDM_PLAYING);
117         m_cmd = WM_CDM_PLAYING;
118     }
119     writeData(QByteArray(blk->buf, blk->buflen));
120 
121 }
122 
pause(void)123 void LibWMPcmPlayer::pause(void)
124 {
125     if(m_cmd != WM_CDM_PAUSED) {
126         Q_EMIT cmdChanged(WM_CDM_PAUSED);
127         m_cmd = WM_CDM_PAUSED;
128 
129         m_readyToPlay.wakeAll();
130     }
131 }
132 
stop(void)133 void LibWMPcmPlayer::stop(void)
134 {
135     if(m_cmd != WM_CDM_STOPPED) {
136         Q_EMIT cmdChanged(WM_CDM_STOPPED);
137         m_cmd = WM_CDM_STOPPED;
138 
139         m_readyToPlay.wakeAll();
140     }
141 }
142 
executeCmd(int cmd)143 void LibWMPcmPlayer::executeCmd(int cmd)
144 {
145     switch(cmd) {
146     case WM_CDM_PLAYING:
147 DEBUGLOG("set play\n");
148         m_media->play();
149         break;
150     case WM_CDM_PAUSED:
151 DEBUGLOG("set pause\n");
152         m_media->pause();
153         break;
154     case WM_CDM_STOPPED:
155 DEBUGLOG("set stop\n");
156         m_media->stop();
157         break;
158     default:
159         cmd = WM_CDM_STOPPED;
160         break;
161     }
162 }
163 
stateChanged(Phonon::State newstate,Phonon::State oldstate)164 void LibWMPcmPlayer::stateChanged( Phonon::State newstate, Phonon::State oldstate )
165 {
166     DEBUGLOG("stateChanged from %i to %i\n", oldstate, newstate);
167 }
168 
169 static LibWMPcmPlayer *PhononObject = NULL;
170 
phonon_open(void)171 int phonon_open(void)
172 {
173     DEBUGLOG("phonon_open\n");
174 
175     if(PhononObject) {
176         ERRORLOG("Already initialized!\n");
177         return -1;
178     }
179 
180     PhononObject = new LibWMPcmPlayer();
181 
182     return 0;
183 }
184 
phonon_close(void)185 int phonon_close(void)
186 {
187     DEBUGLOG("phonon_close\n");
188 
189     if(!PhononObject) {
190         ERRORLOG("Unable to close\n");
191         return -1;
192     }
193 
194     delete PhononObject;
195 
196     PhononObject = NULL;
197 
198     return 0;
199 }
200 
201 /*
202  * Play some audio and pass a status message upstream, if applicable.
203  * Returns 0 on success.
204  */
205 int
phonon_play(struct cdda_block * blk)206 phonon_play(struct cdda_block *blk)
207 {
208     DEBUGLOG("phonon_play %ld samples, frame %i\n",
209         blk->buflen / (2 * 2), blk->frame);
210 
211     if(!PhononObject) {
212         ERRORLOG("Unable to play\n");
213         blk->status = WM_CDM_CDDAERROR;
214         return -1;
215     }
216 
217     PhononObject->setNextBuffer(blk);
218 
219     return 0;
220 }
221 
222 /*
223  * Pause the audio immediately.
224  */
225 int
phonon_pause(void)226 phonon_pause(void)
227 {
228     DEBUGLOG("phonon_pause\n");
229 
230     if(!PhononObject) {
231         ERRORLOG("Unable to pause\n");
232         return -1;
233     }
234 
235     PhononObject->pause();
236 
237     return 0;
238 }
239 
240 /*
241  * Stop the audio immediately.
242  */
243 int
phonon_stop(void)244 phonon_stop(void)
245 {
246     DEBUGLOG("phonon_stop\n");
247 
248     if(!PhononObject) {
249         ERRORLOG("Unable to stop\n");
250         return -1;
251     }
252 
253     PhononObject->stop();
254 
255     return 0;
256 }
257 
258 /*
259  * Get the current audio state.
260  */
261 int
phonon_state(struct cdda_block * blk)262 phonon_state(struct cdda_block *blk)
263 {
264     DEBUGLOG("phonon_state\n");
265 
266     return -1; /* not implemented yet for PHONON */
267 }
268 
269 static struct audio_oops phonon_oops = {
270     phonon_open,
271     phonon_close,
272     phonon_play,
273     phonon_pause,
274     phonon_stop,
275     phonon_state,
276     NULL,
277     NULL
278 };
279 
280 extern "C" struct audio_oops*
setup_phonon(const char * dev,const char * ctl)281 setup_phonon(const char *dev, const char *ctl)
282 {
283     DEBUGLOG("setup_phonon\n");
284 
285     phonon_open();
286 
287     return &phonon_oops;
288 }
289 
290 #include "audio_phonon.moc"
291