Sound Engine crash Course

From Uzebox Wiki
Revision as of 22:16, 9 January 2009 by Uze (talk | contribs) (New page: If you are like most, composing music is not your cup of tea. However, you will indeed want to quickly put some sound FXs in your games. Here's a quick crash course on how to do just that....)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

If you are like most, composing music is not your cup of tea. However, you will indeed want to quickly put some sound FXs in your games. Here's a quick crash course on how to do just that.

The current engine works with the concept of "patches". Patches are a sequence of parameters that defines how your notes or sound effects evolves over time. There's already a couples of patches I've made for Megatris I've regrouped in an include file. Add this include to your program file:


   #include "data/patches.inc"


IF you are useing the pre-beta3 kernel, look into it, you will see something like:


   //FX: "Echo Droplet"
   const char patch01[] PROGMEM ={
   0,
   0,PC_ENV_SPEED,-12,
   5,PC_NOTE_UP,12,
   5,PC_NOTE_DOWN,12,
   5,PC_NOTE_UP,12,
   5,PC_NOTE_DOWN,12,
   5,PC_NOTE_CUT,0,
   0,PATCH_END
   };


This is called a command stream. The first byte, is the sound type. 0 is for wavetable sounds, 1 for noise channels sound. (from beta3 and onwards, this byte is removed, more on that later). The rest is a sequence of commands. Commands are made of 3 bytes: the first one is a time delta in term of frames (frames happens at 1/60 of a sec) to wait until this command is executed. The second byte is the command type and the last byte is the command value. So in this example we have a wavetable sound (0), then when the sound is triggered (time zero), set the volume envelope decay speed to -12. On each frame after wards, -12 will automatically be subtracted from the sound's volume. Then, after a wait of 5 frames, raise's the sound pitch by 12 semitones (one octave). Then wait again for 5 frames then lower the sound's pitch by an octave. And so on, until the last command, which must be a PATCH_END command (no value byte for this one). Have a look at the .h files for all the possible commands.

From Beta3 the patch system has been tweaked to support a PCM channel which allows to play samples of arbitrary length. The sound type byte as been removed from the command stream and move into a special array of structs.

   const struct PatchStruct patches[] PROGMEM = {
   {0,NULL,patch00,0,0},
   {0,NULL,patch01,0,0},
   {0,NULL,patch02,0,0},
   {0,NULL,patch03,0,0},
   {1,NULL,patch04,0,0},
   ...
   };


Let's interpret one entry:


   {1,NULL,patch04,0,0} 


First parameter (1) is the sound type, in this case its to be played on the noise channel (0=wave,1=noise,2=PCM) Second parameter (NULL) is a pointer to the PCM data if it were a PCM patch Third parameter (patch04) is the patch's command stream pointer Fourth (0) is the loop start position for PCM samples Fifth (0) is the loop end position for PCM samples

Soooo....now let play some sounds! Add this line to your main() function:


   InitMusicPlayer(patches);


And *then* you can trigger fxs anywhere in your code. If you look in patches.inc, patch 19 is the "t-spin" fx from megatris


   TriggerFx(19,0xff,true);


In this case: 19 is the patch number 0xff is the volume true is the 'retrig' attribute. It basically means that if another TriggerFx() call is made for the same patch *before* the previous one is finished playing, it will re-trigger it right away on the same channel instead of starting another simultaneous instance of the sound onto another channel (determined by the voice stealing algorithm).

Creating new patches is really trial an error. I suggest you make an empty project to create new patches. It will compile and flash faster.

That's it!