Page 1 of 3
VGM playback on the SPC
Posted: Fri Jul 02, 2010 4:11 pm
by mic_
Here's something I've been working on for the past couple of days:
http://www.youtube.com/watch?v=AUlwVFGTmzY
It's a VGM player running on the SPC. SN76489 only, naturally. VGM files are played as-is. The SNES CPU sends the entire file over to the SPC along with my playback code and some additional data, and then the SPC takes over.
There are still some issues to sort out before I make a proper release.
Re: VGM playback on the SPC
Posted: Fri Jul 02, 2010 7:56 pm
by Hamtaro126
mic_ wrote:Here's something I've been working on for the past couple of days:
http://www.youtube.com/watch?v=AUlwVFGTmzY
It's a VGM player running on the SPC. SN76489 only, naturally. VGM files are played as-is. The SNES CPU sends the entire file over to the SPC along with my playback code and some additional data, and then the SPC takes over.
There are still some issues to sort out before I make a proper release.
Great! This is a cool start of a engine for SNES/SPC-700
Very impressive stuff!
Posted: Sun Jul 04, 2010 3:05 am
by mic_
I've created
a tool that converts from .VGM to .SPC.
Give it a .VGM/.VGZ file and it'll spit out an .SPC file with the VGM data and my playback code.
The zip also includes the SPC-700 assembly code for the player.
Posted: Sun Jul 04, 2010 8:19 am
by blargg
Oh man, I'm really tempted to see if I can get that technique for crisp square waves at any frequency working with this. Glad you included the source.

Posted: Sun Jul 04, 2010 9:01 am
by mic_
Looks like I had forgot to include the frequency table in the zip though (oops). I've reuploaded it with freqtb.inc included.
Another thing I'm considering is adding support for NeoGeo Pocket VGMs. Afaik the sound chip in the NGP is nearly identical to the SN76489, except that it has separate volume controls for right and left, and is controlled through two ports instead of one. The problem is that NGP songs wouldn't pack as well with the compression scheme I'm using.
Posted: Sun Jul 04, 2010 9:53 am
by blargg
Quick thought on timing, after I skimmed the source and noticed you were using timers but were considering cycle-timing. At first I was like, yeah, cycle-time it, duh, but then you'd have to cycle-time everything. Since you can have the third timer increment T2OUT every 16 cycles, you can get 131072 per second. Then just divide by 3 to get a tick almost every 1/44100 second (about 1% less). Maybe you already do this.
Posted: Sun Jul 04, 2010 10:16 am
by mic_
Since you can have the third timer increment T2OUT every 16 cycles, you can get 131072 per second.
I do use timer2. But I thought it ran at 64kHz.
Posted: Sun Jul 04, 2010 10:35 am
by blargg
Yeah, sorry, you're right. I messed up in my calculations. And duh, anything above 32 kHz is wasted anyway since the DSP only runs at 32 kHz. So it would make sense to just run timer 2 with a period of 2 (*16), giving you 32 kHz, and keep track of the fraction (which has to be what you're doing already). I'll refrain from further ignorance and wait until I have actually tried the source out and read it more closely.

Posted: Sun Jul 04, 2010 10:49 am
by mic_
So it would make sense to just run timer 2 with a period of 2 (*16), giving you 32 kHz, and keep track of the fraction (which has to be what you're doing already).
Actually I keep a table of floor(n*64/44.1), where n=[1..16].
But I altered some timer values because it turned out that with the overhead from all the other code I needed to run the timers for slightly shorter periods than what would've been the case under optimal conditions.
Posted: Mon Jul 05, 2010 1:53 pm
by blargg
Any chance on getting the source to vgm2spc.exe?
The scheme I want to try involves having several square wave samples, and storing in the sample index and pitch to play it at in the converted VGM data. The samples have periods of 16, 32, 48, 64, etc. perhaps more inbetween if necessary. I compared a 48 played at pitch 1.5 to a 32, and the timbre hardly changes, so this seems a reasonable granularity.
BTW, the syntax for BBS and other bit instructions (clr1, set1, etc.) in wla-dx is bbs addr .bit,branch_label. The space must be present. wla-dx is charming, isn't it?
Posted: Tue Jul 06, 2010 1:43 am
by mic_
Hmm.. Doesn't a change to SRCNx require a new KON to take effect? Then how do you plan to get around the distortion that would cause?
EDIT: Nevermind, I had an INC too many so I ended up writing to one of the ADSR regs instead of SRCN.
I've actually implemented your idea in a more limited form (or maybe it's not what you had in mind at all..). For values of P < 0x2000 I use a sample twice as wide and double the value of P. Something like this:
Code: Select all
mov SAMPLE,#0
mov y,#$20
mov a,#$00
cmpw ya,PVAL
bcc +
asl PVAL
rol PVAL+1
mov SAMPLE,#2
+:
This makes low frequency square waves more square.
It could easily be extended one step further to use a sample with 4x the sample rate if P < 0x1000.
Posted: Tue Jul 06, 2010 9:17 am
by blargg
Cool, that's the idea. I'm not that interested in messing with SNES stuff right now, so guess I'll leave it to you. BTW, the reason I used more samples than one per octave is that pitches going above 1.5 or so caused some audible aliasing. Octaves are easier to handle the calculations for, though. I had concluded that the host-side program would have to calculate the pitches if you were using more than one sample per octave.
Posted: Tue Jul 06, 2010 10:27 am
by mic_
I've made an updated version available
here.
It takes care of most of the TODOs, adds dynamic switching to samples with higher sample rates when possible, has an adjusted volume table, and possibly other things I've forgotten. On the converter side I've added slightly better compression and partial GD3->ID666 conversion.
The source for the converter is now also included in the zip.
Posted: Tue Jul 06, 2010 12:38 pm
by Bregalad
Hmm.. Doesn't a change to SRCNx require a new KON to take effect? Then how do you plan to get around the distortion that would cause?
Surprisingly enough, it doesn't. It takes effect when the sample loops, where the "new" loop value from the new instrument is used instead.
I think Dragon Quest 5 does this for the "sap" spell - it simulate a NES sound effect cycling through duty cycles (but sounds incredibly muffled).
BTW it's also possible to dynamically change BRR sample data - Chrono Trigger does this for sample #16 to constantly change the duty cycle - resulting in a cool effect. Doing something like that (by changing the position of the END flag in the BRR sample) could be another way to change the frequency of the playing sample dynamically.
Posted: Tue Jul 06, 2010 3:19 pm
by caitsith2
Awesome work on vgm2spc. I have a bug report for the current version.
Code: Select all
if ((outData.size() + extraDataBlock.size()) > 0xEEC0)
{
printf("Error: the vgm data is too large to fit. the maximum size after packing is 61120 bytes\n");
delete [] vgmData;
outData.clear();
return;
}
That should actually be the following code, based upon the addition of the second frequency table.
Code: Select all
if ((outData.size() + extraDataBlock.size()) > 0xE8C0)
{
printf("Error: the vgm data is too large to fit. the maximum size after packing is 59584 bytes\n");
delete [] vgmData;
outData.clear();
return;
}
Ideally though, we should never hard code this value, and instead, get the file size of s-smp_player.bin every time, to determine max size of the output vgm.