Audio: Added sound recorder

This commit is contained in:
Sour 2019-03-15 12:58:30 -04:00
parent 033469ff01
commit 92d915b585
11 changed files with 207 additions and 4 deletions

View file

@ -125,6 +125,7 @@
<ClInclude Include="TraceLogger.h" />
<ClInclude Include="VideoDecoder.h" />
<ClInclude Include="VideoRenderer.h" />
<ClInclude Include="WaveRecorder.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AviRecorder.cpp" />
@ -189,6 +190,7 @@
<ClCompile Include="TraceLogger.cpp" />
<ClCompile Include="VideoDecoder.cpp" />
<ClCompile Include="VideoRenderer.cpp" />
<ClCompile Include="WaveRecorder.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0}</ProjectGuid>

View file

@ -245,6 +245,9 @@
<ClInclude Include="AviRecorder.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="WaveRecorder.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -396,6 +399,9 @@
<ClCompile Include="AviRecorder.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="WaveRecorder.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -5,6 +5,7 @@
#include "SoundResampler.h"
#include "RewindManager.h"
#include "VideoRenderer.h"
#include "WaveRecorder.h"
#include "../Utilities/Equalizer.h"
#include "../Utilities/blip_buf.h"
@ -73,8 +74,11 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount)
out = _sampleBuffer;
}
bool isRecording = _console->GetVideoRenderer()->IsRecording() /* TODO || _waveRecorder*/;
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
if(isRecording) {
if(_waveRecorder) {
_waveRecorder->WriteSamples(out, count, cfg.SampleRate, true);
}
_console->GetVideoRenderer()->AddRecordingSound(out, count, cfg.SampleRate);
}
@ -109,4 +113,19 @@ void SoundMixer::ProcessEqualizer(int16_t* samples, uint32_t sampleCount)
double SoundMixer::GetRateAdjustment()
{
return _resampler->GetRateAdjustment();
}
void SoundMixer::StartRecording(string filepath)
{
_waveRecorder.reset(new WaveRecorder(filepath, _console->GetSettings()->GetAudioConfig().SampleRate, true));
}
void SoundMixer::StopRecording()
{
_waveRecorder.reset();
}
bool SoundMixer::IsRecording()
{
return _waveRecorder.get() != nullptr;
}

View file

@ -5,6 +5,7 @@
class Console;
class Equalizer;
class SoundResampler;
class WaveRecorder;
class SoundMixer
{
@ -13,6 +14,7 @@ private:
Console *_console;
unique_ptr<Equalizer> _equalizer;
unique_ptr<SoundResampler> _resampler;
shared_ptr<WaveRecorder> _waveRecorder;
int16_t *_sampleBuffer = nullptr;
void ProcessEqualizer(int16_t *samples, uint32_t sampleCount);
@ -27,4 +29,8 @@ public:
void RegisterAudioDevice(IAudioDevice *audioDevice);
AudioStatistics GetStatistics();
double GetRateAdjustment();
void StartRecording(string filepath);
void StopRecording();
bool IsRecording();
};

View file

@ -27,7 +27,7 @@ double SoundResampler::GetRateAdjustment()
double SoundResampler::GetTargetRateAdjustment()
{
AudioConfig cfg = _console->GetSettings()->GetAudioConfig();
bool isRecording = _console->GetVideoRenderer()->IsRecording() /* TODO || _waveRecorder */;
bool isRecording = _console->GetSoundMixer()->IsRecording() || _console->GetVideoRenderer()->IsRecording();
if(!isRecording && !cfg.DisableDynamicSampleRate) {
//Don't deviate from selected sample rate while recording
//TODO: Have 2 output streams (one for recording, one for the speakers)

85
Core/WaveRecorder.cpp Normal file
View file

@ -0,0 +1,85 @@
#include "stdafx.h"
#include "WaveRecorder.h"
#include "MessageManager.h"
WaveRecorder::WaveRecorder(string outputFile, uint32_t sampleRate, bool isStereo)
{
_stream = ofstream(outputFile, ios::out | ios::binary);
_outputFile = outputFile;
_streamSize = 0;
_sampleRate = sampleRate;
_isStereo = isStereo;
WriteHeader();
MessageManager::DisplayMessage("SoundRecorder", "SoundRecorderStarted", _outputFile);
}
WaveRecorder::~WaveRecorder()
{
CloseFile();
}
void WaveRecorder::WriteHeader()
{
_stream << "RIFF";
uint32_t size = 0;
_stream.write((char*)&size, sizeof(size));
_stream << "WAVE";
_stream << "fmt ";
uint32_t chunkSize = 16;
_stream.write((char*)&chunkSize, sizeof(chunkSize));
uint16_t format = 1; //PCM
uint16_t channelCount = _isStereo ? 2 : 1;
uint16_t bytesPerSample = 2;
uint16_t blockAlign = channelCount * bytesPerSample;
uint32_t byteRate = _sampleRate * channelCount * bytesPerSample;
uint16_t bitsPerSample = bytesPerSample * 8;
_stream.write((char*)&format, sizeof(format));
_stream.write((char*)&channelCount, sizeof(channelCount));
_stream.write((char*)&_sampleRate, sizeof(_sampleRate));
_stream.write((char*)&byteRate, sizeof(byteRate));
_stream.write((char*)&blockAlign, sizeof(blockAlign));
_stream.write((char*)&bitsPerSample, sizeof(bitsPerSample));
_stream << "data";
_stream.write((char*)&size, sizeof(size));
}
bool WaveRecorder::WriteSamples(int16_t * samples, uint32_t sampleCount, uint32_t sampleRate, bool isStereo)
{
if(_sampleRate != sampleRate || _isStereo != isStereo) {
//Format changed, stop recording
CloseFile();
return false;
} else {
uint32_t sampleBytes = sampleCount * (isStereo ? 4 : 2);
_stream.write((char*)samples, sampleBytes);
_streamSize += sampleBytes;
return true;
}
}
void WaveRecorder::UpdateSizeValues()
{
_stream.seekp(4, ios::beg);
uint32_t fileSize = _streamSize + 36;
_stream.write((char*)&fileSize, sizeof(fileSize));
_stream.seekp(40, ios::beg);
_stream.write((char*)&_streamSize, sizeof(_streamSize));
}
void WaveRecorder::CloseFile()
{
if(_stream && _stream.is_open()) {
UpdateSizeValues();
_stream.close();
MessageManager::DisplayMessage("SoundRecorder", "SoundRecorderStopped", _outputFile);
}
}

21
Core/WaveRecorder.h Normal file
View file

@ -0,0 +1,21 @@
#include "stdafx.h"
class WaveRecorder
{
private:
std::ofstream _stream;
uint32_t _streamSize;
uint32_t _sampleRate;
bool _isStereo;
string _outputFile;
void WriteHeader();
void UpdateSizeValues();
void CloseFile();
public:
WaveRecorder(string outputFile, uint32_t sampleRate, bool isStereo);
~WaveRecorder();
bool WriteSamples(int16_t* samples, uint32_t sampleCount, uint32_t sampleRate, bool isStereo);
};

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "../Core/Console.h"
#include "../Core/VideoRenderer.h"
#include "../Core/SoundMixer.h"
extern shared_ptr<Console> _console;
enum class VideoCodec;
@ -10,4 +11,8 @@ extern "C"
DllExport void __stdcall AviRecord(char* filename, VideoCodec codec, uint32_t compressionLevel) { _console->GetVideoRenderer()->StartRecording(filename, codec, compressionLevel); }
DllExport void __stdcall AviStop() { _console->GetVideoRenderer()->StopRecording(); }
DllExport bool __stdcall AviIsRecording() { return _console->GetVideoRenderer()->IsRecording(); }
DllExport void __stdcall WaveRecord(char* filename) { _console->GetSoundMixer()->StartRecording(filename); }
DllExport void __stdcall WaveStop() { _console->GetSoundMixer()->StopRecording(); }
DllExport bool __stdcall WaveIsRecording() { return _console->GetSoundMixer()->IsRecording(); }
}

View file

@ -132,6 +132,9 @@
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.pnlRenderer = new System.Windows.Forms.Panel();
this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames();
this.mnuSoundRecorder = new System.Windows.Forms.ToolStripMenuItem();
this.mnuWaveRecord = new System.Windows.Forms.ToolStripMenuItem();
this.mnuWaveStop = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMain.SuspendLayout();
this.pnlRenderer.SuspendLayout();
this.SuspendLayout();
@ -751,6 +754,7 @@
// toolsToolStripMenuItem
//
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuSoundRecorder,
this.mnuVideoRecorder,
this.toolStripMenuItem11,
this.mnuLogWindow,
@ -930,6 +934,33 @@
this.ctrlRecentGames.TabIndex = 1;
this.ctrlRecentGames.Visible = false;
//
// mnuSoundRecorder
//
this.mnuSoundRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuWaveRecord,
this.mnuWaveStop});
this.mnuSoundRecorder.Image = global::Mesen.GUI.Properties.Resources.Microphone;
this.mnuSoundRecorder.Name = "mnuSoundRecorder";
this.mnuSoundRecorder.Size = new System.Drawing.Size(159, 22);
this.mnuSoundRecorder.Text = "Sound Recorder";
this.mnuSoundRecorder.DropDownOpening += new System.EventHandler(this.mnuSoundRecorder_DropDownOpening);
//
// mnuWaveRecord
//
this.mnuWaveRecord.Image = global::Mesen.GUI.Properties.Resources.Record;
this.mnuWaveRecord.Name = "mnuWaveRecord";
this.mnuWaveRecord.Size = new System.Drawing.Size(155, 22);
this.mnuWaveRecord.Text = "Record...";
this.mnuWaveRecord.Click += new System.EventHandler(this.mnuWaveRecord_Click);
//
// mnuWaveStop
//
this.mnuWaveStop.Image = global::Mesen.GUI.Properties.Resources.MediaStop;
this.mnuWaveStop.Name = "mnuWaveStop";
this.mnuWaveStop.Size = new System.Drawing.Size(155, 22);
this.mnuWaveStop.Text = "Stop Recording";
this.mnuWaveStop.Click += new System.EventHandler(this.mnuWaveStop_Click);
//
// frmMain
//
this.AllowDrop = true;
@ -1057,5 +1088,8 @@
private System.Windows.Forms.ToolStripMenuItem mnuAviRecord;
private System.Windows.Forms.ToolStripMenuItem mnuAviStop;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem11;
private System.Windows.Forms.ToolStripMenuItem mnuSoundRecorder;
private System.Windows.Forms.ToolStripMenuItem mnuWaveRecord;
private System.Windows.Forms.ToolStripMenuItem mnuWaveStop;
}
}

View file

@ -452,5 +452,30 @@ namespace Mesen.GUI.Forms
mnuAviRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.AviIsRecording();
mnuAviStop.Enabled = EmuRunner.IsRunning() && RecordApi.AviIsRecording();
}
private void mnuWaveRecord_Click(object sender, EventArgs e)
{
using(SaveFileDialog sfd = new SaveFileDialog()) {
sfd.SetFilter(ResourceHelper.GetMessage("FilterWave"));
sfd.InitialDirectory = ConfigManager.WaveFolder;
//TODO
//sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".wav";
if(sfd.ShowDialog(this) == DialogResult.OK) {
RecordApi.WaveRecord(sfd.FileName);
}
}
}
private void mnuWaveStop_Click(object sender, EventArgs e)
{
RecordApi.WaveStop();
}
private void mnuSoundRecorder_DropDownOpening(object sender, EventArgs e)
{
mnuSoundRecorder.Enabled = EmuRunner.IsRunning();
mnuWaveRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.WaveIsRecording();
mnuWaveStop.Enabled = EmuRunner.IsRunning() && RecordApi.WaveIsRecording();
}
}
}

View file

@ -16,11 +16,11 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void AviStop();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool AviIsRecording();
/*[DllImport(DllPath)] public static extern void WaveRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void WaveRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void WaveStop();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool WaveIsRecording();
[DllImport(DllPath)] public static extern void MoviePlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename);
/*[DllImport(DllPath)] public static extern void MoviePlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void MovieRecord(ref RecordMovieOptions options);
[DllImport(DllPath)] public static extern void MovieStop();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MoviePlaying();