gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Audio > New music format...?

#162577 - Ruben - Fri Sep 05, 2008 11:19 am

Hi all. Was wondering if anyone was interested in certain specs for a new music format I'll be creating. So far, I've got... uh... *gets my notebook out* OK... so far, I've got these points down:

-16 fully pannable channels
-Uses 22.10 fixed point accuracy to allow for files of up to 4mb (like certain songs that can't be "emulated" per-s?)
-Supports 256 point attack, decay, sustain and release
-Up to 4 notes in 1 channel (though still limited to 16 notes polyphony)
-Pitch shifting
-Key shifting (like writing ".byte SHIFT, 2" then ".byte NOTE, C5" would play D5)
-127 levels of volume per channel
-256 levels of global volume
-Vibrato
-Instruments (like MIDI or IT-ish)

Anyone else got any specs they'd like that I'm missing? I plan to make this player free for all uses, but I won't release the sources for a while. Since these specs kinda fit for most tracker music and a bit of MIDI, I was wondering if maybe I should include MIDI support in the converter.

So any ideas?

#162578 - Kyoufu Kawa - Fri Sep 05, 2008 12:38 pm

Ruben wrote:
maybe I should include MIDI support in the converter.
Yes, you should. That'd be great. How 'bout sound effects?

#162581 - Ruben - Fri Sep 05, 2008 12:56 pm

Kyoufu Kawa wrote:
How 'bout sound effects?


Sound effects like which ones? Like lo-pass filters and the such? Or reverb, chorus, etc? Cos I'm still trying to figure out how reverb works along with Cearn (math genius :P I'm just good at math, not a genius).

Or am I TOTALLY off track and you meant "sound" sound effects? ... OK, lemme rephrase that...
By "sound effects", do you mean like, say, a blaster sound effect and having dedicated channels for that?

#162584 - Kyoufu Kawa - Fri Sep 05, 2008 2:13 pm

Ruben wrote:
By "sound effects", do you mean like, say, a blaster sound effect and having dedicated channels for that?
Yes, yes I did.

Actually, some kind of priority system would be nice... when playing a sound, find a channel that the music's not using at that moment. If the music needs a channel that's already used by a sound effect, it drops that note instead. Or some shit like that, I dunno.

#162587 - Ruben - Fri Sep 05, 2008 2:32 pm

Quote:
Actually, some kind of priority system would be nice... when playing a sound, find a channel that the music's not using at that moment...

That's kinda what I have planned... something like:
-16 "real" channel structures; these get read and then mixed by the mixer. So far, they only have 4 words:
Code:
typedef struct {
   u8  stat;   //status
   u8  pan;    //panning (0-FFh)
   u8  lvol;   //left volume (0-7Fh)
   u8  rvol;   //right volume (0-7Fh)
   u32 pos;    //position (22.10)
   u32 inc;    //increment (22.10)
   smS *wp;    //wave pointer
   
   //smS typedef is the same as the GBA BIOS' sound structure cos I'll be using the MIDIKey2Freq function
} chn;


-16 "fake" channel structures; placed before the "real" channels, they contain a very small amount of information. Namely, only channel music data. In that structure, the music data gets stored, like ticks left till next read, expression, pitch, origin, etc.

So when you call the SFX function... I was thinking of either doing the above and search for an empty channel (there's an int in the mixer data stating channel modes)... or having dedicated channels for SFX. Which one do you think would probably be better?

#162599 - Kyoufu Kawa - Fri Sep 05, 2008 6:21 pm

I'd go for finding free channels, myself.

Know what else'd be cool? If the sound effects could be done -as- music data.

#162604 - tepples - Fri Sep 05, 2008 8:54 pm

Consider this architecture:

A song is a list of references to patterns and when and how to play them.
A pattern is a list of references to instruments and when and how to play them.
An instrument is "like MIDI or IT-ish".

The API has these functions:
  • Play a song with settings
  • Play a pattern with settings
  • Play a note on an instrument with settings
  • Set modifiers of playing song, pattern, or note

Sound effects would be stored as instruments or patterns depending on their complexity.

Another question: What sort of music editor were you planning? Some sort of bytecode that one would use with a compiler (like LilyPond)? Or a custom graphical editor like a tracker?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#162616 - Ruben - Sat Sep 06, 2008 12:29 am

Tepples:

What I had planned was to allow users to make their music in whatever application they use (MODPlug Tracker, MIDI Maestro, etc.), then have the files converted into a format the player will recognize. What I had planned on doing was something like:

-A song has an ID for verification, a few settings like global volume, name, etc, and pointer to tracks.

-A track is a list of events occurring for channel x, which are stored as track-only data; IOW, a track will only have settings like "set program, set volume, play note x at a velocity of v." The converter would deal with making the tracks into this (ie. S3M is stored like a type 0 MIDI file; the converter would convert it into a format similar to a type 1 MIDI file).

Kyoufu:

Like, say, a special effect that uses more than 1 sound and has like pattern data? Yeah, that's possible since the channels would just point to tracks which contain data for that channel, and that channel only. I was also thinking of supporting the sound generators in case the player reaches > 16 notes... but if the sound was, say, a timpani... that would sound weird... so I was thinking if I should support them or not.

#162620 - tepples - Sat Sep 06, 2008 2:56 am

Ruben wrote:
-A track is a list of events occurring for channel x, which are stored as track-only data; IOW, a track will only have settings like "set program, set volume, play note x at a velocity of v." The converter would deal with making the tracks into this (ie. S3M is stored like a type 0 MIDI file; the converter would convert it into a format similar to a type 1 MIDI file).

So what you call "tracks" are similar to my patterns, except that patterns can be played multiple times over the course of a file or looped. It's like type 2 MIDI files.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#162621 - Ruben - Sat Sep 06, 2008 3:07 am

tepples wrote:
So what you call "tracks" are similar to my patterns...

Well, I haven't actually had the time to look at the music player in TOD, but in case it wasn't clear enough, my "tracks" are something like:

Code:
event - data
--------------

PROGRAM, 00 <- Piano 1
VOLUME, 127 <- Volume 127
TEMPO, 154 <- Set tempo for this song to 154

looppoint:
NOTE, C5, 127 <- Note on (C5 @ 127)
WAIT, 3 <- Wait 3 ticks
NOTE, C5, 0 <- Note off (C5)
WAIT, 3 <- Wait 3 ticks
JUMP, looppoint <- Jump to looppoint
EOTr <- End of track

#162637 - tepples - Sat Sep 06, 2008 6:37 pm

I'll take apart idltd.in, the source code for the BGM in TOD. You might use it for inspiration.

Code:
speed 10
channels 6

This tells Tri Engine to play at 89.6 BPM (10 ticks per sixteenth note, where a tick is 280,896 CPU cycles), and to allocate 4 tone generators and 2 voices to music.

Code:
instruments
11-4,0x65  #deep kick
10-1,0x24  #clap
 5-1,0x04  #hat
 6-3,0x04  #open hat
10-7,0x40  #normal note
   9,0x40  #sustain note
10-4,0x00  #B back note
 7-4,0x40  #C back note

Starting volume, decay rate, and timbre for channels 1, 2, and 4.

Code:
ch3instruments
012457899aaa99887665556678abdef9  # saw without fundamental
741007a9fba57b848a634945a75acfda  # bass

Waveforms for channel 3 (the "triangle channel" to NES fans).

Instrument definitions for channels 5 and 6 (soft-mixed PCM) are in another file.

Code:
orders
g1 g2 g3 g4 g5 g6

1 p7 p23-5 p8-5 p9-5 p10-5
  p11 p11 p11 p11 p11 p11 p11 p11 p13
  p14 p14 p14 p14 p14 p14 p14 p14
  p14 p14 p14 p14 p14 p14 p16 g1
2 p7 p9 p9 p9 p10
  p12 p12 p13
  p45-12 p45-12 p45-12 p45-12
  p45-12 p45-12 p45-12 p15 g2

3 p7 p8-12 p8-12 p8-12 p0-12
  p1 p1 p1 p1 p1 p1 p1 p1
  p2 p4 p4 p4 p4 p4 p4 p6 p5 g3
4 p17 p18 p19 p17 p18 p19
  p17 p18 p19 p17 p18 p19 p17 p18 p19 p17 p18 p19
  p20 p20 p20 p20 p20 p20 p20 p20
  p21 p22 p22 p22 p22 p22 p22 p15
  g4

5 p30 p31 p31 p31
  p31 p31 p31 p31 p31 p31 p31 p31
  p32 p33 p33 p34 p33 p33 p33 p34
  p35 p37 p36 p38 p36 p37 p36 p38
  p36 p37 p36 p38 p36 p37 p39
  g5
6 p40 p40 p40 p40 p40 p40 p40 p40 p40 p40 p40 p40
  p41 p42 p42 p42 p42 p42 p42 p42 p43
  p4 p4 p4 p4 p4 p4 p4 p44
  g6

'g' is a goto command, 'p' is a play command that selects a "pattern" aka "track" from the patterns section, and 1-6 are labels.

Code:
patterns

# [snip]
#sample 2: pick bass
40 i3 g0 h1 r2 g0 h1 r1 d1 h1 d0 f1 h1 f1 h1 d1 h1
      g0 h1 r2 g0 h1 r1 d1 h1 d0 f1 h1 f1 d1 c1 bb0 end

0 is a label
'i#' means change program to #. Instrument 0 has a special meaning, used mostly for drum tracks (noise and PCM). It acts like a program change before each note depending on the note's declared pitch, but the actual note is played at middle C.
'a#' through 'g#' mean play a note at octave #, where 'c2' is middle C, then hold for 1 sixteenth note. The 's' (sharp) or 'b' (flat) raises or lowers a pitch by 1 semitone.
'h#' means hold for # sixteenth notes
'r#' means rest: cut the note, then wait for # sixteenth notes
'n#' used in some of the patterns means play a note # semitones above c0. It was added to the system before the a-g commands.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#162668 - Ruben - Mon Sep 08, 2008 1:57 pm

tepples wrote:
Code:
speed 10
channels 6

This tells Tri Engine to [snip] allocate 4 tone generators and 2 voices to music.

Well... I personally think it'd be better to enable the tone generators using bits. IE:
Code:
#define SQR1_ON 0x01
#define SQR2_ON 0x02
#define SAW_ON 0x04 //channel 3 data; written as saw on start up
#define SONG 0x80 //just for verification

typedef struct {
 u8 flags;
 u8 voice_count; //actual voices, not TG's
 u8 track_count;
 u8 xx; //extension?
 
 smp *inst_tbl;
 
 u8 *tracks[];
} song;

const u8 mysong_tr1[] = {
 ...
};

const u8 mysong_tr2[] = {
 ...
};

...

const smp *mysong_insttbl[] = {
 inst1,
 inst2,
 ...
};

const song mysong = {
 SQR1_ON | SAW_ON | SONG, //turn on the square generator and the saw wave generator. tell the engine this *is* a song
 7, //song has 7 "real" samples (sample data)
 16, //16 soft-mixed channels
 0,
 
 mysong_instbl,
 
 mysong_tr1,
 mysong_tr2,
 ...
};


tepples wrote:
Code:
instruments
11-4,0x65  #deep kick
10-1,0x24  #clap
 5-1,0x04  #hat

...


??? How does that work? I would personally do something like...

Code:
typedef struct {
 u8 start_key; //key range - from
 u8 end_key; //key range - to
 u8 root_key; //original key
 u8 fine_tune; //additional finetuning (for use with MIDIKey2Freq)
 smp *samp; //pointer to WaveData-style sample
} subsample;

typedef struct {
 u16 smpcnt; //sample count for this instrument
 u16 flags; //I'm not sure...
 u8 attack;
 u8 decay;
 u8 sustain;
 u8 release;
 
 subsample *subsmp[];
} instrument;

But I'm not sure... :-/

#162679 - tepples - Mon Sep 08, 2008 8:24 pm

Ruben wrote:
tepples wrote:
Code:
instruments
11-4,0x65  #deep kick
10-1,0x24  #clap
 5-1,0x04  #hat

...

??? How does that work?

It's a very thin syntactic sugar over raw writes to GBC PSG registers (see belogic.com/gba/). 11-4 means start at volume 11 and decrease every 4 ticks (64 ticks in a second, unlike the 60 that one would expect), and 0x65 is the value written to the noise frequency register. For channels 1 and 2, as on the NES, 0x00 is 1/8 duty, 0x40 is 1/4 duty, and 0x80 is 1/2 duty.

Quote:
I would personally do something like

That'd probably work OK for sampled instruments and anything else that can use a software envelope. It wouldn't work so well for GBC PSG instruments because of the click whenever you change the volume on a GBC channel, which is part of why PocketNES doesn't sound perfect.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#162682 - Ruben - Tue Sep 09, 2008 12:00 am

Well... after thinking about that, I thought maybe that structure should be for software-enveloped instruments, and the tone generators have nothing like that. IE:

Code:
@ track 1, 2, 3, 4 ...

@ track 8 (square 1 TG)
mysong_tr8:
 .byte VOICE, 0xFF @ FFh: square 1, FEh: square 2, FDh: saw
 .byte VOLUME, 0x07
 
 ...
 
 .byte EOTr


As soon as the player sees a voice value that's >= FDh, it doesn't assign a software channel, but just writes to the "fake" channel structure with the sound data and writes a flag as to what it's playing right now.

#162799 - Ruben - Sat Sep 13, 2008 9:09 am

OK. So I've been thinking and thinking... and I finally came up with something:

-Square2 (at least that's what it's called in XG) is played by channel 1 and 2
-Saw2 (ditto) is played by channel 3

Both of these patches/programs/instruments/etc are hard-coded to run at 50% volume (IOW: No volume changes for them and definitely no envelopes). All other instruments are software mixed. They all have 256 position attack, decay, sustain and release, with a maximum polyphony of 32 notes (Yes, increased now after I made the mixing routine faster; theoretically, 32 channels on would be ~56% CPU (~1.7% per channel) @ 18157Hz, plus another 4% or 5% for player load). The conversion program will look for program patches in a file called "patchinfo.inf", which contains data like this:

Code:
[0]
name=Acoustic Grand Piano
folder=samples\grndpno
0=piano_c0.wav ;starting at C0, which is note number 0...
24=piano_c2.wav ; ... ends at B1, with C2 starting with "piano_c2.wav"


So... any ideas?

And more importantly... does anyone know where I can find PD sound files which I might be able to use as an example?

#163049 - Ruben - Sat Sep 20, 2008 1:35 pm

OK. Did a lot more thinking and I *think* I might use my keyboard as an example sound bank (though I'm not sure if it's legal/etc to do it... hm).

As for instruments... I plan to do something like...

Code:
typedef struct {
 u8   attack;
 u8   decay;
 u8   sustain;
 u8   release;
 
 u8   smp[]; //numbers are "pointers" per-se to samples in the sample pool
} instrument;

const instrument someinst = {
 24,  //attack starts at 24
 234, //decay starts at 234
 256, //sustain level is 256
 63,  //release starts at 63
 
 {
  0, //points to smppool[0] (Cn0)
  7, //points to smppool[7] (Cs0)
 };
};

const smp *smppool[] = {
 &smp1,
 &smp2,
 ...
};

Is that a "sane" format?
As for tracks...

Code:
typedef struct {
 char id[3];   //ID - should be "TRK" or something
 u8   chncnt;  //channel count
 
 u8  *track[]; //pointer to tracks
} song;

Then you define a song with tracks that have a variable tempo each (too much?) and have a pre-defined sample bank for all songs. Like...

Code:
const smp *smppool[] = {
 &strings, //first used instrument detected by converter
 &gpiano1, //second ""      ""        ""    ""     ""
...
};

const u8 song001_trk001[] = {
 SET_PROGRAM, 1, //change to gpiano1
 SET_TEMPO, 125, //change tempo to 125
 MULTINOTE_S,    //multinote start
 NOTE, C4, 127,  //play C4 @ 127
 NOTE, E4, 127,  //play E4 @ 127 along with C4
 MULTINOTE_E,    //multinote end
 WAIT, 3,        //wait 3 ticks
 NOTEOFF_ALL,    //do a note off on all notes started by this channel
 END,
};

Any opinions?

#163051 - Kyoufu Kawa - Sat Sep 20, 2008 2:24 pm

Ruben wrote:
Then you define a song with tracks that have a variable tempo each (too much?)
Too much if you ask me.

#163054 - Ruben - Sat Sep 20, 2008 2:46 pm

Yeah, I thought it was too much but I can't think of any other way to implement a song-like structure that has a tempo of it's own that runs parallel to another song... Hmm...

#163056 - Kyoufu Kawa - Sat Sep 20, 2008 5:21 pm

Keep timers for each track, but have tempo commands set the tempo for all tracks in that song?

#163060 - keldon - Sat Sep 20, 2008 10:55 pm

Ruben wrote:
Yeah, I thought it was too much but I can't think of any other way to implement a song-like structure that has a tempo of it's own that runs parallel to another song... Hmm...


Hmm; well run two song instances in parallel - seems about the most obvious angle <_<

#163087 - Ruben - Mon Sep 22, 2008 6:03 am

LMAO @ keldon.

Anyway... I think I'll allow a maximum of 2 songs running at the same time and keep a variable on each track to see what song it's running... Hmm... why am I coding this whole thing in ASM only..?

#163383 - Ruben - Wed Oct 01, 2008 6:08 am

OK. Re-writing the whole engine. Everything except the actual mixer is coded in C. Maximum of 4 tracks at a time, 32 channels polyphony, 1 track/pattern controls the whole song and all the channels (kinda like the S3M format). So far, it uses 2KBs of EWRAM and 2.8KBs of IWRAM. IWRAM I know is going to rise a fair bit with the mixing routines but that's all for now.

Only uses DMA1 (and DMA2 if built with the stereo define) and TM0 and is pretty light on CPU usage (1.7% CPU per channel, no looping @ 18157Hz). (The BIOS routines eat up *TWELVE* scanlines per channel @ 13379Hz! :O)

Since instruments are going to be multi-sampled (aka: different samples at different "regions"), I'm changing the music format around to something similar to that of S3M, and pre-calculating the samples on the conversion.

Anything I'm missing?

#164010 - Ruben - Sat Oct 18, 2008 1:36 pm

Okey-dokey...

First, I tried using a temp buffer (all @ 18157Hz). ~1.7% CPU per channel. OK, but could be a LOT better.

No temp buffer = ~2% CPU per channel. Not good.

So...
What I've thought of is coming up with another mixer (please don't hate me! I just love writing and re-writing mixers trying to challenge myself~ ^___^)

