core : refactoring + bug fix

- rename callback types
- fix calculation of data frames
This commit is contained in:
Georgi Gerganov
2021-01-23 15:47:59 +02:00
parent ccb7fae08d
commit 597cc48cbb
9 changed files with 165 additions and 144 deletions

View File

@@ -67,7 +67,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (GGWAVE_ALL_WARNINGS)
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
else()
# todo : windows

View File

@@ -17,10 +17,10 @@ cdef extern from "ggwave.h" nogil:
GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST
ctypedef struct ggwave_Parameters:
int sampleRateIn
int sampleRateInp
int sampleRateOut
int samplesPerFrame
ggwave_SampleFormat sampleFormatIn
ggwave_SampleFormat sampleFormatInp
ggwave_SampleFormat sampleFormatOut
ctypedef int ggwave_Instance

View File

@@ -19,10 +19,10 @@ namespace {
std::string g_defaultCaptureDeviceName = "";
SDL_AudioDeviceID g_devIdIn = 0;
SDL_AudioDeviceID g_devIdInp = 0;
SDL_AudioDeviceID g_devIdOut = 0;
SDL_AudioSpec g_obtainedSpecIn;
SDL_AudioSpec g_obtainedSpecInp;
SDL_AudioSpec g_obtainedSpecOut;
GGWave *g_ggWave = nullptr;
@@ -45,7 +45,7 @@ extern "C" {
}
EMSCRIPTEN_KEEPALIVE
int getSampleRate() { return g_ggWave->getSampleRateIn(); }
int getSampleRate() { return g_ggWave->getSampleRateInp(); }
EMSCRIPTEN_KEEPALIVE
int getFramesToRecord() { return g_ggWave->getFramesToRecord(); }
@@ -63,7 +63,7 @@ extern "C" {
int hasDeviceOutput() { return g_devIdOut; }
EMSCRIPTEN_KEEPALIVE
int hasDeviceCapture() { return g_devIdIn; }
int hasDeviceCapture() { return g_devIdInp; }
EMSCRIPTEN_KEEPALIVE
int doInit() {
@@ -79,11 +79,11 @@ bool GGWave_init(
const int playbackId,
const int captureId) {
if (g_devIdIn && g_devIdOut) {
if (g_devIdInp && g_devIdOut) {
return false;
}
if (g_devIdIn == 0 && g_devIdOut == 0) {
if (g_devIdInp == 0 && g_devIdOut == 0) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
@@ -157,47 +157,47 @@ bool GGWave_init(
}
}
if (g_devIdIn == 0) {
if (g_devIdInp == 0) {
SDL_AudioSpec captureSpec;
captureSpec = g_obtainedSpecOut;
captureSpec.freq = GGWave::kBaseSampleRate;
captureSpec.format = AUDIO_F32SYS;
captureSpec.samples = 4096;
SDL_zero(g_obtainedSpecIn);
SDL_zero(g_obtainedSpecInp);
if (captureId >= 0) {
printf("Attempt to open capture device %d : '%s' ...\n", captureId, SDL_GetAudioDeviceName(captureId, SDL_FALSE));
g_devIdIn = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &g_obtainedSpecIn, 0);
g_devIdInp = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &g_obtainedSpecInp, 0);
} else {
printf("Attempt to open default capture device ...\n");
g_devIdIn = SDL_OpenAudioDevice(g_defaultCaptureDeviceName.empty() ? nullptr : g_defaultCaptureDeviceName.c_str(),
SDL_TRUE, &captureSpec, &g_obtainedSpecIn, 0);
g_devIdInp = SDL_OpenAudioDevice(g_defaultCaptureDeviceName.empty() ? nullptr : g_defaultCaptureDeviceName.c_str(),
SDL_TRUE, &captureSpec, &g_obtainedSpecInp, 0);
}
if (!g_devIdIn) {
if (!g_devIdInp) {
printf("Couldn't open an audio device for capture: %s!\n", SDL_GetError());
g_devIdIn = 0;
g_devIdInp = 0;
} else {
printf("Obtained spec for input device (SDL Id = %d):\n", g_devIdIn);
printf(" - Sample rate: %d\n", g_obtainedSpecIn.freq);
printf(" - Format: %d (required: %d)\n", g_obtainedSpecIn.format, captureSpec.format);
printf(" - Channels: %d (required: %d)\n", g_obtainedSpecIn.channels, captureSpec.channels);
printf(" - Samples per frame: %d\n", g_obtainedSpecIn.samples);
printf("Obtained spec for input device (SDL Id = %d):\n", g_devIdInp);
printf(" - Sample rate: %d\n", g_obtainedSpecInp.freq);
printf(" - Format: %d (required: %d)\n", g_obtainedSpecInp.format, captureSpec.format);
printf(" - Channels: %d (required: %d)\n", g_obtainedSpecInp.channels, captureSpec.channels);
printf(" - Samples per frame: %d\n", g_obtainedSpecInp.samples);
reinit = true;
}
}
GGWave::SampleFormat sampleFormatIn = GGWAVE_SAMPLE_FORMAT_UNDEFINED;
GGWave::SampleFormat sampleFormatInp = GGWAVE_SAMPLE_FORMAT_UNDEFINED;
GGWave::SampleFormat sampleFormatOut = GGWAVE_SAMPLE_FORMAT_UNDEFINED;
switch (g_obtainedSpecIn.format) {
case AUDIO_U8: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_U8; break;
case AUDIO_S8: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_I8; break;
case AUDIO_U16SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_U16; break;
case AUDIO_S16SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_I16; break;
case AUDIO_S32SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_F32; break;
case AUDIO_F32SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_F32; break;
switch (g_obtainedSpecInp.format) {
case AUDIO_U8: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_U8; break;
case AUDIO_S8: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I8; break;
case AUDIO_U16SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_U16; break;
case AUDIO_S16SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16; break;
case AUDIO_S32SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32; break;
case AUDIO_F32SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32; break;
}
switch (g_obtainedSpecOut.format) {
@@ -214,10 +214,10 @@ bool GGWave_init(
if (g_ggWave) delete g_ggWave;
g_ggWave = new GGWave({
g_obtainedSpecIn.freq,
g_obtainedSpecInp.freq,
g_obtainedSpecOut.freq,
GGWave::kDefaultSamplesPerFrame,
sampleFormatIn,
sampleFormatInp,
sampleFormatOut});
}
@@ -227,16 +227,16 @@ bool GGWave_init(
GGWave * GGWave_instance() { return g_ggWave; }
bool GGWave_mainLoop() {
if (g_devIdIn == 0 && g_devIdOut == 0) {
if (g_devIdInp == 0 && g_devIdOut == 0) {
return false;
}
static GGWave::CBEnqueueAudio cbQueueAudio = [&](const void * data, uint32_t nBytes) {
static GGWave::CBWaveformOut cbQueueAudio = [&](const void * data, uint32_t nBytes) {
SDL_QueueAudio(g_devIdOut, data, nBytes);
};
static GGWave::CBDequeueAudio cbDequeueAudio = [&](void * data, uint32_t nMaxBytes) {
return SDL_DequeueAudio(g_devIdIn, data, nMaxBytes);
static GGWave::CBWaveformInp cbWaveformInp = [&](void * data, uint32_t nMaxBytes) {
return SDL_DequeueAudio(g_devIdInp, data, nMaxBytes);
};
if (g_ggWave->hasTxData() == false) {
@@ -246,21 +246,21 @@ bool GGWave_mainLoop() {
auto tNow = std::chrono::high_resolution_clock::now();
if ((int) SDL_GetQueuedAudioSize(g_devIdOut) < g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesOut()) {
SDL_PauseAudioDevice(g_devIdIn, SDL_FALSE);
SDL_PauseAudioDevice(g_devIdInp, SDL_FALSE);
if (::getTime_ms(tLastNoData, tNow) > 500.0f) {
g_ggWave->decode(cbDequeueAudio);
if ((int) SDL_GetQueuedAudioSize(g_devIdIn) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesIn()) {
SDL_ClearQueuedAudio(g_devIdIn);
g_ggWave->decode(cbWaveformInp);
if ((int) SDL_GetQueuedAudioSize(g_devIdInp) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesInp()) {
SDL_ClearQueuedAudio(g_devIdInp);
}
} else {
SDL_ClearQueuedAudio(g_devIdIn);
SDL_ClearQueuedAudio(g_devIdInp);
}
} else {
tLastNoData = tNow;
}
} else {
SDL_PauseAudioDevice(g_devIdOut, SDL_TRUE);
SDL_PauseAudioDevice(g_devIdIn, SDL_TRUE);
SDL_PauseAudioDevice(g_devIdInp, SDL_TRUE);
g_ggWave->encode(cbQueueAudio);
}
@@ -269,15 +269,15 @@ bool GGWave_mainLoop() {
}
bool GGWave_deinit() {
if (g_devIdIn == 0 && g_devIdOut == 0) {
if (g_devIdInp == 0 && g_devIdOut == 0) {
return false;
}
delete g_ggWave;
g_ggWave = nullptr;
SDL_PauseAudioDevice(g_devIdIn, 1);
SDL_CloseAudioDevice(g_devIdIn);
SDL_PauseAudioDevice(g_devIdInp, 1);
SDL_CloseAudioDevice(g_devIdInp);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();

View File

@@ -74,12 +74,12 @@ int main(int argc, char** argv) {
ggWave.init(message.size(), message.data(), ggWave.getTxProtocol(protocolId), volume);
std::vector<char> bufferPCM;
GGWave::CBEnqueueAudio cbEnqueueAudio = [&](const void * data, uint32_t nBytes) {
GGWave::CBWaveformOut cbWaveformOut = [&](const void * data, uint32_t nBytes) {
bufferPCM.resize(nBytes);
std::memcpy(bufferPCM.data(), data, nBytes);
};
if (ggWave.encode(cbEnqueueAudio) == false) {
if (ggWave.encode(cbWaveformOut) == false) {
fprintf(stderr, "Failed to generate waveform!\n");
return -4;
}

View File

@@ -821,7 +821,7 @@ void renderMain() {
ImGui::Separator();
ImGui::Text("%s", "");
ImGui::Text("Sample rate (capture): %g, %d B/sample", g_ggWave->getSampleRateIn(), g_ggWave->getSampleSizeBytesIn());
ImGui::Text("Sample rate (capture): %g, %d B/sample", g_ggWave->getSampleRateInp(), g_ggWave->getSampleSizeBytesInp());
ImGui::Text("Sample rate (playback): %g, %d B/sample", g_ggWave->getSampleRateOut(), g_ggWave->getSampleSizeBytesOut());
const float kLabelWidth = ImGui::CalcTextSize("Tx Protocol: ").x;

View File

@@ -45,10 +45,10 @@ extern "C" {
// GGWave instance parameters
typedef struct {
int sampleRateIn; // capture sample rate
int sampleRateInp; // capture sample rate
int sampleRateOut; // playback sample rate
int samplesPerFrame; // number of samples per audio frame
ggwave_SampleFormat sampleFormatIn; // format of the captured audio samples
ggwave_SampleFormat sampleFormatInp; // format of the captured audio samples
ggwave_SampleFormat sampleFormatOut; // format of the playback audio samples
} ggwave_Parameters;
@@ -138,9 +138,9 @@ public:
static constexpr auto kMaxSpectrumHistory = 4;
static constexpr auto kMaxRecordedFrames = 1024;
using Parameters = ggwave_Parameters;
using SampleFormat = ggwave_SampleFormat;
using TxProtocolId = ggwave_TxProtocolId;
using Parameters = ggwave_Parameters;
using SampleFormat = ggwave_SampleFormat;
using TxProtocolId = ggwave_TxProtocolId;
struct TxProtocol {
const char * name; // string identifier of the protocol
@@ -173,8 +173,8 @@ public:
using RecordedData = std::vector<float>;
using TxRxData = std::vector<std::uint8_t>;
using CBEnqueueAudio = std::function<void(const void * data, uint32_t nBytes)>;
using CBDequeueAudio = std::function<uint32_t(void * data, uint32_t nMaxBytes)>;
using CBWaveformOut = std::function<void(const void * data, uint32_t nBytes)>;
using CBWaveformInp = std::function<uint32_t(void * data, uint32_t nMaxBytes)>;
GGWave(const Parameters & parameters);
~GGWave();
@@ -186,8 +186,11 @@ public:
bool init(int dataSize, const char * dataBuffer, const int volume = kDefaultVolume);
bool init(int dataSize, const char * dataBuffer, const TxProtocol & txProtocol, const int volume = kDefaultVolume);
bool encode(const CBEnqueueAudio & cbEnqueueAudio);
void decode(const CBDequeueAudio & cbDequeueAudio);
uint32_t encodeSize_bytes() const;
uint32_t encodeSize_samples() const;
bool encode(const CBWaveformOut & cbWaveformOut);
void decode(const CBWaveformInp & cbWaveformInp);
const bool & hasTxData() const { return m_hasNewTxData; }
const bool & isReceiving() const { return m_receivingData; }
@@ -198,10 +201,10 @@ public:
const int & getFramesToAnalyze() const { return m_framesToAnalyze; }
const int & getFramesLeftToAnalyze() const { return m_framesLeftToAnalyze; }
const int & getSamplesPerFrame() const { return m_samplesPerFrame; }
const int & getSampleSizeBytesIn() const { return m_sampleSizeBytesIn; }
const int & getSampleSizeBytesInp() const { return m_sampleSizeBytesInp; }
const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; }
const float & getSampleRateIn() const { return m_sampleRateIn; }
const float & getSampleRateInp() const { return m_sampleRateInp; }
const float & getSampleRateOut() const { return m_sampleRateOut; }
static TxProtocolId getDefaultTxProtocolId() { return GGWAVE_TX_PROTOCOL_AUDIBLE_FAST; }
@@ -225,13 +228,13 @@ private:
return m_hzPerSample*p.freqStart + m_freqDelta_hz*bit;
}
const float m_sampleRateIn;
const float m_sampleRateInp;
const float m_sampleRateOut;
const int m_samplesPerFrame;
const float m_isamplesPerFrame;
const int m_sampleSizeBytesIn;
const int m_sampleSizeBytesInp;
const int m_sampleSizeBytesOut;
const SampleFormat m_sampleFormatIn;
const SampleFormat m_sampleFormatInp;
const SampleFormat m_sampleFormatOut;
const float m_hzPerSample;
@@ -258,7 +261,7 @@ private:
int m_framesToRecord;
int m_samplesNeeded;
std::vector<float> m_fftIn; // real
std::vector<float> m_fftInp; // real
std::vector<float> m_fftOut; // complex
bool m_hasNewSpectrum;
@@ -280,8 +283,6 @@ private:
// Tx
bool m_hasNewTxData;
int m_nECCBytesPerTx;
int m_sendDataLength;
float m_sendVolume;
int m_txDataLength;

View File

@@ -27,10 +27,10 @@ ggwave_Instance ggwave_init(const ggwave_Parameters parameters) {
static ggwave_Instance curId = 0;
g_instances[curId] = new GGWave({
parameters.sampleRateIn,
parameters.sampleRateInp,
parameters.sampleRateOut,
parameters.samplesPerFrame,
parameters.sampleFormatIn,
parameters.sampleFormatInp,
parameters.sampleFormatOut});
return curId++;
@@ -64,14 +64,14 @@ int ggwave_encode(
int nSamples = 0;
GGWave::CBEnqueueAudio cbEnqueueAudio = [&](const void * data, uint32_t nBytes) {
GGWave::CBWaveformOut cbWaveformOut = [&](const void * data, uint32_t nBytes) {
char * p = (char *) data;
std::copy(p, p + nBytes, outputBuffer);
nSamples = nBytes/ggWave->getSampleSizeBytesOut();
};
if (ggWave->encode(cbEnqueueAudio) == false) {
if (ggWave->encode(cbWaveformOut) == false) {
fprintf(stderr, "Failed to encode data - GGWave instance %d\n", instance);
return -1;
}
@@ -87,7 +87,7 @@ int ggwave_decode(
char * outputBuffer) {
GGWave * ggWave = (GGWave *) g_instances[instance];
GGWave::CBDequeueAudio cbDequeueAudio = [&](void * data, uint32_t nMaxBytes) -> uint32_t {
GGWave::CBWaveformInp cbWaveformInp = [&](void * data, uint32_t nMaxBytes) -> uint32_t {
uint32_t nCopied = std::min((uint32_t) dataSize, nMaxBytes);
std::copy(dataBuffer, dataBuffer + nCopied, (char *) data);
@@ -97,7 +97,7 @@ int ggwave_decode(
return nCopied;
};
ggWave->decode(cbDequeueAudio);
ggWave->decode(cbWaveformInp);
// todo : avoid allocation
GGWave::TxRxData rxData;
@@ -266,15 +266,15 @@ const GGWave::Parameters & GGWave::getDefaultParameters() {
}
GGWave::GGWave(const Parameters & parameters) :
m_sampleRateIn(parameters.sampleRateIn),
m_sampleRateInp(parameters.sampleRateInp),
m_sampleRateOut(parameters.sampleRateOut),
m_samplesPerFrame(parameters.samplesPerFrame),
m_isamplesPerFrame(1.0f/m_samplesPerFrame),
m_sampleSizeBytesIn(bytesForSampleFormat(parameters.sampleFormatIn)),
m_sampleSizeBytesInp(bytesForSampleFormat(parameters.sampleFormatInp)),
m_sampleSizeBytesOut(bytesForSampleFormat(parameters.sampleFormatOut)),
m_sampleFormatIn(parameters.sampleFormatIn),
m_sampleFormatInp(parameters.sampleFormatInp),
m_sampleFormatOut(parameters.sampleFormatOut),
m_hzPerSample(m_sampleRateIn/parameters.samplesPerFrame),
m_hzPerSample(m_sampleRateInp/parameters.samplesPerFrame),
m_ihzPerSample(1.0f/m_hzPerSample),
m_freqDelta_bin(1),
m_freqDelta_hz(2*m_hzPerSample),
@@ -283,12 +283,12 @@ GGWave::GGWave(const Parameters & parameters) :
m_nPostMarkerFrames(0),
m_encodedDataOffset(3),
m_samplesNeeded(m_samplesPerFrame),
m_fftIn(kMaxSamplesPerFrame),
m_fftInp(kMaxSamplesPerFrame),
m_fftOut(2*kMaxSamplesPerFrame),
m_hasNewSpectrum(false),
m_sampleSpectrum(kMaxSamplesPerFrame),
m_sampleAmplitude(kMaxSamplesPerFrame),
m_sampleAmplitudeTmp(kMaxSamplesPerFrame*m_sampleSizeBytesIn),
m_sampleAmplitudeTmp(kMaxSamplesPerFrame*m_sampleSizeBytesInp),
m_hasNewRxData(false),
m_lastRxDataLength(0),
m_rxData(kMaxDataSize),
@@ -301,7 +301,7 @@ GGWave::GGWave(const Parameters & parameters) :
m_outputBlockTmp(kMaxRecordedFrames*kMaxSamplesPerFrame*m_sampleSizeBytesOut),
m_outputBlockI16(kMaxRecordedFrames*kMaxSamplesPerFrame) {
if (m_sampleSizeBytesIn == 0) {
if (m_sampleSizeBytesInp == 0) {
throw std::runtime_error("Invalid or unsupported capture sample format");
}
@@ -390,14 +390,33 @@ bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & txPr
return true;
}
bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
int samplesPerFrameOut = (m_sampleRateOut/m_sampleRateIn)*m_samplesPerFrame;
if (m_sampleRateOut > m_sampleRateIn) {
fprintf(stderr, "Error: capture sample rate (%d Hz) must be <= playback sample rate (%d Hz)\n", (int) m_sampleRateIn, (int) m_sampleRateOut);
uint32_t GGWave::encodeSize_bytes() const {
return encodeSize_samples()*m_sampleSizeBytesOut;
}
uint32_t GGWave::encodeSize_samples() const {
if (m_hasNewTxData == false) {
return 0;
}
int nECCBytesPerTx = getECCBytesForLength(m_txDataLength);
int sendDataLength = m_txDataLength + m_encodedDataOffset;
int totalBytes = sendDataLength + nECCBytesPerTx;
int totalDataFrames = ((totalBytes + m_txProtocol.bytesPerTx - 1)/m_txProtocol.bytesPerTx)*m_txProtocol.framesPerTx;
return (
m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames + m_nMarkerFrames
)*m_samplesPerFrame;
}
bool GGWave::encode(const CBWaveformOut & cbWaveformOut) {
int samplesPerFrameOut = (m_sampleRateOut/m_sampleRateInp)*m_samplesPerFrame;
if (m_sampleRateOut > m_sampleRateInp) {
fprintf(stderr, "Error: capture sample rate (%d Hz) must be <= playback sample rate (%d Hz)\n", (int) m_sampleRateInp, (int) m_sampleRateOut);
return false;
}
if (m_sampleRateOut != m_sampleRateIn) {
fprintf(stderr, "Resampling from %d Hz to %d Hz\n", (int) m_sampleRateIn, (int) m_sampleRateOut);
if (m_sampleRateOut != m_sampleRateInp) {
fprintf(stderr, "Resampling from %d Hz to %d Hz\n", (int) m_sampleRateInp, (int) m_sampleRateOut);
}
int frameId = 0;
@@ -438,10 +457,12 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
}
}
m_nECCBytesPerTx = getECCBytesForLength(m_txDataLength);
m_sendDataLength = m_txDataLength + m_encodedDataOffset;
int nECCBytesPerTx = getECCBytesForLength(m_txDataLength);
int sendDataLength = m_txDataLength + m_encodedDataOffset;
int totalBytes = sendDataLength + nECCBytesPerTx;
int totalDataFrames = ((totalBytes + m_txProtocol.bytesPerTx - 1)/m_txProtocol.bytesPerTx)*m_txProtocol.framesPerTx;
RS::ReedSolomon rsData = RS::ReedSolomon(m_txDataLength, m_nECCBytesPerTx);
RS::ReedSolomon rsData = RS::ReedSolomon(m_txDataLength, nECCBytesPerTx);
RS::ReedSolomon rsLength(1, m_encodedDataOffset - 1);
rsLength.Encode(m_txData.data(), m_txDataEncoded.data());
@@ -450,7 +471,7 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
while (m_hasNewTxData) {
std::fill(m_outputBlock.begin(), m_outputBlock.end(), 0.0f);
if (m_sampleRateOut != m_sampleRateIn) {
if (m_sampleRateOut != m_sampleRateInp) {
for (int k = 0; k < m_txProtocol.nDataBitsPerTx(); ++k) {
double freq = bitFreq(m_txProtocol, k);
@@ -489,9 +510,7 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
::addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames);
}
}
} else if (frameId <
(m_nMarkerFrames + m_nPostMarkerFrames) +
((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx) {
} else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames) {
int dataOffset = frameId - m_nMarkerFrames - m_nPostMarkerFrames;
int cycleModMain = dataOffset%m_txProtocol.framesPerTx;
dataOffset /= m_txProtocol.framesPerTx;
@@ -520,13 +539,10 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
::addAmplitudeSmooth(bit1Amplitude[k/2], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx);
}
}
} else if (frameId <
(m_nMarkerFrames + m_nPostMarkerFrames) +
((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx +
(m_nMarkerFrames)) {
} else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames + m_nMarkerFrames) {
nFreq = m_nBitsInMarker;
int fId = frameId - ((m_nMarkerFrames + m_nPostMarkerFrames) + ((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx);
int fId = frameId - (m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames);
for (int i = 0; i < m_nBitsInMarker; ++i) {
if (i%2 == 0) {
addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames);
@@ -536,6 +552,7 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
}
} else {
m_hasNewTxData = false;
break;
}
if (nFreq == 0) nFreq = 1;
@@ -599,14 +616,14 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break;
case GGWAVE_SAMPLE_FORMAT_I16:
{
cbEnqueueAudio(m_outputBlockI16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut);
cbWaveformOut(m_outputBlockI16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut);
} break;
case GGWAVE_SAMPLE_FORMAT_U8:
case GGWAVE_SAMPLE_FORMAT_I8:
case GGWAVE_SAMPLE_FORMAT_U16:
case GGWAVE_SAMPLE_FORMAT_F32:
{
cbEnqueueAudio(m_outputBlockTmp.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut);
cbWaveformOut(m_outputBlockTmp.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut);
} break;
}
@@ -618,49 +635,49 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
return true;
}
void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
void GGWave::decode(const CBWaveformInp & cbWaveformInp) {
while (m_hasNewTxData == false) {
// read capture data
uint32_t nBytesNeeded = m_samplesNeeded*m_sampleSizeBytesIn;
uint32_t nBytesNeeded = m_samplesNeeded*m_sampleSizeBytesInp;
uint32_t nBytesRecorded = 0;
uint32_t offset = m_samplesPerFrame - m_samplesNeeded;
switch (m_sampleFormatIn) {
switch (m_sampleFormatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break;
case GGWAVE_SAMPLE_FORMAT_U8:
case GGWAVE_SAMPLE_FORMAT_I8:
case GGWAVE_SAMPLE_FORMAT_U16:
case GGWAVE_SAMPLE_FORMAT_I16:
{
nBytesRecorded = cbDequeueAudio(m_sampleAmplitudeTmp.data() + offset, nBytesNeeded);
nBytesRecorded = cbWaveformInp(m_sampleAmplitudeTmp.data() + offset, nBytesNeeded);
} break;
case GGWAVE_SAMPLE_FORMAT_F32:
{
nBytesRecorded = cbDequeueAudio(m_sampleAmplitude.data() + offset, nBytesNeeded);
nBytesRecorded = cbWaveformInp(m_sampleAmplitude.data() + offset, nBytesNeeded);
} break;
}
if (nBytesRecorded % m_sampleSizeBytesIn != 0) {
if (nBytesRecorded % m_sampleSizeBytesInp != 0) {
fprintf(stderr, "Failure during capture - provided bytes (%d) are not multiple of sample size (%d)\n",
nBytesRecorded, m_sampleSizeBytesIn);
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",
nBytesRecorded/m_sampleSizeBytesIn, nBytesNeeded/m_sampleSizeBytesIn);
nBytesRecorded/m_sampleSizeBytesInp, nBytesNeeded/m_sampleSizeBytesInp);
m_samplesNeeded = m_samplesPerFrame;
break;
}
// convert to 32-bit float
switch (m_sampleFormatIn) {
switch (m_sampleFormatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break;
case GGWAVE_SAMPLE_FORMAT_U8:
{
constexpr float scale = 1.0f/128;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<uint8_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(int16_t(*(p + offset + i)) - 128)*scale;
@@ -669,7 +686,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
case GGWAVE_SAMPLE_FORMAT_I8:
{
constexpr float scale = 1.0f/128;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<int8_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(*(p + offset + i))*scale;
@@ -678,7 +695,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
case GGWAVE_SAMPLE_FORMAT_U16:
{
constexpr float scale = 1.0f/32768;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<uint16_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(int32_t(*(p + offset + i)) - 32768)*scale;
@@ -687,7 +704,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
case GGWAVE_SAMPLE_FORMAT_I16:
{
constexpr float scale = 1.0f/32768;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<int16_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(*(p + offset + i))*scale;
@@ -721,9 +738,9 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
}
// calculate spectrum
std::copy(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.begin() + m_samplesPerFrame, m_fftIn.data());
std::copy(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.begin() + m_samplesPerFrame, m_fftInp.data());
FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
FFT(m_fftInp.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
for (int i = 0; i < m_samplesPerFrame; ++i) {
m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]);
@@ -775,15 +792,15 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
std::copy(
m_recordedAmplitude.begin() + offsetTx*step,
m_recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_fftIn.data());
m_recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_fftInp.data());
for (int k = 1; k < rxProtocol.framesPerTx - 1; ++k) {
for (int i = 0; i < m_samplesPerFrame; ++i) {
m_fftIn[i] += m_recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i];
m_fftInp[i] += m_recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i];
}
}
FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
FFT(m_fftInp.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
for (int i = 0; i < m_samplesPerFrame; ++i) {
m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]);
@@ -950,7 +967,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
}
}
} else {
m_samplesNeeded -= nBytesRecorded/m_sampleSizeBytesIn;
m_samplesNeeded -= nBytesRecorded/m_sampleSizeBytesInp;
break;
}
}

View File

@@ -15,7 +15,7 @@
int main() {
ggwave_Parameters parameters = ggwave_getDefaultParameters();
parameters.sampleFormatIn = GGWAVE_SAMPLE_FORMAT_I16;
parameters.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16;
parameters.sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I16;
ggwave_Instance instance = ggwave_init(parameters);

View File

@@ -41,7 +41,7 @@ const std::set<GGWave::SampleFormat> kFormats = {
};
template <typename T>
GGWave::CBEnqueueAudio getCBEnqueueAudio(uint32_t & nSamples, std::vector<T> & buffer) {
GGWave::CBWaveformOut getCBWaveformOut(uint32_t & nSamples, std::vector<T> & buffer) {
return [&nSamples, &buffer](const void * data, uint32_t nBytes) {
nSamples = nBytes/sizeof(T);
CHECK(nSamples*sizeof(T) == nBytes);
@@ -51,7 +51,7 @@ GGWave::CBEnqueueAudio getCBEnqueueAudio(uint32_t & nSamples, std::vector<T> & b
}
template <typename T>
GGWave::CBDequeueAudio getCBDequeueAudio(uint32_t & nSamples, std::vector<T> & buffer) {
GGWave::CBWaveformInp getCBWaveformInp(uint32_t & nSamples, std::vector<T> & buffer) {
return [&nSamples, &buffer](void * data, uint32_t nMaxBytes) {
uint32_t nCopied = std::min((uint32_t) (nSamples*sizeof(T)), nMaxBytes);
const char * p = (char *) (buffer.data() + buffer.size() - nSamples);
@@ -78,12 +78,12 @@ int main() {
std::vector<int16_t> bufferI16;
std::vector<float> bufferF32;
auto convertHelper = [&](GGWave::SampleFormat formatOut, GGWave::SampleFormat formatIn) {
auto convertHelper = [&](GGWave::SampleFormat formatOut, GGWave::SampleFormat formatInp) {
switch (formatOut) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8:
{
switch (formatIn) {
switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferU8, bufferI8); break;
@@ -94,7 +94,7 @@ int main() {
} break;
case GGWAVE_SAMPLE_FORMAT_I8:
{
switch (formatIn) {
switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferI8, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: break;
@@ -105,7 +105,7 @@ int main() {
} break;
case GGWAVE_SAMPLE_FORMAT_U16:
{
switch (formatIn) {
switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferU16, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferU16, bufferI8); break;
@@ -116,7 +116,7 @@ int main() {
} break;
case GGWAVE_SAMPLE_FORMAT_I16:
{
switch (formatIn) {
switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferI16, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferI16, bufferI8); break;
@@ -127,7 +127,7 @@ int main() {
} break;
case GGWAVE_SAMPLE_FORMAT_F32:
{
switch (formatIn) {
switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferF32, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferF32, bufferI8); break;
@@ -142,20 +142,20 @@ int main() {
uint32_t nSamples = 0;
const std::map<GGWave::SampleFormat, GGWave::CBEnqueueAudio> kCBEnqueueAudio = {
{ GGWAVE_SAMPLE_FORMAT_U8, getCBEnqueueAudio(nSamples, bufferU8) },
{ GGWAVE_SAMPLE_FORMAT_I8, getCBEnqueueAudio(nSamples, bufferI8) },
{ GGWAVE_SAMPLE_FORMAT_U16, getCBEnqueueAudio(nSamples, bufferU16) },
{ GGWAVE_SAMPLE_FORMAT_I16, getCBEnqueueAudio(nSamples, bufferI16) },
{ GGWAVE_SAMPLE_FORMAT_F32, getCBEnqueueAudio(nSamples, bufferF32) },
const std::map<GGWave::SampleFormat, GGWave::CBWaveformOut> kCBWaveformOut = {
{ GGWAVE_SAMPLE_FORMAT_U8, getCBWaveformOut(nSamples, bufferU8) },
{ GGWAVE_SAMPLE_FORMAT_I8, getCBWaveformOut(nSamples, bufferI8) },
{ GGWAVE_SAMPLE_FORMAT_U16, getCBWaveformOut(nSamples, bufferU16) },
{ GGWAVE_SAMPLE_FORMAT_I16, getCBWaveformOut(nSamples, bufferI16) },
{ GGWAVE_SAMPLE_FORMAT_F32, getCBWaveformOut(nSamples, bufferF32) },
};
const std::map<GGWave::SampleFormat, GGWave::CBDequeueAudio> kCBDequeueAudio = {
{ GGWAVE_SAMPLE_FORMAT_U8, getCBDequeueAudio(nSamples, bufferU8) },
{ GGWAVE_SAMPLE_FORMAT_I8, getCBDequeueAudio(nSamples, bufferI8) },
{ GGWAVE_SAMPLE_FORMAT_U16, getCBDequeueAudio(nSamples, bufferU16) },
{ GGWAVE_SAMPLE_FORMAT_I16, getCBDequeueAudio(nSamples, bufferI16) },
{ GGWAVE_SAMPLE_FORMAT_F32, getCBDequeueAudio(nSamples, bufferF32) },
const std::map<GGWave::SampleFormat, GGWave::CBWaveformInp> kCBWaveformInp = {
{ GGWAVE_SAMPLE_FORMAT_U8, getCBWaveformInp(nSamples, bufferU8) },
{ GGWAVE_SAMPLE_FORMAT_I8, getCBWaveformInp(nSamples, bufferI8) },
{ GGWAVE_SAMPLE_FORMAT_U16, getCBWaveformInp(nSamples, bufferU16) },
{ GGWAVE_SAMPLE_FORMAT_I16, getCBWaveformInp(nSamples, bufferI16) },
{ GGWAVE_SAMPLE_FORMAT_F32, getCBWaveformInp(nSamples, bufferF32) },
};
{
@@ -183,20 +183,23 @@ int main() {
for (const auto & txProtocol : GGWave::getTxProtocols()) {
for (const auto & formatOut : kFormats) {
for (const auto & formatIn : kFormats) {
printf("Testing: protocol = %s, in = %d, out = %d\n", txProtocol.second.name, formatIn, formatOut);
for (const auto & formatInp : kFormats) {
printf("Testing: protocol = %s, in = %d, out = %d\n", txProtocol.second.name, formatInp, formatOut);
auto parameters = GGWave::getDefaultParameters();
parameters.sampleFormatIn = formatIn;
parameters.sampleFormatInp = formatInp;
parameters.sampleFormatOut = formatOut;
GGWave instance(parameters);
std::string payload = "test message xxxxxxxxxxxx";
std::string payload = "test";
instance.init(payload, txProtocol.second, 25);
instance.encode(kCBEnqueueAudio.at(formatOut));
convertHelper(formatOut, formatIn);
instance.decode(kCBDequeueAudio.at(formatIn));
auto expectedSize = instance.encodeSize_samples();
instance.encode(kCBWaveformOut.at(formatOut));
printf("Expected = %d, actual = %d\n", expectedSize, nSamples);
CHECK(expectedSize == nSamples);
convertHelper(formatOut, formatInp);
instance.decode(kCBWaveformInp.at(formatInp));
{
GGWave::TxRxData result;