diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
commit | a6d58bb6052ac8cb01805a48c4ad2f129126116f (patch) | |
tree | dd867a099fcbb263a8009a9fb22695b87855dad6 /src/modules/mediaplayer/mp_mp3.cpp | |
download | kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip |
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/modules/mediaplayer/mp_mp3.cpp')
-rw-r--r-- | src/modules/mediaplayer/mp_mp3.cpp | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/src/modules/mediaplayer/mp_mp3.cpp b/src/modules/mediaplayer/mp_mp3.cpp new file mode 100644 index 00000000..6e27fa5f --- /dev/null +++ b/src/modules/mediaplayer/mp_mp3.cpp @@ -0,0 +1,468 @@ +//============================================================================= +// +// File : mp_mp3.cpp +// Creation date : Fri Mar 25 20:01:25 2005 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2005 Szymon Stefanek (pragma at kvirc dot net) +// +// This file is based on the mp3tech.c. It is released under the original +// license and the original copyright notice follows. +// +// mp3tech.c +// +// Copyright (C) 2000 Cedric Tefft <cedric@earthling.net> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// This file is based in part on: +// +// * MP3Info 0.5 by Ricardo Cerqueira <rmc@rccn.net> +// * MP3Stat 0.9 by Ed Sweetman <safemode@voicenet.com> and +// Johannes Overmann <overmann@iname.com> +// +// There has been also a remarkable work by Cristopher Tieckle (Crissi) +// +//============================================================================= + +#include "mp_mp3.h" +#include "kvi_options.h" + +#include <qfileinfo.h> +#include <qtextcodec.h> + +#define MAXGENRE 147 +#define GENREROWS 50 + +/* + The Information is stored in the last 128 bytes of an MP3. The Tag + has got the following fields, and the offsets given here, are from + 0-127. + + Field Length Offsets + Tag 3 0-2 + Songname 30 3-32 + Artist 30 33-62 + Album 30 63-92 + Year 4 93-96 + Comment 30 97-126 + Genre 1 127 + + + The string-fields contain ASCII-data, coded in ISO-Latin 1 codepage. + Strings which are smaller than the field length are padded with zero- + bytes. + + Tag: The tag is valid if this field contains the string "TAG". This + has to be uppercase! + + Songname: This field contains the title of the MP3 (string as + above). + + Artist: This field contains the artist of the MP3 (string as above). + + Album: this field contains the album where the MP3 comes from + (string as above). + + Year: this field contains the year when this song has originally + been released (string as above). + + Comment: this field contains a comment for the MP3 (string as + above). Revision to this field has been made in ID3v1.1. See + A.4. + + Genre: this byte contains the offset of a genre in a predefined + list the byte is treated as an unsigned byte. The offset is + starting from 0. See A.3. +*/ + + +const char *typegenre [MAXGENRE+2] = +{ + "Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge", + "Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B", + "Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska", + "Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient", + "Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical", + "Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise", + "Alt. Rock","Bass","Soul","Punk","Space","Meditative", + "Instrumental Pop","Instrumental Rock","Ethnic","Gothic", + "Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance", + "Dream","Southern Rock","Comedy","Cult","Gangsta Rap","Top 40", + "Christian Rap","Pop/Funk","Jungle","Native American","Cabaret", + "New Wave","Psychedelic","Rave","Showtunes","Trailer","Lo-Fi", + "Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical", + "Rock & Roll","Hard Rock","Folk","Folk/Rock","National Folk", + "Swing","Fast-Fusion","Bebob","Latin","Revival","Celtic", + "Bluegrass","Avantgarde","Gothic Rock","Progressive Rock", + "Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band", + "Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson", + "Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus", + "Porn Groove","Satire","Slow Jam","Club","Tango","Samba", + "Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle", + "Duet","Punk Rock","Drum Solo","A Cappella","Euro-House", + "Dance Hall","Goa","Drum & Bass","Club-House","Hardcore","Terror", + "Indie","BritPop","Negerpunk","Polsk Punk","Beat", + "Christian Gangsta Rap","Heavy Metal","Black Metal","Crossover", + "Contemporary Christian","Christian Rock","Merengue","Salsa", + "Thrash Metal","Anime","JPop","Synthpop","" +}; + +const char * get_typegenre(int idx) +{ + if(idx > MAXGENRE)return typegenre[12]; + return typegenre[idx]; +} + +int galphagenreindex[MAXGENRE+2] = +{ + 148,123,74,73,34,99,40,20,26,145,90, + 116,41,135,85,96,138,89,0,107,132,65,88, + 104,102,97,136,61,141,1,32,128,112,57,140, + 2,139,58,125,3,50,22,4,55,127,122,120, + 98,52,48,124,25,54,84,81,115,80,119,5, + 30,36,59,126,38,91,49,6,79,129,137,7, + 35,100,131,19,46,47,33,146,29,8,63,86, + 71,45,142,9,77,82,64,133,10,66,39,11, + 103,12,75,134,53,62,13,109,117,23,108,92, + 93,67,121,43,14,15,68,16,76,87,118,78, + 17,143,114,110,69,21,111,95,105,42,37,24, + 56,44,101,83,94,106,147,113,51,18,130,144, + 60,70,31,72,27,28 +}; + +int *alphagenreindex=&(galphagenreindex[1]); + + +int layer_tab[4]= {0, 3, 2, 1}; + +int frequencies[3][4] = +{ + {22050,24000,16000,50000}, /* MPEG 2.0 */ + {44100,48000,32000,50000}, /* MPEG 1.0 */ + {11025,12000,8000,50000} /* MPEG 2.5 */ +}; + +int bitrate[2][3][14] = +{ + { /* MPEG 2.0 */ + {32,48,56,64,80,96,112,128,144,160,176,192,224,256}, /* layer 1 */ + {8,16,24,32,40,48,56,64,80,96,112,128,144,160}, /* layer 2 */ + {8,16,24,32,40,48,56,64,80,96,112,128,144,160} /* layer 3 */ + }, + { /* MPEG 1.0 */ + {32,64,96,128,160,192,224,256,288,320,352,384,416,448}, /* layer 1 */ + {32,48,56,64,80,96,112,128,160,192,224,256,320,384}, /* layer 2 */ + {32,40,48,56,64,80,96,112,128,160,192,224,256,320} /* layer 3 */ + } +}; + +int frame_size_index[] = {24000, 72000, 72000}; + +const char *mode_text[] = +{ + "Stereo", "Joint Stereo", "Dual Channel", "Mono" +}; + +const char *emphasis_text[] = +{ + "None", "50/15 Microseconds", "Reserved", "CCITT J 17" +}; +void resetmp3infoStruct(mp3info *i) +{ + i->file=0; + i->datasize=0; + i->header_isvalid=0; + memset(&i->header,0,sizeof(i->header)); + i->id3_isvalid=0; + memset(&i->id3,0,sizeof(i->id3)); + i->vbr=0; + i->vbr_average=0; + i->seconds=0; + i->frames=0; + i->badframes=0; +} + +int get_mp3_info(mp3info *mp3) +{ + int frame_type[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + float seconds=0,total_rate=0; + int frames=0,frame_types=0,frames_so_far=0; + int l,vbr_median=-1; + int bitrate,lastrate; + int counter=0; +// mp3header header; + + int sample_pos,data_start=0; + + QFile fi(mp3->filename); + mp3->datasize=fi.size();//filestat.st_size; + + get_id3(mp3); + + if(get_first_header(mp3,0L)) + { + data_start=ftell(mp3->file); + lastrate=15-mp3->header.bitrate; + while((counter < NUM_SAMPLES) && lastrate) + { + sample_pos=(counter*(mp3->datasize/NUM_SAMPLES+1))+data_start; + if(get_first_header(mp3,sample_pos)) + { + bitrate=15-mp3->header.bitrate; + } else { + bitrate=-1; + } + + if(bitrate != lastrate) + { + mp3->vbr=1; + } + lastrate=bitrate; + counter++; + + } + mp3->frames=(mp3->datasize-data_start)/(l=frame_length(&mp3->header)); + mp3->seconds = (int)((float)(frame_length(&mp3->header)*mp3->frames)/ + (float)(header_bitrate(&mp3->header)*125)+0.5); + mp3->vbr_average = (float)header_bitrate(&mp3->header); + } + + return 0; +} + + +int get_first_header(mp3info *mp3, long startpos) +{ + int k, l=0,c; + mp3header h, h2; + long valid_start=0; + + fseek(mp3->file,startpos,SEEK_SET); + while(1) + { + while((c=fgetc(mp3->file)) != 255 && (c != EOF)); + if(c == 255) + { + ungetc(c,mp3->file); + valid_start=ftell(mp3->file); + if((l=get_header(mp3->file,&h))) + { + fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR); + for(k=1; (k < MIN_CONSEC_GOOD_FRAMES) && (mp3->datasize-ftell(mp3->file) >= FRAME_HEADER_SIZE); k++) + { + if(!(l=get_header(mp3->file,&h2))) break; + if(!sameConstant(&h,&h2)) break; + fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR); + } + if(k == MIN_CONSEC_GOOD_FRAMES) + { + fseek(mp3->file,valid_start,SEEK_SET); + memcpy(&(mp3->header), &h2, sizeof(mp3header)); + mp3->header_isvalid = 1; + return 1; + } + } + } else { + return 0; + } + } + return 0; +} + + +// Get next MP3 frame header. +// Return codes: +// positive value = Frame Length of this header +// 0 = No, we did not retrieve a valid frame header + +int get_header(FILE *file,mp3header *header) +{ + unsigned char buffer[FRAME_HEADER_SIZE]; + int fl; + + if(fread(&buffer,FRAME_HEADER_SIZE,1,file)<1) + { + header->sync=0; + return 0; + } + header->sync=(((int)buffer[0]<<4) | ((int)(buffer[1]&0xE0)>>4)); + if(buffer[1] & 0x10) header->version=(buffer[1] >> 3) & 1; + else header->version=2; + header->layer=(buffer[1] >> 1) & 3; + if((header->sync != 0xFFE) || (header->layer != 1)) + { + header->sync=0; + return 0; + } + header->crc=buffer[1] & 1; + header->bitrate=(buffer[2] >> 4) & 0x0F; + header->freq=(buffer[2] >> 2) & 0x3; + header->padding=(buffer[2] >>1) & 0x1; + header->extension=(buffer[2]) & 0x1; + header->mode=(buffer[3] >> 6) & 0x3; + header->mode_extension=(buffer[3] >> 4) & 0x3; + header->copyright=(buffer[3] >> 3) & 0x1; + header->original=(buffer[3] >> 2) & 0x1; + header->emphasis=(buffer[3]) & 0x3; + + return ((fl=frame_length(header)) >= MIN_FRAME_SIZE ? fl : 0); +} + +int frame_length(mp3header *header) +{ + return header->sync == 0xFFE ? + (frame_size_index[3-header->layer]*((header->version&1)+1)* + header_bitrate(header)/header_frequency(header))+ + header->padding : 1; +} + +int header_layer(mp3header *h) +{ + return layer_tab[h->layer]; +} + +int header_bitrate(mp3header *h) +{ + if(h->bitrate > 0) + return bitrate[h->version & 1][3-h->layer][h->bitrate-1]; + else + return -1; // unknown +} + +int header_frequency(mp3header *h) +{ + return frequencies[h->version][h->freq]; +} + +const char *header_emphasis(mp3header *h) +{ + return emphasis_text[h->emphasis]; +} + +const char *header_mode(mp3header *h) +{ + return mode_text[h->mode]; +} + +int header_channels(mp3header * h) +{ + return h->mode == 3 ? 1 : 2; +} + +int header_crc(mp3header *h) +{ + return (!h->crc); +} + +int sameConstant(mp3header *h1, mp3header *h2) +{ + if((*(uint*)h1) == (*(uint*)h2)) return 1; + + if((h1->version == h2->version ) && + (h1->layer == h2->layer ) && + (h1->crc == h2->crc ) && + (h1->freq == h2->freq ) && + (h1->mode == h2->mode ) && + (h1->copyright == h2->copyright ) && + (h1->original == h2->original ) && + (h1->emphasis == h2->emphasis )) + return 1; + else return 0; +} + + +int get_id3(mp3info *mp3) +{ + // this will read ID3v1 tags + int retcode=0; + char fbuf[4]; + + if(mp3->datasize >= 128) + { + if(fseek(mp3->file, -128, SEEK_END )) + { + retcode |= 4; + } else { + fread(fbuf,1,3,mp3->file); fbuf[3] = '\0'; + mp3->id3.genre[0]=255; + + if(!strcmp((const char *)"TAG",(const char *)fbuf)) + { + mp3->id3_isvalid=1; + mp3->datasize -= 128; + fseek(mp3->file, -125, SEEK_END); + fread(mp3->id3.title,1,30,mp3->file); mp3->id3.title[30] = '\0'; + fread(mp3->id3.artist,1,30,mp3->file); mp3->id3.artist[30] = '\0'; + fread(mp3->id3.album,1,30,mp3->file); mp3->id3.album[30] = '\0'; + fread(mp3->id3.year,1,4,mp3->file); mp3->id3.year[4] = '\0'; + fread(mp3->id3.comment,1,30,mp3->file); mp3->id3.comment[30] = '\0'; + if(mp3->id3.comment[28] == '\0') + { + mp3->id3.track[0] = mp3->id3.comment[29]; + } + fread(mp3->id3.genre,1,1,mp3->file); + unpad(mp3->id3.title); + unpad(mp3->id3.artist); + unpad(mp3->id3.album); + unpad(mp3->id3.year); + unpad(mp3->id3.comment); + } + } + } + return retcode; +} + +char *pad(char *string, int length) +{ + int l; + + l=strlen(string); + while(l<length) + { + string[l] = ' '; + l++; + } + + string[l]='\0'; + return string; +} + +// Remove trailing whitespace from the end of a string + +char *unpad(char *string) +{ + char *pos=string+strlen(string)-1; + while(isspace(pos[0])) (pos--)[0]=0; + return string; +} + +bool scan_mp3_file(QString& szFileName,mp3info * i) +{ + //memset(i,0,sizeof(mp3info)); + resetmp3infoStruct(i); + + + i->filename = "text"; + i->file = fopen(QTextCodec::codecForLocale()->fromUnicode(i->filename).data(),"rb"); + if(!i->file)return false; + + get_mp3_info(i); + + fclose(i->file); + + return (i->id3_isvalid); +} + |