Skip to content

Commit

Permalink
Disable format conversations and resampling for SE before caching it.…
Browse files Browse the repository at this point in the history
… Otherwise there is a noticable lag when loading large samples for the first time.

With a cache hit the expensive IO operation is still prevented this way but this moves the CPU intensive part to the audio thread because it has to do the resampling now.

Fix EasyRPG#1833
  • Loading branch information
Ghabry committed Mar 30, 2020
1 parent 3924b52 commit 1d3401d
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 268 deletions.
90 changes: 47 additions & 43 deletions src/audio_decoder.cpp
Expand Up @@ -128,7 +128,7 @@ class WMAUnsupportedFormatDecoder : public AudioDecoder {
};
const char wma_magic[] = { (char)0x30, (char)0x26, (char)0xB2, (char)0x75 };

std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string& filename) {
std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string& filename, bool resample) {
char magic[4] = { 0 };
if (fread(magic, 4, 1, file) != 1)
return nullptr;
Expand All @@ -141,16 +141,20 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
(void)filename;
#endif

#ifndef USE_AUDIO_RESAMPLER
resample = false;
#endif

// Try to use MIDI decoder, use fallback(s) if available
if (!strncmp(magic, "MThd", 4)) {
#ifdef HAVE_WILDMIDI
static bool wildmidi_works = true;
if (wildmidi_works) {
auto mididec = std::unique_ptr<AudioDecoder>(new WildMidiDecoder(filename));
if (mididec->WasInited()) {
# ifdef USE_AUDIO_RESAMPLER
mididec = std::unique_ptr<AudioResampler>(new AudioResampler(std::move(mididec)));
# endif
if (resample) {
mididec = std::unique_ptr<AudioResampler>(new AudioResampler(std::move(mididec)));
}
return mididec;
} else {
wildmidi_works = false;
Expand All @@ -162,9 +166,9 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
auto mididec = std::unique_ptr<AudioDecoder>(new FmMidiDecoder());

if (mididec->WasInited()) {
# ifdef USE_AUDIO_RESAMPLER
mididec = std::unique_ptr<AudioResampler>(new AudioResampler(std::move(mididec), true, AudioResampler::Quality::Low));
# endif
if (resample) {
mididec = std::unique_ptr<AudioResampler>(new AudioResampler(std::move(mididec), true));
}
return mididec;
} else {
Output::Debug("FmMidi Failed: %s", mididec->GetError().c_str());
Expand All @@ -182,11 +186,11 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
return nullptr;
fseek(file, 0, SEEK_SET);
if (!strncmp(magic, "Opus", 4)) {
# ifdef USE_AUDIO_RESAMPLER
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new OpusDecoder())));
# else
return std::unique_ptr<AudioDecoder>(new OpusDecoder());
# endif
if (resample) {
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new OpusDecoder())));
} else {
return std::unique_ptr<AudioDecoder>(new OpusDecoder());
}
}
#endif

Expand All @@ -197,11 +201,11 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
fseek(file, 0, SEEK_SET);

if (!strncmp(magic, "vorb", 4)) {
# ifdef USE_AUDIO_RESAMPLER
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new OggVorbisDecoder())));
# else
return std::unique_ptr<AudioDecoder>(new OggVorbisDecoder());
# endif
if (resample) {
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new OggVorbisDecoder())));
} else {
return std::unique_ptr<AudioDecoder>(new OggVorbisDecoder());
}
}
#endif
}
Expand All @@ -216,11 +220,11 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
Utils::SwapByteOrder(raw_enc);
fseek(file, 0, SEEK_SET);
if (raw_enc == 0x01) { // Codec is normal PCM
# ifdef USE_AUDIO_RESAMPLER
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new WavDecoder())));
# else
return std::unique_ptr<AudioDecoder>(new WavDecoder());
# endif
if (resample) {
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new WavDecoder())));
} else {
return std::unique_ptr<AudioDecoder>(new WavDecoder());
}
}
}

