Dsaudio

From ProjectWiki
Revision as of 19:28, 17 February 2008 by Eris (Talk | contribs)
Jump to: navigation, search

Stuff I'd like to see in the libnds audio implementation

/**************************************************************************************
 thaudio.c - informal RFCish type document for improved lib nds audio implementation. Read everything b4 bitching about anything plz. :p
 
  Design considerations: efficently using fifo, memory, and cpu balanced against ease of use and flexibility.
 
 Document is in 2 parts (so far), first an example .h (cause im lazy and dont feel like retyping all this later :P) and secondly some usage examples. May add arm7 notes..
 
  What is here so far is based on discussions in #dsdev, various libnds and hardware docs on the web, and my many years audio/dsp, embedded programming, and music making experiences. If im overlapping anything else already in development, it is not my intention to step on anyones toes. Objective is simply to make the best audio library available for the DS developer without need to change things later... And then i may complete my Touhou fan game!!!!! \^_^/
 
 eris - 2008/02/15
 
**************************************************************************************/
 
 
/**************************************************************************************
 ARM9 HEADER file showing example UI functions..
 
***************************************************************************************/
 
#ifndef THAUDIO
#define THAUDIO
 
//Flags for sample and channel functions
#define MMFLAGS_LOOP	0x01	//Loop sample
#define MMFLAGS_STEREO	0x02	//stereo sample (requires 2 channels no?)
#define MMFLAGS_STICKY	0x04	//If set indicates this sample cannot be stoped in order to play new sample if all channels are busy. In this way samples can have one of two priority levels as well. If set on a channel can be used to allocate sample for user use (psg for example).
 
//Note only one of these four format flags can be used at once
#define MMFLAGS_PCM8	0x00
#define MMFLAGS_PCM16	0x10
#define MMFLAGS_ADPCM	0x20
#define MMFLAGS_PSG	0x30//if set on channel indicates PSG
//These are used internally
#define MMFLAGS_FROMFILE 0x08	//used internally to indicate the sample was loaded from a file and to free() sample memory apon mmSampleUnload() call
 
//Misc Stuffs
void mmSoundInit(void);	//Initalize audio system (might need a reinit that will free() dynamics mem allocs?)
/*
 Could dynamically alloc ram for arm7's sample and if implemented synth tables allowing user to only allocate what they need. ie: 
  bool mmSoundInitAdvanced(u8 maxsamples, u8 maxpatches) {
    sampletable=malloc(maxsamples*sizeof(sampleentry));
    patchtable=malloc(maxpatches*size(patchentry));
  }
*/
void mmSoundSetMasterLevel(u8 level); //Sets Master audio level (default to full)
void mmSoundSpkrEnable(bool enable); //enables/disables speakers (should be enabled by default in init)
 
//audio sample loading commands
//all 'Load' commands return mmSample handle or -1 if error
//returning u16 value to support up to 65536 samples, however may just use u8 for 256 samples to reduce ram usage. (should be plenty and 65536 is overkill!!)
void mmSampleDefaults(int rate, char level, char pan, u8 flags); //set default rates used by all subsequently 'SimpleLoad'ed samples 
u16 mmSampleLoad(void *start, int len, void *loopstart, int rate, char level, char pan, u8 flags);
u16 mmSampleSimpleLoad(void *start, int len); 
void mmSampleUnload(u16 mmSample); //stops playing, removes sample from sampletable, and if loaded from file (see below) will free() malloc'd ram..
//load sample from filesystem (in raw or wav (or mp3 would be really nice) format)
u16 mmSampleLoadFile(char *file, int rate, char level, char pan, u8 flags);
u16 mmSampleSimpleLoadFile(char *file);
u16 mmSampleLoadWavFile(char *file, char level, char pan, u8 flags);	//riff header is uber ez to parse so why not support .wav format??
u16 mmSampleSimpleLoadWavFile(char *file);
 
char mmSamplePlay(u16 mmSample); 	//returns channel mmChan or -1 if no channels available (if stereo sample high nibble contains right channel # and low nibble contains left channel number)
void mmSampleStop(u16 mmSample); 	//stops sample on all channels currently using it
void mmSampleSetRate(u16 mmSample, int rate);
void mmSampleSetLevel(u16 mmSample, char level);
void mmSampleSetPan(u16 mmSample, char pan);
void mmSampleSetFlags(u16 mmSample, char flags); //ORs Flags? or should just set them??
void mmSampleClearFlags(u16 mmSample, char flags); //if go with ORing
 
//Channel Specific functions.
//if mmSamplePlay() reuses the channel 'Set' values are lost
// mmChan must be 0-15, if stereo sample (and high nibble is used) user is required to mask off upper bits
void mmChanStop(char mmChan); 		//stops sample based on the channel
void mmChanSetRate(char mmChan, int rate);	//sets sample rate
void mmChanSetLevel(char mmChan, u8 level);	//sets vol of a specified channel
void mmChanSetPan(char mmChan, char pan);		//sets pan of specified channel
void mmChanSetFlags(char mmChan,u8 flags);	//sets flags (for instance STICKY, not all flags valid for chans)
void mmChanSamplePlay(char chan, int mmSample);	//Prolly normally not used, but if u did want to use a specific channel for a specific reason, here ya go..
u8 mmChanGetStatus(char chan); //returns channel busy flag and mebbe other things? 
 
