nathan@0: /* nathan@0: * MP3/MPlayer plugin to VDR (C++) nathan@0: * nathan@0: * (C) 2001-2005 Stefan Huelswitt nathan@0: * nathan@0: * This code is free software; you can redistribute it and/or nathan@0: * modify it under the terms of the GNU General Public License nathan@0: * as published by the Free Software Foundation; either version 2 nathan@0: * of the License, or (at your option) any later version. nathan@0: * nathan@0: * This code is distributed in the hope that it will be useful, nathan@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of nathan@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nathan@0: * GNU General Public License for more details. nathan@0: * nathan@0: * You should have received a copy of the GNU General Public License nathan@0: * along with this program; if not, write to the Free Software nathan@0: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. nathan@0: * Or, point your browser to http://www.gnu.org/copyleft/gpl.html nathan@0: */ nathan@0: nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include "common.h" nathan@0: #include "data-mp3.h" nathan@0: #include "decoder-mp3.h" nathan@0: #include "stream.h" nathan@0: nathan@0: #define MAX_FRAME_ERR 10 nathan@0: nathan@0: // ---------------------------------------------------------------- nathan@0: nathan@0: int MadStream(struct mad_stream *stream, cStream *str) nathan@0: { nathan@0: unsigned char *data; nathan@0: unsigned long len; nathan@0: if(str->Stream(data,len,stream->next_frame)) { nathan@0: if(len>0) mad_stream_buffer(stream, data, len); nathan@0: return len; nathan@0: } nathan@0: return -1; nathan@0: } nathan@0: nathan@0: // --- cMP3Decoder ------------------------------------------------------------- nathan@0: nathan@0: cMP3Decoder::cMP3Decoder(const char *Filename, bool preinit) nathan@0: :cDecoder(Filename) nathan@0: { nathan@0: str=0; scan=0; isStream=false; nathan@0: if(preinit) { nathan@0: //d(printf("mp3: preinit\n")) nathan@0: str=new cStream(filename); nathan@0: scan=new cScanID3(str,&urgentLock); nathan@0: } nathan@0: fi=0; stream=0; frame=0; synth=0; nathan@0: } nathan@0: nathan@0: cMP3Decoder::~cMP3Decoder() nathan@0: { nathan@0: Clean(); nathan@0: delete scan; nathan@0: delete str; nathan@0: } nathan@0: nathan@0: bool cMP3Decoder::Valid(void) nathan@0: { nathan@0: bool res=false; nathan@0: if(TryLock()) { nathan@0: struct mad_stream stream; nathan@0: struct mad_header header; nathan@0: mad_stream_init(&stream); nathan@0: mad_stream_options(&stream,MAD_OPTION_IGNORECRC); nathan@0: mad_header_init(&header); nathan@0: if(str->Open() && str->Seek()) { nathan@0: int count=10; nathan@0: do { nathan@0: if(mad_header_decode(&header,&stream)<0) { nathan@0: if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) { nathan@0: if(MadStream(&stream,str)<=0) break; nathan@0: } nathan@0: else if(!MAD_RECOVERABLE(stream.error)) break; nathan@0: count++; nathan@0: } nathan@0: } while(--count); nathan@0: if(!count) res=true; nathan@0: } nathan@0: mad_header_finish(&header); nathan@0: mad_stream_finish(&stream); nathan@0: str->Close(); nathan@0: Unlock(); nathan@0: } nathan@0: return res; nathan@0: } nathan@0: nathan@0: cFileInfo *cMP3Decoder::FileInfo(void) nathan@0: { nathan@0: cFileInfo *fi=0; nathan@0: if(str->HasInfo()) fi=str; nathan@0: else if(TryLock()){ nathan@0: if(str->Open()) { fi=str; str->Close(); } nathan@0: Unlock(); nathan@0: } nathan@0: return fi; nathan@0: } nathan@0: nathan@0: cSongInfo *cMP3Decoder::SongInfo(bool get) nathan@0: { nathan@0: cSongInfo *si=0; nathan@0: if(scan->HasInfo()) si=scan; nathan@0: else if(get && TryLock()) { nathan@0: if(scan->DoScan()) si=scan; nathan@0: Unlock(); nathan@0: } nathan@0: return si; nathan@0: } nathan@0: nathan@0: cPlayInfo *cMP3Decoder::PlayInfo(void) nathan@0: { nathan@0: if(playing) { nathan@0: pi.Index=mad_timer_count(playtime,MAD_UNITS_SECONDS); nathan@0: pi.Total=scan->Total; nathan@0: return π nathan@0: } nathan@0: return 0; nathan@0: } nathan@0: nathan@0: void cMP3Decoder::Init(void) nathan@0: { nathan@0: Clean(); nathan@0: stream=new struct mad_stream; nathan@0: mad_stream_init(stream); nathan@0: mad_stream_options(stream,MAD_OPTION_IGNORECRC); nathan@0: frame=new struct mad_frame; nathan@0: mad_frame_init(frame); nathan@0: synth=new struct mad_synth; nathan@0: mad_synth_init(synth); nathan@0: playtime=mad_timer_zero; skiptime=mad_timer_zero; nathan@0: framenum=framemax=0; mute=errcount=0; nathan@0: } nathan@0: nathan@0: void cMP3Decoder::Clean(void) nathan@0: { nathan@0: playing=false; nathan@0: if(synth) { mad_synth_finish(synth); delete synth; synth=0; } nathan@0: if(frame) { mad_frame_finish(frame); delete frame; frame=0; } nathan@0: if(stream) { mad_stream_finish(stream); delete stream; stream=0; } nathan@0: delete[] fi; fi=0; nathan@0: } nathan@0: nathan@0: bool cMP3Decoder::Start(void) nathan@0: { nathan@0: Lock(true); nathan@0: Init(); playing=true; nathan@0: if(str->Open() && scan->DoScan(true)) { nathan@0: if(!isStream) { nathan@0: str->Seek(); nathan@0: framemax=scan->Frames+20; nathan@0: fi=new struct FrameInfo[framemax]; nathan@0: if(!fi) esyslog("ERROR: no memory for frame index, rewinding disabled"); nathan@0: } nathan@0: Unlock(); nathan@0: return true; nathan@0: } nathan@0: str->Close(); nathan@0: Clean(); nathan@0: Unlock(); nathan@0: return false; nathan@0: } nathan@0: nathan@0: bool cMP3Decoder::Stop(void) nathan@0: { nathan@0: Lock(); nathan@0: if(playing) { nathan@0: str->Close(); nathan@0: Clean(); nathan@0: } nathan@0: Unlock(); nathan@0: return true; nathan@0: } nathan@0: nathan@0: struct Decode *cMP3Decoder::Done(eDecodeStatus status) nathan@0: { nathan@0: ds.status=status; nathan@0: ds.index=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS); nathan@0: ds.pcm=&synth->pcm; nathan@0: Unlock(); // release the lock from Decode() nathan@0: return &ds; nathan@0: } nathan@0: nathan@0: eDecodeStatus cMP3Decoder::DecodeError(bool hdr) nathan@0: { nathan@0: if(stream->error==MAD_ERROR_BUFLEN || stream->error==MAD_ERROR_BUFPTR) { nathan@0: int s=MadStream(stream,str); nathan@0: if(s<0) return dsError; nathan@0: if(s==0) return dsEof; nathan@0: } nathan@0: else if(!MAD_RECOVERABLE(stream->error)) { nathan@0: d(printf("mad: decode %sfailed, frame=%d: %s\n",hdr?"hdr ":"",framenum,mad_stream_errorstr(stream))) nathan@0: return dsError; nathan@0: } nathan@0: else { nathan@0: if(stream->error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags nathan@0: #ifdef DEBUG nathan@0: char buf[10]; nathan@0: int buf2[3]; nathan@0: memcpy(buf,stream->this_frame,8); buf[8]=0; nathan@0: memcpy(buf2,stream->this_frame,8); nathan@0: printf("mad: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf); nathan@0: #endif nathan@0: id3_length_t count=stream->bufend-stream->this_frame; nathan@0: id3_length_t tagsize=id3_tag_query(stream->this_frame,count); nathan@0: if(tagsize>0) { nathan@0: d(printf("mad: skipping over ID3 tag\n")) nathan@0: if(count>tagsize) count=tagsize; nathan@0: mad_stream_skip(stream,count); nathan@0: while(countStream(sdata,slen)) return dsError; nathan@0: if(slen<=0) return dsEof; nathan@0: unsigned long len=min(tagsize-count,slen); nathan@0: count+=len; nathan@0: sdata+=len; slen-=len; nathan@0: if(slen>0) mad_stream_buffer(stream,sdata,slen); nathan@0: } nathan@0: return dsOK; nathan@0: } nathan@0: } nathan@0: errcount+=hdr?1:100; nathan@0: d(printf("mad: decode %serror, frame=%d count=%d: %s\n",hdr?"hdr ":"",framenum,errcount,mad_stream_errorstr(stream))) nathan@0: } nathan@0: return dsOK; nathan@0: } nathan@0: nathan@0: struct Decode *cMP3Decoder::Decode(void) nathan@0: { nathan@0: Lock(); // this is released in Done() nathan@0: eDecodeStatus r; nathan@0: while(playing) { nathan@0: if(errcount>=MAX_FRAME_ERR*100) { nathan@0: esyslog("ERROR: excessive decoding errors, aborting file %s",filename); nathan@0: return Done(dsError); nathan@0: } nathan@0: nathan@0: if(mad_header_decode(&frame->header,stream)<0) { nathan@0: if((r=DecodeError(true))) return Done(r); nathan@0: } nathan@0: else { nathan@0: if(!isStream) { nathan@0: #ifdef DEBUG nathan@0: if(framenum>=framemax) printf("mp3: framenum >= framemax!!!!\n"); nathan@0: #endif nathan@0: if(fi && framenumBufferPos() + (stream->this_frame-stream->buffer); nathan@0: fi[framenum].Time=playtime; nathan@0: } nathan@0: } nathan@0: nathan@0: mad_timer_add(&playtime,frame->header.duration); framenum++; nathan@0: nathan@0: if(mad_timer_compare(playtime,skiptime)>=0) skiptime=mad_timer_zero; nathan@0: else return Done(dsSkip); // skipping, decode next header nathan@0: nathan@0: if(mad_frame_decode(frame,stream)<0) { nathan@0: if((r=DecodeError(false))) return Done(r); nathan@0: } nathan@0: else { nathan@0: errcount=0; nathan@0: scan->InfoHook(&frame->header); nathan@0: mad_synth_frame(synth,frame); nathan@0: if(mute) { mute--; return Done(dsSkip); } nathan@0: return Done(dsPlay); nathan@0: } nathan@0: } nathan@0: } nathan@0: return Done(dsError); nathan@0: } nathan@0: nathan@0: void cMP3Decoder::MakeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, int secs, float bsecs) nathan@0: { nathan@0: mad_timer_t time; nathan@0: *skiptime=playtime; nathan@0: mad_timer_set(&time,abs(secs),0,0); nathan@0: if(secs<0) mad_timer_negate(&time); nathan@0: mad_timer_add(skiptime,time); nathan@0: int full=(int)bsecs; bsecs-=(float)full; nathan@0: mad_timer_set(&time,full,(int)(bsecs*1000.0),1000); nathan@0: mad_timer_negate(&time); nathan@0: mad_timer_add(skiptime,time); nathan@0: d(printf("mp3: skip: playtime=%ld secs=%d full=%d bsecs=%f skiptime=%ld\n", nathan@0: mad_timer_count(playtime,MAD_UNITS_MILLISECONDS),secs,full,bsecs,mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS))) nathan@0: } nathan@0: nathan@0: bool cMP3Decoder::Skip(int Seconds, float bsecs) nathan@0: { nathan@0: Lock(); nathan@0: bool res=false; nathan@0: if(playing && !isStream) { nathan@0: if(!mad_timer_compare(skiptime,mad_timer_zero)) { // allow only one skip at any time nathan@0: mad_timer_t time; nathan@0: MakeSkipTime(&time,playtime,Seconds,bsecs); nathan@0: nathan@0: if(mad_timer_compare(playtime,time)<=0) { // forward skip nathan@0: #ifdef DEBUG nathan@0: int i=mad_timer_count(time,MAD_UNITS_SECONDS); nathan@0: printf("mp3: forward skipping to %02d:%02d\n",i/60,i%60); nathan@0: #endif nathan@0: skiptime=time; mute=1; nathan@0: res=true; nathan@0: } nathan@0: else { // backward skip nathan@0: if(fi) { nathan@0: #ifdef DEBUG nathan@0: int i=mad_timer_count(time,MAD_UNITS_SECONDS); nathan@0: printf("mp3: rewinding to %02d:%02d\n",i/60,i%60); nathan@0: #endif nathan@0: while(framenum && mad_timer_compare(time,fi[--framenum].Time)<0) ; nathan@0: mute=2; if(framenum>=2) framenum-=2; nathan@0: playtime=fi[framenum].Time; nathan@0: str->Seek(fi[framenum].Pos); nathan@0: mad_stream_finish(stream); // reset stream buffer nathan@0: mad_stream_init(stream); nathan@0: #ifdef DEBUG nathan@0: i=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS); nathan@0: printf("mp3: new playtime=%d framenum=%d filepos=%lld\n",i,framenum,fi[framenum].Pos); nathan@0: #endif nathan@0: res=true; nathan@0: } nathan@0: } nathan@0: } nathan@0: } nathan@0: Unlock(); nathan@0: return res; nathan@0: } nathan@0: nathan@0: // --- cScanID3 ---------------------------------------------------------------- nathan@0: nathan@0: // This function was adapted from mad_timer, from the nathan@0: // libmad distribution nathan@0: nathan@0: #define MIN_SCAN_FRAMES 200 // min. number of frames to scan nathan@0: nathan@0: cScanID3::cScanID3(cStream *Str, bool *Urgent) nathan@0: { nathan@0: str=Str; nathan@0: urgent=Urgent; nathan@0: } nathan@0: nathan@0: bool cScanID3::Abort(bool result) nathan@0: { nathan@0: if(!keepOpen) str->Close(); nathan@0: return result; nathan@0: } nathan@0: nathan@0: bool cScanID3::DoScan(bool KeepOpen) nathan@0: { nathan@0: mad_timer_t duration=mad_timer_zero; nathan@0: unsigned int bitrate=0, minrate=~0, maxrate=0; nathan@0: int xframes=0; nathan@0: unsigned int id3_vers=0; nathan@0: bool is_vbr=false, has_id3=false; nathan@0: nathan@0: keepOpen=KeepOpen; nathan@0: if(!str->Open()) return Abort(false); nathan@0: if(HasInfo()) return Abort(true); nathan@0: nathan@0: // check the infocache nathan@0: cCacheData *dat=InfoCache.Search(str); nathan@0: if(dat) { nathan@0: Set(dat); dat->Unlock(); nathan@0: if(!DecoderID) { nathan@0: DecoderID=DEC_MP3; nathan@0: InfoCache.Cache(this,str); nathan@0: } nathan@0: return Abort(true); nathan@0: } nathan@0: nathan@0: Clear(); nathan@0: nathan@0: // do a initial check for a ID3v1 tag at the end of the file nathan@0: // to speed up the following scan nathan@0: if(str->Filesize>=128 && str->Seek(str->Filesize-128)) { nathan@0: unsigned char *data; nathan@0: unsigned long len; nathan@0: if(str->Stream(data,len)) { nathan@0: struct id3_tag *tag=id3_tag_parse(data,len); nathan@0: if(tag) { nathan@0: d(printf("id3-scan: initialy found ID3 V1 tag at EOF\n")) nathan@0: ParseID3(tag); nathan@0: has_id3=true; id3_vers=tag->version; nathan@0: id3_tag_delete(tag); nathan@0: } nathan@0: } nathan@0: } nathan@0: if(!str->Seek()) return Abort(false); nathan@0: nathan@0: // There are three ways of calculating the length of an mp3: nathan@0: // 1) Constant bitrate: One frame can provide the information nathan@0: // needed: # of frames and duration. Just see how long it nathan@0: // is and do the division. nathan@0: // 2) Variable bitrate: Xing tag. It provides the number of nathan@0: // frames. Each frame has the same number of samples, so nathan@0: // just use that. nathan@0: // 3) All: Count up the frames and duration of each frames nathan@0: // by decoding each one. We do this if we've no other nathan@0: // choice, i.e. if it's a VBR file with no Xing tag. nathan@0: nathan@0: struct mad_stream stream; nathan@0: struct mad_header header; nathan@0: mad_stream_init(&stream); nathan@0: mad_stream_options(&stream,MAD_OPTION_IGNORECRC); nathan@0: mad_header_init(&header); nathan@0: bool res=true; nathan@0: int errcount=0; nathan@0: while(1) { nathan@0: if(*urgent) { nathan@0: d(printf("id3-scan: urgent request, aborting!\n")) nathan@0: res=false; break; // abort scan if there is an urgent request for the decoder lock nathan@0: } nathan@0: nathan@0: if(mad_header_decode(&header,&stream)<0) { nathan@0: if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) { nathan@0: int s=MadStream(&stream,str); nathan@0: if(s>0) continue; nathan@0: if(s<0) res=false; nathan@0: break; nathan@0: } nathan@0: else if(stream.error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags nathan@0: #ifdef DEBUG nathan@0: char buf[10]; nathan@0: int buf2[3]; nathan@0: memcpy(buf,stream.this_frame,8); buf[8]=0; nathan@0: memcpy(buf2,stream.this_frame,8); nathan@0: printf("id3-scan: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf); nathan@0: #endif nathan@0: id3_length_t tagsize=id3_tag_query(stream.this_frame,stream.bufend-stream.this_frame); nathan@0: if(tagsize>0) { nathan@0: struct id3_tag *tag=GetID3(&stream,tagsize); nathan@0: if(tag) { nathan@0: unsigned int vers=id3_tag_version(tag); nathan@0: d(printf("id3-scan: found ID3 %s tag (%d.%d)\n",vers==0x100?"V1":"V2",ID3_TAG_VERSION_MAJOR(vers),ID3_TAG_VERSION_MINOR(vers))) nathan@0: if(!has_id3 || vers>id3_vers) { nathan@0: ParseID3(tag); nathan@0: has_id3=true; id3_vers=vers; nathan@0: } nathan@0: id3_tag_delete(tag); nathan@0: } nathan@0: } nathan@0: continue; nathan@0: } nathan@0: else { nathan@0: d(printf("id3-scan: decode header error (frame %d): %s\n",Frames,mad_stream_errorstr(&stream))) nathan@0: errcount++; nathan@0: if(errcountmaxrate) maxrate=header.bitrate; nathan@0: if(header.bitrate=0) { nathan@0: is_vbr=true; nathan@0: } nathan@0: } nathan@0: // Test the first n frames to see if this is a VBR file nathan@0: if(!is_vbr && FramesFilesize * 8.0) / (header.bitrate); // time in seconds nathan@0: long nsamples = 32 * MAD_NSBSAMPLES(&header); // samples per frame nathan@0: nathan@0: Frames = (long)(time * header.samplerate / nsamples); nathan@0: Total = (long)time; nathan@0: Bitrate= (int)bitrate; nathan@0: } nathan@0: else if(xframes>0) { nathan@0: d(printf("mad: vbr, but has Xing frame\n")) nathan@0: mad_timer_multiply(&header.duration, xframes); nathan@0: Frames = xframes; nathan@0: Total = mad_timer_count(header.duration,MAD_UNITS_SECONDS); nathan@0: } nathan@0: else { nathan@0: // the durations have been added up, and the number of frames counted. We do nothing here. nathan@0: d(printf("mad: vbr detected\n")) nathan@0: Total = mad_timer_count(duration,MAD_UNITS_SECONDS); nathan@0: Bitrate = (int)minrate; nathan@0: MaxBitrate = (int)maxrate; nathan@0: } nathan@0: nathan@0: if(!has_id3 || !Title) FakeTitle(str->Filename,".mp3"); nathan@0: InfoCache.Cache(this,str); nathan@0: } nathan@0: } nathan@0: return Abort(res); nathan@0: } nathan@0: nathan@0: // This function was adapted from player.c, from the nathan@0: // libmad distribution nathan@0: nathan@0: struct id3_tag *cScanID3::GetID3(struct mad_stream *stream, id3_length_t tagsize) const nathan@0: { nathan@0: struct id3_tag *tag=0; nathan@0: const id3_byte_t *data; nathan@0: id3_byte_t *allocated=0; nathan@0: nathan@0: id3_length_t count=stream->bufend-stream->this_frame; nathan@0: nathan@0: if(count>=tagsize) { nathan@0: data=stream->this_frame; nathan@0: mad_stream_skip(stream,tagsize); nathan@0: } nathan@0: else { nathan@0: if(!(allocated=(id3_byte_t *)malloc(tagsize))) { nathan@0: esyslog("ERROR: not enough memory for id3 tag buffer"); nathan@0: return 0; nathan@0: } nathan@0: memcpy(allocated,stream->this_frame,count); nathan@0: mad_stream_skip(stream,count); nathan@0: nathan@0: while(countStream(sdata,slen) || !slen) { nathan@0: d(printf("mad: error or eof on ID3 tag parse\n")) nathan@0: free(allocated); nathan@0: return 0; nathan@0: } nathan@0: len=tagsize-count; if(len>slen) len=slen; nathan@0: memcpy(allocated+count,sdata,len); nathan@0: count+=len; nathan@0: sdata+=len; slen-=len; nathan@0: if(slen) mad_stream_buffer(stream,sdata,slen); nathan@0: } nathan@0: data=allocated; nathan@0: } nathan@0: nathan@0: tag=id3_tag_parse(data,tagsize); nathan@0: if(allocated) free(allocated); nathan@0: return tag; nathan@0: } nathan@0: nathan@0: void cScanID3::ParseID3(const struct id3_tag *tag) nathan@0: { nathan@0: d(printf("id3-scan: parsing ID3 tag\n")) nathan@0: ParseStr(tag,ID3_FRAME_TITLE,Title); nathan@0: ParseStr(tag,ID3_FRAME_ARTIST,Artist); nathan@0: ParseStr(tag,ID3_FRAME_ALBUM,Album); nathan@0: char *data=0; nathan@0: ParseStr(tag,ID3_FRAME_YEAR,data); nathan@0: if(data) Year=atol(data); nathan@0: free(data); nathan@0: //ParseStr(tag,ID3_FRAME_TRACK,Track); nathan@0: //ParseStr(tag,ID3_FRAME_GENRE,Genre); nathan@0: } nathan@0: nathan@0: /* nathan@0: void cScanID3::ParsePic(const struct id3_tag *tag, const char *id, char * &name) nathan@0: { nathan@0: const struct id3_frame *frame=id3_tag_findframe(tag,id,0); nathan@0: if(frame) { nathan@0: id3_length_t len; nathan@0: const id3_byte_t *data=id3_field_getbinarydata(&frame->fields[1],&len); nathan@0: if(data && len>0) { nathan@0: static const char salt[] = { "$1$id3__pic$" }; nathan@0: nathan@0: } nathan@0: } nathan@0: } nathan@0: */ nathan@0: nathan@0: // This function was adapted from player.c, from the nathan@0: // libmad distribution nathan@0: nathan@0: void cScanID3::ParseStr(const struct id3_tag *tag, const char *id, char * &data) nathan@0: { nathan@0: const struct id3_frame *frame=id3_tag_findframe(tag,id,0); nathan@0: if(!frame) return; nathan@0: nathan@0: free(data); data=0; nathan@0: const union id3_field *field=&frame->fields[1]; nathan@0: if(id3_field_getnstrings(field)>0) { nathan@0: const id3_ucs4_t *ucs4=id3_field_getstrings(field,0); nathan@0: if(!ucs4) return; nathan@0: if(!strcmp(id,ID3_FRAME_GENRE)) ucs4=id3_genre_name(ucs4); nathan@0: nathan@0: id3_latin1_t *latin1=id3_ucs4_latin1duplicate(ucs4); nathan@0: if(!latin1) return; nathan@0: nathan@0: data=strdup((char *)latin1); nathan@0: free(latin1); nathan@0: } nathan@0: } nathan@0: nathan@0: // XING parsing was adapted from the MAD winamp input plugin, nathan@0: // from the libmad distribution nathan@0: nathan@0: #define XING_MAGIC (('X'<<24) | ('i'<<16) | ('n'<<8) | 'g') nathan@0: #define XING_FRAMES 0x0001 nathan@0: // #define XING_BYTES 0x0002 nathan@0: // #define XING_TOC 0x0004 nathan@0: // #define XING_SCALE 0x0008 nathan@0: nathan@0: int cScanID3::ParseXing(struct mad_bitptr *ptr, unsigned int bitlen) const nathan@0: { nathan@0: if(bitlen>=64 && mad_bit_read(ptr,32)==XING_MAGIC) { nathan@0: int flags=mad_bit_read(ptr, 32); nathan@0: bitlen-=64; nathan@0: return (bitlen>=32 && (flags & XING_FRAMES)) ? mad_bit_read(ptr,32) : 0; nathan@0: } nathan@0: return -1; nathan@0: }