added sounds library
This commit is contained in:
parent
098f2b4005
commit
d9038fee44
35
cmakeaudioplayer/CMakeLists.txt
Normal file
35
cmakeaudioplayer/CMakeLists.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(AudioPlayer)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Use pkg-config to find Vorbis
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(VORBIS REQUIRED vorbis vorbisfile)
|
||||||
|
pkg_check_modules(OGG REQUIRED ogg)
|
||||||
|
find_package(OpenAL REQUIRED)
|
||||||
|
|
||||||
|
add_library(audioplayer
|
||||||
|
src/AudioPlayer.cpp
|
||||||
|
include/AudioPlayer.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(audioplayer
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
${OPENAL_INCLUDE_DIR}
|
||||||
|
${VORBIS_INCLUDE_DIRS}
|
||||||
|
${OGG_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(audioplayer
|
||||||
|
PUBLIC
|
||||||
|
${OPENAL_LIBRARY}
|
||||||
|
${VORBIS_LIBRARIES}
|
||||||
|
${OGG_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test executable
|
||||||
|
add_executable(test_audio examples/test_audio.cpp)
|
||||||
|
target_link_libraries(test_audio PRIVATE audioplayer stdc++fs)
|
||||||
32
cmakeaudioplayer/examples/test_audio.cpp
Normal file
32
cmakeaudioplayer/examples/test_audio.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "AudioPlayer.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
AudioPlayer player;
|
||||||
|
const std::string filename = "Symphony No.6 (1st movement).ogg";
|
||||||
|
|
||||||
|
std::cout << "🔍 Looking for file: " << filename << " in sounds directory...\n";
|
||||||
|
|
||||||
|
if (!player.playFromSoundsDir(filename)) {
|
||||||
|
std::cout << "❌ Failed to play audio file\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "✅ Playing symphony...\n";
|
||||||
|
|
||||||
|
// Check status for 30 seconds
|
||||||
|
for (int i = 0; i < 30; ++i) {
|
||||||
|
std::cout << "📊 Status: " << (player.isPlaying() ? "Playing ▶️" : "Stopped ⏹️") << "\n";
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "❌ Error: " << e.what() << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
cmakeaudioplayer/include/AudioPlayer.hpp
Normal file
29
cmakeaudioplayer/include/AudioPlayer.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#include <vorbis/vorbisfile.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class AudioPlayer {
|
||||||
|
public:
|
||||||
|
AudioPlayer();
|
||||||
|
~AudioPlayer();
|
||||||
|
|
||||||
|
bool playFromSoundsDir(const std::string& filename);
|
||||||
|
void stop();
|
||||||
|
bool isPlaying() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ALCdevice* device;
|
||||||
|
ALCcontext* context;
|
||||||
|
ALuint source;
|
||||||
|
ALuint buffer;
|
||||||
|
bool playing;
|
||||||
|
|
||||||
|
std::vector<char> loadOgg(const std::string& filename);
|
||||||
|
std::string findFileInSounds(const std::string& filename);
|
||||||
|
bool isOggFile(const std::string& filename) const;
|
||||||
|
};
|
||||||
145
cmakeaudioplayer/src/AudioPlayer.cpp
Normal file
145
cmakeaudioplayer/src/AudioPlayer.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include "AudioPlayer.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
AudioPlayer::AudioPlayer() : device(nullptr), context(nullptr), source(0), buffer(0), playing(false) {
|
||||||
|
device = alcOpenDevice(nullptr);
|
||||||
|
if (!device) {
|
||||||
|
throw std::runtime_error("Failed to open audio device");
|
||||||
|
}
|
||||||
|
|
||||||
|
context = alcCreateContext(device, nullptr);
|
||||||
|
if (!context) {
|
||||||
|
alcCloseDevice(device);
|
||||||
|
throw std::runtime_error("Failed to create audio context");
|
||||||
|
}
|
||||||
|
|
||||||
|
alcMakeContextCurrent(context);
|
||||||
|
alGenSources(1, &source);
|
||||||
|
alGenBuffers(1, &buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioPlayer::~AudioPlayer() {
|
||||||
|
if (source)
|
||||||
|
alDeleteSources(1, &source);
|
||||||
|
if (buffer)
|
||||||
|
alDeleteBuffers(1, &buffer);
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
alcMakeContextCurrent(nullptr);
|
||||||
|
alcDestroyContext(context);
|
||||||
|
}
|
||||||
|
if (device)
|
||||||
|
alcCloseDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioPlayer::isOggFile(const std::string& filename) const {
|
||||||
|
std::string ext = std::filesystem::path(filename).extension().string();
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||||
|
return ext == ".ogg";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AudioPlayer::findFileInSounds(const std::string& filename) {
|
||||||
|
// Check relative to executable location first (../../sounds)
|
||||||
|
std::filesystem::path soundsDir = std::filesystem::current_path() / ".." / ".." / "sounds";
|
||||||
|
|
||||||
|
// Fallback to ../sounds if not found
|
||||||
|
std::filesystem::path altSoundsDir = std::filesystem::current_path() / ".." / "sounds";
|
||||||
|
|
||||||
|
std::cout << "🔍 Searching for \"" << filename << "\" in:\n";
|
||||||
|
std::cout << " " << soundsDir << "\n";
|
||||||
|
std::cout << " " << altSoundsDir << "\n";
|
||||||
|
|
||||||
|
if (std::filesystem::exists(soundsDir / filename)) {
|
||||||
|
return (soundsDir / filename).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::filesystem::exists(altSoundsDir / filename)) {
|
||||||
|
return (altSoundsDir / filename).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("❌ File not found: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> AudioPlayer::loadOgg(const std::string& filename) {
|
||||||
|
FILE* file = fopen(filename.c_str(), "rb");
|
||||||
|
if (!file) {
|
||||||
|
throw std::runtime_error("Cannot open file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
OggVorbis_File vf;
|
||||||
|
if (ov_open_callbacks(file, &vf, nullptr, 0, OV_CALLBACKS_DEFAULT) < 0) {
|
||||||
|
fclose(file);
|
||||||
|
throw std::runtime_error("Input not an Ogg file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
vorbis_info* vi = ov_info(&vf, -1);
|
||||||
|
std::vector<char> audioData;
|
||||||
|
char data[4096];
|
||||||
|
int bitstream;
|
||||||
|
long bytes;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytes = ov_read(&vf, data, sizeof(data), 0, 2, 1, &bitstream);
|
||||||
|
if (bytes > 0) {
|
||||||
|
audioData.insert(audioData.end(), data, data + bytes);
|
||||||
|
}
|
||||||
|
} while (bytes > 0);
|
||||||
|
|
||||||
|
// Setup the buffer with the audio data
|
||||||
|
alBufferData(buffer,
|
||||||
|
(vi->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
|
||||||
|
audioData.data(),
|
||||||
|
audioData.size(),
|
||||||
|
vi->rate);
|
||||||
|
|
||||||
|
ov_clear(&vf);
|
||||||
|
return audioData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioPlayer::playFromSoundsDir(const std::string& filename) {
|
||||||
|
try {
|
||||||
|
if (!isOggFile(filename)) {
|
||||||
|
std::cerr << "❌ Error: File must be an .ogg file\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fullPath = findFileInSounds(filename);
|
||||||
|
std::cout << "✅ Found file: " << fullPath << "\n";
|
||||||
|
|
||||||
|
auto audioData = loadOgg(fullPath);
|
||||||
|
alSourcei(source, AL_BUFFER, buffer);
|
||||||
|
|
||||||
|
alGetError(); // Clear any previous errors
|
||||||
|
|
||||||
|
std::cout << "▶️ Starting playback...\n";
|
||||||
|
alSourcePlay(source);
|
||||||
|
|
||||||
|
ALenum error = alGetError();
|
||||||
|
if (error != AL_NO_ERROR) {
|
||||||
|
std::cerr << "❌ OpenAL error: " << error << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
playing = true;
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "❌ Error: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::stop() {
|
||||||
|
alSourceStop(source);
|
||||||
|
playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioPlayer::isPlaying() const {
|
||||||
|
ALint state;
|
||||||
|
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||||
|
return state == AL_PLAYING;
|
||||||
|
}
|
||||||
BIN
sounds/Symphony No.6 (1st movement).ogg
Normal file
BIN
sounds/Symphony No.6 (1st movement).ogg
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user