diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c839311 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required (VERSION 2.8) +project (wave-share) + +option(USE_FINDSDL2 "Use the FindSDL2.cmake script" OFF) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wno-long-long -pedantic") + +# +## Dependencies +find_package(Threads REQUIRED) +find_package(FFTW REQUIRED) +find_package(SDL2) + +if (NOT USE_FINDSDL2 AND NOT SDL2_FOUND) + message(WARNING "Unable to find SDL2 library. It is either not installed or CMake cannot find it." + " In the latter case, setting the USE_FINDSDL2 variable might help:\n" + " $ cmake -D USE_FINDSDL2 .." + ) + + message(FATAL_ERROR "Aborting") +endif() + +string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES) + +add_executable(wave-share main.cpp) +target_include_directories(wave-share PUBLIC ${FFTW_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS}) +target_link_libraries(wave-share PUBLIC ${FFTW_LIBRARIES} ${SDL2_LIBRARIES}) diff --git a/cmake/FindFFTW.cmake b/cmake/FindFFTW.cmake new file mode 100644 index 0000000..941bec6 --- /dev/null +++ b/cmake/FindFFTW.cmake @@ -0,0 +1,27 @@ +# - Find FFTW +# Find the native FFTW includes and library +# +# FFTW_INCLUDE_DIRS - where to find fftw3.h +# FFTW_LIBRARIES - List of libraries when using FFTW. +# FFTWF_LIBRARIES - List of libraries when using FFTW single precision. +# FFTW_FOUND - True if FFTW found. + +if (FFTW_INCLUDE_DIRS) + # Already in cache, be silent + set (FFTW_FIND_QUIETLY TRUE) +endif (FFTW_INCLUDE_DIRS) + +find_path (FFTW_INCLUDE_DIRS fftw3.h) + +#find_library (FFTW_LIBRARIES NAMES fftw3f) + +find_library (FFTW_LIBRARIES NAMES fftw3) +find_library (FFTWF_LIBRARIES NAMES fftw3f) +set (FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTWF_LIBRARIES}) + +# handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if +# all listed variables are TRUE +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args (FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDE_DIRS) + +mark_as_advanced (FFTW_LIBRARIES FFTW_INCLUDE_DIRS) diff --git a/cmake/sdl2/FindSDL2.cmake b/cmake/sdl2/FindSDL2.cmake new file mode 100644 index 0000000..7250e21 --- /dev/null +++ b/cmake/sdl2/FindSDL2.cmake @@ -0,0 +1,203 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindSDL2 +# ------- +# +# Locate SDL2 library +# +# This module defines +# +# :: +# +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL +# SDL2_INCLUDE_DIR, where to find SDL.h +# SDL2_VERSION_STRING, human-readable string containing the version of SDL +# +# +# +# This module responds to the flag: +# +# :: +# +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# +# +# Don't forget to include SDLmain.h and SDLmain.m your project for the +# OS X framework based version. (Other versions link to -lSDLmain which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your +# configuration and no SDL2_LIBRARY, it means CMake did not find your SDL +# library (SDL.dll, libsdl.so, SDL.framework, etc). Set +# SDL2_LIBRARY_TEMP to point to your SDL library, and configure again. +# Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this +# value as appropriate. These values are used to generate the final +# SDL2_LIBRARY variable, but when these values are unset, SDL2_LIBRARY +# does not get created. +# +# +# +# $SDLDIR is an environment variable that would correspond to the +# ./configure --prefix=$SDLDIR used in building SDL. l.e.galup 9-20-02 +# +# Modified by Eric Wing. Added code to assist with automated building +# by using environmental variables and providing a more +# controlled/consistent search behavior. Added new modifications to +# recognize OS X frameworks and additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL +# guidelines. Added a search for SDLmain which is needed by some +# platforms. Added a search for threads which is needed by some +# platforms. Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of SDL2_LIBRARY to +# override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL/SDL.h to just SDL.h +# This needed to change because "proper" SDL convention is #include +# "SDL.h", not . This is done for portability reasons +# because not all systems place things in SDL/ (see FreeBSD). + +if(NOT SDL2_DIR) + set(SDL2_DIR "" CACHE PATH "SDL2 directory") +endif() + +find_path(SDL2_INCLUDE_DIR SDL_scancode.h + HINTS + ENV SDLDIR + ${SDL2_DIR} + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDLDIR} + include/SDL2 include +) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +# SDL-1.1 is the name used by FreeBSD ports... +# don't confuse it for the version number. +find_library(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + ENV SDLDIR + ${SDL2_DIR} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} +) + +# Hide this cache variable from the user, it's an internal implementation +# detail. The documented library variable for the user is SDL2_LIBRARY +# which is derived from SDL2_LIBRARY_TEMP further below. +set_property(CACHE SDL2_LIBRARY_TEMP PROPERTY TYPE INTERNAL) + +if(NOT SDL2_BUILDING_LIBRARY) + if(NOT SDL2_INCLUDE_DIR MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDLmain for compatibility even though they don't + # necessarily need it. + find_library(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + ENV SDLDIR + ${SDL2_DIR} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + endif() +endif() + +# SDL may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +if(NOT APPLE) + find_package(Threads) +endif() + +# MinGW needs an additional link flag, -mwindows +# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -mwindows +if(MINGW) + set(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "link flags for MinGW") +endif() + +if(SDL2_LIBRARY_TEMP) + # For SDLmain + if(SDL2MAIN_LIBRARY AND NOT SDL2_BUILDING_LIBRARY) + list(FIND SDL2_LIBRARY_TEMP "${SDL2MAIN_LIBRARY}" _SDL2_MAIN_INDEX) + if(_SDL2_MAIN_INDEX EQUAL -1) + set(SDL2_LIBRARY_TEMP "${SDL2MAIN_LIBRARY}" ${SDL2_LIBRARY_TEMP}) + endif() + unset(_SDL2_MAIN_INDEX) + endif() + + # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + if(APPLE) + set(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + endif() + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + if(NOT APPLE) + set(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + endif() + + # For MinGW library + if(MINGW) + set(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + endif() + + # Set the final string here so the GUI reflects the final state. + set(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") +endif() + +if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL2_version.h") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL2_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL2_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL2_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL2_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL2_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL2_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL2_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL2_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL2_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") + set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}) + unset(SDL2_VERSION_MAJOR_LINE) + unset(SDL2_VERSION_MINOR_LINE) + unset(SDL2_VERSION_PATCH_LINE) + unset(SDL2_VERSION_MAJOR) + unset(SDL2_VERSION_MINOR) + unset(SDL2_VERSION_PATCH) +endif() + +set(SDL2_LIBRARIES ${SDL2_LIBRARY}) +set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL + REQUIRED_VARS SDL2_LIBRARIES SDL2_INCLUDE_DIRS + VERSION_VAR SDL2_VERSION_STRING) + +mark_as_advanced(SDL2_LIBRARY SDL2_INCLUDE_DIR) + diff --git a/main.cpp b/main.cpp index da1cd38..c71963c 100644 --- a/main.cpp +++ b/main.cpp @@ -3,8 +3,6 @@ * \author Georgi Gerganov */ -#include "build_timestamp.h" - #include "fftw3.h" #include "reed-solomon/rs.hpp" @@ -18,13 +16,18 @@ #include #include #include +#include #ifndef M_PI #define M_PI 3.14159265358979323846f #endif #ifdef __EMSCRIPTEN__ +#include "build_timestamp.h" #include "emscripten/emscripten.h" +#else +#include +#include #endif #ifdef main @@ -32,6 +35,8 @@ #endif static char *g_captureDeviceName = nullptr; +static int g_captureId = -1; +static int g_playbackId = -1; static bool g_isInitialized = false; static int g_totalBytesCaptured = 0; @@ -129,7 +134,7 @@ struct DataRxTx { framesToRecord = 0; framesLeftToRecord = 0; nBitsInMarker = 16; - nMarkerFrames = 64; + nMarkerFrames = 8; nPostMarkerFrames = 0; sendDataLength = (txMode == ::TxMode::FixedLength) ? ::kDefaultFixedLength : textLength + 3; @@ -518,12 +523,13 @@ struct DataRxTx { int decodedLength = rxData[0]; if (rsData->Decode(encodedData.data() + encodedOffset, rxData.data()) == 0) { printf("Decoded length = %d\n", decodedLength); - if (rxData[0] == 'A') { + if (txMode == ::TxMode::FixedLength && rxData[0] == 'A') { printf("[ANSWER] Received sound data successfully!\n"); - } else if (rxData[0] == 'O') { + } else if (txMode == ::TxMode::FixedLength && rxData[0] == 'O') { printf("[OFFER] Received sound data successfully!\n"); } else { - printf("Received sound data succssfully\n"); + std::string s((char *) rxData.data(), decodedLength); + printf("Received sound data successfully: '%s'\n", s.c_str()); } framesToRecord = 0; isValid = true; @@ -719,15 +725,17 @@ int init() { 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 nDevices = SDL_GetNumAudioDevices(SDL_FALSE); + printf("Found %d playback devices:\n", nDevices); + for (int i = 0; i < nDevices; i++) { + printf(" - Playback 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)); + int nDevices = SDL_GetNumAudioDevices(SDL_TRUE); + printf("Found %d capture devices:\n", nDevices); + for (int i = 0; i < nDevices; i++) { + printf(" - Capture device #%d: '%s'\n", i, SDL_GetAudioDeviceName(i, SDL_TRUE)); } } @@ -743,14 +751,18 @@ int init() { 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 (g_playbackId >= 0) { + printf("Attempt to open playback device %d : '%s' ...\n", g_playbackId, SDL_GetAudioDeviceName(g_playbackId, SDL_FALSE)); + devid_out = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(g_playbackId, SDL_FALSE), SDL_FALSE, &desiredSpec, &obtainedSpec, 0); + } else { + printf("Attempt to open default playback device ...\n"); + devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, 0); + } + if (!devid_out) { printf("Couldn't open an audio device for playback: %s!\n", SDL_GetError()); devid_out = 0; } else { - 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); @@ -771,12 +783,13 @@ int init() { captureSpec.format = AUDIO_F32SYS; captureSpec.samples = 1024; - printf("Opening capture device %s%s%s...\n", - g_captureDeviceName ? "'" : "", - g_captureDeviceName ? g_captureDeviceName : "[[default]]", - g_captureDeviceName ? "'" : ""); - - devid_in = SDL_OpenAudioDevice(g_captureDeviceName, SDL_TRUE, &captureSpec, &captureSpec, 0); + if (g_playbackId >= 0) { + printf("Attempt to open capture device %d : '%s' ...\n", g_captureId, SDL_GetAudioDeviceName(g_captureId, SDL_FALSE)); + devid_in = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(g_captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &captureSpec, 0); + } else { + printf("Attempt to open default capture device ...\n"); + devid_in = SDL_OpenAudioDevice(g_captureDeviceName, SDL_TRUE, &captureSpec, &captureSpec, 0); + } if (!devid_in) { printf("Couldn't open an audio device for capture: %s!\n", SDL_GetError()); devid_in = 0; @@ -903,19 +916,99 @@ void update() { } } -int main(int /*argc*/, char** argv) { +static std::map parseCmdArguments(int argc, char ** argv) { + int last = argc; + std::map res; + for (int i = 1; i < last; ++i) { + if (argv[i][0] == '-') { + if (strlen(argv[i]) > 1) { + res[std::string(1, argv[i][1])] = strlen(argv[i]) > 2 ? argv[i] + 2 : ""; + } + } + } + + return res; +} + +int main(int argc, char** argv) { +#ifdef __EMSCRIPTEN__ printf("Build time: %s\n", BUILD_TIMESTAMP); printf("Press the Init button to start\n"); g_captureDeviceName = argv[1]; +#else + printf("Usage: %s [-cN] [-pN] [-tN]\n", argv[0]); + printf(" -cN - select capture device N\n"); + printf(" -pN - select playback device N\n"); + printf(" -tN - transmission protocol:\n"); + printf(" -t0 : Normal\n"); + printf(" -t1 : Fast (default)\n"); + printf(" -t2 : Fastest\n"); + printf(" -t3 : Ultrasonic\n"); + printf("\n"); + + g_captureDeviceName = nullptr; + + auto argm = parseCmdArguments(argc, argv); + g_captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]); + g_playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]); + int txProtocol = argm["t"].empty() ? 1 : std::stoi(argm["t"]); +#endif #ifdef __EMSCRIPTEN__ emscripten_set_main_loop(update, 60, 1); #else - while(true) { - SDL_Delay(20); + init(); + setTxMode(1); + printf("Selecting Tx protocol %d\n", txProtocol); + switch (txProtocol) { + case 0: + { + printf("Using 'Normal' Tx Protocol\n"); + setParameters(1, 40, 9, 3, 0, 50); + } + break; + case 1: + { + printf("Using 'Fast' Tx Protocol\n"); + setParameters(1, 40, 6, 3, 0, 50); + } + break; + case 2: + { + printf("Using 'Fastest' Tx Protocol\n"); + setParameters(1, 40, 3, 3, 0, 50); + } + break; + case 3: + { + printf("Using 'Ultrasonic' Tx Protocol\n"); + setParameters(1, 320, 9, 3, 0, 50); + } + break; + default: + { + printf("Using 'Fast' Tx Protocol\n"); + setParameters(1, 40, 6, 3, 0, 50); + } + }; + printf("\n"); + std::thread inputThread([]() { + while (true) { + std::string input; + std::cout << "Enter text: "; + getline(std::cin, input); + setText(input.size(), input.data()); + std::cout << "Sending ... " << std::endl; + } + }); + + while (true) { + SDL_Delay(1); update(); } + + inputThread.join(); #endif delete g_data;