diff -u -r --new-file /tmp/terminatorX-3.2-orig/README.mmap /tmp/terminatorX-3.2/README.mmap --- /tmp/terminatorX-3.2-orig/README.mmap Thu Jan 1 01:00:00 1970 +++ /tmp/terminatorX-3.2/README.mmap Wed Oct 27 21:58:05 1999 @@ -0,0 +1,73 @@ +streaming support by Benno Senoner ( sbenno@gardena.net ) + +I added from disk streaming because I think that many users +running on low memory boxes could run into big troubles +when scratching large audiofiles. +( The audiofile could not fit into the swaparea, and the +loading of the file could take very long) + +Alexander said he doesn't support streaming because it would +require a complete redesign of the program. +He was wrong +:-) + +It's actually not very difficult to add streaming to the app, +because we can use the mmap() function and his powerful +caching algorithms. + +Basically what I changed is: +instead of loading the file into RAM, I mmap() the entire file +into memory. +Since the kernel loads pages of the file into mem when they +are needed, it could cause audio-dropouts when working with +low audio buffer sizes (low latency) since, the playing thread +might wait too long for the kernel which tries to load the pages +into mem. + +One trick to avoid this it to add a low priority thread , which does +basically read-ahead and read-behind, by accessing to pages +before and past the actual playing position. +It doesn't matter if this thread blocks for a moment, +since it doesn't play any audio data. +The audio thread will always find the needed pages in memory and +will not drop out. + +UPDATE: I added a pagefaulting scheme based on mlock()/munlock() +which works even better than faulting the pages manually, +but this requires root privileges. +The option is activated when +USE_SCHEDULER is defined. + + +With this approach I was able to scratch a 100MB mono file, +on a 16MB RAM box, running X , KDE and Netscape ! + +ATTENTION: + +sometimes: there might occur audio dropouts during streaming from disk,but +that is not the fault of the app but, it's a common problem of Linux. + +For dropout free low-latency performance apply the low-latency patches +at +http://www.gardena.net/benno/linux/audio + +and tune your EIDE as described in the page +( /sbin/hdparm -c 1 -m 8 -u 1 -d 1 /dev/hda for example) + +Linux 2.4 will contain the low-latency patches as default, +and this will be a great benefit for realtime apps like terminator-X. + +I hope that scratchers running on low RAM boxes will enjoy the mmap patch ! +:-) + +BUGS: + +- the 44byte WAV header isn't skipped +- mpg123 and sox support doesn't work (would require decompressing +to a temporary file) + + +Benno. +sbenno@gardena.net + + diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/main.c /tmp/terminatorX-3.2/src/main.c --- /tmp/terminatorX-3.2-orig/src/main.c Mon Jul 26 06:37:35 1999 +++ /tmp/terminatorX-3.2/src/main.c Sat Oct 23 02:29:45 1999 @@ -145,7 +145,7 @@ gtk_main(); - if (globals.scratch_data) free(globals.scratch_data); + //if (globals.scratch_data) free(globals.scratch_data); if (globals.loop_data) free(globals.loop_data); if (globals.rec_buffer) free(globals.rec_buffer); diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/page_align.h /tmp/terminatorX-3.2/src/page_align.h --- /tmp/terminatorX-3.2-orig/src/page_align.h Thu Jan 1 01:00:00 1970 +++ /tmp/terminatorX-3.2/src/page_align.h Fri Oct 22 23:13:26 1999 @@ -0,0 +1,3 @@ +#include +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) + diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/tX_engine.c /tmp/terminatorX-3.2/src/tX_engine.c --- /tmp/terminatorX-3.2-orig/src/tX_engine.c Sun Jul 25 14:00:58 1999 +++ /tmp/terminatorX-3.2/src/tX_engine.c Wed Oct 27 22:01:19 1999 @@ -31,14 +31,17 @@ samples with the same mouse-distance. */ + #include "tX_types.h" #include "tX_engine.h" #include +#include #include #include #include "tX_gui.h" #include "tX_global.h" #include "turntable.h" +#include "page_align.h" #ifndef WIN32 #include @@ -53,7 +56,9 @@ #define XWINDOW xwindow + pthread_t engine_thread=0; +pthread_t pagefaulter_thread=0; pthread_mutex_t stat_lock=PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t thread_lock=PTHREAD_MUTEX_INITIALIZER; @@ -64,6 +69,12 @@ int realpos=0; +int glob_pagefaulter_run; +void *mlock_addr=NULL; +void *old_mlock_addr=NULL; +int mlock_size=0; +int old_mlock_size=0; + void set_real_pos(int pos) { pthread_mutex_lock(&pos_lock); @@ -165,6 +176,102 @@ } #endif +#define DISTANCE1 80000 +#define DISTANCE2 160000 + +void *pagefaulter(void *nil) +{ + int dummy; + int res; + short *buf; + int mypos,maxpos; + int pos_prev1,pos_prev2,pos_next1,pos_next2; + +//fprintf(stderr, "[pagefaulter()] pagefaulter thread up, PID: %i\n", getpid()); + + mlockall(MCL_CURRENT); + + while(1) + { +// printf("pagefaulter: waiting for engine running ....\n"); + if(get_engine_status()==ENG_RUNNING) break; + usleep(50000); + } +// printf("pagefaulter: wait done ....\n"); + glob_pagefaulter_run=1; + + buf=globals.scratch_data; + maxpos=vttgl->maxpos-1; + + while(1) + { + if(glob_pagefaulter_run) + { + mypos=vttgl->realpos; /* we don;t need the spinlock since we only read the current value */ + if(mypos<0) mypos=0; + if(mypos>maxpos) mypos=maxpos; + //printf("pagefaulter: MYPOS=%d maxpos=%d\n",mypos,maxpos); + + pos_prev1=mypos-DISTANCE1; + if(pos_prev1 < 0) pos_prev1=0; + pos_prev2=mypos-DISTANCE2; + if(pos_prev2 < 0) pos_prev2=0; + + pos_next1=mypos+DISTANCE1; + if(pos_next1 > maxpos) pos_next1=maxpos; + pos_next2=mypos+DISTANCE2; + if(pos_next2 > maxpos) pos_next2=maxpos; + + //printf("pos_prev1=%d pos_prev2=%d pos_next1=%d pos_next2=%d\n",pos_prev1,pos_prev2,pos_next1,pos_next2); + +// generates pagefaults around the current playing position + +#ifdef USE_SCHEDULER + if(old_mlock_addr != NULL) + { +// printf("DEBUG: doing munlock: old_mlock addr=%d size=%d\n",old_mlock_addr,old_mlock_size); + res=munlock(old_mlock_addr,old_mlock_size); + if(res<0) + { + perror("munlock failed, exiting. (not running as root ?) munlock"); + exit(1); + } + old_mlock_addr=NULL; + } + mlock_addr=&buf[pos_prev2]; + mlock_size=((char *)&buf[pos_next2])-((char *)&buf[pos_prev2]); + +// printf("DEBUG: doing mlock: mlock addr=%d size=%d\n",mlock_addr,mlock_size); + + res=mlock(mlock_addr,mlock_size); + if(res<0) + { + perror("mlock failed, exiting. (not running as root ?) mlock"); + exit(1); + } + old_mlock_addr=mlock_addr; + old_mlock_size=mlock_size; + + +#else + + dummy+=buf[pos_prev2]+buf[pos_prev2+PAGE_SIZE]+buf[pos_prev1]+buf[pos_prev1+PAGE_SIZE]+buf[pos_next1-PAGE_SIZE]+buf[pos_next1]+buf[pos_next2-PAGE_SIZE]+buf[pos_next2]; +#endif + } + if(!glob_pagefaulter_run) + { +// printf("PAGEFAULTER STOPPED !!!!\n"); + pthread_exit(NULL); + } +#ifdef USE_SCHEDULER +//printf("usleep ....\n"); + usleep(300000); +#else + usleep(100000); +#endif + } +} + void *engine(void *nil) { #ifndef WIN32 @@ -195,7 +302,6 @@ set_engine_status(ENG_ERR_XOPEN); pthread_exit(NULL); } - if (vtt_needle_down(vttgl)) { XCloseDisplay(dpy); @@ -203,6 +309,7 @@ set_engine_status(ENG_ERR_SOUND); pthread_exit(NULL); } + if (globals.xinput_enable) { @@ -402,7 +509,7 @@ } } #endif // WIN32 - if (vtt_block_action(vttgl)) quit=2; + if (vtt_block_action(vttgl)) quit=2; } #ifndef WIN32 @@ -427,6 +534,8 @@ if (quit==1) set_engine_status(ENG_STOPPED); else set_engine_status(ENG_FINISHED); + glob_pagefaulter_run=0; + usleep(200000); pthread_mutex_unlock(&run_lock); pthread_exit(NULL); @@ -465,6 +574,7 @@ pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM); pthread_create(&engine_thread, &pattr, engine, NULL); + pthread_create(&pagefaulter_thread, NULL, pagefaulter, NULL); } else { @@ -472,9 +582,11 @@ fprintf(stderr, "[run_engine()] NO fifo scheduling.\n"); #endif pthread_create(&engine_thread, NULL, engine, NULL); + pthread_create(&pagefaulter_thread, NULL, pagefaulter, NULL); } #else pthread_create(&engine_thread, NULL, engine, NULL); + pthread_create(&pagefaulter_thread, NULL, pagefaulter, NULL); #endif if (!engine_thread) @@ -486,6 +598,7 @@ gtk_label_set(GTK_LABEL(GTK_BUTTON(action_btn)->child), "Stop"); pthread_detach(engine_thread); + pthread_detach(pagefaulter_thread); set_engine_status(ENG_INIT); @@ -499,6 +612,7 @@ int stop_engine() { void *ret; + void *ret2; pthread_mutex_lock(&thread_lock); if (!engine_thread) @@ -508,8 +622,10 @@ } pthread_join(engine_thread, &ret); + pthread_join(pagefaulter_thread, &ret2); engine_thread=0; + pagefaulter_thread=0; pthread_mutex_unlock(&thread_lock); @@ -517,3 +633,4 @@ return (0); } + diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/tX_gui.c /tmp/terminatorX-3.2/src/tX_gui.c --- /tmp/terminatorX-3.2-orig/src/tX_gui.c Mon Jul 26 20:49:15 1999 +++ /tmp/terminatorX-3.2/src/tX_gui.c Sun Oct 24 15:42:22 1999 @@ -131,6 +131,8 @@ FILE* prelis_file=NULL; +extern int glob_pagefaulter_run; + void *prelis_run(void *ptr) { char buffer[4096]; @@ -348,6 +350,7 @@ char buffer[1024]="Couldn't open scratch file: "; char *fn; + prelis_halt(); if (check_busy()) return; @@ -359,8 +362,9 @@ gtk_widget_destroy(GTK_WIDGET(fs)); - if (globals.scratch_data) free(globals.scratch_data); - +// we can't free globals.scratch_data since we are using mmap() instread +// of malloc()ed mem + //if (globals.scratch_data) free(globals.scratch_data); ret = load_wav(newfile, &globals.scratch_data, &globals.scratch_size); if (ret) @@ -394,6 +398,7 @@ scratch_win=NULL; set_disk_status(DISK_IDLE); + } void load_loop(GtkWidget *widget, GtkFileSelection *fs) diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/tX_wavfunc.c /tmp/terminatorX-3.2/src/tX_wavfunc.c --- /tmp/terminatorX-3.2-orig/src/tX_wavfunc.c Sun Jul 25 14:01:20 1999 +++ /tmp/terminatorX-3.2/src/tX_wavfunc.c Wed Oct 27 19:53:15 1999 @@ -30,6 +30,8 @@ #include #ifndef WIN32 #include +#include +#include "page_align.h" #endif #include @@ -37,6 +39,13 @@ #define SOX_BLOCKSIZE 32000 +extern void *old_mlock_addr; + +void *mmap_start=0; +int mmap_len; +FILE *glob_file=NULL; + + #ifdef USE_SOX_INPUT typedef struct { @@ -86,6 +95,52 @@ printf("Loading: %s\n", name); if (parms.verbose) printf("File: %i Bytes Data, %i Bit Depth, %i Hz Samplerate.\n", wav_in.len, wav_in.depth, wav_in.srate); #endif + + +// ******** modified by Benno *********** + + + if(mmap_start) + { + old_mlock_addr=NULL; + //printf("DEBUG: doing munmap: start=%d len=%d\n",mmap_start,mmap_len); + munmap(mmap_start,mmap_len); + mmap_start=0; + mmap_len=0; + } + if(glob_file) + { + fclose(glob_file); + glob_file=NULL; + } + + + +#define MYFILESIZE ((PAGE_ALIGN(wav_in.len))) + +//printf("MYFILESIZE=%d\n",MYFILESIZE); +//printf("DEBUG: trying to mmap() file\n"); + +glob_file=wav_in.handle; +data=mmap(NULL,MYFILESIZE,PROT_READ,MAP_SHARED,fileno(wav_in.handle),0); + +if(data == MAP_FAILED) +{ + perror("ERROR ! , mmap failed, mmap"); + return(LW_ERROR_FILE_NOT_FOUND); +} + +mmap_start=data; +mmap_len=MYFILESIZE; +//printf("DEBUG:mmap() ok start=%d len=%d\n",mmap_start,mmap_len); + + + *data_ptr=data; + *size=MYFILESIZE; + return (LW_NO_ERROR); + +// *** in the case of mmap() we do not need the code below + #ifndef USE_SOX_INPUT diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/turntable.c /tmp/terminatorX-3.2/src/turntable.c --- /tmp/terminatorX-3.2-orig/src/turntable.c Mon Jul 26 21:21:55 1999 +++ /tmp/terminatorX-3.2/src/turntable.c Sat Oct 23 03:40:42 1999 @@ -104,6 +104,7 @@ #include "tX_types.h" #include "tX_global.h" #include "tX_engine.h" +#include "page_align.h" /* For KEEP_DEV_OPEN */ #ifdef HAVE_CONFIG_H @@ -411,8 +412,7 @@ { vtt->pos+=vtt->real_speed; if (vtt->pos>vtt->maxpos) vtt->pos-=vtt->maxpos; - else if (vtt->pos<0) vtt->pos+=vtt->maxpos; - + else if (vtt->pos<0) vtt->pos+=vtt->maxpos; true_pos_a=floor(vtt->pos); real_pos_a=(unsigned int) true_pos_a; @@ -480,7 +480,7 @@ int result; vtt->pos=0; - vtt->maxpos=(f_prec) globals.scratch_size / 2.0; + vtt->maxpos=(f_prec) (globals.scratch_size / 2.0)-2*PAGE_SIZE; vtt->mix_pos=globals.loop_data; vtt->mix_max=globals.loop_data+(size_t) globals.loop_len; @@ -560,7 +560,7 @@ vtt->tmp_out=NULL; #endif - free(vtt->samplebuffer); + //free(vtt->samplebuffer); result = vtt_close_dev(vtt); diff -u -r --new-file /tmp/terminatorX-3.2-orig/src/wav_read.c /tmp/terminatorX-3.2/src/wav_read.c --- /tmp/terminatorX-3.2-orig/src/wav_read.c Sun Jul 25 14:02:32 1999 +++ /tmp/terminatorX-3.2/src/wav_read.c Sun Oct 24 15:36:48 1999 @@ -44,6 +44,7 @@ #include "endian.h" #include "tX_types.h" + /* Read little endian 16bit values little endian */ #ifdef BIG_ENDIAN_MACHINE @@ -130,6 +131,7 @@ #endif handle = popen(buffer, "r"); #else + handle = fopen(file_name, "r"); if(handle) fread((char *) header, 1, 44, handle);