Added music and perfom bug fixes

This commit is contained in:
Vladislav Khorev 2026-07-18 20:21:31 +03:00
parent e5e3d7bb28
commit 825a56a4a4
29 changed files with 185 additions and 68 deletions

7
.gitignore vendored
View File

@ -408,4 +408,9 @@ public
web_resources/ web_resources/
pc_resources/ pc_resources/
resources_hd/ resources_hd/
web_resources_x2/ web_resources_x2/
.artifacts/
*.zip

View File

@ -39,7 +39,8 @@ add_subdirectory("${TP_ROOT}/libpng-1.6.51" libpng-build)
# --- SDL2 --- # --- SDL2 ---
# Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама # Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама
add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build) add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build)
#add_subdirectory("${TP_ROOT}/SDL_mixer-release-2.8.0" sdl-mixer-build) add_subdirectory("${TP_ROOT}/SDL_mixer-release-2.8.0" sdl-mixer-build)
add_subdirectory("${TP_ROOT}/SDL_ttf-release-2.24.0" sdl-ttf-build) add_subdirectory("${TP_ROOT}/SDL_ttf-release-2.24.0" sdl-ttf-build)

View File

@ -150,6 +150,7 @@ target_link_libraries(main
z z
SDL2 SDL2
SDL2_ttf SDL2_ttf
SDL2_mixer
log log
android android
OpenSLES OpenSLES
@ -163,6 +164,7 @@ target_link_libraries(main
target_include_directories(main PRIVATE target_include_directories(main PRIVATE
#${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include #${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/SDL_ttf-release-2.24.0/external/freetype/include ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/SDL_ttf-release-2.24.0/external/freetype/include
#${CMAKE_CURRENT_SOURCE_DIR}/../zlib #${CMAKE_CURRENT_SOURCE_DIR}/../zlib
#${CMAKE_CURRENT_SOURCE_DIR}/../libpng #${CMAKE_CURRENT_SOURCE_DIR}/../libpng
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0 ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/bishkek fight.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/bishkek night.ogg (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/main menu final.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/obshaga.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/sound_bag001.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/sound_bag_close.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-android/app/src/main/assets/audio/sound_bag_open.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -2,6 +2,11 @@
#include <iostream> #include <iostream>
#include "utils/Utils.h" #include "utils/Utils.h"
#ifdef __ANDROID__
#include <android/log.h>
#endif
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
AudioPlayerAsync::AudioPlayerAsync() {} AudioPlayerAsync::AudioPlayerAsync() {}
#else #else
@ -22,33 +27,34 @@ AudioPlayerAsync::~AudioPlayerAsync() {
} }
bool AudioPlayerAsync::init() { bool AudioPlayerAsync::init() {
/*if (initialized) return true; if (initialized) return true;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
std::cerr << "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: " << SDL_GetError() << std::endl; FRG::logger() << "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: " << SDL_GetError() << std::endl;
return false; return false;
} }
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
std::cerr << "Mix_OpenAudio failed: " << Mix_GetError() << std::endl; FRG::logger() << "Mix_OpenAudio failed: " << Mix_GetError() << std::endl;
return false; return false;
} }
Mix_AllocateChannels(16); Mix_AllocateChannels(16);
initialized = true; initialized = true;
FRG::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;*/ FRG::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;
return true; return true;
} }
void AudioPlayerAsync::shutdown() { void AudioPlayerAsync::shutdown() {
if (!initialized) return; if (!initialized) return;
/*
if (currentMusic_) { if (currentMusic_) {
Mix_HaltMusic(); Mix_HaltMusic();
Mix_FreeMusic(currentMusic_); Mix_FreeMusic(currentMusic_);
currentMusic_ = nullptr; currentMusic_ = nullptr;
} }
currentMusicData_.clear();
{ {
std::lock_guard<std::mutex> lock(soundCacheMutex); std::lock_guard<std::mutex> lock(soundCacheMutex);
@ -61,16 +67,20 @@ void AudioPlayerAsync::shutdown() {
Mix_CloseAudio(); Mix_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
initialized = false; initialized = false;
FRG::logger() << "AudioPlayerAsync shutdown" << std::endl;*/ FRG::logger() << "AudioPlayerAsync shutdown" << std::endl;
} }
void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) { void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) {
/*if (!initialized) {
std::cerr << "AudioPlayerAsync not initialized" << std::endl;
if (!initialized) {
FRG::logger() << "AudioPlayerAsync not initialized" << std::endl;
return; return;
} }
if (!soundEnabled_) return; if (!soundEnabled_) return;
auto task = [this, filePath, loops, channel]() { auto task = [this, filePath, loops, channel]() {
Mix_Chunk* sound = nullptr; Mix_Chunk* sound = nullptr;
{ {
@ -80,11 +90,34 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
sound = it->second; sound = it->second;
} }
} }
if (!sound) { if (!sound) {
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"AudioPlayerAsync::playSoundAsync step x2");
std::vector<char> buffer = FRG::readFile(filePath);
if (buffer.empty()) {
__android_log_print(ANDROID_LOG_ERROR, "Game",
"AudioPlayerAsync::playSoundAsync step x3");
FRG::logger() << "Failed to read sound file into buffer: " << filePath << std::endl;
return;
}
__android_log_print(ANDROID_LOG_ERROR, "Game",
"AudioPlayerAsync::playSoundAsync step x4");
SDL_RWops* rw = SDL_RWFromMem(buffer.data(), buffer.size());
if (!rw) {
FRG::logger() << "Failed to create RWops from buffer for " << filePath << ": " << SDL_GetError() << std::endl;
return;
}
sound = Mix_LoadWAV_RW(rw, 1); // 1 means it will free rw automatically
FRG::logger() << "sound = Mix_LoadWAV_RW(rw, 1); just fired " << std::endl;
#else
sound = Mix_LoadWAV(filePath.c_str()); sound = Mix_LoadWAV(filePath.c_str());
#endif
if (!sound) { if (!sound) {
std::cerr << "Failed to load sound " << filePath << ": " << Mix_GetError() << std::endl; FRG::logger() << "Failed to load sound " << filePath << ": " << Mix_GetError() << std::endl;
return; return;
} }
std::lock_guard<std::mutex> cacheLock(soundCacheMutex); std::lock_guard<std::mutex> cacheLock(soundCacheMutex);
@ -93,7 +126,7 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
int result = Mix_PlayChannel(channel, sound, loops); int result = Mix_PlayChannel(channel, sound, loops);
if (result == -1) { if (result == -1) {
std::cerr << "Mix_PlayChannel failed: " << Mix_GetError() << std::endl; FRG::logger() << "Mix_PlayChannel failed: " << Mix_GetError() << std::endl;
} }
}; };
@ -104,11 +137,11 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
taskQueue.push(std::move(task)); taskQueue.push(std::move(task));
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) { void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
/*if (!initialized) return; if (!initialized) return;
if (filePath == currentTrack) if (filePath == currentTrack)
return; return;
@ -122,14 +155,29 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
Mix_FreeMusic(currentMusic_); Mix_FreeMusic(currentMusic_);
currentMusic_ = nullptr; currentMusic_ = nullptr;
} }
Mix_Music* music = Mix_LoadMUS(filePath.c_str()); currentMusicData_.clear();
Mix_Music* music = nullptr;
#ifdef __ANDROID__
currentMusicData_ = FRG::readFile(filePath);
if (!currentMusicData_.empty()) {
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
if (rw) {
music = Mix_LoadMUS_RW(rw, 1);
}
}
#else
music = Mix_LoadMUS(filePath.c_str());
#endif
if (!music) { if (!music) {
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl; FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
return; return;
} }
if (Mix_PlayMusic(music, loops) == -1) { if (Mix_PlayMusic(music, loops) == -1) {
std::cerr << "Mix_PlayMusic failed: " << Mix_GetError() << std::endl; FRG::logger() << "Mix_PlayMusic failed: " << Mix_GetError() << std::endl;
Mix_FreeMusic(music); Mix_FreeMusic(music);
currentMusicData_.clear();
return; return;
} }
currentMusic_ = music; currentMusic_ = music;
@ -142,11 +190,11 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
taskQueue.push(std::move(task)); taskQueue.push(std::move(task));
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::stopMusicAsync() { void AudioPlayerAsync::stopMusicAsync() {
/*if (!initialized) return; if (!initialized) return;
currentTrack = ""; currentTrack = "";
@ -159,11 +207,11 @@ void AudioPlayerAsync::stopMusicAsync() {
}); });
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::pauseMusicAsync() { void AudioPlayerAsync::pauseMusicAsync() {
/*if (!initialized) return; if (!initialized) return;
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
Mix_PauseMusic(); Mix_PauseMusic();
#else #else
@ -173,11 +221,11 @@ void AudioPlayerAsync::pauseMusicAsync() {
}); });
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::resumeMusicAsync() { void AudioPlayerAsync::resumeMusicAsync() {
/*if (!initialized) return; if (!initialized) return;
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
Mix_ResumeMusic(); Mix_ResumeMusic();
#else #else
@ -187,11 +235,11 @@ void AudioPlayerAsync::resumeMusicAsync() {
}); });
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::setMusicVolume(int volume) { void AudioPlayerAsync::setMusicVolume(int volume) {
/*
if (!initialized) return; if (!initialized) return;
volume = std::max(0, std::min(128, volume)); volume = std::max(0, std::min(128, volume));
musicVolume_ = volume; musicVolume_ = volume;
@ -204,11 +252,11 @@ void AudioPlayerAsync::setMusicVolume(int volume) {
}); });
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::setSoundVolume(int volume) { void AudioPlayerAsync::setSoundVolume(int volume) {
/*
if (!initialized) return; if (!initialized) return;
volume = std::max(0, std::min(128, volume)); volume = std::max(0, std::min(128, volume));
soundVolume_ = volume; soundVolume_ = volume;
@ -221,11 +269,11 @@ void AudioPlayerAsync::setSoundVolume(int volume) {
}); });
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs, int loops) { void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs, int loops) {
/*if (!initialized) return; if (!initialized) return;
if (filePath == currentTrack) return; if (filePath == currentTrack) return;
currentTrack = filePath; currentTrack = filePath;
if (!musicEnabled_) return; if (!musicEnabled_) return;
@ -241,14 +289,26 @@ void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fade
} }
Mix_HaltMusic(); Mix_HaltMusic();
if (currentMusic_) { Mix_FreeMusic(currentMusic_); currentMusic_ = nullptr; } if (currentMusic_) { Mix_FreeMusic(currentMusic_); currentMusic_ = nullptr; }
currentMusicData_.clear();
#endif
Mix_Music* music = nullptr;
#ifdef __ANDROID__
currentMusicData_ = FRG::readFile(filePath);
if (!currentMusicData_.empty()) {
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
if (rw) {
music = Mix_LoadMUS_RW(rw, 1);
}
}
#else
music = Mix_LoadMUS(filePath.c_str());
#endif #endif
Mix_Music* music = Mix_LoadMUS(filePath.c_str());
if (!music) { if (!music) {
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl; FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
return; return;
} }
if (Mix_FadeInMusic(music, loops, fadeDurationMs / 2) == -1) { if (Mix_FadeInMusic(music, loops, fadeDurationMs / 2) == -1) {
std::cerr << "Mix_FadeInMusic failed: " << Mix_GetError() << std::endl; FRG::logger() << "Mix_FadeInMusic failed: " << Mix_GetError() << std::endl;
Mix_FreeMusic(music); Mix_FreeMusic(music);
return; return;
} }
@ -262,11 +322,11 @@ void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fade
taskQueue.push(std::move(task)); taskQueue.push(std::move(task));
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePath, int fadeDurationMs, int loops) { void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePath, int fadeDurationMs, int loops) {
/*if (!initialized) return; if (!initialized) return;
if (filePath == currentTrack) return; if (filePath == currentTrack) return;
currentTrack = filePath; currentTrack = filePath;
if (!musicEnabled_) return; if (!musicEnabled_) return;
@ -288,11 +348,11 @@ void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePa
Mix_Music* music = Mix_LoadMUS(filePath.c_str()); Mix_Music* music = Mix_LoadMUS(filePath.c_str());
if (!music) { if (!music) {
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl; FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
return; return;
} }
if (Mix_FadeInMusicPos(music, loops, fadeDurationMs / 2, position) == -1) { if (Mix_FadeInMusicPos(music, loops, fadeDurationMs / 2, position) == -1) {
std::cerr << "Mix_FadeInMusicPos failed: " << Mix_GetError() << std::endl; FRG::logger() << "Mix_FadeInMusicPos failed: " << Mix_GetError() << std::endl;
Mix_FreeMusic(music); Mix_FreeMusic(music);
return; return;
} }
@ -306,11 +366,11 @@ void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePa
taskQueue.push(std::move(task)); taskQueue.push(std::move(task));
cv.notify_one(); cv.notify_one();
#endif #endif
*/
} }
void AudioPlayerAsync::setMusicEnabled(bool enabled) { void AudioPlayerAsync::setMusicEnabled(bool enabled) {
/*if (musicEnabled_ == enabled) return; if (musicEnabled_ == enabled) return;
musicEnabled_ = enabled; musicEnabled_ = enabled;
if (!initialized) return; if (!initialized) return;
@ -330,7 +390,7 @@ void AudioPlayerAsync::setMusicEnabled(bool enabled) {
currentTrack = ""; // clear so crossFadeMusicAsync doesn't bail early currentTrack = ""; // clear so crossFadeMusicAsync doesn't bail early
crossFadeMusicAsync(track); crossFadeMusicAsync(track);
} }
}*/ }
} }
void AudioPlayerAsync::setSoundEnabled(bool enabled) { void AudioPlayerAsync::setSoundEnabled(bool enabled) {

View File

@ -1,13 +1,16 @@
#pragma once #pragma once
#ifdef __ANDROID__
#if 0 #include <SDL_mixer.h>
#include <SDL.h>
#else
#include <SDL2/SDL_mixer.h> #include <SDL2/SDL_mixer.h>
#include <SDL2/SDL.h>
#endif #endif
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <SDL2/SDL.h> #include <vector>
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
#include <queue> #include <queue>
@ -53,14 +56,15 @@ private:
std::queue<std::function<void()>> taskQueue; std::queue<std::function<void()>> taskQueue;
#endif #endif
bool stop = false; bool stop = false;
#if 0
std::unordered_map<std::string, Mix_Chunk*> soundCache; std::unordered_map<std::string, Mix_Chunk*> soundCache;
std::mutex soundCacheMutex; std::mutex soundCacheMutex;
std::string currentTrack; std::string currentTrack;
Mix_Music* currentMusic_ = nullptr; Mix_Music* currentMusic_ = nullptr;
#endif std::vector<char> currentMusicData_;
int musicVolume_ = 128; int musicVolume_ = 128;
int soundVolume_ = 128; int soundVolume_ = 128;
bool musicEnabled_ = true; bool musicEnabled_ = true;

View File

@ -1517,41 +1517,23 @@ namespace FRG
const int uiX = mx; const int uiX = mx;
const int uiY = Environment::projectionHeight - my; const int uiY = Environment::projectionHeight - my;
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 1");
#endif
menuManager.topUiManager.onTouchDown(fingerId, uiX, uiY); menuManager.topUiManager.onTouchDown(fingerId, uiX, uiY);
const bool capturedByTopUi = menuManager.topUiManager.isUiInteractionForFinger(fingerId); const bool capturedByTopUi = menuManager.topUiManager.isUiInteractionForFinger(fingerId);
const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive(); const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive();
if (!dialogueActive && !capturedByTopUi) { if (!dialogueActive && !capturedByTopUi) {
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 2");
#endif
menuManager.uiManager.onTouchDown(fingerId, uiX, uiY); menuManager.uiManager.onTouchDown(fingerId, uiX, uiY);
} }
st.capturedByUi = capturedByTopUi || (!dialogueActive && menuManager.uiManager.isUiInteractionForFinger(fingerId)); st.capturedByUi = capturedByTopUi || (!dialogueActive && menuManager.uiManager.isUiInteractionForFinger(fingerId));
activePointers[fingerId] = st; activePointers[fingerId] = st;
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 3");
#endif
if (st.capturedByUi) { if (st.capturedByUi) {
// UI button / slider / text-field grabbed this press; don't drive // UI button / slider / text-field grabbed this press; don't drive
// gameplay or pinch from it. // gameplay or pinch from it.
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 4");
#endif
return; return;
} }
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 5");
#endif
if (countNonUiPointers() >= 2) { if (countNonUiPointers() >= 2) {
// Second finger landed on the gameplay area: switch to pinch-zoom // Second finger landed on the gameplay area: switch to pinch-zoom

View File

@ -378,7 +378,7 @@ namespace FRG {
#ifndef EMSCRIPTEN #if !defined(EMSCRIPTEN) && !defined(__ANDROID__)
uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) { uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg"); audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
Environment::exitGameLoop = true; Environment::exitGameLoop = true;

View File

@ -206,7 +206,7 @@ int main(int argc, char* argv[]) {
"Shadow Over Bishkek", "Shadow Over Bishkek",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
0, 0, 0, 0,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
); );
if (!FRG::Environment::window) { if (!FRG::Environment::window) {