Main Menu

AYC Music player

Started by jaymzjulian, November 13, 2019, 05:48:38 AM

Previous topic - Next topic

jaymzjulian

This code snip can play music in AYC format - it's called once per frame.  Ideally, you'll compose in something like arkos tracker 2, which will let you target a 30hz refresh, which ius what i'm doing for my game, but this demo track here is 50hz, so set the frame rate to that I have.

EDIT: note that the attached version is outdated, please use the github version at https://github.com/jaymzjulian/vectrex32_ayc_player

The only stuff that need be global is at the top marked - the rest is in the play_the_music function.

This uses 4x more ram than it needs to, since it uses arrays which should really be byte arrays. So it could take 1/4 of the ram if we had those.... i should add that to feature request too!

In terms of ram, it requires:
* the compressed data - which in this case is around 6k elements
* a 512 cyclic buffer per channel, which is 7k elements

so obviously eating 52kbytes instead of 13kbytes is horrible.  Not that 13kbytes is _great_ for music, but the replayer is both _fast_ and _simple_.  my implementation has some problems - specifically, i couldn't get bitwise ops working in gsbasic at all, so i'm using horrible MOD type stuff, which is shite.... i'll look at that later, since i'll need to optimize this further if i'm going to get it into my games....

Tracks are made using YMCruncher, btw, which converts YM's into AYC files - it's actually a compressed format from the amstrad CPC.  I got mine from https://github.com/cdepecker/YMCruncher/ , but at least cpcpower has binaries of it for windows/mac

Vectrex32

Thank you for posting this!

Looks like you're giving me a homework assignment. :-) A function to get the file length should be easy enough. But I'll have to think about how to do byte arrays.

- Bob

jaymzjulian

Looks like you're giving me a homework assignment. :-)

Just my continuing mission to be your most annoying user :)

jaymzjulian

New features in github:

* Loops work correctly
* Everything is correctly functioned now, so it's usefully useable in games
* hooks for a codesprite buffered playback mode, in case you're me and you want to do something fancy ;)

Vectrex32

Just to update the status of this for anyone who's following it: version 1.21 (which is still in beta as of Dec. 2019) includes bitwise operators, a way to read and write binary files, and a way to get the size of a file.

Ask and ye shall receive!

- Bob

jaymzjulian

I actually have this usefully integrated into a game now.  Also, I learned a LOT about vectrex IRQs and how they are terrible while refactoring this (as well as _almost_ enough 6809 that i could code a game....), so now there is 3 player modes that you can choose from:

a) direct - i.e. we shove straight into AY when called.  This requires your Frame Rate to honestly match your music rate
b) buffered via IRQs - this is _simple to use_ but wont always work
c) buffered via Poke for timing - this is _more robust_ and will always work, but requires things of you...

Option b works in a lot of situations, and SHOULD work well, and is the easiest to synchronize - but it's finicky for two reasons:
1) even though the IRQ is literally an inc and std, nonetheless it will corrupt text sprites - i didn't catch this, because my test case was a scale=32 vector, which it does NOT corrupt
2) if things go wrong and ayc_exit isn't called for some reason, IRQs wont be cleaned up and the vectrex32 will crash and you will have to powercycle your vectrex ;)

So I ended up doing a hack to poke into ram what the current frame count is (calculated on the basic side), and synchronize that way.  This actually works really well, HOWEVER for best fidelity you MUST ensure you call ayc_update_timer at lease once per [music rate, usally 1/50th or 1/60th of a second, depending on if you're PAL/NTSC composing tools].  update_music_vbi will do this for you, so if you're just blatting some stuff to screen and moving some sprites around, but your sprites are big, this might actually be fine - this was not nearly as bad as I expected it would be in vxtron! 

The code for all of this is in the github linked above - for general use, and i did this integration myself this week (i'll have to write a readme soon), you basically just need to:

1) copy the globals section to your global
2) copy the actual player code somewhere
3) choose your mode by setting buffer_mode and irq_mode (i set them to 1 and 0 respectivly)

then if buffering:
   4) ensure that you call ayc_pokedata and ayc_init as your first two sprites, and ayc_exit as your last
   5) call the ayc_playcode codesprite at least once - I call it on every call to ReturnToOrigin (it's only 3 bytes of dpram)
   6) call update_music_vbi directly before calling WaitForFrame
   7) optionally, call ayc_update_timer from other places if you have code that takes a long time - if you notice the music dropping, you need to do this
otherwise:
   4) call play_that_music once per frame

you might also want to tune buffer_count - each buffer grows the ayc_pokedata codesprite by 70 bytes.  If it overflows, the music won't stop, it'll just stall slightly, so it's actually better to err low for this.

Maybe it's useful now to other-than-me people ;)

jaymzjulian

update: now an easily includable bai and settings.bai so you don't have to cut/copy/paste the demo code anymore