-32 channels, 128 panning positions, 0-127 volume range.
-Low IWRAM usage (~1KB, 3KB tops), average EWRAM usage (3-4KB).

-Master mixing frequency: 32768Hz (Got that number from GBATek on the GBA's re-sampling frequency)
-Maybe I'll incorporate support for more frequencies later on.
-Up to 4 tracks playing at once
-Support for the square channels (will explain in the future (or quickly: Name a sample "__SQUARE__" and it will play back through the pulse generators, configure with different "undefined" tracker commands and SysEx MIDI commands))
-Most code written in pure ASM (Everything except the music player will be written in ASM)
-Free for non-commercial use, and sorry but no commercial use just yet. d(^_^)
(Remember, I AM 14! :P)

Oh and in case you've been wondering:
No, I'm not going to scrap this project and I haven't scrapped it either. I just haven't worked out a fast mixing algorithm.

#164047 - Ruben - Sun Oct 19, 2008 1:38 pm

Quote:
-Master mixing frequency: 32768Hz


Did I say 32768Hz? I meant 18157Hz, sorry. :P
(Question: Do you think I should re-sample to 36314Hz in the mix down, or is that just crazy?)

#164048 - tepples - Sun Oct 19, 2008 2:51 pm

Ruben wrote:
(Question: Do you think I should re-sample to 36314Hz in the mix down, or is that just crazy?)

GSM Player linearly interpolates the 18157 Hz decoded stream to 36314 Hz. But as an April Fool's joke, I also made a proof-of-concept game demo based on a generic mode 7 engine, and its version of GSM Player lacked this sort of resampling. (This version ended up becoming Luminesweeper's music player.) A couple people on that other forum actually preferred the aliased sound of the demo compared to the "muffled" sound of the original GSM Player. So it might be wise to compare the results with and without resampling, when played through a GBA and headphones.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#164053 - Ruben - Sun Oct 19, 2008 5:59 pm

