mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
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:
+79
-56
@@ -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_;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user