AY to PSG

For anything related to sound (YM2612, PSG, Z80, PCM...)

Moderator: BigEvilCorporation

KanedaFr
Administrateur
Posts: 1154
Joined: Tue Aug 29, 2006 10:56 am
Contact:

AY to PSG

Post by KanedaFr »

from this post
Shiru wrote:How to rip AY music for Sega's PSG.

You need an emulator that capable to record sound registers dump. I'm not sure about ST emulators, but even if there is no such an emulator, there are options: either add PSG log in some open source emulator (easy, just few lines of code), or use other version of the game, for example ZX Spectrum one - as far as I remember, it has the same music, and there are emulators with AY log feature for sure. Even better, there is music rip from ZX Spectrum Rick Dangerous, and a converter that gives you needed data in very simple format in just few clicks, so no programming required for this stage at all.

Once you got AY registers dump, you need to make simple tool that converts it to Sega's PSG. You can't convert complex AY music, because AY is more complex than PSG, but Rick Dangerous has really simple music, which could be converted easily. You just need to parse the original dump, got frequency dividers and volumes, recalc them into Sega's values (AY has 12-bit dividers, the PSG has 10-bit, and their clock frequency is different). Record the result into some simple format, even VGM (unzipped) will work fine. Data without any compression will be large, but it just tens of KB for all the music.
I used MAME VGM to dump fx from a game who uses 3 AY-3-8910 and VGM tools to clean the rips.
I then tried to convert fx following Shiru's advice....with success !
For people interested in this kind of convertion, I would like to share some info with you.

