jmixer.cpp Source File

Main Page Modules Class Hierarchy Compound List File List Compound Members File Members

# jmixer.cpp

    00001 /* MuSE - Multiple Streaming Engine
    00002  * Copyright (C) 2000-2004 Denis Roio aka jaromil <jaromil@dyne.org>
    00003  *
    00004  * This source code is free software; you can redistribute it and/or
    00005  * modify it under the terms of the GNU Public License as published
    00006  * by the Free Software Foundation; either version 2 of the License,
    00007  * or (at your option) any later version.
    00008  *
    00009  * This source code is distributed in the hope that it will be useful,
    00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00012  * Please refer to the GNU Public License for more details.
    00013  *
    00014  * You should have received a copy of the GNU Public License along with
    00015  * this source code; if not, write to:
    00016  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    00017
    00018  "$Id: jmixer.cpp,v 1.10 2004/04/09 17:50:43 jaromil Exp $"
    00019
    00020  */
    00021
    00022 #include <iostream>
    00023 #include <math.h>
    00024 #include <stdio.h>
    00025 #include <stdlib.h>
    00026 #include <unistd.h>
    00027 #include <dirent.h>
    00028 #include <sys/ioctl.h>
    00029 #include <sys/stat.h>
    00030 #include <errno.h>
    00031 #include <fcntl.h>
    00032 #include <sys/soundcard.h>
    00033 #include <string.h>
    00034 #include <signal.h>
    00035 #include <sys/types.h>
    00036 #include <sys/wait.h>
    00037 #include <termios.h>
    00038 #include <sys/time.h>
    00039
    00040 #include <jutils.h>
    00041 #include <audioproc.h>
    00042 #include <jmixer.h>
    00043 #include <playlist.h>
    00044 #include <inchannels.h>
    00045
    00046 #include <config.h>
    00047
    00048 #ifdef HAVE_VORBIS
    00049 #include <out_vorbis.h>
    00050 #endif
    00051
    00052 #ifdef HAVE_LAME
    00053 #include <out_lame.h>
    00054 #endif
    00055
    00056 #define CODENAME "COTURNIX"
    00057
    00058 /* process_buffer BUF_SIZE is:
    00059    BUF_SIZE of 32bit ints *2channels *8resampling_space
    00060
    00061    audio_buffer BUF_SIZE is:
    00062    BUF_SIZE of 16bit short *2channels *8resampling_space
    00063 */
    00064
    00065 #define PARACHAN \
    00066   if(!chan[ch]) { \
    00067     warning("%i:%s %s - channel %i is NULL", \
    00068     __LINE__,__FILE__,__FUNCTION__,ch); \
    00069     return(false); \
    00070   }
    00071
    00072
    00073 Stream_mixer::Stream_mixer() {
    00074   int i;
    00075   for(i=0;i<MAX_CHANNELS;i++)
    00076     chan[i] = NULL;
    00077
    00078   /* here memset takes byte num */
    00079   memset(process_buffer,0,PROCBUF_SIZE*sizeof(int32_t));
    00080   memset(audio_buffer,0,PROCBUF_SIZE*sizeof(int16_t));
    00081
    00082   dsp = 0;
    00083   max = 0;
    00084   have_gui = false;
    00085
    00086   dspout = false;
    00087   linein = false;
    00088   fileout = false;
    00089   quit = false;
    00090
    00091   for(i=0;i<8;i++) peak[i] = 0;
    00092   cpeak = 0;
    00093
    00094   /* this is the base seed for new encoders id */
    00095   idseed = 0; //abs(time(NULL) & getpid()) >> 2;
    00096
    00097   if(pthread_mutex_init (&_mutex,NULL) == -1)
    00098     error("error initializing POSIX thread mutex");
    00099   if(pthread_cond_init (&_cond, NULL) == -1)
    00100     error("error initializing POSIX thread condtition");
    00101   unlock();
    00102 }
    00103
    00104 Stream_mixer::~Stream_mixer() {
    00105   quit = true;
    00106   func("Stream_mixer::~Stream_mixer()");
    00107   int i;
    00108
    00109   if(dsp>0) {
    00110     act("closing soundcard");
    00111     close_soundcard();
    00112   }
    00113
    00114   act("deleting input channels");
    00115   for(i=0;i<MAX_CHANNELS;i++) {
    00116     /* delete_channel(i); */
    00117     if(chan[i]) delete_channel(i);
    00118   }
    00119
    00120   act("deleting output channels");
    00121   OutChannel *outch = (OutChannel*) outchans.begin();
    00122   while(outch) {
    00123     delete_enc( outch->id );
    00124     outch = (OutChannel*) outchans.begin();
    00125   }
    00126
    00127   func("deleting thread mutexes");
    00128   if(pthread_mutex_destroy(&_mutex) == -1)
    00129     error("error destroying POSIX thread mutex");
    00130   if(pthread_cond_destroy(&_cond) == -1)
    00131     error("error destroying POSIX thread condition");
    00132
    00133 }
    00134
    00135 void Stream_mixer::register_gui(GUI *reg_gui) {
    00136   char temp[256];
    00137   gui = reg_gui;
    00138   have_gui = true;
    00139   sprintf(temp,"%s %s codename \"%s\"",PACKAGE, VERSION, CODENAME);
    00140   gui->set_title(temp);
    00141 }
    00142
    00143 bool Stream_mixer::open_soundcard(bool in, bool out) {
    00144
    00145   if(!in && !out) return false;
    00146
    00147   int format,tstereo,speed,caps; //,val;
    00148
    00149   /* can't be nonblocking for correct playing
    00150      but to check if /dev/dsp is free we must use that */
    00151   if((dsp=open("/dev/dsp",O_RDWR|O_NONBLOCK))==-1) {
    00152     error("can't open soundcard: %s", strerror(errno));
    00153     return(false);
    00154   } else {
    00155     if(fcntl(dsp, F_SETFL, 0) <0) { /* remove O_NONBLOCK */
    00156       close(dsp);
    00157       error("can't switch to blocking mode");
    00158       return(false);
    00159     }
    00160     notice("Found soundcard on /dev/dsp");
    00161   }
    00162
    00163   /* BUFFER FRAGMENTATION
    00164      val = (FRAGCOUNT << 16) | FRAGSIZE;
    00165      if(ioctl(dsp,SNDCTL_DSP_SETFRAGMENT,&val)==-1)
    00166      error("failed to set dsp buffers:");
    00167   */
    00168
    00169
    00170   format = AFMT_S16_LE;
    00171
    00172   tstereo = 1; /* only stereo _DSP_ in/out */
    00173   speed = SAMPLE_RATE; /* 44100hz mixing */
    00174
    00175   ioctl(dsp, SNDCTL_DSP_GETCAPS, &caps);
    00176   if(caps & DSP_CAP_DUPLEX) {
    00177     fullduplex = true;
    00178     act("full duplex supported. good");
    00179     ioctl(dsp, SNDCTL_DSP_SETDUPLEX, 0);
    00180     ioctl(dsp, DSP_CAP_DUPLEX, 0);
    00181   } else {
    00182     act("only halfduplex is supported");
    00183     fullduplex = false;
    00184   }
    00185
    00186   /* FORMAT
    00187      if(ioctl(dsp,SNDCTL_DSP_SETFMT,&format)==-1)
    00188      error("failed to set data format");
    00189   */
    00190   if(ioctl(dsp,SNDCTL_DSP_SAMPLESIZE,&format) <0)
    00191     error("failed to set dsp samplesize");
    00192
    00193   /* CHANNELS */
    00194   if(ioctl(dsp,SNDCTL_DSP_STEREO,&tstereo)==-1)
    00195     error("something went wrong with the stereo setting");
    00196
    00197   /* SAMPLERATE */
    00198   if(ioctl(dsp,SNDCTL_DSP_SPEED,&speed)==-1)
    00199     error("speed setting failed");
    00200
    00201   /* GET FRAG SIZE BACK
    00202      if(ioctl(dsp,SNDCTL_DSP_GETBLKSIZE,&val)==-1)
    00203      error("get block size failed to return");
    00204   */
    00205
    00206   act("mixing 16bit %dHz stereo",speed);
    00207
    00208   if(out) dspout = true;
    00209
    00210   livein.init(speed, tstereo+1, &dsp);
    00211   linein = in;
    00212
    00213   return(true);
    00214 } /* open_soundcard */
    00215
    00216 void Stream_mixer::cafudda()
    00217 {
    00218   int i, c=0, cc;
    00219   int total_bitrate=0;
    00220
    00221   /* here memset takes byte num
    00222      max *4 (32bit) *2 (stereo) */
    00223   memset(process_buffer,0,MIX_CHUNK<<3);
    00224
    00225   //  max = 0;
    00226   peak[cpeak] = 0;
    00227
    00228   if(quit) {
    00229     func("QUIT detected while cafudding");
    00230     return;
    00231   }
    00232
    00233   lock();
    00234
    00235   for(i=0;i<MAX_CHANNELS;i++) {
    00236     if(chan[i] != NULL) {
    00237
    00238       /*
    00239       if(chan[i]->update) {
    00240         if(have_gui)
    00241           gui->sel_playlist
    00242             (i,chan[i]->playlist->selected_pos());
    00243         chan[i]->update = false;
    00244       }
    00245       */
    00246
    00247       if(chan[i]->on) {
    00248         cc = chan[i]->erbapipa->mix16stereo(MIX_CHUNK,process_buffer);
    00249         // if(cc!=MIX_CHUNK<<2) warning("hey! mix16stereo on ch[%u] returned %i",i,cc);
    00250         c+=cc;
    00251         if(have_gui)
    00252           if(chan[i]->update) {
    00253             updchan(i);
    00254             chan[i]->update = false;
    00255           }
    00256       } /* if(chan[i].on) */
    00257
    00258     } /* if(chan[i] != NULL) */
    00259   } /* for(i=0;i<MAX_CHANNELS;i++) */
    00260
    00261   if(linein) {
    00262     // ires = livein.mix(process_buffer);
    00263     c += livein.mix(process_buffer);
    00264     // max = (max<ires) ? ires : max;
    00265   }
    00266
    00267   /* here: max = number of 32bit samples in process_buffer
    00268      number of single 16bit stereo samples (max<<1)
    00269      number of bytes (max<<2)
    00270      func("mixxing %i samples (%i bytes)",max,max<<2);
    00271   */
    00272
    00273   if(c>0) {
    00274     /* CLIPPING
    00275        this brings it back to a 16bit resolution
    00276        and puts it into audio_buffer    */
    00277     clip_audio(MIX_CHUNK);
    00278
    00279     unlock();
    00280
    00281     out = (OutChannel*) outchans.begin();
    00282     while(out) {
    00283       if(!out->running) {
    00284         out = (OutChannel*) out->next;
    00285         continue;      }
    00286       out->push(audio_buffer,MIX_CHUNK<<2);
    00287       total_bitrate += out->get_bitrate();
    00288       out = (OutChannel*) out->next;
    00289     }
    00290
    00291     /* WRITE 2 DSP */
    00292     if(dspout) {
    00293       /* write out interleaved stereo 16bit pcm
    00294          dsp takes number of *BYTES*, the format
    00295          is being setted with ioctls in initialization */
    00296       write(dsp,audio_buffer,MIX_CHUNK<<2);
    00297     }
    00298
    00299     /* compute and draw levels */
    00300     cpeak++;
    00301     if(have_gui
    00302        && cpeak==8
    00303        && gui->meter_shown()) {
    00304       gui->vumeter_set( (peak[0]+peak[1]+peak[2]+peak[3]+
    00305                          peak[4]+peak[5]+peak[6]+peak[7])>>3 );
    00306       gui->bpsmeter_set( total_bitrate );
    00307       cpeak = 0;
    00308     }
    00309
    00310   } else {
    00311
    00312     unlock();
    00313
    00314     if(have_gui) {
    00315       if(gui->meter_shown()) {
    00316         gui->vumeter_set( 0 );
    00317         gui->bpsmeter_set( 0 );
    00318       }
    00319     }
    00320
    00321   }
    00322
    00323   /* notice the gui to refresh */
    00324   if(have_gui) gui->signal();
    00325
    00326   /* we don't want massive usage of the cpu
    00327      also thread synchronization is a shamanic practice ;)
    00328      in which we must find the right moment to breath;
    00329
    00330      here we give fifos a bit of air and avoid tight loops
    00331      making the mixing engine wait 20 nanosecs */
    00332   jsleep(0,20);
    00333
    00334 }
    00335
    00336 bool Stream_mixer::create_channel(int ch) {
    00337
    00338   /* paranoia */
    00339   if(chan[ch]) {
    00340     warning("channel %i allready exists");
    00341     unlock();
    00342     return true;
    00343   }
    00344
    00345   Channel *nch;
    00346   nch = new Channel();
    00347
    00348   nch->lock();
    00349   nch->start();
    00350   func("waiting for channel %i thread to start",ch);
    00351   nch->wait();
    00352   /* wait for the existance lock, then we unlock */
    00353   nch->unlock();
    00354
    00355   lock();
    00356   chan[ch] = nch;
    00357   unlock();
    00358
    00359   return(true);
    00360 }
    00361
    00362 bool Stream_mixer::delete_channel(int ch) {
    00363   /* paranoia */
    00364   PARACHAN
    00365
    00366   lock();
    00367   /*
    00368   if(chan[ch]->on) chan[ch]->stop();
    00369   // quit the thread
    00370   if(chan[ch]->running) {
    00371     chan[ch]->quit = true;
    00372     // be sure it quitted
    00373     chan[ch]->signal();
    00374     jsleep(0,50);
    00375     chan[ch]->lock(); chan[ch]->unlock();
    00376
    00377   }
    00378   */
    00379
    00380   /* clean internal allocated buffers */
    00381   delete chan[ch];
    00382   chan[ch] = NULL;
    00383   //  chan[ch]->playlist->cleanup();
    00384   unlock();
    00385   return true;
    00386 }
    00387
    00388 bool Stream_mixer::pause_channel(int ch) {
    00389   /* paranoia */
    00390   PARACHAN
    00391
    00392   /* here i don't lock - c'mon, boolean _is_ atomical */
    00393   if(chan[ch]->opened) {
    00394     if(!chan[ch]->on) {
    00395       lock();
    00396       if(!chan[ch]->play())
    00397         error("can't play channel %u",ch,ch);
    00398       unlock();
    00399     } else {
    00400       chan[ch]->on = false;
    00401       chan[ch]->position = chan[ch]->time.f;
    00402       return true;
    00403     }
    00404   } else warning("tried to switch pause on unopened channel %i",ch);
    00405   return false;
    00406 } /* overloaded non-switching function follows */
    00407 bool Stream_mixer::pause_channel(int ch, bool stat) { /* if stat==true -> pause the channel */
    00408   /* paranoia */
    00409   PARACHAN
    00410
    00411   if(chan[ch]->opened) {
    00412     if(!stat) {
    00413       lock();
    00414       if(!chan[ch]->play())
    00415         error("can't play channel %u",ch,ch);
    00416       unlock();
    00417     } else {
    00418       chan[ch]->on = false;
    00419       return true;
    00420     }
    00421   } else error("can't pause unopened channel %i",ch);
    00422   return false;
    00423 }
    00424
    00425 bool Stream_mixer::set_channel(int ch, int pos) {
    00426   PARACHAN
    00427
    00428     if(!chan[ch]->playlist->sel(pos))
    00429       return(false);
    00430     else
    00431       chan[ch]->opened = false;
    00432
    00433   /* if have_gui select the choosen song
    00434   if(have_gui)
    00435     gui->sel_playlist( ch , pos );
    00436   */
    00437   return(true);
    00438 }
    00439
    00440 /*
    00441   play the selected stream sound on the channel
    00442   the file/stream is loaded
    00443   (CHANGES TO API! RUBIK PERDONO)
    00444   takes only the channel number
    00445   ** int pos starts from 1
    00446   set_channel returns:
    00447   0 - error
    00448   1 - bitstream opened (seekable)
    00449   2 - bitstream opened (non seekable)
    00450 */
    00451 int Stream_mixer::play_channel(int ch) {
    00452   int res = 0;
    00453
    00454   /* paranoia */
    00455   PARACHAN
    00456
    00457   lock();
    00458   if(!chan[ch]->play())
    00459     error("can't play channel %u",ch);
    00460   else
    00461     res = (chan[ch]->seekable) ? 1 : 2;
    00462   unlock();
    00463
    00464   return(res);
    00465 }
    00466
    00467 void Stream_mixer::set_all_volumes(float *vol) {
    00468   int ch;
    00469   lock();
    00470   for(ch=0;ch<MAX_CHANNELS;ch++) {
    00471     if(chan[ch]!=NULL)
    00472       chan[ch]->volume = vol[ch];
    00473   }
    00474   unlock();
    00475 }
    00476
    00477 bool Stream_mixer::set_volume(int ch, float vol) {
    00478   /* paranoia */
    00479   PARACHAN
    00480
    00481   lock();
    00482   chan[ch]->volume = vol;
    00483   unlock();
    00484   return true;
    00485 }
    00486
    00487 void Stream_mixer::crossfade(int ch1, float vol1, int ch2, float vol2) {
    00488   if(!chan[ch1] || !chan[ch2]) {
    00489     warning("Stream_mixer::crossfade(%u,%f,%u,%f) called on a NULL channel",ch1,vol1,ch2,vol2);
    00490     return;
    00491   }
    00492
    00493   lock();
    00494   chan[ch1]->volume = vol1;
    00495   chan[ch2]->volume = vol2;
    00496   unlock();
    00497 }
    00498
    00499 void Stream_mixer::set_speed(int ch, int speed) {
    00500   lock();
    00501   chan[ch]->speed = speed;
    00502   unlock();
    00503   /* poi lo processa l'inchannel dentro al metodo run()
    00504      cioe' il resampling che fa li' quando lo prepara al mixing
    00505   */
    00506 }
    00507
    00508 bool Stream_mixer::stop_channel(int ch) {
    00509   /* paranoia */
    00510   PARACHAN
    00511
    00512   bool res = false;
    00513     //  if(chan[ch]->running) {
    00514   lock();
    00515   res = chan[ch]->stop();
    00516   unlock();
    00517   /*  if(have_gui) {
    00518     int p = chan[ch]->playlist->selected_pos();
    00519     if(p) gui->sel_playlist(ch,p);
    00520     } */
    00521   return(res);
    00522 }
    00523
    00524 bool Stream_mixer::set_position(int ch, float pos) {
    00525
    00526   /* paranoia */
    00527   PARACHAN
    00528   bool res = false;
    00529   if(!chan[ch]->opened) {
    00530     error("can't seek position on channel %u",ch);
    00531     return(res);
    00532   }
    00533
    00534   /*
    00535   if(pos==1.0) {
    00536     set_channel(ch,playlist[ch].sel()+1);
    00537     return(res);
    00538   }
    00539   */
    00540
    00541   if(chan[ch]->seekable && chan[ch]->running) {
    00542     lock();
    00543     //    chan[ch]->erbapipa->flush();
    00544     chan[ch]->lock();
    00545     res = chan[ch]->pos(pos);
    00546     chan[ch]->unlock();
    00547     if(!res) error("can't seek position %f on channel %u",pos,ch);
    00548     // chan[ch]->play(); - this shouldn't be needed
    00549     unlock();
    00550   } else
    00551     error("channel %u is not seekable",ch);
    00552   return(res);
    00553 }
    00554
    00555 /* move song 'pos' in channel 'ch' to the new 'npos' in channel 'nch'
    00556    songs can also be moved within the same channel */
    00557 bool Stream_mixer::move_song(int ch, int pos, int nch, int npos) {
    00558   Entry *x = chan[ch]->playlist->pick(pos);
    00559   func("move song %i on channel %i to channel %i in position %i",
    00560        pos,ch,nch,npos);
    00561   if(x) {
    00562     /* the insert also removes from the old list
    00563        (in future we should be using the linklist API directly) */
    00564     chan[nch]->playlist->insert(x,npos);
    00565     return(true);
    00566   } else
    00567     func("no song to move there!");
    00568
    00569   return(false);
    00570 }
    00571
    00572 bool Stream_mixer::set_live(bool stat) {
    00573   if(dsp<1) {
    00574     warning("ignoring live-in: soundcard not found");
    00575     return(false);
    00576   }
    00577
    00578   if(!( (dspout)
    00579         &&(!fullduplex)
    00580         &&(stat)) ) {
    00581     lock();
    00582     livein.on = linein = stat;
    00583     unlock();
    00584   }
    00585
    00586   return(livein.on);
    00587 }
    00588
    00589 bool Stream_mixer::set_lineout(bool stat) {
    00590   if(dsp<1) {
    00591     error("ignoring sound output: soundcard not found");
    00592     return(false);
    00593   }
    00594
    00595   if(!( (livein.on)
    00596         &&(!fullduplex)
    00597         &&(stat)) ) {
    00598     lock();
    00599     dspout = stat;
    00600     unlock();
    00601   }
    00602   return(dspout&stat);
    00603 }
    00604
    00605 void Stream_mixer::close_soundcard() {
    00606   if(!dsp) return;
    00607   ioctl(dsp, SNDCTL_DSP_RESET, 0);
    00608   close(dsp);
    00609 }
    00610
    00611 bool Stream_mixer::set_playmode(int ch, int mode) {
    00612
    00613   switch(mode) {
    00614   case PLAYMODE_PLAY:
    00615     chan[ch]->playmode = PLAYMODE_PLAY;
    00616     break;
    00617   case PLAYMODE_LOOP:
    00618     chan[ch]->playmode = PLAYMODE_LOOP;
    00619     break;
    00620   case PLAYMODE_CONT:
    00621     chan[ch]->playmode = PLAYMODE_CONT;
    00622     break;
    00623   }
    00624   return true;
    00625 }
    00626
    00627 /* this is the function selecting files for the scandir
    00628    on freebsd systems you should change the following line to:
    00629    int selector(struct dirent *dir) {    */
    00630 int selector(const struct dirent *dir) {
    00631   if( strncasecmp(dir->d_name+strlen(dir->d_name)-4,".mp3",4)==0
    00632 #ifdef HAVE_VORBIS
    00633       || strncasecmp(dir->d_name+strlen(dir->d_name)-4,".ogg",4)==0
    00634 #endif
    00635       || strncasecmp(dir->d_name+strlen(dir->d_name)-3,".pl",3)==0
    00636       || strncasecmp(dir->d_name+strlen(dir->d_name)-4,".pls",4)==0
    00637       || strncasecmp(dir->d_name+strlen(dir->d_name)-4,".m3u",4)==0 )
    00638     return(1);
    00639
    00640   struct stat prcd;
    00641   stat(dir->d_name,&prcd);
    00642   if(S_ISDIR(prcd.st_rdev)) return(1);
    00643
    00644   return(0);
    00645 }
    00646
    00647
    00648
    00649
    00650 bool Stream_mixer::add_to_playlist(int ch, const char *file) {
    00651
    00652   if(!file) {
    00653     warning("Stream_mixer::add_to_playlist(%i,NULL) called",ch);
    00654     return(false);
    00655   }
    00656
    00657   if(!chan[ch]) {
    00658     warning("%i:%s %s - called on NULL channel %i",
    00659             __LINE__,__FILE__,__FUNCTION__,ch);
    00660     warning("call jmixer::create_channel first");
    00661     return(false);
    00662   }
    00663
    00664   char temp[MAX_PATH_SIZE];
    00665   /* in path i store the new allocated string into the playlist */
    00666   char *path, *p;
    00667
    00668   strncpy(temp,file,MAX_PATH_SIZE);
    00669   chomp(temp);
    00670   func("add to playlist %s", temp);
    00671   /* if it's a url, just add it */
    00672   if(strncasecmp(temp,"http://",7)==0) {
    00673     func("it's a network stream url");
    00674     //    lock();
    00675     path = chan[ch]->playlist->addurl(temp);
    00676     //    unlock();
    00677     if(have_gui) gui->add_playlist(ch,path);
    00678     return(true);
    00679   }
    00680
    00681   /* if it's a local file url (like gnome d&d)
    00682      strip away the file:// and treat it normally */
    00683   if(strncasecmp(temp,"file://",7)==0) {
    00684     func("it's a file url (drag & drop)");
    00685     strncpy(temp,&file[7],MAX_PATH_SIZE);
    00686     path = chan[ch]->playlist->addurl(temp);
    00687     if(have_gui) gui->add_playlist(ch,path);
    00688     return(true);
    00689   }
    00690
    00691   /* if it's not a stream, check if the file exists and it's readable */
    00692   FILE *fd = NULL;
    00693   fd = fopen(temp,"r");
    00694   if(!fd) {
    00695     warning("is not a readable file",temp);
    00696     return(false);
    00697   } else fclose(fd);
    00698
    00699   bool res = false;
    00700
    00701   /* check if the file has a correct extension which is supported
    00702      and handle it if it's a playlist */
    00703
    00704   /* IT's A MP3 OR OGG OR WAV */
    00705   if( strncasecmp(temp+strlen(temp)-4,".ogg",4)==0
    00706       || strncasecmp(temp+strlen(temp)-4,".mp3",4)==0
    00707       || strncasecmp(temp+strlen(temp)-4,".wav",4)==0
    00708       || strncasecmp(temp+strlen(temp)-4,".aif",4)==0
    00709       || strncasecmp(temp+strlen(temp)-5,".aiff",4)==0
    00710       || strncasecmp(temp+strlen(temp)-4,".snd",4)==0
    00711       || strncasecmp(temp+strlen(temp)-3,".au",4)==0
    00712       || strncasecmp(temp+strlen(temp)-4,".raw",4)==0
    00713       || strncasecmp(temp+strlen(temp)-4,".paf",4)==0
    00714       || strncasecmp(temp+strlen(temp)-4,".iff",4)==0
    00715       || strncasecmp(temp+strlen(temp)-4,".svx",4)==0
    00716       || strncasecmp(temp+strlen(temp)-3,".sf",4)==0
    00717       || strncasecmp(temp+strlen(temp)-4,".voc",4)==0
    00718       || strncasecmp(temp+strlen(temp)-4,".w64",4)==0
    00719       || strncasecmp(temp+strlen(temp)-4,".pvf",4)==0
    00720       || strncasecmp(temp+strlen(temp)-3,".xi",4)==0
    00721       || strncasecmp(temp+strlen(temp)-4,".htk",4)==0
    00722       || strncasecmp(temp+strlen(temp)-4,".mat",4)==0
    00723       ) {
    00724     func("it's a local file",temp);
    00725     //    lock();
    00726     path = chan[ch]->playlist->addurl(temp);
    00727     //    unlock();
    00728
    00729     if(have_gui) {
    00730       p = path+strlen(path);// *p='\0';
    00731       while(*p!='/') p--; p++;
    00732       gui->add_playlist(ch,p);
    00733     }
    00734
    00735     res = true;
    00736
    00737     /* IT's A PLAYLIST */
    00738   } else if( strncasecmp(temp+strlen(temp)-3,".pl",3)==0
    00739              || strncasecmp(temp+strlen(temp)-4,".pls",4)==0
    00740              || strncasecmp(temp+strlen(temp)-4,".m3u",4)==0 ) {
    00741     func("it's a playlist");
    00742     /* the file is a playlist, read thru it and append it to the existing */
    00743     char votantonio[MAX_PATH_SIZE];
    00744     fd = fopen(temp,"r");
    00745     while(fgets(votantonio,MAX_PATH_SIZE,fd)!=NULL) {
    00746       chomp(votantonio);
    00747       /* ET VOILA', RECURSION in one step out HERE (SENZA MANIII)
    00748          MARO', SO' NU MAGHE! ARRISUSCIT' LI MUORTE! MARONN'O CARMINE!
    00749          ECCHI ME FERME CCHIU'! AGGIA FATT' LA RICORSIOOONE! MAAROOOOOO!
    00750          .. ok, ho sclerato in modo male //jaromil
    00751       */
    00752       add_to_playlist(ch,votantonio);
    00753     }
    00754     fclose(fd);
    00755     res = true;
    00756
    00757     /* TRY IF IT's A DIRECTORY */
    00758   } else {
    00759
    00760     struct stat prcd;
    00761     if(stat(temp,&prcd)<0) {
    00762       error("can't read file status");
    00763       warning("cannot stat %s : %s",temp,strerror(errno));
    00764     } else if(prcd.st_mode & S_IFDIR) {
    00765       func("it's a directory");
    00766
    00767       struct dirent **filelist;
    00768       int found = scandir(temp,&filelist,selector,alphasort);
    00769       if(found<0) {
    00770         error("file not found");
    00771         warning("Stream_mixer::add_to_playlist(%u,%s) : %s",ch,file,strerror(errno));
    00772       } else {
    00773         int c;
    00774         for(c=0;c<found;c++) {
    00775           char barakus[MAX_PATH_SIZE];
    00776           snprintf(barakus,MAX_PATH_SIZE,"%s/%s",temp,filelist[c]->d_name);
    00777           /* et vuala' la ricorsione pure qua */
    00778           add_to_playlist(ch,barakus);
    00779         }
    00780         res = true;
    00781       }
    00782
    00783     } else {
    00784       error("file extension is not recognized");
    00785       warning("error adding %s (extension not recognized)",temp);
    00786     }
    00787   }
    00788
    00789   return(res);
    00790 }
    00791
    00792 void Stream_mixer::rem_from_playlist(int ch, int pos) {
    00793   /* paranoia */
    00794   if(ch>MAX_CHANNELS) {
    00795     warning("Stream_mixer::rem_from_playlist(%u,%u) : channel does'nt exists");
    00796     return;
    00797   }
    00798
    00799   //  lock();
    00800
    00801   chan[ch]->playlist->rem(pos);
    00802
    00803   pos = (pos>chan[ch]->playlist->len()) ? chan[ch]->playlist->len() : pos;
    00804   if(pos>0) {
    00805     chan[ch]->playlist->sel(pos);
    00806     if(have_gui) gui->sel_playlist(ch,pos-1);
    00807   }
    00808   //  unlock();
    00809 }
    00810
    00811 int Stream_mixer::create_enc(enum codec enc) {
    00812   OutChannel *outch = NULL;
    00813   switch(enc) {
    00814
    00815 #ifdef HAVE_VORBIS
    00816   case OGG:
    00817     outch = new OutVorbis;
    00818
    00819     if( ! ((OutVorbis*)outch)->init() ) {
    00820       error("error initializing %s",outch->name);
    00821       delete (OutVorbis*)outch;
    00822       return -1;
    00823     }
    00824     break;
    00825 #endif
    00826
    00827 #ifdef HAVE_LAME
    00828   case MP3:
    00829     outch= new OutLame;
    00830     if( ! ((OutLame*)outch)->init() ) {
    00831       error("error initializing %s",outch->name);
    00832       delete (OutLame*)outch;
    00833       return -1;
    00834
    00835     }
    00836     break;
    00837 #endif
    00838
    00839   default: break; /* placeholder */
    00840
    00841   }
    00842
    00843   outchans.add(outch);
    00844
    00845   idseed += 1000; /* here is a limit of 1000 shouter ID slots for each encoder
    00846                      i bet you'll not reach it */
    00847   outch->id = idseed;
    00848
    00849   notice("%s %s initialized",outch->name,outch->version);
    00850   return outch->id;
    00851 }
    00852
    00853 void Stream_mixer::delete_enc(int id) {
    00854   OutChannel *outch = (OutChannel*) outchans.pick_id(id);
    00855   if(!outch) {
    00856     warning("delete_enc: invalid encoder requested ID:%i",id);
    00857     return;
    00858   }
    00859
    00860   lock();
    00861
    00862   outch->rem();
    00863
    00864   if(outch->running) {
    00865     outch->quit = true;
    00866     jsleep(0,50);
    00867     outch->lock(); outch->unlock();
    00868     outch->flush(); /* QUA we waste some buffer in the pipe
    00869                        CHECK THIS */
    00870   }
    00871
    00872   delete outch;
    00873   unlock();
    00874 }
    00875
    00876 OutChannel *Stream_mixer::get_enc(int id) {
    00877   return (OutChannel*)outchans.pick_id(id);
    00878 }
    00879
    00880 bool Stream_mixer::apply_enc(int id) {
    00881   OutChannel *outch = (OutChannel*)outchans.pick_id(id);
    00882   if(!outch) {
    00883     warning("apply_enc: invalid encoder requested ID:%i",id);
    00884     return false;
    00885   }
    00886
    00887   //  if(!outch->profile_changed) return true;
    00888
    00889   char *qstr = outch->guess_bps();
    00890
    00891   lock();
    00892   outch->lock();
    00893
    00894   outch->initialized = outch->apply_profile();
    00895
    00896   outch->unlock();
    00897   unlock();
    00898
    00899   if(outch->initialized)
    00900     notice("%s SET Q%u %s%s", outch->name, (int)fabs(outch->quality()),
    00901         qstr, (outch->channels()==1)?" mono ":" stereo ");
    00902   else
    00903     error("ERROR %s SET Q%u %s%s",outch->name, (int)fabs(outch->quality()),
    00904           qstr, (outch->channels()==1)?" mono ":" stereo ");
    00905
    00906   return outch->initialized;
    00907 }
    00908
    00909
    00910
    00911 void Stream_mixer::updchan(int ch) {
    00912   if(!chan[ch]) return;
    00913   if(chan[ch]->seekable) {
    00914     snprintf(gui->ch_lcd[ch],9,"%02u:%02u:%02u",
    00915              chan[ch]->time.h,chan[ch]->time.m,chan[ch]->time.s);
    00916     //  if(strncmp(temp,gui->ch_lcd[ch],5)!=0) { // LCD changed */
    00917     //strncpy(gui->ch_lcd[ch],temp,5);
    00918     gui->set_lcd(ch, gui->ch_lcd[ch]);
    00919     //  func("%i: %s %f",ch,gui->ch_lcd[ch],chan[ch]->state);
    00920     //  }
    00921     //  if(gui->ch_pos[ch] != chan[ch]->state) { /* POSITION changed */
    00922     gui->ch_pos[ch] = chan[ch]->state;
    00923     gui->set_pos(ch, chan[ch]->state);
    00924     //  }
    00925   }
    00926 }
    00927
    00932 void Stream_mixer::clip_audio(int samples) {
    00933   int c;
    00934   static float k = 1.0;
    00935   int pproc,sum = 0;
    00936 #ifdef MOP_LOGGING
    00937   static int mopct = 0, supsum = 0;
    00938 #endif
    00939
    00940   if(samples==0) return;
    00941   int words = samples<<1;
    00942
    00943   for(c=0;c<words;c++) {
    00944     /* value of the attenuated sample */
    00945     pproc = (int)(((float)(process_buffer[c]))*k);
    00946
    00947     if(pproc > 32767) {
    00948       /* sum of the exceeding area for tne computation of the current k val */
    00949       sum += (pproc-32767);
    00950       audio_buffer[c] = peak[cpeak] = 32767;
    00951     }
    00952     else if(pproc < -32768) {
    00953       /* sum of the exceeding area for ... */
    00954       sum += (-pproc-32768);
    00955       audio_buffer[c] = -32768;
    00956       }
    00957     else {
    00958       audio_buffer[c] = (short)pproc;
    00959       if (pproc>peak[cpeak])
    00960         peak[cpeak] = pproc;
    00961     }
    00962   }
    00963   k = (k * MOP_ADV_RETM +
    00964        1.0 / ((1.0 + MOP_ADV_KARE * (sum / (float)(samples*32767))))) / (MOP_ADV_RETM + 1.0);
    00965
    00966 #ifdef MOP_LOGGING
    00967   /* every 128 chunks print the current k value and the average of exceeding area */
    00968   if ((mopct % 128) == 0) {
    00969     supsum >>= 7;
    00970     warning("JMIX::clip_audio(%i) : k = (%f,%ld)",samples,k,supsum);
    00971     supsum = sum;
    00972   }
    00973   else
    00974     supsum += sum;
    00975   mopct++;
    00976 #endif
    00977
    00978 }