player-mp3.c
author nathan
Sun, 06 Dec 2009 08:48:57 +0800
branchtrunk
changeset 34 afc13760179b
parent 29 640ce9201139
permissions -rw-r--r--
fixed gcc 4.4.1 const errors
nathan@0
     1
/*
nathan@0
     2
 * MP3/MPlayer plugin to VDR (C++)
nathan@0
     3
 *
nathan@22
     4
 * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
nathan@0
     5
 *
nathan@0
     6
 * This code is free software; you can redistribute it and/or
nathan@0
     7
 * modify it under the terms of the GNU General Public License
nathan@0
     8
 * as published by the Free Software Foundation; either version 2
nathan@0
     9
 * of the License, or (at your option) any later version.
nathan@0
    10
 *
nathan@0
    11
 * This code is distributed in the hope that it will be useful,
nathan@0
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nathan@0
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nathan@0
    14
 * GNU General Public License for more details.
nathan@0
    15
 *
nathan@0
    16
 * You should have received a copy of the GNU General Public License
nathan@0
    17
 * along with this program; if not, write to the Free Software
nathan@0
    18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
nathan@0
    19
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
nathan@0
    20
 */
nathan@0
    21
nathan@0
    22
#include <stdlib.h>
nathan@0
    23
#include <stdio.h>
nathan@0
    24
#include <sys/ioctl.h>
nathan@0
    25
#include <math.h>
nathan@0
    26
#ifdef WITH_OSS
nathan@0
    27
#include <sys/soundcard.h>
nathan@0
    28
#endif
nathan@0
    29
nathan@0
    30
#include <mad.h>
nathan@0
    31
#include <id3tag.h>
nathan@0
    32
nathan@0
    33
#include <vdr/player.h>
nathan@0
    34
#include <vdr/ringbuffer.h>
nathan@0
    35
#include <vdr/thread.h>
nathan@0
    36
#include <vdr/tools.h>
nathan@0
    37
nathan@0
    38
#include "common.h"
nathan@0
    39
#include "setup-mp3.h"
nathan@0
    40
#include "player-mp3.h"
nathan@0
    41
#include "data-mp3.h"
nathan@0
    42
#include "decoder.h"
nathan@0
    43
#include "decoder-core.h"
nathan@0
    44
nathan@0
    45
#ifndef NO_DEBUG
nathan@0
    46
//#define DEBUG_MODE      // debug playmode changes
nathan@0
    47
#define DEBUG_BGR       // debug backround scan thread
nathan@0
    48
#define DEBUG_DELAY 300 // debug write/decode delays
nathan@0
    49
//#define ACC_DUMP        // dump limiter lookup table to /tmp/limiter
nathan@0
    50
#endif
nathan@0
    51
nathan@0
    52
#if !defined(NO_DEBUG) && defined(DEBUG_MODE)
nathan@0
    53
#define dm(x) { (x); }
nathan@0
    54
#else
nathan@0
    55
#define dm(x) ; 
nathan@0
    56
#endif
nathan@0
    57
nathan@0
    58
#if !defined(NO_DEBUG) && defined(DEBUG_BGR)
nathan@0
    59
#define db(x) { (x); }
nathan@0
    60
#else
nathan@0
    61
#define db(x) ; 
nathan@0
    62
#endif
nathan@0
    63
nathan@0
    64
// ----------------------------------------------------------------
nathan@0
    65
nathan@0
    66
#define MP3BUFSIZE (1024*1024)                               // output ringbuffer size
nathan@0
    67
#define OUT_BITS 16                                          // output 16 bit samples to DVB driver
nathan@0
    68
#define OUT_FACT (OUT_BITS/8*2)                              // output factor is 16 bit & 2 channels -> 4 bytes
nathan@0
    69
// cResample
nathan@0
    70
#define MAX_NSAMPLES (1152*7)                                // max. buffer for resampled frame
nathan@0
    71
// cNormalize
nathan@0
    72
#define MIN_GAIN   0.03                                      // min. gain required to launch the normalizer
nathan@0
    73
#define MAX_GAIN   3.0                                       // max. allowed gain
nathan@0
    74
#define USE_FAST_LIMITER
nathan@0
    75
#define LIM_ACC    12                                        // bit, accuracy for lookup table
nathan@0
    76
#define F_LIM_MAX  (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1)  // max. value covered by lookup table
nathan@0
    77
#define LIM_SHIFT  (MAD_F_FRACBITS-LIM_ACC)                  // shift value for table lookup
nathan@0
    78
#define F_LIM_JMP  (mad_fixed_t)(1<<LIM_SHIFT)               // lookup table jump between values
nathan@0
    79
// cLevel
nathan@0
    80
#define POW_WIN 100                                          // window width for smoothing power values
nathan@0
    81
#define EPSILON 0.00000000001                                // anything less than EPSILON is considered zero
nathan@0
    82
nathan@0
    83
// --- cResample ------------------------------------------------------------
nathan@0
    84
nathan@0
    85
// The resample code has been adapted from the madplay project
nathan@0
    86
// (resample.c) found in the libmad distribution
nathan@0
    87
   
nathan@0
    88
class cResample {
nathan@0
    89
private:
nathan@0
    90
  mad_fixed_t ratio;
nathan@0
    91
  mad_fixed_t step;
nathan@0
    92
  mad_fixed_t last;
nathan@0
    93
  mad_fixed_t resampled[MAX_NSAMPLES];
nathan@0
    94
public:
nathan@0
    95
  bool SetInputRate(unsigned int oldrate, unsigned int newrate);
nathan@0
    96
  unsigned int ResampleBlock(unsigned int nsamples, const mad_fixed_t *old);
nathan@0
    97
  const mad_fixed_t *Resampled(void) { return resampled; }
nathan@0
    98
  };
nathan@0
    99
nathan@0
   100
bool cResample::SetInputRate(unsigned int oldrate, unsigned int newrate)
nathan@0
   101
{
nathan@0
   102
  if(oldrate<8000 || oldrate>newrate*6) { // out of range
nathan@0
   103
    esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6);
nathan@0
   104
    return 0;
nathan@0
   105
    }
nathan@0
   106
  ratio=mad_f_tofixed((double)oldrate/(double)newrate);
nathan@0
   107
  step=0; last=0;
nathan@0
   108
#ifdef DEBUG
nathan@0
   109
  static mad_fixed_t oldratio=0;
nathan@0
   110
  if(oldratio!=ratio) {
nathan@0
   111
    printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate);
nathan@0
   112
    oldratio=ratio;
nathan@0
   113
    }
nathan@0
   114
#endif
nathan@0
   115
  return ratio!=MAD_F_ONE;
nathan@0
   116
}
nathan@0
   117
nathan@0
   118
unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old)
nathan@0
   119
{
nathan@0
   120
  // This resampling algorithm is based on a linear interpolation, which is
nathan@0
   121
  // not at all the best sounding but is relatively fast and efficient.
nathan@0
   122
  //
nathan@0
   123
  // A better algorithm would be one that implements a bandlimited
nathan@0
   124
  // interpolation.
nathan@0
   125
nathan@0
   126
  mad_fixed_t *nsam=resampled;
nathan@0
   127
  const mad_fixed_t *end=old+nsamples;
nathan@0
   128
  const mad_fixed_t *begin=nsam;
nathan@0
   129
nathan@0
   130
  if(step < 0) {
nathan@0
   131
    step = mad_f_fracpart(-step);
nathan@0
   132
nathan@0
   133
    while (step < MAD_F_ONE) {
nathan@0
   134
      *nsam++ = step ? last+mad_f_mul(*old-last,step) : last;
nathan@0
   135
      step += ratio;
nathan@0
   136
      if(((step + 0x00000080L) & 0x0fffff00L) == 0)
nathan@0
   137
	step = (step + 0x00000080L) & ~0x0fffffffL;
nathan@0
   138
      }
nathan@0
   139
    step -= MAD_F_ONE;
nathan@0
   140
    }
nathan@0
   141
nathan@0
   142
  while (end - old > 1 + mad_f_intpart(step)) {
nathan@0
   143
    old += mad_f_intpart(step);
nathan@0
   144
    step = mad_f_fracpart(step);
nathan@0
   145
    *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old;
nathan@0
   146
    step += ratio;
nathan@0
   147
    if (((step + 0x00000080L) & 0x0fffff00L) == 0)
nathan@0
   148
      step = (step + 0x00000080L) & ~0x0fffffffL;
nathan@0
   149
    }
nathan@0
   150
nathan@0
   151
  if (end - old == 1 + mad_f_intpart(step)) {
nathan@0
   152
    last = end[-1];
nathan@0
   153
    step = -step;
nathan@0
   154
    }
nathan@0
   155
  else step -= mad_f_fromint(end - old);
nathan@0
   156
nathan@0
   157
  return nsam-begin;
nathan@0
   158
}
nathan@0
   159
nathan@0
   160
// --- cLevel ----------------------------------------------------------------
nathan@0
   161
nathan@0
   162
// The normalize algorithm and parts of the code has been adapted from the
nathan@0
   163
// Normalize 0.7 project. (C) 1999-2002, Chris Vaill <cvaill@cs.columbia.edu>
nathan@0
   164
nathan@0
   165
// A little background on how normalize computes the volume
nathan@0
   166
// of a wav file, in case you want to know just how your
nathan@0
   167
// files are being munged:
nathan@0
   168
//
nathan@0
   169
// The volumes calculated are RMS amplitudes, which corre­
nathan@0
   170
// spond (roughly) to perceived volume. Taking the RMS ampli­
nathan@0
   171
// tude of an entire file would not give us quite the measure
nathan@0
   172
// we want, though, because a quiet song punctuated by short
nathan@0
   173
// loud parts would average out to a quiet song, and the
nathan@0
   174
// adjustment we would compute would make the loud parts
nathan@0
   175
// excessively loud.
nathan@0
   176
//
nathan@0
   177
// What we want is to consider the maximum volume of the
nathan@0
   178
// file, and normalize according to that. We break up the
nathan@0
   179
// signal into 100 chunks per second, and get the signal
nathan@0
   180
// power of each chunk, in order to get an estimation of
nathan@0
   181
// "instantaneous power" over time. This "instantaneous
nathan@0
   182
// power" signal varies too much to get a good measure of the
nathan@0
   183
// original signal's maximum sustained power, so we run a
nathan@0
   184
// smoothing algorithm over the power signal (specifically, a
nathan@0
   185
// mean filter with a window width of 100 elements). The max­
nathan@0
   186
// imum point of the smoothed power signal turns out to be a
nathan@0
   187
// good measure of the maximum sustained power of the file.
nathan@0
   188
// We can then take the square root of the power to get maxi­
nathan@0
   189
// mum sustained RMS amplitude.
nathan@0
   190
nathan@0
   191
class cLevel {
nathan@0
   192
private:
nathan@0
   193
  double maxpow;
nathan@0
   194
  mad_fixed_t peak;
nathan@0
   195
  struct Power {
nathan@0
   196
    // smooth
nathan@0
   197
    int npow, wpow;
nathan@0
   198
    double powsum, pows[POW_WIN];
nathan@0
   199
    // sum
nathan@0
   200
    unsigned int nsum;
nathan@0
   201
    double sum;
nathan@0
   202
    } power[2];
nathan@0
   203
  //
nathan@0
   204
  inline void AddPower(struct Power *p, double pow);
nathan@0
   205
public:
nathan@0
   206
  void Init(void);
nathan@0
   207
  void GetPower(struct mad_pcm *pcm);
nathan@0
   208
  double GetLevel(void);
nathan@0
   209
  double GetPeak(void);
nathan@0
   210
  };
nathan@0
   211
nathan@0
   212
void cLevel::Init(void)
nathan@0
   213
{
nathan@0
   214
  for(int l=0 ; l<2 ; l++) {
nathan@0
   215
    struct Power *p=&power[l];
nathan@0
   216
    p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0;
nathan@0
   217
    for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0;
nathan@0
   218
    }
nathan@0
   219
  maxpow=0.0; peak=0;
nathan@0
   220
}
nathan@0
   221
nathan@0
   222
void cLevel::GetPower(struct mad_pcm *pcm)
nathan@0
   223
{
nathan@0
   224
  for(int i=0 ; i<pcm->channels ; i++) {
nathan@0
   225
    struct Power *p=&power[i];
nathan@0
   226
    mad_fixed_t *data=pcm->samples[i];
nathan@0
   227
    for(int n=pcm->length ; n>0 ; n--) {
nathan@0
   228
      if(*data < -peak) peak = -*data;
nathan@0
   229
      if(*data >  peak) peak =  *data;
nathan@0
   230
      double s=mad_f_todouble(*data++);
nathan@0
   231
      p->sum+=(s*s);
nathan@0
   232
      if(++(p->nsum)>=pcm->samplerate/100) {
nathan@0
   233
        AddPower(p,p->sum/(double)p->nsum);
nathan@0
   234
        p->sum=0.0; p->nsum=0;
nathan@0
   235
        }
nathan@0
   236
      }
nathan@0
   237
    }
nathan@0
   238
}
nathan@0
   239
nathan@0
   240
void cLevel::AddPower(struct Power *p, double pow)
nathan@0
   241
{
nathan@0
   242
  p->powsum+=pow;
nathan@0
   243
  if(p->npow>=POW_WIN) {
nathan@0
   244
    if(p->powsum>maxpow) maxpow=p->powsum;
nathan@0
   245
    p->powsum-=p->pows[p->wpow];
nathan@0
   246
    }
nathan@0
   247
  else p->npow++;
nathan@0
   248
  p->pows[p->wpow]=pow;
nathan@0
   249
  p->wpow=(p->wpow+1) % POW_WIN;
nathan@0
   250
}
nathan@0
   251
nathan@0
   252
double cLevel::GetLevel(void)
nathan@0
   253
{
nathan@0
   254
  if(maxpow<EPSILON) {
nathan@0
   255
    // Either this whole file has zero power, or was too short to ever
nathan@0
   256
    // fill the smoothing buffer.  In the latter case, we need to just
nathan@0
   257
    // get maxpow from whatever data we did collect.
nathan@0
   258
nathan@0
   259
    if(power[0].powsum>maxpow) maxpow=power[0].powsum;
nathan@0
   260
    if(power[1].powsum>maxpow) maxpow=power[1].powsum;
nathan@0
   261
    }
nathan@0
   262
  double level=sqrt(maxpow/(double)POW_WIN);     // adjust for the smoothing window size and root
nathan@0
   263
  d(printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak)))
nathan@0
   264
  return level;
nathan@0
   265
}
nathan@0
   266
nathan@0
   267
double cLevel::GetPeak(void)
nathan@0
   268
{
nathan@0
   269
  return mad_f_todouble(peak);
nathan@0
   270
}
nathan@0
   271
nathan@0
   272
// --- cNormalize ------------------------------------------------------------
nathan@0
   273
nathan@0
   274
class cNormalize {
nathan@0
   275
private:
nathan@0
   276
  mad_fixed_t gain;
nathan@0
   277
  double d_limlvl, one_limlvl;
nathan@0
   278
  mad_fixed_t limlvl;
nathan@0
   279
  bool dogain, dolimit;
nathan@0
   280
#ifdef DEBUG
nathan@0
   281
  // stats
nathan@0
   282
  unsigned long limited, clipped, total;
nathan@0
   283
  mad_fixed_t peak;
nathan@0
   284
#endif
nathan@0
   285
  // limiter
nathan@0
   286
#ifdef USE_FAST_LIMITER
nathan@0
   287
  mad_fixed_t *table, tablestart;
nathan@0
   288
  int tablesize;
nathan@0
   289
  inline mad_fixed_t FastLimiter(mad_fixed_t x);
nathan@0
   290
#endif
nathan@0
   291
  inline mad_fixed_t Limiter(mad_fixed_t x);
nathan@0
   292
public:
nathan@0
   293
  cNormalize(void);
nathan@0
   294
  ~cNormalize();
nathan@0
   295
  void Init(double Level, double Peak);
nathan@0
   296
  void Stats(void);
nathan@0
   297
  void AddGain(struct mad_pcm *pcm);
nathan@0
   298
  };
nathan@0
   299
nathan@0
   300
cNormalize::cNormalize(void)
nathan@0
   301
{
nathan@0
   302
  d_limlvl=(double)MP3Setup.LimiterLevel/100.0;
nathan@0
   303
  one_limlvl=1-d_limlvl;
nathan@0
   304
  limlvl=mad_f_tofixed(d_limlvl);
nathan@0
   305
  d(printf("norm: lim_lev=%f lim_acc=%d\n",d_limlvl,LIM_ACC))
nathan@0
   306
nathan@0
   307
#ifdef USE_FAST_LIMITER
nathan@0
   308
  mad_fixed_t start=limlvl & ~(F_LIM_JMP-1);
nathan@0
   309
  tablestart=start;
nathan@0
   310
  tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2;
nathan@0
   311
  table=new mad_fixed_t[tablesize];
nathan@0
   312
  if(table) {
nathan@0
   313
    d(printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP))
nathan@0
   314
    for(int i=0 ; i<tablesize ; i++) {
nathan@0
   315
      table[i]=Limiter(start);
nathan@0
   316
      start+=F_LIM_JMP;
nathan@0
   317
      }
nathan@0
   318
    tablesize--; // avoid a -1 in FastLimiter()
nathan@0
   319
nathan@0
   320
    // do a quick accuracy check, just to be sure that FastLimiter() is working
nathan@0
   321
    // as expected :-)
nathan@0
   322
#ifdef ACC_DUMP
nathan@0
   323
    FILE *out=fopen("/tmp/limiter","w");
nathan@0
   324
#endif
nathan@0
   325
    mad_fixed_t maxdiff=0;
nathan@0
   326
    for(mad_fixed_t x=F_LIM_MAX ; x>=limlvl ; x-=mad_f_tofixed(1e-4)) {
nathan@0
   327
      mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x));
nathan@0
   328
      if(diff>maxdiff) maxdiff=diff;
nathan@0
   329
#ifdef ACC_DUMP
nathan@0
   330
      fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n",
nathan@0
   331
        mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff));
nathan@0
   332
      if(ferror(out)) break;
nathan@0
   333
#endif
nathan@0
   334
      }
nathan@0
   335
#ifdef ACC_DUMP
nathan@0
   336
    fclose(out);
nathan@0
   337
#endif
nathan@0
   338
    d(printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff)))
nathan@0
   339
    if(mad_f_todouble(maxdiff)>1e-6) {
nathan@0
   340
      esyslog("ERROR: accuracy check failed, normalizer disabled");
nathan@0
   341
      delete table; table=0;
nathan@0
   342
      }
nathan@0
   343
    }
nathan@0
   344
  else esyslog("ERROR: no memory for lookup table, normalizer disabled");
nathan@0
   345
#endif // USE_FAST_LIMITER
nathan@0
   346
}
nathan@0
   347
nathan@0
   348
cNormalize::~cNormalize()
nathan@0
   349
{
nathan@0
   350
#ifdef USE_FAST_LIMITER
nathan@0
   351
  delete[] table;
nathan@0
   352
#endif
nathan@0
   353
}
nathan@0
   354
nathan@0
   355
void cNormalize::Init(double Level, double Peak)
nathan@0
   356
{
nathan@0
   357
  double Target=(double)MP3Setup.TargetLevel/100.0;
nathan@0
   358
  double dgain=Target/Level;
nathan@0
   359
  if(dgain>MAX_GAIN) dgain=MAX_GAIN;
nathan@0
   360
  gain=mad_f_tofixed(dgain);
nathan@0
   361
  // Check if we actually need to apply a gain
nathan@0
   362
  dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN);
nathan@0
   363
#ifdef USE_FAST_LIMITER
nathan@0
   364
  if(!table) dogain=false;
nathan@0
   365
#endif
nathan@0
   366
  // Check if we actually need to do limiting:
nathan@0
   367
  // we have to if limiter is enabled, if gain>1 and if the peaks will clip.
nathan@0
   368
  dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0);
nathan@0
   369
#ifdef DEBUG
nathan@0
   370
  printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak);
nathan@0
   371
  limited=clipped=total=0; peak=0;
nathan@0
   372
#endif
nathan@0
   373
}
nathan@0
   374
nathan@0
   375
void cNormalize::Stats(void)
nathan@0
   376
{
nathan@0
   377
#ifdef DEBUG
nathan@0
   378
  if(total)
nathan@0
   379
    printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n",
nathan@0
   380
           total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak));
nathan@0
   381
#endif
nathan@0
   382
}
nathan@0
   383
nathan@0
   384
mad_fixed_t cNormalize::Limiter(mad_fixed_t x)
nathan@0
   385
{
nathan@0
   386
// Limiter function:
nathan@0
   387
//
nathan@0
   388
//        / x                                                (for x <= lev)
nathan@0
   389
//   x' = |
nathan@0
   390
//        \ tanh((x - lev) / (1-lev)) * (1-lev) + lev        (for x > lev)
nathan@0
   391
//
nathan@0
   392
// call only with x>=0. For negative samples, preserve sign outside this function
nathan@0
   393
//
nathan@0
   394
// With limiter level = 0, this is equivalent to a tanh() function;
nathan@0
   395
// with limiter level = 1, this is equivalent to clipping.
nathan@0
   396
nathan@0
   397
  if(x>limlvl) {
nathan@0
   398
#ifdef DEBUG
nathan@0
   399
    if(x>MAD_F_ONE) clipped++;
nathan@0
   400
    limited++;
nathan@0
   401
#endif
nathan@0
   402
    x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl);
nathan@0
   403
    }
nathan@0
   404
  return x;
nathan@0
   405
}
nathan@0
   406
nathan@0
   407
#ifdef USE_FAST_LIMITER
nathan@0
   408
mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x)
nathan@0
   409
{
nathan@0
   410
// The fast algorithm is based on a linear interpolation between the
nathan@0
   411
// the values in the lookup table. Relays heavly on libmads fixed point format.
nathan@0
   412
nathan@0
   413
  if(x>limlvl) {
nathan@0
   414
    int i=(unsigned int)(x-tablestart)/F_LIM_JMP;
nathan@0
   415
#ifdef DEBUG
nathan@0
   416
    if(x>MAD_F_ONE) clipped++;
nathan@0
   417
    limited++;
nathan@0
   418
    if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n",
nathan@0
   419
                            mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize);
nathan@0
   420
#endif
nathan@0
   421
    mad_fixed_t r=x & (F_LIM_JMP-1);
nathan@0
   422
    x=MAD_F_ONE;
nathan@0
   423
    if(i<tablesize) {
nathan@0
   424
      mad_fixed_t *ptr=&table[i];
nathan@0
   425
      x=*ptr;
nathan@0
   426
      mad_fixed_t d=*(ptr+1)-x;
nathan@0
   427
      //x+=mad_f_mul(d,r)<<LIM_ACC;                // this is not accurate as mad_f_mul() does >>MAD_F_FRACBITS
nathan@0
   428
                                                   // which is senseless in the case of following <<LIM_ACC.
nathan@0
   429
      x+=((long long)d*(long long)r)>>LIM_SHIFT;   // better, don't know if works on all machines
nathan@0
   430
      }
nathan@0
   431
    }
nathan@0
   432
  return x;
nathan@0
   433
}
nathan@0
   434
#endif
nathan@0
   435
nathan@0
   436
#ifdef USE_FAST_LIMITER
nathan@0
   437
#define LIMITER_FUNC FastLimiter
nathan@0
   438
#else
nathan@0
   439
#define LIMITER_FUNC Limiter
nathan@0
   440
#endif
nathan@0
   441
nathan@0
   442
void cNormalize::AddGain(struct mad_pcm *pcm)
nathan@0
   443
{
nathan@0
   444
  if(dogain) {
nathan@0
   445
    for(int i=0 ; i<pcm->channels ; i++) {
nathan@0
   446
      mad_fixed_t *data=pcm->samples[i];
nathan@0
   447
#ifdef DEBUG
nathan@0
   448
      total+=pcm->length;
nathan@0
   449
#endif
nathan@0
   450
      if(dolimit) {
nathan@0
   451
        for(int n=pcm->length ; n>0 ; n--) {
nathan@0
   452
          mad_fixed_t s=mad_f_mul(*data,gain);
nathan@0
   453
          if(s<0) {
nathan@0
   454
            s=-s;
nathan@0
   455
#ifdef DEBUG
nathan@0
   456
            if(s>peak) peak=s;
nathan@0
   457
#endif
nathan@0
   458
            s=LIMITER_FUNC(s);
nathan@0
   459
            s=-s;
nathan@0
   460
            }
nathan@0
   461
          else {
nathan@0
   462
#ifdef DEBUG
nathan@0
   463
            if(s>peak) peak=s;
nathan@0
   464
#endif
nathan@0
   465
            s=LIMITER_FUNC(s);
nathan@0
   466
            }
nathan@0
   467
          *data++=s;
nathan@0
   468
          }
nathan@0
   469
        }
nathan@0
   470
      else {
nathan@0
   471
        for(int n=pcm->length ; n>0 ; n--) {
nathan@0
   472
          mad_fixed_t s=mad_f_mul(*data,gain);
nathan@0
   473
#ifdef DEBUG
nathan@0
   474
          if(s>peak) peak=s;
nathan@0
   475
          else if(-s>peak) peak=-s;
nathan@0
   476
#endif
nathan@0
   477
          if(s>MAD_F_ONE) s=MAD_F_ONE;   // do clipping
nathan@0
   478
          if(s<-MAD_F_ONE) s=-MAD_F_ONE;
nathan@0
   479
          *data++=s;
nathan@0
   480
          }
nathan@0
   481
        }
nathan@0
   482
      }
nathan@0
   483
    }
nathan@0
   484
}
nathan@0
   485
nathan@0
   486
// --- cScale ----------------------------------------------------------------
nathan@0
   487
nathan@0
   488
