diff --git a/src/miniscope.c b/src/miniscope.c index ca180f5..cb2caa1 100644 --- a/src/miniscope.c +++ b/src/miniscope.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -33,47 +34,88 @@ band_mag( fftwf_complex * const cplx, unsigned int band, float scalar ) { float re = cplx[band][0]; float im = cplx[band][1]; - float mag = sqrtf(re*re + im*im) * scalar; + float mag = hypot(re, im) * scalar; return mag; } -int main(int argc, char*argv[]) { - /* The sample type to use */ + +void +miniscope_print( fftwf_complex * const fftout, int nbands, float magscalar, + unsigned char one_line_mode, unsigned char show_maxmag ) +{ + char magchars[] = " .-=+#^"; + char *buf = alloca(nbands+1); + if ( one_line_mode ) + magchars[0] = '_'; + float maxmag = 0; + int i; + for ( i=0; i maxmag ) + maxmag = mag; + char c; + if ( mag <= 0.05 ) c = magchars[0]; + else if ( mag <= 0.10 ) c = magchars[1]; + else if ( mag <= 0.25 ) c = magchars[2]; + else if ( mag <= 0.50 ) c = magchars[3]; + else if ( mag <= 0.95 ) c = magchars[4]; + else if ( mag <= 1.00 ) c = magchars[5]; + else c = magchars[6]; + buf[i] = c; + } + buf[i] = 0; + if ( show_maxmag ) + printf(" %.2f", maxmag); + printf("|%s|", buf); +} + + +int +main( int argc, char*argv[] ) +{ static const pa_sample_spec ss = { .format = PA_SAMPLE_FLOAT32, - .rate = 9600, // pulseaudio will resample its configured audio rate - - .channels = 2 // 2 channel stereo - // .channels = 1 // pulseaudio will downmix (additively) to 1 channel - // .channels = 3 // 2 channel stereo + 1 mixed channel + .rate = 9600, // pulseaudio will resample to this rate + .channels = 2 // 2 channel stereo + //.channels = 1 // downmix (additively) to 1 channel + //.channels = 3 // 2 channel stereo + 1 mixed channel }; - int ret = 1; - int error; - - - if ( argc < 2 ) { - fprintf(stderr, "usage: miniscope baud_rate [ band_width ]\n"); - return 1; - } + unsigned int decode_rate = 50; + unsigned int band_width = decode_rate; + int one_line_mode = 1; + int show_maxmag = 1; int argi = 1; - unsigned int decode_rate = atoi(argv[argi++]); - unsigned int band_width = decode_rate; + while ( argi < argc && argv[argi][0] == '-' ) { + /* -s switch enables "scrolling mode" instead of "one line mode" */ + if ( argv[argi][1] == 's' ) { + one_line_mode = 0; + } else { + fprintf(stderr, + "usage: miniscope [-s] [ analysis_rate [ band_width ] ]\n"); + return 1; + } + argi++; + } + if ( argi < argc ) + decode_rate = atoi(argv[argi++]); if ( argi < argc ) band_width = atoi(argv[argi++]); + assert( band_width <= decode_rate ); unsigned int sample_rate = ss.rate; - - - /* Create the recording stream */ + /* Initiate the capture stream */ + int error; pa_simple *s; - s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error); + s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "spectrum scope", + &ss, NULL, NULL, &error); if ( !s ) { - fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", + pa_strerror(error)); return 1; } @@ -81,126 +123,92 @@ int main(int argc, char*argv[]) { int pa_framesize = pa_frame_size(&ss); int pa_nchannels = ss.channels; + assert( pa_samplesize == sizeof(float) ); assert( pa_framesize == pa_samplesize * pa_nchannels ); - /* Create the FFT plan */ fftwf_plan fftplan; - int fftsize = sample_rate / band_width; - if ( fftsize & 1 ) fprintf(stderr, __FILE__": WARNING: fftsize %u is not even\n", fftsize); - unsigned int nbands = fftsize / 2 + 1; float *fftin = fftwf_malloc(fftsize * sizeof(float) * pa_nchannels); - fftwf_complex *fftout = fftwf_malloc(nbands * sizeof(fftwf_complex) * pa_nchannels); + fftwf_complex *fftout = fftwf_malloc(nbands * + sizeof(fftwf_complex) * pa_nchannels); - /* - * works only for 1 channel: + /* basic fftw plan, works for only 1 channel: fftplan = fftwf_plan_dft_r2c_1d(fftsize, fftin, fftout, FFTW_ESTIMATE); */ - /* - * works for N channels: - */ + /* complex fftw plan, works for N channels: */ fftplan = fftwf_plan_many_dft_r2c( /*rank*/1, &fftsize, /*howmany*/pa_nchannels, fftin, NULL, /*istride*/pa_nchannels, /*idist*/1, fftout, NULL, /*ostride*/1, /*odist*/nbands, - FFTW_ESTIMATE | FFTW_PRESERVE_INPUT ); - /* Nb. FFTW_PRESERVE_INPUT is needed for the "shift the input window" trick */ + FFTW_ESTIMATE); if ( !fftplan ) { fprintf(stderr, __FILE__": fftwf_plan_dft_r2c_1d() failed\n"); return 1; } + /* Calculate the input sample chunk rate */ + int nsamples = sample_rate / decode_rate; + size_t nframes = nsamples; + size_t nbytes = nframes * pa_framesize; - void *pa_samples_in = fftin; // read samples directly into fftin - assert( pa_samplesize == sizeof(float) ); - - - /* - * Prepare the input sample chunk rate - */ - int nsamples = sample_rate / decode_rate; - - // float magscalar = 1.0 / (fftsize/2.0); /* normalize fftw output */ - float magscalar = 1.0 / (nsamples/2.0); /* normalize fftw output */ - + /* Calculate the fftw output normalization factor */ + float magscalar = 1.0 / (nsamples/2.0); +# if 0 float actual_decode_rate = (float)sample_rate / nsamples; fprintf(stderr, "### baud=%.2f ###\n", actual_decode_rate); +# endif + /* Prepare the text scope output buffer */ + // sadly, COLUMNS is not exported by default (?) + char *columns_env = getenv("COLUMNS"); + int columns = columns_env ? atoi(columns_env) : 80; + int show_nbands = ( (columns - 2 - 9) / pa_nchannels ) - 1; + if ( show_nbands > nbands ) + show_nbands = nbands; + char *magline = malloc(show_nbands+1); /* * Run the main loop */ + while ( 1 ) + { + // for possible future use... + // bzero(fftin, (fftsize * sizeof(float) * pa_nchannels)); - ret = 0; - - while ( 1 ) { - - size_t nframes = nsamples; - size_t nbytes = nframes * pa_framesize; - - bzero(fftin, (fftsize * sizeof(float) * pa_nchannels)); - if (pa_simple_read(s, pa_samples_in, nbytes, &error) < 0) { + /* read a chunk of input sample frames (directly into the + * FFT input buffer) */ + if (pa_simple_read(s, fftin, nbytes, &error) < 0) { fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); - ret = 1; - break; + return(1); } - float inmax=0, inmin=0; - int i; - for ( i=0; i 1.0 || fftin[i] < -1.0 ) -// fprintf(stderr, __FILE__": WARNING input datum %.3f\n", fftin[i]); - if ( inmin > fftin[i] ) - inmin = fftin[i]; - if ( inmax < fftin[i] ) - inmax = fftin[i]; - } - - + /* run the FFT to compute the spectrum (for all pa_nchannels) */ fftwf_execute(fftplan); - { - float magmax = 0; - - for ( i=0; i magmax ) - magmax = mag; - char *magchars = " .-=^"; - char c = magchars[0]; - if ( mag > 0.10 ) c = magchars[1]; - if ( mag > 0.25 ) c = magchars[2]; - if ( mag > 0.50 ) c = magchars[3]; - if ( mag > 1.00 ) c = magchars[4]; - printf("%c", c); - - // if ( i > 70 ) - // break; - } - printf("|"); - // printf("in %+4.2f %+4.2f", inmin, inmax); - printf(">mag %.2f", magmax); - printf("\n"); - } - + /* display the spectrum magnitudes for each channel */ + int n; + for ( n=0; n