forked from Mirrorlandia_minetest/minetest
124 lines
5.3 KiB
C++
124 lines
5.3 KiB
C++
/*
|
|
Minetest
|
|
Copyright (C) 2022 DS
|
|
|
|
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
|
|
|
|
/*
|
|
*
|
|
* 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()` from `al_helpers.h` 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.
|
|
*
|
|
*/
|
|
|
|
namespace sound {
|
|
|
|
// 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;
|
|
// step duration for the OpenALSoundManager thread, in seconds
|
|
constexpr f32 SOUNDTHREAD_DTIME = 0.016f;
|
|
|
|
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.");
|
|
|
|
} // namespace sound
|