Wednesday, March 2, 2011

The Nitty-Gritty on MMLTracker's Architecture

As it stands right now, here's the basic structure of MMLTracker from a software perspective. This should serve to define the "playing field" for the project, in a sense, and define some terminology that I'll be using frequently during subsequent status updates.

MMLTracker is written in pure C++ and uses Qt for all GUI elements. I'm trying my hardest to make it cross-platform; all the compilation is handled by g++ and the various Makefiles and whatnot are generated by CMake which means that I should (at least theoretically) be able to tell it to spit out and XCode or Visual Studio project when the time comes to build for OS X and Windows.

This is a nights-and-weekends side project for me, which means I'm cutting a bunch of corners with respect to design documents, etc. and just winging it. This is also my first experience with Qt, but I'm slowly getting the hang of it (the Qt library is enormous!).

I will refer to C++ objects with capital letters in camel-case (i.e. Song, Note, TrackBank). So when I'm talking about a song, I'm talking about the stringing together of tones in a way that sounds good. When I'm talking about a Song, I'm talking about a data structure.

The most important object in MMLTracker is a Song. A Song is targeted for a given platform (NES, Game Boy, Genesis, Wonderswan, etc.), and the target platform imposes some limitations on the song (max. track length, max. sequence length, number of channels, valid instrument settings for each channel, etc.). I haven't quite worked out how to deal with these per-platform settings algorithmically yet, but they're there; as I said, I'm winging it here.

A Song is composed of a Sequence, a TrackBank per channel and an InstrumentBank. A TrackBank stores Tracks, each of which is given a unique ID. An InstrumentBank stores instruments, each of which is given a unique ID. A Sequence is an ordered list of Patterns, and a Pattern is basically a reference to a Track from the TrackBank per channel; this way, tracks can be re-used across patterns.

A Track has a bunch of slots to hold Notes. A Note can be a 'silence' note (stops sound on that channel, used to cut off notes) or (more typically) a regular note with a pitch and an octave. Notes also are associated with an Instrument and some number of Effects.

Effects are where it things start to diverge from what I understand "typical" tracker design to be. Certain types of effects (single-track echoes for example) really ought to be produced programmatically by the software as opposed to you entering every echo note by hand. I'm sure there are other examples of effects that people often have to enter by hand that could be figured out automatically; please point me to them!

The basic plan for the UI looks pretty similar to the way Famitracker is laid out (thanks for the inspiration, Famitracker developers!) There's a sequence editor that allows you to add and remove patterns and pick which tracks go in which pattern. Editing a track requires sticking it in a pattern and selecting the pattern in the sequence editor, which will bring up the pattern in what I'm calling the "note grid", which looks basically like every other tracker note grid ever made.

I may decide to diverge slightly from the standard note grid in the interest of usability. There's no reason trackers have to look like they did when we were writing them for DOS on a 640x480 256-color display. That said, if it ain't broke, I don't intend to fix it.

No comments:

Post a Comment