[Scummvm-git-logs] scummvm master -> 2fc6dba3329cb4dde2cb2f3aaf2ef3ba0876cdb9
athrxx
noreply at scummvm.org
Fri Jun 2 15:04:53 UTC 2023
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
320c2f6983 KYRA: (EOB II/PC98) - fix some more text glitches
0d206f65a1 KYRA: (EOB) - fix minor menu graphics glitch
79ae9ec1d3 KYRA: (EOB/SegaCD) - minor gfx improvement
ec3885a0c5 KYRA: (EOB II/PC98) - fix minor gfx glitch
861d0008b5 KYRA: (EOB II/PC98) - fix game over load dialog font
2fc6dba332 KYRA: (EOB II/PC98) - add sound support
Commit: 320c2f698318023764993b2c742172979b014f21
https://github.com/scummvm/scummvm/commit/320c2f698318023764993b2c742172979b014f21
Author: athrxx (athrxx at scummvm.org)
Date: 2023-06-02T17:04:19+02:00
Commit Message:
KYRA: (EOB II/PC98) - fix some more text glitches
Changed paths:
engines/kyra/engine/chargen.cpp
engines/kyra/engine/eobcommon.cpp
engines/kyra/engine/eobcommon.h
engines/kyra/engine/items_eob.cpp
engines/kyra/graphics/screen_eob_pc98.cpp
engines/kyra/gui/gui_eob.cpp
diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 11dada2c191..169c7b25bb8 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -1966,6 +1966,7 @@ private:
void giveKhelbensCoin();
Common::String convertFromJISX0201(const Common::String &src);
+ Common::String makeTwoByteString(const Common::String &src);
EoBCoreEngine *_vm;
Screen_EoB *_screen;
@@ -2227,8 +2228,6 @@ int TransferPartyWiz::selectCharactersMenu() {
return selection;
}
-
-
void TransferPartyWiz::drawCharPortraitWithStats(int charIndex, bool enabled) {
int16 x = (charIndex % 2) * 159;
int16 y = (charIndex / 2) * 40;
@@ -2296,7 +2295,8 @@ void TransferPartyWiz::convertStats() {
if (_vm->_flags.lang == Common::JA_JPN && _vm->_flags.platform == Common::kPlatformPC98) {
Common::String cname(c->name);
cname = convertFromJISX0201(cname);
- Common::strlcpy(c->name, cname.c_str(), cname.size() + 1);
+ cname = makeTwoByteString(cname);
+ Common::strlcpy(c->name, cname.c_str(), sizeof(c->name));
}
for (int ii = 0; ii < 25; ii++) {
@@ -2553,6 +2553,29 @@ Common::String TransferPartyWiz::convertFromJISX0201(const Common::String &src)
return tmp;
}
+Common::String TransferPartyWiz::makeTwoByteString(const Common::String &src) {
+ Common::String n;
+ for (const uint8 *s = (const uint8*)src.c_str(); *s; ++s) {
+ if (*s < 32 || *s == 127) {
+ n += (char)*s;
+ } else if (*s < 127) {
+ uint8 c = (*s - 32) * 2;
+ assert(c < 190);
+ n += _vm->_ascii2SjisTables2[0][c];
+ n += _vm->_ascii2SjisTables2[0][c + 1];
+ } else if (*s < 212) {
+ n += '\x83';
+ n += (char)(*s - 64);
+ } else {
+ uint8 c = (*s - 212) * 2;
+ assert(c < 8);
+ n += _vm->_ascii2SjisTables2[1][c];
+ n += _vm->_ascii2SjisTables2[1][c + 1];
+ }
+ }
+ return n;
+}
+
// Start functions
bool EoBCoreEngine::startCharacterGeneration(bool defaultParty) {
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index ab7b40d7ba0..f53943e2dfa 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -2046,7 +2046,7 @@ bool EoBCoreEngine::checkPassword() {
return true;
}
-Common::String EoBCoreEngine::convertAsciiToSjis(const Common::String &str) {
+Common::String EoBCoreEngine::makeTwoByteString(const Common::String &str) {
if (_flags.platform != Common::kPlatformFMTowns)
return str;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index ecce2f54990..57e16cc59e5 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -915,7 +915,7 @@ protected:
virtual void seq_segaPausePlayer(bool pause) {}
bool checkPassword();
- Common::String convertAsciiToSjis(const Common::String &str);
+ Common::String makeTwoByteString(const Common::String &str);
virtual int resurrectionSelectDialogue() = 0;
virtual void useHorn(int charIndex, int weaponSlot) {}
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index 7cc4f33a152..008476006a9 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -500,7 +500,7 @@ void EoBCoreEngine::printFullItemName(Item item) {
int cs = (_flags.platform == Common::kPlatformSegaCD && _flags.lang == Common::JA_JPN && _screen->getNumberOfCharacters((tmpString).c_str()) >= 17) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow2) : -1;
- _txt->printMessage(convertAsciiToSjis(tmpString).c_str());
+ _txt->printMessage(makeTwoByteString(tmpString).c_str());
if (cs != -1)
_screen->setFontStyles(_screen->_currentFont, cs);
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
index 4f4f7735ad1..93a2b08ab1e 100644
--- a/engines/kyra/graphics/screen_eob_pc98.cpp
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -319,7 +319,7 @@ uint16 Font12x12PC98::convert(uint16 c) const {
}
PC98Font::PC98Font(uint8 shadowColor, bool useOverlay, int scaleV, const uint8 *convTable1, const char *convTable2, const char *convTable3) : OldDOSFont(Common::kRenderVGA, shadowColor),
- _convTable1(convTable1), _convTable2(convTable2), _convTable3(convTable3), _outputWidth(0), _outputHeight(0), _type(convTable1 && convTable2 && convTable3 ? kJIS_X0201 : kASCII) {
+ _convTable1(convTable1), _convTable2(convTable2), _convTable3(convTable3), _outputWidth(0), _outputHeight(0), _type(convTable1 && convTable2 && convTable3 ? kSJIS : kASCII) {
_numGlyphsMax = 256;
_useOverlay = useOverlay;
_scaleV = scaleV;
@@ -341,7 +341,7 @@ bool PC98Font::load(Common::SeekableReadStream &file) {
}
uint16 PC98Font::convert(uint16 c) const {
- if (_type == kJIS_X0201)
+ if (_type == kSJIS)
c = makeTwoByte(c);
if (!_convTable1 || c < 128)
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 85fac61261e..0f314d44d93 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2904,7 +2904,7 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
if (_vm->_flags.platform == Common::kPlatformFMTowns && _keyPressed.ascii > 31 && _keyPressed.ascii < 123) {
Common::String s;
s.insertChar(in & 0xff, 0);
- s = _vm->convertAsciiToSjis(s);
+ s = _vm->makeTwoByteString(s);
if (s.empty()) {
in = 0;
} else {
@@ -3966,7 +3966,8 @@ bool GUI_EoB::restParty() {
for (int l = 0; !res && restLoop && !_vm->shouldQuit();) {
l++;
- int cs = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _vm->gameFlags().lang == Common::JA_JPN) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow1) : -1;
+ int cs = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _vm->gameFlags().lang == Common::JA_JPN) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow1) :
+ ((_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_menuFont, Font::kStyleNone) : -1);
// Regenerate spells
for (int i = 0; i < 6; i++) {
Commit: 0d206f65a1c35da9f07d4150a93628a213b503d1
https://github.com/scummvm/scummvm/commit/0d206f65a1c35da9f07d4150a93628a213b503d1
Author: athrxx (athrxx at scummvm.org)
Date: 2023-06-02T17:04:19+02:00
Commit Message:
KYRA: (EOB) - fix minor menu graphics glitch
Changed paths:
engines/kyra/engine/eobcommon.cpp
engines/kyra/gui/gui_eob.cpp
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index f53943e2dfa..50865482e26 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -702,7 +702,7 @@ void EoBCoreEngine::writeSettings() {
if (_sound) {
if (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformSegaCD) {
if (!_configMusic)
- snd_playSong(0);
+ snd_stopSound();
} else if (!_configSounds) {
_sound->haltTrack();
}
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 0f314d44d93..d80f7c58db0 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2322,12 +2322,17 @@ void GUI_EoB::runCampMenu() {
Button *buttonList = 0;
for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ bool buttonsUnchanged = true;
+
if (newMenu != -1) {
drawCampMenu();
+
if (newMenu == 2) {
updateOptionsStrings();
if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
keepButtons = false;
+ else
+ buttonsUnchanged = false;
}
if (!keepButtons) {
releaseButtons(buttonList);
@@ -2569,8 +2574,11 @@ void GUI_EoB::runCampMenu() {
} else {
Common::Point p = _vm->getMousePos();
for (Button *b = buttonList; b; b = b->nextButton) {
- if ((b->arg & 2) && _vm->posWithinRect(p.x, p.y, b->x, b->y, b->x + b->width, b->y + b->height))
+ if ((b->arg & 2) && _vm->posWithinRect(p.x, p.y, b->x, b->y, b->x + b->width, b->y + b->height)) {
+ if (highlightButton && highlightButton != b && !prevHighlightButton)
+ prevHighlightButton = highlightButton;
highlightButton = b;
+ }
}
}
@@ -2584,7 +2592,7 @@ void GUI_EoB::runCampMenu() {
_charSelectRedraw = redrawPortraits = false;
if (prevHighlightButton != highlightButton && newMenu == -1 && runLoop) {
- drawMenuButton(prevHighlightButton, false, false, true);
+ drawMenuButton(prevHighlightButton, false, false, buttonsUnchanged);
drawMenuButton(highlightButton, false, true, false);
_screen->updateScreen();
prevHighlightButton = highlightButton;
Commit: 79ae9ec1d362ed08f11de463c4c914d29b503b09
https://github.com/scummvm/scummvm/commit/79ae9ec1d362ed08f11de463c4c914d29b503b09
Author: athrxx (athrxx at scummvm.org)
Date: 2023-06-02T17:04:19+02:00
Commit Message:
KYRA: (EOB/SegaCD) - minor gfx improvement
(button behaviour in character generator)
Changed paths:
engines/kyra/engine/chargen.cpp
diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 169c7b25bb8..f67e1e0db4e 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -548,7 +548,8 @@ void CharacterGenerator::drawButton(int index, int buttonState) {
const uint8 *bt = &_chargenSegaButtonCoords[index * 5];
_screen->sega_getRenderer()->fillRectWithTiles(0, bt[0], bt[1], bt[2], bt[3], (index > 9 ? 0x24BC : 0x2411) + bt[4] + (buttonState ? (bt[2] * bt[3]) : 0), true);
_screen->sega_getRenderer()->render(0, bt[0], bt[1], bt[2], bt[3]);
- _screen->updateScreen();
+ if (buttonState)
+ _screen->updateScreen();
return;
}
@@ -588,7 +589,7 @@ void CharacterGenerator::drawButton(int index, int buttonState) {
_screen->copyRegion(160, 0, c->destX << 3, c->destY, p->w << 3, p->h, 2, 0, Screen::CR_NO_P_CHECK);
_screen->updateScreen();
-}
+ }
void CharacterGenerator::processButtonClick(int index) {
drawButton(index, 1);
Commit: ec3885a0c579c9d4973c39c66d4a9cd4bba6d989
https://github.com/scummvm/scummvm/commit/ec3885a0c579c9d4973c39c66d4a9cd4bba6d989
Author: athrxx (athrxx at scummvm.org)
Date: 2023-06-02T17:04:20+02:00
Commit Message:
KYRA: (EOB II/PC98) - fix minor gfx glitch
(completely clear Japanese text when leaving party
transfer dialog)
Changed paths:
engines/kyra/engine/chargen.cpp
diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index f67e1e0db4e..0266005fdf0 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -2005,8 +2005,11 @@ TransferPartyWiz::~TransferPartyWiz() {
bool TransferPartyWiz::start() {
_screen->copyPage(0, 12);
- if (!selectAndLoadTransferFile())
+ if (!selectAndLoadTransferFile()) {
+ _screen->clearPage(0);
+ _screen->clearPage(2);
return false;
+ }
convertStats();
Commit: 861d0008b51fc8f5a3da884e6f3d81d6528ab1fb
https://github.com/scummvm/scummvm/commit/861d0008b51fc8f5a3da884e6f3d81d6528ab1fb
Author: athrxx (athrxx at scummvm.org)
Date: 2023-06-02T17:04:20+02:00
Commit Message:
KYRA: (EOB II/PC98) - fix game over load dialog font
Changed paths:
engines/kyra/engine/eobcommon.cpp
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 50865482e26..96eb9e67bda 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -777,7 +777,7 @@ bool EoBCoreEngine::checkPartyStatus(bool handleDeath) {
gui_drawAllCharPortraitsWithStats();
if (checkPartyStatusExtra()) {
- Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+ Screen::FontId of = _screen->setFont(_titleFont);
gui_updateControls();
int x = 0;
int y = 0;
Commit: 2fc6dba3329cb4dde2cb2f3aaf2ef3ba0876cdb9
https://github.com/scummvm/scummvm/commit/2fc6dba3329cb4dde2cb2f3aaf2ef3ba0876cdb9
Author: athrxx (athrxx at scummvm.org)
Date: 2023-06-02T17:04:20+02:00
Commit Message:
KYRA: (EOB II/PC98) - add sound support
(still with minor issues, but mostly finished)
Changed paths:
engines/kyra/engine/chargen.cpp
engines/kyra/engine/darkmoon.cpp
engines/kyra/engine/darkmoon.h
engines/kyra/engine/eobcommon.h
engines/kyra/engine/kyra_rpg.cpp
engines/kyra/gui/gui_eob.cpp
engines/kyra/resource/staticres_eob.cpp
engines/kyra/sequence/sequences_darkmoon.cpp
engines/kyra/sound/drivers/capcom98.cpp
engines/kyra/sound/drivers/capcom98.h
engines/kyra/sound/sound_intern.h
engines/kyra/sound/sound_pc98_darkmoon.cpp
engines/kyra/sound/sound_pc98_eob.cpp
diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 0266005fdf0..3cd03201112 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -89,6 +89,7 @@ private:
uint16 _chargenMaxStats[7];
const uint8 _menuColor1, _menuColor2, _menuColor3;
+ const uint8 _trackNo;
const char *const *_chargenStrings1;
const char *const *_chargenStrings2;
@@ -141,7 +142,9 @@ CharacterGenerator::CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen) :
_updateBoxShapesIndex(0), _lastUpdateBoxShapesIndex(0), _magicShapesBox(6), _activeBox(0),
_menuColor1(vm->gameFlags().platform == Common::kPlatformSegaCD ? 0xFF : (vm->_configRenderMode == Common::kRenderCGA ? 1 : vm->guiSettings()->colors.guiColorWhite)),
_menuColor2(vm->gameFlags().platform == Common::kPlatformSegaCD ? 0x55 : vm->guiSettings()->colors.guiColorLightRed),
- _menuColor3(vm->gameFlags().platform == Common::kPlatformSegaCD ? 0x99 : vm->guiSettings()->colors.guiColorBlack) {
+ _menuColor3(vm->gameFlags().platform == Common::kPlatformSegaCD ? 0x99 : vm->guiSettings()->colors.guiColorBlack),
+ _trackNo(vm->game() == GI_EOB1 ? (vm->gameFlags().platform == Common::kPlatformPC98 ? 1 :
+ (vm->gameFlags().platform == Common::kPlatformSegaCD ? 8: 20)) : (vm->gameFlags().platform == Common::kPlatformPC98 ? 53 : 13)) {
_chargenStatStrings = _vm->_chargenStatStrings;
_chargenRaceSexStrings = _vm->_chargenRaceSexStrings;
@@ -352,7 +355,7 @@ bool CharacterGenerator::createCustomParty(const uint8 ***faceShapes) {
checkForCompleteParty();
initButtonsFromList(0, 5);
- _vm->snd_playSong(_vm->game() == GI_EOB1 ? (_vm->gameFlags().platform == Common::kPlatformPC98 ? 1 : (_vm->gameFlags().platform == Common::kPlatformSegaCD ? 8: 20)) : 13);
+ _vm->snd_playSong(_trackNo);
_activeBox = 0;
for (bool loop = true; loop && (!_vm->shouldQuit());) {
@@ -894,13 +897,13 @@ int CharacterGenerator::getInput(Button *buttonList) {
if (_vm->game() == GI_EOB1 && _vm->sound()->checkTrigger()) {
_vm->sound()->resetTrigger();
- _vm->snd_playSong(20);
+ _vm->snd_playSong(_trackNo);
} else if (_vm->game() == GI_EOB2 && !_vm->sound()->isPlaying()) {
// WORKAROUND for EOB II: The original implements the same sound trigger check as in EOB I.
// However, Westwood seems to have forgotten to set the trigger at the end of the AdLib song,
// so that the music will not loop. We simply check whether the sound driver is still playing.
_vm->delay(3 * _vm->_tickLength);
- _vm->snd_playSong(13);
+ _vm->snd_playSong(_trackNo);
}
return _vm->checkInput(buttonList, false, 0);
diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index 0928387df79..4aea8b53b01 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -606,6 +606,11 @@ bool DarkMoonEngine::restParty_extraAbortCondition() {
return true;
}
+void DarkMoonEngine::snd_playLevelScore() {
+ if (_flags.platform == Common::kPlatformPC98)
+ snd_playSong(0);
+}
+
void DarkMoonEngine::snd_loadAmigaSounds(int level, int sub) {
if (_flags.platform != Common::kPlatformAmiga)
return;
@@ -691,9 +696,9 @@ void DarkMoonEngine::snd_loadAmigaSounds(int level, int sub) {
_amigaCurSoundIndex = sndIndex;
}
-void DarkMoonEngine::snd_playLevelScore() {
- if (_flags.platform == Common::kPlatformPC98)
- snd_playSong(0);
+void DarkMoonEngine::snd_updateLevelScore() {
+ if (_flags.platform == Common::kPlatformPC98 && !_sound->isPlaying())
+ snd_playLevelScore();
}
void DarkMoonEngine::useHorn(int charIndex, int weaponSlot) {
diff --git a/engines/kyra/engine/darkmoon.h b/engines/kyra/engine/darkmoon.h
index 743459e3a40..372af40ee50 100644
--- a/engines/kyra/engine/darkmoon.h
+++ b/engines/kyra/engine/darkmoon.h
@@ -120,8 +120,9 @@ private:
bool restParty_extraAbortCondition() override;
// Sound
- void snd_loadAmigaSounds(int level, int sub) override;
void snd_playLevelScore() override;
+ void snd_loadAmigaSounds(int level, int sub) override;
+ void snd_updateLevelScore() override;
const char *const *_amigaSoundFiles2;
const char *const *_amigaSoundMapExtra;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 57e16cc59e5..506292bb127 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -1270,7 +1270,7 @@ protected:
void snd_stopSound();
void snd_fadeOut(int del = 160);
virtual void snd_loadAmigaSounds(int level, int sub) = 0;
- virtual void snd_updateLevelScore() {}
+ virtual void snd_updateLevelScore() = 0;
const char **_amigaSoundMap;
const char *const *_amigaLevelSoundList1;
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index df0fa5c9bf6..1e40a8fdc3d 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -218,7 +218,7 @@ Common::Error KyraRpgEngine::init() {
initStaticResource();
- _envSfxDistThreshold = ((_flags.gameID == GI_EOB2 && _sound->getSfxType() == Sound::kTowns) || _sound->getSfxType() == Sound::kAdLib || _sound->getSfxType() == Sound::kPCSpkr) ? 15 : (_sound->getSfxType() == Sound::kAmiga ? 4 : 3);
+ _envSfxDistThreshold = ((_flags.gameID == GI_EOB2 && (_sound->getSfxType() == Sound::kTowns || _sound->getSfxType() == Sound::kPC98)) || _sound->getSfxType() == Sound::kAdLib || _sound->getSfxType() == Sound::kPCSpkr) ? 15 : (_sound->getSfxType() == Sound::kAmiga ? 4 : 3);
_dialogueButtonLabelColor1 = guiSettings()->buttons.labelColor1;
_dialogueButtonLabelColor2 = guiSettings()->buttons.labelColor2;
@@ -405,6 +405,8 @@ bool KyraRpgEngine::snd_processEnvironmentalSoundEffect(int soundId, int block)
if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns)
_environmentSfxVol = dist ? (16 - dist) * 8 - 1 : 127;
+ else if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformPC98)
+ _environmentSfxVol = (15 - dist) * 6 + 37;
else if (_flags.platform == Common::kPlatformAmiga)
_environmentSfxVol = dist ? (soundId != 13 ? dist : (dist >= 4) ? 4 : dist) : 1;
else if (_flags.platform == Common::kPlatformSegaCD)
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index d80f7c58db0..962c4597805 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2526,7 +2526,7 @@ void GUI_EoB::runCampMenu() {
else if (_vm->_configMusic)
_vm->snd_playSong(11);
else
- _vm->snd_playSong(0);
+ _vm->snd_stopSound();
} else {
_vm->_configSounds ^= true;
_vm->_configMusic = _vm->_configSounds ? 1 : 0;
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 8d02a530f2f..585e082dd6e 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -537,7 +537,7 @@ void EoBCoreEngine::initStaticResource() {
_sound->initAudioResourceInfo(kMusicIntro, &intro);
_sound->initAudioResourceInfo(kMusicFinale, &finale);
- } else if (_flags.platform != Common::kPlatformPC98) {
+ } else if (_flags.platform != Common::kPlatformPC98 || _flags.gameID == GI_EOB2) {
const char *const *files = _staticres->loadStrings(kEoBBaseSoundFilesIngame, temp);
SoundResourceInfo_PC ingame(files, temp);
files = _staticres->loadStrings(kEoBBaseSoundFilesIntro, temp);
diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index 960d525beb3..a64d2e1ccd2 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -273,7 +273,7 @@ void DarkMoonEngine::seq_playIntro() {
// PC-98 --- SFX 0
if (!skipFlag() && !shouldQuit())
- snd_playSong(12);
+ snd_playSong(_flags.platform == Common::kPlatformPC98 ? 54 : 12);
_screen->copyRegion(0, 0, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK);
sq.setPalette(9);
@@ -346,7 +346,8 @@ void DarkMoonEngine::seq_playIntro() {
sq.printText(3, textColor1); // The message was urgent.
- // PC-98 --- SFX 1
+ if (_flags.platform == Common::kPlatformPC98)
+ snd_playSong(55);
sq.loadScene(1, 2);
sq.waitForSongNotifier(++songCurPos);
@@ -446,7 +447,8 @@ void DarkMoonEngine::seq_playIntro() {
sq.animCommand(16);
- // PC-98 --- SFX 2
+ if (_flags.platform == Common::kPlatformPC98)
+ snd_playSong(56);
sq.printText(7, textColor2); // Thank you for coming so quickly
sq.animCommand(16);
@@ -592,7 +594,8 @@ void DarkMoonEngine::seq_playIntro() {
sq.animCommand(20);
sq.animCommand(18);
- // PC-98 --- SFX 3
+ if (_flags.platform == Common::kPlatformPC98)
+ snd_playSong(57);
sq.fadeText();
sq.animCommand(29);
@@ -618,7 +621,7 @@ void DarkMoonEngine::seq_playIntro() {
else {
_screen->setScreenDim(17);
_screen->clearCurDim();
- snd_playSoundEffect(14);
+ snd_playSoundEffect(_flags.platform == Common::kPlatformPC98 ? 12 : 14);
if (_configRenderMode != Common::kRenderEGA)
sq.fadePalette(10, 1);
@@ -659,7 +662,7 @@ void DarkMoonEngine::seq_playFinale() {
sq.delay(18);
if (!skipFlag() && !shouldQuit() && _flags.platform != Common::kPlatformAmiga)
- snd_playSong(1);
+ snd_playSong(_flags.platform == Common::kPlatformPC98 ? 52 : 1);
sq.update(2);
sq.loadScene(1, 2);
@@ -784,8 +787,10 @@ void DarkMoonEngine::seq_playFinale() {
sq.waitForSongNotifier(3);
+ int sfxOffset = (_flags.platform == Common::kPlatformPC98) ? -1 : 0;
+
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(7);
+ snd_playSoundEffect(7 + sfxOffset);
sq.delay(8);
sq.animCommand(10);
@@ -819,7 +824,7 @@ void DarkMoonEngine::seq_playFinale() {
sq.waitForSongNotifier(4);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(7);
+ snd_playSoundEffect(7 + sfxOffset);
sq.delay(8);
sq.animCommand(10);
@@ -856,7 +861,7 @@ void DarkMoonEngine::seq_playFinale() {
sq.delay(36);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(11);
+ snd_playSoundEffect(11 + sfxOffset);
sq.delay(54);
sq.fadeText();
@@ -865,7 +870,7 @@ void DarkMoonEngine::seq_playFinale() {
sq.waitForSongNotifier(5);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(6);
+ snd_playSoundEffect(6 + sfxOffset);
if (_flags.platform == Common::kPlatformAmiga)
sq.copyPalette(6, 0);
@@ -882,7 +887,7 @@ void DarkMoonEngine::seq_playFinale() {
sq.animCommand(19);
sq.animCommand(19, 36);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(12);
+ snd_playSoundEffect(12 + sfxOffset);
sq.fadeText();
sq.printText(17, textColor2); // Thank you
@@ -892,12 +897,12 @@ void DarkMoonEngine::seq_playFinale() {
sq.printText(18, textColor2); // You have earned my deepest respect
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(11);
+ snd_playSoundEffect(11 + sfxOffset);
sq.animCommand(20);
sq.animCommand(19);
sq.animCommand(19);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(11);
+ snd_playSoundEffect(11 + sfxOffset);
sq.delay(36);
sq.fadeText();
@@ -905,13 +910,13 @@ void DarkMoonEngine::seq_playFinale() {
sq.animCommand(19);
sq.animCommand(19, 18);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(11);
+ snd_playSoundEffect(11 + sfxOffset);
sq.animCommand(20, 18);
sq.fadeText();
sq.delay(28);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(12);
+ snd_playSoundEffect(12 + sfxOffset);
sq.delay(3);
sq.loadScene(5, 2);
@@ -919,26 +924,26 @@ void DarkMoonEngine::seq_playFinale() {
_screen->copyRegion(0, 0, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK);
} else {
sq.updateAmigaSound();
- snd_playSoundEffect(6);
+ snd_playSoundEffect(6 + sfxOffset);
if (_configRenderMode != Common::kRenderEGA)
sq.setPaletteWithoutTextColor(0);
_screen->crossFadeRegion(0, 0, 8, 8, 304, 128, 2, 0);
}
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(12);
+ snd_playSoundEffect(12 + sfxOffset);
sq.delay(5);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(11);
+ snd_playSoundEffect(11 + sfxOffset);
sq.delay(11);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(12);
+ snd_playSoundEffect(12 + sfxOffset);
sq.delay(7);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(11);
+ snd_playSoundEffect(11 + sfxOffset);
sq.delay(12);
if (!skipFlag() && !shouldQuit())
- snd_playSoundEffect(12);
+ snd_playSoundEffect(12 + sfxOffset);
sq.updateAmigaSound();
removeInputTop();
@@ -958,7 +963,7 @@ void DarkMoonEngine::seq_playFinale() {
sq.delay(18);
if (!skipFlag() && !shouldQuit() && _flags.platform != Common::kPlatformAmiga)
- snd_playSong(_flags.platform == Common::kPlatformFMTowns ? 16 : 1);
+ snd_playSong(_flags.platform == Common::kPlatformFMTowns ? 16 : (_flags.platform == Common::kPlatformPC98 ? 52 : 1));
int temp = 0;
diff --git a/engines/kyra/sound/drivers/capcom98.cpp b/engines/kyra/sound/drivers/capcom98.cpp
index b8600b54916..5ac992da59b 100644
--- a/engines/kyra/sound/drivers/capcom98.cpp
+++ b/engines/kyra/sound/drivers/capcom98.cpp
@@ -22,31 +22,1376 @@
#ifdef ENABLE_EOB
#include "kyra/sound/drivers/capcom98.h"
+#include "audio/softsynth/fmtowns_pc98/pc98_audio.h"
+#include "common/func.h"
+#include "common/system.h"
namespace Kyra {
+class CapcomPC98Player {
+public:
+ enum Flags {
+ kStdPlay = 1 << 0,
+ kPrioPlay = 1 << 1,
+ kPrioClaim = 1 << 2,
+ kFadeOut = 1 << 3,
+ kStdStop = 1 << 8,
+ kPrioStop = 1 << 9,
+ kSavedState = 1 << 10
+ };
+
+ CapcomPC98Player(bool playerPrio, uint16 playerFlag, uint16 reservedChanFlags);
+ virtual ~CapcomPC98Player() {}
+
+ virtual bool init() = 0;
+ virtual void deinit() = 0;
+ virtual void reset() = 0;
+ virtual void loadInstruments(const uint8 *data, uint16 number) {}
+
+ void startSound(const uint8 *data, uint8 volume, bool loop);
+ void stopSound();
+ uint8 getMarker(uint8 id) const { return _soundMarkers[id & 0x0F]; }
+ static uint16 getPlayerStatus() { return _flags; }
+
+ virtual void setMasterVolume (int vol) = 0;
+
+ void nextTick();
+ virtual void processSounds() {}
+
+protected:
+ uint16 _soundMarkers[16];
+ const uint16 _reservedChanFlags;
+
+private:
+ virtual void send(uint32 evt) = 0;
+ virtual PC98AudioCore::MutexLock lockMutex() = 0;
+ void storeEvent(uint32 evt);
+ void restorePlayer();
+ virtual void restoreStateIntern() {}
+ uint16 playFlag() const { return _playerFlag & (kStdPlay | kPrioPlay); }
+ uint16 extraFlag() const { return _playerFlag & kPrioClaim; }
+ uint16 stopFlag() const { return (_playerFlag & (kStdPlay | kPrioPlay)) << 8; }
+
+ const uint8 *_data;
+ const uint8 *_curPos;
+ uint16 _numEventsTotal;
+ uint16 _numEventsLeft;
+ uint8 _volume;
+ uint16 _midiTicker;
+
+ static uint16 _flags;
+ bool _loop;
+
+ Common::Array<uint32> _storedEvents;
+
+ const bool _playerPrio;
+ const uint16 _playerFlag;
+};
+
+class CapcomPC98_MIDI final : public CapcomPC98Player {
+public:
+ typedef Common::Functor0Mem<PC98AudioCore::MutexLock, CapcomPC98AudioDriverInternal> MutexProc;
+
+ CapcomPC98_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, MutexProc &mutexProc);
+ ~CapcomPC98_MIDI();
+
+ bool init() override;
+ void deinit() override;
+ void reset() override;
+
+ void setMasterVolume (int vol) override;
+
+private:
+ void send(uint32 evt) override;
+ PC98AudioCore::MutexLock lockMutex() override;
+
+ MidiDriver *_midi;
+ const bool _isMT32;
+ const uint8 *_programMapping;
+
+ static const uint8 _programMapping_mt32ToGM[128];
+
+ MutexProc &_mproc;
+};
+
+class CapcomPC98_FM_Channel {
+public:
+ CapcomPC98_FM_Channel(uint8 id, PC98AudioCore *&ac, const Common::Array<const uint8*>&instruments);
+ ~CapcomPC98_FM_Channel();
+
+ void reset();
+
+ void keyOff();
+ void noteOff(uint8 note);
+ void noteOn(uint8 note, uint8 velo);
+ void programChange(uint8 prg);
+ void pitchBend(uint16 pb);
+ void restore();
+
+ void modWheel(uint8 mw);
+ void breathControl(uint8 bc);
+ void pitchBendSensitivity(uint8 pbs);
+ void portamentoTime(uint8 pt);
+ void volume(uint8 vol);
+ void togglePortamento(uint8 enable);
+ void allNotesOff();
+
+ void processSounds();
+
+private:
+ typedef Common::Functor0Mem<void, CapcomPC98_FM_Channel> VbrHandlerProc;
+
+ const uint8 _regOffset;
+ uint8 _program;
+ bool _isPlaying;
+ uint8 _note;
+ uint8 _carrier;
+ uint8 _volume;
+ uint8 _velocity;
+ uint8 _noteEffCur;
+ uint8 _noteEffPrev;
+ uint8 _breathControl;
+ uint16 _frequency;
+ uint8 _modWheel;
+ uint16 _pitchBendSensitivity;
+ int16 _pitchBendPara;
+ int16 _pitchBendEff;
+ int32 _vbrState;
+ uint32 _vbrStep;
+ uint16 _vbrCycleTicker;
+ uint16 _vbrDelayTicker;
+ VbrHandlerProc *_vbrHandler;
+ bool _prtEnable;
+ uint8 _prtTime;
+ int32 _prtState;
+ int32 _prtStep;
+ uint16 _prtCycleLength;
+
+ struct Instrument {
+ char name[9];
+ uint8 fbl_alg;
+ uint8 vbrType;
+ uint8 vbrCycleLength;
+ uint8 vbrSensitivity;
+ uint8 resetEffect;
+ uint8 vbrDelay;
+ /*uint8 ff;
+ uint8 f10;
+ uint8 f11;
+ uint8 f12;
+ uint8 f13;
+ uint8 f14;*/
+ uint8 *regData;
+ } _instrument;
+
+private:
+ void loadInstrument(const uint8 *in);
+ void updatePitchBend();
+ void updateVolume();
+ void updatePortamento();
+ void updateFrequency();
+ void setupPortamento();
+ void setupVibrato();
+
+ void dummyProc() {}
+ void vbrHandler0();
+ void vbrHandler1();
+ void vbrHandler2();
+ void vbrHandler3();
+
+ VbrHandlerProc *_vbrHandlers[5];
+ PC98AudioCore *&_ac;
+ const Common::Array<const uint8*>&_instruments;
+
+ static const uint16 _freqMSBTable[12];
+ static const uint8 _freqLSBTables[12][64];
+ static const uint8 _volTablesInst[4][128];
+ static const uint8 _volTableOut[128];
+ static const uint8 _volTablePara[128];
+};
+
+class CapcomPC98_FM final : public CapcomPC98Player, PC98AudioPluginDriver {
+public:
+ typedef Common::Functor0Mem<void, CapcomPC98AudioDriverInternal> CBProc;
+
+ CapcomPC98_FM(Audio::Mixer *mixer, CBProc &cbproc, bool playerPrio, uint16 playerFlag, uint8 reservedChanFlags, bool needsTimer);
+ ~CapcomPC98_FM() override;
+
+ bool init() override;
+ void deinit() override;
+ void reset() override;
+ void loadInstruments(const uint8 *data, uint16 number) override;
+
+ void setMasterVolume (int vol) override;
+
+ PC98AudioCore::MutexLock lockMutex() override;
+
+private:
+ void send(uint32 evt) override;
+ void timerCallbackB() override;
+ void processSounds() override;
+
+ void controlChange(uint8 ch, uint8 control, uint8 val);
+
+ void restoreStateIntern() override;
+
+ PC98AudioCore *_ac;
+ CapcomPC98_FM_Channel **_chan;
+ Common::Array<const uint8*>_instruments;
+
+ CBProc &_cbProc;
+
+ bool _ready;
+
+ static const uint8 _initData[72];
+};
+
class CapcomPC98AudioDriverInternal {
public:
- CapcomPC98AudioDriverInternal();
+ CapcomPC98AudioDriverInternal(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev);
~CapcomPC98AudioDriverInternal();
+ static CapcomPC98AudioDriverInternal *open(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev);
+ static void close();
+
+ bool isUsable() const { return _ready; }
+
+ void reset();
+ void loadFMInstruments(const uint8 *data);
+ void startSong(const uint8 *data, uint8 volume, bool loop);
+ void stopSong();
+ void startSoundEffect(const uint8 *data, uint8 volume);
+ void stopSoundEffect();
+ int checkSoundMarker() const;
+ bool songIsPlaying() const;
+ bool soundEffectIsPlaying() const;
+
+ void setMusicVolume(int volume);
+ void setSoundEffectVolume(int volume);
+
+ void timerCallback();
+ PC98AudioCore::MutexLock lockMutex();
+
private:
+ void updateMasterVolume();
+
+ CapcomPC98Player *_players[2];
+ CapcomPC98_FM *_fmDevice;
+ CapcomPC98_FM::CBProc *_timerProc;
+ CapcomPC98_MIDI::MutexProc *_mutexProc;
+
+ static CapcomPC98AudioDriverInternal *_refInstance;
+ static int _refCount;
+
+ int _musicVolume;
+ int _sfxVolume;
+
+ int _marker;
+ bool _ready;
+};
+
+uint16 CapcomPC98Player::_flags = 0;
+
+CapcomPC98Player::CapcomPC98Player(bool playerPrio, uint16 playerFlag, uint16 reservedChanFlags) : _playerPrio(playerPrio), _playerFlag(playerFlag), _reservedChanFlags(reservedChanFlags), _data(nullptr), _curPos(nullptr), _numEventsTotal(0), _numEventsLeft(0), _volume(0), _midiTicker(0), _loop(false) {
+ memset(_soundMarkers, 0, sizeof(_soundMarkers));
+ _flags = 0;
+}
+
+void CapcomPC98Player::startSound(const uint8 *data, uint8 volume, bool loop) {
+ stopSound();
+
+ PC98AudioCore::MutexLock lock = lockMutex();
+ _numEventsTotal = _numEventsLeft = READ_LE_UINT16(data);
+ _data = _curPos = data + 2;
+ _volume = volume & 0x7f;
+ _loop = loop;
+ _midiTicker = 0;
+
+ if (_volume != 0x7f) {
+ for (int i = 0; i < 16; ++i) {
+ if ((1 << i) & _reservedChanFlags)
+ send(0x0007B0 | i | (_volume << 16));
+ }
+ }
+
+ _flags &= ~(stopFlag() | kFadeOut);
+ _flags |= (playFlag() | extraFlag());
+}
+
+void CapcomPC98Player::stopSound() {
+ while (_flags & playFlag()) {
+ g_system->delayMillis(5);
+ PC98AudioCore::MutexLock lock = lockMutex();
+ _flags |= stopFlag();
+ }
+}
+
+void CapcomPC98Player::nextTick() {
+ if (_flags & playFlag()) {
+ if (!_playerPrio) {
+ if (_flags & kPrioClaim) {
+ _flags &= ~kPrioClaim;
+ _flags |= kSavedState;
+ for (int i = 0; i < 16; ++i) {
+ if ((1 << i) & ~_reservedChanFlags)
+ send(0x007BB0 | i);
+ }
+ } else if (((_flags & kPrioStop) || !(_flags & kPrioPlay)) && (_flags & kSavedState)) {
+ _flags &= ~kSavedState;
+ restorePlayer();
+ }
+ }
+
+ if (_flags & stopFlag()) {
+ for (int i = 0; i < 16; ++i) {
+ if ((1 << i) & _reservedChanFlags)
+ send(0x007BB0 | i);
+ }
+ _flags &= ~playFlag();
+
+ } else if (_numEventsLeft) {
+ bool eot = false;
+
+ while (_numEventsLeft && !eot) {
+ eot = false;
+
+ uint32 in = READ_LE_UINT32(_curPos);
+ if ((in & 0xFF) > _midiTicker)
+ break;
+
+ _midiTicker -= (in & 0xFF);
+ if (_playerPrio || !(_flags & kPrioPlay) || ((_flags & kPrioPlay) && ((1 << ((in >> 8) & 0x0f)) & _reservedChanFlags))) {
+ if (_volume == 0x7f || ((in >> 12) & 0xfff) != 0x70B)
+ send(in >> 8);
+ } else {
+ storeEvent(in >> 8);
+ }
+ _curPos += 4;
+ eot = (--_numEventsLeft == 0);
+ }
+
+ if (eot) {
+ if (_loop) {
+ _numEventsLeft = _numEventsTotal;
+ _curPos = _data;
+ } else if (_playerPrio || !(_flags & kPrioPlay)) {
+ for (int i = 0; i < 16; ++i) {
+ if ((1 << i) & _reservedChanFlags)
+ send(0x007BB0 | i);
+ }
+
+ _flags &= ~playFlag();
+ }
+ }
+ }
+ }
+
+ processSounds();
+
+ _midiTicker++;
+}
+
+void CapcomPC98Player::storeEvent(uint32 evt) {
+ if ((1 << (evt & 0x0f)) & _reservedChanFlags)
+ return;
+
+ uint8 st = evt & 0xff;
+
+ for (Common::Array<uint32>::iterator i = _storedEvents.begin(); i != _storedEvents.end(); ++i) {
+ if ((*i & 0xff) == st) {
+ *i = evt;
+ return;
+ }
+ }
+
+ st >>= 4;
+
+ if (st == 0x0b || st == 0x0c || st == 0x0e)
+ _storedEvents.push_back(evt);
+}
+
+void CapcomPC98Player::restorePlayer() {
+ restoreStateIntern();
+ for (Common::Array<uint32>::iterator i = _storedEvents.begin(); i != _storedEvents.end(); ++i)
+ send(*i);
+ _storedEvents.clear();
+}
+
+CapcomPC98_MIDI::CapcomPC98_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, MutexProc &mutexProc) : CapcomPC98Player(true, kStdPlay, 0xffff), _isMT32(isMT32), _mproc(mutexProc), _midi(nullptr), _programMapping(nullptr) {
+ _midi = MidiDriver::createMidi(dev);
+ uint8 *map = new uint8[128];
+ assert(map);
+
+ if (isMT32) {
+ memcpy(map, _programMapping_mt32ToGM, 128);
+ } else {
+ for (uint8 i = 0; i < 128; ++i)
+ map[i] = i;
+ }
+
+ _programMapping = map;
+}
+
+CapcomPC98_MIDI::~CapcomPC98_MIDI() {
+ _midi->close();
+ delete _midi;
+ delete[] _programMapping;
+}
+
+bool CapcomPC98_MIDI::init() {
+ if (!_midi || !_programMapping)
+ return false;
+
+ if (_midi->open())
+ return false;
+
+ if (_isMT32) {
+ _midi->sendMT32Reset();
+ } else {
+ static const byte gmResetSysEx[] = { 0x7E, 0x7F, 0x09, 0x01 };
+ _midi->sysEx(gmResetSysEx, sizeof(gmResetSysEx));
+ g_system->delayMillis(100);
+ }
+
+ reset();
+
+ return true;
+}
+
+void CapcomPC98_MIDI::deinit() {
+
+}
+
+void CapcomPC98_MIDI::reset() {
+
+}
+
+void CapcomPC98_MIDI::send(uint32 evt) {
+ if ((evt & 0xF0) == 0xC0) {
+ evt = (evt & 0xFF) | (_programMapping[(evt >> 8) & 0xFF] << 8);
+ } else if ((evt & 0xF0) == 0xB0 && ((evt >> 8) & 0xFF) == 3) {
+ _soundMarkers[evt & 0x0F] = (evt >> 16) & 0xFF;
+ return;
+ }
+ _midi->send(evt);
+}
+
+void CapcomPC98_MIDI::setMasterVolume (int vol) {
+
+}
+
+PC98AudioCore::MutexLock CapcomPC98_MIDI::lockMutex() {
+ if (!_mproc.isValid())
+ error("CapcomPC98_MIDI::lockMutex(): Invalid call");
+ return _mproc();
+}
+
+// This is not identical to the one we have in our common code (not even similar).
+const uint8 CapcomPC98_MIDI::_programMapping_mt32ToGM[128] = {
+ 0x00, 0x02, 0x01, 0x03, 0x04, 0x05, 0x10, 0x13, 0x16, 0x65, 0x0a, 0x00, 0x68, 0x67, 0x2e, 0x25,
+ 0x08, 0x09, 0x0a, 0x0c, 0x0d, 0x0e, 0x57, 0x38, 0x3b, 0x3c, 0x3d, 0x3e, 0x3b, 0x3b, 0x3b, 0x1f,
+ 0x3d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, 0x1f, 0x1f, 0x35, 0x38, 0x37, 0x38, 0x36, 0x33, 0x39, 0x70,
+ 0x30, 0x30, 0x31, 0x22, 0x22, 0x22, 0x22, 0x7a, 0x58, 0x5a, 0x5e, 0x59, 0x5b, 0x60, 0x60, 0x1a,
+ 0x51, 0x4f, 0x4e, 0x50, 0x54, 0x55, 0x56, 0x52, 0x4a, 0x49, 0x4c, 0x4d, 0x6e, 0x6b, 0x6d, 0x6c,
+ 0x2f, 0x2f, 0x5e, 0x52, 0x57, 0x22, 0x56, 0x38, 0x20, 0x24, 0x5d, 0x22, 0x21, 0x5d, 0x4d, 0x5d,
+ 0x29, 0x24, 0x66, 0x39, 0x22, 0x65, 0x22, 0x5c, 0x57, 0x69, 0x6a, 0x69, 0x6c, 0x6d, 0x0f, 0x35,
+ 0x70, 0x71, 0x72, 0x76, 0x75, 0x74, 0x73, 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7b, 0x7d, 0x7e, 0x7f
};
-CapcomPC98AudioDriverInternal::CapcomPC98AudioDriverInternal() {
+CapcomPC98_FM_Channel::CapcomPC98_FM_Channel(uint8 id, PC98AudioCore *&ac, const Common::Array<const uint8*>&instruments) : _regOffset(id), _ac(ac), _instruments(instruments),
+ _isPlaying(false), _note(0), _carrier(0), _volume(0), _velocity(0), _noteEffCur(0), _program(0), _noteEffPrev(0), _breathControl(0), _frequency(0), _modWheel(0), _pitchBendSensitivity(0),
+ _pitchBendPara(0), _pitchBendEff(0), _vbrState(0), _vbrStep(0), _vbrCycleTicker(0), _vbrDelayTicker(0), _vbrHandler(nullptr), _prtEnable(false),
+ _prtTime(0), _prtState(0), _prtStep(0), _prtCycleLength(0) {
+ typedef void (CapcomPC98_FM_Channel::*Proc)();
+ static const Proc procs[] = {
+ &CapcomPC98_FM_Channel::vbrHandler0,
+ &CapcomPC98_FM_Channel::vbrHandler1,
+ &CapcomPC98_FM_Channel::vbrHandler2,
+ &CapcomPC98_FM_Channel::vbrHandler3,
+ &CapcomPC98_FM_Channel::dummyProc
+ };
+
+ assert(ARRAYSIZE(_vbrHandlers) == ARRAYSIZE(procs));
+ for (int i = 0; i < ARRAYSIZE(_vbrHandlers); ++i) {
+ _vbrHandlers[i] = new VbrHandlerProc(this, procs[i]);
+ assert(_vbrHandlers[i]);
+ }
+
+ memset(&_instrument, 0, sizeof(_instrument));
+ _instrument.regData = new uint8[52];
+ memset(_instrument.regData, 0, 52);
+
+ reset();
+}
+
+CapcomPC98_FM_Channel::~CapcomPC98_FM_Channel() {
+ for (int i = 0; i < ARRAYSIZE(_vbrHandlers); ++i)
+ delete _vbrHandlers[i];
+
+ delete[] _instrument.regData;
+}
+
+void CapcomPC98_FM_Channel::reset() {
+ _vbrHandler = _vbrHandlers[4];
+ _frequency = 0xffff;
+ _vbrState = 0;
+ _prtState = 0;
+ _prtCycleLength = 0;
+ _prtEnable = false;
+ _pitchBendSensitivity = 3072;
+ _pitchBendEff = 0;
+ _pitchBendPara = 0;
+ _isPlaying = false;
+ _note = 0xff;
+ _volume = 0x7f;
+ _velocity = _breathControl = 0x40;
+ _noteEffCur = 60;
+ _modWheel = 0;
+}
+
+void CapcomPC98_FM_Channel::keyOff() {
+ _ac->writeReg(0, 0x28, _regOffset);
+}
+
+void CapcomPC98_FM_Channel::noteOff(uint8 note) {
+ if (!_isPlaying || _note != note)
+ return;
+ keyOff();
+ _isPlaying = false;
+}
+
+void CapcomPC98_FM_Channel::noteOn(uint8 note, uint8 velo) {
+ _noteEffPrev = _noteEffCur;
+ _note = note;
+ _velocity = velo;
+
+ if (note > 11)
+ note -= 12;
+ if (note > 127)
+ note += 12;
+
+ _noteEffCur = note;
+
+ if (!_isPlaying && _instrument.resetEffect)
+ setupVibrato();
+
+ setupPortamento();
+ updateFrequency();
+
+ if (!_isPlaying) {
+ updateVolume();
+ _ac->writeReg(0, 0x28, _regOffset | 0xf0);
+ }
+
+ _isPlaying = true;
+}
+
+void CapcomPC98_FM_Channel::programChange(uint8 prg) {
+ if (prg >= _instruments.size())
+ return;
+
+ _program = prg;
+
+ loadInstrument(_instruments[prg]);
+
+ _ac->writeReg(0, 0xb0 + _regOffset, _instrument.fbl_alg);
+ static const uint8 carriers[] = { 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F };
+ _carrier = carriers[_instrument.fbl_alg & 7];
+
+ const uint8 *s = _instrument.regData;
+ for (int i = _regOffset; i < 16 + _regOffset; i += 4) {
+ _ac->writeReg(0, 0x30 + i, s[0]);
+ _ac->writeReg(0, 0x50 + i, s[2]);
+ _ac->writeReg(0, 0x60 + i, s[3]);
+ _ac->writeReg(0, 0x70 + i, s[4]);
+ _ac->writeReg(0, 0x80 + i, s[5]);
+ _ac->writeReg(0, 0x90 + i, s[6]);
+ s += 13;
+ }
+
+ setupVibrato();
+}
+
+void CapcomPC98_FM_Channel::pitchBend(uint16 pb) {
+ _pitchBendPara = (pb - 0x2000) << 2;
+ updatePitchBend();
+}
+
+void CapcomPC98_FM_Channel::restore() {
+ programChange(_program);
+}
+
+void CapcomPC98_FM_Channel::modWheel(uint8 mw) {
+ _modWheel = mw;
+}
+
+void CapcomPC98_FM_Channel::breathControl(uint8 bc) {
+ _breathControl = bc;
+ updateVolume();
+}
+
+void CapcomPC98_FM_Channel::pitchBendSensitivity(uint8 pbs) {
+ _pitchBendSensitivity = pbs;
+ pitchBend(_pitchBendPara);
+}
+
+void CapcomPC98_FM_Channel::portamentoTime(uint8 pt) {
+ _prtTime = pt;
+}
+
+void CapcomPC98_FM_Channel::volume(uint8 vol) {
+ _volume = vol;
+ updateVolume();
+}
+
+void CapcomPC98_FM_Channel::togglePortamento(uint8 enable) {
+ _prtEnable = (enable >= 0x40);
+}
+
+void CapcomPC98_FM_Channel::allNotesOff() {
+ _isPlaying = false;
+ for (int i = _regOffset; i < 16 + _regOffset; i += 4) {
+ _ac->writeReg(0, 0x40 + i, 0x7f);
+ _ac->writeReg(0, 0x80 + i, 0xff);
+ }
+ _ac->writeReg(0, 0x28, _regOffset);
+}
+
+void CapcomPC98_FM_Channel::processSounds() {
+ if (!_isPlaying)
+ return;
+
+ if (_vbrHandler->isValid())
+ (*_vbrHandler)();
+ updatePortamento();
+ updateFrequency();
+}
+
+void CapcomPC98_FM_Channel::loadInstrument(const uint8 *in) {
+ memcpy(_instrument.name, in, 8);
+ in += 8;
+ _instrument.fbl_alg = *in++;
+ _instrument.vbrType = *in++;
+ _instrument.vbrCycleLength = *in++;
+ _instrument.vbrSensitivity = *in++;
+ _instrument.resetEffect = *in++;
+ _instrument.vbrDelay = *in++;
+ /*_instrument.ff = **/in++;
+ /*_instrument.f10 = **/in++;
+ /*_instrument.f11 = **/in++;
+ /*_instrument.f12 = **/in++;
+ /*_instrument.f13 = **/in++;
+ /*_instrument.f14 = **/in++;
+ assert(_instrument.regData);
+ memcpy(_instrument.regData, in, 52);
+}
+
+void CapcomPC98_FM_Channel::updatePitchBend() {
+ _pitchBendEff = (_pitchBendPara * (_pitchBendSensitivity << 1)) >> 16;
+ updateFrequency();
+}
+
+void CapcomPC98_FM_Channel::updateVolume() {
+ uint8 cr = _carrier;
+
+ const uint8 *s = _instrument.regData;
+ for (int i = _regOffset; i < 16 + _regOffset; i += 4) {
+ uint16 vol = 0;
+ if (cr & 1) {
+ vol += _volTableOut[_volume];
+ //vol += _fadeState;
+ }
+
+ uint8 a = _breathControl;
+ uint8 b = s[10];
+ if (b & 0x80) {
+ a = ~a & 0x7f;
+ b &= 0x7f;
+ }
+ vol += (((_volTablePara[a] * b) & 0x7fff) >> 7);
+
+ a = _velocity;
+ b = s[7];
+ if (b & 0x80) {
+ a = ~a & 0x7f;
+ b &= 0x7f;
+ }
+ vol += (((_volTablePara[a] * b) & 0x7fff) >> 7);
+
+ a = _volTablesInst[s[8] & 3][_noteEffCur];
+ b = s[9];
+ if (b & 0x80) {
+ a = ~a & 0x7f;
+ b &= 0x7f;
+ }
+ vol += (((a * b) & 0x7fff) >> 7);
+ vol += s[1];
+ vol = MIN<uint16>(vol, 0x7f);
+
+ _ac->writeReg(0, 0x40 + i, vol & 0xff);
+ s += 13;
+ cr >>= 1;
+ }
+}
+
+void CapcomPC98_FM_Channel::updatePortamento() {
+ if (_prtCycleLength) {
+ _prtCycleLength--;
+ _prtState += _prtStep;
+ } else {
+ _prtState = 0;
+ }
+}
+
+void CapcomPC98_FM_Channel::updateFrequency() {
+ int16 tone = (MIN<int16>(_modWheel + _instrument.vbrSensitivity, 255) * (_vbrState >> 16)) >> 8;
+ tone = CLIP<int16>(tone + (_noteEffCur << 8), 0, 0x5fff);
+ tone = CLIP<int16>(tone + _pitchBendEff, 0, 0x5fff);
+ tone = CLIP<int16>(tone + (_prtState >> 16), 0, 0x5fff);
+
+ uint8 block = ((tone >> 8) / 12) & 7;
+ uint8 msb = (tone >> 8) % 12;
+ uint8 lsb = (tone & 0xff) >> 2;
+
+ uint16 freq = (block << 11) + _freqMSBTable[msb] + _freqLSBTables[msb][lsb];
+ if (_frequency == freq)
+ return;
+
+ _frequency = freq;
+ _ac->writeReg(0, 0xa4 + _regOffset, freq >> 8);
+ _ac->writeReg(0, 0xa0 + _regOffset, freq & 0xff);
+}
+
+void CapcomPC98_FM_Channel::setupPortamento() {
+ if (!_prtTime || !_prtEnable) {
+ _prtCycleLength = 0;
+ _prtState = 0;
+ return;
+ }
+
+ int16 diff = (_noteEffCur << 8) - CLIP<int16>((_prtState >> 16) | (_noteEffPrev << 8), 0, 0x5fff);
+ _prtCycleLength = _prtTime;
+ _prtStep = (diff << 16) / _prtTime;
+ _prtState = (-diff << 16);
+}
+
+void CapcomPC98_FM_Channel::setupVibrato() {
+ _vbrHandler = _vbrHandlers[4];
+
+ if (_instrument.vbrCycleLength == 0 || _instrument.vbrType > 4)
+ return;
+
+ _vbrDelayTicker = _instrument.vbrDelay;
+
+ switch (_instrument.vbrType) {
+ case 0:
+ case 4:
+ _vbrStep = ((_instrument.vbrType == 4 ? 512 : 3072) << 16) / _instrument.vbrCycleLength;
+ _vbrCycleTicker = 0;
+ _vbrState = 0;
+ _vbrHandler = _vbrHandlers[0];
+ break;
+
+ case 1:
+ _vbrState = _vbrDelayTicker ? 0 : 3072 << 16;
+ _vbrCycleTicker = 0;
+ _vbrHandler = _vbrHandlers[1];
+ break;
+
+ case 2:
+ case 3:
+ _vbrStep = (6144 << 16) / _instrument.vbrCycleLength;
+ _vbrState = _vbrDelayTicker ? 0 : (_instrument.vbrType == 2 ? -3072 : 3072) << 16;
+ _vbrCycleTicker = _instrument.vbrCycleLength - 1;
+ _vbrHandler = _vbrHandlers[_instrument.vbrType];
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CapcomPC98_FM_Channel::vbrHandler0() {
+ if (_vbrDelayTicker) {
+ _vbrDelayTicker--;
+ return;
+ }
+
+ if ((_vbrCycleTicker < _instrument.vbrCycleLength) || (_vbrCycleTicker >= _instrument.vbrCycleLength * 3))
+ _vbrState += _vbrStep;
+ else
+ _vbrState -= _vbrStep;
+
+ if (++_vbrCycleTicker >= _instrument.vbrCycleLength * 4) {
+ _vbrCycleTicker = 0;
+ _vbrState = 0;
+ }
+}
+
+void CapcomPC98_FM_Channel::vbrHandler1() {
+ if (_vbrDelayTicker) {
+ _vbrDelayTicker--;
+ return;
+ }
+
+ _vbrState = ((_vbrCycleTicker >= _instrument.vbrCycleLength) ? -3072 : 3072) << 16;
+
+ if (++_vbrCycleTicker >= (_instrument.vbrCycleLength << 1))
+ _vbrCycleTicker = 0;
+}
+
+void CapcomPC98_FM_Channel::vbrHandler2() {
+ if (_vbrDelayTicker) {
+ _vbrDelayTicker--;
+ return;
+ }
+
+ _vbrState += _vbrStep;
+
+ if (++_vbrCycleTicker >= _instrument.vbrCycleLength) {
+ _vbrCycleTicker = 0;
+ _vbrState = -(3072 << 16);
+ }
+}
+
+void CapcomPC98_FM_Channel::vbrHandler3() {
+ if (_vbrDelayTicker) {
+ _vbrDelayTicker--;
+ return;
+ }
+
+ _vbrState -= _vbrStep;
+
+ if (++_vbrCycleTicker >= _instrument.vbrCycleLength) {
+ _vbrCycleTicker = 0;
+ _vbrState = 3072 << 16;
+ }
+}
+
+const uint16 CapcomPC98_FM_Channel::_freqMSBTable[] = {
+ 0x026a, 0x028f, 0x02b6, 0x02df, 0x030b, 0x0339, 0x036a, 0x039e, 0x03d5, 0x0410, 0x044e, 0x048f
+};
+
+const uint8 CapcomPC98_FM_Channel::_freqLSBTables[12][64] = {
+ {
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
+ 0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x11, 0x12,
+ 0x12, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b,
+ 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x21, 0x21, 0x22, 0x23, 0x23, 0x24, 0x24
+ },
+ {
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x13,
+ 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c,
+ 0x1d, 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x21, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x26, 0x26
+ },
+ {
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14,
+ 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e,
+ 0x1f, 0x1f, 0x20, 0x21, 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, 0x25, 0x26, 0x27, 0x27, 0x28, 0x29
+ },
+ {
+ 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0a,
+ 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15,
+ 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x1f, 0x20,
+ 0x21, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b
+ },
+ {
+ 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0b, 0x0c, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x15, 0x15, 0x16,
+ 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x22,
+ 0x22, 0x23, 0x24, 0x25, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e
+ },
+ {
+ 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x15, 0x15, 0x16, 0x17, 0x18,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, 0x2e, 0x2f, 0x30, 0x31
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0c,
+ 0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x33
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1a, 0x1b, 0x1c,
+ 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
+ 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44
+ }
+};
+
+const uint8 CapcomPC98_FM_Channel::_volTablesInst[4][128] = {
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+ },
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1e, 0x23, 0x28, 0x2d, 0x32, 0x37, 0x3c,
+ 0x41, 0x46, 0x4b, 0x50, 0x55, 0x5a, 0x5f, 0x64, 0x69, 0x6e, 0x73, 0x78, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
+ },
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
+ },
+ {
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f,
+ 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f
+ }
+};
+
+const uint8 CapcomPC98_FM_Channel::_volTableOut[] = {
+ 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x25, 0x25,
+ 0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20,
+ 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1a,
+ 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x15, 0x15,
+ 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10,
+ 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a,
+ 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05,
+ 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00
+};
+
+const uint8 CapcomPC98_FM_Channel::_volTablePara[] = {
+ 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+ 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60,
+ 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50,
+ 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40,
+ 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
+ 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
+ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+
+CapcomPC98_FM::CapcomPC98_FM(Audio::Mixer *mixer, CBProc &proc, bool playerPrio, uint16 playerFlag, uint8 reservedChanFlags, bool needsTimer) : CapcomPC98Player(playerPrio, playerFlag, reservedChanFlags), PC98AudioPluginDriver(), _cbProc(proc), _ac(nullptr), _chan(nullptr), _ready(false) {
+ _ac = new PC98AudioCore(mixer, needsTimer ? this : nullptr, kType26);
+ assert(_ac);
+ _chan = new CapcomPC98_FM_Channel*[3];
+ assert(_chan);
+ for (int i = 0; i < 3; ++i)
+ _chan[i] = new CapcomPC98_FM_Channel(i, _ac, _instruments);
+}
+
+CapcomPC98_FM::~CapcomPC98_FM() {
+ delete _ac;
+ if (_chan) {
+ for (int i = 0; i < 3; ++i)
+ delete _chan[i];
+ delete[] _chan;
+ }
+}
+
+bool CapcomPC98_FM::init() {
+ if (!(_chan && _ac && _ac->init()))
+ return false;
+
+ _ac->writeReg(0, 7, 0xBF);
+ for (int i = 0; i < 14; ++i) {
+ if (i != 7)
+ _ac->writeReg(0, i, 0);
+ }
+
+ static const uint8 iniData[] = { 0x00, 0x7F, 0x1F, 0x1F, 0x1F, 0xFF, 0x00 };
+ for (int i = 0; i < 7; ++i) {
+ for (int ii = 0; ii < 16; ++ii) {
+ if ((ii & 3) != 3)
+ _ac->writeReg(0, 0x30 + (i << 4) + ii, iniData[i]);
+ }
+ }
+
+ for (int i = 0; i < 3; ++i)
+ _ac->writeReg(0, 0xB0 + i, 0xC0);
+
+ _ac->writeReg(0, 0x26, 230);
+ _ac->writeReg(0, 0x27, 0x2a);
+
+ loadInstruments(_initData, 1);
+
+ reset();
+
+ _ready = true;
+
+ return true;
+}
+
+void CapcomPC98_FM::deinit() {
+ PC98AudioCore::MutexLock lock = _ac->stackLockMutex();
+ _ready = false;
+}
+
+void CapcomPC98_FM::reset() {
+ for (int i = 0; i < 3; ++i)
+ _chan[i]->reset();
+
+ for (int i = 0; i < 3; ++i)
+ _chan[i]->keyOff();
+
+ for (int i = 0; i < 3; ++i)
+ _chan[i]->programChange(0);
+}
+
+void CapcomPC98_FM::loadInstruments(const uint8 *data, uint16 number) {
+ _instruments.clear();
+ while (number--) {
+ _instruments.push_back(data);
+ data += 72;
+ }
+}
+
+void CapcomPC98_FM::setMasterVolume (int vol) {
+
+}
+
+PC98AudioCore::MutexLock CapcomPC98_FM::lockMutex() {
+ if (!_ready)
+ error("CapcomPC98_FM::lockMutex(): Invalid call");
+ return _ac->stackLockMutex();
+}
+
+void CapcomPC98_FM::send(uint32 evt) {
+ uint8 ch = evt & 0x0f;
+ uint8 p1 = (evt >> 8) & 0xff;
+ uint8 p2 = (evt >> 16) & 0xff;
+
+ if (ch > 2)
+ return;
+
+ switch (evt & 0xf0) {
+ case 0x80:
+ _chan[ch]->noteOff(p1);
+ break;
+ case 0x90:
+ if (p2)
+ _chan[ch]->noteOn(p1, p2);
+ else
+ _chan[ch]->noteOff(p1);
+ break;
+ case 0xb0:
+ controlChange(ch, p1, p2);
+ break;
+ case 0xc0:
+ _chan[ch]->programChange(p1);
+ break;
+ case 0xe0:
+ _chan[ch]->pitchBend(((p2 & 0x7f) << 7) | (p1 & 0x7f));
+ break;
+ default:
+ break;
+ }
+}
+
+
+void CapcomPC98_FM::timerCallbackB() {
+ if (_ready && _cbProc.isValid()) {
+ PC98AudioCore::MutexLock lock = _ac->stackLockMutex();
+ _cbProc();
+ }
+}
+
+void CapcomPC98_FM::processSounds() {
+ for (int i = 0; i < 3; ++i)
+ _chan[i]->processSounds();
+}
+
+void CapcomPC98_FM::controlChange(uint8 ch, uint8 control, uint8 val) {
+ if (ch > 2)
+ return;
+
+ switch (control) {
+ case 1:
+ _chan[ch]->modWheel(val);
+ break;
+ case 2:
+ _chan[ch]->breathControl(val);
+ break;
+ case 3:
+ _soundMarkers[ch] = val;
+ break;
+ case 4:
+ _chan[ch]->pitchBendSensitivity(val);
+ break;
+ case 5:
+ _chan[ch]->portamentoTime(val);
+ break;
+ case 7:
+ _chan[ch]->volume(val);
+ break;
+ case 65:
+ _chan[ch]->togglePortamento(val);
+ break;
+ case 123:
+ _chan[ch]->allNotesOff();
+ break;
+ default:
+ break;
+ }
+}
+
+void CapcomPC98_FM::restoreStateIntern() {
+ for (int i = 0; i < 3; ++i) {
+ if ((1 << i) & _reservedChanFlags)
+ continue;
+ _chan[i]->restore();
+ }
+}
+
+const uint8 CapcomPC98_FM::_initData[72] = {
+ 0x49, 0x4e, 0x49, 0x54, 0x5f, 0x56, 0x4f, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x1f,
+ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+CapcomPC98AudioDriverInternal *CapcomPC98AudioDriverInternal::_refInstance = 0;
+int CapcomPC98AudioDriverInternal::_refCount = 0;
+
+CapcomPC98AudioDriverInternal::CapcomPC98AudioDriverInternal(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev) : _ready(false), _fmDevice(nullptr), _timerProc(nullptr), _mutexProc(nullptr), _marker(0), _musicVolume(0), _sfxVolume(0) {
+ MusicType type = MidiDriver::getMusicType(dev);
+ _timerProc = new CapcomPC98_FM::CBProc(this, &CapcomPC98AudioDriverInternal::timerCallback);
+ assert(_timerProc);
+ _mutexProc = new CapcomPC98_MIDI::MutexProc(this, &CapcomPC98AudioDriverInternal::lockMutex);
+ assert(_mutexProc);
+
+ if (type == MT_MT32 || type == MT_GM) {
+ _players[0] = new CapcomPC98_MIDI(dev, type == MT_MT32, *_mutexProc);
+ _players[1] = _fmDevice = new CapcomPC98_FM(mixer, *_timerProc, true, CapcomPC98Player::kPrioPlay, 7, true);
+ _marker = 1;
+ } else {
+ _players[0] = new CapcomPC98_FM(mixer, *_timerProc, false, CapcomPC98Player::kStdPlay, 3, false);
+ _players[1] = _fmDevice = new CapcomPC98_FM(mixer, *_timerProc, true, CapcomPC98Player::kPrioPlay | CapcomPC98Player::kPrioClaim, 4, true);
+ }
+
+ bool ready = true;
+ for (int i = 0; i < 2; ++i) {
+ if (!(_players[i] && _players[i]->init()))
+ ready = false;
+ }
+
+ _ready = ready;
}
CapcomPC98AudioDriverInternal::~CapcomPC98AudioDriverInternal() {
+ _ready = false;
+
+ for (int i = 0; i < 2; ++i)
+ _players[i]->deinit();
+
+ for (int i = 0; i < 2; ++i)
+ delete _players[i];
+ delete _timerProc;
+ delete _mutexProc;
}
-CapcomPC98AudioDriver::CapcomPC98AudioDriver() {
+CapcomPC98AudioDriverInternal *CapcomPC98AudioDriverInternal::open(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev) {
+ _refCount++;
+ if (_refCount == 1 && _refInstance == 0)
+ _refInstance = new CapcomPC98AudioDriverInternal(mixer, dev);
+ else if (_refCount < 2 || _refInstance == 0)
+ error("CapcomPC98AudioDriverInternal::open(): Internal instance management failure");
+
+ return _refInstance;
+}
+
+void CapcomPC98AudioDriverInternal::close() {
+ if (!_refCount)
+ return;
+
+ _refCount--;
+
+ if (!_refCount) {
+ delete _refInstance;
+ _refInstance = 0;
+ }
+}
+
+void CapcomPC98AudioDriverInternal::reset() {
+ for (int i = 0; i < 2; ++i)
+ _players[i]->reset();
+}
+
+void CapcomPC98AudioDriverInternal::loadFMInstruments(const uint8 *data) {
+ for (int i = 0; i < 2; ++i)
+ _players[i]->loadInstruments(data + 2, READ_LE_UINT16(data));
+}
+
+void CapcomPC98AudioDriverInternal::startSong(const uint8 *data, uint8 volume, bool loop) {
+ stopSong();
+ if (_ready)
+ _players[0]->startSound(data, volume, loop);
+}
+
+void CapcomPC98AudioDriverInternal::stopSong() {
+ if (_ready)
+ _players[0]->stopSound();
+}
+
+void CapcomPC98AudioDriverInternal::startSoundEffect(const uint8 *data, uint8 volume) {
+ stopSoundEffect();
+ if (_ready)
+ _players[1]->startSound(data, volume, false);
+}
+
+void CapcomPC98AudioDriverInternal::stopSoundEffect() {
+ if (_ready)
+ _players[1]->stopSound();
+}
+
+int CapcomPC98AudioDriverInternal::checkSoundMarker() const {
+ return _players[0]->getMarker(_marker);
+}
+
+bool CapcomPC98AudioDriverInternal::songIsPlaying() const {
+ return CapcomPC98Player::getPlayerStatus() & CapcomPC98Player::kStdPlay;
+}
+
+bool CapcomPC98AudioDriverInternal::soundEffectIsPlaying() const {
+ return CapcomPC98Player::getPlayerStatus() & CapcomPC98Player::kPrioPlay;
+}
+
+void CapcomPC98AudioDriverInternal::setMusicVolume(int volume) {
+ _musicVolume = volume;
+ updateMasterVolume();
+}
+
+void CapcomPC98AudioDriverInternal::setSoundEffectVolume(int volume) {
+ _sfxVolume = volume;
+ updateMasterVolume();
+}
+
+void CapcomPC98AudioDriverInternal::timerCallback() {
+ for (int i = 0; i < 2; ++i)
+ _players[i]->nextTick();
+}
+
+PC98AudioCore::MutexLock CapcomPC98AudioDriverInternal::lockMutex() {
+ if (!_ready)
+ error("CapcomPC98AudioDriverInternal::lockMutex(): Invalid call");
+
+ return _fmDevice->lockMutex();
+}
+
+void CapcomPC98AudioDriverInternal::updateMasterVolume() {
+ if (!_ready)
+ return;
+ _players[0]->setMasterVolume(_musicVolume);
+ _players[1]->setMasterVolume(_sfxVolume);
+}
+
+CapcomPC98AudioDriver::CapcomPC98AudioDriver(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev) {
+ _drv = CapcomPC98AudioDriverInternal::open(mixer, dev);
}
CapcomPC98AudioDriver::~CapcomPC98AudioDriver() {
+ CapcomPC98AudioDriverInternal::close();
+ _drv = 0;
+}
+
+bool CapcomPC98AudioDriver::isUsable() const {
+ return (_drv && _drv->isUsable());
+}
+
+void CapcomPC98AudioDriver::reset() {
+ if (_drv)
+ _drv->reset();
+}
+
+void CapcomPC98AudioDriver::loadFMInstruments(const uint8 *data) {
+ if (_drv)
+ _drv->loadFMInstruments(data);
+}
+
+void CapcomPC98AudioDriver::startSong(const uint8 *data, uint8 volume, bool loop) {
+ if (_drv)
+ _drv->startSong(data, volume, loop);
+}
+
+void CapcomPC98AudioDriver::stopSong() {
+ if (_drv)
+ _drv->stopSong();
+}
+
+void CapcomPC98AudioDriver::startSoundEffect(const uint8 *data, uint8 volume) {
+ if (_drv)
+ _drv->startSoundEffect(data, volume);
+}
+
+void CapcomPC98AudioDriver::stopSoundEffect() {
+ if (_drv)
+ _drv->stopSoundEffect();
+}
+
+int CapcomPC98AudioDriver::checkSoundMarker() const {
+ return _drv ? _drv->checkSoundMarker() : 99;
+}
+
+bool CapcomPC98AudioDriver::songIsPlaying() const {
+ return _drv ? _drv->songIsPlaying() : false;
+}
+
+bool CapcomPC98AudioDriver::soundEffectIsPlaying() const {
+ return _drv ? _drv->soundEffectIsPlaying() : false;
+}
+
+void CapcomPC98AudioDriver::setMusicVolume(int volume) {
+ if (_drv)
+ _drv->setMusicVolume(volume);
+}
+void CapcomPC98AudioDriver::setSoundEffectVolume(int volume) {
+ if (_drv)
+ _drv->setSoundEffectVolume(volume);
}
} // End of namespace Kyra
diff --git a/engines/kyra/sound/drivers/capcom98.h b/engines/kyra/sound/drivers/capcom98.h
index 9da302435f7..869f1ac0da3 100644
--- a/engines/kyra/sound/drivers/capcom98.h
+++ b/engines/kyra/sound/drivers/capcom98.h
@@ -24,8 +24,8 @@
#ifndef KYRA_SOUND_CAPCOM98_H
#define KYRA_SOUND_CAPCOM98_H
+#include "audio/mididrv.h"
#include "common/scummsys.h"
-//#include "common/array.h"
namespace Audio {
class Mixer;
@@ -37,9 +37,25 @@ class CapcomPC98AudioDriverInternal;
class CapcomPC98AudioDriver {
public:
- CapcomPC98AudioDriver();
+ CapcomPC98AudioDriver(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev);
~CapcomPC98AudioDriver();
+ bool isUsable() const;
+
+ // All data passed to the following functions has to be maintained by the caller.
+ void reset();
+ void loadFMInstruments(const uint8 *data);
+ void startSong(const uint8 *data, uint8 volume, bool loop);
+ void stopSong();
+ void startSoundEffect(const uint8 *data, uint8 volume);
+ void stopSoundEffect();
+ int checkSoundMarker() const;
+ bool songIsPlaying() const;
+ bool soundEffectIsPlaying() const;
+
+ void setMusicVolume(int volume);
+ void setSoundEffectVolume(int volume);
+
private:
CapcomPC98AudioDriverInternal *_drv;
};
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 6be2c35842d..aa0dc70c679 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -540,13 +540,14 @@ public:
~SoundPC98_Darkmoon() override;
kType getMusicType() const override;
+ kType getSfxType() const override;
bool init() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override { return true; }
- void loadSoundFile(uint file) override {}
+ void loadSoundFile(uint file) override;
void loadSoundFile(Common::String name) override;
void playTrack(uint8 track) override;
@@ -565,11 +566,22 @@ public:
void resetTrigger() override;
private:
+ void restartBackgroundMusic();
+ const uint8 *getData(uint16 track) const;
+
KyraEngine_v1 *_vm;
CapcomPC98AudioDriver *_driver;
+ uint8 *_soundData, *_fileBuffer;
+
+ int _lastTrack;
+ const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
+ SoundResourceInfo_PC *_resInfo[3];
int _currentResourceSet;
+ Common::String _soundFileLoaded;
+
+ MidiDriver::DeviceHandle _dev;
kType _drvType;
bool _ready;
};
diff --git a/engines/kyra/sound/sound_pc98_darkmoon.cpp b/engines/kyra/sound/sound_pc98_darkmoon.cpp
index b7b6380d17a..0b56e627d9f 100644
--- a/engines/kyra/sound/sound_pc98_darkmoon.cpp
+++ b/engines/kyra/sound/sound_pc98_darkmoon.cpp
@@ -30,105 +30,202 @@
namespace Kyra {
SoundPC98_Darkmoon::SoundPC98_Darkmoon(KyraEngine_v1 *vm, MidiDriver::DeviceHandle dev, Audio::Mixer *mixer) : Sound(vm, mixer),
- _vm(vm), _driver(0), _currentResourceSet(-1), _ready(false), _drvType(kPC98) {
+ _vm(vm), _driver(nullptr), _soundData(nullptr), _currentResourceSet(-1), _ready(false), _dev(dev), _drvType(kPC98), _lastTrack(-1) {
+
+ memset(&_resInfo, 0, sizeof(_resInfo));
+ _soundData = new uint8[20600];
+ memset(_soundData, 0, 20600);
+ _fileBuffer = new uint8[10500];
+ memset(_fileBuffer, 0, 10500);
+
MusicType type = MidiDriver::getMusicType(dev);
- if (type == MT_MT32) {
+ if (type == MT_MT32)
_drvType = kMidiMT32;
- } else if (type == MT_GM) {
+ else if (type == MT_GM)
_drvType = kMidiGM;
- } else {
-
- }
}
SoundPC98_Darkmoon::~SoundPC98_Darkmoon() {
delete _driver;
+ delete[] _soundData;
+ delete[] _fileBuffer;
+ for (int i = 0; i < 3; i++)
+ initAudioResourceInfo(i, nullptr);
}
Sound::kType SoundPC98_Darkmoon::getMusicType() const {
return _drvType;
}
+Sound::kType SoundPC98_Darkmoon::getSfxType() const {
+ return kPC98;
+}
+
bool SoundPC98_Darkmoon::init() {
- _driver = new CapcomPC98AudioDriver();
- _ready = true;
- return true;
+ _driver = new CapcomPC98AudioDriver(_mixer, _dev);
+ _ready = (_soundData && _driver && _driver->isUsable());
+ return _ready;
}
void SoundPC98_Darkmoon::initAudioResourceInfo(int set, void *info) {
- //delete _resInfo[set];
- //_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
+ if (set < kMusicIntro || set > kMusicFinale)
+ return;
+ delete _resInfo[set];
+ _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : nullptr;
}
void SoundPC98_Darkmoon::selectAudioResourceSet(int set) {
- if (set == _currentResourceSet || !_ready)
+ if (set < kMusicIntro || set > kMusicFinale || set == _currentResourceSet || !_ready)
return;
+ if (_resInfo[set])
+ _currentResourceSet = set;
+}
- //if (!_resInfo[set])
- // return;
+void SoundPC98_Darkmoon::loadSoundFile(uint file) {
+ if (!_ready)
+ return;
- _currentResourceSet = set;
+ if (file < res()->fileListSize)
+ loadSoundFile(res()->fileList[file]);
}
void SoundPC98_Darkmoon::loadSoundFile(Common::String name) {
if (!_ready)
return;
- //if (file >= _resInfo[_currentResourceSet]->fileListSize)
- // return;
+ haltTrack();
+ stopAllSoundEffects();
- //Common::SeekableReadStream *s = _vm->resource()->createReadStream(_resInfo[_currentResourceSet]->fileList[file]);
- //_driver->loadMusicData(s);
- //delete s;
+ name += (_drvType == kPC98 ? ".SDO" : ".SDM");
+ if (!_ready || _soundFileLoaded == name)
+ return;
+
+ Common::SeekableReadStream *in = _vm->resource()->createReadStream(name.c_str());
+ if (!in)
+ error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", name.c_str());
+
+ uint16 sz = in->readUint16LE();
+ uint8 cmp = in->readByte();
+ in->seek(1, SEEK_CUR);
+ uint32 outSize = in->readUint32LE();
+ if ((cmp == 0 && outSize > 10500) || (cmp != 0 && outSize > 20600))
+ error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", name.c_str());
+ sz -= in->pos();
+ in->seek(2, SEEK_CUR);
+
+ memset(_fileBuffer, 0, 10500);
+ uint16 readSize = in->read(_fileBuffer, 10500);
+ assert(sz == readSize);
+ delete in;
+
+ memset(_soundData, 0, 20600);
+ if (cmp == 0) {
+ memcpy(_soundData, _fileBuffer, outSize);
+ } else if (cmp == 3) {
+ Screen::decodeFrame3(_fileBuffer, _soundData, outSize, true);
+ } else if (cmp == 4) {
+ Screen::decodeFrame4(_fileBuffer, _soundData, outSize);
+ } else {
+ error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", name.c_str());
+ }
+
+ uint16 instrOffs = READ_LE_UINT16(_soundData);
+ if (instrOffs >= 20600)
+ error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", name.c_str());
+
+ _driver->loadFMInstruments(_soundData + instrOffs);
+ _driver->reset();
}
void SoundPC98_Darkmoon::playTrack(uint8 track) {
- if (!_musicEnabled || !_ready)
- return;
+ if (track == 0 || track == 2)
+ _lastTrack = track;
+ playSoundEffect(track, 127);
}
void SoundPC98_Darkmoon::haltTrack() {
- if (!_musicEnabled || !_ready)
+ if (!_ready)
return;
- //playTrack(0);
+ _driver->stopSong();
+ _lastTrack = -1;
}
bool SoundPC98_Darkmoon::isPlaying() const {
- return false;
+ return _ready && _driver && _driver->songIsPlaying();
}
-void SoundPC98_Darkmoon::playSoundEffect(uint16 track, uint8) {
- if (!_sfxEnabled || !_ready || track >= 120)
+void SoundPC98_Darkmoon::playSoundEffect(uint16 track, uint8 vol) {
+ if (!_ready)
+ return;
+
+ if (track == 0 || track == 2) {
+ restartBackgroundMusic();
return;
- //_driver->startSoundEffect(track);
+ }
+
+ const uint8 *data = getData(track);
+ if (!data)
+ return;
+
+ if (track < 52 || track > 67) {
+ if (_sfxEnabled)
+ _driver->startSoundEffect(data, vol);
+ } else if (_musicEnabled) {
+ _lastTrack = track;
+ _driver->startSong(data, vol, false);
+ }
}
void SoundPC98_Darkmoon::stopAllSoundEffects() {
-
+ if (_ready)
+ _driver->stopSoundEffect();
}
void SoundPC98_Darkmoon::beginFadeOut() {
-
+ haltTrack();
+ stopAllSoundEffects();
}
int SoundPC98_Darkmoon::checkTrigger() {
- return 99;
+ return _driver ? _driver->checkSoundMarker() : 99;
}
void SoundPC98_Darkmoon::resetTrigger() {
}
+void SoundPC98_Darkmoon::restartBackgroundMusic() {
+ if (_lastTrack == -1) {
+ haltTrack();
+ stopAllSoundEffects();
+ } else {
+ _lastTrack = -1;
+ const uint8 *data = getData(0);
+ if (!data)
+ return;
+ if (_musicEnabled)
+ _driver->startSong(data, 127, true);
+ }
+}
+
+const uint8 *SoundPC98_Darkmoon::getData(uint16 track) const {
+ if (!_ready || track >= 120)
+ return nullptr;
+
+ uint16 offset = READ_LE_UINT16(&_soundData[(track + 1) << 1]);
+ return (offset < 20600) ? &_soundData[offset] : nullptr;
+}
+
void SoundPC98_Darkmoon::updateVolumeSettings() {
if (!_driver || !_ready)
return;
- //bool mute = false;
- //if (ConfMan.hasKey("mute"))
- // mute = ConfMan.getBool("mute");
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
- //_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
- //_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+ _driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+ _driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
}
} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_pc98_eob.cpp b/engines/kyra/sound/sound_pc98_eob.cpp
index 16583569e5e..1bade38bd66 100644
--- a/engines/kyra/sound/sound_pc98_eob.cpp
+++ b/engines/kyra/sound/sound_pc98_eob.cpp
@@ -97,7 +97,7 @@ void SoundPC98_EoB::playTrack(uint8 track) {
}
void SoundPC98_EoB::haltTrack() {
- if (!_musicEnabled || !_ready)
+ if (!_ready)
return;
playTrack(0);
}
More information about the Scummvm-git-logs
mailing list