From cefc0bc96f33443dac9566e6499ccf8481acedea Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 22 Jun 2014 17:02:04 +0200 Subject: [PATCH] Play the game's SND0.AT3 when a game is selected in the menu and on the "game screen". --- CMakeLists.txt | 1 + Common/ChunkFile.h | 1 + Core/HLE/sceAudiocodec.cpp | 9 +- Core/HLE/sceMp3.cpp | 4 +- Core/HW/SimpleAudioDec.cpp | 20 +--- Core/HW/SimpleAudioDec.h | 18 ++-- Qt/PPSSPP.pro | 1 + UI/BackgroundAudio.cpp | 186 +++++++++++++++++++++++++++++++++++++ UI/BackgroundAudio.h | 6 ++ UI/GameScreen.cpp | 10 ++ UI/GameScreen.h | 3 +- UI/MainScreen.cpp | 9 ++ UI/MainScreen.h | 1 + UI/NativeApp.cpp | 5 +- UI/UI.vcxproj | 2 + UI/UI.vcxproj.filters | 4 +- 16 files changed, 245 insertions(+), 35 deletions(-) create mode 100644 UI/BackgroundAudio.cpp create mode 100644 UI/BackgroundAudio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d7a2e229f..381f724932 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1401,6 +1401,7 @@ endif() set(NativeAppSource UI/NativeApp.cpp + UI/BackgroundAudio.cpp UI/DevScreens.cpp UI/EmuScreen.cpp android/jni/TestRunner.cpp diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 8dec9d211b..fa8efce3be 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -18,6 +18,7 @@ #pragma once // Extremely simple serialization framework. +// Currently mis-named, a native ChunkFile is something different (a RIFF file) // (mis)-features: // + Super fast diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index 5773d72958..e5ffd49275 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -81,7 +81,8 @@ int sceAudiocodecInit(u32 ctxPtr, int codec) { if (removeDecoder(ctxPtr)) { WARN_LOG_REPORT(HLE, "sceAudiocodecInit(%08x, %d): replacing existing context", ctxPtr, codec); } - auto decoder = new SimpleAudio(ctxPtr, codec); + auto decoder = new SimpleAudio(codec); + decoder->SetCtxPtr(ctxPtr); audioList[ctxPtr] = decoder; INFO_LOG(ME, "sceAudiocodecInit(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); DEBUG_LOG(ME, "Number of playing sceAudioCodec audios : %d", (int)audioList.size()); @@ -107,7 +108,8 @@ int sceAudiocodecDecode(u32 ctxPtr, int codec) { if (!decoder && oldStateLoaded) { // We must have loaded an old state that did not have sceAudiocodec information. // Fake it by creating the desired context. - decoder = new SimpleAudio(ctxPtr, codec); + decoder = new SimpleAudio(codec); + decoder->SetCtxPtr(ctxPtr); audioList[ctxPtr] = decoder; } @@ -181,7 +183,8 @@ void __sceAudiocodecDoState(PointerWrap &p){ p.DoArray(codec_, s >= 2 ? count : (int)ARRAY_SIZE(codec_)); p.DoArray(ctxPtr_, s >= 2 ? count : (int)ARRAY_SIZE(ctxPtr_)); for (int i = 0; i < count; i++) { - auto decoder = new SimpleAudio(ctxPtr_[i], codec_[i]); + auto decoder = new SimpleAudio(codec_[i]); + decoder->SetCtxPtr(ctxPtr_[i]); audioList[ctxPtr_[i]] = decoder; } delete[] codec_; diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index c986fec08e..0c3e9c80e3 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -333,7 +333,7 @@ int sceMp3Init(u32 mp3) { // for mp3, if required freq is 48000, reset resampling Frequency to 48000 seems get better sound quality (e.g. Miku Custom BGM) if (ctx->freq == 48000) { - ctx->decoder->setResampleFrequency(ctx->freq); + ctx->decoder->SetResampleFrequency(ctx->freq); } // For mp3 file, if ID3 tag is detected, we must move startPos to 0x400 (stream start position), remove 0x400 bytes of the sourcebuff, and reduce the available buffer size by 0x400 @@ -557,7 +557,7 @@ u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumedAddr, u int outpcmbytes = 0; ctx->decoder->Decode((void*)inbuff, 4096, outbuff, &outpcmbytes); - Memory::Write_U32(ctx->decoder->getSourcePos(), sourceBytesConsumedAddr); + Memory::Write_U32(ctx->decoder->GetSourcePos(), sourceBytesConsumedAddr); Memory::Write_U32(outpcmbytes, sampleBytesAddr); return 0; } diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index 843257f612..55adecfda4 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -61,13 +61,7 @@ bool SimpleAudio::GetAudioCodecID(int audioType) { } SimpleAudio::SimpleAudio(int audioType) -: codec_(0), codecCtx_(0), swrCtx_(0), audioType(audioType), outSamples(0), wanted_resample_freq(44100) { - Init(); -} - - -SimpleAudio::SimpleAudio(u32 ctxPtr, int audioType) -: codec_(0), codecCtx_(0), swrCtx_(0), ctxPtr(ctxPtr), audioType(audioType), outSamples(0), wanted_resample_freq(44100) { +: codec_(0), codecCtx_(0), swrCtx_(0), ctxPtr(0xFFFFFFFF), audioType(audioType), outSamples(0), srcPos(0), wanted_resample_freq(44100) { Init(); } @@ -240,18 +234,14 @@ bool SimpleAudio::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbyte #endif // USE_FFMPEG } -int SimpleAudio::getOutSamples(){ +int SimpleAudio::GetOutSamples(){ return outSamples; } -int SimpleAudio::getSourcePos(){ +int SimpleAudio::GetSourcePos(){ return srcPos; } -void SimpleAudio::setResampleFrequency(int freq){ - wanted_resample_freq = freq; -} - void AudioClose(SimpleAudio **ctx) { #ifdef USE_FFMPEG delete *ctx; @@ -349,9 +339,9 @@ u32 AuCtx::AuDecode(u32 pcmAddr) // count total output pcm size outpcmbufsize += pcmframesize; // count total output samples - SumDecodedSamples += decoder->getOutSamples(); + SumDecodedSamples += decoder->GetOutSamples(); // get consumed source length - int srcPos = decoder->getSourcePos(); + int srcPos = decoder->GetSourcePos(); // remove the consumed source sourcebuff.erase(0, srcPos); // reduce the available Aubuff size diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index 0df5252cb6..34d3900c98 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -33,11 +33,6 @@ struct SwrContext; // Based on http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html#_a13 -// Ideally, Maxim's Atrac3+ decoder would be available as a standalone library -// that we could link, as that would be totally sufficient for the use case here. -// However, it will be maintained as a part of FFMPEG so that's the way we'll go -// for simplicity and sanity. - // audioType enum { PSP_CODEC_AT3PLUS = 0x00001000, @@ -49,19 +44,22 @@ enum { class SimpleAudio { public: SimpleAudio(int audioType); - SimpleAudio(u32 ctxPtr, int audioType); ~SimpleAudio(); bool Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbytes); bool IsOK() const; - int getOutSamples(); - int getSourcePos(); + + int GetOutSamples(); + int GetSourcePos(); bool ResetCodecCtx(int channels, int samplerate); - void setResampleFrequency(int freq); bool GetAudioCodecID(int audioType); // Get audioCodecId from audioType // These two are only here because of save states. int GetAudioType() const { return audioType; } + void SetResampleFrequency(int freq) { wanted_resample_freq = freq; } + + // Just metadata. + void SetCtxPtr(u32 ptr) { ctxPtr = ptr; } u32 GetCtxPtr() const { return ctxPtr; } private: @@ -73,13 +71,11 @@ private: int srcPos; // bytes consumed in source during the last decoding int wanted_resample_freq; // wanted resampling rate/frequency -#ifdef USE_FFMPEG AVFrame *frame_; AVCodec *codec_; AVCodecContext *codecCtx_; SwrContext *swrCtx_; int audioCodecId; // AV_CODEC_ID_XXX -#endif // USE_FFMPEG }; void AudioClose(SimpleAudio **ctx); diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 67d06fe679..d83e211688 100644 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -91,6 +91,7 @@ symbian { # UI SOURCES += $$P/UI/*Screen.cpp \ $$P/UI/*Screens.cpp \ + $$P/UI/BackgroundAudio.cpp \ $$P/UI/Store.cpp \ $$P/UI/GamepadEmu.cpp \ $$P/UI/GameInfoCache.cpp \ diff --git a/UI/BackgroundAudio.cpp b/UI/BackgroundAudio.cpp new file mode 100644 index 0000000000..a7d51eaac0 --- /dev/null +++ b/UI/BackgroundAudio.cpp @@ -0,0 +1,186 @@ +#include +#include "base/logging.h" +#include "base/timeutil.h" +#include "native/file/chunk_file.h" + +#include "Common/CommonTypes.h" +#include "Core/HW/SimpleAudioDec.h" +#include "Common/FixedSizeQueue.h" +#include "GameInfoCache.h" + +// 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) + : data_(data), + raw_offset_(0), + file_((const uint8_t *)&data[0], + (int32_t)data.size()), + raw_data_(0), + raw_data_size_(0), + buffer_(0), + decoder_(0) { + + // Normally 8k but let's be safe. + buffer_ = new short[32 * 1024]; + + int codec = PSP_CODEC_AT3PLUS; + + 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: + // Dunno? 3rd Birthday has this. + default: + return; + } + + num_channels = temp >> 16; + + samplesPerSec = file_.readInt(); + avgBytesPerSec = file_.readInt(); + + temp = file_.readInt(); + raw_bytes_per_frame_ = temp & 0xFFFF; + Nothing = temp >> 16; + file_.ascend(); + // ILOG("got fmt data: %i", samplesPerSec); + } else { + ELOG("Error - no format chunk in wav"); + file_.ascend(); + return; + } + + 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 (/*raw_bytes_per_frame_ == 280 && */ num_channels == 2) { + file_.readData(raw_data_, numBytes); + } else { + ELOG("Error - bad blockalign or channels"); + free(raw_data_); + raw_data_ = 0; + 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; + decoder_ = new SimpleAudio(codec); + ILOG("read ATRAC, frames: %i, rate %i", numFrames, sample_rate); + } + + ~AT3PlusReader() { + } + + void Shutdown() { + free(raw_data_); + raw_data_ = 0; + delete[] buffer_; + buffer_ = 0; + delete decoder_; + decoder_ = 0; + } + + bool IsOK() { return raw_data_ != 0; } + + void Read(short *buffer, int len) { + if (!raw_data_) + return; + while (bgQueue.size() < len * 2) { + int outBytes; + decoder_->Decode(raw_data_ + raw_offset_, raw_bytes_per_frame_, (uint8_t *)buffer_, &outBytes); + if (!outBytes) + return; + + for (int i = 0; i < outBytes / 2; i++) { + bgQueue.push(buffer_[i]); + } + + // loop! + raw_offset_ += raw_bytes_per_frame_; + if (raw_offset_ >= raw_data_size_) { + raw_offset_ = 0; + } + } + + for (int i = 0; i < len * 2; i++) { + buffer[i] = bgQueue.pop_front(); + } + } + +private: + const std::string &data_; + ChunkFile file_; + uint8_t *raw_data_; + int raw_data_size_; + int raw_offset_; + int raw_bytes_per_frame_; + FixedSizeQueue bgQueue; + short *buffer_; + SimpleAudio *decoder_; +}; + +static std::string bgGamePath; +static int playbackOffset; +static AT3PlusReader *at3Reader; +static double gameLastChanged; + +void SetBackgroundAudioGame(const std::string &path) { + if (path == bgGamePath) { + // Do nothing + return; + } + time_update(); + gameLastChanged = time_now_d(); + if (at3Reader) { + at3Reader->Shutdown(); + delete at3Reader; + at3Reader = 0; + } + playbackOffset = 0; + bgGamePath = path; +} + +int MixBackgroundAudio(short *buffer, int size) { + time_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 (!at3Reader && bgGamePath.size() && (time_now_d() - gameLastChanged > 0.5)) { + // Grab some audio from the current game and play it. + GameInfo *gameInfo = g_gameInfoCache.GetInfo(bgGamePath, GAMEINFO_WANTSND); + if (!gameInfo) + return 0; + + if (gameInfo->sndFileData.size()) { + const std::string &data = gameInfo->sndFileData; + at3Reader = new AT3PlusReader(data); + } + } + + if (at3Reader) { + at3Reader->Read(buffer, size); + } else { + memset(buffer, 0, size * 2 * sizeof(s16)); + } + return 0; +} \ No newline at end of file diff --git a/UI/BackgroundAudio.h b/UI/BackgroundAudio.h new file mode 100644 index 0000000000..7511f34023 --- /dev/null +++ b/UI/BackgroundAudio.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void SetBackgroundAudioGame(const std::string &path); +int MixBackgroundAudio(short *buffer, int size); diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index df36195c7e..9a38a5c877 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -31,9 +31,19 @@ #include "UI/GameInfoCache.h" #include "UI/MiscScreens.h" #include "UI/MainScreen.h" +#include "UI/BackgroundAudio.h" + #include "Core/Host.h" #include "Core/Config.h" +GameScreen::GameScreen(const std::string &gamePath) : UIDialogScreenWithGameBackground(gamePath) { + SetBackgroundAudioGame(gamePath); +} + +GameScreen::~GameScreen() { + SetBackgroundAudioGame(""); +} + void GameScreen::CreateViews() { GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); diff --git a/UI/GameScreen.h b/UI/GameScreen.h index e13b4f49e4..cbca7dc13a 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -29,7 +29,8 @@ class GameScreen : public UIDialogScreenWithGameBackground { public: - GameScreen(const std::string &gamePath) : UIDialogScreenWithGameBackground(gamePath) {} + GameScreen(const std::string &gamePath); + ~GameScreen(); virtual void update(InputState &input); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index b089486396..1b61de8094 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -35,6 +35,7 @@ #include "Core/Reporting.h" #include "Core/SaveState.h" +#include "UI/BackgroundAudio.h" #include "UI/EmuScreen.h" #include "UI/MainScreen.h" #include "UI/GameScreen.h" @@ -704,8 +705,14 @@ UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) { MainScreen::MainScreen() : highlightProgress_(0.0f), prevHighlightProgress_(0.0f), backFromStore_(false) { System_SendMessage("event", "mainscreen"); + SetBackgroundAudioGame(""); } +MainScreen::~MainScreen() { + SetBackgroundAudioGame(""); +} + + void MainScreen::CreateViews() { // Information in the top left. // Back button to the bottom left. @@ -878,6 +885,7 @@ void MainScreen::sendMessage(const char *message, const char *value) { if (!strcmp(message, "boot")) { screenManager()->switchScreen(new EmuScreen(value)); + SetBackgroundAudioGame(value); } if (!strcmp(message, "control mapping")) { UpdateUIState(UISTATE_MENU); @@ -990,6 +998,7 @@ UI::EventReturn MainScreen::OnGameHighlight(UI::EventParams &e) { highlightedGamePath_ = path; highlightProgress_ = 0.0f; } + SetBackgroundAudioGame(highlightedGamePath_); return UI::EVENT_DONE; } diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 093cbf5a73..c73539b7b9 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -29,6 +29,7 @@ class MainScreen : public UIScreenWithBackground { public: MainScreen(); + ~MainScreen(); virtual bool isTopLevel() const { return true; } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index d7393f7a5b..569870c29d 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -86,6 +86,7 @@ #include "UI/OnScreenDisplay.h" #include "UI/MiscScreens.h" #include "UI/TiltEventProcessor.h" +#include "UI/BackgroundAudio.h" #if !defined(MOBILE_DEVICE) #include "Common/KeyMap.h" @@ -207,8 +208,8 @@ int NativeMix(short *audio, int num_samples) { if (g_mixer && GetUIState() == UISTATE_INGAME) { num_samples = g_mixer->Mix(audio, num_samples); } else { - // MixBackgroundAudio(audio, num_samples); - memset(audio, 0, num_samples * 2 * sizeof(short)); + MixBackgroundAudio(audio, num_samples); + // memset(audio, 0, num_samples * 2 * sizeof(short)); } #ifdef _WIN32 diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index e83f00b294..938c45c27b 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -19,6 +19,7 @@ + @@ -41,6 +42,7 @@ + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index 1e26e33ebb..5cdfefd726 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -47,6 +47,7 @@ Screens + @@ -94,10 +95,11 @@ Screens + {faee5dce-633b-4ba6-b19d-ea70ee3c1c38} - + \ No newline at end of file