ID3 Parser Help [resolved]

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
sturatt
Posts: 46
Joined: Thu Jul 13, 2006 4:21 pm

ID3 Parser Help [resolved]

Post by sturatt »

Ive written a function to parse ID3v1 tags out of mp3 files, but i cant seem to get it to work correctly. It usually gives close to what im looking for, but usually adds extra stuff to the end.

the structure of an ID3v1 tag is like this:

Song title 30 characters
Artist 30 characters
Album 30 characters
Year 4 characters
Comment 30 characters
Genre 1 byte

Those are always at the end of the mp3 file.

i wrote a function to get just the title artist and album, but cant seem to get it to work.

Code: Select all

ID3Tag ParseID3(char *mp3path)
{
    char id3buffer[512];
    ID3Tag TmpID3;
    sprintf(TmpID3.ID3Title,"ERROR");
    sprintf(TmpID3.ID3Artist,"ERROR");
    sprintf(TmpID3.ID3Album,"ERROR");
    
    int id3fd; //our local file descriptor
    id3fd = sceIoOpen(mp3path, 0x0001, 0777);

    if &#40;id3fd < 0&#41;
    &#123;
        return TmpID3;
    &#125;

    //search for ID3v1 tags first, easier to find and parse
    sceIoLseek&#40;id3fd, -128, SEEK_END&#41;;
    sceIoRead&#40;id3fd,id3buffer,128&#41;;

    if &#40;strstr&#40;id3buffer,"TAG"&#41; != 0&#41;
    &#123;
        goto ID3v1_FOUND;
    &#125;
    else //look for ID3v2 tags when i implement it
    &#123;
        return TmpID3;  //this is where the search for ID3v2 tags code will go
                        //when it gets implemented.
    &#125;
    
    //**** An ID3v1 tag was found ****

    ID3v1_FOUND&#58;
    sceIoLseek&#40;id3fd, -125, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Title,30&#41;;
    sceIoLseek&#40;id3fd, -95, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Artist,30&#41;;
    sceIoLseek&#40;id3fd, -65, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Album,30&#41;;
    TmpID3.versionfound = 1;
    sceIoClose&#40;id3fd&#41;;
    
    return TmpID3;
&#125;
and if it matters, ID3Tag is a struct that looks like this:

Code: Select all

struct ID3Tag &#123;
    char ID3Title&#91;100&#93;;
    char ID3Artist&#91;100&#93;;
    char ID3Album&#91;100&#93;;
    int versionfound;
&#125;;

i think it might have something to do with the sceIoRead() function, but i dont know. Maybe im using it incorrectly?
Last edited by sturatt on Sun Jul 16, 2006 5:32 am, edited 1 time in total.
memon
Posts: 63
Joined: Mon Oct 03, 2005 10:51 pm

Post by memon »

If you try to output the strings using printf style function they need to be null terminated. So after reading a string. Try something like this:

Code: Select all

sceIoRead&#40;id3fd,TmpID3.ID3Title,30&#41;;
TmpID3.ID3Title&#91;30&#93; = '\0;
Your current implementation should only fail when reading exactly 30 char strings, since the specification requires to pad the unused space with zeroes.
sturatt
Posts: 46
Joined: Thu Jul 13, 2006 4:21 pm

Post by sturatt »

That solved the problem of the weird output, I appreciate it.
sakya
Posts: 190
Joined: Fri Apr 28, 2006 5:48 pm
Contact:

Post by sakya »

Hi! :)

I modified a little the function to parse ID3v1, added genre decoding, year and comments.
Maybe will be usefull to someone. :)

Code: Select all

struct ID3Tag &#123;
	char ID3Title&#91;100&#93;;
	char ID3Artist&#91;100&#93;;
	char ID3Album&#91;100&#93;;
	char ID3Year&#91;10&#93;;
	char ID3Comment&#91;100&#93;;
	char ID3GenreCode&#91;10&#93;;
	char ID3GenreText&#91;100&#93;;
	int versionfound;
&#125;;

struct ID3Tag ParseID3&#40;char *mp3path&#41;;

Code: Select all

#include <stdio.h>
#include <string.h>
#include <pspkernel.h>
#include "id3.h"

struct genre
&#123;
	int code;
	char *text;
&#125;;

