ALStream WIP
This commit is contained in:
parent
961a37fe0a
commit
8bd43098d5
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user