/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #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::ControllerDefaults::ControllerDefaults() : // The -1 value indicates no default value should be set on the controller. instrumentBank(-1), drumkit(-1), channelPressure(-1), pitchBend(-1), modulation(-1), volume(-1), panning(-1), expression(-1), sustain(-1), rpn(-1), pitchBendSensitivity(-1) { Common::fill(program, program + ARRAYSIZE(program), -1); } MidiDriver_Multisource::MidiDriver_Multisource() : _instrumentRemapping(nullptr), _userVolumeScaling(false), _userMusicVolume(192), _userSfxVolume(192), _userMute(false), _timerRate(0), _fadeDelay(0), _timer_param(nullptr), _timer_proc(nullptr) { 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::setControllerDefault(ControllerDefaultType type) { // Call the overload with the General MIDI default for the controller. switch (type) { case CONTROLLER_DEFAULT_PITCH_BEND: setControllerDefault(type, MIDI_PITCH_BEND_DEFAULT); break; case CONTROLLER_DEFAULT_VOLUME: setControllerDefault(type, 100); break; case CONTROLLER_DEFAULT_PANNING: setControllerDefault(type, MIDI_PANNING_DEFAULT); break; case CONTROLLER_DEFAULT_EXPRESSION: setControllerDefault(type, MIDI_EXPRESSION_DEFAULT); break; case CONTROLLER_DEFAULT_RPN: setControllerDefault(type, MIDI_RPN_NULL); break; case CONTROLLER_DEFAULT_PITCH_BEND_SENSITIVITY: setControllerDefault(type, GM_PITCH_BEND_SENSITIVITY_DEFAULT); break; case CONTROLLER_DEFAULT_PROGRAM: case CONTROLLER_DEFAULT_INSTRUMENT_BANK: case CONTROLLER_DEFAULT_DRUMKIT: case CONTROLLER_DEFAULT_CHANNEL_PRESSURE: case CONTROLLER_DEFAULT_MODULATION: case CONTROLLER_DEFAULT_SUSTAIN: default: setControllerDefault(type, 0); break; } } void MidiDriver_Multisource::setControllerDefault(ControllerDefaultType type, int16 value) { // Set the specified default value on _controllerDefaults on the field // corresponding to the specified controller. switch (type) { case CONTROLLER_DEFAULT_PROGRAM: Common::fill(_controllerDefaults.program, _controllerDefaults.program + ARRAYSIZE(_controllerDefaults.program), value); break; case CONTROLLER_DEFAULT_INSTRUMENT_BANK: _controllerDefaults.instrumentBank = value; break; case CONTROLLER_DEFAULT_DRUMKIT: _controllerDefaults.drumkit = value; break; case CONTROLLER_DEFAULT_CHANNEL_PRESSURE: _controllerDefaults.channelPressure = value; break; case CONTROLLER_DEFAULT_PITCH_BEND: _controllerDefaults.pitchBend = value; break; case CONTROLLER_DEFAULT_MODULATION: _controllerDefaults.modulation = value; break; case CONTROLLER_DEFAULT_VOLUME: _controllerDefaults.volume = value; break; case CONTROLLER_DEFAULT_PANNING: _controllerDefaults.panning = value; break; case CONTROLLER_DEFAULT_EXPRESSION: _controllerDefaults.expression = value; break; case CONTROLLER_DEFAULT_SUSTAIN: _controllerDefaults.sustain = value; break; case CONTROLLER_DEFAULT_RPN: _controllerDefaults.rpn = value; break; case CONTROLLER_DEFAULT_PITCH_BEND_SENSITIVITY: _controllerDefaults.pitchBendSensitivity = value; break; default: warning("MidiDriver_Multisource::setControllerDefault - Unknown controller default type %i", type); break; } } void MidiDriver_Multisource::setControllerDefaults(ControllerDefaultType type, int16 *value) { // Set the specified default values on _controllerDefaults on the field // corresponding to the specified controller. switch (type) { case CONTROLLER_DEFAULT_PROGRAM: Common::copy(value, value + ARRAYSIZE(_controllerDefaults.program), _controllerDefaults.program); break; default: warning("MidiDriver_Multisource::setControllerDefaults - Unsupported controller default type %i", type); break; } } void MidiDriver_Multisource::clearControllerDefault(ControllerDefaultType type) { // Reset the default value for this controller to -1. setControllerDefault(type, -1); } 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::resetSourceVolume() { for (int i = 0; i < MAXIMUM_SOURCES; ++i) { resetSourceVolume(i); } } void MidiDriver_Multisource::resetSourceVolume(uint8 source) { assert(source < MAXIMUM_SOURCES); setSourceVolume(source, _sources[source].neutralVolume); } 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::setInstrumentRemapping(const byte *instrumentRemapping) { _instrumentRemapping = instrumentRemapping; } 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); } int MidiDriver_NULL_Multisource::open() { // Setup a timer callback so "fades" will end after the specified time // (effectively becoming timers, because there is no audio). _timerRate = getBaseTempo(); g_system->getTimerManager()->installTimerProc(timerCallback, _timerRate, this, "MidiDriver_NULL_Multisource"); return 0; } void MidiDriver_NULL_Multisource::close() { g_system->getTimerManager()->removeTimerProc(timerCallback); } void MidiDriver_NULL_Multisource::timerCallback(void *data) { MidiDriver_NULL_Multisource *driver = (MidiDriver_NULL_Multisource *)data; driver->onTimer(); }