summaryrefslogtreecommitdiff
path: root/core/multimedia/opieplayer/audiodevice.cpp
Unidiff
Diffstat (limited to 'core/multimedia/opieplayer/audiodevice.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/multimedia/opieplayer/audiodevice.cpp386
1 files changed, 386 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/audiodevice.cpp b/core/multimedia/opieplayer/audiodevice.cpp
new file mode 100644
index 0000000..8861015
--- a/dev/null
+++ b/core/multimedia/opieplayer/audiodevice.cpp
@@ -0,0 +1,386 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20#include <stdlib.h>
21#include <qpe/qpeapplication.h>
22#include <qpe/config.h>
23#include "audiodevice.h"
24
25#if ( defined Q_WS_QWS || defined(_WS_QWS_) ) && !defined(QT_NO_COP)
26#include "qpe/qcopenvelope_qws.h"
27#endif
28
29#ifdef Q_WS_WIN
30#include <windows.h>
31#include <mmsystem.h>
32#include <mmreg.h>
33#endif
34
35#if defined(Q_WS_X11) || defined(Q_WS_QWS)
36#include <fcntl.h>
37#include <sys/ioctl.h>
38#include <sys/soundcard.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41#include <sys/types.h>
42#include <unistd.h>
43#endif
44
45#if defined(Q_OS_WIN32)
46static const int expectedBytesPerMilliSecond = 2 * 2 * 44000 / 1000;
47static const int timerResolutionMilliSeconds = 30;
48static const int sound_fragment_bytes = timerResolutionMilliSeconds * expectedBytesPerMilliSecond;
49#else
50# if defined(QT_QWS_IPAQ)
51static const int sound_fragment_shift = 14;
52# else
53static const int sound_fragment_shift = 16;
54# endif
55static const int sound_fragment_bytes = (1<<sound_fragment_shift);
56#endif
57
58
59class AudioDevicePrivate {
60public:
61 int handle;
62 unsigned int frequency;
63 unsigned int channels;
64 unsigned int bytesPerSample;
65 unsigned int bufferSize;
66#ifndef Q_OS_WIN32
67 bool can_GETOSPACE;
68 char* unwrittenBuffer;
69 unsigned int unwritten;
70#endif
71
72 static int dspFd;
73 static bool muted;
74 static unsigned int leftVolume;
75 static unsigned int rightVolume;
76};
77
78
79#ifdef Q_WS_QWS
80// This is for keeping the device open in-between playing files when
81// the device makes clicks and it starts to drive you insane! :)
82// Best to have the device not open when not using it though
83//#define KEEP_DEVICE_OPEN
84#endif
85
86
87int AudioDevicePrivate::dspFd = 0;
88bool AudioDevicePrivate::muted = FALSE;
89unsigned int AudioDevicePrivate::leftVolume = 0;
90unsigned int AudioDevicePrivate::rightVolume = 0;
91
92
93void AudioDevice::getVolume( unsigned int& leftVolume, unsigned int& rightVolume, bool &muted ) {
94 muted = AudioDevicePrivate::muted;
95 unsigned int volume;
96#ifdef Q_OS_WIN32
97 HWAVEOUT handle;
98 WAVEFORMATEX formatData;
99 formatData.cbSize = sizeof(WAVEFORMATEX);
100 formatData.wFormatTag = WAVE_FORMAT_PCM;
101 formatData.nAvgBytesPerSec = 4 * 44000;
102 formatData.nBlockAlign = 4;
103 formatData.nChannels = 2;
104 formatData.nSamplesPerSec = 44000;
105 formatData.wBitsPerSample = 16;
106 waveOutOpen(&handle, WAVE_MAPPER, &formatData, 0L, 0L, CALLBACK_NULL);
107 if ( waveOutGetVolume( handle, (LPDWORD)&volume ) )
108 qDebug( "get volume of audio device failed" );
109 waveOutClose( handle );
110 leftVolume = volume & 0xFFFF;
111 rightVolume = volume >> 16;
112#else
113 int mixerHandle = open( "/dev/mixer", O_RDWR );
114 if ( mixerHandle >= 0 ) {
115 ioctl( mixerHandle, MIXER_READ(0), &volume );
116 close( mixerHandle );
117 } else
118 qDebug( "get volume of audio device failed" );
119 leftVolume = ((volume & 0x00FF) << 16) / 101;
120 rightVolume = ((volume & 0xFF00) << 8) / 101;
121#endif
122}
123
124
125void AudioDevice::setVolume( unsigned int leftVolume, unsigned int rightVolume, bool muted ) {
126 AudioDevicePrivate::muted = muted;
127 if ( muted ) {
128 AudioDevicePrivate::leftVolume = leftVolume;
129 AudioDevicePrivate::rightVolume = rightVolume;
130 leftVolume = 0;
131 rightVolume = 0;
132 } else {
133 leftVolume = ( (int) leftVolume < 0 ) ? 0 : (( leftVolume > 0xFFFF ) ? 0xFFFF : leftVolume );
134 rightVolume = ( (int)rightVolume < 0 ) ? 0 : (( rightVolume > 0xFFFF ) ? 0xFFFF : rightVolume );
135 }
136#ifdef Q_OS_WIN32
137 HWAVEOUT handle;
138 WAVEFORMATEX formatData;
139 formatData.cbSize = sizeof(WAVEFORMATEX);
140 formatData.wFormatTag = WAVE_FORMAT_PCM;
141 formatData.nAvgBytesPerSec = 4 * 44000;
142 formatData.nBlockAlign = 4;
143 formatData.nChannels = 2;
144 formatData.nSamplesPerSec = 44000;
145 formatData.wBitsPerSample = 16;
146 waveOutOpen(&handle, WAVE_MAPPER, &formatData, 0L, 0L, CALLBACK_NULL);
147 unsigned int volume = (rightVolume << 16) | leftVolume;
148 if ( waveOutSetVolume( handle, volume ) )
149 qDebug( "set volume of audio device failed" );
150 waveOutClose( handle );
151#else
152 // Volume can be from 0 to 100 which is 101 distinct values
153 unsigned int rV = (rightVolume * 101) >> 16;
154
155# if 0
156 unsigned int lV = (leftVolume * 101) >> 16;
157 unsigned int volume = ((rV << 8) & 0xFF00) | (lV & 0x00FF);
158 int mixerHandle = 0;
159 if ( ( mixerHandle = open( "/dev/mixer", O_RDWR ) ) >= 0 ) {
160 ioctl( mixerHandle, MIXER_WRITE(0), &volume );
161 close( mixerHandle );
162 } else
163 qDebug( "set volume of audio device failed" );
164# else
165 // This is the way this has to be done now I guess, doesn't allow for
166 // independant right and left channel setting, or setting for different outputs
167 Config cfg("Sound");
168 cfg.setGroup("System");
169 cfg.writeEntry("Volume",(int)rV);
170# endif
171
172#endif
173// qDebug( "setting volume to: 0x%x", volume );
174#if ( defined Q_WS_QWS || defined(_WS_QWS_) ) && !defined(QT_NO_COP)
175 // Send notification that the volume has changed
176 QCopEnvelope( "QPE/System", "volumeChange(bool)" ) << muted;
177#endif
178}
179
180
181
182
183AudioDevice::AudioDevice( unsigned int f, unsigned int chs, unsigned int bps ) {
184 d = new AudioDevicePrivate;
185 d->frequency = f;
186 d->channels = chs;
187 d->bytesPerSample = bps;
188
189 connect( qApp, SIGNAL( volumeChanged(bool) ), this, SLOT( volumeChanged(bool) ) );
190
191#ifdef Q_OS_WIN32
192 UINT result;
193 WAVEFORMATEX formatData;
194 formatData.cbSize = sizeof(WAVEFORMATEX);
195/*
196 // Other possible formats windows supports
197 formatData.wFormatTag = WAVE_FORMAT_MPEG;
198 formatData.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
199 formatData.wFormatTag = WAVE_FORMAT_ADPCM;
200*/
201 formatData.wFormatTag = WAVE_FORMAT_PCM;
202 formatData.nAvgBytesPerSec = bps * chs * f;
203 formatData.nBlockAlign = bps * chs;
204 formatData.nChannels = chs;
205 formatData.nSamplesPerSec = f;
206 formatData.wBitsPerSample = bps * 8;
207 // Open a waveform device for output
208 if (result = waveOutOpen((LPHWAVEOUT)&d->handle, WAVE_MAPPER, &formatData, 0L, 0L, CALLBACK_NULL)) {
209 QString errorMsg = "error opening audio device.\nReason: %i - ";
210 switch (result) {
211 case MMSYSERR_ALLOCATED:errorMsg += "Specified resource is already allocated."; break;
212 case MMSYSERR_BADDEVICEID:errorMsg += "Specified device identifier is out of range."; break;
213 case MMSYSERR_NODRIVER:errorMsg += "No device driver is present."; break;
214 case MMSYSERR_NOMEM:errorMsg += "Unable to allocate or lock memory."; break;
215 case WAVERR_BADFORMAT:errorMsg += "Attempted to open with an unsupported waveform-audio format."; break;
216 case WAVERR_SYNC: errorMsg += "The device is synchronous but waveOutOpen was called without using the WAVE_ALLOWSYNC flag."; break;
217 default: errorMsg += "Undefined error"; break;
218 }
219 qDebug( errorMsg, result );
220 }
221
222 d->bufferSize = sound_fragment_bytes;
223#else
224
225 int fragments = 0x10000 * 8 + sound_fragment_shift;
226 int format = AFMT_S16_LE;
227 int capabilities = 0;
228
229#ifdef KEEP_DEVICE_OPEN
230 if ( AudioDevicePrivate::dspFd == 0 ) {
231#endif
232 if ( ( d->handle = ::open( "/dev/dsp", O_WRONLY ) ) < 0 ) {
233 qDebug( "error opening audio device /dev/dsp, sending data to /dev/null instead" );
234 d->handle = ::open( "/dev/null", O_WRONLY );
235 }
236#ifdef KEEP_DEVICE_OPEN
237 AudioDevicePrivate::dspFd = d->handle;
238 } else {
239 d->handle = AudioDevicePrivate::dspFd;
240 }
241#endif
242
243 ioctl( d->handle, SNDCTL_DSP_GETCAPS, &capabilities );
244 ioctl( d->handle, SNDCTL_DSP_SETFRAGMENT, &fragments );
245 ioctl( d->handle, SNDCTL_DSP_SETFMT, &format );
246 ioctl( d->handle, SNDCTL_DSP_SPEED, &d->frequency );
247 if ( ioctl( d->handle, SNDCTL_DSP_CHANNELS, &d->channels ) == -1 ) {
248 d->channels = ( d->channels == 1 ) ? 2 : d->channels;
249 ioctl( d->handle, SNDCTL_DSP_CHANNELS, &d->channels );
250 }
251
252 d->bufferSize = sound_fragment_bytes;
253 d->unwrittenBuffer = new char[d->bufferSize];
254 d->unwritten = 0;
255 d->can_GETOSPACE = TRUE; // until we find otherwise
256
257 //if ( chs != d->channels ) qDebug( "Wanted %d, got %d channels", chs, d->channels );
258 //if ( f != d->frequency ) qDebug( "wanted %dHz, got %dHz", f, d->frequency );
259 //if ( capabilities & DSP_CAP_BATCH ) qDebug( "Sound card has local buffer" );
260 //if ( capabilities & DSP_CAP_REALTIME )qDebug( "Sound card has realtime sync" );
261 //if ( capabilities & DSP_CAP_TRIGGER ) qDebug( "Sound card has precise trigger" );
262 //if ( capabilities & DSP_CAP_MMAP ) qDebug( "Sound card can mmap" );
263#endif
264}
265
266
267AudioDevice::~AudioDevice() {
268#ifdef Q_OS_WIN32
269 waveOutClose( (HWAVEOUT)d->handle );
270#else
271# ifndef KEEP_DEVICE_OPEN
272 close( d->handle ); // Now it should be safe to shut the handle
273# endif
274 delete d->unwrittenBuffer;
275 delete d;
276#endif
277}
278
279
280void AudioDevice::volumeChanged( bool muted )
281{
282 AudioDevicePrivate::muted = muted;
283}
284
285
286void AudioDevice::write( char *buffer, unsigned int length )
287{
288#ifdef Q_OS_WIN32
289 // returns immediately and (to be implemented) emits completedIO() when finished writing
290 WAVEHDR *lpWaveHdr = (WAVEHDR *)malloc( sizeof(WAVEHDR) );
291 // maybe the buffer should be copied so that this fool proof, but its a performance hit
292 lpWaveHdr->lpData = buffer;
293 lpWaveHdr->dwBufferLength = length;
294 lpWaveHdr->dwFlags = 0L;
295 lpWaveHdr->dwLoops = 0L;
296 waveOutPrepareHeader( (HWAVEOUT)d->handle, lpWaveHdr, sizeof(WAVEHDR) );
297 // waveOutWrite returns immediately. the data is sent in the background.
298 if ( waveOutWrite( (HWAVEOUT)d->handle, lpWaveHdr, sizeof(WAVEHDR) ) )
299 qDebug( "failed to write block to audio device" );
300 // emit completedIO();
301#else
302 int t = ::write( d->handle, buffer, length );
303 if ( t<0 ) t = 0;
304 if ( t != (int)length) {
305 qDebug("Ahhh!! memcpys 1");
306 memcpy(d->unwrittenBuffer,buffer+t,length-t);
307 d->unwritten = length-t;
308 }
309#endif
310}
311
312
313unsigned int AudioDevice::channels() const
314{
315 return d->channels;
316}
317
318
319unsigned int AudioDevice::frequency() const
320{
321 return d->frequency;
322}
323
324
325unsigned int AudioDevice::bytesPerSample() const
326{
327 return d->bytesPerSample;
328}
329
330
331unsigned int AudioDevice::bufferSize() const
332{
333 return d->bufferSize;
334}
335
336unsigned int AudioDevice::canWrite() const
337{
338#ifdef Q_OS_WIN32
339 return bufferSize(); // Any better?
340#else
341 audio_buf_info info;
342 if ( d->can_GETOSPACE && ioctl(d->handle,SNDCTL_DSP_GETOSPACE,&info) ) {
343 d->can_GETOSPACE = FALSE;
344 fcntl( d->handle, F_SETFL, O_NONBLOCK );
345 }
346 if ( d->can_GETOSPACE ) {
347 int t = info.fragments * sound_fragment_bytes;
348 return QMIN(t,(int)bufferSize());
349 } else {
350 if ( d->unwritten ) {
351 int t = ::write( d->handle, d->unwrittenBuffer, d->unwritten );
352 if ( t<0 ) t = 0;
353 if ( (unsigned)t!=d->unwritten ) {
354 memcpy(d->unwrittenBuffer,d->unwrittenBuffer+t,d->unwritten-t);
355 d->unwritten -= t;
356 } else {
357 d->unwritten = 0;
358 }
359 }
360 if ( d->unwritten )
361 return 0;
362 else
363 return d->bufferSize;
364 }
365#endif
366}
367
368
369int AudioDevice::bytesWritten() {
370#ifdef Q_OS_WIN32
371 MMTIME pmmt = { TIME_BYTES, 0 };
372 if ( ( waveOutGetPosition( (HWAVEOUT)d->handle, &pmmt, sizeof(MMTIME) ) != MMSYSERR_NOERROR ) || ( pmmt.wType != TIME_BYTES ) ) {
373 qDebug( "failed to get audio device position" );
374 return -1;
375 }
376 return pmmt.u.cb;
377#else
378 int buffered = 0;
379 if ( ioctl( d->handle, SNDCTL_DSP_GETODELAY, &buffered ) ) {
380 qDebug( "failed to get audio device position" );
381 return -1;
382 }
383 return buffered;
384#endif
385}
386