mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
335 lines
11 KiB
C++
335 lines
11 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* aint with this program; if not, write to the Free Software
|
|
*
|
|
*/
|
|
|
|
#ifndef TITANIC_QMIXER_H
|
|
#define TITANIC_QMIXER_H
|
|
|
|
#include "audio/mixer.h"
|
|
#include "titanic/sound/wave_file.h"
|
|
|
|
namespace Titanic {
|
|
|
|
enum QMixFlag {
|
|
QMIX_OPENSINGLE = 0, // Open the single channel specified by iChannel
|
|
QMIX_OPENALL = 1, // Opens all the channels, iChannel ignored
|
|
QMIX_OPENCOUNT = 2, // Open iChannel Channels (eg. if iChannel = 4 will create channels 0-3)
|
|
QMIX_OPENAVAILABLE = 3, // Open the first unopened channel, and return channel number
|
|
|
|
// Channel function flags
|
|
QMIX_ALL = 0x01, // apply to all channels
|
|
QMIX_NOREMIX = 0x02, // don't remix
|
|
QMIX_CONTROL_NOREMIX = 0x04, // don't remix
|
|
QMIX_USEONCE = 0x10 // settings are temporary
|
|
};
|
|
|
|
// qsWaveMixEnableChannel flags: if mode==0, use conventional, high-performance
|
|
// stereo mixer. Non-zero modes imply some form of additional processing.
|
|
enum QMixChannelFlag {
|
|
QMIX_CHANNEL_STEREO = 0x0000, // Perform stereo mixing
|
|
QMIX_CHANNEL_QSOUND = 0x0001, // Perform QSound localization (default)
|
|
QMIX_CHANNEL_DOPPLER = 0x0002, // Calculate velocity using position updates
|
|
QMIX_CHANNEL_RANGE = 0x0004, // Do range effects
|
|
QMIX_CHANNEL_ELEVATION = 0x0008, // Do elevation effects
|
|
QMIX_CHANNEL_NODOPPLERPITCH = 0x0010, // Disable Doppler pitch shift for this channel
|
|
QMIX_CHANNEL_PITCH_COPY = 0x0000, // Pitch shifting using copying (fastest)
|
|
QMIX_CHANNEL_PITCH_LINEAR = 0x0100, // Pitch shifting using linear interpolation (better but slower)
|
|
QMIX_CHANNEL_PITCH_SPLINE = 0x0200, // Pitch shifting using spline interpolation (better yet, but much slower)
|
|
QMIX_CHANNEL_PITCH_FILTER = 0x0300, // Pitch shifting using FIR filter (best, but slowest)
|
|
QMIX_CHANNEL_PITCH_MASK = 0x0700 // Bits reserved for pitch types
|
|
};
|
|
|
|
/**
|
|
* Options for dwFlags parameter in QSWaveMixPlayEx.
|
|
*
|
|
* Notes: The QMIX_USELRUCHANNEL flag has two roles. When QMIX_CLEARQUEUE is also set,
|
|
* the channel that has been playing the longest (least-recently-used) is cleared and
|
|
* the buffer played. When QMIX_QUEUEWAVE is set, the channel that will first finish
|
|
* playing will be selected and the buffer queued to play. Of course, if an unused
|
|
* channel is found, it will be selected instead.
|
|
* If QMIX_WAIT hasn't been specified, then the channel number will be returned
|
|
* in the iChannel field.
|
|
*/
|
|
enum QMixPlayFlag {
|
|
QMIX_QUEUEWAVE = 0x0000, // Queue on channel
|
|
QMIX_CLEARQUEUE = 0x0001, // Clear queue on channel
|
|
QMIX_USELRUCHANNEL = 0x0002, // See notes above
|
|
QMIX_HIGHPRIORITY = 0x0004,
|
|
QMIX_WAIT = 0x0008, // Queue to be played with other sounds
|
|
QMIX_IMMEDIATE = 0x0020, // Apply volume/pan changes without interpolation
|
|
|
|
QMIX_PLAY_SETEVENT = 0x0100, // Calls SetEvent in the original library when done
|
|
QMIX_PLAY_PULSEEVENT = 0x0200, // Calls PulseEvent in the original library when done
|
|
QMIX_PLAY_NOTIFYSTOP = 0x0400 // Do callback even when stopping or flushing sound
|
|
};
|
|
|
|
/**
|
|
* Mixer configuration structure for qsWaveMixInitEx
|
|
*/
|
|
struct QMIXCONFIG {
|
|
uint32 dwSize;
|
|
uint32 dwFlags;
|
|
uint32 dwSamplingRate; // Sampling rate in Hz
|
|
void *lpIDirectSound;
|
|
const void *lpGuid;
|
|
int iChannels; // Number of channels
|
|
int iOutput; // if 0, uses best output device
|
|
int iLatency; // (in ms) if 0, uses default for output device
|
|
int iMath; // style of math
|
|
uint hwnd;
|
|
|
|
QMIXCONFIG() : dwSize(40), dwFlags(0), dwSamplingRate(0), lpIDirectSound(nullptr),
|
|
lpGuid(nullptr), iChannels(0), iOutput(0), iLatency(0), iMath(0), hwnd(0) {}
|
|
QMIXCONFIG(uint32 rate, int channels, int latency) : dwSize(40), dwFlags(0),
|
|
dwSamplingRate(rate), iChannels(channels), iLatency(latency),
|
|
lpIDirectSound(nullptr), lpGuid(nullptr), iOutput(0), iMath(0), hwnd(0) {}
|
|
};
|
|
|
|
/**
|
|
* Vector positioning in metres
|
|
*/
|
|
struct QSVECTOR {
|
|
double x;
|
|
double y;
|
|
double z;
|
|
|
|
QSVECTOR() : x(0.0), y(0.0), z(0.0) {}
|
|
QSVECTOR(double xp, double yp, double zp) : x(xp), y(yp), z(zp) {}
|
|
};
|
|
|
|
/**
|
|
* Polar positioning
|
|
*/
|
|
struct QSPOLAR {
|
|
double azimuth; // degrees
|
|
double range; // meters
|
|
double elevation; // degrees
|
|
|
|
QSPOLAR() : azimuth(0.0), range(0.0), elevation(0.0) {}
|
|
QSPOLAR(double azimuth_, double range_, double elevation_) :
|
|
azimuth(azimuth_), range(range_), elevation(elevation_) {}
|
|
};
|
|
|
|
struct QMIX_DISTANCES {
|
|
int cbSize; // Structure size
|
|
double minDistance; // sounds are at full volume if closer than this
|
|
double maxDistance; // sounds are muted if further away than this
|
|
double scale; // relative amount to adjust rolloff by
|
|
|
|
QMIX_DISTANCES() : cbSize(16), minDistance(0.0), maxDistance(0.0), scale(0.0) {}
|
|
QMIX_DISTANCES(double minDistance_, double maxDistance_, double scale_) :
|
|
cbSize(16), minDistance(minDistance_), maxDistance(maxDistance_), scale(scale_) {}
|
|
};
|
|
|
|
typedef void (*LPQMIXDONECALLBACK)(int iChannel, CWaveFile *lpWave, void *dwUser);
|
|
|
|
struct QMIXPLAYPARAMS {
|
|
uint dwSize; // Size of the play structure
|
|
void *lpImage; // Additional preprocessed audio for high performance
|
|
uint hwndNotify; // if set, WOM_OPEN and WOM_DONE messages sent to that window
|
|
LPQMIXDONECALLBACK callback; // Callback function
|
|
void *dwUser; // User data accompanying callback
|
|
int lStart;
|
|
int lStartLoop;
|
|
int lEndLoop;
|
|
int lEnd;
|
|
const void *lpChannelParams; // initialize with these parameters
|
|
// Properties introduced by ScummVM
|
|
Audio::Mixer::SoundType _soundType;
|
|
|
|
QMIXPLAYPARAMS() : dwSize(36), lpImage(nullptr), hwndNotify(0), callback(nullptr),
|
|
dwUser(nullptr), lStart(0), lStartLoop(0), lEndLoop(0), lEnd(0),
|
|
lpChannelParams(nullptr), _soundType(Audio::Mixer::kPlainSoundType) {}
|
|
};
|
|
|
|
/**
|
|
* This class represents an interface to the QMixer library developed by
|
|
* QSound Labs, Inc. Which itself is apparently based on Microsoft's
|
|
* WaveMix API.
|
|
*
|
|
* It does not currently have any actual code from the library,
|
|
* and instead remaps calls to ScummVM's existing mixer where possible.
|
|
* This means that advanced features of the QMixer library, like being
|
|
* able to set up both the player and sounds at different positions are
|
|
* currently ignored, and all sounds play at full volume.
|
|
*/
|
|
class QMixer {
|
|
struct SoundEntry {
|
|
bool _started;
|
|
CWaveFile *_waveFile;
|
|
Audio::SoundHandle _soundHandle;
|
|
LPQMIXDONECALLBACK _callback;
|
|
int _loops;
|
|
void *_userData;
|
|
SoundEntry() : _started(false), _waveFile(nullptr), _callback(nullptr),
|
|
_loops(0), _userData(nullptr) {}
|
|
|
|
SoundEntry(CWaveFile *waveFile, LPQMIXDONECALLBACK callback, int loops, void *userData) :
|
|
_started(false), _waveFile(waveFile), _callback(callback), _loops(loops), _userData(userData) {}
|
|
};
|
|
struct ChannelEntry {
|
|
// Currently playing and any following queued sounds for the channel
|
|
Common::List<SoundEntry> _sounds;
|
|
// Current channel volume
|
|
byte _volume;
|
|
// Current time in milliseconds for paning (volume) changes
|
|
uint _panRate;
|
|
// Fields used to transition between volume levels
|
|
uint _volumeChangeStart;
|
|
uint _volumeChangeEnd;
|
|
byte _volumeStart;
|
|
byte _volumeEnd;
|
|
// Distance of source
|
|
double _distance;
|
|
bool _resetDistance;
|
|
|
|
ChannelEntry() : _volume(0), _panRate(0), _volumeChangeStart(0),
|
|
_volumeChangeEnd(0), _volumeStart(0), _volumeEnd(0),
|
|
_distance(0.0), _resetDistance(true) {}
|
|
|
|
/**
|
|
* Calculates the raw volume level to pass to ScummVM playStream, taking
|
|
* into the sound's volume level and distance from origin
|
|
*/
|
|
byte getRawVolume() const;
|
|
};
|
|
private:
|
|
Common::Array<ChannelEntry> _channels;
|
|
protected:
|
|
Audio::Mixer *_mixer;
|
|
public:
|
|
QMixer(Audio::Mixer *mixer);
|
|
virtual ~QMixer();
|
|
|
|
/**
|
|
* Initializes the mixer
|
|
*/
|
|
bool qsWaveMixInitEx(const QMIXCONFIG &config);
|
|
|
|
/**
|
|
* Activates the mixer
|
|
*/
|
|
void qsWaveMixActivate(bool fActivate);
|
|
|
|
/**
|
|
* Opens channels in the mixer for access
|
|
*/
|
|
int qsWaveMixOpenChannel(int iChannel, QMixFlag mode);
|
|
|
|
/**
|
|
* Enables a given channel
|
|
*/
|
|
int qsWaveMixEnableChannel(int iChannel, uint flags, bool enabled);
|
|
|
|
/**
|
|
* Closes down the mixer
|
|
*/
|
|
void qsWaveMixCloseSession();
|
|
|
|
/**
|
|
* Stops a sound from playing
|
|
*/
|
|
void qsWaveMixFreeWave(Audio::SoundHandle &handle);
|
|
|
|
/**
|
|
* Flushes a channel
|
|
*/
|
|
void qsWaveMixFlushChannel(int iChannel, uint flags = 0);
|
|
|
|
/**
|
|
* Sets the amount of time, in milliseconds, to effect a change in
|
|
* a channel property (e.g. volume, position). Non-zero values
|
|
* smooth out changes
|
|
* @param iChannel Channel to change
|
|
* @param flags Flags
|
|
* @param rate Pan rate in milliseconds
|
|
*/
|
|
void qsWaveMixSetPanRate(int iChannel, uint flags, uint rate);
|
|
|
|
/**
|
|
* Sets the volume for a channel
|
|
*/
|
|
void qsWaveMixSetVolume(int iChannel, uint flags, uint volume);
|
|
|
|
/**
|
|
* Sets the relative position of a channel
|
|
* @param iChannel Channel number
|
|
* @param Flags Flags
|
|
* @param position Vector position for channel
|
|
*/
|
|
void qsWaveMixSetSourcePosition(int iChannel, uint flags, const QSVECTOR &position);
|
|
|
|
/**
|
|
* Sets the relative position of a channel using polar co-ordinates
|
|
* @param iChannel Channel number
|
|
* @param Flags Flags
|
|
* @param position Polar position for channel
|
|
*/
|
|
void qsWaveMixSetPolarPosition(int iChannel, uint flags, const QSPOLAR &position);
|
|
|
|
/**
|
|
* Sets the listener position
|
|
*/
|
|
void qsWaveMixSetListenerPosition(const QSVECTOR &position, uint flags = 0);
|
|
|
|
/**
|
|
* Sets the listener orientation
|
|
*/
|
|
void qsWaveMixSetListenerOrientation(const QSVECTOR &direction, const QSVECTOR &up, uint flags = 0);
|
|
|
|
/**
|
|
* Sets the mapping ditance range
|
|
*/
|
|
void qsWaveMixSetDistanceMapping(int iChannel, uint flags, const QMIX_DISTANCES &distances);
|
|
|
|
/**
|
|
* Sets the frequency/rate of sound playback
|
|
*/
|
|
void qsWaveMixSetFrequency(int iChannel, uint flags, uint frequency);
|
|
|
|
/**
|
|
* Sets the velocity of the source (listener)
|
|
*/
|
|
void qsWaveMixSetSourceVelocity(int iChannel, uint flags, const QSVECTOR &velocity);
|
|
|
|
/**
|
|
* Plays sound
|
|
* @param iChannel The channel number to be played on
|
|
* @param flags Play flags
|
|
* @param mixWave Data for the sound to be played
|
|
* @param loops Number of loops to play (-1 for forever)
|
|
* @param params Playback parameter data
|
|
*/
|
|
int qsWaveMixPlayEx(int iChannel, uint flags, CWaveFile *waveFile, int loops, const QMIXPLAYPARAMS ¶ms);
|
|
|
|
/**
|
|
* Returns true if there are no more buffers playing or queued on the channel
|
|
*/
|
|
bool qsWaveMixIsChannelDone(int iChannel) const;
|
|
|
|
/**
|
|
* Handles regularly updating the mixer
|
|
*/
|
|
void qsWaveMixPump();
|
|
};
|
|
|
|
} // End of namespace Titanic
|
|
|
|
#endif /* TITANIC_QMIXER_H */
|