VOGONS


First post, by BenMcLean

User metadata
Rank Newbie
Rank
Newbie

I am developing an open source VR re-creation of Wolfenstein 3-D for the Oculus Quest and PC VR in C# using the Godot engine. Unlike previous efforts to bring Wolfenstein 3-D to VR, mine aims to be very close to an exact, pixel-perfect reproduction with identical assets lifted from the original DOS files at runtime. Also plan to support Spear of Destiny and Super 3-D Noah's Ark.

This isn't a source port: I'm aiming to do a high level imitation of the game's rules but take every possible advantage of modern hardware and programming techniques behind the scenes, which is why it uses the Godot engine instead of the Wolfenstein 3-D engine.

Here's where I need help from you all:

Because authenticity to the original 1992 title is super important, I want to include real time OPL emulation. I found this C# port of the DOSBOX OPL emulation to get me started, but it has a major problem: missing notes.

Back in the day, Wolfenstein 3-D used interrupts to allow feeding note data into the Adlib/Soundblaster card at 700 Hz. But modern systems don't have interrupts. I can't send note data in at exactly 700 Hz, so the result is that the "start" signal and the "stop" signal for some notes end up arriving at the emulated synthesizer chip at the same time, so the note gets skipped. This is especially noticeable on the track for level 2 of the shareware (where Robert Prince composed some faster arpeggios) and on the Adlib sound effects. The DigiSounds are perfect but the Adlib sound effects sound like they got run over by a truck.

I hate this, but I haven't been able to figure out what to do about it. Microsoft offers a high precision timer in C# for music programming, but only for Windows. The Oculus Quest is an Android device so this needs to run on both Android and PC. I tried to use multi-threading to solve this but the multi-threaded version got the same crappy results.

I know DOSBOX manages to have good sound, including on programs that use DOSBOX's OPL emulation to play back IMF sequences on Android, so what I'm trying to do must be possible. How is this accomplished? And how can I get whatever they did to work on my new program?

I could really use some help with this! If nobody with insider knowledge on OPL emulation gets involved to help me out, then I'll just have to release a Wolfenstein 3-D VR program with crappy sound.

And don't tell me to pre-convert all the songs. I'm not doing that.

(later edit) Oh, and by the way, if you happen to know anyone who really likes 3D modelling, I need somebody to make 3D models of the Wolfenstein 3-D weapons to use for the player's hands in this game for me. I don't do 3D modelling so if I don't get help with this part, then the game's going to come out with generic VR controllers for guns. All contributors will of course be thanked by name (or anonymous if requested) in the credits.

Reply 1 of 8, by Tiido

User metadata
Rank l33t
Rank
l33t

Your best bet is emulating (SampleRate / 700Hz) amount of samples with the OPL core and then dumping next set of music data to it and repeat the process, no need for any timers. You probably need to put these little output sample chunks into a larger buffer that the OS will then play for you, in case of 44100Hz / 700 you get 63 samples which is a very short buffer that is probably very difficult to get going without underruns etc. but something larger will probably work very well but that's up to experimentation to find optimal playback buffer size that works without problems.

T-04YBSC, a new YMF71x based sound card & Official VOGONS thread about it
Newly made 4MB 60ns 30pin SIMMs ~
mida sa loed ? nagunii aru ei saa 😜

Reply 2 of 8, by BenMcLean

User metadata
Rank Newbie
Rank
Newbie

The way this emulator is written uses the system clock to time things. There isn't a way to send in a buffer of note data. Its API only takes in note signals in real time. If I could figure out how to send in a buffer, I would.

Reply 3 of 8, by BenMcLean

User metadata
Rank Newbie
Rank
Newbie

Oh wait, you're talking about a different sample rate for the output?

This is already coming from a buffer provided by the emulator into a buffer provided by the Godot engine. If I add another buffer on the output then the sound will be triple buffered! Won't that cause higher latency than is tolerable for the sound effects?

Reply 4 of 8, by Tiido

User metadata
Rank l33t
Rank
l33t

