diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f5fdc27..e09fe1d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,16 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install -y libsdl2-dev libzip-dev libogg-dev libvorbis-dev libtinyxml2-dev zipcmp zipmerge ziptool + sudo apt install -y libsdl2-dev libzip-dev libogg-dev libvorbis-dev libopus-dev libopusfile-dev libopusenc-dev opus-tools libtinyxml2-dev zipcmp zipmerge ziptool + - name: Build libopusenc + run: | + git clone https://gitlab.xiph.org/xiph/libopusenc.git + cd libopusenc + ./autogen.sh + ./configure + make -j 10 + sudo make install + sudo ldconfig - name: Build run: | cmake -S. -Bbuild-rel -DCMAKE_BUILD_TYPE:STRING=Release @@ -38,7 +47,16 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install -y libsdl2-dev libzip-dev libogg-dev libvorbis-dev libtinyxml2-dev zipcmp zipmerge ziptool + sudo apt install -y libsdl2-dev libzip-dev libogg-dev libvorbis-dev libopus-dev libopusfile-dev opus-tools libtinyxml2-dev zipcmp zipmerge ziptool + - name: Build libopusenc + run: | + git clone https://gitlab.xiph.org/xiph/libopusenc.git + cd libopusenc + ./autogen.sh + ./configure + make -j 10 + sudo make install + sudo ldconfig - name: Build run: | cmake -S. -Bbuild-rel -DCMAKE_BUILD_TYPE:STRING=Release diff --git a/CMake/FindOgg.cmake b/CMake/FindOgg.cmake new file mode 100644 index 0000000..9cf5ce4 --- /dev/null +++ b/CMake/FindOgg.cmake @@ -0,0 +1,61 @@ +# - Find ogg +# Find the native ogg includes and libraries +# +# OGG_INCLUDE_DIRS - where to find ogg.h, etc. +# OGG_LIBRARIES - List of libraries when using ogg. +# OGG_FOUND - True if ogg found. + +if (OGG_INCLUDE_DIR) + # Already in cache, be silent + set(OGG_FIND_QUIETLY TRUE) +endif () + +find_package (PkgConfig QUIET) +pkg_check_modules (PC_OGG QUIET ogg>=1.3.0) + +set (OGG_VERSION ${PC_OGG_VERSION}) + +find_path (OGG_INCLUDE_DIR ogg/ogg.h + HINTS + ${PC_OGG_INCLUDEDIR} + ${PC_OGG_INCLUDE_DIRS} + ${OGG_ROOT} + ) +# MSVC built ogg may be named ogg_static. +# The provided project files name the library with the lib prefix. +find_library (OGG_LIBRARY + NAMES + ogg + ogg_static + libogg + libogg_static + HINTS + ${PC_OGG_LIBDIR} + ${PC_OGG_LIBRARY_DIRS} + ${OGG_ROOT} + ) +# Handle the QUIETLY and REQUIRED arguments and set OGG_FOUND +# to TRUE if all listed variables are TRUE. +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args (Ogg + REQUIRED_VARS + OGG_LIBRARY + OGG_INCLUDE_DIR + VERSION_VAR + OGG_VERSION + ) + +if (OGG_FOUND) + set (OGG_LIBRARIES ${OGG_LIBRARY}) + set (OGG_INCLUDE_DIRS ${OGG_INCLUDE_DIR}) + + if(NOT TARGET Ogg::ogg) + add_library(Ogg::ogg UNKNOWN IMPORTED) + set_target_properties(Ogg::ogg PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${OGG_INCLUDE_DIRS}" + IMPORTED_LOCATION "${OGG_LIBRARIES}" + ) + endif () +endif () + +mark_as_advanced (OGG_INCLUDE_DIR OGG_LIBRARY) diff --git a/CMake/FindOpus.cmake b/CMake/FindOpus.cmake new file mode 100644 index 0000000..ce7b7b2 --- /dev/null +++ b/CMake/FindOpus.cmake @@ -0,0 +1,44 @@ +# - FindOpus.cmake +# Find the native opus includes and libraries +# +# OPUS_INCLUDE_DIRS - where to find opus/opus.h, etc. +# OPUS_LIBRARIES - List of libraries when using libopus(file). +# OPUS_FOUND - True if libopus found. + +if(OPUS_INCLUDE_DIR AND OPUS_LIBRARY AND OPUSFILE_LIBRARY) + # Already in cache, be silent + set(OPUS_FIND_QUIETLY TRUE) +endif(OPUS_INCLUDE_DIR AND OPUS_LIBRARY AND OPUSFILE_LIBRARY) + +find_path(OPUS_INCLUDE_DIR + NAMES opusfile.h + PATH_SUFFIXES opus +) + +# MSVC built opus may be named opus_static +# The provided project files name the library with the lib prefix. +find_library(OPUS_LIBRARY + NAMES opus opus_static libopus libopus_static +) +find_library(OPUSFILE_LIBRARY + NAMES opusfile opusfile_static libopusfile libopusfile_static +) + +# Handle the QUIETLY and REQUIRED arguments and set OPUS_FOUND +# to TRUE if all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Opus DEFAULT_MSG + OPUSFILE_LIBRARY OPUS_LIBRARY OPUS_INCLUDE_DIR +) + +if(OPUS_FOUND) + set(OPUS_LIBRARIES ${OPUSFILE_LIBRARY} ${OPUS_LIBRARY}) + set(OPUS_INCLUDE_DIRS ${OPUS_INCLUDE_DIR}) + if(NOT TARGET Opus::opus) + add_library(Opus::opus UNKNOWN IMPORTED) + set_target_properties(Opus::opus PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${OPUS_INCLUDE_DIRS}" + IMPORTED_LOCATION "${OPUS_LIBRARIES}" + ) + endif() +endif(OPUS_FOUND) diff --git a/CMake/FindOpusenc.cmake b/CMake/FindOpusenc.cmake new file mode 100644 index 0000000..330fdf7 --- /dev/null +++ b/CMake/FindOpusenc.cmake @@ -0,0 +1,51 @@ +# FindOpusenc.cmake +# This module locates the opusenc library and its headers. +# It defines the following variables: +# - Opusenc_FOUND: Boolean indicating if opusenc was found +# - Opusenc_INCLUDE_DIRS: Include directories for opusenc +# - Opusenc_LIBRARIES: Libraries to link against opusenc +# - Opusenc_VERSION: Version of the found opusenc library (optional) + +# Locate the opusenc header +find_path(Opusenc_INCLUDE_DIR + NAMES opusenc.h + HINTS + ${CMAKE_PREFIX_PATH} + PATH_SUFFIXES + include + opus +) + +# Locate the opusenc library +find_library(Opusenc_LIBRARY + NAMES opusenc + HINTS + ${CMAKE_PREFIX_PATH} + PATH_SUFFIXES + lib +) + +# Check if both the library and headers were found +if (Opusenc_INCLUDE_DIR AND Opusenc_LIBRARY) + set(Opusenc_FOUND TRUE) +else () + set(Opusenc_FOUND FALSE) +endif () + +# Provide include directories and libraries if found +if (Opusenc_FOUND) + set(Opusenc_INCLUDE_DIRS ${Opusenc_INCLUDE_DIR}) + set(Opusenc_LIBRARIES ${Opusenc_LIBRARY}) + if(NOT TARGET Opus::opusenc) + add_library(Opus::opusenc UNKNOWN IMPORTED) + set_target_properties(Opus::opusenc PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Opusenc_INCLUDE_DIRS}" + IMPORTED_LOCATION "${Opusenc_LIBRARIES}" + ) + endif() + # Optional: Retrieve library version + set(Opusenc_VERSION "Unknown") # Adjust if version detection is needed +endif () + +# Mark variables as advanced to reduce clutter +mark_as_advanced(Opusenc_INCLUDE_DIR Opusenc_LIBRARY) diff --git a/CMake/FindVorbis.cmake b/CMake/FindVorbis.cmake new file mode 100644 index 0000000..57e6055 --- /dev/null +++ b/CMake/FindVorbis.cmake @@ -0,0 +1,210 @@ +#[=======================================================================[.rst: +FindVorbis +---------- + +Finds the native vorbis, vorbisenc amd vorbisfile includes and libraries. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``Vorbis::vorbis`` + The Vorbis library +``Vorbis::vorbisenc`` + The VorbisEnc library +``Vorbis::vorbisfile`` + The VorbisFile library + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``Vorbis_Vorbis_INCLUDE_DIRS`` + List of include directories when using vorbis. +``Vorbis_Enc_INCLUDE_DIRS`` + List of include directories when using vorbisenc. +``Vorbis_File_INCLUDE_DIRS`` + List of include directories when using vorbisfile. +``Vorbis_Vorbis_LIBRARIES`` + List of libraries when using vorbis. +``Vorbis_Enc_LIBRARIES`` + List of libraries when using vorbisenc. +``Vorbis_File_LIBRARIES`` + List of libraries when using vorbisfile. +``Vorbis_FOUND`` + True if vorbis and requested components found. +``Vorbis_Vorbis_FOUND`` + True if vorbis found. +``Vorbis_Enc_FOUND`` + True if vorbisenc found. +``Vorbis_Enc_FOUND`` + True if vorbisfile found. + +Cache variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``Vorbis_Vorbis_INCLUDE_DIR`` + The directory containing ``vorbis/vorbis.h``. +``Vorbis_Enc_INCLUDE_DIR`` + The directory containing ``vorbis/vorbisenc.h``. +``Vorbis_File_INCLUDE_DIR`` + The directory containing ``vorbis/vorbisenc.h``. +``Vorbis_Vorbis_LIBRARY`` + The path to the vorbis library. +``Vorbis_Enc_LIBRARY`` + The path to the vorbisenc library. +``Vorbis_File_LIBRARY`` + The path to the vorbisfile library. + +Hints +^^^^^ + +A user may set ``Vorbis_ROOT`` to a vorbis installation root to tell this module where to look. + +#]=======================================================================] + +if (Vorbis_Vorbis_INCLUDE_DIR) + # Already in cache, be silent + set (Vorbis_FIND_QUIETLY TRUE) +endif () + +set (Vorbis_Vorbis_FIND_QUIETLY TRUE) +set (Vorbis_Enc_FIND_QUIETLY TRUE) +set (Vorbis_File_FIND_QUIETLY TRUE) + +find_package (Ogg QUIET) + +find_package (PkgConfig QUIET) +pkg_check_modules (PC_Vorbis_Vorbis QUIET vorbis) +pkg_check_modules (PC_Vorbis_Enc QUIET vorbisenc) +pkg_check_modules (PC_Vorbis_File QUIET vorbisfile) + +set (Vorbis_VERSION ${PC_Vorbis_Vorbis_VERSION}) + +find_path (Vorbis_Vorbis_INCLUDE_DIR vorbis/codec.h + HINTS + ${PC_Vorbis_Vorbis_INCLUDEDIR} + ${PC_Vorbis_Vorbis_INCLUDE_DIRS} + ${Vorbis_ROOT} + ) + +find_path (Vorbis_Enc_INCLUDE_DIR vorbis/vorbisenc.h + HINTS + ${PC_Vorbis_Enc_INCLUDEDIR} + ${PC_Vorbis_Enc_INCLUDE_DIRS} + ${Vorbis_ROOT} + ) + +find_path (Vorbis_File_INCLUDE_DIR vorbis/vorbisfile.h + HINTS + ${PC_Vorbis_File_INCLUDEDIR} + ${PC_Vorbis_File_INCLUDE_DIRS} + ${Vorbis_ROOT} + ) + +find_library (Vorbis_Vorbis_LIBRARY + NAMES + vorbis + vorbis_static + libvorbis + libvorbis_static + HINTS + ${PC_Vorbis_Vorbis_LIBDIR} + ${PC_Vorbis_Vorbis_LIBRARY_DIRS} + ${Vorbis_ROOT} + ) + +find_library (Vorbis_Enc_LIBRARY + NAMES + vorbisenc + vorbisenc_static + libvorbisenc + libvorbisenc_static + HINTS + ${PC_Vorbis_Enc_LIBDIR} + ${PC_Vorbis_Enc_LIBRARY_DIRS} + ${Vorbis_ROOT} + ) + +find_library (Vorbis_File_LIBRARY + NAMES + vorbisfile + vorbisfile_static + libvorbisfile + libvorbisfile_static + HINTS + ${PC_Vorbis_File_LIBDIR} + ${PC_Vorbis_File_LIBRARY_DIRS} + ${Vorbis_ROOT} + ) + +include (FindPackageHandleStandardArgs) + +if (Vorbis_Vorbis_LIBRARY AND Vorbis_Vorbis_INCLUDE_DIR AND Ogg_FOUND) + set (Vorbis_Vorbis_FOUND TRUE) +endif () + +if (Vorbis_Enc_LIBRARY AND Vorbis_Enc_INCLUDE_DIR AND Vorbis_Vorbis_FOUND) + set (Vorbis_Enc_FOUND TRUE) +endif () + +if (Vorbis_Vorbis_FOUND AND Vorbis_File_LIBRARY AND Vorbis_File_INCLUDE_DIR) + set (Vorbis_File_FOUND TRUE) +endif () + +find_package_handle_standard_args (Vorbis + REQUIRED_VARS + Vorbis_Vorbis_LIBRARY + Vorbis_Vorbis_INCLUDE_DIR + Ogg_FOUND + HANDLE_COMPONENTS + VERSION_VAR Vorbis_VERSION) + + +if (Vorbis_Vorbis_FOUND) + set (Vorbis_Vorbis_INCLUDE_DIRS ${VORBIS_INCLUDE_DIR}) + set (Vorbis_Vorbis_LIBRARIES ${VORBIS_LIBRARY} ${OGG_LIBRARIES}) + if (NOT TARGET Vorbis::vorbis) + add_library (Vorbis::vorbis UNKNOWN IMPORTED) + set_target_properties (Vorbis::vorbis PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_Vorbis_INCLUDE_DIR}" + IMPORTED_LOCATION "${Vorbis_Vorbis_LIBRARY}" + INTERFACE_LINK_LIBRARIES Ogg::ogg + ) + endif () + + if (Vorbis_Enc_FOUND) + set (Vorbis_Enc_INCLUDE_DIRS ${Vorbis_Enc_INCLUDE_DIR}) + set (Vorbis_Enc_LIBRARIES ${Vorbis_Enc_LIBRARY} ${Vorbis_Enc_LIBRARIES}) + if (NOT TARGET Vorbis::vorbisenc) + add_library (Vorbis::vorbisenc UNKNOWN IMPORTED) + set_target_properties (Vorbis::vorbisenc PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_Enc_INCLUDE_DIR}" + IMPORTED_LOCATION "${Vorbis_Enc_LIBRARY}" + INTERFACE_LINK_LIBRARIES Vorbis::vorbis + ) + endif () + endif () + + if (Vorbis_File_FOUND) + set (Vorbis_File_INCLUDE_DIRS ${Vorbis_File_INCLUDE_DIR}) + set (Vorbis_File_LIBRARIES ${Vorbis_File_LIBRARY} ${Vorbis_File_LIBRARIES}) + if (NOT TARGET Vorbis::vorbisfile) + add_library (Vorbis::vorbisfile UNKNOWN IMPORTED) + set_target_properties (Vorbis::vorbisfile PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_File_INCLUDE_DIR}" + IMPORTED_LOCATION "${Vorbis_File_LIBRARY}" + INTERFACE_LINK_LIBRARIES Vorbis::vorbis + ) + endif () + endif () + +endif () + +mark_as_advanced (Vorbis_Vorbis_INCLUDE_DIR Vorbis_Vorbis_LIBRARY) +mark_as_advanced (Vorbis_Enc_INCLUDE_DIR Vorbis_Enc_LIBRARY) +mark_as_advanced (Vorbis_File_INCLUDE_DIR Vorbis_File_LIBRARY) diff --git a/CMakeLists.txt b/CMakeLists.txt index 354ab8e..c99954a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,7 @@ elseif ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "arm64") endif() vcpkg_bootstrap() -vcpkg_install_packages(zlib bzip2 libzip libogg libvorbis) +vcpkg_install_packages(zlib bzip2 libzip libogg opus libopusenc libvorbis) endif() @@ -115,7 +115,9 @@ FetchContent_Declare( PATCH_COMMAND ${stormlib_apply_path_if_needed} ) FetchContent_MakeAvailable(StormLib) -list(APPEND ADDITIONAL_LIB_INCLUDES extern/StormLib/src) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") + include_directories(future, extern/StormLib/src) target_link_libraries(future PUBLIC storm) @@ -125,6 +127,7 @@ target_link_libraries(future PRIVATE libzip::zip ) file (COPY assets/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/assets) +list(APPEND CMAKE_MODULE_PATH "CMake") if ((CMAKE_SYSTEM_NAME STREQUAL "Linux") OR (CMAKE_SYSTEM_NAME STREQUAL "Darwin")) @@ -134,29 +137,40 @@ target_link_libraries(future PRIVATE SDL2::SDL2) find_package(OpenGL) target_link_libraries(future PRIVATE OpenGL::GL) -find_library(OGG_LIB ogg) -target_link_libraries(future PRIVATE "${OGG_LIB}") +find_package(Ogg) +target_link_libraries(Ogg::ogg) -find_library(VORBIS_LIB vorbis) -target_link_libraries(future PRIVATE "${VORBIS_LIB}") +find_package(Vorbis REQUIRED) +target_link_libraries(future PRIVATE Vorbis::vorbis) +target_link_libraries(future PRIVATE Vorbis::vorbisfile) +target_link_libraries(future PRIVATE Vorbis::vorbisenc) -find_library(VORBISFILE_LIB vorbisfile) -target_link_libraries(future PRIVATE "${VORBISFILE_LIB}") +find_package(Opus) +target_link_libraries(future PRIVATE ${OPUS_LIBRARY}) -find_library(VORBISENC_LIB vorbisenc) -target_link_libraries(future PRIVATE "${VORBISENC_LIB}") +find_package(Opusenc) +target_link_libraries(future PRIVATE ${Opusenc_LIBRARY}) elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - - find_package(Ogg CONFIG REQUIRED) target_link_libraries(future PRIVATE Ogg::ogg) -find_package(Vorbis CONFIG REQUIRED) +find_package(Opus) +target_link_libraries(future PRIVATE ${OPUS_LIBRARY}) + +find_package(Opusenc) +target_link_libraries(future PRIVATE ${Opusenc_LIBRARY}) + +find_package(Vorbis REQUIRED) +target_link_libraries(future PRIVATE Vorbis::vorbis) target_link_libraries(future PRIVATE Vorbis::vorbisfile) +target_link_libraries(future PRIVATE Vorbis::vorbisenc) + endif() +target_include_directories(future PRIVATE ${OPUS_INCLUDE_DIR}) + if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CPACK_GENERATOR "External") elseif(CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch|CafeOS") diff --git a/windows/CustomStreamedAudio.cpp b/windows/CustomStreamedAudio.cpp index d5b1ab6..6f999f6 100644 --- a/windows/CustomStreamedAudio.cpp +++ b/windows/CustomStreamedAudio.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "mio.hpp" #undef max @@ -100,6 +102,12 @@ constexpr static std::array audioTypeToStr = { "ogg", "flac", }; +// TODO simd +static void PcmS16ToF(float* dst, int16_t* src, uint64_t numFrames) { + for (size_t i = 0; i < numFrames; i++) { + dst[i] = src[i] / 32768.0f; + } +} // Write `data` to either the archive, or if the archive is null, a file on disk static void WriteFileData(char* path, void* data, size_t size, Archive* a) { @@ -388,6 +396,30 @@ static OggType GetOggType(OggFileData* data) { } +static int OpeWriteCallback(void* vData, const unsigned char* ptr, opus_int32 len) { + OggFileData* data = static_cast(vData); + size_t toRead = len; + + if (toRead > data->size - data->pos) { + toRead = data->size - data->pos; + } + + memcpy((char*)data->data + data->pos, ptr, toRead); + data->pos += toRead; + + return 0; +} + +static int OpeCloseCallback(void* data) { + return 0; +} + +static const OpusEncCallbacks opusCbs = { + OpeWriteCallback, + OpeCloseCallback, +}; + + static size_t VorbisReadCallback(void* out, size_t size, size_t elems, void* src) { OggFileData* data = static_cast(src); size_t toRead = size * elems; @@ -476,7 +508,26 @@ typedef struct ChannelInfo { size_t channelSizes[2]; } ChannelInfo; -static void SplitOgg(ChannelInfo* info, std::unique_ptr channels[2], uint64_t sampleRate, uint64_t numFrames, size_t fileSize) { +static void SplitOggVorbis(ChannelInfo* info, std::unique_ptr channels[2], uint64_t* sampleRate, uint64_t numFrames, size_t fileSize) { + for (size_t i = 0; i < 2; i++) { + OggFileData data; + data.data = malloc(fileSize); + data.size = fileSize; + data.pos = 0; + OggOpusComments* comments = ope_comments_create(); + ope_comments_add(comments, "ENCODER", "future using libopus libopusenc"); + + // TODO, hardcoded to 48KHz even if the original song isn't. Should we resample it to 48k? + OggOpusEnc* enc = ope_encoder_create_callbacks(&opusCbs, &data, comments, 48000, 1, 0, nullptr); + ope_encoder_write_float(enc, channels[i].get(), numFrames); + ope_encoder_drain(enc); + ope_encoder_destroy(enc); + info->channelData[i] = data.data; + info->channelSizes[i] = data.pos; + } + + + #if 0 MemoryBuffer outBuffer[2]; // fileSize will likely always be larger than the original file but it will never need to reallocate the buffer. init_memory_buffer(&outBuffer[0], fileSize); @@ -571,6 +622,7 @@ static void SplitOgg(ChannelInfo* info, std::unique_ptr channels[2], ui info->channelData[1] = outBuffer[1].data; info->channelSizes[0] = outBuffer[0].size; info->channelSizes[1] = outBuffer[1].size; + #endif } static void WriteWavData(int16_t* l, int16_t* r, ChannelInfo* info, uint64_t numFrames, uint64_t sampleRate) { @@ -595,7 +647,7 @@ static void WriteWavData(int16_t* l, int16_t* r, ChannelInfo* info, uint64_t num drwav_uninit(&outWav); } -static void ProcessAudioFile(std::vector* fileQueue, std::unordered_map* seqMetaMap, bool loopTimeInSamples, Archive* a) { +static void ProcessAudioFile(std::vector* fileQueue, std::unordered_map* seqMetaMap, bool loopTimeInSamples, bool transcodeToOpus, Archive* a) { while (filesProcessed < fileQueue->size()) { char** inputp = &(*fileQueue)[filesProcessed.fetch_add(1, std::memory_order_relaxed)]; char* input = *inputp; @@ -649,7 +701,7 @@ static void ProcessAudioFile(std::vector* fileQueue, std::unordered_map* fileQueue, std::unordered_map(numFrames * numChannels); drwav_read_pcm_frames_s16(&wav, numFrames, sampleData.get()); + auto sampleDataL = std::make_unique(numFrames); auto sampleDataR = std::make_unique(numFrames); size_t pos = 0; @@ -672,7 +725,17 @@ static void ProcessAudioFile(std::vector* fileQueue, std::unordered_map channelDataF[2]; + channelDataF[0] = std::make_unique(numFrames); + channelDataF[1] = std::make_unique(numFrames); + PcmS16ToF(channelDataF[0].get(), sampleDataL.get(), numFrames); + PcmS16ToF(channelDataF[1].get(), sampleDataR.get(), numFrames); + SplitOggVorbis(&infos, channelDataF, &sampleRate, numFrames, fileSize); + audioType = AudioType::ogg; + } } } else if (FLAC_CHECK(dataU8)) { audioType = AudioType::flac; @@ -691,8 +754,18 @@ static void ProcessAudioFile(std::vector* fileQueue, std::unordered_map channelDataF[2]; + channelDataF[0] = std::make_unique(numFrames); + channelDataF[1] = std::make_unique(numFrames); + PcmS16ToF(channelDataF[0].get(), sampleDataL.get(), numFrames); + PcmS16ToF(channelDataF[1].get(), sampleDataR.get(), numFrames); + SplitOggVorbis(&infos, channelDataF, &sampleRate, numFrames, fileSize); + audioType = AudioType::ogg; + } } drflac_close(flac); } @@ -711,30 +784,30 @@ static void ProcessAudioFile(std::vector* fileQueue, std::unordered_maprate; - numChannels = vi->channels; - channels[0] = std::make_unique(numFrames); - channels[1] = std::make_unique(numFrames); - - do { - float** pcm; - int bitStream; - read = ov_read_float(&vf, &pcm, 4096, &bitStream); - memcpy(&channels[0].get()[pos], pcm[0], read * sizeof(float)); - memcpy(&channels[1].get()[pos], pcm[1], read * sizeof(float)); - pos += read; - } while (read > 0); - - if (numChannels == 2) { - SplitOgg(&infos, channels, sampleRate, numFrames, fileSize); + int ret = ov_open_callbacks(&fileData, &vf, nullptr, 0, cbs); + + vorbis_info* vi = ov_info(&vf, -1); + + numFrames = ov_pcm_total(&vf, -1); + sampleRate = vi->rate; + numChannels = vi->channels; + channels[0] = std::make_unique(numFrames); + channels[1] = std::make_unique(numFrames); + + do { + float** pcm; + int bitStream; + read = ov_read_float(&vf, &pcm, 4096, &bitStream); + memcpy(&channels[0].get()[pos], pcm[0], read * sizeof(float)); + memcpy(&channels[1].get()[pos], pcm[1], read * sizeof(float)); + pos += read; + } while (read > 0); + + if (numChannels == 2) { + SplitOggVorbis(&infos, channels, &sampleRate, numFrames, fileSize); + } } } - } else { return; } @@ -779,10 +852,10 @@ static void PackFilesMgrWorker(std::vector* fileQueue, std::unordered_map } } - const unsigned int numThreads = 1;// std::thread::hardware_concurrency(); + const unsigned int numThreads = std::thread::hardware_concurrency(); auto packFileThreads = std::make_unique(numThreads); for (unsigned int i = 0; i < numThreads; i++) { - packFileThreads[i] = std::thread(ProcessAudioFile, fileQueue, fanfareMap, thisx->GetLoopTimeType(), a.get()); + packFileThreads[i] = std::thread(ProcessAudioFile, fileQueue, fanfareMap, thisx->GetLoopTimeType(), thisx->GetTranscode(), a.get()); } for (unsigned int i = 0; i < numThreads; i++) { @@ -799,19 +872,22 @@ static std::array sToggleLabels = { "Create Archive", }; -int CustomStreamedAudioWindow::GetRadioState() { +int CustomStreamedAudioWindow::GetRadioState() const { return mRadioState; } -char* CustomStreamedAudioWindow::GetSavePath() { +char* CustomStreamedAudioWindow::GetSavePath() const { return mSavePath; } -bool CustomStreamedAudioWindow::GetLoopTimeType() -{ +bool CustomStreamedAudioWindow::GetLoopTimeType() const { return mLoopIsISamples; } +bool CustomStreamedAudioWindow::GetTranscode() const { + return mTranscodeToOpus; +} + static bool FillFileCallback(char* path) { char* ext = strrchr(path, '.'); if (ext != nullptr) { @@ -868,6 +944,10 @@ void CustomStreamedAudioWindow::DrawWindow() { ImGui::SameLine(); ImGui::RadioButton("O2R", &mRadioState, 2); + ImGui::SameLine(); + ImGui::Checkbox("Transcode to opus", &mTranscodeToOpus); + ImGui::SetItemTooltip("Transcode uncompressed files to the opus codec.\nRecommended because of its speed and space savings."); + if (ImGui::Button("Set Save Path")) { GetSaveFilePath(&mSavePath); } diff --git a/windows/CustomStreamedAudio.h b/windows/CustomStreamedAudio.h index 94c0a57..6bd3b07 100644 --- a/windows/CustomStreamedAudio.h +++ b/windows/CustomStreamedAudio.h @@ -21,9 +21,10 @@ class CustomStreamedAudioWindow : public WindowBase { CustomStreamedAudioWindow(); ~CustomStreamedAudioWindow(); void DrawWindow(); - int GetRadioState(); - char* GetSavePath(); - bool GetLoopTimeType(); + int GetRadioState() const; + char* GetSavePath() const; + bool GetLoopTimeType() const; + bool GetTranscode() const; private: void DrawPendingFilesList(); void ClearPathBuff(); @@ -40,6 +41,7 @@ class CustomStreamedAudioWindow : public WindowBase { bool mThreadIsDone = false; bool mPackAsArchive = true; bool mLoopIsISamples = false; + bool mTranscodeToOpus = true; }; #endif