mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-21 18:02:43 +01:00
Streaming implementation
This commit is contained in:
parent
76fc914695
commit
139034c53b
@ -20,6 +20,8 @@ set(RWENGINE_SOURCES
|
||||
src/audio/Sound.hpp
|
||||
src/audio/SoundBuffer.cpp
|
||||
src/audio/SoundBuffer.hpp
|
||||
src/audio/SoundBufferStreamed.cpp
|
||||
src/audio/SoundBufferStreamed.hpp
|
||||
src/audio/SoundManager.cpp
|
||||
src/audio/SoundManager.hpp
|
||||
src/audio/SoundSource.cpp
|
||||
|
@ -2,6 +2,12 @@
|
||||
|
||||
#include "audio/SoundBuffer.hpp"
|
||||
|
||||
Sound::~Sound() {
|
||||
if (buffer) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::isPlaying() const {
|
||||
return buffer->isPlaying();
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ struct Sound {
|
||||
std::unique_ptr<SoundBuffer> buffer;
|
||||
|
||||
Sound() = default;
|
||||
~Sound() = default;
|
||||
~Sound();
|
||||
|
||||
bool isPlaying() const;
|
||||
|
||||
|
@ -2,8 +2,13 @@
|
||||
|
||||
#include <rw/types.hpp>
|
||||
|
||||
#include "audio/alCheck.hpp"
|
||||
#include "audio/SoundSource.hpp"
|
||||
#include "audio/alCheck.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr int kNrBuffersStreaming = 4;
|
||||
constexpr int kSizeOfChunk = 4;
|
||||
} // namespace
|
||||
|
||||
SoundBuffer::SoundBuffer() {
|
||||
alCheck(alGenSources(1, &source));
|
||||
@ -29,7 +34,6 @@ bool SoundBuffer::bufferData(SoundSource& soundSource) {
|
||||
static_cast<ALsizei>(soundSource.data.size() * sizeof(int16_t)),
|
||||
soundSource.sampleRate));
|
||||
alCheck(alSourcei(source, AL_BUFFER, buffer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -53,12 +57,18 @@ bool SoundBuffer::isStopped() const {
|
||||
|
||||
void SoundBuffer::play() {
|
||||
alCheck(alSourcePlay(source));
|
||||
|
||||
running = true;
|
||||
}
|
||||
void SoundBuffer::pause() {
|
||||
alCheck(alSourcePause(source));
|
||||
|
||||
running = false;
|
||||
}
|
||||
void SoundBuffer::stop() {
|
||||
alCheck(alSourceStop(source));
|
||||
|
||||
running = false;
|
||||
}
|
||||
|
||||
void SoundBuffer::setPosition(const glm::vec3& position) {
|
||||
|
@ -4,20 +4,22 @@
|
||||
#include <al.h>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
class SoundSource;
|
||||
|
||||
/// OpenAL tool for playing
|
||||
/// sound instance.
|
||||
struct SoundBuffer {
|
||||
SoundBuffer();
|
||||
~SoundBuffer();
|
||||
bool bufferData(SoundSource& soundSource);
|
||||
virtual ~SoundBuffer();
|
||||
virtual bool bufferData(SoundSource& soundSource);
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
bool isStopped() const;
|
||||
|
||||
void play();
|
||||
virtual void play();
|
||||
void pause();
|
||||
void stop();
|
||||
|
||||
@ -28,6 +30,8 @@ struct SoundBuffer {
|
||||
void setMaxDistance(float maxDist);
|
||||
|
||||
ALuint source;
|
||||
bool running = false;
|
||||
private:
|
||||
ALuint buffer;
|
||||
};
|
||||
|
||||
|
108
rwengine/src/audio/SoundBufferStreamed.cpp
Normal file
108
rwengine/src/audio/SoundBufferStreamed.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "audio/SoundBufferStreamed.hpp"
|
||||
|
||||
#include <rw/types.hpp>
|
||||
|
||||
#include "audio/SoundSource.hpp"
|
||||
#include "audio/alCheck.hpp"
|
||||
|
||||
SoundBufferStreamed::SoundBufferStreamed() {
|
||||
alCheck(alGenSources(1, &source));
|
||||
|
||||
alCheck(alGenBuffers(kNrBuffersStreaming, buffers.data()));
|
||||
|
||||
alCheck(alSourcef(source, AL_PITCH, 1));
|
||||
alCheck(alSourcef(source, AL_GAIN, 1));
|
||||
alCheck(alSource3f(source, AL_POSITION, 0, 0, 0));
|
||||
alCheck(alSource3f(source, AL_VELOCITY, 0, 0, 0));
|
||||
alCheck(alSourcei(source, AL_LOOPING, AL_FALSE));
|
||||
}
|
||||
|
||||
SoundBufferStreamed::~SoundBufferStreamed() {
|
||||
alCheck(alDeleteBuffers(kNrBuffersStreaming, buffers.data()));
|
||||
}
|
||||
|
||||
bool SoundBufferStreamed::bufferData(SoundSource &soundSource) {
|
||||
/* Rewind the source position and clear the buffer queue */
|
||||
alSourceRewind(source);
|
||||
alSourcei(source, AL_BUFFER, 0);
|
||||
|
||||
std::lock_guard<std::mutex> lock(soundSource.mutex);
|
||||
/* Fill the buffer queue */
|
||||
for (auto i = 0u; i < buffers.size() &&
|
||||
streamedData * kSizeOfChunk < soundSource.data.size();
|
||||
i++) {
|
||||
auto sizeOfNextChunk = std::min(static_cast<size_t>(kSizeOfChunk),
|
||||
soundSource.data.size());
|
||||
|
||||
alCheck(alBufferData(
|
||||
buffers[i],
|
||||
soundSource.channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
|
||||
&soundSource.data[streamedData * kSizeOfChunk],
|
||||
static_cast<ALsizei>(sizeOfNextChunk * sizeof(int16_t)),
|
||||
soundSource.sampleRate));
|
||||
streamedData++;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(source, kNrBuffersStreaming, buffers.data());
|
||||
|
||||
this->soundSource = &soundSource;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundBufferStreamed::play() {
|
||||
alSourcePlay(source);
|
||||
|
||||
running = true;
|
||||
|
||||
loadingThread = std::async(std::launch::async,
|
||||
&SoundBufferStreamed::updateBuffers, this);
|
||||
}
|
||||
|
||||
void SoundBufferStreamed::updateBuffers() {
|
||||
while (running) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
ALint processed, state;
|
||||
|
||||
/* Get relevant source info */
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
std::lock_guard<std::mutex> lock(soundSource->mutex);
|
||||
|
||||
/* Unqueue and handle each processed buffer */
|
||||
while (processed > 0 &&
|
||||
streamedData * kSizeOfChunk < soundSource->data.size()) {
|
||||
ALuint bufid{};
|
||||
auto sizeOfNextChunk =
|
||||
std::min(static_cast<size_t>(kSizeOfChunk),
|
||||
soundSource->data.size() -
|
||||
static_cast<size_t>(kSizeOfChunk) * streamedData);
|
||||
|
||||
alSourceUnqueueBuffers(source, 1, &bufid);
|
||||
processed--;
|
||||
|
||||
if (sizeOfNextChunk > 0) {
|
||||
alBufferData(bufid,
|
||||
soundSource->channels == 1 ? AL_FORMAT_MONO16
|
||||
: AL_FORMAT_STEREO16,
|
||||
&soundSource->data[streamedData * kSizeOfChunk],
|
||||
sizeOfNextChunk * sizeof(int16_t),
|
||||
soundSource->sampleRate);
|
||||
streamedData++;
|
||||
alSourceQueueBuffers(source, 1, &bufid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the source hasn't underrun */
|
||||
if (state != AL_PLAYING && state != AL_PAUSED) {
|
||||
ALint queued;
|
||||
|
||||
/* If no buffers are queued, playback is finished */
|
||||
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
|
||||
if (queued == 0) return;
|
||||
|
||||
alSourcePlay(source);
|
||||
}
|
||||
}
|
||||
}
|
27
rwengine/src/audio/SoundBufferStreamed.hpp
Normal file
27
rwengine/src/audio/SoundBufferStreamed.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _RWENGINE_SOUND_BUFFER_STREAMED_HPP_
|
||||
#define _RWENGINE_SOUND_BUFFER_STREAMED_HPP_
|
||||
|
||||
#include "audio/SoundBuffer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <future>
|
||||
|
||||
struct SoundBufferStreamed : public SoundBuffer {
|
||||
static constexpr unsigned int kNrBuffersStreaming = 4;
|
||||
static constexpr unsigned int kSizeOfChunk = 4096;
|
||||
|
||||
SoundBufferStreamed();
|
||||
~SoundBufferStreamed() override;
|
||||
bool bufferData(SoundSource& soundSource) final;
|
||||
|
||||
virtual void play() final;
|
||||
|
||||
private:
|
||||
SoundSource* soundSource = nullptr;
|
||||
void updateBuffers();
|
||||
unsigned int streamedData = 0;
|
||||
std::array<ALuint, kNrBuffersStreaming> buffers;
|
||||
std::future<void> loadingThread;
|
||||
};
|
||||
|
||||
#endif
|
@ -7,10 +7,11 @@ extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
}
|
||||
|
||||
#include "audio/alCheck.hpp"
|
||||
#include "audio/Sound.hpp"
|
||||
#include "audio/SoundBuffer.hpp"
|
||||
#include "audio/SoundBufferStreamed.hpp"
|
||||
#include "audio/SoundSource.hpp"
|
||||
#include "audio/alCheck.hpp"
|
||||
#include "engine/GameData.hpp"
|
||||
#include "engine/GameWorld.hpp"
|
||||
#include "render/ViewCamera.hpp"
|
||||
@ -110,7 +111,7 @@ void SoundManager::deinitializeOpenAL() {
|
||||
}
|
||||
|
||||
bool SoundManager::loadSound(const std::string& name,
|
||||
const std::string& fileName) {
|
||||
const std::string& fileName, bool streamed) {
|
||||
Sound* sound = nullptr;
|
||||
auto sound_iter = sounds.find(name);
|
||||
|
||||
@ -123,9 +124,9 @@ bool SoundManager::loadSound(const std::string& name,
|
||||
sound = &it->second;
|
||||
|
||||
sound->source = std::make_shared<SoundSource>();
|
||||
sound->buffer = std::make_unique<SoundBuffer>();
|
||||
sound->buffer = streamed ? std::make_unique<SoundBufferStreamed>() : std::make_unique<SoundBuffer>();
|
||||
|
||||
sound->source->loadFromFile(fileName);
|
||||
sound->source->loadFromFile(fileName, streamed);
|
||||
sound->isLoaded = sound->buffer->bufferData(*sound->source);
|
||||
}
|
||||
|
||||
@ -148,7 +149,7 @@ size_t SoundManager::createSfxInstance(size_t index) {
|
||||
Sound* sound = nullptr;
|
||||
auto soundRef = sfx.find(index);
|
||||
|
||||
if(soundRef == sfx.end()) {
|
||||
if (soundRef == sfx.end()) {
|
||||
// Sound source is not loaded yet
|
||||
loadSound(index);
|
||||
soundRef = sfx.find(index);
|
||||
@ -161,8 +162,7 @@ size_t SoundManager::createSfxInstance(size_t index) {
|
||||
// Let's use this buffer
|
||||
sound.buffer = std::make_unique<SoundBuffer>();
|
||||
sound.source = soundRef->second.source;
|
||||
sound.isLoaded =
|
||||
sound.buffer->bufferData(*sound.source);
|
||||
sound.isLoaded = sound.buffer->bufferData(*sound.source);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@ -341,7 +341,6 @@ void SoundManager::setSoundPosition(const std::string& name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::setVolume(float vol) {
|
||||
_volume = vol;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
~SoundManager();
|
||||
|
||||
/// Load sound from file and store it with selected name
|
||||
bool loadSound(const std::string& name, const std::string& fileName);
|
||||
bool loadSound(const std::string& name, const std::string& fileName, bool streamed = true);
|
||||
|
||||
/// Load selected sfx sound
|
||||
void loadSound(size_t index);
|
||||
|
@ -288,6 +288,7 @@ void SoundSource::decodeFramesLegacy(size_t framesToDecode) {
|
||||
// Write samples to audio buffer
|
||||
for (size_t i = 0;
|
||||
i < static_cast<size_t>(frame->nb_samples); i++) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
// Interleave left/right channels
|
||||
for (size_t channel = 0; channel < channels;
|
||||
channel++) {
|
||||
@ -337,6 +338,7 @@ void SoundSource::decodeFrames(size_t framesToDecode) {
|
||||
|
||||
for (size_t i = 0;
|
||||
i < static_cast<size_t>(frame->nb_samples); i++) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
// Interleave left/right channels
|
||||
for (size_t channel = 0; channel < channels;
|
||||
channel++) {
|
||||
@ -417,6 +419,7 @@ void SoundSource::decodeAndResampleFrames(const rwfs::path& filePath,
|
||||
RW_ERROR("Error resampling " << filePath << '\n');
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (size_t i = 0;
|
||||
i <
|
||||
static_cast<size_t>(resampled->nb_samples) * channels;
|
||||
@ -512,7 +515,7 @@ void SoundSource::loadFromFile(const rwfs::path& filePath, bool streaming) {
|
||||
decodeFramesWrap(filePath);
|
||||
|
||||
if (streaming) {
|
||||
auto loadingThread = std::async(
|
||||
loadingThread = std::async(
|
||||
std::launch::async,
|
||||
&SoundSource::decodeRestSoundFramesAndCleanup, this, filePath);
|
||||
} else {
|
||||
@ -531,7 +534,7 @@ void SoundSource::loadSfx(LoaderSDT& sdt, size_t index, bool asWave,
|
||||
decodeFramesSfxWrap();
|
||||
|
||||
if (streaming) {
|
||||
auto loadingThread =
|
||||
loadingThread =
|
||||
std::async(std::launch::async,
|
||||
&SoundSource::decodeRestSfxFramesAndCleanup, this);
|
||||
} else {
|
||||
|
@ -10,6 +10,7 @@ extern "C" {
|
||||
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
|
||||
/// Structure for input data
|
||||
struct InputData {
|
||||
@ -31,6 +32,7 @@ class LoaderSDT;
|
||||
class SoundSource {
|
||||
friend class SoundManager;
|
||||
friend struct SoundBuffer;
|
||||
friend struct SoundBufferStreamed;
|
||||
|
||||
public:
|
||||
bool allocateAudioFrame();
|
||||
@ -59,7 +61,8 @@ public:
|
||||
void decodeFramesWrap(const rwfs::path& filePath);
|
||||
void decodeFramesSfxWrap();
|
||||
void decodeFrames(size_t framesToDecode);
|
||||
void decodeAndResampleFrames(const rwfs::path& filePath, size_t framesToDecode);
|
||||
void decodeAndResampleFrames(const rwfs::path& filePath,
|
||||
size_t framesToDecode);
|
||||
|
||||
void cleanupAfterSoundLoading();
|
||||
void cleanupAfterSfxLoading();
|
||||
@ -99,6 +102,9 @@ private:
|
||||
std::unique_ptr<char[]> raw_sound;
|
||||
std::unique_ptr<uint8_t[]> inputDataStart;
|
||||
InputData input{};
|
||||
|
||||
std::mutex mutex;
|
||||
std::future<void> loadingThread;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user