Just when you thought this was all vaporware ...
One piece of the GUI, the Sequence Editor, is functionally complete. It's a pretty simple widget that exposes the Sequence data structure to the user and allows them to insert and remove patterns and assign tracks to patterns. I've tried to do this "the right way" as much as possible, using Qt4's nice MVC features to cleanly separate the GUI from the underlying logic.
It's not very pretty yet. At the very minimum I need to style the first column so that it's clear that the first column is pattern numbers and the rest of the columns are track IDs.
At this point I've overcome some of my initial Qt inertia (I'm not an expert at it by any means, but I've gotten the hang of it). Next up is exposing the track grid to the user. At that point, I'll probably have enough functionality in place to create the play button and get one step closer to real-time note playing.
If you're interested, according to sloccount the source repository has just surpassed 1000 lines of code. A milestone!
Sunday, March 6, 2011
Thursday, March 3, 2011
Putting the "MML" in MMLTracker
You might be wondering where the MML part of this whole equation comes in. I anticipate it working like this: nothing about the object structure I described previously is specific to MML, or to any particular platform. This is by design. One of the things MMLTracker will do is output MML for the target platform, probably by passing the Song to an MMLWriter that will go through the Sequence one Pattern at a time, outputting the MML for that Pattern. I'm really interested, however, in making this thing output not only MML, but native code (.VGM, .NSF, etc) for the target platform as well. I'm anticipating doing this simply (hah) by writing a new Writer (VGMWriter, NSFWriter) that will be passed the Song and will output a file of the appropriate format.
On the topic of whether MMLTracker will take MML as input, that's still up in the air. Taking MML as input requires me writing an MML parser, and writing parsers sucks.
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.
Subscribe to:
Posts (Atom)