Skip to content

VGM web player in JavaScript, official topic

Technical discussion about the VGM format, and all the software you need to handle VGM files.

Moderator: Staff

  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

VGM web player in JavaScript, official topic

Post by neologix »

I've finally successfully implemented gzcompressed VGM handling in the VGM player using zlib.js, officially bringing the player's capability to VGM v1.50 spec.
SN76489, YM2413, and YM2612 cores are ported from Genesis Plus GX; YM2151 core is slightly modified from Kuma's MDX player. The player itself is my hand-rolled "NPlay" script to attach audio output to the XAudioJS library by Grant Galitz using jQuery. Many thanks to the SpritesMind Forums community for the massive amount of info on the YM2612, Grant Galitz for making XAudioJS so I didn't have to, GitHub user imaya for the zlib.js that powers the VGZ decompression, ValleyBell for the massive amounts of direct help with the development of the various cores, vampirefrog for various suggestions, support, and other web VGM playback development, and the vgmrips community for advancing the VGM format beyond the SMS, Game Gear, and Genesis.

For future reference, all new discussion on vgmrips on VGM playback via JavaScript using my player and/or the above scripts should be here.

I need help cleaning up the YM2612 and YM2413 implementations because they each sound a bit off in their own ways, but I have a feeling the same problem afflicts them both. Any volunteers? (You can submit patches as pull requests on the repos)
  • User avatar
  • MaliceX Offline
  • Posts: 226
  • Joined: 2012-09-29, 11:45:48
  • Location: Australia
  • Contact:

Post by MaliceX »

Firstly, sensational work on putting all this together. Other than the issues which have been raised earlier, this is one damn good application for a decent browser's JS engine. No Flash loader required. :P

I've taken the time to set up a local copy of your player to test with some VGMs I had. There most certainly are specific playback issues, but whether they're based on the cores themselves or the sampling routine is yet to be determined conclusively.

I'll examine this further once i understand the codebase a bit more, but certainly it is indeed still early days yet promising progress. :)
-dj.tuBIG/MaliceX
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

@MaliceX - Thanks for taking the time to try it out! If you want to use a somewhat smoother (though Chrome/new Firefox only so far) audio sink, download nsink.js into the player's js/ folder and add the following line in the player's html before the <script> tag sourcing sn76489.js:

Code: Select all

<script type='text/javascript' src="js/nsink.js"></script>
This won't fix the generation issues, but should mitigate some of the buffering problems. I don't immediately switch to this hand-rolled solution yet because the player still needs optimization and my filter node code doesn't yet seem to work.

(edit - maybe increase BUF_SIZE to 16384 and change the 65536 to BUF_SIZE_R for some more buffer alleviation)

Post by vampirefrog »

This is looking pretty cool so far. Have you separated the VGM file reading code, by any chance? I'd like to reuse it (+zlib code) to read some files for the pack upload page (reading the tag and the header, actually). I am thinking of some VGM or VGMReader class with a function to pass the full buffer and just get the header and tags after that.

Also, the issue remains where the web player plays a few ms every second if the tab is inactive. There is a way to detect if a tab is inactive (via 'visibilitychange' event), but I don't know how to deal with the skipping.

EDIT: You might have to use WebWorker.
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

The file loading (including any needed decompression) is done in nplay.js and the raw decompressed arraybuffer is passed to the given VgmFile object (look for _v.load() in nplay.js). Loading GD3 tag content would theoretically be done in VGM.js during VgmFile.load but I didn't start on it yet since I don't very much look forward to writing a string conversion routine to make it happen properly. Populating given HTML elements with loaded GD3 tags would likely be done in nplay.js after that.

The web player slowing down if the tab is inactive is entirely due to how I wrote the buffering using XAudioJS. I messaged Grant Galitz about how to optimize my code and fix it but I haven't heard back from him yet. The temporary modern-browser-only basic audio sink I wrote, nsink.js, allows continuous play even if the tab is inactive, but due to how I fucked up the VGM parsing loop (i.e. I left it essentially untouched from BlackAura's original method of continuous command feeding) I can't yet properly pass VGM's sample output to the script processor node and therefore to a biquad filter node to fix the random crackling.

I have a feeling fixing VgmFile.fillSamples should not only solve the inactive tab issue but also let me use AudioContext-based audio sinks with it.

Post by vampirefrog »

Sounds like you depend on a bunch of circumstances out of your control, which you can move under your control with a bit of effort.
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

