#include "ggwave/ggwave.h" #include #include #include #include #include #include #define CHECK(cond) \ if (!(cond)) { \ fprintf(stderr, "[%s:%d] Check failed: %s\n", __FILE__, __LINE__, #cond); \ exit(1); \ } #define CHECK_T(cond) CHECK(cond) #define CHECK_F(cond) CHECK(!(cond)) const std::map kSampleScale = { { typeid(uint8_t), std::numeric_limits::max() }, { typeid(int8_t), std::numeric_limits::max() }, { typeid(uint16_t), std::numeric_limits::max() }, { typeid(int16_t), std::numeric_limits::max() }, { typeid(float), 1.0f }, }; const std::map kSampleOffset = { { typeid(uint8_t), 0.5f*std::numeric_limits::max() }, { typeid(int8_t), 0.0f }, { typeid(uint16_t), 0.5f*std::numeric_limits::max() }, { typeid(int16_t), 0.0f }, { typeid(float), 0.0f }, }; const std::set kFormats = { GGWAVE_SAMPLE_FORMAT_U8, GGWAVE_SAMPLE_FORMAT_I8, GGWAVE_SAMPLE_FORMAT_U16, GGWAVE_SAMPLE_FORMAT_I16, GGWAVE_SAMPLE_FORMAT_F32, }; template GGWave::CBWaveformOut getCBWaveformOut(uint32_t & nSamples, std::vector & buffer) { return [&nSamples, &buffer](const void * data, uint32_t nBytes) { nSamples = nBytes/sizeof(T); CHECK(nSamples*sizeof(T) == nBytes); buffer.resize(nSamples); std::copy((char *) data, (char *) data + nBytes, (char *) buffer.data()); }; } template GGWave::CBWaveformInp getCBWaveformInp(uint32_t & nSamples, std::vector & 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); std::copy(p, p + nCopied, (char *) data); nSamples -= nCopied/sizeof(T); return nCopied; }; } template void convert(const std::vector & src, std::vector & dst) { int n = src.size(); dst.resize(n); for (int i = 0; i < n; ++i) { dst[i] = ((float(src[i]) - kSampleOffset.at(typeid(S)))/kSampleScale.at(typeid(S)))*kSampleScale.at(typeid(D)) + kSampleOffset.at(typeid(D)); } } int main() { std::vector bufferU8; std::vector bufferI8; std::vector bufferU16; std::vector bufferI16; std::vector bufferF32; auto convertHelper = [&](GGWave::SampleFormat formatOut, GGWave::SampleFormat formatInp) { switch (formatOut) { case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_U8: { 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; case GGWAVE_SAMPLE_FORMAT_U16: convert(bufferU8, bufferU16); break; case GGWAVE_SAMPLE_FORMAT_I16: convert(bufferU8, bufferI16); break; case GGWAVE_SAMPLE_FORMAT_F32: convert(bufferU8, bufferF32); break; }; } break; case GGWAVE_SAMPLE_FORMAT_I8: { 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; case GGWAVE_SAMPLE_FORMAT_U16: convert(bufferI8, bufferU16); break; case GGWAVE_SAMPLE_FORMAT_I16: convert(bufferI8, bufferI16); break; case GGWAVE_SAMPLE_FORMAT_F32: convert(bufferI8, bufferF32); break; }; } break; case GGWAVE_SAMPLE_FORMAT_U16: { 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; case GGWAVE_SAMPLE_FORMAT_U16: break; case GGWAVE_SAMPLE_FORMAT_I16: convert(bufferU16, bufferI16); break; case GGWAVE_SAMPLE_FORMAT_F32: convert(bufferU16, bufferF32); break; }; } break; case GGWAVE_SAMPLE_FORMAT_I16: { 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; case GGWAVE_SAMPLE_FORMAT_U16: convert(bufferI16, bufferU16); break; case GGWAVE_SAMPLE_FORMAT_I16: break; case GGWAVE_SAMPLE_FORMAT_F32: convert(bufferI16, bufferF32); break; }; } break; case GGWAVE_SAMPLE_FORMAT_F32: { 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; case GGWAVE_SAMPLE_FORMAT_U16: convert(bufferF32, bufferU16); break; case GGWAVE_SAMPLE_FORMAT_I16: convert(bufferF32, bufferI16); break; case GGWAVE_SAMPLE_FORMAT_F32: break; }; } break; }; }; uint32_t nSamples = 0; const std::map 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 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) }, }; { GGWave instance(GGWave::getDefaultParameters()); std::string payload = "hello"; CHECK(instance.init(payload)); // data CHECK_F(instance.init(-1, "asd")); CHECK_T(instance.init(0, nullptr)); CHECK_T(instance.init(0, "asd")); CHECK_T(instance.init(1, "asd")); CHECK_T(instance.init(2, "asd")); CHECK_T(instance.init(3, "asd")); // volume CHECK_F(instance.init(payload.size(), payload.c_str(), -1)); CHECK_T(instance.init(payload.size(), payload.c_str(), 0)); CHECK_T(instance.init(payload.size(), payload.c_str(), 50)); CHECK_T(instance.init(payload.size(), payload.c_str(), 100)); CHECK_F(instance.init(payload.size(), payload.c_str(), 101)); } // capture / playback at different sample rates { auto parameters = GGWave::getDefaultParameters(); std::string payload = "hello"; // encode { parameters.sampleRateInp = 48000; parameters.sampleRateOut = 12000; GGWave instanceOut(parameters); instanceOut.init(payload, 25); auto expectedSize = instanceOut.encodeSize_samples(); instanceOut.encode(kCBWaveformOut.at(parameters.sampleFormatOut)); printf("Expected = %d, actual = %d\n", expectedSize, nSamples); CHECK(expectedSize == nSamples); convertHelper(parameters.sampleFormatOut, parameters.sampleFormatInp); } // decode { parameters.samplesPerFrame *= float(parameters.sampleRateOut)/parameters.sampleRateInp; parameters.sampleRateInp = parameters.sampleRateOut; GGWave instanceInp(parameters); instanceInp.decode(kCBWaveformInp.at(parameters.sampleFormatInp)); GGWave::TxRxData result; CHECK(instanceInp.takeRxData(result) == (int) payload.size()); for (int i = 0; i < (int) payload.size(); ++i) { CHECK(payload[i] == result[i]); } } } for (const auto & txProtocol : GGWave::getTxProtocols()) { for (const auto & formatOut : kFormats) { for (const auto & formatInp : kFormats) { printf("Testing: protocol = %s, in = %d, out = %d\n", txProtocol.second.name, formatInp, formatOut); auto parameters = GGWave::getDefaultParameters(); parameters.sampleFormatInp = formatInp; parameters.sampleFormatOut = formatOut; GGWave instance(parameters); std::string payload = "test"; instance.init(payload, txProtocol.second, 25); 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; CHECK(instance.takeRxData(result) == (int) payload.size()); for (int i = 0; i < (int) payload.size(); ++i) { CHECK(payload[i] == result[i]); } } } } return 0; }