Add Audio Dumping to File
This commit is contained in:
parent
78d1af4b50
commit
645cb4d69e
14 changed files with 240 additions and 5 deletions
|
@ -329,6 +329,7 @@ static ConfigSetting generalSettings[] = {
|
|||
ConfigSetting("ScreenshotsAsPNG", &g_Config.bScreenshotsAsPNG, false, true, true),
|
||||
ConfigSetting("UseFFV1", &g_Config.bUseFFV1, false),
|
||||
ConfigSetting("DumpFrames", &g_Config.bDumpFrames, false),
|
||||
ConfigSetting("DumpAudio", &g_Config.bDumpAudio, false),
|
||||
ConfigSetting("StateSlot", &g_Config.iCurrentStateSlot, 0, true, true),
|
||||
ConfigSetting("RewindFlipFrequency", &g_Config.iRewindFlipFrequency, 0, true, true),
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ public:
|
|||
bool bScreenshotsAsPNG;
|
||||
bool bUseFFV1;
|
||||
bool bDumpFrames;
|
||||
bool bDumpAudio;
|
||||
bool bEnableLogging;
|
||||
bool bDumpDecryptedEboot;
|
||||
bool bFullscreenOnDoubleclick;
|
||||
|
|
|
@ -501,6 +501,7 @@
|
|||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AnySuitable</InlineFunctionExpansion>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WaveFile.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\ext\disarm.h" />
|
||||
|
@ -731,6 +732,7 @@
|
|||
<ClInclude Include="Util\PPGeDraw.h" />
|
||||
<ClInclude Include="Util\ppge_atlas.h" />
|
||||
<ClInclude Include="..\ext\xxhash.h" />
|
||||
<ClInclude Include="WaveFile.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\android\jni\Android.mk" />
|
||||
|
|
|
@ -676,6 +676,7 @@
|
|||
<ClCompile Include="AVIDump.cpp">
|
||||
<Filter>Core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WaveFile.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
|
@ -1242,6 +1243,7 @@
|
|||
<ClInclude Include="AVIDump.h">
|
||||
<Filter>Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WaveFile.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "Core/Host.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/HLE/__sceAudio.h"
|
||||
#include "Core/HLE/sceAudio.h"
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
|
@ -67,6 +68,9 @@ static int audioHostIntervalCycles;
|
|||
|
||||
static s32 *mixBuffer;
|
||||
|
||||
WaveFileWriter g_wave_writer;
|
||||
bool m_logAudio;
|
||||
|
||||
// High and low watermarks, basically. For perfect emulation, the correct values are 0 and 1, respectively.
|
||||
// TODO: Tweak. Hm, there aren't actually even used currently...
|
||||
static int chanQueueMaxSizeFactor;
|
||||
|
@ -134,6 +138,13 @@ void __AudioInit() {
|
|||
|
||||
resampler.Clear();
|
||||
CoreTiming::RegisterMHzChangeCallback(&__AudioCPUMHzChange);
|
||||
|
||||
if (g_Config.bDumpAudio)
|
||||
{
|
||||
std::string audio_file_name = GetSysDirectory(DIRECTORY_VIDEO_DUMP) + "audiodump.wav";
|
||||
File::CreateEmptyFile(audio_file_name);
|
||||
__StartLogAudio(audio_file_name);
|
||||
}
|
||||
}
|
||||
|
||||
void __AudioDoState(PointerWrap &p) {
|
||||
|
@ -177,6 +188,11 @@ void __AudioShutdown() {
|
|||
mixBuffer = 0;
|
||||
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
|
||||
chans[i].clear();
|
||||
|
||||
if (g_Config.bDumpAudio)
|
||||
{
|
||||
__StopLogAudio();
|
||||
}
|
||||
}
|
||||
|
||||
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) {
|
||||
|
@ -364,6 +380,15 @@ void __AudioUpdate() {
|
|||
|
||||
if (g_Config.bEnableSound) {
|
||||
resampler.PushSamples(mixBuffer, hwBlockSize);
|
||||
if (m_logAudio)
|
||||
{
|
||||
s16 *clamped_data = new s16[hwBlockSize * 2];
|
||||
|
||||
for (int i = 0; i < hwBlockSize * 2; i++) {
|
||||
clamped_data[i] = clamp_s16(mixBuffer[i]);
|
||||
}
|
||||
g_wave_writer.AddStereoSamples(clamped_data, hwBlockSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -385,3 +410,32 @@ void __PushExternalAudio(const s32 *audio, int numSamples) {
|
|||
resampler.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void __StartLogAudio(const std::string& filename)
|
||||
{
|
||||
if (!m_logAudio)
|
||||
{
|
||||
m_logAudio = true;
|
||||
g_wave_writer.Start(filename, 44100);
|
||||
g_wave_writer.SetSkipSilence(false);
|
||||
NOTICE_LOG(SCEAUDIO, "Starting Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(SCEAUDIO, "Audio logging has already been started");
|
||||
}
|
||||
}
|
||||
|
||||
void __StopLogAudio()
|
||||
{
|
||||
if (m_logAudio)
|
||||
{
|
||||
m_logAudio = false;
|
||||
g_wave_writer.Stop();
|
||||
NOTICE_LOG(SCEAUDIO, "Stopping Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(SCEAUDIO, "Audio logging has already been stopped");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "sceAudio.h"
|
||||
#include "Core/WaveFile.h"
|
||||
|
||||
struct AudioDebugStats {
|
||||
int buffered;
|
||||
|
@ -45,3 +46,7 @@ void __AudioWakeThreads(AudioChannel &chan, int result);
|
|||
int __AudioMix(short *outstereo, int numSamples, int sampleRate);
|
||||
const AudioDebugStats *__AudioGetDebugStats();
|
||||
void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu!
|
||||
|
||||
// Audio Dumping stuff
|
||||
void __StartLogAudio(const std::string& filename);
|
||||
void __StopLogAudio();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/WaveFile.h"
|
||||
|
||||
struct AudioDebugStats;
|
||||
|
||||
|
|
116
Core/WaveFile.cpp
Normal file
116
Core/WaveFile.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Core/WaveFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Core/Config.h"
|
||||
|
||||
constexpr size_t WaveFileWriter::BUFFER_SIZE;
|
||||
|
||||
WaveFileWriter::WaveFileWriter()
|
||||
{
|
||||
}
|
||||
|
||||
WaveFileWriter::~WaveFileWriter()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate)
|
||||
{
|
||||
// Check if the file is already open
|
||||
if (file)
|
||||
{
|
||||
PanicAlert("The file %s was already open, the file header will not be written.",
|
||||
filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
file.Open(filename, "wb");
|
||||
if (!file)
|
||||
{
|
||||
PanicAlert("The file %s could not be opened for writing. Please check if it's already opened "
|
||||
"by another program.",
|
||||
filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_size = 0;
|
||||
|
||||
// -----------------
|
||||
// Write file header
|
||||
// -----------------
|
||||
Write4("RIFF");
|
||||
Write(100 * 1000 * 1000); // write big value in case the file gets truncated
|
||||
Write4("WAVE");
|
||||
Write4("fmt ");
|
||||
|
||||
Write(16); // size of fmt block
|
||||
Write(0x00020001); // two channels, uncompressed
|
||||
|
||||
const u32 sample_rate = HLESampleRate;
|
||||
Write(sample_rate);
|
||||
Write(sample_rate * 2 * 2); // two channels, 16bit
|
||||
|
||||
Write(0x00100004);
|
||||
Write4("data");
|
||||
Write(100 * 1000 * 1000 - 32);
|
||||
|
||||
// We are now at offset 44
|
||||
if (file.Tell() != 44)
|
||||
PanicAlert("Wrong offset: %lld", (long long)file.Tell());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaveFileWriter::Stop()
|
||||
{
|
||||
// u32 file_size = (u32)ftello(file);
|
||||
file.Seek(4, SEEK_SET);
|
||||
Write(audio_size + 36);
|
||||
|
||||
file.Seek(40, SEEK_SET);
|
||||
Write(audio_size);
|
||||
|
||||
file.Close();
|
||||
}
|
||||
|
||||
void WaveFileWriter::Write(u32 value)
|
||||
{
|
||||
file.WriteArray(&value, 1);
|
||||
}
|
||||
|
||||
void WaveFileWriter::Write4(const char* ptr)
|
||||
{
|
||||
file.WriteBytes(ptr, 4);
|
||||
}
|
||||
|
||||
void WaveFileWriter::AddStereoSamples(const short* sample_data, u32 count)
|
||||
{
|
||||
if (!file)
|
||||
PanicAlert("WaveFileWriter - file not open.");
|
||||
|
||||
if (count > BUFFER_SIZE * 2)
|
||||
PanicAlert("WaveFileWriter - buffer too small (count = %u).", count);
|
||||
|
||||
if (skip_silence)
|
||||
{
|
||||
bool all_zero = true;
|
||||
|
||||
for (u32 i = 0; i < count * 2; i++)
|
||||
{
|
||||
if (sample_data[i])
|
||||
all_zero = false;
|
||||
}
|
||||
|
||||
if (all_zero)
|
||||
return;
|
||||
}
|
||||
|
||||
file.WriteBytes(sample_data, count * 4);
|
||||
audio_size += count * 4;
|
||||
}
|
44
Core/WaveFile.h
Normal file
44
Core/WaveFile.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Class: WaveFileWriter
|
||||
// Description: Simple utility class to make it easy to write long 16-bit stereo
|
||||
// audio streams to disk.
|
||||
// Use Start() to start recording to a file, and AddStereoSamples to add wave data.
|
||||
// The float variant will convert from -1.0-1.0 range and clamp.
|
||||
// Alternatively, AddSamplesBE for big endian wave data.
|
||||
// If Stop is not called when it destructs, the destructor will call Stop().
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
|
||||
class WaveFileWriter
|
||||
{
|
||||
public:
|
||||
WaveFileWriter();
|
||||
~WaveFileWriter();
|
||||
|
||||
bool Start(const std::string& filename, unsigned int HLESampleRate);
|
||||
void Stop();
|
||||
|
||||
void SetSkipSilence(bool skip) { skip_silence = skip; }
|
||||
void AddStereoSamples(const short* sample_data, u32 count);
|
||||
u32 GetAudioSize() const { return audio_size; }
|
||||
private:
|
||||
static constexpr size_t BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
File::IOFile file;
|
||||
bool skip_silence = false;
|
||||
u32 audio_size = 0;
|
||||
std::array<short, BUFFER_SIZE> conv_buffer{};
|
||||
void Write(u32 value);
|
||||
void Write4(const char* ptr);
|
||||
};
|
||||
|
|
@ -678,6 +678,7 @@ void GameSettingsScreen::CreateViews() {
|
|||
systemSettings->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, sy->T("Screenshots as PNG")));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bDumpFrames, sy->T("Dump Frames")));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bUseFFV1, sy->T("Use FFV1 for Frame Dumps")));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bDumpAudio, sy->T("Dump Audio")));
|
||||
#endif
|
||||
systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Day Light Saving")));
|
||||
static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY"};
|
||||
|
|
|
@ -276,6 +276,7 @@ namespace MainWindow {
|
|||
// Movie menu
|
||||
TranslateMenuItem(menu, ID_MOVIE_DUMPFRAMES);
|
||||
TranslateMenuItem(menu, ID_MOVIE_USEFFV1);
|
||||
TranslateMenuItem(menu, ID_MOVIE_DUMPAUDIO);
|
||||
|
||||
// Skip display multipliers x1-x10
|
||||
TranslateMenuItem(menu, ID_OPTIONS_FULLSCREEN, L"\tAlt+Return, F11");
|
||||
|
@ -942,6 +943,10 @@ namespace MainWindow {
|
|||
g_Config.bUseFFV1 = !g_Config.bUseFFV1;
|
||||
break;
|
||||
|
||||
case ID_MOVIE_DUMPAUDIO:
|
||||
g_Config.bDumpAudio = !g_Config.bDumpAudio;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// Handle the dynamic shader switching here.
|
||||
|
@ -982,6 +987,7 @@ namespace MainWindow {
|
|||
CHECKITEM(ID_OPTIONS_IGNOREWINKEY, g_Config.bIgnoreWindowsKey);
|
||||
CHECKITEM(ID_MOVIE_DUMPFRAMES, g_Config.bDumpFrames);
|
||||
CHECKITEM(ID_MOVIE_USEFFV1, g_Config.bUseFFV1);
|
||||
CHECKITEM(ID_MOVIE_DUMPAUDIO, g_Config.bDumpAudio);
|
||||
|
||||
static const int displayrotationitems[] = {
|
||||
ID_EMULATION_ROTATION_H,
|
||||
|
|
|
@ -449,6 +449,7 @@ BEGIN
|
|||
BEGIN
|
||||
MENUITEM "Dump Frames", ID_MOVIE_DUMPFRAMES
|
||||
MENUITEM "Lossless Codec (FFV1)", ID_MOVIE_USEFFV1
|
||||
MENUITEM "Dump Audio", ID_MOVIE_DUMPAUDIO
|
||||
END
|
||||
|
||||
POPUP "Options"
|
||||
|
|
|
@ -330,6 +330,7 @@
|
|||
#define ID_GEDBG_WATCH 40164
|
||||
#define ID_MOVIE_DUMPFRAMES 40165
|
||||
#define ID_MOVIE_USEFFV1 40166
|
||||
#define ID_MOVIE_DUMPAUDIO 40167
|
||||
|
||||
// Dummy option to let the buffered rendering hotkey cycle through all the options.
|
||||
#define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue