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.
|
* Automatically executed when an End Of Track meta event is received.
|
||||||
*/
|
*/
|
||||||
void deinitSource(uint8 source) override;
|
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;
|
void stopAllNotes(bool stopSustainedNotes = false) override;
|
||||||
|
|
||||||
|
@ -148,6 +140,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void initControlData() override;
|
void initControlData() override;
|
||||||
void initMidiDevice() override;
|
void initMidiDevice() override;
|
||||||
|
void applySourceVolume(uint8 source) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void writeRhythmSetup(byte note, byte customTimbreId);
|
void writeRhythmSetup(byte note, byte customTimbreId);
|
||||||
|
|
|
@ -55,11 +55,6 @@ MidiDriver_Miles_Midi::MidiDriver_Miles_Midi(MusicType midiType, MilesMT32Instru
|
||||||
_instrumentTablePtr = instrumentTablePtr;
|
_instrumentTablePtr = instrumentTablePtr;
|
||||||
_instrumentTableCount = instrumentTableCount;
|
_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);
|
setSourceNeutralVolume(MILES_DEFAULT_SOURCE_NEUTRAL_VOLUME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +136,7 @@ void MidiDriver_Miles_Midi::send(int8 source, uint32 b) {
|
||||||
|
|
||||||
byte command = b & 0xf0;
|
byte command = b & 0xf0;
|
||||||
byte dataChannel = b & 0xf;
|
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];
|
MidiChannelEntry &outputChannelEntry = _midiChannels[outputChannel];
|
||||||
// Only send the message to the MIDI device if the channel is not locked or
|
// 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].locked = true;
|
||||||
_midiChannels[lockChannel].lockDataChannel = dataChannel;
|
_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
|
// Copy current controller values so they can be restored when unlocking the channel
|
||||||
*_midiChannels[lockChannel].unlockData = *_midiChannels[lockChannel].currentData;
|
*_midiChannels[lockChannel].unlockData = *_midiChannels[lockChannel].currentData;
|
||||||
_midiChannels[lockChannel].currentData->source = source;
|
_midiChannels[lockChannel].currentData->source = source;
|
||||||
|
@ -424,7 +419,7 @@ void MidiDriver_Miles_Midi::unlockChannel(uint8 outputChannel) {
|
||||||
|
|
||||||
// Unlock the channel
|
// Unlock the channel
|
||||||
channel.locked = false;
|
channel.locked = false;
|
||||||
_sources[channel.currentData->source].channelMap[channel.lockDataChannel] = channel.lockDataChannel;
|
_channelMap[channel.currentData->source][channel.lockDataChannel] = channel.lockDataChannel;
|
||||||
channel.lockDataChannel = -1;
|
channel.lockDataChannel = -1;
|
||||||
channel.currentData->source = channel.unlockData->source;
|
channel.currentData->source = channel.unlockData->source;
|
||||||
|
|
||||||
|
@ -874,11 +869,7 @@ void MidiDriver_Miles_Midi::deinitSource(uint8 source) {
|
||||||
MidiDriver_MT32GM::deinitSource(source);
|
MidiDriver_MT32GM::deinitSource(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiDriver_Miles_Midi::setSourceVolume(uint8 source, uint16 volume) {
|
void MidiDriver_Miles_Midi::applySourceVolume(uint8 source) {
|
||||||
assert(source < MAXIMUM_SOURCES);
|
|
||||||
|
|
||||||
_sources[source].volume = volume;
|
|
||||||
|
|
||||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||||
if (!isOutputChannelUsed(i))
|
if (!isOutputChannelUsed(i))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -6,6 +6,7 @@ MODULE_OBJS := \
|
||||||
audiostream.o \
|
audiostream.o \
|
||||||
fmopl.o \
|
fmopl.o \
|
||||||
mididrv.o \
|
mididrv.o \
|
||||||
|
mididrv_ms.o \
|
||||||
midiparser_qt.o \
|
midiparser_qt.o \
|
||||||
midiparser_smf.o \
|
midiparser_smf.o \
|
||||||
midiparser_xmidi.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,
|
// 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.
|
// 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::MT32_DEFAULT_CHANNEL_VOLUME;
|
||||||
const uint8 MidiDriver_MT32GM::GM_DEFAULT_CHANNEL_VOLUME;
|
const uint8 MidiDriver_MT32GM::GM_DEFAULT_CHANNEL_VOLUME;
|
||||||
const uint8 MidiDriver_MT32GM::MAXIMUM_MT32_ACTIVE_NOTES;
|
const uint8 MidiDriver_MT32GM::MAXIMUM_MT32_ACTIVE_NOTES;
|
||||||
const uint8 MidiDriver_MT32GM::MAXIMUM_GM_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.
|
// These are the power-on default instruments of the Roland MT-32 family.
|
||||||
const byte MidiDriver_MT32GM::MT32_DEFAULT_INSTRUMENTS[8] = {
|
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)
|
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) :
|
MidiDriver_MT32GM::MidiDriver_MT32GM(MusicType midiType) :
|
||||||
_driver(0),
|
_driver(0),
|
||||||
_nativeMT32(false),
|
_nativeMT32(false),
|
||||||
|
@ -85,18 +90,10 @@ MidiDriver_MT32GM::MidiDriver_MT32GM(MusicType midiType) :
|
||||||
_midiDataReversePanning(false),
|
_midiDataReversePanning(false),
|
||||||
_midiDeviceReversePanning(false),
|
_midiDeviceReversePanning(false),
|
||||||
_scaleGSPercussionVolumeToMT32(false),
|
_scaleGSPercussionVolumeToMT32(false),
|
||||||
_userVolumeScaling(true),
|
|
||||||
_userMusicVolume(192),
|
|
||||||
_userSfxVolume(192),
|
|
||||||
_userMute(false),
|
|
||||||
_isOpen(false),
|
_isOpen(false),
|
||||||
_outputChannelMask(65535), // Channels 1-16
|
_outputChannelMask(65535), // Channels 1-16
|
||||||
_baseFreq(250),
|
_baseFreq(250),
|
||||||
_timerRate(0),
|
_sysExDelay(0) {
|
||||||
_fadeDelay(0),
|
|
||||||
_sysExDelay(0),
|
|
||||||
_timer_param(0),
|
|
||||||
_timer_proc(0) {
|
|
||||||
memset(_controlData, 0, sizeof(_controlData));
|
memset(_controlData, 0, sizeof(_controlData));
|
||||||
|
|
||||||
switch (midiType) {
|
switch (midiType) {
|
||||||
|
@ -113,11 +110,10 @@ MidiDriver_MT32GM::MidiDriver_MT32GM(MusicType midiType) :
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
||||||
// Default source type: 0 = music, 1+ = SFX
|
_availableChannels[i] = 0;
|
||||||
_sources[i].type = i == 0 ? SOURCE_TYPE_MUSIC : SOURCE_TYPE_SFX;
|
|
||||||
// Default MIDI channel mapping: data channel == output channel
|
// Default MIDI channel mapping: data channel == output channel
|
||||||
for (int j = 0; j < MIDI_CHANNEL_COUNT; ++j) {
|
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) {
|
uint32 MidiDriver_MT32GM::property(int prop, uint32 param) {
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
case PROP_USER_VOLUME_SCALING:
|
|
||||||
if (param == 0xFFFF)
|
|
||||||
return _userVolumeScaling ? 1 : 0;
|
|
||||||
_userVolumeScaling = param > 0;
|
|
||||||
break;
|
|
||||||
case PROP_MIDI_DATA_REVERSE_PANNING:
|
case PROP_MIDI_DATA_REVERSE_PANNING:
|
||||||
if (param == 0xFFFF)
|
if (param == 0xFFFF)
|
||||||
return _midiDataReversePanning ? 1 : 0;
|
return _midiDataReversePanning ? 1 : 0;
|
||||||
_midiDataReversePanning = param > 0;
|
_midiDataReversePanning = param > 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
MidiDriver::property(prop, param);
|
return MidiDriver_Multisource::property(prop, param);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -871,100 +861,6 @@ void MidiDriver_MT32GM::stopAllNotes(bool stopSustainedNotes) {
|
||||||
_activeNotesMutex.unlock();
|
_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() {
|
void MidiDriver_MT32GM::clearSysExQueue() {
|
||||||
Common::StackLock lock(_sysExQueueMutex);
|
Common::StackLock lock(_sysExQueueMutex);
|
||||||
|
@ -1031,23 +927,23 @@ bool MidiDriver_MT32GM::allocateSourceChannels(uint8 source, uint8 numChannels)
|
||||||
}
|
}
|
||||||
// Clear the source channel mapping.
|
// Clear the source channel mapping.
|
||||||
if (i != MIDI_RHYTHM_CHANNEL)
|
if (i != MIDI_RHYTHM_CHANNEL)
|
||||||
_sources[source].channelMap[i] = -1;
|
_channelMap[source][i] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_allocationMutex.unlock();
|
_allocationMutex.unlock();
|
||||||
|
|
||||||
_sources[source].availableChannels = claimedChannels;
|
_availableChannels[source] = claimedChannels;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int8 MidiDriver_MT32GM::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
int8 MidiDriver_MT32GM::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
||||||
int8 outputChannel = _sources[source].channelMap[dataChannel];
|
int8 outputChannel = _channelMap[source][dataChannel];
|
||||||
if (outputChannel == -1) {
|
if (outputChannel == -1) {
|
||||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||||
if ((_sources[source].availableChannels >> i) & 1) {
|
if ((_availableChannels[source] >> i) & 1) {
|
||||||
_sources[source].availableChannels &= ~(1 << i);
|
_availableChannels[source] &= ~(1 << i);
|
||||||
_sources[source].channelMap[dataChannel] = i;
|
_channelMap[source][dataChannel] = i;
|
||||||
outputChannel = i;
|
outputChannel = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1062,7 +958,7 @@ int8 MidiDriver_MT32GM::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
||||||
void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
||||||
assert(source < MAXIMUM_SOURCES);
|
assert(source < MAXIMUM_SOURCES);
|
||||||
|
|
||||||
abortFade(source, FADE_ABORT_TYPE_END_VOLUME);
|
MidiDriver_Multisource::deinitSource(source);
|
||||||
|
|
||||||
// Free channels which were used by this source.
|
// Free channels which were used by this source.
|
||||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||||
|
@ -1072,102 +968,47 @@ void MidiDriver_MT32GM::deinitSource(uint8 source) {
|
||||||
if (_controlData[i]->source == source)
|
if (_controlData[i]->source == source)
|
||||||
_controlData[i]->source = -1;
|
_controlData[i]->source = -1;
|
||||||
}
|
}
|
||||||
_sources[source].availableChannels = 0xFFFF;
|
_availableChannels[source] = 0xFFFF;
|
||||||
// Reset the data to output channel mapping
|
// Reset the data to output channel mapping
|
||||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
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
|
// TODO Optionally reset some controllers to their
|
||||||
// default values? Pitch wheel, volume, sustain...
|
// default values? Pitch wheel, volume, sustain...
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiDriver_MT32GM::setSourceType(SourceType type) {
|
void MidiDriver_MT32GM::applySourceVolume(uint8 source) {
|
||||||
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
|
|
||||||
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
for (int i = 0; i < MIDI_CHANNEL_COUNT; ++i) {
|
||||||
if (!isOutputChannelUsed(i))
|
if (!isOutputChannelUsed(i))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (_controlData[i]->source == source)
|
if (source == 0xFF || _controlData[i]->source == source)
|
||||||
controlChange(i, MIDI_CONTROLLER_VOLUME, _controlData[i]->volume, source, *_controlData[i]);
|
controlChange(i, MIDI_CONTROLLER_VOLUME, _controlData[i]->volume, _controlData[i]->source, *_controlData[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiDriver_MT32GM::setSourceVolume(uint16 volume) {
|
void MidiDriver_MT32GM::stopAllNotes(uint8 source, uint8 channel) {
|
||||||
for (int i = 0; i < MAXIMUM_SOURCES; ++i) {
|
_activeNotesMutex.lock();
|
||||||
setSourceVolume(i, volume);
|
|
||||||
|
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) {
|
_activeNotesMutex.unlock();
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiDriver_MT32GM::onTimer() {
|
void MidiDriver_MT32GM::onTimer() {
|
||||||
updateFading();
|
MidiDriver_Multisource::onTimer();
|
||||||
|
|
||||||
_sysExQueueMutex.lock();
|
_sysExQueueMutex.lock();
|
||||||
|
|
||||||
|
|
202
audio/mt32gm.h
202
audio/mt32gm.h
|
@ -24,6 +24,7 @@
|
||||||
#define AUDIO_MT32GM_H
|
#define AUDIO_MT32GM_H
|
||||||
|
|
||||||
#include "audio/mididrv.h"
|
#include "audio/mididrv.h"
|
||||||
|
#include "audio/mididrv_ms.h"
|
||||||
#include "common/mutex.h"
|
#include "common/mutex.h"
|
||||||
#include "common/queue.h"
|
#include "common/queue.h"
|
||||||
|
|
||||||
|
@ -60,18 +61,12 @@
|
||||||
* or override the mapMT32InstrumentToGM and mapGMInstrumentToMT32 functions
|
* or override the mapMT32InstrumentToGM and mapGMInstrumentToMT32 functions
|
||||||
* for more advanced mapping algorithms.
|
* 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
|
* - Reverse stereo
|
||||||
* If the game has MIDI data with reversed stereo compared to the targeted
|
* If the game has MIDI data with reversed stereo compared to the targeted
|
||||||
* output device, set the MIDI_DATA_REVERSE_PANNING property to reverse
|
* output device, set the MIDI_DATA_REVERSE_PANNING property to reverse
|
||||||
* stereo. The driver wil automatically reverse stereo when MT-32 data is
|
* stereo. The driver wil automatically reverse stereo when MT-32 data is
|
||||||
* sent to a GM/GS device or the other way around.
|
* sent to a GM/GS device or the other way around.
|
||||||
*
|
*
|
||||||
* - Correct Roland GS bank and drumkit selects
|
* - Correct Roland GS bank and drumkit selects
|
||||||
* Some games' MIDI data relies on a feature of the Roland SC-55 MIDI module
|
* Some games' MIDI data relies on a feature of the Roland SC-55 MIDI module
|
||||||
* which automatically corrects invalid bank selects and drumkit program
|
* 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.
|
* necessary amount of time for the MIDI device to process the message.
|
||||||
* Use clearSysExQueue to remove all messages from the queue, in case device
|
* Use clearSysExQueue to remove all messages from the queue, in case device
|
||||||
* initialization has to be aborted.
|
* initialization has to be aborted.
|
||||||
*
|
*
|
||||||
* - Multiple MIDI sources
|
* - Multiple MIDI sources
|
||||||
* If the game plays multiple streams of MIDI data at the same time, each
|
* 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
|
* stream can be marked with a source number. This enables the following
|
||||||
* features:
|
* feature:
|
||||||
* - Channel mapping
|
* - Channel mapping
|
||||||
* If multiple sources use the same MIDI channels, the driver can map the
|
* If multiple sources use the same MIDI channels, the driver can map the
|
||||||
* data channels to different output channels to avoid conflicts. Use
|
* data channels to different output channels to avoid conflicts. Use
|
||||||
* allocateSourceChannels to allocate output channels to a source. The
|
* allocateSourceChannels to allocate output channels to a source. The
|
||||||
* data channels are automatically mapped to the allocated output channels
|
* data channels are automatically mapped to the allocated output channels
|
||||||
* during playback. The allocated channels are freed when the source is
|
* during playback. The allocated channels are freed when the source is
|
||||||
* deinitialized; this is done automatically when an End Of Track MIDI event
|
* deinitialized.
|
||||||
* is received, or manually by calling deinitSource.
|
|
||||||
* If you only have one source of MIDI data or the sources do not use
|
* 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
|
* 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
|
* 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
|
* using the allocateChannel function and MidiChannel objects. These two
|
||||||
* methods are not coordinated in any way, so don't use both at the same
|
* methods are not coordinated in any way, so don't use both at the same
|
||||||
* time.
|
* 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:
|
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_INSTRUMENTS[8];
|
||||||
static const byte MT32_DEFAULT_PANNING[8];
|
static const byte MT32_DEFAULT_PANNING[8];
|
||||||
static const uint8 MT32_DEFAULT_CHANNEL_VOLUME = 98;
|
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_MT32_ACTIVE_NOTES = 48;
|
||||||
static const uint8 MAXIMUM_GM_ACTIVE_NOTES = 96;
|
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:
|
protected:
|
||||||
/**
|
/**
|
||||||
* This stores the values of the MIDI controllers for
|
* This stores the values of the MIDI controllers for
|
||||||
|
@ -289,6 +235,11 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
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(MusicType midiType);
|
||||||
~MidiDriver_MT32GM();
|
~MidiDriver_MT32GM();
|
||||||
|
|
||||||
|
@ -340,45 +291,6 @@ public:
|
||||||
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
|
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
|
||||||
|
|
||||||
void stopAllNotes(bool stopSustainedNotes = false) 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.
|
* Removes all SysEx messages in the SysEx queue.
|
||||||
*/
|
*/
|
||||||
|
@ -409,49 +321,7 @@ public:
|
||||||
* Deinitializes a source. This will abort active fades, free any output
|
* Deinitializes a source. This will abort active fades, free any output
|
||||||
* channels allocated to the source and stop active notes.
|
* channels allocated to the source and stop active notes.
|
||||||
*/
|
*/
|
||||||
virtual void deinitSource(uint8 source);
|
void deinitSource(uint8 source) override;
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
|
@ -576,11 +446,6 @@ protected:
|
||||||
*/
|
*/
|
||||||
byte correctInstrumentBank(byte outputChannel, byte patchId);
|
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.
|
* 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
|
* 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);
|
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 _allocationMutex; // For operations on MIDI channel allocation
|
||||||
Common::Mutex _activeNotesMutex; // For operations on active notes registration
|
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.
|
// True if GS percussion channel volume should be scaled to match MT-32 volume.
|
||||||
bool _scaleGSPercussionVolumeToMT32;
|
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.
|
// True if this MIDI driver has been opened.
|
||||||
bool _isOpen;
|
bool _isOpen;
|
||||||
// Bitmask of the MIDI channels in use by the output device.
|
// Bitmask of the MIDI channels in use by the output device.
|
||||||
uint16 _outputChannelMask;
|
uint16 _outputChannelMask;
|
||||||
int _baseFreq;
|
int _baseFreq;
|
||||||
uint32 _timerRate;
|
|
||||||
|
|
||||||
// stores the controller values for each MIDI channel
|
// stores the controller values for each MIDI channel
|
||||||
MidiChannelControlData *_controlData[MIDI_CHANNEL_COUNT];
|
MidiChannelControlData *_controlData[MIDI_CHANNEL_COUNT];
|
||||||
|
// The mapping of MIDI data channels to output channels for each source.
|
||||||
MidiSource _sources[MAXIMUM_SOURCES];
|
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
|
// Maps used for MT-32 <> GM instrument mapping. Set these to an alternate
|
||||||
// 128 byte array to customize the mapping.
|
// 128 byte array to customize the mapping.
|
||||||
|
@ -646,9 +510,6 @@ protected:
|
||||||
// Active note registration
|
// Active note registration
|
||||||
ActiveNote *_activeNotes;
|
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
|
// The current number of microseconds that have to elapse before the next
|
||||||
// SysEx message can be sent.
|
// SysEx message can be sent.
|
||||||
uint32 _sysExDelay;
|
uint32 _sysExDelay;
|
||||||
|
@ -656,21 +517,6 @@ protected:
|
||||||
Common::Queue<SysExData> _sysExQueue;
|
Common::Queue<SysExData> _sysExQueue;
|
||||||
// Mutex for write access to the SysEx queue.
|
// Mutex for write access to the SysEx queue.
|
||||||
Common::Mutex _sysExQueueMutex;
|
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
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue