mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Call the sound effect mixer directly from Mix instead of pushing the samples from background audio.
This commit is contained in:
parent
830679503d
commit
19f4eadeb1
@ -1,13 +1,19 @@
|
||||
#include "Common/System/System.h"
|
||||
#include "Core/HW/StereoResampler.h" // TODO: doesn't belong in Core/HW...
|
||||
#include "UI/AudioCommon.h"
|
||||
#include "UI/BackgroundAudio.h"
|
||||
|
||||
StereoResampler g_resampler;
|
||||
|
||||
// numFrames is number of stereo frames.
|
||||
// This is called from *outside* the emulator thread.
|
||||
int __AudioMix(int16_t *outstereo, int numFrames, int sampleRate) {
|
||||
return g_resampler.Mix(outstereo, numFrames, false, sampleRate);
|
||||
int __AudioMix(int16_t *outStereo, int numFrames, int sampleRateHz) {
|
||||
int validFrames = g_resampler.Mix(outStereo, numFrames, false, sampleRateHz);
|
||||
|
||||
// Mix sound effects on top.
|
||||
g_BackgroundAudio.SFX().Mix(outStereo, validFrames, sampleRateHz);
|
||||
|
||||
return validFrames;
|
||||
}
|
||||
|
||||
void System_AudioGetDebugStats(char *buf, size_t bufSize) {
|
||||
|
@ -257,49 +257,6 @@ BackgroundAudio::~BackgroundAudio() {
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
BackgroundAudio::Sample *BackgroundAudio::LoadSample(const std::string &path) {
|
||||
size_t bytes;
|
||||
uint8_t *data = g_VFS.ReadFile(path.c_str(), &bytes);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RIFFReader reader(data, (int)bytes);
|
||||
|
||||
WavData wave;
|
||||
wave.Read(reader);
|
||||
|
||||
delete [] data;
|
||||
|
||||
if (wave.num_channels != 2 || wave.sample_rate != 44100 || 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());
|
||||
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 BackgroundAudio::Sample(samples, wave.numFrames);
|
||||
}
|
||||
|
||||
void BackgroundAudio::LoadSamples() {
|
||||
samples_.resize((size_t)UI::UISound::COUNT);
|
||||
samples_[(size_t)UI::UISound::BACK] = std::unique_ptr<Sample>(LoadSample("sfx_back.wav"));
|
||||
samples_[(size_t)UI::UISound::SELECT] = std::unique_ptr<Sample>(LoadSample("sfx_select.wav"));
|
||||
samples_[(size_t)UI::UISound::CONFIRM] = std::unique_ptr<Sample>(LoadSample("sfx_confirm.wav"));
|
||||
samples_[(size_t)UI::UISound::TOGGLE_ON] = std::unique_ptr<Sample>(LoadSample("sfx_toggle_on.wav"));
|
||||
samples_[(size_t)UI::UISound::TOGGLE_OFF] = std::unique_ptr<Sample>(LoadSample("sfx_toggle_off.wav"));
|
||||
|
||||
UI::SetSoundCallback([](UI::UISound sound) {
|
||||
g_BackgroundAudio.PlaySFX(sound);
|
||||
});
|
||||
}
|
||||
|
||||
void BackgroundAudio::PlaySFX(UI::UISound sfx) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
plays_.push_back(PlayInstance{ sfx, 0, 64, false });
|
||||
}
|
||||
|
||||
void BackgroundAudio::Clear(bool hard) {
|
||||
if (!hard) {
|
||||
fadingOut_ = true;
|
||||
@ -372,28 +329,6 @@ bool BackgroundAudio::Play() {
|
||||
}
|
||||
}
|
||||
|
||||
// Mix in menu sound effects. Terribly slow mixer but meh.
|
||||
if (!plays_.empty()) {
|
||||
for (int i = 0; i < sz * 2; i += 2) {
|
||||
std::vector<PlayInstance>::iterator iter = plays_.begin();
|
||||
while (iter != plays_.end()) {
|
||||
PlayInstance inst = *iter;
|
||||
auto sample = samples_[(int)inst.sound].get();
|
||||
if (!sample || iter->offset >= sample->length_) {
|
||||
iter->done = true;
|
||||
iter = plays_.erase(iter);
|
||||
} else {
|
||||
if (!iter->done) {
|
||||
buffer[i] += sample->data_[inst.offset * 2] * inst.volume >> 8;
|
||||
buffer[i + 1] += sample->data_[inst.offset * 2 + 1] * inst.volume >> 8;
|
||||
}
|
||||
iter->offset++;
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System_AudioPushSamples(buffer, sz);
|
||||
|
||||
if (at3Reader_ && fadingOut_ && volume_ <= 0.0f) {
|
||||
@ -431,3 +366,87 @@ void BackgroundAudio::Update() {
|
||||
sndLoadPending_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int16_t Clamp16(int32_t sample) {
|
||||
if (sample < -32767) return -32767;
|
||||
if (sample > 32767) return 32767;
|
||||
return sample;
|
||||
}
|
||||
|
||||
void SoundEffectMixer::Mix(int16_t *buffer, int sz, int sampleRateHz) {
|
||||
// Mix in menu sound effects. Terribly slow mixer but meh.
|
||||
if (plays_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::vector<PlayInstance>::iterator iter = plays_.begin(); iter != plays_.end(); ) {
|
||||
auto sample = samples_[(int)iter->sound].get();
|
||||
for (int i = 0; i < sz * 2; i += 2) {
|
||||
if (!sample || iter->offset >= sample->length_) {
|
||||
iter->done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clamping add on top. Not great, we should be mixing at higher bitrate instead. Oh well.
|
||||
int left = buffer[i];
|
||||
int right = buffer[i + 1];
|
||||
|
||||
left = Clamp16(left + (sample->data_[iter->offset * 2] * iter->volume >> 8));
|
||||
right = Clamp16(right + (sample->data_[iter->offset * 2 + 1] * iter->volume >> 8));
|
||||
|
||||
buffer[i] = left;
|
||||
buffer[i + 1] = right;
|
||||
|
||||
iter->offset++;
|
||||
}
|
||||
|
||||
if (iter->done) {
|
||||
iter = plays_.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundEffectMixer::Play(UI::UISound sfx) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
plays_.push_back(PlayInstance{ sfx, 0, 64, false });
|
||||
}
|
||||
|
||||
Sample *SoundEffectMixer::LoadSample(const std::string &path) {
|
||||
size_t bytes;
|
||||
uint8_t *data = g_VFS.ReadFile(path.c_str(), &bytes);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RIFFReader reader(data, (int)bytes);
|
||||
|
||||
WavData wave;
|
||||
wave.Read(reader);
|
||||
|
||||
delete[] data;
|
||||
|
||||
if (wave.num_channels != 2 || wave.sample_rate != 44100 || 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());
|
||||
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);
|
||||
}
|
||||
|
||||
void SoundEffectMixer::LoadSamples() {
|
||||
samples_.resize((size_t)UI::UISound::COUNT);
|
||||
samples_[(size_t)UI::UISound::BACK] = std::unique_ptr<Sample>(LoadSample("sfx_back.wav"));
|
||||
samples_[(size_t)UI::UISound::SELECT] = std::unique_ptr<Sample>(LoadSample("sfx_select.wav"));
|
||||
samples_[(size_t)UI::UISound::CONFIRM] = std::unique_ptr<Sample>(LoadSample("sfx_confirm.wav"));
|
||||
samples_[(size_t)UI::UISound::TOGGLE_ON] = std::unique_ptr<Sample>(LoadSample("sfx_toggle_on.wav"));
|
||||
samples_[(size_t)UI::UISound::TOGGLE_OFF] = std::unique_ptr<Sample>(LoadSample("sfx_toggle_off.wav"));
|
||||
|
||||
UI::SetSoundCallback([](UI::UISound sound) {
|
||||
g_BackgroundAudio.SFX().Play(sound);
|
||||
});
|
||||
}
|
||||
|
@ -10,6 +10,38 @@
|
||||
|
||||
class AT3PlusReader;
|
||||
|
||||
struct Sample {
|
||||
// data must be new-ed.
|
||||
Sample(int16_t *data, int length) : data_(data), length_(length) {}
|
||||
~Sample() {
|
||||
delete[] data_;
|
||||
}
|
||||
int16_t *data_;
|
||||
int length_; // stereo samples.
|
||||
};
|
||||
|
||||
// Mixer for things played on top of everything.
|
||||
class SoundEffectMixer {
|
||||
public:
|
||||
static Sample *LoadSample(const std::string &path);
|
||||
void LoadSamples();
|
||||
|
||||
void Mix(int16_t *buffer, int sz, int sampleRateHz);
|
||||
void Play(UI::UISound sfx);
|
||||
|
||||
std::vector<std::unique_ptr<Sample>> samples_;
|
||||
|
||||
struct PlayInstance {
|
||||
UI::UISound sound;
|
||||
int offset;
|
||||
int volume; // 0..255
|
||||
bool done;
|
||||
};
|
||||
|
||||
std::mutex mutex_;
|
||||
std::vector<PlayInstance> plays_;
|
||||
};
|
||||
|
||||
class BackgroundAudio {
|
||||
public:
|
||||
BackgroundAudio();
|
||||
@ -19,8 +51,9 @@ public:
|
||||
void Update();
|
||||
bool Play();
|
||||
|
||||
void LoadSamples();
|
||||
void PlaySFX(UI::UISound sfx);
|
||||
SoundEffectMixer &SFX() {
|
||||
return sfxMixer_;
|
||||
}
|
||||
|
||||
private:
|
||||
void Clear(bool hard);
|
||||
@ -33,35 +66,14 @@ private:
|
||||
Path bgGamePath_;
|
||||
std::atomic<bool> sndLoadPending_;
|
||||
int playbackOffset_ = 0;
|
||||
AT3PlusReader *at3Reader_;
|
||||
AT3PlusReader *at3Reader_ = nullptr;
|
||||
double gameLastChanged_ = 0.0;
|
||||
double lastPlaybackTime_ = 0.0;
|
||||
int *buffer = nullptr;
|
||||
bool fadingOut_ = true;
|
||||
float volume_ = 0.0f;
|
||||
float delta_ = -0.0001f;
|
||||
|
||||
struct PlayInstance {
|
||||
UI::UISound sound;
|
||||
int offset;
|
||||
int volume; // 0..255
|
||||
bool done;
|
||||
};
|
||||
|
||||
struct Sample {
|
||||
// data must be new-ed.
|
||||
Sample(int16_t *data, int length) : data_(data), length_(length) {}
|
||||
~Sample() {
|
||||
delete[] data_;
|
||||
}
|
||||
int16_t *data_;
|
||||
int length_; // stereo samples.
|
||||
};
|
||||
|
||||
static Sample *LoadSample(const std::string &path);
|
||||
|
||||
std::vector<PlayInstance> plays_;
|
||||
std::vector<std::unique_ptr<Sample>> samples_;
|
||||
SoundEffectMixer sfxMixer_;
|
||||
};
|
||||
|
||||
extern BackgroundAudio g_BackgroundAudio;
|
||||
|
@ -763,7 +763,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
|
||||
#endif
|
||||
|
||||
// TODO: Load these in the background instead of synchronously.
|
||||
g_BackgroundAudio.LoadSamples();
|
||||
g_BackgroundAudio.SFX().LoadSamples();
|
||||
|
||||
if (!boot_filename.empty() && stateToLoad.Valid()) {
|
||||
SaveState::Load(stateToLoad, -1, [](SaveState::Status status, const std::string &message, void *) {
|
||||
|
Loading…
Reference in New Issue
Block a user