diff --git a/README.md b/README.md
index a09811a..faa9912 100644
--- a/README.md
+++ b/README.md
@@ -20,10 +20,13 @@ This library allows you to communicate small amounts of data between air-gapped
This library is used only to generate and analyze the RAW waveforms that are played and captured from your audio devices (speakers, microphones, etc.). You are free to use any audio backend (e.g. PulseAudio, ALSA, etc.) as long as you provide callbacks for queuing and dequeuing audio samples.
-You can easily test the library on your phone with these free mobile apps:
+You can easily test the library using the `waver` application:
+
+
+
**Browser demo:** https://ggwave.ggerganov.com
@@ -105,6 +108,19 @@ emcmake cmake ..
make
```
+
+## Installing the Waver application
+
+[](https://snapcraft.io/waver)
+
+### Linux
+
+```bash
+sudo snap install waver
+sudo snap connect waver:audio-record :audio-record
+```
+
+
## Todo
- [ ] Improve library interface
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index eb80a19..c34ea97 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -6,7 +6,8 @@ if (GGWAVE_SUPPORT_SDL2)
# SDL2
if (EMSCRIPTEN)
- set(CMAKE_CXX_FLAGS "-s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"writeArrayToMemory\"]'")
+ set(CMAKE_CXX_FLAGS "-s USE_SDL=2 -s USE_PTHREADS=1 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
+
unset(SDL2_INCLUDE_DIRS)
unset(SDL2_LIBRARIES)
endif()
@@ -67,7 +68,11 @@ if (GGWAVE_SUPPORT_SDL2)
if (EMSCRIPTEN)
# emscripten sdl2 examples
+ set(CMAKE_CXX_FLAGS "-s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"writeArrayToMemory\"]'")
add_subdirectory(ggwave-wasm)
+
+ set(CMAKE_CXX_FLAGS "-s TOTAL_MEMORY=67108864 -s USE_SDL=2 -s USE_PTHREADS=1 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
+ add_subdirectory(waver)
else()
# non-emscripten sdl2 examples
diff --git a/examples/third-party/imgui/imgui-extra/imgui_impl.cpp b/examples/third-party/imgui/imgui-extra/imgui_impl.cpp
index bc94c39..9c3a196 100644
--- a/examples/third-party/imgui/imgui-extra/imgui_impl.cpp
+++ b/examples/third-party/imgui/imgui-extra/imgui_impl.cpp
@@ -25,6 +25,13 @@ bool ImGui_PreInit() {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+#elif __EMSCRIPTEN__
+ const char* glsl_version = "#version 100";
+ //const char* glsl_version = "#version 300 es";
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#else
// GL 3.0 + GLSL 130
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
@@ -46,6 +53,8 @@ ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context) {
#if __APPLE__
// GL 3.2 Core + GLSL 150
const char* glsl_version = "#version 150";
+#elif __EMSCRIPTEN__
+ const char* glsl_version = "#version 100";
#else
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
@@ -83,12 +92,7 @@ ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context) {
// Setup Platform/Renderer bindings
bool res = true;
res &= ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
-
-#ifdef __EMSCRIPTEN__
- res &= ImGui_ImplOpenGL3_Init("#version 300 es");
-#else
res &= ImGui_ImplOpenGL3_Init(glsl_version);
-#endif
return res ? ctx : nullptr;
}
diff --git a/examples/waver/CMakeLists.txt b/examples/waver/CMakeLists.txt
index 987cfdc..fea657f 100644
--- a/examples/waver/CMakeLists.txt
+++ b/examples/waver/CMakeLists.txt
@@ -1,17 +1,48 @@
-add_executable(waver main.cpp common.cpp interface.cpp interface-unix.cpp)
+set(TARGET waver)
-target_include_directories(waver PRIVATE
- ..
- ${SDL2_INCLUDE_DIRS}
- )
+if (EMSCRIPTEN)
+ add_executable(${TARGET} main.cpp common.cpp interface.cpp interface-emscripten.cpp)
-target_link_libraries(waver PRIVATE
- ggwave
- ggwave-common
- ggwave-common-sdl2
- ggsock
- imgui-sdl2
- ${CMAKE_THREAD_LIBS_INIT}
- )
+ target_include_directories(${TARGET} PRIVATE
+ ..
+ ${SDL2_INCLUDE_DIRS}
+ ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/
+ )
-install(TARGETS waver RUNTIME DESTINATION bin)
+ target_link_libraries(${TARGET} PRIVATE
+ ggwave
+ ggwave-common
+ ggwave-common-sdl2
+ ggsock
+ imgui-sdl2
+ ${CMAKE_THREAD_LIBS_INIT}
+ )
+
+ set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
+ -s FORCE_FILESYSTEM=1 \
+ --preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../assets/fonts@/ \
+ ")
+
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/build_timestamp.h.tmpl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/build_timestamp.h @ONLY)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/style.css COPYONLY)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/background-0.png ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/background-0.png COPYONLY)
+else()
+ add_executable(${TARGET} main.cpp common.cpp interface.cpp interface-unix.cpp)
+
+ target_include_directories(${TARGET} PRIVATE
+ ..
+ ${SDL2_INCLUDE_DIRS}
+ )
+
+ target_link_libraries(${TARGET} PRIVATE
+ ggwave
+ ggwave-common
+ ggwave-common-sdl2
+ ggsock
+ imgui-sdl2
+ ${CMAKE_THREAD_LIBS_INIT}
+ )
+
+ install(TARGETS ${TARGET} RUNTIME DESTINATION bin)
+endif()
diff --git a/examples/waver/README.md b/examples/waver/README.md
index 185d582..643208d 100644
--- a/examples/waver/README.md
+++ b/examples/waver/README.md
@@ -1,8 +1,39 @@
# waver
-Simple GUI program using `ggwave`. The UI is the same as in the `Waver: Data Over Sound` mobile applications.
+Waver allows you to send and receive text messages from nearby devices through sound waves.
-
+This application can be used to communicate with multiple nearby devices at once. Both audible and ultrasound communication protocols are available. The app does not connect to the internet and all information is transmitted only through sound. In order to receive incoming messages you only need to allow access to your device's microphone so that it can record nearby sounds.
+
+### Install
+
+
+
+
+```bash
+sudo snap install waver
+sudo snap connect waver:audio-record :audio-record
+```
+
+## How to use
+
+
+
+- Before starting - make sure the speaker of your device is enabled and disconnect/unplug any headphones. The app uses your device's speaker to emit sounds when sending a text message
+- To send a message - tap on "Messages", enter some text at the bottom of the screen and click "Send"
+- Any nearby device that is also running this application can capture the emitted sound and display the received message
+- In the settings menu, you can adjust the volume and the transmission protocol that will be used when sending messages. Make sure to adjust the volume level high enough, so the sounds can be picked up by other devices
+- Tap on "Spectrum" to see a real-time frequency spectrum of the currently captured audio by your device's microphone
+
+
+## File sharing in a local network
+
+As of v1.3.0 Waver supports file sharing. It works like this:
+
+- Add files that you would like to transmit by sharing them with Waver
+- In the "Files" menu, click on "Broadcast". This plays an audio message that contains a file broadcast offer
+- Nearby devices in the same local network can receive this offer and initiate a TCP/IP connection to your device
+- The files are transmitted over TCP/IP. The sound message is used only to initiate the network connections between the devices
+- Waver allows sharing multiple files to multiple devices at once
diff --git a/examples/waver/background-0.png b/examples/waver/background-0.png
new file mode 100644
index 0000000..df85c07
Binary files /dev/null and b/examples/waver/background-0.png differ
diff --git a/examples/waver/build_timestamp.h.tmpl b/examples/waver/build_timestamp.h.tmpl
new file mode 100644
index 0000000..63cb816
--- /dev/null
+++ b/examples/waver/build_timestamp.h.tmpl
@@ -0,0 +1 @@
+static const char * BUILD_TIMESTAMP="@GIT_DATE@ (@GIT_SHA1@)";
diff --git a/examples/waver/common.cpp b/examples/waver/common.cpp
index e3efec3..d8dda7b 100644
--- a/examples/waver/common.cpp
+++ b/examples/waver/common.cpp
@@ -438,7 +438,13 @@ std::thread initMain() {
g_isRunning = true;
g_ggWave = GGWave_instance();
+#ifdef __EMSCRIPTEN__
+ GGSock::FileServer::Parameters p;
+ p.nWorkerThreads = 0;
+ g_fileServer.init(p);
+#else
g_fileServer.init({});
+#endif
g_fileClient.setErrorCallback([](GGSock::Communicator::TErrorCode code) {
printf("Disconnected with code = %d\n", code);
@@ -666,6 +672,11 @@ void renderMain() {
static bool scrollMessagesToBottom = true;
static bool hasAudioCaptureData = false;
static bool hasNewMessages = false;
+#ifdef __EMSCRIPTEN__
+ static bool hasFileSharingSupport = false;
+#else
+ static bool hasFileSharingSupport = true;
+#endif
static double tStartInput = 0.0f;
static double tEndInput = -100.0f;
@@ -768,7 +779,14 @@ void renderMain() {
}
ImGui::SameLine();
- if (ImGui::ButtonSelectable(ICON_FA_FILE " Files", { 0.40f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight }, windowId == WindowId::Files)) {
+ if (!hasFileSharingSupport) {
+ ImGui::ButtonDisabled(ICON_FA_FILE " Files", { 0.40f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight });
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("File sharing is not supported on this platform!");
+ ImGui::EndTooltip();
+ }
+ } else if (ImGui::ButtonSelectable(ICON_FA_FILE " Files", { 0.40f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight }, windowId == WindowId::Files)) {
windowId = WindowId::Files;
}
ImGui::SameLine();
@@ -1030,7 +1048,7 @@ void renderMain() {
ImGui::TextDisabled("|");
ImGui::SameLine();
- if (ImGui::ButtonDisablable("Receive", {}, !messageSelected.received || messageSelected.type != Message::FileBroadcast)) {
+ if (ImGui::ButtonDisablable("Receive", {}, !messageSelected.received || messageSelected.type != Message::FileBroadcast || !hasFileSharingSupport)) {
auto broadcastInfo = parseBroadcastInfo(messageSelected.data);
g_remoteIP = broadcastInfo.ip;
diff --git a/examples/waver/index-tmpl.html b/examples/waver/index-tmpl.html
new file mode 100644
index 0000000..2cf8ed4
--- /dev/null
+++ b/examples/waver/index-tmpl.html
@@ -0,0 +1,184 @@
+
+
+