nathan@0: /* nathan@0: * MP3/MPlayer plugin to VDR (C++) nathan@0: * nathan@0: * (C) 2001-2007 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: #include nathan@0: #include nathan@0: #ifdef WITH_OSS nathan@0: #include nathan@0: #endif nathan@0: nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include "common.h" nathan@0: #include "setup-mp3.h" nathan@0: #include "player-mp3.h" nathan@0: #include "data-mp3.h" nathan@0: #include "decoder.h" nathan@0: #include "decoder-core.h" nathan@0: nathan@0: #ifndef NO_DEBUG nathan@0: //#define DEBUG_MODE // debug playmode changes nathan@0: #define DEBUG_BGR // debug backround scan thread nathan@0: #define DEBUG_DELAY 300 // debug write/decode delays nathan@0: //#define ACC_DUMP // dump limiter lookup table to /tmp/limiter nathan@0: #endif nathan@0: nathan@0: #if !defined(NO_DEBUG) && defined(DEBUG_MODE) nathan@0: #define dm(x) { (x); } nathan@0: #else nathan@0: #define dm(x) ; nathan@0: #endif nathan@0: nathan@0: #if !defined(NO_DEBUG) && defined(DEBUG_BGR) nathan@0: #define db(x) { (x); } nathan@0: #else nathan@0: #define db(x) ; nathan@0: #endif nathan@0: nathan@0: // ---------------------------------------------------------------- nathan@0: nathan@0: #define MP3BUFSIZE (1024*1024) // output ringbuffer size nathan@0: #define OUT_BITS 16 // output 16 bit samples to DVB driver nathan@0: #define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes nathan@0: // cResample nathan@0: #define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame nathan@0: // cNormalize nathan@0: #define MIN_GAIN 0.03 // min. gain required to launch the normalizer nathan@0: #define MAX_GAIN 3.0 // max. allowed gain nathan@0: #define USE_FAST_LIMITER nathan@0: #define LIM_ACC 12 // bit, accuracy for lookup table nathan@0: #define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1) // max. value covered by lookup table nathan@0: #define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup nathan@0: #define F_LIM_JMP (mad_fixed_t)(1<newrate*6) { // out of range nathan@0: esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6); nathan@0: return 0; nathan@0: } nathan@0: ratio=mad_f_tofixed((double)oldrate/(double)newrate); nathan@0: step=0; last=0; nathan@0: #ifdef DEBUG nathan@0: static mad_fixed_t oldratio=0; nathan@0: if(oldratio!=ratio) { nathan@0: printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate); nathan@0: oldratio=ratio; nathan@0: } nathan@0: #endif nathan@0: return ratio!=MAD_F_ONE; nathan@0: } nathan@0: nathan@0: unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old) nathan@0: { nathan@0: // This resampling algorithm is based on a linear interpolation, which is nathan@0: // not at all the best sounding but is relatively fast and efficient. nathan@0: // nathan@0: // A better algorithm would be one that implements a bandlimited nathan@0: // interpolation. nathan@0: nathan@0: mad_fixed_t *nsam=resampled; nathan@0: const mad_fixed_t *end=old+nsamples; nathan@0: const mad_fixed_t *begin=nsam; nathan@0: nathan@0: if(step < 0) { nathan@0: step = mad_f_fracpart(-step); nathan@0: nathan@0: while (step < MAD_F_ONE) { nathan@0: *nsam++ = step ? last+mad_f_mul(*old-last,step) : last; nathan@0: step += ratio; nathan@0: if(((step + 0x00000080L) & 0x0fffff00L) == 0) nathan@0: step = (step + 0x00000080L) & ~0x0fffffffL; nathan@0: } nathan@0: step -= MAD_F_ONE; nathan@0: } nathan@0: nathan@0: while (end - old > 1 + mad_f_intpart(step)) { nathan@0: old += mad_f_intpart(step); nathan@0: step = mad_f_fracpart(step); nathan@0: *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old; nathan@0: step += ratio; nathan@0: if (((step + 0x00000080L) & 0x0fffff00L) == 0) nathan@0: step = (step + 0x00000080L) & ~0x0fffffffL; nathan@0: } nathan@0: nathan@0: if (end - old == 1 + mad_f_intpart(step)) { nathan@0: last = end[-1]; nathan@0: step = -step; nathan@0: } nathan@0: else step -= mad_f_fromint(end - old); nathan@0: nathan@0: return nsam-begin; nathan@0: } nathan@0: nathan@0: // --- cLevel ---------------------------------------------------------------- nathan@0: nathan@0: // The normalize algorithm and parts of the code has been adapted from the nathan@0: // Normalize 0.7 project. (C) 1999-2002, Chris Vaill nathan@0: nathan@0: // A little background on how normalize computes the volume nathan@0: // of a wav file, in case you want to know just how your nathan@0: // files are being munged: nathan@0: // nathan@0: // The volumes calculated are RMS amplitudes, which corre­ nathan@0: // spond (roughly) to perceived volume. Taking the RMS ampli­ nathan@0: // tude of an entire file would not give us quite the measure nathan@0: // we want, though, because a quiet song punctuated by short nathan@0: // loud parts would average out to a quiet song, and the nathan@0: // adjustment we would compute would make the loud parts nathan@0: // excessively loud. nathan@0: // nathan@0: // What we want is to consider the maximum volume of the nathan@0: // file, and normalize according to that. We break up the nathan@0: // signal into 100 chunks per second, and get the signal nathan@0: // power of each chunk, in order to get an estimation of nathan@0: // "instantaneous power" over time. This "instantaneous nathan@0: // power" signal varies too much to get a good measure of the nathan@0: // original signal's maximum sustained power, so we run a nathan@0: // smoothing algorithm over the power signal (specifically, a nathan@0: // mean filter with a window width of 100 elements). The max­ nathan@0: // imum point of the smoothed power signal turns out to be a nathan@0: // good measure of the maximum sustained power of the file. nathan@0: // We can then take the square root of the power to get maxi­ nathan@0: // mum sustained RMS amplitude. nathan@0: nathan@0: class cLevel { nathan@0: private: nathan@0: double maxpow; nathan@0: mad_fixed_t peak; nathan@0: struct Power { nathan@0: // smooth nathan@0: int npow, wpow; nathan@0: double powsum, pows[POW_WIN]; nathan@0: // sum nathan@0: unsigned int nsum; nathan@0: double sum; nathan@0: } power[2]; nathan@0: // nathan@0: inline void AddPower(struct Power *p, double pow); nathan@0: public: nathan@0: void Init(void); nathan@0: void GetPower(struct mad_pcm *pcm); nathan@0: double GetLevel(void); nathan@0: double GetPeak(void); nathan@0: }; nathan@0: nathan@0: void cLevel::Init(void) nathan@0: { nathan@0: for(int l=0 ; l<2 ; l++) { nathan@0: struct Power *p=&power[l]; nathan@0: p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0; nathan@0: for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0; nathan@0: } nathan@0: maxpow=0.0; peak=0; nathan@0: } nathan@0: nathan@0: void cLevel::GetPower(struct mad_pcm *pcm) nathan@0: { nathan@0: for(int i=0 ; ichannels ; i++) { nathan@0: struct Power *p=&power[i]; nathan@0: mad_fixed_t *data=pcm->samples[i]; nathan@0: for(int n=pcm->length ; n>0 ; n--) { nathan@0: if(*data < -peak) peak = -*data; nathan@0: if(*data > peak) peak = *data; nathan@0: double s=mad_f_todouble(*data++); nathan@0: p->sum+=(s*s); nathan@0: if(++(p->nsum)>=pcm->samplerate/100) { nathan@0: AddPower(p,p->sum/(double)p->nsum); nathan@0: p->sum=0.0; p->nsum=0; nathan@0: } nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: void cLevel::AddPower(struct Power *p, double pow) nathan@0: { nathan@0: p->powsum+=pow; nathan@0: if(p->npow>=POW_WIN) { nathan@0: if(p->powsum>maxpow) maxpow=p->powsum; nathan@0: p->powsum-=p->pows[p->wpow]; nathan@0: } nathan@0: else p->npow++; nathan@0: p->pows[p->wpow]=pow; nathan@0: p->wpow=(p->wpow+1) % POW_WIN; nathan@0: } nathan@0: nathan@0: double cLevel::GetLevel(void) nathan@0: { nathan@0: if(maxpowmaxpow) maxpow=power[0].powsum; nathan@0: if(power[1].powsum>maxpow) maxpow=power[1].powsum; nathan@0: } nathan@0: double level=sqrt(maxpow/(double)POW_WIN); // adjust for the smoothing window size and root nathan@0: d(printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak))) nathan@0: return level; nathan@0: } nathan@0: nathan@0: double cLevel::GetPeak(void) nathan@0: { nathan@0: return mad_f_todouble(peak); nathan@0: } nathan@0: nathan@0: // --- cNormalize ------------------------------------------------------------ nathan@0: nathan@0: class cNormalize { nathan@0: private: nathan@0: mad_fixed_t gain; nathan@0: double d_limlvl, one_limlvl; nathan@0: mad_fixed_t limlvl; nathan@0: bool dogain, dolimit; nathan@0: #ifdef DEBUG nathan@0: // stats nathan@0: unsigned long limited, clipped, total; nathan@0: mad_fixed_t peak; nathan@0: #endif nathan@0: // limiter nathan@0: #ifdef USE_FAST_LIMITER nathan@0: mad_fixed_t *table, tablestart; nathan@0: int tablesize; nathan@0: inline mad_fixed_t FastLimiter(mad_fixed_t x); nathan@0: #endif nathan@0: inline mad_fixed_t Limiter(mad_fixed_t x); nathan@0: public: nathan@0: cNormalize(void); nathan@0: ~cNormalize(); nathan@0: void Init(double Level, double Peak); nathan@0: void Stats(void); nathan@0: void AddGain(struct mad_pcm *pcm); nathan@0: }; nathan@0: nathan@0: cNormalize::cNormalize(void) nathan@0: { nathan@0: d_limlvl=(double)MP3Setup.LimiterLevel/100.0; nathan@0: one_limlvl=1-d_limlvl; nathan@0: limlvl=mad_f_tofixed(d_limlvl); nathan@0: d(printf("norm: lim_lev=%f lim_acc=%d\n",d_limlvl,LIM_ACC)) nathan@0: nathan@0: #ifdef USE_FAST_LIMITER nathan@0: mad_fixed_t start=limlvl & ~(F_LIM_JMP-1); nathan@0: tablestart=start; nathan@0: tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2; nathan@0: table=new mad_fixed_t[tablesize]; nathan@0: if(table) { nathan@0: d(printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP)) nathan@0: for(int i=0 ; i=limlvl ; x-=mad_f_tofixed(1e-4)) { nathan@0: mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x)); nathan@0: if(diff>maxdiff) maxdiff=diff; nathan@0: #ifdef ACC_DUMP nathan@0: fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n", nathan@0: mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff)); nathan@0: if(ferror(out)) break; nathan@0: #endif nathan@0: } nathan@0: #ifdef ACC_DUMP nathan@0: fclose(out); nathan@0: #endif nathan@0: d(printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff))) nathan@0: if(mad_f_todouble(maxdiff)>1e-6) { nathan@0: esyslog("ERROR: accuracy check failed, normalizer disabled"); nathan@0: delete table; table=0; nathan@0: } nathan@0: } nathan@0: else esyslog("ERROR: no memory for lookup table, normalizer disabled"); nathan@0: #endif // USE_FAST_LIMITER nathan@0: } nathan@0: nathan@0: cNormalize::~cNormalize() nathan@0: { nathan@0: #ifdef USE_FAST_LIMITER nathan@0: delete[] table; nathan@0: #endif nathan@0: } nathan@0: nathan@0: void cNormalize::Init(double Level, double Peak) nathan@0: { nathan@0: double Target=(double)MP3Setup.TargetLevel/100.0; nathan@0: double dgain=Target/Level; nathan@0: if(dgain>MAX_GAIN) dgain=MAX_GAIN; nathan@0: gain=mad_f_tofixed(dgain); nathan@0: // Check if we actually need to apply a gain nathan@0: dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN); nathan@0: #ifdef USE_FAST_LIMITER nathan@0: if(!table) dogain=false; nathan@0: #endif nathan@0: // Check if we actually need to do limiting: nathan@0: // we have to if limiter is enabled, if gain>1 and if the peaks will clip. nathan@0: dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0); nathan@0: #ifdef DEBUG nathan@0: printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak); nathan@0: limited=clipped=total=0; peak=0; nathan@0: #endif nathan@0: } nathan@0: nathan@0: void cNormalize::Stats(void) nathan@0: { nathan@0: #ifdef DEBUG nathan@0: if(total) nathan@0: printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n", nathan@0: total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak)); nathan@0: #endif nathan@0: } nathan@0: nathan@0: mad_fixed_t cNormalize::Limiter(mad_fixed_t x) nathan@0: { nathan@0: // Limiter function: nathan@0: // nathan@0: // / x (for x <= lev) nathan@0: // x' = | nathan@0: // \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev) nathan@0: // nathan@0: // call only with x>=0. For negative samples, preserve sign outside this function nathan@0: // nathan@0: // With limiter level = 0, this is equivalent to a tanh() function; nathan@0: // with limiter level = 1, this is equivalent to clipping. nathan@0: nathan@0: if(x>limlvl) { nathan@0: #ifdef DEBUG nathan@0: if(x>MAD_F_ONE) clipped++; nathan@0: limited++; nathan@0: #endif nathan@0: x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl); nathan@0: } nathan@0: return x; nathan@0: } nathan@0: nathan@0: #ifdef USE_FAST_LIMITER nathan@0: mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x) nathan@0: { nathan@0: // The fast algorithm is based on a linear interpolation between the nathan@0: // the values in the lookup table. Relays heavly on libmads fixed point format. nathan@0: nathan@0: if(x>limlvl) { nathan@0: int i=(unsigned int)(x-tablestart)/F_LIM_JMP; nathan@0: #ifdef DEBUG nathan@0: if(x>MAD_F_ONE) clipped++; nathan@0: limited++; nathan@0: if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n", nathan@0: mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize); nathan@0: #endif nathan@0: mad_fixed_t r=x & (F_LIM_JMP-1); nathan@0: x=MAD_F_ONE; nathan@0: if(i>MAD_F_FRACBITS nathan@0: // which is senseless in the case of following <>LIM_SHIFT; // better, don't know if works on all machines nathan@0: } nathan@0: } nathan@0: return x; nathan@0: } nathan@0: #endif nathan@0: nathan@0: #ifdef USE_FAST_LIMITER nathan@0: #define LIMITER_FUNC FastLimiter nathan@0: #else nathan@0: #define LIMITER_FUNC Limiter nathan@0: #endif nathan@0: nathan@0: void cNormalize::AddGain(struct mad_pcm *pcm) nathan@0: { nathan@0: if(dogain) { nathan@0: for(int i=0 ; ichannels ; i++) { nathan@0: mad_fixed_t *data=pcm->samples[i]; nathan@0: #ifdef DEBUG nathan@0: total+=pcm->length; nathan@0: #endif nathan@0: if(dolimit) { nathan@0: for(int n=pcm->length ; n>0 ; n--) { nathan@0: mad_fixed_t s=mad_f_mul(*data,gain); nathan@0: if(s<0) { nathan@0: s=-s; nathan@0: #ifdef DEBUG nathan@0: if(s>peak) peak=s; nathan@0: #endif nathan@0: s=LIMITER_FUNC(s); nathan@0: s=-s; nathan@0: } nathan@0: else { nathan@0: #ifdef DEBUG nathan@0: if(s>peak) peak=s; nathan@0: #endif nathan@0: s=LIMITER_FUNC(s); nathan@0: } nathan@0: *data++=s; nathan@0: } nathan@0: } nathan@0: else { nathan@0: for(int n=pcm->length ; n>0 ; n--) { nathan@0: mad_fixed_t s=mad_f_mul(*data,gain); nathan@0: #ifdef DEBUG nathan@0: if(s>peak) peak=s; nathan@0: else if(-s>peak) peak=-s; nathan@0: #endif nathan@0: if(s>MAD_F_ONE) s=MAD_F_ONE; // do clipping nathan@0: if(s<-MAD_F_ONE) s=-MAD_F_ONE; nathan@0: *data++=s; nathan@0: } nathan@0: } nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: // --- cScale ---------------------------------------------------------------- nathan@0: nathan@0: // The dither code has been adapted from the madplay project nathan@0: // (audio.c) found in the libmad distribution nathan@0: nathan@0: enum eAudioMode { amRoundBE, amDitherBE, amRoundLE, amDitherLE }; nathan@0: nathan@0: class cScale { nathan@0: private: nathan@0: enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 }; nathan@0: #ifdef DEBUG nathan@0: // audio stats nathan@0: unsigned long clipped_samples; nathan@0: mad_fixed_t peak_clipping; nathan@0: mad_fixed_t peak_sample; nathan@0: #endif nathan@0: // dither nathan@0: struct dither { nathan@0: mad_fixed_t error[3]; nathan@0: mad_fixed_t random; nathan@0: } leftD, rightD; nathan@0: // nathan@0: inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true); nathan@0: inline unsigned long Prng(unsigned long state); nathan@0: signed long LinearRound(mad_fixed_t sample); nathan@0: signed long LinearDither(mad_fixed_t sample, struct dither *dither); nathan@0: public: nathan@0: void Init(void); nathan@0: void Stats(void); nathan@0: unsigned int ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode); nathan@0: }; nathan@0: nathan@0: void cScale::Init(void) nathan@0: { nathan@0: #ifdef DEBUG nathan@0: clipped_samples=0; peak_clipping=peak_sample=0; nathan@0: #endif nathan@0: memset(&leftD,0,sizeof(leftD)); nathan@0: memset(&rightD,0,sizeof(rightD)); nathan@0: } nathan@0: nathan@0: void cScale::Stats(void) nathan@0: { nathan@0: #ifdef DEBUG nathan@0: printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n", nathan@0: clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample)); nathan@0: #endif nathan@0: } nathan@0: nathan@0: // gather signal statistics while clipping nathan@0: mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats) nathan@0: { nathan@0: #ifndef DEBUG nathan@0: if (sample > MAX) sample = MAX; nathan@0: if (sample < MIN) sample = MIN; nathan@0: #else nathan@0: if(!stats) { nathan@0: if (sample > MAX) sample = MAX; nathan@0: if (sample < MIN) sample = MIN; nathan@0: } nathan@0: else { nathan@0: if (sample >= peak_sample) { nathan@0: if (sample > MAX) { nathan@0: ++clipped_samples; nathan@0: if (sample - MAX > peak_clipping) nathan@0: peak_clipping = sample - MAX; nathan@0: sample = MAX; nathan@0: } nathan@0: peak_sample = sample; nathan@0: } nathan@0: else if (sample < -peak_sample) { nathan@0: if (sample < MIN) { nathan@0: ++clipped_samples; nathan@0: if (MIN - sample > peak_clipping) nathan@0: peak_clipping = MIN - sample; nathan@0: sample = MIN; nathan@0: } nathan@0: peak_sample = -sample; nathan@0: } nathan@0: } nathan@0: #endif nathan@0: return sample; nathan@0: } nathan@0: nathan@0: // generic linear sample quantize routine nathan@0: signed long cScale::LinearRound(mad_fixed_t sample) nathan@0: { nathan@0: // round nathan@0: sample += (1L << (MAD_F_FRACBITS - OUT_BITS)); nathan@0: // clip nathan@0: sample=Clip(sample); nathan@0: // quantize and scale nathan@0: return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS); nathan@0: } nathan@0: nathan@0: // 32-bit pseudo-random number generator nathan@0: unsigned long cScale::Prng(unsigned long state) nathan@0: { nathan@0: return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; nathan@0: } nathan@0: nathan@0: // generic linear sample quantize and dither routine nathan@0: signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither) nathan@0: { nathan@0: // noise shape nathan@0: sample += dither->error[0] - dither->error[1] + dither->error[2]; nathan@0: dither->error[2] = dither->error[1]; nathan@0: dither->error[1] = dither->error[0] / 2; nathan@0: // bias nathan@0: mad_fixed_t output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1)); nathan@0: const int scalebits = MAD_F_FRACBITS + 1 - OUT_BITS; nathan@0: const mad_fixed_t mask = (1L << scalebits) - 1; nathan@0: // dither nathan@0: const mad_fixed_t random = Prng(dither->random); nathan@0: output += (random & mask) - (dither->random & mask); nathan@0: dither->random = random; nathan@0: // clip nathan@0: output=Clip(output); nathan@0: sample=Clip(sample,false); nathan@0: // quantize nathan@0: output &= ~mask; nathan@0: // error feedback nathan@0: dither->error[0] = sample - output; nathan@0: // scale nathan@0: return output >> scalebits; nathan@0: } nathan@0: nathan@0: #define PUT_BE(data,sample) { *data++=(sample)>>8; *data++=(sample)>>0; } nathan@0: #define PUT_LE(data,sample) { *data++=(sample)>>0; *data++=(sample)>>8; } nathan@0: nathan@0: // write a block of signed 16-bit PCM samples nathan@0: 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) nathan@0: { nathan@0: unsigned int len=size/OUT_FACT; nathan@0: if(len>nsamples) { len=nsamples; size=len*OUT_FACT; } nathan@0: nsamples-=len; nathan@0: switch(mode) { nathan@0: case amRoundBE: nathan@0: while(len--) { nathan@0: signed int sample=LinearRound(*left++); nathan@0: PUT_BE(data,sample); nathan@0: if(right) sample=LinearRound(*right++); nathan@0: PUT_BE(data,sample); nathan@0: } nathan@0: break; nathan@0: case amDitherBE: nathan@0: while(len--) { nathan@0: signed int sample=LinearDither(*left++,&leftD); nathan@0: PUT_BE(data,sample); nathan@0: if(right) sample=LinearDither(*right++,&rightD); nathan@0: PUT_BE(data,sample); nathan@0: } nathan@0: break; nathan@0: case amRoundLE: nathan@0: while(len--) { nathan@0: signed int sample=LinearRound(*left++); nathan@0: PUT_LE(data,sample); nathan@0: if(right) sample=LinearRound(*right++); nathan@0: PUT_LE(data,sample); nathan@0: } nathan@0: break; nathan@0: case amDitherLE: nathan@0: while(len--) { nathan@0: signed int sample=LinearDither(*left++,&leftD); nathan@0: PUT_LE(data,sample); nathan@0: if(right) sample=LinearDither(*right++,&rightD); nathan@0: PUT_LE(data,sample); nathan@0: } nathan@0: break; nathan@0: } nathan@0: return size; nathan@0: } nathan@0: nathan@0: // --- cShuffle ---------------------------------------------------------------- nathan@0: nathan@0: class cShuffle { nathan@0: private: nathan@0: int *shuffle, max; nathan@0: unsigned int seed; nathan@0: // nathan@0: int Index(int pos); nathan@0: public: nathan@0: cShuffle(void); nathan@0: ~cShuffle(); nathan@0: void Shuffle(int num, int curr); nathan@0: void Del(int pos); nathan@0: void Flush(void); nathan@0: int First(void); nathan@0: int Next(int curr); nathan@0: int Prev(int curr); nathan@0: int Goto(int pos, int curr); nathan@0: }; nathan@0: nathan@0: cShuffle::cShuffle(void) nathan@0: { nathan@0: shuffle=0; max=0; nathan@0: seed=time(0); nathan@0: } nathan@0: nathan@0: cShuffle::~cShuffle(void) nathan@0: { nathan@0: Flush(); nathan@0: } nathan@0: nathan@0: void cShuffle::Flush(void) nathan@0: { nathan@0: delete shuffle; shuffle=0; nathan@0: max=0; nathan@0: } nathan@0: nathan@0: int cShuffle::Index(int pos) nathan@0: { nathan@0: if(pos>=0) nathan@0: for(int i=0; imax) { nathan@0: memcpy(ns,shuffle,max*sizeof(int)); nathan@0: oldmax=max; nathan@0: } nathan@0: delete shuffle; nathan@0: } nathan@0: shuffle=ns; max=num; nathan@0: } nathan@0: if(!oldmax) curr=-1; nathan@0: for(int i=oldmax ; i=2) { nathan@0: for(int i=in ; i= i); nathan@0: int t=shuffle[i]; nathan@0: shuffle[i]=shuffle[ran+in]; nathan@0: shuffle[ran+in]=t; nathan@0: } nathan@0: } nathan@0: #ifdef DEBUG nathan@0: printf("shuffle: order (%d , %d -> %d) ",num,curr,in); nathan@0: for(int i=0 ; i=0) { nathan@0: if(i+1=0 && i+10) ? shuffle[i-1] : -1; nathan@0: } nathan@0: nathan@0: int cShuffle::Goto(int pos, int curr) nathan@0: { nathan@0: int i=Index(curr); nathan@0: int g=Index(pos); nathan@0: if(g>=0) { nathan@0: if(gi) { nathan@0: for(int l=g; l>i+1; l--) shuffle[l]=shuffle[l-1]; nathan@0: shuffle[i+1]=pos; nathan@0: } nathan@0: #ifdef DEBUG nathan@0: printf("shuffle: goto order (%d -> %d , %d -> %d) ",pos,g,curr,i); nathan@0: for(int i=0 ; i0); nathan@0: shuffleMode=(MP3Setup.InitShuffleMode>0); nathan@0: } nathan@0: nathan@0: cPlayManager::~cPlayManager() nathan@0: { nathan@0: Flush(); nathan@0: Release(); nathan@0: listMutex.Lock(); nathan@0: stopscan=true; bgCond.Broadcast(); nathan@0: listMutex.Unlock(); nathan@0: Cancel(2); nathan@0: delete shuffle; nathan@0: } nathan@0: nathan@0: void cPlayManager::ThrottleWait(void) nathan@0: { nathan@0: while(!stopscan && !release && throttle) { nathan@0: db(printf("mgr: background scan throttled\n")) nathan@0: bgCond.Wait(listMutex); nathan@0: db(printf("mgr: background scan throttle wakeup\n")) nathan@0: } nathan@0: } nathan@0: nathan@0: void cPlayManager::Action(void) nathan@0: { nathan@0: db(printf("mgr: background scan thread started (pid=%d)\n", getpid())) nathan@0: nice(5); nathan@0: listMutex.Lock(); nathan@0: while(!stopscan) { nathan@0: for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) { nathan@0: ThrottleWait(); nathan@0: listMutex.Unlock(); nathan@0: if(!(scan->user & SCANNED_ID3)) { nathan@0: db(printf("mgr: scanning (id3) %s\n",scan->Name())) nathan@0: cSongInfo *si=scan->Info(true); nathan@0: if(si && si->Level>0.0) scan->user|=SCANNED_LVL; nathan@0: scan->user|=SCANNED_ID3; nathan@0: } nathan@0: listMutex.Lock(); nathan@0: } nathan@0: if(MP3Setup.BgrScan>1) { nathan@0: pass2=true; nathan@0: for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) { nathan@0: if(scan==curr) continue; nathan@0: ThrottleWait(); nathan@0: listMutex.Unlock(); nathan@0: if(!(scan->user & SCANNED_LVL)) { nathan@0: cDecoder *dec=scan->Decoder(); nathan@0: if(dec) { nathan@0: cSongInfo *si=scan->Info(false); nathan@0: if(!dec->IsStream() && (!si || si->Level<=0.0) && dec->Start()) { nathan@0: db(printf("mgr: scanning (lvl) %s\n",scan->Name())) nathan@0: cLevel level; nathan@0: level.Init(); nathan@0: bool go=true; nathan@0: while(go && !release) { nathan@0: if(throttle) { nathan@0: listMutex.Lock(); ThrottleWait(); listMutex.Unlock(); nathan@0: continue; nathan@0: } nathan@0: struct Decode *ds=dec->Decode(); nathan@0: switch(ds->status) { nathan@0: case dsPlay: nathan@0: level.GetPower(ds->pcm); nathan@0: break; nathan@0: case dsSkip: nathan@0: case dsSoftError: nathan@0: break; nathan@0: case dsEof: nathan@0: { nathan@0: double l=level.GetLevel(); nathan@0: if(l>0.0) { nathan@0: cSongInfo *si=dec->SongInfo(false); nathan@0: cFileInfo *fi=dec->FileInfo(); nathan@0: if(si && fi) { nathan@0: si->Level=l; nathan@0: si->Peak=level.GetPeak(); nathan@0: InfoCache.Cache(si,fi); nathan@0: } nathan@0: } nathan@0: } nathan@0: //fall through nathan@0: case dsOK: nathan@0: case dsError: nathan@0: scan->user|=SCANNED_LVL; nathan@0: go=false; nathan@0: break; nathan@0: } nathan@0: } nathan@0: } nathan@0: else scan->user|=SCANNED_LVL; nathan@0: dec->Stop(); nathan@0: } nathan@0: } nathan@0: listMutex.Lock(); nathan@0: } nathan@0: pass2=false; nathan@0: } nathan@0: do { nathan@0: scan=0; release=false; fgCond.Broadcast(); nathan@0: db(printf("mgr: background scan idle\n")) nathan@0: bgCond.Wait(listMutex); nathan@0: db(printf("mgr: background scan idle wakeup\n")) nathan@0: } while(!stopscan && (release || throttle)); nathan@0: } nathan@0: listMutex.Unlock(); nathan@0: db(printf("mgr: background scan thread ended (pid=%d)\n", getpid())) nathan@0: } nathan@0: nathan@0: void cPlayManager::Throttle(bool thr) nathan@0: { nathan@0: if(MP3Setup.BgrScan) { nathan@0: if(!thr && throttle) { nathan@0: db(printf("mgr: bgr-scan -> run (%d)\n",time_ms())) nathan@0: listMutex.Lock(); nathan@0: throttle=false; bgCond.Broadcast(); nathan@0: listMutex.Unlock(); nathan@0: } nathan@0: if(thr && !throttle) { nathan@0: db(printf("mgr: bgr-scan -> throttle (%d)\n",time_ms())) nathan@0: throttle=true; nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: void cPlayManager::ToggleShuffle(void) nathan@0: { nathan@0: shuffleMode=!shuffleMode; nathan@0: d(printf("mgr: shuffle mode toggled : %d\n",shuffleMode)) nathan@0: if(shuffleMode && !eol) { nathan@0: curr=0; currIndex=-1; nathan@0: shuffle->Shuffle(maxIndex+1,-1); nathan@0: Next(); nathan@0: } nathan@0: } nathan@0: nathan@0: void cPlayManager::ToggleLoop(void) nathan@0: { nathan@0: loopMode=!loopMode; nathan@0: d(printf("mgr: loop mode toggled : %d\n",loopMode)) nathan@0: } nathan@0: nathan@0: bool cPlayManager::Info(int num, cMP3PlayInfo *pi) nathan@0: { nathan@0: cSong *s; nathan@0: int idx=num-1; nathan@0: if(idx<0) { idx=currIndex; s=curr; } nathan@0: else { s=list.Get(idx); } nathan@0: memset(pi,0,sizeof(*pi)); nathan@0: pi->Num=idx+1; nathan@0: pi->MaxNum=maxIndex+1; nathan@0: pi->Loop=loopMode; nathan@0: pi->Shuffle=shuffleMode; nathan@0: bool res=false; nathan@0: if(s) { nathan@0: strn0cpy(pi->Title,s->Name(),sizeof(pi->Title)); nathan@0: strn0cpy(pi->Filename,s->FullPath(),sizeof(pi->Filename)); nathan@0: cSongInfo *si=s->Info(false); nathan@0: if(si && si->HasInfo()) { nathan@0: static char *modestr[] = { "Mono","Dual","Joint-Stereo","Stereo" }; nathan@0: nathan@0: if(si->Title) strn0cpy(pi->Title,si->Title,sizeof(pi->Title)); nathan@0: if(si->Artist) strn0cpy(pi->Artist,si->Artist,sizeof(pi->Artist)); nathan@0: if(si->Album) strn0cpy(pi->Album,si->Album,sizeof(pi->Album)); nathan@0: strn0cpy(pi->SMode,modestr[si->ChMode],sizeof(pi->SMode)); nathan@0: pi->Year=si->Year; nathan@0: pi->SampleFreq=si->SampleFreq; nathan@0: pi->Bitrate=si->Bitrate; nathan@0: pi->MaxBitrate=si->MaxBitrate; nathan@0: res=true; nathan@0: } nathan@0: } nathan@0: pi->Hash=MakeHashBuff((char *)pi,(char *)&pi->Loop-(char *)pi); nathan@0: return res; nathan@0: } nathan@0: nathan@0: void cPlayManager::Add(cPlayList *pl) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: bool real=false; nathan@0: for(cSong *song=pl->First(); song; song=pl->cList::Next(song)) { nathan@0: cSong *ns=new cSong(song); nathan@0: list.Add(ns); nathan@0: real=true; nathan@0: } nathan@0: if(real) { nathan@0: if(MP3Setup.BgrScan) { stopscan=false; if(!Active()) Start(); } nathan@0: else stopscan=true; nathan@0: bgCond.Broadcast(); nathan@0: maxIndex=list.Count()-1; nathan@0: if(shuffleMode) shuffle->Shuffle(maxIndex+1,currIndex); nathan@0: if(!curr) Next(); nathan@0: } nathan@0: } nathan@0: nathan@0: void cPlayManager::Flush(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: Halt(); nathan@0: list.Clear(); nathan@0: shuffle->Flush(); nathan@0: } nathan@0: nathan@0: void cPlayManager::Halt(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: curr=0; currIndex=-1; nathan@0: playNew=true; nathan@0: stopscan=true; bgCond.Broadcast(); nathan@0: NoScan(0); nathan@0: NoPlay(0); nathan@0: } nathan@0: nathan@0: void cPlayManager::NoScan(cSong *nono) nathan@0: { nathan@0: // call with listMutex locked!! nathan@0: while((nono && pass2 && scan==nono) || (!nono && scan)) { nathan@0: release=true; bgCond.Broadcast(); nathan@0: d(printf("mgr: waiting for bgr release ... (pass2=%d nono=%p scan=%p)\n",pass2,nono,scan)) nathan@0: fgCond.Wait(listMutex); nathan@0: } nathan@0: } nathan@0: nathan@0: void cPlayManager::NoPlay(cSong *nono) nathan@0: { nathan@0: // call with listMutex locked!! nathan@0: while((nono && play==nono) || (!nono && play)) { nathan@0: playNew=true; nathan@0: fgCond.Wait(listMutex); nathan@0: } nathan@0: } nathan@0: nathan@0: bool cPlayManager::Next(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: int ni; nathan@0: cSong *n; nathan@0: if(shuffleMode) { nathan@0: if(curr) { nathan@0: ni=shuffle->Next(currIndex); nathan@0: if(ni<0) { nathan@0: if(loopMode || eol) { nathan@0: shuffle->Shuffle(maxIndex+1,-1); nathan@0: ni=shuffle->First(); nathan@0: } nathan@0: else eol=true; nathan@0: } nathan@0: } nathan@0: else nathan@0: ni=shuffle->First(); nathan@0: n=(ni>=0) ? list.Get(ni) : 0; nathan@0: } nathan@0: else { nathan@0: if(curr) { nathan@0: n=list.cList::Next(curr); nathan@0: if(!n) { nathan@0: if(loopMode || eol) n=list.First(); nathan@0: else eol=true; nathan@0: } nathan@0: } nathan@0: else nathan@0: n=list.First(); nathan@0: ni=n ? n->Index() : -1; nathan@0: } nathan@0: if(n) { nathan@0: curr=n; currIndex=ni; nathan@0: playNew=true; eol=false; nathan@0: d(printf("mgr: next -> %d\n",currIndex)) nathan@0: return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: bool cPlayManager::Prev(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: int ni; nathan@0: cSong *n; nathan@0: if(shuffleMode) { nathan@0: ni=shuffle->Prev(currIndex); nathan@0: n=(ni>=0) ? list.Get(ni) : 0; nathan@0: } nathan@0: else { nathan@0: n=list.cList::Prev(curr); nathan@0: ni=n ? n->Index() : -1; nathan@0: } nathan@0: if(n) { nathan@0: curr=n; currIndex=ni; nathan@0: playNew=true; eol=false; nathan@0: d(printf("mgr: prev -> %d\n",currIndex)) nathan@0: return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: void cPlayManager::Goto(int num) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: if(num>0 && num<=maxIndex+1) { nathan@0: int idx=num-1; nathan@0: if(shuffleMode) { nathan@0: if(eol) { nathan@0: shuffle->Shuffle(maxIndex+1,-1); nathan@0: currIndex=shuffle->Goto(idx,-1); nathan@0: } nathan@0: else nathan@0: currIndex=shuffle->Goto(idx,currIndex); nathan@0: } nathan@0: else nathan@0: currIndex=idx; nathan@0: curr=(currIndex>=0) ? list.Get(currIndex) : 0; nathan@0: playNew=true; eol=false; nathan@0: d(printf("mgr: goto -> %d\n",currIndex)) nathan@0: } nathan@0: } nathan@0: nathan@0: cSong *cPlayManager::Current(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: if(!play) { nathan@0: NoScan(curr); nathan@0: play=curr; nathan@0: playNew=false; nathan@0: if(play) d(printf("mgr: playing %s\n",play->Name())) nathan@0: else d(printf("mgr: nothing to play\n")) nathan@0: fgCond.Broadcast(); nathan@0: } nathan@0: return play; nathan@0: } nathan@0: nathan@0: bool cPlayManager::NextCurrent(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: return (!eol && (playNew || Next())); nathan@0: } nathan@0: nathan@0: bool cPlayManager::NewCurrent(void) nathan@0: { nathan@0: return playNew; nathan@0: } nathan@0: nathan@0: void cPlayManager::Release(void) nathan@0: { nathan@0: cMutexLock lock(&listMutex); nathan@0: play=0; nathan@0: fgCond.Broadcast(); nathan@0: } nathan@0: nathan@0: // --- cOutput ----------------------------------------------------------------- nathan@0: nathan@0: struct FrameHeader { nathan@0: unsigned int samplerate; nathan@0: }; nathan@0: #define FHS sizeof(struct FrameHeader) nathan@0: nathan@0: class cOutput { nathan@0: protected: nathan@0: cMP3Player *player; nathan@0: cScale scale; nathan@0: public: nathan@0: cOutput(cMP3Player *Player); nathan@0: virtual ~cOutput() {} nathan@0: virtual void Init(void); nathan@0: virtual unsigned int SampleRate(unsigned int PcmSampleRate)=0; nathan@0: virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)=0; nathan@0: virtual int Output(const unsigned char *Data, int Len, bool SOF)=0; nathan@0: virtual bool Poll(void)=0; nathan@0: virtual void Play(void)=0; nathan@0: virtual void Pause(void)=0; nathan@0: #ifdef DEBUG nathan@0: virtual void Stats(void); nathan@0: #endif nathan@0: }; nathan@0: nathan@0: cOutput::cOutput(cMP3Player *Player) nathan@0: { nathan@0: player=Player; nathan@0: } nathan@0: nathan@0: void cOutput::Init(void) nathan@0: { nathan@0: scale.Init(); nathan@0: } nathan@0: nathan@0: #ifdef DEBUG nathan@0: void cOutput::Stats(void) nathan@0: { nathan@0: scale.Stats(); nathan@0: } nathan@0: #endif nathan@0: nathan@0: // --- cOutputDvb -------------------------------------------------------------- nathan@0: nathan@0: /* nathan@0: struct LPCMHeader { int id:8; // id nathan@0: int frame_count:8; // number of frames nathan@0: int access_ptr:16; // first acces unit pointer, i.e. start of audio frame nathan@0: bool emphasis:1; // audio emphasis on-off nathan@0: bool mute:1; // audio mute on-off nathan@0: bool reserved:1; // reserved nathan@0: int frame_number:5; // audio frame number nathan@0: int quant_wlen:2; // quantization word length nathan@0: int sample_freq:2; // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3) nathan@0: bool reserved2:1; // reserved nathan@0: int chan_count:3; // number of audio channels - 1 (e.g. stereo = 1) nathan@0: int dyn_range_ctrl:8; // dynamic range control (0x80 if off) nathan@0: }; nathan@0: */ nathan@0: nathan@0: #define FRAMESIZE 2048 // max. frame size allowed for DVB driver nathan@0: nathan@0: class cOutputDvb : public cOutput { nathan@0: private: nathan@0: cPoller poll; nathan@0: unsigned int outSr; nathan@0: bool only48khz; nathan@0: public: nathan@0: cOutputDvb(cMP3Player *Player); nathan@0: virtual unsigned int SampleRate(unsigned int PcmSampleRate); nathan@0: virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr); nathan@0: virtual int Output(const unsigned char *Data, int Len, bool SOF); nathan@0: virtual bool Poll(void); nathan@0: virtual void Play(void); nathan@0: virtual void Pause(void); nathan@0: }; nathan@0: nathan@0: cOutputDvb::cOutputDvb(cMP3Player *Player) nathan@0: :cOutput(Player) nathan@0: { nathan@0: only48khz=MP3Setup.Only48kHz; nathan@0: outSr=0; nathan@0: #if APIVERSNUM == 10318 nathan@0: cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttDolbyFirst); nathan@0: #elif APIVERSNUM >= 10319 nathan@0: cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio); nathan@0: #endif nathan@0: d(printf("mp3-dvb: using DVB output\n")) nathan@0: } nathan@0: nathan@0: unsigned int cOutputDvb::SampleRate(unsigned int PcmSampleRate) nathan@0: { nathan@0: unsigned int samplerate=48000; nathan@0: if(!only48khz) { nathan@0: switch(PcmSampleRate) { // If one of the supported frequencies, do it without resampling. nathan@0: case 96000: // Select a "even" upsampling frequency if possible, too. nathan@0: samplerate=96000; nathan@0: break; nathan@0: //case 48000: // this is already the default ... nathan@0: // samplerate=48000; nathan@0: // break; nathan@0: case 11025: nathan@0: case 22050: nathan@0: case 44100: nathan@0: samplerate=44100; nathan@0: break; nathan@0: case 8000: nathan@0: case 16000: nathan@0: case 32000: nathan@0: samplerate=32000; nathan@0: break; nathan@0: } nathan@0: } nathan@0: return samplerate; nathan@0: } nathan@0: nathan@0: cFrame *cOutputDvb::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr) nathan@0: { nathan@0: static const unsigned char header[] = { nathan@0: 0x00, // PES header nathan@0: 0x00, nathan@0: 0x01, nathan@0: 0xBD, // private stream nathan@0: 0x00, nathan@0: 0x00, nathan@0: 0x87, // mpeg2, aligned, copyright, original nathan@0: 0x00, // no pts/dts nathan@0: 0x00, // PES header data len nathan@0: 0xA0, // aLPCM header nathan@0: 0xFF, nathan@0: 0x00, nathan@0: 0x04, nathan@0: 0x00, nathan@0: 0x01, // 2-channel stereo (n-1) nathan@0: 0x80 // neutral dynamic range nathan@0: }; nathan@0: cFrame *f=0; nathan@0: unsigned char *buff=MALLOC(uchar,FRAMESIZE); nathan@0: if(buff) { nathan@0: struct FrameHeader *fh=(struct FrameHeader *)buff; nathan@0: fh->samplerate=sr; nathan@0: memcpy(buff+FHS,header,sizeof(header)); nathan@0: int srMode; nathan@0: switch(sr) { nathan@0: default: nathan@0: case 48000: srMode=0<<4; break; nathan@0: case 96000: srMode=1<<4; break; nathan@0: case 44100: srMode=2<<4; break; nathan@0: case 32000: srMode=3<<4; break; nathan@0: } nathan@0: buff[14+FHS]|=srMode; nathan@0: unsigned int outlen=scale.ScaleBlock(buff+sizeof(header)+FHS,FRAMESIZE-sizeof(header)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherBE:amRoundBE); nathan@0: if(outlen) { nathan@0: // lPCM has 600 fps which is 80 samples at 48kHz per channel nathan@0: // Frame size = (sample_rate * quantization * channels)/4800 nathan@0: buff[10+FHS]=outlen*(4800/16/2)/sr; nathan@0: outlen+=(sizeof(header)-6); nathan@0: buff[4+FHS]=outlen>>8; nathan@0: buff[5+FHS]=outlen; nathan@0: f=new cFrame(buff,-(outlen+6+FHS),ftUnknown,index); nathan@0: } nathan@0: if(!f) free(buff); nathan@0: } nathan@0: return f; nathan@0: } nathan@0: nathan@0: #ifdef BROKEN_PCM nathan@0: #include "player-mp3-sample.c" nathan@0: #endif nathan@0: nathan@0: int cOutputDvb::Output(const unsigned char *Data, int Len, bool SOF) nathan@0: { nathan@0: int n=0; nathan@0: if(SOF) { nathan@0: #ifdef BROKEN_PCM nathan@0: struct FrameHeader *fh=(struct FrameHeader *)Data; nathan@0: if(fh->samplerate!=outSr) { nathan@0: if(outSr) { nathan@0: // at this point we would need access to AUDIO_STOP/AUDIO_PLAY nathan@0: // ioctl, but unfortunaly VDR's API doesn't provides this. nathan@0: // So we have to do magic to make the driver switch samplerate. nathan@0: const unsigned char *p=testAudio; nathan@0: int pc=sizeof(testAudio); nathan@0: int r; nathan@0: do { nathan@0: #if APIVERSNUM < 10318 nathan@0: r=player->PlayVideo(p,pc); nathan@0: #else nathan@0: r=player->PlayPes(p,pc); nathan@0: #endif nathan@0: if(r>0) { p+=r; pc-=r; } nathan@0: if(r==0) Poll(); nathan@0: } while(r>=0 && pc>0); nathan@0: } nathan@0: outSr=fh->samplerate; nathan@0: d(printf("mp3-dvb: output samplerate now %d\n",outSr)) nathan@0: } nathan@0: #endif nathan@0: n=FHS; nathan@0: Data+=n; Len-=n; nathan@0: } nathan@0: #if APIVERSNUM < 10318 nathan@0: int r=player->PlayVideo(Data,Len); nathan@0: #else nathan@0: int r=player->PlayPes(Data,Len); nathan@0: #endif nathan@0: return (r>=0 ? r+n : -1); nathan@0: } nathan@0: nathan@0: bool cOutputDvb::Poll(void) nathan@0: { nathan@0: return player->DevicePoll(poll,500); nathan@0: } nathan@0: nathan@0: void cOutputDvb::Play(void) nathan@0: { nathan@0: #ifndef BROKEN_PCM nathan@0: player->DevicePlay(); nathan@0: #endif nathan@0: } nathan@0: nathan@0: void cOutputDvb::Pause(void) nathan@0: { nathan@0: #ifndef BROKEN_PCM nathan@0: player->DeviceFreeze(); nathan@0: #endif nathan@0: } nathan@0: nathan@0: // --- cOutputOss -------------------------------------------------------------- nathan@0: nathan@0: #ifdef WITH_OSS nathan@0: nathan@0: const char *dspdevice="/dev/dsp"; nathan@0: nathan@0: class cOutputOss : public cOutput { nathan@0: private: nathan@0: int fd; nathan@0: cPoller poll; nathan@0: unsigned int outSr; nathan@0: unsigned char buff[8192]; nathan@0: // nathan@0: bool Reset(unsigned int sr); nathan@0: public: nathan@0: cOutputOss(cMP3Player *Player); nathan@0: virtual ~cOutputOss(); nathan@0: virtual void Init(void); nathan@0: virtual unsigned int SampleRate(unsigned int PcmSampleRate); nathan@0: virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr); nathan@0: virtual int Output(const unsigned char *Data, int Len, bool SOF); nathan@0: virtual bool Poll(void); nathan@0: virtual void Play(void); nathan@0: virtual void Pause(void); nathan@0: }; nathan@0: nathan@0: cOutputOss::cOutputOss(cMP3Player *Player) nathan@0: :cOutput(Player) nathan@0: { nathan@0: fd=-1; outSr=0; nathan@0: d(printf("mp3-oss: using OSS output\n")) nathan@0: } nathan@0: nathan@0: cOutputOss::~cOutputOss() nathan@0: { nathan@0: close(fd); nathan@0: } nathan@0: nathan@0: void cOutputOss::Init(void) nathan@0: { nathan@0: if(fd<0) { nathan@0: fd=open(dspdevice,O_WRONLY|O_NONBLOCK); nathan@0: if(fd>=0) poll.Add(fd,true); nathan@0: else esyslog("ERROR: Cannot open dsp device '%s': %s!",dspdevice,strerror(errno)); nathan@0: } nathan@0: cOutput::Init(); nathan@0: } nathan@0: nathan@0: bool cOutputOss::Reset(unsigned int sr) nathan@0: { nathan@0: if(fd>=0) { nathan@0: CHECK(ioctl(fd,SNDCTL_DSP_SYNC,0)); nathan@0: int format=AFMT_S16_LE; nathan@0: CHECK(ioctl(fd,SNDCTL_DSP_SETFMT,&format)); nathan@0: if(format==AFMT_S16_LE) { nathan@0: int channels=2; nathan@0: CHECK(ioctl(fd,SNDCTL_DSP_CHANNELS,&channels)); nathan@0: if(channels==2) { nathan@0: int real=sr; nathan@0: CHECK(ioctl(fd,SNDCTL_DSP_SPEED,&real)); nathan@0: d(printf("oss: DSP samplerate now %d\n",real)) nathan@0: if(abs(real-sr)samplerate=sr; nathan@0: cFrame *f=0; nathan@0: unsigned int outlen=scale.ScaleBlock(buff+FHS,sizeof(buff)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherLE:amRoundLE); nathan@0: if(outlen) nathan@0: f=new cFrame(buff,outlen+FHS,ftUnknown,index); nathan@0: return f; nathan@0: } nathan@0: nathan@0: int cOutputOss::Output(const unsigned char *Data, int Len, bool SOF) nathan@0: { nathan@0: if(fd>=0) { nathan@0: int n=0; nathan@0: if(SOF) { nathan@0: struct FrameHeader *fh=(struct FrameHeader *)Data; nathan@0: if(fh->samplerate!=outSr) Reset(fh->samplerate); nathan@0: n=FHS; nathan@0: Data+=n; Len-=n; nathan@0: } nathan@0: int r=write(fd,Data,Len); nathan@0: if(r<0 && !FATALERRNO) r=0; nathan@0: if(r>=0) return n+r; nathan@0: } nathan@0: return -1; nathan@0: } nathan@0: nathan@0: bool cOutputOss::Poll(void) nathan@0: { nathan@0: return fd>=0 ? poll.Poll(500) : false; nathan@0: } nathan@0: nathan@0: void cOutputOss::Play(void) nathan@0: { nathan@0: } nathan@0: nathan@0: void cOutputOss::Pause(void) nathan@0: { nathan@0: CHECK(ioctl(fd,SNDCTL_DSP_POST,0)); nathan@0: } nathan@0: nathan@0: #endif nathan@0: nathan@0: // --- cMP3Player -------------------------------------------------------------- nathan@0: nathan@0: cMP3Player::cMP3Player() nathan@0: :cPlayer(MP3Setup.BackgrMode==1 ? pmAudioOnly : pmAudioOnlyBlack) nathan@0: { nathan@0: active=true; started=false; isStream=false; nathan@0: ringBuffer=new cRingBufferFrame(MP3BUFSIZE); nathan@0: rframe=0; pframe=0; decoder=0; output=0; nathan@0: playMode=pmStartup; state=msStop; nathan@0: playindex=total=0; nathan@0: } nathan@0: nathan@0: cMP3Player::~cMP3Player() nathan@0: { nathan@0: Detach(); nathan@0: delete ringBuffer; nathan@0: } nathan@0: nathan@0: void cMP3Player::Activate(bool On) nathan@0: { nathan@0: if(On) { nathan@0: d(printf("mp3: player active true requested...\n")) nathan@0: if(!started) { nathan@0: playMode=pmStartup; Start(); started=true; nathan@0: playModeMutex.Lock(); nathan@0: WaitPlayMode(pmStartup,true); // wait for the decoder to become ready nathan@0: playModeMutex.Unlock(); nathan@0: Lock(); nathan@0: Play(); nathan@0: Unlock(); nathan@0: } nathan@0: d(printf("mp3: player active true done\n")) nathan@0: } nathan@0: else if(started && active) { nathan@0: d(printf("mp3: player active false requested...\n")) nathan@0: Lock(); StopPlay(); Unlock(); nathan@0: active=false; nathan@0: SetPlayMode(pmStartup); nathan@0: Cancel(2); nathan@0: d(printf("mp3: player active false done\n")) nathan@0: } nathan@0: } nathan@0: nathan@0: void cMP3Player::SetPlayMode(ePlayMode mode) nathan@0: { nathan@0: playModeMutex.Lock(); nathan@0: if(mode!=playMode) { nathan@0: playMode=mode; nathan@0: dm(printf("mp3: setting mode=%d (pid=%d)\n",mode,getpid())) nathan@0: playModeCond.Broadcast(); nathan@0: } nathan@0: playModeMutex.Unlock(); nathan@0: } nathan@0: nathan@0: void cMP3Player::WaitPlayMode(ePlayMode mode, bool inv) nathan@0: { nathan@0: // must be called with playModeMutex LOCKED !!! nathan@0: nathan@0: while(active && ((!inv && mode!=playMode) || (inv && mode==playMode))) { nathan@0: dm(printf("mp3: entering wait for mode%s%d with mode=%d (pid=%d)\n",inv?"!=":"==",mode,playMode,getpid())) nathan@0: playModeCond.Wait(playModeMutex); nathan@0: dm(printf("mp3: returning from wait with mode=%d (pid=%d)\n",playMode,getpid())) nathan@0: } nathan@0: } nathan@0: nathan@0: void cMP3Player::Action(void) nathan@0: { nathan@0: cSong *playing=0; nathan@0: struct mad_pcm *pcm=0; nathan@0: cResample resample[2]; nathan@0: unsigned int nsamples[2]; nathan@0: const mad_fixed_t *data[2]; nathan@0: cLevel level; nathan@0: cNormalize norm; nathan@0: bool haslevel=false; nathan@0: const unsigned char *p=0; nathan@0: int pc=0, readindex=0; nathan@0: bool imageValid=true; nathan@0: int imageCheck=0; nathan@0: #ifdef DEBUG nathan@0: int beat=0; nathan@0: #endif nathan@0: #ifdef DEBUG_DELAY nathan@0: int lastwrite=0; nathan@0: #endif nathan@0: nathan@0: dsyslog("mp3: player thread started (pid=%d)", getpid()); nathan@0: state=msStop; nathan@0: SetPlayMode(pmStopped); nathan@0: nathan@0: delete output; output=0; nathan@0: #ifdef WITH_OSS nathan@0: if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) output=new cOutputOss(this); nathan@0: #endif nathan@0: if(MP3Setup.AudioOutMode==AUDIOOUTMODE_DVB) output=new cOutputDvb(this); nathan@0: if(!output) { nathan@0: d(printf("mp3: audiooutmode mismatch or no output driver\n")) nathan@0: esyslog("ERROR: no audio output driver. balling out"); nathan@0: goto abort; nathan@0: } nathan@0: nathan@0: while(active) { nathan@0: #ifdef DEBUG nathan@0: { nathan@0: int now=time(0); nathan@0: if(now>=beat) { nathan@0: int avail=ringBuffer->Available(); nathan@0: printf("mp3: heartbeat buffer=%d now=%d\n",avail,now&4095); nathan@0: //output->Stats(); if(haslevel) norm.Stats(); nathan@0: beat=now+(avail>(MP3BUFSIZE*10/100) ? (avail<(MP3BUFSIZE*50/100) ? 2 : 20) : 1); nathan@0: } nathan@0: } nathan@0: #endif nathan@0: nathan@0: Lock(); nathan@0: nathan@0: next: nathan@0: if(!pframe && playing && !imageValid && imageCheckImage(mem,len); nathan@0: if(mem) { nathan@0: if(playindex) SLEEP(80); // stillpicture ioctl freezes without this nathan@0: DeviceStillPicture(mem,len); nathan@0: free(mem); nathan@0: } nathan@0: } nathan@0: nathan@0: bool SOF=false; nathan@0: if(!pframe && playMode==pmPlay) { nathan@0: pframe=ringBuffer->Get(); nathan@0: if(pframe) { nathan@0: playindex=pframe->Index(); nathan@0: p=pframe->Data(); nathan@0: pc=pframe->Count(); nathan@0: SOF=true; nathan@0: } nathan@0: } nathan@0: nathan@0: if(pframe) { nathan@0: #ifdef DEBUG_DELAY nathan@0: { nathan@0: int now=time_ms(); nathan@0: if(lastwrite && lastwriteOutput(p,pc,SOF); nathan@0: if(w>0) { nathan@0: p+=w; pc-=w; nathan@0: if(pc<=0) { nathan@0: ringBuffer->Drop(pframe); nathan@0: pframe=0; nathan@0: goto next; nathan@0: } nathan@0: } nathan@0: else if(w<0 && FATALERRNO) { nathan@0: LOG_ERROR; nathan@0: d(printf("mp3: output failed: %s\n",strerror(errno))) nathan@0: Unlock(); nathan@0: goto abort; nathan@0: } nathan@0: } nathan@0: nathan@0: if(mgr->NewCurrent() && playMode==pmPlay && state!=msStart) { nathan@0: Empty(); nathan@0: state=msRestart; nathan@0: d(printf("mp3: stale song change, restart.\n")) nathan@0: } nathan@0: nathan@0: if(!rframe && playMode==pmPlay) { nathan@0: switch(state) { nathan@0: case msStart: nathan@0: d(printf("mp3: starting play\n")) nathan@0: mgr->Throttle(true); nathan@0: playindex=readindex=total=0; nathan@0: playing=mgr->Current(); nathan@0: if(playing) { nathan@0: if((decoder=playing->Decoder()) && decoder->Start()) { nathan@0: isStream=decoder->IsStream(); levelgood=!isStream; haslevel=false; nathan@0: cSongInfo *si=playing->Info(true); nathan@0: if(si) { nathan@0: if(si->Level>0.0) { nathan@0: d(printf("mp3: found song level=%f peak=%f\n",si->Level,si->Peak)) nathan@0: haslevel=true; nathan@0: norm.Init(si->Level,si->Peak); nathan@0: } nathan@0: if(si->HasInfo()) nathan@0: total=SecondsToFrames(si->Total); nathan@0: } nathan@0: d(printf("mp3: isStream=%d levelgood=%d haslevel=%d\n",isStream,levelgood,haslevel)) nathan@0: output->Init(); nathan@0: level.Init(); nathan@0: if(MP3Setup.BackgrMode==2) imageValid=false; nathan@0: state=msDecode; nathan@0: break; nathan@0: } nathan@0: else nathan@0: esyslog("ERROR: playlist entry %s is not a valid file",playing->Name()); nathan@0: } nathan@0: else nathan@0: d(printf("mp3: no current on start play\n")) nathan@0: state=msEof; nathan@0: break; nathan@0: case msDecode: nathan@0: { nathan@0: #ifdef DEBUG_DELAY nathan@0: int now=time_ms(); nathan@0: #endif nathan@0: struct Decode *ds=decoder->Decode(); nathan@0: #ifdef DEBUG_DELAY nathan@0: now=time_ms()-now; nathan@0: if(now>DEBUG_DELAY) printf("mp3: decode delayed %d ms\n",now); nathan@0: #endif nathan@0: switch(ds->status) { nathan@0: case dsPlay: nathan@0: pcm=ds->pcm; nathan@0: readindex=ds->index; nathan@0: state=msNormalize; nathan@0: break; nathan@0: case dsSkip: nathan@0: case dsSoftError: nathan@0: // skipping, state unchanged, next decode nathan@0: break; nathan@0: case dsEof: nathan@0: if(!haslevel && levelgood) { // save level & peak to infocache on eof nathan@0: double l=level.GetLevel(); nathan@0: if(l>0.0) { nathan@0: cSongInfo *si=decoder->SongInfo(false); nathan@0: cFileInfo *fi=decoder->FileInfo(); nathan@0: if(si && fi) { nathan@0: si->Level=l; nathan@0: si->Peak=level.GetPeak(); nathan@0: InfoCache.Cache(si,fi); nathan@0: } nathan@0: } nathan@0: } nathan@0: state=msEof; nathan@0: break; nathan@0: case dsOK: nathan@0: case dsError: nathan@0: state=msError; nathan@0: break; nathan@0: } nathan@0: break; nathan@0: } nathan@0: case msNormalize: nathan@0: if(!haslevel) { if(levelgood) level.GetPower(pcm); } nathan@0: else norm.AddGain(pcm); nathan@0: state=msResample; nathan@0: break; nathan@0: case msResample: nathan@0: #ifdef DEBUG nathan@0: { nathan@0: static unsigned int oldrate=0; nathan@0: if(oldrate!=pcm->samplerate) { nathan@0: printf("mp3: new input sample rate %d\n",pcm->samplerate); nathan@0: oldrate=pcm->samplerate; nathan@0: } nathan@0: } nathan@0: #endif nathan@0: nsamples[0]=nsamples[1]=pcm->length; nathan@0: data[0]=pcm->samples[0]; nathan@0: data[1]=pcm->channels>1 ? pcm->samples[1]:0; nathan@0: nathan@0: dvbSampleRate=output->SampleRate(pcm->samplerate); nathan@0: if(dvbSampleRate!=pcm->samplerate) { nathan@0: if(resample[0].SetInputRate(pcm->samplerate,dvbSampleRate)) { nathan@0: nsamples[0]=resample[0].ResampleBlock(nsamples[0],data[0]); nathan@0: data[0] =resample[0].Resampled(); nathan@0: } nathan@0: if(data[1] && resample[1].SetInputRate(pcm->samplerate,dvbSampleRate)) { nathan@0: nsamples[1]=resample[1].ResampleBlock(nsamples[1],data[1]); nathan@0: data[1] =resample[1].Resampled(); nathan@0: } nathan@0: } nathan@0: state=msOutput; nathan@0: break; nathan@0: case msOutput: nathan@0: if(nsamples[0]>0) rframe=output->MakeFrame(nsamples[0],data,readindex,dvbSampleRate); nathan@0: else state=msDecode; nathan@0: break; nathan@0: case msError: nathan@0: case msEof: nathan@0: d(printf("mp3: eof or error\n")) nathan@0: state=msWait; nathan@0: // fall through nathan@0: case msRestart: nathan@0: case msStop: nathan@0: d(printf("mp3: stopping play\n")) nathan@0: if(decoder) { decoder->Stop(); decoder=0; } nathan@0: mgr->Release(); playing=0; imageValid=true; nathan@0: levelgood=false; nathan@0: #ifdef DEBUG nathan@0: output->Stats(); if(haslevel) norm.Stats(); nathan@0: #endif nathan@0: if(state==msStop) SetPlayMode(pmStopped); nathan@0: if(state==msRestart) state=msStart; nathan@0: break; nathan@0: case msWait: nathan@0: if(ringBuffer->Available()==0) { nathan@0: if(mgr->NextCurrent()) { nathan@0: d(printf("mp3: playing next\n")) nathan@0: state=msStart; nathan@0: } nathan@0: else { nathan@0: d(printf("mp3: end of playlist\n")) nathan@0: if(MP3Setup.AbortAtEOL) { nathan@0: active=false; nathan@0: d(printf("mp3: aborting player...\n")) nathan@0: } nathan@0: else d(printf("mp3: player idle...\n")) nathan@0: SetPlayMode(pmStopped); nathan@0: } nathan@0: } nathan@0: break; nathan@0: } nathan@0: } nathan@0: nathan@0: if(rframe && ringBuffer->Put(rframe)) rframe=0; nathan@0: nathan@0: Unlock(); nathan@0: nathan@0: if((rframe || state==msWait) && pframe) { nathan@0: mgr->Throttle(false); nathan@0: output->Poll(); nathan@0: } nathan@0: else if(playMode!=pmPlay) { nathan@0: mgr->Throttle(false); nathan@0: if(!imageValid) nathan@0: SLEEP(100); nathan@0: else { nathan@0: playModeMutex.Lock(); nathan@0: if(playMode!=pmPlay) WaitPlayMode(playMode,true); nathan@0: playModeMutex.Unlock(); nathan@0: } nathan@0: #ifdef DEBUG_DELAY nathan@0: lastwrite=0; nathan@0: #endif nathan@0: } nathan@0: else if(state!=msWait && ringBuffer->Available()<(MP3BUFSIZE*50/100)) { nathan@0: mgr->Throttle(true); nathan@0: } nathan@0: } nathan@0: nathan@0: abort: nathan@0: Lock(); nathan@0: delete rframe; nathan@0: delete output; output=0; nathan@0: if(decoder) { decoder->Stop(); decoder=0; } nathan@0: mgr->Release(); playing=0; nathan@0: SetPlayMode(pmStopped); nathan@0: Unlock(); nathan@0: active=false; nathan@0: nathan@0: dsyslog("mp3: player thread ended (pid=%d)", getpid()); nathan@0: } nathan@0: nathan@0: void cMP3Player::Empty(void) nathan@0: { nathan@0: Lock(); nathan@0: delete rframe; rframe=0; pframe=0; nathan@0: ringBuffer->Clear(); nathan@0: DeviceClear(); nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: void cMP3Player::StopPlay(void) // StopPlay() must be called in locked state!!! nathan@0: { nathan@0: if(playMode!=pmStopped) { nathan@0: Empty(); nathan@0: state=msStop; nathan@0: SetPlayMode(pmPlay); nathan@0: Unlock(); // let the decode thread process the stop signal nathan@0: playModeMutex.Lock(); nathan@0: WaitPlayMode(pmStopped,false); nathan@0: playModeMutex.Unlock(); nathan@0: Lock(); nathan@0: } nathan@0: } nathan@0: nathan@0: void cMP3Player::Pause(void) nathan@0: { nathan@0: Lock(); nathan@0: if(playMode==pmPaused) Play(); nathan@0: else if(playMode==pmPlay && !isStream) { nathan@0: d(printf("mp3: pause\n")) nathan@0: if(output) output->Pause(); nathan@0: SetPlayMode(pmPaused); nathan@0: } nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: void cMP3Player::Play(void) nathan@0: { nathan@0: Lock(); nathan@0: if(playMode!=pmPlay) { nathan@0: d(printf("mp3: play\n")) nathan@0: if(playMode==pmStopped) state=msStart; nathan@0: if(output) output->Play(); nathan@0: SetPlayMode(pmPlay); nathan@0: } nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: bool cMP3Player::PrevCheck(void) nathan@0: { nathan@0: bool res=false; nathan@0: Lock(); nathan@0: if(playindex>=2000 && !isStream) { nathan@0: state=msRestart; res=true; nathan@0: Empty(); nathan@0: d(printf("mp3: skip to start of song\n")) nathan@0: } nathan@0: Unlock(); nathan@0: return res; nathan@0: } nathan@0: nathan@0: void cMP3Player::SkipSeconds(int secs) nathan@0: { nathan@0: if(playMode!=pmStopped && !isStream) { nathan@0: Lock(); nathan@0: d(printf("mp3: skip secs %d\n",secs)) nathan@0: if(playMode==pmPaused) SetPlayMode(pmPlay); nathan@0: float bufsecs=(float)ringBuffer->Available() / (float)(dvbSampleRate*OUT_FACT); nathan@0: d(printf("mp3: ringbuffer available %f secs\n",bufsecs)) nathan@0: if(secs>0 && bufsecs>=(float)secs) { nathan@0: // clear intermediate queue nathan@0: if(pframe) { nathan@0: ringBuffer->Drop(pframe); nathan@0: pframe=0; nathan@0: } nathan@0: DeviceClear(); nathan@0: // skip inside ringbuffer nathan@0: int skipindex=playindex+secs*1000; nathan@0: d(printf("mp3: skipping play=%d skip=%d ...",playindex,skipindex)) nathan@0: cFrame *f; nathan@0: do { nathan@0: f=ringBuffer->Get(); nathan@0: if(f) { nathan@0: playindex=f->Index(); nathan@0: ringBuffer->Drop(f); nathan@0: d(printf("*")) nathan@0: } nathan@0: } while(f && playindexSkip(secs,bufsecs)) levelgood=false; nathan@0: Empty(); nathan@0: } nathan@0: Unlock(); nathan@0: } nathan@0: } nathan@0: nathan@0: bool cMP3Player::GetIndex(int &Current, int &Total, bool SnapToIFrame) nathan@0: { nathan@0: Current=SecondsToFrames(playindex/1000); Total=total; nathan@0: return total>=0; nathan@0: } nathan@0: nathan@0: bool cMP3Player::GetReplayMode(bool &Play, bool &Forward, int &Speed) nathan@0: { nathan@0: Play=(playMode==pmPlay); nathan@0: Forward=true; nathan@0: Speed=-1; nathan@0: return true; nathan@0: }