AL streaming test

This commit is contained in:
MihailRis 2024-03-01 02:37:32 +03:00
parent 8bd43098d5
commit cc9a3e3ec8
6 changed files with 127 additions and 17 deletions

View File

@ -45,9 +45,28 @@ std::shared_ptr<PCMStream> ALStream::getSource() const {
} }
} }
Speaker* ALStream::createSpeaker() { bool ALStream::preloadBuffer(uint buffer, bool loop) {
size_t read = source->read(this->buffer, BUFFER_SIZE, loop);
if (!read)
return false;
ALenum format = AL::to_al_format(source->getChannels(), source->getBitsPerSample());
AL_CHECK(alBufferData(buffer, format, this->buffer, read, source->getSampleRate()));
return true;
}
Speaker* ALStream::createSpeaker(bool loop) {
this->loop = loop;
uint source = al->getFreeSource(); uint source = al->getFreeSource();
// TODO: prepare source and enqueue buffers if (source == 0) {
return nullptr;
}
for (uint i = 0; i < ALStream::STREAM_BUFFERS; i++) {
uint buffer = al->getFreeBuffer();
if (!preloadBuffer(buffer, loop)) {
break;
}
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
}
return new ALSpeaker(al, source, PRIORITY_HIGH); return new ALSpeaker(al, source, PRIORITY_HIGH);
} }
@ -65,14 +84,40 @@ speakerid_t ALStream::getSpeaker() const {
} }
void ALStream::update(double delta) { void ALStream::update(double delta) {
// TODO: implement if (this->speaker == 0) {
return;
}
Speaker* speaker = audio::get(this->speaker);
if (speaker == nullptr) {
speaker = 0;
return;
}
ALSpeaker* alspeaker = dynamic_cast<ALSpeaker*>(speaker);
uint source = alspeaker->source;
uint processed = AL::getSourcei(source, AL_BUFFERS_PROCESSED);
while (processed--) {
uint buffer;
AL_CHECK(alSourceUnqueueBuffers(source, 1, &buffer));
unusedBuffers.push(buffer);
std::cout << "unqueue " << buffer << std::endl;
}
if (!unusedBuffers.empty()) {
uint buffer = unusedBuffers.front();
if (preloadBuffer(buffer, loop)) {
unusedBuffers.pop();
std::cout << "queue " << buffer << std::endl;
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
}
}
} }
void ALStream::setTime(duration_t time) { void ALStream::setTime(duration_t time) {
// TODO: implement // TODO: implement
} }
ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority) : al(al), source(source), priority(priority) { ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority) : al(al), priority(priority), source(source) {
} }
ALSpeaker::~ALSpeaker() { ALSpeaker::~ALSpeaker() {

View File

@ -1,6 +1,7 @@
#ifndef SRC_AUDIO_AUDIO_H_ #ifndef SRC_AUDIO_AUDIO_H_
#define SRC_AUDIO_AUDIO_H_ #define SRC_AUDIO_AUDIO_H_
#include <queue>
#include <vector> #include <vector>
#include <string> #include <string>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -43,28 +44,38 @@ namespace audio {
}; };
class ALStream : public Stream { class ALStream : public Stream {
static inline constexpr size_t BUFFER_SIZE = 44100;
ALAudio* al; ALAudio* al;
std::shared_ptr<PCMStream> source; std::shared_ptr<PCMStream> source;
std::queue<uint> unusedBuffers;
speakerid_t speaker = 0; speakerid_t speaker = 0;
bool keepSource; bool keepSource;
char buffer[BUFFER_SIZE];
bool loop = false;
bool preloadBuffer(uint buffer, bool loop);
public: public:
ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource); ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource);
~ALStream(); ~ALStream();
std::shared_ptr<PCMStream> getSource() const override; std::shared_ptr<PCMStream> getSource() const override;
void bindSpeaker(speakerid_t speaker) override; void bindSpeaker(speakerid_t speaker) override;
Speaker* createSpeaker() override; Speaker* createSpeaker(bool loop) override;
speakerid_t getSpeaker() const override; speakerid_t getSpeaker() const override;
void update(double delta) override; void update(double delta) override;
void setTime(duration_t time) override; void setTime(duration_t time) override;
static inline constexpr uint STREAM_BUFFERS = 3;
}; };
/// @brief AL source adapter /// @brief AL source adapter
class ALSpeaker : public Speaker { class ALSpeaker : public Speaker {
ALAudio* al; ALAudio* al;
uint source;
int priority; int priority;
public: public:
uint source;
ALSpeaker(ALAudio* al, uint source, int priority); ALSpeaker(ALAudio* al, uint source, int priority);
~ALSpeaker(); ~ALSpeaker();

View File

@ -35,25 +35,25 @@ namespace audio {
} }
} }
std::shared_ptr<PCMStream> getSource() const { std::shared_ptr<PCMStream> getSource() const override {
return source; return source;
} }
void bindSpeaker(speakerid_t speaker) { void bindSpeaker(speakerid_t speaker) override {
} }
Speaker* createSpeaker() { Speaker* createSpeaker(bool loop) override{
return nullptr; return nullptr;
} }
speakerid_t getSpeaker() const { speakerid_t getSpeaker() const override {
return 0; return 0;
} }
void update(double delta) { void update(double delta) override {
} }
void setTime(duration_t time) { void setTime(duration_t time) override {
} }
}; };

