VGM playback on the SPC

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

VGM playback on the SPC

Post 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.
User avatar
Hamtaro126
Posts: 783
Joined: Thu Jan 19, 2006 5:08 pm

Re: VGM playback on the SPC

Post 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!
AKA SmilyMZX/AtariHacker.
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Post 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.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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. :)
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Post 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.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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.
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Post 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.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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. :)
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Post 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.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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?
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Post 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.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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.
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Post 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.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post 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.
Useless, lumbering half-wits don't scare us.
caitsith2
Posts: 74
Joined: Mon May 26, 2008 11:41 pm

Post 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.
Post Reply