Smash/Riot Logo

Smash/Riot

Focusing on Interactive Media and Unique Experiences

Making of Trisector: Music

The process for making the dynamic music for Trisector

Jesse/SmashRiot

6-Minute Read

I’m pretty far from being a musician, but I seriously love music. Since I was trying to do Trisector solo, that meant I needed to make a song or two for the game.

In the mid to late 1990s, I wrote a some demo scene styled tracks using Fast Tracker 2. Sometime around 1998/1999 I picked up a copy of Rebirth and made a bunch of noise with that software (Pitch Black Edition was my favorite mod). I even made a RB-338 mod as well, but that’s seriously lost to time.

Rebirth 338 is one of the few software boxes I’ve kept through the years, even though I can no longer run it on my Mac:

Rebirth 338

For Trisector, I wanted to try to create some original music. During the first few months of development, I had a few awesome chiptunes that were used as placeholders, but the time came where I really needed to replace the placeholders for release. Overall, I spent about 2 months trying to create/tweak the music and this article will talk about what I ended up using.

I first tried to create some music using various modern day trackers but didn’t make much progress. I had Rebirth 338 on my iPad and Figure on my iPhone, so I set some sounds/patterns up, piped the audio through a Xenyx 302 USB Mixer, and captured the audio via Audacity. Figure and ReBirth both support WIST for sync and beat matching, so I simply set ReBirth as the master and Figure as the slave. ReBirth and Figure syncing via WIST made the live playing pretty painless. Here’s a picture of the setup I used:

Rebirth 338

I spent a few nights doing a lot of recording sessions and ended up with a few decent songs. This song loop is the one that best fit the sound I wanted in Trisector. Total size of the song loop after crushing with afconvert was about 3.2MB.

The song loop fit the feel of the later levels, but didn’t fit the early levels so well. Also, the song loop didn’t fully fit with the pulse of the action, so I went farther down the rabbit hole and made what I call the MusicBox. Essentially, the MusicBox is a Singleton class that controls all of the music within the game. The MusicBox plays configured samples based on the number and type of enemies and/or bullets on the screen at the start of each bar.

The MusicBox has an instance of a MusicBoxSample for each sample and included some parameters such as bar length, min/max bars, min/soft/max threat, and a random threshold. The samples were all set at 130BPM and were between 1 and 8 bars in length.

Based on how hectic the action is at the start of a bar, the music would change accordingly. The result was that calm moments had less going on in the song, and more hectic moments had a lot going on. With about 11 samples, I was able to give each level a custom version of the main Trisector song by tweaking the parameters of each MusicBoxSample.

Each instance of the MusicBoxSample had a single sample that was set at 130BPM. I used Figure/ReBirth to create each sample, and then exported them so I could clean them up using Audacity. Once the sample was trimmed and cleaned in Audacity, I gave it a final crush with afconvert (e.g. afconvert -v -f ‘caff’ -d aac -s 1 -c 2 -b 128000 bass_fill_raw.caf bass_fill_final.caf ). The total size of all samples used for the music is 744K.

The MusicBox has a scheduler that ticks every 1.8462 seconds, which is 60 seconds / 130BPM = 0.4615 seconds per beat * 4 beats per bar = 1.8462 seconds per bar. The scheduler calls each configured MusicBoxSample with the current bar number and threat parameters, and the MusicBoxSample determines if it should play the sample or not. If the MusicBoxSample plays a sample, the next bar that it can play again will be determined so it doesn’t play over an already running sample.

For example, the Intro song is pretty simple with just two samples (Cathedral and Drums):

-(void) setupIntroSong;
{
    // turn off all
    [self resetAllInstruments];

    // setup instruments
    [mbsLeadCath  setRandChance:100  minBars:0  maxBars:0  minThreat:0  softThreat:0  maxThreat:100];
    [mbsDrumMain  setRandChance:40   minBars:4  maxBars:0  minThreat:0  softThreat:0  maxThreat:100];
}

Since the intro doesn’t have any threat, it loops the Cathedral sample and after 4 bars has a 40% chance to play the Drum Main sample.

In comparison, the song for the first four levels is a bit more complex than the Intro:

