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);
|
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) {
|
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);
|
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() {
|
ALAudio* ALAudio::create() {
|
||||||
ALCdevice* device = alcOpenDevice(nullptr);
|
ALCdevice* device = alcOpenDevice(nullptr);
|
||||||
if (device == nullptr)
|
if (device == nullptr)
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
namespace audio {
|
namespace audio {
|
||||||
struct ALBuffer;
|
struct ALBuffer;
|
||||||
class ALAudio;
|
class ALAudio;
|
||||||
|
class PCMStream;
|
||||||
|
|
||||||
class ALSound : public Sound {
|
class ALSound : public Sound {
|
||||||
ALAudio* al;
|
ALAudio* al;
|
||||||
@ -41,6 +42,23 @@ namespace audio {
|
|||||||
Speaker* newInstance(int priority) const override;
|
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
|
/// @brief AL source adapter
|
||||||
class ALSpeaker : public Speaker {
|
class ALSpeaker : public Speaker {
|
||||||
ALAudio* al;
|
ALAudio* al;
|
||||||
@ -101,6 +119,7 @@ namespace audio {
|
|||||||
std::vector<std::string> getAvailableDevices() const;
|
std::vector<std::string> getAvailableDevices() const;
|
||||||
|
|
||||||
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
|
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
|
||||||
|
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
|
||||||
|
|
||||||
void setListener(
|
void setListener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
|
|||||||
@ -13,6 +13,10 @@ Sound* NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
|
|||||||
return new NoSound(pcm, keepPCM);
|
return new NoSound(pcm, keepPCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream* NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
|
||||||
|
return new NoStream(stream, keepSource);
|
||||||
|
}
|
||||||
|
|
||||||
NoAudio* NoAudio::create() {
|
NoAudio* NoAudio::create() {
|
||||||
return new NoAudio();
|
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 {
|
class NoAudio : public Backend {
|
||||||
public:
|
public:
|
||||||
~NoAudio() {}
|
~NoAudio() {}
|
||||||
|
|
||||||
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
|
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
|
||||||
|
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
|
||||||
|
|
||||||
void setListener(
|
void setListener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
|
|||||||
@ -17,6 +17,75 @@ namespace audio {
|
|||||||
|
|
||||||
using 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) {
|
void audio::initialize(bool enabled) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
backend = ALAudio::create();
|
backend = ALAudio::create();
|
||||||
@ -28,6 +97,9 @@ void audio::initialize(bool enabled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PCM* audio::loadPCM(const fs::path& file, bool headerOnly) {
|
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();
|
std::string ext = file.extension().u8string();
|
||||||
if (ext == ".wav" || ext == ".WAV") {
|
if (ext == ".wav" || ext == ".WAV") {
|
||||||
return wav::load_pcm(file, headerOnly);
|
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) {
|
Sound* audio::loadSound(const fs::path& file, bool keepPCM) {
|
||||||
std::shared_ptr<PCM> pcm(loadPCM(file, !keepPCM && backend->isDummy()));
|
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) {
|
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");
|
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(
|
void audio::setListener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
glm::vec3 velocity,
|
glm::vec3 velocity,
|
||||||
|
|||||||
@ -35,18 +35,21 @@ namespace audio {
|
|||||||
uint8_t channels;
|
uint8_t channels;
|
||||||
uint8_t bitsPerSample;
|
uint8_t bitsPerSample;
|
||||||
uint sampleRate;
|
uint sampleRate;
|
||||||
|
bool seekable;
|
||||||
|
|
||||||
PCM(
|
PCM(
|
||||||
std::vector<char> data,
|
std::vector<char> data,
|
||||||
size_t totalSamples,
|
size_t totalSamples,
|
||||||
uint8_t channels,
|
uint8_t channels,
|
||||||
uint8_t bitsPerSample,
|
uint8_t bitsPerSample,
|
||||||
uint sampleRate
|
uint sampleRate,
|
||||||
|
bool seekable
|
||||||
) : data(std::move(data)),
|
) : data(std::move(data)),
|
||||||
totalSamples(totalSamples),
|
totalSamples(totalSamples),
|
||||||
channels(channels),
|
channels(channels),
|
||||||
bitsPerSample(bitsPerSample),
|
bitsPerSample(bitsPerSample),
|
||||||
sampleRate(sampleRate) {}
|
sampleRate(sampleRate),
|
||||||
|
seekable(seekable) {}
|
||||||
|
|
||||||
inline size_t countSamplesMono() const {
|
inline size_t countSamplesMono() const {
|
||||||
return totalSamples / channels;
|
return totalSamples / channels;
|
||||||
@ -74,6 +77,9 @@ namespace audio {
|
|||||||
/// @brief Close stream
|
/// @brief Close stream
|
||||||
virtual void close()=0;
|
virtual void close()=0;
|
||||||
|
|
||||||
|
/// @brief Check if stream is open
|
||||||
|
virtual bool isOpen() const=0;
|
||||||
|
|
||||||
/// @brief Get total samples number if seekable or 0
|
/// @brief Get total samples number if seekable or 0
|
||||||
virtual size_t getTotalSamples() const=0;
|
virtual size_t getTotalSamples() const=0;
|
||||||
|
|
||||||
@ -105,10 +111,19 @@ namespace audio {
|
|||||||
public:
|
public:
|
||||||
virtual ~Stream() {};
|
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
|
/// @brief Create new speaker bound to the Stream
|
||||||
/// and having high priority
|
/// and having high priority
|
||||||
/// @return speaker id or 0
|
/// @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
|
/// @brief Get id of the bound speaker
|
||||||
/// @return speaker id or 0 if no speaker bound
|
/// @return speaker id or 0 if no speaker bound
|
||||||
@ -230,14 +245,13 @@ namespace audio {
|
|||||||
virtual ~Backend() {};
|
virtual ~Backend() {};
|
||||||
|
|
||||||
virtual Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
|
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(
|
virtual void setListener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
glm::vec3 velocity,
|
glm::vec3 velocity,
|
||||||
glm::vec3 lookAt,
|
glm::vec3 lookAt,
|
||||||
glm::vec3 up
|
glm::vec3 up
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
virtual void update(double delta) = 0;
|
virtual void update(double delta) = 0;
|
||||||
|
|
||||||
/// @brief Check if backend is an abstraction that does not internally
|
/// @brief Check if backend is an abstraction that does not internally
|
||||||
@ -275,6 +289,18 @@ namespace audio {
|
|||||||
/// @return new PCMStream instance
|
/// @return new PCMStream instance
|
||||||
extern PCMStream* openPCMStream(const fs::path& file);
|
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
|
/// @brief Configure 3D listener
|
||||||
/// @param position listener position
|
/// @param position listener position
|
||||||
/// @param velocity listener velocity (used for Doppler effect)
|
/// @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);
|
vorbis_info* info = ov_info(&vf, -1);
|
||||||
uint channels = info->channels;
|
uint channels = info->channels;
|
||||||
uint sampleRate = info->rate;
|
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) {
|
if (!headerOnly) {
|
||||||
const int bufferSize = 4096;
|
const int bufferSize = 4096;
|
||||||
@ -54,7 +55,7 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
|
|||||||
totalSamples = data.size();
|
totalSamples = data.size();
|
||||||
}
|
}
|
||||||
ov_clear(&vf);
|
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 {
|
class OggStream : public PCMStream {
|
||||||
@ -82,6 +83,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t read(char* buffer, size_t bufferSize, bool loop) {
|
size_t read(char* buffer, size_t bufferSize, bool loop) {
|
||||||
|
if (closed) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int bitstream;
|
int bitstream;
|
||||||
long bytes = 0;
|
long bytes = 0;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
@ -108,8 +112,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
ov_clear(&vf);
|
if (!closed) {
|
||||||
closed = true;
|
ov_clear(&vf);
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOpen() const {
|
||||||
|
return !closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t getTotalSamples() const {
|
size_t getTotalSamples() const {
|
||||||
@ -138,7 +148,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void seek(size_t position) {
|
void seek(size_t position) {
|
||||||
if (seekable) {
|
if (!closed && seekable) {
|
||||||
ov_raw_seek(&vf, position);
|
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()+"'");
|
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