Merge pull request #17928 from BinBashBanana/master

Rewrite RWebAudio driver
This commit is contained in:
LibretroAdmin 2025-05-26 12:53:27 +02:00 committed by GitHub
commit cd4894d627
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 330 additions and 196 deletions

View File

@ -3,6 +3,7 @@
- EMSCRIPTEN/RWEBCAM: Fix camera driver
- EMSCRIPTEN/RWEBINPUT: Add accelerometer/gyroscope support
- EMSCRIPTEN/RWEBPAD: Add rumble support
- EMSCRIPTEN/RWEBAUDIO: Rewrite driver, set as default audio driver
# 1.21.0
- 3DS: Fix unique IDs for newer cores

View File

@ -27,7 +27,7 @@ HAVE_GLSL = 1
HAVE_SCREENSHOTS = 1
HAVE_REWIND = 1
HAVE_AUDIOMIXER = 1
HAVE_CC_RESAMPLER = 1
HAVE_CC_RESAMPLER ?= 1
HAVE_EGL ?= 0
HAVE_OPENGLES = 1
HAVE_RJPEG = 0
@ -54,8 +54,6 @@ HAVE_BSV_MOVIE = 1
HAVE_CHD ?= 0
HAVE_NETPLAYDISCOVERY ?= 0
HAVE_AL ?= 1
# enables pthreads, requires special headers on the web server:
# see https://web.dev/articles/coop-coep
HAVE_THREADS ?= 0
@ -63,18 +61,14 @@ HAVE_THREADS ?= 0
# requires HAVE_THREADS
HAVE_AUDIOWORKLET ?= 0
# WARNING -- READ BEFORE ENABLING
# The rwebaudio driver is known to have several audio bugs, such as
# minor crackling, or the entire page freezing/crashing.
# It works perfectly on chrome, but even firefox has really bad audio quality.
# I should also note, the driver on iOS is completely broken (crashes the page).
# You have been warned.
HAVE_RWEBAUDIO ?= 0
# doesn't work on PROXY_TO_PTHREAD
HAVE_RWEBAUDIO ?= 1
# whether the browser thread is allowed to block to wait for audio to play,
# may lead to the issues mentioned above.
# currently this variable is only used by audioworklet;
# rwebaudio will always busywait and openal will never busywait.
# requires ASYNC or PROXY_TO_PTHREAD
HAVE_AL ?= 0
# whether the browser thread is allowed to block to wait for audio to play, not CPU usage-friendly!
# currently this variable is only used by rwebaudio and audioworklet; openal will never busywait.
ALLOW_AUDIO_BUSYWAIT ?= 0
# minimal asyncify; better performance than full asyncify,
@ -196,6 +190,9 @@ endif
ifeq ($(HAVE_RWEBAUDIO), 1)
LDFLAGS += --js-library emscripten/library_rwebaudio.js
DEFINES += -DHAVE_RWEBAUDIO
ifeq ($(PROXY_TO_PTHREAD), 1)
$(error ERROR: RWEBAUDIO is incompatible with PROXY_TO_PTHREAD)
endif
endif
ifeq ($(HAVE_AUDIOWORKLET), 1)
@ -205,18 +202,24 @@ ifeq ($(HAVE_AUDIOWORKLET), 1)
ifeq ($(HAVE_THREADS), 0)
$(error ERROR: AUDIOWORKLET requires HAVE_THREADS)
endif
ifeq ($(PROXY_TO_PTHREAD), 1)
else ifeq ($(ASYNC), 1)
endif
ifeq ($(HAVE_AL), 1)
LDFLAGS += -lopenal
DEFINES += -DHAVE_AL
endif
ifeq ($(PROXY_TO_PTHREAD), 1)
else ifeq ($(ASYNC), 1)
else
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
ifeq ($(MIN_ASYNC), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_ASYNC_BLOCK
else
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
ifeq ($(MIN_ASYNC), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_ASYNC_BLOCK
else
DEFINES += -DEMSCRIPTEN_AUDIO_FAKE_BLOCK
endif
ifneq ($(ALLOW_AUDIO_BUSYWAIT), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
endif
DEFINES += -DEMSCRIPTEN_AUDIO_FAKE_BLOCK
endif
ifneq ($(ALLOW_AUDIO_BUSYWAIT), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
endif
endif
@ -227,16 +230,11 @@ endif
# explanation of some of these defines:
# EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK: audio blocking occurs in the main loop instead of in the audio driver functions.
# EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK: along with above, enables external blocking in the write function.
# ALLOW_AUDIO_BUSYWAIT: write function will busywait. init function may still use an external block.
# EMSCRIPTEN_AUDIO_BUSYWAIT: write function will busywait. init function may still use an external block.
# EMSCRIPTEN_AUDIO_ASYNC_BLOCK: external block uses emscripten_sleep (requires MIN_ASYNC).
# EMSCRIPTEN_AUDIO_FAKE_BLOCK: external block uses main loop timing (doesn't require asyncify).
# when building with either PROXY_TO_PTHREAD or ASYNC (full asyncify), none of the above are required.
ifeq ($(HAVE_AL), 1)
LDFLAGS += -lopenal
DEFINES += -DHAVE_AL
endif
ifeq ($(HAVE_THREADS), 1)
LDFLAGS += -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD_POOL_SIZE)
CFLAGS += -pthread -s SHARED_MEMORY

View File

@ -1,6 +1,7 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2015 - Michael Lelli
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2025 - OlyB
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
@ -17,81 +18,230 @@
#include <stdlib.h>
#include <unistd.h>
#include <boolean.h>
#include <retro_timers.h>
#include "../audio_driver.h"
#include "../../verbosity.h"
#include "../../frontend/drivers/platform_emscripten.h"
#define RWEBAUDIO_BUFFER_SIZE_MS 10
/* forward declarations */
unsigned RWebAudioSampleRate(void);
void *RWebAudioInit(unsigned latency);
ssize_t RWebAudioWrite(const void *s, size_t len);
ssize_t RWebAudioQueueBuffer(size_t num_frames, float *left, float *right);
bool RWebAudioStop(void);
bool RWebAudioStart(void);
void RWebAudioSetNonblockState(bool state);
void RWebAudioFree(void);
size_t RWebAudioWriteAvail(void);
size_t RWebAudioBufferSize(void);
size_t RWebAudioWriteAvailFrames(void);
size_t RWebAudioBufferSizeFrames(void);
void RWebAudioRecalibrateTime(void);
bool RWebAudioResumeCtx(void);
typedef struct rweb_audio
typedef struct rwebaudio_data
{
bool is_paused;
} rweb_audio_t;
size_t tmpbuf_frames;
size_t tmpbuf_offset;
float *tmpbuf_left;
float *tmpbuf_right;
bool nonblock;
bool running;
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
bool block_requested;
#endif
} rwebaudio_data_t;
static rwebaudio_data_t *rwebaudio_static_data = NULL;
static void rwebaudio_free(void *data)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return;
RWebAudioFree();
free(data);
if (rwebaudio->tmpbuf_left)
free(rwebaudio->tmpbuf_left);
if (rwebaudio->tmpbuf_right)
free(rwebaudio->tmpbuf_right);
free(rwebaudio);
rwebaudio_static_data = NULL;
}
static void *rwebaudio_init(const char *device, unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)calloc(1, sizeof(rweb_audio_t));
rwebaudio_data_t *rwebaudio;
if (rwebaudio_static_data)
{
RARCH_ERR("[RWebAudio] Tried to start already running driver!\n");
return NULL;
}
rwebaudio = (rwebaudio_data_t*)calloc(1, sizeof(rwebaudio_data_t));
if (!rwebaudio)
return NULL;
if (RWebAudioInit(latency))
*new_rate = RWebAudioSampleRate();
if (!RWebAudioInit(latency))
{
RARCH_ERR("[RWebAudio] Failed to initialize driver!\n");
return NULL;
}
rwebaudio_static_data = rwebaudio;
*new_rate = RWebAudioSampleRate();
rwebaudio->tmpbuf_frames = RWEBAUDIO_BUFFER_SIZE_MS * *new_rate / 1000;
rwebaudio->tmpbuf_left = memalign(sizeof(float), rwebaudio->tmpbuf_frames * sizeof(float));
rwebaudio->tmpbuf_right = memalign(sizeof(float), rwebaudio->tmpbuf_frames * sizeof(float));
RARCH_LOG("[RWebAudio] Device rate: %d Hz.\n", *new_rate);
RARCH_LOG("[RWebAudio] Buffer size: %lu bytes.\n", RWebAudioBufferSizeFrames() * 2 * sizeof(float));
return rwebaudio;
}
static ssize_t rwebaudio_write(void *data, const void *s, size_t len)
{
return RWebAudioWrite(s, len);
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
const float *samples = (const float*)s;
size_t num_frames = len / 2 / sizeof(float);
size_t written = 0;
if (!rwebaudio)
return -1;
while (num_frames)
{
rwebaudio->tmpbuf_left[rwebaudio->tmpbuf_offset] = *(samples++);
rwebaudio->tmpbuf_right[rwebaudio->tmpbuf_offset] = *(samples++);
num_frames--;
if (++rwebaudio->tmpbuf_offset == rwebaudio->tmpbuf_frames)
{
size_t queued = RWebAudioQueueBuffer(rwebaudio->tmpbuf_frames, rwebaudio->tmpbuf_left, rwebaudio->tmpbuf_right);
rwebaudio->tmpbuf_offset = 0;
/* fast-forward or context is suspended */
if (queued < rwebaudio->tmpbuf_frames)
break;
written += queued;
}
}
if (rwebaudio->nonblock)
return written;
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
if (RWebAudioWriteAvailFrames() == 0)
{
rwebaudio->block_requested = true;
platform_emscripten_enter_fake_block(1);
}
#endif
/* async external block doesn't need to do anything else */
#else
while (RWebAudioWriteAvailFrames() == 0)
{
#ifdef EMSCRIPTEN_FULL_ASYNCIFY
retro_sleep(1);
#endif
RWebAudioResumeCtx();
}
#endif
return written;
}
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
/* returns true if fake block should continue */
bool rwebaudio_external_block(void)
{
rwebaudio_data_t *rwebaudio = rwebaudio_static_data;
if (!rwebaudio)
return false;
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
if (!rwebaudio->block_requested)
return false;
#endif
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
while (!rwebaudio->nonblock && RWebAudioWriteAvailFrames() == 0)
{
RWebAudioResumeCtx();
#ifdef EMSCRIPTEN_AUDIO_ASYNC_BLOCK
retro_sleep(1);
#else
return true;
#endif
}
#endif
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
rwebaudio->block_requested = false;
platform_emscripten_exit_fake_block();
return true; /* return to RAF if needed */
#endif
return false;
}
#endif
void rwebaudio_recalibrate_time(void)
{
if (rwebaudio_static_data)
RWebAudioRecalibrateTime();
}
static bool rwebaudio_stop(void *data)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)data;
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return false;
rwebaudio->is_paused = true;
rwebaudio->running = false;
return RWebAudioStop();
}
static void rwebaudio_set_nonblock_state(void *data, bool state)
{
RWebAudioSetNonblockState(state);
}
static bool rwebaudio_alive(void *data)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)data;
if (!rwebaudio)
return false;
return !rwebaudio->is_paused;
}
static bool rwebaudio_start(void *data, bool is_shutdown)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)data;
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return false;
rwebaudio->is_paused = false;
rwebaudio->running = true;
return RWebAudioStart();
}
static size_t rwebaudio_write_avail(void *data) {return RWebAudioWriteAvail();}
static size_t rwebaudio_buffer_size(void *data) {return RWebAudioBufferSize();}
static bool rwebaudio_alive(void *data)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return false;
return rwebaudio->running;
}
static void rwebaudio_set_nonblock_state(void *data, bool state)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return;
rwebaudio->nonblock = state;
RWebAudioSetNonblockState(state);
}
static size_t rwebaudio_write_avail(void *data)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
size_t avail_frames;
if (!rwebaudio)
return 0;
avail_frames = RWebAudioWriteAvailFrames();
if (avail_frames > rwebaudio->tmpbuf_offset)
return (avail_frames - rwebaudio->tmpbuf_offset) * 2 * sizeof(float);
return 0;
}
static size_t rwebaudio_buffer_size(void *data)
{
return RWebAudioBufferSizeFrames() * 2 * sizeof(float);
}
static bool rwebaudio_use_float(void *data) { return true; }
audio_driver_t audio_rwebaudio = {

View File

@ -1177,7 +1177,7 @@
/* Desired audio latency in milliseconds. Might not be honored
* if driver can't provide given latency. */
#if defined(ANDROID) || defined(RETROFW) || defined(MIYOO) || (defined(EMSCRIPTEN) && !defined(HAVE_AUDIOWORKLET))
#if defined(ANDROID) || defined(RETROFW) || defined(MIYOO) || (defined(EMSCRIPTEN) && defined(HAVE_AL))
/* For most Android devices, 64ms is way too low. */
#define DEFAULT_OUT_LATENCY 128
#define DEFAULT_IN_LATENCY 128
@ -1683,7 +1683,7 @@
#if defined(__QNX__) || defined(_XBOX1) || defined(_XBOX360) || (defined(__MACH__) && defined(IOS)) || defined(ANDROID) || defined(WIIU) || defined(HAVE_NEON) || defined(GEKKO) || defined(__ARM_NEON__) || defined(__PS3__)
#define DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL RESAMPLER_QUALITY_LOWER
#elif defined(PSP) || defined(_3DS) || defined(VITA) || defined(PS2) || defined(DINGUX) || defined(EMSCRIPTEN)
#elif defined(PSP) || defined(_3DS) || defined(VITA) || defined(PS2) || defined(DINGUX)
#define DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL RESAMPLER_QUALITY_LOWEST
#else
#define DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL RESAMPLER_QUALITY_NORMAL

View File

@ -586,7 +586,7 @@ static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_
#if defined(RS90) || defined(MIYOO)
static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_NEAREST;
#elif defined(PSP) || defined(EMSCRIPTEN)
#elif defined(PSP) || (defined(EMSCRIPTEN) && defined(HAVE_CC_RESAMPLER))
static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_CC;
#else
static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_SINC;

View File

@ -1,140 +1,81 @@
//"use strict";
var LibraryRWebAudio = {
$RA__deps: ['$Browser'],
$RA: {
BUFFER_SIZE: 2048,
$RWA: {
/* add 10 ms of silence on start, seems to prevent underrun on start/unpausing (terrible crackling in firefox) */
MIN_START_OFFSET_SEC: 0.01,
/* firefox and safari need more latency (transparent to audio driver) */
EXTRA_LATENCY_SEC_NONCHROME: 0.01,
EXTRA_LATENCY_SEC_CHROME: 0,
PLATFORM_EMSCRIPTEN_BROWSER_CHROMIUM: 1,
context: null,
buffers: [],
numBuffers: 0,
bufIndex: 0,
bufOffset: 0,
startTime: 0,
contextRunning: false,
nonblock: false,
currentTimeWorkaround: false,
setStartTime: function() {
if (RA.context.currentTime) {
RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000;
Module["resumeMainLoop"]();
} else window['setTimeout'](RA.setStartTime, 0);
},
getCurrentPerfTime: function() {
if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000;
else return 0;
},
process: function(queueBuffers) {
var currentTime = RA.getCurrentPerfTime();
for (var i = 0; i < RA.bufIndex; i++) {
if (RA.buffers[i].endTime !== 0 && RA.buffers[i].endTime < currentTime) {
RA.buffers[i].endTime = 0;
var buf = RA.buffers.splice(i, 1);
RA.buffers[RA.numBuffers - 1] = buf[0];
i--;
RA.bufIndex--;
} else if (!RA.startTime) {
RA.setStartTime();
}
}
},
fillBuffer: function(buf, samples) {
var count = 0;
const leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0);
const rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1);
while (samples && RA.bufOffset !== RA.BUFFER_SIZE) {
leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}};
rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}};
RA.bufOffset++;
count++;
samples--;
}
return count;
},
queueAudio: function() {
var index = RA.bufIndex;
var startTime;
if (RA.bufIndex) startTime = RA.buffers[RA.bufIndex - 1].endTime;
else startTime = RA.context.currentTime;
RA.buffers[index].endTime = startTime + RA.buffers[index].duration;
const bufferSource = RA.context.createBufferSource();
bufferSource.buffer = RA.buffers[index];
bufferSource.connect(RA.context.destination);
bufferSource.start(startTime);
RA.bufIndex++;
RA.bufOffset = 0;
},
block: function() {
do {
RA.process();
} while (RA.bufIndex === RA.numBuffers);
}
endTime: 0,
latency: 0,
virtualBufferFrames: 0,
currentTimeDiff: 0,
extraLatencySec: 0
},
RWebAudioInit: function(latency) {
var ac = window['AudioContext'] || window['webkitAudioContext'];
/* AudioContext.currentTime can be inaccurate: https://bugzilla.mozilla.org/show_bug.cgi?id=901247 */
$RWebAudioGetCurrentTime: function() {
return performance.now() / 1000 - RWA.currentTimeDiff;
},
$RWebAudioStateChangeCB: function() {
RWA.contextRunning = RWA.context.state == "running";
},
RWebAudioResumeCtx: function() {
if (!RWA.contextRunning) RWA.context.resume();
return RWA.contextRunning;
},
RWebAudioInit__deps: ["$RWebAudioStateChangeCB", "platform_emscripten_get_browser"],
RWebAudioInit: function(latency) {
var ac = window.AudioContext || window.webkitAudioContext;
if (!ac) return 0;
RA.context = new ac();
RWA.context = new ac();
RWA.currentTimeDiff = performance.now() / 1000 - RWA.context.currentTime;
RWA.nonblock = false;
RWA.endTime = 0;
RWA.latency = latency;
RWA.virtualBufferFrames = Math.round(RWA.latency * RWA.context.sampleRate / 1000);
RWA.context.addEventListener("statechange", RWebAudioStateChangeCB);
RWebAudioStateChangeCB();
RWA.extraLatencySec = (_platform_emscripten_get_browser() == RWA.PLATFORM_EMSCRIPTEN_BROWSER_CHROMIUM) ? RWA.EXTRA_LATENCY_SEC_CHROME : RWA.EXTRA_LATENCY_SEC_NONCHROME;
RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0;
if (RA.numBuffers < 2) RA.numBuffers = 2;
for (var i = 0; i < RA.numBuffers; i++) {
RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate);
RA.buffers[i].endTime = 0
}
RA.nonblock = false;
RA.startTime = 0;
// chrome hack to get currentTime running
RA.context.createGain();
window['setTimeout'](RA.setStartTime, 0);
Module["pauseMainLoop"]();
return 1;
},
RWebAudioSampleRate: function() {
return RA.context.sampleRate;
return RWA.context.sampleRate;
},
RWebAudioWrite: function (buf, size) {
RA.process();
var samples = size / 8;
var count = 0;
RWebAudioQueueBuffer__deps: ["$RWebAudioGetCurrentTime", "RWebAudioResumeCtx", "RWebAudioWriteAvailFrames"],
RWebAudioQueueBuffer: function(num_frames, left, right) {
if (RWA.nonblock && _RWebAudioWriteAvailFrames() < num_frames) return 0;
if (!_RWebAudioResumeCtx()) return 0;
while (samples) {
if (RA.bufIndex === RA.numBuffers) {
if (RA.nonblock) break;
else RA.block();
}
var buffer = RWA.context.createBuffer(2, num_frames, RWA.context.sampleRate);
buffer.getChannelData(0).set(HEAPF32.subarray(left >> 2, (left >> 2) + num_frames));
buffer.getChannelData(1).set(HEAPF32.subarray(right >> 2, (right >> 2) + num_frames));
var bufferSource = RWA.context.createBufferSource();
bufferSource.buffer = buffer;
bufferSource.connect(RWA.context.destination);
var fill = RA.fillBuffer(buf, samples);
samples -= fill;
count += fill;
buf += fill * 8;
var currentTime = RWebAudioGetCurrentTime();
/* when empty, start rounded up to nearest 1 ms, add MIN_START_OFFSET_SEC */
var startTime = RWA.endTime > currentTime ? RWA.endTime : Math.ceil(currentTime * 1000) / 1000 + RWA.MIN_START_OFFSET_SEC;
RWA.endTime = startTime + buffer.duration;
bufferSource.start(startTime + RWA.extraLatencySec);
if (RA.bufOffset === RA.BUFFER_SIZE) {
RA.queueAudio();
}
}
return count * 8;
return num_frames;
},
RWebAudioStop: function() {
RA.bufIndex = 0;
RA.bufOffset = 0;
return true;
},
@ -143,29 +84,32 @@ var LibraryRWebAudio = {
},
RWebAudioSetNonblockState: function(state) {
RA.nonblock = state;
RWA.nonblock = state;
},
RWebAudioFree__deps: ["$RWebAudioStateChangeCB"],
RWebAudioFree: function() {
RA.bufIndex = 0;
RA.bufOffset = 0;
RWA.context.removeEventListener("statechange", RWebAudioStateChangeCB);
RWA.context.close();
RWA.contextRunning = false;
},
RWebAudioBufferSize: function() {
return RA.numBuffers * RA.BUFFER_SIZE * 8;
RWebAudioBufferSizeFrames: function() {
return RWA.virtualBufferFrames;
},
RWebAudioWriteAvail: function() {
RA.process();
return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8;
RWebAudioWriteAvailFrames__deps: ["$RWebAudioGetCurrentTime"],
RWebAudioWriteAvailFrames: function() {
var avail = Math.round(((RWA.latency / 1000) - RWA.endTime + RWebAudioGetCurrentTime()) * RWA.context.sampleRate);
if (avail <= 0) return 0;
if (avail >= RWA.virtualBufferFrames) return RWA.virtualBufferFrames;
return avail;
},
RWebAudioRecalibrateTime: function() {
if (RA.startTime) {
RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000;
}
if (RWA.contextRunning) RWA.currentTimeDiff = performance.now() / 1000 - RWA.context.currentTime;
}
};
autoAddDeps(LibraryRWebAudio, '$RA');
autoAddDeps(LibraryRWebAudio, '$RWA');
addToLibrary(LibraryRWebAudio);

View File

@ -532,6 +532,16 @@ void platform_emscripten_set_canvas_size(int width, int height)
PlatformEmscriptenSetCanvasSize(width, height);
}
enum platform_emscripten_browser platform_emscripten_get_browser(void)
{
return emscripten_platform_data->browser;
}
enum platform_emscripten_os platform_emscripten_get_os(void)
{
return emscripten_platform_data->os;
}
/* frontend driver impl */
static void frontend_emscripten_get_env(int *argc, char *argv[],

View File

@ -184,4 +184,18 @@ void platform_emscripten_set_wake_lock(bool state);
*/
void platform_emscripten_set_canvas_size(int width, int height);
/**
* Get the browser that the program is running in.
*
* @return enum platform_emscripten_browser
*/
enum platform_emscripten_browser platform_emscripten_get_browser(void);
/**
* Get the OS that the program is running in.
*
* @return enum platform_emscripten_os
*/
enum platform_emscripten_os platform_emscripten_get_os(void);
#endif

View File

@ -6004,11 +6004,17 @@ int rarch_main(int argc, char *argv[], void *data)
#if defined(EMSCRIPTEN)
#if defined(EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK) && defined(HAVE_AUDIOWORKLET)
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
#ifdef HAVE_AUDIOWORKLET
bool audioworklet_external_block(void);
#endif
#ifdef HAVE_RWEBAUDIO
void RWebAudioRecalibrateTime(void);
bool rwebaudio_external_block(void);
#endif
#endif
#ifdef HAVE_RWEBAUDIO
void rwebaudio_recalibrate_time(void);
#endif
void emscripten_mainloop(void)
@ -6033,13 +6039,19 @@ void emscripten_mainloop(void)
if (platform_emscripten_should_drop_iter())
return;
#if defined(EMSCRIPTEN_AUDIO_FAKE_BLOCK) && defined(HAVE_AUDIOWORKLET)
#ifdef HAVE_RWEBAUDIO
rwebaudio_recalibrate_time();
#endif
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
#ifdef HAVE_AUDIOWORKLET
if (audioworklet_external_block())
return;
#endif
#ifdef HAVE_RWEBAUDIO
RWebAudioRecalibrateTime();
if (rwebaudio_external_block())
return;
#endif
#endif
emscripten_frame_count++;
@ -6064,8 +6076,13 @@ void emscripten_mainloop(void)
ret = runloop_iterate();
#if defined(EMSCRIPTEN_AUDIO_ASYNC_BLOCK) && defined(HAVE_AUDIOWORKLET)
#ifdef EMSCRIPTEN_AUDIO_ASYNC_BLOCK
#ifdef HAVE_AUDIOWORKLET
audioworklet_external_block();
#endif
#ifdef HAVE_RWEBAUDIO
rwebaudio_external_block();
#endif
#endif
task_queue_check();