Skip to content

Commit

Permalink
Merge pull request #533 from a740g/sndopen-unique-key-fix
Browse files Browse the repository at this point in the history
Use std::hash to generate a proper unique key in _SNDOPEN
  • Loading branch information
a740g authored Aug 26, 2024
2 parents 700eab6 + ccc4b9c commit 2e70d22
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 67 deletions.
5 changes: 3 additions & 2 deletions internal/c/parts/audio/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@ struct SoundHandle {
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
intptr_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 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)
RawStream *rawStream; // Raw sample frame queue
Expand Down Expand Up @@ -1620,7 +1620,8 @@ 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
audioEngine.soundHandles[handle]->bufferKey = (intptr_t)qbsFileName->chr; // make a unique key and save it
audioEngine.soundHandles[handle]->bufferKey = std::hash<std::string_view>{}(
std::string_view(reinterpret_cast<const char *>(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 = InitializeSoundFromMemory(buffer, bufferSize, handle); // create the ma_sound
Expand Down
126 changes: 64 additions & 62 deletions internal/c/parts/audio/framework.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <stack>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -58,101 +60,101 @@ 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
/// @brief A class that can manage a list of buffers using unique keys.
class BufferMap {
private:
/// @brief A buffer that is made up of a raw pointer, size and reference count
/// @brief A buffer that is made up of std::vector of bytes and reference count.
struct Buffer {
void *data;
size_t size;
std::vector<uint8_t> data;
size_t refCount;

Buffer(const void *src, size_t size) : data(size), refCount(1) { std::memcpy(data.data(), src, size); }
};

std::unordered_map<intptr_t, Buffer> buffers;
std::unordered_map<uint64_t, Buffer> buffers;

public:
// Delete assignment operators
BufferMap &operator=(const BufferMap &) = delete;
BufferMap &operator=(BufferMap &&) = delete;

/// @brief This will simply free all buffers that were allocated
~BufferMap() {
for (auto &it : buffers) {
free(it.second.data);
AUDIO_DEBUG_PRINT("Buffer freed of size %llu", it.second.size);
}
}
/// @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);
}

/// @brief Adds a buffer to the map using a unique key only if it was not added before
/// @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, intptr_t key) {
if (data && size && key && buffers.find(key) == buffers.end()) {
Buffer buf = {};

buf.data = malloc(size);
if (!buf.data)
return false;

buf.size = size;
buf.refCount = 1;
memcpy(buf.data, data, size);
buffers.emplace(key, std::move(buf));

AUDIO_DEBUG_PRINT("Added buffer of size %llu to map", size);
return true;
}

AUDIO_DEBUG_PRINT("Failed to add buffer of size %llu", size);
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(intptr_t key) {
const auto it = buffers.find(key);
/// @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()) {
auto &buf = it->second;
buf.refCount += 1;
AUDIO_DEBUG_PRINT("Increased reference count to %llu", buf.refCount);
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(intptr_t key) {
const auto it = buffers.find(key);
/// @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()) {
auto &buf = it->second;
buf.refCount -= 1;
AUDIO_DEBUG_PRINT("Decreased reference count to %llu", buf.refCount);

if (buf.refCount < 1) {
free(buf.data);
AUDIO_DEBUG_PRINT("Buffer freed of size %llu", buf.size);
buffers.erase(key);
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<const void *, size_t> GetBuffer(intptr_t key) const {
const auto it = buffers.find(key);
if (it == buffers.end()) {
AUDIO_DEBUG_PRINT("Buffer not found");
return {nullptr, 0};
/// @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<const void *, size_t> 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()};
}
const auto &buf = it->second;
AUDIO_DEBUG_PRINT("Returning buffer of size %llu", buf.size);
return {buf.data, buf.size};

AUDIO_DEBUG_PRINT("Buffer not found");

return {nullptr, 0};
}
};

Expand Down
6 changes: 3 additions & 3 deletions source/global/version.bas
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
DIM SHARED Version AS STRING
DIM SHARED IsCiVersion AS _BYTE
Version$ = "3.14.0"
$VERSIONINFO:FILEVERSION#=3,14,0,0
$VERSIONINFO:PRODUCTVERSION#=3,14,0,0
Version$ = "3.14.1"
$VERSIONINFO:FILEVERSION#=3,14,1,0
$VERSIONINFO:PRODUCTVERSION#=3,14,1,0
' If ./internal/version.txt exist, then this is some kind of CI build with a label
IF _FILEEXISTS("internal/version.txt") THEN
Expand Down

0 comments on commit 2e70d22

Please sign in to comment.