// The dither code has been adapted from the madplay project
nathan@0
   489
// (audio.c) found in the libmad distribution
nathan@0
   490
nathan@0
   491
enum eAudioMode { amRoundBE, amDitherBE, amRoundLE, amDitherLE };
nathan@0
   492
nathan@0
   493
class cScale {
nathan@0
   494
private:
nathan@0
   495
  enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 };
nathan@0
   496
#ifdef DEBUG
nathan@0
   497
  // audio stats
nathan@0
   498
  unsigned long clipped_samples;
nathan@0
   499
  mad_fixed_t peak_clipping;
nathan@0
   500
  mad_fixed_t peak_sample;
nathan@0
   501
#endif
nathan@0
   502
  // dither
nathan@0
   503
  struct dither {
nathan@0
   504
    mad_fixed_t error[3];
nathan@0
   505
    mad_fixed_t random;
nathan@0
   506
    } leftD, rightD;
nathan@0
   507
  //
nathan@0
   508
  inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true);
nathan@0
   509
  inline unsigned long Prng(unsigned long state);
nathan@0
   510
  signed long LinearRound(mad_fixed_t sample);
nathan@0
   511
  signed long LinearDither(mad_fixed_t sample, struct dither *dither);
nathan@0
   512
public:
nathan@0
   513
  void Init(void);
nathan@0
   514
  void Stats(void);
nathan@0
   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);
nathan@0
   516
  };
nathan@0
   517
nathan@0
   518
void cScale::Init(void)
nathan@0
   519
{
nathan@0
   520
#ifdef DEBUG
nathan@0
   521
  clipped_samples=0; peak_clipping=peak_sample=0;
nathan@0
   522
#endif
nathan@0
   523
  memset(&leftD,0,sizeof(leftD));
nathan@0
   524
  memset(&rightD,0,sizeof(rightD));
nathan@0
   525
}
nathan@0
   526
nathan@0
   527
void cScale::Stats(void)
nathan@0
   528
{
nathan@0
   529
#ifdef DEBUG
nathan@0
   530
  printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n",
nathan@0
   531
         clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample));
nathan@0
   532
#endif
nathan@0
   533
}
nathan@0
   534
nathan@0
   535
// gather signal statistics while clipping
nathan@0
   536
mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats)
nathan@0
   537
{
nathan@0
   538
#ifndef DEBUG
nathan@0
   539
  if (sample > MAX) sample = MAX;
nathan@0
   540
  if (sample < MIN) sample = MIN;
nathan@0
   541
#else
nathan@0
   542
  if(!stats) {
nathan@0
   543
    if (sample > MAX) sample = MAX;
nathan@0
   544
    if (sample < MIN) sample = MIN;
nathan@0
   545
    }
nathan@0
   546
  else {
nathan@0
   547
    if (sample >= peak_sample) {
nathan@0
   548
      if (sample > MAX) {
nathan@0
   549
        ++clipped_samples;
nathan@0
   550
        if (sample - MAX > peak_clipping)
nathan@0
   551
	  peak_clipping = sample - MAX;
nathan@0
   552
        sample = MAX;
nathan@0
   553
        }
nathan@0
   554
      peak_sample = sample;
nathan@0
   555
      }
nathan@0
   556
    else if (sample < -peak_sample) {
nathan@0
   557
      if (sample < MIN) {
nathan@0
   558
        ++clipped_samples;
nathan@0
   559
        if (MIN - sample > peak_clipping)
nathan@0
   560
	  peak_clipping = MIN - sample;
nathan@0
   561
        sample = MIN;
nathan@0
   562
        }
nathan@0
   563
      peak_sample = -sample;
nathan@0
   564
      }
nathan@0
   565
    }
nathan@0
   566
#endif
nathan@0
   567
  return sample;
nathan@0
   568
}
nathan@0
   569
nathan@0
   570
// generic linear sample quantize routine
nathan@0
   571
signed long cScale::LinearRound(mad_fixed_t sample)
nathan@0
   572
{
nathan@0
   573
  // round
nathan@0
   574
  sample += (1L << (MAD_F_FRACBITS - OUT_BITS));
nathan@0
   575
  // clip
nathan@0
   576
  sample=Clip(sample);
nathan@0
   577
  // quantize and scale
nathan@0
   578
  return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS);
nathan@0
   579
}
nathan@0
   580
nathan@0
   581
// 32-bit pseudo-random number generator
nathan@0
   582
unsigned long cScale::Prng(unsigned long state)
nathan@0
   583
{
nathan@0
   584
  return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
nathan@0
   585
}
nathan@0
   586
nathan@0
   587
// generic linear sample quantize and dither routine
nathan@0
   588
signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither)
nathan@0
   589
{
nathan@0
   590
  // noise shape
nathan@0
   591
  sample += dither->error[0] - dither->error[1] + dither->error[2];
nathan@0
   592
  dither->error[2] = dither->error[1];
nathan@0
   593
  dither->error[1] = dither->error[0] / 2;
nathan@0
   594
  // bias
nathan@0
   595
  mad_fixed_t output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1));
nathan@0
   596
  const int scalebits = MAD_F_FRACBITS + 1 - OUT_BITS;
nathan@0
   597
  const mad_fixed_t mask = (1L << scalebits) - 1;
nathan@0
   598
  // dither
nathan@0
   599
  const mad_fixed_t random = Prng(dither->random);
nathan@0
   600
  output += (random & mask) - (dither->random & mask);
nathan@0
   601
  dither->random = random;
nathan@0
   602
  // clip
nathan@0
   603
  output=Clip(output);
nathan@0
   604
  sample=Clip(sample,false);
nathan@0
   605
  // quantize
nathan@0
   606
  output &= ~mask;
nathan@0
   607
  // error feedback
nathan@0
   608
  dither->error[0] = sample - output;
nathan@0
   609
  // scale
nathan@0
   610
  return output >> scalebits;
nathan@0
   611
}
nathan@0
   612
nathan@0
   613
#define PUT_BE(data,sample) { *data++=(sample)>>8; *data++=(sample)>>0; }
nathan@0
   614
#define PUT_LE(data,sample) { *data++=(sample)>>0; *data++=(sample)>>8; }
nathan@0
   615
nathan@0
   616
// write a block of signed 16-bit PCM samples
nathan@0
   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)
nathan@0
   618
{
nathan@0
   619
  unsigned int len=size/OUT_FACT;
nathan@0
   620
  if(len>nsamples) { len=nsamples; size=len*OUT_FACT; }
nathan@0
   621
  nsamples-=len;
nathan@0
   622
  switch(mode) {
nathan@0
   623
    case amRoundBE:
nathan@0
   624
      while(len--) {
nathan@0
   625
        signed int sample=LinearRound(*left++);
nathan@0
   626
        PUT_BE(data,sample);
nathan@0
   627
        if(right) sample=LinearRound(*right++);
nathan@0
   628
        PUT_BE(data,sample);
nathan@0
   629
        }
nathan@0
   630
      break;
nathan@0
   631
    case amDitherBE:
nathan@0
   632
      while(len--) {
nathan@0
   633
	signed int sample=LinearDither(*left++,&leftD);
nathan@0
   634
        PUT_BE(data,sample);
nathan@0
   635
	if(right) sample=LinearDither(*right++,&rightD);
nathan@0
   636
        PUT_BE(data,sample);
nathan@0
   637
        }
nathan@0
   638
      break;
nathan@0
   639
    case amRoundLE:
nathan@0
   640
      while(len--) {
nathan@0
   641
        signed int sample=LinearRound(*left++);
nathan@0
   642
        PUT_LE(data,sample);
nathan@0
   643
        if(right) sample=LinearRound(*right++);
nathan@0
   644
        PUT_LE(data,sample);
nathan@0
   645
        }
nathan@0
   646
      break;
nathan@0
   647
    case amDitherLE:
nathan@0
   648
      while(len--) {
nathan@0
   649
	signed int sample=LinearDither(*left++,&leftD);
nathan@0
   650
        PUT_LE(data,sample);
nathan@0
   651
	if(right) sample=LinearDither(*right++,&rightD);
nathan@0
   652
        PUT_LE(data,sample);
nathan@0
   653
        }
nathan@0
   654
      break;
nathan@0
   655
    }
nathan@0
   656
 return size;
nathan@0
   657
}
nathan@0
   658
nathan@0
   659
// --- cShuffle ----------------------------------------------------------------
nathan@0
   660
nathan@0
   661
class cShuffle {
nathan@0
   662
private:
nathan@0
   663
  int *shuffle, max;
nathan@0
   664
  unsigned int seed;
nathan@0
   665
  //
nathan@0
   666
  int Index(int pos);
nathan@0
   667
public:
nathan@0
   668
  cShuffle(void);
nathan@0
   669
  ~cShuffle();
nathan@0
   670
  void Shuffle(int num, int curr);
nathan@0
   671
  void Del(int pos);
nathan@0
   672
  void Flush(void);
nathan@0
   673
  int First(void);
nathan@0
   674
  int Next(int curr);
nathan@0
   675
  int Prev(int curr);
nathan@0
   676
  int Goto(int pos, int curr);
nathan@0
   677
  };
nathan@0
   678
nathan@0
   679
cShuffle::cShuffle(void)
nathan@0
   680
{
nathan@0
   681
  shuffle=0; max=0;
nathan@0
   682
  seed=time(0);
nathan@0
   683
}
nathan@0
   684
nathan@0
   685
cShuffle::~cShuffle(void)
nathan@0
   686
{
nathan@0
   687
  Flush();
nathan@0
   688
}
nathan@0
   689
nathan@0
   690
void cShuffle::Flush(void)
nathan@0
   691
{
nathan@0
   692
  delete shuffle; shuffle=0;
nathan@0
   693
  max=0;
nathan@0
   694
}
nathan@0
   695
nathan@0
   696
int cShuffle::Index(int pos)
nathan@0
   697
{
nathan@0
   698
  if(pos>=0)
nathan@0
   699
    for(int i=0; i<max; i++) if(shuffle[i]==pos) return i;
nathan@0
   700
  return -1;
nathan@0
   701
}
nathan@0
   702
nathan@0
   703
void cShuffle::Shuffle(int num, int curr)
nathan@0
   704
{
nathan@0
   705
  int oldmax=0;
nathan@0
   706
  if(num!=max) {
nathan@0
   707
    int *ns=new int[num];
nathan@0
   708
    if(shuffle) {
nathan@0
   709
      if(num>max) {
nathan@0
   710
        memcpy(ns,shuffle,max*sizeof(int));
nathan@0
   711
        oldmax=max;
nathan@0
   712
        }
nathan@0
   713
      delete shuffle;
nathan@0
   714
      }
nathan@0
   715
    shuffle=ns; max=num;
nathan@0
   716
    }
nathan@0
   717
  if(!oldmax) curr=-1;
nathan@0
   718
  for(int i=oldmax ; i<max ; i++) shuffle[i]=i;
nathan@0
   719
nathan@0
   720
  int in=Index(curr)+1; if(in<0) in=0;
nathan@0
   721
  if((max-in)>=2) {
nathan@0
   722
    for(int i=in ; i<max ; i++) {
nathan@0
   723
      int ran=(rand_r(&seed) % ((max-in)*4-4))/4; ran+=((ran+in) >= i);
nathan@0
   724
      int t=shuffle[i];
nathan@0
   725
      shuffle[i]=shuffle[ran+in];
nathan@0
   726
      shuffle[ran+in]=t;
nathan@0
   727
      }
nathan@0
   728
    }
nathan@0
   729
#ifdef DEBUG
nathan@0
   730
  printf("shuffle: order (%d , %d -> %d) ",num,curr,in);
nathan@0
   731
  for(int i=0 ; i<max ; i++) printf("%d ",shuffle[i]);
nathan@0
   732
  printf("\n");
nathan@0
   733
#endif
nathan@0
   734
}
nathan@0
   735
nathan@0
   736
void cShuffle::Del(int pos)
nathan@0
   737
{
nathan@0
   738
  int i=Index(pos);
nathan@0
   739
  if(i>=0) {
nathan@0
   740
    if(i+1<max) memmove(&shuffle[i],&shuffle[i+1],(max-i-1)*sizeof(int));
nathan@0
   741
    max--;
nathan@0
   742
    }
nathan@0
   743
}
nathan@0
   744
nathan@0
   745
int cShuffle::First(void)
nathan@0
   746
{
nathan@0
   747
  return shuffle[0];
nathan@0
   748
}
nathan@0
   749
nathan@0
   750
int cShuffle::Next(int curr)
nathan@0
   751
{
nathan@0
   752
  int i=Index(curr);
nathan@0
   753
  return (i>=0 && i+1<max) ? shuffle[i+1] : -1;
nathan@0
   754
}
nathan@0
   755
nathan@0
   756
int cShuffle::Prev(int curr)
nathan@0
   757
{
nathan@0
   758
  int i=Index(curr);
nathan@0
   759
  return (i>0) ? shuffle[i-1] : -1;
nathan@0
   760
}
nathan@0
   761
nathan@0
   762
int cShuffle::Goto(int pos, int curr)
nathan@0
   763
{
nathan@0
   764
  int i=Index(curr);
nathan@0
   765
  int g=Index(pos);
nathan@0
   766
  if(g>=0) {
nathan@0
   767
    if(g<i) {
nathan@0
   768
      for(int l=g; l<i; l++) shuffle[l]=shuffle[l+1];
nathan@0
   769
      shuffle[i]=pos;
nathan@0
   770
      }
nathan@0
   771
    else if(g>i) {
nathan@0
   772
      for(int l=g; l>i+1; l--) shuffle[l]=shuffle[l-1];
nathan@0
   773
      shuffle[i+1]=pos;
nathan@0
   774
      }
nathan@0
   775
#ifdef DEBUG
nathan@0
   776
    printf("shuffle: goto order (%d -> %d , %d -> %d) ",pos,g,curr,i);
nathan@0
   777
    for(int i=0 ; i<max ; i++) printf("%d ",shuffle[i]);
nathan@0
   778
    printf("\n");
nathan@0
   779
#endif
nathan@0
   780
    return pos;
nathan@0
   781
    }
nathan@0
   782
  return -1;
nathan@0
   783
}
nathan@0
   784
nathan@0
   785
// --- cPlayManager ------------------------------------------------------------
nathan@0
   786
nathan@0
   787
#define SCANNED_ID3 1
nathan@0
   788
#define SCANNED_LVL 2
nathan@0
   789
nathan@0
   790
cPlayManager *mgr=0;
nathan@0
   791
nathan@0
   792
cPlayManager::cPlayManager(void)
nathan@0
   793
{
nathan@0
   794
  curr=0; currIndex=-1;
nathan@0
   795
  scan=0; stopscan=throttle=pass2=release=false;
nathan@0
   796
  play=0; playNew=eol=false;
nathan@0
   797
  shuffle=new cShuffle;
nathan@0
   798
  loopMode=(MP3Setup.InitLoopMode>0);
nathan@0
   799
  shuffleMode=(MP3Setup.InitShuffleMode>0);
nathan@0
   800
}
nathan@0
   801
nathan@0
   802
cPlayManager::~cPlayManager()
nathan@0
   803
{
nathan@0
   804
  Flush();
nathan@0
   805
  Release();
nathan@0
   806
  listMutex.Lock();
nathan@0
   807
  stopscan=true; bgCond.Broadcast();
nathan@0
   808
  listMutex.Unlock();
nathan@0
   809
  Cancel(2);
nathan@0
   810
  delete shuffle;
nathan@0
   811
}
nathan@0
   812
nathan@0
   813
void cPlayManager::ThrottleWait(void)
nathan@0
   814
{
nathan@0
   815
  while(!stopscan && !release && throttle) {
nathan@0
   816
    db(printf("mgr: background scan throttled\n"))
nathan@0
   817
    bgCond.Wait(listMutex);
nathan@0
   818
    db(printf("mgr: background scan throttle wakeup\n"))
nathan@0
   819
    }
nathan@0
   820
}
nathan@0
   821
nathan@0
   822
void cPlayManager::Action(void)
nathan@0
   823
{
nathan@0
   824
  db(printf("mgr: background scan thread started (pid=%d)\n", getpid()))
nathan@29
   825
  if(nice(5)<0);
nathan@0
   826
  listMutex.Lock();
nathan@0
   827
  while(!stopscan) {
nathan@0
   828
    for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) {
nathan@0
   829
      ThrottleWait();
nathan@0
   830
      listMutex.Unlock();
nathan@0
   831
      if(!(scan->user & SCANNED_ID3)) {
nathan@0
   832
        db(printf("mgr: scanning (id3) %s\n",scan->Name()))
nathan@0
   833
        cSongInfo *si=scan->Info(true);
nathan@0
   834
        if(si && si->Level>0.0) scan->user|=SCANNED_LVL;
nathan@0
   835
        scan->user|=SCANNED_ID3;
nathan@0
   836
        }
nathan@0
   837
      listMutex.Lock();
nathan@0
   838
      }
nathan@0
   839
    if(MP3Setup.BgrScan>1) {
nathan@0
   840
      pass2=true;
nathan@0
   841
      for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) {
nathan@0
   842
        if(scan==curr) continue;
nathan@0
   843
        ThrottleWait();
nathan@0
   844
        listMutex.Unlock();
nathan@0
   845
        if(!(scan->user & SCANNED_LVL)) {
nathan@0
   846
          cDecoder *dec=scan->Decoder();
nathan@0
   847
          if(dec) {
nathan@0
   848
            cSongInfo *si=scan->Info(false);
nathan@0
   849
            if(!dec->IsStream() && (!si || si->Level<=0.0) && dec->Start()) {
nathan@0
   850
              db(printf("mgr: scanning (lvl) %s\n",scan->Name()))
nathan@0
   851
              cLevel level;
nathan@0
   852
              level.Init();
nathan@0
   853
              bool go=true;
nathan@0
   854
              while(go && !release) {
nathan@0
   855
                if(throttle) {
nathan@0
   856
                  listMutex.Lock(); ThrottleWait(); listMutex.Unlock();
nathan@0
   857
                  continue;
nathan@0
   858
                  }
nathan@0
   859
                struct Decode *ds=dec->Decode();
nathan@0
   860
                switch(ds->status) {
nathan@0
   861
                  case dsPlay:
nathan@0
   862
                    level.GetPower(ds->pcm);
nathan@0
   863
                    break;
nathan@0
   864
                  case dsSkip:
nathan@0
   865
                  case dsSoftError:
nathan@0
   866
                    break;
nathan@0
   867
                  case dsEof:
nathan@0
   868
                    {
nathan@0
   869
                    double l=level.GetLevel();
nathan@0
   870
                    if(l>0.0) {
nathan@0
   871
                      cSongInfo *si=dec->SongInfo(false);
nathan@0
   872
                      cFileInfo *fi=dec->FileInfo();
nathan@0
   873
                      if(si && fi) {
nathan@0
   874
                        si->Level=l;
nathan@0
   875
                        si->Peak=level.GetPeak();
nathan@0
   876
                        InfoCache.Cache(si,fi);
nathan@0
   877
                        }
nathan@0
   878
                      }
nathan@0
   879
                    }
nathan@0
   880
                    //fall through
nathan@0
   881
                  case dsOK:
nathan@0
   882
                  case dsError:
nathan@0
   883
                    scan->user|=SCANNED_LVL;
nathan@0
   884
                    go=false;
nathan@0
   885
                    break;
nathan@0
   886
                  }
nathan@0
   887
                }
nathan@0
   888
              }
nathan@0
   889
            else scan->user|=SCANNED_LVL;
nathan@0
   890
            dec->Stop();
nathan@0
   891
            }
nathan@0
   892
          }
nathan@0
   893
        listMutex.Lock();
nathan@0
   894
        }
nathan@0
   895
      pass2=false;
nathan@0
   896
      }
nathan@0
   897
    do {
nathan@0
   898
      scan=0; release=false; fgCond.Broadcast();
nathan@0
   899
      db(printf("mgr: background scan idle\n"))
nathan@0
   900
      bgCond.Wait(listMutex);
nathan@0
   901
      db(printf("mgr: background scan idle wakeup\n"))
nathan@0
   902
      } while(!stopscan && (release || throttle));
nathan@0
   903
    }
nathan@0
   904
  listMutex.Unlock();
nathan@0
   905
  db(printf("mgr: background scan thread ended (pid=%d)\n", getpid()))
nathan@0
   906
}
nathan@0
   907
nathan@0
   908
void cPlayManager::Throttle(bool thr)
nathan@0
   909
{
nathan@0
   910
  if(MP3Setup.BgrScan) {
nathan@0
   911
    if(!thr && throttle) {
nathan@23
   912
      db(printf("mgr: bgr-scan -> run (%llu)\n",cTimeMs::Now()))
nathan@0
   913
      listMutex.Lock();
nathan@0
   914
      throttle=false; bgCond.Broadcast();
nathan@0
   915
      listMutex.Unlock();
nathan@0
   916
      }
nathan@0
   917
    if(thr && !throttle) {
nathan@23
   918
      db(printf("mgr: bgr-scan -> throttle (%llu)\n",cTimeMs::Now()))
nathan@0
   919
      throttle=true;
nathan@0
   920
      }
nathan@0
   921
    }
nathan@0
   922
}
nathan@0
   923
nathan@0
   924
void cPlayManager::ToggleShuffle(void)
nathan@0
   925
{
nathan@0
   926
  shuffleMode=!shuffleMode;
nathan@0
   927
  d(printf("mgr: shuffle mode toggled : %d\n",shuffleMode))
nathan@0
   928
  if(shuffleMode && !eol) {
nathan@0
   929
    curr=0; currIndex=-1;
nathan@0
   930
    shuffle->Shuffle(maxIndex+1,-1);
nathan@0
   931
    Next();
nathan@0
   932
    }
nathan@0
   933
}
nathan@0
   934
nathan@0
   935
void cPlayManager::ToggleLoop(void)
nathan@0
   936
{
nathan@0
   937
  loopMode=!loopMode;
nathan@0
   938
  d(printf("mgr: loop mode toggled : %d\n",loopMode))
nathan@0
   939
}
nathan@0
   940
nathan@0
   941
bool cPlayManager::Info(int num, cMP3PlayInfo *pi)
nathan@0
   942
{
nathan@0
   943
  cSong *s;
nathan@0
   944
  int idx=num-1;
nathan@0
   945
  if(idx<0) { idx=currIndex; s=curr; }
nathan@0
   946
  else      { s=list.Get(idx); }
nathan@0
   947
  memset(pi,0,sizeof(*pi));
nathan@0
   948
  pi->Num=idx+1;
nathan@0
   949
  pi->MaxNum=maxIndex+1;
nathan@0
   950
  pi->Loop=loopMode;
nathan@0
   951
  pi->Shuffle=shuffleMode;
nathan@0
   952
  bool res=false;
nathan@0
   953
  if(s) {
nathan@0
   954
    strn0cpy(pi->Title,s->Name(),sizeof(pi->Title));
nathan@0
   955
    strn0cpy(pi->Filename,s->FullPath(),sizeof(pi->Filename));
nathan@0
   956
    cSongInfo *si=s->Info(false);
nathan@0
   957
    if(si && si->HasInfo()) {
nathan@6
   958
      static const char *modestr[] = { "Mono","Dual","Joint-Stereo","Stereo" };
nathan@0
   959
nathan@0
   960
      if(si->Title)  strn0cpy(pi->Title,si->Title,sizeof(pi->Title));
nathan@0
   961
      if(si->Artist) strn0cpy(pi->Artist,si->Artist,sizeof(pi->Artist));
nathan@0
   962
      if(si->Album)  strn0cpy(pi->Album,si->Album,sizeof(pi->Album));
nathan@0
   963
      strn0cpy(pi->SMode,modestr[si->ChMode],sizeof(pi->SMode));
nathan@0
   964
      pi->Year=si->Year;
nathan@0
   965
      pi->SampleFreq=si->SampleFreq;
nathan@0
   966
      pi->Bitrate=si->Bitrate;
nathan@0
   967
      pi->MaxBitrate=si->MaxBitrate;
nathan@0
   968
      res=true;
nathan@0
   969
      }
nathan@0
   970
    }
nathan@0
   971
  pi->Hash=MakeHashBuff((char *)pi,(char *)&pi->Loop-(char *)pi);
nathan@0
   972
  return res;
nathan@0
   973
}
nathan@0
   974
nathan@0
   975
void cPlayManager::Add(cPlayList *pl)
nathan@0
   976
{
nathan@0
   977
  cMutexLock lock(&listMutex);
nathan@0
   978
  bool real=false;
nathan@0
   979
  for(cSong *song=pl->First(); song; song=pl->cList<cSong>::Next(song)) {
nathan@0
   980
    cSong *ns=new cSong(song);
nathan@0
   981
    list.Add(ns);
nathan@0
   982
    real=true;
nathan@0
   983
    }
nathan@0
   984
  if(real) {
nathan@0
   985
    if(MP3Setup.BgrScan) { stopscan=false; if(!Active()) Start(); }
nathan@0
   986
    else stopscan=true;
nathan@0
   987
    bgCond.Broadcast();
nathan@0
   988
    maxIndex=list.Count()-1;
nathan@0
   989
    if(shuffleMode) shuffle->Shuffle(maxIndex+1,currIndex);
nathan@0
   990
    if(!curr) Next();
nathan@0
   991
    }
nathan@0
   992
}
nathan@0
   993
nathan@0
   994
void cPlayManager::Flush(void)
nathan@0
   995
{
nathan@0
   996
  cMutexLock lock(&listMutex);
nathan@0
   997
  Halt();
nathan@0
   998
  list.Clear();
nathan@0
   999
  shuffle->Flush();
nathan@0
  1000
}
nathan@0
  1001
nathan@0
  1002
void cPlayManager::Halt(void)
nathan@0
  1003
{
nathan@0
  1004
  cMutexLock lock(&listMutex);
nathan@0
  1005
  curr=0; currIndex=-1;
nathan@0
  1006
  playNew=true;
nathan@0
  1007
  stopscan=true; bgCond.Broadcast();
nathan@0
  1008
  NoScan(0);
nathan@0
  1009
  NoPlay(0);
nathan@0
  1010
}
nathan@0
  1011
nathan@0
  1012
void cPlayManager::NoScan(cSong *nono)
nathan@0
  1013
{
nathan@0
  1014
  // call with listMutex locked!!
nathan@0
  1015
  while((nono && pass2 && scan==nono) || (!nono && scan)) {
nathan@0
  1016
    release=true; bgCond.Broadcast();
nathan@0
  1017
    d(printf("mgr: waiting for bgr release ... (pass2=%d nono=%p scan=%p)\n",pass2,nono,scan))
nathan@0
  1018
    fgCond.Wait(listMutex);
nathan@0
  1019
    }
nathan@0
  1020
}
nathan@0
  1021
nathan@0
  1022
void cPlayManager::NoPlay(cSong *nono)
nathan@0
  1023
{
nathan@0
  1024
  // call with listMutex locked!!
nathan@0
  1025
  while((nono && play==nono) || (!nono && play)) {
nathan@0
  1026
    playNew=true;
nathan@0
  1027
    fgCond.Wait(listMutex);
nathan@0
  1028
    }
nathan@0
  1029
}
nathan@0
  1030
nathan@0
  1031
bool cPlayManager::Next(void)
nathan@0
  1032
{
nathan@0
  1033
  cMutexLock lock(&listMutex);
nathan@0
  1034
  int ni;
nathan@0
  1035
  cSong *n;
nathan@0
  1036
  if(shuffleMode) {
nathan@0
  1037
    if(curr) {
nathan@0
  1038
      ni=shuffle->Next(currIndex);
nathan@0
  1039
      if(ni<0) {
nathan@0
  1040
        if(loopMode || eol) {
nathan@0
  1041
          shuffle->Shuffle(maxIndex+1,-1);
nathan@0
  1042
          ni=shuffle->First();
nathan@0
  1043
          }
nathan@0
  1044
        else eol=true;
nathan@0
  1045
        }
nathan@0
  1046
      }
nathan@0
  1047
    else
nathan@0
  1048
      ni=shuffle->First();
nathan@0
  1049
    n=(ni>=0) ? list.Get(ni) : 0;
nathan@0
  1050
    }
nathan@0
  1051
  else {
nathan@0
  1052
    if(curr) {
nathan@0
  1053
      n=list.cList<cSong>::Next(curr);
nathan@0
  1054
      if(!n) {
nathan@0
  1055
        if(loopMode || eol) n=list.First();
nathan@0
  1056
        else eol=true;
nathan@0
  1057
        }
nathan@0
  1058
      }
nathan@0
  1059
    else
nathan@0
  1060
      n=list.First();
nathan@0
  1061
    ni=n ? n->Index() : -1;
nathan@0
  1062
    }
nathan@0
  1063
  if(n) {
nathan@0
  1064
    curr=n; currIndex=ni;
nathan@0
  1065
    playNew=true; eol=false;
nathan@0
  1066
    d(printf("mgr: next -> %d\n",currIndex))
nathan@0
  1067
    return true;
nathan@0
  1068
    }
nathan@0
  1069
  return false;
nathan@0
  1070
}
nathan@0
  1071
nathan@0
  1072
bool cPlayManager::Prev(void)
nathan@0
  1073
{
nathan@0
  1074
  cMutexLock lock(&listMutex);
nathan@0
  1075
  int ni;
nathan@0
  1076
  cSong *n;
nathan@0
  1077
  if(shuffleMode) {
nathan@0
  1078
    ni=shuffle->Prev(currIndex);
nathan@0
  1079
    n=(ni>=0) ? list.Get(ni) : 0;
nathan@0
  1080
    }
nathan@0
  1081
  else {
nathan@0
  1082
    n=list.cList<cSong>::Prev(curr);
nathan@0
  1083
    ni=n ? n->Index() : -1;
nathan@0
  1084
    }
nathan@0
  1085
  if(n) {
nathan@0
  1086
    curr=n; currIndex=ni;
nathan@0
  1087
    playNew=true; eol=false;
nathan@0
  1088
    d(printf("mgr: prev -> %d\n",currIndex))
nathan@0
  1089
    return true;
nathan@0
  1090
    }
nathan@0
  1091
  return false;
nathan@0
  1092
}
nathan@0
  1093
nathan@0
  1094
void cPlayManager::Goto(int num)
nathan@0
  1095
{
nathan@0
  1096
  cMutexLock lock(&listMutex);
nathan@0
  1097
  if(num>0 && num<=maxIndex+1) {
nathan@0
  1098
    int idx=num-1;
nathan@0
  1099
    if(shuffleMode) {
nathan@0
  1100
      if(eol) {
nathan@0
  1101
        shuffle->Shuffle(maxIndex+1,-1);
nathan@0
  1102
        currIndex=shuffle->Goto(idx,-1);
nathan@0
  1103
        }
nathan@0
  1104
      else
nathan@0
  1105
        currIndex=shuffle->Goto(idx,currIndex);
nathan@0
  1106
      }
nathan@0
  1107
    else
nathan@0
  1108
      currIndex=idx;
nathan@0
  1109
    curr=(currIndex>=0) ? list.Get(currIndex) : 0;
nathan@0
  1110
    playNew=true; eol=false;
nathan@0
  1111
    d(printf("mgr: goto -> %d\n",currIndex))
nathan@0
  1112
    }
nathan@0
  1113
}
nathan@0
  1114
nathan@0
  1115
cSong *cPlayManager::Current(void)
nathan@0
  1116
{
nathan@0
  1117
  cMutexLock lock(&listMutex);
nathan@0
  1118
  if(!play) {
nathan@0
  1119
    NoScan(curr);
nathan@0
  1120
    play=curr;
nathan@0
  1121
    playNew=false;
nathan@0
  1122
    if(play) d(printf("mgr: playing %s\n",play->Name()))
nathan@0
  1123
    else d(printf("mgr: nothing to play\n"))
nathan@0
  1124
    fgCond.Broadcast();
nathan@0
  1125
    }
nathan@0
  1126
  return play;
nathan@0
  1127
}
nathan@0
  1128
nathan@0
  1129
bool cPlayManager::NextCurrent(void)
nathan@0
  1130
{
nathan@0
  1131
  cMutexLock lock(&listMutex);
nathan@0
  1132
  return (!eol && (playNew || Next()));
nathan@0
  1133
}
nathan@0
  1134
nathan@0
  1135
bool cPlayManager::NewCurrent(void)
nathan@0
  1136
{
nathan@0
  1137
  return playNew;
nathan@0
  1138
}
nathan@0
  1139
nathan@0
  1140
void cPlayManager::Release(void)
nathan@0
  1141
{
nathan@0
  1142
  cMutexLock lock(&listMutex);
nathan@0
  1143
  play=0;
nathan@0
  1144
  fgCond.Broadcast();
nathan@0
  1145
}
nathan@0
  1146
nathan@0
  1147
// --- cOutput -----------------------------------------------------------------
nathan@0
  1148
nathan@0
  1149
struct FrameHeader {
nathan@0
  1150
  unsigned int samplerate;
nathan@0
  1151
  };
nathan@0
  1152
#define FHS sizeof(struct FrameHeader)
nathan@0
  1153
nathan@0
  1154
class cOutput {
nathan@0
  1155
protected:
nathan@0
  1156
  cMP3Player *player;
nathan@0
  1157
  cScale scale;
nathan@0
  1158
public:
nathan@0
  1159
  cOutput(cMP3Player *Player);
nathan@0
  1160
  virtual ~cOutput() {}
nathan@0
  1161
  virtual void Init(void);
nathan@0
  1162
  virtual unsigned int SampleRate(unsigned int PcmSampleRate)=0;
nathan@0
  1163
  virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)=0;
nathan@0
  1164
  virtual int Output(const unsigned char *Data, int Len, bool SOF)=0;
nathan@0
  1165
  virtual bool Poll(void)=0;
nathan@0
  1166
  virtual void Play(void)=0;
nathan@0
  1167
  virtual void Pause(void)=0;
nathan@0
  1168
#ifdef DEBUG
nathan@0
  1169
  virtual void Stats(void);
nathan@0
  1170
#endif
nathan@0
  1171
  };
nathan@0
  1172
nathan@0
  1173
cOutput::cOutput(cMP3Player *Player)
nathan@0
  1174
{
nathan@0
  1175
  player=Player;
nathan@0
  1176
}
nathan@0
  1177
nathan@0
  1178
void cOutput::Init(void)
nathan@0
  1179
{
nathan@0
  1180
  scale.Init();
nathan@0
  1181
}
nathan@0
  1182
nathan@0
  1183
#ifdef DEBUG
nathan@0
  1184
void cOutput::Stats(void)
nathan@0
  1185
{
nathan@0
  1186
  scale.Stats();
nathan@0
  1187
}
nathan@0
  1188
#endif
nathan@0
  1189
nathan@0
  1190
// --- cOutputDvb --------------------------------------------------------------
nathan@0
  1191
nathan@0
  1192
/*
nathan@0
  1193
struct LPCMHeader { int id:8;              // id
nathan@0
  1194
                    int frame_count:8;     // number of frames
nathan@0
  1195
                    int access_ptr:16;     // first acces unit pointer, i.e. start of audio frame
nathan@0
  1196
                    bool emphasis:1;       // audio emphasis on-off
nathan@0
  1197
                    bool mute:1;           // audio mute on-off
nathan@0
  1198
                    bool reserved:1;       // reserved
nathan@0
  1199
                    int frame_number:5;    // audio frame number
nathan@0
  1200
                    int quant_wlen:2;      // quantization word length
nathan@0
  1201
                    int sample_freq:2;     // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
nathan@0
  1202
                    bool reserved2:1;      // reserved
nathan@0
  1203
                    int chan_count:3;      // number of audio channels - 1 (e.g. stereo = 1)
nathan@0
  1204
                    int dyn_range_ctrl:8;  // dynamic range control (0x80 if off)
nathan@0
  1205
                    };
nathan@0
  1206
*/
nathan@0
  1207
nathan@0
  1208
#define FRAMESIZE 2048 // max. frame size allowed for DVB driver
nathan@0
  1209
nathan@0
  1210
class cOutputDvb : public cOutput {
nathan@0
  1211
private:
nathan@0
  1212
  cPoller poll;
nathan@0
  1213
  unsigned int outSr;
nathan@0
  1214
  bool only48khz;
nathan@0
  1215
public:
nathan@0
  1216
  cOutputDvb(cMP3Player *Player);
nathan@0
  1217
  virtual unsigned int SampleRate(unsigned int PcmSampleRate);
nathan@0
  1218
  virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr);
nathan@0
  1219
  virtual int Output(const unsigned char *Data, int Len, bool SOF);
nathan@0
  1220
  virtual bool Poll(void);
nathan@0
  1221
  virtual void Play(void);
nathan@0
  1222
  virtual void Pause(void);
nathan@0
  1223
  };
nathan@0
  1224
nathan@0
  1225
cOutputDvb::cOutputDvb(cMP3Player *Player)
nathan@0
  1226
:cOutput(Player)
nathan@0
  1227
{
nathan@0
  1228
  only48khz=MP3Setup.Only48kHz;
nathan@0
  1229
  outSr=0;
nathan@0
  1230
  cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio);
nathan@0
  1231
  d(printf("mp3-dvb: using DVB output\n"))
nathan@0
  1232
}
nathan@0
  1233
nathan@0
  1234
unsigned int cOutputDvb::SampleRate(unsigned int PcmSampleRate)
nathan@0
  1235
{
nathan@0
  1236
  unsigned int samplerate=48000;
nathan@0
  1237
  if(!only48khz) {
nathan@0
  1238
    switch(PcmSampleRate) { // If one of the supported frequencies, do it without resampling.
nathan@0
  1239
      case 96000:           // Select a "even" upsampling frequency if possible, too.
nathan@0
  1240
        samplerate=96000;
nathan@0
  1241
        break;
nathan@0
  1242
      //case 48000: // this is already the default ...
nathan@0
  1243
      //  samplerate=48000;
nathan@0
  1244
      //  break;
nathan@0
  1245
      case 11025:
nathan@0
  1246
      case 22050:
nathan@0
  1247
      case 44100:
nathan@0
  1248
        samplerate=44100;
nathan@0
  1249
        break;
nathan@0
  1250
      case 8000:
nathan@0
  1251
      case 16000:
nathan@0
  1252
      case 32000:
nathan@0
  1253
        samplerate=32000;
nathan@0
  1254
        break;
nathan@0
  1255
      }
nathan@0
  1256
    }
nathan@0
  1257
  return samplerate;
nathan@0
  1258
}
nathan@0
  1259
nathan@0
  1260
cFrame *cOutputDvb::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)
nathan@0
  1261
{
nathan@0
  1262
  static const unsigned char header[] = {
nathan@0
  1263
    0x00, // PES header
nathan@0
  1264
    0x00,
nathan@0
  1265
    0x01,
nathan@0
  1266
    0xBD, // private stream
nathan@0
  1267
    0x00,
nathan@0
  1268
    0x00,
nathan@0
  1269
    0x87, // mpeg2, aligned, copyright, original
nathan@0
  1270
    0x00, // no pts/dts
nathan@0
  1271
    0x00, // PES header data len
nathan@0
  1272
    0xA0, // aLPCM header
nathan@0
  1273
    0xFF,
nathan@0
  1274
    0x00,
nathan@0
  1275
    0x04,
nathan@0
  1276
    0x00,
nathan@0
  1277
    0x01, // 2-channel stereo (n-1)
nathan@0
  1278
    0x80  // neutral dynamic range
nathan@0
  1279
    };
nathan@0
  1280
  cFrame *f=0;
nathan@0
  1281
  unsigned char *buff=MALLOC(uchar,FRAMESIZE);
nathan@0
  1282
  if(buff) {
nathan@0
  1283
    struct FrameHeader *fh=(struct FrameHeader *)buff;
nathan@0
  1284
    fh->samplerate=sr;
nathan@0
  1285
    memcpy(buff+FHS,header,sizeof(header));
nathan@0
  1286
    int srMode;
nathan@0
  1287
    switch(sr) {
nathan@0
  1288
      default:
nathan@0
  1289
      case 48000: srMode=0<<4; break;
nathan@0
  1290
      case 96000: srMode=1<<4; break;
nathan@0
  1291
      case 44100: srMode=2<<4; break;
nathan@0
  1292
      case 32000: srMode=3<<4; break;
nathan@0
  1293
      }
nathan@0
  1294
    buff[14+FHS]|=srMode;
nathan@0
  1295
    unsigned int outlen=scale.ScaleBlock(buff+sizeof(header)+FHS,FRAMESIZE-sizeof(header)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherBE:amRoundBE);
nathan@0
  1296
    if(outlen) { 
nathan@0
  1297
      // lPCM has 600 fps which is 80 samples at 48kHz per channel
nathan@0
  1298
      // Frame size = (sample_rate * quantization * channels)/4800
nathan@0
  1299
      buff[10+FHS]=outlen*(4800/16/2)/sr;
nathan@0
  1300
      outlen+=(sizeof(header)-6);
nathan@0
  1301
      buff[4+FHS]=outlen>>8;
nathan@0
  1302
      buff[5+FHS]=outlen;
nathan@0
  1303
      f=new cFrame(buff,-(outlen+6+FHS),ftUnknown,index);
nathan@0
  1304
      }
nathan@0
  1305
    if(!f) free(buff);
nathan@0
  1306
    }
nathan@0
  1307
  return f;
nathan@0
  1308
}
nathan@0
  1309
nathan@0
  1310
#ifdef BROKEN_PCM
nathan@0
  1311
#include "player-mp3-sample.c"
nathan@0
  1312
#endif
nathan@0
  1313
nathan@0
  1314
int cOutputDvb::Output(const unsigned char *Data, int Len, bool SOF)
nathan@0
  1315
{
nathan@0
  1316
  int n=0;
nathan@0
  1317
  if(SOF) {
nathan@0
  1318
#ifdef BROKEN_PCM
nathan@0
  1319
    struct FrameHeader *fh=(struct FrameHeader *)Data;
nathan@0
  1320
    if(fh->samplerate!=outSr) {
nathan@0
  1321
      if(outSr) {
nathan@0
  1322
        // at this point we would need access to AUDIO_STOP/AUDIO_PLAY
nathan@0
  1323
        // ioctl, but unfortunaly VDR's API doesn't provides this.
nathan@0
  1324
        // So we have to do magic to make the driver switch samplerate.
nathan@0
  1325
        const unsigned char *p=testAudio;
nathan@0
  1326
        int pc=sizeof(testAudio);
nathan@0
  1327
        int r;
nathan@0
  1328
        do {
nathan@0
  1329
          r=player->PlayPes(p,pc);
nathan@0
  1330
          if(r>0) { p+=r; pc-=r; }
nathan@0
  1331
          if(r==0) Poll();
nathan@0
  1332
          } while(r>=0 && pc>0);
nathan@0
  1333
        }
nathan@0
  1334
      outSr=fh->samplerate;
nathan@0
  1335
      d(printf("mp3-dvb: output samplerate now %d\n",outSr))
nathan@0
  1336
      }
nathan@0
  1337
#endif
nathan@0
  1338
    n=FHS;
nathan@0
  1339
    Data+=n; Len-=n;
nathan@0
  1340
    }
nathan@0
  1341
  int r=player->PlayPes(Data,Len);
nathan@0
  1342
  return (r>=0 ? r+n : -1);
nathan@0
  1343
}
nathan@0
  1344
nathan@0
  1345
bool cOutputDvb::Poll(void)
nathan@0
  1346
{
nathan@0
  1347
  return player->DevicePoll(poll,500);
nathan@0
  1348
}
nathan@0
  1349
nathan@0
  1350
void cOutputDvb::Play(void)
nathan@0
  1351
{
nathan@0
  1352
#ifndef BROKEN_PCM
nathan@0
  1353
  player->DevicePlay();
nathan@0
  1354
#endif
nathan@0
  1355
}
nathan@0
  1356
nathan@0
  1357
void cOutputDvb::Pause(void)
nathan@0
  1358
{
nathan@0
  1359
#ifndef BROKEN_PCM
nathan@0
  1360
  player->DeviceFreeze();
nathan@0
  1361
#endif
nathan@0
  1362
}
nathan@0
  1363
nathan@0
  1364
// --- cOutputOss --------------------------------------------------------------
nathan@0
  1365
nathan@0
  1366
#ifdef WITH_OSS
nathan@0
  1367
nathan@0
  1368
const char *dspdevice="/dev/dsp";
nathan@0
  1369
nathan@0
  1370
class cOutputOss : public cOutput {
nathan@0
  1371
private:
nathan@0
  1372
  int fd;
nathan@0
  1373
  cPoller poll;
nathan@0
  1374
  unsigned int outSr;
nathan@0
  1375
  unsigned char buff[8192];
nathan@0
  1376
  //
nathan@0
  1377
  bool Reset(unsigned int sr);
nathan@0
  1378
public:
nathan@0
  1379
  cOutputOss(cMP3Player *Player);
nathan@0
  1380
  virtual ~cOutputOss();
nathan@0
  1381
  virtual void Init(void);
nathan@0
  1382
  virtual unsigned int SampleRate(unsigned int PcmSampleRate);
nathan@0
  1383
  virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr);
nathan@0
  1384
  virtual int Output(const unsigned char *Data, int Len, bool SOF);
nathan@0
  1385
  virtual bool Poll(void);
nathan@0
  1386
  virtual void Play(void);
nathan@0
  1387
  virtual void Pause(void);
nathan@0
  1388
  };
nathan@0
  1389
nathan@0
  1390
cOutputOss::cOutputOss(cMP3Player *Player)
nathan@0
  1391
:cOutput(Player)
nathan@0
  1392
{
nathan@0
  1393
  fd=-1; outSr=0;
nathan@0
  1394
  d(printf("mp3-oss: using OSS output\n"))
nathan@0
  1395
}
nathan@0
  1396
nathan@0
  1397
cOutputOss::~cOutputOss()
nathan@0
  1398
{
nathan@21
  1399
  if(fd>=0) close(fd);
nathan@0
  1400
}
nathan@0
  1401
nathan@0
  1402
void cOutputOss::Init(void)
nathan@0
  1403
{
nathan@0
  1404
  if(fd<0) {
nathan@0
  1405
    fd=open(dspdevice,O_WRONLY|O_NONBLOCK);
nathan@21
  1406
    if(fd>=0) {
nathan@21
  1407
      if(fcntl(fd,F_SETFL,0)==0)
nathan@21
  1408
        poll.Add(fd,true);
nathan@21
  1409
      else {
nathan@21
  1410
        esyslog("ERROR: Cannot make dsp device '%s' blocking: %s!",dspdevice,strerror(errno));
nathan@21
  1411
        close(fd); fd=-1;
nathan@21
  1412
        }
nathan@21
  1413
      }
nathan@0
  1414
    else esyslog("ERROR: Cannot open dsp device '%s': %s!",dspdevice,strerror(errno));
nathan@0
  1415
    }
nathan@0
  1416
  cOutput::Init();
nathan@0
  1417
}
nathan@0
  1418
nathan@0
  1419
bool cOutputOss::Reset(unsigned int sr)
nathan@0
  1420
{
nathan@0
  1421
  if(fd>=0) {
nathan@0
  1422
    CHECK(ioctl(fd,SNDCTL_DSP_SYNC,0));
nathan@0
  1423
    int format=AFMT_S16_LE;
nathan@0
  1424
    CHECK(ioctl(fd,SNDCTL_DSP_SETFMT,&format));
nathan@0
  1425
    if(format==AFMT_S16_LE) {
nathan@0
  1426
      int channels=2;
nathan@0
  1427
      CHECK(ioctl(fd,SNDCTL_DSP_CHANNELS,&channels));
nathan@0
  1428
      if(channels==2) {
nathan@0
  1429
        int real=sr;
nathan@0
  1430
        CHECK(ioctl(fd,SNDCTL_DSP_SPEED,&real));
nathan@0
  1431
        d(printf("oss: DSP samplerate now %d\n",real))
nathan@29
  1432
        if((unsigned int)abs(real-sr)<sr/50) {
nathan@0
  1433
          outSr=sr;
nathan@0
  1434
          d(printf("mp3-oss: DSP reset done\n"))
nathan@0
  1435
          return true;
nathan@0
  1436
          }
nathan@0
  1437
        else {
nathan@0
  1438
          d(printf("mp3-oss: driver can't handle samplerate %d, got %d\n",sr,real))
nathan@0
  1439
          esyslog("ERROR: OSS driver can't handle samplerate %d, got %d\n",sr,real);
nathan@0
  1440
          }
nathan@0
  1441
        }
nathan@0
  1442
      else {
nathan@0
  1443
        d(printf("mp3-oss: 2-channel stereo not supported\n"))
nathan@0
  1444
        esyslog("ERROR: OSS driver doesn't support 2-channel stereo.");
nathan@0
  1445
        }
nathan@0
  1446
      }
nathan@0
  1447
    else {
nathan@0
  1448
      d(printf("mp3-oss: little-endian samples not supported\n"))
nathan@0
  1449
      esyslog("ERROR: OSS driver doesn't support 16-bit little-endian samples.");
nathan@0
  1450
      }
nathan@0
  1451
    close(fd); fd=-1;
nathan@0
  1452
    }
nathan@0
  1453
  return false;
nathan@0
  1454
}
nathan@0
  1455
nathan@0
  1456
unsigned int cOutputOss::SampleRate(unsigned int PcmSampleRate)
nathan@0
  1457
{
nathan@0
  1458
  return PcmSampleRate;
nathan@0
  1459
}
nathan@0
  1460
nathan@0
  1461
cFrame *cOutputOss::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)
nathan@0
  1462
{
nathan@0
  1463
  struct FrameHeader *fh=(struct FrameHeader *)buff;
nathan@0
  1464
  fh->samplerate=sr;
nathan@0
  1465
  cFrame *f=0;
nathan@0
  1466
  unsigned int outlen=scale.ScaleBlock(buff+FHS,sizeof(buff)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherLE:amRoundLE);
nathan@0
  1467
  if(outlen) 
nathan@0
  1468
    f=new cFrame(buff,outlen+FHS,ftUnknown,index);
nathan@0
  1469
  return f;
nathan@0
  1470
}
nathan@0
  1471
nathan@0
  1472
int cOutputOss::Output(const unsigned char *Data, int Len, bool SOF)
nathan@0
  1473
{
nathan@0
  1474
  if(fd>=0) {
nathan@0
  1475
    int n=0;
nathan@0
  1476
    if(SOF) {
nathan@0
  1477
      struct FrameHeader *fh=(struct FrameHeader *)Data;
nathan@0
  1478
      if(fh->samplerate!=outSr) Reset(fh->samplerate);
nathan@0
  1479
      n=FHS;
nathan@0
  1480
      Data+=n; Len-=n;
nathan@0
  1481
      }
nathan@21
  1482
    if(poll.Poll(0)) {
nathan@21
  1483
      int r=write(fd,Data,Len);
nathan@21
  1484
      if(r<0 && FATALERRNO) return -1;
nathan@21
  1485
      if(r>0) n+=r;
nathan@21
  1486
      }
nathan@21
  1487
    return n;
nathan@0
  1488
    }
nathan@0
  1489
  return -1;
nathan@0
  1490
}
nathan@0
  1491
nathan@0
  1492
bool cOutputOss::Poll(void)
nathan@0
  1493
{
nathan@0
  1494
  return fd>=0 ? poll.Poll(500) : false;
nathan@0
  1495
}
nathan@0
  1496
