using System; using System.Diagnostics; using System.IO; using System.Media; using System.Windows.Forms; using PKHeX.Core; namespace PKHeX.WinForms; public partial class SAV_Chatter : Form { private readonly SaveFile Origin; private readonly SaveFile SAV; private readonly IChatter Chatter; private readonly SoundPlayer Sounds = new(); public SAV_Chatter(SaveFile sav) { InitializeComponent(); WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); SAV = (Origin = sav).Clone(); Chatter = SAV is SAV5 s5 ? s5.Chatter : ((SAV4)SAV).Chatter; CHK_Initialized.Checked = Chatter.Initialized; MT_Confusion.Text = Chatter.ConfusionChance.ToString(); } private void CHK_Initialized_CheckedChanged(object sender, EventArgs e) { Chatter.Initialized = CHK_Initialized.Checked; MT_Confusion.Text = Chatter.ConfusionChance.ToString(); } private void B_ImportPCM_Click(object sender, EventArgs e) { using var ofd = new OpenFileDialog(); ofd.Filter = "PCM File|*.pcm"; if (ofd.ShowDialog() != DialogResult.OK) return; var path = ofd.FileName; var len = new FileInfo(path).Length; if (len != IChatter.SIZE_PCM) { WinFormsUtil.Error($"Incorrect size, got {len} bytes, expected {IChatter.SIZE_PCM} bytes."); return; } byte[] data = File.ReadAllBytes(path); data.CopyTo(Chatter.Recording); CHK_Initialized.Checked = Chatter.Initialized = true; MT_Confusion.Text = Chatter.ConfusionChance.ToString(); } private void B_ExportPCM_Click(object sender, EventArgs e) { using var sfd = new SaveFileDialog(); sfd.Filter = "PCM File|*.pcm"; sfd.FileName = "Recording.pcm"; if (sfd.ShowDialog() != DialogResult.OK) return; File.WriteAllBytes(sfd.FileName, Chatter.Recording); } private void B_ExportWAV_Click(object sender, EventArgs e) { using var sfd = new SaveFileDialog(); sfd.Filter = "WAV File|*.wav"; sfd.FileName = "Recording.wav"; if (sfd.ShowDialog() != DialogResult.OK) return; File.WriteAllBytes(sfd.FileName, ConvertPCMToWAV(Chatter.Recording)); } private void B_PlayRecording_Click(object sender, EventArgs e) { if (!Chatter.Initialized && !Chatter.Recording.ContainsAnyExcept(0x00)) return; var data = ConvertPCMToWAV(Chatter.Recording); Sounds.Stream = new MemoryStream(data); try { Sounds.Play(); } catch { Debug.WriteLine("Failed to play sound."); } } private void B_Save_Click(object sender, EventArgs e) { Origin.CopyChangesFrom(SAV); Sounds.Stop(); Close(); } private void B_Cancel_Click(object sender, EventArgs e) { Sounds.Stop(); Close(); } private static int GetWAVExpectedLength() => WAVHeader.Length + (IChatter.SIZE_PCM * 2); /// /// Size: 2x "/> /// private static ReadOnlySpan WAVHeader => [ // RIFF chunk 0x52, 0x49, 0x46, 0x46, // chunk name: "RIFF" 0xF4, 0x07, 0x00, 0x00, // chunk size: 2036 0x57, 0x41, 0x56, 0x45, // format: "WAVE" // fmt subchunk 0x66, 0x6D, 0x74, 0x20, // subchunk name: "fmt " 0x10, 0x00, 0x00, 0x00, // subchunk size: 16 0x01, 0x00, // wFormatTag: WAVE_FORMAT_PCM (1) 0x01, 0x00, // nChannels: mono (1) 0xD0, 0x07, 0x00, 0x00, // nSamplesPerSec: 2000 0xD0, 0x07, 0x00, 0x00, // nAvgBytesPerSec: 2000 0x01, 0x00, // nBlockAlign: 1 0x08, 0x00, // wBitsPerSample: 8 // data subchunk 0x64, 0x61, 0x74, 0x61, // subchunk name: "data" 0xD0, 0x07, 0x00, 0x00, // subchunk size: 2000 ]; /// /// Convert 4-bit PCM to 8-bit PCM and adds the WAV file header. /// /// Unsigned 4-bit PCM data /// WAV file with unsigned 8-bit PCM data private static byte[] ConvertPCMToWAV(ReadOnlySpan pcm) { byte[] data = new byte[GetWAVExpectedLength()]; ConvertPCMToWAV(pcm, data); return data; } private static void ConvertPCMToWAV(ReadOnlySpan pcm, Span result) { WAVHeader.CopyTo(result); var i = WAVHeader.Length; foreach (byte b in pcm) { result[i++] = (byte)((b & 0x0F) << 4); result[i++] = (byte)(b & 0xF0); } } }