Dsaudio
Stuff I'd like to see in the libnds audio implementation
Contents |
Project Status: Drawing board
Introduction
The current libnds audio implementation lacks well most everything. There are only three audio related functions.
This page is for considering what would be the best way to implement full featured audio functionality that makes full use of the DS's wonderful audio hardware and what features would best serve everyone's needs without adding unnecessary bloat to support rarely used functions while hopefully not preventing a developer from adding these features later (without them modifying arm7 code preferably).
Objective of the project overall is to make useful an extensible high quality developer friendly audio library available for DS homebrew... Because, like others, need or otherwise would like it for my game!!!!! :p
Feel free to comment or discuss concerns or give feedback such as "I'll never use this bit and don't see why anyone else would either", or "I really need this useful feature that can be useful to others.", or anything that comes to mind.
What is here so far is based on discussions on irc, various libnds and hardware docs on the web like these.
Now I'm like, well duh! Truly thanukfl for your help.
Todo
Pretty much everything! But more specifically:
- Create some type of generalized overview of the complete system
- Document/define structures, and types for lib specific return values
- Otherwise improve documentation in general
- Tho a lot of it is self evident, still need to work out the arm7 side enough to assist in defining an efficient scheme for communication (utilizing the newly added FIFO functionality) between the arm7 and arm9.
Functions
So far functions present the arm9 side of things a game/app dev would see.
Cheers pal. I do appercitae the writing.
Audio Sample Loading
all 'Load' commands return mmSample handle or -1 if error most sample loadinging 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!!)
Arguments The following argument may be used by functions in this section: rate - sample rate in hertz. For instance 22050, 44100, 9600 are typical values level - volume 0-127 pan - pan 0-127 flags - various sample flags (see table) start - pointer to start of sample len - sample length in bytes Return Values Load functions return a handle to the loaded sample as type mmSample. (probably a 16 bit integer). Return value will be negative if function fails.
mmSampleDefaults
void mmSampleDefaults(int rate, char level, char pan, u8 flags);
set default values used by all subsequently 'SimpleLoad'ed samples.
Loading From Ram
mmSampleLoad
mmSample mmSampleLoad(void *start, int len, void *loopstart, int rate, char level, char pan, u8 flags);
mmSampleSimpleLoad
mmSample mmSampleSimpleLoad(void *start, int len);
Action reqruies knowledge, and now I can act!
mmSampleUnload
void mmSampleUnload(mmSample sample);
stops playing and removes sample from sampletable, and if loaded from file (see below) will free() malloc'd ram..
Audio Sample Playback
Arguments The following argument may be used by Audio sample playback functions: mmSample - sample handle Return Values Load functions returns the channel used to play the sample. Return value will be negative if function fails.
mmSamplePlay
char mmSamplePlay(mmSample sample);
returns channel mmChan or -1 if no channels available (if stereo sample high nibble contains right channel # and low nibble contains left channel number)
mmSampleStop
void mmSampleStop(mmSample sample);
stops sample on all channels currently using it
mmSampleSetRate
void mmSampleSetRate(mmSample sample, int rate);
mmSampleSetLevel
void mmSampleSetLevel(mmSample sample, char level);
mmSampleSetPan
void mmSampleSetPan(mmSample sample, char pan);
mmSampleSetFlags
void mmSampleSetFlags(mmSample sample, char flags);
ORs Flags? or should just set them??
mmSampleClearFlags
void mmSampleClearFlags(mmSample sample, char flags);
if go with ORing</source>
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
mmChanStop
void mmChanStop(char mmChan);
stops sample based on the channel
mmChanSetRate
void mmChanSetRate(char mmChan, int rate);
sets sample rate
mmChanSetLevel
void mmChanSetLevel(char mmChan, u8 level);
sets vol of a specified channel
mmChanSetPan
void mmChanSetPan(char mmChan, char pan);
sets pan of specified channel
mmChanSetFlags
void mmChanSetFlags(char mmChan,u8 flags);
sets flags (for instance STICKY, not all flags valid for chans)
mmChanSamplePlay
void mmChanSamplePlay(char chan, mmSample sample);
Prolly normally not used, but if u did want to use a specific channel for a specific reason, here ya go..
mmChanGetStatus
u8 mmChanGetStatus(char chan);
returns channel busy flag and mebbe other things?
PSG/NOISE Related Functions
these may be unnecessary if the synth option is implemented...
mmChanPsgNote
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
mmChanPsgNote
void mmChanPsgNote(char mmChan, u8 note, u8 level, u8 pan,u8 duty);
same as above but sets all at once
mmChanPsgNoteDetune
void mmChanPsgNoteDetune(char mmChan, int amount);
Detunes note by specified amount (for pitch bends)
mmChanGet
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
Synthesizer Functions
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'.
mmSynthPatchCreatePsg
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);
Pass this info as a struct?? would save memory that way :) 4 lfo params can be combined into one byte if only 4 lfo are available
mmSynthPatchCreateNoise
u8 mmSynthPatchCreateNoise(u16 atime, u16 dtime, u8 slevel, u16 rtime, u8 envamount, u8 vollfo, u8 dutylfo, u8 panlfo);
mmSynthPatchCreateSample
u8 mmSynthPatchCreateSample(mmSample sample, 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
mmSynthLfo
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 :)
mmSynthNoteOn
u8 mmSynthNoteOn(u8 patch, u8 note);
returns channel used...
mmSynthNoteOnVel
u8 mmSynthNoteOnVel(u8 patch, u8 note, u8 velocity);
same as above but with velocity (volume) param
mmSynthNoteOff
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
Defines, Globals, and Structures
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 hardware channel to retrig 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); } };
References/External Links
Hardware references and other DS audio libraries
Maxmod library It would be nice if this was merged or at least compatible with eKids's project.
gbatek sound
neimod
#dsdev@irc.blitzed.org
Example Header
Mostly useless outdated header example can be found here. Original notes related to implementing an audio library written in .c syntax. This wiki page represents a more refined version of these header notes.