diff --git a/CMakeLists.txt b/CMakeLists.txt index b772fb72..db1dc456 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ if (WIN32) find_package(glfw3 REQUIRED) find_package(spng REQUIRED) find_package(glm REQUIRED) + find_package(libogg REQUIRED) set(PNGLIB spng::spng) else() find_package(Lua REQUIRED) @@ -118,7 +119,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() include_directories(${LUA_INCLUDE_DIR}) -target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ZLIB::ZLIB ${PNGLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) +target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ZLIB::ZLIB vorbis vorbisfile ${PNGLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/Dockerfile b/Dockerfile index 3785e02c..ad2fce9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ libpng-dev \ libopenal-dev \ libluajit-5.1-dev \ + libvorbis-dev \ ca-certificates \ && rm -rf /var/lib/apt/lists/* diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 55018716..bf97ff6f 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -7,6 +7,7 @@ #include "NoAudio.h" #include "../coders/wav.h" +#include "../coders/ogg.h" namespace audio { static speakerid_t nextId = 1; @@ -30,7 +31,9 @@ PCM* audio::loadPCM(const fs::path& file, bool headerOnly) { std::string ext = file.extension().u8string(); if (ext == ".wav" || ext == ".WAV") { return wav::load_pcm(file, headerOnly); - } // TODO: OGG support + } else if (ext == ".ogg" || ext == ".OGG") { + return ogg::load_pcm(file, headerOnly); + } throw std::runtime_error("unsupported audio format"); } diff --git a/src/coders/ogg.cpp b/src/coders/ogg.cpp new file mode 100644 index 00000000..641d15a7 --- /dev/null +++ b/src/coders/ogg.cpp @@ -0,0 +1,56 @@ +#include "ogg.h" + +#include +#include +#include + +#include "../audio/audio.h" +#include "../typedefs.h" + +static inline const char* vorbis_error_message(int code) { + switch (code) { + case 0: return "no error"; + case OV_EREAD: return "a read from media returned an error"; + case OV_ENOTVORBIS: return "bitstream does not contain any Vorbis data"; + case OV_EVERSION: return "vorbis version mismatch"; + case OV_EBADHEADER: return "invalid Vorbis bitstream header"; + case OV_EFAULT: return "internal logic fault"; + default: + return "unknown"; + } +} + +audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) { + OggVorbis_File vf; + int code; + if ((code = ov_fopen(file.u8string().c_str(), &vf))) { + throw std::runtime_error(vorbis_error_message(code)); + } + + std::vector data; + + vorbis_info* info = ov_info(&vf, -1); + uint channels = info->channels; + uint sampleRate = info->rate; + + if (!headerOnly) { + const int bufferSize = 4096; + int section = 0; + char buffer[bufferSize]; + + bool eof = false; + while (!eof) { + long ret = ov_read(&vf, buffer, bufferSize, 0, 2, true, §ion); + if (ret == 0) { + eof = true; + } else if (ret < 0) { + std::cerr << "ogg::load_pcm: " << vorbis_error_message(ret) << std::endl; + } else { + data.insert(data.end(), std::begin(buffer), std::end(buffer)); + } + } + } + + ov_clear(&vf); + return new audio::PCM(data, channels, 16, sampleRate); +} diff --git a/src/coders/ogg.h b/src/coders/ogg.h new file mode 100644 index 00000000..c1203be2 --- /dev/null +++ b/src/coders/ogg.h @@ -0,0 +1,14 @@ +#ifndef CODERS_OGG_H_ +#define CODERS_OGG_H_ + +#include + +namespace audio { + struct PCM; +} + +namespace ogg { + extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly); +} + +#endif // CODERS_OGG_H_