From ffdf40af346be53001adac2b7cce3767d5a89c06 Mon Sep 17 00:00:00 2001 From: hayati ayguen Date: Sun, 27 Oct 2019 19:45:20 +0100 Subject: [PATCH] added rtl_wavestream * minor fix(es) in waveread and rtl_wavestat Signed-off-by: hayati ayguen --- src/CMakeLists.txt | 8 +- src/convenience/waveread.c | 19 ++- src/convenience/waveread.h | 4 +- src/rtl_wavestat.c | 5 +- src/rtl_wavestream.c | 313 +++++++++++++++++++++++++++++++++++++ 5 files changed, 340 insertions(+), 9 deletions(-) create mode 100644 src/rtl_wavestream.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 480f337..5d3be1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -129,12 +129,13 @@ add_executable(rtl_adsb rtl_adsb.c) add_executable(rtl_power rtl_power.c) add_executable(rtl_raw2wav rtl_raw2wav.c convenience/convenience.c convenience/wavewrite.c) add_executable(rtl_wavestat rtl_wavestat.c convenience/convenience.c convenience/waveread.c) +add_executable(rtl_wavestream rtl_wavestream.c convenience/convenience.c convenience/waveread.c) if (WITH_RPC) add_executable(rtl_rpcd rtl_rpcd.c rtlsdr_rpc_msg.c) - set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_udp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_raw2wav rtl_wavestat rtl_rpcd) + set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_udp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_raw2wav rtl_wavestat rtl_wavestream rtl_rpcd) else() - set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_udp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_raw2wav rtl_wavestat) + set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_udp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_raw2wav rtl_wavestat rtl_wavestream) endif() target_link_libraries(rtl_sdr ${RTLSDR_TOOL_LIB} convenience_static @@ -187,6 +188,7 @@ if(UNIX) target_link_libraries(rtl_power m) target_link_libraries(rtl_raw2wav m) target_link_libraries(rtl_wavestat m) + target_link_libraries(rtl_wavestream m) if(APPLE) target_link_libraries(rtl_test m) else() @@ -206,6 +208,7 @@ if(WIN32) target_link_libraries(rtl_power libgetopt_static) target_link_libraries(rtl_raw2wav libgetopt_static m) target_link_libraries(rtl_wavestat libgetopt_static m) + target_link_libraries(rtl_wavestream libgetopt_static m) set_property(TARGET rtl_sdr APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_tcp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_udp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) @@ -217,6 +220,7 @@ if(WIN32) set_property(TARGET rtl_power APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_raw2wav APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_wavestat APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) + set_property(TARGET rtl_wavestream APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) if (WITH_RPC) target_link_libraries(rtl_rpcd ws2_32 libgetopt_static) set_property(TARGET rtl_rpcd APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) diff --git a/src/convenience/waveread.c b/src/convenience/waveread.c index 56d72fc..5d5e481 100644 --- a/src/convenience/waveread.c +++ b/src/convenience/waveread.c @@ -149,14 +149,23 @@ int waveReadHeader(FILE * f, uint32_t *srate, uint32_t *freq, int *bitsPerSampl if ( memcmp(waveHdr.d.hdr.ID, "data", 4 ) ) return 41; - smpSize = *numChannels * (*bitsPerSample + 7) / 8; + smpSize = (*bitsPerSample + 7) / 8; /* round up to next byte */ + smpSize *= *numChannels; *nFrames = waveHdr.d.hdr.size / smpSize; +#if 0 + fprintf(stderr, "riffSize = %lu\n", (unsigned long)waveHdr.r.hdr.size ); + fprintf(stderr, "dataSize = %lu\n", (unsigned long)waveHdr.d.hdr.size); + fprintf(stderr, "nBlockAlign = %d\n", (int)waveHdr.f.nBlockAlign); + fprintf(stderr, "smpSize = %d\n", (int)smpSize); + fprintf(stderr, "*nFrames = %lu\n", (unsigned long)(*nFrames) ); +#endif + return 0; } -int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData) +int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData, size_t *numRead) { size_t nw; switch (waveHdr.f.nBitsPerSample) @@ -167,6 +176,7 @@ int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanDa case 8: /* no endian conversion needed for single bytes */ nw = fread(vpData, sizeof(uint8_t), numSamples, f); + *numRead = nw; return (nw == numSamples) ? 0 : 1; case 16: /* TODO: endian conversion needed */ @@ -175,11 +185,12 @@ int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanDa { /* TODO: convert back endianness */ } + *numRead = nw; return (nw == numSamples) ? 0 : 1; } } -int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData) +int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData, size_t *numRead) { size_t nw; switch (waveHdr.f.nBitsPerSample) @@ -190,6 +201,7 @@ int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData case 8: /* no endian conversion needed for single bytes */ nw = fread(vpData, waveHdr.f.nChannels * sizeof(uint8_t), numFrames, f); + *numRead = nw; return (nw == numFrames) ? 0 : 1; case 16: /* TODO: endian conversion needed */ @@ -198,6 +210,7 @@ int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData { /* TODO: convert back endianness */ } + *numRead = nw; return (nw == numFrames) ? 0 : 1; } } diff --git a/src/convenience/waveread.h b/src/convenience/waveread.h index 24d57a9..67ee9d6 100644 --- a/src/convenience/waveread.h +++ b/src/convenience/waveread.h @@ -28,8 +28,8 @@ extern "C" { int waveReadHeader(FILE * f, uint32_t *samplerate, uint32_t *freq, int *bitsPerSample, int *numChannels , uint32_t *nFrames, int16_t *formatTag); -int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData); -int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData); /* returns 0, when no errors occured */ +int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData, size_t *numRead); +int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData, size_t *numRead); /* returns 0, when no errors occured */ void waveGetStartTime(time_t *tim, double *fraction); void waveGetStopTime(time_t *tim, double *fraction); diff --git a/src/rtl_wavestat.c b/src/rtl_wavestat.c index 581a4a4..c2c980d 100644 --- a/src/rtl_wavestat.c +++ b/src/rtl_wavestat.c @@ -71,6 +71,7 @@ void usage(void) "\t-t print start time in localtime: 'yyy-mm-ddThh:mm:dd.zzz'\n" "\t-d print file duration in frames (= num samples per channel)\n" "\t-D print file duration in seconds\n" + "\t-w input file\n" "\t-v verbose output\n" ); } @@ -251,12 +252,12 @@ int main(int argc, char **argv) } if ( printAll || printDurationSmp ) { if ( printFieldName ) - fprintf(stdout, "duration/smp:\t"); + fprintf(stdout, "duration/frames:\t"); fprintf(stdout, "%lu\n", (unsigned long)numFrames); } if ( printAll || printDurationTim ) { if ( printFieldName ) - fprintf(stdout, "duration/sec:\t"); + fprintf(stdout, "duration/secs:\t"); fprintf(stdout, "%f\n", (double)numFrames/(double)srate); } diff --git a/src/rtl_wavestream.c b/src/rtl_wavestream.c new file mode 100644 index 0000000..d51aff2 --- /dev/null +++ b/src/rtl_wavestream.c @@ -0,0 +1,313 @@ +/* + * rtl-wavestream, stream raw data (in specified sample format) + * Copyright (C) 2019 by Hayati Ayguen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 + #include + #include + #include + +#else + #include + #include + #include + #include + #include + + #include "getopt/getopt.h" + + #if defined(_MSC_VER) && (_MSC_VER < 1900) + #define snprintf _snprintf + #endif +#endif + +#include "convenience/convenience.h" +#include "convenience/waveread.h" + +#include "rtl_app_ver.h" + + +static volatile int do_exit = 0; +static int verbosity = 0; + +#define BLOCKLEN 65536 + +/* read up to 64K samples */ +static uint8_t inpBuffer[BLOCKLEN * sizeof(int32_t)]; +/* output is max 4 times bigger: uint8_t -> int32_t */ +static uint8_t outBuffer[BLOCKLEN * sizeof(int32_t) * sizeof(int32_t)]; + +void usage(void) +{ + fprintf(stderr, "rtl_wavestream, stream raw data (in specified format)\n"); + fprintf(stderr, "rtl_wavestream version %u.%u %s (%s)\n", + APP_VER_MAJOR, APP_VER_MINOR, + APP_VER_ID, __DATE__ ); + fprintf(stderr, + "Use:\trtl_wavestream [-options] \n" + "\t-f sample format for output. default = input format\n" + "\t-w input file\n" + "\t-v verbose output\n" ); +} + +#ifdef _WIN32 +BOOL WINAPI +sighandler(int signum) +{ + if (CTRL_C_EVENT == signum) { + fprintf(stderr, "Signal caught, exiting!\n"); + do_exit = 1; + return TRUE; + } + return FALSE; +} +#else +static void sighandler(int signum) +{ + fprintf(stderr, "Signal caught, exiting!\n"); + do_exit = 1; +} +#endif + + +int main(int argc, char **argv) +{ +#ifndef _WIN32 + struct sigaction sigact; +#endif + int r, opt; + const char * wavfilename = NULL; + const char * targetFmtStr = "pcm16"; + FILE * inpfile = NULL; + uint32_t freq = 0; + uint32_t srate = 0; + int nChan = 0; + int nBits = 0; + int targetFmt = 0; /* PCM16 = 0, FLOAT32 = 1 */ + int inputFmt = 0; /* PCM16 = 0, FLOAT32 = 1 */ + int16_t formatTag; + uint32_t numFrames; + time_t tim = 0; + double fraction = 0.0; + struct tm *ptm = NULL; + + while ((opt = getopt(argc, argv, "f:w:vh")) != -1) { + switch (opt) { + case 'f': targetFmtStr = optarg; break; + case 'w': wavfilename = optarg; break; + case 'v': ++verbosity; break; + case 'h': + case '?': + default: + usage(); + exit(1); + break; + } + } + + if (verbosity) + fprintf(stderr, "verbosity set to %d\n", verbosity); + + if (optind < argc) { + wavfilename = argv[optind]; + } + + if ( !strcmp(targetFmtStr, "pcm") || !strcmp(targetFmtStr, "pcm16") + || !strcmp(targetFmtStr, "PCM") || !strcmp(targetFmtStr, "PCM16") ) + { + targetFmt = 0; /* PCM16 = 0, FLOAT32 = 1 */ + if (verbosity) + fprintf(stderr, "target sample format: PCM16\n"); + } + else if ( !strcmp(targetFmtStr, "flt32") || !strcmp(targetFmtStr, "float32") || !strcmp(targetFmtStr, "float") + || !strcmp(targetFmtStr, "FLT32") || !strcmp(targetFmtStr, "FLOAT32") || !strcmp(targetFmtStr, "FLOAT") ) + { + targetFmt = 1; /* PCM16 = 0, FLOAT32 = 1 */ + if (verbosity) + fprintf(stderr, "target sample format: FLOAT32\n"); + } + else + { + fprintf(stderr, "Error: unsupported target format. accepting 'PCM16'/'PCM' or 'FLOAT32'/'FLOAT'\n"); + exit(1); + } + + +#ifndef _WIN32 + sigact.sa_handler = sighandler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGPIPE, &sigact, NULL); +#else + SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); +#endif + +#ifdef _WIN32 + _setmode(_fileno(stdout), _O_BINARY); +#endif + + if (!wavfilename) { + fprintf(stderr, "Error: No input file specified!\n"); + exit(1); + } else { + if (verbosity >= 2) + fprintf(stderr, "Opening input '%s'\n", wavfilename); + inpfile = fopen(wavfilename, "rb"); + if (!inpfile) { + fprintf(stderr, "Error: Failed to open input %s\n", wavfilename); + exit(1); + } + if (verbosity) + fprintf(stderr, "Opened '%s' for input\n", wavfilename); + } + + r = waveReadHeader(inpfile, &srate, &freq, &nBits, &nChan, &numFrames, &formatTag); + if ( r ) { + fprintf(stderr, "Error %d reading/evaluating wave file header\n", r); + } else if ( verbosity >= 2 ) { + fprintf(stderr, "Success reading/evaluating wave file header\n"); + } + + if ( verbosity ) { + fprintf(stderr, "frequency/Hz:\t%lu\n", (unsigned long)freq); + fprintf(stderr, "samplerate/Hz:\t%lu\n", (unsigned long)srate); + fprintf(stderr, "num_channels:\t%d\n", nChan); + fprintf(stderr, "bits_per_sample:\t%d\n", nBits); + if (formatTag == 0x0001) + fprintf(stderr, "sample_format:\t0x%04X\tPCM\n", (unsigned)formatTag); + else if (formatTag == 0x0003) + fprintf(stderr, "sample_format:\t0x%04X\tIEEE_FLOAT\n", (unsigned)formatTag); + else + fprintf(stderr, "sample_format:\t0x%04X\n", (unsigned)formatTag); + fprintf(stderr, "duration/frames:\t%lu\t0x%lX\n", (unsigned long)numFrames, (unsigned long)numFrames); + fprintf(stderr, "duration/secs:\t%f\n", (double)numFrames/(double)srate); + } + + if ( formatTag == 0x0001 && nBits == 16 ) { + inputFmt = 0; + if (verbosity) + fprintf(stderr, "input sample format: PCM16\n"); + } + else if ( formatTag == 0x0003 && nBits == 32 ) { + inputFmt = 1; + if (verbosity) + fprintf(stderr, "input sample format: FLOAT32\n"); + } + else + { + fprintf(stderr, "Error: unsupported input format. only 'PCM16' and 'FLOAT32' supported.\n"); + exit(1); + } + + { + void * pvInp = &inpBuffer[0]; + void * pvOut = &outBuffer[0]; + const size_t numFramesPerRead = BLOCKLEN / nChan; + const size_t numSmpPerRead = numFramesPerRead * nChan; + const size_t inpSmpSize = (inputFmt == 0) ? sizeof(int16_t) : sizeof(float); + const size_t outSmpSize = (targetFmt == 0) ? sizeof(int16_t) : sizeof(float); + size_t numSamples = numFrames * nChan; + size_t readTotal = 0; + size_t numRead; + + if ( verbosity ) + { + fprintf(stderr, "input sample size = %u\n", (unsigned)inpSmpSize); + fprintf(stderr, "output sample size = %u\n", (unsigned)outSmpSize); + fprintf(stderr, "samples per read = %u smp\n", (unsigned)numSmpPerRead); + } + + while ( !do_exit ) + { + const size_t numToRead = numSmpPerRead; + const size_t readErr = waveReadSamples(inpfile, pvInp, numToRead, 0, &numRead); + if ( numRead != numToRead ) + fprintf(stderr, "Error: reading %lu delivered %lu smp after %lu smp - left %lu frames\n" + , (unsigned long)numToRead, (unsigned long)numRead + , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); + if ( readErr ) + { + fprintf(stderr, "Error reading samples after %lu smp - left %lu frames\n" + , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); + } + else if ( verbosity >= 2 ) + fprintf(stderr, "read %lu samples: left frames: %lu\n" + , (unsigned long)numToRead, (unsigned long)numSamples); + if ( !numRead ) + break; + + if ( inputFmt == targetFmt ) { + size_t w = fwrite(pvInp, inpSmpSize, numRead, stdout); + if ( w != numRead ) { + fprintf(stderr, "Error writing read samples after %lu smp - left %lu frames\n" + , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); + break; + } + } + else if ( inputFmt == 0 && targetFmt == 1 ) { + const int16_t *ai = (const int16_t*)pvInp; + float * ao = (float*)pvOut; + size_t w; + for ( size_t k = 0; k < numRead; ++k ) + ao[k] = ai[k] * (1.0F / 32768.0F); + w = fwrite(pvOut, outSmpSize, numRead, stdout); + if ( w != numRead ) { + fprintf(stderr, "Error writing converted samples after %lu smp - left %lu frames\n" + , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); + break; + } + } + else if ( inputFmt == 1 && targetFmt == 0 ) { + const float *ai = (const float*)pvInp; + int16_t * ao = (int16_t*)pvOut; + size_t w; + for ( size_t k = 0; k < numRead; ++k ) + ao[k] = (int16_t)( ai[k] * 32768.0F ); + w = fwrite(pvOut, outSmpSize, numRead, stdout); + if ( w != numRead ) { + fprintf(stderr, "Error writing converted samples after %lu smp - left %lu frames\n" + , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); + break; + } + } + numSamples -= numRead; + readTotal += numRead; + } + + if ( verbosity ) + { + fprintf(stderr, "Written %lu samples in total - left %lu of %lu frames\n" + , (unsigned long)readTotal, (unsigned long)(numSamples / nChan), (unsigned long)(numFrames * nChan) ); + } + } + + fclose(inpfile); + + return 0; +} + +/* vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab */