Thmp3

From ProjectWiki
Jump to: navigation, search

mp3lib for NDS

Contents

Project Status: Alpha Testing

What is this

Touhou Mp3, or simply Thmp3, is a library for playing mp3s as BGM (background music) in Nintendo DS games with the goals of being simple to use, modular, small, and fast.

Some features include:

  • up to 44100 sample rates, 192K/VBR bitrates, 16 bit stereo playback possible (however 32000 is more reliable at this time)
  • m3u support, playlist manager
  • ID3v1 supported
  • playback position indicator (perfect for rhythm games :D)
  • supports streaming audio from FAT, or Nitrofs
  • most work is done on the ARM7 cpu
  • small size
  • speedy code
  • c++ compatibility

Drawbacks:

  • buffers use a lot of memory
  • pcm dma transfers and decoding use some arm9 memory bandwidth
  • high bit/sample rates use all or most all of the ARM7 CPU

It may not be the best option for all games, but if you want high quality bgm and have cpu to spare might give it a try. ^^ Was originally written for a touhou fangame and is where it gets it's name. Because of interest from other developers was decided to release as a separate lib. Hopes you find it useful.

  • 2008.8.14 - fifo added for a bit now but refining things still
  • 2009.7.3 - Updated version comming soon! including pausing, seemless switching between songs, syncronous operations, song swapping (for pausing currently playing song to play alternate music and returning back), and much more :D (2009.07.13 ~ beta is on svn)

Where to get it

Currently the sources for this are part of LibThds and is probably the easiest to use due to its dependence on Thfifo. The latest version of the source can also be downloaded via SVN (see libthds page) or directly from the svn httpd:

Note: Thfifo is required for interprocessor communications.

An example project from svn is here:
thmp3 example

Very very alpha release: thmp3 v.1.alpha 15MB with all the mp3s and such ~_~
This is a self contained project however the above links to the svn sources will contain the newest versions and should be used instead.

Dont expect much yet, it will play mp3s, but is still very immature and has some issues with song advance (A button). tho stopping the song first (b button) helps. These errors should repair themselves after fifo is added. ^^

Todo/Bugs

  • add volume/fadeout support!!!! (i soooo keeep forgetting this) done
  • add fifo support (this will fix only known bug relating to song switching as well) done
  • modify decoder assembly language file to write pcm data to two seperate buffers instead of interleaving LRLR no need to do this, the C function is insanely efficient as is :p
  • need functions to set playback and artist format callbacks
  • add information headers to all files ^^
  • explain things better ~_~
  • add moar examples!!

ARM9 Functions

Mp3 Playback

mp3Init
Initalize mp3 player (on the ARM9 side). Will wait for arm7 to indicate diet pills its ready before continuing. Should be called once and only once at start of program.

void mp3Init(void);

mp3Play
Start playing the mp3 specified by the filesystem path.
(FAT and/or Nitrofs should be initialized prior to calling this or any other mp3/playlist functions!)

  • filename - pointer to string containing path to and the mp3 filename
bool mp3Play(char *filename);

mp3Update
This function should be placed in the game's idle loop. It is used to refill the input buffer from the stream as needed. The mp3 will not start, change, or much of anything untill this is called.

This along with mp3Init, and mp3Play represent the minimal requirements for playing an mp3. ^^

void mp3Update(void);

mp3Stop
Stops a playing an mp3

void mp3Stop(void);

mp3Queue
Sets the next song to play when this one eof's.

  • filename - pointer to string containing path to and the mp3 filename
void mp3Queue(const char *filename);

mp3GetSongPos
Get the song's current playback position in minutes, seconds, and milliseconds.
All input variables should be of type int !!!

  • mins - pointer to mins variable
  • secs - pointer to seconds variable
  • ms - pointer to milliseconds variable

Returns the elapsed time in ms*100.

int mp3GetSongPos(int *mins, int *secs, int *ms);

mp3SetPlayCb
Sets a callback thats called whenever the song changes (for displaying title) should be in the format of: void myMp3PlayCallback(char *filename, char *title). See example below..

  • func - address of a function to call each time song changes. Set as NULL to disable (default).
void mp3SetPlayCb(void *func)

mp3SetVolume
Sets playback volume.

  • volume - new volume (1 ~ 127) will change volume for current and all subsequent mp3 playbacks
void mp3SetVolume(char volume)

Mp3 Playback Macros

mp3SetOpts(o)
Sets various optons for mp3 playback, such as:

  • MP3_ADVANCE - advance to next song on playlist after this one finishes
  • MP3_REPEAT - return to beginning of playlist after last song
  • MP3_LOOP - loop currently playing song over and over and over diet supplements and over till ya cant stand it no moar!! ;;
#define mp3SetOpts(o) (mp3Ipc->flags|=(o))	//set options

mp3ClearOpts
Same as above but instead will clear the given options

#define mp3ClearOpts(o) (mp3Ipc->flags=(mp3Ipc->flags&~(o)))

mp3PlaylistAdv Advance to the next song on the playlist.

#define mp3PlaylistAdv() (mp3PlaylistPlay(mp3Pl.nowplaying+1))


//macros to get or set various things
//set options
#define mp3SetOpts(o) (mp3Ipc->flags|=(o))	
//same as above but clears instead
#define mp3ClearOpts(o) (mp3Ipc->flags=(mp3Ipc->flags&~(o)))
//move to next song on playlist
#define mp3PlaylistAdv() (mp3PlaylistPlay(mp3Pl.nowplaying+1))
//song position in samples
#define mp3GetSongPosSamples() (mp3Ipc->mfram)
//song position in milliseconds
#define mp3GetSongPosms() ((mp3Ipc->mfram*100)/mp3Ipc->framenfo.samprate)
//mp3 bitrate such as 96000 128000 192000, 
#define mp3GetBitrate()	(mp3Ipc->framenfo.bitrate)
//number of channels (1 = mono, 2 = stereo)
#define mp3GetChans() (mp3Ipc->framenfo.nChans)
//Sample rate. like: 44100,32000,19200 etc 
#define mp3GetRate() (mp3Ipc->framenfo.samprate)
//bits per sample either 8, 16, 24 (we only support 16!!!!)
#define mp3GetBits() (mp3Ipc->framenfo.bitsPerSample)
//mp3 layer
#define mp3GetLayer() (mp3Ipc->framenfo.layer)
//mp3 layer version
#define mp3GetVersion() (mp3Ipc->framenfo.version)
//last error 
#define mp3GetError() (mp3Ipc->error)
//How much data (in bytes) is in the pcm buffer
#define mp3GetPCMUsed() (mp3Ipc->pcmbleft)
//same as above but reports in percent instead ^^
#define mp3GetPCMUsedPct() ((int)((mp3GetPCMUsed()*100)/PCMBUF_SAMPLES))
//how much data (bytes) is in the stream buffer
#define mp3GetDataUsed() (mp3Ipc->dleft)
//same as above but reports in percent instead ^^
#define mp3GetDataUsedPct() ((int)((mp3Ipc->dleft*100)/DECODEBUF_SIZE))
//position of playback pointer within pcm buffer
#define mp3GetPBPos() (mp3Ipc->pcmbpos)	//useless except for debugging and even then should be removed mebbe? @_@
//Number of frames decoded
#define mp3GetDecoded() (mp3Ipc->fdec) 
//re/set the above variable
#define mp3SetDecoded(d) (mp3Ipc->fdec=(d))	
//this value is incremented each idle loop of the arm7. should increment +60 each second.
//starts moving slower as arm7 becomes saturated. used mostly for debugging
#define mp3GetVBlnks() (mp3Ipc->vfram) //Number of 
//re/set the above variable
#define mp3SetVBlnks(d) (mp3Ipc->vfram=(d))	//zero or whatever them
#define mp3GetState() (mp3Ipc->state)		//arm7's current operational state
#define mp3GetCommand() (mp3Ipc->command)	//for debug rly, reads contents of command buffer (fifo will prolly eliminate this)
//fade out nicely in x amount of seconds (see thSound for exact seconds format!_!)
#define mp3FadeOut(s) (thSndFade(HWSCHANNEL0, (s)),thSndFade(HWSCHANNEL1, (s)))

Playlist

All functions here are for the arm9.

mp3ReadId3V1
Read IDv3 tag from mp3.

  • filename - pointer to string containing path to and the mp3 filename
  • id3 - pointer to structure of type ID3V1_T (see below)
