author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (side-by-side diff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /core/multimedia/opieplayer/audiodevice.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
Diffstat (limited to 'core/multimedia/opieplayer/audiodevice.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/multimedia/opieplayer/audiodevice.cpp | 386 |
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 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#include <stdlib.h> +#include <qpe/qpeapplication.h> +#include <qpe/config.h> +#include "audiodevice.h" + +#if ( defined Q_WS_QWS || defined(_WS_QWS_) ) && !defined(QT_NO_COP) +#include "qpe/qcopenvelope_qws.h" +#endif + +#ifdef Q_WS_WIN +#include <windows.h> +#include <mmsystem.h> +#include <mmreg.h> +#endif + +#if defined(Q_WS_X11) || defined(Q_WS_QWS) +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/soundcard.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#if defined(Q_OS_WIN32) +static const int expectedBytesPerMilliSecond = 2 * 2 * 44000 / 1000; +static const int timerResolutionMilliSeconds = 30; +static const int sound_fragment_bytes = timerResolutionMilliSeconds * expectedBytesPerMilliSecond; +#else +# if defined(QT_QWS_IPAQ) +static const int sound_fragment_shift = 14; +# else +static const int sound_fragment_shift = 16; +# endif +static const int sound_fragment_bytes = (1<<sound_fragment_shift); +#endif + + +class AudioDevicePrivate { +public: + int handle; + unsigned int frequency; + unsigned int channels; + unsigned int bytesPerSample; + unsigned int bufferSize; +#ifndef Q_OS_WIN32 + bool can_GETOSPACE; + char* unwrittenBuffer; + unsigned int unwritten; +#endif + + static int dspFd; + static bool muted; + static unsigned int leftVolume; + static unsigned int rightVolume; +}; + + +#ifdef Q_WS_QWS +// This is for keeping the device open in-between playing files when +// the device makes clicks and it starts to drive you insane! :) +// Best to have the device not open when not using it though +//#define KEEP_DEVICE_OPEN +#endif + + +int AudioDevicePrivate::dspFd = 0; +bool AudioDevicePrivate::muted = FALSE; +unsigned int AudioDevicePrivate::leftVolume = 0; +unsigned int AudioDevicePrivate::rightVolume = 0; + + +void AudioDevice::getVolume( unsigned int& leftVolume, unsigned int& rightVolume, bool &muted ) { + muted = AudioDevicePrivate::muted; + unsigned int volume; +#ifdef Q_OS_WIN32 + HWAVEOUT handle; + WAVEFORMATEX formatData; + formatData.cbSize = sizeof(WAVEFORMATEX); + formatData.wFormatTag = WAVE_FORMAT_PCM; + formatData.nAvgBytesPerSec = 4 * 44000; + formatData.nBlockAlign = 4; + formatData.nChannels = 2; + formatData.nSamplesPerSec = 44000; + formatData.wBitsPerSample = 16; + waveOutOpen(&handle, WAVE_MAPPER, &formatData, 0L, 0L, CALLBACK_NULL); + if ( waveOutGetVolume( handle, (LPDWORD)&volume ) ) + qDebug( "get volume of audio device failed" ); + waveOutClose( handle ); + leftVolume = volume & 0xFFFF; + rightVolume = volume >> 16; +#else + int mixerHandle = open( "/dev/mixer", O_RDWR ); + if ( mixerHandle >= 0 ) { + ioctl( mixerHandle, MIXER_READ(0), &volume ); + close( mixerHandle ); + } else + qDebug( "get volume of audio device failed" ); + leftVolume = ((volume & 0x00FF) << 16) / 101; + rightVolume = ((volume & 0xFF00) << 8) / 101; +#endif +} + + +void AudioDevice::setVolume( unsigned int leftVolume, unsigned int rightVolume, bool muted ) { + AudioDevicePrivate::muted = muted; + if ( muted ) { + AudioDevicePrivate::leftVolume = leftVolume; + AudioDevicePrivate::rightVolume = rightVolume; + leftVolume = 0; + rightVolume = 0; + } else { + leftVolume = ( (int) leftVolume < 0 ) ? 0 : (( leftVolume > 0xFFFF ) ? 0xFFFF : leftVolume ); + rightVolume = ( (int)rightVolume < 0 ) ? 0 : (( rightVolume > 0xFFFF ) ? 0xFFFF : rightVolume ); + } +#ifdef Q_OS_WIN32 + HWAVEOUT handle; + WAVEFORMATEX formatData; + formatData.cbSize = sizeof(WAVEFORMATEX); + formatData.wFormatTag = WAVE_FORMAT_PCM; + formatData.nAvgBytesPerSec = 4 * 44000; + formatData.nBlockAlign = 4; + formatData.nChannels = 2; + formatData.nSamplesPerSec = 44000; + formatData.wBitsPerSample = 16; + waveOutOpen(&handle, WAVE_MAPPER, &formatData, 0L, 0L, CALLBACK_NULL); + unsigned int volume = (rightVolume << 16) | leftVolume; + if ( waveOutSetVolume( handle, volume ) ) + qDebug( "set volume of audio device failed" ); + waveOutClose( handle ); +#else + // Volume can be from 0 to 100 which is 101 distinct values + unsigned int rV = (rightVolume * 101) >> 16; + +# if 0 + unsigned int lV = (leftVolume * 101) >> 16; + unsigned int volume = ((rV << 8) & 0xFF00) | (lV & 0x00FF); + int mixerHandle = 0; + if ( ( mixerHandle = open( "/dev/mixer", O_RDWR ) ) >= 0 ) { + ioctl( mixerHandle, MIXER_WRITE(0), &volume ); + close( mixerHandle ); + } else + qDebug( "set volume of audio device failed" ); +# else + // This is the way this has to be done now I guess, doesn't allow for + // independant right and left channel setting, or setting for different outputs + Config cfg("Sound"); + cfg.setGroup("System"); + cfg.writeEntry("Volume",(int)rV); +# endif + +#endif +// qDebug( "setting volume to: 0x%x", volume ); +#if ( defined Q_WS_QWS || defined(_WS_QWS_) ) && !defined(QT_NO_COP) + // Send notification that the volume has changed + QCopEnvelope( "QPE/System", "volumeChange(bool)" ) << muted; +#endif +} + + + + +AudioDevice::AudioDevice( unsigned int f, unsigned int chs, unsigned int bps ) { + d = new AudioDevicePrivate; + d->frequency = f; + d->channels = chs; + d->bytesPerSample = bps; + + connect( qApp, SIGNAL( volumeChanged(bool) ), this, SLOT( volumeChanged(bool) ) ); + +#ifdef Q_OS_WIN32 + UINT result; + WAVEFORMATEX formatData; + formatData.cbSize = sizeof(WAVEFORMATEX); +/* + // Other possible formats windows supports + formatData.wFormatTag = WAVE_FORMAT_MPEG; + formatData.wFormatTag = WAVE_FORMAT_MPEGLAYER3; + formatData.wFormatTag = WAVE_FORMAT_ADPCM; +*/ + formatData.wFormatTag = WAVE_FORMAT_PCM; + formatData.nAvgBytesPerSec = bps * chs * f; + formatData.nBlockAlign = bps * chs; + formatData.nChannels = chs; + formatData.nSamplesPerSec = f; + formatData.wBitsPerSample = bps * 8; + // Open a waveform device for output + if (result = waveOutOpen((LPHWAVEOUT)&d->handle, WAVE_MAPPER, &formatData, 0L, 0L, CALLBACK_NULL)) { + QString errorMsg = "error opening audio device.\nReason: %i - "; + switch (result) { + case MMSYSERR_ALLOCATED: errorMsg += "Specified resource is already allocated."; break; + case MMSYSERR_BADDEVICEID: errorMsg += "Specified device identifier is out of range."; break; + case MMSYSERR_NODRIVER: errorMsg += "No device driver is present."; break; + case MMSYSERR_NOMEM: errorMsg += "Unable to allocate or lock memory."; break; + case WAVERR_BADFORMAT: errorMsg += "Attempted to open with an unsupported waveform-audio format."; break; + case WAVERR_SYNC: errorMsg += "The device is synchronous but waveOutOpen was called without using the WAVE_ALLOWSYNC flag."; break; + default: errorMsg += "Undefined error"; break; + } + qDebug( errorMsg, result ); + } + + d->bufferSize = sound_fragment_bytes; +#else + + int fragments = 0x10000 * 8 + sound_fragment_shift; + int format = AFMT_S16_LE; + int capabilities = 0; + +#ifdef KEEP_DEVICE_OPEN + if ( AudioDevicePrivate::dspFd == 0 ) { +#endif + if ( ( d->handle = ::open( "/dev/dsp", O_WRONLY ) ) < 0 ) { + qDebug( "error opening audio device /dev/dsp, sending data to /dev/null instead" ); + d->handle = ::open( "/dev/null", O_WRONLY ); + } +#ifdef KEEP_DEVICE_OPEN + AudioDevicePrivate::dspFd = d->handle; + } else { + d->handle = AudioDevicePrivate::dspFd; + } +#endif + + ioctl( d->handle, SNDCTL_DSP_GETCAPS, &capabilities ); + ioctl( d->handle, SNDCTL_DSP_SETFRAGMENT, &fragments ); + ioctl( d->handle, SNDCTL_DSP_SETFMT, &format ); + ioctl( d->handle, SNDCTL_DSP_SPEED, &d->frequency ); + if ( ioctl( d->handle, SNDCTL_DSP_CHANNELS, &d->channels ) == -1 ) { + d->channels = ( d->channels == 1 ) ? 2 : d->channels; + ioctl( d->handle, SNDCTL_DSP_CHANNELS, &d->channels ); + } + + d->bufferSize = sound_fragment_bytes; + d->unwrittenBuffer = new char[d->bufferSize]; + d->unwritten = 0; + d->can_GETOSPACE = TRUE; // until we find otherwise + + //if ( chs != d->channels ) qDebug( "Wanted %d, got %d channels", chs, d->channels ); + //if ( f != d->frequency ) qDebug( "wanted %dHz, got %dHz", f, d->frequency ); + //if ( capabilities & DSP_CAP_BATCH ) qDebug( "Sound card has local buffer" ); + //if ( capabilities & DSP_CAP_REALTIME )qDebug( "Sound card has realtime sync" ); + //if ( capabilities & DSP_CAP_TRIGGER ) qDebug( "Sound card has precise trigger" ); + //if ( capabilities & DSP_CAP_MMAP ) qDebug( "Sound card can mmap" ); +#endif +} + + +AudioDevice::~AudioDevice() { +#ifdef Q_OS_WIN32 + waveOutClose( (HWAVEOUT)d->handle ); +#else +# ifndef KEEP_DEVICE_OPEN + close( d->handle ); // Now it should be safe to shut the handle +# endif + delete d->unwrittenBuffer; + delete d; +#endif +} + + +void AudioDevice::volumeChanged( bool muted ) +{ + AudioDevicePrivate::muted = muted; +} + + +void AudioDevice::write( char *buffer, unsigned int length ) +{ +#ifdef Q_OS_WIN32 + // returns immediately and (to be implemented) emits completedIO() when finished writing + WAVEHDR *lpWaveHdr = (WAVEHDR *)malloc( sizeof(WAVEHDR) ); + // maybe the buffer should be copied so that this fool proof, but its a performance hit + lpWaveHdr->lpData = buffer; + lpWaveHdr->dwBufferLength = length; + lpWaveHdr->dwFlags = 0L; + lpWaveHdr->dwLoops = 0L; + waveOutPrepareHeader( (HWAVEOUT)d->handle, lpWaveHdr, sizeof(WAVEHDR) ); + // waveOutWrite returns immediately. the data is sent in the background. + if ( waveOutWrite( (HWAVEOUT)d->handle, lpWaveHdr, sizeof(WAVEHDR) ) ) + qDebug( "failed to write block to audio device" ); + // emit completedIO(); +#else + int t = ::write( d->handle, buffer, length ); + if ( t<0 ) t = 0; + if ( t != (int)length) { + qDebug("Ahhh!! memcpys 1"); + memcpy(d->unwrittenBuffer,buffer+t,length-t); + d->unwritten = length-t; + } +#endif +} + + +unsigned int AudioDevice::channels() const +{ + return d->channels; +} + + +unsigned int AudioDevice::frequency() const +{ + return d->frequency; +} + + +unsigned int AudioDevice::bytesPerSample() const +{ + return d->bytesPerSample; +} + + +unsigned int AudioDevice::bufferSize() const +{ + return d->bufferSize; +} + +unsigned int AudioDevice::canWrite() const +{ +#ifdef Q_OS_WIN32 + return bufferSize(); // Any better? +#else + audio_buf_info info; + if ( d->can_GETOSPACE && ioctl(d->handle,SNDCTL_DSP_GETOSPACE,&info) ) { + d->can_GETOSPACE = FALSE; + fcntl( d->handle, F_SETFL, O_NONBLOCK ); + } + if ( d->can_GETOSPACE ) { + int t = info.fragments * sound_fragment_bytes; + return QMIN(t,(int)bufferSize()); + } else { + if ( d->unwritten ) { + int t = ::write( d->handle, d->unwrittenBuffer, d->unwritten ); + if ( t<0 ) t = 0; + if ( (unsigned)t!=d->unwritten ) { + memcpy(d->unwrittenBuffer,d->unwrittenBuffer+t,d->unwritten-t); + d->unwritten -= t; + } else { + d->unwritten = 0; + } + } + if ( d->unwritten ) + return 0; + else + return d->bufferSize; + } +#endif +} + + +int AudioDevice::bytesWritten() { +#ifdef Q_OS_WIN32 + MMTIME pmmt = { TIME_BYTES, 0 }; + if ( ( waveOutGetPosition( (HWAVEOUT)d->handle, &pmmt, sizeof(MMTIME) ) != MMSYSERR_NOERROR ) || ( pmmt.wType != TIME_BYTES ) ) { + qDebug( "failed to get audio device position" ); + return -1; + } + return pmmt.u.cb; +#else + int buffered = 0; + if ( ioctl( d->handle, SNDCTL_DSP_GETODELAY, &buffered ) ) { + qDebug( "failed to get audio device position" ); + return -1; + } + return buffered; +#endif +} + |