View File

@ -13,6 +13,7 @@ namespace audio {
static speakerid_t nextId = 1; static speakerid_t nextId = 1;
static Backend* backend; static Backend* backend;
static std::unordered_map<speakerid_t, std::unique_ptr<Speaker>> speakers; static std::unordered_map<speakerid_t, std::unique_ptr<Speaker>> speakers;
static std::unordered_map<speakerid_t, std::shared_ptr<Stream>> streams;
} }
using namespace audio; using namespace audio;
@ -200,6 +201,34 @@ speakerid_t audio::play(
return id; return id;
} }
speakerid_t audio::play(
std::shared_ptr<Stream> stream,
glm::vec3 position,
float volume,
float pitch,
bool loop
) {
Speaker* speaker = stream->createSpeaker(loop);
if (speaker == nullptr) {
remove_lower_priority_speaker(PRIORITY_HIGH);
speaker = stream->createSpeaker(loop);
}
if (speaker == nullptr) {
return 0;
}
speakerid_t id = nextId++;
streams.emplace(id, stream);
speakers.emplace(id, speaker);
stream->bindSpeaker(id);
speaker->setPosition(position);
speaker->setVolume(volume);
speaker->setPitch(pitch);
speaker->setLoop(false);
speaker->play();
return id;
}
Speaker* audio::get(speakerid_t id) { Speaker* audio::get(speakerid_t id) {
auto found = speakers.find(id); auto found = speakers.find(id);
if (found == speakers.end()) { if (found == speakers.end()) {
@ -211,6 +240,10 @@ Speaker* audio::get(speakerid_t id) {
void audio::update(double delta) { void audio::update(double delta) {
backend->update(delta); backend->update(delta);
for (auto& entry : streams) {
entry.second->update(delta);
}
for (auto it = speakers.begin(); it != speakers.end();) { for (auto it = speakers.begin(); it != speakers.end();) {
if (it->second->isStopped()) { if (it->second->isStopped()) {
it = speakers.erase(it); it = speakers.erase(it);

View File

@ -118,8 +118,9 @@ namespace audio {
/// @brief Create new speaker bound to the Stream /// @brief Create new speaker bound to the Stream
/// and having high priority /// and having high priority
/// @param loop is stream looped (required for correct buffers preload)
/// @return speaker id or 0 /// @return speaker id or 0
virtual Speaker* createSpeaker() = 0; virtual Speaker* createSpeaker(bool loop) = 0;
/// @brief Unbind previous speaker and bind new speaker to the stream /// @brief Unbind previous speaker and bind new speaker to the stream
/// @param speaker speaker id or 0 if all you need is unbind speaker /// @param speaker speaker id or 0 if all you need is unbind speaker
@ -331,6 +332,21 @@ namespace audio {
int priority int priority
); );
/// @brief Play stream in the world
/// @param stream target stream
/// @param position stream world position
/// @param volume stream volume [0.0-1.0]
/// @param pitch stream pitch multiplier [0.0-...]
/// @param loop loop stream
/// @return speaker id or 0
extern speakerid_t play(
std::shared_ptr<Stream> stream,
glm::vec3 position,
float volume,
float pitch,
bool loop
);
/// @brief Get speaker by id /// @brief Get speaker by id
/// @param id speaker id /// @param id speaker id
/// @return speaker or nullptr /// @return speaker or nullptr

View File

@ -17,6 +17,7 @@ static inline const char* vorbis_error_message(int code) {
case OV_EVERSION: return "vorbis version mismatch"; case OV_EVERSION: return "vorbis version mismatch";
case OV_EBADHEADER: return "invalid Vorbis bitstream header"; case OV_EBADHEADER: return "invalid Vorbis bitstream header";
case OV_EFAULT: return "internal logic fault"; case OV_EFAULT: return "internal logic fault";
case OV_EINVAL: return "header couldn't be read or are corrupt";
default: default:
return "unknown"; return "unknown";
} }
@ -86,20 +87,24 @@ public:
if (closed) { if (closed) {
return 0; return 0;
} }
int bitstream; int bitstream = 0;
long bytes = 0; long bytes = 0;
size_t size = 0; size_t size = 0;
do { do {
do { do {
bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream); bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream);
if (bytes < 0) { if (bytes < 0) {
std::cerr << vorbis_error_message(bytes) << std::endl; std::cerr << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes << std::endl;
continue; continue;
} }
size += bytes; size += bytes;
bufferSize -= bytes; bufferSize -= bytes;
buffer += bytes; buffer += bytes;
} while (bytes > 0); } while (bytes > 0 && bufferSize > 0);
if (bufferSize == 0) {
break;
}
if (loop) { if (loop) {
seek(0); seek(0);