No, not an additional buffer. You control the amount of samples you get from the emulator, normally you have the emulation core that you run for as many cycles as you need to generate necessary amount of samples into a buffer. That output you then resampled to some standard sample rate in case of sample accurate emulation (none of these old chips run at any standard rate, OPL2/3 sample rate is 3579545 / 72 for example) and you output it via some OS, library, etc. calls, and it is up to you to keep supplying enough samples to keep the buffers filled to avoid underruns. OS or library will provide you means to tell you when another buffer needs to be dispatched and that determines your timing more or less.
Like I said before, you need to generate (SampleRate / PlaybackTimer) amount of samples per block, each block using parameters sent to the chip from the music engine and SFX stuff. As far as music goes, it doesn't even need to be realtime, you can generate any amount of data ahead of time since you always know what music data is needing to be written in the chip and can render ahead a stream that you then play back.

T-04YBSC, a new YMF71x based sound card & Official VOGONS thread about it
Newly made 4MB 60ns 30pin SIMMs ~
mida sa loed ? nagunii aru ei saa 😜

Reply 5 of 8, by digistorm

User metadata
Rank Member
Rank
Member

As far as music goes, it doesn't even need to be realtime, you can generate any amount of data ahead of time since you always know what music data is needing to be written in the chip and can render ahead a stream that you then play back.

I think he means that Wolf3D also uses OPL partially for the sound effects. Maybe the sound effects go to a fixed channel of the OPL chip - I don't know - but at some point you have to calculate the real time effects and combine them into the OPL output to be sent upstream to some audio API or driver.

Reply 6 of 8, by Tiido

User metadata
Rank l33t
Rank
l33t

Yeah, SFX do require the realtime factor though there are ways around it too. Scope of this project would allow music and SFX to be separated and run with their dedicated emulators so that channel allocation never becomes an issue. SFX may simply be rendered as part of level loading to ease back on CPU requirements, and without loss of any authenticity apart from whatever inaccuracy the emulation core imposes.

T-04YBSC, a new YMF71x based sound card & Official VOGONS thread about it
Newly made 4MB 60ns 30pin SIMMs ~
mida sa loed ? nagunii aru ei saa 😜

Reply 7 of 8, by BenMcLean

User metadata
Rank Newbie
Rank
Newbie

Wolfenstein 3-D does all its Adlib sound effects only on channel 0.
All of Robert Prince's Wolfenstein 3-D music is restricted by design to only use channels 1-7 in order to keep channel 0 clear at all times for the Adlib sound effects.
Sorry, I should probably have mentioned that from the start.

In addition to Adlib sound effects, there are also SoundBlaster sound effects in the game (called DigiSounds) for some sounds. I've already got those working great without needing SoundBlaster emulation. (I'm even doing 3-D positional audio in VR for those!) But for the full Wolfenstein 3-D experience, you need both the Adlib sound effects and the DigiSounds in addition to the Adlib music.

The OPL emulator I've got really isn't built to let you render ahead of time. It uses the system clock in its internals. And since I don't really understand how it works, because I've never done this before, I'm kind of at a loss as to how to change it to allow rendering ahead of time. I could really use some programming help in this specific area from anyone who knows what they're doing on this.

I'd prefer realtime OPL emulation but I'd also be just fine with rendering the adlib sounds when the game starts to play back later, and rendering the music when each level loads if only I could figure out how to do it.

Reply 8 of 8, by BenMcLean

User metadata
Rank Newbie
Rank
Newbie
BenMcLean wrote on 2020-05-07, 01:02:

The way this emulator is written uses the system clock to time things. There isn't a way to send in a buffer of note data. Its API only takes in note signals in real time. If I could figure out how to send in a buffer, I would.

What I said there turned out to have been incorrect.

It isn't tied to the system clock at all. Instead, time in the simulation moves forward when the ReadBuffer method is called, by exactly how much time corresponds to the number of frames of audio I request from it. This means having to stop requesting more frames at the exact point when it's time to send the next adlib input signal and send that signal before requesting more. Seemed very weird to me but I eventually did figure that out.