/* * MP3/MPlayer plugin to VDR (C++) * * (C) 2001-2005 Stefan Huelswitt * * This code 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 code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */ #ifdef TEST_MAIN #define HAVE_SNDFILE #define REMOTE_LIRC #define _GNU_SOURCE #define DEBUG #endif #ifdef HAVE_SNDFILE #include #include #include #include #include #include #include #include #include "common.h" #include "setup-mp3.h" #include "decoder-snd.h" #include "data.h" #include "network.h" //#include "menu-async.h" #include "i18n.h" //#include "version.h" #ifndef SNDFILE_1 #error You must use libsndfile version 1.x.x #endif #define CDFS_TRACK_OFF 150 #define CDFS_PROC "/proc/cdfs" #define CDFS_MARK_ID "CD (discid=%x) contains %d tracks:" #define CDFS_MARK_TR "%*[^[][ %d - %d" #define CDFS_TRACK "track-" #define CDDB_PROTO 5 // used protocol level #define CDDB_TOUT 30*1000 // connection timeout (ms) const char *cddbpath="/var/lib/cddb"; // default local cddb path #define CDDB_DEBUG // debug cddb queries //#define DEBUG_CDFS // debug cdfs parsing //#define GUARD_DEBUG // enable framebuffer guard #if !defined(NO_DEBUG) && defined( DEBUG_CDFS) #define dc(x) { (x); } #else #define dc(x) ; #endif #ifndef TEST_MAIN // --- cSndDecoder ------------------------------------------------------------- #define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t)) cSndDecoder::cSndDecoder(const char *Filename) :cDecoder(Filename) ,file(Filename) ,info(&file) { pcm=0; framebuff=0; playing=ready=false; } cSndDecoder::~cSndDecoder() { Clean(); } bool cSndDecoder::Valid(void) { bool res=false; if(TryLock()) { if(file.Open(false)) res=true; cDecoder::Unlock(); } return res; } cFileInfo *cSndDecoder::FileInfo(void) { cFileInfo *fi=0; if(file.HasInfo()) fi=&file; else if(TryLock()){ if(file.Open()) { fi=&file; file.Close(); } cDecoder::Unlock(); } return fi; } cSongInfo *cSndDecoder::SongInfo(bool get) { cSongInfo *si=0; if(info.HasInfo()) si=&info; else if(get && TryLock()) { if(info.DoScan(false)) si=&info; cDecoder::Unlock(); } return si; } cPlayInfo *cSndDecoder::PlayInfo(void) { if(playing) { pi.Index=index/info.SampleFreq; pi.Total=info.Total; return π } return 0; } void cSndDecoder::Init(void) { Clean(); pcm=new struct mad_pcm; framebuff=MALLOC(int,2*SF_SAMPLES+8); #ifdef GUARD_DEBUG for(int i=0; i<8; i++) framebuff[i+(SF_SAMPLES*2)-4]=0xdeadbeaf; #endif index=0; } bool cSndDecoder::Clean(void) { playing=false; buffMutex.Lock(); run=false; bgCond.Broadcast(); buffMutex.Unlock(); cThread::Cancel(3); buffMutex.Lock(); if(!ready) { deferedN=-1; ready=true; } fgCond.Broadcast(); buffMutex.Unlock(); delete pcm; pcm=0; #ifdef GUARD_DEBUG if(framebuff) { printf("music: decoder-snd: bufferguard"); for(int i=0; i<8; i++) printf(" %08x",framebuff[i+(SF_SAMPLES*2)-4]); printf("\n"); } #endif free(framebuff); framebuff=0; file.Close(); return false; } bool cSndDecoder::Start(void) { cDecoder::Lock(true); Init(); playing=true; if(file.Open() && info.DoScan(true)) { d(printf("music: decoder-snd: open rate=%d frames=%lld channels=%d format=0x%x seek=%d\n", file.sfi.samplerate,file.sfi.frames,file.sfi.channels,file.sfi.format,file.sfi.seekable)) if(file.sfi.channels<=2) { ready=false; run=true; softCount=0; cThread::Start(); cDecoder::Unlock(); return true; } else esyslog("music: ERROR: cannot play sound file %s: more than 2 channels",filename); } cDecoder::Unlock(); return Clean(); } bool cSndDecoder::Stop(void) { cDecoder::Lock(); if(playing) Clean(); cDecoder::Unlock(); return true; } void cSndDecoder::Action(void) { buffMutex.Lock(); while(run) { if(ready) bgCond.Wait(buffMutex); if(!ready) { buffMutex.Unlock(); deferedN=file.Stream(framebuff,SF_SAMPLES); buffMutex.Lock(); ready=true; fgCond.Broadcast(); } } buffMutex.Unlock(); } struct Decode *cSndDecoder::Done(eDecodeStatus status) { ds.status=status; ds.index=index*1000/info.SampleFreq; ds.pcm=pcm; cDecoder::Unlock(); // release the lock from Decode() return &ds; } struct Decode *cSndDecoder::Decode(void) { cDecoder::Lock(); // this is released in Done() if(playing) { cMutexLock lock(&buffMutex); while(!ready) if(!softCount || !fgCond.TimedWait(buffMutex,softCount*5)) { if(softCount<20) softCount++; return Done(dsSoftError); } softCount=0; ready=false; bgCond.Broadcast(); int n=deferedN; if(n<0) return Done(dsError); if(n==0) return Done(dsEof); pcm->samplerate=file.sfi.samplerate; pcm->channels=file.sfi.channels; pcm->length=n; index+=n; int *data=framebuff; mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1]; const int s=(sizeof(int)*8)-1-MAD_F_FRACBITS; // shift value for mad_fixed conversion if(pcm->channels>1) { for(; n>0 ; n--) { *sam0++=(*data++) >> s; *sam1++=(*data++) >> s; } } else { for(; n>0 ; n--) *sam0++=(*data++) >> s; } return Done(dsPlay); } return Done(dsError); } bool cSndDecoder::Skip(int Seconds, float bsecs) { cDecoder::Lock(); bool res=false; if(playing && file.sfi.seekable) { float fsecs=(float)Seconds-bsecs; sf_count_t frames=(sf_count_t)(fsecs*(float)file.sfi.samplerate); sf_count_t newpos=file.Seek(0,true)+frames; if(newpos>file.sfi.frames) newpos=file.sfi.frames-1; if(newpos<0) newpos=0; d(printf("music: decoder-snd: skip: secs=%d fsecs=%f frames=%lld current=%lld new=%lld\n",Seconds,fsecs,frames,file.Seek(0,true),newpos)) buffMutex.Lock(); frames=file.Seek(newpos,false); ready=false; bgCond.Broadcast(); buffMutex.Unlock(); if(frames>=0) { index=frames; #ifdef DEBUG int i=frames/file.sfi.samplerate; printf("music: decoder-snd: skipping to %02d:%02d (frame %lld)\n",i/60,i%60,frames); #endif res=true; } } cDecoder::Unlock(); return res; } #endif //TEST_MAIN // --- cDiscID ------------------------------------------------------------------- class cDiscID { public: int discid, ntrks, nsecs; int *offsets; // cDiscID(void); ~cDiscID(); bool Get(void); }; cDiscID::cDiscID(void) { offsets=0; discid=ntrks=0; } cDiscID::~cDiscID() { delete offsets; } bool cDiscID::Get(void) { bool res=false; FILE *f=fopen(CDFS_PROC,"r"); if(f) { char line[256]; bool state=false; int tr=0; while(fgets(line,sizeof(line),f)) { if(!state) { int id, n; if(sscanf(line,CDFS_MARK_ID,&id,&n)==2) { d(printf("music: decoder-snd: discid: found id=%08x n=%d\n",id,n)) if(discid==id && ntrks==n) { res=true; break; } else { discid=id; ntrks=n; delete offsets; offsets=new int[ntrks]; state=true; } } } else { int off, end; if(sscanf(line,CDFS_MARK_TR,&off,&end)==2) { dc(printf("music: decoder-snd: discid: found offset=%d end=%d for track %d\n",off,end,tr+1)) offsets[tr]=off+CDFS_TRACK_OFF; if(++tr==ntrks) { nsecs=(end+1)/75; dc(printf("music: decoder-snd: discid: nsecs=%d / 0x%x\n",nsecs,nsecs)) res=true; break; } } } } fclose(f); } return res; } // --- cCDDBSong --------------------------------------------------------------- // The CDDB code is loosely based on the implementation in mp3c 0.27 which is // (C) 1999-2001 WSPse, Matthias Hensler, class cCDDBSong : public cListObject { public: cCDDBSong(void); ~cCDDBSong(); // int Track; char *TTitle, *ExtT; char *Title, *Artist, *Genre; }; cCDDBSong::cCDDBSong(void) { Title=Artist=Genre=0; TTitle=ExtT=0; } cCDDBSong::~cCDDBSong() { free(Title); free(Artist); free(Genre); free(TTitle); free(ExtT); } // --- cCDDBDisc --------------------------------------------------------------- const char *sampler[] = { // some artist names to identify sampler discs "various", "varios", "variété", "compilation", "sampler", "mixed", "divers", "v.a.", "VA", "misc", "none", 0 }; class cCDDBDisc : public cList { private: int DiscID; // bool isSampler; char *DTitle, *ExtD; // cCDDBSong *GetTrack(const char *name, unsigned int pos); cCDDBSong *FindTrack(int tr); void Strcat(char * &store, char *value); bool Split(const char *source, char div, char * &first, char * &second, bool only3=false); void Put(const char *from, char * &to); void Clean(void); public: cCDDBDisc(void); ~cCDDBDisc(); bool Load(cDiscID *id, const char *filename); bool Cached(cDiscID *id) { return DiscID==id->discid; } bool TrackInfo(int tr, cSongInfo *si); // char *Album, *Artist, *Genre; int Year; }; cCDDBDisc::cCDDBDisc(void) { Album=Artist=Genre=0; DTitle=ExtD=0; DiscID=0; } cCDDBDisc::~cCDDBDisc() { Clean(); } void cCDDBDisc::Clean(void) { free(DTitle); DTitle=0; free(ExtD); ExtD=0; free(Artist); Artist=0; free(Album); Album=0; free(Genre); Genre=0; Year=-1; DiscID=0; isSampler=false; } bool cCDDBDisc::TrackInfo(int tr, cSongInfo *si) { cCDDBSong *s=FindTrack(tr); if(s) { Put(s->Title,si->Title); if(s->Artist) Put(s->Artist,si->Artist); else Put(Artist,si->Artist); Put(Album,si->Album); if(s->Genre) Put(s->Genre, si->Genre); if(Year>0) si->Year=Year; return true; } return false; } void cCDDBDisc::Put(const char *from, char * &to) { free(to); to=from ? strdup(from):0; } bool cCDDBDisc::Load(cDiscID *id, const char *filename) { char *p; Clean(); Clear(); d(printf("music: decoder-snd: cddb: loading discid %08x from %s\n",id->discid,filename)) DiscID=id->discid; FILE *f=fopen(filename,"r"); if(f) { char buff[1024]; while(fgets(buff,sizeof(buff),f)) { int i=strlen(buff); while(i && (buff[i-1]=='\n' || buff[i-1]=='\r')) buff[--i]=0; if(buff[0]=='#') { // special comment line handling } else { p=strchr(buff,'='); if(p) { *p=0; char *name =compactspace(buff); char *value=compactspace(p+1); if(*name && *value) { if(!strcasecmp(name,"DTITLE")) Strcat(DTitle,value); else if(!strcasecmp(name,"EXTD")) Strcat(ExtD,value); else if(!strcasecmp(name,"DGENRE")) Strcat(Genre,value); else if(!strcasecmp(name,"DYEAR")) Year=atoi(value); else if(!strncasecmp(name,"TTITLE",6)) { cCDDBSong *s=GetTrack(name,6); if(s) Strcat(s->TTitle,value); } else if(!strncasecmp(name,"EXTT",4)) { cCDDBSong *s=GetTrack(name,4); if(s) Strcat(s->ExtT,value); } } } } } fclose(f); // read all data, now post-processing if(Count()>0) { if(DTitle) { if(Split(DTitle,'/',Artist,Album)) { for(int n=0 ; sampler[n] ; n++) if(!strncasecmp(Artist,sampler[n],strlen(sampler[n]))) { isSampler=true; break; } } else { Album=strdup(DTitle); isSampler=true; } } d(printf("music: decoder-snd: cddb: found artist='%s' album='%s' genre='%s' isSampler=%d\n",Artist,Album,Genre,isSampler)) free(DTitle); DTitle=0; if(!isSampler && Artist && Album && !strncmp(Album,Artist,strlen(Artist))) { d(printf("music: decoder-snd: cddb: detecting sampler from Artist==Album\n")) isSampler=true; } if(!isSampler) { int nofail1=0, nofail2=0; cCDDBSong *s=First(); while(s) { if(s->TTitle) { if(strstr(s->TTitle," / ")) nofail1++; //if(strstr(s->TTitle," - ")) nofail2++; } s=Next(s); } if(nofail1==Count() || nofail2==Count()) { d(printf("music: decoder-snd: cddb: detecting sampler from nofail\n")) isSampler=true; } } if(Year<0 && ExtD && (p=strstr(ExtD,"YEAR:"))) Year=atoi(p+5); free(ExtD); ExtD=0; d(printf("music: decoder-snd: cddb: found year=%d\n",Year)) cCDDBSong *s=First(); while(s) { if(s->TTitle) { if(isSampler) { if(!Split(s->TTitle,'/',s->Artist,s->Title) && !Split(s->TTitle,'-',s->Artist,s->Title,true)) { s->Title=compactspace(strdup(s->TTitle)); if(s->ExtT) s->Artist=compactspace(strdup(s->ExtT)); } } else { s->Title=compactspace(strdup(s->TTitle)); if(Artist) s->Artist=strdup(Artist); } } else s->Title=strdup(tr("Unknown")); free(s->TTitle); s->TTitle=0; free(s->ExtT); s->ExtT=0; d(printf("music: decoder-snd: cddb: found track %d title='%s' artist='%s'\n",s->Track,s->Title,s->Artist)) s=Next(s); } return true; } } return false; } bool cCDDBDisc::Split(const char *source, char div, char * &first, char * &second, bool only3) { int pos=-1, n=0; const char *p, l[4]={ ' ',div,' ',0 }; if ((p=strstr(source,l))) { pos=p-source; n=3; } else if(!only3 && (p=strchr(source,div))) { pos=p-source; n=1; } if(pos>=0) { free(first); first=strdup(source); first[pos]=0; compactspace(first); free(second); second=strdup(source+pos+n); compactspace(second); return true; } return false; } void cCDDBDisc::Strcat(char * &store, char *value) { if(store) { char *n=MALLOC(char,strlen(store)+strlen(value)+1); if(n) { strcpy(n,store); strcat(n,value); free(store); store=n; } } else store=strdup(value); } cCDDBSong *cCDDBDisc::GetTrack(const char *name, unsigned int pos) { cCDDBSong *s=0; if(strlen(name)>pos) { int tr=atoi(&name[pos]); s=FindTrack(tr); if(!s) { s=new cCDDBSong; Add(s); s->Track=tr; } } return s; } cCDDBSong *cCDDBDisc::FindTrack(int tr) { cCDDBSong *s=First(); while(s) { if(s->Track==tr) break; s=Next(s); } return s; } #ifndef TEST_MAIN // --- cCDDB ------------------------------------------------------------------- class cCDDB : public cScanDir, cMutex { private: cCDDBDisc cache; cFileSource *src; cFileObj *file; cNet *net; char searchID[10], cddbstr[256]; // virtual void DoItem(cFileSource *src, const char *subdir, const char *name); bool LocalQuery(cDiscID *id); bool RemoteGet(cDiscID *id); bool GetLine(char *buff, int size, bool log=true); int GetCddbResponse(void); int DoCddbCmd(const char *format, ...); public: cCDDB(void); virtual ~cCDDB(); bool Lookup(cDiscID *id, int track, cSongInfo *si); }; cCDDB cddb; cCDDB::cCDDB(void) { src=0; file=0; net=0; } cCDDB::~cCDDB() { delete file; delete src; delete net; } bool cCDDB::Lookup(cDiscID *id, int track, cSongInfo *si) { bool res=false; Lock(); if(!cache.Cached(id)) { if(LocalQuery(id) || (MP3Setup.UseCddb>1 && RemoteGet(id) && LocalQuery(id))) cache.Load(id,file->FullPath()); } if(cache.Cached(id) && cache.TrackInfo(track,si)) res=true; Unlock(); return res; } bool cCDDB::LocalQuery(cDiscID *id) { bool res=false; delete file; file=0; if(!src) src=new cFileSource(cddbpath,"CDDB database",false); if(src) { snprintf(searchID,sizeof(searchID),"%08x",id->discid); if(ScanDir(src,0,stDir,0,0,false) && file) res=true; } return res; } void cCDDB::DoItem(cFileSource *src, const char *subdir, const char *name) { if(!file) { file=new cFileObj(src,name,searchID,otFile); if(access(file->FullPath(),R_OK)) { delete file; file=0; } } } bool cCDDB::RemoteGet(cDiscID *id) { bool res=false; MP3Setup.ShowStatus = 2; delete net; net=new cNet(16*1024,CDDB_TOUT,CDDB_TOUT); if(net->Connect(MP3Setup.CddbHost,MP3Setup.CddbPort)) { int code=GetCddbResponse(); if(code/100==2) { const char *host=getenv("HOSTNAME"); if(!host) host="unknown"; const char *user=getenv("USER"); if(!user) user="nobody"; code=DoCddbCmd("cddb hello %s %s %s %s\n",user,host,"vdr-plugin-music", "0.3.0-rc2"); if(code/100==2) { code=DoCddbCmd("proto %d\n",CDDB_PROTO); if(code>0) { char off[1024]; off[0]=0; for(int i=0 ; intrks ; i++) sprintf(&off[strlen(off)]," %d",id->offsets[i]); code=DoCddbCmd("cddb query %08x %d %s %d\n",id->discid,id->ntrks,off,id->nsecs); if(code/100==2) { char *cat=0; if(code==200) cat=strdup(cddbstr); else if(code==210) { if(GetLine(off,sizeof(off))) { cat=strdup(off); while(GetLine(off,sizeof(off)) && off[0]!='.'); } } if(cat) { char *s=index(cat,' '); if(s) *s=0; code=DoCddbCmd("cddb read %s %08x\n",cat,id->discid); if(code==210) { char *name=0; asprintf(&name,"%s/%s/%08x",cddbpath,cat,id->discid); if(MakeDirs(name,false)) { FILE *out=fopen(name,"w"); if(out) { while(GetLine(off,sizeof(off),false) && off[0]!='.') fputs(off,out); fclose(out); res=true; } else esyslog("music: fopen() failed: %s",strerror(errno)); } free(name); } else if(code>0) esyslog("music: server read error: %d %s",code,cddbstr); free(cat); } } else if(code>0) esyslog("music: server query error: %d %s",code,cddbstr); } else esyslog("music: server proto error: %d %s",code,cddbstr); } else if(code>0) esyslog("music: server hello error: %d %s",code,cddbstr); } else if(code>0) esyslog("music: server sign-on error: %d %s",code,cddbstr); } delete net; net=0; MP3Setup.ShowStatus = 0; return res; } bool cCDDB::GetLine(char *buff, int size, bool log) { if(net->Gets(buff,size)>0) { #ifdef CDDB_DEBUG if(log) printf("music: decoder-snd: cddb: <- %s",buff); #endif return true; } return false; } int cCDDB::GetCddbResponse(void) { char buf[1024]; if(GetLine(buf,sizeof(buf))) { int code; if(sscanf(buf,"%d %255[^\n]",&code,cddbstr)==2) return code; else esyslog("music: Unexpected server response: %s",buf); } return -1; } int cCDDB::DoCddbCmd(const char *format, ...) { va_list ap; va_start(ap,format); char *buff=0; vasprintf(&buff,format,ap); va_end(ap); #ifdef CDDB_DEBUG printf("music: decoder-snd: cddb: -> %s",buff); #endif int r=net->Puts(buff); free(buff); if(r<0) return -1; return GetCddbResponse(); } // --- cSndInfo ---------------------------------------------------------------- cSndInfo::cSndInfo(cSndFile *File) { file=File; id=new cDiscID; } cSndInfo::~cSndInfo() { delete id; } bool cSndInfo::Abort(bool result) { if(!keepOpen) file->Close(); return result; } bool cSndInfo::DoScan(bool KeepOpen) { keepOpen=KeepOpen; if(!file->Open()) return Abort(false); if(HasInfo()) return Abort(true); // check the infocache cCacheData *dat=InfoCache.Search(file); if(dat) { Set(dat); dat->Unlock(); if(!DecoderID) { DecoderID=DEC_SND; InfoCache.Cache(this,file); } return Abort(true); } Clear(); if(file->FsType!=CDFS_MAGIC || !MP3Setup.UseCddb || !CDDBLookup(file->Filename)) // FakeTitle(file->Filename); std::string TITLE; std::string ARTIST; std::string COMMENT; std::string YEAR; // TITLE = sf_get_string(file->sf, SF_STR_TITLE); // ARTIST = sf_get_string(file->sf, SF_STR_ARTIST); // COMMENT = sf_get_string(file->sf, SF_STR_COMMENT); // YEAR = sf_get_string(file->sf, SF_STR_DATE); // printf("FLAC-TITLE = '%s'\n", TITLE.c_str()); // printf("FLAC-ARTIST = '%s'\n", ARTIST.c_str()); // printf("FLAC-COMMENT = '%s'\n", COMMENT.c_str()); // printf("FLAC-YEAR = '%s'\n", YEAR.c_str()); if(sf_get_string(file->sf, SF_STR_TITLE)) printf("FLAC-TITLE = '%s'\n", sf_get_string(file->sf, SF_STR_TITLE)); if(sf_get_string(file->sf, SF_STR_ARTIST)) printf("FLAC-ARTIST = '%s'\n", sf_get_string(file->sf, SF_STR_ARTIST)); if(sf_get_string(file->sf, SF_STR_ALBUM)) printf("FLAC-ALBUM = '%s'\n", sf_get_string(file->sf, SF_STR_ALBUM)); if(sf_get_string(file->sf, SF_STR_COMMENT)) printf("FLAC-COMMENT = '%s'\n", sf_get_string(file->sf, SF_STR_COMMENT)); if(sf_get_string(file->sf, SF_STR_DATE)) printf("FLAC-YEAR = '%s'\n", sf_get_string(file->sf, SF_STR_DATE)); if(sf_get_string(file->sf, SF_STR_TITLE)) Title = strdup(sf_get_string(file->sf, SF_STR_TITLE)); else FakeTitle(file->Filename); if(sf_get_string(file->sf, SF_STR_ARTIST)) Artist = strdup(sf_get_string(file->sf, SF_STR_ARTIST)); if(sf_get_string(file->sf, SF_STR_ALBUM)) Album = strdup(sf_get_string(file->sf, SF_STR_ALBUM)); if(sf_get_string(file->sf, SF_STR_COMMENT)) Comment = strdup(sf_get_string(file->sf, SF_STR_COMMENT)); if(sf_get_string(file->sf, SF_STR_DATE)) Year = atoi(sf_get_string(file->sf, SF_STR_DATE)); Frames=file->sfi.frames; SampleFreq=file->sfi.samplerate; Channels=file->sfi.channels; ChMode=Channels>1 ? 3:0; Total=Frames/SampleFreq; Bitrate=file->Filesize*8/Total; //XXX SampleFreq*Channels*file->sfi.pcmbitwidth; DecoderID=DEC_SND; InfoDone(); InfoCache.Cache(this,file); return Abort(true); } bool cSndInfo::CDDBLookup(const char *filename) { if(id->Get()) { int tr; const char *s=strstr(filename,CDFS_TRACK); if(s && sscanf(s+strlen(CDFS_TRACK),"%d",&tr)==1) { d(printf("music: decoder-snd: looking up disc id %08x track %d\n",id->discid,tr)) return cddb.Lookup(id,tr-1,this); } } return false; } // --- cSndFile ---------------------------------------------------------------- cSndFile::cSndFile(const char *Filename) :cFileInfo(Filename) { sf=0; } cSndFile::~cSndFile() { Close(); } bool cSndFile::Open(bool log) { if(sf) return (Seek()>=0); if(FileInfo(log)) { sf=sf_open(Filename,SFM_READ,&sfi); if(!sf && log) Error("open"); } return (sf!=0); } void cSndFile::Close(void) { if(sf) { sf_close(sf); sf=0; } } void cSndFile::Error(const char *action) { char buff[128]; sf_error_str(sf,buff,sizeof(buff)); esyslog("music: ERROR: sndfile %s failed on %s: %s",action,Filename,buff); } sf_count_t cSndFile::Seek(sf_count_t frames, bool relativ) { int dir=SEEK_CUR; if(!relativ) dir=SEEK_SET; int n=sf_seek(sf,frames,dir); if(n<0) Error("seek"); return n; } sf_count_t cSndFile::Stream(int *buffer, sf_count_t frames) { sf_count_t n=sf_readf_int(sf,buffer,frames); if(n<0) Error("read"); return n; } #endif //TEST_MAIN #endif //HAVE_SNDFILE #ifdef TEST_MAIN // // to compile: // g++ -g -DTEST_MAIN -o test mp3-decoder-snd.c tools.o thread.o -lpthread // // calling: // test // extern const char *tr(const char *test) { return test; } int main (int argc, char *argv[]) { cCDDBDisc cddb; cddb.Load(1,argv[1]); return 0; } #endif