mirror of
https://github.com/DrBeef/Raze.git
synced 2025-02-20 18:52:43 +00:00
- movieplayer update from GZDoom.
This commit is contained in:
parent
5a580c145e
commit
09b31afd40
7 changed files with 1060 additions and 589 deletions
|
@ -47,6 +47,10 @@
|
|||
#include "filesystem.h"
|
||||
#include "vm.h"
|
||||
#include "printf.h"
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <zmusic.h>
|
||||
#include "filereadermusicinterface.h"
|
||||
|
||||
class MoviePlayer
|
||||
{
|
||||
|
@ -67,6 +71,78 @@ public:
|
|||
virtual FTextureID GetTexture() = 0;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// A simple filter is used to smooth out jittery timers
|
||||
static const double AudioAvgFilterCoeff{std::pow(0.01, 1.0/10.0)};
|
||||
// A threshold is in place to avoid constantly skipping due to imprecise timers.
|
||||
static constexpr double AudioSyncThreshold{0.03};
|
||||
|
||||
class MovieAudioTrack
|
||||
{
|
||||
SoundStream *AudioStream = nullptr;
|
||||
int SampleRate = 0;
|
||||
int FrameSize = 0;
|
||||
int64_t EndClockDiff = 0;
|
||||
|
||||
public:
|
||||
MovieAudioTrack() = default;
|
||||
~MovieAudioTrack()
|
||||
{
|
||||
if(AudioStream)
|
||||
S_StopCustomStream(AudioStream);
|
||||
}
|
||||
|
||||
bool Start(int srate, int channels, MusicCustomStreamType sampletype, StreamCallback callback, void *ptr)
|
||||
{
|
||||
SampleRate = srate;
|
||||
FrameSize = channels * ((sampletype == MusicSamples16bit) ? sizeof(int16_t) : sizeof(float));
|
||||
int bufsize = 40 * SampleRate / 1000 * FrameSize;
|
||||
AudioStream = S_CreateCustomStream(bufsize, SampleRate, channels, sampletype, callback, ptr);
|
||||
return !!AudioStream;
|
||||
}
|
||||
|
||||
void Finish()
|
||||
{
|
||||
if(AudioStream)
|
||||
S_StopCustomStream(AudioStream);
|
||||
AudioStream = nullptr;
|
||||
}
|
||||
|
||||
uint64_t GetClockTime(uint64_t clock)
|
||||
{
|
||||
// If there's no stream playing, report the frame clock adjusted by the audio
|
||||
// end time. This ensures the returned clock keeps incrementing even after
|
||||
// the audio stopped.
|
||||
if(!AudioStream || EndClockDiff != 0)
|
||||
return clock + EndClockDiff;
|
||||
|
||||
auto pos = AudioStream->GetPlayPosition();
|
||||
int64_t postime = static_cast<int64_t>(pos.samplesplayed / double(SampleRate) * 1'000'000'000.0);
|
||||
postime = std::max<int64_t>(0, postime - pos.latency.count());
|
||||
|
||||
if(AudioStream->IsEnded())
|
||||
{
|
||||
// If the stream just ended, get the difference between the frame clock and
|
||||
// the audio end time, so future calls keep incrementing the clock from this
|
||||
// point. An alternative option may be to allow the AudioStream to hook into
|
||||
// the audio device clock, which can keep incrementing at the same rate
|
||||
// without the stream itself actually playing.
|
||||
EndClockDiff = postime - clock;
|
||||
}
|
||||
|
||||
return static_cast<uint64_t>(postime);
|
||||
}
|
||||
|
||||
SoundStream *GetAudioStream() const noexcept { return AudioStream; }
|
||||
int GetSampleRate() const noexcept { return SampleRate; }
|
||||
int GetFrameSize() const noexcept { return FrameSize; }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
|
@ -174,8 +250,16 @@ public:
|
|||
class MvePlayer : public MoviePlayer
|
||||
{
|
||||
InterplayDecoder decoder;
|
||||
MovieAudioTrack audioTrack;
|
||||
bool failed = false;
|
||||
|
||||
bool StreamCallback(SoundStream*, void *buff, int len)
|
||||
{
|
||||
return decoder.FillSamples(buff, len);
|
||||
}
|
||||
static bool StreamCallbackC(SoundStream *stream, void *buff, int len, void *userdata)
|
||||
{ return static_cast<MvePlayer*>(userdata)->StreamCallback(stream, buff, len); }
|
||||
|
||||
public:
|
||||
bool isvalid() { return !failed; }
|
||||
|
||||
|
@ -193,12 +277,23 @@ public:
|
|||
bool Frame(uint64_t clock) override
|
||||
{
|
||||
if (failed) return false;
|
||||
bool playon = decoder.RunFrame(clock);
|
||||
|
||||
if (!audioTrack.GetAudioStream() && decoder.HasAudio() && clock != 0)
|
||||
{
|
||||
S_StopMusic(true);
|
||||
// start audio playback
|
||||
if (!audioTrack.Start(decoder.GetSampleRate(), decoder.NumChannels(), MusicSamples16bit, StreamCallbackC, this))
|
||||
decoder.DisableAudio();
|
||||
}
|
||||
|
||||
bool playon = decoder.RunFrame(audioTrack.GetClockTime(clock));
|
||||
return playon;
|
||||
}
|
||||
|
||||
~MvePlayer()
|
||||
{
|
||||
audioTrack.Finish();
|
||||
|
||||
decoder.Close();
|
||||
}
|
||||
|
||||
|
@ -221,6 +316,9 @@ class VpxPlayer : public MoviePlayer
|
|||
AnimTextures animtex;
|
||||
const TArray<int> animSnd;
|
||||
|
||||
ZMusic_MusicStream MusicStream = nullptr;
|
||||
MovieAudioTrack AudioTrack;
|
||||
|
||||
unsigned width, height;
|
||||
TArray<uint8_t> Pic;
|
||||
TArray<uint8_t> readBuf;
|
||||
|
@ -240,6 +338,12 @@ class VpxPlayer : public MoviePlayer
|
|||
public:
|
||||
int soundtrack = -1;
|
||||
|
||||
bool StreamCallback(SoundStream*, void *buff, int len)
|
||||
{
|
||||
return ZMusic_FillStream(MusicStream, buff, len);
|
||||
}
|
||||
static bool StreamCallbackC(SoundStream *stream, void *buff, int len, void *userdata)
|
||||
{ return static_cast<VpxPlayer*>(userdata)->StreamCallback(stream, buff, len); }
|
||||
|
||||
public:
|
||||
bool isvalid() { return !failed; }
|
||||
|
@ -405,9 +509,21 @@ public:
|
|||
|
||||
void Start() override
|
||||
{
|
||||
if (soundtrack > 0)
|
||||
if (SoundStream *stream = AudioTrack.GetAudioStream())
|
||||
{
|
||||
S_ChangeMusic(fileSystem.GetFileFullName(soundtrack, false), 0, false);
|
||||
stream->SetPaused(false);
|
||||
}
|
||||
else if (soundtrack >= 0)
|
||||
{
|
||||
FileReader reader = fileSystem.OpenFileReader(soundtrack);
|
||||
if (reader.isOpen())
|
||||
{
|
||||
MusicStream = ZMusic_OpenSong(GetMusicReader(reader), MDEV_DEFAULT, nullptr);
|
||||
}
|
||||
if (!MusicStream)
|
||||
{
|
||||
Printf(PRINT_BOLD, "Failed to decode %s\n", fileSystem.GetFileFullName(soundtrack, false));
|
||||
}
|
||||
}
|
||||
animtex.SetSize(AnimTexture::YUV, width, height);
|
||||
}
|
||||
|
@ -420,8 +536,31 @@ public:
|
|||
|
||||
bool Frame(uint64_t clock) override
|
||||
{
|
||||
if (!AudioTrack.GetAudioStream() && MusicStream && clock != 0)
|
||||
{
|
||||
S_StopMusic(true);
|
||||
|
||||
bool ok = false;
|
||||
SoundStreamInfo info{};
|
||||
ZMusic_GetStreamInfo(MusicStream, &info);
|
||||
// if mBufferSize == 0, the music stream is played externally (e.g.
|
||||
// Windows' MIDI synth), which we can't keep synced. Play anyway?
|
||||
if (info.mBufferSize > 0 && ZMusic_Start(MusicStream, 0, false))
|
||||
{
|
||||
ok = AudioTrack.Start(info.mSampleRate, abs(info.mNumChannels),
|
||||
(info.mNumChannels < 0) ? MusicSamples16bit : MusicSamplesFloat, &StreamCallbackC, this);
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
ZMusic_Close(MusicStream);
|
||||
MusicStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
clock = AudioTrack.GetClockTime(clock);
|
||||
|
||||
bool stop = false;
|
||||
if (clock > nextframetime)
|
||||
if (clock >= nextframetime)
|
||||
{
|
||||
nextframetime += nsecsperframe;
|
||||
|
||||
|
@ -461,15 +600,21 @@ public:
|
|||
return !stop;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
void Stop() override
|
||||
{
|
||||
S_StopMusic(true);
|
||||
if (SoundStream *stream = AudioTrack.GetAudioStream())
|
||||
stream->SetPaused(true);
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
~VpxPlayer()
|
||||
{
|
||||
if(MusicStream)
|
||||
{
|
||||
AudioTrack.Finish();
|
||||
ZMusic_Close(MusicStream);
|
||||
}
|
||||
vpx_codec_destroy(&codec);
|
||||
animtex.Clean();
|
||||
}
|
||||
|
@ -488,13 +633,10 @@ public:
|
|||
|
||||
struct AudioData
|
||||
{
|
||||
int hFx;
|
||||
SmackerAudioInfo inf;
|
||||
|
||||
int16_t samples[6000 * 20]; // must be a multiple of the stream buffer size and larger than the initial chunk of audio
|
||||
|
||||
int nWrite;
|
||||
int nRead;
|
||||
int nWrite = 0;
|
||||
int nRead = 0;
|
||||
};
|
||||
|
||||
class SmkPlayer : public MoviePlayer
|
||||
|
@ -506,46 +648,59 @@ class SmkPlayer : public MoviePlayer
|
|||
uint8_t palette[768];
|
||||
AnimTextures animtex;
|
||||
TArray<uint8_t> pFrame;
|
||||
TArray<uint8_t> audioBuffer;
|
||||
TArray<int16_t> audioBuffer;
|
||||
int nFrames;
|
||||
bool fullscreenScale;
|
||||
uint64_t nFrameNs;
|
||||
int nFrame = 0;
|
||||
const TArray<int> animSnd;
|
||||
FString filename;
|
||||
SoundStream* stream = nullptr;
|
||||
MovieAudioTrack AudioTrack;
|
||||
bool hassound = false;
|
||||
|
||||
public:
|
||||
bool isvalid() { return hSMK.isValid; }
|
||||
|
||||
static bool StreamCallbackFunc(SoundStream* stream, void* buff, int len, void* userdata)
|
||||
bool StreamCallback(SoundStream* stream, void* buff, int len)
|
||||
{
|
||||
SmkPlayer* pId = (SmkPlayer*)userdata;
|
||||
memcpy(buff, &pId->adata.samples[pId->adata.nRead], len);
|
||||
pId->adata.nRead += len / 2;
|
||||
if (pId->adata.nRead >= (int)countof(pId->adata.samples)) pId->adata.nRead = 0;
|
||||
const int samplerate = AudioTrack.GetSampleRate();
|
||||
const int framesize = AudioTrack.GetFrameSize();
|
||||
|
||||
int avail = (adata.nWrite - adata.nRead) * 2;
|
||||
|
||||
int wrote = 0;
|
||||
while(wrote < len)
|
||||
{
|
||||
if (avail == 0)
|
||||
{
|
||||
auto read = Smacker_GetAudioData(hSMK, 0, audioBuffer.Data());
|
||||
if (read == 0)
|
||||
{
|
||||
if (wrote == 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
adata.nWrite = read / 2;
|
||||
avail = read;
|
||||
}
|
||||
|
||||
int todo = std::min(len-wrote, avail);
|
||||
|
||||
memcpy((char*)buff+wrote, &audioBuffer[adata.nRead], todo);
|
||||
adata.nRead += todo / 2;
|
||||
if(adata.nRead == adata.nWrite)
|
||||
adata.nRead = adata.nWrite = 0;
|
||||
avail -= todo;
|
||||
wrote += todo;
|
||||
}
|
||||
|
||||
if (wrote < len)
|
||||
memset((char*)buff+wrote, 0, len-wrote);
|
||||
return true;
|
||||
}
|
||||
|
||||
void copy8bitSamples(unsigned count)
|
||||
{
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
adata.samples[adata.nWrite] = (audioBuffer[i] - 128) << 8;
|
||||
if (++adata.nWrite >= (int)countof(adata.samples)) adata.nWrite = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void copy16bitSamples(unsigned count)
|
||||
{
|
||||
auto ptr = (uint16_t*)audioBuffer.Data();
|
||||
for (unsigned i = 0; i < count/2; i++)
|
||||
{
|
||||
adata.samples[adata.nWrite] = *ptr++;
|
||||
if (++adata.nWrite >= (int)countof(adata.samples)) adata.nWrite = 0;
|
||||
}
|
||||
}
|
||||
static bool StreamCallbackC(SoundStream* stream, void* buff, int len, void* userdata)
|
||||
{ return static_cast<SmkPlayer*>(userdata)->StreamCallback(stream, buff, len); }
|
||||
|
||||
|
||||
SmkPlayer(const char *fn, TArray<int>& ans, int flags_) : animSnd(std::move(ans))
|
||||
|
@ -564,25 +719,26 @@ public:
|
|||
Smacker_GetPalette(hSMK, palette);
|
||||
|
||||
numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
|
||||
if (numAudioTracks)
|
||||
if (numAudioTracks && SoundEnabled())
|
||||
{
|
||||
adata.nWrite = 0;
|
||||
adata.nRead = 0;
|
||||
adata.inf = Smacker_GetAudioTrackDetails(hSMK, 0);
|
||||
if (adata.inf.idealBufferSize > 0)
|
||||
{
|
||||
audioBuffer.Resize(adata.inf.idealBufferSize);
|
||||
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
||||
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
||||
else copy16bitSamples(read);
|
||||
audioBuffer.Resize(adata.inf.idealBufferSize / 2);
|
||||
hassound = true;
|
||||
}
|
||||
for (int i = 1;i < numAudioTracks;++i)
|
||||
Smacker_DisableAudioTrack(hSMK, i);
|
||||
numAudioTracks = 1;
|
||||
}
|
||||
if (!hassound)
|
||||
{
|
||||
adata.inf = {};
|
||||
Smacker_DisableAudioTrack(hSMK, 0);
|
||||
numAudioTracks = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -594,6 +750,8 @@ public:
|
|||
void Start() override
|
||||
{
|
||||
animtex.SetSize(AnimTexture::Paletted, nWidth, nHeight);
|
||||
if (SoundStream *stream = AudioTrack.GetAudioStream())
|
||||
stream->SetPaused(false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -604,30 +762,29 @@ public:
|
|||
|
||||
bool Frame(uint64_t clock) override
|
||||
{
|
||||
if (!AudioTrack.GetAudioStream() && numAudioTracks && clock != 0)
|
||||
{
|
||||
S_StopMusic(true);
|
||||
|
||||
if (!AudioTrack.Start(adata.inf.sampleRate, adata.inf.nChannels, MusicSamples16bit, StreamCallbackC, this))
|
||||
{
|
||||
Smacker_DisableAudioTrack(hSMK, 0);
|
||||
numAudioTracks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
clock = AudioTrack.GetClockTime(clock);
|
||||
int frame = int(clock / nFrameNs);
|
||||
|
||||
twod->ClearScreen();
|
||||
if (frame > nFrame)
|
||||
{
|
||||
Smacker_GetPalette(hSMK, palette);
|
||||
Smacker_GetFrame(hSMK, pFrame.Data());
|
||||
animtex.SetFrame(palette, pFrame.Data());
|
||||
if (numAudioTracks && SoundEnabled())
|
||||
{
|
||||
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
||||
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
||||
else copy16bitSamples(read);
|
||||
if (!stream && read) // the sound may not start in the first frame, but the stream cannot start without any sound data present.
|
||||
stream = S_CreateCustomStream(6000, adata.inf.sampleRate, adata.inf.nChannels, MusicSamples16bit, StreamCallbackFunc, this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (frame > nFrame)
|
||||
if (frame >= nFrame)
|
||||
{
|
||||
nFrame++;
|
||||
Smacker_GetNextFrame(hSMK);
|
||||
Smacker_GetPalette(hSMK, palette);
|
||||
Smacker_GetFrame(hSMK, pFrame.Data());
|
||||
animtex.SetFrame(palette, pFrame.Data());
|
||||
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!hassound) for (unsigned i = 0; i < animSnd.Size(); i += 2)
|
||||
{
|
||||
|
@ -647,13 +804,15 @@ public:
|
|||
|
||||
void Stop() override
|
||||
{
|
||||
if (stream) S_StopCustomStream(stream);
|
||||
if (SoundStream *stream = AudioTrack.GetAudioStream())
|
||||
stream->SetPaused(true);
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound && !hassound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
~SmkPlayer()
|
||||
{
|
||||
AudioTrack.Finish();
|
||||
Smacker_Close(hSMK);
|
||||
animtex.Clean();
|
||||
}
|
||||
|
@ -687,7 +846,7 @@ MoviePlayer* OpenMovie(const char* filename, TArray<int>& ans, const int* framet
|
|||
{
|
||||
size_t nLen = strlen(filename);
|
||||
// Strip the drive letter and retry.
|
||||
if (nLen >= 3 && isalpha((uint8_t)filename[0]) && filename[1] == ':' && filename[2] == '/')
|
||||
if (nLen >= 3 && isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
|
||||
{
|
||||
filename += 3;
|
||||
fr = fileSystem.OpenFileReader(filename);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,6 +44,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "files.h"
|
||||
#include "animtexture.h"
|
||||
#include "s_music.h"
|
||||
|
@ -103,26 +108,49 @@ public:
|
|||
|
||||
bool Open(FileReader &fr);
|
||||
void Close();
|
||||
|
||||
bool RunFrame(uint64_t clock);
|
||||
|
||||
struct AudioData
|
||||
{
|
||||
int hFx;
|
||||
int nChannels;
|
||||
uint16_t nSampleRate;
|
||||
uint8_t nBitDepth;
|
||||
bool FillSamples(void *buff, int len);
|
||||
|
||||
int16_t samples[6000 * kAudioBlocks]; // must be a multiple of the stream buffer size
|
||||
int nWrite;
|
||||
int nRead;
|
||||
};
|
||||
|
||||
AudioData audio;
|
||||
AnimTextures animtex;
|
||||
bool HasAudio() const noexcept { return bAudioEnabled; }
|
||||
int NumChannels() const noexcept { return audio.nChannels; }
|
||||
int GetSampleRate() const noexcept { return audio.nSampleRate; }
|
||||
void DisableAudio();
|
||||
|
||||
AnimTextures& animTex() { return animtex; }
|
||||
|
||||
private:
|
||||
struct AudioPacket
|
||||
{
|
||||
size_t nSize = 0;
|
||||
std::unique_ptr<uint8_t[]> pData;
|
||||
};
|
||||
|
||||
struct VideoPacket
|
||||
{
|
||||
uint16_t nPalStart=0, nPalCount=0;
|
||||
uint32_t nDecodeMapSize = 0;
|
||||
uint32_t nVideoDataSize = 0;
|
||||
bool bSendFlag = false;
|
||||
std::unique_ptr<uint8_t[]> pData;
|
||||
};
|
||||
|
||||
struct AudioData
|
||||
{
|
||||
int nChannels = 0;
|
||||
uint16_t nSampleRate = 0;
|
||||
uint8_t nBitDepth = 0;
|
||||
bool bCompressed = false;
|
||||
|
||||
std::unique_ptr<int16_t[]> samples;
|
||||
int nWrite = 0;
|
||||
int nRead = 0;
|
||||
|
||||
std::deque<AudioPacket> Packets;
|
||||
};
|
||||
AudioData audio;
|
||||
|
||||
struct DecodeMap
|
||||
{
|
||||
uint8_t* pData;
|
||||
|
@ -156,24 +184,30 @@ private:
|
|||
void DecodeBlock14(int32_t offset);
|
||||
void DecodeBlock15(int32_t offset);
|
||||
|
||||
std::mutex PacketMutex;
|
||||
FileReader fr;
|
||||
|
||||
bool bIsPlaying, bAudioStarted;
|
||||
bool bIsPlaying, bAudioEnabled;
|
||||
|
||||
uint32_t nTimerRate, nTimerDiv;
|
||||
uint32_t nWidth, nHeight, nFrame;
|
||||
double nFps;
|
||||
uint64_t nFrameDuration;
|
||||
|
||||
std::vector<uint8_t> ChunkData;
|
||||
int ProcessNextChunk();
|
||||
|
||||
std::deque<VideoPacket> VideoPackets;
|
||||
uint8_t* pVideoBuffers[2];
|
||||
uint32_t nCurrentVideoBuffer, nPreviousVideoBuffer;
|
||||
int32_t videoStride;
|
||||
|
||||
const uint8_t *ChunkPtr = nullptr;
|
||||
DecodeMap decodeMap;
|
||||
|
||||
AnimTextures animtex;
|
||||
Palette palette[256];
|
||||
uint64_t nNextFrameTime = 0;
|
||||
SoundStream* stream = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,8 +29,8 @@ namespace SmackerCommon {
|
|||
class BitReader
|
||||
{
|
||||
public:
|
||||
BitReader(SmackerCommon::FileStream &file, uint32_t size);
|
||||
~BitReader();
|
||||
BitReader(const uint8_t *data, uint32_t size) : bitData(data), totalSize(size) { }
|
||||
~BitReader() = default;
|
||||
uint32_t GetBit();
|
||||
uint32_t GetBits(uint32_t n);
|
||||
void SkipBits(uint32_t n);
|
||||
|
@ -39,15 +39,10 @@ class BitReader
|
|||
uint32_t GetPosition();
|
||||
|
||||
private:
|
||||
uint32_t totalSize;
|
||||
uint32_t currentOffset;
|
||||
uint32_t bytesRead;
|
||||
|
||||
SmackerCommon::FileStream *file;
|
||||
|
||||
TArray<uint8_t> Cache;
|
||||
|
||||
void FillCache();
|
||||
const uint8_t *bitData = nullptr;
|
||||
uint32_t totalSize = 0;
|
||||
uint32_t currentOffset = 0;
|
||||
uint32_t bytesRead = 0;
|
||||
};
|
||||
|
||||
} // close namespace SmackerCommon
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
#include <stdint.h>
|
||||
#include "FileStream.h"
|
||||
#include "BitReader.h"
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
// exportable interface
|
||||
|
@ -61,7 +64,6 @@ struct SmackerAudioInfo
|
|||
{
|
||||
uint32_t sampleRate;
|
||||
uint8_t nChannels;
|
||||
uint8_t bitsPerSample;
|
||||
|
||||
uint32_t idealBufferSize;
|
||||
};
|
||||
|
@ -71,6 +73,7 @@ void Smacker_Close (SmackerHandle &handle);
|
|||
uint32_t Smacker_GetNumAudioTracks (SmackerHandle &handle);
|
||||
SmackerAudioInfo Smacker_GetAudioTrackDetails (SmackerHandle &handle, uint32_t trackIndex);
|
||||
uint32_t Smacker_GetAudioData (SmackerHandle &handle, uint32_t trackIndex, int16_t *data);
|
||||
void Smacker_DisableAudioTrack (SmackerHandle &handle, uint32_t trackIndex);
|
||||
uint32_t Smacker_GetNumFrames (SmackerHandle &handle);
|
||||
void Smacker_GetFrameSize (SmackerHandle &handle, uint32_t &width, uint32_t &height);
|
||||
uint32_t Smacker_GetCurrentFrameNum (SmackerHandle &handle);
|
||||
|
@ -86,6 +89,12 @@ const int kMaxAudioTracks = 7;
|
|||
struct HuffContext;
|
||||
struct DBCtx;
|
||||
|
||||
struct SmackerPacket
|
||||
{
|
||||
size_t size = 0;
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
};
|
||||
|
||||
struct SmackerAudioTrack
|
||||
{
|
||||
uint32_t sizeInBytes;
|
||||
|
@ -99,6 +108,7 @@ struct SmackerAudioTrack
|
|||
uint32_t bufferSize;
|
||||
|
||||
uint32_t bytesReadThisFrame;
|
||||
std::deque<SmackerPacket> packetData;
|
||||
};
|
||||
|
||||
class SmackerDecoder
|
||||
|
@ -114,8 +124,10 @@ class SmackerDecoder
|
|||
void GetPalette(uint8_t *palette);
|
||||
void GetFrame(uint8_t *frame);
|
||||
|
||||
uint32_t GetNumAudioTracks();
|
||||
SmackerAudioInfo GetAudioTrackDetails(uint32_t trackIndex);
|
||||
uint32_t GetAudioData(uint32_t trackIndex, int16_t *audioBuffer);
|
||||
void DisableAudioTrack(uint32_t trackIndex);
|
||||
uint32_t GetNumFrames();
|
||||
uint32_t GetCurrentFrameNum();
|
||||
float GetFrameRate();
|
||||
|
@ -125,6 +137,7 @@ class SmackerDecoder
|
|||
private:
|
||||
SmackerCommon::FileStream file;
|
||||
char signature[4];
|
||||
std::mutex fileMutex;
|
||||
|
||||
// video related members
|
||||
uint32_t nFrames;
|
||||
|
@ -135,6 +148,10 @@ class SmackerDecoder
|
|||
|
||||
bool isVer4;
|
||||
|
||||
uint32_t currentReadFrame;
|
||||
std::vector<uint8_t> packetData;
|
||||
|
||||
std::deque<SmackerPacket> framePacketData;
|
||||
SmackerAudioTrack audioTracks[kMaxAudioTracks];
|
||||
|
||||
uint32_t treeSize;
|
||||
|
@ -160,9 +177,9 @@ class SmackerDecoder
|
|||
int DecodeBigTree(SmackerCommon::BitReader &bits, HuffContext *hc, DBCtx *ctx);
|
||||
int GetCode(SmackerCommon::BitReader &bits, std::vector<int> &recode, int *last);
|
||||
int ReadPacket();
|
||||
int DecodeFrame(uint32_t frameSize);
|
||||
int DecodeFrame(const uint8_t *dataPtr, uint32_t frameSize);
|
||||
void GetFrameSize(uint32_t &width, uint32_t &height);
|
||||
int DecodeAudio(uint32_t size, SmackerAudioTrack &track);
|
||||
int DecodeAudio(const uint8_t *dataPtr, uint32_t size, SmackerAudioTrack &track);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,25 +22,6 @@
|
|||
|
||||
namespace SmackerCommon {
|
||||
|
||||
BitReader::BitReader(SmackerCommon::FileStream &file, uint32_t size)
|
||||
{
|
||||
this->file = &file;
|
||||
this->totalSize = size;
|
||||
this->currentOffset = 0;
|
||||
this->bytesRead = 0;
|
||||
|
||||
this->Cache.Resize(size);
|
||||
file.ReadBytes(this->Cache.Data(), size);
|
||||
}
|
||||
|
||||
BitReader::~BitReader()
|
||||
{
|
||||
}
|
||||
|
||||
void BitReader::FillCache()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t BitReader::GetSize()
|
||||
{
|
||||
return totalSize * 8;
|
||||
|
@ -53,7 +34,7 @@ uint32_t BitReader::GetPosition()
|
|||
|
||||
uint32_t BitReader::GetBit()
|
||||
{
|
||||
uint32_t ret = (Cache[currentOffset>>3]>>(currentOffset&7))&1;
|
||||
uint32_t ret = (bitData[currentOffset>>3]>>(currentOffset&7))&1;
|
||||
currentOffset++;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "limits.h"
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
std::vector<class SmackerDecoder*> classInstances;
|
||||
|
||||
|
@ -93,10 +94,9 @@ void Smacker_Close(SmackerHandle &handle)
|
|||
handle.isValid = false;
|
||||
}
|
||||
|
||||
uint32_t Smacker_GetNumAudioTracks(SmackerHandle &)
|
||||
uint32_t Smacker_GetNumAudioTracks(SmackerHandle &handle)
|
||||
{
|
||||
// TODO: fixme
|
||||
return 1;
|
||||
return classInstances[handle.instanceIndex]->GetNumAudioTracks();
|
||||
}
|
||||
|
||||
SmackerAudioInfo Smacker_GetAudioTrackDetails(SmackerHandle &handle, uint32_t trackIndex)
|
||||
|
@ -115,6 +115,18 @@ uint32_t Smacker_GetAudioData(SmackerHandle &handle, uint32_t trackIndex, int16_
|
|||
return classInstances[handle.instanceIndex]->GetAudioData(trackIndex, data);
|
||||
}
|
||||
|
||||
/* Disables an audio track if it's enabled.
|
||||
*
|
||||
* When getting video or audio data, encoded data is stored for other tracks so
|
||||
* they can be read and decoded later. This function prevents a build-up of
|
||||
* encoded audio data for the specified track if such data isn't going to be
|
||||
* decoded and read by the caller.
|
||||
*/
|
||||
void Smacker_DisableAudioTrack(SmackerHandle &handle, uint32_t trackIndex)
|
||||
{
|
||||
classInstances[handle.instanceIndex]->DisableAudioTrack(trackIndex);
|
||||
}
|
||||
|
||||
uint32_t Smacker_GetNumFrames(SmackerHandle &handle)
|
||||
{
|
||||
return classInstances[handle.instanceIndex]->GetNumFrames();
|
||||
|
@ -165,6 +177,7 @@ void Smacker_GotoFrame(SmackerHandle &handle, uint32_t frameNum)
|
|||
SmackerDecoder::SmackerDecoder()
|
||||
{
|
||||
isVer4 = false;
|
||||
currentReadFrame = 0;
|
||||
currentFrame = 0;
|
||||
picture = 0;
|
||||
nextPos = 0;
|
||||
|
@ -223,6 +236,7 @@ static const uint8_t smk_pal[64] = {
|
|||
|
||||
enum SAudFlags {
|
||||
SMK_AUD_PACKED = 0x80000000,
|
||||
SMK_AUD_PRESENT = 0x40000000,
|
||||
SMK_AUD_16BITS = 0x20000000,
|
||||
SMK_AUD_STEREO = 0x10000000,
|
||||
SMK_AUD_BINKAUD = 0x08000000,
|
||||
|
@ -372,6 +386,10 @@ bool SmackerDecoder::Open(const char *fileName)
|
|||
frameFlags[i] = file.ReadByte();
|
||||
}
|
||||
|
||||
auto maxFrameSize = std::max_element(frameSizes.begin(), frameSizes.end());
|
||||
if (maxFrameSize != frameSizes.end())
|
||||
packetData.reserve(*maxFrameSize);
|
||||
|
||||
// handle possible audio streams
|
||||
for (int i = 0; i < kMaxAudioTracks; i++)
|
||||
{
|
||||
|
@ -379,6 +397,11 @@ bool SmackerDecoder::Open(const char *fileName)
|
|||
audioTracks[i].bufferSize = 0;
|
||||
audioTracks[i].bytesReadThisFrame = 0;
|
||||
|
||||
// Disable non-consecutive enabled tracks. Not sure how to otherwise report
|
||||
// them properly for Smacker_GetNumAudioTracks.
|
||||
if (i > 0 && !(audioTracks[i-1].flags & SMK_AUD_PRESENT))
|
||||
audioTracks[i].flags &= ~SMK_AUD_PRESENT;
|
||||
|
||||
if (audioTracks[i].flags & 0xFFFFFF)
|
||||
{
|
||||
/*
|
||||
|
@ -426,13 +449,18 @@ bool SmackerDecoder::Open(const char *fileName)
|
|||
{
|
||||
if (frameFlag & 1)
|
||||
{
|
||||
// skip size
|
||||
file.Skip(4);
|
||||
|
||||
uint32_t size = file.ReadUint32LE();
|
||||
uint32_t unpackedSize = file.ReadUint32LE();
|
||||
|
||||
// If the track isn't 16-bit, double the buffer size for converting 8-bit to 16-bit.
|
||||
if (!(audioTracks[i].flags & SMK_AUD_16BITS))
|
||||
unpackedSize *= 2;
|
||||
|
||||
audioTracks[i].bufferSize = unpackedSize;
|
||||
audioTracks[i].buffer = new uint8_t[unpackedSize];
|
||||
|
||||
// skip size
|
||||
file.Skip(size - 8);
|
||||
}
|
||||
frameFlag >>= 1;
|
||||
}
|
||||
|
@ -633,7 +661,10 @@ int SmackerDecoder::DecodeHeaderTree(SmackerCommon::BitReader &bits, std::vector
|
|||
// static int decode_header_trees(SmackVContext *smk) {
|
||||
bool SmackerDecoder::DecodeHeaderTrees()
|
||||
{
|
||||
SmackerCommon::BitReader bits(file, treeSize);
|
||||
auto treeData = std::make_unique<uint8_t[]>(treeSize);
|
||||
file.ReadBytes(treeData.get(), treeSize);
|
||||
|
||||
SmackerCommon::BitReader bits(treeData.get(), treeSize);
|
||||
|
||||
if (!bits.GetBit())
|
||||
{
|
||||
|
@ -683,58 +714,53 @@ bool SmackerDecoder::DecodeHeaderTrees()
|
|||
DecodeHeaderTree(bits, type_tbl, type_last, typeSize);
|
||||
}
|
||||
|
||||
/* FIXME - we don't seems to read/use EVERY bit we 'load' into the bit reader
|
||||
* and as my bitreader reads from the file rather than a buffer read from file
|
||||
* of size 'treeSize', I need to make sure I consume the remaining bits (and thus increment
|
||||
* the file read position to where the code expects it to be when this function returns (ie
|
||||
* 'treeSize' number of bytes must be read
|
||||
*/
|
||||
uint32_t left = bits.GetSize() - bits.GetPosition();
|
||||
bits.SkipBits(left);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmackerDecoder::GetNextFrame()
|
||||
{
|
||||
ReadPacket();
|
||||
}
|
||||
|
||||
int SmackerDecoder::ReadPacket()
|
||||
{
|
||||
// test-remove
|
||||
if (currentFrame >= nFrames)
|
||||
return 1;
|
||||
return;
|
||||
|
||||
// seek to next frame position
|
||||
file.Seek(nextPos, SmackerCommon::FileStream::kSeekStart);
|
||||
std::unique_lock flock(fileMutex);
|
||||
while (framePacketData.empty())
|
||||
{
|
||||
if (ReadPacket() > 0)
|
||||
return;
|
||||
}
|
||||
SmackerPacket pkt = std::move(framePacketData.front());
|
||||
framePacketData.pop_front();
|
||||
flock.unlock();
|
||||
|
||||
uint32_t frameSize = frameSizes[currentFrame] & (~3);
|
||||
uint32_t frameSize = (uint32_t)pkt.size;
|
||||
uint8_t frameFlag = frameFlags[currentFrame];
|
||||
|
||||
const uint8_t *packetDataPtr = pkt.data.get();
|
||||
|
||||
// handle palette change
|
||||
if (frameFlag & kSMKpal)
|
||||
{
|
||||
int size, sz, t, off, j, pos;
|
||||
int size, sz, t, off, j;
|
||||
uint8_t *pal = palette;
|
||||
uint8_t oldpal[768];
|
||||
const uint8_t *dataEnd;
|
||||
|
||||
memcpy(oldpal, pal, 768);
|
||||
size = file.ReadByte();
|
||||
size = *(packetDataPtr++);
|
||||
size = size * 4 - 1;
|
||||
frameSize -= size;
|
||||
frameSize--;
|
||||
sz = 0;
|
||||
pos = file.GetPosition() + size;
|
||||
dataEnd = packetDataPtr + size;
|
||||
|
||||
while (sz < 256)
|
||||
{
|
||||
t = file.ReadByte();
|
||||
t = *(packetDataPtr++);
|
||||
if (t & 0x80){ /* skip palette entries */
|
||||
sz += (t & 0x7F) + 1;
|
||||
pal += ((t & 0x7F) + 1) * 3;
|
||||
} else if (t & 0x40){ /* copy with offset */
|
||||
off = file.ReadByte() * 3;
|
||||
off = *(packetDataPtr++) * 3;
|
||||
j = (t & 0x3F) + 1;
|
||||
while (j-- && sz < 256) {
|
||||
*pal++ = oldpal[off + 0];
|
||||
|
@ -745,47 +771,92 @@ int SmackerDecoder::ReadPacket()
|
|||
}
|
||||
} else { /* new entries */
|
||||
*pal++ = smk_pal[t];
|
||||
*pal++ = smk_pal[file.ReadByte() & 0x3F];
|
||||
*pal++ = smk_pal[file.ReadByte() & 0x3F];
|
||||
*pal++ = smk_pal[*(packetDataPtr++) & 0x3F];
|
||||
*pal++ = smk_pal[*(packetDataPtr++) & 0x3F];
|
||||
sz++;
|
||||
}
|
||||
}
|
||||
|
||||
file.Seek(pos, SmackerCommon::FileStream::kSeekStart);
|
||||
|
||||
packetDataPtr = dataEnd;
|
||||
}
|
||||
|
||||
if (frameSize > 0)
|
||||
DecodeFrame(packetDataPtr, frameSize);
|
||||
|
||||
currentFrame++;
|
||||
}
|
||||
|
||||
int SmackerDecoder::ReadPacket()
|
||||
{
|
||||
// test-remove
|
||||
if (currentReadFrame >= nFrames)
|
||||
return 1;
|
||||
|
||||
// seek to next frame position
|
||||
file.Seek(nextPos, SmackerCommon::FileStream::kSeekStart);
|
||||
|
||||
uint32_t frameSize = frameSizes[currentReadFrame] & (~3);
|
||||
uint8_t frameFlag = frameFlags[currentReadFrame];
|
||||
|
||||
packetData.resize(frameSize);
|
||||
file.ReadBytes(packetData.data(), frameSize);
|
||||
|
||||
const uint8_t *packetDataPtr = packetData.data();
|
||||
|
||||
// skip pal data for later, after getting audio data
|
||||
if (frameFlag & kSMKpal)
|
||||
{
|
||||
int size = (*packetDataPtr * 4);
|
||||
packetDataPtr += size;
|
||||
frameSize -= size;
|
||||
}
|
||||
|
||||
frameFlag >>= 1;
|
||||
|
||||
// check for and handle audio
|
||||
for (int i = 0; i < kMaxAudioTracks; i++)
|
||||
for (int i = 0; i < kMaxAudioTracks; i++)
|
||||
{
|
||||
audioTracks[i].bytesReadThisFrame = 0;
|
||||
|
||||
if (frameFlag & 1)
|
||||
if (frameFlag & 1)
|
||||
{
|
||||
uint32_t size = file.ReadUint32LE() - 4;
|
||||
uint32_t size = *(packetDataPtr++);
|
||||
size |= *(packetDataPtr++) << 8;
|
||||
size |= *(packetDataPtr++) << 16;
|
||||
size |= *(packetDataPtr++) << 24;
|
||||
frameSize -= size;
|
||||
frameSize -= 4;
|
||||
size -= 4;
|
||||
|
||||
DecodeAudio(size, audioTracks[i]);
|
||||
if (audioTracks[i].flags & SMK_AUD_PRESENT)
|
||||
{
|
||||
SmackerPacket pkt;
|
||||
pkt.size = size;
|
||||
pkt.data = std::make_unique<uint8_t[]>(size);
|
||||
memcpy(pkt.data.get(), packetDataPtr, size);
|
||||
audioTracks[i].packetData.emplace_back(std::move(pkt));
|
||||
}
|
||||
packetDataPtr += size;
|
||||
}
|
||||
frameFlag >>= 1;
|
||||
}
|
||||
|
||||
if (frameSize == 0) {
|
||||
return -1;
|
||||
SmackerPacket pkt;
|
||||
frameFlag = frameFlags[currentReadFrame];
|
||||
if (frameSize != 0 || (frameFlag & kSMKpal))
|
||||
{
|
||||
int palsize = (frameFlag & kSMKpal) ? packetData[0] * 4 : 0;
|
||||
int totalSize = frameSize + palsize;
|
||||
pkt.size = totalSize;
|
||||
pkt.data = std::make_unique<uint8_t[]>(totalSize);
|
||||
memcpy(pkt.data.get(), packetData.data(), palsize);
|
||||
memcpy(pkt.data.get()+palsize, packetDataPtr, frameSize);
|
||||
}
|
||||
framePacketData.emplace_back(std::move(pkt));
|
||||
|
||||
DecodeFrame(frameSize);
|
||||
|
||||
currentFrame++;
|
||||
++currentReadFrame;
|
||||
|
||||
nextPos = file.GetPosition();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SmackerDecoder::DecodeFrame(uint32_t frameSize)
|
||||
int SmackerDecoder::DecodeFrame(const uint8_t *dataPtr, uint32_t frameSize)
|
||||
{
|
||||
last_reset(mmap_tbl, mmap_last);
|
||||
last_reset(mclr_tbl, mclr_last);
|
||||
|
@ -805,7 +876,8 @@ int SmackerDecoder::DecodeFrame(uint32_t frameSize)
|
|||
|
||||
stride = frameWidth;
|
||||
|
||||
SmackerCommon::BitReader bits(file, frameSize);
|
||||
SmackerCommon::BitReader bits(dataPtr, frameSize);
|
||||
dataPtr += frameSize;
|
||||
|
||||
while (blk < blocks)
|
||||
{
|
||||
|
@ -935,24 +1007,14 @@ int SmackerDecoder::DecodeFrame(uint32_t frameSize)
|
|||
}
|
||||
}
|
||||
|
||||
/* FIXME - we don't seems to read/use EVERY bit we 'load' into the bit reader
|
||||
* and as my bitreader reads from the file rather than a buffer read from file
|
||||
* of size 'frameSize', I need to make sure I consume the remaining bits (and thus increment
|
||||
* the file read position to where the code expects it to be when this function returns (ie
|
||||
* 'frameSize' number of bytes must be read
|
||||
*/
|
||||
uint32_t left = bits.GetSize() - bits.GetPosition();
|
||||
bits.SkipBits(left);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode Smacker audio data
|
||||
*/
|
||||
int SmackerDecoder::DecodeAudio(uint32_t size, SmackerAudioTrack &track)
|
||||
int SmackerDecoder::DecodeAudio(const uint8_t *dataPtr, uint32_t size, SmackerAudioTrack &track)
|
||||
{
|
||||
HuffContext h[4];
|
||||
SmackerCommon::VLCtable vlc[4];
|
||||
int val;
|
||||
int i, res;
|
||||
|
@ -961,19 +1023,15 @@ int SmackerDecoder::DecodeAudio(uint32_t size, SmackerAudioTrack &track)
|
|||
int pred[2] = {0, 0};
|
||||
|
||||
int16_t *samples = reinterpret_cast<int16_t*>(track.buffer);
|
||||
int8_t *samples8 = reinterpret_cast<int8_t*>(track.buffer);
|
||||
|
||||
int buf_size = track.bufferSize;
|
||||
SmackerCommon::BitReader bits(dataPtr, size);
|
||||
|
||||
if (buf_size <= 4) {
|
||||
unpackedSize = bits.GetBits(32);
|
||||
if (unpackedSize <= 4) {
|
||||
Printf("SmackerDecoder::DecodeAudio() - Packet is too small\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SmackerCommon::BitReader bits(file, size);
|
||||
|
||||
unpackedSize = bits.GetBits(32);
|
||||
|
||||
if (!bits.GetBit()) {
|
||||
// no sound data
|
||||
return 1;
|
||||
|
@ -987,7 +1045,7 @@ int SmackerDecoder::DecodeAudio(uint32_t size, SmackerAudioTrack &track)
|
|||
return -1;
|
||||
}
|
||||
|
||||
memset(h, 0, sizeof(HuffContext) * 4);
|
||||
HuffContext h[4];
|
||||
|
||||
// Initialize
|
||||
for (i = 0; i < (1 << (sampleBits + stereo)); i++) {
|
||||
|
@ -1045,7 +1103,7 @@ int SmackerDecoder::DecodeAudio(uint32_t size, SmackerAudioTrack &track)
|
|||
for (i = stereo; i >= 0; i--)
|
||||
pred[i] = bits.GetBits(8);
|
||||
for (i = 0; i <= stereo; i++)
|
||||
*samples8++ = pred[i];
|
||||
*samples++ = (pred[i]-128) << 8;
|
||||
for (; i < unpackedSize; i++) {
|
||||
if (i & stereo){
|
||||
if (VLC_GetSize(vlc[1]))
|
||||
|
@ -1053,23 +1111,21 @@ int SmackerDecoder::DecodeAudio(uint32_t size, SmackerAudioTrack &track)
|
|||
else
|
||||
res = 0;
|
||||
pred[1] += (int8_t)h[1].values[res];
|
||||
*samples8++ = pred[1];
|
||||
*samples++ = (pred[1]-128) << 8;
|
||||
} else {
|
||||
if (VLC_GetSize(vlc[0]))
|
||||
res = VLC_GetCodeBits(bits, vlc[0]);
|
||||
else
|
||||
res = 0;
|
||||
pred[0] += (int8_t)h[0].values[res];
|
||||
*samples8++ = pred[0];
|
||||
*samples++ = (pred[0]-128) << 8;
|
||||
}
|
||||
}
|
||||
unpackedSize *= 2;
|
||||
}
|
||||
|
||||
track.bytesReadThisFrame = unpackedSize;
|
||||
|
||||
uint32_t left = bits.GetSize() - bits.GetPosition();
|
||||
bits.SkipBits(left);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1113,11 +1169,32 @@ void SmackerDecoder::GotoFrame(uint32_t frameNum)
|
|||
|
||||
// file.Seek(firstFrameFilePos, SmackerCommon::FileStream::kSeekStart);
|
||||
|
||||
currentReadFrame = 0;
|
||||
currentFrame = 0;
|
||||
nextPos = firstFrameFilePos;
|
||||
|
||||
for (unsigned i = 0; i < frameNum + 1; i++)
|
||||
framePacketData.clear();
|
||||
for (unsigned j = 0; j < kMaxAudioTracks; j++)
|
||||
audioTracks[j].packetData.clear();
|
||||
|
||||
for (unsigned i = 0; i < frameNum; i++)
|
||||
{
|
||||
GetNextFrame();
|
||||
for (unsigned j = 0; j < kMaxAudioTracks; j++)
|
||||
audioTracks[j].packetData.clear();
|
||||
}
|
||||
|
||||
GetNextFrame();
|
||||
}
|
||||
|
||||
uint32_t SmackerDecoder::GetNumAudioTracks()
|
||||
{
|
||||
for(uint32_t i = 0;i < kMaxAudioTracks;++i)
|
||||
{
|
||||
if (!(audioTracks[i].flags & SMK_AUD_PRESENT))
|
||||
return i;
|
||||
}
|
||||
return kMaxAudioTracks;
|
||||
}
|
||||
|
||||
SmackerAudioInfo SmackerDecoder::GetAudioTrackDetails(uint32_t trackIndex)
|
||||
|
@ -1127,7 +1204,6 @@ SmackerAudioInfo SmackerDecoder::GetAudioTrackDetails(uint32_t trackIndex)
|
|||
|
||||
info.sampleRate = track->sampleRate;
|
||||
info.nChannels = track->nChannels;
|
||||
info.bitsPerSample = track->bitsPerSample;
|
||||
|
||||
// audio buffer size in bytes
|
||||
info.idealBufferSize = track->bufferSize;
|
||||
|
@ -1143,9 +1219,37 @@ uint32_t SmackerDecoder::GetAudioData(uint32_t trackIndex, int16_t *audioBuffer)
|
|||
|
||||
SmackerAudioTrack *track = &audioTracks[trackIndex];
|
||||
|
||||
std::unique_lock flock(fileMutex);
|
||||
if (!(track->flags & SMK_AUD_PRESENT))
|
||||
return 0;
|
||||
|
||||
while (track->packetData.empty())
|
||||
{
|
||||
if (ReadPacket() > 0)
|
||||
return 0;
|
||||
}
|
||||
SmackerPacket pkt = std::move(track->packetData.front());
|
||||
track->packetData.pop_front();
|
||||
flock.unlock();
|
||||
|
||||
track->bytesReadThisFrame = 0;
|
||||
|
||||
const uint8_t *packetDataPtr = pkt.data.get();
|
||||
uint32_t size = (uint32_t)pkt.size;
|
||||
|
||||
DecodeAudio(packetDataPtr, size, *track);
|
||||
|
||||
if (track->bytesReadThisFrame) {
|
||||
memcpy(audioBuffer, track->buffer, std::min(track->bufferSize, track->bytesReadThisFrame));
|
||||
}
|
||||
|
||||
return track->bytesReadThisFrame;
|
||||
}
|
||||
|
||||
void SmackerDecoder::DisableAudioTrack(uint32_t trackIndex)
|
||||
{
|
||||
SmackerAudioTrack *track = &audioTracks[trackIndex];
|
||||
std::unique_lock flock(fileMutex);
|
||||
track->flags &= ~SMK_AUD_PRESENT;
|
||||
track->packetData.clear();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue