From 9cf2d476b808b4ebc572f75aa30cbb4355232efd Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Tue, 21 Sep 2021 06:48:16 +0300 Subject: [PATCH] Add interface for changing ggwave's internal logging (#52) * ggwave : add interface for changing ggwave's internal logging Using GGWave::setLogFile() it is now possible to change the log file used internally by ggwave, or disable it all together. * ggwave : add comments about thread-safety of setLogFile --- include/ggwave/ggwave.h | 26 ++++++++++++++++++++ src/ggwave.cpp | 53 +++++++++++++++++++++++++---------------- tests/test-ggwave.c | 3 +++ 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 5753993..3dac5fc 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -90,6 +90,23 @@ extern "C" { // the python module and unfortunately had to do it this way typedef int ggwave_Instance; + // Change file stream for internal ggwave logging. NULL - disable logging + // + // Intentionally passing it as void * instead of FILE * to avoid including a header + // + // // log to standard error + // ggwave_setLogFile(stderr); + // + // // log to standard output + // ggwave_setLogFile(stdout); + // + // // disable logging + // ggwave_setLogFile(NULL); + // + // Note: not thread-safe. Do not call while any GGWave instances are running + // + GGWAVE_API void ggwave_setLogFile(void * fptr); + // Helper method to get default instance parameters GGWAVE_API ggwave_Parameters ggwave_getDefaultParameters(void); @@ -306,6 +323,15 @@ public: GGWave(const Parameters & parameters); ~GGWave(); + // set file stream for the internal ggwave logging + // + // By default, ggwave prints internal log messages to stderr. + // To disable logging all together, call this method with nullptr. + // + // Note: not thread-safe. Do not call while any GGWave instances are running + // + static void setLogFile(FILE * fptr); + static const Parameters & getDefaultParameters(); // set Tx data to encode diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 2a958fe..acdb5cf 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -15,14 +15,23 @@ #define M_PI 3.14159265358979323846 #endif +#define ggprintf(...) \ + g_fptr && fprintf(g_fptr, __VA_ARGS__) + // // C interface // namespace { +FILE * g_fptr = stderr; std::map g_instances; } +extern "C" +void ggwave_setLogFile(void * fptr) { + GGWave::setLogFile((FILE *) fptr); +} + extern "C" ggwave_Parameters ggwave_getDefaultParameters(void) { return GGWave::getDefaultParameters(); @@ -62,12 +71,12 @@ int ggwave_encode( GGWave * ggWave = (GGWave *) g_instances[instance]; if (ggWave == nullptr) { - fprintf(stderr, "Invalid GGWave instance %d\n", instance); + ggprintf("Invalid GGWave instance %d\n", instance); return -1; } if (ggWave->init(dataSize, dataBuffer, ggWave->getTxProtocol(txProtocolId), volume) == false) { - fprintf(stderr, "Failed to initialize GGWave instance %d\n", instance); + ggprintf("Failed to initialize GGWave instance %d\n", instance); return -1; } @@ -89,7 +98,7 @@ int ggwave_encode( }; if (ggWave->encode(cbWaveformOut) == false) { - fprintf(stderr, "Failed to encode data - GGWave instance %d\n", instance); + ggprintf("Failed to encode data - GGWave instance %d\n", instance); return -1; } @@ -263,7 +272,7 @@ int bytesForSampleFormat(GGWave::SampleFormat sampleFormat) { case GGWAVE_SAMPLE_FORMAT_F32: return sizeof(float); break; }; - fprintf(stderr, "Invalid sample format: %d\n", (int) sampleFormat); + ggprintf("Invalid sample format: %d\n", (int) sampleFormat); return 0; } @@ -274,6 +283,10 @@ struct GGWave::Impl { Resampler resampler; }; +void GGWave::setLogFile(FILE * fptr) { + g_fptr = fptr; +} + const GGWave::Parameters & GGWave::getDefaultParameters() { static ggwave_Parameters result { -1, // vaiable payload length @@ -370,12 +383,12 @@ GGWave::GGWave(const Parameters & parameters) : } if (m_sampleRateInp < kSampleRateMin) { - fprintf(stderr, "Error: capture sample rate (%g Hz) must be >= %g Hz\n", m_sampleRateInp, kSampleRateMin); + ggprintf("Error: capture sample rate (%g Hz) must be >= %g Hz\n", m_sampleRateInp, kSampleRateMin); throw std::runtime_error("Invalid capture/playback sample rate"); } if (m_sampleRateInp > kSampleRateMax) { - fprintf(stderr, "Error: capture sample rate (%g Hz) must be <= %g Hz\n", m_sampleRateInp, kSampleRateMax); + ggprintf("Error: capture sample rate (%g Hz) must be <= %g Hz\n", m_sampleRateInp, kSampleRateMax); throw std::runtime_error("Invalid capture/playback sample rate"); } @@ -399,18 +412,18 @@ bool GGWave::init(int dataSize, const char * dataBuffer, const int volume) { bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & txProtocol, const int volume) { if (dataSize < 0) { - fprintf(stderr, "Negative data size: %d\n", dataSize); + ggprintf("Negative data size: %d\n", dataSize); return false; } auto maxLength = m_isFixedPayloadLength ? m_payloadLength : kMaxLengthVarible; if (dataSize > maxLength) { - fprintf(stderr, "Truncating data from %d to %d bytes\n", dataSize, maxLength); + ggprintf("Truncating data from %d to %d bytes\n", dataSize, maxLength); dataSize = maxLength; } if (volume < 0 || volume > 100) { - fprintf(stderr, "Invalid volume: %d\n", volume); + ggprintf("Invalid volume: %d\n", volume); return false; } @@ -740,14 +753,14 @@ void GGWave::decode(const CBWaveformInp & cbWaveformInp) { } if (nBytesRecorded % m_sampleSizeBytesInp != 0) { - fprintf(stderr, "Failure during capture - provided bytes (%d) are not multiple of sample size (%d)\n", + ggprintf("Failure during capture - provided bytes (%d) are not multiple of sample size (%d)\n", nBytesRecorded, m_sampleSizeBytesInp); m_samplesNeeded = m_samplesPerFrame; break; } if (nBytesRecorded > nBytesNeeded) { - fprintf(stderr, "Failure during capture - more samples were provided (%d) than requested (%d)\n", + ggprintf("Failure during capture - more samples were provided (%d) than requested (%d)\n", nBytesRecorded/m_sampleSizeBytesInp, nBytesNeeded/m_sampleSizeBytesInp); m_samplesNeeded = m_samplesPerFrame; break; @@ -891,7 +904,7 @@ bool GGWave::takeRxAmplitude(AmplitudeData & dst) { bool GGWave::computeFFTR(const float * src, float * dst, int N, float d) { if (N > kMaxSamplesPerFrame) { - fprintf(stderr, "computeFFTR: N (%d) must be <= %d\n", N, GGWave::kMaxSamplesPerFrame); + ggprintf("computeFFTR: N (%d) must be <= %d\n", N, GGWave::kMaxSamplesPerFrame); return false; } @@ -948,7 +961,7 @@ void GGWave::decode_variable() { } if (m_analyzingData) { - fprintf(stderr, "Analyzing captured data ..\n"); + ggprintf("Analyzing captured data ..\n"); auto tStart = std::chrono::high_resolution_clock::now(); const int stepsPerFrame = 16; @@ -1057,8 +1070,8 @@ void GGWave::decode_variable() { if (m_rxData[0] != 0) { std::string s((char *) m_rxData.data(), decodedLength); - fprintf(stderr, "Decoded length = %d, protocol = '%s' (%d)\n", decodedLength, rxProtocol.name, rxProtocolId); - fprintf(stderr, "Received sound data successfully: '%s'\n", s.c_str()); + ggprintf("Decoded length = %d, protocol = '%s' (%d)\n", decodedLength, rxProtocol.name, rxProtocolId); + ggprintf("Received sound data successfully: '%s'\n", s.c_str()); isValid = true; m_hasNewRxData = true; @@ -1081,7 +1094,7 @@ void GGWave::decode_variable() { m_framesToRecord = 0; if (isValid == false) { - fprintf(stderr, "Failed to capture sound data. Please try again (length = %d)\n", m_rxData[0]); + ggprintf("Failed to capture sound data. Please try again (length = %d)\n", m_rxData[0]); m_lastRxDataLength = -1; m_framesToRecord = -1; } @@ -1095,7 +1108,7 @@ void GGWave::decode_variable() { m_framesLeftToAnalyze = 0; auto tEnd = std::chrono::high_resolution_clock::now(); - fprintf(stderr, "Time to analyze: %g ms\n", getTime_ms(tStart, tEnd)); + ggprintf("Time to analyze: %g ms\n", getTime_ms(tStart, tEnd)); } // check if receiving data @@ -1134,7 +1147,7 @@ void GGWave::decode_variable() { if (isReceiving) { std::time_t timestamp = std::time(nullptr); - fprintf(stderr, "%sReceiving sound data ...\n", std::asctime(std::localtime(×tamp))); + ggprintf("%sReceiving sound data ...\n", std::asctime(std::localtime(×tamp))); m_receivingData = true; std::fill(m_rxData.begin(), m_rxData.end(), 0); @@ -1183,7 +1196,7 @@ void GGWave::decode_variable() { if (isEnded && m_framesToRecord > 1) { std::time_t timestamp = std::time(nullptr); m_recvDuration_frames -= m_framesLeftToRecord - 1; - fprintf(stderr, "%sReceived end marker. Frames left = %d, recorded = %d\n", std::asctime(std::localtime(×tamp)), m_framesLeftToRecord, m_recvDuration_frames); + ggprintf("%sReceived end marker. Frames left = %d, recorded = %d\n", std::asctime(std::localtime(×tamp)), m_framesLeftToRecord, m_recvDuration_frames); m_nMarkersSuccess = 0; m_framesLeftToRecord = 1; } @@ -1321,7 +1334,7 @@ void GGWave::decode_fixed() { if (rsData.Decode(m_txDataEncoded.data(), m_rxData.data()) == 0) { if (m_rxData[0] != 0) { - fprintf(stderr, "Received sound data successfully: '%s'\n", m_rxData.data()); + ggprintf("Received sound data successfully: '%s'\n", m_rxData.data()); isValid = true; m_hasNewRxData = true; diff --git a/tests/test-ggwave.c b/tests/test-ggwave.c index daa23f4..ebc756f 100644 --- a/tests/test-ggwave.c +++ b/tests/test-ggwave.c @@ -14,6 +14,9 @@ #define CHECK_F(cond) CHECK(!(cond)) int main() { + //ggwave_setLogFile(NULL); // disable logging + ggwave_setLogFile(stdout); + ggwave_Parameters parameters = ggwave_getDefaultParameters(); parameters.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16; parameters.sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I16;