📄 spectrogram_mode_funcs.c
字号:
#include <math.h>#include "fft-spectra.h"#include "image.h"#define NSILENCE 20static float ymax=0,ymin=1e40; /* Remember maximal and minimal intensity for plotting with a constant scale */static float *silence_data=NULL, /* Remember a silence noise - a sample of FFT data acquired in silence for INTENSITY_SCALE_SMART mode */ silence_threshold=0; /* FFT sample with smaller correlation will be interpreted as a signal */static int silence_nacquire=0; /* Use this many FFT samples for learning the silence noise. (Will be set once to nonzero value and decremented untill zero */static int current_y = 0; /* The position in the main_pixmap. */static image_t main_img = {0,0,NULL}; /* Few image lines for the spectrogram flow */static GdkPixmap *main_pixmap = NULL;static GdkGC *gc_line=NULL, *gc_bg=NULL, *gc_silent=NULL; static color_t *color_bg=NULL;static int interpolate_missing_data_points=0;static int logscale = 0; /* Scale the intensity logarithmically? */extern GSList *linetags; /* Display custom vertical lines to see tones of interest. */extern int data_display_from, /* Zoom boundaries - range of data points which should be displayed */ data_display_to;extern PangoLayout *pango_layout; /* Pango for rendering text */void spectrogram_read_profile(int *width, int *height){ /* Eh, not very nice, but it is no performance problem and saves typing: Reuse the code of spectra display mode to read linetags etc. Do not care about reading twice the colors and window dimensions. */ fourier_read_profile(width,height); if (!color_bg) color_bg = get_color( sconf_get_string_or_die(settings,"spectrogram_bg_color") ); gc_bg = get_gc( sconf_get_string_or_die(settings,"spectrogram_bg_color") ); gc_line = get_gc( sconf_get_string_or_die(settings,"spectrogram_line") ); gc_silent = get_gc( sconf_get_string_or_die(settings,"scalebar_pointer") ); if ( !sconf_get_int(settings,"spectrogram_width",width) ) exit_nicely("Could not read config value of \"%s/spectrogram_width\".\n", settings?settings:"default"); if ( !sconf_get_int(settings,"spectrogram_height",height) ) exit_nicely("Could not read config value of \"%s/spectrogram_height\".\n", settings?settings:"default");}void spectrogram_gtk_configure(GtkWidget *widget){ if ( main_img.data && main_width!=main_img.width ) FREE(main_img.data); if ( !main_img.data ) { main_img.width = main_width; main_img.height = 1; MALLOC(main_img.data,guchar,3*main_img.width*main_img.height); }}/* The spectrogram mode does not refresh the whole screen, but only a single line. It must be told explicitly to clean the screen and therefore we need to remember the main_pixmap pointer.*/void spectrogram_set_main_pixmap(GdkPixmap *pixmap){ main_pixmap = pixmap;}void spectrogram_clean_screen(void){ int w,h; if ( main_img.data && color_bg ) image_fill_rectangle(&main_img,color_bg, 0,0,main_img.width,main_img.height); if (main_pixmap && gc_bg) { gdk_drawable_get_size(main_pixmap,&w,&h); gdk_draw_rectangle(main_pixmap,gc_bg, TRUE, 0,0,w,h); } current_y = 0; spectrogram_init_intensity();}void spectrogram_init_intensity(void){ if ( intensity_scale_mode==INTENSITY_SCALE_CONSTANT ) { ymax = 0; ymin = 1e40; } if ( intensity_scale_mode==INTENSITY_SCALE_SMART ) { silence_nacquire=NSILENCE; silence_threshold=0; }}inline void set_color_grade(int num, guchar *ptr){ if ( num<255 ) { *ptr = 0; *(ptr+1) = 0; *(ptr+2) = num; } else if ( num<2*255 ) { *ptr = 0; *(ptr+1) = num-255; *(ptr+2) = 2*255-num; } else if ( num<3*255 ) { *ptr = num-2*255; *(ptr+1) = 3*255-num; *(ptr+2) = 0; } else { *ptr = 255; *(ptr+1) = num-3*255; *(ptr+2) = num-3*255; }}void spectrogram_draw_side_scalebar(GdkPixmap *pixmap, int width,int height){ int ipixel, j; float tmp; guchar buf[width*3]; for (ipixel=0; ipixel<height; ipixel++) { tmp = (height-ipixel)*4*255.0/height; for (j=0; j<width; j++) set_color_grade(tmp, buf+j*3); gdk_draw_rgb_image(pixmap, gc_bg,0,ipixel,width,1,GDK_RGB_DITHER_MAX,buf,width*3); }}float calc_data_correlation(float *data1,float *data2,int ndata, int from, int to){ float coeff=0, tmp, *end1_ptr; int n; n = to-from+1; data1 += from; data2 += from; end1_ptr = &(data1[n]); while ( end1_ptr>data1 ) { tmp = *data1 - *data2; coeff += tmp*tmp/n; data1++; data2++; } return coeff;}void spectrogram_main_draw(GdkPixmap *pixmap, int width,int height){ float ymin_tmp,ymax_tmp,tmp,scale,samples_per_pixel,max,color,color_prev,dcolor,coef; int idx,i,j,ipixel,ipixel_prev,idata_min,idata_max,tw,th; GSList *l=linetags; linetag_t *tag; /* Get the data */ if ( !(refresh_status&REFRESH_FFT_STOP) ) { get_fft_sample(ndata_in, data, data_display_from,data_display_to,&ymin_tmp,&ymax_tmp,logscale); if ( intensity_scale_mode==INTENSITY_SCALE_FLEXIBLE ) { ymax = ymax_tmp; ymin = ymin_tmp; } else { if ( ymax_tmp>ymax ) ymax = ymax_tmp; if ( ymin_tmp<ymin ) ymin = ymin_tmp; if ( intensity_scale_mode==INTENSITY_SCALE_CONSTANT ) { ymax_tmp = ymax; ymin_tmp = ymin; } else /* INTENSITY_SCALE_SMART */ { if (!silence_data) { MALLOC(silence_data,float,ndata_out); silence_nacquire = NSILENCE; memcpy(silence_data,data,ndata_out*sizeof(float)); } coef = calc_data_correlation(data,silence_data,ndata_out,data_display_from,data_display_to); if ( silence_nacquire>0 ) { silence_nacquire--; if ( silence_threshold<coef ) silence_threshold=coef; ymax_tmp = ymax; } else if ( silence_threshold*2>coef ) ymax_tmp = ymax; } } } scale = 4*255/(ymax_tmp-ymin_tmp); /* Plot results */ samples_per_pixel = 1.0*(data_display_to-data_display_from+1)/width; if ( samples_per_pixel > 1 ) { idx = 0; /* More samples than pixels: For each pixel, go through the nearby data samples and select the one with the largest intensity */ for (ipixel=0; ipixel<width; ipixel++) { idata_min = pixel_to_idata(ipixel-0.5,width); idata_max = pixel_to_idata(ipixel+0.5,width); max = data[idata_min]; for (i=idata_min+1; i<idata_max; i++) if ( max<data[i] ) max=data[i]; tmp = scale*(max-ymin_tmp); set_color_grade(tmp, main_img.data+idx); idx+=3; } } else { /* More pixels than data samples: For each data sample calculate the corresponding pixel. */ if (interpolate_missing_data_points) { ipixel = idata_to_pixel(data_display_from,width); color = scale*(data[data_display_from]-ymin_tmp); for (i=data_display_from; i<=data_display_to; i++) { color_prev = color; ipixel_prev = ipixel; ipixel = idata_to_pixel(i,width); color = scale*(data[i]-ymin_tmp); dcolor = (color-color_prev)/(ipixel-ipixel_prev); for (j=ipixel_prev+1; j<=ipixel; j++) { if ( j<0 || j>=main_img.width ) exit_nicely("FIXME: ipixel=%d!\n",j); color_prev += dcolor; set_color_grade(color_prev, &(main_img.data[3*j])); } } } else { for (i=data_display_from; i<=data_display_to; i++) { ipixel = idata_to_pixel(i,width); if ( ipixel<0 || ipixel>=main_img.width ) exit_nicely("FIXME: ipixel=%d!\n",ipixel); color = scale*(data[i]-ymin_tmp); set_color_grade(color, &(main_img.data[3*ipixel])); } } } gdk_draw_rgb_image(pixmap, gc_bg,0,current_y, width,1,GDK_RGB_DITHER_MAX,main_img.data,width*3); /* Draw custom tags */ if ( view_custom_grid ) { while (l) { tag = (linetag_t *) l->data; if ( tag->idata>data_display_from && tag->idata<data_display_to ) { ipixel = floor( 0.5+idata_to_pixel(tag->idata,width) ); gdk_draw_line(pixmap,tag->gc, ipixel,0,ipixel,height); pango_layout_set_text(pango_layout,tag->label,strlen(tag->label)); pango_layout_get_pixel_size(pango_layout, &tw,&th); gdk_draw_rectangle(pixmap,gc_bg,TRUE,ipixel+4,5*SB_MARGIN-1,tw+2,th+2); gdk_draw_layout(pixmap, tag->gc, ipixel+5, 5*SB_MARGIN, pango_layout); } l = l->next; } } if ( ++current_y==height ) current_y = 0; gdk_draw_line(pixmap, silence_nacquire>0? gc_silent:gc_line,0,current_y, width,current_y);}void spectrogram_interpolate_toggle(void){ interpolate_missing_data_points = interpolate_missing_data_points ? 0 : 1; if (!interpolate_missing_data_points) image_fill_rectangle(&main_img,color_bg, 0,0,main_img.width,main_img.height);}void spectrogram_logarithmic_intensity_toggle(void){ logscale = logscale ? 0 : 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -