1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/decoder-mp3.c Sat Dec 29 14:47:40 2007 +0100
1.3 @@ -0,0 +1,641 @@
1.4 +/*
1.5 + * MP3/MPlayer plugin to VDR (C++)
1.6 + *
1.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
1.8 + *
1.9 + * This code is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License
1.11 + * as published by the Free Software Foundation; either version 2
1.12 + * of the License, or (at your option) any later version.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
1.23 + */
1.24 +
1.25 +#include <stdlib.h>
1.26 +#include <stdio.h>
1.27 +
1.28 +#include "common.h"
1.29 +#include "data-mp3.h"
1.30 +#include "decoder-mp3.h"
1.31 +#include "stream.h"
1.32 +
1.33 +#define MAX_FRAME_ERR 10
1.34 +
1.35 +// ----------------------------------------------------------------
1.36 +
1.37 +int MadStream(struct mad_stream *stream, cStream *str)
1.38 +{
1.39 + unsigned char *data;
1.40 + unsigned long len;
1.41 + if(str->Stream(data,len,stream->next_frame)) {
1.42 + if(len>0) mad_stream_buffer(stream, data, len);
1.43 + return len;
1.44 + }
1.45 + return -1;
1.46 +}
1.47 +
1.48 +// --- cMP3Decoder -------------------------------------------------------------
1.49 +
1.50 +cMP3Decoder::cMP3Decoder(const char *Filename, bool preinit)
1.51 +:cDecoder(Filename)
1.52 +{
1.53 + str=0; scan=0; isStream=false;
1.54 + if(preinit) {
1.55 + //d(printf("mp3: preinit\n"))
1.56 + str=new cStream(filename);
1.57 + scan=new cScanID3(str,&urgentLock);
1.58 + }
1.59 + fi=0; stream=0; frame=0; synth=0;
1.60 +}
1.61 +
1.62 +cMP3Decoder::~cMP3Decoder()
1.63 +{
1.64 + Clean();
1.65 + delete scan;
1.66 + delete str;
1.67 +}
1.68 +
1.69 +bool cMP3Decoder::Valid(void)
1.70 +{
1.71 + bool res=false;
1.72 + if(TryLock()) {
1.73 + struct mad_stream stream;
1.74 + struct mad_header header;
1.75 + mad_stream_init(&stream);
1.76 + mad_stream_options(&stream,MAD_OPTION_IGNORECRC);
1.77 + mad_header_init(&header);
1.78 + if(str->Open() && str->Seek()) {
1.79 + int count=10;
1.80 + do {
1.81 + if(mad_header_decode(&header,&stream)<0) {
1.82 + if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) {
1.83 + if(MadStream(&stream,str)<=0) break;
1.84 + }
1.85 + else if(!MAD_RECOVERABLE(stream.error)) break;
1.86 + count++;
1.87 + }
1.88 + } while(--count);
1.89 + if(!count) res=true;
1.90 + }
1.91 + mad_header_finish(&header);
1.92 + mad_stream_finish(&stream);
1.93 + str->Close();
1.94 + Unlock();
1.95 + }
1.96 + return res;
1.97 +}
1.98 +
1.99 +cFileInfo *cMP3Decoder::FileInfo(void)
1.100 +{
1.101 + cFileInfo *fi=0;
1.102 + if(str->HasInfo()) fi=str;
1.103 + else if(TryLock()){
1.104 + if(str->Open()) { fi=str; str->Close(); }
1.105 + Unlock();
1.106 + }
1.107 + return fi;
1.108 +}
1.109 +
1.110 +cSongInfo *cMP3Decoder::SongInfo(bool get)
1.111 +{
1.112 + cSongInfo *si=0;
1.113 + if(scan->HasInfo()) si=scan;
1.114 + else if(get && TryLock()) {
1.115 + if(scan->DoScan()) si=scan;
1.116 + Unlock();
1.117 + }
1.118 + return si;
1.119 +}
1.120 +
1.121 +cPlayInfo *cMP3Decoder::PlayInfo(void)
1.122 +{
1.123 + if(playing) {
1.124 + pi.Index=mad_timer_count(playtime,MAD_UNITS_SECONDS);
1.125 + pi.Total=scan->Total;
1.126 + return π
1.127 + }
1.128 + return 0;
1.129 +}
1.130 +
1.131 +void cMP3Decoder::Init(void)
1.132 +{
1.133 + Clean();
1.134 + stream=new struct mad_stream;
1.135 + mad_stream_init(stream);
1.136 + mad_stream_options(stream,MAD_OPTION_IGNORECRC);
1.137 + frame=new struct mad_frame;
1.138 + mad_frame_init(frame);
1.139 + synth=new struct mad_synth;
1.140 + mad_synth_init(synth);
1.141 + playtime=mad_timer_zero; skiptime=mad_timer_zero;
1.142 + framenum=framemax=0; mute=errcount=0;
1.143 +}
1.144 +
1.145 +void cMP3Decoder::Clean(void)
1.146 +{
1.147 + playing=false;
1.148 + if(synth) { mad_synth_finish(synth); delete synth; synth=0; }
1.149 + if(frame) { mad_frame_finish(frame); delete frame; frame=0; }
1.150 + if(stream) { mad_stream_finish(stream); delete stream; stream=0; }
1.151 + delete[] fi; fi=0;
1.152 +}
1.153 +
1.154 +bool cMP3Decoder::Start(void)
1.155 +{
1.156 + Lock(true);
1.157 + Init(); playing=true;
1.158 + if(str->Open() && scan->DoScan(true)) {
1.159 + if(!isStream) {
1.160 + str->Seek();
1.161 + framemax=scan->Frames+20;
1.162 + fi=new struct FrameInfo[framemax];
1.163 + if(!fi) esyslog("ERROR: no memory for frame index, rewinding disabled");
1.164 + }
1.165 + Unlock();
1.166 + return true;
1.167 + }
1.168 + str->Close();
1.169 + Clean();
1.170 + Unlock();
1.171 + return false;
1.172 +}
1.173 +
1.174 +bool cMP3Decoder::Stop(void)
1.175 +{
1.176 + Lock();
1.177 + if(playing) {
1.178 + str->Close();
1.179 + Clean();
1.180 + }
1.181 + Unlock();
1.182 + return true;
1.183 +}
1.184 +
1.185 +struct Decode *cMP3Decoder::Done(eDecodeStatus status)
1.186 +{
1.187 + ds.status=status;
1.188 + ds.index=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS);
1.189 + ds.pcm=&synth->pcm;
1.190 + Unlock(); // release the lock from Decode()
1.191 + return &ds;
1.192 +}
1.193 +
1.194 +eDecodeStatus cMP3Decoder::DecodeError(bool hdr)
1.195 +{
1.196 + if(stream->error==MAD_ERROR_BUFLEN || stream->error==MAD_ERROR_BUFPTR) {
1.197 + int s=MadStream(stream,str);
1.198 + if(s<0) return dsError;
1.199 + if(s==0) return dsEof;
1.200 + }
1.201 + else if(!MAD_RECOVERABLE(stream->error)) {
1.202 + d(printf("mad: decode %sfailed, frame=%d: %s\n",hdr?"hdr ":"",framenum,mad_stream_errorstr(stream)))
1.203 + return dsError;
1.204 + }
1.205 + else {
1.206 + if(stream->error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags
1.207 +#ifdef DEBUG
1.208 + char buf[10];
1.209 + int buf2[3];
1.210 + memcpy(buf,stream->this_frame,8); buf[8]=0;
1.211 + memcpy(buf2,stream->this_frame,8);
1.212 + printf("mad: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf);
1.213 +#endif
1.214 + id3_length_t count=stream->bufend-stream->this_frame;
1.215 + id3_length_t tagsize=id3_tag_query(stream->this_frame,count);
1.216 + if(tagsize>0) {
1.217 + d(printf("mad: skipping over ID3 tag\n"))
1.218 + if(count>tagsize) count=tagsize;
1.219 + mad_stream_skip(stream,count);
1.220 + while(count<tagsize) {
1.221 + unsigned char *sdata;
1.222 + unsigned long slen;
1.223 + if(!str->Stream(sdata,slen)) return dsError;
1.224 + if(slen<=0) return dsEof;
1.225 + unsigned long len=min(tagsize-count,slen);
1.226 + count+=len;
1.227 + sdata+=len; slen-=len;
1.228 + if(slen>0) mad_stream_buffer(stream,sdata,slen);
1.229 + }
1.230 + return dsOK;
1.231 + }
1.232 + }
1.233 + errcount+=hdr?1:100;
1.234 + d(printf("mad: decode %serror, frame=%d count=%d: %s\n",hdr?"hdr ":"",framenum,errcount,mad_stream_errorstr(stream)))
1.235 + }
1.236 + return dsOK;
1.237 +}
1.238 +
1.239 +struct Decode *cMP3Decoder::Decode(void)
1.240 +{
1.241 + Lock(); // this is released in Done()
1.242 + eDecodeStatus r;
1.243 + while(playing) {
1.244 + if(errcount>=MAX_FRAME_ERR*100) {
1.245 + esyslog("ERROR: excessive decoding errors, aborting file %s",filename);
1.246 + return Done(dsError);
1.247 + }
1.248 +
1.249 + if(mad_header_decode(&frame->header,stream)<0) {
1.250 + if((r=DecodeError(true))) return Done(r);
1.251 + }
1.252 + else {
1.253 + if(!isStream) {
1.254 +#ifdef DEBUG
1.255 + if(framenum>=framemax) printf("mp3: framenum >= framemax!!!!\n");
1.256 +#endif
1.257 + if(fi && framenum<framemax) {
1.258 + fi[framenum].Pos=str->BufferPos() + (stream->this_frame-stream->buffer);
1.259 + fi[framenum].Time=playtime;
1.260 + }
1.261 + }
1.262 +
1.263 + mad_timer_add(&playtime,frame->header.duration); framenum++;
1.264 +
1.265 + if(mad_timer_compare(playtime,skiptime)>=0) skiptime=mad_timer_zero;
1.266 + else return Done(dsSkip); // skipping, decode next header
1.267 +
1.268 + if(mad_frame_decode(frame,stream)<0) {
1.269 + if((r=DecodeError(false))) return Done(r);
1.270 + }
1.271 + else {
1.272 + errcount=0;
1.273 + scan->InfoHook(&frame->header);
1.274 + mad_synth_frame(synth,frame);
1.275 + if(mute) { mute--; return Done(dsSkip); }
1.276 + return Done(dsPlay);
1.277 + }
1.278 + }
1.279 + }
1.280 + return Done(dsError);
1.281 +}
1.282 +
1.283 +void cMP3Decoder::MakeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, int secs, float bsecs)
1.284 +{
1.285 + mad_timer_t time;
1.286 + *skiptime=playtime;
1.287 + mad_timer_set(&time,abs(secs),0,0);
1.288 + if(secs<0) mad_timer_negate(&time);
1.289 + mad_timer_add(skiptime,time);
1.290 + int full=(int)bsecs; bsecs-=(float)full;
1.291 + mad_timer_set(&time,full,(int)(bsecs*1000.0),1000);
1.292 + mad_timer_negate(&time);
1.293 + mad_timer_add(skiptime,time);
1.294 + d(printf("mp3: skip: playtime=%ld secs=%d full=%d bsecs=%f skiptime=%ld\n",
1.295 + mad_timer_count(playtime,MAD_UNITS_MILLISECONDS),secs,full,bsecs,mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS)))
1.296 +}
1.297 +
1.298 +bool cMP3Decoder::Skip(int Seconds, float bsecs)
1.299 +{
1.300 + Lock();
1.301 + bool res=false;
1.302 + if(playing && !isStream) {
1.303 + if(!mad_timer_compare(skiptime,mad_timer_zero)) { // allow only one skip at any time
1.304 + mad_timer_t time;
1.305 + MakeSkipTime(&time,playtime,Seconds,bsecs);
1.306 +
1.307 + if(mad_timer_compare(playtime,time)<=0) { // forward skip
1.308 +#ifdef DEBUG
1.309 + int i=mad_timer_count(time,MAD_UNITS_SECONDS);
1.310 + printf("mp3: forward skipping to %02d:%02d\n",i/60,i%60);
1.311 +#endif
1.312 + skiptime=time; mute=1;
1.313 + res=true;
1.314 + }
1.315 + else { // backward skip
1.316 + if(fi) {
1.317 +#ifdef DEBUG
1.318 + int i=mad_timer_count(time,MAD_UNITS_SECONDS);
1.319 + printf("mp3: rewinding to %02d:%02d\n",i/60,i%60);
1.320 +#endif
1.321 + while(framenum && mad_timer_compare(time,fi[--framenum].Time)<0) ;
1.322 + mute=2; if(framenum>=2) framenum-=2;
1.323 + playtime=fi[framenum].Time;
1.324 + str->Seek(fi[framenum].Pos);
1.325 + mad_stream_finish(stream); // reset stream buffer
1.326 + mad_stream_init(stream);
1.327 +#ifdef DEBUG
1.328 + i=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS);
1.329 + printf("mp3: new playtime=%d framenum=%d filepos=%lld\n",i,framenum,fi[framenum].Pos);
1.330 +#endif
1.331 + res=true;
1.332 + }
1.333 + }
1.334 + }
1.335 + }
1.336 + Unlock();
1.337 + return res;
1.338 +}
1.339 +
1.340 +// --- cScanID3 ----------------------------------------------------------------
1.341 +
1.342 +// This function was adapted from mad_timer, from the
1.343 +// libmad distribution
1.344 +
1.345 +#define MIN_SCAN_FRAMES 200 // min. number of frames to scan
1.346 +
1.347 +cScanID3::cScanID3(cStream *Str, bool *Urgent)
1.348 +{
1.349 + str=Str;
1.350 + urgent=Urgent;
1.351 +}
1.352 +
1.353 +bool cScanID3::Abort(bool result)
1.354 +{
1.355 + if(!keepOpen) str->Close();
1.356 + return result;
1.357 +}
1.358 +
1.359 +bool cScanID3::DoScan(bool KeepOpen)
1.360 +{
1.361 + mad_timer_t duration=mad_timer_zero;
1.362 + unsigned int bitrate=0, minrate=~0, maxrate=0;
1.363 + int xframes=0;
1.364 + unsigned int id3_vers=0;
1.365 + bool is_vbr=false, has_id3=false;
1.366 +
1.367 + keepOpen=KeepOpen;
1.368 + if(!str->Open()) return Abort(false);
1.369 + if(HasInfo()) return Abort(true);
1.370 +
1.371 + // check the infocache
1.372 + cCacheData *dat=InfoCache.Search(str);
1.373 + if(dat) {
1.374 + Set(dat); dat->Unlock();
1.375 + if(!DecoderID) {
1.376 + DecoderID=DEC_MP3;
1.377 + InfoCache.Cache(this,str);
1.378 + }
1.379 + return Abort(true);
1.380 + }
1.381 +
1.382 + Clear();
1.383 +
1.384 + // do a initial check for a ID3v1 tag at the end of the file
1.385 + // to speed up the following scan
1.386 + if(str->Filesize>=128 && str->Seek(str->Filesize-128)) {
1.387 + unsigned char *data;
1.388 + unsigned long len;
1.389 + if(str->Stream(data,len)) {
1.390 + struct id3_tag *tag=id3_tag_parse(data,len);
1.391 + if(tag) {
1.392 + d(printf("id3-scan: initialy found ID3 V1 tag at EOF\n"))
1.393 + ParseID3(tag);
1.394 + has_id3=true; id3_vers=tag->version;
1.395 + id3_tag_delete(tag);
1.396 + }
1.397 + }
1.398 + }
1.399 + if(!str->Seek()) return Abort(false);
1.400 +
1.401 + // There are three ways of calculating the length of an mp3:
1.402 + // 1) Constant bitrate: One frame can provide the information
1.403 + // needed: # of frames and duration. Just see how long it
1.404 + // is and do the division.
1.405 + // 2) Variable bitrate: Xing tag. It provides the number of
1.406 + // frames. Each frame has the same number of samples, so
1.407 + // just use that.
1.408 + // 3) All: Count up the frames and duration of each frames
1.409 + // by decoding each one. We do this if we've no other
1.410 + // choice, i.e. if it's a VBR file with no Xing tag.
1.411 +
1.412 + struct mad_stream stream;
1.413 + struct mad_header header;
1.414 + mad_stream_init(&stream);
1.415 + mad_stream_options(&stream,MAD_OPTION_IGNORECRC);
1.416 + mad_header_init(&header);
1.417 + bool res=true;
1.418 + int errcount=0;
1.419 + while(1) {
1.420 + if(*urgent) {
1.421 + d(printf("id3-scan: urgent request, aborting!\n"))
1.422 + res=false; break; // abort scan if there is an urgent request for the decoder lock
1.423 + }
1.424 +
1.425 + if(mad_header_decode(&header,&stream)<0) {
1.426 + if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) {
1.427 + int s=MadStream(&stream,str);
1.428 + if(s>0) continue;
1.429 + if(s<0) res=false;
1.430 + break;
1.431 + }
1.432 + else if(stream.error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags
1.433 +#ifdef DEBUG
1.434 + char buf[10];
1.435 + int buf2[3];
1.436 + memcpy(buf,stream.this_frame,8); buf[8]=0;
1.437 + memcpy(buf2,stream.this_frame,8);
1.438 + printf("id3-scan: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf);
1.439 +#endif
1.440 + id3_length_t tagsize=id3_tag_query(stream.this_frame,stream.bufend-stream.this_frame);
1.441 + if(tagsize>0) {
1.442 + struct id3_tag *tag=GetID3(&stream,tagsize);
1.443 + if(tag) {
1.444 + unsigned int vers=id3_tag_version(tag);
1.445 + 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)))
1.446 + if(!has_id3 || vers>id3_vers) {
1.447 + ParseID3(tag);
1.448 + has_id3=true; id3_vers=vers;
1.449 + }
1.450 + id3_tag_delete(tag);
1.451 + }
1.452 + }
1.453 + continue;
1.454 + }
1.455 + else {
1.456 + d(printf("id3-scan: decode header error (frame %d): %s\n",Frames,mad_stream_errorstr(&stream)))
1.457 + errcount++;
1.458 + if(errcount<MAX_FRAME_ERR*100 && MAD_RECOVERABLE(stream.error)) continue;
1.459 + res=false; break;
1.460 + }
1.461 + }
1.462 + errcount=0;
1.463 + if(header.bitrate>maxrate) maxrate=header.bitrate;
1.464 + if(header.bitrate<minrate) minrate=header.bitrate;
1.465 +
1.466 + // Limit xing testing to the first frame header
1.467 + if(!Frames) {
1.468 + if((xframes=ParseXing(&stream.anc_ptr, stream.anc_bitlen))>=0) {
1.469 + is_vbr=true;
1.470 + }
1.471 + }
1.472 + // Test the first n frames to see if this is a VBR file
1.473 + if(!is_vbr && Frames<MIN_SCAN_FRAMES) {
1.474 + if(bitrate && header.bitrate!=bitrate) is_vbr=true;
1.475 + else bitrate=header.bitrate;
1.476 + }
1.477 + // We have to assume it's not a VBR file if it hasn't already been
1.478 + // marked as one and we've checked n frames for different bitrates
1.479 + else if(!is_vbr && has_id3)
1.480 + {
1.481 + break;
1.482 + }
1.483 +
1.484 + Frames++;
1.485 + mad_timer_add(&duration,header.duration);
1.486 + }
1.487 + mad_header_finish(&header);
1.488 + mad_stream_finish(&stream);
1.489 +
1.490 + if(res) {
1.491 + d(printf("mad: scanned %d frames%s\n",Frames,Frames?"":"(is this really a mp3?)"))
1.492 + if(Frames) {
1.493 + SampleFreq=header.samplerate;
1.494 + Channels=MAD_NCHANNELS(&header);
1.495 + ChMode=header.mode;
1.496 + DecoderID=DEC_MP3;
1.497 + InfoDone();
1.498 +
1.499 + if(!is_vbr) {
1.500 + d(printf("mad: constant birate\n"))
1.501 + double time = (str->Filesize * 8.0) / (header.bitrate); // time in seconds
1.502 + long nsamples = 32 * MAD_NSBSAMPLES(&header); // samples per frame
1.503 +
1.504 + Frames = (long)(time * header.samplerate / nsamples);
1.505 + Total = (long)time;
1.506 + Bitrate= (int)bitrate;
1.507 + }
1.508 + else if(xframes>0) {
1.509 + d(printf("mad: vbr, but has Xing frame\n"))
1.510 + mad_timer_multiply(&header.duration, xframes);
1.511 + Frames = xframes;
1.512 + Total = mad_timer_count(header.duration,MAD_UNITS_SECONDS);
1.513 + }
1.514 + else {
1.515 + // the durations have been added up, and the number of frames counted. We do nothing here.
1.516 + d(printf("mad: vbr detected\n"))
1.517 + Total = mad_timer_count(duration,MAD_UNITS_SECONDS);
1.518 + Bitrate = (int)minrate;
1.519 + MaxBitrate = (int)maxrate;
1.520 + }
1.521 +
1.522 + if(!has_id3 || !Title) FakeTitle(str->Filename,".mp3");
1.523 + InfoCache.Cache(this,str);
1.524 + }
1.525 + }
1.526 + return Abort(res);
1.527 +}
1.528 +
1.529 +// This function was adapted from player.c, from the
1.530 +// libmad distribution
1.531 +
1.532 +struct id3_tag *cScanID3::GetID3(struct mad_stream *stream, id3_length_t tagsize) const
1.533 +{
1.534 + struct id3_tag *tag=0;
1.535 + const id3_byte_t *data;
1.536 + id3_byte_t *allocated=0;
1.537 +
1.538 + id3_length_t count=stream->bufend-stream->this_frame;
1.539 +
1.540 + if(count>=tagsize) {
1.541 + data=stream->this_frame;
1.542 + mad_stream_skip(stream,tagsize);
1.543 + }
1.544 + else {
1.545 + if(!(allocated=(id3_byte_t *)malloc(tagsize))) {
1.546 + esyslog("ERROR: not enough memory for id3 tag buffer");
1.547 + return 0;
1.548 + }
1.549 + memcpy(allocated,stream->this_frame,count);
1.550 + mad_stream_skip(stream,count);
1.551 +
1.552 + while(count<tagsize) {
1.553 + unsigned char *sdata;
1.554 + unsigned long len, slen;
1.555 +
1.556 + if(!str->Stream(sdata,slen) || !slen) {
1.557 + d(printf("mad: error or eof on ID3 tag parse\n"))
1.558 + free(allocated);
1.559 + return 0;
1.560 + }
1.561 + len=tagsize-count; if(len>slen) len=slen;
1.562 + memcpy(allocated+count,sdata,len);
1.563 + count+=len;
1.564 + sdata+=len; slen-=len;
1.565 + if(slen) mad_stream_buffer(stream,sdata,slen);
1.566 + }
1.567 + data=allocated;
1.568 + }
1.569 +
1.570 + tag=id3_tag_parse(data,tagsize);
1.571 + if(allocated) free(allocated);
1.572 + return tag;
1.573 +}
1.574 +
1.575 +void cScanID3::ParseID3(const struct id3_tag *tag)
1.576 +{
1.577 + d(printf("id3-scan: parsing ID3 tag\n"))
1.578 + ParseStr(tag,ID3_FRAME_TITLE,Title);
1.579 + ParseStr(tag,ID3_FRAME_ARTIST,Artist);
1.580 + ParseStr(tag,ID3_FRAME_ALBUM,Album);
1.581 + char *data=0;
1.582 + ParseStr(tag,ID3_FRAME_YEAR,data);
1.583 + if(data) Year=atol(data);
1.584 + free(data);
1.585 + //ParseStr(tag,ID3_FRAME_TRACK,Track);
1.586 + //ParseStr(tag,ID3_FRAME_GENRE,Genre);
1.587 +}
1.588 +
1.589 +/*
1.590 +void cScanID3::ParsePic(const struct id3_tag *tag, const char *id, char * &name)
1.591 +{
1.592 + const struct id3_frame *frame=id3_tag_findframe(tag,id,0);
1.593 + if(frame) {
1.594 + id3_length_t len;
1.595 + const id3_byte_t *data=id3_field_getbinarydata(&frame->fields[1],&len);
1.596 + if(data && len>0) {
1.597 + static const char salt[] = { "$1$id3__pic$" };
1.598 +
1.599 + }
1.600 + }
1.601 +}
1.602 +*/
1.603 +
1.604 +// This function was adapted from player.c, from the
1.605 +// libmad distribution
1.606 +
1.607 +void cScanID3::ParseStr(const struct id3_tag *tag, const char *id, char * &data)
1.608 +{
1.609 + const struct id3_frame *frame=id3_tag_findframe(tag,id,0);
1.610 + if(!frame) return;
1.611 +
1.612 + free(data); data=0;
1.613 + const union id3_field *field=&frame->fields[1];
1.614 + if(id3_field_getnstrings(field)>0) {
1.615 + const id3_ucs4_t *ucs4=id3_field_getstrings(field,0);
1.616 + if(!ucs4) return;
1.617 + if(!strcmp(id,ID3_FRAME_GENRE)) ucs4=id3_genre_name(ucs4);
1.618 +
1.619 + id3_latin1_t *latin1=id3_ucs4_latin1duplicate(ucs4);
1.620 + if(!latin1) return;
1.621 +
1.622 + data=strdup((char *)latin1);
1.623 + free(latin1);
1.624 + }
1.625 +}
1.626 +
1.627 +// XING parsing was adapted from the MAD winamp input plugin,
1.628 +// from the libmad distribution
1.629 +
1.630 +#define XING_MAGIC (('X'<<24) | ('i'<<16) | ('n'<<8) | 'g')
1.631 +#define XING_FRAMES 0x0001
1.632 +// #define XING_BYTES 0x0002
1.633 +// #define XING_TOC 0x0004
1.634 +// #define XING_SCALE 0x0008
1.635 +
1.636 +int cScanID3::ParseXing(struct mad_bitptr *ptr, unsigned int bitlen) const
1.637 +{
1.638 + if(bitlen>=64 && mad_bit_read(ptr,32)==XING_MAGIC) {
1.639 + int flags=mad_bit_read(ptr, 32);
1.640 + bitlen-=64;
1.641 + return (bitlen>=32 && (flags & XING_FRAMES)) ? mad_bit_read(ptr,32) : 0;
1.642 + }
1.643 + return -1;
1.644 +}