/*! \file main.cpp * \brief Send/Receive data through sound * \author Georgi Gerganov */ #include "build_timestamp.h" #include "fftw3.h" #include "reed-solomon/rs.hpp" #include #include #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846f #endif #ifdef __EMSCRIPTEN__ #include "emscripten/emscripten.h" #endif #ifdef main #undef main #endif static SDL_AudioDeviceID devid_in = 0; static SDL_AudioDeviceID devid_out = 0; struct DataRxTx; static DataRxTx *g_data = nullptr; namespace { //constexpr float IRAND_MAX = 1.0f/RAND_MAX; //inline float frand() { return ((float)(rand()%RAND_MAX)*IRAND_MAX); } constexpr double kBaseSampleRate = 48000.0; constexpr auto kMaxSamplesPerFrame = 1024; constexpr auto kMaxDataBits = 256; constexpr auto kMaxDataSize = 256; constexpr auto kMaxSpectrumHistory = 4; constexpr auto kMaxRecordedFrames = 64*10; using AmplitudeData = std::array; using AmplitudeData16 = std::array; using SpectrumData = std::array; using RecordedData = std::array; inline void addAmplitudeSmooth(const AmplitudeData & src, AmplitudeData & dst, float scalar, int startId, int finalId, int cycleMod, int nPerCycle) { int nTotal = nPerCycle*finalId; float frac = 0.15f; float ds = frac*nTotal; float ids = 1.0f/ds; int nBegin = frac*nTotal; int nEnd = (1.0f - frac)*nTotal; for (int i = startId; i < finalId; i++) { float k = cycleMod*finalId + i; if (k < nBegin) { dst[i] += scalar*src[i]*(k*ids); } else if (k > nEnd) { dst[i] += scalar*src[i]*(((float)(nTotal) - k)*ids); } else { dst[i] += scalar*src[i]; } } } template float getTime_ms(const T & tStart, const T & tEnd) { return ((float)(std::chrono::duration_cast(tEnd - tStart).count()))/1000.0; } } struct DataRxTx { DataRxTx(int aSampleRateOut, int aSampleRate, int aSamplesPerFrame, int aSampleSizeB, const char * text) { sampleSizeBytes = aSampleSizeB; sampleRate = aSampleRate; sampleRateOut = aSampleRateOut; samplesPerFrame = aSamplesPerFrame; init(strlen(text), text); } void init(int textLength, const char * stext) { const uint8_t * text = reinterpret_cast(stext); frameId = 0; nIterations = 0; hasData = false; isamplesPerFrame = 1.0f/samplesPerFrame; sendVolume = ((double)(paramVolume))/100.0f; hzPerFrame = sampleRate/samplesPerFrame; ihzPerFrame = 1.0/hzPerFrame; framesPerTx = paramFramesPerTx; nDataBitsPerTx = paramBytesPerTx*8; nECCBytesPerTx = paramECCBytesPerTx; framesToAnalyze = 0; framesLeftToAnalyze = 0; framesToRecord = 0; framesLeftToRecord = 0; nBitsInMarker = 16; nMarkerFrames = 64; nPostMarkerFrames = 0; sendDataLength = 82; recvDuration_frames = nMarkerFrames + nPostMarkerFrames + framesPerTx*((sendDataLength + nECCBytesPerTx)/paramBytesPerTx + 1); d0 = paramFreqDelta/2; freqDelta_hz = hzPerFrame*paramFreqDelta; freqStart_hz = hzPerFrame*paramFreqStart; if (paramFreqDelta == 1) { d0 = 1; freqDelta_hz *= 2; } outputBlock.fill(0); encodedData.fill(0); for (int k = 0; k < (int) phaseOffsets.size(); ++k) { phaseOffsets[k] = (M_PI*k)/(nDataBitsPerTx); } #ifdef __EMSCRIPTEN__ std::random_shuffle(phaseOffsets.begin(), phaseOffsets.end()); #endif for (int k = 0; k < (int) dataBits.size(); ++k) { double freq = freqStart_hz + freqDelta_hz*k; dataFreqs_hz[k] = freq; double phaseOffset = phaseOffsets[k]; double curHzPerFrame = sampleRateOut/samplesPerFrame; double curIHzPerFrame = 1.0/curHzPerFrame; for (int i = 0; i < samplesPerFrame; i++) { bit1Amplitude[k][i] = std::sin((2.0*M_PI*i)*freq*isamplesPerFrame*curIHzPerFrame + phaseOffset); } for (int i = 0; i < samplesPerFrame; i++) { bit0Amplitude[k][i] = std::sin((2.0*M_PI*i)*(freq + hzPerFrame*d0)*isamplesPerFrame*curIHzPerFrame + phaseOffset); } } if (rs) delete rs; rs = new RS::ReedSolomon(sendDataLength, nECCBytesPerTx); if (textLength > 0) { static std::array theData; theData.fill(0); for (int i = 0; i < textLength; ++i) theData[i] = text[i]; rs->Encode(theData.data(), encodedData.data()); hasData = true; } // Rx receivingData = false; analyzingData = false; sampleAmplitude.fill(0); sampleSpectrum.fill(0); sampleSpectrumTmp.fill(0); for (auto & s : sampleAmplitudeHistory) { s.fill(0); } rxData.fill(0); if (fftPlan) fftwf_destroy_plan(fftPlan); if (fftIn) fftwf_free(fftIn); if (fftOut) fftwf_free(fftOut); fftIn = (float*) fftwf_malloc(sizeof(float)*samplesPerFrame); fftOut = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex)*samplesPerFrame); fftPlan = fftwf_plan_dft_r2c_1d(1*samplesPerFrame, fftIn, fftOut, FFTW_ESTIMATE); for (int i = 0; i < samplesPerFrame; ++i) { fftOut[i][0] = 0.0f; fftOut[i][1] = 0.0f; } } void send() { int samplesPerFrameOut = (sampleRateOut/sampleRate)*samplesPerFrame; if (sampleRateOut != sampleRate) { printf("Resampling from %d Hz to %d Hz\n", (int) sampleRate, (int) sampleRateOut); } while(hasData) { int nBytesPerTx = nDataBitsPerTx/8; std::fill(outputBlock.begin(), outputBlock.end(), 0.0f); std::uint16_t nFreq = 0; if (sampleRateOut != sampleRate) { for (int k = 0; k < nDataBitsPerTx; ++k) { double freq = freqStart_hz + freqDelta_hz*k; double phaseOffset = phaseOffsets[k]; double curHzPerFrame = sampleRateOut/samplesPerFrame; double curIHzPerFrame = 1.0/curHzPerFrame; for (int i = 0; i < samplesPerFrameOut; i++) { bit1Amplitude[k][i] = std::sin((2.0*M_PI*(i + frameId*samplesPerFrameOut))*freq*isamplesPerFrame*curIHzPerFrame + phaseOffset); } for (int i = 0; i < samplesPerFrameOut; i++) { bit0Amplitude[k][i] = std::sin((2.0*M_PI*(i + frameId*samplesPerFrameOut))*(freq + hzPerFrame*d0)*isamplesPerFrame*curIHzPerFrame + phaseOffset); } } } if (frameId < nMarkerFrames) { nFreq = nBitsInMarker; for (int i = 0; i < nBitsInMarker; ++i) { if (i%2 == 0) { ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, nMarkerFrames); } else { ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, nMarkerFrames); } } } else if (frameId < nMarkerFrames + nPostMarkerFrames) { nFreq = nBitsInMarker; for (int i = 0; i < nBitsInMarker; ++i) { if (i%2 == 0) { ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - nMarkerFrames, nPostMarkerFrames); } else { ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - nMarkerFrames, nPostMarkerFrames); } } } else if (frameId < (nMarkerFrames + nPostMarkerFrames) + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*framesPerTx) { int dataOffset = frameId - nMarkerFrames - nPostMarkerFrames; int cycleModMain = dataOffset%framesPerTx; dataOffset /= framesPerTx; dataOffset *= nBytesPerTx; dataBits.fill(0); if (paramFreqDelta > 1) { for (int j = 0; j < nBytesPerTx; ++j) { for (int i = 0; i < 8; ++i) { dataBits[j*8 + i] = encodedData[dataOffset + j] & (1 << i); } } for (int k = 0; k < nDataBitsPerTx; ++k) { ++nFreq; if (dataBits[k] == false) { ::addAmplitudeSmooth(bit0Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); continue; } ::addAmplitudeSmooth(bit1Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); } } else { for (int j = 0; j < nBytesPerTx; ++j) { { uint8_t d = encodedData[dataOffset + j] & 15; dataBits[(2*j + 0)*16 + d] = 1; } { uint8_t d = encodedData[dataOffset + j] & 240; dataBits[(2*j + 1)*16 + (d >> 4)] = 1; } } for (int k = 0; k < 2*nBytesPerTx*16; ++k) { if (dataBits[k] == 0) continue; ++nFreq; if (k%2) { ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); } else { ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); } } } } else { textToSend = ""; hasData = false; } if (nFreq == 0) nFreq = 1; float scale = 1.0f/nFreq; for (int i = 0; i < samplesPerFrameOut; ++i) { outputBlock[i] *= scale; } for (int i = 0; i < samplesPerFrameOut; ++i) { outputBlock16[frameId*samplesPerFrameOut + i] = std::round(32000.0*outputBlock[i]); } ++frameId; } SDL_QueueAudio(devid_out, outputBlock16.data(), 2*frameId*samplesPerFrameOut); } void receive() { static int nCalls = 0; static float tSum_ms = 0.0f; auto tCallStart = std::chrono::high_resolution_clock::now(); if (needUpdate) { init(0, ""); needUpdate = false; } while (hasData == false) { // read capture data int nBytesRecorded = SDL_DequeueAudio(devid_in, sampleAmplitude.data(), samplesPerFrame*sampleSizeBytes); if (nBytesRecorded != 0) { { sampleAmplitudeHistory[historyId] = sampleAmplitude; if (++historyId >= ::kMaxSpectrumHistory) { historyId = 0; } if (historyId == 0 && receivingData == false) { std::fill(sampleAmplitudeAverage.begin(), sampleAmplitudeAverage.end(), 0.0f); for (auto & s : sampleAmplitudeHistory) { for (int i = 0; i < samplesPerFrame; ++i) { sampleAmplitudeAverage[i] += s[i]; } } float norm = 1.0f/::kMaxSpectrumHistory; for (int i = 0; i < samplesPerFrame; ++i) { sampleAmplitudeAverage[i] *= norm; } // calculate spectrum std::copy(sampleAmplitudeAverage.begin(), sampleAmplitudeAverage.begin() + samplesPerFrame, fftIn); fftwf_execute(fftPlan); for (int i = 0; i < samplesPerFrame; ++i) { sampleSpectrumTmp[i] = (fftOut[i][0]*fftOut[i][0] + fftOut[i][1]*fftOut[i][1]); } for (int i = 1; i < samplesPerFrame/2; ++i) { sampleSpectrumTmp[i] += sampleSpectrumTmp[samplesPerFrame - i]; sampleSpectrumTmp[samplesPerFrame - i] = 0.0f; } sampleSpectrum = sampleSpectrumTmp; } if (framesLeftToRecord > 0) { std::copy(sampleAmplitude.begin(), sampleAmplitude.begin() + samplesPerFrame, recordedAmplitude.data() + (framesToRecord - framesLeftToRecord)*samplesPerFrame); if (--framesLeftToRecord <= 0) { std::fill(sampleSpectrum.begin(), sampleSpectrum.end(), 0.0f); analyzingData = true; } } } if (analyzingData) { int nBytesPerTx = nDataBitsPerTx/8; int stepsPerFrame = 16; int step = samplesPerFrame/stepsPerFrame; std::fill(sampleAmplitudeAverage.begin(), sampleAmplitudeAverage.end(), 0.0f); int offsetStart = 0; framesToAnalyze = nMarkerFrames*stepsPerFrame; framesLeftToAnalyze = framesToAnalyze; bool isValid = false; //for (int ii = nMarkerFrames*stepsPerFrame/2; ii < (nMarkerFrames + nPostMarkerFrames)*stepsPerFrame; ++ii) { for (int ii = nMarkerFrames*stepsPerFrame - 1; ii >= nMarkerFrames*stepsPerFrame/2; --ii) { offsetStart = ii; for (int itx = 0; itx < 1024; ++itx) { int offsetTx = offsetStart + itx*framesPerTx*stepsPerFrame; if (offsetTx >= recvDuration_frames*stepsPerFrame) { break; } std::copy( recordedAmplitude.begin() + offsetTx*step, recordedAmplitude.begin() + offsetTx*step + samplesPerFrame, fftIn); for (int k = 1; k < framesPerTx-1; ++k) { for (int i = 0; i < samplesPerFrame; ++i) { fftIn[i] += recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i]; } } fftwf_execute(fftPlan); for (int i = 0; i < samplesPerFrame; ++i) { sampleSpectrumTmp[i] = (fftOut[i][0]*fftOut[i][0] + fftOut[i][1]*fftOut[i][1]); } for (int i = 1; i < samplesPerFrame/2; ++i) { sampleSpectrumTmp[i] += sampleSpectrumTmp[samplesPerFrame - i]; sampleSpectrumTmp[samplesPerFrame - i] = 0.0f; } uint8_t curByte = 0; if (paramFreqDelta > 1) { for (int i = 0; i < nDataBitsPerTx; ++i) { int k = i%8; int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); if (sampleSpectrumTmp[bin] > 1*sampleSpectrumTmp[bin + d0]) { curByte += 1 << k; } else if (sampleSpectrumTmp[bin + d0] > 1*sampleSpectrumTmp[bin]) { } else { } if (k == 7) { encodedData[itx*nBytesPerTx + i/8] = curByte; curByte = 0; } } } else { for (int i = 0; i < 2*nBytesPerTx; ++i) { int bin = std::round(dataFreqs_hz[0]*ihzPerFrame) + i*16; int kmax = 0; double amax = 0.0; for (int k = 0; k < 16; ++k) { if (sampleSpectrumTmp[bin + k] > amax) { kmax = k; amax = sampleSpectrumTmp[bin + k]; } } if (i%2) { curByte += (kmax << 4); encodedData[itx*nBytesPerTx + i/2] = curByte; curByte = 0; } else { curByte = kmax; } } } } if ((rs->Decode(encodedData.data(), rxData.data()) == 0) && ((rxData[0] == 'O') || rxData[0] == 'A')) { if (rxData[0] == 'A') { printf("[ANSWER] Received SDP sound data successfully!\n"); } else if (rxData[0] == 'O') { printf("[OFFER] Received SDP sound data successfully!\n"); } else { printf("Received SDP sound data succssfully\n"); } framesToRecord = 0; isValid = true; } if (isValid) { break; } --framesLeftToAnalyze; } if (isValid == false) { printf("Failed to capture SDP sound data. Please try again\n"); framesToRecord = -1; } receivingData = false; analyzingData = false; framesToAnalyze = 0; framesLeftToAnalyze = 0; } // check if receiving data if (receivingData == false) { bool isReceiving = true; for (int i = 0; i < nBitsInMarker; ++i) { int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); if (i%2 == 0) { if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + d0]) isReceiving = false; } else { if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + d0]) isReceiving = false; } } if (isReceiving) { std::time_t timestamp = std::time(nullptr); printf("%sReceiving WebRTC SDP sound data from another peer ...\n", std::asctime(std::localtime(×tamp))); rxData.fill(0); receivingData = true; framesToRecord = recvDuration_frames; framesLeftToRecord = recvDuration_frames; } } } else { break; } ++nIterations; } auto tCallEnd = std::chrono::high_resolution_clock::now(); tSum_ms += getTime_ms(tCallStart, tCallEnd); if (++nCalls == 10) { averageRxTime_ms = tSum_ms/nCalls; tSum_ms = 0.0f; nCalls = 0; } if ((int) SDL_GetQueuedAudioSize(devid_in) > 32*sampleSizeBytes*samplesPerFrame) { printf("nIter = %d, Queue size: %d\n", nIterations, SDL_GetQueuedAudioSize(devid_in)); SDL_ClearQueuedAudio(devid_in); } } int nIterations; bool needUpdate = false; int paramFreqDelta = 6; int paramFreqStart = 40; int paramFramesPerTx = 6; int paramBytesPerTx = 2; int paramECCBytesPerTx = 32; int paramVolume = 10; // Rx bool receivingData; bool analyzingData; fftwf_plan fftPlan = 0; float *fftIn; fftwf_complex *fftOut = 0; ::AmplitudeData sampleAmplitude; ::SpectrumData sampleSpectrum; ::SpectrumData sampleSpectrumTmp; std::array rxData; std::array encodedData; int historyId = 0; ::AmplitudeData sampleAmplitudeAverage; std::array<::AmplitudeData, ::kMaxSpectrumHistory> sampleAmplitudeHistory; ::RecordedData recordedAmplitude; // Tx bool hasData; int sampleSizeBytes; float sampleRate; float sampleRateOut; int samplesPerFrame; float isamplesPerFrame; ::AmplitudeData outputBlock; ::AmplitudeData16 outputBlock16; std::array<::AmplitudeData, ::kMaxDataBits> bit1Amplitude; std::array<::AmplitudeData, ::kMaxDataBits> bit0Amplitude; float sendVolume; float hzPerFrame; float ihzPerFrame; int d0 = 1; float freqStart_hz; float freqDelta_hz; int frameId; int nRampFrames; int nRampFramesBegin; int nRampFramesEnd; int nRampFramesBlend; int dataId; int framesPerTx; int framesToAnalyze; int framesLeftToAnalyze; int framesToRecord; int framesLeftToRecord; int nBitsInMarker; int nMarkerFrames; int nPostMarkerFrames; int recvDuration_frames; std::array dataBits; std::array phaseOffsets; std::array dataFreqs_hz; int nDataBitsPerTx; int nECCBytesPerTx; int sendDataLength; RS::ReedSolomon *rs = nullptr; float averageRxTime_ms = 0.0; std::string textToSend; }; // JS interface extern "C" { int setText(int textLength, const char * text) { g_data->init(textLength, text); return 0; } int getText(char * text) { std::copy(g_data->rxData.begin(), g_data->rxData.end(), text); return 0; } int getSampleRate() { return g_data->sampleRate; } float getAverageRxTime_ms() { return g_data->averageRxTime_ms; } int getFramesToRecord() { return g_data->framesToRecord; } int getFramesLeftToRecord() { return g_data->framesLeftToRecord; } int getFramesToAnalyze() { return g_data->framesToAnalyze; } int getFramesLeftToAnalyze() { return g_data->framesLeftToAnalyze; } void setParameters( int paramFreqDelta, int paramFreqStart, int paramFramesPerTx, int paramBytesPerTx, int /*paramECCBytesPerTx*/, int paramVolume) { if (g_data == nullptr) return; g_data->paramFreqDelta = paramFreqDelta; g_data->paramFreqStart = paramFreqStart; g_data->paramFramesPerTx = paramFramesPerTx; g_data->paramBytesPerTx = paramBytesPerTx; g_data->paramVolume = paramVolume; g_data->needUpdate = true; } } // main loop void update() { SDL_Event e; SDL_bool shouldTerminate = SDL_FALSE; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { shouldTerminate = SDL_TRUE; } } if (g_data->hasData == false) { SDL_PauseAudioDevice(devid_out, SDL_FALSE); static auto tLastNoData = std::chrono::high_resolution_clock::now(); auto tNow = std::chrono::high_resolution_clock::now(); if (SDL_GetQueuedAudioSize(devid_out) == 0) { SDL_PauseAudioDevice(devid_in, SDL_FALSE); if (::getTime_ms(tLastNoData, tNow) > 500.0f) { g_data->receive(); } else { SDL_ClearQueuedAudio(devid_in); } } else { tLastNoData = tNow; //SDL_ClearQueuedAudio(devid_in); //SDL_Delay(10); } } else { SDL_PauseAudioDevice(devid_out, SDL_TRUE); SDL_PauseAudioDevice(devid_in, SDL_TRUE); g_data->send(); } if (shouldTerminate) { SDL_Log("Shutting down.\n"); SDL_PauseAudioDevice(devid_in, 1); SDL_CloseAudioDevice(devid_in); SDL_PauseAudioDevice(devid_out, 1); SDL_CloseAudioDevice(devid_out); SDL_CloseAudio(); SDL_Quit(); #ifdef __EMSCRIPTEN__ emscripten_cancel_main_loop(); #endif } } int main(int /*argc*/, char** argv) { printf("Build time: %s\n", BUILD_TIMESTAMP); const char *captureDeviceName = argv[1]; SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); if (SDL_Init(SDL_INIT_AUDIO) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); return (1); } SDL_SetHintWithPriority( SDL_HINT_AUDIO_RESAMPLING_MODE, "medium", SDL_HINT_OVERRIDE ); { int devcount = SDL_GetNumAudioDevices(SDL_FALSE); for (int i = 0; i < devcount; i++) { printf("Output device #%d: '%s'\n", i, SDL_GetAudioDeviceName(i, SDL_FALSE)); } } { int devcount = SDL_GetNumAudioDevices(SDL_TRUE); for (int i = 0; i < devcount; i++) { printf("Capture device #%d: '%s'\n", i, SDL_GetAudioDeviceName(i, SDL_TRUE)); } } SDL_AudioSpec desiredSpec; SDL_zero(desiredSpec); desiredSpec.freq = ::kBaseSampleRate; desiredSpec.format = AUDIO_S16SYS; desiredSpec.channels = 1; desiredSpec.samples = 16*1024; desiredSpec.callback = NULL; SDL_AudioSpec obtainedSpec; SDL_zero(obtainedSpec); //devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, SDL_AUDIO_ALLOW_ANY_CHANGE); //devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, 0); if (!devid_out) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for playback: %s!\n", SDL_GetError()); SDL_Quit(); exit(1); } printf("Obtained spec for output device (SDL Id = %d):\n", devid_out); printf(" - Sample rate: %d (required: %d)\n", obtainedSpec.freq, desiredSpec.freq); printf(" - Format: %d (required: %d)\n", obtainedSpec.format, desiredSpec.format); printf(" - Channels: %d (required: %d)\n", obtainedSpec.channels, desiredSpec.channels); printf(" - Samples per frame: %d (required: %d)\n", obtainedSpec.samples, desiredSpec.samples); if (obtainedSpec.format != desiredSpec.format || obtainedSpec.channels != desiredSpec.channels || obtainedSpec.samples != desiredSpec.samples) { SDL_CloseAudio(); throw std::runtime_error("Failed to initialize desired SDL_OpenAudio!"); } SDL_AudioSpec captureSpec; captureSpec = obtainedSpec; captureSpec.freq = ::kBaseSampleRate; captureSpec.format = AUDIO_F32SYS; captureSpec.samples = 1024; SDL_Log("Opening capture device %s%s%s...\n", captureDeviceName ? "'" : "", captureDeviceName ? captureDeviceName : "[[default]]", captureDeviceName ? "'" : ""); devid_in = SDL_OpenAudioDevice(argv[1], SDL_TRUE, &captureSpec, &captureSpec, 0); if (!devid_in) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for capture: %s!\n", SDL_GetError()); SDL_Quit(); exit(1); } printf("Obtained spec for input device (SDL Id = %d):\n", devid_out); printf(" - Sample rate: %d\n", captureSpec.freq); printf(" - Format: %d (required: %d)\n", captureSpec.format, desiredSpec.format); printf(" - Channels: %d (required: %d)\n", captureSpec.channels, desiredSpec.channels); printf(" - Samples per frame: %d\n", captureSpec.samples); int sampleSizeBytes = 4; //switch (obtainedSpec.format) { // case AUDIO_U8: // case AUDIO_S8: // sampleSizeBytes = 1; // break; // case AUDIO_U16SYS: // case AUDIO_S16SYS: // sampleSizeBytes = 2; // break; // case AUDIO_S32SYS: // case AUDIO_F32SYS: // sampleSizeBytes = 4; // break; //} g_data = new DataRxTx(obtainedSpec.freq, ::kBaseSampleRate, captureSpec.samples, sampleSizeBytes, ""); #ifdef __EMSCRIPTEN__ emscripten_set_main_loop(update, 60, 1); #else while(true) { SDL_Delay(20); update(); } #endif delete g_data; return 0; }