using System; using System.IO; namespace MAME.Core { public struct QSOUND_CHANNEL { public int bank; /* bank (x16) */ public int address; /* start address */ public int pitch; /* pitch */ public int reg3; /* unknown (always 0x8000) */ public int loop; /* loop address */ public int end; /* end address */ public int vol; /* master volume */ public int pan; /* Pan value */ public int reg9; /* unknown */ /* Work variables */ public int key; /* Key on / key off */ public int lvol; /* left volume */ public int rvol; /* right volume */ public int lastdt; /* last sample value */ public int offset; /* current offset counter */ }; public struct qsound_info { /* Private variables */ public QSOUND_CHANNEL[] channel; public int data; /* register latch data */ public int sample_rom_length; public int[] pan_table; /* Pan volume table */ public float frq_ratio; /* Frequency ratio */ }; public unsafe class QSound { public static sbyte[] qsoundrom; public static qsound_info QChip; public static void qsound_start() { int i; QChip.sample_rom_length = qsoundrom.Length; QChip.channel = new QSOUND_CHANNEL[16]; //QChip.frq_ratio = 16.0; QChip.pan_table = new int[33]; for (i = 0; i < 33; i++) { QChip.pan_table[i] = (int)((256 / Math.Sqrt(32)) * Math.Sqrt(i)); } } public static void qsound_data_h_w(byte data) { QChip.data = (QChip.data & 0xff) | (data << 8); } public static void qsound_data_l_w(byte data) { QChip.data = (QChip.data & 0xff00) | data; } public static void qsound_cmd_w(byte data) { qsound_set_command(data, QChip.data); } public static byte qsound_status_r() { /* Port ready bit (0x80 if ready) */ return 0x80; } private static void qsound_set_command(int data, int value) { int ch = 0, reg = 0; if (data < 0x80) { ch = data >> 3; reg = data & 0x07; } else { if (data < 0x90) { ch = data - 0x80; reg = 8; } else { if (data >= 0xba && data < 0xca) { ch = data - 0xba; reg = 9; } else { /* Unknown registers */ ch = 99; reg = 99; } } } switch (reg) { case 0: /* Bank */ ch = (ch + 1) & 0x0f; /* strange ... */ QChip.channel[ch].bank = (value & 0x7f) << 16; break; case 1: /* start */ QChip.channel[ch].address = value; break; case 2: /* pitch */ QChip.channel[ch].pitch = value * 16; if (value == 0) { /* Key off */ QChip.channel[ch].key = 0; } break; case 3: /* unknown */ QChip.channel[ch].reg3 = value; break; case 4: /* loop offset */ QChip.channel[ch].loop = value; break; case 5: /* end */ QChip.channel[ch].end = value; break; case 6: /* master volume */ if (value == 0) { /* Key off */ QChip.channel[ch].key = 0; } else if (QChip.channel[ch].key == 0) { /* Key on */ QChip.channel[ch].key = 1; QChip.channel[ch].offset = 0; QChip.channel[ch].lastdt = 0; } QChip.channel[ch].vol = value; break; case 7: /* unused */ break; case 8: { int pandata = (value - 0x10) & 0x3f; if (pandata > 32) { pandata = 32; } QChip.channel[ch].rvol = QChip.pan_table[pandata]; QChip.channel[ch].lvol = QChip.pan_table[32 - pandata]; QChip.channel[ch].pan = value; } break; case 9: QChip.channel[ch].reg9 = value; break; } } public static void qsound_update(int offset, int length) { int i, j; int rvol, lvol, count; for (i = 0; i < length; i++) { Sound.qsoundstream.streamoutput_Ptrs[0][offset + i] = 0; Sound.qsoundstream.streamoutput_Ptrs[1][offset + i] = 0; } for (i = 0; i < 16; i++) { if (QChip.channel[i].key != 0) { rvol = (QChip.channel[i].rvol * QChip.channel[i].vol) >> 8; lvol = (QChip.channel[i].lvol * QChip.channel[i].vol) >> 8; for (j = 0; j < length; j++) { count = (QChip.channel[i].offset) >> 16; QChip.channel[i].offset &= 0xffff; if (count != 0) { QChip.channel[i].address += count; if (QChip.channel[i].address >= QChip.channel[i].end) { if (QChip.channel[i].loop == 0) { /* Reached the end of a non-looped sample */ QChip.channel[i].key = 0; break; } /* Reached the end, restart the loop */ QChip.channel[i].address = (QChip.channel[i].end - QChip.channel[i].loop) & 0xffff; } QChip.channel[i].lastdt = qsoundrom[(QChip.channel[i].bank + QChip.channel[i].address) % (QChip.sample_rom_length)]; } Sound.qsoundstream.streamoutput_Ptrs[0][offset + j] += ((QChip.channel[i].lastdt * lvol) >> 6); Sound.qsoundstream.streamoutput_Ptrs[1][offset + j] += ((QChip.channel[i].lastdt * rvol) >> 6); QChip.channel[i].offset += QChip.channel[i].pitch; } } } } public static void SaveStateBinary(BinaryWriter writer) { int i; for (i = 0; i < 16; i++) { writer.Write(QChip.channel[i].bank); writer.Write(QChip.channel[i].address); writer.Write(QChip.channel[i].pitch); writer.Write(QChip.channel[i].loop); writer.Write(QChip.channel[i].end); writer.Write(QChip.channel[i].vol); writer.Write(QChip.channel[i].pan); writer.Write(QChip.channel[i].key); writer.Write(QChip.channel[i].lvol); writer.Write(QChip.channel[i].rvol); writer.Write(QChip.channel[i].lastdt); writer.Write(QChip.channel[i].offset); } writer.Write(QChip.data); } public static void LoadStateBinary(BinaryReader reader) { int i; for (i = 0; i < 16; i++) { QChip.channel[i].bank = reader.ReadInt32(); QChip.channel[i].address = reader.ReadInt32(); QChip.channel[i].pitch = reader.ReadInt32(); QChip.channel[i].loop = reader.ReadInt32(); QChip.channel[i].end = reader.ReadInt32(); QChip.channel[i].vol = reader.ReadInt32(); QChip.channel[i].pan = reader.ReadInt32(); QChip.channel[i].key = reader.ReadInt32(); QChip.channel[i].lvol = reader.ReadInt32(); QChip.channel[i].rvol = reader.ReadInt32(); QChip.channel[i].lastdt = reader.ReadInt32(); QChip.channel[i].offset = reader.ReadInt32(); } QChip.data = reader.ReadInt32(); } } }