Merge pull request #13239 from hrydgard/interface-audio

Add sound effects for PPSSPP interface navigation
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2020-08-03 23:08:32 +02:00
commit ed946edff4
25 files changed
+449 -242

No files matched your search

+1
View File
@@ -433,6 +433,7 @@ static ConfigSetting generalSettings[] = {
ConfigSetting("Language", &g_Config.sLanguageIni, &DefaultLangRegion),
ConfigSetting("ForceLagSync2", &g_Config.bForceLagSync, false, true, true),
ConfigSetting("DiscordPresence", &g_Config.bDiscordPresence, true, true, false), // Or maybe it makes sense to have it per-game? Race conditions abound...
ConfigSetting("UISound", &g_Config.bUISound, false, true, false),
ReportedConfigSetting("NumWorkerThreads", &g_Config.iNumWorkerThreads, &DefaultNumWorkers, true, true),
ConfigSetting("AutoLoadSaveState", &g_Config.iAutoLoadSaveState, 0, true, true),
+1
View File
@@ -196,6 +196,7 @@ public:
int iMaxRecent;
int iCurrentStateSlot;
int iRewindFlipFrequency;
bool bUISound;
bool bEnableStateUndo;
int iAutoLoadSaveState; // 0 = off, 1 = oldest, 2 = newest, >2 = slot number + 3
bool bEnableCheats;
+2 -2
View File
@@ -1882,11 +1882,11 @@ static u32 sceMpegAvcCopyYCbCr(u32 mpeg, u32 sourceAddr, u32 YCbCrAddr)
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(ME, "UNIMPL sceMpegAvcCopyYCbCr(%08x, %08x, %08x): bad mpeg handle", mpeg, sourceAddr, YCbCrAddr);
ERROR_LOG(ME, "UNIMPL sceMpegAvcCopyYCbCr(%08x, %08x, %08x): bad mpeg handle", mpeg, sourceAddr, YCbCrAddr);
return -1;
}
ERROR_LOG(ME, "UNIMPL sceMpegAvcCopyYCbCr(%08x, %08x, %08x)", mpeg, sourceAddr, YCbCrAddr);
WARN_LOG(ME, "UNIMPL sceMpegAvcCopyYCbCr(%08x, %08x, %08x)", mpeg, sourceAddr, YCbCrAddr);
return 0;
}
+1 -2
View File
@@ -1,7 +1,7 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#ifndef MOBILE_DEVICE
#include <string>
#include "Core/WaveFile.h"
@@ -105,4 +105,3 @@ void WaveFileWriter::AddStereoSamples(const short* sample_data, u32 count)
file.WriteBytes(sample_data, count * 4);
audio_size += count * 4;
}
#endif
-4
View File
@@ -11,7 +11,6 @@
// ---------------------------------------------------------------------------------
#pragma once
#ifndef MOBILE_DEVICE
#include <array>
#include <string>
@@ -40,6 +39,3 @@ private:
void Write(u32 value);
void Write4(const char* ptr);
};
#endif
+294 -212
View File
@@ -4,6 +4,8 @@
#include "base/logging.h"
#include "base/timeutil.h"
#include "file/chunk_file.h"
#include "file/vfs.h"
#include "ui/root.h"
#include "Common/CommonTypes.h"
#include "Core/HW/SimpleAudioDec.h"
@@ -11,173 +13,195 @@
#include "Common/FixedSizeQueue.h"
#include "GameInfoCache.h"
#include "Core/Config.h"
#include "UI/BackgroundAudio.h"
struct WavData {
int num_channels = -1;
int sample_rate = -1;
int numFrames = -1;
int samplesPerSec = -1;
int avgBytesPerSec = -1;
int raw_offset_loop_start = 0;
int raw_offset_loop_end = 0;
int loop_start_offset = 0;
int loop_end_offset = 0;
int codec = 0;
int raw_bytes_per_frame = 0;
uint8_t *raw_data = nullptr;
int raw_data_size = 0;
u8 at3_extradata[16];
void Read(RIFFReader &riff);
~WavData() {
free(raw_data);
raw_data = nullptr;
}
};
void WavData::Read(RIFFReader &file_) {
// If we have no loop start info, we'll just loop the entire audio.
raw_offset_loop_start = 0;
raw_offset_loop_end = 0;
if (file_.Descend('RIFF')) {
file_.ReadInt(); //get past 'WAVE'
if (file_.Descend('fmt ')) { //enter the format chunk
int temp = file_.ReadInt();
int format = temp & 0xFFFF;
switch (format) {
case 0xFFFE:
codec = PSP_CODEC_AT3PLUS;
break;
case 0x270:
codec = PSP_CODEC_AT3;
break;
case 1:
// Raw wave data, no codec
codec = 0;
break;
default:
ERROR_LOG(SCEAUDIO, "Unexpected wave format %04x", format);
return;
}
num_channels = temp >> 16;
samplesPerSec = file_.ReadInt();
/*avgBytesPerSec =*/ file_.ReadInt();
temp = file_.ReadInt();
raw_bytes_per_frame = temp & 0xFFFF;
if (codec == PSP_CODEC_AT3) {
// The first two bytes are actually not a useful part of the extradata.
// We already read 16 bytes, so make sure there's enough left.
if (file_.GetCurrentChunkSize() >= 32) {
file_.ReadData(at3_extradata, 16);
} else {
memset(at3_extradata, 0, sizeof(at3_extradata));
}
}
file_.Ascend();
// ILOG("got fmt data: %i", samplesPerSec);
} else {
ELOG("Error - no format chunk in wav");
file_.Ascend();
return;
}
if (file_.Descend('smpl')) {
std::vector<u8> smplData;
smplData.resize(file_.GetCurrentChunkSize());
file_.ReadData(&smplData[0], (int)smplData.size());
int numLoops = *(int *)&smplData[28];
struct AtracLoopInfo {
int cuePointID;
int type;
int startSample;
int endSample;
int fraction;
int playCount;
};
if (numLoops > 0 && smplData.size() >= 36 + sizeof(AtracLoopInfo) * numLoops) {
AtracLoopInfo *loops = (AtracLoopInfo *)&smplData[36];
int samplesPerFrame = codec == PSP_CODEC_AT3PLUS ? 2048 : 1024;
for (int i = 0; i < numLoops; ++i) {
// Only seen forward loops, so let's ignore others.
if (loops[i].type != 0)
continue;
// We ignore loop interpolation (fraction) and play count for now.
raw_offset_loop_start = (loops[i].startSample / samplesPerFrame) * raw_bytes_per_frame;
loop_start_offset = loops[i].startSample % samplesPerFrame;
raw_offset_loop_end = (loops[i].endSample / samplesPerFrame) * raw_bytes_per_frame;
loop_end_offset = loops[i].endSample % samplesPerFrame;
if (loops[i].playCount == 0) {
// This was an infinite loop, so ignore the rest.
// In practice, there's usually only one and it's usually infinite.
break;
}
}
}
file_.Ascend();
}
// enter the data chunk
if (file_.Descend('data')) {
int numBytes = file_.GetCurrentChunkSize();
numFrames = numBytes / raw_bytes_per_frame; // numFrames
raw_data = (uint8_t *)malloc(numBytes);
raw_data_size = numBytes;
if (num_channels == 1 || num_channels == 2) {
file_.ReadData(raw_data, numBytes);
} else {
ELOG("Error - bad blockalign or channels");
free(raw_data);
raw_data = nullptr;
return;
}
file_.Ascend();
} else {
ELOG("Error - no data chunk in wav");
file_.Ascend();
return;
}
file_.Ascend();
} else {
ELOG("Could not descend into RIFF file.");
return;
}
sample_rate = samplesPerSec;
}
// Really simple looping in-memory AT3 player that also takes care of reading the file format.
// Turns out that AT3 files used for this are modified WAVE files so fairly easy to parse.
class AT3PlusReader {
public:
AT3PlusReader(const std::string &data)
explicit AT3PlusReader(const std::string &data)
: file_((const uint8_t *)&data[0], (int32_t)data.size()) {
// Normally 8k but let's be safe.
buffer_ = new short[32 * 1024];
int codec = PSP_CODEC_AT3PLUS;
u8 at3_extradata[16];
skip_next_samples_ = 0;
int num_channels, sample_rate, numFrames, samplesPerSec, avgBytesPerSec, Nothing;
if (file_.Descend('RIFF')) {
file_.ReadInt(); //get past 'WAVE'
if (file_.Descend('fmt ')) { //enter the format chunk
int temp = file_.ReadInt();
int format = temp & 0xFFFF;
switch (format) {
case 0xFFFE:
codec = PSP_CODEC_AT3PLUS;
break;
case 0x270:
codec = PSP_CODEC_AT3;
break;
default:
ERROR_LOG(SCEAUDIO, "Unexpected SND0.AT3 format %04x", format);
return;
}
wave_.Read(file_);
num_channels = temp >> 16;
samplesPerSec = file_.ReadInt();
avgBytesPerSec = file_.ReadInt();
temp = file_.ReadInt();
raw_bytes_per_frame_ = temp & 0xFFFF;
Nothing = temp >> 16;
// Not currently used, but part of the format.
(void)avgBytesPerSec;
(void)Nothing;
if (codec == PSP_CODEC_AT3) {
// The first two bytes are actually not a useful part of the extradata.
// We already read 16 bytes, so make sure there's enough left.
if (file_.GetCurrentChunkSize() >= 32) {
file_.ReadData(at3_extradata, 16);
} else {
memset(at3_extradata, 0, sizeof(at3_extradata));
}
}
file_.Ascend();
// ILOG("got fmt data: %i", samplesPerSec);
} else {
ELOG("Error - no format chunk in wav");
file_.Ascend();
return;
}
// If we have no loop info, we'll just loop the entire audio.
raw_offset_loop_start_ = 0;
raw_offset_loop_end_ = 0;
skip_next_samples_ = 0;
if (file_.Descend('smpl')) {
std::vector<u8> smplData;
smplData.resize(file_.GetCurrentChunkSize());
file_.ReadData(&smplData[0], (int)smplData.size());
int numLoops = *(int *)&smplData[28];
struct AtracLoopInfo {
int cuePointID;
int type;
int startSample;
int endSample;
int fraction;
int playCount;
};
if (numLoops > 0 && smplData.size() >= 36 + sizeof(AtracLoopInfo) * numLoops) {
AtracLoopInfo *loops = (AtracLoopInfo *)&smplData[36];
int samplesPerFrame = codec == PSP_CODEC_AT3PLUS ? 2048 : 1024;
for (int i = 0; i < numLoops; ++i) {
// Only seen forward loops, so let's ignore others.
if (loops[i].type != 0)
continue;
// We ignore loop interpolation (fraction) and play count for now.
raw_offset_loop_start_ = (loops[i].startSample / samplesPerFrame) * raw_bytes_per_frame_;
loop_start_offset_ = loops[i].startSample % samplesPerFrame;
raw_offset_loop_end_ = (loops[i].endSample / samplesPerFrame) * raw_bytes_per_frame_;
loop_end_offset_ = loops[i].endSample % samplesPerFrame;
if (loops[i].playCount == 0) {
// This was an infinite loop, so ignore the rest.
// In practice, there's usually only one and it's usually infinite.
break;
}
}
}
file_.Ascend();
}
if (file_.Descend('data')) { //enter the data chunk
int numBytes = file_.GetCurrentChunkSize();
numFrames = numBytes / raw_bytes_per_frame_; // numFrames
raw_data_ = (uint8_t *)malloc(numBytes);
raw_data_size_ = numBytes;
if (num_channels == 1 || num_channels == 2) {
file_.ReadData(raw_data_, numBytes);
} else {
ELOG("Error - bad blockalign or channels");
free(raw_data_);
raw_data_ = nullptr;
return;
}
file_.Ascend();
} else {
ELOG("Error - no data chunk in wav");
file_.Ascend();
return;
}
file_.Ascend();
} else {
ELOG("Could not descend into RIFF file. Data size=%d", (int32_t)data.size());
return;
decoder_ = new SimpleAudio(wave_.codec, wave_.sample_rate, wave_.num_channels);
if (wave_.codec == PSP_CODEC_AT3) {
decoder_->SetExtraData(&wave_.at3_extradata[2], 14, wave_.raw_bytes_per_frame);
}
sample_rate = samplesPerSec;
decoder_ = new SimpleAudio(codec, sample_rate, num_channels);
if (codec == PSP_CODEC_AT3) {
decoder_->SetExtraData(&at3_extradata[2], 14, raw_bytes_per_frame_);
}
ILOG("read ATRAC, frames: %i, rate %i", numFrames, sample_rate);
ILOG("read ATRAC, frames: %d, rate %d", wave_.numFrames, wave_.sample_rate);
}
~AT3PlusReader() {
}
void Shutdown() {
free(raw_data_);
raw_data_ = nullptr;
delete[] buffer_;
buffer_ = nullptr;
delete decoder_;
decoder_ = nullptr;
}
bool IsOK() { return raw_data_ != nullptr; }
bool IsOK() { return wave_.raw_data != nullptr; }
bool Read(int *buffer, int len) {
if (!raw_data_)
if (!wave_.raw_data)
return false;
while (bgQueue.size() < (size_t)(len * 2)) {
int outBytes = 0;
decoder_->Decode(raw_data_ + raw_offset_, raw_bytes_per_frame_, (uint8_t *)buffer_, &outBytes);
decoder_->Decode(wave_.raw_data + raw_offset_, wave_.raw_bytes_per_frame, (uint8_t *)buffer_, &outBytes);
if (!outBytes)
return false;
if (raw_offset_loop_end_ != 0 && raw_offset_ == raw_offset_loop_end_) {
if (wave_.raw_offset_loop_end != 0 && raw_offset_ == wave_.raw_offset_loop_end) {
// Only take the remaining bytes, but convert to stereo s16.
outBytes = std::min(outBytes, loop_end_offset_ * 4);
outBytes = std::min(outBytes, wave_.loop_end_offset * 4);
}
int start = skip_next_samples_;
@@ -187,16 +211,16 @@ public:
bgQueue.push(buffer_[i]);
}
if (raw_offset_loop_end_ != 0 && raw_offset_ == raw_offset_loop_end_) {
if (wave_.raw_offset_loop_end != 0 && raw_offset_ == wave_.raw_offset_loop_end) {
// Time to loop. Account for the addition below.
raw_offset_ = raw_offset_loop_start_ - raw_bytes_per_frame_;
raw_offset_ = wave_.raw_offset_loop_start - wave_.raw_bytes_per_frame;
// This time we're counting each stereo sample.
skip_next_samples_ = loop_start_offset_ * 2;
skip_next_samples_ = wave_.loop_start_offset * 2;
}
// Handle loops when there's no loop info.
raw_offset_ += raw_bytes_per_frame_;
if (raw_offset_ >= raw_data_size_) {
raw_offset_ += wave_.raw_bytes_per_frame;
if (raw_offset_ >= wave_.raw_data_size) {
raw_offset_ = 0;
}
}
@@ -209,123 +233,181 @@ public:
private:
RIFFReader file_;
uint8_t *raw_data_ = nullptr;
int raw_data_size_ = 0;
WavData wave_;
int raw_offset_ = 0;
int raw_bytes_per_frame_;
int raw_offset_loop_start_ = 0;
int raw_offset_loop_end_ = 0;
int loop_start_offset_ = 0;
int loop_end_offset_ = 0;
int skip_next_samples_ = 0;
FixedSizeQueue<s16, 128 * 1024> bgQueue;
short *buffer_ = nullptr;
SimpleAudio *decoder_ = nullptr;
};
static std::mutex g_bgMutex;
static std::string bgGamePath;
static int playbackOffset;
static AT3PlusReader *at3Reader;
static double gameLastChanged;
static double lastPlaybackTime;
static int buffer[44100];
static bool fadingOut = true;
static float volume;
static float delta = -0.0001f;
BackgroundAudio g_BackgroundAudio;
static void ClearBackgroundAudio(bool hard) {
if (!hard) {
fadingOut = true;
volume = 1.0f;
return;
}
if (at3Reader) {
at3Reader->Shutdown();
delete at3Reader;
at3Reader = nullptr;
}
playbackOffset = 0;
BackgroundAudio::BackgroundAudio() {
buffer = new int[BUFSIZE]();
}
void SetBackgroundAudioGame(const std::string &path) {
BackgroundAudio::~BackgroundAudio() {
delete[] buffer;
}
BackgroundAudio::Sample *BackgroundAudio::LoadSample(const std::string &path) {
size_t bytes;
uint8_t *data = VFSReadFile(path.c_str(), &bytes);
if (!data) {
return nullptr;
}
RIFFReader reader(data, (int)bytes);
WavData wave;
wave.Read(reader);
if (wave.num_channels != 2 || wave.sample_rate != 44100 || wave.raw_bytes_per_frame != 4) {
ELOG("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;
volume_ = 1.0f;
return;
}
if (at3Reader_) {
delete at3Reader_;
at3Reader_ = nullptr;
}
playbackOffset_ = 0;
}
void BackgroundAudio::SetGame(const std::string &path) {
time_update();
std::lock_guard<std::mutex> lock(g_bgMutex);
if (path == bgGamePath) {
std::lock_guard<std::mutex> lock(mutex_);
if (path == bgGamePath_) {
// Do nothing
return;
}
if (path.size() == 0) {
ClearBackgroundAudio(false);
fadingOut = true;
Clear(false);
fadingOut_ = true;
} else {
ClearBackgroundAudio(true);
gameLastChanged = time_now_d();
fadingOut = false;
Clear(true);
gameLastChanged_ = time_now_d();
fadingOut_ = false;
}
volume = 1.0f;
bgGamePath = path;
volume_ = 1.0f;
bgGamePath_ = path;
}
int PlayBackgroundAudio() {
int BackgroundAudio::Play() {
time_update();
std::lock_guard<std::mutex> lock(g_bgMutex);
std::lock_guard<std::mutex> lock(mutex_);
// Immediately stop the sound if it is turned off while playing.
if (!g_Config.bEnableSound) {
ClearBackgroundAudio(true);
Clear(true);
__PushExternalAudio(0, 0);
return 0;
}
double now = time_now();
if (at3Reader) {
int sz = lastPlaybackTime <= 0.0 ? 44100 / 60 : (int)((now - lastPlaybackTime) * 44100);
sz = std::min((int)ARRAY_SIZE(buffer) / 2, sz);
double now = time_now_d();
int sz = lastPlaybackTime_ <= 0.0 ? 44100 / 60 : (int)((now - lastPlaybackTime_) * 44100);
sz = std::min(BUFSIZE / 2, sz);
if (at3Reader_) {
if (sz >= 16) {
if (at3Reader->Read(buffer, sz)) {
if (!fadingOut) {
__PushExternalAudio(buffer, sz);
} else {
if (at3Reader_->Read(buffer, sz)) {
if (fadingOut_) {
for (int i = 0; i < sz*2; i += 2) {
buffer[i] *= volume;
buffer[i + 1] *= volume;
volume += delta;
}
__PushExternalAudio(buffer, sz);
if (volume <= 0.0f) {
ClearBackgroundAudio(true);
fadingOut = false;
gameLastChanged = 0;
buffer[i] *= volume_;
buffer[i + 1] *= volume_;
volume_ += delta_;
}
}
}
lastPlaybackTime = now;
}
} else {
__PushExternalAudio(0, 0);
lastPlaybackTime = now;
for (int i = 0; i < sz * 2; i += 2) {
buffer[i] = 0;
buffer[i + 1] = 0;
}
}
// 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++;
}
}
}
}
__PushExternalAudio(buffer, sz);
if (at3Reader_ && fadingOut_ && volume_ <= 0.0f) {
Clear(true);
fadingOut_ = false;
gameLastChanged_ = 0;
}
lastPlaybackTime_ = now;
return 0;
}
// Stuff that should be on the UI thread only, like anything to do with
// g_gameInfoCache.
void UpdateBackgroundAudio() {
void BackgroundAudio::Update() {
// If there's a game, and some time has passed since the selected game
// last changed... (to prevent crazy amount of reads when skipping through a list)
if (bgGamePath.size() && (time_now_d() - gameLastChanged > 0.5)) {
std::lock_guard<std::mutex> lock(g_bgMutex);
if (!at3Reader) {
if (bgGamePath_.size() && (time_now_d() - gameLastChanged_ > 0.5)) {
std::lock_guard<std::mutex> lock(mutex_);
if (!at3Reader_) {
// Grab some audio from the current game and play it.
if (!g_gameInfoCache)
return;
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(NULL, bgGamePath, GAMEINFO_WANTSND);
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(NULL, bgGamePath_, GAMEINFO_WANTSND);
if (!gameInfo)
return;
@@ -336,8 +418,8 @@ void UpdateBackgroundAudio() {
if (gameInfo->sndFileData.size()) {
const std::string &data = gameInfo->sndFileData;
at3Reader = new AT3PlusReader(data);
lastPlaybackTime = 0.0;
at3Reader_ = new AT3PlusReader(data);
lastPlaybackTime_ = 0.0;
}
}
}
+59 -3
View File
@@ -1,7 +1,63 @@
#pragma once
#include <string>
#include <mutex>
#include <vector>
void SetBackgroundAudioGame(const std::string &path);
int PlayBackgroundAudio();
void UpdateBackgroundAudio();
#include "ui/root.h"
class AT3PlusReader;
class BackgroundAudio {
public:
BackgroundAudio();
~BackgroundAudio();
void Clear(bool hard);
void SetGame(const std::string &path);
void Update();
int Play();
void LoadSamples();
void PlaySFX(UI::UISound sfx);
private:
enum {
BUFSIZE = 44100,
};
std::mutex mutex_;
std::string bgGamePath_;
int playbackOffset_ = 0;
AT3PlusReader *at3Reader_;
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_;
};
extern BackgroundAudio g_BackgroundAudio;
+1 -1
View File
@@ -193,7 +193,7 @@ void EmuScreen::bootGame(const std::string &filename) {
return;
}
SetBackgroundAudioGame("");
g_BackgroundAudio.SetGame("");
// Check permission status first, in case we came from a shortcut.
if (!bootAllowStorage(filename))
+1 -1
View File
@@ -38,7 +38,7 @@
#include "UI/BackgroundAudio.h"
GameScreen::GameScreen(const std::string &gamePath) : UIDialogScreenWithGameBackground(gamePath) {
SetBackgroundAudioGame(gamePath);
g_BackgroundAudio.SetGame(gamePath);
}
GameScreen::~GameScreen() {
+2 -2
View File
@@ -816,8 +816,9 @@ void GameSettingsScreen::CreateViews() {
systemSettingsScroll->Add(systemSettings);
tabHolder->AddTab(ms->T("System"), systemSettingsScroll);
systemSettings->Add(new ItemHeader(sy->T("UI Language")));
systemSettings->Add(new ItemHeader(sy->T("UI Language"))); // Should be renamed "UI"?
systemSettings->Add(new Choice(dev->T("Language", "Language")))->OnClick.Handle(this, &GameSettingsScreen::OnLanguage);
systemSettings->Add(new CheckBox(&g_Config.bUISound, dev->T("UI Sound")));
systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team")));
enableReports_ = Reporting::IsEnabled();
@@ -859,7 +860,6 @@ void GameSettingsScreen::CreateViews() {
}
}
#endif
systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP")));
const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png";
const std::string bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg";
+5 -5
View File
@@ -935,12 +935,12 @@ UI::EventReturn GameBrowser::OnHomebrewStore(UI::EventParams &e) {
MainScreen::MainScreen() {
System_SendMessage("event", "mainscreen");
SetBackgroundAudioGame("");
g_BackgroundAudio.SetGame("");
lastVertical_ = UseVerticalLayout();
}
MainScreen::~MainScreen() {
SetBackgroundAudioGame("");
g_BackgroundAudio.SetGame("");
}
void MainScreen::CreateViews() {
@@ -1283,7 +1283,7 @@ UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) {
// Restore focus if it was highlighted (e.g. by gamepad.)
restoreFocusGamePath_ = highlightedGamePath_;
SetBackgroundAudioGame(path);
g_BackgroundAudio.SetGame(path);
lockBackgroundAudio_ = true;
screenManager()->push(new GameScreen(path));
return UI::EVENT_DONE;
@@ -1314,7 +1314,7 @@ UI::EventReturn MainScreen::OnGameHighlight(UI::EventParams &e) {
}
if ((!highlightedGamePath_.empty() || e.a == FF_LOSTFOCUS) && !lockBackgroundAudio_) {
SetBackgroundAudioGame(highlightedGamePath_);
g_BackgroundAudio.SetGame(highlightedGamePath_);
}
lockBackgroundAudio_ = false;
@@ -1401,7 +1401,7 @@ void MainScreen::dialogFinished(const Screen *dialog, DialogResult result) {
restoreFocusGamePath_.clear();
} else {
// Not refocusing, so we need to stop the audio.
SetBackgroundAudioGame("");
g_BackgroundAudio.SetGame("");
}
}
}
+7 -2
View File
@@ -271,7 +271,7 @@ std::string NativeQueryConfig(std::string query) {
int NativeMix(short *audio, int num_samples) {
if (GetUIState() != UISTATE_INGAME) {
PlayBackgroundAudio();
g_BackgroundAudio.Play();
}
int sample_rate = System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE);
@@ -446,6 +446,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
g_Discord.SetPresenceMenu();
// TODO: Load these in the background instead of synchronously.
g_BackgroundAudio.LoadSamples();
// Make sure UI state is MENU.
ResetUIState();
@@ -1040,7 +1043,7 @@ void NativeRender(GraphicsContext *graphicsContext) {
if (GetUIState() != UISTATE_INGAME) {
// Note: We do this from NativeRender so that the graphics context is
// guaranteed valid, to be safe - g_gameInfoCache messes around with textures.
UpdateBackgroundAudio();
g_BackgroundAudio.Update();
}
float xres = dp_xres;
@@ -1205,6 +1208,8 @@ void NativeUpdate() {
screenManager->update();
g_Discord.Update();
UI::SetSoundEnabled(g_Config.bUISound);
}
bool NativeIsAtTopLevel() {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -285,6 +285,7 @@ static bool IsLocalPath(const char *path) {
return isUnixLocal || isWindowsLocal;
}
// The returned data should be free'd with delete[].
uint8_t *VFSReadFile(const char *filename, size_t *size) {
if (IsLocalPath(filename)) {
// Local path, not VFS.
+2 -2
View File
@@ -48,7 +48,7 @@ private:
class DirectoryAssetReader : public AssetReader {
public:
DirectoryAssetReader(const char *path) {
explicit DirectoryAssetReader(const char *path) {
strncpy(path_, path, ARRAY_SIZE(path_));
path_[ARRAY_SIZE(path_) - 1] = '\0';
}
@@ -61,6 +61,6 @@ public:
}
private:
char path_[512];
char path_[512]{};
};
+21
View File
@@ -2,6 +2,7 @@
#include <deque>
#include "ppsspp_config.h"
#include "base/timeutil.h"
#include "ui/root.h"
#include "ui/viewgroup.h"
@@ -17,6 +18,10 @@ static bool focusMovementEnabled;
bool focusForced;
static std::mutex eventMutex_;
static std::function<void(UISound)> soundCallback;
static bool soundEnabled = true;
struct DispatchQueueItem {
Event *e;
EventParams params;
@@ -133,6 +138,22 @@ void MoveFocus(ViewGroup *root, FocusDirection direction) {
if (neigh.view) {
neigh.view->SetFocus();
root->SubviewFocused(neigh.view);
PlayUISound(UISound::SELECT);
}
}
void SetSoundEnabled(bool enabled) {
soundEnabled = enabled;
}
void SetSoundCallback(std::function<void(UISound)> func) {
soundCallback = func;
}
void PlayUISound(UISound sound) {
if (soundEnabled && soundCallback) {
soundCallback(sound);
}
}
+16
View File
@@ -1,5 +1,7 @@
#pragma once
#include <functional>
#include "ui/ui_context.h"
#include "input/input_state.h"
@@ -26,4 +28,18 @@ bool KeyEvent(const KeyInput &key, ViewGroup *root);
bool TouchEvent(const TouchInput &touch, ViewGroup *root);
bool AxisEvent(const AxisInput &axis, ViewGroup *root);
enum class UISound {
SELECT = 0,
BACK,
CONFIRM,
TOGGLE_ON,
TOGGLE_OFF,
COUNT,
};
void SetSoundEnabled(bool enabled);
void SetSoundCallback(std::function<void(UISound)> func);
void PlayUISound(UISound sound);
} // namespace UI
+1
View File
@@ -168,6 +168,7 @@ bool UIDialogScreen::key(const KeyInput &key) {
} else {
finished_ = true;
TriggerFinish(DR_BACK);
UI::PlayUISound(UI::UISound::BACK);
}
return true;
}
+21 -2
View File
@@ -376,6 +376,7 @@ bool StickyChoice::Key(const KeyInput &key) {
if (key.flags & KEY_DOWN) {
if (IsAcceptKey(key)) {
down_ = true;
UI::PlayUISound(UI::UISound::TOGGLE_ON);
Click();
return true;
}
@@ -424,6 +425,11 @@ void ClickableItem::Draw(UIContext &dc) {
DrawBG(dc, style);
}
void Choice::Click() {
ClickableItem::Click();
UI::PlayUISound(UI::UISound::CONFIRM);
}
void Choice::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const {
if (atlasImage_.isValid()) {
dc.Draw()->GetAtlas()->measureImage(atlasImage_, &w, &h);
@@ -596,8 +602,10 @@ void PopupHeader::Draw(UIContext &dc) {
}
void CheckBox::Toggle() {
if (toggle_)
if (toggle_) {
*toggle_ = !(*toggle_);
UI::PlayUISound(*toggle_ ? UI::UISound::TOGGLE_ON : UI::UISound::TOGGLE_OFF);
}
}
bool CheckBox::Toggled() const {
@@ -668,8 +676,14 @@ void CheckBox::GetContentDimensions(const UIContext &dc, float &w, float &h) con
}
void BitCheckBox::Toggle() {
if (bitfield_)
if (bitfield_) {
*bitfield_ = *bitfield_ ^ bit_;
if (*bitfield_ & bit_) {
UI::PlayUISound(UI::UISound::TOGGLE_ON);
} else {
UI::PlayUISound(UI::UISound::TOGGLE_OFF);
}
}
}
bool BitCheckBox::Toggled() const {
@@ -689,6 +703,11 @@ void Button::GetContentDimensions(const UIContext &dc, float &w, float &h) const
h += paddingH_;
}
void Button::Click() {
Clickable::Click();
UI::PlayUISound(UI::UISound::CONFIRM);
}
void Button::Draw(UIContext &dc) {
Style style = dc.theme->buttonStyle;
+2
View File
@@ -519,6 +519,7 @@ public:
Button(const std::string &text, ImageID imageID, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), text_(text), imageID_(imageID) {}
void Click() override;
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
const std::string &GetText() const { return text_; }
@@ -647,6 +648,7 @@ public:
Choice(ImageID image, LayoutParams *layoutParams = nullptr)
: ClickableItem(layoutParams), atlasImage_(image), iconImage_(ImageID::invalid()), centered_(false), highlighted_(false), selected_(false) {}
virtual void Click();
virtual void HighlightChanged(bool highlighted);
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;
+11 -4
View File
@@ -9,6 +9,7 @@
#include "math/curves.h"
#include "ui/ui_context.h"
#include "ui/ui_tween.h"
#include "ui/root.h"
#include "ui/view.h"
#include "ui/viewgroup.h"
#include "gfx_es2/draw_buffer.h"
@@ -1321,11 +1322,17 @@ void ChoiceStrip::HighlightChoice(unsigned int choice){
bool ChoiceStrip::Key(const KeyInput &input) {
bool ret = false;
if (input.flags & KEY_DOWN) {
if (IsTabLeftKey(input) && selected_ > 0) {
SetSelection(selected_ - 1);
if (IsTabLeftKey(input)) {
if (selected_ > 0) {
SetSelection(selected_ - 1);
UI::PlayUISound(UI::UISound::TOGGLE_OFF); // Maybe make specific sounds for this at some point?
}
ret = true;
} else if (IsTabRightKey(input) && selected_ < (int)views_.size() - 1) {
SetSelection(selected_ + 1);
} else if (IsTabRightKey(input)) {
if (selected_ < (int)views_.size() - 1) {
SetSelection(selected_ + 1);
UI::PlayUISound(UI::UISound::TOGGLE_ON);
}
ret = true;
}
}