new audio core, RWebAudio. Glitchy, but works well, even with requestAnimationFrame callbacks

This commit is contained in:
ToadKing 2013-08-28 00:03:25 -04:00
parent c30d0287d1
commit 04be8cbee2
9 changed files with 300 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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

View 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);

View File

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

View File

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