When playing back VGMs with SCC sound the timing seems to be off (it may be something else but sounds like timing to me.
I have recorded 3 time the output of Space Manbow, track 3 aka Battle Ship. One is an emulated playback on linux (to verify it is not an issue inside the VGM. The second is the track played back on MSX with vgmplay-msx on a Yamaha CX7 with snatcher cartridge in slot 1, MFR in slot 2. The third is the same track on the same MSX but then through the BOSS demo (which is just using the assembly code from the ROM I believe).
Yes, you can hear some hitches in Space Manbow in particular. The reason is that due to VGMPlay being a generic player, I can’t keep the SCC paged into memory permanently, so it does a quick interslot write whenever data is sent to the SCC. The interslot write is optimised, but still not super quick, plus there’s also the player processing overhead. So e.g. at 00:12.81 that track changes the waveforms of three channels, meaning it does 96 interslot writes in succession for just that plus some other things, which normally happens in the space of 11 ms (almost a whole frame in game), but in VGMPlay it will take longer.
So I’m aware of that issue, but I’m having some trouble coming up with a good solution for it. On the turboR the playback will be better, by the way.
Here’s the log of everything that happens at 00:12.81:
0x00004BCB: A0 00 01 AY8910: Chn A Freq. Fine: 0x01 0x00004BCE: 72 Wait: 3 sample(s) ( 0.07 ms) (total 564995 (00:12.81)) 0x00004BCF: A0 08 12 AY8910: Chn A Volume 13%, Envelope Mode: 8 0x00004BD2: 71 Wait: 2 sample(s) ( 0.05 ms) (total 564997 (00:12.81)) 0x00004BD3: A0 02 93 AY8910: Chn B Freq. Fine: 0x93 0x00004BD6: 71 Wait: 2 sample(s) ( 0.05 ms) (total 564999 (00:12.81)) 0x00004BD7: A0 09 03 AY8910: Chn B Volume 20%, Envelope Mode: 0 0x00004BDA: 78 Wait: 9 sample(s) ( 0.20 ms) (total 565008 (00:12.81)) 0x00004BDB: A0 0B 00 AY8910: Envelope Freq. Fine: 0x00 0x00004BDE: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565009 (00:12.81)) 0x00004BDF: A0 0C 01 AY8910: Envelope Freq. Coarse: 0x01 0x00004BE2: 71 Wait: 2 sample(s) ( 0.05 ms) (total 565011 (00:12.81)) 0x00004BE3: A0 0D 08 AY8910: Envelope Shape: 0x08 0x00004BE6: 74 Wait: 5 sample(s) ( 0.11 ms) (total 565016 (00:12.81)) 0x00004BE7: A0 07 BC AY8910: Enable: Channel AB-, Noise ---, Port IO 0x00004BEA: 7F Wait: 16 sample(s) ( 0.36 ms) (total 565032 (00:12.81)) 0x00004BEB: 7A Wait: 11 sample(s) ( 0.25 ms) (total 565043 (00:12.81)) 0x00004BEC: D2 01 02 E0 K051649: Ch 1: Set Frequency LSB = E0 0x00004BF0: 72 Wait: 3 sample(s) ( 0.07 ms) (total 565046 (00:12.81)) 0x00004BF1: D2 01 03 07 K051649: Ch 1: Set Frequency MSB = 7 0x00004BF5: 73 Wait: 4 sample(s) ( 0.09 ms) (total 565050 (00:12.81)) 0x00004BF6: D2 01 04 C8 K051649: Ch 2: Set Frequency LSB = C8 0x00004BFA: 72 Wait: 3 sample(s) ( 0.07 ms) (total 565053 (00:12.81)) 0x00004BFB: D2 01 05 00 K051649: Ch 2: Set Frequency MSB = 0 0x00004BFF: 73 Wait: 4 sample(s) ( 0.09 ms) (total 565057 (00:12.81)) 0x00004C00: D2 01 06 79 K051649: Ch 3: Set Frequency LSB = 79 0x00004C04: 74 Wait: 5 sample(s) ( 0.11 ms) (total 565062 (00:12.81)) 0x00004C05: D2 02 01 0A K051649: Ch 1: Set Volume: 0xA = 66% 0x00004C09: 73 Wait: 4 sample(s) ( 0.09 ms) (total 565066 (00:12.81)) 0x00004C0A: D2 02 02 08 K051649: Ch 2: Set Volume: 0x8 = 53% 0x00004C0E: 72 Wait: 3 sample(s) ( 0.07 ms) (total 565069 (00:12.81)) 0x00004C0F: D2 02 03 0A K051649: Ch 3: Set Volume: 0xA = 66% 0x00004C13: 73 Wait: 4 sample(s) ( 0.09 ms) (total 565073 (00:12.81)) 0x00004C14: D2 03 00 0E K051649: Channel Enable: -123- 0x00004C18: 75 Wait: 6 sample(s) ( 0.14 ms) (total 565079 (00:12.81)) 0x00004C19: D2 03 00 00 K051649: Channel Enable: ----- 0x00004C1D: D2 00 20 70 K051649: Ch 1: Write Waveform at 00 = 70 0x00004C21: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565080 (00:12.81)) 0x00004C22: D2 00 21 70 K051649: Ch 1: Write Waveform at 01 = 70 0x00004C26: D2 00 22 70 K051649: Ch 1: Write Waveform at 02 = 70 0x00004C2A: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565081 (00:12.81)) 0x00004C2B: D2 00 23 70 K051649: Ch 1: Write Waveform at 03 = 70 0x00004C2F: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565082 (00:12.81)) 0x00004C30: D2 00 24 70 K051649: Ch 1: Write Waveform at 04 = 70 0x00004C34: D2 00 25 70 K051649: Ch 1: Write Waveform at 05 = 70 0x00004C38: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565083 (00:12.81)) 0x00004C39: D2 00 26 70 K051649: Ch 1: Write Waveform at 06 = 70 0x00004C3D: D2 00 27 70 K051649: Ch 1: Write Waveform at 07 = 70 0x00004C41: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565084 (00:12.81)) 0x00004C42: D2 00 28 80 K051649: Ch 1: Write Waveform at 08 = 80 0x00004C46: D2 00 29 80 K051649: Ch 1: Write Waveform at 09 = 80 0x00004C4A: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565085 (00:12.81)) 0x00004C4B: D2 00 2A 80 K051649: Ch 1: Write Waveform at 0A = 80 0x00004C4F: D2 00 2B 80 K051649: Ch 1: Write Waveform at 0B = 80 0x00004C53: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565086 (00:12.81)) 0x00004C54: D2 00 2C 80 K051649: Ch 1: Write Waveform at 0C = 80 0x00004C58: D2 00 2D 80 K051649: Ch 1: Write Waveform at 0D = 80 0x00004C5C: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565087 (00:12.81)) 0x00004C5D: D2 00 2E 80 K051649: Ch 1: Write Waveform at 0E = 80 0x00004C61: D2 00 2F 80 K051649: Ch 1: Write Waveform at 0F = 80 0x00004C65: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565088 (00:12.81)) 0x00004C66: D2 00 30 70 K051649: Ch 1: Write Waveform at 10 = 70 0x00004C6A: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565089 (00:12.81)) 0x00004C6B: D2 00 31 70 K051649: Ch 1: Write Waveform at 11 = 70 0x00004C6F: D2 00 32 70 K051649: Ch 1: Write Waveform at 12 = 70 0x00004C73: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565090 (00:12.81)) 0x00004C74: D2 00 33 80 K051649: Ch 1: Write Waveform at 13 = 80 0x00004C78: D2 00 34 80 K051649: Ch 1: Write Waveform at 14 = 80 0x00004C7C: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565091 (00:12.81)) 0x00004C7D: D2 00 35 80 K051649: Ch 1: Write Waveform at 15 = 80 0x00004C81: D2 00 36 70 K051649: Ch 1: Write Waveform at 16 = 70 0x00004C85: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565092 (00:12.81)) 0x00004C86: D2 00 37 70 K051649: Ch 1: Write Waveform at 17 = 70 0x00004C8A: D2 00 38 70 K051649: Ch 1: Write Waveform at 18 = 70 0x00004C8E: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565093 (00:12.81)) 0x00004C8F: D2 00 39 70 K051649: Ch 1: Write Waveform at 19 = 70 0x00004C93: D2 00 3A 80 K051649: Ch 1: Write Waveform at 1A = 80 0x00004C97: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565094 (00:12.81)) 0x00004C98: D2 00 3B 80 K051649: Ch 1: Write Waveform at 1B = 80 0x00004C9C: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565095 (00:12.81)) 0x00004C9D: D2 00 3C 80 K051649: Ch 1: Write Waveform at 1C = 80 0x00004CA1: D2 00 3D 80 K051649: Ch 1: Write Waveform at 1D = 80 0x00004CA5: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565096 (00:12.81)) 0x00004CA6: D2 00 3E 80 K051649: Ch 1: Write Waveform at 1E = 80 0x00004CAA: D2 00 3F 80 K051649: Ch 1: Write Waveform at 1F = 80 0x00004CAE: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565097 (00:12.81)) 0x00004CAF: D2 03 00 0E K051649: Channel Enable: -123- 0x00004CB3: 74 Wait: 5 sample(s) ( 0.11 ms) (total 565102 (00:12.81)) 0x00004CB4: D2 03 00 00 K051649: Channel Enable: ----- 0x00004CB8: D2 00 40 00 K051649: Ch 2: Write Waveform at 00 = 00 0x00004CBC: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565103 (00:12.81)) 0x00004CBD: D2 00 41 19 K051649: Ch 2: Write Waveform at 01 = 19 0x00004CC1: D2 00 42 31 K051649: Ch 2: Write Waveform at 02 = 31 0x00004CC5: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565104 (00:12.81)) 0x00004CC6: D2 00 43 47 K051649: Ch 2: Write Waveform at 03 = 47 0x00004CCA: D2 00 44 5A K051649: Ch 2: Write Waveform at 04 = 5A 0x00004CCE: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565105 (00:12.81)) 0x00004CCF: D2 00 45 6A K051649: Ch 2: Write Waveform at 05 = 6A 0x00004CD3: D2 00 46 75 K051649: Ch 2: Write Waveform at 06 = 75 0x00004CD7: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565106 (00:12.81)) 0x00004CD8: D2 00 47 7D K051649: Ch 2: Write Waveform at 07 = 7D 0x00004CDC: D2 00 48 7F K051649: Ch 2: Write Waveform at 08 = 7F 0x00004CE0: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565107 (00:12.81)) 0x00004CE1: D2 00 49 7D K051649: Ch 2: Write Waveform at 09 = 7D 0x00004CE5: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565108 (00:12.81)) 0x00004CE6: D2 00 4A 75 K051649: Ch 2: Write Waveform at 0A = 75 0x00004CEA: D2 00 4B 6A K051649: Ch 2: Write Waveform at 0B = 6A 0x00004CEE: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565109 (00:12.81)) 0x00004CEF: D2 00 4C 5A K051649: Ch 2: Write Waveform at 0C = 5A 0x00004CF3: D2 00 4D 47 K051649: Ch 2: Write Waveform at 0D = 47 0x00004CF7: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565110 (00:12.81)) 0x00004CF8: D2 00 4E 31 K051649: Ch 2: Write Waveform at 0E = 31 0x00004CFC: D2 00 4F 19 K051649: Ch 2: Write Waveform at 0F = 19 0x00004D00: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565111 (00:12.81)) 0x00004D01: D2 00 50 80 K051649: Ch 2: Write Waveform at 10 = 80 0x00004D05: D2 00 51 90 K051649: Ch 2: Write Waveform at 11 = 90 0x00004D09: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565112 (00:12.81)) 0x00004D0A: D2 00 52 A0 K051649: Ch 2: Write Waveform at 12 = A0 0x00004D0E: D2 00 53 B0 K051649: Ch 2: Write Waveform at 13 = B0 0x00004D12: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565113 (00:12.81)) 0x00004D13: D2 00 54 C0 K051649: Ch 2: Write Waveform at 14 = C0 0x00004D17: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565114 (00:12.81)) 0x00004D18: D2 00 55 D0 K051649: Ch 2: Write Waveform at 15 = D0 0x00004D1C: D2 00 56 E0 K051649: Ch 2: Write Waveform at 16 = E0 0x00004D20: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565115 (00:12.81)) 0x00004D21: D2 00 57 F0 K051649: Ch 2: Write Waveform at 17 = F0 0x00004D25: D2 00 58 00 K051649: Ch 2: Write Waveform at 18 = 00 0x00004D29: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565116 (00:12.81)) 0x00004D2A: D2 00 59 10 K051649: Ch 2: Write Waveform at 19 = 10 0x00004D2E: D2 00 5A 20 K051649: Ch 2: Write Waveform at 1A = 20 0x00004D32: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565117 (00:12.81)) 0x00004D33: D2 00 5B 30 K051649: Ch 2: Write Waveform at 1B = 30 0x00004D37: D2 00 5C 40 K051649: Ch 2: Write Waveform at 1C = 40 0x00004D3B: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565118 (00:12.81)) 0x00004D3C: D2 00 5D 50 K051649: Ch 2: Write Waveform at 1D = 50 0x00004D40: D2 00 5E 60 K051649: Ch 2: Write Waveform at 1E = 60 0x00004D44: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565119 (00:12.81)) 0x00004D45: D2 00 5F 70 K051649: Ch 2: Write Waveform at 1F = 70 0x00004D49: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565120 (00:12.81)) 0x00004D4A: D2 03 00 0E K051649: Channel Enable: -123- 0x00004D4E: 73 Wait: 4 sample(s) ( 0.09 ms) (total 565124 (00:12.81)) 0x00004D4F: D2 03 00 00 K051649: Channel Enable: ----- 0x00004D53: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565125 (00:12.81)) 0x00004D54: D2 00 60 30 K051649: Ch 3/4: Write Waveform at 00 = 30 0x00004D58: D2 00 61 50 K051649: Ch 3/4: Write Waveform at 01 = 50 0x00004D5C: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565126 (00:12.81)) 0x00004D5D: D2 00 62 50 K051649: Ch 3/4: Write Waveform at 02 = 50 0x00004D61: D2 00 63 30 K051649: Ch 3/4: Write Waveform at 03 = 30 0x00004D65: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565127 (00:12.81)) 0x00004D66: D2 00 64 00 K051649: Ch 3/4: Write Waveform at 04 = 00 0x00004D6A: D2 00 65 00 K051649: Ch 3/4: Write Waveform at 05 = 00 0x00004D6E: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565128 (00:12.81)) 0x00004D6F: D2 00 66 10 K051649: Ch 3/4: Write Waveform at 06 = 10 0x00004D73: D2 00 67 40 K051649: Ch 3/4: Write Waveform at 07 = 40 0x00004D77: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565129 (00:12.81)) 0x00004D78: D2 00 68 60 K051649: Ch 3/4: Write Waveform at 08 = 60 0x00004D7C: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565130 (00:12.81)) 0x00004D7D: D2 00 69 70 K051649: Ch 3/4: Write Waveform at 09 = 70 0x00004D81: D2 00 6A 60 K051649: Ch 3/4: Write Waveform at 0A = 60 0x00004D85: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565131 (00:12.81)) 0x00004D86: D2 00 6B 30 K051649: Ch 3/4: Write Waveform at 0B = 30 0x00004D8A: D2 00 6C F0 K051649: Ch 3/4: Write Waveform at 0C = F0 0x00004D8E: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565132 (00:12.81)) 0x00004D8F: D2 00 6D E0 K051649: Ch 3/4: Write Waveform at 0D = E0 0x00004D93: D2 00 6E E0 K051649: Ch 3/4: Write Waveform at 0E = E0 0x00004D97: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565133 (00:12.81)) 0x00004D98: D2 00 6F 00 K051649: Ch 3/4: Write Waveform at 0F = 00 0x00004D9C: D2 00 70 20 K051649: Ch 3/4: Write Waveform at 10 = 20 0x00004DA0: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565134 (00:12.81)) 0x00004DA1: D2 00 71 20 K051649: Ch 3/4: Write Waveform at 11 = 20 0x00004DA5: D2 00 72 10 K051649: Ch 3/4: Write Waveform at 12 = 10 0x00004DA9: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565135 (00:12.81)) 0x00004DAA: D2 00 73 C0 K051649: Ch 3/4: Write Waveform at 13 = C0 0x00004DAE: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565136 (00:12.81)) 0x00004DAF: D2 00 74 A0 K051649: Ch 3/4: Write Waveform at 14 = A0 0x00004DB3: D2 00 75 90 K051649: Ch 3/4: Write Waveform at 15 = 90 0x00004DB7: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565137 (00:12.81)) 0x00004DB8: D2 00 76 A0 K051649: Ch 3/4: Write Waveform at 16 = A0 0x00004DBC: D2 00 77 C0 K051649: Ch 3/4: Write Waveform at 17 = C0 0x00004DC0: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565138 (00:12.81)) 0x00004DC1: D2 00 78 00 K051649: Ch 3/4: Write Waveform at 18 = 00 0x00004DC5: D2 00 79 00 K051649: Ch 3/4: Write Waveform at 19 = 00 0x00004DC9: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565139 (00:12.81)) 0x00004DCA: D2 00 7A D0 K051649: Ch 3/4: Write Waveform at 1A = D0 0x00004DCE: D2 00 7B B0 K051649: Ch 3/4: Write Waveform at 1B = B0 0x00004DD2: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565140 (00:12.81)) 0x00004DD3: D2 00 7C B0 K051649: Ch 3/4: Write Waveform at 1C = B0 0x00004DD7: D2 00 7D D0 K051649: Ch 3/4: Write Waveform at 1D = D0 0x00004DDB: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565141 (00:12.81)) 0x00004DDC: D2 00 7E 00 K051649: Ch 3/4: Write Waveform at 1E = 00 0x00004DE0: D2 00 7F 00 K051649: Ch 3/4: Write Waveform at 1F = 00 0x00004DE4: 70 Wait: 1 sample(s) ( 0.02 ms) (total 565142 (00:12.82)) 0x00004DE5: D2 03 00 0E K051649: Channel Enable: -123-
Okay, so I’m no expert by any means of the codebase or how music playback works. So forgive me if in advance for my possibly stupid remarks.
What I am getting from what you said is you can’t keep SCC paged all the time, vgmplay-msx being a generic player and all. So does this mean before you send any commands to any music chip you page it into memory, send the commands, and then unpage? If so then maybe it is an idea to not unpage at the end of the commands for that chip and keep a global state of the chip ‘in page’? Then for those chips that need paging (I guess not all chips need that) and commands need to be send to, you first check if the chip is already paged and if so you can just send the commands. If it is not paged you unpage the paged chip and page the chip commands have to be sent to. My guess is that most VGM’s have just one chip that needs paging so this might reduce the work on the CPU (unless all these checks take up just as much or more time).
That’s correct.
Keeping the chips paged in (SFG: page 0, SCC: page 2, Neotron: page 2) is not a bad idea in itself, and that is what a specialised player would do, however the problem is that I use the same memory location for code (pages 0-1) or VGM data (page 2).
Okay, is it than an idea to use page 3 for VGM data? It looks like then you would be able to keep SCC and Neotron paged. And I’ve not noticed these issues on SFG (but I am not as familiar with those tracks)
And if you could keep the ‘player’ code limited to one page (page 1) and have e.g. the vgm loading and gunzip code in page 0 then you may not need page 0 during playback...
Indeed on SFG there’s no issue, only SCC really just because it sometimes has a lot of burst data for wave updates, Space Manbow in particular.
Page 3 is the system area, VGM data is stored in the memory mapper so I can only page in 16K banks. I could have a smaller buffer in page 3 which gets populated from the memory mapper, but that would add copying overhead. Something to consider though.
I think your last suggestion has the most merit, but it’s fairly complicated to do since I’m quite tight on memory already, it’s a bit of a puzzle. However I do eventually need to do some thorough rearranging anyway for features like on-the-fly decompression and a UI.
Some other ideas;
Another possibility would be to scan the VGM data prior to playback and replace sequences of wave data updates with a custom block transfer command. Truthfully it’d be way too much work and added complexity, as well as adding an additional wait before playback and other difficulties, so I’m not going to do it, but worth mentioning anyway.
Or when wave data writes are encountered, do a forward scan of the VGM data to process all wave data writes in one go until a non-wave data command is encountered or the wait time exceeds a certain amount. May be interesting to explore, not sure it would actually save time though.
I investigated some more, the (optimised) interslot write overhead is 122 cycles, but it looks like all the processing here together takes roughly 800 cycles per command. And some 200 of them arrive in the span of milliseconds there at 00:12.81, taking about 45 ms in VGMPlay.
Even if I reduce the total time taken by e.g. using vgm_facc to reduce the timing resolution (fewer wait commands) and using an SCC instead of SCC+ (on SCC+ channel 4/5 writes are doubled due to using SCC+ mode, this could be optimised), which more or less brings the total time spent there down to 22 ms orso, it’s still audible. Even in the parts where only 1 channel’s waveform is updated, I can hear it.
So maybe we’re focusing too much on the interslot write, and need to take a broader view, also given that those optimisations themselves introduce a cost elsewhere so it will never go down to 0. I wonder how far do we need to optimise to get down to an acceptable level? Even halving the time does not seem enough. Is it even possible to optimise so much? It sounds good on turboR, so at some point it must be ok, but the R800 is so much faster than the Z80…
Yeah R800 is much faster...
I’m not sure it is feasible but you could consider ‘compiling’ vgm into playable code instead of ‘interpreting’, if you get what I mean. That would add time before playback starts but could deliver more stable playback.
As I mentioned, at some point in the future I’d like to do on the fly decompression, since the non-PCM-streaming songs have plenty of free CPU time during playback. If that pipeline is in place then it may also be possible to insert an optimisation step without increasing the loading time.
But that’s not going to happen anytime soon probably. I wish I could come up with a simpler solution.