From ca84180f222fdd64fd35afe60c5bfdc4c498dba9 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 11 Jun 2022 19:52:25 +0300 Subject: [PATCH] ggwave : add default constructor --- examples/arduino-rx-web/arduino-rx-web.cpp | 2 +- examples/arduino-rx/arduino-rx.ino | 28 +++--- examples/arduino-tx2/arduino-tx2.ino | 10 +- examples/ggwave-common-sdl2.cpp | 2 +- examples/ggwave-to-file/main.cpp | 2 +- examples/r2t2/r2t2-rx.cpp | 2 +- include/ggwave/ggwave.h | 105 ++++++++++++--------- src/ggwave.cpp | 47 +++++---- tests/test-ggwave.cpp | 6 +- 9 files changed, 105 insertions(+), 99 deletions(-) diff --git a/examples/arduino-rx-web/arduino-rx-web.cpp b/examples/arduino-rx-web/arduino-rx-web.cpp index b798353..954a793 100644 --- a/examples/arduino-rx-web/arduino-rx-web.cpp +++ b/examples/arduino-rx-web/arduino-rx-web.cpp @@ -293,7 +293,7 @@ bool GGWave_mainLoop() { SDL_PauseAudioDevice(g_devIdInp, SDL_TRUE); const auto nBytes = g_ggWave->encode(); - SDL_QueueAudio(g_devIdOut, g_ggWave->txData(), nBytes); + SDL_QueueAudio(g_devIdOut, g_ggWave->txWaveform(), nBytes); } return true; diff --git a/examples/arduino-rx/arduino-rx.ino b/examples/arduino-rx/arduino-rx.ino index d192c2f..74699ec 100644 --- a/examples/arduino-rx/arduino-rx.ino +++ b/examples/arduino-rx/arduino-rx.ino @@ -5,28 +5,29 @@ const int kPinSpeaker = 10; using TSample = int16_t; -static const size_t kSampleSize_bytes = sizeof(TSample); +const size_t kSampleSize_bytes = sizeof(TSample); // default number of output channels -static const char channels = 1; +const char channels = 1; // default PCM output frequency -static const int frequency = 6000; -static const int samplesPerFrame = 128; +const int frequency = 6000; +const int samplesPerFrame = 128; -static const int qpow = 9; -static const int qmax = 1 << qpow; +const int qpow = 9; +const int qmax = 1 << qpow; volatile int qhead = 0; volatile int qtail = 0; volatile int qsize = 0; -// Buffer to read samples into, each sample is 16-bits +// buffer to read samples into, each sample is 16-bits TSample sampleBuffer[qmax]; volatile int err = 0; -GGWave * g_ggwave = nullptr; +// global GGwave instance +GGWave ggwave; // helper function to output the generated GGWave waveform via a buzzer void send_text(GGWave & ggwave, uint8_t pin, const char * text, GGWave::TxProtocolId protocolId) { @@ -57,6 +58,8 @@ void setup() { Serial.println(F("Trying to create ggwave instance")); + ggwave.setLogFile(nullptr); + auto p = GGWave::getDefaultParameters(); p.payloadLength = 16; @@ -84,17 +87,12 @@ void setup() { GGWave::Protocols::rx().toggle(GGWAVE_PROTOCOL_MT_FAST, true); GGWave::Protocols::rx().toggle(GGWAVE_PROTOCOL_MT_FASTEST, true); - delay(1000); - static GGWave ggwave(p); + ggwave.prepare(p); Serial.println(ggwave.heapSize()); delay(1000); - ggwave.setLogFile(nullptr); - - g_ggwave = &ggwave; - Serial.println(F("Instance initialized")); // Configure the data receive callback @@ -115,8 +113,6 @@ void setup() { } void loop() { - auto & ggwave = *g_ggwave; - int nr = 0; int niter = 0; diff --git a/examples/arduino-tx2/arduino-tx2.ino b/examples/arduino-tx2/arduino-tx2.ino index 3cc452c..e5288c1 100644 --- a/examples/arduino-tx2/arduino-tx2.ino +++ b/examples/arduino-tx2/arduino-tx2.ino @@ -8,7 +8,8 @@ const int kPinButton1 = 4; const int samplesPerFrame = 128; const int sampleRate = 6000; -GGWave * g_ggwave = nullptr; +// global GGwave instance +GGWave ggwave; char txt[64]; #define P(str) (strcpy_P(txt, PSTR(str)), txt) @@ -36,6 +37,7 @@ void send_text(GGWave & ggwave, uint8_t pin, const char * text, GGWave::TxProtoc void setup() { Serial.begin(57600); + while (!Serial); pinMode(kPinLed0, OUTPUT); pinMode(kPinSpeaker, OUTPUT); @@ -55,12 +57,10 @@ void setup() { p.operatingMode = (ggwave_OperatingMode) (GGWAVE_OPERATING_MODE_TX | GGWAVE_OPERATING_MODE_TX_ONLY_TONES | GGWAVE_OPERATING_MODE_USE_DSS); GGWave::Protocols::tx().only(GGWAVE_PROTOCOL_MT_FASTEST); - static GGWave ggwave(p); + ggwave.prepare(p); ggwave.setLogFile(nullptr); Serial.println(ggwave.heapSize()); - g_ggwave = &ggwave; - Serial.println(F("Instance initialized")); } @@ -68,8 +68,6 @@ int pressed = 0; bool isDown = false; void loop() { - auto & ggwave = *g_ggwave; - delay(1000); digitalWrite(kPinLed0, HIGH); diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index bf7da04..fc1002d 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -275,7 +275,7 @@ bool GGWave_mainLoop() { SDL_PauseAudioDevice(g_devIdInp, SDL_TRUE); const auto nBytes = g_ggWave->encode(); - SDL_QueueAudio(g_devIdOut, g_ggWave->txData(), nBytes); + SDL_QueueAudio(g_devIdOut, g_ggWave->txWaveform(), nBytes); } return true; diff --git a/examples/ggwave-to-file/main.cpp b/examples/ggwave-to-file/main.cpp index 9b92c61..3f8dc1d 100644 --- a/examples/ggwave-to-file/main.cpp +++ b/examples/ggwave-to-file/main.cpp @@ -102,7 +102,7 @@ int main(int argc, char** argv) { } std::vector bufferPCM(nBytes); - std::memcpy(bufferPCM.data(), ggWave.txData(), nBytes); + std::memcpy(bufferPCM.data(), ggWave.txWaveform(), nBytes); fprintf(stderr, "Output size = %d bytes\n", (int) bufferPCM.size()); diff --git a/examples/r2t2/r2t2-rx.cpp b/examples/r2t2/r2t2-rx.cpp index 7d00b0b..cc4ceb0 100644 --- a/examples/r2t2/r2t2-rx.cpp +++ b/examples/r2t2/r2t2-rx.cpp @@ -283,7 +283,7 @@ bool GGWave_mainLoop() { SDL_PauseAudioDevice(g_devIdInp, SDL_TRUE); const auto nBytes = g_ggWave->encode(); - SDL_QueueAudio(g_devIdOut, g_ggWave->txData(), nBytes); + SDL_QueueAudio(g_devIdOut, g_ggWave->txWaveform(), nBytes); } return true; diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 789ec2c..15238d3 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -518,9 +518,9 @@ public: using Tone = int8_t; - // generated tones + // Tone data structure // - // Each Tone is the bin index of the tone frequency. + // Each Tone element is the bin index of the tone frequency. // For protocol p: // - freq_hz = (p.freqStart + Tone) * hzPerSample // - duration_ms = p.txDuration_ms(samplesPerFrame, sampleRate) @@ -537,27 +537,32 @@ public: using RecordedData = ggvector; using TxRxData = ggvector; - // default constructor + // Default constructor // // The GGWave object is not ready to use until you call prepare() // No memory is allocated with this constructor. // GGWave() = default; - // constructor with parameters + // Constructor with parameters // // Construct and prepare the GGWave object using the given parameters. - // The constructor calls prepare() for you. + // This constructor calls prepare() for you. // GGWave(const Parameters & parameters); ~GGWave(); - // prepare the GGWave object + // Prepare the GGWave object // // All memory buffers used by the GGWave instance are allocated with this function. // No memory allocations occur after that. // + // Call this method if you used the default constructor. + // Do not call this method if you used the constructor with parameters. + // + // The encode() and decode() methods will not work until this method is called. + // // The sizes of the buffers are determined by the parameters and the contents of: // // - GGWave::Protocols::rx() @@ -593,7 +598,7 @@ public: // bool prepare(const Parameters & parameters, bool allocate = true); - // set file stream for the internal ggwave logging + // 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. @@ -604,53 +609,55 @@ public: static const Parameters & getDefaultParameters(); - // set Tx data to encode + // Set Tx data to encode into sound // // This prepares the GGWave instance for transmission. - // To perform the actual encoding, the encode() method must be called + // To perform the actual encoding, call the encode() method. // - // returns false upon invalid parameters or failure to initialize + // Returns false upon invalid parameters or failure to initialize the transmission // bool init(const char * text, TxProtocolId protocolId, const int volume = kDefaultVolume); bool init(int dataSize, const char * dataBuffer, TxProtocolId protocolId, const int volume = kDefaultVolume); - // expected waveform size of the encoded Tx data in bytes + // Expected waveform size of the encoded Tx data in bytes // - // When the output sampling rate is not equal to operating sample rate the result of this method is overestimation of - // the actual number of bytes that would be produced + // When the output sampling rate is not equal to operating sample rate the result of this method is overestimation + // of the actual number of bytes that would be produced // uint32_t encodeSize_bytes() const; - // expected waveform size of the encoded Tx data in samples + // Expected waveform size of the encoded Tx data in samples // - // When the output sampling rate is not equal to operating sample rate the result of this method is overestimation of - // the actual number of samples that would be produced + // When the output sampling rate is not equal to operating sample rate the result of this method is overestimation + // of the actual number of samples that would be produced // uint32_t encodeSize_samples() const; - // encode Tx data into an audio waveform + // Encode Tx data into an audio waveform // - // After calling this method, the generated waveform is available through the txData() method + // After calling this method, use the Tx methods to get the encoded audio data. // - // returns the number of bytes in the generated waveform + // The generated waveform is available through the txWaveform() method + // The tone frequencies are available through the txTones() method + // + // Returns the number of bytes in the generated waveform // uint32_t encode(); - const void * txData() const; - - // decode an audio waveform + // Decode an audio waveform // // data - pointer to the waveform data // nBytes - number of bytes in the waveform // + // The samples pointed to by "data" should be in the format given by sampleFormatInp(). // After calling this method, use the Rx methods to check if any data was decoded successfully. // - // returns false if the provided waveform is somehow invalid + // Returns false if the provided waveform is somehow invalid // bool decode(const void * data, uint32_t nBytes); // - // instance state + // Instance state // bool isDSSEnabled() const; @@ -659,7 +666,7 @@ public: int sampleSizeInp() const; int sampleSizeOut() const; - float hzPerSample() const; + float hzPerSample() const; float sampleRateInp() const; float sampleRateOut() const; SampleFormat sampleFormatInp() const; @@ -671,21 +678,27 @@ public: // Tx // - // get a list of the tones generated for the last waveform + // Get the generated Wavform samples for the last encode() call // - // Call this method after calling encode() to get a list of the tones - // participating in the generated waveform + // Call this method after calling encode() to get the generated waveform. The format of the samples pointed to by + // the returned pointer is determined by the sampleFormatOut() method. + // + const void * txWaveform() const; + + // Get a list of the tones generated for the last encode() call + // + // Call this method after calling encode() to get a list of the tones participating in the generated waveform // const Tones txTones() const; // true if there is data pending to be transmitted bool txHasData() const; - // consume the amplitude data from the last generated waveform + // Consume the amplitude data from the last generated waveform bool txTakeAmplitudeI16(AmplitudeI16 & dst); - // the instance will allow Tx only with these protocols - // they are determined upon construction, using GGWave::Protocols::tx() + // The instance will allow Tx only with these protocols. They are determined upon construction or when calling the + // prepare() method, base on the contents of the global GGWave::Protocols::tx() const TxProtocols & txProtocols() const; // @@ -703,14 +716,16 @@ public: bool rxStopReceiving(); - // the instance will attempt to decode only these protocols - // they are determined upon construction, using GGWave::Protocols::rx() + // The instance will attempt to decode only these protocols. + // They are determined upon construction or when calling the prepare() method, base on the contents of the global + // GGWave::Protocols::rx() + // + // Note: do not enable protocols that were not enabled upon preparation of the GGWave instance, or the decoding + // will likely crash // - // note: do not enable protocols that were not enabled upon construction of the GGWave - // instance, or the decoding will likely crash RxProtocols & rxProtocols(); - // information about last received data + // Information about last received data int rxDataLength() const; const TxRxData & rxData() const; const RxProtocol & rxProtocol() const; @@ -718,14 +733,16 @@ public: const Spectrum & rxSpectrum() const; const Amplitude & rxAmplitude() const; - // consume the received data + // Consume the received data + // + // Returns the data length in bytes // - // returns the data length in bytes int rxTakeData(TxRxData & dst); - // consume the received spectrum / amplitude data + // Consume the received spectrum / amplitude data + // + // Returns true if there was new data available // - // returns true if there was new data available bool rxTakeSpectrum(Spectrum & dst); bool rxTakeAmplitude(Amplitude & dst); @@ -733,7 +750,7 @@ public: // Utils // - // compute FFT of real values + // Compute FFT of real values // // src - input real-valued data, size is N // dst - output complex-valued data, size is 2*N @@ -742,7 +759,7 @@ public: // bool computeFFTR(const float * src, float * dst, int N); - // resample audio waveforms from one sample rate to another using sinc interpolation + // Resample audio waveforms from one sample rate to another using sinc interpolation class Resampler { public: // this controls the number of neighboring samples @@ -803,7 +820,7 @@ private: double bitFreq(const Protocol & p, int bit) const; - // initialized via prepare() + // Initialized via prepare() float m_sampleRateInp = -1.0f; float m_sampleRateOut = -1.0f; float m_sampleRate = -1.0f; @@ -835,7 +852,7 @@ private: bool m_txOnlyTones = false; bool m_isDSSEnabled = false; - // common + // Common TxRxData m_dataEncoded; TxRxData m_workRSLength; // Reed-Solomon work buffers TxRxData m_workRSData; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 1c2fe10..46be4fd 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -122,7 +122,7 @@ int ggwave_encode( } { - auto pSrc = (const char *) ggWave->txData(); + auto pSrc = (const char *) ggWave->txWaveform(); auto pDst = ( char *) waveformBuffer; memcpy(pDst, pSrc, nBytes); } @@ -1023,35 +1023,11 @@ uint32_t GGWave::encode() { m_tx.lastAmplitudeSize = offset; - // the encoded waveform can be accessed via the txData() method + // the encoded waveform can be accessed via the txWaveform() method // we return the size of the waveform in bytes: return offset*m_sampleSizeOut; } -const void * GGWave::txData() const { - if (m_isTxEnabled == false) { - ggprintf("Tx is disabled - cannot transmit data with this GGWave instance\n"); - return nullptr; - } - - switch (m_sampleFormatOut) { - case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break; - case GGWAVE_SAMPLE_FORMAT_I16: - { - return m_tx.outputI16.data(); - } break; - case GGWAVE_SAMPLE_FORMAT_U8: - case GGWAVE_SAMPLE_FORMAT_I8: - case GGWAVE_SAMPLE_FORMAT_U16: - case GGWAVE_SAMPLE_FORMAT_F32: - { - return m_tx.outputTmp.data(); - } break; - } - - return nullptr; -} - bool GGWave::decode(const void * data, uint32_t nBytes) { if (m_isRxEnabled == false) { ggprintf("Rx is disabled - cannot receive data with this GGWave instance\n"); @@ -1213,6 +1189,25 @@ int GGWave::heapSize() const { return m_heapSize; } // Tx // +const void * GGWave::txWaveform() const { + switch (m_sampleFormatOut) { + case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break; + case GGWAVE_SAMPLE_FORMAT_I16: + { + return m_tx.outputI16.data(); + } break; + case GGWAVE_SAMPLE_FORMAT_U8: + case GGWAVE_SAMPLE_FORMAT_I8: + case GGWAVE_SAMPLE_FORMAT_U16: + case GGWAVE_SAMPLE_FORMAT_F32: + { + return m_tx.outputTmp.data(); + } break; + } + + return nullptr; +} + const GGWave::Tones GGWave::txTones() const { return { m_tx.tones.data(), m_tx.nTones }; } bool GGWave::txHasData() const { return m_tx.hasData; } diff --git a/tests/test-ggwave.cpp b/tests/test-ggwave.cpp index c2b53a1..2e1223a 100644 --- a/tests/test-ggwave.cpp +++ b/tests/test-ggwave.cpp @@ -219,7 +219,7 @@ int main(int argc, char ** argv) { const auto nBytes = instanceOut.encode(); printf("Expected = %d, actual = %d\n", expectedSize, nBytes); CHECK(expectedSize >= nBytes); - { auto p = (const uint8_t *)(instanceOut.txData()); buffer.resize(nBytes); memcpy(buffer.data(), p, nBytes); } + { auto p = (const uint8_t *)(instanceOut.txWaveform()); buffer.resize(nBytes); memcpy(buffer.data(), p, nBytes); } addNoiseHelper(0.01, parameters.sampleFormatOut); // add some artificial noise convertHelper(parameters.sampleFormatOut, parameters.sampleFormatInp); } @@ -273,7 +273,7 @@ int main(int argc, char ** argv) { const auto nBytes = instance.encode(); printf("Expected = %d, actual = %d\n", expectedSize, nBytes); CHECK(expectedSize == nBytes); - { auto p = (const uint8_t *)(instance.txData()); buffer.resize(nBytes); memcpy(buffer.data(), p, nBytes); } + { auto p = (const uint8_t *)(instance.txWaveform()); buffer.resize(nBytes); memcpy(buffer.data(), p, nBytes); } convertHelper(formatOut, formatInp); instance.decode(buffer.data(), buffer.size()); @@ -300,7 +300,7 @@ int main(int argc, char ** argv) { const auto nBytes = instance.encode(); printf("Expected = %d, actual = %d\n", expectedSize, nBytes); CHECK(expectedSize == nBytes); - { auto p = (const uint8_t *)(instance.txData()); buffer.resize(nBytes); memcpy(buffer.data(), p, nBytes); } + { auto p = (const uint8_t *)(instance.txWaveform()); buffer.resize(nBytes); memcpy(buffer.data(), p, nBytes); } convertHelper(formatOut, formatInp); instance.decode(buffer.data(), buffer.size());