Finally, some sound
It's been a while since my last entry in the devlog and there are reasons for that but none of them are worth talking about. For the last 1 ½ weeks I have been in my refuge in Sweden and while I have other duties, some chunk of time went into making Protracktor a reality.
I'm now at a point where the play routine seems to play most modules almost correctly. What I need to do now is take my original TinyMod port to JavaScript (or rather: Coffeescript) and make it run again in some form. Technically it would probably need a port to use an AudioWorker for the rendering, but that would be another one of these distractions I seem to run into. The reason why I want to get that working is to compare a couple of things – I've noticed some interesting differences in playback between MilkyTracker and my routine that hint to some subtle bugs (maybe) in TinyMod or just my port in handling channel volumes and this is bad for a player but would be unacceptable for an actual module editor.
The next step would be to refactor the whole thing a little bit so that it works better with Rust's borrowing. The majority of my time trying to get the port to work was fighting with borrowing and figuring out how to restructure the code in a way that reduced the amount of references I need to throw around. The original code is C++ and since everything is running in a single thread, it doesn't particularly care if references (or pointers) are thrown around and are held as references in several places at once. My idea is here to dissolve the class based structure as much as possible and clearly divide up the data between actual module data (that needs to be read only for the player code so that I can modify it in the editor) and runtime player state (of which there is actually a lot)
The other thing I've been dealing with is that the original code for the most part uses signed integers for everything (except for the places that need floating point math) and you can't really do that in Rust when you're trying to index into arrays or Vecs. I've been trying to replace signed integers with unsigned ones but quickly ran into a ton of issues where stuff needed to be signed to work properly.
On the way I discovered that I completely misunderstood what the “as” keyword in Rust is doing. I totally expected it to cast integers but what's actually happening for integers is that the binary representation is not changed. This makes sense as “as” is supposed to never panic and you can't really usefully cast a negative integer into an unsigned integer. To give you an example, let unsigned: u8 = 255; let signed = unsigned as i8;
will end up with signed being -1, as that's represented as the same bits as 255 in an unsigned byte.
This is actually useful in places but means in need to be really careful to do the correct thing. There are ways to do actual conversion, but they, obviously, can fail and so need error handling.
Combine this with C/C++ lax way of handling array access (the original C++ code does a lot of pointer math, something that need careful rewriting with slices in Rust for the most part) and there are some real challenges for me to clean up the code.
Then I'll try to extract the play routine up to a point that I can use it in both a simple CLI player (which currently is what this is) and the actual Protracktor editor.
I haven't made any progress on the UI side of things, but I really would love to start with something simple that at least visualises the module structure in real time and maybe try to add some scope graphics if possible as well.
It was really an incredible moment yesterday evening when I figured out the final bug in the code that messed up the sound and heard a clear sound for the first time. Turns out I did some math wrong in the loader and the sample offsets were all messed up. The module file format is such a weird relic from the past where binary files were actually, you know, binary files, instead of the usual zip archive with a bunch of easy to parse files in them.
I hope, now that I have something somewhat substantial to show, that I can quickly open up my development process and start all the stuff I planned like making videos and maybe even streaming my coding sessions. (He writes, typing this into the void of an unknown blog on the intarweb)