I just looked over the VgmFile.load function again and apparently there was a readUnicodeCString as well as a GD3 tag parser implemented (I don't think it was me, though, I think it was from the original BlackAura code). I don't think the supposedly parsed tag is exposed yet, though it looks like it should theoretically be a simple matter of someone implementing an VgmFile.onTagLoaded function on a per-project basis. For my player specifically I'll probably eventually hook it via jQuery to some text block I haven't yet decided to place.

Re circumstances: It's less about "in or out of my control" and more about "do I have a decent block of time to dedicate to it" (except for the waiting for a response from GG, which is entirely passive). I started adding stereo mix functions to all the chips so their audio outputs are fed directly to a given interlaced buffer so I don't have to perform extra loops in the VGM mixer to add them separately (though the YM2612 running at its native 53267Hz rate is predictably giving me problems, so for the moment on my local copy I turn on "interval mode," explained below). I also started writing a VGM mix function to simplify fillSamples to a more AudioContext-friendly processor.

Re YM2612: I finalized the YM2612 from two versions of the Genesis Plus GX ym2612.c, the last one from before Eke-Eke implemented BlipBuffer and the latest one from after. His implementing BlipBuffer allowed him to simplify the timing of the YM2612, enabling it to run at its native rate but still outputting the same (maybe higher) quality sound directly to the audio buffer. I often consider BlipBuffer (and other similarly implemented bandlimited synthesis techiques) as "magic." Before BlipBuffer/"magic" he was scaling the timing of the chip to the output sample rate like VGMPlay/in_vgm still does, essentially forcing the chip to run at 44100Hz (or maybe even 48000Hz) and adjusting all the related timings accordingly (envelope generator, LFO, etc). In ym2612.js, the adjusted timing versions (which I've dubbed "interval mode" for some reason that I forgot to write down) are commented as "vb" (i.e. ValleyBell, because he still uses that scaling method in VGMPlay) and run if cfg.mode (at the top of the file) is set to 1. If interval mode is on, I can direct-mix samples 1-to-1. If it's off, I essentially have to generate 1.2*(sample_count) samples and pick the appropriately interpolated sample_count samples out to mix because I didn't port BlipBuffer/"magic" to JS.

Post by vampirefrog »

That's ok, I wrote my own code in the meanwhile. it is easy to convert the GD3, just view it as an uint16 array and use String.fromCharCode().

http://vgm.mdscene.net/packs/js/vgmfile.js

Post by vampirefrog »

Can you add a file upload input so we can play our own VGMs on your js player page?

Post by vampirefrog »

Also, as an update to my own web player, I'm now generataing the mp3's (also oggs) in a streaming fashion on the server side, with a modified vgmplay that outputs to stdout, then I pipe that to lame/oggenc and their output is piped to http output, but also a cache file which is later accessed directly. It has some bugs and the most noticeable is that you don't have the full song length, but I have some workarounds for that.

Post by vampirefrog »

Neo, why don't you offer to create a web player for project2612? I'm sure it would be trivial at this point. All you need to write is a script that extracts the vgm file from the zip and add your player somewhere on the pack page.
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

vampirefrog wrote:Can you add a file upload input so we can play our own VGMs on your js player page?
Once I fix the audio sink problems I'll add modern-browser-only file upload to my online player. I've been playing with some drag-and-drop scripting for a separate but somewhat related VGM webapp project for some time, but haven't yet added file upload to the player because I wanted to keep the playback restricted to a specific set of test files without having to navigate back-and-forth between browser and Windows Explorer.
vampirefrog wrote:Neo, why don't you offer to create a web player for project2612? I'm sure it would be trivial at this point. All you need to write is a script that extracts the vgm file from the zip and add your player somewhere on the pack page.
Once I optimize the YM2612 and YM2612 PCM handling so they don't sound like complete garbage on YM2612 VGMs with data block PCM events (listen to how the web player handles Raijin's Rockman X Boomer Kuwanger cover in the "test" playlist, for example), I'll absolutely offer the web player to P2612 like I did to SMS Power (Bock and Maxim are willing to accept it once YM2413 playback is cleaned up and the player script package is optimized for convenience, though technically they can have SN76489-only VGM playback embedded into the site now if they want guided installation instructions from me). Converting P2612's stored zips to an on-page playlist with functional player would require a little more work than the demo page shows, though.

Also, I just contacted the JSMESS team about doing a performance comparison between their Emscripten-compiled Mega Drive's YM2612 and my hand-ported script, so hopefully I can decouple their YM2612 from a recent copy of their script and study it to optimize mine.

Post by vampirefrog »

It looks like the ScriptProcessorNodes will be replaced by Audio Workers sometime in the future, so that might yield better results for any web player that uses JS to generate the sound instead of the built-in oscillators and nodes.

https://developer.mozilla.org/en-US/doc ... cessorNode

Post by vampirefrog »

Why don't you do this project in AS3, so we can enjoy listening to VGMs in the browser without waiting for the WebAudio API to get its shit together?

I've already made a working VGM loader + HuC6280 player, you can start with that and just add the other chips. I can help you figure it out, and you have an extra language to add to your CV. And the VGM community gets a VGM web player that works across all browsers. Everybody wins.

AS3 is a nice language, btw, I'm sure you'll like it. It's just javascript, with typed variables and with classes and packages.

Anyway, have a look here for my code, which you can use as the starting point.

You'll need to install apache flex sdk and try to compile main.mxml with mxmlc.

Also, as soon as I figure out how to use Crossbridge, we can just compile C++ into swf and just use that directly.
  • User avatar
  • MaliceX Offline
  • Posts: 226
  • Joined: 2012-09-29, 11:45:48
  • Location: Australia
  • Contact:

Post by MaliceX »

Less browser plugins to use of course. Not everyone installs Flash, reusable core components etc. The AS3 route is less accessible to most people due to the requirement of another SDK. (For JS, it's only your browser :P)
-dj.tuBIG/MaliceX
Post Reply