nathan@0
  1497
void cOutputOss::Play(void)
nathan@0
  1498
{
nathan@0
  1499
}
nathan@0
  1500
nathan@0
  1501
void cOutputOss::Pause(void)
nathan@0
  1502
{
nathan@0
  1503
  CHECK(ioctl(fd,SNDCTL_DSP_POST,0));
nathan@0
  1504
}
nathan@0
  1505
nathan@0
  1506
#endif
nathan@0
  1507
nathan@0
  1508
// --- cMP3Player --------------------------------------------------------------
nathan@0
  1509
nathan@0
  1510
cMP3Player::cMP3Player()
nathan@0
  1511
:cPlayer(MP3Setup.BackgrMode==1 ? pmAudioOnly : pmAudioOnlyBlack)
nathan@0
  1512
{
nathan@0
  1513
  active=true; started=false; isStream=false;
nathan@0
  1514
  ringBuffer=new cRingBufferFrame(MP3BUFSIZE);
nathan@0
  1515
  rframe=0; pframe=0; decoder=0; output=0;
nathan@0
  1516
  playMode=pmStartup; state=msStop;
nathan@0
  1517
  playindex=total=0;
nathan@0
  1518
}
nathan@0
  1519
nathan@0
  1520
cMP3Player::~cMP3Player()
nathan@0
  1521
{
nathan@0
  1522
  Detach();
nathan@0
  1523
  delete ringBuffer;
nathan@0
  1524
}
nathan@0
  1525
nathan@0
  1526
void cMP3Player::Activate(bool On)
nathan@0
  1527
{
nathan@0
  1528
  if(On) {
nathan@0
  1529
    d(printf("mp3: player active true requested...\n"))
nathan@0
  1530
    if(!started) {
nathan@0
  1531
      playMode=pmStartup; Start(); started=true;
nathan@0
  1532
      playModeMutex.Lock();
nathan@0
  1533
      WaitPlayMode(pmStartup,true); // wait for the decoder to become ready
nathan@0
  1534
      playModeMutex.Unlock();
nathan@0
  1535
      Lock();
nathan@0
  1536
      Play();
nathan@0
  1537
      Unlock();
nathan@0
  1538
      }
nathan@0
  1539
    d(printf("mp3: player active true done\n"))
nathan@0
  1540
    }
nathan@0
  1541
  else if(started && active) {
nathan@0
  1542
    d(printf("mp3: player active false requested...\n"))
nathan@0
  1543
    Lock(); StopPlay(); Unlock();
nathan@0
  1544
    active=false;
nathan@0
  1545
    SetPlayMode(pmStartup);
nathan@0
  1546
    Cancel(2);
nathan@0
  1547
    d(printf("mp3: player active false done\n"))
nathan@0
  1548
    }
nathan@0
  1549
}
nathan@0
  1550
nathan@0
  1551
void cMP3Player::SetPlayMode(ePlayMode mode)
nathan@0
  1552
{
nathan@0
  1553
  playModeMutex.Lock();
nathan@0
  1554
  if(mode!=playMode) {
nathan@0
  1555
    playMode=mode;
nathan@0
  1556
    dm(printf("mp3: setting mode=%d (pid=%d)\n",mode,getpid()))
nathan@0
  1557
    playModeCond.Broadcast();
nathan@0
  1558
    }
nathan@0
  1559
  playModeMutex.Unlock();
nathan@0
  1560
}
nathan@0
  1561
nathan@0
  1562
void cMP3Player::WaitPlayMode(ePlayMode mode, bool inv)
nathan@0
  1563
{
nathan@0
  1564
  // must be called with playModeMutex LOCKED !!!
nathan@0
  1565
nathan@0
  1566
  while(active && ((!inv && mode!=playMode) || (inv && mode==playMode))) {
nathan@0
  1567
    dm(printf("mp3: entering wait for mode%s%d with mode=%d (pid=%d)\n",inv?"!=":"==",mode,playMode,getpid()))
nathan@0
  1568
    playModeCond.Wait(playModeMutex);
nathan@0
  1569
    dm(printf("mp3: returning from wait with mode=%d (pid=%d)\n",playMode,getpid()))
nathan@0
  1570
    }
nathan@0
  1571
}
nathan@0
  1572
nathan@0
  1573
void cMP3Player::Action(void)
nathan@0
  1574
{
nathan@0
  1575
  cSong *playing=0;
nathan@0
  1576
  struct mad_pcm *pcm=0;
nathan@0
  1577
  cResample resample[2];
nathan@0
  1578
  unsigned int nsamples[2];
nathan@0
  1579
  const mad_fixed_t *data[2];
nathan@0
  1580
  cLevel level;
nathan@0
  1581
  cNormalize norm;
nathan@0
  1582
  bool haslevel=false;
nathan@0
  1583
  const unsigned char *p=0;
nathan@0
  1584
  int pc=0, readindex=0;
nathan@0
  1585
  bool imageValid=true;
nathan@23
  1586
  cTimeMs imageCheck;
nathan@0
  1587
#ifdef DEBUG
nathan@0
  1588
  int beat=0;
nathan@0
  1589
#endif
nathan@0
  1590
#ifdef DEBUG_DELAY
nathan@23
  1591
  cTimeMs lastwrite(2000000);
nathan@0
  1592
#endif
nathan@0
  1593
nathan@0
  1594
  dsyslog("mp3: player thread started (pid=%d)", getpid());
nathan@0
  1595
  state=msStop;
nathan@0
  1596
  SetPlayMode(pmStopped);
nathan@0
  1597
nathan@0
  1598
  delete output; output=0;
nathan@0
  1599
#ifdef WITH_OSS
nathan@0
  1600
  if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) output=new cOutputOss(this);
nathan@0
  1601
#endif
nathan@0
  1602
  if(MP3Setup.AudioOutMode==AUDIOOUTMODE_DVB) output=new cOutputDvb(this);
nathan@0
  1603
  if(!output) {
nathan@0
  1604
    d(printf("mp3: audiooutmode mismatch or no output driver\n"))
nathan@0
  1605
    esyslog("ERROR: no audio output driver. balling out");
nathan@0
  1606
    goto abort;
nathan@0
  1607
    }
nathan@0
  1608
nathan@0
  1609
  while(active) {
nathan@0
  1610
#ifdef DEBUG
nathan@0
  1611
    {
nathan@0
  1612
    int now=time(0);
nathan@0
  1613
    if(now>=beat) {
nathan@0
  1614
      int avail=ringBuffer->Available();
nathan@0
  1615
      printf("mp3: heartbeat buffer=%d now=%d\n",avail,now&4095);
nathan@0
  1616
      //output->Stats(); if(haslevel) norm.Stats();
nathan@0
  1617
      beat=now+(avail>(MP3BUFSIZE*10/100) ? (avail<(MP3BUFSIZE*50/100) ? 2 : 20) : 1);
nathan@0
  1618
      }
nathan@0
  1619
    }
nathan@0
  1620
#endif
nathan@0
  1621
nathan@0
  1622
    Lock();
nathan@0
  1623
nathan@0
  1624
next:
nathan@23
  1625
    if(!pframe && playing && !imageValid && imageCheck.Elapsed()) {
nathan@0
  1626
      unsigned char *mem;
nathan@0
  1627
      int len;
nathan@23
  1628
      imageCheck.Set(250);
nathan@0
  1629
      imageValid=playing->Image(mem,len);
nathan@0
  1630
      if(mem) {
nathan@23
  1631
        if(playindex) cCondWait::SleepMs(80); // stillpicture ioctl freezes without this
nathan@0
  1632
        DeviceStillPicture(mem,len);
nathan@0
  1633
        free(mem);
nathan@0
  1634
        }
nathan@0
  1635
      }
nathan@0
  1636
nathan@0
  1637
    bool SOF=false;
nathan@0
  1638
    if(!pframe && playMode==pmPlay) {
nathan@0
  1639
      pframe=ringBuffer->Get();
nathan@0
  1640
      if(pframe) {
nathan@0
  1641
        playindex=pframe->Index();
nathan@0
  1642
        p=pframe->Data();
nathan@0
  1643
        pc=pframe->Count();
nathan@0
  1644
        SOF=true;
nathan@0
  1645
        }
nathan@0
  1646
      }
nathan@0
  1647
nathan@0
  1648
    if(pframe) {
nathan@0
  1649
#ifdef DEBUG_DELAY
nathan@0
  1650
      {
nathan@23
  1651
      if(lastwrite.TimedOut())
nathan@23
  1652
        printf("mp3: write delayed %llu ms\n",lastwrite.Elapsed());
nathan@23
  1653
      lastwrite.Set(DEBUG_DELAY+50);
nathan@0
  1654
      }
nathan@0
  1655
#endif
nathan@0
  1656
      int w=output->Output(p,pc,SOF);
nathan@0
  1657
      if(w>0) {
nathan@0
  1658
        p+=w; pc-=w;
nathan@0
  1659
        if(pc<=0) {
nathan@0
  1660
          ringBuffer->Drop(pframe);
nathan@0
  1661
          pframe=0;
nathan@0
  1662
          goto next;
nathan@0
  1663
          }
nathan@0
  1664
        }
nathan@0
  1665
      else if(w<0 && FATALERRNO) {
nathan@0
  1666
        LOG_ERROR;
nathan@0
  1667
        d(printf("mp3: output failed: %s\n",strerror(errno)))
nathan@0
  1668
        Unlock();
nathan@0
  1669
        goto abort;
nathan@0
  1670
        }
nathan@0
  1671
      }
nathan@0
  1672
nathan@0
  1673
    if(mgr->NewCurrent() && playMode==pmPlay && state!=msStart) {
nathan@0
  1674
      Empty();
nathan@0
  1675
      state=msRestart;
nathan@0
  1676
      d(printf("mp3: stale song change, restart.\n"))
nathan@0
  1677
      }
nathan@0
  1678
nathan@0
  1679
    if(!rframe && playMode==pmPlay) {
nathan@0
  1680
      switch(state) {
nathan@0
  1681
        case msStart:
nathan@0
  1682
          d(printf("mp3: starting play\n"))
nathan@0
  1683
          mgr->Throttle(true);
nathan@0
  1684
          playindex=readindex=total=0;
nathan@0
  1685
          playing=mgr->Current();
nathan@0
  1686
          if(playing) {
nathan@0
  1687
            if((decoder=playing->Decoder()) && decoder->Start()) {
nathan@0
  1688
              isStream=decoder->IsStream(); levelgood=!isStream; haslevel=false;
nathan@0
  1689
              cSongInfo *si=playing->Info(true);
nathan@0
  1690
              if(si) {
nathan@0
  1691
                if(si->Level>0.0) {
nathan@0
  1692
                  d(printf("mp3: found song level=%f peak=%f\n",si->Level,si->Peak))
nathan@0
  1693
                  haslevel=true;
nathan@0
  1694
                  norm.Init(si->Level,si->Peak);
nathan@0
  1695
                  }
nathan@0
  1696
                if(si->HasInfo())
nathan@0
  1697
                  total=SecondsToFrames(si->Total);
nathan@0
  1698
                }
nathan@0
  1699
              d(printf("mp3: isStream=%d levelgood=%d haslevel=%d\n",isStream,levelgood,haslevel))
nathan@0
  1700
              output->Init();
nathan@0
  1701
              level.Init();
nathan@0
  1702
              if(MP3Setup.BackgrMode==2) imageValid=false;
nathan@0
  1703
              state=msDecode;
nathan@0
  1704
              break;
nathan@0
  1705
              }
nathan@0
  1706
            else
nathan@0
  1707
              esyslog("ERROR: playlist entry %s is not a valid file",playing->Name());
nathan@0
  1708
            }
nathan@0
  1709
          else
nathan@0
  1710
            d(printf("mp3: no current on start play\n"))
nathan@0
  1711
          state=msEof;
nathan@0
  1712
          break;
nathan@0
  1713
        case msDecode:
nathan@0
  1714
          {
nathan@0
  1715
#ifdef DEBUG_DELAY
nathan@23
  1716
          cTimeMs check(DEBUG_DELAY);
nathan@0
  1717
#endif
nathan@0
  1718
          struct Decode *ds=decoder->Decode();
nathan@0
  1719
#ifdef DEBUG_DELAY
nathan@23
  1720
          if(check.TimedOut()) printf("mp3: decode delayed %llu ms\n",check.Elapsed());
nathan@0
  1721
#endif
nathan@0
  1722
          switch(ds->status) {
nathan@0
  1723
            case dsPlay:
nathan@0
  1724
              pcm=ds->pcm;
nathan@0
  1725
              readindex=ds->index;
nathan@0
  1726
              state=msNormalize;
nathan@0
  1727
              break;
nathan@0
  1728
            case dsSkip:
nathan@0
  1729
            case dsSoftError:
nathan@0
  1730
              // skipping, state unchanged, next decode
nathan@0
  1731
              break;
nathan@0
  1732
            case dsEof:
nathan@0
  1733
              if(!haslevel && levelgood) { // save level & peak to infocache on eof
nathan@0
  1734
                double l=level.GetLevel();
nathan@0
  1735
                if(l>0.0) {
nathan@0
  1736
                  cSongInfo *si=decoder->SongInfo(false);
nathan@0
  1737
                  cFileInfo *fi=decoder->FileInfo();
nathan@0
  1738
                  if(si && fi) {
nathan@0
  1739
                    si->Level=l;
nathan@0
  1740
                    si->Peak=level.GetPeak();
nathan@0
  1741
                    InfoCache.Cache(si,fi);
nathan@0
  1742
                    }
nathan@0
  1743
                  }
nathan@0
  1744
                }
nathan@0
  1745
              state=msEof;
nathan@0
  1746
              break;
nathan@0
  1747
            case dsOK:
nathan@0
  1748
            case dsError:
nathan@0
  1749
              state=msError;
nathan@0
  1750
              break;
nathan@0
  1751
            }
nathan@0
  1752
          break;
nathan@0
  1753
          }
nathan@0
  1754
        case msNormalize:
nathan@0
  1755
          if(!haslevel) { if(levelgood) level.GetPower(pcm); }
nathan@0
  1756
          else norm.AddGain(pcm);
nathan@0
  1757
          state=msResample;
nathan@0
  1758
          break;
nathan@0
  1759
        case msResample:
nathan@0
  1760
#ifdef DEBUG
nathan@0
  1761
          {
nathan@0
  1762
          static unsigned int oldrate=0;
nathan@0
  1763
          if(oldrate!=pcm->samplerate) {
nathan@0
  1764
            printf("mp3: new input sample rate %d\n",pcm->samplerate);
nathan@0
  1765
            oldrate=pcm->samplerate;
nathan@0
  1766
            }
nathan@0
  1767
          }
nathan@0
  1768
#endif
nathan@0
  1769
          nsamples[0]=nsamples[1]=pcm->length;
nathan@0
  1770
          data[0]=pcm->samples[0];
nathan@0
  1771
          data[1]=pcm->channels>1 ? pcm->samples[1]:0;
nathan@0
  1772
nathan@0
  1773
          dvbSampleRate=output->SampleRate(pcm->samplerate);
nathan@0
  1774
          if(dvbSampleRate!=pcm->samplerate) {
nathan@0
  1775
            if(resample[0].SetInputRate(pcm->samplerate,dvbSampleRate)) {
nathan@0
  1776
              nsamples[0]=resample[0].ResampleBlock(nsamples[0],data[0]);
nathan@0
  1777
              data[0]    =resample[0].Resampled();
nathan@0
  1778
              }
nathan@0
  1779
            if(data[1] && resample[1].SetInputRate(pcm->samplerate,dvbSampleRate)) {
nathan@0
  1780
              nsamples[1]=resample[1].ResampleBlock(nsamples[1],data[1]);
nathan@0
  1781
              data[1]    =resample[1].Resampled();
nathan@0
  1782
              }
nathan@0
  1783
            }
nathan@0
  1784
          state=msOutput;
nathan@0
  1785
          break;
nathan@0
  1786
        case msOutput:
nathan@0
  1787
          if(nsamples[0]>0) rframe=output->MakeFrame(nsamples[0],data,readindex,dvbSampleRate);
nathan@0
  1788
          else state=msDecode;
nathan@0
  1789
          break;
nathan@0
  1790
        case msError:
nathan@0
  1791
        case msEof:
nathan@0
  1792
          d(printf("mp3: eof or error\n"))
nathan@0
  1793
          state=msWait;
nathan@0
  1794
          // fall through
nathan@0
  1795
        case msRestart:
nathan@0
  1796
        case msStop:
nathan@0
  1797
          d(printf("mp3: stopping play\n"))
nathan@0
  1798
          if(decoder) { decoder->Stop(); decoder=0; }
nathan@0
  1799
          mgr->Release(); playing=0; imageValid=true;
nathan@0
  1800
          levelgood=false;
nathan@0
  1801
#ifdef DEBUG
nathan@0
  1802
          output->Stats(); if(haslevel) norm.Stats();
nathan@0
  1803
#endif
nathan@0
  1804
          if(state==msStop) SetPlayMode(pmStopped);
nathan@0
  1805
          if(state==msRestart) state=msStart;
nathan@0
  1806
          break;
nathan@0
  1807
        case msWait:
nathan@0
  1808
          if(ringBuffer->Available()==0) {
nathan@0
  1809
            if(mgr->NextCurrent()) {
nathan@0
  1810
              d(printf("mp3: playing next\n"))
nathan@0
  1811
              state=msStart;
nathan@0
  1812
              }
nathan@0
  1813
            else {
nathan@0
  1814
              d(printf("mp3: end of playlist\n"))
nathan@0
  1815
              if(MP3Setup.AbortAtEOL) {
nathan@0
  1816
                active=false;
nathan@0
  1817
                d(printf("mp3: aborting player...\n"))
nathan@0
  1818
                }
nathan@0
  1819
              else d(printf("mp3: player idle...\n"))
nathan@0
  1820
              SetPlayMode(pmStopped);
nathan@0
  1821
              }
nathan@0
  1822
            }
nathan@0
  1823
          break;
nathan@0
  1824
        }
nathan@0
  1825
      }
nathan@0
  1826
nathan@0
  1827
    if(rframe && ringBuffer->Put(rframe)) rframe=0;
nathan@0
  1828
nathan@0
  1829
    Unlock();
nathan@0
  1830
nathan@0
  1831
    if((rframe || state==msWait) && pframe) {
nathan@0
  1832
      mgr->Throttle(false);
nathan@0
  1833
      output->Poll();
nathan@0
  1834
      }
nathan@0
  1835
    else if(playMode!=pmPlay) {
nathan@0
  1836
      mgr->Throttle(false);
nathan@0
  1837
      if(!imageValid)
nathan@23
  1838
        cCondWait::SleepMs(100);
nathan@0
  1839
      else {
nathan@0
  1840
        playModeMutex.Lock();
nathan@0
  1841
        if(playMode!=pmPlay) WaitPlayMode(playMode,true);
nathan@0
  1842
        playModeMutex.Unlock();
nathan@0
  1843
        }
nathan@0
  1844
#ifdef DEBUG_DELAY
nathan@23
  1845
      lastwrite.Set(2000000);
nathan@0
  1846
#endif
nathan@0
  1847
      }
nathan@0
  1848
    else if(state!=msWait && ringBuffer->Available()<(MP3BUFSIZE*50/100)) {
nathan@0
  1849
      mgr->Throttle(true);
nathan@0
  1850
      }
nathan@0
  1851
    }