//PSG/NOISE channel related functions, these may be unnecessary if the synth option is implemented...
void mmChanPsgNote(char mmChan, u8 note);	//Plays specified diatonic note for instance 0=LowestC 12=next octave's C etc etc 0 tru 127 sorta like midi
void mmChanPsgNote(char mmChan, u8 note, u8 level, u8 pan,u8 duty); //same as above but sets all at once
void mmChanPsgNoteDetune(char mmChan, int amount); //Detunes note by specified amount (for pitch bends)
char mmChanGet(u8 type);	//returns a free channel than can be used for psg or whatever (or -1 if none)  type specify to get any free channel, or psg channel, or noise channel using defines below... MAY need to at the same time flag this channel as being in use to prevent arm7 from using it before arm9 gets a chance.. tho only should happen if interrupts or multiprocess?! In that case change name to mmChanAlloc
 
#define MMGET_ANY	0x0
#define MMGET_PSG	0x1
#define MMGET_NOISE	0x2
 
/* This is wholely unnecessary but left in for discussion purposes. Originally i intended this for things such as possible streaming audio or arm9 based sequencers...
 
void mmSoundSetCallback(void *function); //arm9 function to be called when a sample reaches end should be in the form of: void sampleCallback(char mmChan, u16 mmSample) {}; 
 
Not needed because, a simpler (from library, not user, standpoint) more flexible way to stream would be:
 create say a 2048 byte buffer
 load first 1024 bytes from source
 start a looping sample pointing to start of buffer
 start a timer with interval equiv to 1024 samples
 every timer interval load next 1024 bytes to either first or second half of sample buffer alternating with each timer interrupt thus creating a continuious stream of audio :D
*/
 
//Synthesizer
//now would be the time to add synth functions (using the psg/noise and mostly arm7 cpu) such as: AD(SR) envelope generators for volume. A few (4 works for me) LFOs that can be assigned to volume, dutycycle, pitch, or pan of each 'patch'.
 
//Pass this info as a struct?? would save memory that way :)
u8 mmSynthPatchCreatePsg(u16 atime, u16 dtime, u8 slevel, u16 rtime, u8 envamount, u16 glidetime, u8 duty, u8 pan, u8 vollfo, u8 dutylfo, u8 pitchlfo, u8 panlfo); //4 lfo params can be combined into one byte if only 4 lfo are available
u8 mmSynthPatchCreateNoise(u16 atime, u16 dtime, u8 slevel, u16 rtime, u8 envamount, u8 vollfo, u8 dutylfo, u8 panlfo);
u8 mmSynthPatchCreateSample(u8 mmSample, u16 atime, u16 dtime, u8 slevel, u16 rtime, u8 envamount, u16 glidetime, u8 pan, u8 vollfo, u8 pitchlfo, u8 panlfo);
//4 LFO that can be used by a patch to modify various patch params
void mmSynthLfo(u8 lfo, u8 lfotype, u16 freq, u8 amount); //lfo = 0 thru 3 indicating which lfo to adjust. freq = fixed decimal frequency, 8 bits for decimal. amount is the max level , type is either triangle, square (mebbe sawtooth ramp up, ramp down for bomp bomp bomp or womp womp womp effects :)
u8 mmSynthNoteOn(u8 patch, u8 note); //returns channel used...
u8 mmSynthNoteOnVel(u8 patch, u8 note, u8 velocity); //same as above but with velocity (volume) param
void mmSynthNoteOff(u8 patch, u8 note); //stops playing note based on patch/note (note u could also use the mmSynthNoteOn()'s return value with mmChanStop() for doing same thing with less overhead);
//Add mmSynthNoteBend() or just use the mmChanPsgNoteDetune ??? would be less cpu ... dunno
#endif

/**************************************************************************************

USAGE EXAMPLES!

Simplest sample playback scenario:

mmSoundInit(); //Initalize sound lib and set default params (22050khz, PCM8, no loop, mono)
mmSamplePlay(mmSampleSimpleLoad((void *)mysample_raw, mysample_len)); 

Okai, mmSoundInit(); must be called at least once for everything else so im not gonna bother putting in further examples

Playing a 44100khz 16bit sample and stopping it after a little bit...

u16 testsample=mmSampleLoad((void *)mysample_raw, mysample_len, NULL, 44100, 127, 64, MMFLAGS_PCM16);
mmSamplePlay(testsample);
msleep(20000);
mmSampleStop(testsample);

Playing a sample from filesystem using the default rate/vol/pan/flags:

u16 testsample=mmSampleSimpleLoadFile("/pathto/mysample.raw");
mmSamplePlay(testsample);

Reusing the same channel for same sample

u16 testsample=mmSampleSimpleLoad((void*)mysample_raw,mysample_len);
u8 samplechan=mmSamplePlay(testsample);
doSomethingForABit();
mmChanSamplePlay(samplechan,testsample); //reuse same channel

Alternate way to do sorta same thing, in this case make a imaginary spaceship's laser firing noise:

u16 shipshootsample=mmSampleSimpleLoad((void*)mysample_raw,mysample_len);
u8 shipshootchannel=mmChanGet(MMGET_ANY); //Get an unused sample channel
mmChanSetFlags(MMFLAGS_STICKY); //optional, used to indicate this channel shouldnt be dynamically allocated to other sounds
while(1) {
 if(shipgotshot) {
  mmChanSamplePlay(shipshootchannel,shipshootsample);
 }
};
                                                                                                                                                                            • /
Personal tools
irssi scripts
eggdrop scripts