gates_of_skeldal/libs/music.cpp

160 lines
3.8 KiB
C++

#include <string_view>
#include "music.h"
#include <platform/platform.h>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string_view>
#include <vector>
#include <utility>
class MUSStreamParser: public IMusicStream {
public:
MUSStreamParser(const char *name) {
_data = map_file_to_memory(file_icase_find(name), &sz);
if (_data) {
_loaded = true;
iter = reinterpret_cast<const uint8_t *>(_data);
chans = read_short();
freq = read_int();
iter += 4;
blocks = read_int();
iter += 8;
btable = reinterpret_cast<const short *>(iter);
iter += 512;
}
}
~MUSStreamParser() {
if (_data) unmap_file(_data, sz);
}
MUSStreamParser(const MUSStreamParser &) = delete;
MUSStreamParser& operator=(const MUSStreamParser &) = delete;
bool is_loaded() const {return _loaded;}
virtual TMUSIC_STREAM_INFO get_info() const override {
TMUSIC_STREAM_INFO r;
r.format = 2;
r.channels = chans;
r.freq = freq;
return r;
}
virtual std::string_view read() override {
if (unprocessed_buffer.empty()) {
if (!process_next()) return {};
}
return std::exchange(unprocessed_buffer, {});
}
virtual void put_back(std::string_view data) override {
unprocessed_buffer = data;
}
protected:
bool _loaded = false;
void *_data;
std::size_t sz;
std::int16_t chans;
std::int32_t freq;
std::int32_t blocks;
const short *btable;
const uint8_t *iter;
std::vector<int16_t> outbuff;
std::string_view unprocessed_buffer;
int32_t read_int() {
int32_t r = iter[0] | (iter[1] << 8) | (iter[2]<<16) | (iter[3] << 24);
iter+=4;
return r;
}
int16_t read_short() {
int16_t r = iter[0] | (iter[1] << 8);
iter+=2;
return r;
}
bool process_next() {
if (blocks == 0) {
return false;
}
--blocks;
std::int32_t p = read_int();
read_int();
outbuff.clear();
short accum[2]={0,0};
int c = 0;
for (int i = 0; i < p; ++i) {
uint8_t p = *iter++;
short val=accum[c]+btable[p];
accum[c]=val;
if (p==0) //a bug in compression algorithm
{
val-=31767;
}
c = (c + 1) % chans;
outbuff.push_back(val);
}
unprocessed_buffer = {reinterpret_cast<const char *>(outbuff.data()), outbuff.size()*2};
return true;
}
};
void music_close(TMUSIC_STREAM *stream) {
if (stream) {
IMusicStream *s = static_cast<IMusicStream *>(stream);
delete s;
}
}
TMUSIC_STREAM *music_open(const char *filename) {
MUSStreamParser *player = new MUSStreamParser(filename);
if (player->is_loaded()) return player;
delete player;
return NULL;
}
TMUSIC_STREAM_CHUNK music_read(TMUSIC_STREAM *stream) {
TMUSIC_STREAM_CHUNK r = {NULL,0};
if (stream) {
IMusicStream *s = static_cast<IMusicStream *>(stream);
auto data = s->read();
r.ptr = data.data();
r.sz = data.size();
}
return r;
}
char music_is_eof(const TMUSIC_STREAM_CHUNK *chunk) {
return chunk->sz == 0;
}
void music_put_back_chunk(TMUSIC_STREAM *stream, const TMUSIC_STREAM_CHUNK *chunk){
if (stream) {
IMusicStream *s = static_cast<IMusicStream *>(stream);
s->put_back({reinterpret_cast<const char *>(chunk->ptr), chunk->sz});
}
}
TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream) {
if (stream) {
const IMusicStream *s = static_cast<const IMusicStream *>(stream);
return s->get_info();
} else {
TMUSIC_STREAM_INFO r = {};
return r;
}
}