2 important things to know:
AY could set register hi and low value whenever you want
SN could set register hi only after setting the low value first
so, when converting, you have to keep in memory the low/hi value to write the 2 each times.
This could result in wrong value the first time (when hi or low isn't defined yet) and unwanted write (when AY is changing its 2 values) so your tool must handle this or you'll have to delete them manually (actually it's the way I follow througth a visual tool)

Then, the frequency convertion:
From an old article of Atari Mag, I know

Code: Select all

tone freq = AY Freq / (16*AY reg hi&low value)
From SMS power, I know

Code: Select all

tone freq = SN Freq / (16*2*SN reg value)
so...

Code: Select all

SN reg value = (SN Freq*AY reg value)/(2*AY Freq)

I now have to find a way to handle noise....
tell me if you want to know more or want to add more (Shiru?)
HardWareMan
Very interested
Posts: 753
Joined: Sat Dec 15, 2007 7:49 am

Post by HardWareMan »

Go on!
mic_
Very interested
Posts: 265
Joined: Tue Aug 12, 2008 12:26 pm
Location: Sweden
Contact:

Post by mic_ »

You handle the envelopes as well? Without it, a lot of AY music will probably sound pretty weird.

For the noise, maybe you could do something like this:

Code: Select all

if (AY_noise_period < 10)
    SN_noise = 0;
else if (AY_noise_period < 20)
    SN_noise = 1;
else
    SN_noise = 2;

SN_noise_volume = 0;
div = 0;
for (i = 0; i < 3; i++) {
    SN_noise_volume += AY_amplitude[i] * AY_mixer[i].noiseEnabled;
    div += AY_mixer[i].noiseEnabled;
}

if (div == 0)
    div = SN_noise_volume + 1;

SN_noise_volume /= div;
SN_noise_volume ^= 0x0F;
KanedaFr
Administrateur
Posts: 1154
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr »

mic_ wrote:You handle the envelopes as well? Without it, a lot of AY music will probably sound pretty weird.
unless I'm wrong, it won't be possible.
SN don't know what envelop is...
perhaps a way to do it will be to use the FM ?
For the noise, maybe you could do something like this:
thanks, I'll try this...
just need to understand what noise is exactly (white vs periodic for ex)
mic_
Very interested
Posts: 265
Joined: Tue Aug 12, 2008 12:26 pm
Location: Sweden
Contact:

Post by mic_ »

unless I'm wrong, it won't be possible.
SN don't know what envelop is...
If you want to play the songs on an SMS (in a game) it might be tricky, because you'd waste a lot of CPU time simulating the envelopes. But if you're converting to VGM it's certainly possible. The maximum envelope frequency on the spectrum is what.. about 7 kHz? The amplitude can change 16 steps at that rate, which comes to about 110 kHz maximum for a single amplitude step. The VGM sample rate is 44.1 kHz, so you simply run the envelope generators in your conversion program, and you can choose to either use lower precision or clamp the rate to 44.1 kHz.
just need to understand what noise is exactly (white vs periodic for ex)
I think you can ignore periodic noise for your AY conversions, since it's not something the AY has. It's not really noise anyway, but more like a square wave (which you can use to get better bass sounds when the shift register is controlled by the tone3 oscillator).
KanedaFr
Administrateur
Posts: 1154
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr »

Ok...I'm trying to play the result VGM on the genny, but with a Z80 driver, not 68K size....

from what I know, 1 VGM sample is 1/44100 => 0,02 ms => 20µs
so I used some asm code to fake a 0,020ms delay every time I need it...
but, there is something I don't understand...

Code: Select all

	ld c,0x10 		;7clocks
j0:	ld b,0xFF		;7clocks
j1:	dec b			;4 clocks
	jp nz, j1		;10clocks
	dec c			;4 clocks
	jp nz, j0		;10clocks
this should result to
((14*255)+14)*16 = 57344 clocks at 3.58Mhz = 0,016s
so a lot more from what I need....but it works!
If I reduce the loop (remove the c loop for example) trying to get back to 20µs (not 20ms), it's too fast

Did I miss something ? perhaps my way to calculate clock cycle to time ?
mic_
Very interested
Posts: 265
Joined: Tue Aug 12, 2008 12:26 pm
Location: Sweden
Contact:

Post by mic_ »

Depends on what you're doing between each time you run that loop. VGMs contain delay commands that can delay anywhere between 1 and 65535 samples between each write to the PSG port.
KanedaFr
Administrateur
Posts: 1154
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr »

yeah, I know... it's these delays I'm trying to handle...

pseudo code

Code: Select all

Loop:
  read VGM code
  if delay (0x61...)
      sampleCount = read VGM data
      while sampleCount--
        call sampleLoop
  else if PSG data
    write PSG data
so my 'sampleLoop' must take the time expected : 1/44100 seconds = 20µs
for what ever reason, it only works if it takes 20ms
so is it my z80 code ? my way to compute z80 code time ? or my way to compute a sample time ?
I don't know...
it works like this but I don't know why...very frustrating!
Chilly Willy
Very interested
Posts: 2994
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy »

Well, that delay time is actually 7+16(7+255*14+14), but it's still close enough to .016 to not make a difference in the overall problem. If that delay is actually happening, there's NO WAY you're processing at 44,100 Hz, so the code above is not being executed, or it's not as shown. We'll need more code to figure anything out.
HardWareMan
Very interested
Posts: 753
Joined: Sat Dec 15, 2007 7:49 am

Post by HardWareMan »

Wait, "delay" in the VGM instruction to wait next VInt? Then everything is fine, because 20ms = 50Hz (PAL) / 16ms ~ 60Hz.
And one more thing. To avoid play jitter, you must use external timer, becouse every VInt handler take some time, wich do not must used in delay. Every VInt event must be triggered with equal intervals.
Chilly Willy
Very interested
Posts: 2994
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy »

Yeah, if he's going for vblank timing, he's definitely close enough. As for the vblank int - just leave the ints disabled. There's no need for the vblank int on the z80 for most drivers, especially when you do cycle counting.
mic_
Very interested
Posts: 265
Joined: Tue Aug 12, 2008 12:26 pm
Location: Sweden
Contact:

Post by mic_ »

VGM has four different delay commands (5 actually, but I'm not counting PCM write delays since they're not relevant here).

0x61 nn nn - Wait nnnn samples
0x62 - Wait 735 samples (16 ms)
0x63 - Wait 882 samples (20 ms)
0x7n - Wait n+1 samples
KanedaFr
Administrateur
Posts: 1154
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr »

Ok, it seems I wasn't clear enought.

You're allright with the fact my loop take ~20ms (thanks Chilly Willy for correcting)
Now, my problem is a VGM sample is ~20µs
Like mic_ said in the previous post, a delay of 735 samples is 16ms =
735 x 1/44100
I already handle each delay commands (0x61/0x62/0x63/0x7n) but for whatever reason (and it's WHAT I'm trying to understand), it works with a delay of 20ms not 20µs
I just don't understand
I'll recheck my code because it's just a nonsense...


oh, and yes, I don't use VInt since it's not fast enought
KanedaFr
Administrateur
Posts: 1154
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr »

Ok, I found my error....
I read delay hi then delay low :(
I worked by chance in fact ;)

It's now working using this code for the delay:

Code: Select all

wait1sample:
	ld b,6 			;7 clocks
	dec b			;4 clocks
	dec b			;4 clocks
j1:	dec b			;4 clocks
	jp nz, j1		;10 clocks					
	
	jp waitDelay	;10 clocks
for VGM, 1 sample is 1/44100 = 0,00002267s
on Z80, 1 sample is 3580000/44100 = 81 clocks at 3.58MHz
here we have 7+4+4+(4*14)+10=81

hop, got some fx to test now :)
thanks everyone
HardWareMan
Very interested
Posts: 753
Joined: Sat Dec 15, 2007 7:49 am

Post by HardWareMan »

KanedaFr wrote:Ok, I found my error....
I read delay hi then delay low :(
Litle endian described! :3

Code: Select all

  _0x61 nn nn_ : Wait n samples, n can range from 0 to 65535 (approx 1.49
               seconds). Longer pauses than this are represented by multiple
               wait commands.
  0x62       : wait 735 samples (60th of a second), a shortcut for
               _0x61 0xdf 0x02_
  0x63       : wait 882 samples (50th of a second), a shortcut for
               _0x61 0x72 0x03_
Did you RTFM tightly? :3
Post Reply