OPL3 C++ research implementation

An investigation into the behaviour of the Yamaha OPL3 chipset.
With a view to a more accurate emulator

OPL3 C++ research implementation

Postby sto » Thu Nov 08, 2012 4:45 am

Hi people,

this is my first post here, and I just wanted to inform you that I took many of your discoveries and started working on a C++ implementation. It is mainly a research project, and the first goal I achieved was to get rid of all floating point arithmetic and use only bit shifts and such. I already have output that roughly sounds like music, but unfortunately it is pretty disturbed, and I don't know why, thus I'd like to ask you people for help. The project is hosted on sourceforge: http://sourceforge.net/p/peepeeplayer/code (the OPL branch, in the src/ymf262 directory)

So if anyone would take a look and give me some comments - that would be great!

Thanks in advance,
Sto
sto
 
Posts: 60
Joined: Thu Nov 08, 2012 4:33 am

Re: OPL3 C++ research implementation

Postby opl3 » Thu Nov 08, 2012 3:57 pm

Interesting, I have to take a look and maybe contribute too, because I don't have the energy to start maintaining any public projects.

I have my own C implementation, so I don't know why C++ would be useful here or does it really matter. The underlying algorithm is still the same. True, no floating point is needed at all, and sometimes other emulators just use floats to generate integer lookup tables, and sometimes lookup tables are used instead of pure bit shifting to save time, but meanwhile that takes more memory.

My implementation does has only the basic operator functionality, like feedback, amplitude modulation (tremolo), but no vibrato, and no rhythm support. I have a few "players" that take in some different formats like DosBox DRO (new DRO, not old DRO) format, and .laa files, and they simulate the output to raw PCM file, that can be opened in audio editor or converted to .wav file by just adding a header. The concept of time is tied to number of generated samples, so there is no sampling rate conversion or anything, the sampling rate is just assumed as 49716 (integer), while the chip is actually run with a nominal 14318180 Hz clock, divided by 288. The clock most likely does not have tolerance tighter than 20ppm, could even be up to 100ppm, so I don't think rounding the sampling rate to nearest integer is any problem.
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: OPL3 C++ research implementation

Postby sto » Thu Nov 08, 2012 6:17 pm

Well, I chose C++ because the player I'm trying to integrate the emulator into is written in C++ and I simply don't like C ;)

And to my problems: First, it seems that my feedback calculation/integration is wrong, but I cannot figure out why. Second, I get a channel or operator outputting very strange things, which I also cannot trace down. I'm a programmer, not a hardware genius (though I know the basics of multiplexers/gates/etc.), so there is quite some complexity in it which is a little bit too much for me. It's not easy to convert a grey box hardware design to a clean software design, but exactly that's my goal, and I want to achieve it. If anybody wants a C implementation - it's GPL'ed, so there is no problem :)
sto
 
Posts: 60
Joined: Thu Nov 08, 2012 4:33 am

Re: OPL3 C++ research implementation

Postby opl3 » Fri Nov 09, 2012 7:49 am

Can you describe what kind of weird operator or channel output you are getting?

You have implemented a lot in general, but many things need some adjustments to be absolutely accurate with the original. A lot of the info is already on this forum.

I don't think you should average the feedback first, you should just sum the two previous values together and then shift by correct amount to get the phase input. At least I did not average them first, and found it to be correct when shifted to correct position. Because you average it, you effectively drop one bit off first, which is important as the value is incorrect then when you shift if back to correct position.

Another thing is you really must not average output of two operators as the output of channel, as channel output should be pure sum of two operators (when they are in parallel output mode).

Other things I noticed you should check:
1) You seem to reset the envelope generator to silence at key-on event. This is incorrect, as it should start the attack from whatever amplitude it happens to be.
2) Your phase generator seems to be calculated differently according to what we have found here. BLOCK=0 will drop one bit off from FNUM and BLOCK=1 will use the value as it is before multiplier is applied. Also MULTI=0.5x will drop one more bit off, and MULTI=1x will use the value as it is.
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: OPL3 C++ research implementation

Postby sto » Fri Nov 09, 2012 11:54 am

Thanks for the reply. I should have mentioned that the implementation I use is a heavily hacked version of http://opl3.cozendey.com/ - I have invested many hours last weekend to analyze the math and to get rid of the floating point values, but the basic logic is still the same. I'm currently at work, but I will create an mp3 at home in a few hours so you can hear the output, but first I will implement your suggestions.

And yes, I am aware of the average of two operators - I somehow didn't manage to correct it yet, even if it is only one line in code :)
sto
 
Posts: 60
Joined: Thu Nov 08, 2012 4:33 am

Re: OPL3 C++ research implementation

Postby opl3 » Fri Nov 09, 2012 3:20 pm

Hehe, I remember exchanging few emails with the author (Robson), he did a very good job, while his implementation is not directly usable in players and emulators because it uses heavy floating point operations and is implemented in java. I do sometimes use his website to play stuff I like, so in that sense it is a marvellous web app. The one thing I know is that it is based on how the emulated waves and original waves sound and look when played on Sound Blaster, but the OPL3 chip uses a separate DAC than PCM channel of Sound Blaster so they are not directly comparable. I have directly captured the digital output with USB logic analyser, so I can compare the output of my emulator code exactly with the chip output digitally.

