Skip to content

General JavaScript VGM player discussion

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

Moderator: Staff

  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

Ok, making progress here...

The new version is on the same URL as before. I have created 2 ES6 classes with several methods. VGMPlay is the basic class,VGMPlay_WebAudio extends from it and adds playback. Playing a VGM file on a URL on the same domain as where the script is running is now as simple as:

const vgmplay_webaudio = new VGMPlay_WebAudio();
vgmplay_webaudio.loadURL('http://vgmsite.org/03.vgm', true);

I also added two other buttons which can be used to load the VGM file and generate 100 buffers from 16kB of wave data in a local buffer. 100 buffers of SCC+ data takes 6.5 seconds here. Maybe it's not very useful right now but I wanted to know how this can be done. :) Maybe I can create a generate MP3 button...

I know it has issues, you need to refresh when switching between the two types. Also stop is not implemented etc.

I think I know a way to integrate the slider nicely, but first I need to implement more basic functions.
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

I was thinking what it would mean if vgm files are played through Javascript. There are two ways the files can come to the user as part of the playback routine; a zip file containing all files or loose vgm files. How common is it that the zip files contain uncompressed vgm files? It's the advice I got, so probably common? Transporting the zip file to the user is going to take too long, so then it would mean uncompressed vgm files... that's not that great. :o I guess it's not that hard to fix though with some scripting.:)

About the player; I have a progress bar but I'm not very happy about it because it takes too much CPU+GPU. I guess it's just implemented bad, it uses an image (96 bytes, but still), it can be done in regular CSS, but I'm not very familiar with the DOM/CSS3 thing and finding a good howto proved difficult, stuff that stopped working when new Chrome releases came etc. I guess it could be done properly these days, but I have to really dive into CSS3 to learn. I feel HTML progress bars are not smooth enough, even if they're called smooth... I haven't seen anything close to what OSX or Windows do natively. The youtube one is the best I've seen I think. For now I'll leave it to this, I can fix it later on....

On the javascript side, I'm using callbacks now, functions in the main script can then expect a call when stuff happens. So the main player can respond visually and change the track etc.

I'm pretty sure I can pull of the mp3 thing, using lamejs. I think I'll make it can be enabled. This would mean all generated data is saved in memory. If all wave data has been generated in memory, creating the mp3 won't take long!

And I'm still thinking about the buffering thing; to separate the generation of the playing. So just generate everything, then make it seek able. I think mobile phones like it much better like this; one boost at the beginning, then play the wave. Yes, it uses more memory, but what is 50MB these days?

But, before features like those last two I'll finish normal playback completely. :)
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

niekniek wrote:I was thinking what it would mean if vgm files are played through Javascript. There are two ways the files can come to the user as part of the playback routine; a zip file containing all files or loose vgm files. How common is it that the zip files contain uncompressed vgm files? It's the advice I got, so probably common? Transporting the zip file to the user is going to take too long, so then it would mean uncompressed vgm files... that's not that great. :o I guess it's not that hard to fix though with some scripting.:)
Actually, if the entire load-and-playback routine is implemented entirely in client-side JavaScript, you (the browser) only get the resulting raw audio and no VGM files, zipped or otherwise, are directly downloaded. If you're serving fully rendered waves for download like the current server-side implementation, on the other hand, that's another story.

Regarding generation itself, yes you can generate the entire wave at once and then it'll be seekable, but then you won't be able to dynamically manipulate data on-the-fly like toggling channels and/or chips during playback. Also, you'll have to remember to loop loopable songs either way.
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

neologix wrote:Regarding generation itself, yes you can generate the entire wave at once and then it'll be seekable, but then you won't be able to dynamically manipulate data on-the-fly like toggling channels and/or chips during playback. Also, you'll have to remember to loop loopable songs either way.
It's also unreliable because browsers may not always allow that much memory to be allocated by the JS script. I've had that problem in the past, but not so far with the latest versions of Firefox and Chrome on PC. Mobile devices might still be an issue though.
  • User avatar
  • ValleyBell Offline
  • Posts: 4808
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

niekniek wrote:And I'm still thinking about the buffering thing; to separate the generation of the playing. So just generate everything, then make it seek able. I think mobile phones like it much better like this; one boost at the beginning, then play the wave. Yes, it uses more memory, but what is 50MB these days?
You'll enjoy those few 1-3 hour VGMs made from songs that get transposed up by a semitone after every loop.
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

neologix wrote: Actually, if the entire load-and-playback routine is implemented entirely in client-side JavaScript, you (the browser) only get the resulting raw audio and no VGM files, zipped or otherwise, are directly downloaded. If you're serving fully rendered waves for download like the current server-side implementation, on the other hand, that's another story.
I'm not sure what you mean. If the entire load-and-playback routine is implemented client-side, how would I get wave data without VGM files? They wil be downloaded by the library through XHR and then inserted in the Emscripten version of VGMPlay.
neologix wrote: Regarding generation itself, yes you can generate the entire wave at once and then it'll be seekable, but then you won't be able to dynamically manipulate data on-the-fly like toggling channels and/or chips during playback. Also, you'll have to remember to loop loopable songs either way.
True. Maybe offer both ways...
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

