Files
2026-02-02 04:50:13 +01:00

281 lines
6.5 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "efh/efh.h"
namespace Efh {
void EfhEngine::songDelay(int delay) {
debugC(3, kDebugEngine, "songDelay %d", delay);
int remainingDelay = delay / 2;
while (remainingDelay > 0 && !shouldQuit()) {
remainingDelay -= 3;
_system->delayMillis(3);
}
}
void EfhEngine::playNote(int frequencyIndex, int totalDelay) {
debugC(3, kDebugEngine, "playNote %d %d", frequencyIndex, totalDelay);
if (frequencyIndex > 0 && frequencyIndex < 72) {
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 0x1234DD / kSoundFrequency[frequencyIndex], -1);
songDelay(totalDelay);
_speaker->stop();
} else {
warning("playNote - Skip note with frequency index %d", frequencyIndex);
}
}
Common::CustomEventType EfhEngine::playSong(uint8 *buffer) {
debugC(3, kDebugEngine, "playSong");
_speaker = new Audio::PCSpeaker();
_speaker->init();
Common::CustomEventType inputAction = kActionNone;
int totalDelay = 0;
int8 stopFl;
uint8 varC = *buffer++;
Common::Event event;
do {
stopFl = *buffer & 0x3F;
if (stopFl != 0) {
int delay = stopFl * varC * 0x2200 / 1000;
if (*buffer > 0x7F)
delay /= 2;
if (*buffer & 0x40)
delay = (delay * 2) / 3;
++buffer;
uint8 frequencyIndex = *buffer;
++buffer;
if (frequencyIndex > 0x7F)
totalDelay += delay;
else if (frequencyIndex == 0)
songDelay(delay);
else {
playNote(frequencyIndex, totalDelay + delay);
totalDelay = 0;
}
}
songDelay(10);
_system->getEventManager()->pollEvent(event);
if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
inputAction = event.customType;
// Hack, sometimes there's a ghost event after the 2nd note
if (inputAction == kActionSkipSong || inputAction == kActionSkipSongAndIntro)
stopFl = 0;
}
} while (stopFl != 0 && !shouldQuit());
delete _speaker;
_speaker = nullptr;
return inputAction;
}
void EfhEngine::generateSound1(int lowFreq, int highFreq, int duration) {
debugC(3, kDebugEngine, "generateSound1 %d %d %d - suspicious code", lowFreq, highFreq, duration);
if (lowFreq < 19)
lowFreq = 19;
if (highFreq < 19)
highFreq = 19;
uint16 var2 = 0;
duration /= 20;
_speaker = new Audio::PCSpeaker();
_speaker->init();
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, highFreq, -1);
songDelay(10);
_speaker->stop();
for (int i = 0; i < duration; ++i) {
var2 = ROR(var2 + 0x9248, 3);
int val = (var2 * (highFreq - lowFreq)) >> 16;
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, lowFreq + val, -1);
songDelay(10);
_speaker->stop();
}
delete _speaker;
_speaker = nullptr;
}
void EfhEngine::generateSound2(int startFreq, int endFreq, int speed) {
debugC(3, kDebugEngine, "generateSound2 %d %d %d", startFreq, endFreq, speed);
if (startFreq < 19)
startFreq = 19;
if (endFreq < 19)
endFreq = 19;
int delta;
// The original is using -/+1 but it takes ages even with speed / 10, so I switched to -/+5
if (startFreq > endFreq)
delta = -50;
else
delta = 50;
_speaker = new Audio::PCSpeaker();
_speaker->init();
int curFreq = startFreq;
do {
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, curFreq, -1);
// The original is just looping, making the sound improperly timed as the length of a loop is directly related to the speed of the CPU
// Dividing by 10 is just a guess based on how it sounds. I suspect it may be still too much
songDelay(speed);
_speaker->stop();
curFreq += delta;
} while (curFreq < endFreq && !shouldQuit());
delete _speaker;
_speaker = nullptr;
}
void EfhEngine::generateSound3() {
debugC(3, kDebugEngine, "generateSound3");
_speaker = new Audio::PCSpeaker();
_speaker->init();
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 88, -1);
// The original makes me think the delay is so short it's not possible to hear. So that delay is guessed (and short)
songDelay(30);
_speaker->stop();
delete _speaker;
_speaker = nullptr;
}
void EfhEngine::generateSound4(int repeat) {
debugC(3, kDebugEngine, "generateSound4 %d", repeat);
for (int i = 0; i < repeat; ++i)
//It looks identical, so I'm reusing generateSound1
generateSound1(256, 4096, 10);
}
void EfhEngine::generateSound5(int repeat) {
debugC(3, kDebugEngine, "generateSound5 %d", repeat);
for (int i = 0; i < repeat; ++i)
//It looks identical, so I'm reusing generateSound2
generateSound2(256, 4096, 2);
}
void EfhEngine::generateSound(int16 soundType) {
debugC(3, kDebugEngine, "generateSound %d", soundType);
switch (soundType) {
case 5:
generateSound3();
break;
case 9:
generateSound1(20, 888, 500);
g_system->delayMillis(100);
generateSound1(20, 888, 500);
break;
case 10:
generateSound5(1);
break;
case 13:
generateSound2(256, 4096, 2);
break;
case 14:
generateSound2(20, 400, 20);
break;
case 15:
generateSound2(100, 888, 10);
break;
case 16:
generateSound1(2000, 6096, 1500);
break;
case 17:
generateSound4(1);
break;
default:
debug("generateSound %d - Not implemented because not used by the engine", soundType);
break;
}
}
void EfhEngine::genericGenerateSound(int16 soundType, int16 repeatCount) {
debugC(3, kDebugEngine, "genericGenerateSound %d %d", soundType, repeatCount);
if (repeatCount <= 0)
return;
switch (soundType) {
case 0:
case 1:
case 2:
generateSound(5);
break;
case 3:
case 4:
case 6:
generateSound(9);
break;
case 5:
case 7:
generateSound(13);
break;
case 8:
case 9:
case 10:
generateSound(10);
g_system->delayMillis(100);
generateSound(9);
break;
case 14:
generateSound(14);
break;
case 11:
case 12:
case 13:
for (int counter = 0; counter < repeatCount; ++counter) {
generateSound(17);
g_system->delayMillis(100);
}
break;
case 15:
generateSound(16);
default:
break;
}
}
} // End of namespace Efh