Audio playback in movie playback; movie decoder now uses pre-allocated buffers

This commit is contained in:
2015-05-15 18:12:59 +02:00
parent 2546196615
commit 8872d9d92d
18 changed files with 470 additions and 104 deletions

View File

@@ -2,9 +2,11 @@ CXX=g++
CXXOPTS=-Og -ggdb -fvar-tracking-assignments -Wall -Wextra -Wno-unused-function -pedantic -std=c++14 -march=native -fstack-protector-strong --param=ssp-buffer-size=4 -flto -I.
LDOPTS=-Wl,--sort-common,--as-needed
render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc render/Object.cc
render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc render/Object.cc render/AlResource.cc render/AudioStream.cc
render_LIBS ::= -lSDL2 -lSDL2_ttf -lglbinding -lopenal
game_CXXSRCS ::= game/GSMvePlay.cc game/GSShowObject.cc
iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc
iffexplore_LIBS ::=
@@ -15,10 +17,10 @@ font2png_CXXSRCS ::= font2png.cc
font2png_LIBS ::= -lpng
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
mvedecode_LIBS ::= -lSDL2 -lSDL2_ttf -lglbinding
mvedecode_LIBS ::= $(render_LIBS)
objdecode_CXXSRCS ::= objdecode.cc TreFile.cc IffFile.cc util.cc ObjDecoder.cc exceptions.cc decompress.cc PaletteDecoder.cc $(render_CXXSRCS) $(game_CXXSRCS)
objdecode_LIBS ::= -lSDL2 -lSDL2_ttf -lglbinding
objdecode_LIBS ::= $(render_LIBS)
progs ::= iffexplore treexplore mvedecode objdecode

View File

