2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
6 * This code is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This code is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
24 #include <sys/ioctl.h>
27 #include <sys/soundcard.h>
33 #include <vdr/player.h>
34 #include <vdr/ringbuffer.h>
35 #include <vdr/thread.h>
36 #include <vdr/tools.h>
39 #include "setup-mp3.h"
40 #include "player-mp3.h"
43 #include "decoder-core.h"
46 //#define DEBUG_MODE // debug playmode changes
47 #define DEBUG_BGR // debug backround scan thread
48 #define DEBUG_DELAY 300 // debug write/decode delays
49 //#define ACC_DUMP // dump limiter lookup table to /tmp/limiter
52 #if !defined(NO_DEBUG) && defined(DEBUG_MODE)
53 #define dm(x) { (x); }
58 #if !defined(NO_DEBUG) && defined(DEBUG_BGR)
59 #define db(x) { (x); }
64 // ----------------------------------------------------------------
66 #define MP3BUFSIZE (1024*1024) // output ringbuffer size
67 #define OUT_BITS 16 // output 16 bit samples to DVB driver
68 #define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes
70 #define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame
72 #define MIN_GAIN 0.03 // min. gain required to launch the normalizer
73 #define MAX_GAIN 3.0 // max. allowed gain
74 #define USE_FAST_LIMITER
75 #define LIM_ACC 12 // bit, accuracy for lookup table
76 #define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1) // max. value covered by lookup table
77 #define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup
78 #define F_LIM_JMP (mad_fixed_t)(1<<LIM_SHIFT) // lookup table jump between values
80 #define POW_WIN 100 // window width for smoothing power values
81 #define EPSILON 0.00000000001 // anything less than EPSILON is considered zero
83 // --- cResample ------------------------------------------------------------
85 // The resample code has been adapted from the madplay project
86 // (resample.c) found in the libmad distribution
93 mad_fixed_t resampled[MAX_NSAMPLES];
95 bool SetInputRate(unsigned int oldrate, unsigned int newrate);
96 unsigned int ResampleBlock(unsigned int nsamples, const mad_fixed_t *old);
97 const mad_fixed_t *Resampled(void) { return resampled; }
100 bool cResample::SetInputRate(unsigned int oldrate, unsigned int newrate)
102 if(oldrate<8000 || oldrate>newrate*6) { // out of range
103 esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6);
106 ratio=mad_f_tofixed((double)oldrate/(double)newrate);
109 static mad_fixed_t oldratio=0;
110 if(oldratio!=ratio) {
111 printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate);
115 return ratio!=MAD_F_ONE;
118 unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old)
120 // This resampling algorithm is based on a linear interpolation, which is
121 // not at all the best sounding but is relatively fast and efficient.
123 // A better algorithm would be one that implements a bandlimited
126 mad_fixed_t *nsam=resampled;
127 const mad_fixed_t *end=old+nsamples;
128 const mad_fixed_t *begin=nsam;
131 step = mad_f_fracpart(-step);
133 while (step < MAD_F_ONE) {
134 *nsam++ = step ? last+mad_f_mul(*old-last,step) : last;
136 if(((step + 0x00000080L) & 0x0fffff00L) == 0)
137 step = (step + 0x00000080L) & ~0x0fffffffL;
142 while (end - old > 1 + mad_f_intpart(step)) {
143 old += mad_f_intpart(step);
144 step = mad_f_fracpart(step);
145 *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old;
147 if (((step + 0x00000080L) & 0x0fffff00L) == 0)
148 step = (step + 0x00000080L) & ~0x0fffffffL;
151 if (end - old == 1 + mad_f_intpart(step)) {
155 else step -= mad_f_fromint(end - old);
160 // --- cLevel ----------------------------------------------------------------
162 // The normalize algorithm and parts of the code has been adapted from the
163 // Normalize 0.7 project. (C) 1999-2002, Chris Vaill <cvaill@cs.columbia.edu>
165 // A little background on how normalize computes the volume
166 // of a wav file, in case you want to know just how your
167 // files are being munged:
169 // The volumes calculated are RMS amplitudes, which corre
170 // spond (roughly) to perceived volume. Taking the RMS ampli
171 // tude of an entire file would not give us quite the measure
172 // we want, though, because a quiet song punctuated by short
173 // loud parts would average out to a quiet song, and the
174 // adjustment we would compute would make the loud parts
177 // What we want is to consider the maximum volume of the
178 // file, and normalize according to that. We break up the
179 // signal into 100 chunks per second, and get the signal
180 // power of each chunk, in order to get an estimation of
181 // "instantaneous power" over time. This "instantaneous
182 // power" signal varies too much to get a good measure of the
183 // original signal's maximum sustained power, so we run a
184 // smoothing algorithm over the power signal (specifically, a
185 // mean filter with a window width of 100 elements). The max
186 // imum point of the smoothed power signal turns out to be a
187 // good measure of the maximum sustained power of the file.
188 // We can then take the square root of the power to get maxi
189 // mum sustained RMS amplitude.
198 double powsum, pows[POW_WIN];
204 inline void AddPower(struct Power *p, double pow);
207 void GetPower(struct mad_pcm *pcm);
208 double GetLevel(void);
209 double GetPeak(void);
212 void cLevel::Init(void)
214 for(int l=0 ; l<2 ; l++) {
215 struct Power *p=&power[l];
216 p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0;
217 for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0;
222 void cLevel::GetPower(struct mad_pcm *pcm)
224 for(int i=0 ; i<pcm->channels ; i++) {
225 struct Power *p=&power[i];
226 mad_fixed_t *data=pcm->samples[i];
227 for(int n=pcm->length ; n>0 ; n--) {
228 if(*data < -peak) peak = -*data;
229 if(*data > peak) peak = *data;
230 double s=mad_f_todouble(*data++);
232 if(++(p->nsum)>=pcm->samplerate/100) {
233 AddPower(p,p->sum/(double)p->nsum);
234 p->sum=0.0; p->nsum=0;
240 void cLevel::AddPower(struct Power *p, double pow)
243 if(p->npow>=POW_WIN) {
244 if(p->powsum>maxpow) maxpow=p->powsum;
245 p->powsum-=p->pows[p->wpow];
248 p->pows[p->wpow]=pow;
249 p->wpow=(p->wpow+1) % POW_WIN;
252 double cLevel::GetLevel(void)
255 // Either this whole file has zero power, or was too short to ever
256 // fill the smoothing buffer. In the latter case, we need to just
257 // get maxpow from whatever data we did collect.
259 if(power[0].powsum>maxpow) maxpow=power[0].powsum;
260 if(power[1].powsum>maxpow) maxpow=power[1].powsum;
262 double level=sqrt(maxpow/(double)POW_WIN); // adjust for the smoothing window size and root
263 d(printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak)))
267 double cLevel::GetPeak(void)
269 return mad_f_todouble(peak);
272 // --- cNormalize ------------------------------------------------------------
277 double d_limlvl, one_limlvl;
279 bool dogain, dolimit;
282 unsigned long limited, clipped, total;
286 #ifdef USE_FAST_LIMITER
287 mad_fixed_t *table, tablestart;
289 inline mad_fixed_t FastLimiter(mad_fixed_t x);
291 inline mad_fixed_t Limiter(mad_fixed_t x);
295 void Init(double Level, double Peak);
297 void AddGain(struct mad_pcm *pcm);
300 cNormalize::cNormalize(void)
302 d_limlvl=(double)MP3Setup.LimiterLevel/100.0;
303 one_limlvl=1-d_limlvl;
304 limlvl=mad_f_tofixed(d_limlvl);
305 d(printf("norm: lim_lev=%f lim_acc=%d\n",d_limlvl,LIM_ACC))
307 #ifdef USE_FAST_LIMITER
308 mad_fixed_t start=limlvl & ~(F_LIM_JMP-1);
310 tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2;
311 table=new mad_fixed_t[tablesize];
313 d(printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP))
314 for(int i=0 ; i<tablesize ; i++) {
315 table[i]=Limiter(start);
318 tablesize--; // avoid a -1 in FastLimiter()
320 // do a quick accuracy check, just to be sure that FastLimiter() is working
323 FILE *out=fopen("/tmp/limiter","w");
325 mad_fixed_t maxdiff=0;
326 for(mad_fixed_t x=F_LIM_MAX ; x>=limlvl ; x-=mad_f_tofixed(1e-4)) {
327 mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x));
328 if(diff>maxdiff) maxdiff=diff;
330 fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n",
331 mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff));
332 if(ferror(out)) break;
338 d(printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff)))
339 if(mad_f_todouble(maxdiff)>1e-6) {
340 esyslog("ERROR: accuracy check failed, normalizer disabled");
341 delete table; table=0;
344 else esyslog("ERROR: no memory for lookup table, normalizer disabled");
345 #endif // USE_FAST_LIMITER
348 cNormalize::~cNormalize()
350 #ifdef USE_FAST_LIMITER
355 void cNormalize::Init(double Level, double Peak)
357 double Target=(double)MP3Setup.TargetLevel/100.0;
358 double dgain=Target/Level;
359 if(dgain>MAX_GAIN) dgain=MAX_GAIN;
360 gain=mad_f_tofixed(dgain);
361 // Check if we actually need to apply a gain
362 dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN);
363 #ifdef USE_FAST_LIMITER
364 if(!table) dogain=false;
366 // Check if we actually need to do limiting:
367 // we have to if limiter is enabled, if gain>1 and if the peaks will clip.
368 dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0);
370 printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak);
371 limited=clipped=total=0; peak=0;
375 void cNormalize::Stats(void)
379 printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n",
380 total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak));
384 mad_fixed_t cNormalize::Limiter(mad_fixed_t x)
388 // / x (for x <= lev)
390 // \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev)
392 // call only with x>=0. For negative samples, preserve sign outside this function
394 // With limiter level = 0, this is equivalent to a tanh() function;
395 // with limiter level = 1, this is equivalent to clipping.
399 if(x>MAD_F_ONE) clipped++;
402 x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl);
407 #ifdef USE_FAST_LIMITER
408 mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x)
410 // The fast algorithm is based on a linear interpolation between the
411 // the values in the lookup table. Relays heavly on libmads fixed point format.
414 int i=(unsigned int)(x-tablestart)/F_LIM_JMP;
416 if(x>MAD_F_ONE) clipped++;
418 if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n",
419 mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize);
421 mad_fixed_t r=x & (F_LIM_JMP-1);
424 mad_fixed_t *ptr=&table[i];
426 mad_fixed_t d=*(ptr+1)-x;
427 //x+=mad_f_mul(d,r)<<LIM_ACC; // this is not accurate as mad_f_mul() does >>MAD_F_FRACBITS
428 // which is senseless in the case of following <<LIM_ACC.
429 x+=((long long)d*(long long)r)>>LIM_SHIFT; // better, don't know if works on all machines
436 #ifdef USE_FAST_LIMITER
437 #define LIMITER_FUNC FastLimiter
439 #define LIMITER_FUNC Limiter
442 void cNormalize::AddGain(struct mad_pcm *pcm)
445 for(int i=0 ; i<pcm->channels ; i++) {
446 mad_fixed_t *data=pcm->samples[i];
451 for(int n=pcm->length ; n>0 ; n--) {
452 mad_fixed_t s=mad_f_mul(*data,gain);
471 for(int n=pcm->length ; n>0 ; n--) {
472 mad_fixed_t s=mad_f_mul(*data,gain);
475 else if(-s>peak) peak=-s;
477 if(s>MAD_F_ONE) s=MAD_F_ONE; // do clipping
478 if(s<-MAD_F_ONE) s=-MAD_F_ONE;
486 // --- cScale ----------------------------------------------------------------
488 // The dither code has been adapted from the madplay project
489 // (audio.c) found in the libmad distribution
491 enum eAudioMode { amRoundBE, amDitherBE, amRoundLE, amDitherLE };
495 enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 };
498 unsigned long clipped_samples;
499 mad_fixed_t peak_clipping;
500 mad_fixed_t peak_sample;
504 mad_fixed_t error[3];
508 inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true);
509 inline unsigned long Prng(unsigned long state);
510 signed long LinearRound(mad_fixed_t sample);
511 signed long LinearDither(mad_fixed_t sample, struct dither *dither);
515 unsigned int ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode);
518 void cScale::Init(void)
521 clipped_samples=0; peak_clipping=peak_sample=0;
523 memset(&leftD,0,sizeof(leftD));
524 memset(&rightD,0,sizeof(rightD));
527 void cScale::Stats(void)
530 printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n",
531 clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample));
535 // gather signal statistics while clipping
536 mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats)
539 if (sample > MAX) sample = MAX;
540 if (sample < MIN) sample = MIN;
543 if (sample > MAX) sample = MAX;
544 if (sample < MIN) sample = MIN;
547 if (sample >= peak_sample) {
550 if (sample - MAX > peak_clipping)
551 peak_clipping = sample - MAX;
554 peak_sample = sample;
556 else if (sample < -peak_sample) {
559 if (MIN - sample > peak_clipping)
560 peak_clipping = MIN - sample;
563 peak_sample = -sample;
570 // generic linear sample quantize routine
571 signed long cScale::LinearRound(mad_fixed_t sample)
574 sample += (1L << (MAD_F_FRACBITS - OUT_BITS));
577 // quantize and scale
578 return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS);
581 // 32-bit pseudo-random number generator
582 unsigned long cScale::Prng(unsigned long state)
584 return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
587 // generic linear sample quantize and dither routine
588 signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither)
591 sample += dither->error[0] - dither->error[1] + dither->error[2];
592 dither->error[2] = dither->error[1];
593 dither->error[1] = dither->error[0] / 2;
595 mad_fixed_t output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1));
596 const int scalebits = MAD_F_FRACBITS + 1 - OUT_BITS;
597 const mad_fixed_t mask = (1L << scalebits) - 1;
599 const mad_fixed_t random = Prng(dither->random);
600 output += (random & mask) - (dither->random & mask);
601 dither->random = random;
604 sample=Clip(sample,false);
608 dither->error[0] = sample - output;
610 return output >> scalebits;
613 #define PUT_BE(data,sample) { *data++=(sample)>>8; *data++=(sample)>>0; }
614 #define PUT_LE(data,sample) { *data++=(sample)>>0; *data++=(sample)>>8; }
616 // write a block of signed 16-bit PCM samples
617 unsigned int cScale::ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode)
619 unsigned int len=size/OUT_FACT;
620 if(len>nsamples) { len=nsamples; size=len*OUT_FACT; }
625 signed int sample=LinearRound(*left++);
627 if(right) sample=LinearRound(*right++);
633 signed int sample=LinearDither(*left++,&leftD);
635 if(right) sample=LinearDither(*right++,&rightD);
641 signed int sample=LinearRound(*left++);
643 if(right) sample=LinearRound(*right++);
649 signed int sample=LinearDither(*left++,&leftD);
651 if(right) sample=LinearDither(*right++,&rightD);
659 // --- cShuffle ----------------------------------------------------------------
670 void Shuffle(int num, int curr);
676 int Goto(int pos, int curr);
679 cShuffle::cShuffle(void)
685 cShuffle::~cShuffle(void)
690 void cShuffle::Flush(void)
692 delete shuffle; shuffle=0;
696 int cShuffle::Index(int pos)
699 for(int i=0; i<max; i++) if(shuffle[i]==pos) return i;
703 void cShuffle::Shuffle(int num, int curr)
707 int *ns=new int[num];
710 memcpy(ns,shuffle,max*sizeof(int));
718 for(int i=oldmax ; i<max ; i++) shuffle[i]=i;
720 int in=Index(curr)+1; if(in<0) in=0;
722 for(int i=in ; i<max ; i++) {
723 int ran=(rand_r(&seed) % ((max-in)*4-4))/4; ran+=((ran+in) >= i);
725 shuffle[i]=shuffle[ran+in];
730 printf("shuffle: order (%d , %d -> %d) ",num,curr,in);
731 for(int i=0 ; i<max ; i++) printf("%d ",shuffle[i]);
736 void cShuffle::Del(int pos)
740 if(i+1<max) memmove(&shuffle[i],&shuffle[i+1],(max-i-1)*sizeof(int));
745 int cShuffle::First(void)
750 int cShuffle::Next(int curr)
753 return (i>=0 && i+1<max) ? shuffle[i+1] : -1;
756 int cShuffle::Prev(int curr)
759 return (i>0) ? shuffle[i-1] : -1;
762 int cShuffle::Goto(int pos, int curr)
768 for(int l=g; l<i; l++) shuffle[l]=shuffle[l+1];
772 for(int l=g; l>i+1; l--) shuffle[l]=shuffle[l-1];
776 printf("shuffle: goto order (%d -> %d , %d -> %d) ",pos,g,curr,i);
777 for(int i=0 ; i<max ; i++) printf("%d ",shuffle[i]);
785 // --- cPlayManager ------------------------------------------------------------
787 #define SCANNED_ID3 1
788 #define SCANNED_LVL 2
792 cPlayManager::cPlayManager(void)
794 curr=0; currIndex=-1;
795 scan=0; stopscan=throttle=pass2=release=false;
796 play=0; playNew=eol=false;
797 shuffle=new cShuffle;
798 loopMode=(MP3Setup.InitLoopMode>0);
799 shuffleMode=(MP3Setup.InitShuffleMode>0);
802 cPlayManager::~cPlayManager()
807 stopscan=true; bgCond.Broadcast();
813 void cPlayManager::ThrottleWait(void)
815 while(!stopscan && !release && throttle) {
816 db(printf("mgr: background scan throttled\n"))
817 bgCond.Wait(listMutex);
818 db(printf("mgr: background scan throttle wakeup\n"))
822 void cPlayManager::Action(void)
824 db(printf("mgr: background scan thread started (pid=%d)\n", getpid()))
828 for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) {
831 if(!(scan->user & SCANNED_ID3)) {
832 db(printf("mgr: scanning (id3) %s\n",scan->Name()))
833 cSongInfo *si=scan->Info(true);
834 if(si && si->Level>0.0) scan->user|=SCANNED_LVL;
835 scan->user|=SCANNED_ID3;
839 if(MP3Setup.BgrScan>1) {
841 for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) {
842 if(scan==curr) continue;
845 if(!(scan->user & SCANNED_LVL)) {
846 cDecoder *dec=scan->Decoder();
848 cSongInfo *si=scan->Info(false);
849 if(!dec->IsStream() && (!si || si->Level<=0.0) && dec->Start()) {
850 db(printf("mgr: scanning (lvl) %s\n",scan->Name()))
854 while(go && !release) {
856 listMutex.Lock(); ThrottleWait(); listMutex.Unlock();
859 struct Decode *ds=dec->Decode();
862 level.GetPower(ds->pcm);
869 double l=level.GetLevel();
871 cSongInfo *si=dec->SongInfo(false);
872 cFileInfo *fi=dec->FileInfo();
875 si->Peak=level.GetPeak();
876 InfoCache.Cache(si,fi);
883 scan->user|=SCANNED_LVL;
889 else scan->user|=SCANNED_LVL;
898 scan=0; release=false; fgCond.Broadcast();
899 db(printf("mgr: background scan idle\n"))
900 bgCond.Wait(listMutex);
901 db(printf("mgr: background scan idle wakeup\n"))
902 } while(!stopscan && (release || throttle));
905 db(printf("mgr: background scan thread ended (pid=%d)\n", getpid()))
908 void cPlayManager::Throttle(bool thr)
910 if(MP3Setup.BgrScan) {
911 if(!thr && throttle) {
912 db(printf("mgr: bgr-scan -> run (%llu)\n",cTimeMs::Now()))
914 throttle=false; bgCond.Broadcast();
917 if(thr && !throttle) {
918 db(printf("mgr: bgr-scan -> throttle (%llu)\n",cTimeMs::Now()))
924 void cPlayManager::ToggleShuffle(void)
926 shuffleMode=!shuffleMode;
927 d(printf("mgr: shuffle mode toggled : %d\n",shuffleMode))
928 if(shuffleMode && !eol) {
929 curr=0; currIndex=-1;
930 shuffle->Shuffle(maxIndex+1,-1);
935 void cPlayManager::ToggleLoop(void)
938 d(printf("mgr: loop mode toggled : %d\n",loopMode))
941 bool cPlayManager::Info(int num, cMP3PlayInfo *pi)
945 if(idx<0) { idx=currIndex; s=curr; }
946 else { s=list.Get(idx); }
947 memset(pi,0,sizeof(*pi));
949 pi->MaxNum=maxIndex+1;
951 pi->Shuffle=shuffleMode;
954 strn0cpy(pi->Title,s->Name(),sizeof(pi->Title));
955 strn0cpy(pi->Filename,s->FullPath(),sizeof(pi->Filename));
956 cSongInfo *si=s->Info(false);
957 if(si && si->HasInfo()) {
958 static const char *modestr[] = { "Mono","Dual","Joint-Stereo","Stereo" };
960 if(si->Title) strn0cpy(pi->Title,si->Title,sizeof(pi->Title));
961 if(si->Artist) strn0cpy(pi->Artist,si->Artist,sizeof(pi->Artist));
962 if(si->Album) strn0cpy(pi->Album,si->Album,sizeof(pi->Album));
963 strn0cpy(pi->SMode,modestr[si->ChMode],sizeof(pi->SMode));
965 pi->SampleFreq=si->SampleFreq;
966 pi->Bitrate=si->Bitrate;
967 pi->MaxBitrate=si->MaxBitrate;
971 pi->Hash=MakeHashBuff((char *)pi,(char *)&pi->Loop-(char *)pi);
975 void cPlayManager::Add(cPlayList *pl)
977 cMutexLock lock(&listMutex);
979 for(cSong *song=pl->First(); song; song=pl->cList<cSong>::Next(song)) {
980 cSong *ns=new cSong(song);
985 if(MP3Setup.BgrScan) { stopscan=false; if(!Active()) Start(); }
988 maxIndex=list.Count()-1;
989 if(shuffleMode) shuffle->Shuffle(maxIndex+1,currIndex);
994 void cPlayManager::Flush(void)
996 cMutexLock lock(&listMutex);
1002 void cPlayManager::Halt(void)
1004 cMutexLock lock(&listMutex);
1005 curr=0; currIndex=-1;
1007 stopscan=true; bgCond.Broadcast();
1012 void cPlayManager::NoScan(cSong *nono)
1014 // call with listMutex locked!!
1015 while((nono && pass2 && scan==nono) || (!nono && scan)) {
1016 release=true; bgCond.Broadcast();
1017 d(printf("mgr: waiting for bgr release ... (pass2=%d nono=%p scan=%p)\n",pass2,nono,scan))
1018 fgCond.Wait(listMutex);
1022 void cPlayManager::NoPlay(cSong *nono)
1024 // call with listMutex locked!!
1025 while((nono && play==nono) || (!nono && play)) {
1027 fgCond.Wait(listMutex);
1031 bool cPlayManager::Next(void)
1033 cMutexLock lock(&listMutex);
1038 ni=shuffle->Next(currIndex);
1040 if(loopMode || eol) {
1041 shuffle->Shuffle(maxIndex+1,-1);
1042 ni=shuffle->First();
1048 ni=shuffle->First();
1049 n=(ni>=0) ? list.Get(ni) : 0;
1053 n=list.cList<cSong>::Next(curr);
1055 if(loopMode || eol) n=list.First();
1061 ni=n ? n->Index() : -1;
1064 curr=n; currIndex=ni;
1065 playNew=true; eol=false;
1066 d(printf("mgr: next -> %d\n",currIndex))
1072 bool cPlayManager::Prev(void)
1074 cMutexLock lock(&listMutex);
1078 ni=shuffle->Prev(currIndex);
1079 n=(ni>=0) ? list.Get(ni) : 0;
1082 n=list.cList<cSong>::Prev(curr);
1083 ni=n ? n->Index() : -1;
1086 curr=n; currIndex=ni;
1087 playNew=true; eol=false;
1088 d(printf("mgr: prev -> %d\n",currIndex))
1094 void cPlayManager::Goto(int num)
1096 cMutexLock lock(&listMutex);
1097 if(num>0 && num<=maxIndex+1) {
1101 shuffle->Shuffle(maxIndex+1,-1);
1102 currIndex=shuffle->Goto(idx,-1);
1105 currIndex=shuffle->Goto(idx,currIndex);
1109 curr=(currIndex>=0) ? list.Get(currIndex) : 0;
1110 playNew=true; eol=false;
1111 d(printf("mgr: goto -> %d\n",currIndex))
1115 cSong *cPlayManager::Current(void)
1117 cMutexLock lock(&listMutex);
1122 if(play) d(printf("mgr: playing %s\n",play->Name()))
1123 else d(printf("mgr: nothing to play\n"))
1129 bool cPlayManager::NextCurrent(void)
1131 cMutexLock lock(&listMutex);
1132 return (!eol && (playNew || Next()));
1135 bool cPlayManager::NewCurrent(void)
1140 void cPlayManager::Release(void)
1142 cMutexLock lock(&listMutex);
1147 // --- cOutput -----------------------------------------------------------------
1149 struct FrameHeader {
1150 unsigned int samplerate;
1152 #define FHS sizeof(struct FrameHeader)
1159 cOutput(cMP3Player *Player);
1160 virtual ~cOutput() {}
1161 virtual void Init(void);
1162 virtual unsigned int SampleRate(unsigned int PcmSampleRate)=0;
1163 virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)=0;
1164 virtual int Output(const unsigned char *Data, int Len, bool SOF)=0;
1165 virtual bool Poll(void)=0;
1166 virtual void Play(void)=0;
1167 virtual void Pause(void)=0;
1169 virtual void Stats(void);
1173 cOutput::cOutput(cMP3Player *Player)
1178 void cOutput::Init(void)
1184 void cOutput::Stats(void)
1190 // --- cOutputDvb --------------------------------------------------------------
1193 struct LPCMHeader { int id:8; // id
1194 int frame_count:8; // number of frames
1195 int access_ptr:16; // first acces unit pointer, i.e. start of audio frame
1196 bool emphasis:1; // audio emphasis on-off
1197 bool mute:1; // audio mute on-off
1198 bool reserved:1; // reserved
1199 int frame_number:5; // audio frame number
1200 int quant_wlen:2; // quantization word length
1201 int sample_freq:2; // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
1202 bool reserved2:1; // reserved
1203 int chan_count:3; // number of audio channels - 1 (e.g. stereo = 1)
1204 int dyn_range_ctrl:8; // dynamic range control (0x80 if off)
1208 #define FRAMESIZE 2048 // max. frame size allowed for DVB driver
1210 class cOutputDvb : public cOutput {
1216 cOutputDvb(cMP3Player *Player);
1217 virtual unsigned int SampleRate(unsigned int PcmSampleRate);
1218 virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr);
1219 virtual int Output(const unsigned char *Data, int Len, bool SOF);
1220 virtual bool Poll(void);
1221 virtual void Play(void);
1222 virtual void Pause(void);
1225 cOutputDvb::cOutputDvb(cMP3Player *Player)
1228 only48khz=MP3Setup.Only48kHz;
1230 cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio);
1231 d(printf("mp3-dvb: using DVB output\n"))
1234 unsigned int cOutputDvb::SampleRate(unsigned int PcmSampleRate)
1236 unsigned int samplerate=48000;
1238 switch(PcmSampleRate) { // If one of the supported frequencies, do it without resampling.
1239 case 96000: // Select a "even" upsampling frequency if possible, too.
1242 //case 48000: // this is already the default ...
1243 // samplerate=48000;
1260 cFrame *cOutputDvb::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)
1262 static const unsigned char header[] = {
1266 0xBD, // private stream
1269 0x87, // mpeg2, aligned, copyright, original
1271 0x00, // PES header data len
1272 0xA0, // aLPCM header
1277 0x01, // 2-channel stereo (n-1)
1278 0x80 // neutral dynamic range
1281 unsigned char *buff=MALLOC(uchar,FRAMESIZE);
1283 struct FrameHeader *fh=(struct FrameHeader *)buff;
1285 memcpy(buff+FHS,header,sizeof(header));
1289 case 48000: srMode=0<<4; break;
1290 case 96000: srMode=1<<4; break;
1291 case 44100: srMode=2<<4; break;
1292 case 32000: srMode=3<<4; break;
1294 buff[14+FHS]|=srMode;
1295 unsigned int outlen=scale.ScaleBlock(buff+sizeof(header)+FHS,FRAMESIZE-sizeof(header)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherBE:amRoundBE);
1297 // lPCM has 600 fps which is 80 samples at 48kHz per channel
1298 // Frame size = (sample_rate * quantization * channels)/4800
1299 buff[10+FHS]=outlen*(4800/16/2)/sr;
1300 outlen+=(sizeof(header)-6);
1301 buff[4+FHS]=outlen>>8;
1303 f=new cFrame(buff,-(outlen+6+FHS),ftUnknown,index);
1311 #include "player-mp3-sample.c"
1314 int cOutputDvb::Output(const unsigned char *Data, int Len, bool SOF)
1319 struct FrameHeader *fh=(struct FrameHeader *)Data;
1320 if(fh->samplerate!=outSr) {
1322 // at this point we would need access to AUDIO_STOP/AUDIO_PLAY
1323 // ioctl, but unfortunaly VDR's API doesn't provides this.
1324 // So we have to do magic to make the driver switch samplerate.
1325 const unsigned char *p=testAudio;
1326 int pc=sizeof(testAudio);
1329 r=player->PlayPes(p,pc);
1330 if(r>0) { p+=r; pc-=r; }
1332 } while(r>=0 && pc>0);
1334 outSr=fh->samplerate;
1335 d(printf("mp3-dvb: output samplerate now %d\n",outSr))
1341 int r=player->PlayPes(Data,Len);
1342 return (r>=0 ? r+n : -1);
1345 bool cOutputDvb::Poll(void)
1347 return player->DevicePoll(poll,500);
1350 void cOutputDvb::Play(void)
1353 player->DevicePlay();
1357 void cOutputDvb::Pause(void)
1360 player->DeviceFreeze();
1364 // --- cOutputOss --------------------------------------------------------------
1368 const char *dspdevice="/dev/dsp";
1370 class cOutputOss : public cOutput {
1375 unsigned char buff[8192];
1377 bool Reset(unsigned int sr);
1379 cOutputOss(cMP3Player *Player);
1380 virtual ~cOutputOss();
1381 virtual void Init(void);
1382 virtual unsigned int SampleRate(unsigned int PcmSampleRate);
1383 virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr);
1384 virtual int Output(const unsigned char *Data, int Len, bool SOF);
1385 virtual bool Poll(void);
1386 virtual void Play(void);
1387 virtual void Pause(void);
1390 cOutputOss::cOutputOss(cMP3Player *Player)
1394 d(printf("mp3-oss: using OSS output\n"))
1397 cOutputOss::~cOutputOss()
1399 if(fd>=0) close(fd);
1402 void cOutputOss::Init(void)
1405 fd=open(dspdevice,O_WRONLY|O_NONBLOCK);
1407 if(fcntl(fd,F_SETFL,0)==0)
1410 esyslog("ERROR: Cannot make dsp device '%s' blocking: %s!",dspdevice,strerror(errno));
1414 else esyslog("ERROR: Cannot open dsp device '%s': %s!",dspdevice,strerror(errno));
1419 bool cOutputOss::Reset(unsigned int sr)
1422 CHECK(ioctl(fd,SNDCTL_DSP_SYNC,0));
1423 int format=AFMT_S16_LE;
1424 CHECK(ioctl(fd,SNDCTL_DSP_SETFMT,&format));
1425 if(format==AFMT_S16_LE) {
1427 CHECK(ioctl(fd,SNDCTL_DSP_CHANNELS,&channels));
1430 CHECK(ioctl(fd,SNDCTL_DSP_SPEED,&real));
1431 d(printf("oss: DSP samplerate now %d\n",real))
1432 if(abs(real-sr)<sr/50) {
1434 d(printf("mp3-oss: DSP reset done\n"))
1438 d(printf("mp3-oss: driver can't handle samplerate %d, got %d\n",sr,real))
1439 esyslog("ERROR: OSS driver can't handle samplerate %d, got %d\n",sr,real);
1443 d(printf("mp3-oss: 2-channel stereo not supported\n"))
1444 esyslog("ERROR: OSS driver doesn't support 2-channel stereo.");
1448 d(printf("mp3-oss: little-endian samples not supported\n"))
1449 esyslog("ERROR: OSS driver doesn't support 16-bit little-endian samples.");
1456 unsigned int cOutputOss::SampleRate(unsigned int PcmSampleRate)
1458 return PcmSampleRate;
1461 cFrame *cOutputOss::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)
1463 struct FrameHeader *fh=(struct FrameHeader *)buff;
1466 unsigned int outlen=scale.ScaleBlock(buff+FHS,sizeof(buff)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherLE:amRoundLE);
1468 f=new cFrame(buff,outlen+FHS,ftUnknown,index);
1472 int cOutputOss::Output(const unsigned char *Data, int Len, bool SOF)
1477 struct FrameHeader *fh=(struct FrameHeader *)Data;
1478 if(fh->samplerate!=outSr) Reset(fh->samplerate);
1483 int r=write(fd,Data,Len);
1484 if(r<0 && FATALERRNO) return -1;
1492 bool cOutputOss::Poll(void)
1494 return fd>=0 ? poll.Poll(500) : false;
1497 void cOutputOss::Play(void)
1501 void cOutputOss::Pause(void)
1503 CHECK(ioctl(fd,SNDCTL_DSP_POST,0));
1508 // --- cMP3Player --------------------------------------------------------------
1510 cMP3Player::cMP3Player()
1511 :cPlayer(MP3Setup.BackgrMode==1 ? pmAudioOnly : pmAudioOnlyBlack)
1513 active=true; started=false; isStream=false;
1514 ringBuffer=new cRingBufferFrame(MP3BUFSIZE);
1515 rframe=0; pframe=0; decoder=0; output=0;
1516 playMode=pmStartup; state=msStop;
1520 cMP3Player::~cMP3Player()
1526 void cMP3Player::Activate(bool On)
1529 d(printf("mp3: player active true requested...\n"))
1531 playMode=pmStartup; Start(); started=true;
1532 playModeMutex.Lock();
1533 WaitPlayMode(pmStartup,true); // wait for the decoder to become ready
1534 playModeMutex.Unlock();
1539 d(printf("mp3: player active true done\n"))
1541 else if(started && active) {
1542 d(printf("mp3: player active false requested...\n"))
1543 Lock(); StopPlay(); Unlock();
1545 SetPlayMode(pmStartup);
1547 d(printf("mp3: player active false done\n"))
1551 void cMP3Player::SetPlayMode(ePlayMode mode)
1553 playModeMutex.Lock();
1554 if(mode!=playMode) {
1556 dm(printf("mp3: setting mode=%d (pid=%d)\n",mode,getpid()))
1557 playModeCond.Broadcast();
1559 playModeMutex.Unlock();
1562 void cMP3Player::WaitPlayMode(ePlayMode mode, bool inv)
1564 // must be called with playModeMutex LOCKED !!!
1566 while(active && ((!inv && mode!=playMode) || (inv && mode==playMode))) {
1567 dm(printf("mp3: entering wait for mode%s%d with mode=%d (pid=%d)\n",inv?"!=":"==",mode,playMode,getpid()))
1568 playModeCond.Wait(playModeMutex);
1569 dm(printf("mp3: returning from wait with mode=%d (pid=%d)\n",playMode,getpid()))
1573 void cMP3Player::Action(void)
1576 struct mad_pcm *pcm=0;
1577 cResample resample[2];
1578 unsigned int nsamples[2];
1579 const mad_fixed_t *data[2];
1582 bool haslevel=false;
1583 const unsigned char *p=0;
1584 int pc=0, readindex=0;
1585 bool imageValid=true;
1591 cTimeMs lastwrite(2000000);
1594 dsyslog("mp3: player thread started (pid=%d)", getpid());
1596 SetPlayMode(pmStopped);
1598 delete output; output=0;
1600 if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) output=new cOutputOss(this);
1602 if(MP3Setup.AudioOutMode==AUDIOOUTMODE_DVB) output=new cOutputDvb(this);
1604 d(printf("mp3: audiooutmode mismatch or no output driver\n"))
1605 esyslog("ERROR: no audio output driver. balling out");
1614 int avail=ringBuffer->Available();
1615 printf("mp3: heartbeat buffer=%d now=%d\n",avail,now&4095);
1616 //output->Stats(); if(haslevel) norm.Stats();
1617 beat=now+(avail>(MP3BUFSIZE*10/100) ? (avail<(MP3BUFSIZE*50/100) ? 2 : 20) : 1);
1625 if(!pframe && playing && !imageValid && imageCheck.Elapsed()) {
1628 imageCheck.Set(250);
1629 imageValid=playing->Image(mem,len);
1631 if(playindex) cCondWait::SleepMs(80); // stillpicture ioctl freezes without this
1632 DeviceStillPicture(mem,len);
1638 if(!pframe && playMode==pmPlay) {
1639 pframe=ringBuffer->Get();
1641 playindex=pframe->Index();
1651 if(lastwrite.TimedOut())
1652 printf("mp3: write delayed %llu ms\n",lastwrite.Elapsed());
1653 lastwrite.Set(DEBUG_DELAY+50);
1656 int w=output->Output(p,pc,SOF);
1660 ringBuffer->Drop(pframe);
1665 else if(w<0 && FATALERRNO) {
1667 d(printf("mp3: output failed: %s\n",strerror(errno)))
1673 if(mgr->NewCurrent() && playMode==pmPlay && state!=msStart) {
1676 d(printf("mp3: stale song change, restart.\n"))
1679 if(!rframe && playMode==pmPlay) {
1682 d(printf("mp3: starting play\n"))
1683 mgr->Throttle(true);
1684 playindex=readindex=total=0;
1685 playing=mgr->Current();
1687 if((decoder=playing->Decoder()) && decoder->Start()) {
1688 isStream=decoder->IsStream(); levelgood=!isStream; haslevel=false;
1689 cSongInfo *si=playing->Info(true);
1692 d(printf("mp3: found song level=%f peak=%f\n",si->Level,si->Peak))
1694 norm.Init(si->Level,si->Peak);
1697 total=SecondsToFrames(si->Total);
1699 d(printf("mp3: isStream=%d levelgood=%d haslevel=%d\n",isStream,levelgood,haslevel))
1702 if(MP3Setup.BackgrMode==2) imageValid=false;
1707 esyslog("ERROR: playlist entry %s is not a valid file",playing->Name());
1710 d(printf("mp3: no current on start play\n"))
1716 cTimeMs check(DEBUG_DELAY);
1718 struct Decode *ds=decoder->Decode();
1720 if(check.TimedOut()) printf("mp3: decode delayed %llu ms\n",check.Elapsed());
1722 switch(ds->status) {
1725 readindex=ds->index;
1730 // skipping, state unchanged, next decode
1733 if(!haslevel && levelgood) { // save level & peak to infocache on eof
1734 double l=level.GetLevel();
1736 cSongInfo *si=decoder->SongInfo(false);
1737 cFileInfo *fi=decoder->FileInfo();
1740 si->Peak=level.GetPeak();
1741 InfoCache.Cache(si,fi);
1755 if(!haslevel) { if(levelgood) level.GetPower(pcm); }
1756 else norm.AddGain(pcm);
1762 static unsigned int oldrate=0;
1763 if(oldrate!=pcm->samplerate) {
1764 printf("mp3: new input sample rate %d\n",pcm->samplerate);
1765 oldrate=pcm->samplerate;
1769 nsamples[0]=nsamples[1]=pcm->length;
1770 data[0]=pcm->samples[0];
1771 data[1]=pcm->channels>1 ? pcm->samples[1]:0;
1773 dvbSampleRate=output->SampleRate(pcm->samplerate);
1774 if(dvbSampleRate!=pcm->samplerate) {
1775 if(resample[0].SetInputRate(pcm->samplerate,dvbSampleRate)) {
1776 nsamples[0]=resample[0].ResampleBlock(nsamples[0],data[0]);
1777 data[0] =resample[0].Resampled();
1779 if(data[1] && resample[1].SetInputRate(pcm->samplerate,dvbSampleRate)) {
1780 nsamples[1]=resample[1].ResampleBlock(nsamples[1],data[1]);
1781 data[1] =resample[1].Resampled();
1787 if(nsamples[0]>0) rframe=output->MakeFrame(nsamples[0],data,readindex,dvbSampleRate);
1788 else state=msDecode;
1792 d(printf("mp3: eof or error\n"))
1797 d(printf("mp3: stopping play\n"))
1798 if(decoder) { decoder->Stop(); decoder=0; }
1799 mgr->Release(); playing=0; imageValid=true;
1802 output->Stats(); if(haslevel) norm.Stats();
1804 if(state==msStop) SetPlayMode(pmStopped);
1805 if(state==msRestart) state=msStart;
1808 if(ringBuffer->Available()==0) {
1809 if(mgr->NextCurrent()) {
1810 d(printf("mp3: playing next\n"))
1814 d(printf("mp3: end of playlist\n"))
1815 if(MP3Setup.AbortAtEOL) {
1817 d(printf("mp3: aborting player...\n"))
1819 else d(printf("mp3: player idle...\n"))
1820 SetPlayMode(pmStopped);
1827 if(rframe && ringBuffer->Put(rframe)) rframe=0;
1831 if((rframe || state==msWait) && pframe) {
1832 mgr->Throttle(false);
1835 else if(playMode!=pmPlay) {
1836 mgr->Throttle(false);
1838 cCondWait::SleepMs(100);
1840 playModeMutex.Lock();
1841 if(playMode!=pmPlay) WaitPlayMode(playMode,true);
1842 playModeMutex.Unlock();
1845 lastwrite.Set(2000000);
1848 else if(state!=msWait && ringBuffer->Available()<(MP3BUFSIZE*50/100)) {
1849 mgr->Throttle(true);
1856 delete output; output=0;
1857 if(decoder) { decoder->Stop(); decoder=0; }
1858 mgr->Release(); playing=0;
1859 SetPlayMode(pmStopped);
1863 dsyslog("mp3: player thread ended (pid=%d)", getpid());
1866 void cMP3Player::Empty(void)
1869 delete rframe; rframe=0; pframe=0;
1870 ringBuffer->Clear();
1875 void cMP3Player::StopPlay(void) // StopPlay() must be called in locked state!!!
1877 if(playMode!=pmStopped) {
1880 SetPlayMode(pmPlay);
1881 Unlock(); // let the decode thread process the stop signal
1882 playModeMutex.Lock();
1883 WaitPlayMode(pmStopped,false);
1884 playModeMutex.Unlock();
1889 void cMP3Player::Pause(void)
1892 if(playMode==pmPaused) Play();
1893 else if(playMode==pmPlay && !isStream) {
1894 d(printf("mp3: pause\n"))
1895 if(output) output->Pause();
1896 SetPlayMode(pmPaused);
1901 void cMP3Player::Play(void)
1904 if(playMode!=pmPlay) {
1905 d(printf("mp3: play\n"))
1906 if(playMode==pmStopped) state=msStart;
1907 if(output) output->Play();
1908 SetPlayMode(pmPlay);
1913 bool cMP3Player::PrevCheck(void)
1917 if(playindex>=2000 && !isStream) {
1918 state=msRestart; res=true;
1920 d(printf("mp3: skip to start of song\n"))
1926 void cMP3Player::SkipSeconds(int secs)
1928 if(playMode!=pmStopped && !isStream) {
1930 d(printf("mp3: skip secs %d\n",secs))
1931 if(playMode==pmPaused) SetPlayMode(pmPlay);
1932 float bufsecs=(float)ringBuffer->Available() / (float)(dvbSampleRate*OUT_FACT);
1933 d(printf("mp3: ringbuffer available %f secs\n",bufsecs))
1934 if(secs>0 && bufsecs>=(float)secs) {
1935 // clear intermediate queue
1937 ringBuffer->Drop(pframe);
1941 // skip inside ringbuffer
1942 int skipindex=playindex+secs*1000;
1943 d(printf("mp3: skipping play=%d skip=%d ...",playindex,skipindex))
1946 f=ringBuffer->Get();
1948 playindex=f->Index();
1949 ringBuffer->Drop(f);
1952 } while(f && playindex<skipindex);
1953 d(printf("\nmp3: skipped play=%d skip=%d\n",playindex,skipindex))
1956 if(decoder && decoder->Skip(secs,bufsecs)) levelgood=false;
1963 bool cMP3Player::GetIndex(int &Current, int &Total, bool SnapToIFrame)
1965 Current=SecondsToFrames(playindex/1000); Total=total;
1969 bool cMP3Player::GetReplayMode(bool &Play, bool &Forward, int &Speed)
1971 Play=(playMode==pmPlay);