├── HelloWorld ├── build.sh └── main.c ├── HelloWorldNanoVG ├── build.sh ├── resources │ └── Roboto-Regular.ttf ├── Makefile └── main.c ├── HelloIndexedDB ├── build.sh └── main.c ├── HelloWorldGLFW ├── build.sh └── main.c ├── .gitignore ├── .gitmodules ├── HelloWAM ├── MyWAM-awp.js ├── build.sh ├── MyWAM.h ├── Makefile ├── MyWAM-awn.js ├── MyWAM.cpp └── MyWAM.html └── README.md /HelloWorld/build.sh: -------------------------------------------------------------------------------- 1 | emcc --emrun -o index.html main.c 2 | emrun --browser chrome index.html 3 | -------------------------------------------------------------------------------- /HelloWorldNanoVG/build.sh: -------------------------------------------------------------------------------- 1 | rm index.html 2 | emmake make 3 | emrun --browser chrome index.html 4 | -------------------------------------------------------------------------------- /HelloIndexedDB/build.sh: -------------------------------------------------------------------------------- 1 | emcc --emrun -o index.html main.c 2 | emrun --browser chrome index.html 3 | -------------------------------------------------------------------------------- /HelloWorldNanoVG/resources/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/olilarkin/ADC2018/HEAD/HelloWorldNanoVG/resources/Roboto-Regular.ttf -------------------------------------------------------------------------------- /HelloWorld/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | printf("hello world\n"); 7 | return 1; 8 | } 9 | -------------------------------------------------------------------------------- /HelloWorldGLFW/build.sh: -------------------------------------------------------------------------------- 1 | emcc -s USE_GLFW=3 -s USE_WEBGL2=0 -s FULL_ES3=1 --emrun -o index.html main.c; 2 | emrun --browser chrome index.html 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.html 3 | *.DS_Store 4 | /HelloWorldNanoVG/index.js 5 | /HelloWorldGLFW/index.js 6 | /HelloWorld/index.js 7 | /HelloWAM/MyWAM.wasm.js 8 | /HelloWAM/MyWAM.js 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "HelloWorldNanoVG/nanovg"] 2 | path = HelloWorldNanoVG/nanovg 3 | url = https://github.com/memononen/nanovg.git 4 | [submodule "HelloWAM/wam"] 5 | path = HelloWAM/wam 6 | url = https://github.com/webaudiomodules/api.git 7 | -------------------------------------------------------------------------------- /HelloWAM/MyWAM-awp.js: -------------------------------------------------------------------------------- 1 | class MyWAMProcessor extends AudioWorkletGlobalScope.WAMProcessor 2 | { 3 | constructor(options) { 4 | options = options || {} 5 | options.mod = AudioWorkletGlobalScope.WAM.MyWAM; 6 | super(options); 7 | } 8 | } 9 | 10 | registerProcessor("MyWAM", MyWAMProcessor); 11 | -------------------------------------------------------------------------------- /HelloWAM/build.sh: -------------------------------------------------------------------------------- 1 | rm MyWAM.js 2 | 3 | emmake make 4 | 5 | echo "AudioWorkletGlobalScope.WAM = AudioWorkletGlobalScope.WAM || {}; AudioWorkletGlobalScope.WAM.MyWAM = { ENVIRONMENT: 'WEB' };" > MyWAM.tmp.js; 6 | cat MyWAM.js >> MyWAM.tmp.js 7 | mv MyWAM.tmp.js MyWAM.js 8 | 9 | emrun --no_emrun_detect --browser chrome MyWAM.html 10 | -------------------------------------------------------------------------------- /HelloWAM/MyWAM.h: -------------------------------------------------------------------------------- 1 | #include "processor.h" 2 | 3 | using namespace WAM; 4 | 5 | class MyWAM : public Processor 6 | { 7 | public: 8 | MyWAM(); 9 | 10 | const char* init(uint32_t bufsize, uint32_t sr, void* pDesc) override; 11 | void onProcess(WAM::AudioBus* pAudio, void* pData) override; 12 | void onParam(uint32_t idparam, double value) override; 13 | 14 | private: 15 | float mGain = 1.f; 16 | }; 17 | -------------------------------------------------------------------------------- /HelloWAM/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = ./MyWAM.js 2 | 3 | SRC = MyWAM.cpp wam/wamsdk/processor.cpp 4 | 5 | CFLAGS = -std=c++11 -I./wam/wamsdk 6 | 7 | EXPORTS = "[\ 8 | '_createModule','_wam_init','_wam_terminate','_wam_resize', \ 9 | '_wam_onprocess', '_wam_onmidi', '_wam_onsysex', '_wam_onparam', \ 10 | '_wam_onmessageN', '_wam_onmessageS', '_wam_onmessageA', '_wam_onpatch' \ 11 | ]" 12 | 13 | LDFLAGS = -s EXPORTED_FUNCTIONS=$(EXPORTS) -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap', 'setValue', 'UTF8ToString']" -O2 14 | JSFLAGS = -s SINGLE_FILE=1 -s ALLOW_MEMORY_GROWTH=1 -s BINARYEN_ASYNC_COMPILATION=0 -s EXPORT_NAME="'AudioWorkletGlobalScope.WAM.MyWAM'" 15 | 16 | $(TARGET): $(OBJECTS) 17 | $(CC) $(CFLAGS) $(LDFLAGS) $(JSFLAGS) -o $@ $(SRC) -------------------------------------------------------------------------------- /HelloWAM/MyWAM-awn.js: -------------------------------------------------------------------------------- 1 | class MyWAMController extends WAMController 2 | { 3 | constructor (actx, options) { 4 | options = options || {}; 5 | options.numberOfInputs = 0; 6 | options.numberOfOutputs = 1; 7 | options.outputChannelCount = [2]; 8 | 9 | super(actx, "MyWAM", options); 10 | } 11 | 12 | static importScripts (actx) { 13 | var origin = location.origin + "/"; 14 | return new Promise( (resolve) => { 15 | actx.audioWorklet.addModule(origin + "MyWAM.js").then(() => { 16 | actx.audioWorklet.addModule(origin + "wam/wamsdk/wam-processor.js").then(() => { 17 | actx.audioWorklet.addModule(origin + "MyWAM-awp.js").then(() => { 18 | resolve(); 19 | }) }) }); 20 | }) 21 | } 22 | 23 | onmessage(msg) { 24 | if(msg.type == "descriptor") { 25 | createGUI(msg.data.parameters); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADC2018 2 | A collection of examples using emscripten for the audio developers conference [(see video of presentation).](https://www.youtube.com/watch?v=IRLxMhksUZ0) 3 | Code is mostly extracted and simplified from the emscripten tests. 4 | 5 | * Check out this repository recursively using the following command: 6 | ``` 7 | git clone --recursive https://github.com/olilarkin/ADC2018 8 | ``` 9 | * [Download and install emscripten](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html), following the instructions carefully 10 | 11 | * Add *source PATH_TO_EMSDK_DIR/emsdk_env.sh* to your .bash_profile, so that you get the toolchain environment variables Each time you launch the terminal. You should be able to type ```emcc -v``` in a terminal and get the version number. 12 | 13 | * Edit /etc/mime.types to add a type for wasm. Add a single line as follows: 14 | 15 | ```application/wasm wasm``` 16 | 17 | * Each folder has a shell script ```build.sh```, that should build and run the example. 18 | 19 | Enjoy! 20 | 21 | Oli Larkin 22 | 23 | 24 | -------------------------------------------------------------------------------- /HelloWorldGLFW/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define GLFW_INCLUDE_ES2 5 | #include 6 | 7 | GLFWwindow* g_window; 8 | 9 | void render(); 10 | void error_callback(int error, const char* description); 11 | 12 | void render() { 13 | static float g = 0.; 14 | glClearColor(g, g, g, 1.0f); 15 | glClear(GL_COLOR_BUFFER_BIT); 16 | g += 0.01; 17 | g = fmod(g, 1.); 18 | } 19 | 20 | void error_callback(int error, const char* description) { 21 | fprintf(stderr, "Error %d: %s\n", error, description); 22 | } 23 | 24 | int main() { 25 | 26 | glfwSetErrorCallback(error_callback); 27 | 28 | if (!glfwInit()) { 29 | printf("Could not create window.\n"); 30 | return -1; 31 | } 32 | 33 | glfwWindowHint(GLFW_RESIZABLE , 1); 34 | g_window = glfwCreateWindow(600, 450, "Hello GLFW", NULL, NULL); 35 | 36 | if (!g_window) { 37 | printf("Could not create window\n"); 38 | glfwTerminate(); 39 | return -1; 40 | } 41 | glfwMakeContextCurrent(g_window); 42 | 43 | emscripten_set_main_loop(render, 0, 1); 44 | 45 | glfwTerminate(); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /HelloWAM/MyWAM.cpp: -------------------------------------------------------------------------------- 1 | #include "MyWAM.h" 2 | #include 3 | 4 | MyWAM::MyWAM() 5 | { 6 | } 7 | 8 | const char* MyWAM::init(uint32_t bufsize, uint32_t sr, void* pDesc) 9 | { 10 | //Very ugly JSON cstring could be simplified using a library 11 | const char* descriptor = "{\"audio\": { \"inputs\": [{ \"id\":0, \"channels\":0 }], \"outputs\": [{ \"id\":0, \"channels\":2 }] },\"parameters\": [{\"id\":0, \"name\":\"Gain\", \"type\":\"float\", \"min\":0.000000, \"max\":100.000000, \"default\":100.000000, \"rate\":\"control\"}]}"; 12 | 13 | return descriptor; 14 | } 15 | 16 | #define BPFRAND() -1. + (2. * rand()/(RAND_MAX+1.) ) // returns random value between -1. and 1. 17 | 18 | void MyWAM::onProcess(WAM::AudioBus* pAudio, void* pData) 19 | { 20 | for (int i = 0; i < 128; i++) 21 | { 22 | pAudio->outputs[0][i] = BPFRAND() * mGain; 23 | pAudio->outputs[1][i] = pAudio->outputs[0][i]; 24 | } 25 | } 26 | 27 | void MyWAM::onParam(uint32_t idparam, double value) 28 | { 29 | switch (idparam) { 30 | case 0: 31 | mGain = value / 100.; 32 | break; 33 | default: 34 | break; 35 | } 36 | } 37 | 38 | extern "C" { void* createModule() { return new MyWAM(); } } 39 | 40 | -------------------------------------------------------------------------------- /HelloIndexedDB/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //call to write to sync file system to IndexedDB 5 | EMSCRIPTEN_KEEPALIVE void syncfs() 6 | { 7 | EM_ASM({ 8 | if(Module.syncdone == 1) { 9 | Module.syncdone = 0; 10 | FS.syncfs(false, function (err) { 11 | assert(!err); 12 | console.log("Synced to IDBFS..."); 13 | Module.syncdone = 1; 14 | }); 15 | } 16 | }); 17 | } 18 | 19 | EMSCRIPTEN_KEEPALIVE void fsready() 20 | { 21 | syncfs(); 22 | } 23 | 24 | int main() 25 | { 26 | EM_ASM( 27 | var name = "/ADC2018"; 28 | FS.mkdir(name); 29 | FS.mount(IDBFS, {}, name); 30 | Module.syncdone = 0; 31 | FS.syncfs(true, function (err) { 32 | assert(!err); 33 | console.log("Synced from IDBFS..."); 34 | Module.syncdone = 1; 35 | ccall('fsready', 'v'); 36 | }); 37 | ); 38 | 39 | //TODO: check for HelloWorld.txt existance and print it! 40 | FILE* pFile; 41 | pFile = fopen("/ADC2018/HelloWorld.txt", "w"); 42 | 43 | if (pFile != NULL) 44 | { 45 | fputs("HelloWorld", pFile); 46 | fclose(pFile); 47 | printf("wrote HelloWorld.txt"); 48 | syncfs(); 49 | } 50 | 51 | emscripten_exit_with_live_runtime(); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /HelloWorldNanoVG/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = index.html 2 | 3 | SRC = main.c nanovg/src/nanovg.c #nanovg/example/perf.c 4 | 5 | EXPORTS = "['_main','_nvgBeginFrame','_nvgCancelFrame','_nvgEndFrame','_nvgGlobalCompositeOperation','_nvgGlobalCompositeBlendFunc','_nvgGlobalCompositeBlendFuncSeparate','_nvgRGB','_nvgRGBf','_nvgRGBA','_nvgRGBAf','_nvgLerpRGBA','_nvgTransRGBA','_nvgTransRGBAf','_nvgHSL','_nvgHSLA','_nvgSave','_nvgRestore','_nvgReset','_nvgShapeAntiAlias','_nvgStrokeColor','_nvgStrokePaint','_nvgFillColor','_nvgFillPaint','_nvgMiterLimit','_nvgStrokeWidth','_nvgLineCap','_nvgLineJoin','_nvgGlobalAlpha','_nvgResetTransform','_nvgTransform','_nvgTranslate','_nvgRotate','_nvgSkewX','_nvgSkewY','_nvgScale','_nvgCurrentTransform','_nvgTransformIdentity','_nvgTransformTranslate','_nvgTransformScale','_nvgTransformRotate','_nvgTransformSkewX','_nvgTransformSkewY','_nvgTransformMultiply','_nvgTransformPremultiply','_nvgTransformInverse','_nvgTransformPoint','_nvgDegToRad','_nvgRadToDeg','_nvgImageSize','_nvgDeleteImage','_nvgLinearGradient','_nvgBoxGradient','_nvgRadialGradient','_nvgImagePattern','_nvgScissor','_nvgIntersectScissor','_nvgResetScissor','_nvgBeginPath','_nvgMoveTo','_nvgLineTo','_nvgBezierTo','_nvgQuadTo','_nvgArcTo','_nvgClosePath','_nvgPathWinding','_nvgArc','_nvgRect','_nvgRoundedRect','_nvgRoundedRectVarying','_nvgEllipse','_nvgCircle','_nvgFill','_nvgStroke','_nvgCreateGLES2','_nvgDeleteGLES2','_nvglCreateImageFromHandleGLES2','_nvglImageHandleGLES2','_nvgCreateFont','_nvgCreateFontMem','_nvgFindFont','_nvgAddFallbackFontId','_nvgAddFallbackFont','_nvgFontSize','_nvgFontBlur','_nvgTextLetterSpacing','_nvgTextLineHeight','_nvgTextAlign','_nvgFontFaceId','_nvgFontFace','_nvgText','_nvgTextBox','_nvgTextBounds','_nvgTextBoxBounds','_nvgTextGlyphPositions','_nvgTextMetrics','_nvgTextBreakLines']" 6 | 7 | CFLAGS = -I./ -Inanovg/src/ -O2 8 | LDFLAGS = -s EXPORTED_FUNCTIONS=$(EXPORTS) --emrun 9 | JSFLAGS = -s BINARYEN_ASYNC_COMPILATION=1 -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s USE_GLFW=3 -s USE_WEBGL2=1 -s FULL_ES3=1 -s ASSERTIONS=0 --preload-file resources/@/ 10 | $(TARGET): $(OBJECTS) 11 | $(CC) $(CFLAGS) $(LDFLAGS) $(JSFLAGS) -o $@ $(SRC) 12 | -------------------------------------------------------------------------------- /HelloWAM/MyWAM.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MyWAM 6 | 51 | 52 | 53 | 54 |
55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /HelloWorldNanoVG/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define GLFW_INCLUDE_ES2 3 | #define GLFW_INCLUDE_GLEXT 4 | #include 5 | #include "nanovg.h" 6 | #define NANOVG_GLES2_IMPLEMENTATION 7 | #include "nanovg_gl.h" 8 | #include "nanovg_gl_utils.h" 9 | 10 | #include 11 | 12 | void errorcb(int error, const char* desc) 13 | { 14 | printf("GLFW error %d: %s\n", error, desc); 15 | } 16 | 17 | GLFWwindow* window; 18 | NVGcontext* vg = NULL; 19 | 20 | void render() { 21 | int winWidth, winHeight; 22 | int fbWidth, fbHeight; 23 | float pxRatio; 24 | 25 | glfwGetWindowSize(window, &winWidth, &winHeight); 26 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 27 | // Calculate pixel ration for hi-dpi devices. 28 | pxRatio = (float)fbWidth / (float)winWidth; 29 | 30 | // Update and render 31 | glViewport(0, 0, fbWidth, fbHeight); 32 | glClearColor(0.f, 0.f, 1.f, 1.0f); 33 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 34 | 35 | glEnable(GL_BLEND); 36 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 37 | glEnable(GL_CULL_FACE); 38 | glDisable(GL_DEPTH_TEST); 39 | 40 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 41 | 42 | nvgFillColor(vg, nvgRGBf(1.f, 0.f, 0.f)); 43 | nvgCircle(vg, winWidth/2., winHeight/2., 100.); 44 | nvgFill(vg); 45 | 46 | nvgFontSize(vg, 50.); 47 | nvgFontFace(vg, "sans"); 48 | nvgFillColor(vg, nvgRGBf(0.f, 0.f, 0.f)); 49 | 50 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 51 | nvgText(vg, winWidth/2., winHeight/2., "Hello World", NULL); 52 | 53 | nvgEndFrame(vg); 54 | 55 | glEnable(GL_DEPTH_TEST); 56 | 57 | glfwSwapBuffers(window); 58 | glfwPollEvents(); 59 | } 60 | 61 | int main() 62 | { 63 | if (!glfwInit()) { 64 | printf("Failed to init GLFW."); 65 | return -1; 66 | } 67 | 68 | glfwSetErrorCallback(errorcb); 69 | glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 70 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 71 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 72 | 73 | window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); 74 | if (!window) { 75 | glfwTerminate(); 76 | return -1; 77 | } 78 | 79 | glfwMakeContextCurrent(window); 80 | 81 | vg = nvgCreateGLES2(NVG_ANTIALIAS | NVG_STENCIL_STROKES); 82 | if (vg == NULL) { 83 | printf("Could not init nanovg.\n"); 84 | return -1; 85 | } 86 | 87 | nvgCreateFont(vg, "sans", "Roboto-Regular.ttf"); 88 | 89 | glfwSwapInterval(0); 90 | 91 | glfwSetTime(0); 92 | 93 | emscripten_set_main_loop(render, 0, 1); 94 | 95 | nvgDeleteGLES2(vg); 96 | 97 | glfwTerminate(); 98 | return 0; 99 | } 100 | --------------------------------------------------------------------------------