mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-08 06:06:51 +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:
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +371,7 @@ struct Controller {
|
|||||||
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]);
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,11 @@ namespace Game {
|
|||||||
Stream stream("LEVEL2_DEMO.PHD");
|
Stream stream("LEVEL2_DEMO.PHD");
|
||||||
level = new Level(stream);
|
level = new Level(stream);
|
||||||
|
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
||||||
Sound::play(new Stream("05.ogg"), 1, 1, 0);
|
Sound::play(new Stream("05.ogg"), 1, 1, 0);
|
||||||
//Sound::play(new Stream("03.mp3"), 1, 1, 0);
|
//Sound::play(new Stream("03.mp3"), 1, 1, 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void free() {
|
void free() {
|
||||||
|
42
src/sound.h
42
src/sound.h
@@ -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"
|
||||||
|
#ifdef DECODE_MP3
|
||||||
#include "libs/minimp3/minimp3.h"
|
#include "libs/minimp3/minimp3.h"
|
||||||
|
#endif
|
||||||
|
#ifdef DECODE_OGG
|
||||||
#define STB_VORBIS_HEADER_ONLY
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
#include "libs/stb_vorbis/stb_vorbis.c"
|
#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) {
|
||||||
|
@@ -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
|
@@ -41,9 +41,31 @@
|
|||||||
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");
|
||||||
|
|
||||||
Module.setStatus('Downloading...');
|
Module.setStatus('Downloading...');
|
||||||
@@ -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>
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user