/* * Routines for setting up the parameters/buffers/display * * Copyright (C) 1995 Philip VanBaren (C) 1996 Emil LAURENTIU */ #include #include #include #include #include #include #include "freq.h" #include "extern.h" #include "display.h" int fftlen = 1024; /* Number of points for FFT */ long SampleRate = 20000L; /* A/D sampling rate */ int logfreq = 0; /* Flag set to 1 for log-based frequency * scale */ float ys = 0.1; /* Flag set for max of y-axis */ float threshold_level = 0.05; /* Treshold level used in DTMF & * CTCSS modes */ int dtmf_delay = 80; /* 1/3 Length of generated DTMF tones in ms */ int Soundcard = SC_DEF; /* Soundcard number (as defined in * freq.h) */ int sb_irq = SB_IRQ; /* IRQ used by the Soundblaster card */ int sb_dma = SB_DMA; /* DMA channel used by the Soundblaster card */ int sb_addr = SB_ADDR;/* I/O address of the Soundblaster card */ float maxfreq; double alpha; /* Gaussian window parameter */ struct rgb background = {0, 0, 20}, warn = {20, 0, 0}, graph = {30, 35, 60}, tick = {40, 40, 40}, label = {0, 63, 0}, border = {63, 0, 0}, text = {63, 63, 0}, darkhl = {20, 20, 20}, lighthl = {63, 63, 63}; /* Center frequencies for equalizer-mode display */ double center_freq[] = {4, 5, 6.3, 8, 10, 12.5, 16, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000, 25000}; #define EQUALIZER_BINS (sizeof(center_freq)/sizeof(center_freq[0])) /* Soundcard function pointers */ void ( *init_soundcard ) ( void ) = NULL; void ( *reset_soundcard ) ( void ) = NULL; void ( *halt_soundcard ) ( void ) = NULL; void ( *cleanup_soundcard ) ( void ) = NULL; void ( *recordblock ) ( void far * ) = NULL; void ( *set_mixer ) ( int, int ) = NULL; int sample_size; /* Bits per sample (8 or 16) */ int mixers; /* Mixers available (1) or not (0) */ #include void far * aligned_malloc( long len ) { void far *ptr; unsigned int far *orig_ptr; unsigned seg, orig_seg, orig_off; long lin_addr; /* Allocate double the required space */ ptr = farmalloc( len * 2 + 32 ); if ( ptr != NULL ) { /* Compute a new pointer to the buffer such that the next "len" bytes */ /* do not cross a page boundary, and the offset is zero */ /* (as required by DMA transfers) */ orig_off = FP_OFF( ptr ); orig_seg = FP_SEG( ptr ); /* reserve 4 bytes for the original pointer */ lin_addr = ( orig_seg * 16L ) + orig_off + 4L; if ( ( lin_addr & 0xF0000L ) != ( ( lin_addr + len - 1 ) & 0xF0000L ) ) lin_addr = ( lin_addr + len - 1 ) & 0xF0000L; else lin_addr = ( lin_addr + 15 ) & 0xFFFF0L; seg = ( unsigned int ) ( lin_addr / 16 ); orig_ptr = ( unsigned far * ) MK_FP( seg - 1, 0x000C ); orig_ptr[0] = orig_off; orig_ptr[1] = orig_seg; ptr = MK_FP( seg, 0 ); /* * printf("Original: %04x:%04x, New: %04x:%04x, Linear: %05lx\n", * orig_seg,orig_off,FP_SEG(ptr),FP_OFF(ptr),lin_addr); */ } return ptr; } void aligned_free( void far * ptr ) { if ( ptr != NULL ) { unsigned far *old_ptr = ( unsigned far * ) MK_FP( FP_SEG( ptr ) - 1, 0x000c ); /* * printf("Freeing: %04x:%04x, Ptr: %04x:%04x\n", * FP_SEG(old_ptr),FP_OFF(old_ptr),FP_SEG(ptr),FP_OFF(ptr)); */ farfree( MK_FP( old_ptr[1], old_ptr[0] ) ); } } /* * Parse the ini file, if it exists */ void parse_ini_file( void ) { int i; FILE *fp; char buffer[100]; if ( ( fp = fopen( ini_file, "r" ) ) != NULL ) { while ( !feof( fp ) ) { fgets( buffer, sizeof( buffer ), fp ); for ( i = 0; ( buffer[i] != 0 ) && ( buffer[i] != ':' ); i++ ) buffer[i] = toupper( buffer[i] ); if ( strncmp( buffer, "SOUNDCARD:", 10 ) == 0 ) Soundcard = parsecardname( buffer + 10 ); sscanf( buffer, "SAMPLE RATE:%ld", &SampleRate ); sscanf( buffer, "FFT LENGTH:%d", &fftlen ); sscanf( buffer, "LOG FREQ SCALE:%d", &logfreq ); sscanf( buffer, "MAX AMP:%f", &ys ); sscanf( buffer, "BASE FREQUENCY:%f", &freq_base ); sscanf( buffer, "FREQUENCY FACTOR:%f", &freq_scalefactor ); sscanf( buffer, "DTMF & CTCSS THRESHOLD LEVEL:%f", &threshold_level ); sscanf( buffer, "DTMF DELAY (100 - 1000 MS):%d", &dtmf_delay ); sscanf( buffer, "CTCSS ACTIVE:%lx,%lx", &ctcss_act1, &ctcss_act2 ); sscanf( buffer, "BACKGROUND COLOR:%d,%d,%d", &background.red, &background.green, &background.blue ); sscanf( buffer, "CLIPPING WARNING COLOR:%d,%d,%d", &warn.red, &warn.green, &warn.blue ); sscanf( buffer, "GRAPH COLOR:%d,%d,%d", &graph.red, &graph.green, &graph.blue ); sscanf( buffer, "TICK MARK COLOR:%d,%d,%d", &tick.red, &tick.green, &tick.blue ); sscanf( buffer, "AXIS LABEL COLOR:%d,%d,%d", &label.red, &label.green, &label.blue ); sscanf( buffer, "BORDER COLOR:%d,%d,%d", &border.red, &border.green, &border.blue ); sscanf( buffer, "TEXT COLOR:%d,%d,%d", &text.red, &text.green, &text.blue ); sscanf( buffer, "CURSOR UPPER COLOR:%d,%d,%d", &darkhl.red, &darkhl.green, &darkhl.blue ); sscanf( buffer, "CURSOR LOWER COLOR:%d,%d,%d", &lighthl.red, &lighthl.green, &lighthl.blue ); } fclose( fp ); dtmf_delay /= 3; } } void setnormalpalette( void ) { draw_setpalette( 0, background.red, background.green, background.blue ); draw_setpalette( LABEL_COLOR, label.red, label.green, label.blue ); draw_setpalette( BORDER_COLOR, border.red, border.green, border.blue ); draw_setpalette( TEXT_COLOR, text.red, text.green, text.blue ); draw_setpalette( GRAPH_COLOR, graph.red, graph.green, graph.blue ); draw_setpalette( DARK_HIGHLIGHT, darkhl.red, darkhl.green, darkhl.blue ); draw_setpalette( LIGHT_HIGHLIGHT, lighthl.red, lighthl.green, lighthl.blue ); } void setbwpalette( void ) { draw_setpalette( 0, 0, 0, 0 ); draw_setpalette( LABEL_COLOR, 63, 63, 63 ); draw_setpalette( BORDER_COLOR, 63, 63, 63 ); draw_setpalette( TEXT_COLOR, 63, 63, 63 ); draw_setpalette( GRAPH_COLOR, 63, 63, 63 ); draw_setpalette( DARK_HIGHLIGHT, 20, 20, 20 ); draw_setpalette( LIGHT_HIGHLIGHT, 63, 63, 63 ); } /* * Parse the command line */ void parse_command( int argc, char *argv[], char *environ[] ) { int i = 0; while ( i < argc ) { if ( argv[i][0] == '-' ) { switch ( argv[i][1] ) { case 'C': /* Select the sound card */ case 'c': Soundcard = parsecardname( argv[i] + 2 ); break; case 'S': /* Set the sampling rate (in Hz) */ case 's': SampleRate = atol( &argv[i][2] ); break; case 'F': /* Set the FFT length */ case 'f': fftlen = atoi( &argv[i][2] ); break; case 'M': /* Set the maximum value for linear display */ case 'm': ys = atof( &argv[i][2] ); break; case 'L': /* Set logarithmic frequency scale */ case 'l': logfreq = 1; break; case '?': /* Display some help information */ case 'H': case 'h': puts( "-Cnumber selects the soundcard (0=SB, 1=SB16)." ); puts( "-Snumber sets the sampling rate." ); puts( "-Fnumber sets the length of the FFT." ); puts( "-Mnumber sets the scale maximum." ); puts( "-L sets a logarithmic scale for the frequency axis." ); puts( "-? or -H displays this message." ); exit( 0 ); default: printf( "Ignoring unrecognized switch: %s\n", argv[i] ); puts( "Press to continue..." ); getch( ); } } else printf( "Ignoring unrecognized parameter: %s (use -h for help)\n", argv[i] ); i++; } /* * Watch for bad choice of log scales */ if ( ys > 2.0 ) ys = 2.0; if ( ys < 0.01 ) ys = 0.01; if ( SampleRate > 88200L ) SampleRate = 88200L; if ( SampleRate < 5000L ) SampleRate = 5000L; if ( fftlen < 8 ) fftlen = 8; if ( fftlen > MAX_LEN ) fftlen = MAX_LEN; /* Convert fftlen to a power of 2 */ for ( i = 0; fftlen > 1; fftlen >>= 1, i++ ); if ( i ) fftlen <<= i; /* Set up the soundcard function pointers */ #ifdef SC_SB8 if ( Soundcard == SC_SB8 ) init_sb8( environ ); #endif #ifdef SC_SB16 if ( Soundcard == SC_SB16 ) init_sb16( environ ); #endif if ( reset_soundcard == NULL ) { puts( "Error: Invalid soundcard selection" ); puts( "Valid choices are:" ); #ifdef SC_SB8 printf( " %s (%d)\n", SC_SB8_NAME, SC_SB8 ); #endif #ifdef SC_SB16 printf( " %s (%d)\n", SC_SB16_NAME, SC_SB16 ); #endif exit( 1 ); } } /* Parse a sound card number or label */ int parsecardname( char *label ) { int i = 0, j; char name[10]; /* Skip over any leading space */ while ( label[i] == ' ' ) i++; /* Check for numerical values */ if ( label[i] <= '9' ) return ( label[i] - '0' ); /* Convert the string to upper case */ for ( j = 0; label[i] && ( j < 9 ); i++, j++ ) name[j] = toupper( label[i] ); name[j] = 0; /* Check for valid string values */ #ifdef SC_SB8 if ( strncmp( name, SC_SB8_NAME, strlen( SC_SB8_NAME ) ) == 0 ) return SC_SB8; #endif #ifdef SC_SB16 if ( strncmp( name, SC_SB16_NAME, strlen( SC_SB16_NAME ) ) == 0 ) return SC_SB16; #endif return ( -1 ); /* If none match, return failure */ } /* * Allocate memory for and initialize the buffers */ void setup_buffers( int length ) { int i, ll; char far *p; for ( i = 0; i < BUFFERS; i++ ) { if ( ( buffer[i] = aligned_malloc( length * ( sample_size / 8 ) ) ) == NULL ) { puts( "Unable to allocate enough memory for the buffers!" ); exit( 1 ); } /* Pre-set the buffer to all zeros */ p = buffer[i]; for ( ll = 0; ll < length * ( sample_size / 8 ); ll++ ) *p++ = 0; flag[i] = 0; } if ( ( fftdata = ( short * ) malloc( length * sizeof( short ) ) ) == NULL ) { puts( "Unable to allocate enough memory for the buffers!" ); exit( 1 ); } if ( ( wind = ( short * ) malloc( length * sizeof( short ) ) ) == NULL ) { puts( "Unable to allocate enough memory for the buffers!" ); exit( 1 ); } if ( ( displayval = ( long * ) malloc( length / 2 * sizeof( long ) ) ) == NULL ) { puts( "Unable to allocate enough memory for the buffers!" ); exit( 1 ); } if ( ( ybase = ( long * ) malloc( length / 2 * sizeof( long ) ) ) == NULL ) { puts( "Unable to allocate enough memory for the buffers!" ); exit( 1 ); } /* * Clear out the memory buffers */ for ( i = 0; i < ( WINDOW_RIGHT - WINDOW_LEFT + 1 ); i++ ) lasty[i] = WINDOW_BOTTOM; } void compute_window_function( void ) { int i; /* * Calculate FFT Windowing function */ for ( i = 0; i < fftlen; i++ ) { double val; val = 0.54 - 0.46 * cos( 2 * M_PI * i / fftlen ); /* Hamming */ wind[i] = floor( val * 32767 + 0.5 ); } } /* * Do some range checking on the base and scale factors */ void xrange_check( void ) { if ( freq_scalefactor < 1.0 ) freq_scalefactor = 1.0; if ( freq_scalefactor > 16.0 ) freq_scalefactor = 16.0; if ( logfreq ) { double max_base = SampleRate / 2 / exp( log( fftlen / 2 ) / freq_scalefactor ); if ( freq_base < ( double ) SampleRate / fftlen ) freq_base = ( double ) SampleRate / fftlen; if ( freq_base > max_base ) freq_base = max_base; } else { if ( freq_base < 0 ) freq_base = 0; if ( ( freq_base + SampleRate / ( 2 * freq_scalefactor ) ) > SampleRate / 2 ) freq_base = SampleRate / 2 - SampleRate / ( 2 * freq_scalefactor ); } if ( logfreq ) maxfreq = freq_base * exp( log( fftlen / 2 ) / freq_scalefactor ); else maxfreq = freq_base + ( double ) SampleRate / ( freq_scalefactor * 2.0 ); } /* * Set up X axis scales */ void setup_xscale( void ) { int i; /* * Do some range checking on the base and scale factors */ xrange_check( ); /* * Initialize graph x scale (linear or logarithmic). This array points to * the bin to be plotted on a given line. */ for ( i = 0; i <= ( WINDOW_RIGHT - WINDOW_LEFT + 1 ); i++ ) { int val; if ( logfreq ) val = floor( ( double ) fftlen * freq_base / ( double ) SampleRate * exp( ( i - 0.45 ) / ( double ) ( WINDOW_RIGHT - WINDOW_LEFT ) * log( fftlen / 2 ) / freq_scalefactor ) + 0.5 ); else val = floor( ( ( i - 0.45 ) / ( double ) ( WINDOW_RIGHT - WINDOW_LEFT ) * ( double ) fftlen / 2.0 / freq_scalefactor ) + ( freq_base / ( double ) SampleRate * ( double ) fftlen ) + 0.5 ); if ( val < 0 ) val = 0; if ( val >= fftlen / 2 ) val = fftlen / 2 - 1; if ( i > 0 ) x2[i - 1] = val; if ( i <= ( WINDOW_RIGHT - WINDOW_LEFT ) ) x[i] = val; } /* Compute the ending locations for lines holding multiple bins */ for ( i = 0; i <= ( WINDOW_RIGHT - WINDOW_LEFT ); i++ ) { if ( x2[i] <= ( x[i] + 1 ) ) x2[i] = 0; } /* * If lines are repeated on the screen, flag this so that we don't have to * recompute the y values. */ for ( i = ( WINDOW_RIGHT - WINDOW_LEFT ); i > 0; i-- ) { if ( x[i] == x[i - 1] ) { x[i] = -1; x2[i] = 0; } } setup_linscales( ); frequency_scale( ); } /* * Set up linear amplitude scale factors */ void setup_linscales( void ) { int i; double scale; /* * Compute the (linear) y scale factor. */ scale = ( WINDOW_BOTTOM - WINDOW_TOP ) / ( ys * 32768.0 ); shift = 4; /* Display data has an extra factor of 16 for * better resolution */ { /* * Make maximum use of available bits (use only 12 bits--other 4 used for * higher resolution in the data) */ while ( scale < 4096 ) { scale *= 2; shift++; } scale = floor( scale + 0.5 ); for ( i = 0; i < ( WINDOW_RIGHT - WINDOW_LEFT + 1 ); i++ ) { if ( x[i] == -1 ) yscale[i] = 0; else yscale[i] = scale; } } shiftscale = 1 / pow( 2, shift ); } void frequency_scale( void ) { int i; double step, freq; char text[20]; /* * Put up the frequency scale. */ draw_bar( WINDOW_LEFT - 10, WINDOW_BOTTOM + 3, WINDOW_RIGHT + 10, 469, 0 ); draw_fontcolor( LABEL_COLOR ); if ( logfreq ) step = log( fftlen / 2 ) / ( 32.0 * freq_scalefactor ); else step = ( double ) SampleRate / ( 64.0 * freq_scalefactor ); for ( i = 0; i <= 32; i++ ) { int x = WINDOW_LEFT + ( ( double ) i * ( WINDOW_RIGHT - WINDOW_LEFT ) / 32.0 ); int y = WINDOW_BOTTOM + 3; draw_line( x, y, x, y + 3, BORDER_COLOR ); if ( logfreq ) freq = freq_base * exp( step * i ); else freq = freq_base + i * step; if ( freq < 0 ) freq = 0; sprintf( text, "%.0f", freq ); draw_text_vertical( x - _font_width / 2, y + 6, text ); } draw_fontcolor( TEXT_COLOR ); } void amplitude_scale( void ) { int i; char text[20]; draw_bar( 0, WINDOW_TOP - 10, WINDOW_LEFT - 3, WINDOW_BOTTOM + 7, 0 ); draw_bar( WINDOW_RIGHT + 3, WINDOW_TOP - 10, 639, WINDOW_BOTTOM + 7, 0 ); draw_fontcolor( LABEL_COLOR ); /* * Put up the amplitude scale */ { double scale = ( double ) ( ys * 32768.0 ) / ( double ) ( WINDOW_BOTTOM - WINDOW_TOP ); for ( i = 0; i <= 10; i++ ) { int x = WINDOW_LEFT - 3; int y = WINDOW_BOTTOM - i * ys * 3276.8 / scale; draw_line( x, y, x - 3, y, BORDER_COLOR ); if ( ys > 0.095 ) sprintf( text, "%4.2f", ( float ) i * ys * 0.1 ); else sprintf( text, "%5.3f", ( float ) i * ys * 0.1 ); draw_text_right( x - 6, y - _font_height / 2, text ); draw_line( WINDOW_RIGHT + 3, y, WINDOW_RIGHT + 6, y, BORDER_COLOR ); draw_text_left( WINDOW_RIGHT + 9, y - _font_height / 2, text ); } } draw_fontcolor( TEXT_COLOR ); } void log_state( void ) { draw_bar( 24, 24, 112, 36, 0 ); draw_fontcolor( LABEL_COLOR ); if ( log_mode ) draw_text_left( 24, 24, "Logging ON" ); else draw_text_left( 24, 24, "Logging OFF" ); } void update_header( void ) { char ach[100]; draw_bar( 0, 0, 639, WINDOW_TOP - 10, 0 ); draw_bar( WINDOW_LEFT - 5, WINDOW_TOP - 10, WINDOW_RIGHT + 5, WINDOW_TOP - 4, 0 ); draw_fontcolor( TEXT_COLOR ); sprintf( ach, "Sampling rate: %ld Hz", SampleRate ); draw_text_centered( SRX, SRY, ach ); sprintf( ach, "FFT Length: %d points", fftlen ); draw_text_centered( FLX, FLY, ach ); sprintf( ach, "Frequency resolution: %.4g Hz", ( double ) SampleRate / ( double ) fftlen ); draw_text_centered( FRX, FRY, ach ); if ( dtmf_nr[0] != 0 ) { dtmf_nr[64] = 0; /* maximum 64 DTMF numbers */ draw_fontcolor( GRAPH_COLOR ); draw_text_left( WINDOW_LEFT, MGY - 36, "Number stored in memory: " ); draw_text_left( WINDOW_LEFT, MGY - 24, dtmf_nr ); draw_fontcolor( TEXT_COLOR ); } if ( gen_ctcss != CTCSS_MAX ) { draw_fontcolor( GRAPH_COLOR ); sprintf( ach, "CTCSS %.1f Hz", f_ctcss[gen_ctcss] ); draw_text_right( WINDOW_RIGHT, 2, ach ); sprintf( ach, "Error %.2f Hz", err_ctcss ); draw_text_right( WINDOW_RIGHT, 14, ach ); draw_fontcolor( TEXT_COLOR ); } if ( dtmf_mode ) { draw_fontcolor( BORDER_COLOR ); draw_text_left( 24, 0, "DTMF mode" ); draw_fontcolor( LABEL_COLOR ); draw_text_left( 24, 60, "DTMF numbers:" ); draw_threshold_level( TEXT_COLOR ); log_state( ); } if ( ctcss_mode ) { draw_fontcolor( BORDER_COLOR ); draw_text_left( 24, 0, "CTCSS mode" ); draw_fontcolor( GRAPH_COLOR ); draw_text_left( 24, 60, "CTCSS Freq:" ); draw_threshold_level( TEXT_COLOR ); log_state( ); } if ( mixers ) { sprintf( ach, "Mic:%3d Ext:%3d CD:%3d", mic_level, ext_level, int_level ); draw_text_left( LVX, LVY, ach ); } } void show_help( void ) { draw_bar( 0, 0, 639, WINDOW_TOP - 10, 0 ); draw_bar( WINDOW_LEFT - 5, WINDOW_TOP - 10, WINDOW_RIGHT + 5, WINDOW_TOP - 4, 0 ); if ( help_mode == 1 ) { draw_text_left( 20, 2, " 'D' Toggle DTMF mode" ); draw_text_left( 20, 14, " 'T' Toggle CTCSS mode" ); draw_text_left( 20, 26, " 'I' Input a DTMF number" ); draw_text_left( 20, 38, " Compose the stored DTMF #" ); draw_text_left( 20, 50, " 'A' Generate a subAudible tone" ); draw_text_left( 20, 62, " 'G' Toggle loGfile (dtmf & ctcss)" ); draw_text_left( 20, 74, " Freeze display" ); draw_text_left( 340, 2, " 'L' Toggle Log/Linear frequency" ); draw_text_left( 340, 14, " 'R' Change the sampling Rate" ); draw_text_left( 340, 26, " 'F' Change the FFT length" ); draw_text_left( 340, 38, " 'P' Toggle Peak display mode" ); draw_text_left( 340, 50, " 'S' Save State to INI file" ); draw_text_left( 340, 62, "'H','?' Toggle this Help screen" ); draw_text_left( 340, 74, "'Q','E' Exit from the program" ); } else { draw_text_left( 100, 2, " Shift the frequency axis" ); draw_text_left( 100, 14, " '<','>' Expand/contract the frequency axis" ); draw_text_left( 100, 26, " Change the amplitude scale" ); draw_text_left( 100, 38, " Threshold level in DTMF & CTCSS mode" ); draw_text_left( 100, 50, "/ Move to the minimum/maximum freqency" ); if ( mixers ) { draw_text_left( 60, 62, "'C' Toggle B&W/color display" ); draw_text_left( 340, 62, "'V' Refresh display" ); draw_text_left( 60, 74, "(),[],{} Adjust mic,ext,CD input level" ); } else { draw_text_left( 60, 62, "'C' Toggle B&W/color display" ); draw_text_left( 340, 62, "'V' Refresh display" ); } } }