From 27ecda3124d7354794615e7789a9a39a12086691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D1=8C=D0=B1=D0=B5=D1=80=D1=82=20=D0=93=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=B5=D0=B2?= Date: Sat, 1 Mar 2025 18:20:50 +0600 Subject: [PATCH] added sounds and music in main cpp by cliking wasd or arrow will play sound of steps and music in setup start playing --- cmakeaudioplayer/include/AudioPlayer.hpp | 16 +++- cmakeaudioplayer/src/AudioPlayer.cpp | 91 ++++++++++++++----- main.cpp | 23 ++++- sounds/Звук-Идут-по-земле.ogg | Bin 0 -> 8986 bytes 4 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 sounds/Звук-Идут-по-земле.ogg diff --git a/cmakeaudioplayer/include/AudioPlayer.hpp b/cmakeaudioplayer/include/AudioPlayer.hpp index c8e2d12..b1d23f1 100644 --- a/cmakeaudioplayer/include/AudioPlayer.hpp +++ b/cmakeaudioplayer/include/AudioPlayer.hpp @@ -12,18 +12,26 @@ public: AudioPlayer(); ~AudioPlayer(); - bool playFromSoundsDir(const std::string& filename); + // Для музыки с зацикливанием (если filename пустой - продолжает играть текущую) + bool playMusic(const std::string& filename = ""); + + // Для одноразовых звуковых эффектов + bool playSound(const std::string& filename); + void stop(); bool isPlaying() const; private: ALCdevice* device; ALCcontext* context; - ALuint source; - ALuint buffer; + ALuint musicSource; // Источник для музыки + ALuint soundSource; // Источник для звуков + ALuint musicBuffer; // Буфер для музыки + ALuint soundBuffer; // Буфер для звуков bool playing; + std::string currentMusic; // Хранит имя текущего музыкального файла - std::vector loadOgg(const std::string& filename); + std::vector loadOgg(const std::string& filename, ALuint buffer); std::string findFileInSounds(const std::string& filename); bool isOggFile(const std::string& filename) const; }; diff --git a/cmakeaudioplayer/src/AudioPlayer.cpp b/cmakeaudioplayer/src/AudioPlayer.cpp index 4d37c27..e650ea9 100644 --- a/cmakeaudioplayer/src/AudioPlayer.cpp +++ b/cmakeaudioplayer/src/AudioPlayer.cpp @@ -6,7 +6,8 @@ #include #include -AudioPlayer::AudioPlayer() : device(nullptr), context(nullptr), source(0), buffer(0), playing(false) { +AudioPlayer::AudioPlayer() : device(nullptr), context(nullptr), + musicSource(0), soundSource(0), musicBuffer(0), soundBuffer(0), playing(false) { device = alcOpenDevice(nullptr); if (!device) { throw std::runtime_error("Failed to open audio device"); @@ -19,15 +20,21 @@ AudioPlayer::AudioPlayer() : device(nullptr), context(nullptr), source(0), buffe } alcMakeContextCurrent(context); - alGenSources(1, &source); - alGenBuffers(1, &buffer); + alGenSources(1, &musicSource); + alGenSources(1, &soundSource); + alGenBuffers(1, &musicBuffer); + alGenBuffers(1, &soundBuffer); } AudioPlayer::~AudioPlayer() { - if (source) - alDeleteSources(1, &source); - if (buffer) - alDeleteBuffers(1, &buffer); + if (musicSource) + alDeleteSources(1, &musicSource); + if (soundSource) + alDeleteSources(1, &soundSource); + if (musicBuffer) + alDeleteBuffers(1, &musicBuffer); + if (soundBuffer) + alDeleteBuffers(1, &soundBuffer); if (context) { alcMakeContextCurrent(nullptr); @@ -72,7 +79,7 @@ std::string AudioPlayer::findFileInSounds(const std::string& filename) { throw std::runtime_error("❌ File not found: " + filename); } -std::vector AudioPlayer::loadOgg(const std::string& filename) { +std::vector AudioPlayer::loadOgg(const std::string& filename, ALuint buffer) { FILE* file = fopen(filename.c_str(), "rb"); if (!file) { throw std::runtime_error("Cannot open file: " + filename); @@ -108,45 +115,79 @@ std::vector AudioPlayer::loadOgg(const std::string& filename) { return audioData; } -bool AudioPlayer::playFromSoundsDir(const std::string& filename) { +bool AudioPlayer::playMusic(const std::string& filename) { try { + // Если filename пустой, просто проверяем играет ли музыка + if (filename.empty()) { + if (!isPlaying()) { + alSourcei(musicSource, AL_LOOPING, AL_TRUE); // Включаем зацикливание + alSourcePlay(musicSource); + } + return true; + } + + // Если filename не пустой, загружаем новую музыку if (!isOggFile(filename)) { - std::cerr << "❌ Error: File must be an .ogg file\n"; + std::cerr << "❌ Error: Music file must be an .ogg file\n"; return false; } std::string fullPath = findFileInSounds(filename); - std::cout << "✅ Found file: " << fullPath << "\n"; + std::cout << "✅ Found music file: " << fullPath << "\n"; - auto audioData = loadOgg(fullPath); - alSourcei(source, AL_BUFFER, buffer); + // Останавливаем текущую музыку + alSourceStop(musicSource); - alGetError(); // Clear any previous errors + // Загружаем и настраиваем новую музыку + loadOgg(fullPath, musicBuffer); + alSourcei(musicSource, AL_BUFFER, musicBuffer); + alSourcei(musicSource, AL_LOOPING, AL_TRUE); // Включаем зацикливание - std::cout << "▶️ Starting playback...\n"; - alSourcePlay(source); - - ALenum error = alGetError(); - if (error != AL_NO_ERROR) { - std::cerr << "❌ OpenAL error: " << error << std::endl; - return false; - } + std::cout << "▶️ Starting music playback...\n"; + alSourcePlay(musicSource); + currentMusic = filename; playing = true; return true; } catch (const std::exception& e) { - std::cerr << "❌ Error: " << e.what() << std::endl; + std::cerr << "❌ Error playing music: " << e.what() << std::endl; + return false; + } +} + +bool AudioPlayer::playSound(const std::string& filename) { + try { + if (!isOggFile(filename)) { + std::cerr << "❌ Error: Sound file must be an .ogg file\n"; + return false; + } + + std::string fullPath = findFileInSounds(filename); + std::cout << "✅ Found sound file: " << fullPath << "\n"; + + // Загружаем и настраиваем звук + loadOgg(fullPath, soundBuffer); + alSourcei(soundSource, AL_BUFFER, soundBuffer); + alSourcei(soundSource, AL_LOOPING, AL_FALSE); // Выключаем зацикливание + + std::cout << "▶️ Playing sound effect...\n"; + alSourcePlay(soundSource); + + return true; + } catch (const std::exception& e) { + std::cerr << "❌ Error playing sound: " << e.what() << std::endl; return false; } } void AudioPlayer::stop() { - alSourceStop(source); + alSourceStop(musicSource); + alSourceStop(soundSource); playing = false; } bool AudioPlayer::isPlaying() const { ALint state; - alGetSourcei(source, AL_SOURCE_STATE, &state); + alGetSourcei(musicSource, AL_SOURCE_STATE, &state); return state == AL_PLAYING; } diff --git a/main.cpp b/main.cpp index d2013ff..33f4340 100755 --- a/main.cpp +++ b/main.cpp @@ -402,8 +402,11 @@ namespace ZL std::cout << "\nAfter removal:\n"; ZL::PrintInventory(); - // Initialize audio player + // Initialize audio player and start background music GameObjects::audioPlayer = std::make_unique(); + if (GameObjects::audioPlayer) { + GameObjects::audioPlayer->playMusic("Symphony No.6 (1st movement).ogg"); + } /// } @@ -457,25 +460,37 @@ namespace ZL case SDLK_LEFT: case SDLK_a: Env::leftPressed = true; + if (GameObjects::audioPlayer) { + GameObjects::audioPlayer->playSound("Звук-Идут-по-земле.ogg"); + } break; case SDLK_RIGHT: case SDLK_d: Env::rightPressed = true; + if (GameObjects::audioPlayer) { + GameObjects::audioPlayer->playSound("Звук-Идут-по-земле.ogg"); + } break; case SDLK_UP: case SDLK_w: Env::upPressed = true; + if (GameObjects::audioPlayer) { + GameObjects::audioPlayer->playSound("Звук-Идут-по-земле.ogg"); + } break; case SDLK_DOWN: case SDLK_s: Env::downPressed = true; + if (GameObjects::audioPlayer) { + GameObjects::audioPlayer->playSound("Звук-Идут-по-земле.ogg"); + } break; case SDLK_SPACE: // Play the symphony when space is pressed if (GameObjects::audioPlayer) { - GameObjects::audioPlayer->playFromSoundsDir("Symphony No.6 (1st movement).ogg"); - } - break; + GameObjects::audioPlayer->playMusic("Symphony No.6 (1st movement).ogg"); + } + break; } } diff --git a/sounds/Звук-Идут-по-земле.ogg b/sounds/Звук-Идут-по-земле.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fca9763c534f58ab987c19af6e1aba8ac71fdc87 GIT binary patch literal 8986 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 oxCk@bSwT7=jnn>%^!;5U@9l0=^Mt&&$RE