mirror of
https://github.com/libretro/RetroArch
synced 2025-03-03 04:14:00 +00:00
new audio core, RWebAudio. Glitchy, but works well, even with requestAnimationFrame callbacks
This commit is contained in:
parent
c30d0287d1
commit
04be8cbee2
@ -22,6 +22,7 @@ OBJ = frontend/frontend_emscripten.o \
|
||||
screenshot.o \
|
||||
cheats.o \
|
||||
audio/utils.o \
|
||||
emscripten/RWebAudio.o \
|
||||
input/overlay.o \
|
||||
fifo_buffer.o \
|
||||
gfx/scaler/scaler.o \
|
||||
@ -39,7 +40,6 @@ OBJ = frontend/frontend_emscripten.o \
|
||||
performance.o
|
||||
|
||||
HAVE_OPENGL = 1
|
||||
HAVE_AL = 1
|
||||
HAVE_RGUI = 1
|
||||
HAVE_SDL = 1
|
||||
HAVE_SDL_IMAGE = 1
|
||||
@ -59,7 +59,7 @@ libretro = libretro_emscripten.bc
|
||||
|
||||
LIBS = -lm
|
||||
DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\"
|
||||
LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY)
|
||||
LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js
|
||||
|
||||
ifeq ($(SCALER_NO_SIMD), 1)
|
||||
DEFINES += -DSCALER_NO_SIMD
|
||||
@ -90,12 +90,6 @@ ifeq ($(HAVE_OPENGL), 1)
|
||||
DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
OBJ += audio/openal.o
|
||||
DEFINES += -DHAVE_AL
|
||||
LIBS += -lopenal
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_ZLIB), 1)
|
||||
OBJ += gfx/rpng/rpng.o file_extract.o
|
||||
DEFINES += -DHAVE_ZLIB
|
||||
@ -165,6 +159,7 @@ clean:
|
||||
rm -f gfx/fonts/*.o
|
||||
rm -f gfx/py_state/*.o
|
||||
rm -f gfx/rpng/*.o
|
||||
rm -f gfx/glsym/*.o
|
||||
rm -f record/*.o
|
||||
rm -f input/*.o
|
||||
rm -f tools/*.o
|
||||
|
@ -63,6 +63,7 @@ enum
|
||||
AUDIO_PS3,
|
||||
AUDIO_XENON360,
|
||||
AUDIO_WII,
|
||||
AUDIO_RWEBAUDIO,
|
||||
AUDIO_NULL,
|
||||
|
||||
INPUT_ANDROID,
|
||||
@ -130,6 +131,8 @@ enum
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_SL
|
||||
#elif defined(HAVE_DSOUND)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND
|
||||
#elif defined(EMSCRIPTEN)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_RWEBAUDIO
|
||||
#elif defined(HAVE_SDL)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
|
||||
#elif defined(HAVE_XAUDIO)
|
||||
@ -335,6 +338,8 @@ static const bool rate_control = false;
|
||||
// Rate control delta. Defines how much rate_control is allowed to adjust input rate.
|
||||
#if defined(__QNX__)
|
||||
static const float rate_control_delta = 0.000;
|
||||
#elif defined(EMSCRIPTEN)
|
||||
static const float rate_control_delta = 0.002;
|
||||
#else
|
||||
static const float rate_control_delta = 0.005;
|
||||
#endif
|
||||
|
3
driver.c
3
driver.c
@ -84,6 +84,9 @@ static const audio_driver_t *audio_drivers[] = {
|
||||
#ifdef GEKKO
|
||||
&audio_gx,
|
||||
#endif
|
||||
#ifdef EMSCRIPTEN
|
||||
&audio_rwebaudio,
|
||||
#endif
|
||||
#ifdef HAVE_NULLAUDIO
|
||||
&audio_null,
|
||||
#endif
|
||||
|
1
driver.h
1
driver.h
@ -511,6 +511,7 @@ extern const audio_driver_t audio_coreaudio;
|
||||
extern const audio_driver_t audio_xenon360;
|
||||
extern const audio_driver_t audio_ps3;
|
||||
extern const audio_driver_t audio_gx;
|
||||
extern const audio_driver_t audio_rwebaudio;
|
||||
extern const audio_driver_t audio_null;
|
||||
extern const video_driver_t video_gl;
|
||||
extern const video_driver_t video_psp1;
|
||||
|
90
emscripten/RWebAudio.c
Normal file
90
emscripten/RWebAudio.c
Normal file
@ -0,0 +1,90 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2013 - Michael Lelli
|
||||
*
|
||||
* 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../driver.h"
|
||||
#include "../general.h"
|
||||
|
||||
#include "RWebAudio.h"
|
||||
|
||||
static void ra_free(void *data)
|
||||
{
|
||||
RWebAudioFree();
|
||||
}
|
||||
|
||||
static void *ra_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
(void)rate;
|
||||
void *data = RWebAudioInit(latency);
|
||||
g_settings.audio.out_rate = RWebAudioSampleRate();
|
||||
RARCH_LOG("audio out rate: %u\n", g_settings.audio.out_rate);
|
||||
return data;
|
||||
}
|
||||
|
||||
static ssize_t ra_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
(void)data;
|
||||
return RWebAudioWrite(buf, size);
|
||||
}
|
||||
|
||||
static bool ra_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return RWebAudioStop();
|
||||
}
|
||||
|
||||
static void ra_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
(void)data;
|
||||
RWebAudioSetNonblockState(state);
|
||||
}
|
||||
|
||||
static bool ra_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return RWebAudioStart();
|
||||
}
|
||||
|
||||
static bool ra_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t ra_write_avail(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return RWebAudioWriteAvail();
|
||||
}
|
||||
|
||||
static size_t ra_buffer_size(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return RWebAudioBufferSize();
|
||||
}
|
||||
|
||||
const audio_driver_t audio_rwebaudio = {
|
||||
ra_init,
|
||||
ra_write,
|
||||
ra_stop,
|
||||
ra_start,
|
||||
ra_set_nonblock_state,
|
||||
ra_free,
|
||||
ra_use_float,
|
||||
"rwebaudio",
|
||||
ra_write_avail,
|
||||
ra_buffer_size,
|
||||
};
|
||||
|
29
emscripten/RWebAudio.h
Normal file
29
emscripten/RWebAudio.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2013 - Michael Lelli
|
||||
*
|
||||
* 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
unsigned RWebAudioSampleRate(void);
|
||||
void *RWebAudioInit(unsigned latency);
|
||||
ssize_t RWebAudioWrite(const void *buf, size_t size);
|
||||
bool RWebAudioStop(void);
|
||||
bool RWebAudioStart(void);
|
||||
void RWebAudioSetNonblockState(bool state);
|
||||
void RWebAudioFree(void);
|
||||
size_t RWebAudioWriteAvail(void);
|
||||
size_t RWebAudioBufferSize(void);
|
||||
int RWebAudioEnoughSpace(void);
|
161
emscripten/library_rwebaudio.js
Normal file
161
emscripten/library_rwebaudio.js
Normal file
@ -0,0 +1,161 @@
|
||||
//"use strict";
|
||||
|
||||
var LibraryRWebAudio = {
|
||||
$RA__deps: ['$Browser'],
|
||||
$RA: {
|
||||
SCRIPTNODE_BUFFER: 1024,
|
||||
|
||||
context: null,
|
||||
leftBuffer: null,
|
||||
rightBuffer: null,
|
||||
blank: null,
|
||||
scriptNode: null,
|
||||
bufferNode: null,
|
||||
start: 0,
|
||||
end: 0,
|
||||
size: 0,
|
||||
lastWrite: 0,
|
||||
nonblock: false,
|
||||
|
||||
npot: function(n) {
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n++;
|
||||
return n;
|
||||
},
|
||||
|
||||
process: function(e) {
|
||||
var left = e.outputBuffer.getChannelData(0);
|
||||
var right = e.outputBuffer.getChannelData(1);
|
||||
var samples1 = RA.size;
|
||||
var samples2 = 0;
|
||||
samples1 = e.outputBuffer.length > samples1 ? samples1 : e.outputBuffer.length;
|
||||
|
||||
if (samples1 + RA.start > RA.leftBuffer.length) {
|
||||
samples2 = samples1 + RA.start - RA.leftBuffer.length;
|
||||
samples1 = samples1 - samples2;
|
||||
}
|
||||
|
||||
var remaining = e.outputBuffer.length - (samples1 + samples2);
|
||||
|
||||
if (samples1) {
|
||||
left.set(RA.leftBuffer.subarray(RA.start, RA.start + samples1), 0);
|
||||
right.set(RA.rightBuffer.subarray(RA.start, RA.start + samples1), 0);
|
||||
}
|
||||
|
||||
if (samples2) {
|
||||
left.set(RA.leftBuffer.subarray(0, samples2), samples1);
|
||||
right.set(RA.rightBuffer.subarray(0, samples2), samples1);
|
||||
}
|
||||
|
||||
/*if (remaining) {
|
||||
left.set(RA.blank.subarray(0, remaining), samples1 + samples2);
|
||||
right.set(RA.blank.subarray(0, remaining), samples1 + samples2);
|
||||
}*/
|
||||
|
||||
RA.start = (RA.start + samples1 + samples2) % RA.leftBuffer.length;
|
||||
RA.size -= samples1 + samples2;
|
||||
}
|
||||
},
|
||||
|
||||
RWebAudioSampleRate: function() {
|
||||
return RA.context.sampleRate;
|
||||
},
|
||||
|
||||
RWebAudioInit: function(latency) {
|
||||
var ac = window['AudioContext'] || window['webkitAudioContext'];
|
||||
var bufferSize;
|
||||
|
||||
if (!ac) return 0;
|
||||
|
||||
RA.context = new ac();
|
||||
// account for script processor overhead
|
||||
latency -= 32;
|
||||
// because we have to guess on how many samples the core will send when
|
||||
// returning early, we double the buffer size to account for times when it
|
||||
// sends more than we expect it to without losing samples
|
||||
bufferSize = RA.npot(RA.context.sampleRate * latency / 1000) * 2;
|
||||
RA.leftBuffer = new Float32Array(bufferSize);
|
||||
RA.rightBuffer = new Float32Array(bufferSize);
|
||||
RA.blank = new Float32Array(RA.SCRIPTNODE_BUFFER);
|
||||
RA.bufferNode = RA.context.createBufferSource();
|
||||
RA.bufferNode.buffer = RA.context.createBuffer(2, RA.SCRIPTNODE_BUFFER, RA.context.sampleRate);
|
||||
RA.bufferNode.loop = true;
|
||||
RA.scriptNode = RA.context.createScriptProcessor(RA.SCRIPTNODE_BUFFER, 2, 2);
|
||||
RA.scriptNode.onaudioprocess = RA.process;
|
||||
RA.bufferNode.connect(RA.scriptNode);
|
||||
RA.scriptNode.connect(RA.context.destination);
|
||||
RA.bufferNode.start(0);
|
||||
RA.start = RA.end = RA.size = 0;
|
||||
RA.nonblock = false;
|
||||
return 1;
|
||||
},
|
||||
|
||||
RWebAudioWrite: function (buf, size) {
|
||||
var samples = size / 8;
|
||||
var free = RA.leftBuffer.length - RA.size;
|
||||
if (free < samples)
|
||||
RA.start = (RA.start + free) % RA.leftBuffer.length;
|
||||
|
||||
for (var i = 0; i < samples; i++) {
|
||||
RA.leftBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8', 'float') }}};
|
||||
RA.rightBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8 + 4', 'float') }}};
|
||||
RA.end = (RA.end + 1) % RA.leftBuffer.length;
|
||||
}
|
||||
|
||||
RA.lastWrite = size;
|
||||
RA.size += samples;
|
||||
return size;
|
||||
},
|
||||
|
||||
RWebAudioStop: function() {
|
||||
RA.scriptNode.onaudioprocess = null;
|
||||
return true;
|
||||
},
|
||||
|
||||
RWebAudioStart: function() {
|
||||
RA.scriptNode.onaudioprocess = RA.process;
|
||||
return true;
|
||||
},
|
||||
|
||||
RWebAudioSetNonblockState: function(state) {
|
||||
RA.nonblock = state;
|
||||
},
|
||||
|
||||
RWebAudioFree: function() {
|
||||
RA.scriptNode.onaudioprocess = null;
|
||||
RA.start = RA.end = RA.size = RA.lastWrite = 0;
|
||||
return;
|
||||
},
|
||||
|
||||
RWebAudioWriteAvail: function() {
|
||||
var free = (RA.leftBuffer.length / 2) - RA.size;
|
||||
// 4 byte samples, 2 channels
|
||||
free *= 8;
|
||||
|
||||
if (free < 0)
|
||||
return 0;
|
||||
else
|
||||
return free;
|
||||
},
|
||||
|
||||
RWebAudioBufferSize: function() {
|
||||
return RA.leftBuffer.length / 2;
|
||||
},
|
||||
|
||||
RWebAudioEnoughSpace__deps: ['RWebAudioWriteAvail'],
|
||||
RWebAudioEnoughSpace: function() {
|
||||
var guess = RA.lastWrite;
|
||||
var available = _RWebAudioWriteAvail();
|
||||
if (RA.nonblock) return true;
|
||||
if (!guess) return true;
|
||||
return (guess < available) ? 1 : 0;
|
||||
}
|
||||
};
|
||||
|
||||
autoAddDeps(LibraryRWebAudio, '$RA');
|
||||
mergeInto(LibraryManager.library, LibraryRWebAudio);
|
@ -18,6 +18,7 @@
|
||||
#include "../general.h"
|
||||
#include "../conf/config_file.h"
|
||||
#include "../file.h"
|
||||
#include "../emscripten/RWebAudio.h"
|
||||
|
||||
#ifdef HAVE_RGUI
|
||||
#include "../frontend/menu/rgui.h"
|
||||
@ -55,6 +56,11 @@ static void endloop(void)
|
||||
|
||||
static void mainloop(void)
|
||||
{
|
||||
if (!RWebAudioEnoughSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_extern.system.shutdown)
|
||||
{
|
||||
endloop();
|
||||
|
@ -67,6 +67,8 @@ const char *config_get_default_audio(void)
|
||||
return "ps3";
|
||||
case AUDIO_WII:
|
||||
return "gx";
|
||||
case AUDIO_RWEBAUDIO:
|
||||
return "rwebaudio";
|
||||
case AUDIO_NULL:
|
||||
return "null";
|
||||
default:
|
||||
|
Loading…
x
Reference in New Issue
Block a user