mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-05 09:10:29 +00:00
aa0f307e06
Silences the clang warning: static data member specialization of '_singleton' must originally be declared in namespace 'Common'; accepted as a C++0x extension [-Wc++0x-extensions] Wrapping "namespace Common {}" around the macro assignment causes clang to complain about a spurious semicolon, and removing the semicolon at the end of the macro causes some editors to misbehave. Changing the requirement of using the macro in one namespace (the global) to another (Common) seems a small price to pay to silence a warning.
734 lines
20 KiB
C++
734 lines
20 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 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 "lure/sound.h"
|
|
#include "lure/game.h"
|
|
#include "lure/memory.h"
|
|
#include "lure/res.h"
|
|
#include "lure/room.h"
|
|
|
|
#include "common/algorithm.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/endian.h"
|
|
#include "audio/midiparser.h"
|
|
|
|
namespace Common {
|
|
DECLARE_SINGLETON(Lure::SoundManager);
|
|
}
|
|
|
|
namespace Lure {
|
|
|
|
//#define SOUND_CROP_CHANNELS
|
|
|
|
SoundManager::SoundManager() {
|
|
Disk &disk = Disk::getReference();
|
|
_soundMutex = g_system->createMutex();
|
|
|
|
int index;
|
|
_descs = disk.getEntry(SOUND_DESC_RESOURCE_ID);
|
|
_numDescs = _descs->size() / sizeof(SoundDescResource);
|
|
_soundData = NULL;
|
|
_paused = false;
|
|
|
|
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
|
|
_isRoland = MidiDriver::getMusicType(dev) != MT_ADLIB;
|
|
_nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
|
|
|
|
Common::set_to(_channelsInUse, _channelsInUse + NUM_CHANNELS, false);
|
|
|
|
_driver = MidiDriver::createMidi(dev);
|
|
int statusCode = _driver->open();
|
|
if (statusCode) {
|
|
warning("Sound driver returned error code %d", statusCode);
|
|
_driver = NULL;
|
|
|
|
} else {
|
|
if (_nativeMT32) {
|
|
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
|
_driver->sendMT32Reset();
|
|
} else {
|
|
_driver->sendGMReset();
|
|
}
|
|
|
|
for (index = 0; index < NUM_CHANNELS; ++index) {
|
|
_channelsInner[index].midiChannel = _driver->allocateChannel();
|
|
/* 90 is power on default for midi compliant devices */
|
|
_channelsInner[index].volume = 90;
|
|
}
|
|
}
|
|
|
|
syncSounds();
|
|
}
|
|
|
|
SoundManager::~SoundManager() {
|
|
if (_driver)
|
|
_driver->setTimerCallback(this, NULL);
|
|
|
|
removeSounds();
|
|
_activeSounds.clear();
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
_playingSounds.clear();
|
|
g_system->unlockMutex(_soundMutex);
|
|
|
|
delete _descs;
|
|
delete _soundData;
|
|
|
|
if (_driver) {
|
|
_driver->close();
|
|
delete _driver;
|
|
_driver = NULL;
|
|
}
|
|
|
|
g_system->deleteMutex(_soundMutex);
|
|
}
|
|
|
|
void SoundManager::saveToStream(Common::WriteStream *stream) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::saveToStream");
|
|
SoundListIterator i;
|
|
|
|
for (i = _activeSounds.begin(); i != _activeSounds.end(); ++i) {
|
|
SoundDescResource *rec = (*i).get();
|
|
stream->writeByte(rec->soundNumber);
|
|
}
|
|
stream->writeByte(0xff);
|
|
}
|
|
|
|
void SoundManager::loadFromStream(Common::ReadStream *stream) {
|
|
// Stop any existing sounds playing
|
|
killSounds();
|
|
|
|
// Load any playing sounds
|
|
uint8 soundNumber;
|
|
while ((soundNumber = stream->readByte()) != 0xff) {
|
|
uint8 soundIndex = descIndexOf(soundNumber);
|
|
if (soundIndex != 0xff) {
|
|
// Make sure that the sound is allowed to be restored
|
|
SoundDescResource &rec = soundDescs()[soundIndex];
|
|
if ((rec.flags & SF_RESTORE) != 0)
|
|
// Requeue the sound for playing
|
|
addSound(soundIndex, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SoundManager::loadSection(uint16 sectionId) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::loadSection = %xh", sectionId);
|
|
killSounds();
|
|
|
|
if (_soundData) {
|
|
delete _soundData;
|
|
_driver->setTimerCallback(this, NULL);
|
|
}
|
|
|
|
_soundData = Disk::getReference().getEntry(sectionId);
|
|
_soundsTotal = *_soundData->data();
|
|
|
|
_driver->setTimerCallback(this, &onTimer);
|
|
}
|
|
|
|
void SoundManager::bellsBodge() {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::bellsBodge");
|
|
Resources &res = Resources::getReference();
|
|
Room &room = Room::getReference();
|
|
|
|
RoomData *roomData = res.getRoom(room.roomNumber());
|
|
if (roomData && roomData->areaFlag != res.fieldList().getField(AREA_FLAG)) {
|
|
res.fieldList().setField(AREA_FLAG, roomData->areaFlag);
|
|
|
|
switch (roomData->areaFlag) {
|
|
case 0:
|
|
killSound(1);
|
|
break;
|
|
case 1:
|
|
addSound(2);
|
|
killSound(33);
|
|
break;
|
|
case 2:
|
|
setVolume(0, 15);
|
|
// Deliberate fall through
|
|
default:
|
|
killSound(1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SoundManager::killSounds() {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::killSounds");
|
|
|
|
// Stop the player playing all sounds
|
|
musicInterface_KillAll();
|
|
|
|
// Clear the active sounds
|
|
_activeSounds.clear();
|
|
Common::set_to(_channelsInUse, _channelsInUse + NUM_CHANNELS, false);
|
|
}
|
|
|
|
void SoundManager::addSound(uint8 soundIndex, bool tidyFlag) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::addSound index=%d", soundIndex);
|
|
Game &game = Game::getReference();
|
|
|
|
if (tidyFlag)
|
|
tidySounds();
|
|
|
|
if (game.preloadFlag())
|
|
// Don't add a sound if in room preloading
|
|
return;
|
|
|
|
SoundDescResource &rec = soundDescs()[soundIndex];
|
|
int numChannels;
|
|
|
|
if (_isRoland)
|
|
numChannels = (rec.numChannels & 3) + 1;
|
|
else
|
|
numChannels = ((rec.numChannels >> 2) & 3) + 1;
|
|
|
|
int channelCtr, channelCtr2;
|
|
for (channelCtr = 0; channelCtr <= (NUM_CHANNELS - numChannels); ++channelCtr) {
|
|
for (channelCtr2 = 0; channelCtr2 < numChannels; ++channelCtr2)
|
|
if (_channelsInUse[channelCtr + channelCtr2])
|
|
break;
|
|
|
|
if (channelCtr2 == numChannels)
|
|
break;
|
|
}
|
|
|
|
if (channelCtr > (NUM_CHANNELS - numChannels)) {
|
|
// No channels free
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::addSound - no channels free");
|
|
return;
|
|
}
|
|
|
|
// Mark the found channels as in use
|
|
Common::set_to(_channelsInUse+channelCtr, _channelsInUse+channelCtr + numChannels, true);
|
|
|
|
SoundDescResource *newEntry = new SoundDescResource();
|
|
newEntry->soundNumber = rec.soundNumber;
|
|
newEntry->channel = channelCtr;
|
|
newEntry->numChannels = numChannels;
|
|
newEntry->flags = rec.flags;
|
|
|
|
if (_isRoland)
|
|
newEntry->volume = rec.volume;
|
|
else /* resource volumes do not seem to work well with our AdLib emu */
|
|
newEntry->volume = 240; /* 255 causes clipping with AdLib */
|
|
|
|
_activeSounds.push_back(SoundList::value_type(newEntry));
|
|
|
|
musicInterface_Play(rec.soundNumber, channelCtr, numChannels);
|
|
musicInterface_SetVolume(channelCtr, newEntry->volume);
|
|
}
|
|
|
|
void SoundManager::addSound2(uint8 soundIndex) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::addSound2 index=%d", soundIndex);
|
|
tidySounds();
|
|
|
|
if (soundIndex == 6) {
|
|
// Chinese torture
|
|
stopSound(6); // sometimes its still playing when restarted
|
|
addSound(6, false);
|
|
} else {
|
|
SoundDescResource &descEntry = soundDescs()[soundIndex];
|
|
SoundDescResource *rec = findSound(descEntry.soundNumber);
|
|
if (rec == NULL)
|
|
// Sound isn't active, so go and add it
|
|
addSound(soundIndex, false);
|
|
}
|
|
}
|
|
|
|
|
|
void SoundManager::stopSound(uint8 soundIndex) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::stopSound index=%d", soundIndex);
|
|
SoundDescResource &rec = soundDescs()[soundIndex];
|
|
musicInterface_Stop(rec.soundNumber);
|
|
}
|
|
|
|
void SoundManager::killSound(uint8 soundNumber) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::stopSound soundNumber=%d", soundNumber);
|
|
musicInterface_Stop(soundNumber);
|
|
}
|
|
|
|
void SoundManager::setVolume(uint8 soundNumber, uint8 volume) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::setVolume soundNumber=%d, volume=%d",
|
|
soundNumber, volume);
|
|
musicInterface_TidySounds();
|
|
|
|
SoundDescResource *entry = findSound(soundNumber);
|
|
if (entry)
|
|
musicInterface_SetVolume(entry->channel, volume);
|
|
}
|
|
|
|
uint8 SoundManager::descIndexOf(uint8 soundNumber) {
|
|
SoundDescResource *rec = soundDescs();
|
|
|
|
for (uint8 index = 0; index < _numDescs; ++index, ++rec) {
|
|
if (rec->soundNumber == soundNumber)
|
|
return index;
|
|
}
|
|
|
|
return 0xff; // Couldn't find entry
|
|
}
|
|
|
|
// Used to sync the volume for all channels with the Config Manager
|
|
//
|
|
void SoundManager::syncSounds() {
|
|
musicInterface_TidySounds();
|
|
|
|
bool mute = false;
|
|
if (ConfMan.hasKey("mute"))
|
|
mute = ConfMan.getBool("mute");
|
|
_musicVolume = mute ? 0 : MIN(255, ConfMan.getInt("music_volume"));
|
|
_sfxVolume = mute ? 0 : MIN(255, ConfMan.getInt("sfx_volume"));
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
if ((*i)->isMusic())
|
|
(*i)->setVolume(_musicVolume);
|
|
else
|
|
(*i)->setVolume(_sfxVolume);
|
|
}
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
SoundDescResource *SoundManager::findSound(uint8 soundNumber) {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::findSound soundNumber=%d", soundNumber);
|
|
SoundListIterator i;
|
|
|
|
for (i = _activeSounds.begin(); i != _activeSounds.end(); ++i) {
|
|
SoundDescResource *rec = (*i).get();
|
|
|
|
if (rec->soundNumber == soundNumber) {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "SoundManager::findSound - sound found");
|
|
return rec;
|
|
}
|
|
}
|
|
|
|
// Signal that sound wasn't found
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "SoundManager::findSound - sound not found");
|
|
return NULL;
|
|
}
|
|
|
|
void SoundManager::tidySounds() {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "SoundManager::tidySounds");
|
|
SoundListIterator i = _activeSounds.begin();
|
|
|
|
while (i != _activeSounds.end()) {
|
|
SoundDescResource *rec = (*i).get();
|
|
|
|
if (musicInterface_CheckPlaying(rec->soundNumber))
|
|
// Still playing, so move to next entry
|
|
++i;
|
|
else {
|
|
// Mark the channels that it used as now being free
|
|
Common::set_to(_channelsInUse+rec->channel, _channelsInUse+rec->channel+rec->numChannels, false);
|
|
|
|
i = _activeSounds.erase(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SoundManager::removeSounds() {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::removeSounds");
|
|
bellsBodge();
|
|
|
|
SoundListIterator i = _activeSounds.begin();
|
|
|
|
while (i != _activeSounds.end()) {
|
|
SoundDescResource *rec = (*i).get();
|
|
|
|
if ((rec->flags & SF_IN_USE) != 0)
|
|
musicInterface_Stop(rec->soundNumber);
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void SoundManager::restoreSounds() {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::restoreSounds");
|
|
SoundListIterator i = _activeSounds.begin();
|
|
|
|
while (i != _activeSounds.end()) {
|
|
SoundDescResource *rec = (*i).get();
|
|
|
|
if ((rec->numChannels != 0) && ((rec->flags & SF_RESTORE) != 0)) {
|
|
Common::set_to(_channelsInUse+rec->channel, _channelsInUse+rec->channel+rec->numChannels, true);
|
|
|
|
musicInterface_Play(rec->soundNumber, rec->channel, rec->numChannels);
|
|
musicInterface_SetVolume(rec->channel, rec->volume);
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void SoundManager::fadeOut() {
|
|
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::fadeOut");
|
|
|
|
// Fade out all the active sounds
|
|
musicInterface_TidySounds();
|
|
|
|
bool inProgress = true;
|
|
while (inProgress)
|
|
{
|
|
inProgress = false;
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
MidiMusic *music = (*i).get();
|
|
if (music->getVolume() > 0) {
|
|
inProgress = true;
|
|
music->setVolume(music->getVolume() >= 10 ? (music->getVolume() - 10) : 0);
|
|
}
|
|
}
|
|
|
|
g_system->unlockMutex(_soundMutex);
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
// Kill all the sounds
|
|
musicInterface_KillAll();
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
// musicInterface_Play
|
|
// Play the specified sound
|
|
|
|
void SoundManager::musicInterface_Play(uint8 soundNumber, uint8 channelNumber, uint8 numChannels) {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_Play soundNumber=%d, channel=%d",
|
|
soundNumber, channelNumber);
|
|
Game &game = Game::getReference();
|
|
|
|
if (!_soundData)
|
|
error("Sound section has not been specified");
|
|
|
|
uint8 soundNum = soundNumber & 0x7f;
|
|
if (soundNum > _soundsTotal)
|
|
error("Invalid sound index %d requested", soundNum);
|
|
|
|
if (_driver == NULL)
|
|
// Only play sounds if a sound driver is active
|
|
return;
|
|
|
|
bool isMusic = (soundNumber & 0x80) != 0;
|
|
|
|
if (!game.soundFlag())
|
|
// Don't play sounds if sound is turned off
|
|
return;
|
|
|
|
uint32 dataOfs = READ_LE_UINT32(_soundData->data() + soundNum * 4 + 2);
|
|
uint8 *soundStart = _soundData->data() + dataOfs;
|
|
uint32 dataSize;
|
|
|
|
if (soundNum == _soundsTotal - 1)
|
|
dataSize = _soundData->size() - dataOfs;
|
|
else {
|
|
uint32 nextDataOfs = READ_LE_UINT32(_soundData->data() + (soundNum + 1) * 4 + 2);
|
|
dataSize = nextDataOfs - dataOfs;
|
|
}
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MidiMusic *sound = new MidiMusic(_driver, _channelsInner, channelNumber, soundNum,
|
|
isMusic, numChannels, soundStart, dataSize);
|
|
_playingSounds.push_back(MusicList::value_type(sound));
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
// musicInterface_Stop
|
|
// Stops the specified sound from playing
|
|
|
|
void SoundManager::musicInterface_Stop(uint8 soundNumber) {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_Stop soundNumber=%d", soundNumber);
|
|
musicInterface_TidySounds();
|
|
uint8 soundNum = soundNumber & 0x7f;
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
MidiMusic *music = (*i).get();
|
|
if (music->soundNumber() == soundNum) {
|
|
_playingSounds.erase(i);
|
|
break;
|
|
}
|
|
}
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
// musicInterface_CheckPlaying
|
|
// Returns true if a sound is still playing
|
|
|
|
bool SoundManager::musicInterface_CheckPlaying(uint8 soundNumber) {
|
|
debugC(ERROR_DETAILED, kLureDebugSounds, "musicInterface_CheckPlaying soundNumber=%d", soundNumber);
|
|
musicInterface_TidySounds();
|
|
uint8 soundNum = soundNumber & 0x7f;
|
|
bool result = false;
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
MidiMusic *music = (*i).get();
|
|
if (music->soundNumber() == soundNum) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
g_system->unlockMutex(_soundMutex);
|
|
|
|
return result;
|
|
}
|
|
|
|
// musicInterface_SetVolume
|
|
// Sets the volume of the specified channel
|
|
|
|
void SoundManager::musicInterface_SetVolume(uint8 channelNum, uint8 volume) {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_SetVolume channel=%d, volume=%d",
|
|
channelNum, volume);
|
|
musicInterface_TidySounds();
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
MidiMusic *music = (*i).get();
|
|
if (music->channelNumber() == channelNum)
|
|
music->setVolume(volume);
|
|
}
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
// musicInterface_KillAll
|
|
// Stops all currently active sounds playing
|
|
|
|
void SoundManager::musicInterface_KillAll() {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_KillAll");
|
|
musicInterface_TidySounds();
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
MidiMusic *music = (*i).get();
|
|
music->stopMusic();
|
|
}
|
|
|
|
_playingSounds.clear();
|
|
_activeSounds.clear();
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
// musicInterface_ContinuePlaying
|
|
// The original player used this method for any sound managers needing continual calls
|
|
|
|
void SoundManager::musicInterface_ContinuePlaying() {
|
|
// No implementation needed
|
|
}
|
|
|
|
// musicInterface_TrashReverb
|
|
// Trashes reverb on actively playing sounds
|
|
|
|
void SoundManager::musicInterface_TrashReverb() {
|
|
// TODO: Handle support for trashing reverb
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_TrashReverb");
|
|
}
|
|
|
|
// musicInterface_KillAll
|
|
// Scans all the active sounds and deallocates any objects that have finished playing
|
|
|
|
void SoundManager::musicInterface_TidySounds() {
|
|
debugC(ERROR_DETAILED, kLureDebugSounds, "musicInterface_TidySounds");
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
MusicListIterator i = _playingSounds.begin();
|
|
while (i != _playingSounds.end()) {
|
|
MidiMusic *music = (*i).get();
|
|
if (!music->isPlaying())
|
|
i = _playingSounds.erase(i);
|
|
else
|
|
++i;
|
|
}
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
void SoundManager::onTimer(void *data) {
|
|
SoundManager *snd = (SoundManager *) data;
|
|
snd->doTimer();
|
|
}
|
|
|
|
void SoundManager::doTimer() {
|
|
if (_paused)
|
|
return;
|
|
|
|
g_system->lockMutex(_soundMutex);
|
|
|
|
MusicListIterator i;
|
|
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
|
|
MidiMusic *music = (*i).get();
|
|
if (music->isPlaying())
|
|
music->onTimer();
|
|
}
|
|
|
|
g_system->unlockMutex(_soundMutex);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
MidiMusic::MidiMusic(MidiDriver *driver, ChannelEntry channels[NUM_CHANNELS],
|
|
uint8 channelNum, uint8 soundNum, bool isMus, uint8 numChannels, void *soundData, uint32 size) {
|
|
_driver = driver;
|
|
assert(_driver);
|
|
_channels = channels;
|
|
_soundNumber = soundNum;
|
|
_channelNumber = channelNum;
|
|
_isMusic = isMus;
|
|
|
|
_numChannels = numChannels;
|
|
_volume = 0;
|
|
for (int i = 0; i < _numChannels; ++i) {
|
|
/* 90 is power on default for midi compliant devices */
|
|
_channels[_channelNumber + i].volume = 90;
|
|
}
|
|
|
|
if (_isMusic)
|
|
setVolume(Sound.musicVolume());
|
|
else
|
|
setVolume(Sound.sfxVolume());
|
|
|
|
_parser = MidiParser::createParser_SMF();
|
|
_parser->setMidiDriver(this);
|
|
_parser->setTimerRate(_driver->getBaseTempo());
|
|
|
|
_soundData = (uint8 *)soundData;
|
|
_soundSize = size;
|
|
|
|
// Check whether the music data is compressed - if so, decompress it for the duration
|
|
// of playing the sound
|
|
|
|
_decompressedSound = NULL;
|
|
if ((*_soundData == 'C') || (*_soundData == 'c')) {
|
|
uint32 packedSize = size - 0x201;
|
|
_decompressedSound = Memory::allocate(packedSize * 2);
|
|
|
|
uint16 *data = (uint16 *)(_soundData + 1);
|
|
uint16 *dataDest = (uint16 *) _decompressedSound->data();
|
|
byte *idx = ((byte *)data) + 0x200;
|
|
|
|
for (uint i = 0; i < packedSize; i++)
|
|
#if defined(SCUMM_NEED_ALIGNMENT)
|
|
memcpy(dataDest++, (byte*)((byte*)data + *(idx + i) * sizeof(uint16)), sizeof(uint16));
|
|
#else
|
|
*dataDest++ = data[*(idx + i)];
|
|
#endif
|
|
|
|
_soundData = _decompressedSound->data() + ((*_soundData == 'c') ? 1 : 0);
|
|
_soundSize = _decompressedSound->size();
|
|
}
|
|
|
|
playMusic();
|
|
}
|
|
|
|
MidiMusic::~MidiMusic() {
|
|
_parser->unloadMusic();
|
|
delete _parser;
|
|
delete _decompressedSound;
|
|
}
|
|
|
|
void MidiMusic::setVolume(int volume) {
|
|
if (volume < 0)
|
|
volume = 0;
|
|
else if (volume > 255)
|
|
volume = 255;
|
|
|
|
if (_volume == volume)
|
|
return;
|
|
|
|
_volume = volume;
|
|
|
|
volume *= _isMusic ? Sound.musicVolume() : Sound.sfxVolume();
|
|
|
|
for (int i = 0; i < _numChannels; ++i) {
|
|
if (_channels[_channelNumber + i].midiChannel != NULL)
|
|
_channels[_channelNumber + i].midiChannel->volume(
|
|
_channels[_channelNumber + i].volume *
|
|
volume / 65025);
|
|
}
|
|
}
|
|
|
|
void MidiMusic::playMusic() {
|
|
debugC(ERROR_DETAILED, kLureDebugSounds, "MidiMusic::PlayMusic playing sound %d", _soundNumber);
|
|
_parser->loadMusic(_soundData, _soundSize);
|
|
_parser->setTrack(0);
|
|
_isPlaying = true;
|
|
}
|
|
|
|
void MidiMusic::send(uint32 b) {
|
|
#ifdef SOUND_CROP_CHANNELS
|
|
if ((b & 0xF) >= _numChannels) return;
|
|
byte channel = _channelNumber + (byte)(b & 0x0F);
|
|
#else
|
|
byte channel = _channelNumber + ((byte)(b & 0x0F) % _numChannels);
|
|
#endif
|
|
|
|
if ((channel >= NUM_CHANNELS) || (_channels[channel].midiChannel == NULL))
|
|
return;
|
|
|
|
if ((b & 0xFFF0) == 0x07B0) {
|
|
// Adjust volume changes by song and master volume
|
|
byte volume = (byte)((b >> 16) & 0x7F);
|
|
_channels[channel].volume = volume;
|
|
int master_volume = _isMusic ? Sound.musicVolume() : Sound.sfxVolume();
|
|
volume = volume * _volume * master_volume / 65025;
|
|
b = (b & 0xFF00FFFF) | (volume << 16);
|
|
} else if ((b & 0xF0) == 0xC0) {
|
|
if (Sound.isRoland() && !Sound.hasNativeMT32()) {
|
|
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
|
|
}
|
|
}
|
|
else if ((b & 0xFFF0) == 0x007BB0) {
|
|
// No implementation
|
|
}
|
|
|
|
_channels[channel].midiChannel->send(b);
|
|
}
|
|
|
|
void MidiMusic::metaEvent(byte type, byte *data, uint16 length) {
|
|
//Only thing we care about is End of Track.
|
|
if (type != 0x2F)
|
|
return;
|
|
|
|
stopMusic();
|
|
}
|
|
|
|
void MidiMusic::onTimer() {
|
|
if (_isPlaying)
|
|
_parser->onTimer();
|
|
}
|
|
|
|
void MidiMusic::stopMusic() {
|
|
debugC(ERROR_DETAILED, kLureDebugSounds, "MidiMusic::stopMusic sound %d", _soundNumber);
|
|
_isPlaying = false;
|
|
_parser->unloadMusic();
|
|
}
|
|
|
|
} // End of namespace Lure
|