steffest / BassoonTracker

Webbased old-school Amiga music tracker in plain old javascript - Plays and edits Amiga Mod files and FastTracker XM files
http://www.stef.be/bassoontracker/
MIT License
1.01k stars 61 forks source link

standalone player: how to trigger custom notes + effects using js? #66

Closed coderofsalvation closed 3 years ago

coderofsalvation commented 3 years ago

Hi,

First of all: thank you very much for creating all this. I'd like to use the standalone player-lib as an .XM soundengine for gamelike-applications, as it's one of the few libraries which plays (on my) mobile without audio-crackles (respect, this is probably not easy). Obviously, it also comes with a great webeditor (well done), which offers really cool collaboration possibilities for bundling sounds.

Anyways, there's 2 things I need to figure out. Could you give any pointers on do the following using REALTIME javascript-script-calls?

is this BassoonTracker.playSample(sampleIndex,sampleRate) as mentioned in the player docs? If so, is there any way I can hack panning in there? That would be awesome for an VR / immersive experience point of view.

is this BassoonTracker.setCurrentSongPosition(songPos) as mentioned in the player docs? Is songPos 0 == pattern 0? or just row 0 of the global song? How would you use this to (quoted from the docs) have several looping "subsongs" in 1 file, so for example you could have a different tune for each game section without loading additional data

steffest commented 3 years ago

Hey, Thanks for finding it useful! And yes: I did give mobile playback performance some extra attention :-) To answer your questions: To play back a sample (that is in the XM file) you can use the BassoonTracker.audio.playSample (the previous docs talked about BassoonTracker.playSample but that was incorrect, sorry) BassoonTracker.audio.playSample(1,285) Plays sample 1 with note G on the 4th octave (period 285)

BassoonTracker.audio.playSample(5,428) Plays sample 3 with note C on the 3th octave (period 428)

You can find the note/period mapping on https://github.com/steffest/BassoonTracker/blob/master/script/src/enum.js#L235

the "playSample" has some more parameters, although some are somewhat "internal"

function(index,period,volume,track,effects,time,noteIndex)

The volume and track parameters are interesting maybe for your case. Volume is a number between 0 and 100 Track is the track to play the sample. This is important because any note on that same track will cut off the previous playing sample so if you want to be sure your injected sample keeps on playing, you can add an empty track to your XM and use that one. e.g. BassoonTracker.audio.playSample(5,428,100,31) (Plays back sample 5 with period 428 with Volume 100 on track 31)

For panning: I pushed a new version of the player library that exposes the Instruments, so you can access the panning through there.

You can access all instruments through BassoonTracker.getInstruments() This returns an array. Each instrument has a "sample" property and that sample property has a panning property that goes from -127 to 127 (meaning full left to full right)

So to set the panning for sample 3 BassoonTracker.getInstruments()[3].sample.panning = 127; and then to play it with volume 100: BassoonTracker.audio.playSample(3,428,100);


For jumping to a specific position in the patternlist. In the XM file you can use the "B" command, but you can also do that from external javascript: You can use BassoonTracker.setCurrentSongPosition(position) (where position is a number) this only works when the song is not playing. You can check this with BassoonTracker.isPlaying()

so to jump to position 4 in the song position list: if (BassoonTracker.isPlaying()) BassoonTracker.togglePlay(); BassoonTracker.setCurrentSongPosition(4); BassoonTracker.togglePlay();

Then the song starts playing from position 4 If you would then e.g. in position 8 have a B command like B04 - this will jump back to position 4 So then the song will loop between position 4 and 8 which might be - for example - a certain section of the game. Then when the game jumps to another section, you can do

BassoonTracker.togglePlay(); BassoonTracker.setCurrentSongPosition(9); BassoonTracker.togglePlay();

And will proceed from position 9 onwards, playing a different section of the song.

Of course you can also just load a different song and play that, But this way you can keep everything in the same XM file, reusing samples and having a smaller load time because everything is in 1 file.

I hope this makes some sense.

If you want I can whip up a quick prototype to demo this. Please poke me if you need anything else. Best regards and good luck with your project!

coderofsalvation commented 3 years ago

Awesome, thank you so much for the detailed explanation (and getInstruments()-call) :) I was able to put together a quick demo here The good news: it works very well on my smartphone :) Feel free to reference it or use it in whatever way you want.

There's something beautiful about XM files, it's lightweight and can do quite a lot. In the demo you basically get true stereo sound, with an extra layer of interactivity, without exploding your smartphone's CPU :) The player is also just 22k, quite impressive.

Oh btw, last question, is it somehow possible to hook fx into the master / audioWorklet? Would love to apply an master-effect (echo e.g.) based on (in-game) position somehow. I've noticed BassoonTracker.audio.context however I'm not sure how to go from there. Any idea how I could somehow hook this example filter into that?

Again , superthanks already for creating all this amazing stuff.

coderofsalvation commented 3 years ago

UPDATE: nevermind :)

I was studying the repository a bit..and I got naughty and tried to roll my own FilterChain-function...and it worked :) For example, the reverb (the sportcenter.m4a is quite good actually!) can be controlled using javascript. Thanks! so much ideas, so little time :)