#include "VgmRecorder.h"
#include "log.h"
#include <cstring>
VgmRecorder::VgmRecorder()
{
m_bRecording = false;
m_PendingWait = 0;
m_TotalSamples = 0;
m_ClockRate = 0;
m_bPAL = false;
m_bPSGUsed = false;
m_bAY8910Used = false;
}
VgmRecorder::~VgmRecorder()
{
if (m_bRecording)
{
Stop();
}
}
void VgmRecorder::Start(const char* file_path, int clock_rate, bool is_pal)
{
if (m_bRecording)
return;
m_FilePath = file_path;
m_ClockRate = clock_rate;
m_bPAL = is_pal;
m_bRecording = true;
m_PendingWait = 0;
m_TotalSamples = 0;
m_bPSGUsed = false;
m_bAY8910Used = false;
m_CommandBuffer.clear();
}
void VgmRecorder::Stop()
{
if (!m_bRecording)
return;
FlushPendingWait();
WriteCommand(0x66);
std::ofstream file(m_FilePath.c_str(), std::ios::binary);
if (file.is_open())
{
u8 header[256];
memset(header, 0, 256);
header[0x00] = 0x56;
header[0x01] = 0x67;
header[0x02] = 0x6d;
header[0x03] = 0x20;
u32 eof_offset = (256 + (u32)m_CommandBuffer.size()) - 4;
header[0x04] = (eof_offset >> 0) & 0xFF;
header[0x05] = (eof_offset >> 8) & 0xFF;
header[0x06] = (eof_offset >> 16) & 0xFF;
header[0x07] = (eof_offset >> 24) & 0xFF;
header[0x08] = 0x70;
header[0x09] = 0x01;
header[0x0A] = 0x00;
header[0x0B] = 0x00;
if (m_bPSGUsed)
{
u32 psg_clock = m_ClockRate;
header[0x0C] = (psg_clock >> 0) & 0xFF;
header[0x0D] = (psg_clock >> 8) & 0xFF;
header[0x0E] = (psg_clock >> 16) & 0xFF;
header[0x0F] = (psg_clock >> 24) & 0xFF;
}
header[0x10] = 0x00;
header[0x11] = 0x00;
header[0x12] = 0x00;
header[0x13] = 0x00;
header[0x14] = 0x00;
header[0x15] = 0x00;
header[0x16] = 0x00;
header[0x17] = 0x00;
header[0x18] = (m_TotalSamples >> 0) & 0xFF;
header[0x19] = (m_TotalSamples >> 8) & 0xFF;
header[0x1A] = (m_TotalSamples >> 16) & 0xFF;
header[0x1B] = (m_TotalSamples >> 24) & 0xFF;
header[0x1C] = 0x00;
header[0x1D] = 0x00;
header[0x1E] = 0x00;
header[0x1F] = 0x00;
header[0x20] = 0x00;
header[0x21] = 0x00;
header[0x22] = 0x00;
header[0x23] = 0x00;
u32 rate = m_bPAL ? 50 : 60;
header[0x24] = (rate >> 0) & 0xFF;
header[0x25] = (rate >> 8) & 0xFF;
header[0x26] = (rate >> 16) & 0xFF;
header[0x27] = (rate >> 24) & 0xFF;
if (m_bPSGUsed)
{
header[0x28] = 0x09;
header[0x29] = 0x00;
header[0x2A] = 0x10;
header[0x2B] = 0x00;
}
header[0x34] = 0xCC;
header[0x35] = 0x00;
header[0x36] = 0x00;
header[0x37] = 0x00;
if (m_bAY8910Used)
{
u32 ay_clock = m_ClockRate;
header[0x74] = (ay_clock >> 0) & 0xFF;
header[0x75] = (ay_clock >> 8) & 0xFF;
header[0x76] = (ay_clock >> 16) & 0xFF;
header[0x77] = (ay_clock >> 24) & 0xFF;
header[0x78] = 0x00;
header[0x79] = 0x01;
}
file.write(reinterpret_cast<const char*>(header), 256);
file.write(reinterpret_cast<const char*>(&m_CommandBuffer[0]), m_CommandBuffer.size());
file.close();
}
m_bRecording = false;
m_CommandBuffer.clear();
}
void VgmRecorder::WritePSG(u8 data)
{
if (!m_bRecording)
return;
FlushPendingWait();
m_bPSGUsed = true;
WriteCommand(0x50, data);
}
void VgmRecorder::WriteAY8910(u8 reg, u8 data)
{
if (!m_bRecording)
return;
FlushPendingWait();
m_bAY8910Used = true;
WriteCommand(0xA0, reg, data);
}
void VgmRecorder::UpdateTiming(int elapsed_samples)
{
if (!m_bRecording)
return;
m_PendingWait += elapsed_samples;
m_TotalSamples += elapsed_samples;
}
void VgmRecorder::WriteCommand(u8 command)
{
m_CommandBuffer.push_back(command);
}
void VgmRecorder::WriteCommand(u8 command, u8 data)
{
m_CommandBuffer.push_back(command);
m_CommandBuffer.push_back(data);
}
void VgmRecorder::WriteCommand(u8 command, u8 data1, u8 data2)
{
m_CommandBuffer.push_back(command);
m_CommandBuffer.push_back(data1);
m_CommandBuffer.push_back(data2);
}
void VgmRecorder::WriteWait(int samples)
{
if (samples <= 0)
return;
while (samples > 0)
{
if (samples == 735)
{
WriteCommand(0x62);
samples -= 735;
}
else if (samples == 882)
{
WriteCommand(0x63);
samples -= 882;
}
else if (samples <= 16)
{
WriteCommand(0x70 + (samples - 1));
samples = 0;
}
else if (samples <= 65535)
{
WriteCommand(0x61);
m_CommandBuffer.push_back(samples & 0xFF);
m_CommandBuffer.push_back((samples >> 8) & 0xFF);
samples = 0;
}
else
{
WriteCommand(0x61);
m_CommandBuffer.push_back(0xFF);
m_CommandBuffer.push_back(0xFF);
samples -= 65535;
}
}
}
void VgmRecorder::FlushPendingWait()
{
if (m_PendingWait > 0)
{
WriteWait(m_PendingWait);
m_PendingWait = 0;
}
}