ALStream WIP

This commit is contained in:
MihailRis 2024-02-29 13:21:58 +03:00
parent 961a37fe0a
commit 8bd43098d5
8 changed files with 245 additions and 12 deletions

View File

@ -28,6 +28,50 @@ Speaker* ALSound::newInstance(int priority) const {
return new ALSpeaker(al, source, priority);
}
ALStream::ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource)
: al(al), source(source), keepSource(keepSource) {
}
ALStream::~ALStream() {
bindSpeaker(0);
source = nullptr;
}
std::shared_ptr<PCMStream> ALStream::getSource() const {
if (keepSource) {
return source;
} else {
return nullptr;
}
}
Speaker* ALStream::createSpeaker() {
uint source = al->getFreeSource();
// TODO: prepare source and enqueue buffers
return new ALSpeaker(al, source, PRIORITY_HIGH);
}
void ALStream::bindSpeaker(speakerid_t speaker) {
auto sp = audio::get(this->speaker);
if (sp) {
sp->stop();
}
this->speaker = speaker;
}
speakerid_t ALStream::getSpeaker() const {
return speaker;
}
void ALStream::update(double delta) {
// TODO: implement
}
void ALStream::setTime(duration_t time) {
// TODO: implement
}
ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority) : al(al), source(source), priority(priority) {
}
@ -162,6 +206,10 @@ Sound* ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return new ALSound(this, buffer, pcm, keepPCM);
}
Stream* ALAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new ALStream(this, stream, keepSource);
}
ALAudio* ALAudio::create() {
ALCdevice* device = alcOpenDevice(nullptr);
if (device == nullptr)

View File

@ -20,6 +20,7 @@
namespace audio {
struct ALBuffer;
class ALAudio;
class PCMStream;
class ALSound : public Sound {
ALAudio* al;
@ -41,6 +42,23 @@ namespace audio {
Speaker* newInstance(int priority) const override;
};
class ALStream : public Stream {
ALAudio* al;
std::shared_ptr<PCMStream> source;
speakerid_t speaker = 0;
bool keepSource;
public:
ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource);
~ALStream();
std::shared_ptr<PCMStream> getSource() const override;
void bindSpeaker(speakerid_t speaker) override;
Speaker* createSpeaker() override;
speakerid_t getSpeaker() const override;
void update(double delta) override;
void setTime(duration_t time) override;
};
/// @brief AL source adapter
class ALSpeaker : public Speaker {
ALAudio* al;
@ -101,6 +119,7 @@ namespace audio {
std::vector<std::string> getAvailableDevices() const;
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener(
glm::vec3 position,

View File

@ -13,6 +13,10 @@ Sound* NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return new NoSound(pcm, keepPCM);
}
Stream* NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new NoStream(stream, keepSource);
}
NoAudio* NoAudio::create() {
return new NoAudio();
}

View File

@ -24,11 +24,45 @@ namespace audio {
}
};
class NoStream : public Stream {
std::shared_ptr<PCMStream> source;
duration_t duration;
public:
NoStream(std::shared_ptr<PCMStream> source, bool keepSource) {
duration = source->getTotalDuration();
if (keepSource) {
this->source = source;
}
}
std::shared_ptr<PCMStream> getSource() const {
return source;
}
void bindSpeaker(speakerid_t speaker) {
}
Speaker* createSpeaker() {
return nullptr;
}
speakerid_t getSpeaker() const {
return 0;
}
void update(double delta) {
}
void setTime(duration_t time) {
}
};
class NoAudio : public Backend {
public:
~NoAudio() {}
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener(
glm::vec3 position,

View File

@ -17,6 +17,75 @@ namespace audio {
using namespace audio;
/// @brief pcm source that does not initialize buffer
class PCMVoidSource : public PCMStream {
size_t totalSamples;
size_t remain;
uint sampleRate;
bool seekable;
bool closed = false;
public:
PCMVoidSource(size_t totalSamples, uint sampleRate, bool seekable)
: totalSamples(totalSamples),
remain(totalSamples),
sampleRate(sampleRate),
seekable(seekable)
{}
size_t read(char* buffer, size_t bufferSize, bool loop) override {
if (closed) {
return 0;
}
if (!seekable || loop) {
return bufferSize;
}
size_t n = std::min(bufferSize, totalSamples);
remain -= n;
return n;
}
void close() override {
closed = true;
}
bool isOpen() const override {
return !closed;
}
size_t getTotalSamples() const override {
return totalSamples;
}
duration_t getTotalDuration() const {
return static_cast<duration_t>(totalSamples) /
static_cast<duration_t>(sampleRate);
}
uint getChannels() const override {
return 1;
}
uint getSampleRate() const override {
return sampleRate;
}
uint getBitsPerSample() const override {
return 8;
}
bool isSeekable() const override {
return seekable;
}
void seek(size_t position) override {
if (closed || !seekable) {
return;
}
position %= totalSamples;
remain = totalSamples - position;
}
};
void audio::initialize(bool enabled) {
if (enabled) {
backend = ALAudio::create();
@ -28,6 +97,9 @@ void audio::initialize(bool enabled) {
}
PCM* audio::loadPCM(const fs::path& file, bool headerOnly) {
if (!fs::exists(file)) {
throw std::runtime_error("file not found '"+file.u8string()+"'");
}
std::string ext = file.extension().u8string();
if (ext == ".wav" || ext == ".WAV") {
return wav::load_pcm(file, headerOnly);
@ -39,7 +111,7 @@ PCM* audio::loadPCM(const fs::path& file, bool headerOnly) {
Sound* audio::loadSound(const fs::path& file, bool keepPCM) {
std::shared_ptr<PCM> pcm(loadPCM(file, !keepPCM && backend->isDummy()));
return backend->createSound(pcm, keepPCM);
return createSound(pcm, keepPCM);
}
Sound* audio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
@ -54,6 +126,26 @@ PCMStream* audio::openPCMStream(const fs::path& file) {
throw std::runtime_error("unsupported audio stream format");
}
Stream* audio::openStream(const fs::path& file, bool keepSource) {
if (!keepSource && backend->isDummy()) {
auto header = loadPCM(file, true);
// using void source sized as audio instead of actual audio file
return openStream(
std::make_shared<PCMVoidSource>(header->totalSamples, header->sampleRate, header->seekable),
keepSource
);
}
return openStream(
std::shared_ptr<PCMStream>(openPCMStream(file)),
keepSource
);
}
Stream* audio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return backend->openStream(stream, keepSource);
}
void audio::setListener(
glm::vec3 position,
glm::vec3 velocity,

View File

@ -35,18 +35,21 @@ namespace audio {
uint8_t channels;
uint8_t bitsPerSample;
uint sampleRate;
bool seekable;
PCM(
std::vector<char> data,
size_t totalSamples,
uint8_t channels,
uint8_t bitsPerSample,
uint sampleRate
uint sampleRate,
bool seekable
) : data(std::move(data)),
totalSamples(totalSamples),
channels(channels),
bitsPerSample(bitsPerSample),
sampleRate(sampleRate) {}
sampleRate(sampleRate),
seekable(seekable) {}
inline size_t countSamplesMono() const {
return totalSamples / channels;
@ -74,6 +77,9 @@ namespace audio {
/// @brief Close stream
virtual void close()=0;
/// @brief Check if stream is open
virtual bool isOpen() const=0;
/// @brief Get total samples number if seekable or 0
virtual size_t getTotalSamples() const=0;
@ -105,10 +111,19 @@ namespace audio {
public:
virtual ~Stream() {};
/// @brief Get pcm data source
/// @return PCM stream or nullptr if audio::openStream
/// keepSource argument is set to false
virtual std::shared_ptr<PCMStream> getSource() const = 0;
/// @brief Create new speaker bound to the Stream
/// and having high priority
/// @return speaker id or 0
virtual speakerid_t createSpeaker() = 0;
virtual Speaker* createSpeaker() = 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
virtual void bindSpeaker(speakerid_t speaker) = 0;
/// @brief Get id of the bound speaker
/// @return speaker id or 0 if no speaker bound
@ -230,14 +245,13 @@ namespace audio {
virtual ~Backend() {};
virtual Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
virtual Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) = 0;
virtual void setListener(
glm::vec3 position,
glm::vec3 velocity,
glm::vec3 lookAt,
glm::vec3 up
) = 0;
virtual void update(double delta) = 0;
/// @brief Check if backend is an abstraction that does not internally
@ -275,6 +289,18 @@ namespace audio {
/// @return new PCMStream instance
extern PCMStream* openPCMStream(const fs::path& file);
/// @brief Open new audio stream from file
/// @param file audio file path
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @return new Stream instance
extern Stream* openStream(const fs::path& file, bool keepSource);
/// @brief Open new audio stream from source
/// @param stream PCM data source
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @return new Stream instance
extern Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource);
/// @brief Configure 3D listener
/// @param position listener position
/// @param velocity listener velocity (used for Doppler effect)

View File

@ -33,7 +33,8 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
vorbis_info* info = ov_info(&vf, -1);
uint channels = info->channels;
uint sampleRate = info->rate;
size_t totalSamples = ov_seekable(&vf) ? ov_pcm_total(&vf, -1) : 0;
bool seekable = ov_seekable(&vf);
size_t totalSamples = seekable ? ov_pcm_total(&vf, -1) : 0;
if (!headerOnly) {
const int bufferSize = 4096;
@ -54,7 +55,7 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
totalSamples = data.size();
}
ov_clear(&vf);
return new PCM(std::move(data), totalSamples, channels, 16, sampleRate);
return new PCM(std::move(data), totalSamples, channels, 16, sampleRate, seekable);
}
class OggStream : public PCMStream {
@ -82,6 +83,9 @@ public:
}
size_t read(char* buffer, size_t bufferSize, bool loop) {
if (closed) {
return 0;
}
int bitstream;
long bytes = 0;
size_t size = 0;
@ -108,8 +112,14 @@ public:
}
void close() {
ov_clear(&vf);
closed = true;
if (!closed) {
ov_clear(&vf);
closed = true;
}
}
bool isOpen() const {
return !closed;
}
size_t getTotalSamples() const {
@ -138,7 +148,7 @@ public:
}
void seek(size_t position) {
if (seekable) {
if (!closed && seekable) {
ov_raw_seek(&vf, position);
}
}

View File

@ -114,5 +114,5 @@ audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) {
throw std::runtime_error("could not load wav data of '"+file.u8string()+"'");
}
}
return new audio::PCM(std::move(data), totalSamples, channels, bitsPerSample, sampleRate);
return new audio::PCM(std::move(data), totalSamples, channels, bitsPerSample, sampleRate, true);
}