bool mp3ReadId3V1(char *filename, ID3V1_T *id3);

mp3GetId3FromFile
Same as above but uses an already open filehandle instead.

  • f - already opened filehandle (should be an mp3 ofc! :)
  • id3 - pointer to structure of type ID3V1_T
bool mp3GetId3FromFile(FILE *f, ID3V1_T *id3);

ID3V1_T structure The ID3V1_T structure type used by the two preceeding functions is defined as:

//This at last 128 (-3 for "TAG") bytes of file is the tag
typedef struct _ID3V1_T {
	char	title[30];
	char	artist[30];
	char	album[30];
	char	year[4];
	char	comment[30];
	char	genre;
} ID3V1_T __attribute__((packed));

mp3PlaylistClear
Clears all entries from the playlist.

void mp3PlaylistClear(void);

mp3PlaylistAdd
Add a single song to the playlist.

  • filename - pointer to string containing path to and the mp3 filename
  • title - ascii string containing title of song
void mp3PlaylistAdd(char *filename, char *title);

mp3FormatId3
This function determins is the default way id3 data is presented. Hard to explain this @_@. The user may override this to change how titles are created for the playlist by setting a different callback.

  • id3 - pointer to already populated id3 struct
  • titlestr - buffer to hold title string (should be 256 bytes at least)
void mp3FormatId3(ID3V1_T *id3, char *titlestr);

mp3PlaylistAddDir
Add all mp3s in directory to playlist.

  • dirpath - path/to/yourmp3s/
bool mp3PlaylistAddDir(char *dirpath);

mp3PlaylistAddM3u
Add all songs in .m3u file to playlist. M3u is the text based playlist format popular with most mp3 players.

  • filename - path/to/your.m3u
int mp3PlaylistAddM3u(char *filename);

mp3PlaylistPlay
Plays a song from the playlist

  • index - integer describing which song to play. 0 = first song, 1 = second song on list, 2 = third song, etc etc
bool mp3PlaylistPlay(int index);

Playlist Macros

//Get number of song in playlist
#define mp3GetPlaylistCnt() (mp3Pl.numsongs)
//Returns char * to entry 'i' on playlist's title
#define mp3GetPlaylistTitle(i) (mp3Pl.entry[i]->title)
//Returns char * to entry 'i' on playlist's filename
#define mp3GetPlaylistFilename(i) (mp3Pl.entry[i]->filename)
//get currently playing song index
#define mp3GetPlaylistNowPlay() (mp3Pl.nowplaying)

ARM7

mp3Init
Initializes the ARM7 side of mp3 decoder. Waits for ARM9 to be initialized before returning. Should be called once in the arm7's main() function before the idle loop.
Will return false if helix decoder fails to initialize.

bool mp3Init(void);

mp3Loop
This function should be placed in the ARM7's idle loop. It is here all the real work is done, the function may not always return before the end of each vblank interval so take this into consideration.

void mp3Loop(void);

Examples

Simplest Playback

Basic Mp3 Playing. Probably the simplest way to use this library.

#include <thmp3.h> //Make sure you gots this in the top plz :D
 
mp3Init();
mp3Play("path/to/your.mp3");
 
while(1) {
  mp3Update();
};

All subsequent examples will assume you have already called mp3Init(), and have mp3Update(); in your idle loop..

Read ID3v1 tag from mp3

ID3V1_T id3;
mp3ReadId3V1(filename,&id3);
printf("%s - %s [%s (%s)]\n",id3.artist, id3.title, id3.album, id3.year);

Will print:

Artistname - Songtitle [Album name (YEAR)]

Display song position

int mins,secs,ms;
mp3GetSongPos(&mins, &secs, &ms);
iprintf("TIME (M:S:MS): %02d:%02d:%02d  \n",mins,secs,ms);

Load m3u and play the first song in the list

mp3PlaylistAddM3u("nitro:/playlist.m3u");	//add m3u playlist
mp3PlaylistPlay(0); //play first song  (mp3PlaylistAdv(); will advance to next song on list

List all songs on playlist

int c=0;
do {
	iprintf("%3d%s\n",c, mp3GetPlaylistTitle(c));   
} while(++c<mp3GetPlaylistCnt());

Personal tools
irssi scripts
eggdrop scripts