HOLY %^@#!!!!!!!!!!!!!!!

Just... WOW!!! That sounds effin' incredible!!! Looks like 18157Hz with no "final result" re-sampling sounds best~ Thanks! ^_________^

And DAMN! I couldn't even tell those files were compressed from how good they sounded!!!

#164110 - naleksiev - Mon Oct 20, 2008 9:54 pm

Ruben wrote:
I might use my keyboard as an example sound bank (though I'm not sure if it's legal/etc to do it... hm).


I was thinking about doing a tracker before but never finished it. Anyway I was using different method for getting the samples. I created a single note MIDI files and then played them and capture the output using WinAMP. This method also allowed me to choose the quality I want to for the samples. Also I thought it's probably better quality because the sound never get converted to analog. Of course I don't know as well how legal this is.

#164118 - Ruben - Tue Oct 21, 2008 2:49 am

naleksiev wrote:
I was thinking about doing a tracker before but never finished it. Anyway I was using different method for getting the samples. I created a single note MIDI files and then played them and capture the output using WinAMP.

Yeah, I've done that too but I guess the samples are definitely illegal to use, since they were taken from a sound driver without permission.

naleksiev wrote:
This method also allowed me to choose the quality I want to for the samples.

Really? I've been able to record all my samples @ 18157Hz all the time, from anywhere.

