mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-02 16:38:41 +00:00
Sound refactor and improvements (#12764)
This commit is contained in:
parent
8e1af25738
commit
edcbfa31c9
52 changed files with 2802 additions and 1211 deletions
613
src/client/sound_openal_internal.h
Normal file
613
src/client/sound_openal_internal.h
Normal file
|
@ -0,0 +1,613 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2022 DS
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
OpenAL support based on work by:
|
||||
Copyright (C) 2011 Sebastian 'Bahamada' Rühl
|
||||
Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
|
||||
Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "log.h"
|
||||
#include "porting.h"
|
||||
#include "sound_openal.h"
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
//#include <alext.h>
|
||||
#elif defined(__APPLE__)
|
||||
#define OPENAL_DEPRECATED
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
//#include <OpenAL/alext.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <AL/alext.h>
|
||||
#endif
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* The coordinate space for sounds (sound-space):
|
||||
* ----------------------------------------------
|
||||
*
|
||||
* * The functions from ISoundManager (see sound.h) take spatial vectors in node-space.
|
||||
* * All other `v3f`s here are, if not told otherwise, in sound-space, which is
|
||||
* defined as node-space mirrored along the x-axis.
|
||||
* (This is needed because OpenAL uses a right-handed coordinate system.)
|
||||
* * Use `swap_handedness()` to convert between those two coordinate spaces.
|
||||
*
|
||||
*
|
||||
* How sounds are loaded:
|
||||
* ----------------------
|
||||
*
|
||||
* * Step 1:
|
||||
* `loadSoundFile` or `loadSoundFile` is called. This adds an unopen sound with
|
||||
* the given name to `m_sound_datas_unopen`.
|
||||
* Unopen / lazy sounds (`ISoundDataUnopen`) are ogg-vorbis files that we did not yet
|
||||
* start to decode. (Decoding an unopen sound does not fail under normal circumstances
|
||||
* (because we check whether the file exists at least), if it does fail anyways,
|
||||
* we should notify the user.)
|
||||
* * Step 2:
|
||||
* `addSoundToGroup` is called, to add the name from step 1 to a group. If the
|
||||
* group does not yet exist, a new one is created. A group can later be played.
|
||||
* (The mapping is stored in `m_sound_groups`.)
|
||||
* * Step 3:
|
||||
* `playSound` or `playSoundAt` is called.
|
||||
* * Step 3.1:
|
||||
* If the group with the name `spec.name` does not exist, and `spec.use_local_fallback`
|
||||
* is true, a new group is created using the user's sound-pack.
|
||||
* * Step 3.2:
|
||||
* We choose one random sound name from the given group.
|
||||
* * Step 3.3:
|
||||
* We open the sound (see `openSingleSound`).
|
||||
* If the sound is already open (in `m_sound_datas_open`), we take that one.
|
||||
* Otherwise we open it by calling `ISoundDataUnopen::open`. We choose (by
|
||||
* sound length), whether it's a single-buffer (`SoundDataOpenBuffer`) or
|
||||
* streamed (`SoundDataOpenStream`) sound.
|
||||
* Single-buffer sounds are always completely loaded. Streamed sounds can be
|
||||
* partially loaded.
|
||||
* The sound is erased from `m_sound_datas_unopen` and added to `m_sound_datas_open`.
|
||||
* Open sounds are kept forever.
|
||||
* * Step 3.4:
|
||||
* We create the new `PlayingSound`. It has a `shared_ptr` to its open sound.
|
||||
* If the open sound is streaming, the playing sound needs to be stepped using
|
||||
* `PlayingSound::stepStream` for enqueuing buffers. For this purpose, the sound
|
||||
* is added to `m_sounds_streaming` (as `weak_ptr`).
|
||||
* If the sound is fading, it is added to `m_sounds_fading` for regular fade-stepping.
|
||||
* The sound is also added to `m_sounds_playing`, so that one can access it
|
||||
* via its sound handle.
|
||||
* * Step 4:
|
||||
* Streaming sounds are updated. For details see [Streaming of sounds].
|
||||
* * Step 5:
|
||||
* At deinitialization, we can just let the destructors do their work.
|
||||
* Sound sources are deleted (and with this also stopped) by ~PlayingSound.
|
||||
* Buffers can't be deleted while sound sources using them exist, because
|
||||
* PlayingSound has a shared_ptr to its ISoundData.
|
||||
*
|
||||
*
|
||||
* Streaming of sounds:
|
||||
* --------------------
|
||||
*
|
||||
* In each "bigstep", all streamed sounds are stepStream()ed. This means a
|
||||
* sound can be stepped at any point in time in the bigstep's interval.
|
||||
*
|
||||
* In the worst case, a sound is stepped at the start of one bigstep and in the
|
||||
* end of the next bigstep. So between two stepStream()-calls lie at most
|
||||
* 2 * STREAM_BIGSTEP_TIME seconds.
|
||||
* As there are always 2 sound buffers enqueued, at least one untouched full buffer
|
||||
* is still available after the first stepStream().
|
||||
* If we take a MIN_STREAM_BUFFER_LENGTH > 2 * STREAM_BIGSTEP_TIME, we can hence
|
||||
* not run into an empty queue.
|
||||
*
|
||||
* The MIN_STREAM_BUFFER_LENGTH needs to be a little bigger because of dtime jitter,
|
||||
* other sounds that may have taken long to stepStream(), and sounds being played
|
||||
* faster due to Doppler effect.
|
||||
*
|
||||
*/
|
||||
|
||||
// constants
|
||||
|
||||
// in seconds
|
||||
constexpr f32 REMOVE_DEAD_SOUNDS_INTERVAL = 2.0f;
|
||||
// maximum length in seconds that a sound can have without being streamed
|
||||
constexpr f32 SOUND_DURATION_MAX_SINGLE = 3.0f;
|
||||
// minimum time in seconds of a single buffer in a streamed sound
|
||||
constexpr f32 MIN_STREAM_BUFFER_LENGTH = 1.0f;
|
||||
// duration in seconds of one bigstep
|
||||
constexpr f32 STREAM_BIGSTEP_TIME = 0.3f;
|
||||
|
||||
static_assert(MIN_STREAM_BUFFER_LENGTH > STREAM_BIGSTEP_TIME * 2.0f,
|
||||
"See [Streaming of sounds].");
|
||||
static_assert(SOUND_DURATION_MAX_SINGLE >= MIN_STREAM_BUFFER_LENGTH * 2.0f,
|
||||
"There's no benefit in streaming if we can't queue more than 2 buffers.");
|
||||
|
||||
|
||||
/**
|
||||
* RAII wrapper for openal sound buffers.
|
||||
*/
|
||||
struct RAIIALSoundBuffer final
|
||||
{
|
||||
RAIIALSoundBuffer() noexcept = default;
|
||||
explicit RAIIALSoundBuffer(ALuint buffer) noexcept : m_buffer(buffer) {};
|
||||
|
||||
~RAIIALSoundBuffer() noexcept { reset(0); }
|
||||
|
||||
DISABLE_CLASS_COPY(RAIIALSoundBuffer)
|
||||
|
||||
RAIIALSoundBuffer(RAIIALSoundBuffer &&other) noexcept : m_buffer(other.release()) {}
|
||||
RAIIALSoundBuffer &operator=(RAIIALSoundBuffer &&other) noexcept;
|
||||
|
||||
ALuint get() noexcept { return m_buffer; }
|
||||
|
||||
ALuint release() noexcept { return std::exchange(m_buffer, 0); }
|
||||
|
||||
void reset(ALuint buf) noexcept;
|
||||
|
||||
static RAIIALSoundBuffer generate() noexcept;
|
||||
|
||||
private:
|
||||
// According to openal specification:
|
||||
// > Deleting buffer name 0 is a legal NOP.
|
||||
//
|
||||
// and:
|
||||
// > [...] the NULL buffer (i.e., 0) which can always be queued.
|
||||
ALuint m_buffer = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* For vorbisfile to read from our buffer instead of from a file.
|
||||
*/
|
||||
struct OggVorbisBufferSource {
|
||||
std::string buf;
|
||||
size_t cur_offset = 0;
|
||||
|
||||
static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource) noexcept;
|
||||
static int seek_func(void *datasource, ogg_int64_t offset, int whence) noexcept;
|
||||
static int close_func(void *datasource) noexcept;
|
||||
static long tell_func(void *datasource) noexcept;
|
||||
|
||||
static const ov_callbacks s_ov_callbacks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata of an Ogg-Vorbis file, used for decoding.
|
||||
* We query this information once and store it in this struct.
|
||||
*/
|
||||
struct OggFileDecodeInfo {
|
||||
std::string name_for_logging;
|
||||
bool is_stereo;
|
||||
ALenum format; // AL_FORMAT_MONO16 or AL_FORMAT_STEREO16
|
||||
size_t bytes_per_sample;
|
||||
ALsizei freq;
|
||||
ALuint length_samples = 0;
|
||||
f32 length_seconds = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII wrapper for OggVorbis_File.
|
||||
*/
|
||||
struct RAIIOggFile {
|
||||
bool m_needs_clear = false;
|
||||
OggVorbis_File m_file;
|
||||
|
||||
RAIIOggFile() = default;
|
||||
|
||||
DISABLE_CLASS_COPY(RAIIOggFile)
|
||||
|
||||
~RAIIOggFile() noexcept
|
||||
{
|
||||
if (m_needs_clear)
|
||||
ov_clear(&m_file);
|
||||
}
|
||||
|
||||
OggVorbis_File *get() { return &m_file; }
|
||||
|
||||
std::optional<OggFileDecodeInfo> getDecodeInfo(const std::string &filename_for_logging);
|
||||
|
||||
/**
|
||||
* Main function for loading ogg vorbis sounds.
|
||||
* Loads exactly the specified interval of PCM-data, and creates an OpenAL
|
||||
* buffer with it.
|
||||
*
|
||||
* @param decode_info Cached meta information of the file.
|
||||
* @param pcm_start First sample in the interval.
|
||||
* @param pcm_end One after last sample of the interval (=> exclusive).
|
||||
* @return An AL sound buffer, or a 0-buffer on failure.
|
||||
*/
|
||||
RAIIALSoundBuffer loadBuffer(const OggFileDecodeInfo &decode_info, ALuint pcm_start,
|
||||
ALuint pcm_end);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class for the openal device and context
|
||||
*/
|
||||
class SoundManagerSingleton
|
||||
{
|
||||
public:
|
||||
struct AlcDeviceDeleter {
|
||||
void operator()(ALCdevice *p)
|
||||
{
|
||||
alcCloseDevice(p);
|
||||
}
|
||||
};
|
||||
|
||||
struct AlcContextDeleter {
|
||||
void operator()(ALCcontext *p)
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(p);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_ptr_alcdevice = std::unique_ptr<ALCdevice, AlcDeviceDeleter>;
|
||||
using unique_ptr_alccontext = std::unique_ptr<ALCcontext, AlcContextDeleter>;
|
||||
|
||||
unique_ptr_alcdevice m_device;
|
||||
unique_ptr_alccontext m_context;
|
||||
|
||||
public:
|
||||
bool init();
|
||||
|
||||
~SoundManagerSingleton();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stores sound pcm data buffers.
|
||||
*/
|
||||
struct ISoundDataOpen
|
||||
{
|
||||
OggFileDecodeInfo m_decode_info;
|
||||
|
||||
explicit ISoundDataOpen(const OggFileDecodeInfo &decode_info) :
|
||||
m_decode_info(decode_info) {}
|
||||
|
||||
virtual ~ISoundDataOpen() = default;
|
||||
|
||||
/**
|
||||
* Iff the data is streaming, there is more than one buffer.
|
||||
* @return Whether it's streaming data.
|
||||
*/
|
||||
virtual bool isStreaming() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Load a buffer containing data starting at the given offset. Or just get it
|
||||
* if it was already loaded.
|
||||
*
|
||||
* This function returns multiple values:
|
||||
* * `buffer`: The OpenAL buffer.
|
||||
* * `buffer_end`: The offset (in the file) where `buffer` ends (exclusive).
|
||||
* * `offset_in_buffer`: Offset relative to `buffer`'s start where the requested
|
||||
* `offset` is.
|
||||
* `offset_in_buffer == 0` is guaranteed if some loaded buffer ends at
|
||||
* `offset`.
|
||||
*
|
||||
* @param offset The start of the buffer.
|
||||
* @return `{buffer, buffer_end, offset_in_buffer}` or `{0, sound_data_end, 0}`
|
||||
* if `offset` is invalid.
|
||||
*/
|
||||
virtual std::tuple<ALuint, ALuint, ALuint> getOrLoadBufferAt(ALuint offset) = 0;
|
||||
|
||||
static std::shared_ptr<ISoundDataOpen> fromOggFile(std::unique_ptr<RAIIOggFile> oggfile,
|
||||
const std::string &filename_for_logging);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will be opened lazily when first used.
|
||||
*/
|
||||
struct ISoundDataUnopen
|
||||
{
|
||||
virtual ~ISoundDataUnopen() = default;
|
||||
|
||||
// Note: The ISoundDataUnopen is moved (see &&). It is not meant to be kept
|
||||
// after opening.
|
||||
virtual std::shared_ptr<ISoundDataOpen> open(const std::string &sound_name) && = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sound file is in a memory buffer.
|
||||
*/
|
||||
struct SoundDataUnopenBuffer final : ISoundDataUnopen
|
||||
{
|
||||
std::string m_buffer;
|
||||
|
||||
explicit SoundDataUnopenBuffer(std::string &&buffer) : m_buffer(std::move(buffer)) {}
|
||||
|
||||
std::shared_ptr<ISoundDataOpen> open(const std::string &sound_name) && override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sound file is in file system.
|
||||
*/
|
||||
struct SoundDataUnopenFile final : ISoundDataUnopen
|
||||
{
|
||||
std::string m_path;
|
||||
|
||||
explicit SoundDataUnopenFile(const std::string &path) : m_path(path) {}
|
||||
|
||||
std::shared_ptr<ISoundDataOpen> open(const std::string &sound_name) && override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Non-streaming opened sound data.
|
||||
* All data is completely loaded in one buffer.
|
||||
*/
|
||||
struct SoundDataOpenBuffer final : ISoundDataOpen
|
||||
{
|
||||
RAIIALSoundBuffer m_buffer;
|
||||
|
||||
SoundDataOpenBuffer(std::unique_ptr<RAIIOggFile> oggfile,
|
||||
const OggFileDecodeInfo &decode_info);
|
||||
|
||||
bool isStreaming() const noexcept override { return false; }
|
||||
|
||||
std::tuple<ALuint, ALuint, ALuint> getOrLoadBufferAt(ALuint offset) override
|
||||
{
|
||||
if (offset >= m_decode_info.length_samples)
|
||||
return {0, m_decode_info.length_samples, 0};
|
||||
return {m_buffer.get(), m_decode_info.length_samples, offset};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Streaming opened sound data.
|
||||
*
|
||||
* Uses a sorted list of contiguous sound data regions (`ContiguousBuffers`s) for
|
||||
* efficient seeking.
|
||||
*/
|
||||
struct SoundDataOpenStream final : ISoundDataOpen
|
||||
{
|
||||
/**
|
||||
* An OpenAL buffer that goes until `m_end` (exclusive).
|
||||
*/
|
||||
struct SoundBufferUntil final
|
||||
{
|
||||
ALuint m_end;
|
||||
RAIIALSoundBuffer m_buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* A sorted non-empty vector of contiguous buffers.
|
||||
* The start (inclusive) of each buffer is the end of its predecessor, or
|
||||
* `m_start` for the first buffer.
|
||||
*/
|
||||
struct ContiguousBuffers final
|
||||
{
|
||||
ALuint m_start;
|
||||
std::vector<SoundBufferUntil> m_buffers;
|
||||
};
|
||||
|
||||
std::unique_ptr<RAIIOggFile> m_oggfile;
|
||||
// A sorted vector of non-overlapping, non-contiguous `ContiguousBuffers`s.
|
||||
std::vector<ContiguousBuffers> m_bufferss;
|
||||
|
||||
SoundDataOpenStream(std::unique_ptr<RAIIOggFile> oggfile,
|
||||
const OggFileDecodeInfo &decode_info);
|
||||
|
||||
bool isStreaming() const noexcept override { return true; }
|
||||
|
||||
std::tuple<ALuint, ALuint, ALuint> getOrLoadBufferAt(ALuint offset) override;
|
||||
|
||||
private:
|
||||
// offset must be before after_it's m_start and after (after_it-1)'s last m_end
|
||||
// new buffer will be inserted into m_bufferss before after_it
|
||||
// returns same as getOrLoadBufferAt
|
||||
std::tuple<ALuint, ALuint, ALuint> loadBufferAt(ALuint offset,
|
||||
std::vector<ContiguousBuffers>::iterator after_it);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A sound that is currently played.
|
||||
* Can be streaming.
|
||||
* Can be fading.
|
||||
*/
|
||||
class PlayingSound final
|
||||
{
|
||||
struct FadeState {
|
||||
f32 step;
|
||||
f32 target_gain;
|
||||
};
|
||||
|
||||
ALuint m_source_id;
|
||||
std::shared_ptr<ISoundDataOpen> m_data;
|
||||
ALuint m_next_sample_pos = 0;
|
||||
bool m_looping;
|
||||
bool m_is_positional;
|
||||
bool m_stopped_means_dead = true;
|
||||
std::optional<FadeState> m_fade_state = std::nullopt;
|
||||
|
||||
public:
|
||||
PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data, bool loop,
|
||||
f32 volume, f32 pitch, f32 start_time,
|
||||
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt);
|
||||
|
||||
~PlayingSound() noexcept
|
||||
{
|
||||
alDeleteSources(1, &m_source_id);
|
||||
}
|
||||
|
||||
DISABLE_CLASS_COPY(PlayingSound)
|
||||
|
||||
// return false means streaming finished
|
||||
bool stepStream();
|
||||
|
||||
// retruns true if it wasn't fading already
|
||||
bool fade(f32 step, f32 target_gain) noexcept;
|
||||
|
||||
// returns true if more fade is needed later
|
||||
bool doFade(f32 dtime) noexcept;
|
||||
|
||||
void updatePosVel(const v3f &pos, const v3f &vel) noexcept;
|
||||
|
||||
void setGain(f32 gain) noexcept;
|
||||
|
||||
f32 getGain() noexcept;
|
||||
|
||||
void setPitch(f32 pitch) noexcept { alSourcef(m_source_id, AL_PITCH, pitch); }
|
||||
|
||||
bool isStreaming() const noexcept { return m_data->isStreaming(); }
|
||||
|
||||
void play() noexcept { alSourcePlay(m_source_id); }
|
||||
|
||||
// returns one of AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED
|
||||
ALint getState() noexcept
|
||||
{
|
||||
ALint state;
|
||||
alGetSourcei(m_source_id, AL_SOURCE_STATE, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool isDead() noexcept
|
||||
{
|
||||
// streaming sounds can (but should not) stop because the queue runs empty
|
||||
return m_stopped_means_dead && getState() == AL_STOPPED;
|
||||
}
|
||||
|
||||
void pause() noexcept
|
||||
{
|
||||
// this is a NOP if state != AL_PLAYING
|
||||
alSourcePause(m_source_id);
|
||||
}
|
||||
|
||||
void resume() noexcept
|
||||
{
|
||||
if (getState() == AL_PAUSED)
|
||||
play();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The public ISoundManager interface
|
||||
*/
|
||||
|
||||
class OpenALSoundManager final : public ISoundManager
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<SoundFallbackPathProvider> m_fallback_path_provider;
|
||||
|
||||
ALCdevice *m_device;
|
||||
ALCcontext *m_context;
|
||||
|
||||
// time in seconds until which removeDeadSounds will be called again
|
||||
f32 m_time_until_dead_removal = REMOVE_DEAD_SOUNDS_INTERVAL;
|
||||
|
||||
// loaded sounds
|
||||
std::unordered_map<std::string, std::unique_ptr<ISoundDataUnopen>> m_sound_datas_unopen;
|
||||
std::unordered_map<std::string, std::shared_ptr<ISoundDataOpen>> m_sound_datas_open;
|
||||
// sound groups
|
||||
std::unordered_map<std::string, std::vector<std::string>> m_sound_groups;
|
||||
|
||||
// currently playing sounds
|
||||
std::unordered_map<sound_handle_t, std::shared_ptr<PlayingSound>> m_sounds_playing;
|
||||
|
||||
// streamed sounds
|
||||
std::vector<std::weak_ptr<PlayingSound>> m_sounds_streaming_current_bigstep;
|
||||
std::vector<std::weak_ptr<PlayingSound>> m_sounds_streaming_next_bigstep;
|
||||
// time left until current bigstep finishes
|
||||
f32 m_stream_timer = STREAM_BIGSTEP_TIME;
|
||||
|
||||
std::vector<std::weak_ptr<PlayingSound>> m_sounds_fading;
|
||||
|
||||
// if true, all sounds will be directly paused after creation
|
||||
bool m_is_paused = false;
|
||||
|
||||
private:
|
||||
void stepStreams(f32 dtime);
|
||||
void doFades(f32 dtime);
|
||||
|
||||
/**
|
||||
* Gives the open sound for a loaded sound.
|
||||
* Opens the sound if currently unopened.
|
||||
*
|
||||
* @param sound_name Name of the sound.
|
||||
* @return The open sound.
|
||||
*/
|
||||
std::shared_ptr<ISoundDataOpen> openSingleSound(const std::string &sound_name);
|
||||
|
||||
/**
|
||||
* Gets a random sound name from a group.
|
||||
*
|
||||
* @param group_name The name of the sound group.
|
||||
* @return The name of a sound in the group, or "" on failure. Getting the
|
||||
* sound with `openSingleSound` directly afterwards will not fail.
|
||||
*/
|
||||
std::string getLoadedSoundNameFromGroup(const std::string &group_name);
|
||||
|
||||
/**
|
||||
* Same as `getLoadedSoundNameFromGroup`, but if sound does not exist, try to
|
||||
* load from local files.
|
||||
*/
|
||||
std::string getOrLoadLoadedSoundNameFromGroup(const std::string &group_name);
|
||||
|
||||
std::shared_ptr<PlayingSound> createPlayingSound(const std::string &sound_name,
|
||||
bool loop, f32 volume, f32 pitch, f32 start_time,
|
||||
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt);
|
||||
|
||||
void playSoundGeneric(sound_handle_t id, const std::string &group_name, bool loop,
|
||||
f32 volume, f32 fade, f32 pitch, bool use_local_fallback, f32 start_time,
|
||||
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt);
|
||||
|
||||
/**
|
||||
* Deletes sounds that are dead (=finished).
|
||||
*
|
||||
* @return Number of removed sounds.
|
||||
*/
|
||||
int removeDeadSounds();
|
||||
|
||||
public:
|
||||
OpenALSoundManager(SoundManagerSingleton *smg,
|
||||
std::unique_ptr<SoundFallbackPathProvider> fallback_path_provider);
|
||||
|
||||
~OpenALSoundManager() override;
|
||||
|
||||
DISABLE_CLASS_COPY(OpenALSoundManager)
|
||||
|
||||
/* Interface */
|
||||
|
||||
void step(f32 dtime) override;
|
||||
void pauseAll() override;
|
||||
void resumeAll() override;
|
||||
|
||||
void updateListener(const v3f &pos_, const v3f &vel_, const v3f &at_, const v3f &up_) override;
|
||||
void setListenerGain(f32 gain) override;
|
||||
|
||||
bool loadSoundFile(const std::string &name, const std::string &filepath) override;
|
||||
bool loadSoundData(const std::string &name, std::string &&filedata) override;
|
||||
void addSoundToGroup(const std::string &sound_name, const std::string &group_name) override;
|
||||
|
||||
void playSound(sound_handle_t id, const SoundSpec &spec) override;
|
||||
void playSoundAt(sound_handle_t id, const SoundSpec &spec, const v3f &pos_,
|
||||
const v3f &vel_) override;
|
||||
void stopSound(sound_handle_t sound) override;
|
||||
void fadeSound(sound_handle_t soundid, f32 step, f32 target_gain) override;
|
||||
void updateSoundPosVel(sound_handle_t sound, const v3f &pos_, const v3f &vel_) override;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue