Audio: Added sound recorder
This commit is contained in:
parent
033469ff01
commit
92d915b585
11 changed files with 207 additions and 4 deletions
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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
85
Core/WaveRecorder.cpp
Normal 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
21
Core/WaveRecorder.h
Normal 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);
|
||||
};
|
|
@ -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(); }
|
||||
}
|
34
UI/Forms/frmMain.Designer.cs
generated
34
UI/Forms/frmMain.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue