Skip to content

Commit

Permalink
Merge pull request #573 from a740g/audiolib-vfs-simplification
Browse files Browse the repository at this point in the history
Avoid calling BufferMap::ReleaseBuffer() from VFS::OnClose()
  • Loading branch information
a740g authored Nov 24, 2024
2 parents b0ee8fb + 3f14daa commit 59f87a6
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 57 deletions.
126 changes: 71 additions & 55 deletions internal/c/parts/audio/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,31 @@ struct AudioEngine {
class BufferMap {
private:
/// @brief A buffer that is made up of std::vector of bytes and reference count.
struct Buffer {
struct ManagedBuffer {
std::vector<uint8_t> data;
size_t refCount;

Buffer() : refCount(0) {}
ManagedBuffer() : refCount(0) {}

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

Buffer(std::vector<uint8_t> &&src) : data(std::move(src)), refCount(1) {}
ManagedBuffer(std::vector<uint8_t> &&src) : data(std::move(src)), refCount(1) {}
};

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

public:
// Delete assignment operators
// Delete copy constructor and assignment operators
BufferMap(const BufferMap &) = delete;
BufferMap &operator=(const BufferMap &) = delete;
// Delete move constructor and assignment operators
BufferMap(BufferMap &&) = delete;
BufferMap &operator=(BufferMap &&) = delete;

BufferMap() = default;

/// @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.
Expand All @@ -61,7 +66,7 @@ struct AudioEngine {
auto it = buffers.find(key);

if (it == buffers.end()) {
buffers.emplace(std::make_pair(key, Buffer(data, size)));
buffers.emplace(key, ManagedBuffer(data, size));

AUDIO_DEBUG_PRINT("Copied buffer of size %zu to map, key: %" PRIu64, size, key);
} else {
Expand All @@ -88,7 +93,7 @@ struct AudioEngine {
auto it = buffers.find(key);

if (it == buffers.end()) {
buffers.emplace(std::make_pair(key, Buffer(std::move(buffer))));
buffers.emplace(key, ManagedBuffer(std::move(buffer)));

AUDIO_DEBUG_PRINT("Moved buffer of size %zu to map, key: %" PRIu64, buffers[key].data.size(), key);
} else {
Expand All @@ -105,23 +110,9 @@ struct AudioEngine {
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 %zu, key: %" PRIu64, it->second.refCount, key);
} 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) {
void ReleaseBuffer(uint64_t key) {
auto it = buffers.find(key);

if (it != buffers.end()) {
Expand All @@ -146,7 +137,7 @@ struct AudioEngine {
auto it = buffers.find(key);

if (it != buffers.end()) {
AUDIO_DEBUG_PRINT("Returning buffer of size %zu", it->second.data.size());
AUDIO_DEBUG_PRINT("Returning buffer of size %zu, key: %" PRIu64, it->second.data.size(), key);

return {it->second.data.data(), it->second.data.size()};
}
Expand Down Expand Up @@ -220,16 +211,15 @@ struct AudioEngine {

auto vfs = reinterpret_cast<VFS *>(pVFS);
auto key = std::strtoull(pFilePath, NULL, 10); // transform pFilePath, look-up in BufferMap
auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(key);

if (buffer) {
if (vfs->bufferMap->GetBuffer(key).first) {
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;

AUDIO_DEBUG_PRINT("Opened memory buffer (%llu) %llu", key, vfs->nextFd);
AUDIO_DEBUG_PRINT("Using memory buffer (%llu) %llu", key, vfs->nextFd);
} else {
auto file = std::fopen(pFilePath, "rb");
if (!file) {
Expand Down Expand Up @@ -287,10 +277,6 @@ struct AudioEngine {
std::fclose(state->realFile);

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);
Expand Down Expand Up @@ -383,7 +369,7 @@ struct AudioEngine {
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);
auto bufferSize = vfs->bufferMap->GetBuffer(state->key).second;

switch (origin) {
case ma_seek_origin::ma_seek_origin_start:
Expand Down Expand Up @@ -456,7 +442,7 @@ struct AudioEngine {

AUDIO_DEBUG_PRINT("File %llu size: %zu", fd, state->realFileSize);
} else {
auto [buffer, bufferSize] = vfs->bufferMap->GetBuffer(state->key);
auto bufferSize = vfs->bufferMap->GetBuffer(state->key).second;

pInfo->sizeInBytes = bufferSize;

Expand Down Expand Up @@ -2348,8 +2334,9 @@ struct AudioEngine {
/// snd_un_init gets called. This also increments 'lowestFreeHandle' to allocated handle + 1.
/// @return Returns a non-negative handle if successful.
int32_t CreateHandle() {
if (!isInitialized)
if (!isInitialized) {
return INVALID_SOUND_HANDLE_INTERNAL; // We cannot return 0 here. Since 0 is a valid internal handle
}

size_t h, vectorSize = soundHandles.size(); // Save the vector size

Expand Down Expand Up @@ -2430,6 +2417,7 @@ struct AudioEngine {
// Free any initialized miniaudio sound
if (soundHandles[handle]->type == SoundHandle::Type::STATIC) {
ma_sound_uninit(&soundHandles[handle]->maSound);

AUDIO_DEBUG_PRINT("Sound uninitialized");
}

Expand All @@ -2445,6 +2433,7 @@ struct AudioEngine {
if (soundHandles[handle]->maAudioBuffer) {
ma_audio_buffer_uninit_and_free(soundHandles[handle]->maAudioBuffer);
soundHandles[handle]->maAudioBuffer = nullptr;

AUDIO_DEBUG_PRINT("Audio buffer uninitialized & freed");
}

Expand All @@ -2453,16 +2442,26 @@ struct AudioEngine {
free_mem_lock((mem_lock *)soundHandles[handle]->memLockOffset);
soundHandles[handle]->memLockId = INVALID_MEM_LOCK;
soundHandles[handle]->memLockOffset = nullptr;

AUDIO_DEBUG_PRINT("MemSound stuff invalidated");
}

// Release buffer added by _SNDOPEN
if (soundHandles[handle]->bufferKey) {
AUDIO_DEBUG_PRINT("Releasing buffer %llu", soundHandles[handle]->bufferKey);

bufferMap.ReleaseBuffer(soundHandles[handle]->bufferKey);
soundHandles[handle]->bufferKey = 0;
}

// Now simply set the 'isUsed' member to false so that the handle can be recycled
soundHandles[handle]->isUsed = false;
soundHandles[handle]->type = SoundHandle::Type::NONE;

// Save the free handle to lowestFreeHandle if it is lower than lowestFreeHandle
if (handle < lowestFreeHandle)
if (handle < lowestFreeHandle) {
lowestFreeHandle = handle;
}

AUDIO_DEBUG_PRINT("Sound handle %i marked as free", handle);
}
Expand Down Expand Up @@ -3064,15 +3063,23 @@ 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 miniaudio to load the sound from memory
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
auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey); // convert the buffer key to a string
// 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 copy of the buffer
if (!audioEngine.bufferMap.AddBuffer(qbsFileName->chr, qbsFileName->len, audioEngine.soundHandles[handle]->bufferKey)) {
AUDIO_DEBUG_PRINT("Failed to add buffer to buffer map");

goto handle_cleanup;
}

// Convert the buffer key to a string
auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey);

// Create the ma_sound
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
&audioEngine.soundHandles[handle]->maSound);
} else {
std::string fileName(reinterpret_cast<char const *>(qbsFileName->chr), qbsFileName->len);

Expand All @@ -3089,37 +3096,46 @@ int32_t func__sndopen(qbs *qbsFileName, qbs *qbsRequirements, int32_t passed) {
if (contents.empty()) {
AUDIO_DEBUG_PRINT("Failed to open sound file '%s'", fileName.c_str());

audioEngine.soundHandles[handle]->isUsed = false;

return AudioEngine::INVALID_SOUND_HANDLE;
goto handle_cleanup;
}

AUDIO_DEBUG_PRINT("Sound length: %zu", contents.size());

// Configure miniaudio to load the sound from memory
audioEngine.soundHandles[handle]->bufferKey = std::hash<std::string_view>{}(
std::string_view(reinterpret_cast<const char *>(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
// Make a unique key and save it
audioEngine.soundHandles[handle]->bufferKey =
std::hash<std::string_view>{}(std::string_view(reinterpret_cast<const char *>(contents.data()), contents.size()));

// Make a copy of the buffer
if (!audioEngine.bufferMap.AddBuffer(std::move(contents), audioEngine.soundHandles[handle]->bufferKey)) {
AUDIO_DEBUG_PRINT("Failed to add buffer to buffer map");

goto handle_cleanup;
}

// Convert the buffer key to a string
auto fname = std::to_string(audioEngine.soundHandles[handle]->bufferKey);

// Create the ma_sound
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
&audioEngine.soundHandles[handle]->maSound);
}
}

// 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;
goto handle_cleanup;
}

AUDIO_DEBUG_PRINT("Sound successfully loaded");

return handle;

handle_cleanup:
audioEngine.soundHandles[handle]->isUsed = false;

return AudioEngine::INVALID_SOUND_HANDLE;
}

/// @brief Frees and unloads an open sound.
Expand Down
16 changes: 14 additions & 2 deletions internal/c/parts/audio/extras/primesynth/primesynth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,8 @@ double map(double value, const sf::Modulator &mod) {
return x >= 0.5 ? 1.0 : off;
} else if (mod.polarity == sf::SourcePolarity::Unipolar) {
const double x = mod.direction == sf::SourceDirection::Positive ? value : 1.0 - value;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
switch (mod.type) {
case sf::SourceType::Linear:
return x;
Expand All @@ -893,10 +895,13 @@ double map(double value, const sf::Modulator &mod) {
case sf::SourceType::Convex:
return conv::convex(x);
}
#pragma GCC diagnostic pop
} else {
const int dir = mod.direction == sf::SourceDirection::Positive ? 1 : -1;
const int sign = value > 0.5 ? 1 : -1;
const double x = 2.0 * value - 1.0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
switch (mod.type) {
case sf::SourceType::Linear:
return dir * x;
Expand All @@ -905,6 +910,7 @@ double map(double value, const sf::Modulator &mod) {
case sf::SourceType::Convex:
return sign * dir * conv::convex(sign * x);
}
#pragma GCC diagnostic pop
}
throw std::runtime_error("unknown modulator controller type");
}
Expand Down Expand Up @@ -964,7 +970,7 @@ static constexpr double ATTEN_FACTOR = 0.4;

Voice::Voice(std::size_t noteID, double outputRate, const Sample &sample, const GeneratorSet &generators, const ModulatorParameterSet &modparams,
std::uint8_t key, std::uint8_t velocity)
: noteID_(noteID), sampleBuffer_(sample.buffer), generators_(generators), actualKey_(key), percussion_(false), fineTuning_(0.0), coarseTuning_(0.0),
: noteID_(noteID), actualKey_(key), sampleBuffer_(sample.buffer), generators_(generators), percussion_(false), fineTuning_(0.0), coarseTuning_(0.0),
steps_(0), status_(State::Playing), index_(sample.start), deltaIndex_(0u), volume_({1.0, 1.0}), amp_(0.0), deltaAmp_(0.0),
volEnv_(outputRate, CALC_INTERVAL), modEnv_(outputRate, CALC_INTERVAL), vibLFO_(outputRate, CALC_INTERVAL), modLFO_(outputRate, CALC_INTERVAL) {
rtSample_.mode = static_cast<SampleMode>(0b11 & generators.getOrDefault(sf::Generator::SampleModes));
Expand Down Expand Up @@ -1184,6 +1190,8 @@ void Voice::updateModulatedParams(sf::Generator destination) {
}
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
switch (destination) {
case sf::Generator::Pan:
case sf::Generator::InitialAttenuation:
Expand Down Expand Up @@ -1255,6 +1263,7 @@ void Voice::updateModulatedParams(sf::Generator destination) {
getModulatedGenerator(sf::Generator::CoarseTune) + 0.01 * (fineTuning_ + getModulatedGenerator(sf::Generator::FineTune));
break;
}
#pragma GCC diagnostic pop
}

Channel::Channel(double outputRate)
Expand Down Expand Up @@ -1511,6 +1520,8 @@ void Channel::addVoice(std::unique_ptr<Voice> voice) {
void Channel::updateRPN() {
const std::uint16_t rpn = getSelectedRPN();
const auto data = static_cast<std::int32_t>(rpns_.at(rpn));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
switch (static_cast<midi::RPN>(rpn)) {
case midi::RPN::PitchBendSensitivity:
pitchBendSensitivity_ = data / 128.0;
Expand All @@ -1533,14 +1544,15 @@ void Channel::updateRPN() {
break;
}
}
#pragma GCC diagnostic pop
}

// Used when dealing with soundfonts that are missing some expected default presets
static bool no_drums = false;
static bool no_piano = false;

Synthesizer::Synthesizer(double outputRate, std::size_t numChannels)
: volume_(1.0), midiStd_(midi::Standard::GM), defaultMIDIStd_(midi::Standard::GM), stdFixed_(false) {
: midiStd_(midi::Standard::GM), defaultMIDIStd_(midi::Standard::GM), stdFixed_(false), volume_(1.0) {
conv::initialize();

channels_.reserve(numChannels);
Expand Down

0 comments on commit 59f87a6

Please sign in to comment.