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

Generated on Thu Dec 16 12:28:21 2004 for MuSE by doxygen1.3