nathan@0
  1852
nathan@0
  1853
abort:
nathan@0
  1854
  Lock();
nathan@0
  1855
  delete rframe;
nathan@0
  1856
  delete output; output=0;
nathan@0
  1857
  if(decoder) { decoder->Stop(); decoder=0; }
nathan@0
  1858
  mgr->Release(); playing=0;
nathan@0
  1859
  SetPlayMode(pmStopped);
nathan@0
  1860
  Unlock();
nathan@0
  1861
  active=false;
nathan@0
  1862
nathan@0
  1863
  dsyslog("mp3: player thread ended (pid=%d)", getpid());
nathan@0
  1864
}
nathan@0
  1865
nathan@0
  1866
void cMP3Player::Empty(void)
nathan@0
  1867
{
nathan@0
  1868
  Lock();
nathan@0
  1869
  delete rframe; rframe=0; pframe=0;
nathan@0
  1870
  ringBuffer->Clear();
nathan@0
  1871
  DeviceClear();
nathan@0
  1872
  Unlock();
nathan@0
  1873
}
nathan@0
  1874
nathan@0
  1875
void cMP3Player::StopPlay(void) // StopPlay() must be called in locked state!!!
nathan@0
  1876
{
nathan@0
  1877
  if(playMode!=pmStopped) {
nathan@0
  1878
    Empty();
nathan@0
  1879
    state=msStop;
nathan@0
  1880
    SetPlayMode(pmPlay);
nathan@0
  1881
    Unlock();                 // let the decode thread process the stop signal
nathan@0
  1882
    playModeMutex.Lock();
nathan@0
  1883
    WaitPlayMode(pmStopped,false);
nathan@0
  1884
    playModeMutex.Unlock();
nathan@0
  1885
    Lock();
nathan@0
  1886
    }
nathan@0
  1887
}
nathan@0
  1888
nathan@0
  1889
void cMP3Player::Pause(void)
nathan@0
  1890
{
nathan@0
  1891
  Lock();
nathan@0
  1892
  if(playMode==pmPaused) Play();
nathan@0
  1893
  else if(playMode==pmPlay && !isStream) {
nathan@0
  1894
    d(printf("mp3: pause\n"))
nathan@0
  1895
    if(output) output->Pause();
nathan@0
  1896
    SetPlayMode(pmPaused);
nathan@0
  1897
    }
nathan@0
  1898
  Unlock();
nathan@0
  1899
}
nathan@0
  1900
nathan@0
  1901
void cMP3Player::Play(void)
nathan@0
  1902
{
nathan@0
  1903
  Lock();
nathan@0
  1904
  if(playMode!=pmPlay) {
nathan@0
  1905
    d(printf("mp3: play\n"))
nathan@0
  1906
    if(playMode==pmStopped) state=msStart;
nathan@0
  1907
    if(output) output->Play();
nathan@0
  1908
    SetPlayMode(pmPlay);
nathan@0
  1909
    }
nathan@0
  1910
  Unlock();
nathan@0
  1911
}
nathan@0
  1912
nathan@0
  1913
bool cMP3Player::PrevCheck(void)
nathan@0
  1914
{
nathan@0
  1915
  bool res=false;
nathan@0
  1916
  Lock();
nathan@0
  1917
  if(playindex>=2000 && !isStream) {
nathan@0
  1918
    state=msRestart; res=true;
nathan@0
  1919
    Empty();
nathan@0
  1920
    d(printf("mp3: skip to start of song\n"))
nathan@0
  1921
    }
nathan@0
  1922
  Unlock();
nathan@0
  1923
  return res;
nathan@0
  1924
}
nathan@0
  1925
nathan@0
  1926
void cMP3Player::SkipSeconds(int secs)
nathan@0
  1927
{
nathan@0
  1928
  if(playMode!=pmStopped && !isStream) {
nathan@0
  1929
    Lock();
nathan@0
  1930
    d(printf("mp3: skip secs %d\n",secs))
nathan@0
  1931
    if(playMode==pmPaused) SetPlayMode(pmPlay);
nathan@0
  1932
    float bufsecs=(float)ringBuffer->Available() / (float)(dvbSampleRate*OUT_FACT);
nathan@0
  1933
    d(printf("mp3: ringbuffer available %f secs\n",bufsecs))
nathan@0
  1934
    if(secs>0 && bufsecs>=(float)secs) {
nathan@0
  1935
      // clear intermediate queue
nathan@0
  1936
      if(pframe) {
nathan@0
  1937
        ringBuffer->Drop(pframe);
nathan@0
  1938
        pframe=0;
nathan@0
  1939
        }
nathan@0
  1940
      DeviceClear();
nathan@0
  1941
      // skip inside ringbuffer
nathan@0
  1942
      int skipindex=playindex+secs*1000;
nathan@0
  1943
      d(printf("mp3: skipping play=%d skip=%d ...",playindex,skipindex))
nathan@0
  1944
      cFrame *f;
nathan@0
  1945
      do {
nathan@0
  1946
        f=ringBuffer->Get();
nathan@0
  1947
        if(f) {
nathan@0
  1948
          playindex=f->Index();
nathan@0
  1949
          ringBuffer->Drop(f);
nathan@0
  1950
          d(printf("*"))
nathan@0
  1951
          }
nathan@0
  1952
        } while(f && playindex<skipindex);
nathan@0
  1953
      d(printf("\nmp3: skipped play=%d skip=%d\n",playindex,skipindex))
nathan@0
  1954
      }
nathan@0
  1955
    else {
nathan@0
  1956
      if(decoder && decoder->Skip(secs,bufsecs)) levelgood=false;
nathan@0
  1957
      Empty();
nathan@0
  1958
      }
nathan@0
  1959
    Unlock();
nathan@0
  1960
    }
nathan@0
  1961
}
nathan@0
  1962
nathan@0
  1963
bool cMP3Player::GetIndex(int &Current, int &Total, bool SnapToIFrame)
nathan@0
  1964
{
nathan@0
  1965
  Current=SecondsToFrames(playindex/1000); Total=total;
nathan@0
  1966
  return total>=0;
nathan@0
  1967
}
nathan@0
  1968
nathan@0
  1969
bool cMP3Player::GetReplayMode(bool &Play, bool &Forward, int &Speed)
nathan@0
  1970
{
nathan@0
  1971
  Play=(playMode==pmPlay);
nathan@0
  1972
  Forward=true;
nathan@0
  1973
  Speed=-1;
nathan@0
  1974
  return true;
nathan@0
  1975
}