2020-09-28 04:15:06 +02:00
|
|
|
#define MUSIC_DEFINITION
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "Music.h"
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
#include <SDL.h>
|
2022-03-09 22:35:29 +01:00
|
|
|
#include <FAudio.h>
|
2020-07-30 23:06:07 +02:00
|
|
|
#include <physfsrwops.h>
|
2020-07-19 21:43:29 +02:00
|
|
|
|
2022-12-01 07:30:16 +01:00
|
|
|
#include "Alloc.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "BinaryBlob.h"
|
2021-12-26 14:38:16 +01:00
|
|
|
#include "FileSystemUtils.h"
|
2021-04-02 21:02:01 +02:00
|
|
|
#include "Game.h"
|
Replace main game music with music area map
The main game used a set of copy-pasted code to set the music of each
area. There WAS some redundancy built-in, but only three rooms in each
direction from the entrance of a zone.
Given this, it's completely possible for players to mismatch the music
of the area and level. In fact, it's easy to do it even on accident,
especially since 2.3 now lets you quicksave and quit during cutscenes.
Just play a cutscene that has Pause music, then quicksave, quit, and
reload. Also some other accidental ways that I've forgotten about.
To fix this, I've done what mapclass has and made an areamap. Except for
music. This map is the map of the track number of every single room,
except for three special cases: -1 for do nothing and don't change music
(usually because multiple different tracks can be played in this room),
-2 for Tower music (needs to be track 2 or 9 depending on Flip Mode),
and -3 for the start of Space Station 2 (track 1 in time trials, track 4
otherwise).
I've thoroughly tested this areamap by playing through the game and
entering every single room. Additionally I've also thoroughly tested all
special cases (entering the Ship through the teleporter or main
entrance, using the Ship's jukebox, the Tower in Flip Mode and regular
mode, and the start of Space Station 2 in time trial and in regular
mode).
Closes #449.
2021-05-12 04:59:02 +02:00
|
|
|
#include "Graphics.h"
|
2020-04-04 00:37:59 +02:00
|
|
|
#include "Map.h"
|
Replace main game music with music area map
The main game used a set of copy-pasted code to set the music of each
area. There WAS some redundancy built-in, but only three rooms in each
direction from the entrance of a zone.
Given this, it's completely possible for players to mismatch the music
of the area and level. In fact, it's easy to do it even on accident,
especially since 2.3 now lets you quicksave and quit during cutscenes.
Just play a cutscene that has Pause music, then quicksave, quit, and
reload. Also some other accidental ways that I've forgotten about.
To fix this, I've done what mapclass has and made an areamap. Except for
music. This map is the map of the track number of every single room,
except for three special cases: -1 for do nothing and don't change music
(usually because multiple different tracks can be played in this room),
-2 for Tower music (needs to be track 2 or 9 depending on Flip Mode),
and -3 for the start of Space Station 2 (track 1 in time trials, track 4
otherwise).
I've thoroughly tested this areamap by playing through the game and
entering every single room. Additionally I've also thoroughly tested all
special cases (entering the Ship through the teleporter or main
entrance, using the Ship's jukebox, the Tower in Flip Mode and regular
mode, and the start of Space Station 2 in time trial and in regular
mode).
Closes #449.
2021-05-12 04:59:02 +02:00
|
|
|
#include "Script.h"
|
2022-03-09 22:35:29 +01:00
|
|
|
#include "Unused.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "UtilityClass.h"
|
2021-02-24 00:21:29 +01:00
|
|
|
#include "Vlogging.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-12-26 14:57:38 +01:00
|
|
|
#include <vector>
|
|
|
|
|
2022-03-09 22:35:29 +01:00
|
|
|
/* stb_vorbis */
|
|
|
|
|
|
|
|
#define malloc SDL_malloc
|
|
|
|
#define realloc SDL_realloc
|
2022-12-01 07:30:16 +01:00
|
|
|
#define free VVV_free
|
2022-03-09 22:35:29 +01:00
|
|
|
#ifdef memset /* Thanks, Apple! */
|
|
|
|
#undef memset
|
|
|
|
#endif
|
|
|
|
#define memset SDL_memset
|
|
|
|
#ifdef memcpy /* Thanks, Apple! */
|
|
|
|
#undef memcpy
|
|
|
|
#endif
|
|
|
|
#define memcpy SDL_memcpy
|
|
|
|
#define memcmp SDL_memcmp
|
|
|
|
|
|
|
|
#define pow SDL_pow
|
|
|
|
#define log(x) SDL_log(x)
|
|
|
|
#define sin(x) SDL_sin(x)
|
|
|
|
#define cos(x) SDL_cos(x)
|
|
|
|
#define floor SDL_floor
|
|
|
|
#define abs(x) SDL_abs(x)
|
|
|
|
#define ldexp(v, e) SDL_scalbn((v), (e))
|
|
|
|
#define exp(x) SDL_exp(x)
|
|
|
|
|
|
|
|
#define qsort SDL_qsort
|
|
|
|
|
|
|
|
#define assert SDL_assert
|
|
|
|
|
|
|
|
#define FILE SDL_RWops
|
|
|
|
#ifdef SEEK_SET
|
|
|
|
#undef SEEK_SET
|
|
|
|
#endif
|
|
|
|
#ifdef SEEK_CUR
|
|
|
|
#undef SEEK_CUR
|
|
|
|
#endif
|
|
|
|
#ifdef SEEK_END
|
|
|
|
#undef SEEK_END
|
|
|
|
#endif
|
|
|
|
#ifdef EOF
|
|
|
|
#undef EOF
|
|
|
|
#endif
|
|
|
|
#define SEEK_SET 0
|
|
|
|
#define SEEK_CUR 1
|
|
|
|
#define SEEK_END 2
|
|
|
|
#define EOF -1
|
|
|
|
#define fopen(path, mode) SDL_RWFromFile(path, mode)
|
|
|
|
#define fopen_s(io, path, mode) (!(*io = fopen(path, mode)))
|
|
|
|
#define fclose(io) SDL_RWclose(io)
|
|
|
|
#define fread(dst, size, count, io) SDL_RWread(io, dst, size, count)
|
|
|
|
#define fseek(io, offset, whence) SDL_RWseek(io, offset, whence)
|
|
|
|
#define ftell(io) SDL_RWtell(io)
|
|
|
|
|
|
|
|
#define FAudio_alloca(x) SDL_stack_alloc(uint8_t, x)
|
|
|
|
#define FAudio_dealloca(x) SDL_stack_free(x)
|
|
|
|
|
|
|
|
#define STB_VORBIS_NO_PUSHDATA_API 1
|
|
|
|
#define STB_VORBIS_NO_INTEGER_CONVERSION 1
|
|
|
|
#include <stb_vorbis.h>
|
|
|
|
|
|
|
|
/* End stb_vorbis include */
|
|
|
|
|
|
|
|
#define VVV_MAX_VOLUME 128
|
|
|
|
#define VVV_MAX_CHANNELS 8
|
|
|
|
|
|
|
|
class SoundTrack;
|
|
|
|
class MusicTrack;
|
|
|
|
static std::vector<SoundTrack> soundTracks;
|
|
|
|
static std::vector<MusicTrack> musicTracks;
|
|
|
|
|
|
|
|
static FAudio* faudioctx = NULL;
|
|
|
|
static FAudioMasteringVoice* masteringvoice = NULL;
|
2022-01-14 22:46:04 +01:00
|
|
|
|
2021-12-26 14:57:38 +01:00
|
|
|
class SoundTrack
|
|
|
|
{
|
|
|
|
public:
|
2022-01-14 22:46:04 +01:00
|
|
|
SoundTrack(const char* fileName)
|
|
|
|
{
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
unsigned char* mem;
|
2022-01-14 22:46:04 +01:00
|
|
|
size_t length;
|
2023-03-29 05:48:43 +02:00
|
|
|
voice_index = -1;
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
|
2023-03-18 23:12:24 +01:00
|
|
|
FILESYSTEM_loadAssetToMemory(fileName, &mem, &length);
|
2022-01-14 22:46:04 +01:00
|
|
|
if (mem == NULL)
|
|
|
|
{
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
vlog_error("Unable to load sound file %s", fileName);
|
|
|
|
SDL_assert(0 && "Sound file missing!");
|
2022-01-14 22:46:04 +01:00
|
|
|
return;
|
|
|
|
}
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
|
|
|
|
SDL_zerop(this);
|
2023-03-19 22:00:34 +01:00
|
|
|
if (length >= 4 && SDL_memcmp(mem, "OggS", 4) == 0)
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
{
|
|
|
|
LoadOGG(fileName, mem, length);
|
2023-03-29 05:48:43 +02:00
|
|
|
callbacks.OnBufferStart = &SoundTrack::refillReserve;
|
|
|
|
callbacks.OnBufferEnd = &SoundTrack::swapBuffers;
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LoadWAV(fileName, mem, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadWAV(const char* fileName, unsigned char* mem, const size_t length)
|
|
|
|
{
|
|
|
|
SDL_AudioSpec spec;
|
|
|
|
SDL_RWops *fileIn;
|
2022-03-09 22:35:29 +01:00
|
|
|
fileIn = SDL_RWFromConstMem(mem, length);
|
|
|
|
if (SDL_LoadWAV_RW(fileIn, 1, &spec, &wav_buffer, &wav_length) == NULL)
|
2022-01-14 22:46:04 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
vlog_error("Unable to load WAV file %s", fileName);
|
|
|
|
goto end;
|
2022-01-14 22:46:04 +01:00
|
|
|
}
|
2022-03-09 22:35:29 +01:00
|
|
|
format.nChannels = spec.channels;
|
|
|
|
format.nSamplesPerSec = spec.freq;
|
|
|
|
format.wFormatTag = FAUDIO_FORMAT_PCM;
|
2023-03-18 23:59:17 +01:00
|
|
|
format.wBitsPerSample = SDL_AUDIO_BITSIZE(spec.format);
|
2022-03-09 22:35:29 +01:00
|
|
|
format.nBlockAlign = format.nChannels * format.wBitsPerSample;
|
|
|
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
|
|
|
format.cbSize = 0;
|
|
|
|
valid = true;
|
|
|
|
end:
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(mem);
|
2022-01-14 22:46:04 +01:00
|
|
|
}
|
|
|
|
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
void LoadOGG(const char* fileName, unsigned char* mem, const size_t length)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
stb_vorbis_info vorbis_info;
|
|
|
|
vorbis = stb_vorbis_open_memory(mem, length, &err, NULL);
|
|
|
|
if (vorbis == NULL)
|
|
|
|
{
|
|
|
|
vlog_error("Unable to create Vorbis handle for %s, error %d", fileName, err);
|
|
|
|
VVV_free(mem);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
vorbis_info = stb_vorbis_get_info(vorbis);
|
|
|
|
format.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT;
|
|
|
|
format.wBitsPerSample = sizeof(float) * 8;
|
|
|
|
format.nChannels = vorbis_info.channels;
|
|
|
|
format.nSamplesPerSec = vorbis_info.sample_rate;
|
|
|
|
format.nBlockAlign = format.nChannels * format.wBitsPerSample;
|
|
|
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
|
|
|
format.cbSize = 0;
|
|
|
|
|
|
|
|
channels = format.nChannels;
|
2023-03-29 05:48:43 +02:00
|
|
|
size = format.nAvgBytesPerSec / 20;
|
|
|
|
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
decoded_buf_playing = (Uint8*) SDL_malloc(size);
|
2023-03-29 05:48:43 +02:00
|
|
|
decoded_buf_reserve = (Uint8*) SDL_malloc(size);
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
|
|
|
|
ogg_file = mem;
|
|
|
|
valid = true;
|
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
void Dispose(void)
|
2021-12-26 14:38:16 +01:00
|
|
|
{
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(wav_buffer);
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
|
|
|
|
VVV_free(decoded_buf_playing);
|
2023-03-29 05:48:43 +02:00
|
|
|
VVV_free(decoded_buf_reserve);
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
VVV_freefunc(stb_vorbis_close, vorbis);
|
|
|
|
VVV_free(ogg_file);
|
2021-12-26 14:38:16 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
void Play(void)
|
2022-01-14 22:46:04 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (!valid)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < VVV_MAX_CHANNELS; i++)
|
2022-01-14 22:46:04 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
FAudioVoiceState voicestate;
|
|
|
|
FAudioSourceVoice_GetState(voices[i], &voicestate, 0);
|
|
|
|
if (voicestate.BuffersQueued == 0)
|
|
|
|
{
|
2023-03-27 06:14:02 +02:00
|
|
|
if (SDL_memcmp(&voice_formats[i], &format, sizeof(format)) != 0)
|
2022-03-09 22:35:29 +01:00
|
|
|
{
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_freefunc(FAudioVoice_DestroyVoice, voices[i]);
|
2023-03-29 05:48:43 +02:00
|
|
|
if (vorbis != NULL)
|
|
|
|
{
|
|
|
|
FAudio_CreateSourceVoice(faudioctx, &voices[i], &format, 0, 2.0f, &callbacks, NULL, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FAudio_CreateSourceVoice(faudioctx, &voices[i], &format, 0, 2.0f, NULL, NULL, NULL);
|
|
|
|
}
|
2023-03-27 06:14:02 +02:00
|
|
|
voice_formats[i] = format;
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
FAudioBuffer faudio_buffer = {
|
2022-03-09 22:35:29 +01:00
|
|
|
FAUDIO_END_OF_STREAM, /* Flags */
|
|
|
|
wav_length * 8, /* AudioBytes */
|
|
|
|
wav_buffer, /* AudioData */
|
|
|
|
0, /* playbegin */
|
|
|
|
0, /* playlength */
|
|
|
|
0, /* LoopBegin */
|
|
|
|
0, /* LoopLength */
|
|
|
|
0, /* LoopCount */
|
|
|
|
NULL
|
|
|
|
};
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
if (vorbis != NULL)
|
|
|
|
{
|
|
|
|
stb_vorbis_seek_start(vorbis);
|
|
|
|
faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(
|
|
|
|
vorbis,
|
|
|
|
channels,
|
|
|
|
(float*) decoded_buf_playing,
|
|
|
|
size / sizeof(float)
|
|
|
|
);
|
|
|
|
faudio_buffer.AudioBytes = size;
|
|
|
|
faudio_buffer.pAudioData = decoded_buf_playing;
|
2023-03-29 05:48:43 +02:00
|
|
|
faudio_buffer.pContext = this;
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
}
|
2022-03-09 22:35:29 +01:00
|
|
|
if (FAudioSourceVoice_SubmitSourceBuffer(voices[i], &faudio_buffer, NULL))
|
|
|
|
{
|
|
|
|
vlog_error("Unable to queue sound buffer");
|
2023-03-29 05:48:43 +02:00
|
|
|
voice_index = -1;
|
2022-03-09 22:35:29 +01:00
|
|
|
return;
|
|
|
|
}
|
2022-06-09 21:46:40 +02:00
|
|
|
FAudioVoice_SetVolume(voices[i], volume, FAUDIO_COMMIT_NOW);
|
2022-03-09 22:35:29 +01:00
|
|
|
if (FAudioSourceVoice_Start(voices[i], 0, FAUDIO_COMMIT_NOW))
|
|
|
|
{
|
|
|
|
vlog_error("Unable to start voice processing");
|
2023-03-29 05:48:43 +02:00
|
|
|
voice_index = -1;
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2023-03-29 05:48:43 +02:00
|
|
|
voice_index = i;
|
2022-03-09 22:35:29 +01:00
|
|
|
return;
|
|
|
|
}
|
2022-01-14 22:46:04 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-26 14:38:16 +01:00
|
|
|
|
2022-03-31 20:13:57 +02:00
|
|
|
static void Init(int audio_rate)
|
2022-01-14 22:52:52 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (voices == NULL)
|
2022-01-14 22:52:52 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
voices = (FAudioSourceVoice**) SDL_malloc(sizeof(FAudioSourceVoice*) * VVV_MAX_CHANNELS);
|
|
|
|
for (int i = 0; i < VVV_MAX_CHANNELS; i++)
|
|
|
|
{
|
|
|
|
FAudioWaveFormatEx format;
|
|
|
|
format.nChannels = 1; /* Assume 1 for SoundTracks. Will be recreated if mismatched during play */
|
|
|
|
format.nSamplesPerSec = audio_rate;
|
|
|
|
format.wFormatTag = FAUDIO_FORMAT_PCM;
|
|
|
|
format.wBitsPerSample = 16;
|
|
|
|
format.nBlockAlign = format.nChannels * format.wBitsPerSample;
|
|
|
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
|
|
|
format.cbSize = 0;
|
2023-03-27 06:14:02 +02:00
|
|
|
voice_formats[i] = format;
|
2022-03-09 22:35:29 +01:00
|
|
|
if (FAudio_CreateSourceVoice(faudioctx, &voices[i], &format, 0, 2.0f, NULL, NULL, NULL))
|
|
|
|
{
|
|
|
|
vlog_error("Unable to create source voice no. %i", i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2022-01-14 22:52:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static void Pause(void)
|
2022-01-14 22:46:04 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
for (size_t i = 0; i < VVV_MAX_CHANNELS; i++)
|
|
|
|
{
|
|
|
|
FAudioSourceVoice_Stop(voices[i], 0, FAUDIO_COMMIT_NOW);
|
|
|
|
}
|
2022-01-14 22:46:04 +01:00
|
|
|
}
|
2021-12-26 14:38:16 +01:00
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static void Resume(void)
|
2021-12-26 14:38:16 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
for (size_t i = 0; i < VVV_MAX_CHANNELS; i++)
|
|
|
|
{
|
|
|
|
FAudioSourceVoice_Start(voices[i], 0, FAUDIO_COMMIT_NOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static void Destroy(void)
|
2022-03-09 22:35:29 +01:00
|
|
|
{
|
|
|
|
if (voices != NULL)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < VVV_MAX_CHANNELS; i++)
|
|
|
|
{
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_freefunc(FAudioVoice_DestroyVoice, voices[i]);
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(voices);
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2021-12-26 14:38:16 +01:00
|
|
|
}
|
|
|
|
|
2022-01-14 22:46:04 +01:00
|
|
|
static void SetVolume(int soundVolume)
|
2021-12-26 14:38:16 +01:00
|
|
|
{
|
2022-06-09 21:46:40 +02:00
|
|
|
volume = (float) soundVolume / VVV_MAX_VOLUME;
|
2022-03-09 22:35:29 +01:00
|
|
|
for (size_t i = 0; i < VVV_MAX_CHANNELS; i++)
|
|
|
|
{
|
2022-06-09 21:46:40 +02:00
|
|
|
FAudioVoice_SetVolume(voices[i], volume, FAUDIO_COMMIT_NOW);
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2021-12-26 14:38:16 +01:00
|
|
|
}
|
2022-01-14 22:46:04 +01:00
|
|
|
|
2023-03-29 05:48:43 +02:00
|
|
|
static void refillReserve(FAudioVoiceCallback* callback, void* ctx)
|
|
|
|
{
|
|
|
|
bool inbounds;
|
|
|
|
SoundTrack* t = (SoundTrack*) ctx;
|
|
|
|
FAudioBuffer faudio_buffer;
|
|
|
|
SDL_zero(faudio_buffer);
|
|
|
|
UNUSED(callback);
|
|
|
|
faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*) t->decoded_buf_reserve, t->size / sizeof(float));
|
|
|
|
faudio_buffer.AudioBytes = t->size;
|
|
|
|
faudio_buffer.pAudioData = t->decoded_buf_reserve;
|
|
|
|
faudio_buffer.pContext = t;
|
|
|
|
if (faudio_buffer.PlayLength == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
inbounds = t->voice_index >= 0 && t->voice_index < VVV_MAX_CHANNELS;
|
|
|
|
if (!inbounds)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FAudioSourceVoice_SubmitSourceBuffer(voices[t->voice_index], &faudio_buffer, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void swapBuffers(FAudioVoiceCallback* callback, void* ctx)
|
|
|
|
{
|
|
|
|
SoundTrack* t = (SoundTrack*) ctx;
|
|
|
|
Uint8* tmp = t->decoded_buf_playing;
|
|
|
|
UNUSED(callback);
|
|
|
|
t->decoded_buf_playing = t->decoded_buf_reserve;
|
|
|
|
t->decoded_buf_reserve = tmp;
|
|
|
|
}
|
|
|
|
|
2022-07-29 01:48:58 +02:00
|
|
|
Uint8 *wav_buffer;
|
2022-03-09 22:35:29 +01:00
|
|
|
Uint32 wav_length;
|
|
|
|
FAudioWaveFormatEx format;
|
2023-03-29 05:48:43 +02:00
|
|
|
int voice_index;
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
|
|
|
|
unsigned char* ogg_file;
|
|
|
|
stb_vorbis* vorbis;
|
|
|
|
int channels;
|
|
|
|
Uint32 size;
|
|
|
|
Uint8* decoded_buf_playing;
|
2023-03-29 05:48:43 +02:00
|
|
|
Uint8* decoded_buf_reserve;
|
|
|
|
FAudioVoiceCallback callbacks;
|
Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.
This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.
This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.
Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.
Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.
Fixes #900.
2023-03-19 02:24:30 +01:00
|
|
|
|
2022-03-09 22:35:29 +01:00
|
|
|
bool valid;
|
|
|
|
|
|
|
|
static FAudioSourceVoice** voices;
|
2023-03-27 06:14:02 +02:00
|
|
|
static FAudioWaveFormatEx voice_formats[VVV_MAX_CHANNELS];
|
2022-06-09 21:46:40 +02:00
|
|
|
static float volume;
|
2022-01-14 22:46:04 +01:00
|
|
|
};
|
2022-03-09 22:35:29 +01:00
|
|
|
FAudioSourceVoice** SoundTrack::voices = NULL;
|
2023-03-27 06:14:02 +02:00
|
|
|
FAudioWaveFormatEx SoundTrack::voice_formats[VVV_MAX_CHANNELS];
|
2022-06-10 20:17:38 +02:00
|
|
|
float SoundTrack::volume = 0.0f;
|
2021-12-26 14:38:16 +01:00
|
|
|
|
2022-01-14 23:24:22 +01:00
|
|
|
class MusicTrack
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MusicTrack(SDL_RWops *rw)
|
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
SDL_zerop(this);
|
|
|
|
read_buf = (Uint8*) SDL_malloc(rw->size(rw));
|
|
|
|
SDL_RWread(rw, read_buf, rw->size(rw), 1);
|
|
|
|
int err;
|
|
|
|
stb_vorbis_info vorbis_info;
|
|
|
|
stb_vorbis_comment vorbis_comment;
|
|
|
|
vorbis = stb_vorbis_open_memory(read_buf, rw->size(rw), &err, NULL);
|
|
|
|
if (vorbis == NULL)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
vlog_error("Unable to create Vorbis handle, error %d", err);
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(read_buf);
|
2022-03-09 22:35:29 +01:00
|
|
|
goto end;
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
2022-03-09 22:35:29 +01:00
|
|
|
vorbis_info = stb_vorbis_get_info(vorbis);
|
|
|
|
format.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT;
|
|
|
|
format.wBitsPerSample = sizeof(float) * 8;
|
|
|
|
format.nChannels = vorbis_info.channels;
|
|
|
|
format.nSamplesPerSec = vorbis_info.sample_rate;
|
|
|
|
format.nBlockAlign = format.nChannels * format.wBitsPerSample;
|
|
|
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
|
|
|
format.cbSize = 0;
|
|
|
|
|
|
|
|
channels = format.nChannels;
|
|
|
|
size = format.nAvgBytesPerSec / 20;
|
|
|
|
|
|
|
|
decoded_buf_playing = (Uint8*) SDL_malloc(size);
|
|
|
|
decoded_buf_reserve = (Uint8*) SDL_malloc(size);
|
|
|
|
|
|
|
|
loopbegin = 0;
|
|
|
|
looplength = 0;
|
|
|
|
vorbis_comment = stb_vorbis_get_comment(vorbis);
|
|
|
|
parseComments(this, vorbis_comment.comment_list, vorbis_comment.comment_list_length);
|
|
|
|
valid = true;
|
|
|
|
|
|
|
|
end:
|
|
|
|
SDL_RWclose(rw);
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
void Dispose(void)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
2023-03-19 01:51:55 +01:00
|
|
|
VVV_freefunc(stb_vorbis_close, vorbis);
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(read_buf);
|
|
|
|
VVV_free(decoded_buf_playing);
|
|
|
|
VVV_free(decoded_buf_reserve);
|
2022-03-09 22:35:29 +01:00
|
|
|
if (!IsHalted())
|
|
|
|
{
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_freefunc(FAudioVoice_DestroyVoice, musicVoice);
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Play(bool loop)
|
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (!valid)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2022-03-09 22:35:29 +01:00
|
|
|
|
|
|
|
shouldloop = loop;
|
|
|
|
sample_pos = 0;
|
|
|
|
stb_vorbis_seek_start(vorbis);
|
|
|
|
|
2023-11-28 21:29:44 +01:00
|
|
|
Halt();
|
2023-03-18 23:33:30 +01:00
|
|
|
|
2023-11-28 21:29:44 +01:00
|
|
|
SDL_zero(callbacks);
|
|
|
|
callbacks.OnBufferStart = &MusicTrack::refillReserve;
|
|
|
|
callbacks.OnBufferEnd = &MusicTrack::swapBuffers;
|
|
|
|
FAudio_CreateSourceVoice(faudioctx, &musicVoice, &format, 0, 2.0f, &callbacks, NULL, NULL);
|
2022-03-09 22:35:29 +01:00
|
|
|
|
|
|
|
FAudioBuffer faudio_buffer;
|
|
|
|
SDL_zero(faudio_buffer);
|
|
|
|
if (looplength == 0)
|
|
|
|
{
|
|
|
|
faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(vorbis, channels, (float*) decoded_buf_playing, size / sizeof(float));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int samples_read = stb_vorbis_get_samples_float_interleaved(vorbis, channels, (float*) decoded_buf_playing, size / sizeof(float));
|
|
|
|
faudio_buffer.PlayLength = SDL_min(samples_read, (loopbegin + looplength) - sample_pos);
|
|
|
|
}
|
|
|
|
faudio_buffer.AudioBytes = size;
|
|
|
|
faudio_buffer.pAudioData = decoded_buf_playing;
|
|
|
|
faudio_buffer.pContext = this;
|
|
|
|
sample_pos += faudio_buffer.PlayLength;
|
|
|
|
if (FAudioSourceVoice_SubmitSourceBuffer(musicVoice, &faudio_buffer, NULL))
|
|
|
|
{
|
|
|
|
vlog_error("Unable to queue sound buffer");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Resume();
|
2022-01-14 23:24:22 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static void Halt(void)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (!IsHalted())
|
|
|
|
{
|
|
|
|
FAudioSourceVoice_FlushSourceBuffers(musicVoice);
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_freefunc(FAudioVoice_DestroyVoice, musicVoice);
|
2022-04-10 21:50:15 +02:00
|
|
|
paused = true;
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static bool IsHalted(void)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
return musicVoice == NULL;
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static void Pause(void)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (!IsHalted())
|
|
|
|
{
|
|
|
|
FAudioSourceVoice_Stop(musicVoice, 0, FAUDIO_COMMIT_NOW);
|
2022-04-10 21:50:15 +02:00
|
|
|
paused = true;
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static bool IsPaused(void)
|
2022-04-10 21:50:15 +02:00
|
|
|
{
|
|
|
|
return paused || IsHalted();
|
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
static void Resume(void)
|
2022-01-14 23:24:22 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (!IsHalted())
|
|
|
|
{
|
|
|
|
FAudioSourceVoice_Start(musicVoice, 0, FAUDIO_COMMIT_NOW);
|
2022-04-10 21:50:15 +02:00
|
|
|
paused = false;
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void SetVolume(int musicVolume)
|
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
float adj_vol = (float) musicVolume / VVV_MAX_VOLUME;
|
|
|
|
if (!IsHalted())
|
|
|
|
{
|
|
|
|
FAudioVoice_SetVolume(musicVoice, adj_vol, FAUDIO_COMMIT_NOW);
|
|
|
|
}
|
2022-01-14 23:24:22 +01:00
|
|
|
}
|
|
|
|
|
2022-03-09 22:35:29 +01:00
|
|
|
stb_vorbis* vorbis;
|
|
|
|
int channels;
|
|
|
|
Uint32 size;
|
|
|
|
int loopbegin;
|
|
|
|
int looplength;
|
|
|
|
int sample_pos; //stb_vorbis offset not yet functional on pulldata API. TODO Replace when fixed
|
2022-01-14 23:24:22 +01:00
|
|
|
|
2022-03-09 22:35:29 +01:00
|
|
|
FAudioVoiceCallback callbacks;
|
|
|
|
FAudioWaveFormatEx format;
|
|
|
|
|
2022-07-29 01:48:58 +02:00
|
|
|
Uint8* decoded_buf_playing;
|
|
|
|
Uint8* decoded_buf_reserve;
|
|
|
|
Uint8* read_buf;
|
2022-03-09 22:35:29 +01:00
|
|
|
bool shouldloop;
|
|
|
|
bool valid;
|
2021-12-26 14:57:38 +01:00
|
|
|
|
2022-04-10 21:50:15 +02:00
|
|
|
static bool paused;
|
2022-03-09 22:35:29 +01:00
|
|
|
static FAudioSourceVoice* musicVoice;
|
|
|
|
|
|
|
|
static void refillReserve(FAudioVoiceCallback* callback, void* ctx)
|
|
|
|
{
|
|
|
|
MusicTrack* t = (MusicTrack*) ctx;
|
|
|
|
FAudioBuffer faudio_buffer;
|
|
|
|
SDL_zero(faudio_buffer);
|
|
|
|
UNUSED(callback);
|
|
|
|
if (t->looplength == 0)
|
|
|
|
{
|
|
|
|
faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*) t->decoded_buf_reserve, t->size / sizeof(float));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int samples_read = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*) t->decoded_buf_reserve, t->size / sizeof(float));
|
|
|
|
faudio_buffer.PlayLength = SDL_min(samples_read, (t->loopbegin + t->looplength) - t->sample_pos);
|
|
|
|
}
|
|
|
|
faudio_buffer.AudioBytes = t->size;
|
|
|
|
faudio_buffer.pAudioData = t->decoded_buf_reserve;
|
|
|
|
faudio_buffer.pContext = t;
|
|
|
|
if (faudio_buffer.PlayLength == 0)
|
|
|
|
{
|
|
|
|
if (t->shouldloop)
|
|
|
|
{
|
|
|
|
stb_vorbis_seek(t->vorbis, t->loopbegin);
|
|
|
|
t->sample_pos = t->loopbegin;
|
|
|
|
if (t->looplength != 0)
|
|
|
|
{
|
|
|
|
int samples_read = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*) t->decoded_buf_reserve, t->size / sizeof(float));
|
|
|
|
faudio_buffer.PlayLength = SDL_min(samples_read, (t->loopbegin + t->looplength) - t->sample_pos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
faudio_buffer.PlayLength = stb_vorbis_get_samples_float_interleaved(t->vorbis, t->channels, (float*) t->decoded_buf_reserve, t->size / sizeof(float));
|
|
|
|
}
|
|
|
|
if (faudio_buffer.PlayLength == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t->sample_pos += faudio_buffer.PlayLength;
|
|
|
|
FAudioSourceVoice_SubmitSourceBuffer(musicVoice, &faudio_buffer, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void swapBuffers(FAudioVoiceCallback* callback, void* ctx)
|
|
|
|
{
|
|
|
|
MusicTrack* t = (MusicTrack*) ctx;
|
|
|
|
Uint8* tmp = t->decoded_buf_playing;
|
|
|
|
UNUSED(callback);
|
|
|
|
t->decoded_buf_playing = t->decoded_buf_reserve;
|
|
|
|
t->decoded_buf_reserve = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lifted from SDL_mixer, we used it in 2.3 and previous */
|
2022-11-14 04:27:58 +01:00
|
|
|
static void parseComments(
|
|
|
|
MusicTrack* t, char** comments, const int comment_list_length
|
|
|
|
) {
|
2022-03-09 22:35:29 +01:00
|
|
|
int loopend = 0;
|
|
|
|
for (int i = 0; i < comment_list_length; i++)
|
|
|
|
{
|
2022-11-14 04:27:58 +01:00
|
|
|
char* param = SDL_strdup(comments[i]);
|
2022-11-14 04:41:32 +01:00
|
|
|
if (param == NULL)
|
|
|
|
{
|
|
|
|
vlog_error(
|
|
|
|
"Could not allocate memory to parse '%s'. Ignoring comments.",
|
|
|
|
comments[i]
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
2022-11-14 04:27:58 +01:00
|
|
|
char* argument = param;
|
|
|
|
char* value = SDL_strchr(param, '=');
|
2022-03-09 22:35:29 +01:00
|
|
|
if (value == NULL)
|
|
|
|
{
|
|
|
|
value = param + SDL_strlen(param);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*(value++) = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from
|
|
|
|
* string if it is present at position 4. */
|
|
|
|
char buf[5];
|
|
|
|
SDL_strlcpy(buf, argument, sizeof(buf));
|
2022-11-14 04:27:58 +01:00
|
|
|
if (SDL_strcasecmp(buf, "LOOP") == 0
|
|
|
|
&& ((argument[4] == '_') || (argument[4] == '-')))
|
2022-03-09 22:35:29 +01:00
|
|
|
{
|
|
|
|
SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
|
|
|
|
{
|
|
|
|
t->loopbegin = _Mix_ParseTime(value, t->format.nSamplesPerSec);
|
|
|
|
}
|
|
|
|
else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0)
|
|
|
|
{
|
|
|
|
t->looplength = SDL_strtoll(value, NULL, 10);
|
|
|
|
}
|
|
|
|
else if (SDL_strcasecmp(argument, "LOOPEND") == 0)
|
|
|
|
{
|
|
|
|
loopend = _Mix_ParseTime(value, t->format.nSamplesPerSec);
|
|
|
|
}
|
|
|
|
|
2022-11-14 04:47:37 +01:00
|
|
|
if (t->loopbegin < 0 || t->looplength < 0 || loopend < 0)
|
|
|
|
{
|
|
|
|
vlog_warn(
|
|
|
|
"A track loop comment had a negative value. "
|
|
|
|
"Ignoring all comments for the track."
|
|
|
|
);
|
|
|
|
t->loopbegin = 0;
|
|
|
|
t->looplength = 0;
|
|
|
|
loopend = 0;
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(param);
|
2022-11-14 04:47:37 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(param);
|
2022-03-09 22:35:29 +01:00
|
|
|
}
|
|
|
|
if (loopend != 0)
|
|
|
|
{
|
|
|
|
t->looplength = loopend - t->loopbegin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-14 04:03:26 +01:00
|
|
|
static int _Mix_ParseTime(char* time, const long samplerate_hz)
|
2022-03-09 22:35:29 +01:00
|
|
|
{
|
2022-11-14 04:03:26 +01:00
|
|
|
char* num_start = time;
|
|
|
|
char* p;
|
|
|
|
Sint64 result = 0;
|
2022-03-09 22:35:29 +01:00
|
|
|
int val;
|
|
|
|
|
|
|
|
/* Time is directly expressed as a sample position */
|
|
|
|
if (SDL_strchr(time, ':') == NULL)
|
|
|
|
{
|
|
|
|
return SDL_strtoll(time, NULL, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (p = time; *p != '\0'; ++p)
|
|
|
|
{
|
|
|
|
if (*p == '.' || *p == ':')
|
|
|
|
{
|
2022-11-14 04:03:26 +01:00
|
|
|
const char c = *p;
|
|
|
|
*p = '\0';
|
|
|
|
val = SDL_atoi(num_start);
|
|
|
|
if (val < 0)
|
2022-03-09 22:35:29 +01:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
result = result * 60 + val;
|
|
|
|
num_start = p + 1;
|
|
|
|
*p = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*p == '.')
|
|
|
|
{
|
2022-11-14 04:03:26 +01:00
|
|
|
const double val_f = SDL_atof(p);
|
2022-03-09 22:35:29 +01:00
|
|
|
if (val_f < 0)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return result * samplerate_hz + (Sint64) (val_f * samplerate_hz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-14 04:03:26 +01:00
|
|
|
val = SDL_atoi(num_start);
|
|
|
|
if (val < 0)
|
2022-03-09 22:35:29 +01:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return (result * 60 + val) * samplerate_hz;
|
|
|
|
}
|
|
|
|
};
|
2022-04-10 21:50:15 +02:00
|
|
|
bool MusicTrack::paused = false;
|
2022-03-09 22:35:29 +01:00
|
|
|
FAudioSourceVoice* MusicTrack::musicVoice = NULL;
|
2021-12-26 14:41:01 +01:00
|
|
|
|
|
|
|
musicclass::musicclass(void)
|
2021-12-26 14:38:16 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
safeToProcessMusic= false;
|
|
|
|
m_doFadeInVol = false;
|
|
|
|
m_doFadeOutVol = false;
|
|
|
|
musicVolume = 0;
|
Don't reset entire musicclass when mounting and unmounting assets
musicclass::init() re-initializes every attribute of musicclass
unnecessarily, when initialization should be put in a constructor
instead. This is bad, because music.init() gets called whenever we enter
and exit a custom level that has assets.
Otherwise, this would result in a bug where music.usingmmmmmm would be
reset, causing you to revert to PPPPPP on the title screen whenever you
enter a level with MMMMMM selected and exit it.
This also causes a confusing desync between game.usingmmmmmm and
music.usingmmmmmm since the values of the two variables are now
different (these duplicate variables should probably be removed, too,
and a lot of other duplicate variables like these exist, too, which are
a real headache). Which means despite MMMMMM playing on the title
screen, exiting the game and re-launching it will play PPPPPP instead.
What's even more is that going to game options and switching to PPPPPP
will play PPPPPP, but afterwards launching and re-entering will play
MMMMMM. Again, having duplicate variables is very bad, and should
probably be fixed, but that's a separate patch.
2020-11-13 00:55:58 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
user_music_volume = USER_VOLUME_MAX;
|
|
|
|
user_sound_volume = USER_VOLUME_MAX;
|
2021-04-12 02:41:46 +02:00
|
|
|
|
2023-06-04 00:52:24 +02:00
|
|
|
currentsong = -1;
|
2023-06-09 00:38:26 +02:00
|
|
|
haltedsong = -1;
|
2021-09-07 03:56:39 +02:00
|
|
|
nicechange = -1;
|
|
|
|
nicefade = false;
|
|
|
|
quick_fade = true;
|
Don't reset entire musicclass when mounting and unmounting assets
musicclass::init() re-initializes every attribute of musicclass
unnecessarily, when initialization should be put in a constructor
instead. This is bad, because music.init() gets called whenever we enter
and exit a custom level that has assets.
Otherwise, this would result in a bug where music.usingmmmmmm would be
reset, causing you to revert to PPPPPP on the title screen whenever you
enter a level with MMMMMM selected and exit it.
This also causes a confusing desync between game.usingmmmmmm and
music.usingmmmmmm since the values of the two variables are now
different (these duplicate variables should probably be removed, too,
and a lot of other duplicate variables like these exist, too, which are
a real headache). Which means despite MMMMMM playing on the title
screen, exiting the game and re-launching it will play PPPPPP instead.
What's even more is that going to game options and switching to PPPPPP
will play PPPPPP, but afterwards launching and re-entering will play
MMMMMM. Again, having duplicate variables is very bad, and should
probably be fixed, but that's a separate patch.
2020-11-13 00:55:58 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
usingmmmmmm = false;
|
Don't reset entire musicclass when mounting and unmounting assets
musicclass::init() re-initializes every attribute of musicclass
unnecessarily, when initialization should be put in a constructor
instead. This is bad, because music.init() gets called whenever we enter
and exit a custom level that has assets.
Otherwise, this would result in a bug where music.usingmmmmmm would be
reset, causing you to revert to PPPPPP on the title screen whenever you
enter a level with MMMMMM selected and exit it.
This also causes a confusing desync between game.usingmmmmmm and
music.usingmmmmmm since the values of the two variables are now
different (these duplicate variables should probably be removed, too,
and a lot of other duplicate variables like these exist, too, which are
a real headache). Which means despite MMMMMM playing on the title
screen, exiting the game and re-launching it will play PPPPPP instead.
What's even more is that going to game options and switching to PPPPPP
will play PPPPPP, but afterwards launching and re-entering will play
MMMMMM. Again, having duplicate variables is very bad, and should
probably be fixed, but that's a separate patch.
2020-11-13 00:55:58 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::init(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2022-03-09 22:35:29 +01:00
|
|
|
if (FAudioCreate(&faudioctx, 0, FAUDIO_DEFAULT_PROCESSOR))
|
|
|
|
{
|
|
|
|
vlog_error("Unable to initialize FAudio");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (FAudio_CreateMasteringVoice(faudioctx, &masteringvoice, 2, 44100, 0, 0, NULL))
|
|
|
|
{
|
|
|
|
vlog_error("Unable to create mastering voice");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-31 20:13:57 +02:00
|
|
|
SoundTrack::Init(44100);
|
2022-03-09 22:35:29 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
soundTracks.push_back(SoundTrack( "sounds/jump.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/jump2.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/hurt.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/souleyeminijingle.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/coin.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/save.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crumble.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/vanish.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/blip.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/preteleport.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/teleport.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crew1.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crew2.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crew3.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crew4.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crew5.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crew6.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/terminal.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/gamesaved.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crashing.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/blip2.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/countdown.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/go.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/crash.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/combine.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/newrecord.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/trophy.wav" ));
|
|
|
|
soundTracks.push_back(SoundTrack( "sounds/rescue.wav" ));
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
#ifdef VVV_COMPILEMUSIC
|
2021-09-07 03:56:39 +02:00
|
|
|
binaryBlob musicWriteBlob;
|
2020-07-30 23:06:07 +02:00
|
|
|
#define FOREACH_TRACK(blob, track_name) blob.AddFileToBinaryBlob("data/" track_name);
|
2021-09-07 03:56:39 +02:00
|
|
|
TRACK_NAMES(musicWriteBlob)
|
2020-07-01 00:17:02 +02:00
|
|
|
#undef FOREACH_TRACK
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
musicWriteBlob.writeBinaryBlob("data/BinaryMusic.vvv");
|
|
|
|
musicWriteBlob.clear();
|
2020-01-01 21:29:24 +01:00
|
|
|
#endif
|
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
num_mmmmmm_tracks = 0;
|
|
|
|
num_pppppp_tracks = 0;
|
2020-07-01 02:08:37 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
if (!mmmmmm_blob.unPackBinary("mmmmmm.vvv"))
|
|
|
|
{
|
|
|
|
if (pppppp_blob.unPackBinary("vvvvvvmusic.vvv"))
|
|
|
|
{
|
|
|
|
vlog_info("Loading music from PPPPPP blob...");
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
mmmmmm = false;
|
|
|
|
usingmmmmmm=false;
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
int index;
|
|
|
|
SDL_RWops* rw;
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2022-03-24 17:35:01 +01:00
|
|
|
#define TRACK_LOAD_BLOB(blob, track_name) \
|
2021-09-07 03:56:39 +02:00
|
|
|
index = blob.getIndex("data/" track_name); \
|
2022-03-24 17:35:01 +01:00
|
|
|
if (index >= 0 && index < blob.max_headers) \
|
|
|
|
{ \
|
|
|
|
rw = SDL_RWFromConstMem(blob.getAddress(index), blob.getSize(index)); \
|
|
|
|
if (rw == NULL) \
|
|
|
|
{ \
|
|
|
|
vlog_error("Unable to read music file header: %s", SDL_GetError()); \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
musicTracks.push_back(MusicTrack(rw)); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FOREACH_TRACK(blob, track_name) TRACK_LOAD_BLOB(blob, track_name)
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
TRACK_NAMES(pppppp_blob)
|
2020-07-30 23:06:07 +02:00
|
|
|
|
|
|
|
#undef FOREACH_TRACK
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vlog_info("Loading music from loose files...");
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
SDL_RWops* rw;
|
2022-03-24 17:35:01 +01:00
|
|
|
|
2020-07-30 23:06:07 +02:00
|
|
|
#define FOREACH_TRACK(_, track_name) \
|
2021-09-07 03:56:39 +02:00
|
|
|
rw = PHYSFSRWOPS_openRead(track_name); \
|
2022-03-24 17:35:01 +01:00
|
|
|
if (rw == NULL) \
|
|
|
|
{ \
|
|
|
|
vlog_error("Unable to read loose music file: %s", SDL_GetError()); \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
musicTracks.push_back(MusicTrack(rw)); \
|
|
|
|
}
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
TRACK_NAMES(_)
|
2020-07-30 23:06:07 +02:00
|
|
|
|
|
|
|
#undef FOREACH_TRACK
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vlog_info("Loading PPPPPP and MMMMMM blobs...");
|
2020-07-30 23:06:07 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
mmmmmm = true;
|
|
|
|
int index;
|
2022-03-24 17:35:01 +01:00
|
|
|
SDL_RWops* rw;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2022-03-24 17:35:01 +01:00
|
|
|
#define FOREACH_TRACK(blob, track_name) TRACK_LOAD_BLOB(blob, track_name)
|
2021-09-07 03:56:39 +02:00
|
|
|
|
|
|
|
TRACK_NAMES(mmmmmm_blob)
|
|
|
|
|
|
|
|
num_mmmmmm_tracks += musicTracks.size();
|
|
|
|
|
|
|
|
size_t index_ = 0;
|
|
|
|
while (mmmmmm_blob.nextExtra(&index_))
|
|
|
|
{
|
|
|
|
rw = SDL_RWFromConstMem(mmmmmm_blob.getAddress(index_), mmmmmm_blob.getSize(index_));
|
|
|
|
musicTracks.push_back(MusicTrack( rw ));
|
|
|
|
|
|
|
|
num_mmmmmm_tracks++;
|
|
|
|
index_++;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ohCrap = pppppp_blob.unPackBinary("vvvvvvmusic.vvv");
|
|
|
|
SDL_assert(ohCrap && "Music not found!");
|
|
|
|
|
|
|
|
TRACK_NAMES(pppppp_blob)
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-07-01 00:17:02 +02:00
|
|
|
#undef FOREACH_TRACK
|
2022-03-24 17:35:01 +01:00
|
|
|
#undef TRACK_LOAD_BLOB
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
num_pppppp_tracks += musicTracks.size() - num_mmmmmm_tracks;
|
2020-07-01 02:08:37 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
SDL_RWops* rw;
|
|
|
|
size_t index_ = 0;
|
|
|
|
while (pppppp_blob.nextExtra(&index_))
|
|
|
|
{
|
|
|
|
rw = SDL_RWFromConstMem(pppppp_blob.getAddress(index_), pppppp_blob.getSize(index_));
|
|
|
|
musicTracks.push_back(MusicTrack( rw ));
|
2020-07-01 02:08:37 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
num_pppppp_tracks++;
|
|
|
|
index_++;
|
|
|
|
}
|
Fix resumemusic/musicfadein not working
It seems like they were unfinished. This commit makes them properly
work.
When a track is stopped with stopmusic() or musicfadeout(),
resumemusic() will resume from where the track stopped. musicfadein()
does the same but does it with a gradual fade instead of suddenly
playing it at full volume.
I changed several interfaces around for this. First, setting currentsong
to -1 when music is stopped is handled in the hook callback that gets
called by SDL_mixer whenever the music stops. Otherwise, it'd be
problematic if currentsong was set to -1 when the song starts fading out
instead of when the song actually ends.
Also, music.play() has a few optional arguments now, to reduce the
copying-and-pasting of music code.
Lastly, we have to roll our own tracker of music length by using
SDL_GetPerformanceCounter(), because there's no way to get the music
position if a song fades out. (We could implicitly keep the music
position if we abruptly stopped the song using Mix_PauseMusic(), and
resume it using Mix_ResumeMusic(), but ignoring the fact that those two
functions are also used on the unfocus-pause (which, as it turns out, is
basically a non-issue because the unfocus-pause can use some other
functions), there's no equivalent for fading out, i.e. there's no
"fade out and pause when it fully fades out" function in SDL_mixer.) And
then we have to account for the unfocus-pause in our manual tracker.
Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::destroy(void)
|
2021-02-16 01:24:21 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
for (size_t i = 0; i < soundTracks.size(); ++i)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
soundTracks[i].Dispose();
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
soundTracks.clear();
|
2022-03-09 22:35:29 +01:00
|
|
|
SoundTrack::Destroy();
|
2021-09-07 03:56:39 +02:00
|
|
|
|
|
|
|
for (size_t i = 0; i < musicTracks.size(); ++i)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
musicTracks[i].Dispose();
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
musicTracks.clear();
|
|
|
|
|
|
|
|
pppppp_blob.clear();
|
|
|
|
mmmmmm_blob.clear();
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_freefunc(FAudioVoice_DestroyVoice, masteringvoice);
|
|
|
|
VVV_freefunc(FAudio_Release, faudioctx);
|
2021-02-16 01:24:21 +01:00
|
|
|
}
|
|
|
|
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
void musicclass::play(int t)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (mmmmmm && usingmmmmmm)
|
|
|
|
{
|
|
|
|
// Don't conjoin this if-statement with the above one...
|
|
|
|
if (num_mmmmmm_tracks > 0)
|
|
|
|
{
|
|
|
|
t %= num_mmmmmm_tracks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (num_pppppp_tracks > 0)
|
|
|
|
{
|
|
|
|
t %= num_pppppp_tracks;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mmmmmm && !usingmmmmmm)
|
|
|
|
{
|
|
|
|
t += num_mmmmmm_tracks;
|
|
|
|
}
|
|
|
|
|
|
|
|
safeToProcessMusic = true;
|
|
|
|
|
|
|
|
if (currentsong == t && !m_doFadeOutVol)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentsong = t;
|
2023-06-09 00:38:26 +02:00
|
|
|
haltedsong = -1;
|
2021-09-07 03:56:39 +02:00
|
|
|
|
|
|
|
if (t == -1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!INBOUNDS_VEC(t, musicTracks))
|
|
|
|
{
|
|
|
|
vlog_error("play() out-of-bounds!");
|
|
|
|
currentsong = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-24 03:37:32 +02:00
|
|
|
if (currentsong == Music_PATHCOMPLETE ||
|
|
|
|
currentsong == Music_PLENARY ||
|
|
|
|
(!map.custommode && (currentsong == Music_PATHCOMPLETE + num_mmmmmm_tracks
|
|
|
|
|| currentsong == Music_PLENARY + num_mmmmmm_tracks)))
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
2023-05-24 03:37:32 +02:00
|
|
|
// No fade in or repeat
|
2022-01-14 22:46:04 +01:00
|
|
|
if (musicTracks[t].Play(false))
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
m_doFadeInVol = false;
|
|
|
|
m_doFadeOutVol = false;
|
2022-01-14 22:46:04 +01:00
|
|
|
musicVolume = VVV_MAX_VOLUME;
|
2024-07-06 01:19:25 +02:00
|
|
|
MusicTrack::SetVolume(VVV_MAX_VOLUME * user_music_volume / USER_VOLUME_MAX);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (m_doFadeOutVol)
|
|
|
|
{
|
|
|
|
// We're already fading out
|
|
|
|
nicechange = t;
|
|
|
|
nicefade = true;
|
|
|
|
currentsong = -1;
|
|
|
|
|
|
|
|
if (quick_fade)
|
|
|
|
{
|
|
|
|
fadeMusicVolumeOut(500); // fade out quicker
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
quick_fade = true;
|
|
|
|
}
|
|
|
|
}
|
2022-01-14 22:46:04 +01:00
|
|
|
else if (musicTracks[t].Play(true))
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
m_doFadeInVol = false;
|
|
|
|
m_doFadeOutVol = false;
|
|
|
|
fadeMusicVolumeIn(3000);
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 23:24:14 +01:00
|
|
|
void musicclass::resume(void)
|
Fix resumemusic/musicfadein not working
It seems like they were unfinished. This commit makes them properly
work.
When a track is stopped with stopmusic() or musicfadeout(),
resumemusic() will resume from where the track stopped. musicfadein()
does the same but does it with a gradual fade instead of suddenly
playing it at full volume.
I changed several interfaces around for this. First, setting currentsong
to -1 when music is stopped is handled in the hook callback that gets
called by SDL_mixer whenever the music stops. Otherwise, it'd be
problematic if currentsong was set to -1 when the song starts fading out
instead of when the song actually ends.
Also, music.play() has a few optional arguments now, to reduce the
copying-and-pasting of music code.
Lastly, we have to roll our own tracker of music length by using
SDL_GetPerformanceCounter(), because there's no way to get the music
position if a song fades out. (We could implicitly keep the music
position if we abruptly stopped the song using Mix_PauseMusic(), and
resume it using Mix_ResumeMusic(), but ignoring the fact that those two
functions are also used on the unfocus-pause (which, as it turns out, is
basically a non-issue because the unfocus-pause can use some other
functions), there's no equivalent for fading out, i.e. there's no
"fade out and pause when it fully fades out" function in SDL_mixer.) And
then we have to account for the unfocus-pause in our manual tracker.
Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
|
|
|
{
|
2023-06-09 00:38:26 +02:00
|
|
|
if (currentsong == -1)
|
|
|
|
{
|
|
|
|
currentsong = haltedsong;
|
|
|
|
haltedsong = -1;
|
|
|
|
}
|
2022-01-14 22:46:04 +01:00
|
|
|
MusicTrack::Resume();
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
}
|
Fix resumemusic/musicfadein not working
It seems like they were unfinished. This commit makes them properly
work.
When a track is stopped with stopmusic() or musicfadeout(),
resumemusic() will resume from where the track stopped. musicfadein()
does the same but does it with a gradual fade instead of suddenly
playing it at full volume.
I changed several interfaces around for this. First, setting currentsong
to -1 when music is stopped is handled in the hook callback that gets
called by SDL_mixer whenever the music stops. Otherwise, it'd be
problematic if currentsong was set to -1 when the song starts fading out
instead of when the song actually ends.
Also, music.play() has a few optional arguments now, to reduce the
copying-and-pasting of music code.
Lastly, we have to roll our own tracker of music length by using
SDL_GetPerformanceCounter(), because there's no way to get the music
position if a song fades out. (We could implicitly keep the music
position if we abruptly stopped the song using Mix_PauseMusic(), and
resume it using Mix_ResumeMusic(), but ignoring the fact that those two
functions are also used on the unfocus-pause (which, as it turns out, is
basically a non-issue because the unfocus-pause can use some other
functions), there's no equivalent for fading out, i.e. there's no
"fade out and pause when it fully fades out" function in SDL_mixer.) And
then we have to account for the unfocus-pause in our manual tracker.
Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
|
|
|
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
void musicclass::resumefade(const int fadein_ms)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
resume();
|
|
|
|
fadeMusicVolumeIn(fadein_ms);
|
Fix resumemusic/musicfadein not working
It seems like they were unfinished. This commit makes them properly
work.
When a track is stopped with stopmusic() or musicfadeout(),
resumemusic() will resume from where the track stopped. musicfadein()
does the same but does it with a gradual fade instead of suddenly
playing it at full volume.
I changed several interfaces around for this. First, setting currentsong
to -1 when music is stopped is handled in the hook callback that gets
called by SDL_mixer whenever the music stops. Otherwise, it'd be
problematic if currentsong was set to -1 when the song starts fading out
instead of when the song actually ends.
Also, music.play() has a few optional arguments now, to reduce the
copying-and-pasting of music code.
Lastly, we have to roll our own tracker of music length by using
SDL_GetPerformanceCounter(), because there's no way to get the music
position if a song fades out. (We could implicitly keep the music
position if we abruptly stopped the song using Mix_PauseMusic(), and
resume it using Mix_ResumeMusic(), but ignoring the fact that those two
functions are also used on the unfocus-pause (which, as it turns out, is
basically a non-issue because the unfocus-pause can use some other
functions), there's no equivalent for fading out, i.e. there's no
"fade out and pause when it fully fades out" function in SDL_mixer.) And
then we have to account for the unfocus-pause in our manual tracker.
Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::fadein(void)
|
Fix resumemusic/musicfadein not working
It seems like they were unfinished. This commit makes them properly
work.
When a track is stopped with stopmusic() or musicfadeout(),
resumemusic() will resume from where the track stopped. musicfadein()
does the same but does it with a gradual fade instead of suddenly
playing it at full volume.
I changed several interfaces around for this. First, setting currentsong
to -1 when music is stopped is handled in the hook callback that gets
called by SDL_mixer whenever the music stops. Otherwise, it'd be
problematic if currentsong was set to -1 when the song starts fading out
instead of when the song actually ends.
Also, music.play() has a few optional arguments now, to reduce the
copying-and-pasting of music code.
Lastly, we have to roll our own tracker of music length by using
SDL_GetPerformanceCounter(), because there's no way to get the music
position if a song fades out. (We could implicitly keep the music
position if we abruptly stopped the song using Mix_PauseMusic(), and
resume it using Mix_ResumeMusic(), but ignoring the fact that those two
functions are also used on the unfocus-pause (which, as it turns out, is
basically a non-issue because the unfocus-pause can use some other
functions), there's no equivalent for fading out, i.e. there's no
"fade out and pause when it fully fades out" function in SDL_mixer.) And
then we have to account for the unfocus-pause in our manual tracker.
Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
resumefade(3000); // 3000 ms fadein
|
Fix resumemusic/musicfadein not working
It seems like they were unfinished. This commit makes them properly
work.
When a track is stopped with stopmusic() or musicfadeout(),
resumemusic() will resume from where the track stopped. musicfadein()
does the same but does it with a gradual fade instead of suddenly
playing it at full volume.
I changed several interfaces around for this. First, setting currentsong
to -1 when music is stopped is handled in the hook callback that gets
called by SDL_mixer whenever the music stops. Otherwise, it'd be
problematic if currentsong was set to -1 when the song starts fading out
instead of when the song actually ends.
Also, music.play() has a few optional arguments now, to reduce the
copying-and-pasting of music code.
Lastly, we have to roll our own tracker of music length by using
SDL_GetPerformanceCounter(), because there's no way to get the music
position if a song fades out. (We could implicitly keep the music
position if we abruptly stopped the song using Mix_PauseMusic(), and
resume it using Mix_ResumeMusic(), but ignoring the fact that those two
functions are also used on the unfocus-pause (which, as it turns out, is
basically a non-issue because the unfocus-pause can use some other
functions), there's no equivalent for fading out, i.e. there's no
"fade out and pause when it fully fades out" function in SDL_mixer.) And
then we have to account for the unfocus-pause in our manual tracker.
Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 21:07:19 +02:00
|
|
|
void musicclass::pause(void)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
MusicTrack::Pause();
|
2021-04-02 21:07:19 +02:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::haltdasmusik(void)
|
2023-04-06 04:32:42 +02:00
|
|
|
{
|
|
|
|
haltdasmusik(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void musicclass::haltdasmusik(const bool from_fade)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
/* Just pauses music. This is intended. */
|
|
|
|
pause();
|
2023-06-09 00:38:26 +02:00
|
|
|
haltedsong = currentsong;
|
2021-09-07 03:56:39 +02:00
|
|
|
currentsong = -1;
|
|
|
|
m_doFadeInVol = false;
|
|
|
|
m_doFadeOutVol = false;
|
2023-04-06 04:32:42 +02:00
|
|
|
if (!from_fade)
|
|
|
|
{
|
|
|
|
nicefade = false;
|
|
|
|
nicechange = -1;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::silencedasmusik(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
musicVolume = 0;
|
2021-09-11 02:02:24 +02:00
|
|
|
m_doFadeInVol = false;
|
|
|
|
m_doFadeOutVol = false;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
struct FadeState
|
2021-04-02 21:02:01 +02:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
int start_volume;
|
|
|
|
int end_volume;
|
|
|
|
int duration_ms;
|
|
|
|
int step_ms;
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct FadeState fade;
|
|
|
|
|
|
|
|
enum FadeCode
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
Fade_continue,
|
|
|
|
Fade_finished
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static enum FadeCode processmusicfade(struct FadeState* state, int* volume)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
int range;
|
|
|
|
int new_volume;
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
if (state->duration_ms == 0 /* Fast path. */
|
|
|
|
|| state->start_volume == state->end_volume /* Fast path. */
|
|
|
|
|| state->step_ms >= state->duration_ms /* We're finished. */)
|
|
|
|
{
|
|
|
|
*volume = state->end_volume;
|
|
|
|
state->step_ms = 0;
|
|
|
|
return Fade_finished;
|
|
|
|
}
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
range = state->end_volume - state->start_volume;
|
|
|
|
new_volume = range * state->step_ms / state->duration_ms;
|
|
|
|
new_volume += state->start_volume;
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
*volume = new_volume;
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
state->step_ms += game.get_timestep();
|
Fix fade volume durations being incorrect
The previous fade system used only one variable, the amount of volume to
fade per frame. However, this variable was an integer, meaning any
decimal portion would be truncated, and would lead to a longer fade
duration than intended.
The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms /
game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is
usually 34, so a 3000 millisecond fade would be calculated as 128 /
(3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and
then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This
essentially means 1 is added to or subtracted from the volume every
frame, and given that the max volume is 128, this means that the fade
lasts for 128 frames. Now, instead of the fade duration lasting 3
seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 =
4.352 seconds long.
This could be fixed using floats, but when you introduce floats, you now
have 1.9999998 problems. For instance, I'm concerned about
floating-point determinism issues.
What I've done instead is switch the system to use four different
variables instead: the start volume, the end volume, the total duration,
and the duration completed so far (called the "step"). For every frame,
the game interpolates which value should be used based on the step, the
total duration, and the start and end volumes, and then adds the
timestep to the step. This way, fades will be correctly timed, and we
don't have potential determinism issues.
Doing this also fixes inaccuracies with the game timestep changing
during the fade, since the timestep is only used in the calculation
once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
return Fade_continue;
|
2021-04-02 21:02:01 +02:00
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
void musicclass::fadeMusicVolumeIn(int ms)
|
|
|
|
{
|
2021-09-11 04:37:33 +02:00
|
|
|
if (halted())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
m_doFadeInVol = true;
|
|
|
|
m_doFadeOutVol = false;
|
2021-04-12 19:38:44 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
/* Ensure it starts at 0 */
|
|
|
|
musicVolume = 0;
|
2021-04-12 19:38:44 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
/* Fix 1-frame glitch */
|
2022-01-14 22:46:04 +01:00
|
|
|
MusicTrack::SetVolume(0);
|
2021-04-12 19:41:44 +02:00
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
fade.step_ms = 0;
|
|
|
|
fade.duration_ms = ms;
|
|
|
|
fade.start_volume = 0;
|
2022-01-14 22:46:04 +01:00
|
|
|
fade.end_volume = VVV_MAX_VOLUME;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
void musicclass::fadeMusicVolumeOut(const int fadeout_ms)
|
|
|
|
{
|
2021-09-11 04:37:33 +02:00
|
|
|
if (halted())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-07 03:56:39 +02:00
|
|
|
m_doFadeInVol = false;
|
|
|
|
m_doFadeOutVol = true;
|
|
|
|
|
|
|
|
fade.step_ms = 0;
|
|
|
|
/* Duration is proportional to current volume. */
|
2022-01-14 22:46:04 +01:00
|
|
|
fade.duration_ms = fadeout_ms * musicVolume / VVV_MAX_VOLUME;
|
2021-09-07 03:56:39 +02:00
|
|
|
fade.start_volume = musicVolume;
|
|
|
|
fade.end_volume = 0;
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
}
|
|
|
|
|
2020-11-06 09:51:04 +01:00
|
|
|
void musicclass::fadeout(const bool quick_fade_ /*= true*/)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
fadeMusicVolumeOut(quick_fade_ ? 500 : 2000);
|
|
|
|
quick_fade = quick_fade_;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::processmusicfadein(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
enum FadeCode fade_code = processmusicfade(&fade, &musicVolume);
|
|
|
|
if (fade_code == Fade_finished)
|
|
|
|
{
|
|
|
|
m_doFadeInVol = false;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
void musicclass::processmusicfadeout(void)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
enum FadeCode fade_code = processmusicfade(&fade, &musicVolume);
|
|
|
|
if (fade_code == Fade_finished)
|
|
|
|
{
|
|
|
|
musicVolume = 0;
|
|
|
|
m_doFadeOutVol = false;
|
2023-04-06 04:32:42 +02:00
|
|
|
haltdasmusik(true);
|
2021-09-07 03:56:39 +02:00
|
|
|
}
|
Re-fix resumemusic/musicfadein once again
So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had
a few issues.
The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)
Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.
Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).
And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.
There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 21:56:25 +02:00
|
|
|
}
|
|
|
|
|
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
|
|
|
void musicclass::processmusic(void)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if(!safeToProcessMusic)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_doFadeInVol)
|
|
|
|
{
|
|
|
|
processmusicfadein();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_doFadeOutVol)
|
|
|
|
{
|
|
|
|
processmusicfadeout();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This needs to come after processing fades */
|
2021-09-11 04:35:29 +02:00
|
|
|
if (nicefade && halted())
|
2021-09-07 03:56:39 +02:00
|
|
|
{
|
|
|
|
play(nicechange);
|
|
|
|
nicechange = -1;
|
|
|
|
nicefade = false;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void musicclass::niceplay(int t)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
/* important: do nothing if the correct song is playing! */
|
|
|
|
if ((!mmmmmm && currentsong != t)
|
|
|
|
|| (mmmmmm && usingmmmmmm && currentsong != t)
|
|
|
|
|| (mmmmmm && !usingmmmmmm && currentsong != t + num_mmmmmm_tracks))
|
|
|
|
{
|
|
|
|
if (currentsong != -1)
|
|
|
|
{
|
|
|
|
fadeout(false);
|
|
|
|
}
|
|
|
|
nicefade = true;
|
|
|
|
}
|
|
|
|
nicechange = t;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
Replace main game music with music area map
The main game used a set of copy-pasted code to set the music of each
area. There WAS some redundancy built-in, but only three rooms in each
direction from the entrance of a zone.
Given this, it's completely possible for players to mismatch the music
of the area and level. In fact, it's easy to do it even on accident,
especially since 2.3 now lets you quicksave and quit during cutscenes.
Just play a cutscene that has Pause music, then quicksave, quit, and
reload. Also some other accidental ways that I've forgotten about.
To fix this, I've done what mapclass has and made an areamap. Except for
music. This map is the map of the track number of every single room,
except for three special cases: -1 for do nothing and don't change music
(usually because multiple different tracks can be played in this room),
-2 for Tower music (needs to be track 2 or 9 depending on Flip Mode),
and -3 for the start of Space Station 2 (track 1 in time trials, track 4
otherwise).
I've thoroughly tested this areamap by playing through the game and
entering every single room. Additionally I've also thoroughly tested all
special cases (entering the Ship through the teleporter or main
entrance, using the Ship's jukebox, the Tower in Flip Mode and regular
mode, and the start of Space Station 2 in time trial and in regular
mode).
Closes #449.
2021-05-12 04:59:02 +02:00
|
|
|
static const int areamap[] = {
|
2021-09-07 03:56:39 +02:00
|
|
|
4, 3, 3, 3, 3, 3, 3, 3, 4,-2, 4, 4, 4,12,12,12,12,12,12,12,
|
|
|
|
4, 3, 3, 3, 3, 3, 3, 4, 4,-2, 4, 4, 4, 4,12,12,12,12,12,12,
|
|
|
|
4, 4, 4, 4, 3, 4, 4, 4, 4,-2, 4, 4, 4, 4,12,12,12,12,12,12,
|
|
|
|
4, 4, 4, 4, 3, 4, 4, 4, 4,-2, 4, 4, 1, 1, 1, 1,12,12,12,12,
|
|
|
|
4, 4, 3, 3, 3, 4, 4, 4, 4,-2,-2,-2, 1, 1, 1, 1, 4, 4, 4, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 1, 1, 1, 1, 1, 1,11,11,-1, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 1, 1, 1, 1, 1, 1, 1,11,11,11,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4, 4, 4, 1, 1, 1, 1, 1, 1, 3,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4,-2,-2, 4, 4, 4, 1, 1, 1, 1, 1, 1, 4,
|
|
|
|
4, 4,-1,-1,-1, 4, 4, 4, 4,-2, 4, 4, 4, 1, 1, 1, 1, 1, 1, 4,
|
|
|
|
4, 4,-1,-1,-1, 4, 4, 4, 4,-2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4, 1, 1, 1, 1, 1, 1, 4, 1, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4, 1, 1, 1, 1, 1, 1, 4, 1, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4,-1,-3, 4, 4, 4, 4, 4, 1, 4,
|
|
|
|
4, 4, 4, 4, 4, 3, 3, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
4, 4, 3, 3, 3, 3, 3, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
4, 3, 3, 3, 3, 3, 3, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
3, 3, 3, 3, 3, 4, 4, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
3, 3, 3, 3, 3, 4, 4, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
|
Replace main game music with music area map
The main game used a set of copy-pasted code to set the music of each
area. There WAS some redundancy built-in, but only three rooms in each
direction from the entrance of a zone.
Given this, it's completely possible for players to mismatch the music
of the area and level. In fact, it's easy to do it even on accident,
especially since 2.3 now lets you quicksave and quit during cutscenes.
Just play a cutscene that has Pause music, then quicksave, quit, and
reload. Also some other accidental ways that I've forgotten about.
To fix this, I've done what mapclass has and made an areamap. Except for
music. This map is the map of the track number of every single room,
except for three special cases: -1 for do nothing and don't change music
(usually because multiple different tracks can be played in this room),
-2 for Tower music (needs to be track 2 or 9 depending on Flip Mode),
and -3 for the start of Space Station 2 (track 1 in time trials, track 4
otherwise).
I've thoroughly tested this areamap by playing through the game and
entering every single room. Additionally I've also thoroughly tested all
special cases (entering the Ship through the teleporter or main
entrance, using the Ship's jukebox, the Tower in Flip Mode and regular
mode, and the start of Space Station 2 in time trial and in regular
mode).
Closes #449.
2021-05-12 04:59:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
SDL_COMPILE_TIME_ASSERT(areamap, SDL_arraysize(areamap) == 20 * 20);
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
void musicclass::changemusicarea(int x, int y)
|
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
int room;
|
|
|
|
int track;
|
|
|
|
|
|
|
|
if (script.running)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
room = musicroom(x, y);
|
|
|
|
|
|
|
|
if (!INBOUNDS_ARR(room, areamap))
|
|
|
|
{
|
|
|
|
SDL_assert(0 && "Music map index out-of-bounds!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
track = areamap[room];
|
|
|
|
|
|
|
|
switch (track)
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
/* Don't change music. */
|
|
|
|
return;
|
|
|
|
case -2:
|
|
|
|
/* Special case: Tower music, changes with Flip Mode. */
|
|
|
|
if (graphics.setflipmode)
|
|
|
|
{
|
|
|
|
track = 9; /* ecroF evitisoP */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
track = 2; /* Positive Force */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case -3:
|
|
|
|
/* Special case: start of Space Station 2. */
|
|
|
|
if (game.intimetrial)
|
|
|
|
{
|
|
|
|
track = 1; /* Pushing Onwards */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
track = 4; /* Passion for Exploring */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
niceplay(track);
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 01:36:35 +02:00
|
|
|
void musicclass::playef(int t)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2021-09-07 03:56:39 +02:00
|
|
|
if (!INBOUNDS_VEC(t, soundTracks))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2022-01-14 22:46:04 +01:00
|
|
|
soundTracks[t].Play();
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2021-04-02 21:07:19 +02:00
|
|
|
|
|
|
|
void musicclass::pauseef(void)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
SoundTrack::Pause();
|
2021-04-02 21:07:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void musicclass::resumeef(void)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
SoundTrack::Resume();
|
2021-04-02 21:07:19 +02:00
|
|
|
}
|
2021-09-11 04:35:29 +02:00
|
|
|
|
|
|
|
bool musicclass::halted(void)
|
|
|
|
{
|
2022-04-10 21:50:15 +02:00
|
|
|
return MusicTrack::IsPaused();
|
2021-09-11 04:35:29 +02:00
|
|
|
}
|
2021-12-26 14:48:23 +01:00
|
|
|
|
|
|
|
void musicclass::updatemutestate(void)
|
|
|
|
{
|
|
|
|
if (game.muted)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
MusicTrack::SetVolume(0);
|
|
|
|
SoundTrack::SetVolume(0);
|
2021-12-26 14:48:23 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
SoundTrack::SetVolume(VVV_MAX_VOLUME * user_sound_volume / USER_VOLUME_MAX);
|
2021-12-26 14:48:23 +01:00
|
|
|
|
|
|
|
if (game.musicmuted)
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
MusicTrack::SetVolume(0);
|
2021-12-26 14:48:23 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-14 22:46:04 +01:00
|
|
|
MusicTrack::SetVolume(musicVolume * user_music_volume / USER_VOLUME_MAX);
|
2021-12-26 14:48:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|