1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-07 21:56:37 +02:00

#9 support Web Audio API, defines for ADPCM, MP3 & OGG decoders, play bubble sound when Lara is under water

This commit is contained in:
XProger
2016-10-04 23:00:37 +03:00
parent 65917262bb
commit 831e0765db
7 changed files with 86 additions and 23 deletions

Binary file not shown.

View File

@@ -166,10 +166,7 @@ struct Controller {
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) { if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)]; uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)];
void *p = &level->soundData[c]; void *p = &level->soundData[c];
#ifdef WIN32
Sound::play(new Stream(p, 1024 * 1024), b.volume / 255.0f, 0.0f, Sound::Flags::PAN); Sound::play(new Stream(p, 1024 * 1024), b.volume / 255.0f, 0.0f, Sound::Flags::PAN);
// PlaySound((LPSTR)p, NULL, SND_ASYNC | SND_MEMORY);
#endif
} }
} }
@@ -373,8 +370,8 @@ struct Controller {
case TR::ANIM_CMD_SPECIAL : // special commands case TR::ANIM_CMD_SPECIAL : // special commands
if (frameIndex != animPrevFrame && frameIndex + anim->frameStart == ptr[0]) { if (frameIndex != animPrevFrame && frameIndex + anim->frameStart == ptr[0]) {
switch (ptr[1]) { switch (ptr[1]) {
case TR::ANIM_CMD_SPECIAL_FLIP : angle.y = angle.y + PI; break; case TR::ANIM_CMD_SPECIAL_FLIP : angle.y = angle.y + PI; break;
case TR::ANIM_CMD_SPECIAL_BUBBLE : /* playSound(TR::SND_BUBBLE); */ break; case TR::ANIM_CMD_SPECIAL_BUBBLE : playSound(TR::SND_BUBBLE); break;
case TR::ANIM_CMD_SPECIAL_CTRL : LOG("water out ?\n"); break; case TR::ANIM_CMD_SPECIAL_CTRL : LOG("water out ?\n"); break;
default : LOG("unknown special cmd %d\n", (int)ptr[1]); default : LOG("unknown special cmd %d\n", (int)ptr[1]);
} }

View File

@@ -14,10 +14,12 @@ namespace Game {
Core::init(); Core::init();
Stream stream("LEVEL2_DEMO.PHD"); Stream stream("LEVEL2_DEMO.PHD");
level = new Level(stream); level = new Level(stream);
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0); #ifndef __EMSCRIPTEN__
Sound::play(new Stream("05.ogg"), 1, 1, 0); //Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
//Sound::play(new Stream("03.mp3"), 1, 1, 0); Sound::play(new Stream("05.ogg"), 1, 1, 0);
//Sound::play(new Stream("03.mp3"), 1, 1, 0);
#endif
} }
void free() { void free() {

View File

@@ -1,13 +1,25 @@
#ifndef H_SOUND #ifndef H_SOUND
#define H_SOUND #define H_SOUND
//#define DECODE_ADPCM
//#define DECODE_MP3
#define DECODE_OGG
#ifdef __EMSCRIPTEN__ // TODO: http streaming
#undef DECODE_MP3
#undef DECODE_OGG
#endif
#include "utils.h" #include "utils.h"
#include "libs/minimp3/minimp3.h" #ifdef DECODE_MP3
#define STB_VORBIS_HEADER_ONLY #include "libs/minimp3/minimp3.h"
#include "libs/stb_vorbis/stb_vorbis.c" #endif
#ifdef DECODE_OGG
#define STB_VORBIS_HEADER_ONLY
#include "libs/stb_vorbis/stb_vorbis.c"
#endif
#define SND_CHANNELS_MAX 32 #define SND_CHANNELS_MAX 32
#define BUFFER_SIZE_MP3 8192
namespace Sound { namespace Sound {
@@ -56,6 +68,7 @@ namespace Sound {
} }
}; };
#ifdef DECODE_ADPCM
struct ADPCM : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM struct ADPCM : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM
int size, block; int size, block;
@@ -127,7 +140,9 @@ namespace Sound {
} }
} }
}; };
#endif
#ifdef DECODE_MP3
struct MP3 : Decoder { struct MP3 : Decoder {
mp3_decoder_t mp3; mp3_decoder_t mp3;
char *buffer; char *buffer;
@@ -160,7 +175,9 @@ namespace Sound {
return i; return i;
} }
}; };
#endif
#ifdef DECODE_OGG
struct OGG : Decoder { struct OGG : Decoder {
stb_vorbis *ogg; stb_vorbis *ogg;
stb_vorbis_alloc alloc; stb_vorbis_alloc alloc;
@@ -195,6 +212,7 @@ namespace Sound {
return i; return i;
} }
}; };
#endif
struct Listener { struct Listener {
mat4 matrix; mat4 matrix;
@@ -216,7 +234,7 @@ namespace Sound {
int flags; int flags;
bool isPlaying; bool isPlaying;
Sample(Stream *stream, float volume, float pitch, int flags) : decoder(NULL), volume(volume), pitch(pitch), flags(flags), isPlaying(true) { Sample(Stream *stream, float volume, float pitch, int flags) : decoder(NULL), volume(volume), pitch(pitch), flags(flags) {
uint32 fourcc; uint32 fourcc;
stream->read(fourcc); stream->read(fourcc);
if (fourcc == FOURCC("RIFF")) { // wav if (fourcc == FOURCC("RIFF")) { // wav
@@ -240,19 +258,28 @@ namespace Sound {
stream->seek(size - sizeof(waveFmt)); stream->seek(size - sizeof(waveFmt));
} else if (type == FOURCC("data")) { } else if (type == FOURCC("data")) {
if (waveFmt.format == 1) decoder = new PCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.sampleBits); if (waveFmt.format == 1) decoder = new PCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.sampleBits);
#ifdef DECODE_ADPCM
if (waveFmt.format == 2) decoder = new ADPCM(stream, waveFmt.channels, size, waveFmt.block); if (waveFmt.format == 2) decoder = new ADPCM(stream, waveFmt.channels, size, waveFmt.block);
#endif
break; break;
} else } else
stream->seek(size); stream->seek(size);
} }
} else if (fourcc == FOURCC("OggS")) { // ogg }
#ifdef DECODE_OGG
else if (fourcc == FOURCC("OggS")) { // ogg
stream->seek(-4); stream->seek(-4);
decoder = new OGG(stream, 2); decoder = new OGG(stream, 2);
} else if (fourcc == FOURCC("ID3\3")) { // mp3 }
#endif
#ifdef DECODE_MP3
else if (fourcc == FOURCC("ID3\3")) { // mp3
decoder = new MP3(stream, 2); decoder = new MP3(stream, 2);
} }
#endif
ASSERT(decoder != NULL);
isPlaying = decoder != NULL;
ASSERT(isPlaying);
} }
~Sample() { ~Sample() {
@@ -260,6 +287,7 @@ namespace Sound {
} }
bool render(Frame *frames, int count) { bool render(Frame *frames, int count) {
if (!isPlaying) return 0;
int i = 0; int i = 0;
while (i < count) { while (i < count) {
int res = decoder->decode(&frames[i], count - i); int res = decoder->decode(&frames[i], count - i);
@@ -277,13 +305,17 @@ namespace Sound {
void init() { void init() {
channelsCount = 0; channelsCount = 0;
#ifdef DECODE_MP3
mp3_decode_init(); mp3_decode_init();
#endif
} }
void free() { void free() {
for (int i = 0; i < channelsCount; i++) for (int i = 0; i < channelsCount; i++)
delete channels[i]; delete channels[i];
#ifdef DECODE_MP3
mp3_decode_free(); mp3_decode_free();
#endif
} }
void fill(Frame *frames, int count) { void fill(Frame *frames, int count) {

View File

@@ -1,3 +1,3 @@
set SRC=main.cpp ../libs/minimp3/minimp3.cpp ../libs/stb_vorbis/stb_vorbis.c set SRC=main.cpp
call em++ %SRC% -O2 -s ASSERTIONS=1 -Wno-deprecated-register --llvm-opts 2 --closure 2 -std=c++11 -o OpenLara.js --preload-file ./LEVEL2_DEMO.PHD -I..\ call em++ %SRC% -O2 -s ASSERTIONS=1 -Wno-deprecated-register --llvm-opts 2 --closure 1 -std=c++11 -o OpenLara.js --preload-file ./LEVEL2_DEMO.PHD -I..\
gzip.exe -9 -f OpenLara.data OpenLara.js OpenLara.js.mem gzip.exe -9 -f OpenLara.data OpenLara.js OpenLara.js.mem

View File

@@ -41,8 +41,30 @@
monitorRunDependencies: function(left) { monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left); this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
} },
}; };
function snd_init() {
var AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) return;
var ctx = new (window.AudioContext || window.webkitAudioContext)();
var count = 2048;
var frames = Module._malloc(count * 4); // interleaved short L, R
var proc = ctx.createScriptProcessor(count, 2, 2);
proc.onaudioprocess = function(e) {
var L = e.outputBuffer.getChannelData(0),
R = e.outputBuffer.getChannelData(1);
Module.ccall('snd_fill', 'null', ['number', 'number'], [frames, count]);
var index = frames;
for (var i = 0; i < count; i++) {
L[i] = Module.getValue(index , 'i16') / 0x8000;
R[i] = Module.getValue(index + 2, 'i16') / 0x8000;
index += 4;
}
}
proc.connect(ctx.destination);
}
var gl = canvasElement.getContext("webgl") || canvasElement.getContext("experimental-webgl"); var gl = canvasElement.getContext("webgl") || canvasElement.getContext("experimental-webgl");
@@ -74,5 +96,7 @@
script.src = "OpenLara.js"; script.src = "OpenLara.js";
document.body.appendChild(script); document.body.appendChild(script);
</script> </script>
<audio autoplay loop><source src="05.ogg" type="audio/ogg"></audio>
</body> </body>
</html> </html>

View File

@@ -12,6 +12,12 @@ int getTime() {
return (int)emscripten_get_now(); return (int)emscripten_get_now();
} }
extern "C" {
void EMSCRIPTEN_KEEPALIVE snd_fill(Sound::Frame *frames, int count) {
Sound::fill(frames, count);
}
}
void main_loop() { void main_loop() {
int time = getTime(); int time = getTime();
@@ -168,7 +174,7 @@ EM_BOOL mouseCallback(int eventType, const EmscriptenMouseEvent *e, void *userDa
int main() { int main() {
initGL(); initGL();
emscripten_set_canvas_size(Core::width = 1280, Core::height = 720); emscripten_set_canvas_size(Core::width = 854, Core::height = 480);
emscripten_set_keydown_callback(0, 0, 1, keyCallback); emscripten_set_keydown_callback(0, 0, 1, keyCallback);
emscripten_set_keyup_callback(0, 0, 1, keyCallback); emscripten_set_keyup_callback(0, 0, 1, keyCallback);
@@ -185,6 +191,8 @@ int main() {
Game::init(); Game::init();
emscripten_run_script("snd_init()");
lastTime = getTime(); lastTime = getTime();
fpsTime = lastTime + 1000; fpsTime = lastTime + 1000;
fps = 0; fps = 0;