diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index 92eb5441..1a12034a 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -45,9 +45,28 @@ std::shared_ptr 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(); - // 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); } @@ -65,14 +84,40 @@ speakerid_t ALStream::getSpeaker() const { } 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(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) { // 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() { diff --git a/src/audio/AL/ALAudio.h b/src/audio/AL/ALAudio.h index b37a602d..ca973f48 100644 --- a/src/audio/AL/ALAudio.h +++ b/src/audio/AL/ALAudio.h @@ -1,6 +1,7 @@ #ifndef SRC_AUDIO_AUDIO_H_ #define SRC_AUDIO_AUDIO_H_ +#include #include #include #include @@ -43,28 +44,38 @@ namespace audio { }; class ALStream : public Stream { + static inline constexpr size_t BUFFER_SIZE = 44100; + ALAudio* al; std::shared_ptr source; + std::queue unusedBuffers; speakerid_t speaker = 0; bool keepSource; + char buffer[BUFFER_SIZE]; + bool loop = false; + + bool preloadBuffer(uint buffer, bool loop); public: ALStream(ALAudio* al, std::shared_ptr source, bool keepSource); ~ALStream(); std::shared_ptr getSource() const override; void bindSpeaker(speakerid_t speaker) override; - Speaker* createSpeaker() override; + Speaker* createSpeaker(bool loop) override; speakerid_t getSpeaker() const 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 class ALSpeaker : public Speaker { ALAudio* al; - uint source; int priority; public: + uint source; + ALSpeaker(ALAudio* al, uint source, int priority); ~ALSpeaker(); diff --git a/src/audio/NoAudio.h b/src/audio/NoAudio.h index e2a61881..8d91ebd3 100644 --- a/src/audio/NoAudio.h +++ b/src/audio/NoAudio.h @@ -35,25 +35,25 @@ namespace audio { } } - std::shared_ptr getSource() const { + std::shared_ptr getSource() const override { return source; } - void bindSpeaker(speakerid_t speaker) { + void bindSpeaker(speakerid_t speaker) override { } - Speaker* createSpeaker() { + Speaker* createSpeaker(bool loop) override{ return nullptr; } - speakerid_t getSpeaker() const { + speakerid_t getSpeaker() const override { return 0; } - void update(double delta) { + void update(double delta) override { } - void setTime(duration_t time) { + void setTime(duration_t time) override { } }; diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 37b06b7d..b06043b2 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -13,6 +13,7 @@ namespace audio { static speakerid_t nextId = 1; static Backend* backend; static std::unordered_map> speakers; + static std::unordered_map> streams; } using namespace audio; @@ -200,6 +201,34 @@ speakerid_t audio::play( return id; } +speakerid_t audio::play( + std::shared_ptr 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) { auto found = speakers.find(id); if (found == speakers.end()) { @@ -211,6 +240,10 @@ Speaker* audio::get(speakerid_t id) { void audio::update(double delta) { backend->update(delta); + for (auto& entry : streams) { + entry.second->update(delta); + } + for (auto it = speakers.begin(); it != speakers.end();) { if (it->second->isStopped()) { it = speakers.erase(it); diff --git a/src/audio/audio.h b/src/audio/audio.h index ac6cf4b9..9e924946 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -118,8 +118,9 @@ namespace audio { /// @brief Create new speaker bound to the Stream /// and having high priority + /// @param loop is stream looped (required for correct buffers preload) /// @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 /// @param speaker speaker id or 0 if all you need is unbind speaker @@ -331,6 +332,21 @@ namespace audio { 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, + glm::vec3 position, + float volume, + float pitch, + bool loop + ); + /// @brief Get speaker by id /// @param id speaker id /// @return speaker or nullptr diff --git a/src/coders/ogg.cpp b/src/coders/ogg.cpp index dfe60177..a420a93c 100644 --- a/src/coders/ogg.cpp +++ b/src/coders/ogg.cpp @@ -17,6 +17,7 @@ static inline const char* vorbis_error_message(int code) { case OV_EVERSION: return "vorbis version mismatch"; case OV_EBADHEADER: return "invalid Vorbis bitstream header"; case OV_EFAULT: return "internal logic fault"; + case OV_EINVAL: return "header couldn't be read or are corrupt"; default: return "unknown"; } @@ -86,20 +87,24 @@ public: if (closed) { return 0; } - int bitstream; + int bitstream = 0; long bytes = 0; size_t size = 0; do { do { bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream); if (bytes < 0) { - std::cerr << vorbis_error_message(bytes) << std::endl; + std::cerr << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes << std::endl; continue; } size += bytes; bufferSize -= bytes; buffer += bytes; - } while (bytes > 0); + } while (bytes > 0 && bufferSize > 0); + + if (bufferSize == 0) { + break; + } if (loop) { seek(0);