Expand All @@ -232,11 +236,11 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
!strncmp(magic, "OggS", 4) || // OGG
!strncmp(magic, "fLaC", 4)) { // FLAC
#ifdef HAVE_LIBSNDFILE
# ifdef USE_AUDIO_RESAMPLER
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new LibsndfileDecoder())));
# else
return std::unique_ptr<AudioDecoder>(new LibsndfileDecoder());
# endif
if (resample) {
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new LibsndfileDecoder())));
} else {
return std::unique_ptr<AudioDecoder>(new LibsndfileDecoder());
}
#endif
return nullptr;
}
Expand All @@ -249,25 +253,25 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
// Test for tracker modules
#ifdef HAVE_XMP
if (XMPDecoder::IsModule(filename)) {
# ifdef USE_AUDIO_RESAMPLER
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new XMPDecoder())));
# else
return std::unique_ptr<AudioDecoder>(new XMPDecoder());
# endif
if (resample) {
return std::unique_ptr<AudioDecoder>(new AudioResampler(std::unique_ptr<AudioDecoder>(new XMPDecoder())));
} else {
return std::unique_ptr<AudioDecoder>(new XMPDecoder());
}
}
#endif

// False positive MP3s should be prevented before by checking for common headers
#ifdef HAVE_MPG123
static bool mpg123_works = true;
if (mpg123_works) {
AudioDecoder *mp3dec = nullptr;
AudioDecoder* mp3dec = nullptr;
if (strncmp(magic, "ID3", 3) == 0) {
# ifdef USE_AUDIO_RESAMPLER
mp3dec = new AudioResampler(std::unique_ptr<AudioDecoder>(new Mpg123Decoder()));
# else
mp3dec = new Mpg123Decoder();
# endif
if (resample) {
mp3dec = new AudioResampler(std::unique_ptr<AudioDecoder>(new Mpg123Decoder()));
} else {
mp3dec = new Mpg123Decoder();
}
if (mp3dec) {
if (mp3dec->WasInited())
return std::unique_ptr<AudioDecoder>(mp3dec);
Expand All @@ -281,11 +285,11 @@ std::unique_ptr<AudioDecoder> AudioDecoder::Create(FILE* file, const std::string
// Parsing MP3s seems to be the only reliable way to detect them
if (Mpg123Decoder::IsMp3(file)) {
fseek(file, 0, SEEK_SET);
# ifdef USE_AUDIO_RESAMPLER
mp3dec = new AudioResampler(std::unique_ptr<AudioDecoder>(new Mpg123Decoder()));
# else
mp3dec = new Mpg123Decoder();
# endif
if (resample) {
mp3dec = new AudioResampler(std::unique_ptr<AudioDecoder>(new Mpg123Decoder()));
} else {
mp3dec = new Mpg123Decoder();
}
if (mp3dec) {
if(mp3dec->WasInited())
return std::unique_ptr<AudioDecoder>(mp3dec);
Expand Down
3 changes: 2 additions & 1 deletion src/audio_decoder.h
Expand Up @@ -90,9 +90,10 @@ class AudioDecoder {
*
* @param file File handle to parse
* @param filename Path to the file handle
* @param resample Whether the decoder shall be wrapped into a resampler (if supported)
* @return An audio decoder instance when the format was detected, otherwise null
*/
static std::unique_ptr<AudioDecoder> Create(FILE* file, const std::string& filename);
static std::unique_ptr<AudioDecoder> Create(FILE* file, const std::string& filename, bool resample = true);

/**
* Updates the volume for the fade in/out effect.
Expand Down
41 changes: 13 additions & 28 deletions src/audio_generic.cpp
Expand Up @@ -38,7 +38,7 @@ GenericAudio::GenericAudio() {
BGM_Channels[i].decoder.reset();
}
for (unsigned i = 0; i < nr_of_se_channels; i++) {
SE_Channels[i].se.reset();
SE_Channels[i].decoder.reset();
}
BGM_PlayedOnceIndicator = false;

Expand Down Expand Up @@ -153,7 +153,7 @@ void GenericAudio::BGM_Pitch(int pitch) {
void GenericAudio::SE_Play(std::string const &file, int volume, int pitch) {
if (Muted) return;
for (unsigned i = 0; i < nr_of_se_channels; i++) {
if (!SE_Channels[i].se) {
if (!SE_Channels[i].decoder) {
//If there is an unused se channel
PlayOnChannel(SE_Channels[i], file, volume, pitch);
return;
Expand Down Expand Up @@ -211,16 +211,13 @@ bool GenericAudio::PlayOnChannel(SeChannel& chan, const std::string& file, int v
chan.paused = true; // Pause channel so the audio thread doesn't work on it
chan.stopped = false; // Unstop channel so the audio thread doesn't delete it

std::unique_ptr<AudioSeCache> cache = AudioSeCache::Create(file);
std::unique_ptr<AudioSeCache> cache = AudioSeCache::Create(file);
if (cache) {
cache->SetPitch(pitch);
cache->SetFormat(output_format.frequency, output_format.format, output_format.channels);

chan.se = cache->Decode();
chan.buffer_pos = 0;
chan.volume = volume;
chan.decoder = cache->CreateSeDecoder();
chan.decoder->SetPitch(pitch);
chan.decoder->SetFormat(output_format.frequency, output_format.format, output_format.channels);
chan.volume = volume;
chan.paused = false; // Unpause channel -> Play it.

return true;
} else {
Output::Warning("Couldn't play SE %s. Format not supported", FileFinder::GetPathInsideGamePath(file).c_str());
Expand Down Expand Up @@ -297,13 +294,12 @@ void GenericAudio::Decode(uint8_t* output_buffer, int buffer_length) {
SeChannel& currently_mixed_channel = SE_Channels[i - nr_of_bgm_channels];
float current_master_volume = 1.0;

if (currently_mixed_channel.se && !currently_mixed_channel.paused) {
if (currently_mixed_channel.decoder && !currently_mixed_channel.paused) {
if (currently_mixed_channel.stopped) {
currently_mixed_channel.se.reset();
currently_mixed_channel.decoder.reset();
} else {
volume = current_master_volume * (currently_mixed_channel.volume / 100.0);
channels = currently_mixed_channel.se->channels;
sampleformat = currently_mixed_channel.se->format;
currently_mixed_channel.decoder->GetFormat(frequency, sampleformat, channels);
samplesize = AudioDecoder::GetSamplesizeForFormat(sampleformat);

total_volume += volume;
Expand All @@ -312,23 +308,12 @@ void GenericAudio::Decode(uint8_t* output_buffer, int buffer_length) {
unsigned bytes_to_read = (samplesize * channels * samples_per_frame);
bytes_to_read = (bytes_to_read < scrap_buffer_size) ? bytes_to_read : scrap_buffer_size;

if (currently_mixed_channel.buffer_pos + bytes_to_read >
currently_mixed_channel.se->buffer.size()) {
bytes_to_read = currently_mixed_channel.se->buffer.size() - currently_mixed_channel.buffer_pos;
}

memcpy(scrap_buffer.data(),
&currently_mixed_channel.se->buffer[currently_mixed_channel.buffer_pos],
bytes_to_read);

currently_mixed_channel.buffer_pos += bytes_to_read;

read_bytes = bytes_to_read;
read_bytes = currently_mixed_channel.decoder->Decode(scrap_buffer.data(), bytes_to_read);

// Now decide what to do when a channel has reached its end
if (currently_mixed_channel.buffer_pos >= currently_mixed_channel.se->buffer.size()) {
if (currently_mixed_channel.decoder->IsFinished()) {
// SE are only played once so free the se if finished
currently_mixed_channel.se.reset();
currently_mixed_channel.decoder.reset();
}

channel_used = true;
Expand Down
6 changes: 2 additions & 4 deletions src/audio_generic.h
Expand Up @@ -69,12 +69,10 @@ struct GenericAudio : public AudioInterface {
bool stopped;
};
struct SeChannel {
AudioSeRef se;
size_t buffer_pos;
std::unique_ptr<AudioDecoder> decoder;
int volume;
bool paused;
bool stopped;
bool finished;
};
struct Format {
int frequency;
Expand All @@ -100,4 +98,4 @@ struct GenericAudio : public AudioInterface {
static std::vector<float> mixer_buffer;
};

#endif //EASYRPG_AUDIOGENERIC_H_
#endif

0 comments on commit 1d3401d

Please sign in to comment.