-(void) setupFirstSong;
{
    // turn off all
    [self resetAllInstruments];

    // main
    [mbsBassMain        setRandChance:95  minBars:0   maxBars:0   minThreat:0   softThreat:0   maxThreat:100];
    [mbsBassFill        setRandChance:70  minBars:2   maxBars:0   minThreat:0   softThreat:10  maxThreat:1000];

    // leads
    [mbsLeadBuzz        setRandChance:100 minBars:30  maxBars:0   minThreat:20  softThreat:40  maxThreat:1000];

    // drums
    [mbsDrumMain        setRandChance:95  minBars:2   maxBars:0   minThreat:0   softThreat:1   maxThreat:100];
    [mbsDrumFat         setRandChance:95  minBars:6   maxBars:0   minThreat:20  softThreat:30  maxThreat:100];
    [mbsDrumFill1       setRandChance:60  minBars:6   maxBars:0   minThreat:0   softThreat:0   maxThreat:100];
    [mbsDrumFill2       setRandChance:70  minBars:6   maxBars:0   minThreat:15  softThreat:20  maxThreat:1000];  // accent
    [mbsDrumFill3       setRandChance:95  minBars:6   maxBars:0   minThreat:30  softThreat:40  maxThreat:60];    // before fill 4
    [mbsDrumFill4       setRandChance:95  minBars:6   maxBars:0   minThreat:50  softThreat:60  maxThreat:1000];  // after fill 3
}

This one is a bit more complicated. The minBars/maxBars indicate when the sample starts and stops in the progression. In this case, the main bass starts immediately, then the fill and main drums come in, followed by the drum fills, and finally the buzz at 30 bars. The sample will play if the threat is in min/max range (soft range is 50% gain), and if the random number is less than the random chance. In the above, if there are a lot of bullets and enemies on the screen (over 100 units), then the bass and some fills will stop playing, leaving just the buzz, fill2, and fill4 with a chance of playing. The later levels introduce a few new samples and fills to try to keep the music on pace with the bullet filled action.

Here’s a sample from one run of Level 8 on Normal mode.

Additionally, there was one other important detail since I was using the SimpleAudioEngine of the CocosDenshion Sound Engine from Cocos2d v2.0. Normally, the SimpleAudioEngine has a single SourceGroup that has a finite number of channels, where if all the channels are being used and a new sample is played, one will be overwritten. While the overwriting was ok for effects, it was not so great for music. I ended up creating a separate non-interruptable source group for music and other non-interruptable effects (e.g. Ship’s thrust).

-(void) setupNonInterruptableSourceGroup:(NSUInteger) numChannelsSrcGrp1 numChannelsSrcGrp2:(NSUInteger) numChannelsSrcGrp2;
{
    if ([soundEngine sourceGroupTotal] == 1){
        NSArray *defs = [NSArray arrayWithObjects:[NSNumber numberWithInt:numChannelsSrcGrp2],[NSNumber numberWithInt:numChannelsSrcGrp2], nil];
        [soundEngine defineSourceGroups:defs];
        [soundEngine setSourceGroupNonInterruptible:0 isNonInterruptible:FALSE];
        [soundEngine setSourceGroupNonInterruptible:1 isNonInterruptible:TRUE];
    }
}

-(ALuint) playEffectNoInterrupt:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain;
{
	int soundId = [bufferManager bufferForFile:filePath create:YES];
	if (soundId != kCDNoBuffer){
		return [soundEngine playSound:soundId sourceGroupId:1 pitch:pitch pan:pan gain:gain loop:false];
	} else {
		return CD_MUTE;
	}
}

The function setupNonInterruptableSourceGroup defines a second source group and sets it to NonInterruptable, and then playEffectNoInterrupt plays the effect (music sample in this case) using that NonInterruptable source group. I set the numChannelsSrcGrp2 to 16 since a few of the 11 samples I use in Trisector slightly bled over their bar boundary. Using this setup, I had 16 effects channels for all the bullets and player lasers, and 16 music sample channels for music and the ship’s thrust sound.

Hope you found this article on Trisector’s Music interesting,

Jesse from Smash/Riot

Recent Posts

About

Smash/Riot LLC focuses on interactive media, virtual experiences, games, visual effects, VR/AR, and networked experiences for modern platforms.