mirror of
https://github.com/XProger/OpenLara.git
synced 2025-04-22 03:51:58 +02:00
#129 add IMA audio support for TR3 FMVs
This commit is contained in:
parent
61859dc83a
commit
20503efa6e
178
src/sound.h
178
src/sound.h
@ -3,6 +3,7 @@
|
||||
|
||||
#define DECODE_VAG
|
||||
#define DECODE_ADPCM
|
||||
#define DECODE_IMA
|
||||
|
||||
#define DECODE_OGG
|
||||
|
||||
@ -139,19 +140,62 @@ namespace Sound {
|
||||
|
||||
struct Decoder {
|
||||
Stream *stream;
|
||||
int channels, offset;
|
||||
int channels, freq, offset;
|
||||
Frame prevFrame;
|
||||
|
||||
Decoder(Stream *stream, int channels, int freq) : stream(stream), channels(channels), freq(freq), offset(stream->pos) {
|
||||
memset(&prevFrame, 0, sizeof(prevFrame));
|
||||
}
|
||||
|
||||
Decoder(Stream *stream, int channels) : stream(stream), channels(channels), offset(stream->pos) {}
|
||||
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 freq, size, bits;
|
||||
Frame frameLast;
|
||||
int size, bits;
|
||||
|
||||
PCM(Stream *stream, int channels, int freq, int size, int bits) : Decoder(stream, channels), freq(freq), size(size), bits(bits) { frameLast.L = frameLast.R = 0; }
|
||||
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;
|
||||
@ -178,41 +222,7 @@ namespace Sound {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dL = int(frame.L) - int(frameLast.L);
|
||||
int dR = int(frame.R) - int(frameLast.R);
|
||||
switch (freq) {
|
||||
case 11025 :
|
||||
if (channels == 2) {
|
||||
frames[0].L = frameLast.L + dL / 4; // 0.25 L
|
||||
frames[0].R = frameLast.R + dR / 4; // 0.25 R
|
||||
frames[1].L = frameLast.L + dL / 2; // 0.50 L
|
||||
frames[1].R = frameLast.R + dR / 2; // 0.50 R
|
||||
frames[2].L = frameLast.L + dL * 3 / 4; // 0.75 L
|
||||
frames[2].R = frameLast.R + dR * 3 / 4; // 0.75 R
|
||||
} else {
|
||||
frames[0].L = frames[0].R = frameLast.L + dL / 4; // 0.25 LR
|
||||
frames[1].L = frames[1].R = frameLast.L + dL / 2; // 0.50 LR
|
||||
frames[2].L = frames[2].R = frameLast.L + dL * 3 / 4; // 0.75 LR
|
||||
}
|
||||
frames[3] = frameLast = frame; // 1.00 LR
|
||||
return 4;
|
||||
case 22050 :
|
||||
if (channels == 2) {
|
||||
frames[0].L = frameLast.L + dL / 2; // 0.50 L
|
||||
frames[0].R = frameLast.R + dR / 2; // 0.50 R
|
||||
} else
|
||||
frames[0].L = frames[0].R = frameLast.L + dL / 2; // 0.50 LR
|
||||
frames[1] = frameLast = frame; // 1.00 LR
|
||||
return 2;
|
||||
case 44100 : // not used
|
||||
frames[0] = frameLast = frame;
|
||||
return 1;
|
||||
default : // impossible
|
||||
ASSERT(false);
|
||||
int k = 44100 / freq;
|
||||
for (int i = 0; i < k; i++) frames[i] = frame; // no lerp
|
||||
return k;
|
||||
}
|
||||
return resample(frames, frame);
|
||||
}
|
||||
};
|
||||
|
||||
@ -220,7 +230,7 @@ namespace Sound {
|
||||
struct ADPCM : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM
|
||||
int size, block;
|
||||
|
||||
ADPCM(Stream *stream, int channels, int size, int block) : Decoder(stream, channels), size(size), block(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;
|
||||
@ -290,6 +300,83 @@ namespace Sound {
|
||||
};
|
||||
#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];
|
||||
|
||||
state.idx += indexLUT[n & 7];
|
||||
state.idx = clamp(state.idx, 0, 88);
|
||||
|
||||
int diff = step >> 3;
|
||||
if (n & 1) diff += step >> 2;
|
||||
if (n & 2) diff += step >> 1;
|
||||
if (n & 4) diff += step;
|
||||
|
||||
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 & 0x0F, state[0]);
|
||||
int b = getSample(n >> 4, 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;
|
||||
@ -297,7 +384,7 @@ namespace Sound {
|
||||
Frame buffer[28 * 4];
|
||||
int bufferSize;
|
||||
|
||||
VAG(Stream *stream) : Decoder(stream, 1), s1(0), s2(0), bufferSize(0) {}
|
||||
VAG(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), bufferSize(0) {}
|
||||
|
||||
void predicate(short value) {
|
||||
int inc[] = { 0, 60, 115, 98, 122 };
|
||||
@ -369,7 +456,7 @@ namespace Sound {
|
||||
char *buffer;
|
||||
int size, pos;
|
||||
|
||||
MP3(Stream *stream, int channels) : Decoder(stream, channels), size(stream->size), pos(0) {
|
||||
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);
|
||||
@ -409,7 +496,7 @@ namespace Sound {
|
||||
stb_vorbis *ogg;
|
||||
stb_vorbis_alloc alloc;
|
||||
|
||||
OGG(Stream *stream, int channels) : Decoder(stream, channels), ogg(NULL) {
|
||||
OGG(Stream *stream, int channels) : Decoder(stream, channels, 0), ogg(NULL) {
|
||||
char buf[255];
|
||||
strcpy(buf, Stream::contentDir);
|
||||
strcat(buf, stream->name);
|
||||
@ -420,6 +507,7 @@ namespace Sound {
|
||||
ASSERT(ogg);
|
||||
stb_vorbis_info info = stb_vorbis_get_info(ogg);
|
||||
this->channels = info.channels;
|
||||
this->freq = info.sample_rate;
|
||||
}
|
||||
|
||||
virtual ~OGG() {
|
||||
@ -512,7 +600,7 @@ namespace Sound {
|
||||
} 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, size, waveFmt.block);
|
||||
if (waveFmt.format == 2) decoder = new ADPCM(stream, waveFmt.channels, size, waveFmt.block, waveFmt.samplesPerSec);
|
||||
#endif
|
||||
break;
|
||||
} else
|
||||
|
97
src/video.h
97
src/video.h
@ -10,21 +10,22 @@ struct Video {
|
||||
struct Decoder : Sound::Decoder {
|
||||
int width, height, fps;
|
||||
|
||||
Decoder(Stream *stream) : Sound::Decoder(stream, 2) {}
|
||||
Decoder(Stream *stream) : Sound::Decoder(stream, 2, 0) {}
|
||||
virtual ~Decoder() { /* delete stream; */ }
|
||||
virtual bool decode(uint8 *frame) { return false; }
|
||||
};
|
||||
|
||||
// based on ffmpeg code https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/escape124.c
|
||||
struct Escape124 : Decoder {
|
||||
struct Escape : Decoder {
|
||||
int vfmt, bpp;
|
||||
int sfmt, rate, channels, bps;
|
||||
int framesCount, chunksCount, offset;
|
||||
int curVideoPos, curVideoChunk;
|
||||
int curAudioPos, curAudioChunk;
|
||||
|
||||
Sound::Decoder *audioDecoder;
|
||||
|
||||
uint32 *prevFrame, *nextFrame;
|
||||
Sound::Frame prevSample;
|
||||
|
||||
struct Chunk {
|
||||
int offset;
|
||||
@ -46,7 +47,7 @@ struct Video {
|
||||
MacroBlock *blocks;
|
||||
} codebook[3];
|
||||
|
||||
Escape124(Stream *stream) : Decoder(stream), chunks(NULL) {
|
||||
Escape(Stream *stream) : Decoder(stream), audioDecoder(NULL), chunks(NULL) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
skipLine();
|
||||
|
||||
@ -89,10 +90,20 @@ struct Video {
|
||||
curVideoPos = curVideoChunk = 0;
|
||||
curAudioPos = curAudioChunk = 0;
|
||||
|
||||
prevSample.L = prevSample.R = 0;
|
||||
if (sfmt == 1)
|
||||
audioDecoder = new Sound::PCM(stream, channels, rate, 0, bps); // TR2
|
||||
else if (sfmt == 101)
|
||||
if (bps == 8)
|
||||
audioDecoder = new Sound::PCM(stream, channels, rate, 0, bps); // TR1
|
||||
else
|
||||
audioDecoder = new Sound::IMA(stream, channels, rate); // TR3
|
||||
}
|
||||
|
||||
virtual ~Escape124() {
|
||||
virtual ~Escape() {
|
||||
if (audioDecoder) {
|
||||
audioDecoder->stream = NULL;
|
||||
delete audioDecoder;
|
||||
}
|
||||
delete[] chunks;
|
||||
delete[] codebook[0].blocks;
|
||||
delete[] codebook[1].blocks;
|
||||
@ -199,6 +210,16 @@ struct Video {
|
||||
}
|
||||
stream->setPos(chunks[curVideoChunk].offset + curVideoPos);
|
||||
|
||||
switch (vfmt) {
|
||||
case 124 : return decode124(frame);
|
||||
case 130 : return decode130(frame);
|
||||
default : ASSERT(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool decode124(uint8 *frame) {
|
||||
uint32 flags, size;
|
||||
stream->read(flags);
|
||||
stream->read(size);
|
||||
@ -323,55 +344,46 @@ struct Video {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool decode130(uint8 *frame) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int decode(Sound::Frame *frames, int count) {
|
||||
if (!audioDecoder) return 0;
|
||||
|
||||
if (abs(curAudioChunk - curVideoChunk) > 1) { // sync with video chunk
|
||||
curAudioChunk = curVideoChunk;
|
||||
curAudioPos = 0;
|
||||
}
|
||||
|
||||
if (curAudioChunk >= chunksCount) {
|
||||
for (int i = 0; i < count; i++)
|
||||
frames[i].L = frames[i].R = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
Chunk *chunk = &chunks[curAudioChunk];
|
||||
stream->setPos(chunk->offset + chunk->videoSize + curAudioPos);
|
||||
|
||||
int sampleSize = channels * (bps / 8);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
int i = 0;
|
||||
while (i < count) {
|
||||
if (curAudioChunk >= chunksCount) {
|
||||
frames[i].L = frames[i].R = 0;
|
||||
continue;
|
||||
memset(&frames[i], 0, sizeof(Sound::Frame) * (count - i));
|
||||
break;
|
||||
}
|
||||
|
||||
if (sfmt == 101) {
|
||||
ubyte2 sample;
|
||||
stream->raw(&sample, sizeof(sample));
|
||||
frames[i].L = uint16(sample.x) << 7;
|
||||
frames[i].R = uint16(sample.y) << 7;
|
||||
} else if (sfmt == 1) {
|
||||
Sound::Frame sample;
|
||||
Chunk *chunk = &chunks[curAudioChunk];
|
||||
|
||||
stream->raw(&sample, sizeof(Sound::Frame));
|
||||
|
||||
frames[i].L = (int(prevSample.L) + int(sample.L)) / 2;
|
||||
frames[i].R = (int(prevSample.R) + int(sample.R)) / 2;
|
||||
i++;
|
||||
frames[i] = prevSample = sample;
|
||||
}
|
||||
|
||||
curAudioPos += sampleSize;
|
||||
if (curAudioPos >= chunk->audioSize) {
|
||||
curAudioPos = 0;
|
||||
curAudioChunk++;
|
||||
if (curAudioChunk < chunksCount) {
|
||||
chunk = &chunks[curAudioChunk];
|
||||
stream->setPos(chunk->offset + chunk->videoSize);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
stream->setPos(chunk->offset + chunk->videoSize + curAudioPos);
|
||||
|
||||
int part = min(count - i, (chunk->audioSize - curAudioPos) / (channels * bps / 8));
|
||||
|
||||
int pos = stream->pos;
|
||||
|
||||
while (part > 0) {
|
||||
int res = audioDecoder->decode(&frames[i], part);
|
||||
i += res;
|
||||
part -= res;
|
||||
}
|
||||
|
||||
curAudioPos += stream->pos - pos;
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -382,7 +394,6 @@ struct Video {
|
||||
Texture *frameTex[2];
|
||||
uint8 *frameData;
|
||||
float time, step;
|
||||
float invFPS;
|
||||
bool isPlaying;
|
||||
bool needUpdate;
|
||||
Sound::Sample *sample;
|
||||
@ -392,7 +403,7 @@ struct Video {
|
||||
|
||||
if (!stream) return;
|
||||
|
||||
decoder = new Escape124(stream);
|
||||
decoder = new Escape(stream);
|
||||
frameData = new uint8[decoder->width * decoder->height * 4];
|
||||
memset(frameData, 0, decoder->width * decoder->height * 4);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user