miniscope cleanup
This commit is contained in:
parent
cdde307640
commit
0b1a2bd5e5
204
src/miniscope.c
204
src/miniscope.c
|
@ -15,6 +15,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <alloca.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -33,47 +34,88 @@ band_mag( fftwf_complex * const cplx, unsigned int band, float scalar )
|
||||||
{
|
{
|
||||||
float re = cplx[band][0];
|
float re = cplx[band][0];
|
||||||
float im = cplx[band][1];
|
float im = cplx[band][1];
|
||||||
float mag = sqrtf(re*re + im*im) * scalar;
|
float mag = hypot(re, im) * scalar;
|
||||||
return mag;
|
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<nbands; i++ ) {
|
||||||
|
float mag = band_mag(fftout, i, magscalar);
|
||||||
|
if ( mag > 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 = {
|
static const pa_sample_spec ss = {
|
||||||
.format = PA_SAMPLE_FLOAT32,
|
.format = PA_SAMPLE_FLOAT32,
|
||||||
.rate = 9600, // pulseaudio will resample its configured audio rate
|
.rate = 9600, // pulseaudio will resample to this rate
|
||||||
|
.channels = 2 // 2 channel stereo
|
||||||
.channels = 2 // 2 channel stereo
|
//.channels = 1 // downmix (additively) to 1 channel
|
||||||
// .channels = 1 // pulseaudio will downmix (additively) to 1 channel
|
//.channels = 3 // 2 channel stereo + 1 mixed 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;
|
int argi = 1;
|
||||||
unsigned int decode_rate = atoi(argv[argi++]);
|
while ( argi < argc && argv[argi][0] == '-' ) {
|
||||||
unsigned int band_width = decode_rate;
|
/* -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 )
|
if ( argi < argc )
|
||||||
band_width = atoi(argv[argi++]);
|
band_width = atoi(argv[argi++]);
|
||||||
|
|
||||||
assert( band_width <= decode_rate );
|
assert( band_width <= decode_rate );
|
||||||
|
|
||||||
unsigned int sample_rate = ss.rate;
|
unsigned int sample_rate = ss.rate;
|
||||||
|
|
||||||
|
/* Initiate the capture stream */
|
||||||
|
int error;
|
||||||
/* Create the recording stream */
|
|
||||||
pa_simple *s;
|
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 ) {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,126 +123,92 @@ int main(int argc, char*argv[]) {
|
||||||
int pa_framesize = pa_frame_size(&ss);
|
int pa_framesize = pa_frame_size(&ss);
|
||||||
int pa_nchannels = ss.channels;
|
int pa_nchannels = ss.channels;
|
||||||
|
|
||||||
|
assert( pa_samplesize == sizeof(float) );
|
||||||
assert( pa_framesize == pa_samplesize * pa_nchannels );
|
assert( pa_framesize == pa_samplesize * pa_nchannels );
|
||||||
|
|
||||||
|
|
||||||
/* Create the FFT plan */
|
/* Create the FFT plan */
|
||||||
fftwf_plan fftplan;
|
fftwf_plan fftplan;
|
||||||
|
|
||||||
int fftsize = sample_rate / band_width;
|
int fftsize = sample_rate / band_width;
|
||||||
|
|
||||||
if ( fftsize & 1 )
|
if ( fftsize & 1 )
|
||||||
fprintf(stderr, __FILE__": WARNING: fftsize %u is not even\n", fftsize);
|
fprintf(stderr, __FILE__": WARNING: fftsize %u is not even\n", fftsize);
|
||||||
|
|
||||||
unsigned int nbands = fftsize / 2 + 1;
|
unsigned int nbands = fftsize / 2 + 1;
|
||||||
|
|
||||||
float *fftin = fftwf_malloc(fftsize * sizeof(float) * pa_nchannels);
|
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);
|
||||||
|
|
||||||
/*
|
/* basic fftw plan, works for only 1 channel:
|
||||||
* works only for 1 channel:
|
|
||||||
fftplan = fftwf_plan_dft_r2c_1d(fftsize, fftin, fftout, FFTW_ESTIMATE);
|
fftplan = fftwf_plan_dft_r2c_1d(fftsize, fftin, fftout, FFTW_ESTIMATE);
|
||||||
*/
|
*/
|
||||||
/*
|
/* complex fftw plan, works for N channels: */
|
||||||
* works for N channels:
|
|
||||||
*/
|
|
||||||
fftplan = fftwf_plan_many_dft_r2c(
|
fftplan = fftwf_plan_many_dft_r2c(
|
||||||
/*rank*/1, &fftsize, /*howmany*/pa_nchannels,
|
/*rank*/1, &fftsize, /*howmany*/pa_nchannels,
|
||||||
fftin, NULL, /*istride*/pa_nchannels, /*idist*/1,
|
fftin, NULL, /*istride*/pa_nchannels, /*idist*/1,
|
||||||
fftout, NULL, /*ostride*/1, /*odist*/nbands,
|
fftout, NULL, /*ostride*/1, /*odist*/nbands,
|
||||||
FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
|
FFTW_ESTIMATE);
|
||||||
/* Nb. FFTW_PRESERVE_INPUT is needed for the "shift the input window" trick */
|
|
||||||
|
|
||||||
if ( !fftplan ) {
|
if ( !fftplan ) {
|
||||||
fprintf(stderr, __FILE__": fftwf_plan_dft_r2c_1d() failed\n");
|
fprintf(stderr, __FILE__": fftwf_plan_dft_r2c_1d() failed\n");
|
||||||
return 1;
|
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
|
/* Calculate the fftw output normalization factor */
|
||||||
assert( pa_samplesize == sizeof(float) );
|
float magscalar = 1.0 / (nsamples/2.0);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 */
|
|
||||||
|
|
||||||
|
|
||||||
|
# if 0
|
||||||
float actual_decode_rate = (float)sample_rate / nsamples;
|
float actual_decode_rate = (float)sample_rate / nsamples;
|
||||||
fprintf(stderr, "### baud=%.2f ###\n", actual_decode_rate);
|
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
|
* Run the main loop
|
||||||
*/
|
*/
|
||||||
|
while ( 1 )
|
||||||
|
{
|
||||||
|
// for possible future use...
|
||||||
|
// bzero(fftin, (fftsize * sizeof(float) * pa_nchannels));
|
||||||
|
|
||||||
ret = 0;
|
/* read a chunk of input sample frames (directly into the
|
||||||
|
* FFT input buffer) */
|
||||||
while ( 1 ) {
|
if (pa_simple_read(s, fftin, nbytes, &error) < 0) {
|
||||||
|
|
||||||
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) {
|
|
||||||
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n",
|
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n",
|
||||||
pa_strerror(error));
|
pa_strerror(error));
|
||||||
ret = 1;
|
return(1);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float inmax=0, inmin=0;
|
/* run the FFT to compute the spectrum (for all pa_nchannels) */
|
||||||
int i;
|
|
||||||
for ( i=0; i<fftsize; i++ ) {
|
|
||||||
// if ( fftin[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];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fftwf_execute(fftplan);
|
fftwf_execute(fftplan);
|
||||||
|
|
||||||
{
|
/* display the spectrum magnitudes for each channel */
|
||||||
float magmax = 0;
|
int n;
|
||||||
|
for ( n=0; n<pa_nchannels; n++ )
|
||||||
for ( i=0; i<nbands*pa_nchannels; i++ ) {
|
miniscope_print(fftout+n*nbands, show_nbands, magscalar,
|
||||||
if ( i%nbands == 0 )
|
one_line_mode, show_maxmag);
|
||||||
printf("|");
|
printf( one_line_mode ? "\r" : "\n" );
|
||||||
float mag = band_mag(fftout, i, magscalar);
|
fflush(stdout);
|
||||||
if ( mag > 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Clean up */
|
||||||
|
free(magline);
|
||||||
pa_simple_free(s);
|
pa_simple_free(s);
|
||||||
|
|
||||||
fftwf_free(fftin);
|
fftwf_free(fftin);
|
||||||
fftwf_free(fftout);
|
fftwf_free(fftout);
|
||||||
fftwf_destroy_plan(fftplan);
|
fftwf_destroy_plan(fftplan);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue