AUDIO: Move MIDI multisource to base class
This moves the multisource functionality from the MT-32/GM MIDI driver to a new base class so it can be reused by future multisource drivers.
This commit is contained in:
parent
d8c3e22a54
commit
f660238ab5
7 changed files with 689 additions and 398 deletions
254
audio/mididrv_ms.cpp
Normal file
254
audio/mididrv_ms.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
/* 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 2
|
||||
* 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
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "audio/mididrv_ms.h"
|
||||
|
||||
const uint8 MidiDriver_Multisource::MAXIMUM_SOURCES;
|
||||
const uint16 MidiDriver_Multisource::DEFAULT_SOURCE_NEUTRAL_VOLUME;
|
||||
const uint16 MidiDriver_Multisource::FADING_DELAY;
|
||||
|
||||
MidiDriver_Multisource::MidiSource::MidiSource() :
|
||||
type(SOURCE_TYPE_UNDEFINED),
|
||||
volume(DEFAULT_SOURCE_NEUTRAL_VOLUME),
|
||||
neutralVolume(DEFAULT_SOURCE_NEUTRAL_VOLUME),
|
||||
fadeStartVolume(0),
|
||||
fadeEndVolume(0),
|
||||
fadePassedTime(0),
|
||||
fadeDuration(0) { }
|
||||
|
||||
MidiDriver_Multisource::MidiDriver_Multisource() :
|
||||
_userVolumeScaling(false),
|
||||
_userMusicVolume(192),
|
||||
_userSfxVolume(192),
|
||||
_userMute(false),
|
||||
_timerRate(0),
|
||||
_fadeDelay(0),
|
||||
_timer_param(0),
|
||||
_timer_proc(0) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
// Default source type: 0 = music, 1+ = SFX
|
||||
_sources[i].type = (i == 0 ? SOURCE_TYPE_MUSIC : SOURCE_TYPE_SFX);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::send(uint32 b) {
|
||||
send(-1, b);
|
||||
}
|
||||
|
||||
uint32 MidiDriver_Multisource::property(int prop, uint32 param) {
|
||||
switch (prop) {
|
||||
case PROP_USER_VOLUME_SCALING:
|
||||
if (param == 0xFFFF)
|
||||
return _userVolumeScaling ? 1 : 0;
|
||||
_userVolumeScaling = param > 0;
|
||||
break;
|
||||
default:
|
||||
return MidiDriver::property(prop, param);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::startFade(uint16 duration, uint16 targetVolume) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
startFade(i, duration, targetVolume);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::startFade(uint8 source, uint16 duration, uint16 targetVolume) {
|
||||
Common::StackLock lock(_fadingMutex);
|
||||
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
// Reset the number of microseconds which have passed since the start of
|
||||
// the fade.
|
||||
_sources[source].fadePassedTime = 0;
|
||||
// Set start volume to current volume.
|
||||
_sources[source].fadeStartVolume = _sources[source].volume;
|
||||
_sources[source].fadeEndVolume = targetVolume;
|
||||
// Convert to microseconds and set the duration. A duration > 0 will cause
|
||||
// the fade to be processed by updateFading.
|
||||
_sources[source].fadeDuration = duration * 1000;
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::abortFade(FadeAbortType abortType) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
abortFade(i, abortType);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::abortFade(uint8 source, FadeAbortType abortType) {
|
||||
Common::StackLock lock(_fadingMutex);
|
||||
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
if (!isFading(source)) {
|
||||
// Nothing to abort.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the fade duration to 0. This will stop the fade from being processed
|
||||
// by updateFading.
|
||||
_sources[source].fadeDuration = 0;
|
||||
|
||||
// Now set the intended end volume.
|
||||
uint16 newSourceVolume;
|
||||
switch (abortType) {
|
||||
case FADE_ABORT_TYPE_END_VOLUME:
|
||||
newSourceVolume = _sources[source].fadeEndVolume;
|
||||
break;
|
||||
case FADE_ABORT_TYPE_START_VOLUME:
|
||||
newSourceVolume = _sources[source].fadeStartVolume;
|
||||
break;
|
||||
case FADE_ABORT_TYPE_CURRENT_VOLUME:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
setSourceVolume(source, newSourceVolume);
|
||||
}
|
||||
|
||||
bool MidiDriver_Multisource::isFading() {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
if (isFading(i))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MidiDriver_Multisource::isFading(uint8 source) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
return _sources[source].fadeDuration > 0;
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::updateFading() {
|
||||
Common::StackLock lock(_fadingMutex);
|
||||
|
||||
// Decrease the fade delay by the time that has passed since the last
|
||||
// fading update.
|
||||
_fadeDelay -= (_fadeDelay < _timerRate ? _fadeDelay : _timerRate);
|
||||
|
||||
bool updatedVolume = false;
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
|
||||
if (_sources[i].fadeDuration > 0) {
|
||||
// This source has an active fade.
|
||||
|
||||
// Update the time that has passed since the start of the fade.
|
||||
_sources[i].fadePassedTime += _timerRate;
|
||||
|
||||
if (_sources[i].fadePassedTime >= _sources[i].fadeDuration) {
|
||||
// The fade has finished.
|
||||
|
||||
// Set the end volume.
|
||||
setSourceVolume(i, _sources[i].fadeEndVolume);
|
||||
updatedVolume = true;
|
||||
|
||||
// Stop further processing of this fade.
|
||||
_sources[i].fadeDuration = 0;
|
||||
} else if (_fadeDelay == 0) {
|
||||
// The fade has not yet finished and the fade delay has run
|
||||
// down. Waiting for the fade delay prevents sending out volume
|
||||
// updates on every updateFading call, which can overflow
|
||||
// slower MIDI hardware.
|
||||
|
||||
// Set the new volume value.
|
||||
setSourceVolume(i, ((_sources[i].fadePassedTime * (_sources[i].fadeEndVolume - _sources[i].fadeStartVolume)) /
|
||||
_sources[i].fadeDuration) + _sources[i].fadeStartVolume);
|
||||
updatedVolume = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedVolume)
|
||||
// Set the fade delay to delay the next volume update.
|
||||
_fadeDelay = FADING_DELAY;
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::deinitSource(uint8 source) {
|
||||
abortFade(source, FADE_ABORT_TYPE_END_VOLUME);
|
||||
|
||||
// Stop all active notes for this source.
|
||||
stopAllNotes(source, 0xFF);
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setSourceType(SourceType type) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
setSourceType(i, type);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setSourceType(uint8 source, SourceType type) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].type = type;
|
||||
|
||||
// A changed source type can mean a different user volume level should be
|
||||
// used for this source. Calling applySourceVolume will apply the user
|
||||
// volume.
|
||||
applySourceVolume(source);
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setSourceVolume(uint16 volume) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
setSourceVolume(i, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setSourceVolume(uint8 source, uint16 volume) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].volume = volume;
|
||||
|
||||
// Set the volume for active notes and/or MIDI channels for this source.
|
||||
applySourceVolume(source);
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setSourceNeutralVolume(uint16 volume) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
setSourceNeutralVolume(i, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::setSourceNeutralVolume(uint8 source, uint16 volume) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].neutralVolume = volume;
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::syncSoundSettings() {
|
||||
// Get user volume settings.
|
||||
_userMusicVolume = MIN(256, ConfMan.getInt("music_volume"));
|
||||
_userSfxVolume = MIN(256, ConfMan.getInt("sfx_volume"));
|
||||
_userMute = ConfMan.getBool("mute");
|
||||
|
||||
// Calling applySourceVolume will apply the user volume.
|
||||
applySourceVolume(0xFF);
|
||||
}
|
||||
|
||||
void MidiDriver_Multisource::onTimer() {
|
||||
updateFading();
|
||||
|
||||
if (_timer_proc && _timer_param)
|
||||
_timer_proc(_timer_param);
|
||||
}
|
365
audio/mididrv_ms.h
Normal file
365
audio/mididrv_ms.h
Normal file
|
@ -0,0 +1,365 @@
|
|||
/* 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 2
|
||||
* 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
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_MIDIDRV_MS_H
|
||||
#define AUDIO_MIDIDRV_MS_H
|
||||
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
/**
|
||||
* Abstract base class for MIDI drivers supporting multiple simultaneous
|
||||
* sources of MIDI data.
|
||||
*
|
||||
* These drivers support the following features:
|
||||
*
|
||||
* - Multiple MIDI sources
|
||||
* If the game plays multiple streams of MIDI data at the same time, each
|
||||
* stream can be marked with a source number. When a source has finished
|
||||
* playing, it must be deinitialized to release any resources allocated to
|
||||
* it. This is done automatically when an End Of Track MIDI meta event is
|
||||
* received, or manually by calling deinitSource.
|
||||
* Using source numbers enables the following features:
|
||||
* - Music/SFX volume
|
||||
* Using setSourceType a MIDI source can be designated as music or sound
|
||||
* effects. The driver will then apply the appropriate user volume setting
|
||||
* to the MIDI channel volume. This setting sticks after deinitializing a
|
||||
* source, so if you use the same source numbers for the same types of MIDI
|
||||
* data, you don't need to set the source type repeatedly. The default setup
|
||||
* is music for source 0 and SFX for sources 1 and higher.
|
||||
* - Source volume
|
||||
* If the game changes the volume of the MIDI playback, you can use
|
||||
* setSourceVolume to set the volume level for a source. The driver will
|
||||
* then adjust the current MIDI channel volume and any received MIDI volume
|
||||
* controller messages. Use setSourceNeutralVolume to set the neutral volume
|
||||
* for a source (MIDI volume is not changed when source volume is at this
|
||||
* level; if it is lower or higher, MIDI volume is reduced or increased).
|
||||
* - Volume fading
|
||||
* If the game needs to gradually change the volume of the MIDI playback
|
||||
* (typically for a fade-out), you can use the startFade function. You can
|
||||
* check the status of the fade using isFading, and abort a fade using
|
||||
* abortFade. An active fade is automatically aborted when the fading source
|
||||
* is deinitialized.
|
||||
* The fading functionality uses the source volume, so you should not set
|
||||
* this while a fade is active. After the fade the source volume will remain
|
||||
* at the target level, so if you perform f.e. a fade-out, the source volume
|
||||
* will remain at 0. If you want to start playback again using this source,
|
||||
* use setSourceVolume to set the correct playback volume.
|
||||
* Note that when you stop MIDI playback, notes will not be immediately
|
||||
* silent but will gradually die out ("release"). So if you fade out a
|
||||
* source, stop playback, and immediately reset the source volume, the
|
||||
* note release will be audible. It is recommended to wait about 0.5s
|
||||
* before resetting the source volume.
|
||||
*
|
||||
* - User volume settings
|
||||
* The driver can scale the MIDI channel volume using the user specified
|
||||
* volume settings. Just call syncSoundSettings when the user has changed the
|
||||
* volume settings. Set the USER_VOLUME_SCALING property to true to enable
|
||||
* this functionality.
|
||||
*
|
||||
* A driver extending this class must implement the following functions:
|
||||
* - send(source, data): process a MIDI event for a specific source.
|
||||
* - stopAllNotes(source, channel): stop all active notes for a source and/or
|
||||
* MIDI channel (called when a source is deinitialized).
|
||||
* - applySourceVolume(source): set the current source volume on active notes
|
||||
* and/or MIDI channels.
|
||||
*/
|
||||
class MidiDriver_Multisource : public MidiDriver {
|
||||
public:
|
||||
/**
|
||||
* The maximum number of sources supported. This can be increased if
|
||||
* necessary, but this will consume more memory and processing time.
|
||||
*/
|
||||
static const uint8 MAXIMUM_SOURCES = 10;
|
||||
/**
|
||||
* The default neutral volume level for a source. If the source volume is
|
||||
* set to this level, the volume levels in the MIDI data are used directly;
|
||||
* if source volume is lower or higher, output volume is decreased or
|
||||
* increased, respectively. Use @see setSourceNeutralVolume to change the
|
||||
* default neutral volume.
|
||||
*/
|
||||
static const uint16 DEFAULT_SOURCE_NEUTRAL_VOLUME = 255;
|
||||
|
||||
protected:
|
||||
// Timeout between updates of the channel volume for fades (25ms)
|
||||
static const uint16 FADING_DELAY = 25 * 1000;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The type of audio produced by a MIDI source (music or sound effects).
|
||||
*/
|
||||
enum SourceType {
|
||||
/**
|
||||
* Source type not specified (generally treated as music).
|
||||
*/
|
||||
SOURCE_TYPE_UNDEFINED,
|
||||
/**
|
||||
* Source produces music.
|
||||
*/
|
||||
SOURCE_TYPE_MUSIC,
|
||||
/**
|
||||
* Source produces sound effects.
|
||||
*/
|
||||
SOURCE_TYPE_SFX
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies what happens to the volume when a fade is aborted.
|
||||
*/
|
||||
enum FadeAbortType {
|
||||
/**
|
||||
* The volume is set to the fade's end volume level.
|
||||
*/
|
||||
FADE_ABORT_TYPE_END_VOLUME,
|
||||
/**
|
||||
* The volume remains at the current level.
|
||||
*/
|
||||
FADE_ABORT_TYPE_CURRENT_VOLUME,
|
||||
/**
|
||||
* The volume is reset to the fade's start volume level.
|
||||
*/
|
||||
FADE_ABORT_TYPE_START_VOLUME
|
||||
};
|
||||
|
||||
protected:
|
||||
// This stores data about a specific source of MIDI data.
|
||||
struct MidiSource {
|
||||
// Whether this source sends music or SFX MIDI data.
|
||||
SourceType type;
|
||||
// The source volume (relative volume for this source as defined by the
|
||||
// game). Default is the default neutral value (255).
|
||||
uint16 volume;
|
||||
// The source volume level at which no scaling is performed (volume as
|
||||
// defined in the MIDI data is used directly). Volume values below this
|
||||
// decrease volume, values above increase volume (up to the maximum MIDI
|
||||
// channel volume). Set this to match the volume values used by the game
|
||||
// engine to avoid having to convert them. Default value is 255; minimum
|
||||
// value is 1.
|
||||
uint16 neutralVolume;
|
||||
// The volume level at which the fade started.
|
||||
uint16 fadeStartVolume;
|
||||
// The target volume level for the fade.
|
||||
uint16 fadeEndVolume;
|
||||
// How much time (microseconds) has passed since the start of the fade.
|
||||
int32 fadePassedTime;
|
||||
// The total duration of the fade (microseconds).
|
||||
int32 fadeDuration;
|
||||
|
||||
MidiSource();
|
||||
};
|
||||
|
||||
public:
|
||||
MidiDriver_Multisource();
|
||||
|
||||
// MidiDriver functions
|
||||
using MidiDriver_BASE::send;
|
||||
void send(uint32 b) override;
|
||||
void send(int8 source, uint32 b) override = 0;
|
||||
|
||||
uint32 property(int prop, uint32 param) override;
|
||||
|
||||
/**
|
||||
* Deinitializes a source. This will abort active fades and stop any active
|
||||
* notes.
|
||||
*
|
||||
* @param source The source to deinitialize.
|
||||
*/
|
||||
virtual void deinitSource(uint8 source);
|
||||
/**
|
||||
* Sets the type for all sources (music or SFX).
|
||||
*
|
||||
* @param type The new type for all sources.
|
||||
*/
|
||||
void setSourceType(SourceType type);
|
||||
/**
|
||||
* Sets the type for a specific sources (music or SFX).
|
||||
*
|
||||
* @param source The source for which the type should be set.
|
||||
* @param type The new type for the specified source.
|
||||
*/
|
||||
void setSourceType(uint8 source, SourceType type);
|
||||
/**
|
||||
* Sets the source volume for all sources.
|
||||
*
|
||||
* @param volume The new source volume for all sources.
|
||||
*/
|
||||
void setSourceVolume(uint16 volume);
|
||||
/**
|
||||
* Sets the volume for this source. The volume values in the MIDI data sent
|
||||
* by this source will be scaled by the source volume.
|
||||
*
|
||||
* @param source The source for which the source volume should be set.
|
||||
* @param volume The new source volume for the specified source.
|
||||
*/
|
||||
void setSourceVolume(uint8 source, uint16 volume);
|
||||
/**
|
||||
* Sets the neutral volume for all sources. See the source-specific
|
||||
* setSourceNeutralVolume function for details.
|
||||
*
|
||||
* @param volume The new neutral volume for all sources.
|
||||
*/
|
||||
void setSourceNeutralVolume(uint16 volume);
|
||||
/**
|
||||
* Sets the neutral volume for this source. If the source volume is at this
|
||||
* level, the volume values in the MIDI data sent by this source will not
|
||||
* be changed. At source volumes below or above this value, the MIDI volume
|
||||
* values will be decreased or increased accordingly.
|
||||
*
|
||||
* @param source The source for which the neutral volume should be set.
|
||||
* @param volume The new neutral volume for the specified source.
|
||||
*/
|
||||
void setSourceNeutralVolume(uint8 source, uint16 volume);
|
||||
|
||||
/**
|
||||
* Starts a fade for all sources.
|
||||
* See the source-specific startFade function for more information.
|
||||
*
|
||||
* @param duration The fade duration in milliseconds
|
||||
* @param targetVolume The volume at the end of the fade
|
||||
*/
|
||||
void startFade(uint16 duration, uint16 targetVolume);
|
||||
/**
|
||||
* Starts a fade for a source. This will linearly increase or decrease the
|
||||
* volume of the MIDI channels used by the source to the specified target
|
||||
* value over the specified length of time.
|
||||
*
|
||||
* @param source The source to fade
|
||||
* @param duration The fade duration in milliseconds
|
||||
* @param targetVolume The volume at the end of the fade
|
||||
*/
|
||||
void startFade(uint8 source, uint16 duration, uint16 targetVolume);
|
||||
/**
|
||||
* Aborts any active fades for all sources.
|
||||
* See the source-specific abortFade function for more information.
|
||||
*
|
||||
* @param abortType How to set the volume when aborting the fade (default:
|
||||
* set to the target fade volume).
|
||||
*/
|
||||
void abortFade(FadeAbortType abortType = FADE_ABORT_TYPE_END_VOLUME);
|
||||
/**
|
||||
* Aborts an active fade for a source. Depending on the abort type, the
|
||||
* volume will remain at the current value or be set to the start or end
|
||||
* volume. If there is no active fade for the specified source, this
|
||||
* function does nothing.
|
||||
*
|
||||
* @param source The source that should have its fade aborted
|
||||
* @param abortType How to set the volume when aborting the fade (default:
|
||||
* set to the target fade volume).
|
||||
*/
|
||||
void abortFade(uint8 source, FadeAbortType abortType = FADE_ABORT_TYPE_END_VOLUME);
|
||||
/**
|
||||
* Check if any source has an active fade.
|
||||
*
|
||||
* @return True if any source has an active fade.
|
||||
*/
|
||||
bool isFading();
|
||||
/**
|
||||
* Check if the specified source has an active fade.
|
||||
*
|
||||
* @return True if the specified source has an active fade.
|
||||
*/
|
||||
bool isFading(uint8 source);
|
||||
|
||||
/**
|
||||
* Applies the user volume settings to the MIDI driver. MIDI channel
|
||||
* volumes will be scaled using the user volume.
|
||||
* This function must be called by the engine when the user has changed the
|
||||
* volume settings.
|
||||
*/
|
||||
void syncSoundSettings();
|
||||
|
||||
using MidiDriver::stopAllNotes;
|
||||
/**
|
||||
* Stops all active notes (including sustained notes) for the specified
|
||||
* source and MIDI channel. For both source and channel the value 0xFF can
|
||||
* be specified, in which case active notes will be stopped for all sources
|
||||
* and/or MIDI channels.
|
||||
*
|
||||
* @param source The source for which all notes should be stopped, or all
|
||||
* sources if 0xFF is specified.
|
||||
* @param channel The MIDI channel on which all notes should be stopped, or
|
||||
* all channels if 0xFF is specified.
|
||||
*/
|
||||
virtual void stopAllNotes(uint8 source, uint8 channel) = 0;
|
||||
|
||||
/**
|
||||
* Sets a callback which will be called whenever the driver's timer
|
||||
* callback is called by the underlying emulator or hardware driver. The
|
||||
* callback will only be called when the driver is open. Use
|
||||
* @see getBaseTempo to get the delay between each callback invocation.
|
||||
*
|
||||
* @param timer_param A parameter that will be passed to the callback
|
||||
* function. Optional.
|
||||
* @param timer_proc The function that should be called.
|
||||
*/
|
||||
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) override {
|
||||
_timer_param = timer_param;
|
||||
_timer_proc = timer_proc;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Applies the current source volume to the active notes and/or MIDI
|
||||
* channels of the specified source. 0xFF can be specified to apply the
|
||||
* source volume for all sources.
|
||||
*
|
||||
* @param source The source for which the source volume should be applied,
|
||||
* or all sources if 0xFF is specified.
|
||||
*/
|
||||
virtual void applySourceVolume(uint8 source) = 0;
|
||||
/**
|
||||
* Processes active fades and sets new volume values if necessary.
|
||||
*/
|
||||
void updateFading();
|
||||
/**
|
||||
* Runs the MIDI driver's timer related functionality. Will update volume
|
||||
* fades and calls the timer callback if necessary.
|
||||
*/
|
||||
virtual void onTimer();
|
||||
|
||||
// MIDI source data
|
||||
MidiSource _sources[MAXIMUM_SOURCES];
|
||||
|
||||
// True if the driver should scale MIDI channel volume to the user
|
||||
// specified volume settings.
|
||||
bool _userVolumeScaling;
|
||||
|
||||
// User volume settings
|
||||
uint16 _userMusicVolume;
|
||||
uint16 _userSfxVolume;
|
||||
bool _userMute;
|
||||
|
||||
Common::Mutex _fadingMutex; // For operations on fades
|
||||
|
||||
// The number of microseconds to wait before the next fading step.
|
||||
uint16 _fadeDelay;
|
||||
|
||||
// The number of microseconds between timer callback invocations.
|
||||
uint32 _timerRate;
|
||||
|
||||
// External timer callback
|
||||
void *_timer_param;
|
||||
Common::TimerManager::TimerProc _timer_proc;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -132,14 +132,6 @@ public:
|
|||
* Automatically executed when an End Of Track meta event is received.
|
||||
*/
|
||||
void deinitSource(uint8 source) override;
|
||||
/**
|
||||
* Set the volume for this source. This will be used to scale the volume values in the MIDI
|
||||
* data from this source. Expected volume values are 0 - 256.
|
||||
* Note that source volume remains set for the source number even after deinitializing the
|
||||
* source. If the same source numbers are consistently used for music and SFX sources, the
|
||||
* source volume will only need to be set once.
|
||||
*/
|
||||
void setSourceVolume(uint8 source, uint16 volume) override;
|
||||
|
||||
void stopAllNotes(bool stopSustainedNotes = false) override;
|
||||
|
||||
|
@ -148,6 +140,7 @@ public:
|
|||
protected:
|
||||
void initControlData() override;
|
||||
void initMidiDevice() override;
|
||||
void applySourceVolume(uint8 source) override;
|
||||
|
||||
private:
|
||||
void writeRhythmSetup(byte note, byte customTimbreId);
|
||||
|
|
|
@ -55,11 +55,6 @@ MidiDriver_Miles_Midi::MidiDriver_Miles_Midi(MusicType midiType, MilesMT32Instru
|
|||
_instrumentTablePtr = instrumentTablePtr;
|
||||
_instrumentTableCount = instrumentTableCount;
|
||||
|
||||
// Disable user volume scaling by default. Most (all?)
|
||||
// engines using Miles implement this themselves. Can
|
||||
// be turned on using the property function.
|
||||
_userVolumeScaling = false;
|
||||
|
||||
setSourceNeutralVolume(MILES_DEFAULT_SOURCE_NEUTRAL_VOLUME);
|
||||
}
|
||||
|
||||
|
@ -141,7 +136,7 @@ void MidiDriver_Miles_Midi::send(int8 source, uint32 b) {
|
|||
|
||||
byte command = b & 0xf0;
|
||||
byte dataChannel = b & 0xf;
|
||||
byte outputChannel = source < 0 ? dataChannel : _sources[source].channelMap[dataChannel];
|
||||
byte outputChannel = source < 0 ? dataChannel : _channelMap[source][dataChannel];
|
||||
|
||||
MidiChannelEntry &outputChannelEntry = _midiChannels[outputChannel];
|
||||
// Only send the message to the MIDI device if the channel is not locked or
|
||||
|
@ -382,7 +377,7 @@ void MidiDriver_Miles_Midi::lockChannel(uint8 source, uint8 dataChannel) {
|
|||
|
||||
_midiChannels[lockChannel].locked = true;
|
||||
_midiChannels[lockChannel].lockDataChannel = dataChannel;
|
||||
_sources[source].channelMap[dataChannel] = lockChannel;
|
||||
_channelMap[source][dataChannel] = lockChannel;
|
||||
// Copy current controller values so they can be restored when unlocking the channel
|
||||
*_midiChannels[lockChannel].unlockData = *_midiChannels[lockChannel].currentData;
|
||||
_midiChannels[lockChannel].currentData->source = source;
|
||||
|
@ -424,7 +419,7 @@ void MidiDriver_Miles_Midi::unlockChannel(uint8 outputChannel) {
|
|||
|
||||
// Unlock the channel
|
||||
channel.locked = false;
|
||||
_sources[channel.currentData->source].channelMap[channel.lockDataChannel] = channel.lockDataChannel;
|
||||
_channelMap[channel.currentData->source][channel.lockDataChannel] = channel.lockDataChannel;
|
||||
channel.lockDataChannel = -1;
|
||||
channel.currentData->source = channel.unlockData->source;
|
||||
|
||||
|
@ -874,11 +869,7 @@ void MidiDriver_Miles_Midi::deinitSource(uint8 source) {
|
|||
MidiDriver_MT32GM::deinitSource(source);
|
||||
}
|
||||
|
||||
void MidiDriver_Miles_Midi::setSourceVolume(uint8 source, uint16 volume) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].volume = volume;
|
||||
|
||||
void MidiDriver_Miles_Midi::applySourceVolume(uint8 source) {
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
if (!isOutputChannelUsed(i))
|
||||
continue;
|
||||
|
|
|
@ -6,6 +6,7 @@ MODULE_OBJS := \
|
|||
audiostream.o \
|
||||
fmopl.o \
|
||||
mididrv.o \
|
||||
mididrv_ms.o \
|
||||
midiparser_qt.o \
|
||||
midiparser_smf.o \
|
||||
midiparser_xmidi.o \
|
||||
|
|
239
audio/mt32gm.cpp
239
audio/mt32gm.cpp
|
@ -30,13 +30,10 @@
|
|||
|
||||
// The initialization of the static const integral data members is done in the class definition,
|
||||
// but we still need to provide a definition if they are odr-used.
|
||||
const uint8 MidiDriver_MT32GM::MAXIMUM_SOURCES;
|
||||
const uint16 MidiDriver_MT32GM::DEFAULT_SOURCE_NEUTRAL_VOLUME;
|
||||
const uint8 MidiDriver_MT32GM::MT32_DEFAULT_CHANNEL_VOLUME;
|
||||
const uint8 MidiDriver_MT32GM::GM_DEFAULT_CHANNEL_VOLUME;
|
||||
const uint8 MidiDriver_MT32GM::MAXIMUM_MT32_ACTIVE_NOTES;
|
||||
const uint8 MidiDriver_MT32GM::MAXIMUM_GM_ACTIVE_NOTES;
|
||||
const uint16 MidiDriver_MT32GM::FADING_DELAY;
|
||||
|
||||
// These are the power-on default instruments of the Roland MT-32 family.
|
||||
const byte MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS[8] = {
|
||||
|
@ -78,6 +75,14 @@ const uint8 MidiDriver_MT32GM::GS_DRUMKIT_FALLBACK_MAP[128] = {
|
|||
0, 0, 0, 0, 0, 0, 0, 127 // No drumkit defined; CM-64/32L (127)
|
||||
};
|
||||
|
||||
// Callback hooked up to the driver wrapped by the MIDI driver
|
||||
// object. Executes onTimer and the external callback set by
|
||||
// the setTimerCallback function.
|
||||
void MidiDriver_MT32GM::timerCallback(void *data) {
|
||||
MidiDriver_MT32GM *driver = (MidiDriver_MT32GM *)data;
|
||||
driver->onTimer();
|
||||
}
|
||||
|
||||
MidiDriver_MT32GM::MidiDriver_MT32GM(MusicType midiType) :
|
||||
_driver(0),
|
||||
_nativeMT32(false),
|
||||
|
@ -85,18 +90,10 @@ MidiDriver_MT32GM::MidiDriver_MT32GM(MusicType midiType) :
|
|||
_midiDataReversePanning(false),
|
||||
_midiDeviceReversePanning(false),
|
||||
_scaleGSPercussionVolumeToMT32(false),
|
||||
_userVolumeScaling(true),
|
||||
_userMusicVolume(192),
|
||||
_userSfxVolume(192),
|
||||
_userMute(false),
|
||||
_isOpen(false),
|
||||
_outputChannelMask(65535), // Channels 1-16
|
||||
_baseFreq(250),
|
||||
_timerRate(0),
|
||||
_fadeDelay(0),
|
||||
_sysExDelay(0),
|
||||
_timer_param(0),
|
||||
_timer_proc(0) {
|
||||
_sysExDelay(0) {
|
||||
memset(_controlData, 0, sizeof(_controlData));
|
||||
|
||||
switch (midiType) {
|
||||
|
@ -113,11 +110,10 @@ MidiDriver_MT32GM::MidiDriver_MT32GM(MusicType midiType) :
|
|||
}
|
||||
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
// Default source type: 0 = music, 1+ = SFX
|
||||
_sources[i].type = i == 0 ? SOURCE_TYPE_MUSIC : SOURCE_TYPE_SFX;
|
||||
_availableChannels[i] = 0;
|
||||
// Default MIDI channel mapping: data channel == output channel
|
||||
for (int j = 0; j < MIDI_CHANNEL_COUNT; ++j) {
|
||||
_sources[i].channelMap[j] = j;
|
||||
_channelMap[i][j] = j;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,19 +378,13 @@ void MidiDriver_MT32GM::close() {
|
|||
|
||||
uint32 MidiDriver_MT32GM::property(int prop, uint32 param) {
|
||||
switch (prop) {
|
||||
case PROP_USER_VOLUME_SCALING:
|
||||
if (param == 0xFFFF)
|
||||
return _userVolumeScaling ? 1 : 0;
|
||||
_userVolumeScaling = param > 0;
|
||||
break;
|
||||
case PROP_MIDI_DATA_REVERSE_PANNING:
|
||||
if (param == 0xFFFF)
|
||||
return _midiDataReversePanning ? 1 : 0;
|
||||
_midiDataReversePanning = param > 0;
|
||||
break;
|
||||
default:
|
||||
MidiDriver::property(prop, param);
|
||||
break;
|
||||
return MidiDriver_Multisource::property(prop, param);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -871,100 +861,6 @@ void MidiDriver_MT32GM::stopAllNotes(bool stopSustainedNotes) {
|
|||
_activeNotesMutex.unlock();
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::startFade(uint16 duration, uint16 targetVolume) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
startFade(i, duration, targetVolume);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::startFade(uint8 source, uint16 duration, uint16 targetVolume) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_fadingMutex.lock();
|
||||
|
||||
_sources[source].fadePassedTime = 0;
|
||||
_sources[source].fadeStartVolume = _sources[source].volume;
|
||||
_sources[source].fadeEndVolume = targetVolume;
|
||||
_sources[source].fadeDuration = duration * 1000;
|
||||
|
||||
_fadingMutex.unlock();
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::abortFade(FadeAbortType abortType) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
abortFade(i, abortType);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::abortFade(uint8 source, FadeAbortType abortType) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
if (!isFading(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_fadingMutex.lock();
|
||||
|
||||
_sources[source].fadeDuration = 0;
|
||||
uint16 newSourceVolume;
|
||||
switch (abortType) {
|
||||
case FADE_ABORT_TYPE_END_VOLUME:
|
||||
newSourceVolume = _sources[source].fadeEndVolume;
|
||||
break;
|
||||
case FADE_ABORT_TYPE_START_VOLUME:
|
||||
newSourceVolume = _sources[source].fadeStartVolume;
|
||||
break;
|
||||
case FADE_ABORT_TYPE_CURRENT_VOLUME:
|
||||
default:
|
||||
_fadingMutex.unlock();
|
||||
return;
|
||||
}
|
||||
setSourceVolume(source, newSourceVolume);
|
||||
|
||||
_fadingMutex.unlock();
|
||||
}
|
||||
|
||||
bool MidiDriver_MT32GM::isFading() {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
if (isFading(i))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MidiDriver_MT32GM::isFading(uint8 source) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
return _sources[source].fadeDuration > 0;
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::updateFading() {
|
||||
Common::StackLock lock(_fadingMutex);
|
||||
|
||||
_fadeDelay -= _fadeDelay < _timerRate ? _fadeDelay : _timerRate;
|
||||
|
||||
bool updatedVolume = false;
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
|
||||
if (_sources[i].fadeDuration > 0) {
|
||||
_sources[i].fadePassedTime += _timerRate;
|
||||
|
||||
if (_sources[i].fadePassedTime >= _sources[i].fadeDuration) {
|
||||
// Fade has finished
|
||||
setSourceVolume(i, _sources[i].fadeEndVolume);
|
||||
updatedVolume = true;
|
||||
_sources[i].fadeDuration = 0;
|
||||
} else if (_fadeDelay == 0) {
|
||||
setSourceVolume(i, ((_sources[i].fadePassedTime * (_sources[i].fadeEndVolume - _sources[i].fadeStartVolume)) /
|
||||
_sources[i].fadeDuration) + _sources[i].fadeStartVolume);
|
||||
updatedVolume = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedVolume)
|
||||
_fadeDelay = FADING_DELAY;
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::clearSysExQueue() {
|
||||
Common::StackLock lock(_sysExQueueMutex);
|
||||
|
@ -1031,23 +927,23 @@ bool MidiDriver_MT32GM::allocateSourceChannels(uint8 source, uint8 numChannels)
|
|||
}
|
||||
// Clear the source channel mapping.
|
||||
if (i != MIDI_RHYTHM_CHANNEL)
|
||||
_sources[source].channelMap[i] = -1;
|
||||
_channelMap[source][i] = -1;
|
||||
}
|
||||
|
||||
_allocationMutex.unlock();
|
||||
|
||||
_sources[source].availableChannels = claimedChannels;
|
||||
_availableChannels[source] = claimedChannels;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int8 MidiDriver_MT32GM::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
||||
int8 outputChannel = _sources[source].channelMap[dataChannel];
|
||||
int8 outputChannel = _channelMap[source][dataChannel];
|
||||
if (outputChannel == -1) {
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
if ((_sources[source].availableChannels >> i) & 1) {
|
||||
_sources[source].availableChannels &= ~(1 << i);
|
||||
_sources[source].channelMap[dataChannel] = i;
|
||||
if ((_availableChannels[source] >> i) & 1) {
|
||||
_availableChannels[source] &= ~(1 << i);
|
||||
_channelMap[source][dataChannel] = i;
|
||||
outputChannel = i;
|
||||
break;
|
||||
}
|
||||
|
@ -1062,7 +958,7 @@ int8 MidiDriver_MT32GM::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
|||
void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
abortFade(source, FADE_ABORT_TYPE_END_VOLUME);
|
||||
MidiDriver_Multisource::deinitSource(source);
|
||||
|
||||
// Free channels which were used by this source.
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
|
@ -1072,102 +968,47 @@ void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
|||
if (_controlData[i]->source == source)
|
||||
_controlData[i]->source = -1;
|
||||
}
|
||||
_sources[source].availableChannels = 0xFFFF;
|
||||
_availableChannels[source] = 0xFFFF;
|
||||
// Reset the data to output channel mapping
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
_sources[source].channelMap[i] = i;
|
||||
_channelMap[source][i] = i;
|
||||
}
|
||||
|
||||
_activeNotesMutex.lock();
|
||||
|
||||
// Stop any active notes.
|
||||
for (int i = 0; i < _maximumActiveNotes; ++i) {
|
||||
if (_activeNotes[i].source == source) {
|
||||
if (_activeNotes[i].sustain) {
|
||||
// Turn off sustain
|
||||
controlChange(_activeNotes[i].channel, MIDI_CONTROLLER_SUSTAIN, 0x00, source, *_controlData[i]);
|
||||
} else {
|
||||
// Send note off
|
||||
noteOnOff(_activeNotes[i].channel, MIDI_COMMAND_NOTE_OFF, _activeNotes[i].note, 0x00, source, *_controlData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_activeNotesMutex.unlock();
|
||||
|
||||
// TODO Optionally reset some controllers to their
|
||||
// default values? Pitch wheel, volume, sustain...
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::setSourceType(SourceType type) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
setSourceType(i, type);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::setSourceType(uint8 source, SourceType type) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].type = type;
|
||||
|
||||
// Make sure music/sfx volume gets applied
|
||||
void MidiDriver_MT32GM::applySourceVolume(uint8 source) {
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
if (!isOutputChannelUsed(i))
|
||||
continue;
|
||||
|
||||
if (_controlData[i]->source == source)
|
||||
controlChange(i, MIDI_CONTROLLER_VOLUME, _controlData[i]->volume, source, *_controlData[i]);
|
||||
if (source == 0xFF || _controlData[i]->source == source)
|
||||
controlChange(i, MIDI_CONTROLLER_VOLUME, _controlData[i]->volume, _controlData[i]->source, *_controlData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::setSourceVolume(uint16 volume) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
setSourceVolume(i, volume);
|
||||
void MidiDriver_MT32GM::stopAllNotes(uint8 source, uint8 channel) {
|
||||
_activeNotesMutex.lock();
|
||||
|
||||
for (int i = 0; i < _maximumActiveNotes; ++i) {
|
||||
if ((source == 0xFF || _activeNotes[i].source == source) &&
|
||||
(channel == 0xFF || _activeNotes[i].channel == channel)) {
|
||||
if (_activeNotes[i].sustain) {
|
||||
// Turn off sustain
|
||||
controlChange(_activeNotes[i].channel, MIDI_CONTROLLER_SUSTAIN, 0x00, _activeNotes[i].source, *_controlData[i]);
|
||||
} else {
|
||||
// Send note off
|
||||
noteOnOff(_activeNotes[i].channel, MIDI_COMMAND_NOTE_OFF, _activeNotes[i].note, 0x00, _activeNotes[i].source, *_controlData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::setSourceVolume(uint8 source, uint16 volume) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].volume = volume;
|
||||
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
if (!isOutputChannelUsed(i))
|
||||
continue;
|
||||
|
||||
if (_controlData[i]->source == source)
|
||||
controlChange(i, MIDI_CONTROLLER_VOLUME, _controlData[i]->volume, source, *_controlData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::setSourceNeutralVolume(uint16 volume) {
|
||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||
setSourceNeutralVolume(i, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::setSourceNeutralVolume(uint8 source, uint16 volume) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
_sources[source].neutralVolume = volume;
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::syncSoundSettings() {
|
||||
_userMusicVolume = MIN(256, ConfMan.getInt("music_volume"));
|
||||
_userSfxVolume = MIN(256, ConfMan.getInt("sfx_volume"));
|
||||
_userMute = ConfMan.getBool("mute");
|
||||
|
||||
// Make sure music/sfx volume gets applied
|
||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||
if (!isOutputChannelUsed(i))
|
||||
continue;
|
||||
|
||||
controlChange(i, MIDI_CONTROLLER_VOLUME, _controlData[i]->volume, _controlData[i]->source, *_controlData[i]);
|
||||
}
|
||||
_activeNotesMutex.unlock();
|
||||
}
|
||||
|
||||
void MidiDriver_MT32GM::onTimer() {
|
||||
updateFading();
|
||||
MidiDriver_Multisource::onTimer();
|
||||
|
||||
_sysExQueueMutex.lock();
|
||||
|
||||
|
|
202
audio/mt32gm.h
202
audio/mt32gm.h
|
@ -24,6 +24,7 @@
|
|||
#define AUDIO_MT32GM_H
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/mididrv_ms.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/queue.h"
|
||||
|
||||
|
@ -60,18 +61,12 @@
|
|||
* or override the mapMT32InstrumentToGM and mapGMInstrumentToMT32 functions
|
||||
* for more advanced mapping algorithms.
|
||||
*
|
||||
* - User volume settings
|
||||
* The driver will scale the MIDI channel volume using the user specified
|
||||
* volume settings. Just call syncSoundSettings when the user has changed the
|
||||
* volume settings. Set the USER_VOLUME_SCALING property to false to disable
|
||||
* this functionality.
|
||||
*
|
||||
* - Reverse stereo
|
||||
* If the game has MIDI data with reversed stereo compared to the targeted
|
||||
* output device, set the MIDI_DATA_REVERSE_PANNING property to reverse
|
||||
* stereo. The driver wil automatically reverse stereo when MT-32 data is
|
||||
* sent to a GM/GS device or the other way around.
|
||||
*
|
||||
*
|
||||
* - Correct Roland GS bank and drumkit selects
|
||||
* Some games' MIDI data relies on a feature of the Roland SC-55 MIDI module
|
||||
* which automatically corrects invalid bank selects and drumkit program
|
||||
|
@ -88,19 +83,18 @@
|
|||
* necessary amount of time for the MIDI device to process the message.
|
||||
* Use clearSysExQueue to remove all messages from the queue, in case device
|
||||
* initialization has to be aborted.
|
||||
*
|
||||
*
|
||||
* - Multiple MIDI sources
|
||||
* If the game plays multiple streams of MIDI data at the same time, each
|
||||
* stream can be marked with a source number. This enables the following
|
||||
* features:
|
||||
* feature:
|
||||
* - Channel mapping
|
||||
* If multiple sources use the same MIDI channels, the driver can map the
|
||||
* data channels to different output channels to avoid conflicts. Use
|
||||
* allocateSourceChannels to allocate output channels to a source. The
|
||||
* data channels are automatically mapped to the allocated output channels
|
||||
* during playback. The allocated channels are freed when the source is
|
||||
* deinitialized; this is done automatically when an End Of Track MIDI event
|
||||
* is received, or manually by calling deinitSource.
|
||||
* deinitialized.
|
||||
* If you only have one source of MIDI data or the sources do not use
|
||||
* conflicting channels, you do not need to allocate channels - the channels
|
||||
* in the MIDI data will be used directly. If you do use this feature, you
|
||||
|
@ -114,42 +108,9 @@
|
|||
* using the allocateChannel function and MidiChannel objects. These two
|
||||
* methods are not coordinated in any way, so don't use both at the same
|
||||
* time.
|
||||
* - Music/SFX volume
|
||||
* Using setSourceType a MIDI source can be designated as music or sound
|
||||
* effects. The driver will then apply the appropriate user volume setting
|
||||
* to the MIDI channel volume. This setting sticks after deinitializing a
|
||||
* source, so if you use the same source numbers for the same types of MIDI
|
||||
* data, you don't need to set the source type repeatedly. The default setup
|
||||
* is music for source 0 and SFX for sources 1 and higher.
|
||||
* - Source volume
|
||||
* If the game changes the volume of the MIDI playback, you can use
|
||||
* setSourceVolume to set the volume level for a source. The driver will
|
||||
* then adjust the current MIDI channel volume and any received MIDI volume
|
||||
* controller messages. Use setSourceNeutralVolume to set the neutral volume
|
||||
* for a source (MIDI volume is not changed when source volume is at this
|
||||
* level; if it is lower or higher, MIDI volume is reduced or increased).
|
||||
* - Volume fading
|
||||
* If the game needs to gradually change the volume of the MIDI playback
|
||||
* (typically for a fade-out), you can use the startFade function. You can
|
||||
* check the status of the fade using isFading, and abort a fade using
|
||||
* abortFade. An active fade is automatically aborted when the fading source
|
||||
* is deinitialized.
|
||||
* The fading functionality uses the source volume, so you should not set
|
||||
* this while a fade is active. After the fade the source volume will remain
|
||||
* at the target level, so if you perform f.e. a fade-out, the source volume
|
||||
* will remain at 0. If you want to start playback again using this source,
|
||||
* use setSourceVolume to set the correct playback volume.
|
||||
* Note that when you stop MIDI playback, notes will not be immediately
|
||||
* silent but will gradually die out ("release"). So if you fade out a
|
||||
* source, stop playback, and immediately reset the source volume, the
|
||||
* note release will be audible. It is recommended to wait about 0.5s
|
||||
* before resetting the source volume.
|
||||
*/
|
||||
class MidiDriver_MT32GM : public MidiDriver {
|
||||
class MidiDriver_MT32GM : public MidiDriver_Multisource {
|
||||
public:
|
||||
static const uint8 MAXIMUM_SOURCES = 10;
|
||||
static const uint16 DEFAULT_SOURCE_NEUTRAL_VOLUME = 255;
|
||||
|
||||
static const byte MT32_DEFAULT_INSTRUMENTS[8];
|
||||
static const byte MT32_DEFAULT_PANNING[8];
|
||||
static const uint8 MT32_DEFAULT_CHANNEL_VOLUME = 98;
|
||||
|
@ -161,21 +122,6 @@ protected:
|
|||
static const uint8 MAXIMUM_MT32_ACTIVE_NOTES = 48;
|
||||
static const uint8 MAXIMUM_GM_ACTIVE_NOTES = 96;
|
||||
|
||||
// Timeout between updates of the channel volume for fades (25ms)
|
||||
static const uint16 FADING_DELAY = 25 * 1000;
|
||||
|
||||
public:
|
||||
enum SourceType {
|
||||
SOURCE_TYPE_UNDEFINED,
|
||||
SOURCE_TYPE_MUSIC,
|
||||
SOURCE_TYPE_SFX
|
||||
};
|
||||
|
||||
enum FadeAbortType {
|
||||
FADE_ABORT_TYPE_END_VOLUME,
|
||||
FADE_ABORT_TYPE_CURRENT_VOLUME,
|
||||
FADE_ABORT_TYPE_START_VOLUME
|
||||
};
|
||||
protected:
|
||||
/**
|
||||
* This stores the values of the MIDI controllers for
|
||||
|
@ -289,6 +235,11 @@ protected:
|
|||
};
|
||||
|
||||
public:
|
||||
// Callback hooked up to the driver wrapped by the MIDI driver
|
||||
// object. Executes onTimer and the external callback set by
|
||||
// the setTimerCallback function.
|
||||
static void timerCallback(void *data);
|
||||
|
||||
MidiDriver_MT32GM(MusicType midiType);
|
||||
~MidiDriver_MT32GM();
|
||||
|
||||
|
@ -340,45 +291,6 @@ public:
|
|||
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
|
||||
|
||||
void stopAllNotes(bool stopSustainedNotes = false) override;
|
||||
/**
|
||||
* Starts a fade for all sources.
|
||||
* See the source-specific startFade function for more information.
|
||||
*/
|
||||
void startFade(uint16 duration, uint16 targetVolume);
|
||||
/**
|
||||
* Starts a fade for a source. This will linearly increase or decrease the
|
||||
* volume of the MIDI channels used by the source to the specified target
|
||||
* value over the specified length of time.
|
||||
*
|
||||
* @param source The source to fade
|
||||
* @param duration The fade duration in ms
|
||||
* @param targetVolume The volume at the end of the fade
|
||||
*/
|
||||
void startFade(uint8 source, uint16 duration, uint16 targetVolume);
|
||||
/**
|
||||
* Aborts any active fades for all sources.
|
||||
* See the source-specific abortFade function for more information.
|
||||
*/
|
||||
void abortFade(FadeAbortType abortType = FADE_ABORT_TYPE_END_VOLUME);
|
||||
/**
|
||||
* Aborts an active fade for a source. Depending on the abort type, the
|
||||
* volume will remain at the current value or be set to the start or end
|
||||
* volume. If there is no active fade for the specified source, this
|
||||
* function does nothing.
|
||||
*
|
||||
* @param source The source that should have its fade aborted
|
||||
* @param abortType How to set the volume when aborting the fade (default:
|
||||
* set to the target fade volume).
|
||||
*/
|
||||
void abortFade(uint8 source, FadeAbortType abortType = FADE_ABORT_TYPE_END_VOLUME);
|
||||
/**
|
||||
* Returns true if any source has an active fade.
|
||||
*/
|
||||
bool isFading();
|
||||
/**
|
||||
* Returns true if the specified source has an active fade.
|
||||
*/
|
||||
bool isFading(uint8 source);
|
||||
/**
|
||||
* Removes all SysEx messages in the SysEx queue.
|
||||
*/
|
||||
|
@ -409,49 +321,7 @@ public:
|
|||
* Deinitializes a source. This will abort active fades, free any output
|
||||
* channels allocated to the source and stop active notes.
|
||||
*/
|
||||
virtual void deinitSource(uint8 source);
|
||||
/**
|
||||
* Sets the type for all sources (music or SFX).
|
||||
*/
|
||||
void setSourceType(SourceType type);
|
||||
/**
|
||||
* Sets the type for a specific sources (music or SFX).
|
||||
*/
|
||||
void setSourceType(uint8 source, SourceType type);
|
||||
/**
|
||||
* Sets the volume for all sources.
|
||||
*/
|
||||
void setSourceVolume(uint16 volume);
|
||||
/**
|
||||
* Sets the volume for this source. The volume values in the MIDI data sent
|
||||
* by this source will be scaled by the source volume.
|
||||
*/
|
||||
virtual void setSourceVolume(uint8 source, uint16 volume);
|
||||
void setSourceNeutralVolume(uint16 volume);
|
||||
/**
|
||||
* Sets the neutral volume for this source. If the source volume is at this
|
||||
* level, the volume values in the MIDI data sent by this source will not
|
||||
* be changed. At source volumes below or above this value, the MIDI volume
|
||||
* values will be decreased or increased accordingly.
|
||||
*/
|
||||
void setSourceNeutralVolume(uint8 source, uint16 volume);
|
||||
/**
|
||||
* Applies the user volume settings to the MIDI driver. MIDI channel volumes
|
||||
* will be scaled using the user volume.
|
||||
* This function must be called by the engine when the user has changed the
|
||||
* volume settings.
|
||||
*/
|
||||
void syncSoundSettings();
|
||||
|
||||
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) override {
|
||||
_timer_param = timer_param;
|
||||
_timer_proc = timer_proc;
|
||||
}
|
||||
/**
|
||||
* Runs the MIDI driver's timer related functionality. Will update volume
|
||||
* fades and sends messages from the SysEx queue if necessary.
|
||||
*/
|
||||
virtual void onTimer();
|
||||
void deinitSource(uint8 source) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
@ -576,11 +446,6 @@ protected:
|
|||
*/
|
||||
byte correctInstrumentBank(byte outputChannel, byte patchId);
|
||||
|
||||
/**
|
||||
* Processes active fades and sets new volume values if necessary.
|
||||
*/
|
||||
void updateFading();
|
||||
|
||||
/**
|
||||
* Returns the MIDI output channel mapped to the specified data channel.
|
||||
* If the data channel has not been mapped yet, a new mapping to one of the
|
||||
|
@ -592,7 +457,14 @@ protected:
|
|||
*/
|
||||
virtual int8 mapSourceChannel(uint8 source, uint8 dataChannel);
|
||||
|
||||
Common::Mutex _fadingMutex; // For operations on fades
|
||||
void applySourceVolume(uint8 source) override;
|
||||
void stopAllNotes(uint8 source, uint8 channel) override;
|
||||
/**
|
||||
* Runs the MIDI driver's timer related functionality. Will update volume
|
||||
* fades and sends messages from the SysEx queue if necessary.
|
||||
*/
|
||||
void onTimer() override;
|
||||
|
||||
Common::Mutex _allocationMutex; // For operations on MIDI channel allocation
|
||||
Common::Mutex _activeNotesMutex; // For operations on active notes registration
|
||||
|
||||
|
@ -616,26 +488,18 @@ protected:
|
|||
// True if GS percussion channel volume should be scaled to match MT-32 volume.
|
||||
bool _scaleGSPercussionVolumeToMT32;
|
||||
|
||||
// True if the driver should scale MIDI channel volume to the user specified
|
||||
// volume settings.
|
||||
bool _userVolumeScaling;
|
||||
|
||||
// User volume settings
|
||||
uint16 _userMusicVolume;
|
||||
uint16 _userSfxVolume;
|
||||
bool _userMute;
|
||||
|
||||
// True if this MIDI driver has been opened.
|
||||
bool _isOpen;
|
||||
// Bitmask of the MIDI channels in use by the output device.
|
||||
uint16 _outputChannelMask;
|
||||
int _baseFreq;
|
||||
uint32 _timerRate;
|
||||
|
||||
// stores the controller values for each MIDI channel
|
||||
MidiChannelControlData *_controlData[MIDI_CHANNEL_COUNT];
|
||||
|
||||
MidiSource _sources[MAXIMUM_SOURCES];
|
||||
// The mapping of MIDI data channels to output channels for each source.
|
||||
int8 _channelMap[MAXIMUM_SOURCES][MIDI_CHANNEL_COUNT];
|
||||
// Bitmask specifying which MIDI channels are available for use by each source.
|
||||
uint16 _availableChannels[MAXIMUM_SOURCES];
|
||||
|
||||
// Maps used for MT-32 <> GM instrument mapping. Set these to an alternate
|
||||
// 128 byte array to customize the mapping.
|
||||
|
@ -646,9 +510,6 @@ protected:
|
|||
// Active note registration
|
||||
ActiveNote *_activeNotes;
|
||||
|
||||
// The number of microseconds to wait before the next fading step.
|
||||
uint16 _fadeDelay;
|
||||
|
||||
// The current number of microseconds that have to elapse before the next
|
||||
// SysEx message can be sent.
|
||||
uint32 _sysExDelay;
|
||||
|
@ -656,21 +517,6 @@ protected:
|
|||
Common::Queue<SysExData> _sysExQueue;
|
||||
// Mutex for write access to the SysEx queue.
|
||||
Common::Mutex _sysExQueueMutex;
|
||||
|
||||
// External timer callback
|
||||
void *_timer_param;
|
||||
Common::TimerManager::TimerProc _timer_proc;
|
||||
|
||||
public:
|
||||
// Callback hooked up to the driver wrapped by the MIDI driver
|
||||
// object. Executes onTimer and the external callback set by
|
||||
// the setTimerCallback function.
|
||||
static void timerCallback(void *data) {
|
||||
MidiDriver_MT32GM *driver = (MidiDriver_MT32GM *)data;
|
||||
driver->onTimer();
|
||||
if (driver->_timer_proc && driver->_timer_param)
|
||||
driver->_timer_proc(driver->_timer_param);
|
||||
}
|
||||
};
|
||||
/** @} */
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue