AL streaming test
This commit is contained in:
parent
8bd43098d5
commit
cc9a3e3ec8
@ -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() {
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user