1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-04-21 11:31:56 +02:00
openlara/src/sound.h
2020-02-20 05:23:51 +03:00

1241 lines
42 KiB
C++

#ifndef H_SOUND
#define H_SOUND
#define DECODE_ADPCM
#define DECODE_IMA
#define DECODE_VAG
#define DECODE_XA
#define DECODE_OGG
#if !defined(_OS_PSP) && !defined(_OS_WEB) && !defined(_OS_PSV) && !defined(_OS_3DS)
#define DECODE_MP3
#endif
#include "utils.h"
#ifdef DECODE_MP3
#include "libs/minimp3/minimp3.h"
#endif
#ifdef DECODE_OGG
#ifdef USE_LIBVORBIS
#include <tremor/ivorbisfile.h>
#else
#define STB_VORBIS_HEADER_ONLY
#include "libs/stb_vorbis/stb_vorbis.c"
#endif
#endif
#define SND_CHANNELS_MAX 128
#define SND_FADEOFF_DIST (1024.0f * 8.0f)
#define SND_LOWPASS_FREQ 0.2f
#define SND_MAX_VOLUME 20
#define SND_PAN_FACTOR 0.7f
#define SND_FACING_FACTOR 0.3f
namespace Sound {
static const int8 SPU_POS[] = { 0, 60, 115, 98, 122 };
static const int8 SPU_NEG[] = { 0, 0, -52, -55, -60 };
static const int16 SPU_ZIG_ZAG[7][30] = {
{ 0, 0, 0, 0, 0, 0, -0x0002, +0x000A, -0x0022, +0x0041, -0x0054, +0x0034, +0x0009, -0x010A, +0x0400, -0x0A78, +0x234C, +0x6794, -0x1780, +0x0BCD, -0x0623, +0x0350, -0x016D, +0x006B, +0x000A, -0x0010, +0x0011, -0x0008, +0x0003, -0x0001 },
{ 0, 0, 0, 0, -0x0002, 0, +0x0003, -0x0013, +0x003C, -0x004B, +0x00A2, -0x00E3, +0x0132, -0x0043, -0x0267, +0x0C9D, +0x74BB, -0x11B4, +0x09B8, -0x05BF, +0x0372, -0x01A8, +0x00A6, -0x001B, +0x0005, +0x0006, -0x0008, +0x0003, -0x0001, 0 },
{ 0, 0, 0, -0x0001, +0x0003, -0x0002, -0x0005, +0x001F, -0x004A, +0x00B3, -0x0192, +0x02B1, -0x039E, +0x04F8, -0x05A6, +0x7939, -0x05A6, +0x04F8, -0x039E, +0x02B1, -0x0192, +0x00B3, -0x004A, +0x001F, -0x0005, -0x0002, +0x0003, -0x0001, 0, 0 },
{ 0, 0, -0x0001, +0x0003, -0x0008, +0x0006, +0x0005, -0x001B, +0x00A6, -0x01A8, +0x0372, -0x05BF, +0x09B8, -0x11B4, +0x74BB, +0x0C9D, -0x0267, -0x0043, +0x0132, -0x00E3, +0x00A2, -0x004B, +0x003C, -0x0013, +0x0003, 0, -0x0002, 0, 0, 0 },
{ 0, -0x0001, +0x0003, -0x0008, +0x0011, -0x0010, +0x000A, +0x006B, -0x016D, +0x0350, -0x0623, +0x0BCD, -0x1780, +0x6794, +0x234C, -0x0A78, +0x0400, -0x010A, +0x0009, +0x0034, -0x0054, +0x0041, -0x0022, +0x000A, -0x0001, 0, +0x0001, 0, 0, 0 },
{ 0, +0x0002, -0x0008, +0x0010, -0x0023, +0x002B, +0x001A, -0x00EB, +0x027B, -0x0548, +0x0AFA, -0x16FA, +0x53E0, +0x3C07, -0x1249, +0x080E, -0x0347, +0x015B, -0x0044, -0x0017, +0x0046, -0x0023, +0x0011, -0x0005, 0, 0, 0, 0, 0, 0 },
{ 0, -0x0005, +0x0011, -0x0023, +0x0046, -0x0017, -0x0044, +0x015B, -0x0347, +0x080E, -0x1249, +0x3C07, +0x53E0, -0x16FA, +0x0AFA, -0x0548, +0x027B, -0x00EB, +0x001A, +0x002B, -0x0023, +0x0010, -0x0008, +0x0002, 0, 0, 0, 0, 0, 0 },
};
struct Frame {
int16 L, R;
};
struct FrameHI {
int32 L, R;
};
struct Stats {
int mixer;
int reverb;
int render[2];
int ogg;
} stats;
namespace Filter {
#define MAX_FDN 16
#define MAX_DELAY 954
#define DSP_SCALE_BIT 8
#define DSP_SCALE (1 << DSP_SCALE_BIT)
static const int16 FDN[MAX_FDN] = { 281, 331, 373, 419, 461, 503, 547, 593, 641, 683, 727, 769, 811, 853, 907, 953 };
struct Delay {
int index;
int16 out[MAX_DELAY];
void process(int16 &x, int16 delay) {
index = (index + 1) % delay;
int16 y = out[index];
out[index] = x;
x = y;
}
};
struct Absorption {
int16 out;
void process(int16 &x, int32 gain, int32 damping) {
x = out = (out * damping + ((x * gain * (DSP_SCALE - damping)) >> DSP_SCALE_BIT)) >> DSP_SCALE_BIT;
}
};
struct LowPass {
float buffer[2][4];
LowPass() {
memset(buffer, 0, sizeof(buffer));
}
inline void process(int32 &x, float *out, float freq) {
out[0] += freq * (float(x) - out[0]);
out[1] += freq * (out[0] - out[1]);
out[2] += freq * (out[1] - out[2]);
out[3] += freq * (out[2] - out[3]);
x = int32(out[3]);
}
void process(FrameHI *frames, int count, float freq) {
for (int i = 0; i < count; i++) {
process(frames[i].L, buffer[0], freq);
process(frames[i].R, buffer[1], freq);
}
}
};
struct Reverberation {
Delay df[MAX_FDN];
Absorption af[MAX_FDN];
int32 output[MAX_FDN];
int16 buffer[MAX_FDN];
int16 panCoeff[MAX_FDN][2];
int32 absCoeff[MAX_FDN][2]; // absorption gain & damping
Reverberation() {
for (int i = 0; i < MAX_FDN; i++) {
panCoeff[i][0] = (i % 2) ? -1 : 1;
}
for (int i = 0; i < MAX_FDN; i += 2) {
if ((i / 2) % 2)
panCoeff[i][1] = panCoeff[i + 1][1] = -1;
else
panCoeff[i][1] = panCoeff[i + 1][1] = 1;
}
clear();
}
void clear() {
memset(output, 0, sizeof(output));
memset(df, 0, sizeof(df));
memset(af, 0, sizeof(af));
setRoomSize(vec3(1.0f));
}
void setRoomSize(const vec3 &size) {
float S = (size.x * size.z + size.x * size.y + size.z * size.y) * 2;
float V = size.x * size.y * size.z;
float f = 0.1f; // absorption factor
float rt60 = 0.161f * (V / (S * f));
float k = -10.0f / (44100.0f * rt60);
for (int i = 0; i < MAX_FDN; i++) {
float v = powf(10.0f, FDN[i] * k);
absCoeff[i][0] = int32(v * DSP_SCALE);
absCoeff[i][1] = int32((1.0f - (2.0f / (1.0f + powf(v, 1.0f - 1.0f / 0.15f)))) * DSP_SCALE);
}
};
void process(FrameHI *frames, int count) {
PROFILE_CPU_TIMING(stats.reverb);
for (int i = 0; i < count; i++) {
FrameHI &frame = frames[i];
int32 in = (frame.L + frame.R) / 2;
int32 out = 0;
int32 L = 0;
int32 R = 0;
// apply delay & absorption filters
for (int j = 0; j < MAX_FDN; j++) {
int16 k = clamp(in + output[j], -0x7FFF, 0x7FFF);
df[j].process(k, FDN[j]);
af[j].process(k, absCoeff[j][0], absCoeff[j][1]);
buffer[j] = k;
out += k;
}
out = out * 2 / MAX_FDN;
// apply pan
int16 buf = buffer[MAX_FDN - 1];
for (int j = 0; j < MAX_FDN; j++) {
output[j] = max(0, out - buf);
buf = buffer[j];
L += buf ^ panCoeff[j][0];
R += buf ^ panCoeff[j][1];
}
frame.L += L / MAX_FDN;
frame.R += R / MAX_FDN;
}
}
};
#undef MAX_FDN
#undef MAX_DELAY
};
struct Decoder {
Stream *stream;
int channels, freq, offset;
Frame prevFrame;
Decoder(Stream *stream, int channels, int freq) : stream(stream), channels(channels), freq(freq), offset(stream ? stream->pos : 0) {
memset(&prevFrame, 0, sizeof(prevFrame));
}
virtual ~Decoder() { delete stream; }
virtual int decode(Frame *frames, int count) { return 0; }
virtual void replay() { stream->seek(offset - stream->pos); }
int resample(Sound::Frame *frames, Sound::Frame &frame) {
if (freq == 44100) {
frames[0] = frame;
return 1;
}
int dL = int(frame.L) - int(prevFrame.L);
int dR = int(frame.R) - int(prevFrame.R);
switch (freq) {
case 11025 :
if (channels == 2) {
frames[0].L = prevFrame.L + dL / 4; // 0.25 L
frames[0].R = prevFrame.R + dR / 4; // 0.25 R
frames[1].L = prevFrame.L + dL / 2; // 0.50 L
frames[1].R = prevFrame.R + dR / 2; // 0.50 R
frames[2].L = prevFrame.L + dL * 3 / 4; // 0.75 L
frames[2].R = prevFrame.R + dR * 3 / 4; // 0.75 R
} else {
frames[0].L = frames[0].R = prevFrame.L + dL / 4; // 0.25 LR
frames[1].L = frames[1].R = prevFrame.L + dL / 2; // 0.50 LR
frames[2].L = frames[2].R = prevFrame.L + dL * 3 / 4; // 0.75 LR
}
frames[3] = prevFrame = frame; // 1.00 LR
return 4;
case 22050 :
if (channels == 2) {
frames[0].L = prevFrame.L + dL / 2; // 0.50 L
frames[0].R = prevFrame.R + dR / 2; // 0.50 R
} else
frames[0].L = frames[0].R = prevFrame.L + dL / 2; // 0.50 LR
frames[1] = prevFrame = frame; // 1.00 LR
return 2;
default : // impossible
ASSERT(false);
int k = 44100 / freq;
for (int i = 0; i < k; i++) frames[i] = frame; // no lerp
return k;
}
}
};
struct PCM : Decoder {
int size, bits;
PCM(Stream *stream, int channels, int freq, int size, int bits) : Decoder(stream, channels, freq), size(size), bits(bits) {}
virtual int decode(Frame *frames, int count) {
if (stream->pos - offset >= size) return 0;
// ! in the original game series only 11025 and 22050 Hz single channel samples were used ! //
Frame frame;
if (bits == 16) {
int16 value;
if (channels == 2) {
frame.L = stream->read(value);
frame.R = stream->read(value);
} else
frame.L = frame.R = stream->read(value);
} else if (bits == 8 || bits == -8) {
if (bits > 0) {
uint8 value;
if (channels == 2) {
frame.L = stream->read(value) * 257 - 32768;
frame.R = stream->read(value) * 257 - 32768;
} else
frame.L = frame.R = stream->read(value) * 257 - 32768;
} else {
int8 value;
if (channels == 2) {
frame.L = (stream->read(value) + 128) * 257 - 32768;
frame.R = (stream->read(value) + 128) * 257 - 32768;
} else
frame.L = frame.R = (stream->read(value) + 128) * 257 - 32768;
}
} else {
ASSERT(false);
return 0;
}
return resample(frames, frame);
}
};
#ifdef DECODE_ADPCM
struct ADPCM : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM
int size, block;
ADPCM(Stream *stream, int channels, int freq, int size, int block) : Decoder(stream, channels, freq), size(size), block(block) {}
struct Channel {
int16 c1, c2;
int16 delta;
int16 sample1;
int16 sample2;
int predicate(uint8 nibble) {
static const int table[] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 };
int8 ns = nibble;
if (ns & 8) ns -= 16;
int sample = (sample1 * c1 + sample2 * c2) / 256 + ns * delta;
sample = clamp(sample, -32768, 32767);
sample2 = sample1;
sample1 = sample;
delta = max(table[nibble] * delta / 256, 16);
return sample;
}
} channel[2];
virtual int decode(Frame *frames, int count) {
static const int coeff1[] = { 256, 512, 0, 192, 240, 460, 392 };
static const int coeff2[] = { 0, -256, 0, 64, 0, -208, -232 };
int seek = stream->pos - offset;
if (seek >= size) return 0;
if (seek % block == 0) {
for (int i = 0; i < channels; i++) {
uint8 index;
stream->read(index);
channel[i].c1 = coeff1[index];
channel[i].c2 = coeff2[index];
}
for (int i = 0; i < channels; i++) stream->read(channel[i].delta);
for (int i = 0; i < channels; i++) stream->read(channel[i].sample1);
for (int i = 0; i < channels; i++) stream->read(channel[i].sample2);
if (channels == 1) {
if (freq == 22050) {
ASSERT(count >= 4);
frames[0].L = frames[0].R =
frames[1].L = frames[1].R = channel[0].sample2;
frames[2].L = frames[2].R =
frames[3].L = frames[3].R = channel[0].sample1;
return 4;
} else {
ASSERT(count >= 2);
frames[0].L = frames[0].R = channel[0].sample2;
frames[1].L = frames[1].R = channel[0].sample1;
return 2;
}
} else {
ASSERT(freq == 44100);
ASSERT(count >= 2);
frames[0].L = channel[0].sample2;
frames[0].R = channel[1].sample2;
frames[1].L = channel[0].sample1;
frames[1].R = channel[1].sample1;
return 2;
}
} else {
uint8 value;
stream->read(value);
uint8 n1 = value >> 4, n2 = value & 0xF;
if (channels == 1) {
if (freq == 22050) {
ASSERT(count >= 4);
frames[0].L = frames[0].R =
frames[1].L = frames[1].R = channel[0].predicate(n1);
frames[2].L = frames[2].R =
frames[3].L = frames[3].R = channel[0].predicate(n2);
return 4;
} else {
ASSERT(count >= 2);
frames[0].L = frames[0].R = channel[0].predicate(n1);
frames[1].L = frames[1].R = channel[0].predicate(n2);
return 2;
}
} else {
ASSERT(freq == 44100);
frames[0].L = channel[0].predicate(n1);
frames[0].R = channel[1].predicate(n2);
return 1;
}
}
}
};
#endif
#ifdef DECODE_IMA
struct IMA : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM
struct State {
int amp, idx;
} state[2];
int freq;
IMA(Stream *stream, int channels, int freq) : Decoder(stream, channels, freq) {
memset(state, 0, sizeof(state));
}
int16 getSample(uint8 n, State &state) {
static int indexLUT[] = {
-1, -1, -1, -1, 2, 4, 6, 8,
};
static int stepLUT[] = {
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767
};
int step = stepLUT[state.idx];
int idx = n & 7;
state.idx = clamp(state.idx + indexLUT[idx], 0, 88);
int diff = (2 * idx + 1) * step >> 3;
if (n & 8) {
state.amp -= diff;
if (state.amp < -32768)
state.amp = -32768;
} else {
state.amp += diff;
if (state.amp > 32767)
state.amp = 32767;
}
return state.amp;
}
virtual int decode(Frame *frames, int count) {
uint8 n;
stream->read(n);
int a = getSample(n >> 4, state[0]);
int b = getSample(n & 0x0F, state[1 % channels]);
Frame frame;
if (channels == 2) {
frame.L = a;
frame.R = b;
return resample(frames, frame);
} else {
frame.L = frame.R = a;
int i = resample(frames, frame);
frame.L = frame.R = b;
return i + resample(frames + i, frame);
}
}
};
#endif
#ifdef DECODE_VAG
struct VAG : Decoder {
uint8 pred, shift, flags;
int s1, s2;
Frame buffer[28 * 4];
int bufferSize;
VAG(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), bufferSize(0) {}
void predicate(short value) {
int s = (s1 * SPU_POS[pred] + s2 * SPU_NEG[pred]) >> 6;
s = clamp((value >> shift) + s, -32768, 32767);
s2 = s1;
s1 = s;
}
void resample(Frame *frames, short value) {
predicate(value);
frames[0].L = frames[0].R = s2 + (s1 - s2) / 4; // 0.25
frames[1].L = frames[1].R = s2 + (s1 - s2) / 2; // 0.50
frames[2].L = frames[2].R = s2 + (s1 - s2) * 3 / 4; // 0.75
frames[3].L = frames[3].R = s1; // 1.00
}
int processBlock() {
if (stream->pos >= stream->size)
return 0;
stream->read(pred);
stream->read(flags);
shift = pred & 0x0F;
pred >>= 4;
int i = 0;
while (i < 14) {
uint8 d;
stream->read(d);
resample(&buffer[i * 8 + 0], (d & 0x0F) << 12);
resample(&buffer[i * 8 + 4], (d & 0xF0) << 8);
i++;
}
return i * 8;
}
virtual int decode(Frame *frames, int count) {
int res = 0;
while (res < count) {
if (!bufferSize && !(bufferSize = processBlock())) // if no data in buffer - process next block
break;
int length = min(bufferSize, count - res);
memcpy(&frames[res], buffer, length * sizeof(Frame));
res += length;
if (bufferSize -= length) { // if data remained in buffer, move it to the beginning
memcpy(buffer, &buffer[sizeof(buffer) / sizeof(Frame) - bufferSize], bufferSize * sizeof(Frame));
break;
}
}
return res;
}
virtual void replay() {
stream->setPos(0);
s1 = s2 = 0;
}
};
#endif
#ifdef DECODE_XA
// http://problemkaputt.de/psx-spx.htm#cdromxaaudioadpcmcompression
struct XA : Decoder {
uint8 pred, shift, flags;
int s1, s2;
Frame buffer[18 * 112];
int pos;
struct Group {
uint8 params[16];
uint8 data[112];
} groups[18];
Frame prevFrames[2];
Frame lerpFrames[32];
uint32 lerpPos;
XA(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), pos(COUNT(buffer)), lerpPos(0) {
memset(prevFrames, 0, sizeof(prevFrames));
memset(lerpFrames, 0, sizeof(lerpFrames));
}
void decode28(Group &group, int block, int channel) {
int16 *dst = channel ? &buffer[pos].R : &buffer[pos].L;
int16 &old = channel ? prevFrames[0].R : prevFrames[0].L;
int16 &older = channel ? prevFrames[1].R : prevFrames[1].L;
int shift = 12 - (group.params[4 + block * 2 + channel] & 0x0F);
int filter = (group.params[4 + block * 2 + channel] & 0x30) >> 4;
int f0 = SPU_POS[filter];
int f1 = SPU_NEG[filter];
for (int i = 0; i < 28; i++) {
int t = (group.data[block + i * 4] >> (channel * 4)) & 0x0F;
if (t & 8)
t -= 16;
int s = (t << shift) + ((old * f0 + older * f1 + 32) / 64);
s = clamp(s, -32768, 32767);
older = old;
old = s;
dst[0] = s;
dst += 2; // skip second channel
}
}
void processBlock() {
if (stream->pos >= stream->size)
return;
stream->raw(groups, sizeof(groups));
pos = 0;
for (int i = 0; i < COUNT(groups); i++)
for (int j = 0; j < 4; j++) {
decode28(groups[i], j, 0);
decode28(groups[i], j, 1);
pos += 28;
}
pos = 0;
}
void ZigZagOut(Frame &frame, uint8 p, const int16 *LUT) {
FrameHI sum;
sum.L = sum.R = 0;
for (uint8 i = 1; i < 30; i++) {
Frame &f = lerpFrames[uint8(p - i) & 0x1F];
sum.L += f.L * LUT[i];
sum.R += f.R * LUT[i];
}
frame.L = clamp(sum.L >> 15, -32767, 32767);
frame.R = clamp(sum.R >> 15, -32767, 32767);
}
virtual int decode(Frame *frames, int count) {
if (pos >= COUNT(buffer))
processBlock();
ASSERT((int(COUNT(buffer)) - pos) % 6 == 0)
ASSERT(count % 7 == 0)
count = min(count, (int(COUNT(buffer)) - pos) / 6 * 7);
int i = 0;
while (i < count) {
ASSERT(pos < COUNT(buffer));
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[0]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[1]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[2]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[3]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[4]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[5]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[6]);
}
ASSERT(i == count);
return count;
}
virtual void replay() {
stream->setPos(0);
s1 = s2 = 0;
}
};
#endif
#ifdef DECODE_MP3
struct MP3 : Decoder {
mp3_decoder_t mp3;
char *buffer;
int size, pos;
MP3(Stream *stream, int channels) : Decoder(stream, channels, 0), size(stream->size), pos(0) {
mp3 = mp3_create();
buffer = new char[size]; // TODO: file streaming
stream->raw(buffer, size);
}
virtual ~MP3() {
delete[] buffer;
mp3_done(mp3);
}
virtual int decode(Frame *frames, int count) {
mp3_info_t info;
int i = 0;
char *ptr = (char*)frames;
while (ptr < (char*)&frames[count]) {
int res = mp3_decode(mp3, buffer + pos, size - pos, (short*)ptr, &info);
if (res) {
pos += res;
ptr += info.audio_bytes;
i += info.audio_bytes;
} else
break;
}
return i;
}
virtual void replay() {
mp3_done(mp3);
mp3 = mp3_create();
pos = 0;
}
};
#endif
#ifdef DECODE_OGG
#ifdef USE_LIBVORBIS
struct OGG : Decoder {
OggVorbis_File vf;
FILE *memFile;
uint8 *data;
OGG(Stream *stream, int channels) : Decoder(stream, channels, 0) {
char buf[255];
strcpy(buf, contentDir);
strcat(buf, stream->name);
data = new uint8[stream->size];
stream->raw(data, stream->size);
memFile = fmemopen(data, stream->size, "rb");
int err = ov_open(memFile, &vf, NULL, 0);
ASSERT(err >= 0);
vorbis_info *info = ov_info(&vf, -1);
this->channels = info->channels;
this->freq = info->rate;
}
virtual ~OGG() {
ov_clear(&vf);
fclose(memFile);
delete[] data;
}
virtual int decode(Frame *frames, int count) {
PROFILE_CPU_TIMING(stats.ogg);
int i = 0;
int bytes = count * sizeof(Frame);
while (i < bytes) {
int bitstream;
int res = ov_read(&vf, (char*)frames + i, bytes - i, &bitstream);
if (!res) break;
i += res;
}
return i / sizeof(Frame);
}
virtual void replay() {
fseek(memFile, 0, SEEK_SET);
ov_open(memFile, &vf, NULL, 0);
}
};
#else // stb_vorbis
struct OGG : Decoder {
stb_vorbis *ogg;
stb_vorbis_alloc alloc;
uint8 *data;
OGG(Stream *stream, int channels) : Decoder(stream, channels, 0) {
char buf[255];
strcpy(buf, contentDir);
strcat(buf, stream->name);
data = new uint8[stream->size];
stream->raw(data, stream->size);
alloc.alloc_buffer_length_in_bytes = 256 * 1024;
alloc.alloc_buffer = new char[alloc.alloc_buffer_length_in_bytes];
ogg = stb_vorbis_open_memory(data, stream->size, NULL, &alloc);
ASSERT(ogg);
stb_vorbis_info info = stb_vorbis_get_info(ogg);
this->channels = info.channels;
this->freq = info.sample_rate;
}
virtual ~OGG() {
stb_vorbis_close(ogg);
delete[] alloc.alloc_buffer;
delete[] data;
}
virtual int decode(Frame *frames, int count) {
PROFILE_CPU_TIMING(stats.ogg);
int i = 0;
while (i < count) {
int res = stb_vorbis_get_samples_short_interleaved(ogg, channels, (short*)frames + i, (count - i) * 2);
if (!res) break;
i += res;
}
return i;
}
virtual void replay() {
stb_vorbis_seek_start(ogg);
}
};
#endif
#endif // DECODE_OGG
Core::Mutex lock;
struct Listener {
mat4 matrix;
bool underwater;
} listener[2];
int listenersCount;
Listener& getListener(const vec3 &pos) {
if (listenersCount == 1 || (listener[0].matrix.getPos() - pos).length2() < (listener[1].matrix.getPos() - pos).length2())
return listener[0];
return listener[1];
}
enum Flags {
LOOP = 1,
PAN = 2,
UNIQUE = 4,
REPLAY = 8,
MUSIC = 16,
FLIPPED = 32,
UNFLIPPED = 64,
};
bool flipped;
struct Sample {
const vec3 *uniquePtr;
Decoder *decoder;
vec3 pos;
float volume;
float volumeTarget;
float volumeDelta;
float pitch;
int flags;
int id;
bool isPlaying;
bool isPaused;
bool stopAfterFade;
Sample(Decoder *decoder, float volume, float pitch, int flags, int id) : uniquePtr(NULL), decoder(decoder), volume(volume), volumeTarget(volume), volumeDelta(0.0f), pitch(pitch), flags(flags), id(id) {
isPlaying = decoder != NULL;
isPaused = false;
}
Sample(Stream *stream, const vec3 *pos, float volume, float pitch, int flags, int id) : uniquePtr(pos), decoder(NULL), volume(volume), volumeTarget(volume), volumeDelta(0.0f), pitch(pitch), flags(flags), id(id) {
this->pos = pos ? *pos : vec3(0.0f);
uint32 fourcc;
stream->read(fourcc);
if (fourcc == FOURCC("RIFF")) { // wav
struct {
uint16 format;
uint16 channels;
uint32 samplesPerSec;
uint32 bytesPerSec;
uint16 block;
uint16 sampleBits;
} waveFmt;
stream->seek(8);
while (stream->pos < stream->size) {
uint32 type, size;
stream->read(type);
stream->read(size);
if (type == FOURCC("fmt ")) {
stream->raw(&waveFmt, sizeof(waveFmt));
stream->seek(size - sizeof(waveFmt));
} else if (type == FOURCC("data")) {
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, waveFmt.samplesPerSec, size, waveFmt.block);
#endif
break;
} else
stream->seek(size);
}
}
else if (fourcc == FOURCC("OggS")) { // ogg
stream->seek(-4);
#ifdef DECODE_OGG
decoder = new OGG(stream, 2);
#endif
}
else if (fourcc == FOURCC("ID3\3")) { // mp3
#ifdef DECODE_MP3
decoder = new MP3(stream, 2);
#endif
}
else if (fourcc == FOURCC("SEGA")) { // Sega Saturn PCM mono signed 8-bit 11025 Hz
decoder = new PCM(stream, 1, 11025, stream->size, -8);
}
else { // vag
stream->setPos(0);
#ifdef DECODE_VAG
decoder = new VAG(stream);
#endif
}
if (!decoder)
delete stream;
isPlaying = decoder != NULL;
isPaused = false;
}
~Sample() {
delete decoder;
}
void setVolume(float value, float time) {
if (value < 0.0f) {
stopAfterFade = true;
value = 0.0f;
} else
stopAfterFade = false;
volumeTarget = value;
volumeDelta = volumeTarget - volume;
if (time > 0.0f)
volumeDelta /= 44100.0f * time;
}
vec2 getPan() {
if (!(flags & PAN))
return vec2(1.0f);
mat4 m = Sound::getListener(pos).matrix;
vec3 v = pos - m.offset().xyz();
vec3 n = v.normal();
float dist = max(0.0f, 1.0f - (v.length() / SND_FADEOFF_DIST));
float pan = m.right().xyz().dot(n);
float facing = (0.5f - m.dir().xyz().dot(n) * 0.5f) * SND_FACING_FACTOR + (1.0f - SND_FACING_FACTOR);
vec2 value(min(1.0f, 1.0f - pan),
min(1.0f, 1.0f + pan));
return (value * SND_PAN_FACTOR + (1.0f - SND_PAN_FACTOR)) * facing * dist;
}
bool render(Frame *frames, int count) {
if (!isPlaying) return false;
if (isPaused) {
memset(frames, 0, sizeof(Frame) * count);
return true;
}
// decode
int i = 0;
while (i < count) {
int res = decoder->decode(&frames[i], count - i);
if (res == 0) {
if (!(flags & LOOP)) {
isPlaying = false;
break;
} else
decoder->replay();
}
i += res;
}
// apply volume
#define VOL_CONV(x) (1.0f - sqrtf(1.0f - x * x));
float m = ((flags & MUSIC) ? Core::settings.audio.music : Core::settings.audio.sound) / float(SND_MAX_VOLUME);
float v = volume * m;
vec2 pan = getPan();
vec2 vol = pan * VOL_CONV(v);
for (int j = 0; j < i; j++) {
if (volumeDelta != 0.0f) { // increase / decrease channel volume
volume += volumeDelta;
if ((volumeDelta < 0.0f && volume < volumeTarget) ||
(volumeDelta > 0.0f && volume > volumeTarget)) {
volume = volumeTarget;
volumeDelta = 0.0f;
if (stopAfterFade)
isPlaying = false;
}
v = volume * m;
vol = pan * VOL_CONV(v);
}
frames[j].L = int(frames[j].L * vol.x);
frames[j].R = int(frames[j].R * vol.y);
}
#undef VOL_CONV
return true;
}
void stop() {
isPlaying = false;
}
void replay() {
decoder->replay();
}
void pause() {
isPaused = true;
}
void resume() {
isPaused = false;
}
} *channels[SND_CHANNELS_MAX];
int channelsCount;
typedef void (Callback)(Sample *channel);
Callback *callback;
FrameHI *result;
Frame *buffer;
// TODO: per listener
Filter::Reverberation reverb;
Filter::LowPass lowPass;
void init() {
flipped = false;
channelsCount = 0;
callback = NULL;
buffer = NULL;
result = NULL;
#ifdef DECODE_MP3
mp3_decode_init();
#endif
}
void deinit() {
for (int i = 0; i < channelsCount; i++)
delete channels[i];
#ifdef DECODE_MP3
mp3_decode_free();
#endif
delete[] buffer;
delete[] result;
}
void renderChannels(FrameHI *result, int count, bool music) {
PROFILE_CPU_TIMING(stats.render[music]);
int bufSize = count + count / 2 + 4;
if (!buffer) buffer = new Frame[bufSize]; // + 50% for pitch
for (int i = 0; i < channelsCount; i++) {
if (music != ((channels[i]->flags & MUSIC) != 0))
continue;
if (channels[i]->flags & (FLIPPED | UNFLIPPED)) {
if (!(channels[i]->flags & (flipped ? FLIPPED : UNFLIPPED)))
continue;
vec3 d = channels[i]->pos - getListener(channels[i]->pos).matrix.getPos();
if (fabsf(d.x) > SND_FADEOFF_DIST || fabsf(d.y) > SND_FADEOFF_DIST || fabsf(d.z) > SND_FADEOFF_DIST)
continue;
}
if ((channels[i]->flags & LOOP) && channels[i]->volume < EPS && channels[i]->volumeTarget < EPS)
continue;
memset(buffer, 0, sizeof(Frame) * bufSize);
channels[i]->render(buffer, (int(count * channels[i]->pitch) + 3) / 4 * 4);
if (channels[i]->pitch == 1.0f) { // no pitch
for (int j = 0; j < count; j++) {
result[j].L += buffer[j].L;
result[j].R += buffer[j].R;
}
} else { // has pitch (interpolate values for smooth wave)
float t = 0.0f;
for (int j = 0; j < count; j++, t += channels[i]->pitch) {
int idxA = int(t);
int idxB = (j == (count - 1)) ? idxA : (idxA + 1);
int st = int((t - idxA) * DSP_SCALE);
Frame &a = buffer[idxA];
Frame &b = buffer[idxB];
result[j].L += a.L + ((b.L - a.L) * st >> DSP_SCALE_BIT);
result[j].R += a.R + ((b.R - a.R) * st >> DSP_SCALE_BIT);
}
}
}
}
void convFrames(FrameHI *from, Frame *to, int count) {
for (int i = 0; i < count; i++) {
to[i].L = clamp(from[i].L, -32767, 32767);
to[i].R = clamp(from[i].R, -32767, 32767);
}
}
void fill(Frame *frames, int count) {
OS_LOCK(lock);
PROFILE_CPU_TIMING(stats.mixer);
if (!channelsCount) {
if (result && (Core::settings.audio.music != 0 || Core::settings.audio.sound != 0)) {
memset(result, 0, sizeof(FrameHI) * count);
if (Core::settings.audio.reverb)
reverb.process(result, count);
convFrames(result, frames, count);
} else
memset(frames, 0, sizeof(frames[0]) * count);
return;
}
if (!result) result = new FrameHI[count];
memset(result, 0, sizeof(FrameHI) * count);
if (Core::settings.audio.sound != 0) {
renderChannels(result, count, false);
if (Core::settings.audio.reverb) {
if (listener[0].underwater) {
lowPass.process(result, count, SND_LOWPASS_FREQ);
}
reverb.process(result, count);
}
}
if (Core::settings.audio.music != 0) {
renderChannels(result, count, true);
}
convFrames(result, frames, count);
for (int i = 0; i < channelsCount; i++)
if (!channels[i]->isPlaying) {
if (callback) callback(channels[i]);
delete channels[i];
channels[i] = channels[--channelsCount];
i--;
}
}
Stream *openCDAudioWAD(const char *name, int index = -1) {
if (!Stream::existsContent(name))
return NULL;
Stream *stream = new Stream(name);
if (stream->size) {
struct Item {
char name[260];
int size;
int offset;
} entity;
stream->seek(sizeof(entity) * index);
stream->raw(&entity, sizeof(entity));
stream->setPos(entity.offset);
return stream;
}
delete stream;
return NULL;
}
Stream *openCDAudioMP3(const char *dat, const char *name, int index = -1) {
if (!Stream::existsContent(dat) || !Stream::existsContent(name))
return NULL;
Stream *stream = new Stream(name);
return stream;
}
Sample* getChannel(int id, const vec3 *pos) {
for (int i = 0; i < channelsCount; i++)
if (channels[i]->id == id && channels[i]->uniquePtr == pos)
return channels[i];
return NULL;
}
Sample* play(Stream *stream, const vec3 *pos = NULL, float volume = 1.0f, float pitch = 0.0f, int flags = 0, int id = - 1) {
OS_LOCK(lock);
ASSERT(pitch >= 0.0f);
if (!stream) return NULL;
if (volume > 0.001f) {
if (pos && !(flags & (FLIPPED | UNFLIPPED | MUSIC)) && (flags & PAN)) {
vec3 listenerPos = getListener(*pos).matrix.getPos();
vec3 d = *pos - listenerPos;
if (fabsf(d.x) > SND_FADEOFF_DIST || fabsf(d.y) > SND_FADEOFF_DIST || fabsf(d.z) > SND_FADEOFF_DIST) {
delete stream;
return NULL;
}
}
if (flags & (UNIQUE | REPLAY)) {
Sample *ch = getChannel(id, pos);
if (ch) {
if (pos)
ch->pos = *pos;
ch->pitch = pitch;
if (flags & REPLAY)
ch->replay();
delete stream;
return ch;
}
}
if (channelsCount < SND_CHANNELS_MAX)
return channels[channelsCount++] = new Sample(stream, pos, volume, pitch, flags, id);
LOG("! no free channels\n");
}
delete stream;
return NULL;
}
Sample* play(Decoder *decoder) {
OS_LOCK(lock);
if (channelsCount < SND_CHANNELS_MAX)
return channels[channelsCount++] = new Sample(decoder, 1.0f, 1.0f, MUSIC, -1);
return NULL;
}
void stop(int id = -1) {
OS_LOCK(lock);
for (int i = 0; i < channelsCount; i++)
if (id == -1 || channels[i]->id == id)
channels[i]->stop();
}
void stopAll() {
OS_LOCK(lock);
reverb.clear();
for (int i = 0; i < channelsCount; i++)
delete channels[i];
channelsCount = 0;
}
}
#endif