#164639 - Ruben - Tue Nov 11, 2008 7:20 am

OK guys. A demo of AGBMidi has been sent to the news e-mail so be ready~

d(^_^)

Has example song data (written by hand).. namely, this...

Code:
//////////////////////////////////

.global sng_TestSong
.align

sng_TestSong:
   .word   2
   .word   smpBank001
   
   .word   sng_TestSong_Trk001
   .word   sng_TestSong_Trk002

.align
.size sng_TestSong, .-sng_TestSong

//////////////////////////////////

.global sng_TestSong_Trk001
.align

sng_TestSong_Trk001:
   .byte   MIDI_TEMPO, 172
   .byte   MIDI_VOLUME, MIDI_MAX_VAL
   .byte   MIDI_PAN, MIDI_CNTR_VALUE
   .byte   MIDI_PROGRAM, 1
   .byte   MIDI_WHEEL, MIDI_CNTR_VALUE
   .byte   MIDI_KEY_SHIFT, 0

.LTrack001_Loop0:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_A, MIDI_OCTMID), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_A, MIDI_OCTMID)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_E, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_E, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_G, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_G, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(36)
   .byte   MIDI_WAIT(36)
   .byte   MIDI_WAIT(36)
   .byte   MIDI_WAIT(36)
   .byte   MIDI_WAIT(24)
   .byte   MIDI_WAIT(24)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID+1)
   
//////////////////////////////////

.LTrack001_Loop1:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_E, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_E, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_D, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_D, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_A, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Cs, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_A, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Cs, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_D, MIDI_OCTMID+1), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-1)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_D, MIDI_OCTMID+1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID), 127
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_E, MIDI_OCTMID), 127
   .byte   MIDI_WAIT(48)
   .byte   MIDI_WAIT(48)
   .byte   MIDI_WAIT(48)
   .byte   MIDI_WAIT(48)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_E, MIDI_OCTMID)
   
   .byte   MIDI_LOOP
   .word   .LTrack001_Loop1
   .byte   1
   
   .byte   MIDI_EOF

.align
.size sng_TestSong_Trk001, .-sng_TestSong_Trk001

//////////////////////////////////

.global sng_TestSong_Trk002
.align

sng_TestSong_Trk002:
   .byte   MIDI_VOLUME, MIDI_MAX_VAL
   .byte   MIDI_PAN, MIDI_CNTR_VALUE
   .byte   MIDI_PROGRAM, 0
   .byte   MIDI_WHEEL, MIDI_CNTR_VALUE
   .byte   MIDI_KEY_SHIFT, 0

.LTrack002_Loop0:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_A, MIDI_OCTMID-2), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_A, MIDI_OCTMID-2)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_E, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_E, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_A, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_A, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_G, MIDI_OCTMID-2), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_G, MIDI_OCTMID-2)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_D, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_D, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_G, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_G, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_F, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_F, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1)
   
   .byte   MIDI_LOOP
   .word   .LTrack002_Loop0
   .byte   1
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_A, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(36)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_A, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_G, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(36)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_G, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(36)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Fs, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_E, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(36)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_E, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_Ds, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(24)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_Ds, MIDI_OCTMID-1)
   
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(24)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1)
   
//////////////////////////////////

.LTrack002_Loop1:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-3), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-3)
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-2), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-2)
   
   .byte   MIDI_LOOP
   .word   .LTrack002_Loop1
   .byte   7

.LTrack002_Loop2:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID-2), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID-2)
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1)
   
   .byte   MIDI_LOOP
   .word   .LTrack002_Loop2
   .byte   7
   
.LTrack002_Loop3:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-3), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-3)
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_B, MIDI_OCTMID-2), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_B, MIDI_OCTMID-2)
   
   .byte   MIDI_LOOP
   .word   .LTrack002_Loop3
   .byte   7

.LTrack002_Loop4:
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID-2), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID-2)
   .byte   MIDI_NOTE_ON, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1), 127
   .byte   MIDI_WAIT(12)
   .byte   MIDI_NOTE_OFF, MIDI_NOTE(MIDI_C, MIDI_OCTMID-1)
   
   .byte   MIDI_LOOP
   .word   .LTrack002_Loop4
   .byte   7
   
   .byte   MIDI_EOF

.align
.size sng_TestSong_Trk002, .-sng_TestSong_Trk002

//////////////////////////////////

.global smpBank001
.align

smpBank001:
   .word   inst_square
   .word   inst_string

.align
.size smpBank001, .-smpBank001

//////////////////////////////////

#165198 - Ruben - Fri Dec 12, 2008 12:14 pm

Hi guys.

After a bit of inactivity on the forum, I've finished AGBMidi 1.0a and released it. It can be found here.