This commit is contained in:
Georgi Gerganov
2021-01-06 23:10:45 +02:00
39 changed files with 817 additions and 34 deletions

View File

@@ -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:
<a href="https://apps.apple.com/us/app/waver-data-over-sound/id1543607865?itsct=apps_box&amp;itscg=30200&ign-itsct=apps_box#?platform=iphone" style="display: inline-block; overflow: hidden; border-radius: 13px; width: 250px; height: 83px;"><img height="60px" src="https://tools.applemediaservices.com/api/badges/download-on-the-app-store/white/en-US?size=250x83&amp;releaseDate=1607558400&h=8e5fafc57929918f684abc83ff8311ef" alt="Download on the App Store"></a>
<a href='https://play.google.com/store/apps/details?id=com.ggerganov.Waver&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' src='https://i.imgur.com/BKDCbKv.png' height="60px"/></a>
<a href="https://snapcraft.io/waver">
<img alt="Get it from the Snap Store" src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg" height="60px"/>
</a>
**Browser demo:** https://ggwave.ggerganov.com
@@ -105,6 +108,19 @@ emcmake cmake ..
make
```
## Installing the Waver application
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/waver)
### Linux
```bash
sudo snap install waver
sudo snap connect waver:audio-record :audio-record
```
## Todo
- [ ] Improve library interface

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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.
<a href="https://youtu.be/KWlcgZHJhGQ"><img src="../../media/waver-0-fast.gif"></a>
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
<a href="https://apps.apple.com/us/app/waver-data-over-sound/id1543607865?itsct=apps_box&amp;itscg=30200&ign-itsct=apps_box#?platform=iphone" style="display: inline-block; overflow: hidden; border-radius: 13px; width: 250px; height: 83px;"><img height="60px" src="https://tools.applemediaservices.com/api/badges/download-on-the-app-store/white/en-US?size=250x83&amp;releaseDate=1607558400&h=8e5fafc57929918f684abc83ff8311ef" alt="Download on the App Store"></a>
<a href='https://play.google.com/store/apps/details?id=com.ggerganov.Waver&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' src='https://i.imgur.com/BKDCbKv.png' height="60px"/></a>
<a href="https://snapcraft.io/waver">
<img alt="Get it from the Snap Store" src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg" height="60px"/>
</a>
```bash
sudo snap install waver
sudo snap connect waver:audio-record :audio-record
```
## How to use
<a href="https://youtu.be/Zcgf77T71QM"><img width="100%" src="../../media/waver-preview0.png"></img></a>
- 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

View File

@@ -0,0 +1 @@
static const char * BUILD_TIMESTAMP="@GIT_DATE@ (@GIT_SHA1@)";

View File

@@ -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;

View File

@@ -0,0 +1,184 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Waver</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="waver" />
<meta name="twitter:description" content="Emscripten port of Waver" />
<meta name="twitter:image" content="https://waver.ggerganov.com/waver.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="main-controls">
<div id="description" align="center">
<h2>Waver: Data Over Sound</h2>
<div id="container_status" align="center">
<button onClick="doInit()" id="butInit" style="width:60px;height:30px;" disabled>Init</button>
</div>
</div>
<div id="container_canvas" hidden>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
</div>
<br>
</div>
<div id="footer" class="cell-version">
<span>
Build time: <span class="nav-link">@GIT_DATE@</span> |
Commit hash: <a class="nav-link" href="https://github.com/ggerganov/ggwave/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
</span>
</div>
<div class="cell-about">
<a class="nav-link" href="https://github.com/ggerganov/ggwave/tree/master/examples/waver"><span class="d-none d-sm-inline">View on GitHub </span>
<svg version="1.1" width="16" height="16" viewBox="0 0 16 16" class="octicon octicon-mark-github" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
</a>
</div>
<script type='text/javascript'>
window.mobilecheck = function() {
var check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
return check;
};
var isMobile = mobilecheck();
var isInitialized = false;
window.setInterval(function(){
if (isInitialized == false) return;
//var w = window,
// d = document,
// e = d.documentElement,
// g = d.getElementsByTagName('body')[0],
// x = w.innerWidth || e.clientWidth || g.clientWidth,
// y = w.innerHeight|| e.clientHeight|| g.clientHeight;
//Module._set_window_size(0.99*x, y - 1.40*document.getElementById('footer').clientHeight);
}, 500);
function checkLoop() {
setTimeout(checkLoop, 100);
}
function checkSharedArrayBuffer() {
try {
var a = SharedArrayBuffer;
} catch (e) {
console.log(e instanceof ReferenceError); // true
return false;
}
return true;
}
function onkeydown(event) {
if (event.keyCode >= 112 && event.keyCode <= 123) {
event.stopImmediatePropagation();
}
}
function init() {
document.getElementById("butInit").disabled = false;
window.addEventListener('keydown', onkeydown, true);
setTimeout(checkLoop, 100);
//window.requestAnimationFrame(renderFrame);
}
function doInit() {
let constraints = {
audio: {
sampleRate: 16000,
channelCount: 1,
echoCancellation: false,
autoGainControl: false,
noiseSuppression: false
}
};
if (isInitialized == false) {
Module._do_init();
var but = document.getElementById("butInit");
but.disabled = true;
//but.hidden = true;
but.parentNode.removeChild(but);
isInitialized = true;
}
var x = document.getElementById("container_canvas");
x.hidden = false;
}
//function renderFrame() {
// window.requestAnimationFrame(renderFrame);
//}
var Module = {
arguments: [],
preRun: [(function() {
}) ],
postRun: [(function () {
init();
})
],
canvas: (function() {
var canvas = document.getElementById('canvas');
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvas;
})(),
print: (function() {
return function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
};
})(),
printErr: function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
setStatus: function(text) {
console.log("status: " + text);
},
monitorRunDependencies: function(left) {
// no run dependencies to log
}
};
window.onerror = function() {
console.log("onerror: " + JSON.stringify(event));
if (checkSharedArrayBuffer() == false) {
document.getElementById('container_status').innerHTML = "It seems your browser does not have SharedArrayBuffer support enabled.<br><br>Try openning this page on a PC with latest Chrome browser.";
document.getElementById('container_status').style.color = "#ff0000";
}
};
</script>
<script async type="text/javascript" src="waver.js"></script>
</body>
</html>

View File

@@ -0,0 +1,43 @@
#include "interface.h"
#include <fstream>
void interface_addFile(
const char * ,
const char * ,
const char * ,
size_t ) {
}
void interface_loadAllFiles() {
}
void interface_shareFile(
const char * ,
const char * ,
const char * ,
size_t ) {
}
void interface_openFile(
const char * ,
const char * ,
const char * ,
size_t ) {
}
void interface_deleteFile(
const char * ,
const char * ) {
}
void interface_receiveFile(
const char * ,
const char * ,
const char * ,
size_t ) {
}
bool interface_needReloadFiles() {
return false;
}

View File

@@ -2,6 +2,13 @@
#include "ggwave-common.h"
#ifdef __EMSCRIPTEN__
#include "build_timestamp.h"
#include "emscripten/emscripten.h"
#else
#define EMSCRIPTEN_KEEPALIVE
#endif
#include <imgui-extra/imgui_impl.h>
#include <SDL.h>
@@ -13,12 +20,16 @@
#include <fstream>
#include <vector>
#include <iterator>
#include <functional>
namespace {
void dummy() {}
}
std::string getBinaryPath() {
#ifdef __EMSCRIPTEN__
return "";
#endif
std::string result;
void* p = reinterpret_cast<void*>(dummy);
@@ -179,16 +190,42 @@ bool ImGui_SetStyle() {
return true;
}
static std::function<bool()> g_doInit;
static std::function<void(int, int)> g_setWindowSize;
static std::function<bool()> g_mainUpdate;
void mainUpdate(void *) {
g_mainUpdate();
}
// JS interface
extern "C" {
EMSCRIPTEN_KEEPALIVE
int do_init() {
return g_doInit();
}
EMSCRIPTEN_KEEPALIVE
void set_window_size(int sizeX, int sizeY) {
g_setWindowSize(sizeX, sizeY);
}
}
int main(int argc, char** argv) {
#ifdef __EMSCRIPTEN__
printf("Build time: %s\n", BUILD_TIMESTAMP);
printf("Press the Init button to start\n");
if (argv[1]) {
GGWave_setDefaultCaptureDeviceName(argv[1]);
}
#endif
auto argm = parseCmdArguments(argc, argv);
int captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]);
int playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]);
if (GGWave_init(playbackId, captureId) == false) {
fprintf(stderr, "Failed to initialize GGWave\n");
return -1;
}
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "Error: %s\n", SDL_GetError());
return -1;
@@ -207,8 +244,15 @@ int main(int argc, char** argv) {
const char * windowTitle = "Waver";
#ifdef __EMSCRIPTEN__
SDL_Renderer * renderer;
SDL_Window * window;
SDL_CreateWindowAndRenderer(windowX, windowY, SDL_WINDOW_OPENGL, &window, &renderer);
#else
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Window * window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowX, windowY, window_flags);
#endif
void * gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
@@ -216,6 +260,7 @@ int main(int argc, char** argv) {
ImGui_Init(window, gl_context);
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().Fonts->AddFontFromFileTTF((getBinaryPath() + "DroidSans.ttf").c_str(), 14.0f);
ImGui::GetIO().Fonts->AddFontFromFileTTF((getBinaryPath() + "../examples/assets/fonts/DroidSans.ttf").c_str(), 14.0f);
ImGui::GetIO().Fonts->AddFontFromFileTTF((getBinaryPath() + "../../examples/assets/fonts/DroidSans.ttf").c_str(), 14.0f);
@@ -226,6 +271,7 @@ int main(int argc, char** argv) {
config.MergeMode = true;
config.GlyphOffset = { 0.0f, 0.0f };
ImGui::GetIO().Fonts->AddFontFromFileTTF((getBinaryPath() + "fontawesome-webfont.ttf").c_str(), 14.0f, &config, ranges);
ImGui::GetIO().Fonts->AddFontFromFileTTF((getBinaryPath() + "../examples/assets/fonts/fontawesome-webfont.ttf").c_str(), 14.0f, &config, ranges);
ImGui::GetIO().Fonts->AddFontFromFileTTF((getBinaryPath() + "../../examples/assets/fonts/fontawesome-webfont.ttf").c_str(), 14.0f, &config, ranges);
}
@@ -237,22 +283,58 @@ int main(int argc, char** argv) {
ImGui_NewFrame(window);
ImGui::Render();
auto worker = initMain();
// tmp
//addFile("test0.raw", "test0.raw", std::vector<char>(1024));
//addFile("test1.jpg", "test0.jpg", std::vector<char>(1024*1024 + 624));
//addFile("test2.mpv", "test0.mov", std::vector<char>(1024*1024*234 + 53827));
while (true) {
bool isInitialized = false;
std::thread worker;
g_doInit = [&]() {
if (GGWave_init(playbackId, captureId) == false) {
fprintf(stderr, "Failed to initialize GGWave\n");
return false;
}
worker = initMain();
isInitialized = true;
return true;
};
g_setWindowSize = [&](int sizeX, int sizeY) {
SDL_SetWindowSize(window, sizeX, sizeY);
};
g_mainUpdate = [&]() {
if (isInitialized == false) {
return true;
}
if (ImGui_BeginFrame(window) == false) {
break;
return false;
}
renderMain();
updateMain();
ImGui_EndFrame(window);
return true;
};
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop_arg(mainUpdate, NULL, 0, true);
#else
if (g_doInit() == false) {
printf("Error: failed to initialize audio\n");
return -2;
}
while (true) {
if (g_mainUpdate() == false) break;
}
deinitMain(worker);
@@ -265,6 +347,7 @@ int main(int argc, char** argv) {
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
SDL_Quit();
#endif
return 0;
}

324
examples/waver/style.css Normal file
View File

@@ -0,0 +1,324 @@
body {
background-image: url('background-0.png');
margin: 0; background-color: white;
-webkit-font-smoothing: subpixel-antialiased;
font-smoothing: subpixel-antialiased;
}
#screen {
margin: 0;
padding: 0;
font-size: 13px;
height: 100%;
font: sans-serif;
}
.no-sel {
-moz-user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
-ms-user-select:none;
user-select:none;
-o-user-select:none;
}
.cell {
pointer-events: none;
}
.cell-version {
padding-left: 4px;
padding-top: 0.5em;
text-align: left;
display: inline-block;
float: left;
color: rgba(0, 0, 0, 0.75);
}
.cell-about {
padding-right: 24px;
padding-top: 0.5em;
text-align: right;
display: inline-block;
float: right;
}
.nav-link {
text-decoration: none;
color: rgba(0, 0, 0, 1.0);
}
#main-container {
font-size:12px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
textarea {
font-size:12px;
font-family: monospace;
}
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten_border { border: 1px solid black; }
canvas.emscripten {
border: 0px none;
background-color: black;
text-shadow: 4px 4px #1f1f1fb4;
}
.spinner {
height: 30px;
width: 30px;
margin: 0;
margin-top: 20px;
margin-left: 20px;
display: inline-block;
vertical-align: top;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 5px solid rgb(235, 235, 235);
border-right: 5px solid rgb(235, 235, 235);
border-bottom: 5px solid rgb(235, 235, 235);
border-top: 5px solid rgb(120, 120, 120);
border-radius: 100%;
background-color: rgb(189, 215, 46);
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
#status {
display: inline-block;
vertical-align: top;
margin-top: 30px;
margin-left: 20px;
font-weight: bold;
color: rgb(120, 120, 120);
}
#progress {
height: 20px;
width: 30px;
}
#output {
width: 800px;
height: 200px;
margin: 0 auto;
margin-top: 10px;
border-left: 0px;
border-right: 0px;
padding-left: 0px;
padding-right: 0px;
background-color: black;
color: white;
font-size:10px;
font-family: 'Lucida Console', Monaco, monospace;
outline: none;
}
.led-box {
height: 30px;
width: 25%;
margin: 10px 0;
float: left;
}
.led-box p {
font-size: 12px;
text-align: center;
margin: 1em;
}
.led-red {
margin: 0 auto;
width: 12px;
height: 12px;
background-color: #F00;
border-radius: 50%;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 12px;
-webkit-animation: blinkRed 0.5s infinite;
-moz-animation: blinkRed 0.5s infinite;
-ms-animation: blinkRed 0.5s infinite;
-o-animation: blinkRed 0.5s infinite;
animation: blinkRed 0.5s infinite;
}
@-webkit-keyframes blinkRed {
from { background-color: #F00; }
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
to { background-color: #F00; }
}
@-moz-keyframes blinkRed {
from { background-color: #F00; }
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
to { background-color: #F00; }
}
@-ms-keyframes blinkRed {
from { background-color: #F00; }
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
to { background-color: #F00; }
}
@-o-keyframes blinkRed {
from { background-color: #F00; }
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
to { background-color: #F00; }
}
@keyframes blinkRed {
from { background-color: #F00; }
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
to { background-color: #F00; }
}
.led-yellow {
margin: 0 auto;
width: 12px;
height: 12px;
background-color: #FF0;
border-radius: 50%;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 12px;
-webkit-animation: blinkYellow 1s infinite;
-moz-animation: blinkYellow 1s infinite;
-ms-animation: blinkYellow 1s infinite;
-o-animation: blinkYellow 1s infinite;
animation: blinkYellow 1s infinite;
}
@-webkit-keyframes blinkYellow {
from { background-color: #FF0; }
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
to { background-color: #FF0; }
}
@-moz-keyframes blinkYellow {
from { background-color: #FF0; }
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
to { background-color: #FF0; }
}
@-ms-keyframes blinkYellow {
from { background-color: #FF0; }
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
to { background-color: #FF0; }
}
@-o-keyframes blinkYellow {
from { background-color: #FF0; }
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
to { background-color: #FF0; }
}
@keyframes blinkYellow {
from { background-color: #FF0; }
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
to { background-color: #FF0; }
}
.led-green {
margin: 0 auto;
width: 12px;
height: 12px;
background-color: #ABFF00;
border-radius: 50%;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
}
.led-blue {
margin: 0 auto;
width: 18px;
height: 18px;
background-color: #24E0FF;
border-radius: 50%;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #006 0 -1px 9px, #3F8CFF 0 2px 14px;
}
table td {
border: 1px solid #e8e8e8;
}
table th, table td {
padding: 10px 10px;
}
table td {
border: 1px solid #e8e8e8;
}
table th, table td {
padding: 10px 10px;
}
td[Attributes Style] {
text-align: -webkit-center;
}
td {
display: table-cell;
vertical-align: inherit;
}
table {
margin-bottom: 30px;
width: 800px;
text-align: left;
color: #3f3f3f;
border-collapse: collapse;
border: 1px solid #e8e8e8;
}
table {
margin-bottom: 30px;
width: 800px;
text-align: left;
color: #3f3f3f;
border-collapse: collapse;
border: 1px solid #e8e8e8;
}
table {
border-collapse: separate;
border-spacing: 2px;
}
#description {
margin: 10px;
padding: 10px;
color: rgba(255, 255, 255, 1.00);
text-shadow: 2px 2px #1f1f1fb4;
}
.text-body {
color: rgba(255, 255, 255, 1.00);
text-shadow: 2px 2px #1f1f1fb4;
}
.cell-version {
padding-left: 4px;
padding-top: 0.5em;
text-align: left;
display: inline-block;
float: left;
color: rgba(255, 255, 255, 0.75);
text-shadow: 2px 2px #1f1f1fb4;
}
.cell-about {
padding-right: 24px;
padding-top: 0.5em;
text-align: right;
display: inline-block;
float: right;
}
.nav-link {
text-decoration: none;
color: rgba(255, 255, 255, 1.0);
text-shadow: 2px 2px #1f1f1fb4;
}
.nav-link2 {
text-decoration: none;
color: rgba(0, 255, 0, 1.0);
text-shadow: 2px 2px #1f1f1fb4;
}
svg {
-webkit-filter: invert(100%); /* safari 6.0 - 9.0 */
filter: invert(100%);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB