Add support for 8-bit and mono WAV files. More fixes.

This commit is contained in:
Henrik Rydgård 2023-07-16 15:04:21 +02:00
parent 18fbe7afe8
commit de20a5692f
3 changed files with 45 additions and 15 deletions

View file

@ -369,6 +369,11 @@ void BackgroundAudio::Update() {
}
}
inline int16_t ConvertU8ToI16(uint8_t value) {
int ivalue = value - 128;
return ivalue * 255;
}
Sample *Sample::Load(const std::string &path) {
size_t bytes;
uint8_t *data = g_VFS.ReadFile(path.c_str(), &bytes);
@ -384,14 +389,22 @@ Sample *Sample::Load(const std::string &path) {
delete[] data;
if (wave.num_channels != 2 || wave.raw_bytes_per_frame != 4) {
ERROR_LOG(AUDIO, "Wave format not supported for mixer playback. Must be 16-bit raw stereo. '%s'", path.c_str());
if (wave.num_channels > 2 || wave.raw_bytes_per_frame > sizeof(int16_t) * wave.num_channels) {
ERROR_LOG(AUDIO, "Wave format not supported for mixer playback. Must be 8-bit or 16-bit raw mono or stereo. '%s'", path.c_str());
return nullptr;
}
int16_t *samples = new int16_t[2 * wave.numFrames];
memcpy(samples, wave.raw_data, wave.numFrames * wave.raw_bytes_per_frame);
return new Sample(samples, wave.numFrames, wave.sample_rate);
int16_t *samples = new int16_t[wave.num_channels * wave.numFrames];
if (wave.raw_bytes_per_frame == wave.num_channels * 2) {
// 16-bit
memcpy(samples, wave.raw_data, wave.numFrames * wave.raw_bytes_per_frame);
} else if (wave.raw_bytes_per_frame == wave.num_channels) {
// 8-bit. Convert.
for (int i = 0; i < wave.num_channels * wave.numFrames; i++) {
samples[i] = ConvertU8ToI16(wave.raw_data[i]);
}
}
return new Sample(samples, wave.num_channels, wave.numFrames, wave.sample_rate);
}
static inline int16_t Clamp16(int32_t sample) {
@ -434,15 +447,25 @@ void SoundEffectMixer::Mix(int16_t *buffer, int sz, int sampleRateHz) {
int wholeOffset = iter->offset >> 32;
int frac = (iter->offset >> 20) & 0xFFF; // Use a 12 bit fraction to get away with 32-bit multiplies
int interpolatedLeft = (sample->data_[wholeOffset * 2] * (0x1000 - frac) + sample->data_[(wholeOffset + 1) * 2] * frac) >> 12;
int interpolatedRight = (sample->data_[wholeOffset * 2 + 1] * (0x1000 - frac) + sample->data_[(wholeOffset + 1) * 2 + 1] * frac) >> 12;
if (sample->channels_ == 2) {
int interpolatedLeft = (sample->data_[wholeOffset * 2] * (0x1000 - frac) + sample->data_[(wholeOffset + 1) * 2] * frac) >> 12;
int interpolatedRight = (sample->data_[wholeOffset * 2 + 1] * (0x1000 - frac) + sample->data_[(wholeOffset + 1) * 2 + 1] * frac) >> 12;
// Clamping add on top per sample. Not great, we should be mixing at higher bitrate instead. Oh well.
int left = Clamp16(buffer[i] + (interpolatedLeft * iter->volume >> 8));
int right = Clamp16(buffer[i + 1] + (interpolatedRight * iter->volume >> 8));
// Clamping add on top per sample. Not great, we should be mixing at higher bitrate instead. Oh well.
int left = Clamp16(buffer[i] + (interpolatedLeft * iter->volume >> 8));
int right = Clamp16(buffer[i + 1] + (interpolatedRight * iter->volume >> 8));
buffer[i] = left;
buffer[i + 1] = right;
buffer[i] = left;
buffer[i + 1] = right;
} else if (sample->channels_ == 1) {
int interpolated = (sample->data_[wholeOffset] * (0x1000 - frac) + sample->data_[wholeOffset + 1] * frac) >> 12;
// Clamping add on top per sample. Not great, we should be mixing at higher bitrate instead. Oh well.
int value = Clamp16(buffer[i] + (interpolated * iter->volume >> 8));
buffer[i] = value;
buffer[i + 1] = value;
}
iter->offset += stride;
}
@ -462,6 +485,7 @@ void SoundEffectMixer::Play(UI::UISound sfx, float volume) {
void SoundEffectMixer::UpdateSample(UI::UISound sound, Sample *sample) {
if (sample) {
std::lock_guard<std::mutex> guard(mutex_);
samples_[(size_t)sound] = std::unique_ptr<Sample>(sample);
} else {
LoadDefaultSample(sound);
@ -485,6 +509,7 @@ void SoundEffectMixer::LoadDefaultSample(UI::UISound sound) {
if (!sample) {
ERROR_LOG(SYSTEM, "Failed to load the default sample for UI sound %d", (int)sound);
}
std::lock_guard<std::mutex> guard(mutex_);
samples_[(size_t)sound] = std::unique_ptr<Sample>(sample);
}

View file

@ -12,13 +12,14 @@ class AT3PlusReader;
struct Sample {
// data must be new-ed.
Sample(int16_t *data, int length, int rateInHz) : data_(data), length_(length), rateInHz_(rateInHz) {}
Sample(int16_t *data, int channels, int length, int rateInHz) : channels_(channels), data_(data), length_(length), rateInHz_(rateInHz) {}
~Sample() {
delete[] data_;
}
int16_t *data_;
int length_; // stereo samples.
int length_; // stereo or mono samples.
int rateInHz_; // sampleRate
int channels_;
static Sample *Load(const std::string &path);
};

View file

@ -54,6 +54,7 @@ AudioFileChooser::AudioFileChooser(std::string *value, const std::string &title,
return UI::EVENT_DONE;
});
Add(new Choice(ImageID("I_TRASHCAN"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) {
g_BackgroundAudio.SFX().UpdateSample(sound, nullptr);
value->clear();
return UI::EVENT_DONE;
});
@ -239,7 +240,10 @@ void RetroAchievementsSettingsScreen::CreateTabs() {
using namespace UI;
CreateAccountTab(AddTab("AchievementsAccount", ac->T("Account")));
CreateCustomizeTab(AddTab("AchievementsCustomize", ac->T("Customize")));
if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) {
// Don't bother creating this tab if we don't have a file browser.
CreateCustomizeTab(AddTab("AchievementsCustomize", ac->T("Customize")));
}
CreateDeveloperToolsTab(AddTab("AchievementsDeveloperTools", sy->T("Developer Tools")));
}