But I would have suggested a different project to start modifying, because this was floating point and java. I have many times thought I should poke around ScummVM or DosBox implementations, because after the modifications are done, it could perhaps be imported back to those projects very easily, either replacing the original emulation (or one of them as they have multiple implementations already), or add as new implementation.

If I wanted to join your project somehow, discussing the implementation or improving the code, where would I start? I guess I would need to create a Sourceforge account?
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: OPL3 C++ research implementation

Postby sto » Fri Nov 09, 2012 4:21 pm

That's right, you need a sourceforge account and a ssh key if you want to commit code, but for read-only access there is anonymous access available :)

And btw, I actually have tried out working with the DosBox code, but I didn't have that much success, mainly because it uses magic values and named constants all the time which in other places in the code get reverted while some pieces of code rely on it - whereas the java code looked clean and "hackable".

And as promised, here's an example: http://earvillage.square7.ch/downloads/1869dang.hsc.mp3

// EDIT: If you want to contribute but do not want to create an account, you could anonymously check out the repo and send me patches using "git format-patch" - I'd be glad to apply them, and your name (or the one you tell git) would appear in the commit messages ;)

// EDIT 2: I have changed a few things and I have discovered that key-on/-off, egt and am are not handled correctly, but I still cannot find out why. Some HSCs sound correct when I use "if(!egt)" in the envelope generator, others sound correct when I negate the expression - listen to it here: http://earvillage.square7.ch/downloads/ ... v2.hsc.mp3

// EDIT 3: I finally spotted that the sustain level was checked wrong - instead of "m_env >= (1 << (m_sl+3))" it should have been "m_env >= (m_sl<<4)". Here's the output (it's sampled at 49.7kHz, but the mp3 uses 44.1kHz): http://earvillage.square7.ch/downloads/ ... v3.hsc.mp3
sto
 
Posts: 60
Joined: Thu Nov 08, 2012 4:33 am

Re: OPL3 C++ research implementation

Postby sto » Tue Nov 13, 2012 9:14 pm

Finally, it seems that I got it:
WVSILVIA.HSC: https://www.youtube.com/watch?v=6Aqdoj68mY4 (original OPL3) and my output: http://earvillage.square7.ch/downloads/wvsilvia.hsc.mp3
1869DANG.HSC: http://earvillage.square7.ch/downloads/1869dang-v4.mp3
PLTHEME1.HSC: https://www.youtube.com/watch?v=vhZpB_MyswE - http://earvillage.square7.ch/downloads/pltheme1.mp3
OSDESERT.HSC: http://www.youtube.com/watch?v=puF-XcLD7tQ - http://earvillage.square7.ch/downloads/osdesert.mp3

What I discovered is that it seems that the OPL uses a N.9 bit fractional int for nearly everything as that makes life really easy. Another thing is the maximum envelope rate which I changed to 63 - that was needed to get a similar output to the dosbox emulator which uses a maximum of 60, but without changing the maximum the release would have been too slow which you could clearly hear.
sto
 
Posts: 60
Joined: Thu Nov 08, 2012 4:33 am

Re: OPL3 C++ research implementation

Postby opl3 » Tue Nov 13, 2012 10:35 pm

I know the phase generator definitely uses 9 fractional bits, but what else are you referring to with "almost everything"?

I need to take a better look at your code now that you have gotten it better.

What formats your player can play in addition to .HSC? Do you know if the .HSC implementation matches the original player implementation, I mean does it result into same register writes? It seems the .HSC format uses DOS timer timing of approximately 18.2Hz, or more precisely, as the original Adlib card uses the motherboard crystal oscillator like the timer chip, there should be exactly 2730 and 2/3 samples between simulated timer interrupts.

I have a .LAA player that replicates original OPL register writes at least with the song I have verified it so far, don't know about timing, I just assume it plays midi with as fine timing it can, not reproducing the original coarse timing. I might have a DosBox .DRO player too. I've been thinking about .D00 player, but I ended up logging the register writes that happen every 70Hz timer ticks, because it was easier to play back the register dump than actually play the .D00 file. I think some player runs the original .D00 player code inside a virtual CPU, executing the original code in an emulator, to get it right, instead of replicating all the different versions of .D00 players with native binary compiled from C source code.
opl3
 
Posts: 55
Joined: Sun Sep 26, 2010 8:11 pm

Re: OPL3 C++ research implementation

Postby carbon14 » Wed Nov 14, 2012 11:42 am

My experiments showed that the decay at 63 was the same as that at 60

results

Have I misunderstood what you are saying?
User avatar
carbon14
 
Posts: 124
Joined: Tue Aug 05, 2008 9:11 am
Location: York, England

Next

Return to Yamaha OPL-3 research

Who is online

Users browsing this forum: No registered users and 1 guest