00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00062
00063
00064
00065
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
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
00102 snddev = new SoundDevice();
00103 if( snddev->open(true,true) ) {
00104 dsp = 1;
00105 fullduplex = true;
00106 }
00107
00108
00109 idseed = 0;
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
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
00175
00176 memset(process_buffer,0,MIX_CHUNK<<3);
00177
00178
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
00193
00194
00195
00196
00197
00198
00199
00200 if(chan[i]->on) {
00201
00202
00203 cc = chan[i]->erbapipa->read(MIX_CHUNK<<1,process_buffer);
00204
00205
00206
00207 if(cc<0) continue;
00208
00209 c+=cc;
00210
00211 if(have_gui)
00212 if(chan[i]->update) {
00213 updchan(i);
00214 chan[i]->update = false;
00215 }
00216 }
00217
00218 }
00219 }
00220
00221
00222 if(linein) {
00223 #ifndef PORTAUDIO
00224
00225 c += livein.mix(process_buffer);
00226
00227 #else
00228 linein_samples = snddev->read(linein_buf,MIX_CHUNK);
00229 for(cc=0; cc<linein_samples<<1; cc++) {
00230
00231
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
00249
00250
00251
00252
00253
00254 if(c>0) {
00255
00256
00257
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
00279 if(dspout) {
00280
00281
00282
00283 #ifndef PORTAUDIO
00284 write(dsp,audio_buffer,MIX_CHUNK<<2);
00285 #else
00286 snddev->write(audio_buffer,MIX_CHUNK<<1);
00287 #endif
00288
00289
00290
00291 }
00292
00293
00294 cpeak++;
00295 if(have_gui
00296 && cpeak==8
00297 && gui->meter_shown()) {
00298
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
00319 if(have_gui) gui->signal();
00320
00321
00322
00323
00324
00325
00326
00327 jsleep(0,20);
00328
00329 }
00330
00331 bool Stream_mixer::create_channel(int ch) {
00332
00333
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
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
00359 PARACHAN
00360
00361 lock();
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376 delete chan[ch];
00377 chan[ch] = NULL;
00378
00379 unlock();
00380 return true;
00381 }
00382
00383 bool Stream_mixer::pause_channel(int ch) {
00384
00385 PARACHAN
00386
00387
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 }
00402 bool Stream_mixer::pause_channel(int ch, bool stat) {
00403
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
00429
00430
00431
00432 return(true);
00433 }
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446 int Stream_mixer::play_channel(int ch) {
00447 int res = 0;
00448
00449
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
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
00499
00500
00501 }
00502
00503 bool Stream_mixer::stop_channel(int ch) {
00504
00505 PARACHAN
00506
00507 bool res = false;
00508
00509 lock();
00510 res = chan[ch]->stop();
00511 unlock();
00512
00513
00514
00515
00516 return(res);
00517 }
00518
00519 bool Stream_mixer::set_position(int ch, float pos) {
00520
00521
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
00531
00532
00533
00534
00535
00536 if(chan[ch]->seekable && chan[ch]->running) {
00537 lock();
00538
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
00544 unlock();
00545 } else
00546 error("channel %u is not seekable",ch);
00547 return(res);
00548 }
00549
00550
00551
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
00558
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
00641
00642
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
00672
00673
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
00697 char *path, *p;
00698
00699 strncpy(temp,file,MAX_PATH_SIZE);
00700 chomp(temp);
00701 func("add to playlist %s", temp);
00702
00703 if(strncasecmp(temp,"http://",7)==0) {
00704 func("it's a network stream url");
00705
00706 path = chan[ch]->playlist->addurl(temp);
00707
00708 if(have_gui) gui->add_playlist(ch,path);
00709 return(true);
00710 }
00711
00712
00713
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) {
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
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
00746
00747
00748
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
00770 path = chan[ch]->playlist->addurl(temp);
00771
00772
00773 if(have_gui) {
00774 p = path+strlen(path);
00775 while(*p!='/') p--; p++;
00776 gui->add_playlist(ch,p);
00777 }
00778
00779 res = true;
00780
00781
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
00787 char votantonio[MAX_PATH_SIZE];
00788 fd = fopen(temp,"r");
00789 while(fgets(votantonio,MAX_PATH_SIZE,fd)!=NULL) {
00790 chomp(votantonio);
00791
00792
00793
00794
00795
00796 add_to_playlist(ch,votantonio);
00797 }
00798 fclose(fd);
00799 res = true;
00800
00801
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
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
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
00836 if(ch>MAX_CHANNELS) {
00837 warning("Stream_mixer::rem_from_playlist(%u,%u) : channel does'nt exists");
00838 return;
00839 }
00840
00841
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
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;
00883
00884 }
00885
00886 outchans.add(outch);
00887
00888 idseed += 1000;
00889
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();
00912
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
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
00960
00961 gui->set_lcd(ch, gui->ch_lcd[ch]);
00962
00963
00964
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
00988 pproc = (int)(((float)(process_buffer[c]))*k);
00989
00990 if(pproc > 32767) {
00991
00992 sum += (pproc-32767);
00993 audio_buffer[c] = peak[cpeak] = 32767;
00994 }
00995 else if(pproc < -32768) {
00996
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
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 }