struct genre genreList&#91;&#93; = 
&#123;
   &#123;0 , "Blues"&#125;, &#123;1 , "Classic Rock"&#125;, &#123;2 , "Country"&#125;, &#123;3 , "Dance"&#125;, &#123;4 , "Disco"&#125;, &#123;5 , "Funk"&#125;, &#123;6 , "Grunge"&#125;, &#123;7 , "Hip-Hop"&#125;, &#123;8 , "Jazz"&#125;, &#123;9 , "Metal"&#125;, &#123;10 , "New Age"&#125;,
   &#123;11 , "Oldies"&#125;, &#123;12 , "Other"&#125;, &#123;13 , "Pop"&#125;, &#123;14 , "R&B"&#125;, &#123;15 , "Rap"&#125;, &#123;16 , "Reggae"&#125;, &#123;17 , "Rock"&#125;, &#123;18 , "Techno"&#125;, &#123;19 , "Industrial"&#125;, &#123;20 , "Alternative"&#125;,
   &#123;21 , "Ska"&#125;, &#123;22 , "Death Metal"&#125;, &#123;23 , "Pranks"&#125;, &#123;24 , "Soundtrack"&#125;, &#123;25 , "Euro-Techno"&#125;, &#123;26 , "Ambient"&#125;, &#123;27 , "Trip-Hop"&#125;, &#123;28 , "Vocal"&#125;, &#123;29 , "Jazz+Funk"&#125;, &#123;30 , "Fusion"&#125;,
   &#123;31 , "Trance"&#125;, &#123;32 , "Classical"&#125;, &#123;33 , "Instrumental"&#125;, &#123;34 , "Acid"&#125;, &#123;35 , "House"&#125;, &#123;36 , "Game"&#125;, &#123;37 , "Sound Clip"&#125;, &#123;38 , "Gospel"&#125;, &#123;39 , "Noise"&#125;, &#123;40 , "Alternative Rock"&#125;,
   &#123;41 , "Bass"&#125;, &#123;42 , "Soul"&#125;, &#123;43 , "Punk"&#125;, &#123;44 , "Space"&#125;, &#123;45 , "Meditative"&#125;, &#123;46 , "Instrumental Pop"&#125;, &#123;47 , "Instrumental Rock"&#125;, &#123;48 , "Ethnic"&#125;, &#123;49 , "Gothic"&#125;, &#123;50 , "Darkwave"&#125;,
   &#123;51 , "Techno-Industrial"&#125;, &#123;52 , "Electronic"&#125;, &#123;53 , "Pop-Folk"&#125;, &#123;54 , "Eurodance"&#125;, &#123;55 , "Dream"&#125;, &#123;56 , "Southern Rock"&#125;, &#123;57 , "Comedy"&#125;, &#123;58 , "Cult"&#125;, &#123;59 , "Gangsta"&#125;, &#123;60 , "Top 40"&#125;,
   &#123;61 , "Christian Rap"&#125;, &#123;62 , "Pop/Funk"&#125;, &#123;63 , "Jungle"&#125;, &#123;64 , "Native US"&#125;, &#123;65 , "Cabaret"&#125;, &#123;66 , "New Wave"&#125;, &#123;67 , "Psychadelic"&#125;, &#123;68 , "Rave"&#125;, &#123;69 , "Showtunes"&#125;, &#123;70 , "Trailer"&#125;,
   &#123;71 , "Lo-Fi"&#125;, &#123;72 , "Tribal"&#125;, &#123;73 , "Acid Punk"&#125;, &#123;74 , "Acid Jazz"&#125;, &#123;75 , "Polka"&#125;, &#123;76 , "Retro"&#125;, &#123;77 , "Musical"&#125;, &#123;78 , "Rock & Roll"&#125;, &#123;79 , "Hard Rock"&#125;, &#123;80 , "Folk"&#125;,
   &#123;81 , "Folk-Rock"&#125;, &#123;82 , "National Folk"&#125;, &#123;83 , "Swing"&#125;, &#123;84 , "Fast Fusion"&#125;, &#123;85 , "Bebob"&#125;, &#123;86 , "Latin"&#125;, &#123;87 , "Revival"&#125;, &#123;88 , "Celtic"&#125;, &#123;89 , "Bluegrass"&#125;, &#123;90 , "Avantgarde"&#125;,
   &#123;91 , "Gothic Rock"&#125;, &#123;92 , "Progressive Rock"&#125;, &#123;93 , "Psychedelic Rock"&#125;, &#123;94 , "Symphonic Rock"&#125;, &#123;95 , "Slow Rock"&#125;, &#123;96 , "Big Band"&#125;, &#123;97 , "Chorus"&#125;, &#123;98 , "Easy Listening"&#125;, &#123;99 , "Acoustic"&#125;,
   &#123;100 , "Humour"&#125;, &#123;101 , "Speech"&#125;, &#123;102 , "Chanson"&#125;, &#123;103 , "Opera"&#125;, &#123;104 , "Chamber Music"&#125;, &#123;105 , "Sonata"&#125;, &#123;106 , "Symphony"&#125;, &#123;107 , "Booty Bass"&#125;, &#123;108 , "Primus"&#125;, &#123;109 , "Porn Groove"&#125;,
   &#123;110 , "Satire"&#125;, &#123;111 , "Slow Jam"&#125;, &#123;112 , "Club"&#125;, &#123;113 , "Tango"&#125;, &#123;114 , "Samba"&#125;, &#123;115 , "Folklore"&#125;, &#123;116 , "Ballad"&#125;, &#123;117 , "Power Ballad"&#125;, &#123;118 , "Rhytmic Soul"&#125;, &#123;119 , "Freestyle"&#125;, &#123;120 , "Duet"&#125;,
   &#123;121 , "Punk Rock"&#125;, &#123;122 , "Drum Solo"&#125;, &#123;123 , "Acapella"&#125;, &#123;124 , "Euro-House"&#125;, &#123;125 , "Dance Hall"&#125;, &#123;126 , "Goa"&#125;, &#123;127 , "Drum & Bass"&#125;, &#123;128 , "Club-House"&#125;, &#123;129 , "Hardcore"&#125;, &#123;130 , "Terror"&#125;,
   &#123;131 , "Indie"&#125;, &#123;132 , "BritPop"&#125;, &#123;133 , "Negerpunk"&#125;, &#123;134 , "Polsk Punk"&#125;, &#123;135 , "Beat"&#125;, &#123;136 , "Christian Gangsta"&#125;, &#123;137 , "Heavy Metal"&#125;, &#123;138 , "Black Metal"&#125;, &#123;139 , "Crossover"&#125;, &#123;140 , "Contemporary C"&#125;,
   &#123;141 , "Christian Rock"&#125;, &#123;142 , "Merengue"&#125;, &#123;143 , "Salsa"&#125;, &#123;144 , "Thrash Metal"&#125;, &#123;145 , "Anime"&#125;, &#123;146 , "JPop"&#125;, &#123;147 , "SynthPop"&#125;
