Added stereo support for SFX and speech ACM files

(for files at 44.1 kHz sample rate)

SFX sounds that are dependent on the gsound_compute_relative_volume_
function are excluded from the search for alternative formats.
This commit is contained in:
NovaRain
2020-12-21 16:05:16 +08:00
parent 9e9c3ed5d1
commit 9052396de5
+79 -56
View File
@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unordered_map>
#include <algorithm>
#include <dshow.h>
@@ -63,6 +64,8 @@ static std::vector<sDSSound*> loopingSounds;
static ACMSoundData* acmSoundData = nullptr; // currently loaded ACM file
static std::tr1::unordered_map<std::string, std::wstring> soundsFiles;
DWORD playID = 0;
DWORD loopID = 0;
@@ -70,7 +73,6 @@ static HWND soundwindow = 0;
static sDSSound* speechSound = nullptr; // currently playing sfall speech sound
static sDSSound* backgroundMusic = nullptr; // currently playing sfall background music
//static char playingMusicFile[256];
static bool deathSceneSpeech = false;
static bool lipsPlaying = false;
@@ -261,6 +263,8 @@ static bool IsMute(SoundMode mode) {
loop_play: mode 1 - loop sound playback (with sound overlay)
music_play: mode 2 - loop sound playback with the background game music turned off
speech_play: mode 3 -
Note: when playing via DirectShow there is a small delay of ~100 ms
*/
static sDSSound* PlayingSound(const wchar_t* pathFile, SoundMode mode, long adjustVolume = 0, bool isPaused = false) {
if (!soundwindow) CreateSndWnd();
@@ -333,47 +337,7 @@ enum PlayType : signed char {
PLAYTYPE_slides = 4 // speech for endgame slideshow
};
static const wchar_t *SoundExtensions[] = { L"mp3", L"wma", L"wav" };
/*
TODO: remove sfx support from this
For sfx sounds in wav format, playback must be performed using the game functions (DirectSound)
because there is a small delay (~50-100ms) when using DirectShow
sfx - environment effects must set their volume relative to the location from the player (see gsound_compute_relative_volume_)
*/
static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
if (!path) return false;
int len = 0;
while (len < 4 && path[len] != '\0') len++; // X.acm0
if (len <= 3) return false; // 012345
len = 0;
wchar_t buf[MAX_PATH];
if (playType != PLAYTYPE_music) {
char* master_patches = *ptr_patches; // all sfx/speech sounds must be placed in patches folder
while (master_patches[len]) buf[len] = master_patches[len++];
buf[len++] = L'\\';
}
const char* _path = path - len;
while (len < MAX_PATH && _path[len]) buf[len] = _path[len++];
if (len >= MAX_PATH) return false;
buf[len] = L'\0';
len -= 3; // the position of the first character of the file extension
bool isExist = false;
for (int i = 0; i < 3; i++) {
int j = len;
buf[j++] = SoundExtensions[i][0];
buf[j++] = SoundExtensions[i][1];
buf[j] = SoundExtensions[i][2];
if (GetFileAttributesW(buf) & FILE_ATTRIBUTE_DIRECTORY) continue; // also file not found
isExist = true;
break;
}
static bool __stdcall PrePlaySoundFile(PlayType playType, const wchar_t* file, bool isExist) {
if (playType == PLAYTYPE_music && backgroundMusic != nullptr) {
//if (found && strcmp(path, playingMusicFile) == 0) return true; // don't stop music
StopSfallSound(backgroundMusic->id);
@@ -382,11 +346,10 @@ static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
if (!isExist) return false;
if (playType == PLAYTYPE_music) {
//strcpy_s(playingMusicFile, path);
backgroundMusic = PlayingSound(buf, SNDMODE_engine_music_play); // background music loop
backgroundMusic = PlayingSound(file, SNDMODE_engine_music_play); // background music loop
if (!backgroundMusic) return false;
} else {
if (!PlayingSound(buf, ((playType >= PLAYTYPE_lips) ? SNDMODE_speech_play : SNDMODE_single_play), 0, (playType == PLAYTYPE_slides))) {
if (!PlayingSound(file, ((playType >= PLAYTYPE_lips) ? SNDMODE_speech_play : SNDMODE_single_play), 0, (playType == PLAYTYPE_slides))) {
return false;
}
if (playType == PLAYTYPE_lips) {
@@ -398,6 +361,52 @@ static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
return true;
}
static const wchar_t *SoundExtensions[] = { L"mp3", L"wma", L"wav" };
static bool __stdcall SearchAlternativeFormats(const char* path, PlayType playType, std::string &pathFile) {
size_t len = 0;
wchar_t wPath[MAX_PATH];
if (playType != PLAYTYPE_music) {
char* master_patches = *ptr_patches; // all sfx/speech sounds must be placed in patches folder
while (master_patches[len]) wPath[len] = master_patches[len++];
wPath[len++] = L'\\';
}
const char* _path = path - len;
while (_path[len]) wPath[len] = _path[len++];
if (len >= MAX_PATH) return false;
wPath[len] = L'\0';
len -= 3; // the position of the first character of the file extension
bool isExist = false;
for (int i = 0; i < 3; i++) {
int j = len;
wPath[j++] = SoundExtensions[i][0];
wPath[j++] = SoundExtensions[i][1];
wPath[j] = SoundExtensions[i][2];
if (GetFileAttributesW(wPath) & FILE_ATTRIBUTE_DIRECTORY) continue; // also file not found
isExist = true;
break;
}
soundsFiles.insert(std::make_pair(std::move(pathFile), (isExist) ? wPath : L""));
return PrePlaySoundFile(playType, wPath, isExist);
}
static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
if (!path) return false;
std::string pathFile = path;
if (pathFile.length() <= 4) return false;
std::tr1::unordered_map<std::string, std::wstring>::const_iterator it = soundsFiles.find(pathFile);
if (it != soundsFiles.cend()) {
return PrePlaySoundFile(playType, it->second.c_str(), (it->second.length() > 0));
}
return SearchAlternativeFormats(path, playType, pathFile);
}
static void __fastcall MakeMusicPath(const char* file) {
const char* pathFmt = "%s%s.ACM";
char pathBuf[MAX_PATH];
@@ -462,10 +471,15 @@ static void __fastcall ReleaseSound(sDSSound* sound) {
static void __declspec(naked) soundLoad_hack_A() {
static const DWORD SoundLoadHackEnd = 0x4AD4CC;
__asm {
cmp dword ptr [esp + 16 + 4 + 0x11C + 4], 0x45145F + 5; // called from gsound_load_sound_volume_
jne skip;
mov acmSoundData, ebx;
retn; // exit for SFX sounds for which the volume must be set relative to the player's location (see gsound_compute_relative_volume_)
skip:
mov acmSoundData, 0;
mov esi, [esp + 16 + 4];
cmp esi, 0x46620D + 5; // called from soundStartInterpret_ (op_soundplay_)
je skip;
je playACM;
pushadc;
cmp esi, 0x450926 + 5; // called from gsound_background_play_
sete cl; // PLAYTYPE_sfx / PLAYTYPE_music
@@ -486,7 +500,7 @@ load:
popadc;
jnz playSfallSound;
mov acmSoundData, ebx;
skip:
playACM:
retn; // play acm
playSfallSound:
add esp, 4;
@@ -521,23 +535,34 @@ playSfall:
static void __declspec(naked) soundLoad_hack_B() {
__asm {
xor ebp, ebp;
cmp dword ptr [esp + 16 + 4], 0x46620D + 5; // called from soundStartInterpret_
cmp dword ptr [esp + 16 + 4], 0x46620D + 5; // called from soundStartInterpret_ (op_soundplay_)
cmovne ebp, ebx;
mov acmSoundData, ebp;
retn;
}
}
static AudioDecode* __fastcall ACM_SampleRateChange(AudioDecode* decode/*, AudioFile* audio*/) {
static AudioDecode* __fastcall SoundFormatChange(AudioDecode* decode/*, AudioFile* audio*/) {
/*
example calculation
nBlockAlign = (nBitsPerSample / 8) * nChannels
nAvgBytesPerSec = nSamplesPerSec * nBlockAlign
*/
WAVEFORMATEX* wave = acmSoundData->lpwfxFormat;
if (decode->out_SampleRate != wave->nSamplesPerSec) {
wave->nSamplesPerSec = decode->out_SampleRate;
wave->nAvgBytesPerSec = decode->out_SampleRate * wave->nBlockAlign;
if (decode) {
bool recalc = false;
WAVEFORMATEX* wave = acmSoundData->lpwfxFormat;
// support stereo for SFX and speech sound files with 44.1 kHz
// this condition fixes the problem for mono sound files when the number of channels in the ACM file header is set to 2
if (decode->out_SampleRate == 44100 && decode->out_Channels != wave->nChannels) {
wave->nChannels = (WORD)decode->out_Channels;
wave->nBlockAlign = (wave->wBitsPerSample / 8) * wave->nChannels;
recalc = true;
}
if (decode->out_SampleRate != wave->nSamplesPerSec) {
wave->nSamplesPerSec = decode->out_SampleRate;
recalc = true;
}
if (recalc) wave->nAvgBytesPerSec = wave->nSamplesPerSec * wave->nBlockAlign;
}
return decode;
}
@@ -554,9 +579,7 @@ return:
//mov edx, ebp; // audio
mov ecx, eax; // decode
push audioOpen_AddrRet;
test eax, eax;
jnz ACM_SampleRateChange;
retn;
jmp SoundFormatChange;
skip:
jmp Create_AudioDecoder_;
}