Avoid crashing when a OGG file is broken (thanks to @kblaschke !)
Also added a name tag to Audio for logging so that it's easier to find the audio asset that's causing it
This commit is contained in:
parent
8b1a2d6f0c
commit
e9e87a1c3c
@ -1241,7 +1241,7 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
|
|||||||
shared_ptr<Assets::AssetData> Assets::loadAudio(AssetPath const& path) const {
|
shared_ptr<Assets::AssetData> Assets::loadAudio(AssetPath const& path) const {
|
||||||
return unlockDuring([&]() {
|
return unlockDuring([&]() {
|
||||||
auto newData = make_shared<AudioData>();
|
auto newData = make_shared<AudioData>();
|
||||||
newData->audio = make_shared<Audio>(open(path.basePath));
|
newData->audio = make_shared<Audio>(open(path.basePath), path.basePath);
|
||||||
newData->needsPostProcessing = newData->audio->compressed();
|
newData->needsPostProcessing = newData->audio->compressed();
|
||||||
return newData;
|
return newData;
|
||||||
});
|
});
|
||||||
|
@ -310,49 +310,54 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount, ExtraMixFunction extraMi
|
|||||||
m_mixBuffer[i] = 0;
|
m_mixBuffer[i] = 0;
|
||||||
ramt += silentSamples * channels;
|
ramt += silentSamples * channels;
|
||||||
}
|
}
|
||||||
ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier);
|
try {
|
||||||
while (ramt != bufferSize && !finished) {
|
ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier);
|
||||||
// Only seek back to the beginning and read more data if loops is < 0
|
while (ramt != bufferSize && !finished) {
|
||||||
// (loop forever), or we have more loops to go, otherwise, the sample is
|
// Only seek back to the beginning and read more data if loops is < 0
|
||||||
// finished.
|
// (loop forever), or we have more loops to go, otherwise, the sample is
|
||||||
if (audioInstance->m_loops != 0) {
|
// finished.
|
||||||
audioInstance->m_audio.seekSample(0);
|
if (audioInstance->m_loops != 0) {
|
||||||
ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier);
|
audioInstance->m_audio.seekSample(0);
|
||||||
if (audioInstance->m_loops > 0)
|
ramt += audioInstance->m_audio.resample(channels, sampleRate, m_mixBuffer.ptr() + ramt, bufferSize - ramt, pitchMultiplier);
|
||||||
--audioInstance->m_loops;
|
if (audioInstance->m_loops > 0)
|
||||||
} else {
|
--audioInstance->m_loops;
|
||||||
finished = true;
|
} else {
|
||||||
}
|
finished = true;
|
||||||
}
|
|
||||||
if (audioInstance->m_clockStop && *audioInstance->m_clockStop < sampleEndTime) {
|
|
||||||
for (size_t s = 0; s < ramt / channels; ++s) {
|
|
||||||
unsigned millisecondsInBuffer = (s * 1000) / sampleRate;
|
|
||||||
auto sampleTime = sampleStartTime + millisecondsInBuffer;
|
|
||||||
if (sampleTime > *audioInstance->m_clockStop) {
|
|
||||||
float volume = 0.0f;
|
|
||||||
if (audioInstance->m_clockStopFadeOut > 0)
|
|
||||||
volume = 1.0f - (float)(sampleTime - *audioInstance->m_clockStop) / (float)audioInstance->m_clockStopFadeOut;
|
|
||||||
|
|
||||||
if (volume <= 0) {
|
|
||||||
for (size_t c = 0; c < channels; ++c)
|
|
||||||
m_mixBuffer[s * channels + c] = 0;
|
|
||||||
} else {
|
|
||||||
for (size_t c = 0; c < channels; ++c)
|
|
||||||
m_mixBuffer[s * channels + c] *= volume;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sampleEndTime > *audioInstance->m_clockStop + audioInstance->m_clockStopFadeOut)
|
if (audioInstance->m_clockStop && *audioInstance->m_clockStop < sampleEndTime) {
|
||||||
finished = true;
|
for (size_t s = 0; s < ramt / channels; ++s) {
|
||||||
}
|
unsigned millisecondsInBuffer = (s * 1000) / sampleRate;
|
||||||
|
auto sampleTime = sampleStartTime + millisecondsInBuffer;
|
||||||
|
if (sampleTime > *audioInstance->m_clockStop) {
|
||||||
|
float volume = 0.0f;
|
||||||
|
if (audioInstance->m_clockStopFadeOut > 0)
|
||||||
|
volume = 1.0f - (float)(sampleTime - *audioInstance->m_clockStop) / (float)audioInstance->m_clockStopFadeOut;
|
||||||
|
|
||||||
for (size_t s = 0; s < ramt / channels; ++s) {
|
if (volume <= 0) {
|
||||||
float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd);
|
for (size_t c = 0; c < channels; ++c)
|
||||||
for (size_t c = 0; c < channels; ++c) {
|
m_mixBuffer[s * channels + c] = 0;
|
||||||
float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value;
|
} else {
|
||||||
int16_t& outSample = outBuffer[s * channels + c];
|
for (size_t c = 0; c < channels; ++c)
|
||||||
outSample = clamp(sample + outSample, -32767.0f, 32767.0f);
|
m_mixBuffer[s * channels + c] *= volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sampleEndTime > *audioInstance->m_clockStop + audioInstance->m_clockStopFadeOut)
|
||||||
|
finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t s = 0; s < ramt / channels; ++s) {
|
||||||
|
float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd);
|
||||||
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
|
float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value;
|
||||||
|
int16_t& outSample = outBuffer[s * channels + c];
|
||||||
|
outSample = clamp(sample + outSample, -32767.0f, 32767.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Star::AudioException const& e) {
|
||||||
|
Logger::error("Error reading audio '{}': {}", audioInstance->m_audio.name(), e.what());
|
||||||
|
finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
audioInstance->m_volume.value = audioStopVolEnd;
|
audioInstance->m_volume.value = audioStopVolEnd;
|
||||||
|
@ -239,17 +239,19 @@ public:
|
|||||||
|
|
||||||
size_t readPartial(int16_t* buffer, size_t bufferSize) {
|
size_t readPartial(int16_t* buffer, size_t bufferSize) {
|
||||||
int bitstream;
|
int bitstream;
|
||||||
int read;
|
int read = OV_HOLE;
|
||||||
// ov_read takes int parameter, so do some magic here to make sure we don't
|
// ov_read takes int parameter, so do some magic here to make sure we don't
|
||||||
// overflow
|
// overflow
|
||||||
bufferSize *= 2;
|
bufferSize *= 2;
|
||||||
|
do {
|
||||||
#if STAR_LITTLE_ENDIAN
|
#if STAR_LITTLE_ENDIAN
|
||||||
read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 0, 2, 1, &bitstream);
|
read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 0, 2, 1, &bitstream);
|
||||||
#else
|
#else
|
||||||
read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 1, 2, 1, &bitstream);
|
read = ov_read(&m_vorbisFile, (char*)buffer, bufferSize, 1, 2, 1, &bitstream);
|
||||||
#endif
|
#endif
|
||||||
|
} while (read == OV_HOLE);
|
||||||
if (read < 0)
|
if (read < 0)
|
||||||
throw AudioException("Error in Audio::read");
|
throw AudioException::format("Error in Audio::read ({})", read);
|
||||||
|
|
||||||
// read in bytes, returning number of int16_t samples.
|
// read in bytes, returning number of int16_t samples.
|
||||||
return read / 2;
|
return read / 2;
|
||||||
@ -349,7 +351,8 @@ private:
|
|||||||
ExternalBuffer m_memoryFile;
|
ExternalBuffer m_memoryFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
Audio::Audio(IODevicePtr device) {
|
Audio::Audio(IODevicePtr device, String name) {
|
||||||
|
m_name = name;
|
||||||
if (!device->isOpen())
|
if (!device->isOpen())
|
||||||
device->open(IOMode::Read);
|
device->open(IOMode::Read);
|
||||||
|
|
||||||
@ -579,4 +582,12 @@ size_t Audio::resample(unsigned destinationChannels, unsigned destinationSampleR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String const& Audio::name() const {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::setName(String name) {
|
||||||
|
m_name = std::move(name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ STAR_EXCEPTION(AudioException, StarException);
|
|||||||
// instances is not expensive.
|
// instances is not expensive.
|
||||||
class Audio {
|
class Audio {
|
||||||
public:
|
public:
|
||||||
explicit Audio(IODevicePtr device);
|
explicit Audio(IODevicePtr device, String name = "");
|
||||||
Audio(Audio const& audio);
|
Audio(Audio const& audio);
|
||||||
Audio(Audio&& audio);
|
Audio(Audio&& audio);
|
||||||
|
|
||||||
@ -90,12 +90,16 @@ public:
|
|||||||
int16_t* destinationBuffer, size_t destinationBufferSize,
|
int16_t* destinationBuffer, size_t destinationBufferSize,
|
||||||
double velocity = 1.0);
|
double velocity = 1.0);
|
||||||
|
|
||||||
|
String const& name() const;
|
||||||
|
void setName(String name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// If audio is uncompressed, this will be null.
|
// If audio is uncompressed, this will be null.
|
||||||
CompressedAudioImplPtr m_compressed;
|
CompressedAudioImplPtr m_compressed;
|
||||||
UncompressedAudioImplPtr m_uncompressed;
|
UncompressedAudioImplPtr m_uncompressed;
|
||||||
|
|
||||||
ByteArray m_workingBuffer;
|
ByteArray m_workingBuffer;
|
||||||
|
String m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user