&#125;;
int genreNumber = sizeof &#40;genreList&#41; / sizeof &#40;struct genre&#41;;

struct ID3Tag ParseID3&#40;char *mp3path&#41;
&#123;
    char id3buffer&#91;512&#93;;
    struct ID3Tag TmpID3;

    sprintf&#40;TmpID3.ID3Title,"ERROR"&#41;;
    sprintf&#40;TmpID3.ID3Artist,"ERROR"&#41;;
    sprintf&#40;TmpID3.ID3Album,"ERROR"&#41;;
    sprintf&#40;TmpID3.ID3Year,"ERROR"&#41;;
    sprintf&#40;TmpID3.ID3Comment,"ERROR"&#41;;
    sprintf&#40;TmpID3.ID3GenreCode,"ERROR"&#41;;
    sprintf&#40;TmpID3.ID3GenreText,"ERROR"&#41;;
	
    int id3fd; //our local file descriptor
    id3fd = sceIoOpen&#40;mp3path, 0x0001, 0777&#41;;

    if &#40;id3fd < 0&#41;
    &#123;
        return TmpID3;
    &#125;

    //search for ID3v1 tags first, easier to find and parse
    sceIoLseek&#40;id3fd, -128, SEEK_END&#41;;
    sceIoRead&#40;id3fd,id3buffer,128&#41;;

    if &#40;strstr&#40;id3buffer,"TAG"&#41; != 0&#41;
    &#123;
        goto ID3v1_FOUND;
    &#125;
    else //look for ID3v2 tags when i implement it
    &#123;
        return TmpID3;  //this is where the search for ID3v2 tags code will go
                        //when it gets implemented.
    &#125;
   
    //**** An ID3v1 tag was found ****

    ID3v1_FOUND&#58;
    sceIoLseek&#40;id3fd, -125, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Title,30&#41;;
	TmpID3.ID3Title&#91;30&#93; = '\0';

    sceIoLseek&#40;id3fd, -95, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Artist,30&#41;;
	TmpID3.ID3Artist&#91;30&#93; = '\0';

    sceIoLseek&#40;id3fd, -65, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Album,30&#41;;
	TmpID3.ID3Album&#91;30&#93; = '\0';

    sceIoLseek&#40;id3fd, -35, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Year,4&#41;;
	TmpID3.ID3Year&#91;4&#93; = '\0';

    sceIoLseek&#40;id3fd, -31, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3Comment,30&#41;;
	TmpID3.ID3Comment&#91;30&#93; = '\0';

    sceIoLseek&#40;id3fd, -1, SEEK_END&#41;;
    sceIoRead&#40;id3fd,TmpID3.ID3GenreCode,1&#41;;
	TmpID3.ID3GenreCode&#91;1&#93; = '\0';

	if &#40;&#40;&#40;int&#41;TmpID3.ID3GenreCode&#91;0&#93; >= 0&#41; & &#40;&#40;int&#41;TmpID3.ID3GenreCode&#91;0&#93; < genreNumber&#41;&#41;&#123;
		strcpy&#40;TmpID3.ID3GenreText, genreList&#91;&#40;int&#41;TmpID3.ID3GenreCode&#91;0&#93;&#93;.text&#41;;
	&#125;
	else&#123;
		strcpy&#40;TmpID3.ID3GenreText, ""&#41;;
	&#125;
	TmpID3.ID3GenreText&#91;30&#93; = '\0';

	TmpID3.versionfound = 1;
    sceIoClose&#40;id3fd&#41;;
   
    return TmpID3;
&#125;
Ciaooo
Sakya
Post Reply