@@ -2,6 +2,7 @@
#include <cstring>
#include "common.hh"
#include "compiler.hh"
#include "util.hh"
#include "IffFile.hh"
#include "decompress.hh"
@@ -130,15 +131,17 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
}
}
std::vector<char> parseHuff_(uint8_t const* data, size_t len)
template<typename OutputIt>
size_t parseHuff_(uint8_t const* data, size_t len, OutputIt commands, size_t maxOut)
{
uint8_t numHuffVals;
memcpy(&numHuffVals, data, 1);
const uint8_t numHuffVals = *data;
if (numHuffVals != 22)
throw FormatException{"Unexpected huffman tree size"};
std::array<uint8_t, 44> huffTree;
memcpy(huffTree.data(), data+1, 44);
std::copy(data+1, data+45,
huffTree.begin());
// memcpy(huffTree.data(), data+1, 44);
#ifdef HUFFDEBUG_
printf("Coded data length: %d\nTree:\n", segOfs[1]-(segOfs[0]+45));
@@ -157,8 +160,8 @@ std::vector<char> parseHuff_(uint8_t const* data, size_t len)
int byteValid = 0;
uint8_t byteBuf = 0;
unsigned bytePos = 45;
std::vector<char> commands;
unsigned huffIdx = huffTree.size();
size_t count = 0;
while (huffIdx != 22) {
// Read next bit
unsigned bit;
@@ -179,31 +182,28 @@ std::vector<char> parseHuff_(uint8_t const* data, size_t len)
if (huffIdx < 22) {
commands.push_back(huffIdx);
*commands++ = huffIdx;
if (++count >= maxOut)
throw Exception{"Output buffer overflow"};
huffIdx = huffTree.size();
}
}
if (commands.empty())
if (count == 0)
throw FormatException{"No commands decoded"};
#ifdef HUFFDEBUG_
for(auto ent : commands)
printf("%.2hhx ", ent);
printf("\n");
#endif
return commands;
return count;
}
std::vector<uint8_t> parsePixels_(uint8_t const* data, size_t len)
size_t parsePixels_(uint8_t const* RESTRICT data, size_t len, uint8_t * RESTRICT out, size_t maxOut)
{
if (*data == 0x02) {
return decompressLZ(data+1, len-1);
return decompressLZInto(data+1, len-1, out, maxOut);
} else {
std::vector<uint8_t> ret;
std::copy(data+1, data+len, std::back_inserter(ret));
return ret;
if (len-1 > maxOut)
throw Exception{"Output buffer overflow"};
std::copy(data+1, data+len, out);
return len-1;
}
}
@@ -221,8 +221,19 @@ MveDecoder::Movie::Movie(std::vector<Shot>::const_iterator shotIt,
: shotIt_(shotIt), shotEnd_(shotEnd),
frameIt_(shotIt_->VGAs.cbegin()),
audioIt_(shotIt_->AUDIs.cbegin()),
mve_(mve)
mve_(mve), maxPixel_(0), maxCommand_(0)
{
pixelBuf_.resize(mve_.width_*mve_.height_);
commandBuf_.reserve(32768);
frameBuf_.reserve(mve_.width_*mve_.height_);
frame_.reserve(mve_.width_*mve_.height_);
}
MveDecoder::Movie::~Movie()
{
#ifndef NDEBUG
printf("Maximum command buffer: %zu\n", maxCommand_);
#endif
}
bool MveDecoder::Movie::hasNext() const {
@@ -234,7 +245,7 @@ bool MveDecoder::Movie::hasNext() const {
std::tuple<MveDecoder::Frame const&, MveDecoder::Palette const&> MveDecoder::Movie::decodeNext()
{
frame_ = mve_.parseVGA(**frameIt_++, &frame_);
parseVGA(**frameIt_++);
auto& palette = shotIt_->palt;
if (frameIt_ == shotIt_->VGAs.cend()) {
@@ -256,12 +267,12 @@ bool MveDecoder::Movie::hasAudio() const
MveDecoder::Audio MveDecoder::Movie::getNextAudio()
{
MveDecoder::Audio ret;
if (audioIt_ != shotIt_->AUDIs.cend()) {
if ((shotIt_ != shotEnd_) && (audioIt_ != shotIt_->AUDIs.cend())) {
ret.reserve((shotIt_->AUDIs.cend()-audioIt_)*2940);
for(;audioIt_ != shotIt_->AUDIs.cend();++audioIt_)
std::copy((*audioIt_)->begin(), (*audioIt_)->end(),
std::back_inserter(ret));
for(auto it = (*audioIt_)->begin();it != (*audioIt_)->end();it += 2)
ret.push_back(readU16LE(it));
}
return ret;
@@ -277,7 +288,7 @@ unsigned MveDecoder::Movie::getHeight() const
return mve_.height_;
}
MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* prev) const
void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
{
// Parse header
if (vga.getSize() < 8)
@@ -298,11 +309,17 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
(vga.getSize() - segOfs[0] < 45))
throw FormatException{"Segment 1 too small"};
auto commands = parseHuff_(vga.begin()+segOfs[0], segOfs[1]-segOfs[0]);
commandBuf_.clear();
parseHuff_(vga.begin()+segOfs[0], segOfs[1]-segOfs[0],
std::back_inserter(commandBuf_), commandBuf_.capacity());
maxCommand_ = std::max(maxCommand_, commandBuf_.size());
std::vector<uint8_t> pixels;
if (segOfs[3])
pixels = parsePixels_(vga.begin()+segOfs[3], vga.getSize()-segOfs[3]);
size_t pixelCount = 0;
if (segOfs[3]) {
pixelCount = parsePixels_(vga.begin()+segOfs[3], vga.getSize()-segOfs[3],
pixelBuf_.data(), pixelBuf_.size());
maxPixel_ = std::max(maxPixel_, pixelCount);
}
#ifdef VIDDEBUG_
printf("%lu commands, %lu pixel data, %u mvecs, %lu size data\n",
@@ -311,17 +328,13 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
#endif
// Interpret command stream to render frame
Frame ret;
ret.reserve(width_*height_);
frameBuf_.clear();
bool flag = false;
size_t seg2Idx = 0;
size_t seg3Idx = 0;
size_t seg4Idx = 0;
unsigned size = 0;
auto it = commands.begin();
while (it != commands.end()) {
auto cmd = *it++;
for (auto cmd : commandBuf_) {
#ifdef CMDDEBUG_
printf("CMD %u ", cmd);
#endif
@@ -388,24 +401,22 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
#ifdef CMDDEBUG_
printf("PrevCopy: %u\n", size);
#endif
if (!prev)
throw FormatException{"Reference to non-existing prev. frame"};
if (ret.size()+size > prev->size())
if (frameBuf_.size()+size > frame_.size())
throw FormatException{"Copy from prev exceeds frame"};
std::copy(prev->begin()+ret.size(),
prev->begin()+ret.size()+size,
std::back_inserter(ret));
std::copy(frame_.begin()+frameBuf_.size(),
frame_.begin()+frameBuf_.size()+size,
std::back_inserter(frameBuf_));
} else {
#ifdef CMDDEBUG_
printf("DataCopy: %u @%lu\n", size, seg4Idx);
#endif
if (seg4Idx+size > pixels.size())
if (seg4Idx+size > pixelCount)
throw FormatException{"Copy from seg4 exceeds bounds"};
std::copy(pixels.begin()+seg4Idx,
pixels.begin()+seg4Idx+size,
std::back_inserter(ret));
std::copy(pixelBuf_.begin()+seg4Idx,
pixelBuf_.begin()+seg4Idx+size,
std::back_inserter(frameBuf_));
seg4Idx += size;
}
} else {
@@ -416,39 +427,37 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
(segOfs[3] && (segOfs[2]+seg3Idx > segOfs[3])) ||
(segOfs[2]+seg3Idx > vga.getSize()))
throw FormatException{"Segment 3 overrun"};
if (!prev)
throw FormatException{"Reference to non-existing prev. frame"};
const uint8_t mpos = vga.begin()[segOfs[2]+seg3Idx++];
const signed x = sextend(mpos>>4, 4), y = sextend(mpos&0xf,4);
const signed delta = y*width_+x;
if ((delta < 0) && (ret.size() < static_cast<size_t>(-delta)))
const signed delta = y*mve_.width_+x;
if ((delta < 0) && (frameBuf_.size() < static_cast<size_t>(-delta)))
throw FormatException{"Motion vector outside frame"};
const unsigned ref = ret.size()+delta;
const unsigned ref = frameBuf_.size()+delta;
#ifdef CMDDEBUG_
printf("@%lu %.2hhx -> (%d, %d) -> d %d -> %u\n", seg3Idx-1, mpos, x, y, delta, ref);
#endif
if (ref+size > prev->size())
if (ref+size > frame_.size())
throw FormatException{"Copy from prev exceeds frame"};
std::copy(prev->begin()+ref,
prev->begin()+ref+size,
std::back_inserter(ret));
std::copy(frame_.begin()+ref,
frame_.begin()+ref+size,
std::back_inserter(frameBuf_));
flag = false;
}
}
if (ret.size() != width_*height_)
if (frameBuf_.size() != mve_.width_*mve_.height_)
throw FormatException{"Decoded frame dimension mismatch"};
#ifndef NDEBUG
if (seg4Idx < pixels.size())
printf("%lu unused pixel data\n", pixels.size()-seg4Idx);
if (seg4Idx < pixelCount)
printf("%lu unused pixel data\n", pixelCount-seg4Idx);
#endif
return ret;
frame_.swap(frameBuf_);
}
/*
#include <SDL2/SDL.h>

View File

@@ -15,7 +15,7 @@ public:
}
using Frame = std::vector<uint8_t>;
using Audio = std::vector<uint8_t>;
using Audio = std::vector<int16_t>;
using Palette = std::array<uint8_t, 768>;
private:
using AUDIVec = std::vector<IffFile::Object const*>;
@@ -33,6 +33,7 @@ public:
bool hasNext() const;
std::tuple<Frame const&, Palette const&> decodeNext();
~Movie();
bool hasAudio() const;
Audio getNextAudio();
@@ -45,11 +46,22 @@ public:
std::vector<Shot>::const_iterator shotEnd,
MveDecoder const& mve);
void parseVGA(IffFile::Object const& vga);
std::vector<Shot>::const_iterator shotIt_, shotEnd_;
VGAVec::const_iterator frameIt_;
AUDIVec::const_iterator audioIt_;
Frame frame_;
MveDecoder const& mve_;
// Buffers for frame decoding
std::vector<uint8_t> pixelBuf_;
Frame frameBuf_;
std::vector<char> commandBuf_;
// Buffer usage stats
size_t maxPixel_, maxCommand_;
friend class MveDecoder;
};
@@ -63,8 +75,6 @@ private:
std::vector<Palette> palts_;
std::vector<std::vector<Shot> > branches_;
Frame parseVGA(IffFile::Object const& vga, Frame const* prev) const;
friend struct AudioCBData;
};

View File

@@ -171,8 +171,8 @@ TreFile::TreFile(uint8_t const* base, size_t length)
crc, table3Ptr);
continue;
}
printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
table3Ptr, it->second);
// printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
// table3Ptr, it->second);
table3Ptr = it->second;
}

14
compiler.hh Normal file
View File

@@ -0,0 +1,14 @@
#ifndef WC3RE_COMPILER_HH__
#define WC3RE_COMPILER_HH__
#ifdef __GNUG__
#define RESTRICT __restrict__
#else
#ifdef __MSC_VER
#define RESTRICT __restrict
#else
#define RESTRICT
#endif
#endif
#endif

View File

@@ -1,4 +1,5 @@
#include "common.hh"
#include "compiler.hh"
#include "decompress.hh"
@@ -80,3 +81,92 @@ std::vector<uint8_t> decompressLZ(uint8_t const* data, size_t len)
}
return ret;
}
size_t decompressLZInto(uint8_t const* RESTRICT data, size_t len, uint8_t * RESTRICT out, size_t maxOut)
{
size_t pos = 0, outPos = 0;
while (pos < len) {
uint8_t b = *(data+pos++);
if (!((b&0xe0)==0xe0)) {
unsigned size = 0, replSize = 0;
unsigned replOfs = 0;
if (!(b&0x80)) {
if (pos >= len)
throw FormatException{"Compressed stream overrun"};
uint8_t ofs = *(data+pos++);
size = b&0x3;
replSize = ((b&0x1c)>>2) + 3;
replOfs = ((b&0x60)<<3)+ofs+1;
} else if (!(b&0x40)) {
if (pos+1 >= len)
throw FormatException{"Compressed stream overrun"};
uint8_t b1 = *(data+pos++);
uint8_t b2 = *(data+pos++);
size = (b1&0xc0)>>6;
replSize = (b&0x3f)+4;
replOfs = ((b1&0x3f)<<8)+b2+1;
} else if (!(b&0x20)) {
if (pos+2 >= len)
throw FormatException{"Compressed stream overrun"};
uint8_t b1 = *(data+pos++);
uint8_t b2 = *(data+pos++);
uint8_t b3 = *(data+pos++);
size = b&0x3;
replSize = b3+5+((b&0xc)<<6);
replOfs = ((b&0x10)<<12)+1+(b1<<8)+b2;
}
if (pos+size >= len)
throw FormatException{"Compressed stream overrun"};
if (outPos+size > maxOut)
throw Exception{"Output buffer overrun"};
std::copy(data+pos, data+pos+size,
out+outPos);
pos += size;
outPos += size;
if (replOfs > outPos)
throw FormatException{"Replication offset exceeds buffer"};
if (outPos+replSize > maxOut)
throw Exception{"Output buffer overrun"};
unsigned start = outPos-replOfs;
for (unsigned i = 0;i < replSize;++i)
out[outPos++] = out[start+i];
} else {
unsigned size = (b&0x1f)*4+4;
if (size > 0x70) {
if (pos+(b&0x3) > len)
throw FormatException{"Compressed stream overrun"};
if (outPos+(b&0x03) > maxOut)
throw Exception{"Output buffer overrun"};
std::copy(data+pos, data+pos+(b&0x3),
out+outPos);
pos += (b&0x3);
outPos += (b&0x3);
#ifndef NDEBUG
if (pos < len)
printf("%lu unparsed bytes in compressed data\n", len-pos);
#endif
break;
}
if (pos+size > len)
throw FormatException{"Compressed stream overrun"};
if (outPos+size > maxOut)
throw Exception{"Output buffer overrun"};
std::copy(data+pos, data+pos+size,
out+outPos);
pos += size;
outPos += size;
}
}
return outPos;
}

View File

@@ -9,6 +9,11 @@
/* LZ77-like format consisting of codes specifing copies from the input stream
and/or replication of previously output data */
/* Decompress compressed data in 'data', return decompressed data */
std::vector<uint8_t> decompressLZ(uint8_t const* data, size_t len);
/* Decompress compressed data in 'data' into pre-allocated buffer 'out' of size 'maxOut',
return size of decompressed data */
size_t decompressLZInto(uint8_t const* data, size_t len, uint8_t * out, size_t maxOut);
#endif

View File

@@ -3,12 +3,13 @@
#include "GSMvePlay.hh"
#include "render/Overlay.hh"
#include "render/Renderer.hh"
#include "render/AudioStream.hh"
namespace game {
GSMvePlay::GSMvePlay(render::Renderer& renderer, MveDecoder::Movie& movie)
: GameState(renderer), movie_(movie),
overlay_(nullptr), delta_(0),
nextFT_(1000/15.0f), frameRGB_()
nextFT_(1000/15.0f), frameRGBA_(), decoded_(false), firstFrame_(true)
{
int scrWidth = renderer_.getWidth(), scrHeight = renderer_.getHeight();
const float mvePAR = 1.2f;
@@ -31,11 +32,12 @@ namespace game {
overlay_ = std::make_unique<render::Overlay>(renderer_, scrWidth, scrHeight, left, top,
mveWidth, mveHeight);
auto align4Width = (mveWidth%4)?(mveWidth+(4-(mveWidth%4))):mveWidth;
frameRGB_.resize(align4Width*mveHeight*3);
audio_ = std::make_unique<render::AudioStream>();
frameRGBA_.resize(mveWidth*mveHeight*4);
decode_();
overlay_->setContentRGB8(frameRGB_.data());
overlay_->setContentBGRA8(frameRGBA_.data());
decode_();
}
@@ -45,6 +47,10 @@ namespace game {
void GSMvePlay::decode_()
{
if (movie_.hasAudio()) {
auto samples = movie_.getNextAudio();
audio_->queueSamples(1, 22050, samples.data(), samples.size()*2);
}
if (movie_.hasNext()) {
auto dec = movie_.decodeNext();
auto& frame = std::get<0>(dec);
@@ -55,29 +61,46 @@ namespace game {
for (unsigned y = 0;y < height;++y) {
for (unsigned x = 0;x < width;++x) {
auto idx = frame[y*width+x];
std::copy(&palette[idx*3], &palette[idx*3+3],
&frameRGB_[i]);
i += 3;
frameRGBA_[i] = palette[idx*3+2];
frameRGBA_[i+1] = palette[idx*3+1];
frameRGBA_[i+2] = palette[idx*3];
frameRGBA_[i+3] = 255u;
i += 4;
}
if ((i%4) != 0)
i += 4-(i%4);
}
} else
frameRGB_.resize(0);
frameRGBA_.resize(0);
}
bool GSMvePlay::handleEvent(SDL_Event& event)
{
switch (event.type) {
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) {
renderer_.popGS();
return true;
}
break;
}
return false;
}
void GSMvePlay::draw(unsigned delta_ms)
{
const float frameTime = 1000/15.0f;
if (firstFrame_) {
firstFrame_ = false;
audio_->play();
}
delta_ += delta_ms;
if (delta_ >= nextFT_) {
if (!frameRGB_.size()) {
if (!decoded_)
decode_();
if (!frameRGBA_.size()) {
renderer_.popGS();
return;
}
@@ -89,11 +112,14 @@ namespace game {
delta_ = 0;
overlay_->setContentRGB8(frameRGB_.data());
overlay_->setContentBGRA8(frameRGBA_.data());
// Decode next frame
decoded_ = false;
} else if (!decoded_) {
decode_();
decoded_ = true;
}
overlay_->draw();
}
}

View File

@@ -6,6 +6,7 @@
class MveDecoder;
namespace render {
class Overlay;
class AudioStream;
}
namespace game {
@@ -20,9 +21,11 @@ namespace game {
private:
MveDecoder::Movie& movie_;
std::unique_ptr<render::Overlay> overlay_;
std::unique_ptr<render::AudioStream> audio_;
unsigned delta_;
float nextFT_;
std::vector<uint8_t> frameRGB_;
std::vector<uint8_t> frameRGBA_;
bool decoded_, firstFrame_;
void decode_();
};

33
render/AlResource.cc Normal file
View File

@@ -0,0 +1,33 @@
#include "AlResource.hh"
namespace render {
void ALCDeviceDeleter::operator() (ALCdevice *dev) const
{
alcCloseDevice(dev);
}
void ALCContextDeleter::operator() (ALCcontext *ctx) const
{
alcDestroyContext(ctx);
}
void ALSourceDeleter::operator() (ALuint src) const
{
alDeleteSources(1, &src);
}
void ALSourceDeleter::operator() (ALsizei count, ALuint src[]) const
{
alDeleteSources(count, src);
}
void ALBufferDeleter::operator() (ALuint buf) const
{
alDeleteBuffers(1, &buf);
}
void ALBufferDeleter::operator() (ALsizei count, ALuint buf[]) const
{
alDeleteBuffers(count, buf);
}
}

39
render/AlResource.hh Normal file
View File

@@ -0,0 +1,39 @@
#ifndef WC3RE_RENDER_ALRESOURCE_HH__
#define WC3RE_RENDER_ALRESOURCE_HH__
#include <AL/al.h>
#include <AL/alc.h>
#include <memory>
#include "GlResource.hh"
namespace render {
struct ALCDeviceDeleter {
void operator() (ALCdevice *dev) const;
};
struct ALCContextDeleter {
void operator() (ALCcontext *ctx) const;
};
struct ALSourceDeleter {
void operator() (ALuint src) const;
void operator() (ALsizei count, ALuint src[]) const;
};
struct ALBufferDeleter {
void operator() (ALuint buf) const;
void operator() (ALsizei count, ALuint buf[]) const;
};
using ALCDeviceUPtr = std::unique_ptr<ALCdevice, ALCDeviceDeleter>;
using ALCContextUPtr = std::unique_ptr<ALCcontext, ALCContextDeleter>;
template<class Deleter>
using AlResource = GlResource<Deleter, ALuint>;
using ALSourceResource = AlResource<ALSourceDeleter>;
using ALBufferResource = AlResource<ALBufferDeleter>;
}
#endif

62
render/AudioStream.cc Normal file
View File

@@ -0,0 +1,62 @@
#include "AudioStream.hh"
#include "exceptions.hh"
namespace render {
AudioStream::AudioStream()
{
alGenSources(1, &alSource_.get());
alSourcef(alSource_, AL_MIN_GAIN, 1.0f);
}
AudioStream::~AudioStream()
{
#ifndef NDEBUG
printf("%zu buffers at destruction\n", alBufs_.size());
#endif
}
void AudioStream::play()
{
alSourcePlay(alSource_);
}
void AudioStream::pause()
{
alSourceStop(alSource_);
}
bool AudioStream::isPlaying() const
{
ALenum state;
alGetSourcei(alSource_, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
void AudioStream::queueSamples(unsigned channels, unsigned freq, int16_t const* data, size_t len)
{
// Reuse a processed buffer if possible, or create a new one
ALint doneBufs;
alGetSourcei(alSource_, AL_BUFFERS_PROCESSED, &doneBufs);
ALuint buf;
if (doneBufs > 0)
alSourceUnqueueBuffers(alSource_, 1, &buf);
else {
alGenBuffers(1, &buf);
alBufs_.push_back(buf);
}
// Fill buffer with data
ALenum format;
if (channels == 1)
format = AL_FORMAT_MONO16;
else if (channels == 2)
format = AL_FORMAT_STEREO16;
else
throw Exception{"More than 2 channels not supported"};
alBufferData(buf, format, data, len, freq);
// Enqueue buffer
alSourceQueueBuffers(alSource_, 1, &buf);
}
}

26
render/AudioStream.hh Normal file
View File

@@ -0,0 +1,26 @@
#ifndef WC3RE_RENDER_AUDIOSTREAM_HH__
#define WC3RE_RENDER_AUDIOSTREAM_HH__
#include <vector>
#include "AlResource.hh"
namespace render {
class AudioStream {
public:
AudioStream();
~AudioStream();
void play();
void pause();
bool isPlaying() const;
void queueSamples(unsigned channels, unsigned freq, int16_t const* data, size_t len);
private:
ALSourceResource alSource_;
std::vector<ALBufferResource> alBufs_;
};
}
#endif

View File

@@ -71,15 +71,15 @@ namespace render {
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void Overlay::clear()
{
if (SDL_GL_ExtensionSupported("GL_ARB_clear_texture"))
glClearTexImage(texture_, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
else {
std::vector<uint8_t> zeros(intWidth_*intHeight_*4, 0u);
setContentBGRA8(zeros.data());
}
}
// void Overlay::clear()
// {
// if (SDL_GL_ExtensionSupported("GL_ARB_clear_texture"))
// glClearTexImage(texture_, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
// else {
// std::vector<uint8_t> zeros(intWidth_*intHeight_*4, 0u);
// setContentBGRA8(zeros.data());
// }
// }
void Overlay::setContent(SDL_Surface *content)
{
@@ -91,6 +91,7 @@ namespace render {
glBindTexture(GL_TEXTURE_2D, texture_);
if (content->format->format != SDL_PIXELFORMAT_ARGB8888) {
printf("Warning: Format conversion in Overlay::setContent\n");
SDLSurfaceUPtr tmpSurf(SDL_ConvertSurfaceFormat(content, SDL_PIXELFORMAT_ARGB8888, 0));
if (!tmpSurf)
throw SDLException{};

View File

@@ -18,7 +18,7 @@ namespace render {
void draw() override;
void clear();
// void clear();
void setContent(SDL_Surface *content);
void setContentRGB8(void *data);

View File

@@ -61,6 +61,7 @@ namespace render {
Renderer::Renderer()
: sdlInit_(), ttfInit_(), window_(), context_()
{
// Initialize OpenGL
glbinding::Binding::initialize();
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
@@ -98,6 +99,21 @@ namespace render {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glViewport(0, 0, width_, height_);
// Initialize OpenAL
alcDevice_.reset(alcOpenDevice(nullptr));
if (!alcDevice_)
throw Exception{"OpenAL init failed"};
alcContext_.reset(alcCreateContext(alcDevice_.get(), nullptr));
if (!alcContext_)
throw Exception{"OpenAL init failed"};
alcMakeContextCurrent(alcContext_.get());
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f);
// Setup framerate display
fpsFont_.reset(TTF_OpenFont("DejaVuSans.ttf", 10));
if (!fpsFont_)
throw TTFException{};
@@ -111,9 +127,25 @@ namespace render {
if (!fpsSurf)
throw TTFException{};
fpsOverlay_ = std::make_unique<Overlay>(*this, fpsSurf->w*2, fpsSurf->h, 0, 0, fpsSurf->w*2, fpsSurf->h);
fpsOverlay_->clear();
fpsOverlay_->setContent(fpsSurf.get());
const unsigned fpsWidth = fpsSurf->w*2;
const unsigned fpsHeight = fpsSurf->h;
fpsOverlay_ = std::make_unique<Overlay>(*this, fpsWidth, fpsHeight, 0, 0, fpsWidth, fpsHeight);
{
int bpp;
uint32_t rmask, gmask, bmask, amask;
if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ARGB8888,
&bpp,
&rmask, &gmask, &bmask, &amask))
throw SDLException{};
fpsOverlaySurf_.reset(SDL_CreateRGBSurface(0, fpsWidth, fpsHeight, bpp,
rmask, gmask, bmask, amask));
}
if (SDL_BlitSurface(fpsSurf.get(), nullptr, fpsOverlaySurf_.get(), nullptr) != 0)
throw SDLException{};
fpsOverlay_->setContent(fpsOverlaySurf_.get());
}
Renderer::~Renderer()
@@ -129,7 +161,8 @@ namespace render {
while (!close && gamestates_.size()) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
bool changeGS = false;
while (SDL_PollEvent(&event) && !changeGS) {
switch(event.type) {
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_q) {
@@ -144,10 +177,10 @@ namespace render {
}
break;
}
if (gamestates_.back()->handleEvent(event))
continue;
break;
changeGS = gamestates_.back()->handleEvent(event);
}
if (changeGS)
continue;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -159,12 +192,18 @@ namespace render {
if (fpsTime+delta > 1000) {
if (fpsCount+1 != lastFps) {
std::string fpsText{std::to_string(fpsCount+1) + " FPS"};
SDLSurfaceUPtr fpsSurf(TTF_RenderUTF8_Blended(fpsFont_.get(), fpsText.c_str(),
SDL_Color({255, 255, 255, 255})));
if (!fpsSurf)
throw TTFException{};
fpsOverlay_->clear();
fpsOverlay_->setContent(fpsSurf.get());
if ((SDL_FillRect(fpsOverlaySurf_.get(), nullptr, 0x00000000u) != 0) ||
(SDL_BlitSurface(fpsSurf.get(), nullptr, fpsOverlaySurf_.get(), nullptr) != 0))
throw SDLException{};
fpsOverlay_->setContent(fpsOverlaySurf_.get());
lastFps = fpsCount+1;
}
fpsCount = 0;

View File

@@ -4,6 +4,7 @@
#include <memory>
#include "sdlutil.hh"
#include "AlResource.hh"
namespace game {
class GameState;
@@ -31,8 +32,14 @@ namespace render {
SDLGLContext context_;
std::vector<std::unique_ptr<game::GameState> > gamestates_;
int width_, height_;
ALCDeviceUPtr alcDevice_;
ALCContextUPtr alcContext_;
TTFFontUPtr fpsFont_;
std::unique_ptr<Overlay> fpsOverlay_;
SDLSurfaceUPtr fpsOverlaySurf_;
};
}

View File

@@ -101,7 +101,7 @@ namespace render {
// RAII wrapper for SDL_Init
class SDLInit {
public:
SDLInit(uint32_t flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO) {
SDLInit(uint32_t flags = SDL_INIT_VIDEO) {
if (SDL_Init(flags) < 0)
throw SDLException{};
}