From 826d1607719e63aaedc4f1ee89a2a4b27f371c06 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Sun, 17 Nov 2024 21:00:39 +0530 Subject: [PATCH 1/7] Stop using C macros --- internal/c/parts/audio/audio.cpp | 23 ++++++++------- .../c/parts/audio/extras/libmidi/framework.h | 6 ++-- internal/c/parts/audio/framework.h | 11 ++++++-- internal/c/parts/video/image/image.cpp | 28 ++++++++++--------- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index 72ba5373c..5bac59e47 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -249,8 +249,7 @@ struct AudioEngine { return nullptr; } - ZERO_VARIABLE(pRawStream->maDataSource); - + pRawStream->maDataSource = {}; pRawStream->maDataSourceConfig = ma_data_source_config_init(); pRawStream->maDataSourceConfig.vtable = &rawStreamDataSourceVtable; // attach the vtable to the data source @@ -823,7 +822,7 @@ struct AudioEngine { pause = MML_PAUSE_DEFAULT; duration = 0; dots = 0; - ZERO_VARIABLE(currentState); + currentState = {}; SetPanPosition(PAN_CENTER); maWaveformConfig = ma_waveform_config_init(ma_format::ma_format_f32, 1, ma_engine_get_sample_rate(rawStream->maEngine), @@ -1823,12 +1822,12 @@ struct AudioEngine { isUsed = false; type = Type::NONE; autoKill = false; - ZERO_VARIABLE(maSound); + maSound = {}; maFlags = MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_WAIT_INIT; - ZERO_VARIABLE(maDecoderConfig); + maDecoderConfig = {}; maDecoder = nullptr; bufferKey = 0; - ZERO_VARIABLE(maAudioBufferConfig); + maAudioBufferConfig = {}; maAudioBuffer = nullptr; rawStream = nullptr; psg = nullptr; @@ -1863,10 +1862,10 @@ struct AudioEngine { /// @brief Initializes some important members. AudioEngine() { isInitialized = initializationFailed = false; - ZERO_VARIABLE(maResourceManagerConfig); - ZERO_VARIABLE(maResourceManager); - ZERO_VARIABLE(maEngineConfig); - ZERO_VARIABLE(maEngine); + maResourceManagerConfig = {}; + maResourceManager = {}; + maEngineConfig = {}; + maEngine = {}; maResult = ma_result::MA_SUCCESS; psgVoices.fill(INVALID_SOUND_HANDLE_INTERNAL); // should not use INVALID_SOUND_HANDLE here internalSndRaw = INVALID_SOUND_HANDLE_INTERNAL; // should not use INVALID_SOUND_HANDLE here @@ -1935,7 +1934,7 @@ struct AudioEngine { // This will set it to 'in use' after applying some defaults. soundHandles[h]->type = SoundHandle::Type::NONE; soundHandles[h]->autoKill = false; - ZERO_VARIABLE(soundHandles[h]->maSound); + soundHandles[h]->maSound = {}; // We do not use pitch shifting, so this will give a little performance boost // Spatialization is disabled by default but will be enabled on the fly if required soundHandles[h]->maFlags = MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_WAIT_INIT; @@ -3452,7 +3451,7 @@ void sub__midisoundbank(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) AUDIO_DEBUG_PRINT("Sound bank will be loaded from memory"); } - for (auto i = 0; i < GET_ARRAY_SIZE(SoundBankName); i++) { + for (auto i = 0; i < _countof(SoundBankName); i++) { AUDIO_DEBUG_PRINT("Checking for: %s", SoundBankName[i]); if (requirements.find(SoundBankName[i]) != std::string::npos) { format = SoundBankFormat(i); diff --git a/internal/c/parts/audio/extras/libmidi/framework.h b/internal/c/parts/audio/extras/libmidi/framework.h index 4d660355c..ecaec9306 100644 --- a/internal/c/parts/audio/extras/libmidi/framework.h +++ b/internal/c/parts/audio/extras/libmidi/framework.h @@ -27,10 +27,10 @@ // a740g: Microsoft's _countof replacement for *nix #ifndef _countof -# ifndef __cplusplus -# define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0])) +# ifdef __cplusplus +template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } # else -template static inline constexpr size_t _countof(T const (&_Array)[N]) { return std::extent::value; } +# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) # endif #endif diff --git a/internal/c/parts/audio/framework.h b/internal/c/parts/audio/framework.h index 3b2e86d6c..399b94af9 100644 --- a/internal/c/parts/audio/framework.h +++ b/internal/c/parts/audio/framework.h @@ -34,9 +34,14 @@ #include #include -#define ZERO_VARIABLE(_v_) memset(&(_v_), 0, sizeof(_v_)) -#define GET_ARRAY_SIZE(_x_) (sizeof(_x_) / sizeof(_x_[0])) -#define SAMPLE_FRAME_SIZE(_type_, _channels_) (sizeof(_type_) * (_channels_)) +#ifndef _countof +# ifdef __cplusplus +template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } +# else +# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) +# endif +#endif + #define SILENCE_SAMPLE 0.0f #ifndef MA_DEFAULT_SAMPLE_RATE diff --git a/internal/c/parts/video/image/image.cpp b/internal/c/parts/video/image/image.cpp index dacc2fb43..95e727995 100644 --- a/internal/c/parts/video/image/image.cpp +++ b/internal/c/parts/video/image/image.cpp @@ -39,14 +39,14 @@ #include #include -#ifdef QB64_WINDOWS -# define ZERO_VARIABLE(_v_) ZeroMemory(&(_v_), sizeof(_v_)) -#else -# define ZERO_VARIABLE(_v_) memset(&(_v_), 0, sizeof(_v_)) +#ifndef _countof +# ifdef __cplusplus +template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } +# else +# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) +# endif #endif -#define GET_ARRAY_SIZE(_x_) (sizeof(_x_) / sizeof(_x_[0])) - extern const img_struct *img; // used by func__loadimage extern const img_struct *write_page; // used by func__loadimage extern const uint32_t palette_256[]; // used by func__loadimage @@ -404,7 +404,7 @@ static uint8_t *image_convert_8bpp(const uint32_t *src32, int32_t w, int32_t h, return nullptr; } - ZERO_VARIABLE(cubes); + std::memset(cubes, 0, sizeof(cubes)); // Quantization phase auto src = reinterpret_cast(src32); @@ -495,9 +495,11 @@ static uint8_t *image_extract_8bpp(const uint32_t *src, int32_t w, int32_t h, ui /// @param src_pal The image's original palette. This cannot be NULL /// @param dst_pal The destination palette. This cannot be NULL static void image_remap_palette(uint8_t *src, int32_t w, int32_t h, const uint32_t *src_pal, const uint32_t *dst_pal) { - const auto maxRGBDelta = image_get_color_delta(0, 0, 0, 255, 255, 255); static uint32_t palMap[256]; - ZERO_VARIABLE(palMap); + + const auto maxRGBDelta = image_get_color_delta(0, 0, 0, 255, 255, 255); + + std::memset(palMap, 0, sizeof(palMap)); IMAGE_DEBUG_PRINT("Remapping 8bpp image (%i, %i) palette", w, h); @@ -587,7 +589,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int } // Parse scaler string - for (auto i = 0; i < GET_ARRAY_SIZE(g_ImageScalerName); i++) { + for (auto i = 0; i < _countof(g_ImageScalerName); i++) { IMAGE_DEBUG_PRINT("Checking for: %s", g_ImageScalerName[i]); if (requirements.find(g_ImageScalerName[i]) != std::string::npos) { scaler = (ImageScaler)i; @@ -756,7 +758,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, IMAGE_DEBUG_PRINT("Parsing requirements string: %s", requirements.c_str()); - for (auto i = 0; i < GET_ARRAY_SIZE(formatName); i++) { + for (auto i = 0; i < _countof(formatName); i++) { IMAGE_DEBUG_PRINT("Checking for: %s", formatName[i]); if (requirements.find(formatName[i]) != std::string::npos) { format = (SaveFormat)i; @@ -779,7 +781,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, IMAGE_DEBUG_PRINT("File extension: %s", fileExtension.c_str()); int i; - for (i = 0; i < GET_ARRAY_SIZE(formatName); i++) { + for (i = 0; i < _countof(formatName); i++) { std::string formatExtension; formatExtension = "."; @@ -795,7 +797,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, } } - if (i >= GET_ARRAY_SIZE(formatName)) { // no matches + if (i >= _countof(formatName)) { // no matches IMAGE_DEBUG_PRINT("No matching extension. Adding .%s", formatName[(int)format]); fileName.append("."); From a475f9a690f41f603cc91b8e368471e637fb5458 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Mon, 18 Nov 2024 05:24:28 +0530 Subject: [PATCH 2/7] Use initial ma_vfs implementation by @mkilgore --- internal/c/libqb/include/audio.h | 3 +- internal/c/parts/audio/audio.cpp | 435 ++++++++++++++---- .../c/parts/audio/extras/midi_ma_vtable.cpp | 4 +- internal/c/parts/audio/framework.cpp | 8 - internal/c/parts/audio/framework.h | 112 +---- 5 files changed, 365 insertions(+), 197 deletions(-) diff --git a/internal/c/libqb/include/audio.h b/internal/c/libqb/include/audio.h index e840f4994..17faedd41 100644 --- a/internal/c/libqb/include/audio.h +++ b/internal/c/libqb/include/audio.h @@ -15,8 +15,9 @@ #include #if defined(AUDIO_DEBUG) && AUDIO_DEBUG > 0 +# define AUDIO_DEBUG_FILENAME (std::strrchr(__FILE__, '/') ? std::strrchr(__FILE__, '/') + 1 : __FILE__) # define AUDIO_DEBUG_PRINT(_fmt_, _args_...) \ - fprintf(stderr, "\e[1;37mDEBUG: %s:%d:%s(): \e[1;33m" _fmt_ "\e[1;37m\n", __FILE__, __LINE__, __func__, ##_args_) + fprintf(stderr, "\e[1;37mDEBUG: %s:%d:%s: \e[1;33m" _fmt_ "\e[1;37m\n", AUDIO_DEBUG_FILENAME, __LINE__, __PRETTY_FUNCTION__, ##_args_) # define AUDIO_DEBUG_CHECK(_exp_) \ if (!(_exp_)) \ AUDIO_DEBUG_PRINT("\e[0;31mCondition (%s) failed", #_exp_) diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index 5bac59e47..138873102 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -26,6 +26,313 @@ /// @brief The top-level class that implements the QB64-PE audio engine. struct AudioEngine { + /// @brief A class that can manage a list of buffers using unique keys. + class BufferMap { + private: + /// @brief A buffer that is made up of std::vector of bytes and reference count. + struct Buffer { + std::vector data; + size_t refCount; + + Buffer(const void *src, size_t size) : data(size), refCount(1) { std::memcpy(data.data(), src, size); } + }; + + std::unordered_map buffers; + + public: + // Delete assignment operators + BufferMap &operator=(const BufferMap &) = delete; + BufferMap &operator=(BufferMap &&) = delete; + + /// @brief Adds a buffer to the map using a unique key only if it was not added before. If the buffer is already present then it increases the reference + /// count. + /// @param data The raw data pointer. The data is copied. + /// @param size The size of the data. + /// @param key The unique key that should be used. + /// @return True if successful. + bool AddBuffer(const void *data, size_t size, uint64_t key) { + if (data && size) { + auto it = buffers.find(key); + + if (it == buffers.end()) { + buffers.emplace(std::make_pair(key, Buffer(data, size))); + + AUDIO_DEBUG_PRINT("Added buffer of size %llu to map, key: %llu", size, key); + } else { + it->second.refCount++; + + AUDIO_DEBUG_PRINT("Increased reference count to %llu, key: %llu", it->second.refCount, key); + } + + return true; + } + + AUDIO_DEBUG_PRINT("Invalid buffer or size %p, %llu", data, size); + + return false; + } + + /// @brief Increments the buffer reference count. + /// @param key The unique key for the buffer. + void AddRef(uint64_t key) { + auto it = buffers.find(key); + + if (it != buffers.end()) { + it->second.refCount++; + + AUDIO_DEBUG_PRINT("Increased reference count to %llu", it->second.refCount); + } else { + AUDIO_DEBUG_PRINT("Buffer not found"); + } + } + + /// @brief Decrements the buffer reference count and frees the buffer if the reference count reaches zero. + /// @param key The unique key for the buffer. + void Release(uint64_t key) { + auto it = buffers.find(key); + + if (it != buffers.end()) { + it->second.refCount--; + + AUDIO_DEBUG_PRINT("Decreased reference count to %llu, key: %llu", it->second.refCount, key); + + if (it->second.refCount == 0) { + AUDIO_DEBUG_PRINT("Erasing buffer of size %llu from map, key: %llu", it->second.data.size(), key); + + buffers.erase(it); + } + } else { + AUDIO_DEBUG_PRINT("Buffer not found"); + } + } + + /// @brief Gets the raw pointer and size of the buffer with the given key. + /// @param key The unique key for the buffer. + /// @return An std::pair of the buffer raw pointer and size. + std::pair GetBuffer(uint64_t key) const { + auto it = buffers.find(key); + + if (it != buffers.end()) { + AUDIO_DEBUG_PRINT("Returning buffer of size %llu", it->second.data.size()); + + return {it->second.data.data(), it->second.data.size()}; + } + + AUDIO_DEBUG_PRINT("Buffer not found"); + + return {nullptr, 0}; + } + }; + + /// @brief miniaudio virtual file system class + struct VFS { + ma_vfs_callbacks cb; // has to be first entry + ma_allocation_callbacks allocCb; + + uint64_t nextFd; + + struct FileState { + ma_int64 offset; + uint64_t key; + + FileState() : offset(0), key(0) {} + }; + + std::unordered_map fileMap; + + BufferMap *bufferMap; + + /// @brief Constructs a new VFS object with empty callbacks and no associated buffer map. + VFS() : nextFd(0), bufferMap(nullptr) { + cb = {}; + allocCb = {}; + } + + /// @brief Creates a new virtual file system (VFS) object with callbacks and associates it with the given buffer map. + /// @param bufferMap Pointer to the BufferMap to be used with the VFS. + /// @return Returns a pointer to the created ma_vfs object. + static ma_vfs *Create(BufferMap *bufferMap) { + auto vfs = new VFS(); + vfs->bufferMap = bufferMap; + vfs->cb.onOpen = OnOpen; + vfs->cb.onClose = OnClose; + vfs->cb.onRead = OnRead; + vfs->cb.onSeek = OnSeek; + vfs->cb.onTell = OnTell; + vfs->cb.onInfo = OnInfo; + + AUDIO_DEBUG_PRINT("VFS created"); + + return reinterpret_cast(vfs); + } + + /// @brief Frees the memory of a virtual file system object. + /// @param vfs Pointer to the virtual file system object. + static void Destroy(ma_vfs *vfs) { + delete reinterpret_cast(vfs); + + AUDIO_DEBUG_PRINT("VFS destroyed"); + } + + /// @brief Opens a file in the virtual file system by looking up a buffer using a key. + /// @param pVFS Pointer to the virtual file system. + /// @param pFilePath String representing the file path, interpreted as a key for buffer lookup. + /// @param openMode Mode in which to open the file (unused). + /// @param pFile Pointer to where the file handle will be stored. + /// @return Returns MA_SUCCESS upon successful opening, or MA_DOES_NOT_EXIST if the buffer is not found. + static ma_result OnOpen(ma_vfs *pVFS, const char *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile) { + (void)openMode; + + auto vfs = reinterpret_cast(pVFS); + auto key = strtoull(pFilePath, NULL, 10); // transform pFilePath, look-up in BufferMap + + AUDIO_DEBUG_PRINT("File path: %s, key: %llu", pFilePath, key); + + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(key); + + if (buffer == nullptr) { + return MA_DOES_NOT_EXIST; + } + + vfs->nextFd++; + vfs->fileMap[vfs->nextFd].key = key; + *pFile = reinterpret_cast(vfs->nextFd); + + AUDIO_DEBUG_PRINT("Open %llu", vfs->nextFd); + + return MA_SUCCESS; + } + + /// @brief Closes a file in the virtual file system. + /// @param pVFS Pointer to the virtual file system. + /// @param file Handle to the file within the virtual file system. + /// @return Returns MA_SUCCESS upon successful closing of the file. + static ma_result OnClose(ma_vfs *pVFS, ma_vfs_file file) { + AUDIO_DEBUG_CHECK(pVFS != nullptr); + AUDIO_DEBUG_CHECK(file != nullptr); + + auto vfs = reinterpret_cast(pVFS); + auto fd = uint64_t(file); + auto state = &vfs->fileMap[fd]; + + vfs->bufferMap->Release(state->key); + vfs->fileMap.erase(fd); + + AUDIO_DEBUG_PRINT("Close %llu", fd); + + return MA_SUCCESS; + } + + /// @brief Reads from a file in the virtual file system. + /// @param pVFS Pointer to the virtual file system. + /// @param file Handle to the file within the virtual file system. + /// @param pDst Pointer to the destination buffer. + /// @param sizeInBytes Requested size of the read. + /// @param pBytesRead Pointer to a variable where the actual read size is stored. + /// @return Returns MA_SUCCESS upon successful read. + static ma_result OnRead(ma_vfs *pVFS, ma_vfs_file file, void *pDst, size_t sizeInBytes, size_t *pBytesRead) { + AUDIO_DEBUG_CHECK(pVFS != nullptr); + AUDIO_DEBUG_CHECK(file != nullptr); + AUDIO_DEBUG_CHECK(pDst != nullptr); + AUDIO_DEBUG_CHECK(pBytesRead != nullptr); + + auto vfs = reinterpret_cast(pVFS); + auto fd = uint64_t(file); + auto state = &vfs->fileMap[fd]; + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); + auto readSize = std::min(sizeInBytes, bufferSize - state->offset); + + std::memcpy(pDst, reinterpret_cast(buffer) + state->offset, readSize); + *pBytesRead = readSize; + state->offset += readSize; + + AUDIO_DEBUG_PRINT("Read %llu, bytes: %zu, offset: %lld", fd, readSize, state->offset); + + return MA_SUCCESS; + } + + /// @brief Seeks to a specified position within a file. + /// @param pVFS Pointer to the virtual file system. + /// @param file Handle to the file within the virtual file system. + /// @param offset Offset from the origin to seek to. + /// @param origin Origin of the seek, either start, current, or end of file. + /// @return Returns MA_SUCCESS upon successful seeking. + static ma_result OnSeek(ma_vfs *pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { + AUDIO_DEBUG_CHECK(pVFS != nullptr); + AUDIO_DEBUG_CHECK(file != nullptr); + AUDIO_DEBUG_CHECK(origin == ma_seek_origin::ma_seek_origin_start || origin == ma_seek_origin::ma_seek_origin_current || + origin == ma_seek_origin::ma_seek_origin_end); + + auto vfs = reinterpret_cast(pVFS); + auto fd = uint64_t(file); + auto state = &vfs->fileMap[fd]; + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); + + switch (origin) { + case ma_seek_origin::ma_seek_origin_start: + state->offset = offset; + break; + + case ma_seek_origin::ma_seek_origin_current: + state->offset = state->offset + offset; + break; + + case ma_seek_origin::ma_seek_origin_end: + state->offset = bufferSize + offset; + break; + } + + state->offset = std::clamp(state->offset, 0, bufferSize); + + AUDIO_DEBUG_PRINT("Seek %llu, kind: %d, offset: %lld, result: %lld", fd, origin, offset, state->offset); + + return MA_SUCCESS; + } + + /// @brief Tells the current cursor position of a file in the virtual file system. + /// @param pVFS Pointer to the virtual file system. + /// @param file Handle to the file within the virtual file system. + /// @param pCursor Pointer to a 64-bit signed integer where the current cursor position is stored. + /// @return Returns MA_SUCCESS upon successful retrieval of the cursor position. + static ma_result OnTell(ma_vfs *pVFS, ma_vfs_file file, ma_int64 *pCursor) { + AUDIO_DEBUG_CHECK(pVFS != nullptr); + AUDIO_DEBUG_CHECK(file != nullptr); + AUDIO_DEBUG_CHECK(pCursor != nullptr); + + auto vfs = reinterpret_cast(pVFS); + auto fd = uint64_t(file); + auto state = &vfs->fileMap[fd]; + + *pCursor = state->offset; + + AUDIO_DEBUG_PRINT("Tell %llu, offset: %lld", fd, state->offset); + + return MA_SUCCESS; + } + + /// @brief Retrieves information about a file, specifically its size in bytes. + /// @param pVFS Pointer to the virtual file system. + /// @param file Handle to the file within the virtual file system. + /// @param pInfo Pointer to a structure where file information will be stored, particularly the file size. + /// @return Returns MA_SUCCESS upon successful retrieval of file information. + static ma_result OnInfo(ma_vfs *pVFS, ma_vfs_file file, ma_file_info *pInfo) { + AUDIO_DEBUG_CHECK(pVFS != nullptr); + AUDIO_DEBUG_CHECK(file != nullptr); + AUDIO_DEBUG_CHECK(pInfo != nullptr); + + auto vfs = reinterpret_cast(pVFS); + auto fd = uint64_t(file); + auto state = &vfs->fileMap[fd]; + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); + + pInfo->sizeInBytes = bufferSize; + + AUDIO_DEBUG_PRINT("Info %llu, size: %zu", fd, bufferSize); + + return MA_SUCCESS; + } + }; + /// @brief A miniaudio raw audio stream datasource. struct RawStream { ma_data_source_base maDataSource; // miniaudio data source (this must be the first member of our struct) @@ -356,7 +663,6 @@ struct AudioEngine { static const auto MML_ATTACK_MIN = 0; // the minimum MML attack (percentage) static const auto MML_ATTACK_MAX = 100; // the maximum MML attack (percentage) static constexpr auto PULSE_WAVE_DUTY_CYCLE_DEFAULT = 0.5f; // the default pulse wave duty cycle (square) - static constexpr auto QUARTER_PI = float(M_PI) / 4.0f; /// @brief A simple ADSR envelope generator. class Envelope { @@ -996,6 +1302,8 @@ struct AudioEngine { /// @brief Set the PSG pan position. /// @param value A number between -1.0 to 1.0. Where 0.0 is center. void SetPanPosition(float value) { + static constexpr auto QUARTER_PI = float(M_PI) / 4.0f; + panPosition = std::clamp(value, PAN_LEFT, PAN_RIGHT); // clamp the value; // Calculate the left and right channel gain values using pan law (-3.0dB pan depth) @@ -1800,8 +2108,6 @@ struct AudioEngine { bool autoKill; // Do we need to auto-clean this sample / stream after playback is done? ma_sound maSound; // miniaudio sound ma_uint32 maFlags; // miniaudio flags that were used when initializing the sound - ma_decoder_config maDecoderConfig; // miniaudio decoder configuration - ma_decoder *maDecoder; // this is used for files that are loaded directly from memory uint64_t bufferKey; // a key that will uniquely identify the data the decoder will use ma_audio_buffer_config maAudioBufferConfig; // miniaudio buffer configuration ma_audio_buffer *maAudioBuffer; // this is used for user created audio buffers (memory is managed by miniaudio) @@ -1824,8 +2130,6 @@ struct AudioEngine { autoKill = false; maSound = {}; maFlags = MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_WAIT_INIT; - maDecoderConfig = {}; - maDecoder = nullptr; bufferKey = 0; maAudioBufferConfig = {}; maAudioBuffer = nullptr; @@ -1852,6 +2156,7 @@ struct AudioEngine { std::vector soundHandles; // this is the audio handle list used by the engine and by everything else int32_t lowestFreeHandle; // this is the lowest handle then was recently freed. We'll start checking for free handles from here BufferMap bufferMap; // this is used to keep track of and manage memory used by 'in-memory' sound files + ma_vfs *vfs; // this is an ma_vfs backed by the BufferMap // Delete copy and move constructors and assignments AudioEngine(const AudioEngine &) = delete; @@ -1870,6 +2175,7 @@ struct AudioEngine { psgVoices.fill(INVALID_SOUND_HANDLE_INTERNAL); // should not use INVALID_SOUND_HANDLE here internalSndRaw = INVALID_SOUND_HANDLE_INTERNAL; // should not use INVALID_SOUND_HANDLE here lowestFreeHandle = 0; + vfs = nullptr; } /// @brief Allocates a sound handle. It will return -1 on error. Handle 0 is used internally for Sound and Play and thus cannot be used by the user. @@ -1938,7 +2244,6 @@ struct AudioEngine { // We do not use pitch shifting, so this will give a little performance boost // Spatialization is disabled by default but will be enabled on the fly if required soundHandles[h]->maFlags = MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_WAIT_INIT; - soundHandles[h]->maDecoder = nullptr; soundHandles[h]->bufferKey = 0; soundHandles[h]->maAudioBuffer = nullptr; soundHandles[h]->rawStream = nullptr; @@ -1974,15 +2279,6 @@ struct AudioEngine { delete soundHandles[handle]->psg; soundHandles[handle]->psg = nullptr; - // Free any initialized miniaudio decoder - if (soundHandles[handle]->maDecoder) { - ma_decoder_uninit(soundHandles[handle]->maDecoder); - delete soundHandles[handle]->maDecoder; - soundHandles[handle]->maDecoder = nullptr; - bufferMap.Release(soundHandles[handle]->bufferKey); - AUDIO_DEBUG_PRINT("Decoder uninitialized"); - } - // Free any initialized audio buffer if (soundHandles[handle]->maAudioBuffer) { ma_audio_buffer_uninit_and_free(soundHandles[handle]->maAudioBuffer); @@ -2176,10 +2472,14 @@ struct AudioEngine { if (isInitialized || initializationFailed) return; + // Create VFS + vfs = VFS::Create(&bufferMap); + // Initialize the miniaudio resource manager maResourceManagerConfig = ma_resource_manager_config_init(); AudioEngineAttachCustomBackendVTables(&maResourceManagerConfig); maResourceManagerConfig.pCustomDecodingBackendUserData = NULL; // <- pUserData parameter of each function in the decoding backend vtables + maResourceManagerConfig.pVFS = vfs; maResult = ma_resource_manager_init(&maResourceManagerConfig, &maResourceManager); if (maResult != MA_SUCCESS) { @@ -2242,10 +2542,13 @@ struct AudioEngine { // Shutdown the miniaudio resource manager ma_resource_manager_uninit(&maResourceManager); + AUDIO_DEBUG_PRINT("Audio engine shutdown"); + + // Destroy VFS + VFS::Destroy(vfs); + // Set engine initialized flag as false isInitialized = false; - - AUDIO_DEBUG_PRINT("Audio engine shutdown"); } } @@ -2283,50 +2586,6 @@ struct AudioEngine { } } } - - /// @brief Creates a ma_decoder and ma_sound from a memory buffer for a valid sound handle. - /// @param buffer A raw pointer to the sound file in memory. - /// @param size The size of the file in memory. - /// @param handle A valid sound handle. - /// @return MA_SUCCESS if successful. Else, a valid ma_result. - ma_result InitializeSoundFromMemory(const void *buffer, size_t size, int32_t handle) { - if (!IsHandleValid(handle) || soundHandles[handle]->maDecoder || !buffer || !size) - return MA_INVALID_ARGS; - - soundHandles[handle]->maDecoder = new ma_decoder(); // allocate and zero memory - if (!soundHandles[handle]->maDecoder) { - AUDIO_DEBUG_PRINT("Failed to allocate memory for miniaudio decoder"); - return MA_OUT_OF_MEMORY; - } - - // Setup the decoder & attach the custom backed vtables - soundHandles[handle]->maDecoderConfig = ma_decoder_config_init_default(); - AudioEngineAttachCustomBackendVTables(&soundHandles[handle]->maDecoderConfig); - soundHandles[handle]->maDecoderConfig.sampleRate = ma_engine_get_sample_rate(&maEngine); - - maResult = ma_decoder_init_memory(buffer, size, &soundHandles[handle]->maDecoderConfig, - soundHandles[handle]->maDecoder); // initialize the decoder - if (maResult != MA_SUCCESS) { - delete soundHandles[handle]->maDecoder; - soundHandles[handle]->maDecoder = nullptr; - AUDIO_DEBUG_PRINT("Error %i: failed to initialize miniaudio decoder", maResult); - return maResult; - } - - // Finally, load the sound as a data source - maResult = - ma_sound_init_from_data_source(&maEngine, soundHandles[handle]->maDecoder, soundHandles[handle]->maFlags, NULL, &soundHandles[handle]->maSound); - - if (maResult != MA_SUCCESS) { - ma_decoder_uninit(soundHandles[handle]->maDecoder); - delete soundHandles[handle]->maDecoder; - soundHandles[handle]->maDecoder = nullptr; - AUDIO_DEBUG_PRINT("Error %i: failed to initialize sound", maResult); - return maResult; - } - - return MA_SUCCESS; - } }; /// @brief Raw stream data source vtable. @@ -2653,15 +2912,40 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { std::string_view(reinterpret_cast(qbsFileName->chr), qbsFileName->len)); // make a unique key and save it audioEngine.bufferMap.AddBuffer(qbsFileName->chr, qbsFileName->len, audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size - audioEngine.maResult = audioEngine.InitializeSoundFromMemory(buffer, bufferSize, handle); // create the ma_sound + auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, + &audioEngine.soundHandles[handle]->maSound); // create the ma_sound } else { std::string fileName(reinterpret_cast(qbsFileName->chr), qbsFileName->len); AUDIO_DEBUG_PRINT("Loading sound from file '%s'", fileName.c_str()); - // Forward the request to miniaudio to open the sound file - audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, filepath_fix_directory(fileName), audioEngine.soundHandles[handle]->maFlags, NULL, - NULL, &audioEngine.soundHandles[handle]->maSound); + auto fp = std::fopen(fileName.c_str(), "rb"); + if (!fp) { + AUDIO_DEBUG_PRINT("Failed to open sound file '%s'", fileName.c_str()); + + audioEngine.soundHandles[handle]->isUsed = false; + + return AudioEngine::INVALID_SOUND_HANDLE; + } + + std::string contents; + + std::fseek(fp, 0, SEEK_END); + contents.resize(std::ftell(fp)); + std::rewind(fp); + std::fread(&contents[0], sizeof(uint8_t), contents.size(), fp); + std::fclose(fp); + + AUDIO_DEBUG_PRINT("Sound length: %zd", contents.length()); + + // Configure a miniaudio decoder to load the sound from memory + audioEngine.soundHandles[handle]->bufferKey = std::hash{}(contents); // make a unique key and save it + audioEngine.bufferMap.AddBuffer(contents.data(), contents.length(), audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer + auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size + auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, + &audioEngine.soundHandles[handle]->maSound); // create the ma_sound } // If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE @@ -2721,27 +3005,6 @@ int32_t func__sndcopy(int32_t src_handle) { // Next memcopy the samples from the source to the dest memcpy((void *)audioEngine.soundHandles[dst_handle]->maAudioBuffer->ref.pData, audioEngine.soundHandles[src_handle]->maAudioBuffer->ref.pData, frames * ma_get_bytes_per_frame(format, channels)); // naughty const void* casting, but should be OK - } else if (audioEngine.soundHandles[src_handle]->maDecoder) { - AUDIO_DEBUG_PRINT("Doing custom sound copy for ma_decoder"); - - dst_handle = audioEngine.CreateHandle(); // allocate a sound handle - if (dst_handle < 1) - return AudioEngine::INVALID_SOUND_HANDLE; - - audioEngine.soundHandles[dst_handle]->type = AudioEngine::SoundHandle::Type::STATIC; // set some handle properties - audioEngine.soundHandles[dst_handle]->maFlags = audioEngine.soundHandles[src_handle]->maFlags; // copy the flags - audioEngine.soundHandles[dst_handle]->bufferKey = audioEngine.soundHandles[src_handle]->bufferKey; // copy the BufferMap unique key - audioEngine.bufferMap.AddRef(audioEngine.soundHandles[dst_handle]->bufferKey); // increase the reference count - auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[dst_handle]->bufferKey); // get the buffer pointer and size - audioEngine.maResult = audioEngine.InitializeSoundFromMemory(buffer, bufferSize, dst_handle); // create the ma_sound - - if (audioEngine.maResult != MA_SUCCESS) { - audioEngine.bufferMap.Release(audioEngine.soundHandles[dst_handle]->bufferKey); - audioEngine.soundHandles[dst_handle]->isUsed = false; - AUDIO_DEBUG_PRINT("Error %i: failed to copy sound", audioEngine.maResult); - - return AudioEngine::INVALID_SOUND_HANDLE; - } } else { AUDIO_DEBUG_PRINT("Doing regular miniaudio sound copy"); diff --git a/internal/c/parts/audio/extras/midi_ma_vtable.cpp b/internal/c/parts/audio/extras/midi_ma_vtable.cpp index d94f5f6f9..16225fd5f 100644 --- a/internal/c/parts/audio/extras/midi_ma_vtable.cpp +++ b/internal/c/parts/audio/extras/midi_ma_vtable.cpp @@ -112,13 +112,13 @@ static ma_result ma_midi_read_pcm_frames(ma_midi *pMIDI, void *pFramesOut, ma_ui if (fixedFrames) { // Only attempt to render if we are actually playing if (pMIDI->isReallyPlaying) { - auto dest = pMIDI->frameBlock->Put(fixedFrames); + auto dest = pMIDI->frameBlock->GetWriteBlock(fixedFrames); if (dest) pMIDI->isReallyPlaying = pMIDI->sequencer->Play(dest, fixedFrames) > 0; } // Get partial data from the frame block - totalFramesRead = pMIDI->frameBlock->Get(reinterpret_cast(pFramesOut), frameCount); + totalFramesRead = pMIDI->frameBlock->ReadFrames(reinterpret_cast(pFramesOut), frameCount); // Set the isPlaying flag to true if we still have some data in the buffers pMIDI->isPlaying = !pMIDI->frameBlock->IsEmpty(); diff --git a/internal/c/parts/audio/framework.cpp b/internal/c/parts/audio/framework.cpp index 1ac43bcaa..581f8da4b 100644 --- a/internal/c/parts/audio/framework.cpp +++ b/internal/c/parts/audio/framework.cpp @@ -50,11 +50,3 @@ void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourc maResourceManagerConfig->ppCustomDecodingBackendVTables = maCustomBackendVTables; maResourceManagerConfig->customDecodingBackendCount = ma_countof(maCustomBackendVTables); } - -/// @brief This simply attaches the format decode VTables array to ma_decoder_config -/// @param maDecoderConfig Pointer to a miniaudio decoder config object. This cannot be NULL -void AudioEngineAttachCustomBackendVTables(ma_decoder_config *maDecoderConfig) { - // Attach the VTable - maDecoderConfig->ppCustomBackendVTables = maCustomBackendVTables; - maDecoderConfig->customBackendCount = ma_countof(maCustomBackendVTables); -} diff --git a/internal/c/parts/audio/framework.h b/internal/c/parts/audio/framework.h index 399b94af9..89905a68a 100644 --- a/internal/c/parts/audio/framework.h +++ b/internal/c/parts/audio/framework.h @@ -69,104 +69,6 @@ extern InstrumentBankManager g_InstrumentBankManager; void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig); void AudioEngineAttachCustomBackendVTables(ma_decoder_config *maDecoderConfig); -/// @brief A class that can manage a list of buffers using unique keys. -class BufferMap { - private: - /// @brief A buffer that is made up of std::vector of bytes and reference count. - struct Buffer { - std::vector data; - size_t refCount; - - Buffer(const void *src, size_t size) : data(size), refCount(1) { std::memcpy(data.data(), src, size); } - }; - - std::unordered_map buffers; - - public: - // Delete assignment operators - BufferMap &operator=(const BufferMap &) = delete; - BufferMap &operator=(BufferMap &&) = delete; - - /// @brief Adds a buffer to the map using a unique key only if it was not added before. If the buffer is already present then it increases the reference - /// count. - /// @param data The raw data pointer. The data is copied. - /// @param size The size of the data. - /// @param key The unique key that should be used. - /// @return True if successful. - bool AddBuffer(const void *data, size_t size, uint64_t key) { - if (data && size) { - auto it = buffers.find(key); - - if (it == buffers.end()) { - buffers.emplace(std::make_pair(key, Buffer(data, size))); - - AUDIO_DEBUG_PRINT("Added buffer of size %llu to map", size); - } else { - it->second.refCount++; - - AUDIO_DEBUG_PRINT("Increased reference count to %llu", it->second.refCount); - } - - return true; - } - - AUDIO_DEBUG_PRINT("Invalid buffer or size %p, %llu", data, size); - - return false; - } - - /// @brief Increments the buffer reference count. - /// @param key The unique key for the buffer. - void AddRef(uint64_t key) { - auto it = buffers.find(key); - - if (it != buffers.end()) { - it->second.refCount++; - - AUDIO_DEBUG_PRINT("Increased reference count to %llu", it->second.refCount); - } else { - AUDIO_DEBUG_PRINT("Buffer not found"); - } - } - - /// @brief Decrements the buffer reference count and frees the buffer if the reference count reaches zero. - /// @param key The unique key for the buffer. - void Release(uint64_t key) { - auto it = buffers.find(key); - - if (it != buffers.end()) { - it->second.refCount--; - - AUDIO_DEBUG_PRINT("Decreased reference count to %llu", it->second.refCount); - - if (it->second.refCount == 0) { - AUDIO_DEBUG_PRINT("Erasing buffer of size %llu", it->second.data.size()); - - buffers.erase(it); - } - } else { - AUDIO_DEBUG_PRINT("Buffer not found"); - } - } - - /// @brief Gets the raw pointer and size of the buffer with the given key. - /// @param key The unique key for the buffer. - /// @return An std::pair of the buffer raw pointer and size. - std::pair GetBuffer(uint64_t key) const { - auto it = buffers.find(key); - - if (it != buffers.end()) { - AUDIO_DEBUG_PRINT("Returning buffer of size %llu", it->second.data.size()); - - return {it->second.data.data(), it->second.data.size()}; - } - - AUDIO_DEBUG_PRINT("Buffer not found"); - - return {nullptr, 0}; - } -}; - /// @brief A class that can manage double buffer frame blocks class DoubleBufferFrameBlock { std::vector blocks[2]; @@ -181,6 +83,7 @@ class DoubleBufferFrameBlock { DoubleBufferFrameBlock() : index(0), cursor(0) {} + /// @brief Resets the double buffer frame blocks by clearing both blocks and resetting the index and cursor to their initial states. void Reset() { blocks[0].clear(); blocks[1].clear(); @@ -188,9 +91,14 @@ class DoubleBufferFrameBlock { cursor = 0; } + /// @brief Checks if both blocks are empty. + /// @returns true if both blocks are empty, false otherwise. bool IsEmpty() const { return blocks[0].empty() && blocks[1].empty(); } - float *Put(size_t frames) { + /// @brief Gets a float pointer to the write block that can be written to. + /// @param[in] frames The number of frames to write. + /// @return A pointer to the write block, or nullptr if the block is not empty. + float *GetWriteBlock(size_t frames) { auto writeIndex = 1 - index; if (blocks[writeIndex].empty()) { @@ -202,7 +110,11 @@ class DoubleBufferFrameBlock { return nullptr; // block is not empty } - size_t Get(float *data, size_t frames) { + /// @brief Copies up to `frames` number of frames from the current block to `data`. The cursor is advanced by the number of frames copied. + /// @param[in] data The destination buffer to copy to. + /// @param[in] frames The number of frames to copy. + /// @return The number of frames copied. + size_t ReadFrames(float *data, size_t frames) { if (blocks[index].empty()) { // Switch to the other block index = 1 - index; From 134e4698e7cabec86d66479fe874c822147b35c8 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Mon, 18 Nov 2024 07:14:53 +0530 Subject: [PATCH 3/7] Implement and use AudioEngine_LoadFile() --- internal/c/parts/audio/audio.cpp | 19 +++---- .../c/parts/audio/extras/midi_ma_vtable.cpp | 36 +----------- internal/c/parts/audio/extras/qoa.h | 8 ++- .../c/parts/audio/extras/radv2_ma_vtable.cpp | 23 ++++---- internal/c/parts/audio/framework.cpp | 24 ++++---- internal/c/parts/audio/framework.h | 56 ++++++++++++++++++- 6 files changed, 89 insertions(+), 77 deletions(-) diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index 138873102..3cb33c608 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -2477,7 +2477,7 @@ struct AudioEngine { // Initialize the miniaudio resource manager maResourceManagerConfig = ma_resource_manager_config_init(); - AudioEngineAttachCustomBackendVTables(&maResourceManagerConfig); + AudioEngine_AttachCustomBackendVTables(&maResourceManagerConfig); maResourceManagerConfig.pCustomDecodingBackendUserData = NULL; // <- pUserData parameter of each function in the decoding backend vtables maResourceManagerConfig.pVFS = vfs; @@ -2913,6 +2913,7 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { audioEngine.bufferMap.AddBuffer(qbsFileName->chr, qbsFileName->len, audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, &audioEngine.soundHandles[handle]->maSound); // create the ma_sound } else { @@ -2920,8 +2921,9 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { AUDIO_DEBUG_PRINT("Loading sound from file '%s'", fileName.c_str()); - auto fp = std::fopen(fileName.c_str(), "rb"); - if (!fp) { + auto contents = AudioEngine_LoadFile(fileName.c_str()); + + if (contents.empty()) { AUDIO_DEBUG_PRINT("Failed to open sound file '%s'", fileName.c_str()); audioEngine.soundHandles[handle]->isUsed = false; @@ -2929,14 +2931,6 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { return AudioEngine::INVALID_SOUND_HANDLE; } - std::string contents; - - std::fseek(fp, 0, SEEK_END); - contents.resize(std::ftell(fp)); - std::rewind(fp); - std::fread(&contents[0], sizeof(uint8_t), contents.size(), fp); - std::fclose(fp); - AUDIO_DEBUG_PRINT("Sound length: %zd", contents.length()); // Configure a miniaudio decoder to load the sound from memory @@ -2944,6 +2938,7 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { audioEngine.bufferMap.AddBuffer(contents.data(), contents.length(), audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, &audioEngine.soundHandles[handle]->maSound); // create the ma_sound } @@ -2951,7 +2946,9 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { // If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE if (audioEngine.maResult != MA_SUCCESS) { AUDIO_DEBUG_PRINT("Error %i: failed to open sound", audioEngine.maResult); + audioEngine.soundHandles[handle]->isUsed = false; + return AudioEngine::INVALID_SOUND_HANDLE; } diff --git a/internal/c/parts/audio/extras/midi_ma_vtable.cpp b/internal/c/parts/audio/extras/midi_ma_vtable.cpp index 16225fd5f..fd01e00d3 100644 --- a/internal/c/parts/audio/extras/midi_ma_vtable.cpp +++ b/internal/c/parts/audio/extras/midi_ma_vtable.cpp @@ -499,43 +499,11 @@ static ma_result ma_midi_init_file(const char *pFilePath, const ma_decoding_back return MA_INVALID_FILE; } - // Open the file for reading - auto pFile = fopen(pFilePath, "rb"); - if (!pFile) { + auto tune = AudioEngine_LoadFile>(pFilePath); + if (tune.empty()) { return MA_INVALID_FILE; } - // Find the size of the file - if (fseek(pFile, 0, SEEK_END) != 0) { - fclose(pFile); - return MA_BAD_SEEK; - } - - // Calculate the length - auto file_size = ftell(pFile); - if (file_size < 1) { - fclose(pFile); - return MA_INVALID_FILE; - } - - // Seek to the beginning of the file - if (fseek(pFile, 0, SEEK_SET) != 0) { - fclose(pFile); - return MA_BAD_SEEK; - } - - // Allocate some memory for the tune - std::vector tune(file_size); - - // Read the file - if (fread(&tune[0], sizeof(uint8_t), file_size, pFile) != file_size || ferror(pFile)) { - fclose(pFile); - return MA_IO_ERROR; - } - - // Close the file now that we've read it into memory - fclose(pFile); - return ma_midi_init_common(pMIDI, tune, pFilePath); } diff --git a/internal/c/parts/audio/extras/qoa.h b/internal/c/parts/audio/extras/qoa.h index 82c64b1fc..413018366 100644 --- a/internal/c/parts/audio/extras/qoa.h +++ b/internal/c/parts/audio/extras/qoa.h @@ -394,9 +394,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned #ifdef QOA_RECORD_TOTAL_ERROR qoa_uint64_t best_error = -1; #endif - qoa_uint64_t best_slice; + qoa_uint64_t best_slice = 0; qoa_lms_t best_lms; - int best_scalefactor; + int best_scalefactor = 0; for (int sfi = 0; sfi < 16; sfi++) { /* There is a strong correlation between the scalefactors of @@ -626,12 +626,14 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa qoa_uint64_t slice = qoa_read_u64(bytes, &p); int scalefactor = (slice >> 60) & 0xf; + slice <<= 4; + int slice_start = sample_index * channels + c; int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c; for (int si = slice_start; si < slice_end; si += channels) { int predicted = qoa_lms_predict(&qoa->lms[c]); - int quantized = (slice >> 57) & 0x7; + int quantized = (slice >> 61) & 0x7; int dequantized = qoa_dequant_tab[scalefactor][quantized]; int reconstructed = qoa_clamp_s16(predicted + dequantized); diff --git a/internal/c/parts/audio/extras/radv2_ma_vtable.cpp b/internal/c/parts/audio/extras/radv2_ma_vtable.cpp index 9ac96ce22..ee33fd56d 100644 --- a/internal/c/parts/audio/extras/radv2_ma_vtable.cpp +++ b/internal/c/parts/audio/extras/radv2_ma_vtable.cpp @@ -392,47 +392,44 @@ static ma_result ma_radv2_init_file(const char *pFilePath, const ma_decoding_bac } // Open the file for reading - auto fd = fopen(pFilePath, "rb"); + auto fd = std::fopen(pFilePath, "rb"); if (!fd) { return MA_INVALID_FILE; } // Find the size of the file - if (fseek(fd, 0, SEEK_END) != 0) { - fclose(fd); + if (std::fseek(fd, 0, SEEK_END) != 0) { + std::fclose(fd); return MA_BAD_SEEK; } // Calculate the length - auto file_size = ftell(fd); + auto file_size = std::ftell(fd); if (file_size < 1) { - fclose(fd); + std::fclose(fd); return MA_INVALID_FILE; } // Seek to the beginning of the file - if (fseek(fd, 0, SEEK_SET) != 0) { - fclose(fd); - return MA_BAD_SEEK; - } + std::rewind(fd); // Allocate some memory for the tune pRadv2->tune = new uint8_t[file_size]; if (!pRadv2->tune) { - fclose(fd); + std::fclose(fd); return MA_OUT_OF_MEMORY; } // Read the file - if (fread(pRadv2->tune, sizeof(uint8_t), file_size, fd) != file_size || ferror(fd)) { + if (std::fread(pRadv2->tune, sizeof(uint8_t), file_size, fd) != file_size || ferror(fd)) { delete[] pRadv2->tune; pRadv2->tune = nullptr; - fclose(fd); + std::fclose(fd); return MA_IO_ERROR; } // Close the file now that we've read it into memory - fclose(fd); + std::fclose(fd); // Create the RADv2 Player objects pRadv2->player = new RADPlayer(); diff --git a/internal/c/parts/audio/framework.cpp b/internal/c/parts/audio/framework.cpp index 581f8da4b..f75ffee9f 100644 --- a/internal/c/parts/audio/framework.cpp +++ b/internal/c/parts/audio/framework.cpp @@ -9,30 +9,28 @@ // //---------------------------------------------------------------------------------------------------------------------- -// Enable Ogg Vorbis decoding +// Enable Ogg Vorbis decoding. #define STB_VORBIS_HEADER_ONLY #include "extras/stb_vorbis.c" -// Due to the way miniaudio links to macOS frameworks at runtime, the application may not pass Apple's notarization process :( +// Due to the way miniaudio links to macOS frameworks at runtime, the application may not pass Apple's notarization process. :( // So, we will avoid runtime linking on macOS. See this discussion for more info: https://github.com/mackron/miniaudio/issues/203 #ifdef __APPLE__ # define MA_NO_RUNTIME_LINKING #endif -// The main miniaudio header +// The main miniaudio header. #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" -// The stb_vorbis implementation must come after the implementation of miniaudio +// The stb_vorbis implementation must come after the implementation of miniaudio. #undef STB_VORBIS_HEADER_ONLY #include "extras/stb_vorbis.c" #include "framework.h" -// We just need one instance to manage sound banks +/// @brief The global instance of the instrument bank manager. We just need one instance to manage sound banks. InstrumentBankManager g_InstrumentBankManager; -// Add custom backend (format) vtables here. -// The order in the array defines the order of priority. -// The vtables will be passed in to the resource manager config. -// ma_vtable_modplay should be the last one because libxmp supports 15-channel MODs -// which does not have any signatures and can lead to incorrect detection. +/// @brief The list of custom decoding backends. Add custom backend (format) vtables here. The order in the array defines the order of priority. The vtables +/// will be passed in to the resource manager config. ma_vtable_modplay should be the last one because libxmp supports 15-channel MODs which does not have any +/// signatures and can lead to incorrect detection. // clang-format off static ma_decoding_backend_vtable *maCustomBackendVTables[] = { &ma_vtable_radv2, @@ -43,9 +41,9 @@ static ma_decoding_backend_vtable *maCustomBackendVTables[] = { }; // clang-format on -/// @brief This simply attaches the format decode VTables array to ma_resource_manager_config -/// @param maDecoderConfig Pointer to a miniaudio resource manager config object. This cannot be NULL -void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig) { +/// @brief This attaches the format decode VTables array to ma_resource_manager_config. +/// @param maDecoderConfig Pointer to a miniaudio resource manager config object. This cannot be NULL. +void AudioEngine_AttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig) { // Attach the VTable maResourceManagerConfig->ppCustomDecodingBackendVTables = maCustomBackendVTables; maResourceManagerConfig->customDecodingBackendCount = ma_countof(maCustomBackendVTables); diff --git a/internal/c/parts/audio/framework.h b/internal/c/parts/audio/framework.h index 89905a68a..d3467f96d 100644 --- a/internal/c/parts/audio/framework.h +++ b/internal/c/parts/audio/framework.h @@ -65,9 +65,59 @@ extern ma_decoding_backend_vtable ma_vtable_qoa; // The global instrument bank manager extern InstrumentBankManager g_InstrumentBankManager; -// These attaches our customer backend (format decoders) VTables to various miniaudio structs -void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig); -void AudioEngineAttachCustomBackendVTables(ma_decoder_config *maDecoderConfig); +void AudioEngine_AttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig); + +/// @brief Loads a file into memory. If the file cannot be opened or read, an empty container is returned. +/// @param fileName The name of the file to load. +/// @return A container of the same type as the template parameter, containing the contents of the file. +template Container AudioEngine_LoadFile(const char *fileName) { + if (!fileName || !fileName[0]) { + AUDIO_DEBUG_PRINT("Invalid parameters"); + + return {}; + } + + auto file = std::fopen(fileName, "rb"); + if (!file) { + AUDIO_DEBUG_PRINT("Failed to open file %s", fileName); + + return {}; + } + + if (std::fseek(file, 0, SEEK_END)) { + AUDIO_DEBUG_PRINT("Failed to seek to the end of file %s", fileName); + + std::fclose(file); + + return {}; + } + + auto size = std::ftell(file); + if (size < 0) { + AUDIO_DEBUG_PRINT("Failed to get the size of file %s", fileName); + + std::fclose(file); + + return {}; + } + + Container buffer; + buffer.resize(size); + + std::rewind(file); + + if (std::fread(buffer.data(), 1, size, file) != size_t(size) || std::ferror(file)) { + AUDIO_DEBUG_PRINT("Failed to read file %s", fileName); + + std::fclose(file); + + return {}; + } + + std::fclose(file); + + return buffer; +} /// @brief A class that can manage double buffer frame blocks class DoubleBufferFrameBlock { From cbf4857b843251a78016ff598dbab83d68c6ba53 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Mon, 18 Nov 2024 09:56:23 +0530 Subject: [PATCH 4/7] Implement file streaming support --- internal/c/parts/audio/audio.cpp | 222 +++++++++++++++++++++-------- internal/c/parts/audio/framework.h | 2 +- 2 files changed, 166 insertions(+), 58 deletions(-) diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index 3cb33c608..b555f338f 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -80,7 +80,7 @@ struct AudioEngine { if (it != buffers.end()) { it->second.refCount++; - AUDIO_DEBUG_PRINT("Increased reference count to %llu", it->second.refCount); + AUDIO_DEBUG_PRINT("Increased reference count to %llu, key: %llu", it->second.refCount, key); } else { AUDIO_DEBUG_PRINT("Buffer not found"); } @@ -132,10 +132,12 @@ struct AudioEngine { uint64_t nextFd; struct FileState { - ma_int64 offset; - uint64_t key; + ma_int64 offset; // for memory buffers + uint64_t key; // for memory buffers + FILE *realFile; // for real files + size_t realFileSize; // for real files - FileState() : offset(0), key(0) {} + FileState() : offset(0), key(0), realFile(nullptr), realFileSize(0) {} }; std::unordered_map fileMap; @@ -184,22 +186,55 @@ struct AudioEngine { (void)openMode; auto vfs = reinterpret_cast(pVFS); - auto key = strtoull(pFilePath, NULL, 10); // transform pFilePath, look-up in BufferMap + auto key = std::strtoull(pFilePath, NULL, 10); // transform pFilePath, look-up in BufferMap + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(key); - AUDIO_DEBUG_PRINT("File path: %s, key: %llu", pFilePath, key); + if (buffer) { + vfs->nextFd++; + vfs->fileMap[vfs->nextFd].key = key; + vfs->fileMap[vfs->nextFd].offset = 0; + vfs->fileMap[vfs->nextFd].realFile = nullptr; + vfs->fileMap[vfs->nextFd].realFileSize = 0; - auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(key); + AUDIO_DEBUG_PRINT("Opened memory buffer (%llu) %llu", key, vfs->nextFd); + } else { + auto file = std::fopen(pFilePath, "rb"); + if (!file) { + AUDIO_DEBUG_PRINT("File / memory buffer not found: %s", pFilePath); + + return MA_DOES_NOT_EXIST; + } + + if (std::fseek(file, 0, SEEK_END)) { + AUDIO_DEBUG_PRINT("Failed to seek to the end of file %s", pFilePath); + + std::fclose(file); + + return MA_BAD_SEEK; + } - if (buffer == nullptr) { - return MA_DOES_NOT_EXIST; + auto fileSize = std::ftell(file); + if (fileSize < 0) { + AUDIO_DEBUG_PRINT("Failed to get the size of file %s", pFilePath); + + std::fclose(file); + + return MA_IO_ERROR; + } + + std::rewind(file); + + vfs->nextFd++; + vfs->fileMap[vfs->nextFd].key = 0; + vfs->fileMap[vfs->nextFd].offset = 0; + vfs->fileMap[vfs->nextFd].realFile = file; + vfs->fileMap[vfs->nextFd].realFileSize = fileSize; + + AUDIO_DEBUG_PRINT("Opened file (%s) %llu", pFilePath, vfs->nextFd); } - vfs->nextFd++; - vfs->fileMap[vfs->nextFd].key = key; *pFile = reinterpret_cast(vfs->nextFd); - AUDIO_DEBUG_PRINT("Open %llu", vfs->nextFd); - return MA_SUCCESS; } @@ -215,10 +250,17 @@ struct AudioEngine { auto fd = uint64_t(file); auto state = &vfs->fileMap[fd]; - vfs->bufferMap->Release(state->key); - vfs->fileMap.erase(fd); + if (state->realFile) { + std::fclose(state->realFile); - AUDIO_DEBUG_PRINT("Close %llu", fd); + AUDIO_DEBUG_PRINT("Closed file %llu", fd); + } else { + vfs->bufferMap->Release(state->key); + + AUDIO_DEBUG_PRINT("Closed memory buffer %llu", fd); + } + + vfs->fileMap.erase(fd); return MA_SUCCESS; } @@ -239,14 +281,26 @@ struct AudioEngine { auto vfs = reinterpret_cast(pVFS); auto fd = uint64_t(file); auto state = &vfs->fileMap[fd]; - auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); - auto readSize = std::min(sizeInBytes, bufferSize - state->offset); - std::memcpy(pDst, reinterpret_cast(buffer) + state->offset, readSize); - *pBytesRead = readSize; - state->offset += readSize; + if (state->realFile) { + *pBytesRead = std::fread(pDst, sizeof(uint8_t), sizeInBytes, state->realFile); + + if (std::ferror(state->realFile)) { + AUDIO_DEBUG_PRINT("Error reading file, fd: %llu", fd); + return MA_IO_ERROR; + } + + AUDIO_DEBUG_PRINT("Read %zu bytes from file %llu", *pBytesRead, fd); + } else { + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); + auto readSize = std::min(sizeInBytes, bufferSize - state->offset); - AUDIO_DEBUG_PRINT("Read %llu, bytes: %zu, offset: %lld", fd, readSize, state->offset); + std::memcpy(pDst, reinterpret_cast(buffer) + state->offset, readSize); + *pBytesRead = readSize; + state->offset += readSize; + + AUDIO_DEBUG_PRINT("Read %zu bytes at offset %lld from memory buffer %llu", readSize, state->offset, fd); + } return MA_SUCCESS; } @@ -266,25 +320,60 @@ struct AudioEngine { auto vfs = reinterpret_cast(pVFS); auto fd = uint64_t(file); auto state = &vfs->fileMap[fd]; - auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); - switch (origin) { - case ma_seek_origin::ma_seek_origin_start: - state->offset = offset; - break; + if (state->realFile) { + int whence; + switch (origin) { + case ma_seek_origin::ma_seek_origin_start: + whence = SEEK_SET; + break; - case ma_seek_origin::ma_seek_origin_current: - state->offset = state->offset + offset; - break; + case ma_seek_origin::ma_seek_origin_current: + whence = SEEK_CUR; + break; - case ma_seek_origin::ma_seek_origin_end: - state->offset = bufferSize + offset; - break; - } + case ma_seek_origin::ma_seek_origin_end: + whence = SEEK_END; + break; + + default: + AUDIO_DEBUG_PRINT("Unknown seek origin: %d", origin); + return MA_BAD_SEEK; + } - state->offset = std::clamp(state->offset, 0, bufferSize); + if (std::fseek(state->realFile, offset, whence) == 0) { + AUDIO_DEBUG_PRINT("Seek file %llu to offset %lld", fd, offset); + + return MA_SUCCESS; + } + + AUDIO_DEBUG_PRINT("Failed to seek file %llu to offset %lld", fd, offset); + return MA_BAD_SEEK; + } else { + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); + + switch (origin) { + case ma_seek_origin::ma_seek_origin_start: + state->offset = offset; + break; - AUDIO_DEBUG_PRINT("Seek %llu, kind: %d, offset: %lld, result: %lld", fd, origin, offset, state->offset); + case ma_seek_origin::ma_seek_origin_current: + state->offset = state->offset + offset; + break; + + case ma_seek_origin::ma_seek_origin_end: + state->offset = bufferSize + offset; + break; + + default: + AUDIO_DEBUG_PRINT("Unknown seek origin: %d", origin); + return MA_BAD_SEEK; + } + + state->offset = std::clamp(state->offset, 0, bufferSize); + + AUDIO_DEBUG_PRINT("Seek memory buffer %llu to offset %lld, kind: %d, result: %lld", fd, offset, origin, state->offset); + } return MA_SUCCESS; } @@ -303,9 +392,14 @@ struct AudioEngine { auto fd = uint64_t(file); auto state = &vfs->fileMap[fd]; - *pCursor = state->offset; + if (state->realFile) { + *pCursor = std::ftell(state->realFile); - AUDIO_DEBUG_PRINT("Tell %llu, offset: %lld", fd, state->offset); + AUDIO_DEBUG_PRINT("File %llu position: %lld", fd, *pCursor); + } else { + *pCursor = state->offset; + AUDIO_DEBUG_PRINT("Memory buffer %llu position: %lld", fd, state->offset); + } return MA_SUCCESS; } @@ -323,11 +417,18 @@ struct AudioEngine { auto vfs = reinterpret_cast(pVFS); auto fd = uint64_t(file); auto state = &vfs->fileMap[fd]; - auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); - pInfo->sizeInBytes = bufferSize; + if (state->realFile) { + pInfo->sizeInBytes = state->realFileSize; - AUDIO_DEBUG_PRINT("Info %llu, size: %zu", fd, bufferSize); + AUDIO_DEBUG_PRINT("File %llu size: %zu", fd, state->realFileSize); + } else { + auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); + + pInfo->sizeInBytes = bufferSize; + + AUDIO_DEBUG_PRINT("Memory buffer %llu size: %zu", fd, bufferSize); + } return MA_SUCCESS; } @@ -2889,7 +2990,7 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { // Check if the user wants to unset the decode flag if (requirements.find("nodecode") != std::string::npos) { audioEngine.soundHandles[handle]->maFlags &= ~MA_SOUND_FLAG_DECODE; - AUDIO_DEBUG_PRINT("Sound will not be decoded"); + AUDIO_DEBUG_PRINT("Sound will not be decoded on load"); } // Check if the user wants to unset the async flag @@ -2919,28 +3020,35 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { } else { std::string fileName(reinterpret_cast(qbsFileName->chr), qbsFileName->len); - AUDIO_DEBUG_PRINT("Loading sound from file '%s'", fileName.c_str()); + if (audioEngine.soundHandles[handle]->maFlags & MA_SOUND_FLAG_STREAM) { + AUDIO_DEBUG_PRINT("Streaming sound from file '%s'", fileName.c_str()); - auto contents = AudioEngine_LoadFile(fileName.c_str()); + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fileName.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, + &audioEngine.soundHandles[handle]->maSound); // create the ma_sound + } else { + AUDIO_DEBUG_PRINT("Loading sound from file '%s'", fileName.c_str()); - if (contents.empty()) { - AUDIO_DEBUG_PRINT("Failed to open sound file '%s'", fileName.c_str()); + auto contents = AudioEngine_LoadFile(fileName.c_str()); - audioEngine.soundHandles[handle]->isUsed = false; + if (contents.empty()) { + AUDIO_DEBUG_PRINT("Failed to open sound file '%s'", fileName.c_str()); - return AudioEngine::INVALID_SOUND_HANDLE; - } + audioEngine.soundHandles[handle]->isUsed = false; - AUDIO_DEBUG_PRINT("Sound length: %zd", contents.length()); + return AudioEngine::INVALID_SOUND_HANDLE; + } - // Configure a miniaudio decoder to load the sound from memory - audioEngine.soundHandles[handle]->bufferKey = std::hash{}(contents); // make a unique key and save it - audioEngine.bufferMap.AddBuffer(contents.data(), contents.length(), audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer - auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size - auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + AUDIO_DEBUG_PRINT("Sound length: %zd", contents.length()); - audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, - &audioEngine.soundHandles[handle]->maSound); // create the ma_sound + // Configure a miniaudio decoder to load the sound from memory + audioEngine.soundHandles[handle]->bufferKey = std::hash{}(contents); // make a unique key and save it + audioEngine.bufferMap.AddBuffer(contents.data(), contents.length(), audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer + auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size + auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, + &audioEngine.soundHandles[handle]->maSound); // create the ma_sound + } } // If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE diff --git a/internal/c/parts/audio/framework.h b/internal/c/parts/audio/framework.h index d3467f96d..1803c3ac3 100644 --- a/internal/c/parts/audio/framework.h +++ b/internal/c/parts/audio/framework.h @@ -106,7 +106,7 @@ template Container AudioEngine_LoadFile(const char *fileNam std::rewind(file); - if (std::fread(buffer.data(), 1, size, file) != size_t(size) || std::ferror(file)) { + if (std::fread(buffer.data(), sizeof(uint8_t), size, file) != size_t(size) || std::ferror(file)) { AUDIO_DEBUG_PRINT("Failed to read file %s", fileName); std::fclose(file); From 8b6738aacc41695d37b123335ae8324ce180c3ff Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Mon, 18 Nov 2024 10:41:34 +0530 Subject: [PATCH 5/7] Use std::min --- internal/c/parts/audio/audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index b555f338f..9e71d04a9 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -293,7 +293,7 @@ struct AudioEngine { AUDIO_DEBUG_PRINT("Read %zu bytes from file %llu", *pBytesRead, fd); } else { auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key); - auto readSize = std::min(sizeInBytes, bufferSize - state->offset); + auto readSize = std::min(sizeInBytes, bufferSize - state->offset); std::memcpy(pDst, reinterpret_cast(buffer) + state->offset, readSize); *pBytesRead = readSize; From 7a1f3655f0d4fe425f081cbb08ef25585da6c687 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Mon, 18 Nov 2024 11:19:55 +0530 Subject: [PATCH 6/7] Move _countof to libqb-common.h --- internal/c/libqb/include/libqb-common.h | 9 +++++++++ internal/c/parts/audio/extras/libmidi/framework.h | 11 +---------- internal/c/parts/audio/framework.h | 9 +-------- internal/c/parts/video/image/image.cpp | 13 +++---------- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/internal/c/libqb/include/libqb-common.h b/internal/c/libqb/include/libqb-common.h index 4772caeb8..5260dac02 100644 --- a/internal/c/libqb/include/libqb-common.h +++ b/internal/c/libqb/include/libqb-common.h @@ -60,4 +60,13 @@ #define QB_FALSE 0 #define QB_TRUE -1 +#ifndef _countof +# ifdef __cplusplus +# include +template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } +# else +# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) +# endif +#endif + #endif diff --git a/internal/c/parts/audio/extras/libmidi/framework.h b/internal/c/parts/audio/extras/libmidi/framework.h index ecaec9306..7ab06b1e1 100644 --- a/internal/c/parts/audio/extras/libmidi/framework.h +++ b/internal/c/parts/audio/extras/libmidi/framework.h @@ -3,6 +3,7 @@ #pragma once +#include "libqb-common.h" #include #include #include @@ -24,16 +25,6 @@ // Force-disable all trace messages. Enabling this will cause compile and link errors #undef _RCP_VERBOSE -// a740g: Microsoft's _countof replacement for *nix - -#ifndef _countof -# ifdef __cplusplus -template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } -# else -# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) -# endif -#endif - // a740g: Microsoft's strsafe.h replacements (sigh!) static inline auto strcat_safe(char *dest, size_t destsz, const char *src) { diff --git a/internal/c/parts/audio/framework.h b/internal/c/parts/audio/framework.h index 1803c3ac3..36cf4c67f 100644 --- a/internal/c/parts/audio/framework.h +++ b/internal/c/parts/audio/framework.h @@ -18,6 +18,7 @@ #include "audio.h" #include "extras/foo_midi/InstrumentBankManager.h" +#include "libqb-common.h" #include "miniaudio.h" #include #include @@ -34,14 +35,6 @@ #include #include -#ifndef _countof -# ifdef __cplusplus -template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } -# else -# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) -# endif -#endif - #define SILENCE_SAMPLE 0.0f #ifndef MA_DEFAULT_SAMPLE_RATE diff --git a/internal/c/parts/video/image/image.cpp b/internal/c/parts/video/image/image.cpp index 95e727995..28c45a18f 100644 --- a/internal/c/parts/video/image/image.cpp +++ b/internal/c/parts/video/image/image.cpp @@ -24,6 +24,7 @@ #include "error_handle.h" #include "filepath.h" #include "jo_gif/jo_gif.h" +#include "libqb-common.h" #include "nanosvg/nanosvg.h" #include "nanosvg/nanosvgrast.h" #include "pixelscalers/pixelscalers.h" @@ -39,14 +40,6 @@ #include #include -#ifndef _countof -# ifdef __cplusplus -template static inline constexpr size_t _countof(T const (&)[N]) noexcept { return N; } -# else -# define _countof(Array_) (sizeof(Array_) / sizeof(Array_[0])) -# endif -#endif - extern const img_struct *img; // used by func__loadimage extern const img_struct *write_page; // used by func__loadimage extern const uint32_t palette_256[]; // used by func__loadimage @@ -404,7 +397,7 @@ static uint8_t *image_convert_8bpp(const uint32_t *src32, int32_t w, int32_t h, return nullptr; } - std::memset(cubes, 0, sizeof(cubes)); + ::memset(cubes, 0, sizeof(cubes)); // Quantization phase auto src = reinterpret_cast(src32); @@ -499,7 +492,7 @@ static void image_remap_palette(uint8_t *src, int32_t w, int32_t h, const uint32 const auto maxRGBDelta = image_get_color_delta(0, 0, 0, 255, 255, 255); - std::memset(palMap, 0, sizeof(palMap)); + ::memset(palMap, 0, sizeof(palMap)); IMAGE_DEBUG_PRINT("Remapping 8bpp image (%i, %i) palette", w, h); From cc7eb441012219f5c5d43103a34e443daaeeeeea Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Tue, 19 Nov 2024 07:22:36 +0000 Subject: [PATCH 7/7] Avoid copying sound data when loading the sound from a file --- internal/c/parts/audio/audio.cpp | 68 +++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index 9e71d04a9..730a5b823 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -34,7 +34,11 @@ struct AudioEngine { std::vector data; size_t refCount; + Buffer() : refCount(0) {} + Buffer(const void *src, size_t size) : data(size), refCount(1) { std::memcpy(data.data(), src, size); } + + Buffer(std::vector &&src) : data(std::move(src)), refCount(1) {} }; std::unordered_map buffers; @@ -57,17 +61,44 @@ struct AudioEngine { if (it == buffers.end()) { buffers.emplace(std::make_pair(key, Buffer(data, size))); - AUDIO_DEBUG_PRINT("Added buffer of size %llu to map, key: %llu", size, key); + AUDIO_DEBUG_PRINT("Copied buffer of size %zu to map, key: %" PRIu64, size, key); + } else { + it->second.refCount++; + + AUDIO_DEBUG_PRINT("Increased reference count to %zu, key: %" PRIu64, it->second.refCount, key); + } + + return true; + } + + AUDIO_DEBUG_PRINT("Invalid buffer or size %p, %zu", data, size); + + return false; + } + + /// @brief Adds a buffer to the map using a unique key only if it was not added before. If the buffer is already present then it increases the reference + /// count. + /// @param buffer The buffer data. The data is moved. + /// @param key The unique key that should be used. + /// @return True if successful. + bool AddBuffer(std::vector &&buffer, uint64_t key) { + if (!buffer.empty()) { + auto it = buffers.find(key); + + if (it == buffers.end()) { + buffers.emplace(std::make_pair(key, Buffer(std::move(buffer)))); + + AUDIO_DEBUG_PRINT("Moved buffer of size %zu to map, key: %" PRIu64, buffers[key].data.size(), key); } else { it->second.refCount++; - AUDIO_DEBUG_PRINT("Increased reference count to %llu, key: %llu", it->second.refCount, key); + AUDIO_DEBUG_PRINT("Increased reference count to %zu, key: %" PRIu64, it->second.refCount, key); } return true; } - AUDIO_DEBUG_PRINT("Invalid buffer or size %p, %llu", data, size); + AUDIO_DEBUG_PRINT("Invalid buffer size %zu", buffer.size()); return false; } @@ -80,7 +111,7 @@ struct AudioEngine { if (it != buffers.end()) { it->second.refCount++; - AUDIO_DEBUG_PRINT("Increased reference count to %llu, key: %llu", it->second.refCount, key); + AUDIO_DEBUG_PRINT("Increased reference count to %zu, key: %" PRIu64, it->second.refCount, key); } else { AUDIO_DEBUG_PRINT("Buffer not found"); } @@ -94,10 +125,10 @@ struct AudioEngine { if (it != buffers.end()) { it->second.refCount--; - AUDIO_DEBUG_PRINT("Decreased reference count to %llu, key: %llu", it->second.refCount, key); + AUDIO_DEBUG_PRINT("Decreased reference count to %zu, key: %" PRIu64, it->second.refCount, key); if (it->second.refCount == 0) { - AUDIO_DEBUG_PRINT("Erasing buffer of size %llu from map, key: %llu", it->second.data.size(), key); + AUDIO_DEBUG_PRINT("Erasing buffer of size %zu from map, key: %" PRIu64, it->second.data.size(), key); buffers.erase(it); } @@ -113,7 +144,7 @@ struct AudioEngine { auto it = buffers.find(key); if (it != buffers.end()) { - AUDIO_DEBUG_PRINT("Returning buffer of size %llu", it->second.data.size()); + AUDIO_DEBUG_PRINT("Returning buffer of size %zu", it->second.data.size()); return {it->second.data.data(), it->second.data.size()}; } @@ -132,12 +163,12 @@ struct AudioEngine { uint64_t nextFd; struct FileState { - ma_int64 offset; // for memory buffers uint64_t key; // for memory buffers + ma_int64 offset; // for memory buffers FILE *realFile; // for real files size_t realFileSize; // for real files - FileState() : offset(0), key(0), realFile(nullptr), realFileSize(0) {} + FileState() : key(0), offset(0), realFile(nullptr), realFileSize(0) {} }; std::unordered_map fileMap; @@ -2209,7 +2240,7 @@ struct AudioEngine { bool autoKill; // Do we need to auto-clean this sample / stream after playback is done? ma_sound maSound; // miniaudio sound ma_uint32 maFlags; // miniaudio flags that were used when initializing the sound - uint64_t bufferKey; // a key that will uniquely identify the data the decoder will use + uint64_t bufferKey; // a key that will uniquely identify the data the sound will use ma_audio_buffer_config maAudioBufferConfig; // miniaudio buffer configuration ma_audio_buffer *maAudioBuffer; // this is used for user created audio buffers (memory is managed by miniaudio) RawStream *rawStream; // Raw sample frame queue @@ -3008,7 +3039,7 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { // Load the file from file or memory based on the requirements string if (fromMemory) { - // Configure a miniaudio decoder to load the sound from memory + // Configure miniaudio to load the sound from memory audioEngine.soundHandles[handle]->bufferKey = std::hash{}( std::string_view(reinterpret_cast(qbsFileName->chr), qbsFileName->len)); // make a unique key and save it audioEngine.bufferMap.AddBuffer(qbsFileName->chr, qbsFileName->len, audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer @@ -3028,7 +3059,7 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { } else { AUDIO_DEBUG_PRINT("Loading sound from file '%s'", fileName.c_str()); - auto contents = AudioEngine_LoadFile(fileName.c_str()); + auto contents = AudioEngine_LoadFile>(fileName.c_str()); if (contents.empty()) { AUDIO_DEBUG_PRINT("Failed to open sound file '%s'", fileName.c_str()); @@ -3038,13 +3069,14 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) { return AudioEngine::INVALID_SOUND_HANDLE; } - AUDIO_DEBUG_PRINT("Sound length: %zd", contents.length()); + AUDIO_DEBUG_PRINT("Sound length: %zu", contents.size()); - // Configure a miniaudio decoder to load the sound from memory - audioEngine.soundHandles[handle]->bufferKey = std::hash{}(contents); // make a unique key and save it - audioEngine.bufferMap.AddBuffer(contents.data(), contents.length(), audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer - auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size - auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string + // Configure miniaudio to load the sound from memory + audioEngine.soundHandles[handle]->bufferKey = std::hash{}( + std::string_view(reinterpret_cast(contents.data()), contents.size())); // make a unique key and save it + audioEngine.bufferMap.AddBuffer(std::move(contents), audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer + auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size + auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, fname.c_str(), audioEngine.soundHandles[handle]->maFlags, NULL, NULL, &audioEngine.soundHandles[handle]->maSound); // create the ma_sound