From 9602f090afa5f8d5bb23c6f0a0bb34974d48e456 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 11 Jun 2022 19:27:47 +0300 Subject: [PATCH] ggwave : add default constructor --- include/ggwave/ggwave.h | 164 ++++++++++++++++++++++------------------ src/ggwave.cpp | 118 ++++++++++++++++------------- 2 files changed, 157 insertions(+), 125 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index e43c44f..789ec2c 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -520,13 +520,13 @@ public: // generated tones // - // Each Tone is the bin index of the tone frequency. - // For protocol p: - // - freq_hz = (p.freqStart + Tone) * hzPerSample - // - duration_ms = p.txDuration_ms(samplesPerFrame, sampleRate) + // Each Tone is the bin index of the tone frequency. + // For protocol p: + // - freq_hz = (p.freqStart + Tone) * hzPerSample + // - duration_ms = p.txDuration_ms(samplesPerFrame, sampleRate) // - // If the protocol is mono-tone, each element of the vector corresponds to a single tone. - // Otherwise, the tones within a single Tx are separated by value of -1 + // If the protocol is mono-tone, each element of the vector corresponds to a single tone. + // Otherwise, the tones within a single Tx are separated by value of -1 // using Tones = ggvector; @@ -537,51 +537,68 @@ public: using RecordedData = ggvector; using TxRxData = ggvector; - // constructor + // default constructor // - // All memory buffers used by the GGWave instance are allocated upon construction. - // No memory allocations occur after that. + // The GGWave object is not ready to use until you call prepare() + // No memory is allocated with this constructor. // - // The sizes of the buffers are determined by the parameters and the contents of: + GGWave() = default; + + // constructor with parameters // - // - GGWave::Protocols::rx() - // - GGWave::Protocols::tx() - // - // For optimal performance and minimum memory usage, make sure to enable only the - // Rx and Tx protocols that you need. - // - // For example, using a single protocol for Tx is achieved like this: - // - // Parameters parameters; - // parameters.operatingMode = GGWAVE_OPERATING_MODE_TX; - // GGWave::Protocols::tx().only(GGWave::ProtocolId::GGWAVE_PROTOCOL_AUDIBLE_NORMAL); - // GGWave instance(parameters); - // instance.init(...); - // instance.encode(); - // - // The created instance will only be able to transmit data using the "Normal" - // protocol. Rx will be disabled. - // - // To create a corresponding Rx-only instance, use the following: - // - // Parameters parameters; - // parameters.operatingMode = GGWAVE_OPERATING_MODE_RX; - // GGWave::Protocols::rx().only(GGWave::ProtocolId::GGWAVE_PROTOCOL_AUDIBLE_NORMAL); - // GGWave instance(parameters); - // instance.decode(...); + // Construct and prepare the GGWave object using the given parameters. + // The constructor calls prepare() for you. // GGWave(const Parameters & parameters); + ~GGWave(); - bool prepare(const Parameters & parameters); - bool alloc(void * p, int & n); + // prepare the GGWave object + // + // All memory buffers used by the GGWave instance are allocated with this function. + // No memory allocations occur after that. + // + // The sizes of the buffers are determined by the parameters and the contents of: + // + // - GGWave::Protocols::rx() + // - GGWave::Protocols::tx() + // + // For optimal performance and minimum memory usage, make sure to enable only the + // Rx and Tx protocols that you need. + // + // For example, using a single protocol for Tx is achieved like this: + // + // Parameters parameters; + // parameters.operatingMode = GGWAVE_OPERATING_MODE_TX; + // GGWave::Protocols::tx().only(GGWave::ProtocolId::GGWAVE_PROTOCOL_AUDIBLE_NORMAL); + // GGWave instance(parameters); + // instance.init(...); + // instance.encode(); + // + // The created instance will only be able to transmit data using the "Normal" + // protocol. Rx will be disabled. + // + // To create a corresponding Rx-only instance, use the following: + // + // Parameters parameters; + // parameters.operatingMode = GGWAVE_OPERATING_MODE_RX; + // GGWave::Protocols::rx().only(GGWave::ProtocolId::GGWAVE_PROTOCOL_AUDIBLE_NORMAL); + // GGWave instance(parameters); + // instance.decode(...); + // + // If "allocate" is false, the memory buffers are not allocated and only the required size + // is computed. This is useful if you want to just see how much memory is needed for the + // specific set of parameters and protocols. Do not use this function after you have already + // prepared the instance. Instead, use the heapSize() method to see how much memory is used. + // + bool prepare(const Parameters & parameters, bool allocate = true); // 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. + // 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 + // Note: not thread-safe. Do not call while any GGWave instances are running // static void setLogFile(FILE * fptr); @@ -589,10 +606,10 @@ public: // set Tx data to encode // - // This prepares the GGWave instance for transmission. - // To perform the actual encoding, the encode() method must be called + // This prepares the GGWave instance for transmission. + // To perform the actual encoding, the encode() method must be called // - // returns false upon invalid parameters or failure to initialize + // returns false upon invalid parameters or failure to initialize // bool init(const char * text, TxProtocolId protocolId, const int volume = kDefaultVolume); bool init(int dataSize, const char * dataBuffer, TxProtocolId protocolId, const int volume = kDefaultVolume); @@ -765,15 +782,17 @@ public: struct State { int nSamplesTotal = 0; - int timeInt = 0; - int timeLast = 0; - double timeNow = 0.0; + int timeInt = 0; + int timeLast = 0; + double timeNow = 0.0; }; State m_state; }; private: + bool alloc(void * p, int & n); + void decode_fixed(); void decode_variable(); @@ -784,36 +803,37 @@ private: double bitFreq(const Protocol & p, int bit) const; - const float m_sampleRateInp; - const float m_sampleRateOut; - const float m_sampleRate; - const int m_samplesPerFrame; - const float m_isamplesPerFrame; - const int m_sampleSizeInp; - const int m_sampleSizeOut; - const SampleFormat m_sampleFormatInp; - const SampleFormat m_sampleFormatOut; + // initialized via prepare() + float m_sampleRateInp = -1.0f; + float m_sampleRateOut = -1.0f; + float m_sampleRate = -1.0f; + int m_samplesPerFrame = -1; + float m_isamplesPerFrame = -1.0f; + int m_sampleSizeInp = -1; + int m_sampleSizeOut = -1; + SampleFormat m_sampleFormatInp = GGWAVE_SAMPLE_FORMAT_UNDEFINED; + SampleFormat m_sampleFormatOut = GGWAVE_SAMPLE_FORMAT_UNDEFINED; - const float m_hzPerSample; - const float m_ihzPerSample; + float m_hzPerSample = -1.0f; + float m_ihzPerSample = -1.0f; - const int m_freqDelta_bin; - const float m_freqDelta_hz; + int m_freqDelta_bin = -1; + float m_freqDelta_hz = -1.0f; - const int m_nBitsInMarker; - const int m_nMarkerFrames; - const int m_encodedDataOffset; + int m_nBitsInMarker = -1; + int m_nMarkerFrames = -1; + int m_encodedDataOffset = -1; - const float m_soundMarkerThreshold; + float m_soundMarkerThreshold = -1.0f; - const bool m_isFixedPayloadLength; - const int m_payloadLength; + bool m_isFixedPayloadLength = false; + int m_payloadLength = -1; - const bool m_isRxEnabled; - const bool m_isTxEnabled; - const bool m_needResampling; - const bool m_txOnlyTones; - const bool m_isDSSEnabled; + bool m_isRxEnabled = false; + bool m_isTxEnabled = false; + bool m_needResampling = false; + bool m_txOnlyTones = false; + bool m_isDSSEnabled = false; // common TxRxData m_dataEncoded; @@ -900,8 +920,8 @@ private: mutable Resampler m_resampler; - void * m_heap; - int m_heapSize; + void * m_heap = nullptr; + int m_heapSize = 0; }; #endif diff --git a/src/ggwave.cpp b/src/ggwave.cpp index c1b0714..1c2fe10 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -433,57 +433,7 @@ void ggalloc(ggmatrix & v, int n, int m, void * buf, int & bufSize) { // GGWave // -GGWave::GGWave(const Parameters & parameters) : - m_sampleRateInp (parameters.sampleRateInp), - m_sampleRateOut (parameters.sampleRateOut), - m_sampleRate (parameters.sampleRate), - m_samplesPerFrame (parameters.samplesPerFrame), - m_isamplesPerFrame (1.0f/m_samplesPerFrame), - m_sampleSizeInp (bytesForSampleFormat(parameters.sampleFormatInp)), - m_sampleSizeOut (bytesForSampleFormat(parameters.sampleFormatOut)), - m_sampleFormatInp (parameters.sampleFormatInp), - m_sampleFormatOut (parameters.sampleFormatOut), - m_hzPerSample (m_sampleRate/m_samplesPerFrame), - m_ihzPerSample (1.0f/m_hzPerSample), - m_freqDelta_bin (1), - m_freqDelta_hz (2*m_hzPerSample), - m_nBitsInMarker (16), - m_nMarkerFrames (parameters.payloadLength > 0 ? 0 : kDefaultMarkerFrames), - m_encodedDataOffset (parameters.payloadLength > 0 ? 0 : kDefaultEncodedDataOffset), - m_soundMarkerThreshold(parameters.soundMarkerThreshold), - m_isFixedPayloadLength(parameters.payloadLength > 0), - m_payloadLength (parameters.payloadLength), - m_isRxEnabled (parameters.operatingMode & GGWAVE_OPERATING_MODE_RX), - m_isTxEnabled (parameters.operatingMode & GGWAVE_OPERATING_MODE_TX), - m_needResampling (m_sampleRateInp != m_sampleRate || m_sampleRateOut != m_sampleRate), - m_txOnlyTones (parameters.operatingMode & GGWAVE_OPERATING_MODE_TX_ONLY_TONES), - m_isDSSEnabled (parameters.operatingMode & GGWAVE_OPERATING_MODE_USE_DSS) { - - if (m_sampleSizeInp == 0) { - ggprintf("Invalid or unsupported capture sample format: %d\n", (int) parameters.sampleFormatInp); - return; - } - - if (m_sampleSizeOut == 0) { - ggprintf("Invalid or unsupported playback sample format: %d\n", (int) parameters.sampleFormatOut); - return; - } - - if (parameters.samplesPerFrame > kMaxSamplesPerFrame) { - ggprintf("Invalid samples per frame: %d, max: %d\n", parameters.samplesPerFrame, kMaxSamplesPerFrame); - return; - } - - if (m_sampleRateInp < kSampleRateMin) { - ggprintf("Error: capture sample rate (%g Hz) must be >= %g Hz\n", m_sampleRateInp, kSampleRateMin); - return; - } - - if (m_sampleRateInp > kSampleRateMax) { - ggprintf("Error: capture sample rate (%g Hz) must be <= %g Hz\n", m_sampleRateInp, kSampleRateMax); - return; - } - +GGWave::GGWave(const Parameters & parameters) { prepare(parameters); } @@ -493,8 +443,66 @@ GGWave::~GGWave() { } } -bool GGWave::prepare(const Parameters & parameters) { - // TODO: initialize members from parameters +bool GGWave::prepare(const Parameters & parameters, bool allocate) { + if (m_heap) { + free(m_heap); + m_heap = nullptr; + m_heapSize = 0; + } + + // parameter initialization: + + m_sampleRateInp = parameters.sampleRateInp; + m_sampleRateOut = parameters.sampleRateOut; + m_sampleRate = parameters.sampleRate; + m_samplesPerFrame = parameters.samplesPerFrame; + m_isamplesPerFrame = 1.0f/m_samplesPerFrame; + m_sampleSizeInp = bytesForSampleFormat(parameters.sampleFormatInp); + m_sampleSizeOut = bytesForSampleFormat(parameters.sampleFormatOut); + m_sampleFormatInp = parameters.sampleFormatInp; + m_sampleFormatOut = parameters.sampleFormatOut; + m_hzPerSample = m_sampleRate/m_samplesPerFrame; + m_ihzPerSample = 1.0f/m_hzPerSample; + m_freqDelta_bin = 1; + m_freqDelta_hz = 2*m_hzPerSample; + m_nBitsInMarker = 16; + m_nMarkerFrames = parameters.payloadLength > 0 ? 0 : kDefaultMarkerFrames; + m_encodedDataOffset = parameters.payloadLength > 0 ? 0 : kDefaultEncodedDataOffset; + m_soundMarkerThreshold = parameters.soundMarkerThreshold; + m_isFixedPayloadLength = parameters.payloadLength > 0; + m_payloadLength = parameters.payloadLength; + m_isRxEnabled = parameters.operatingMode & GGWAVE_OPERATING_MODE_RX; + m_isTxEnabled = parameters.operatingMode & GGWAVE_OPERATING_MODE_TX; + m_needResampling = m_sampleRateInp != m_sampleRate || m_sampleRateOut != m_sampleRate; + m_txOnlyTones = parameters.operatingMode & GGWAVE_OPERATING_MODE_TX_ONLY_TONES; + m_isDSSEnabled = parameters.operatingMode & GGWAVE_OPERATING_MODE_USE_DSS; + + if (m_sampleSizeInp == 0) { + ggprintf("Invalid or unsupported capture sample format: %d\n", (int) parameters.sampleFormatInp); + return false; + } + + if (m_sampleSizeOut == 0) { + ggprintf("Invalid or unsupported playback sample format: %d\n", (int) parameters.sampleFormatOut); + return false; + } + + if (parameters.samplesPerFrame > kMaxSamplesPerFrame) { + ggprintf("Invalid samples per frame: %d, max: %d\n", parameters.samplesPerFrame, kMaxSamplesPerFrame); + return false; + } + + if (m_sampleRateInp < kSampleRateMin) { + ggprintf("Error: capture sample rate (%g Hz) must be >= %g Hz\n", m_sampleRateInp, kSampleRateMin); + return false; + } + + if (m_sampleRateInp > kSampleRateMax) { + ggprintf("Error: capture sample rate (%g Hz) must be <= %g Hz\n", m_sampleRateInp, kSampleRateMax); + return false; + } + + // memory allocation: m_heap = nullptr; m_heapSize = 0; @@ -504,6 +512,10 @@ bool GGWave::prepare(const Parameters & parameters) { return false; } + if (allocate == false) { + return true; + } + const auto heapSize0 = m_heapSize; m_heap = calloc(m_heapSize, 1);