/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "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