audio: channels
This commit is contained in:
@@ -162,7 +162,7 @@ bool assetload::sound(
|
|||||||
std::shared_ptr<AssetCfg> config
|
std::shared_ptr<AssetCfg> config
|
||||||
) {
|
) {
|
||||||
auto cfg = dynamic_cast<SoundCfg*>(config.get());
|
auto cfg = dynamic_cast<SoundCfg*>(config.get());
|
||||||
auto sound = audio::loadSound(paths->find(file), cfg->keepPCM);
|
auto sound = audio::load_sound(paths->find(file), cfg->keepPCM);
|
||||||
if (sound == nullptr) {
|
if (sound == nullptr) {
|
||||||
std::cerr << "failed to load sound '" << name << "' from '";
|
std::cerr << "failed to load sound '" << name << "' from '";
|
||||||
std::cerr << file << "'" << std::endl;
|
std::cerr << file << "'" << std::endl;
|
||||||
@@ -172,11 +172,13 @@ bool assetload::sound(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool assetload::animation(Assets* assets,
|
bool assetload::animation(
|
||||||
const ResPaths* paths,
|
Assets* assets,
|
||||||
const std::string directory,
|
const ResPaths* paths,
|
||||||
const std::string name,
|
const std::string directory,
|
||||||
Atlas* dstAtlas) {
|
const std::string name,
|
||||||
|
Atlas* dstAtlas
|
||||||
|
) {
|
||||||
std::string animsDir = directory + "/animations";
|
std::string animsDir = directory + "/animations";
|
||||||
std::string blocksDir = directory + "/blocks";
|
std::string blocksDir = directory + "/blocks";
|
||||||
|
|
||||||
|
|||||||
+29
-13
@@ -19,13 +19,13 @@ ALSound::~ALSound() {
|
|||||||
buffer = 0;
|
buffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Speaker* ALSound::newInstance(int priority) const {
|
Speaker* ALSound::newInstance(int priority, int channel) const {
|
||||||
uint source = al->getFreeSource();
|
uint source = al->getFreeSource();
|
||||||
if (source == 0) {
|
if (source == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
AL_CHECK(alSourcei(source, AL_BUFFER, buffer));
|
AL_CHECK(alSourcei(source, AL_BUFFER, buffer));
|
||||||
return new ALSpeaker(al, source, priority);
|
return new ALSpeaker(al, source, priority, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALStream::ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource)
|
ALStream::ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource)
|
||||||
@@ -54,7 +54,7 @@ bool ALStream::preloadBuffer(uint buffer, bool loop) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Speaker* ALStream::createSpeaker(bool loop) {
|
Speaker* ALStream::createSpeaker(bool loop, int channel) {
|
||||||
this->loop = loop;
|
this->loop = loop;
|
||||||
uint source = al->getFreeSource();
|
uint source = al->getFreeSource();
|
||||||
if (source == 0) {
|
if (source == 0) {
|
||||||
@@ -67,7 +67,7 @@ Speaker* ALStream::createSpeaker(bool loop) {
|
|||||||
}
|
}
|
||||||
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
|
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
|
||||||
}
|
}
|
||||||
return new ALSpeaker(al, source, PRIORITY_HIGH);
|
return new ALSpeaker(al, source, PRIORITY_HIGH, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ void ALStream::update(double delta) {
|
|||||||
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
|
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (speaker->isStopped() && !speaker->isStoppedManually()) {
|
if (speaker->isStopped() && !alspeaker->stopped) {
|
||||||
if (preloaded) {
|
if (preloaded) {
|
||||||
speaker->play();
|
speaker->play();
|
||||||
} else {
|
} else {
|
||||||
@@ -124,7 +124,8 @@ void ALStream::setTime(duration_t time) {
|
|||||||
// TODO: implement
|
// TODO: implement
|
||||||
}
|
}
|
||||||
|
|
||||||
ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority) : al(al), priority(priority), source(source) {
|
ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority, int channel)
|
||||||
|
: al(al), priority(priority), channel(channel), source(source) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ALSpeaker::~ALSpeaker() {
|
ALSpeaker::~ALSpeaker() {
|
||||||
@@ -133,6 +134,23 @@ ALSpeaker::~ALSpeaker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ALSpeaker::update(const Channel* channel, float masterVolume) {
|
||||||
|
float gain = this->volume * channel->getVolume()*masterVolume;
|
||||||
|
AL_CHECK(alSourcef(source, AL_GAIN, gain));
|
||||||
|
|
||||||
|
if (!paused) {
|
||||||
|
if (isPaused() && !channel->isPaused()) {
|
||||||
|
play();
|
||||||
|
} else if (isPlaying() && channel->isPaused()) {
|
||||||
|
AL_CHECK(alSourcePause(source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ALSpeaker::getChannel() const {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
State ALSpeaker::getState() const {
|
State ALSpeaker::getState() const {
|
||||||
int state = AL::getSourcei(source, AL_SOURCE_STATE, AL_STOPPED);
|
int state = AL::getSourcei(source, AL_SOURCE_STATE, AL_STOPPED);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@@ -147,7 +165,7 @@ float ALSpeaker::getVolume() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ALSpeaker::setVolume(float volume) {
|
void ALSpeaker::setVolume(float volume) {
|
||||||
AL_CHECK(alSourcef(source, AL_GAIN, volume));
|
this->volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ALSpeaker::getPitch() const {
|
float ALSpeaker::getPitch() const {
|
||||||
@@ -167,16 +185,18 @@ void ALSpeaker::setLoop(bool loop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ALSpeaker::play() {
|
void ALSpeaker::play() {
|
||||||
stoppedManually = false;
|
paused = false;
|
||||||
|
stopped = false;
|
||||||
AL_CHECK(alSourcePlay(source));
|
AL_CHECK(alSourcePlay(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALSpeaker::pause() {
|
void ALSpeaker::pause() {
|
||||||
|
paused = true;
|
||||||
AL_CHECK(alSourcePause(source));
|
AL_CHECK(alSourcePause(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALSpeaker::stop() {
|
void ALSpeaker::stop() {
|
||||||
stoppedManually = true;
|
stopped = true;
|
||||||
if (source) {
|
if (source) {
|
||||||
AL_CHECK(alSourceStop(source));
|
AL_CHECK(alSourceStop(source));
|
||||||
al->freeSource(source);
|
al->freeSource(source);
|
||||||
@@ -184,10 +204,6 @@ void ALSpeaker::stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ALSpeaker::isStoppedManually() const {
|
|
||||||
return stoppedManually;
|
|
||||||
}
|
|
||||||
|
|
||||||
duration_t ALSpeaker::getTime() const {
|
duration_t ALSpeaker::getTime() const {
|
||||||
return static_cast<duration_t>(AL::getSourcef(source, AL_SEC_OFFSET));
|
return static_cast<duration_t>(AL::getSourcef(source, AL_SEC_OFFSET));
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-5
@@ -40,7 +40,7 @@ namespace audio {
|
|||||||
return pcm;
|
return pcm;
|
||||||
}
|
}
|
||||||
|
|
||||||
Speaker* newInstance(int priority) const override;
|
Speaker* newInstance(int priority, int channel) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ALStream : public Stream {
|
class ALStream : public Stream {
|
||||||
@@ -61,7 +61,7 @@ namespace audio {
|
|||||||
|
|
||||||
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(bool loop) override;
|
Speaker* createSpeaker(bool loop, int channel) 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;
|
||||||
@@ -73,13 +73,19 @@ namespace audio {
|
|||||||
class ALSpeaker : public Speaker {
|
class ALSpeaker : public Speaker {
|
||||||
ALAudio* al;
|
ALAudio* al;
|
||||||
int priority;
|
int priority;
|
||||||
bool stoppedManually = false;
|
int channel;
|
||||||
|
float volume = 0.0f;
|
||||||
public:
|
public:
|
||||||
|
bool stopped = true;
|
||||||
|
bool paused = false;
|
||||||
uint source;
|
uint source;
|
||||||
|
|
||||||
ALSpeaker(ALAudio* al, uint source, int priority);
|
ALSpeaker(ALAudio* al, uint source, int priority, int channel);
|
||||||
~ALSpeaker();
|
~ALSpeaker();
|
||||||
|
|
||||||
|
void update(const Channel* channel, float masterVolume) override;
|
||||||
|
int getChannel() const override;
|
||||||
|
|
||||||
State getState() const override;
|
State getState() const override;
|
||||||
|
|
||||||
float getVolume() const override;
|
float getVolume() const override;
|
||||||
@@ -94,7 +100,6 @@ namespace audio {
|
|||||||
void play() override;
|
void play() override;
|
||||||
void pause() override;
|
void pause() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
bool isStoppedManually() const override;
|
|
||||||
|
|
||||||
duration_t getTime() const override;
|
duration_t getTime() const override;
|
||||||
void setTime(duration_t time) override;
|
void setTime(duration_t time) override;
|
||||||
|
|||||||
+2
-2
@@ -19,7 +19,7 @@ namespace audio {
|
|||||||
return pcm;
|
return pcm;
|
||||||
}
|
}
|
||||||
|
|
||||||
Speaker* newInstance(int priority) const override {
|
Speaker* newInstance(int priority, int channel) const override {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -42,7 +42,7 @@ namespace audio {
|
|||||||
void bindSpeaker(speakerid_t speaker) override {
|
void bindSpeaker(speakerid_t speaker) override {
|
||||||
}
|
}
|
||||||
|
|
||||||
Speaker* createSpeaker(bool loop) override{
|
Speaker* createSpeaker(bool loop, int channel) override{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+118
-26
@@ -14,20 +14,50 @@ namespace audio {
|
|||||||
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;
|
static std::unordered_map<speakerid_t, std::shared_ptr<Stream>> streams;
|
||||||
|
static std::vector<std::unique_ptr<Channel>> channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace audio;
|
using namespace audio;
|
||||||
|
|
||||||
|
Channel::Channel(std::string name) : name(name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
float Channel::getVolume() const {
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Channel::setVolume(float volume) {
|
||||||
|
this->volume = std::max(0.0f, std::min(volume, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Channel::getName() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see pause logic in audio::update
|
||||||
|
void Channel::pause() {
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see pause logic in audio::update
|
||||||
|
void Channel::resume() {
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Channel::isPaused() const {
|
||||||
|
return paused;
|
||||||
|
}
|
||||||
|
|
||||||
size_t PCMStream::readFully(char* buffer, size_t bufferSize, bool loop) {
|
size_t PCMStream::readFully(char* buffer, size_t bufferSize, bool loop) {
|
||||||
if (!isOpen()) {
|
if (!isOpen()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
long bytes = 0;
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
do {
|
do {
|
||||||
|
size_t bytes = 0;
|
||||||
do {
|
do {
|
||||||
bytes = read(buffer, bufferSize);
|
bytes = read(buffer, bufferSize);
|
||||||
if (bytes < 0) {
|
if (bytes == PCMStream::ERROR) {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
size += bytes;
|
size += bytes;
|
||||||
@@ -126,9 +156,14 @@ void audio::initialize(bool enabled) {
|
|||||||
std::cerr << "could not to initialize audio" << std::endl;
|
std::cerr << "could not to initialize audio" << std::endl;
|
||||||
backend = NoAudio::create();
|
backend = NoAudio::create();
|
||||||
}
|
}
|
||||||
|
create_channel("master");
|
||||||
|
create_channel("regular");
|
||||||
|
create_channel("music");
|
||||||
|
create_channel("ambient");
|
||||||
|
create_channel("ui");
|
||||||
}
|
}
|
||||||
|
|
||||||
PCM* audio::loadPCM(const fs::path& file, bool headerOnly) {
|
PCM* audio::load_PCM(const fs::path& file, bool headerOnly) {
|
||||||
if (!fs::exists(file)) {
|
if (!fs::exists(file)) {
|
||||||
throw std::runtime_error("file not found '"+file.u8string()+"'");
|
throw std::runtime_error("file not found '"+file.u8string()+"'");
|
||||||
}
|
}
|
||||||
@@ -141,16 +176,16 @@ PCM* audio::loadPCM(const fs::path& file, bool headerOnly) {
|
|||||||
throw std::runtime_error("unsupported audio format");
|
throw std::runtime_error("unsupported audio format");
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* audio::loadSound(const fs::path& file, bool keepPCM) {
|
Sound* audio::load_sound(const fs::path& file, bool keepPCM) {
|
||||||
std::shared_ptr<PCM> pcm(loadPCM(file, !keepPCM && backend->isDummy()));
|
std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy()));
|
||||||
return createSound(pcm, keepPCM);
|
return create_sound(pcm, keepPCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* audio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
|
Sound* audio::create_sound(std::shared_ptr<PCM> pcm, bool keepPCM) {
|
||||||
return backend->createSound(pcm, keepPCM);
|
return backend->createSound(pcm, keepPCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
PCMStream* audio::openPCMStream(const fs::path& file) {
|
PCMStream* audio::open_PCM_stream(const fs::path& file) {
|
||||||
std::string ext = file.extension().u8string();
|
std::string ext = file.extension().u8string();
|
||||||
if (ext == ".wav" || ext == ".WAV") {
|
if (ext == ".wav" || ext == ".WAV") {
|
||||||
return wav::create_stream(file);
|
return wav::create_stream(file);
|
||||||
@@ -160,27 +195,27 @@ 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) {
|
Stream* audio::open_stream(const fs::path& file, bool keepSource) {
|
||||||
if (!keepSource && backend->isDummy()) {
|
if (!keepSource && backend->isDummy()) {
|
||||||
auto header = loadPCM(file, true);
|
auto header = load_PCM(file, true);
|
||||||
// using void source sized as audio instead of actual audio file
|
// using void source sized as audio instead of actual audio file
|
||||||
return openStream(
|
return open_stream(
|
||||||
std::make_shared<PCMVoidSource>(header->totalSamples, header->sampleRate, header->seekable),
|
std::make_shared<PCMVoidSource>(header->totalSamples, header->sampleRate, header->seekable),
|
||||||
keepSource
|
keepSource
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return openStream(
|
return open_stream(
|
||||||
std::shared_ptr<PCMStream>(openPCMStream(file)),
|
std::shared_ptr<PCMStream>(open_PCM_stream(file)),
|
||||||
keepSource
|
keepSource
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream* audio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
|
Stream* audio::open_stream(std::shared_ptr<PCMStream> stream, bool keepSource) {
|
||||||
return backend->openStream(stream, keepSource);
|
return backend->openStream(stream, keepSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void audio::setListener(
|
void audio::set_listener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
glm::vec3 velocity,
|
glm::vec3 velocity,
|
||||||
glm::vec3 lookAt,
|
glm::vec3 lookAt,
|
||||||
@@ -215,12 +250,13 @@ speakerid_t audio::play(
|
|||||||
float volume,
|
float volume,
|
||||||
float pitch,
|
float pitch,
|
||||||
bool loop,
|
bool loop,
|
||||||
int priority
|
int priority,
|
||||||
|
int channel
|
||||||
) {
|
) {
|
||||||
Speaker* speaker = sound->newInstance(priority);
|
Speaker* speaker = sound->newInstance(priority, channel);
|
||||||
if (speaker == nullptr) {
|
if (speaker == nullptr) {
|
||||||
remove_lower_priority_speaker(priority);
|
remove_lower_priority_speaker(priority);
|
||||||
speaker = sound->newInstance(priority);
|
speaker = sound->newInstance(priority, channel);
|
||||||
}
|
}
|
||||||
if (speaker == nullptr) {
|
if (speaker == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -242,12 +278,13 @@ speakerid_t audio::play(
|
|||||||
bool relative,
|
bool relative,
|
||||||
float volume,
|
float volume,
|
||||||
float pitch,
|
float pitch,
|
||||||
bool loop
|
bool loop,
|
||||||
|
int channel
|
||||||
) {
|
) {
|
||||||
Speaker* speaker = stream->createSpeaker(loop);
|
Speaker* speaker = stream->createSpeaker(loop, channel);
|
||||||
if (speaker == nullptr) {
|
if (speaker == nullptr) {
|
||||||
remove_lower_priority_speaker(PRIORITY_HIGH);
|
remove_lower_priority_speaker(PRIORITY_HIGH);
|
||||||
speaker = stream->createSpeaker(loop);
|
speaker = stream->createSpeaker(loop, channel);
|
||||||
}
|
}
|
||||||
if (speaker == nullptr) {
|
if (speaker == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -266,16 +303,17 @@ speakerid_t audio::play(
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
speakerid_t audio::playStream(
|
speakerid_t audio::play_stream(
|
||||||
const fs::path& file,
|
const fs::path& file,
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
bool relative,
|
bool relative,
|
||||||
float volume,
|
float volume,
|
||||||
float pitch,
|
float pitch,
|
||||||
bool loop
|
bool loop,
|
||||||
|
int channel
|
||||||
) {
|
) {
|
||||||
std::shared_ptr<Stream> stream (openStream(file, false));
|
std::shared_ptr<Stream> stream (open_stream(file, false));
|
||||||
return play(stream, position, relative, volume, pitch, loop);
|
return play(stream, position, relative, volume, pitch, loop, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
Speaker* audio::get(speakerid_t id) {
|
Speaker* audio::get(speakerid_t id) {
|
||||||
@@ -286,6 +324,37 @@ Speaker* audio::get(speakerid_t id) {
|
|||||||
return found->second.get();
|
return found->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int audio::create_channel(const std::string& name) {
|
||||||
|
int index = get_channel_index(name);
|
||||||
|
if (index != -1) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
channels.emplace_back(new Channel(name));
|
||||||
|
return channels.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio::get_channel_index(const std::string& name) {
|
||||||
|
int index = 0;
|
||||||
|
for (auto& channel : channels) {
|
||||||
|
if (channel->getName() == name) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel* audio::get_channel(int index) {
|
||||||
|
if (index < 0 || index >= int(channels.size())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return channels.at(index).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
float audio::get_master_volume() {
|
||||||
|
return channels.at(0)->getVolume();
|
||||||
|
}
|
||||||
|
|
||||||
void audio::update(double delta) {
|
void audio::update(double delta) {
|
||||||
backend->update(delta);
|
backend->update(delta);
|
||||||
|
|
||||||
@@ -293,8 +362,15 @@ void audio::update(double delta) {
|
|||||||
entry.second->update(delta);
|
entry.second->update(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float masterVolume = get_master_volume();
|
||||||
for (auto it = speakers.begin(); it != speakers.end();) {
|
for (auto it = speakers.begin(); it != speakers.end();) {
|
||||||
if (it->second->isStoppedManually()) {
|
auto speaker = it->second.get();
|
||||||
|
int speakerChannel = speaker->getChannel();
|
||||||
|
auto channel = get_channel(speakerChannel);
|
||||||
|
if (channel != nullptr) {
|
||||||
|
speaker->update(channel, speakerChannel == 0 ? 1.0f : masterVolume);
|
||||||
|
}
|
||||||
|
if (speaker->isStopped()) {
|
||||||
streams.erase(it->first);
|
streams.erase(it->first);
|
||||||
it = speakers.erase(it);
|
it = speakers.erase(it);
|
||||||
} else {
|
} else {
|
||||||
@@ -303,6 +379,22 @@ void audio::update(double delta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void audio::reset() {
|
||||||
|
for (auto& entry : speakers) {
|
||||||
|
entry.second->stop();
|
||||||
|
}
|
||||||
|
for (auto& entry : streams) {
|
||||||
|
entry.second->update(0.0f);
|
||||||
|
}
|
||||||
|
for (auto& channel : channels) {
|
||||||
|
if (channel->isPaused()) {
|
||||||
|
channel->resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streams.clear();
|
||||||
|
speakers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void audio::close() {
|
void audio::close() {
|
||||||
speakers.clear();
|
speakers.clear();
|
||||||
delete backend;
|
delete backend;
|
||||||
|
|||||||
+89
-18
@@ -34,6 +34,37 @@ namespace audio {
|
|||||||
stopped
|
stopped
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Mixer channel controls speakers volume and effects
|
||||||
|
/// There is main channel 'master' and sub-channels like 'regular', 'music', 'ambient'...
|
||||||
|
class Channel {
|
||||||
|
/// @brief Channel name
|
||||||
|
std::string name;
|
||||||
|
/// @brief Channel volume setting
|
||||||
|
float volume = 1.0f;
|
||||||
|
bool paused = false;
|
||||||
|
public:
|
||||||
|
Channel(std::string name);
|
||||||
|
|
||||||
|
/// @brief Get channel volume
|
||||||
|
float getVolume() const;
|
||||||
|
|
||||||
|
/// @brief Set channel volume
|
||||||
|
/// @param volume value in range [0.0, 1.0]
|
||||||
|
void setVolume(float volume);
|
||||||
|
|
||||||
|
/// @brief Get channel name
|
||||||
|
const std::string& getName() const;
|
||||||
|
|
||||||
|
/// @brief Pause all speakers in channel
|
||||||
|
void pause();
|
||||||
|
|
||||||
|
/// @brief Unpause all speakers paused by this channel
|
||||||
|
void resume();
|
||||||
|
|
||||||
|
/// @brief Check if the channel is paused
|
||||||
|
bool isPaused() const;
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Pulse-code modulation data
|
/// @brief Pulse-code modulation data
|
||||||
struct PCM {
|
struct PCM {
|
||||||
/// @brief May contain 8 bit and 16 bit PCM data
|
/// @brief May contain 8 bit and 16 bit PCM data
|
||||||
@@ -64,6 +95,8 @@ namespace audio {
|
|||||||
sampleRate(sampleRate),
|
sampleRate(sampleRate),
|
||||||
seekable(seekable) {}
|
seekable(seekable) {}
|
||||||
|
|
||||||
|
/// @brief Get total audio duration
|
||||||
|
/// @return duration in seconds
|
||||||
inline duration_t getDuration() const {
|
inline duration_t getDuration() const {
|
||||||
return static_cast<duration_t>(totalSamples) /
|
return static_cast<duration_t>(totalSamples) /
|
||||||
static_cast<duration_t>(sampleRate);
|
static_cast<duration_t>(sampleRate);
|
||||||
@@ -115,6 +148,9 @@ namespace audio {
|
|||||||
/// @brief Move playhead to the selected sample number
|
/// @brief Move playhead to the selected sample number
|
||||||
/// @param position selected sample number
|
/// @param position selected sample number
|
||||||
virtual void seek(size_t position) = 0;
|
virtual void seek(size_t position) = 0;
|
||||||
|
|
||||||
|
/// @brief Value returning by read(...) in case of error
|
||||||
|
static const size_t ERROR = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Audio streaming interface
|
/// @brief Audio streaming interface
|
||||||
@@ -130,8 +166,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)
|
/// @param loop is stream looped (required for correct buffers preload)
|
||||||
|
/// @param channel channel index
|
||||||
/// @return speaker id or 0
|
/// @return speaker id or 0
|
||||||
virtual Speaker* createSpeaker(bool loop) = 0;
|
virtual Speaker* createSpeaker(bool loop, int channel) = 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
|
||||||
@@ -168,16 +205,26 @@ namespace audio {
|
|||||||
/// @brief Create new sound instance
|
/// @brief Create new sound instance
|
||||||
/// @param priority instance priority. High priority instance can
|
/// @param priority instance priority. High priority instance can
|
||||||
/// take out speaker from low priority instance
|
/// take out speaker from low priority instance
|
||||||
|
/// @param channel channel index
|
||||||
/// @return new speaker with sound bound or nullptr
|
/// @return new speaker with sound bound or nullptr
|
||||||
/// if all speakers are in use
|
/// if all speakers are in use
|
||||||
virtual Speaker* newInstance(int priority) const = 0;
|
virtual Speaker* newInstance(int priority, int channel) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Audio source controller interface
|
/// @brief Audio source controller interface.
|
||||||
|
/// @attention Speaker is not supposed to be reused
|
||||||
class Speaker {
|
class Speaker {
|
||||||
public:
|
public:
|
||||||
virtual ~Speaker() {}
|
virtual ~Speaker() {}
|
||||||
|
|
||||||
|
/// @brief Synchronize the speaker with channel settings
|
||||||
|
/// @param channel speaker channel
|
||||||
|
/// @param masterVolume volume of the master channel
|
||||||
|
virtual void update(const Channel* channel, float masterVolume) = 0;
|
||||||
|
|
||||||
|
/// @brief Check speaker channel index
|
||||||
|
virtual int getChannel() const = 0;
|
||||||
|
|
||||||
/// @brief Get current speaker state
|
/// @brief Get current speaker state
|
||||||
/// @return speaker state
|
/// @return speaker state
|
||||||
virtual State getState() const = 0;
|
virtual State getState() const = 0;
|
||||||
@@ -215,9 +262,6 @@ namespace audio {
|
|||||||
/// @brief Stop and destroy speaker
|
/// @brief Stop and destroy speaker
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
|
|
||||||
/// @brief Check if the speaker has stopped by calling stop()
|
|
||||||
virtual bool isStoppedManually() const = 0;
|
|
||||||
|
|
||||||
/// @brief Get current time position of playing audio
|
/// @brief Get current time position of playing audio
|
||||||
/// @return time position in seconds
|
/// @return time position in seconds
|
||||||
virtual duration_t getTime() const = 0;
|
virtual duration_t getTime() const = 0;
|
||||||
@@ -297,45 +341,45 @@ namespace audio {
|
|||||||
/// @param headerOnly read header only
|
/// @param headerOnly read header only
|
||||||
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
||||||
/// @return PCM audio data
|
/// @return PCM audio data
|
||||||
extern PCM* loadPCM(const fs::path& file, bool headerOnly);
|
extern PCM* load_PCM(const fs::path& file, bool headerOnly);
|
||||||
|
|
||||||
/// @brief Load sound from file
|
/// @brief Load sound from file
|
||||||
/// @param file audio file path
|
/// @param file audio file path
|
||||||
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
|
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
|
||||||
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
||||||
/// @return new Sound instance
|
/// @return new Sound instance
|
||||||
extern Sound* loadSound(const fs::path& file, bool keepPCM);
|
extern Sound* load_sound(const fs::path& file, bool keepPCM);
|
||||||
|
|
||||||
/// @brief Create new sound from PCM data
|
/// @brief Create new sound from PCM data
|
||||||
/// @param pcm PCM data
|
/// @param pcm PCM data
|
||||||
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
|
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
|
||||||
/// @return new Sound instance
|
/// @return new Sound instance
|
||||||
extern Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM);
|
extern Sound* create_sound(std::shared_ptr<PCM> pcm, bool keepPCM);
|
||||||
|
|
||||||
/// @brief Open new PCM stream from file
|
/// @brief Open new PCM stream from file
|
||||||
/// @param file audio file path
|
/// @param file audio file path
|
||||||
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
||||||
/// @return new PCMStream instance
|
/// @return new PCMStream instance
|
||||||
extern PCMStream* openPCMStream(const fs::path& file);
|
extern PCMStream* open_PCM_stream(const fs::path& file);
|
||||||
|
|
||||||
/// @brief Open new audio stream from file
|
/// @brief Open new audio stream from file
|
||||||
/// @param file audio file path
|
/// @param file audio file path
|
||||||
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
|
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
|
||||||
/// @return new Stream instance
|
/// @return new Stream instance
|
||||||
extern Stream* openStream(const fs::path& file, bool keepSource);
|
extern Stream* open_stream(const fs::path& file, bool keepSource);
|
||||||
|
|
||||||
/// @brief Open new audio stream from source
|
/// @brief Open new audio stream from source
|
||||||
/// @param stream PCM data source
|
/// @param stream PCM data source
|
||||||
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
|
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
|
||||||
/// @return new Stream instance
|
/// @return new Stream instance
|
||||||
extern Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource);
|
extern Stream* open_stream(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)
|
||||||
/// @param lookAt point the listener look at
|
/// @param lookAt point the listener look at
|
||||||
/// @param up camera up vector
|
/// @param up camera up vector
|
||||||
extern void setListener(
|
extern void set_listener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
glm::vec3 velocity,
|
glm::vec3 velocity,
|
||||||
glm::vec3 lookAt,
|
glm::vec3 lookAt,
|
||||||
@@ -351,6 +395,7 @@ namespace audio {
|
|||||||
/// @param loop loop sound
|
/// @param loop loop sound
|
||||||
/// @param priority sound priority
|
/// @param priority sound priority
|
||||||
/// (PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH)
|
/// (PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH)
|
||||||
|
/// @param channel channel index
|
||||||
/// @return speaker id or 0
|
/// @return speaker id or 0
|
||||||
extern speakerid_t play(
|
extern speakerid_t play(
|
||||||
Sound* sound,
|
Sound* sound,
|
||||||
@@ -359,7 +404,8 @@ namespace audio {
|
|||||||
float volume,
|
float volume,
|
||||||
float pitch,
|
float pitch,
|
||||||
bool loop,
|
bool loop,
|
||||||
int priority
|
int priority,
|
||||||
|
int channel
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @brief Play stream
|
/// @brief Play stream
|
||||||
@@ -369,6 +415,7 @@ namespace audio {
|
|||||||
/// @param volume stream volume [0.0-1.0]
|
/// @param volume stream volume [0.0-1.0]
|
||||||
/// @param pitch stream pitch multiplier [0.0-...]
|
/// @param pitch stream pitch multiplier [0.0-...]
|
||||||
/// @param loop loop stream
|
/// @param loop loop stream
|
||||||
|
/// @param channel channel index
|
||||||
/// @return speaker id or 0
|
/// @return speaker id or 0
|
||||||
extern speakerid_t play(
|
extern speakerid_t play(
|
||||||
std::shared_ptr<Stream> stream,
|
std::shared_ptr<Stream> stream,
|
||||||
@@ -376,7 +423,8 @@ namespace audio {
|
|||||||
bool relative,
|
bool relative,
|
||||||
float volume,
|
float volume,
|
||||||
float pitch,
|
float pitch,
|
||||||
bool loop
|
bool loop,
|
||||||
|
int channel
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @brief Play stream from file
|
/// @brief Play stream from file
|
||||||
@@ -386,15 +434,16 @@ namespace audio {
|
|||||||
/// @param volume stream volume [0.0-1.0]
|
/// @param volume stream volume [0.0-1.0]
|
||||||
/// @param pitch stream pitch multiplier [0.0-...]
|
/// @param pitch stream pitch multiplier [0.0-...]
|
||||||
/// @param loop loop stream
|
/// @param loop loop stream
|
||||||
|
/// @param channel channel index
|
||||||
/// @return speaker id or 0
|
/// @return speaker id or 0
|
||||||
/// @return
|
extern speakerid_t play_stream(
|
||||||
extern speakerid_t playStream(
|
|
||||||
const fs::path& file,
|
const fs::path& file,
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
bool relative,
|
bool relative,
|
||||||
float volume,
|
float volume,
|
||||||
float pitch,
|
float pitch,
|
||||||
bool loop
|
bool loop,
|
||||||
|
int channel
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @brief Get speaker by id
|
/// @brief Get speaker by id
|
||||||
@@ -402,10 +451,32 @@ namespace audio {
|
|||||||
/// @return speaker or nullptr
|
/// @return speaker or nullptr
|
||||||
extern Speaker* get(speakerid_t id);
|
extern Speaker* get(speakerid_t id);
|
||||||
|
|
||||||
|
/// @brief Create new channel.
|
||||||
|
/// All non-builtin channels will be destroyed on audio::reset() call
|
||||||
|
/// @param name channel name
|
||||||
|
/// @return new channel index
|
||||||
|
extern int create_channel(const std::string& name);
|
||||||
|
|
||||||
|
/// @brief Get channel index by name
|
||||||
|
/// @param name channel name
|
||||||
|
/// @return channel index or -1
|
||||||
|
extern int get_channel_index(const std::string& name);
|
||||||
|
|
||||||
|
/// @brief Get channel by index. 0 - is master channel
|
||||||
|
/// @param name channel index
|
||||||
|
/// @return channel or nullptr
|
||||||
|
extern Channel* get_channel(int index);
|
||||||
|
|
||||||
|
/// @brief Get volume of the master channel
|
||||||
|
extern float get_master_volume();
|
||||||
|
|
||||||
/// @brief Update audio streams and sound instanced
|
/// @brief Update audio streams and sound instanced
|
||||||
/// @param delta time elapsed since the last update (seconds)
|
/// @param delta time elapsed since the last update (seconds)
|
||||||
extern void update(double delta);
|
extern void update(double delta);
|
||||||
|
|
||||||
|
/// @brief Stop all playing audio, destroy all non-builtin channels
|
||||||
|
extern void reset();
|
||||||
|
|
||||||
/// @brief Finalize audio system
|
/// @brief Finalize audio system
|
||||||
extern void close();
|
extern void close();
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -91,7 +91,7 @@ public:
|
|||||||
long bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream);
|
long bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream);
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
std::cerr << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes << std::endl;
|
std::cerr << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes << std::endl;
|
||||||
return 0;
|
return PCMStream::ERROR;
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ void LevelScreen::update(float delta) {
|
|||||||
|
|
||||||
auto player = controller->getPlayer();
|
auto player = controller->getPlayer();
|
||||||
auto camera = player->camera;
|
auto camera = player->camera;
|
||||||
audio::setListener(
|
audio::set_listener(
|
||||||
camera->position,
|
camera->position,
|
||||||
player->hitbox->velocity,
|
player->hitbox->velocity,
|
||||||
camera->position+camera->dir,
|
camera->position+camera->dir,
|
||||||
|
|||||||
Reference in New Issue
Block a user