ValleyBell wrote: You'll enjoy those few 1-3 hour VGMs made from songs that get transposed up by a semitone after every loop.
Hmm, ok. :o On the other hand, YMZ280B VGM files have trouble with seeking as you explained, the 'wave-way' will work in those cases. Are there more issues to expect with seeking with other chips?
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

free hosting broke down...

http://vlessert.nl/vgmplay-js/
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

niekniek wrote:
neologix wrote: Actually, if the entire load-and-playback routine is implemented entirely in client-side JavaScript, you (the browser) only get the resulting raw audio and no VGM files, zipped or otherwise, are directly downloaded. If you're serving fully rendered waves for download like the current server-side implementation, on the other hand, that's another story.
I'm not sure what you mean. If the entire load-and-playback routine is implemented client-side, how would I get wave data without VGM files? They wil be downloaded by the library through XHR and then inserted in the Emscripten version of VGMPlay.
In the case of my player, the Web Audio ScriptProcessorNode does all the work. In the node's audioprocess event handler it asks the VGM processor to make N samples of raw wave, N usually being the buffer size, then add those samples to the audio context's left and right channels:

Code: Select all

			_audio_proc.onaudioprocess = function(ae) {
				var outL = ae.outputBuffer.getChannelData(0),
					outR = ae.outputBuffer.getChannelData(1),
					i = 0, j = 0;
				// TODO: process N samples of VGM
				zero_it(buf, n); // zeroes the temp buffer
				lux.currentVGM.process(lux.config.bufferSize, buf);
				//console.log("audioprocess",buf);
				while (i<lux.config.bufferSize) { // add the temp buffer data to the outputs
					outL[i] = buf[j]; ++j;
					outR[i] = buf[j]; ++j;
					++i;
				}
			};
Web Audio handles the back-end bits of properly setting up output channels, but it will still need data to output. In the code above, ae is the audio context event from which to get the pointers to the output channels. The vast majority of client-side processed audio players will likely have their process handler set up similar to this if the target browser doesn't have that audio format's decoder built-in like MP3 or WAV. The JS MDX player that I frequently reference does it like this.
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

Yeah, I know about that stuff, if you have a look at the URL I posted you'll see the same thing, that's the way it works.

But the VGM processor as you call it also runs client side, so it'll need the VGM data.
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

Well, I think it’s getting pretty OK… please test. Is it missing something needed?

On my todo list:
- Fix: when using a track without a loop, a fraction of the samples is played of the previous track before the next track starts. My android player has that too. What am I doing wrong?
- Display loop points at progress bar
- Volume
- Jsdoc
- Fix for Edge/Firefox and make HTML more compliant...

For later:
- Give option to mute channels per channel, show only buttons for active chips
- I’m still thinking about the wave thing… it has advantages:

Advantages:
1. Every track will be seekable
2. Less chance on buffer issues, especially on mobile phones
3. I can still seek the old way, just generate & buffer from the seeked area. If everything is generated fill the gap(s)
4. Easy to add a ‘generate mp3’ button

Disadvantages:
1. Memory
2. Seeking might be a bit slower when it's not buffered yet
3. It will be interesting to not save data for every loop separately and still loop smooth... :)
Last edited by niekniek on 2017-12-08, 14:01:12, edited 9 times in total.

Post by vampirefrog »

it's looking good. please don't worry about channel muting yet. or even the loop point display. it only needs to play, pause and seek in order for it to be usable on vgmrips.
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

Ok great! Fixed some bugs again, auto-stop when paused longer then 1 minute, included timer.

Let me fix the progress bar first to the gradient way with html5 progress element instead of a div and an image. And I'll add the MIT license.
  • User avatar
  • neologix Offline
  • Posts: 211
  • Joined: 2012-04-22, 4:03:45
  • Location: New York, NY, USA

Post by neologix »

niekniek wrote:Yeah, I know about that stuff, if you have a look at the URL I posted you'll see the same thing, that's the way it works.

But the VGM processor as you call it also runs client side, so it'll need the VGM data.
On web interfaces I use FileReader to allow the user to upload a VGM and call its readAsBinaryString method on a file field's change event. The Electron-based player uses Node file and unzip functions because they're built into Node.js and I won't have to use a JS unzip library like on my web player. I haven't yet added user upload to my web player though.
  • User avatar
  • niekniek Offline
  • Posts: 73
  • Joined: 2017-07-17, 23:28:35

Post by niekniek »

vampirefrog wrote:it's looking good. please don't worry about channel muting yet. or even the loop point display. it only needs to play, pause and seek in order for it to be usable on vgmrips.
Ok, I guess it's at 95% stable now. I've been updating the post above about my todo list, I'll keep working on that. If you want you can build your own Emscripten 'binary' & data file but you can also get it from http://vlessert.nl/vgmplay-js/

I've been testing a bit with my work phone, they gave me a Snapdragon 625 based device. It's 98% ok, but audio drops will occur eventually. It's obvious why, you can see it when using the website with the developer tools. During playback the framerate is just fine. Then a frame hit occurs when filling the 16kB buffer and convert it to float. So one can expect a moment when the buffer fill takes too much.

I can think of three solutions;
- Decrease buffer size, just generate 4 kB more often
- Generate at least some wave audio ahead of playback. I'll do this first, just generate one buffer before going into the whole webaudio routine and use two buffers.
- Use a native app like ViGaMuP. ;)
Post Reply