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 }

Generated on Sat Apr 17 17:38:48 2004 for MuSE by doxygen1.3