- -( dyne // software :: culture :: events :: planet :: discussion :: museum \\ freaknet )- -
 
Main Page | Modules | Class Hierarchy | Class List | File List | Class Members | File Members

jmixer.cpp

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