using System; using System.IO; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using BizHawk.Common.BufferExtensions; using BizHawk.Client.Common; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Common.Components; using BizHawk.Emulation.Common; using ICSharpCode.SharpZipLib.Zip; namespace BizHawk.Client.EmuHawk { public partial class PCESoundDebugger : Form, IToolFormAutoConfig { [RequiredService] private PCEngine _pce { get; set; } public PCESoundDebugger() { InitializeComponent(); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); } byte[] waveformTemp = new byte[32 * 2]; protected override void OnShown(EventArgs e) { for (int i = 0; i < lvChEn.Items.Count; i++) lvChEn.Items[i].Checked = true; base.OnShown(e); } public void UpdateValues() { foreach (var entry in PSGEntries) { entry.wasactive = entry.active; entry.active = false; } bool sync = false; lvPsgWaveforms.BeginUpdate(); lvChannels.BeginUpdate(); for (int i = 0; i < 6; i++) { var ch = _pce.PSG.Channels[i]; //these conditions mean a sample isnt playing if (!ch.Enabled) { lvChannels.Items[i].SubItems[1].Text = "-"; lvChannels.Items[i].SubItems[2].Text = "-"; lvChannels.Items[i].SubItems[3].Text = "(disabled)"; goto DEAD; } if (ch.DDA) { lvChannels.Items[i].SubItems[1].Text = "-"; lvChannels.Items[i].SubItems[2].Text = "-"; lvChannels.Items[i].SubItems[3].Text = "(DDA)"; goto DEAD; } lvChannels.Items[i].SubItems[1].Text = ch.Volume.ToString(); lvChannels.Items[i].SubItems[2].Text = ch.Frequency.ToString(); if (ch.NoiseChannel) { lvChannels.Items[i].SubItems[3].Text = "(noise)"; goto DEAD; } if (ch.Volume == 0) goto DEAD; lvChannels.Items[i].SubItems[3].Text = "-"; //ok, a sample is playing. copy out the waveform short[] waveform = (short[])ch.Wave.Clone(); //hash it var ms = new MemoryStream(waveformTemp); var bw = new BinaryWriter(ms); foreach (var s in waveform) bw.Write(s); bw.Flush(); string md5 = waveformTemp.HashMD5(); if (!PSGEntryTable.ContainsKey(md5)) { var entry = new PSGEntry() { hash = md5, name = md5, waveform = waveform, active = true, hitcount = 1, index = PSGEntries.Count }; PSGEntries.Add(entry); PSGEntryTable[md5] = entry; sync = true; LastSamples[i] = entry; } else { PSGEntry entry = PSGEntryTable[md5]; entry.active = true; //are we playing the same sample as before? if (LastSamples[i] == entry) { } else //if (!entry.wasactive) { LastSamples[i] = entry; entry.hitcount++; if (entry.index < lvPsgWaveforms.Items.Count) lvPsgWaveforms.Items[entry.index].SubItems[1].Text = entry.hitcount.ToString(); else sync = true; } } lvChannels.Items[i].SubItems[3].Text = PSGEntryTable[md5].name; continue; DEAD: LastSamples[i] = null; } if (sync) SyncLists(); lvPsgWaveforms.EndUpdate(); lvChannels.EndUpdate(); } public void FastUpdate() { // Todo } class PSGEntry { public int index; public bool active, wasactive; public int hitcount; public string hash; public string name; public short[] waveform; } PSGEntry[] LastSamples = new PSGEntry[8]; List PSGEntries = new List(); Dictionary PSGEntryTable = new Dictionary(); public void Restart() { } public bool AskSaveChanges() { return true; } public bool UpdateBefore { get { return false; } } //32*16 samples, 16bit, mono, 8khz (but we'll change the sample rate) static readonly byte[] emptyWav = new byte[] { 0x52, 0x49, 0x46, 0x46, 0x24, 0x04, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xE0, 0x2E, 0x00, 0x00, 0xC0, 0x5D, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x04, 0x00, 0x00, }; private void btnExport_Click(object sender, EventArgs e) { string tmpf = Path.GetTempFileName() + ".zip"; using (var stream = new FileStream(tmpf, FileMode.Create, FileAccess.Write, FileShare.Read)) { var zip = new ZipOutputStream(stream) { IsStreamOwner = false, UseZip64 = UseZip64.Off }; foreach (var entry in PSGEntries) { var ze = new ZipEntry(entry.name + ".wav") { CompressionMethod = CompressionMethod.Deflated }; zip.PutNextEntry(ze); var ms = new MemoryStream(); var bw = new BinaryWriter(ms); bw.Write(emptyWav, 0, emptyWav.Length); ms.Position = 0x18; //samplerate and avgbytespersecond bw.Write(20000); bw.Write(20000 * 2); bw.Flush(); ms.Position = 0x2C; for (int i = 0; i < 32; i++) for (int j = 0; j < 16; j++) bw.Write(entry.waveform[i]); bw.Flush(); var buf = ms.GetBuffer(); zip.Write(buf, 0, (int)ms.Length); zip.Flush(); zip.CloseEntry(); } zip.Close(); stream.Flush(); } System.Diagnostics.Process.Start(tmpf); } class ZipDataSource : IStaticDataSource { public ZipDataSource(byte[] data) { this.data = data; } byte[] data; public Stream GetSource() { return new MemoryStream(data); } } private void btnReset_Click(object sender, EventArgs e) { PSGEntryTable.Clear(); PSGEntries.Clear(); for (int i = 0; i < 8; i++) LastSamples[i] = null; SyncLists(); } void SyncLists() { lvPsgWaveforms.Items.Clear(); foreach (var entry in PSGEntries) { var lvi = new ListViewItem(entry.name); lvi.SubItems.Add(entry.hitcount.ToString()); lvPsgWaveforms.Items.Add(lvi); } } private void lvPsgWaveforms_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.F2 && lvPsgWaveforms.SelectedItems.Count > 0) { lvPsgWaveforms.SelectedItems[0].BeginEdit(); } } private void lvPsgWaveforms_ItemActivate(object sender, EventArgs e) { } private void lvPsgWaveforms_AfterLabelEdit(object sender, LabelEditEventArgs e) { var entry = PSGEntries[e.Item]; entry.name = e.Label; } private void lvChEn_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { } private void lvChEn_ItemChecked(object sender, ItemCheckedEventArgs e) { for (int i = 0; i < 6; i++) _pce.PSG.UserMute[i] = !lvChEn.Items[i].Checked; } } }