diff --git a/Makefile.emscripten b/Makefile.emscripten index ac963e4..246cce4 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -10,7 +10,7 @@ EMFLAGS += -s TOTAL_MEMORY=134217728 EMFLAGS += -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 LFLAGS := $(EMFLAGS) -LFLAGS += -s EXPORTED_FUNCTIONS="['_main']" +LFLAGS += -s EXPORTED_FUNCTIONS="['_main','_musfade_setvolval_all']" docs/xsystem35.js: emterpretify_whitelist src/xsystem35 $(CC) xsystem35.bc $(LFLAGS) @emterpretify_whitelist -o $@ diff --git a/docs/style.css b/docs/style.css index c9bc0ed..6145407 100644 --- a/docs/style.css +++ b/docs/style.css @@ -29,3 +29,21 @@ canvas.emscripten { image-rendering: pixelated; image-rendering: -moz-crisp-edges; } + +#volume-control { + float: right; + margin-right: 20px; +} + +#volume-control-icon { + font-size: 20px; + width: 26px; + padding: 5px; + vertical-align: middle; +} + +#volume-control-slider { + width: 60px; + height: 30px; + vertical-align: middle; +} diff --git a/docs/xsystem35.html b/docs/xsystem35.html index ef3a375..dc68879 100644 --- a/docs/xsystem35.html +++ b/docs/xsystem35.html @@ -37,6 +37,11 @@ + +
+ + +
diff --git a/src/music_fader.c b/src/music_fader.c index b42d753..369bfb1 100644 --- a/src/music_fader.c +++ b/src/music_fader.c @@ -372,3 +372,10 @@ int musfade_setvolval(int *valance, int num) { return OK; } + +int musfade_setvolval_all(int val) { + for (int i = 0; i < 16; i++) { + prv.volval[i] = val; + } + return OK; +} diff --git a/src/sdl_video.c b/src/sdl_video.c index 261cbd2..f0268cb 100644 --- a/src/sdl_video.c +++ b/src/sdl_video.c @@ -24,6 +24,9 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include +#endif #include "portab.h" #include "system.h" @@ -63,6 +66,11 @@ int sdl_Initilize(void) { sdl_shadow_init(); +#ifdef __EMSCRIPTEN__ + // Workaround for https://github.com/emscripten-ports/SDL2/issues/41 + SDL_EventState(SDL_RELEASED, 0); +#endif + joy_open(); return 0; } diff --git a/web/shell/loader.ts b/web/shell/loader.ts index feab507..3a292b4 100644 --- a/web/shell/loader.ts +++ b/web/shell/loader.ts @@ -41,9 +41,7 @@ class ImageLoader { FS.writeFile('xsystem35.gr', grGenerator.generate()); FS.writeFile('.xsys35rc', xsystem35.xsys35rc); - $('#loader').hidden = true; - $('#xsystem35').hidden = false; - setTimeout(() => { Module.callMain(); }, 0); + this.shell.run(); } private handleFileSelect(evt:Event) { diff --git a/web/shell/shell.ts b/web/shell/shell.ts index a3c0f27..97d1f68 100644 --- a/web/shell/shell.ts +++ b/web/shell/shell.ts @@ -1,5 +1,8 @@ var $: (selector:string)=>HTMLElement = document.querySelector.bind(document); +// xsystem35 exported functions +declare function _musfade_setvolval_all(vol: number): void; + declare namespace Module { var noInitialRun: boolean; @@ -29,6 +32,7 @@ class System35Shell { private imageLoader: ImageLoader; status: HTMLElement = document.getElementById('status'); private zoom:ZoomManager; + private volumeControl: VolumeControl; constructor() { this.imageLoader = new ImageLoader(this); @@ -56,10 +60,21 @@ class System35Shell { FS.syncfs(true, (err) => { Module.removeRunDependency('syncfs') }); } ]; - xsystem35.cdPlayer = new CDPlayer(this.imageLoader); + this.volumeControl = new VolumeControl(); + this.volumeControl.addEventListener(this.updateVolume.bind(this)); + xsystem35.cdPlayer = new CDPlayer(this.imageLoader, this.volumeControl); this.zoom = new ZoomManager(); } + run() { + $('#loader').hidden = true; + $('#xsystem35').hidden = false; + setTimeout(() => { + Module.callMain(); + this.updateVolume(); + }, 0); + } + setStatus(text: string) { console.log(text); this.status.innerHTML = text; @@ -79,6 +94,10 @@ class System35Shell { }); }, 100); } + + private updateVolume() { + _musfade_setvolval_all(Math.round(this.volumeControl.volume() * 100)); + } } class CDPlayer { @@ -86,8 +105,10 @@ class CDPlayer { private blobs: Blob[]; private currentTrack: number; - constructor(private imageLoader:ImageLoader) { + constructor(private imageLoader:ImageLoader, private volumeControl: VolumeControl) { this.blobs = []; + this.volumeControl.addEventListener(this.onVolumeChanged.bind(this)); + this.audio.volume = this.volumeControl.volume(); } play(track:number, loop:number) { @@ -120,6 +141,10 @@ class CDPlayer { this.audio.load(); this.audio.play(); } + + private onVolumeChanged(evt: CustomEvent) { + this.audio.volume = evt.detail; + } } class ZoomManager { @@ -131,7 +156,7 @@ class ZoomManager { this.canvas = $('#canvas'); this.zoomSelect = $('#zoom'); this.zoomSelect.addEventListener('change', this.handleZoom.bind(this)); - this.zoomSelect.value = localStorage.getItem('zoom'); + this.zoomSelect.value = localStorage.getItem('zoom') || '1'; this.smoothingCheckbox = $('#smoothing'); this.smoothingCheckbox.addEventListener('change', this.handleSmoothing.bind(this)); if (localStorage.getItem('smoothing') == 'false') { @@ -163,4 +188,67 @@ class ZoomManager { } } +class VolumeControl { + private vol: number; // 0.0 - 1.0 + private muted: boolean; + private elem: HTMLElement; + private icon: HTMLElement; + private slider: HTMLInputElement; + + constructor() { + this.vol = Number(localStorage.getItem('volume') || 1); + this.muted = false; + + this.elem = document.getElementById('volume-control'); + this.icon = document.getElementById('volume-control-icon'); + this.slider = document.getElementById('volume-control-slider'); + this.slider.value = String(Math.round(this.vol * 100)); + + this.icon.addEventListener('click', this.onIconClicked.bind(this)); + this.slider.addEventListener('input', this.onSliderValueChanged.bind(this)); + this.slider.addEventListener('change', this.onSliderValueSettled.bind(this)); + } + + volume(): number { + return this.muted ? 0 : parseInt(this.slider.value) / 100; + } + + addEventListener(handler: (evt: CustomEvent) => any) { + this.elem.addEventListener('volumechange', handler); + } + + private onIconClicked(e: Event) { + this.muted = !this.muted; + if (this.muted) { + this.icon.classList.remove('fa-volume-up'); + this.icon.classList.add('fa-volume-off'); + this.slider.value = '0'; + } else { + this.icon.classList.remove('fa-volume-off'); + this.icon.classList.add('fa-volume-up'); + this.slider.value = String(Math.round(this.vol * 100)); + } + this.dispatchEvent(); + } + + private onSliderValueChanged(e: Event) { + this.vol = parseInt(this.slider.value) / 100; + if (this.vol > 0 && this.muted) { + this.muted = false; + this.icon.classList.remove('fa-volume-off'); + this.icon.classList.add('fa-volume-up'); + } + this.dispatchEvent(); + } + + private onSliderValueSettled(e: Event) { + localStorage.setItem('volume', this.vol + ''); + } + + private dispatchEvent() { + var event = new CustomEvent('volumechange', {detail: this.volume()}); + this.elem.dispatchEvent(event); + } +} + xsystem35.shell = new System35Shell();