mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-20 13:15:16 -04:00
mp3 support for playing background music
This commit is contained in:
parent
73a4187f79
commit
dd23d8c989
24 changed files with 2245 additions and 252 deletions
|
@ -14,6 +14,7 @@ SET(files basicobj.c
|
|||
wav_mem.c
|
||||
strlite.c
|
||||
cztable.c
|
||||
minimp3.c
|
||||
music.cpp
|
||||
string_table.cpp )
|
||||
|
||||
|
|
|
@ -334,6 +334,12 @@ int test_file_exist(int group,const char *filename)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int file_is_in_ddl(int group, const char *filename) {
|
||||
THANDLE_DATA h;
|
||||
return get_file_entry(group, filename, &h);
|
||||
|
||||
}
|
||||
|
||||
THANDLE_DATA *def_handle(int handle,const char *filename,ABLOCK_DECODEPROC decompress,char path)
|
||||
{
|
||||
THANDLE_DATA *h;
|
||||
|
@ -369,11 +375,11 @@ THANDLE_DATA *def_handle(int handle,const char *filename,ABLOCK_DECODEPROC decom
|
|||
return h;
|
||||
}
|
||||
|
||||
const void *afile(const char *filename,int group,int32_t *blocksize)
|
||||
static const void *afile2(const char *filename,int group,int32_t *blocksize, char mapped)
|
||||
{
|
||||
char *d;
|
||||
char entr;
|
||||
void *p;
|
||||
const void *p;
|
||||
|
||||
d=alloca(strlen(filename)+1);
|
||||
strcpy(d,filename);
|
||||
|
@ -388,18 +394,36 @@ const void *afile(const char *filename,int group,int32_t *blocksize)
|
|||
*blocksize = *szptr;
|
||||
return szptr+1;
|
||||
}
|
||||
else if (mman_pathlist!=NULL)
|
||||
{
|
||||
else if (mman_pathlist!=NULL) {
|
||||
const char *name = build_pathname(2,mman_pathlist[group],d);
|
||||
size_t sz;
|
||||
|
||||
SEND_LOG("(LOAD) Afile is loading file '%s' from disk (group %d)",d,group);
|
||||
p=load_file(name, &sz);
|
||||
*blocksize=sz;
|
||||
if (mapped) {
|
||||
const char *iname = file_icase_find(name);
|
||||
p = map_file_to_memory(iname, &sz);
|
||||
*blocksize = sz;
|
||||
|
||||
}
|
||||
else {
|
||||
p=load_file(name, &sz);
|
||||
*blocksize=sz;
|
||||
}
|
||||
else return NULL;
|
||||
return p;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
const void *afile(const char *filename,int group,int32_t *blocksize) {
|
||||
return afile2(filename, group, blocksize, 0);
|
||||
}
|
||||
|
||||
const void *afile_mapped(const char *filename,int group,int32_t *blocksize) {
|
||||
return afile2(filename, group, blocksize, 1);
|
||||
}
|
||||
|
||||
|
||||
void *afile_copy(const char *filename,int group,int32_t *blocksize) {
|
||||
const void *ptr = afile(filename, group, blocksize);
|
||||
|
@ -721,3 +745,8 @@ int32_t get_handle_size(int handle)
|
|||
void ablock_free(const void *ptr) {
|
||||
if (need_to_be_free(ptr)) free((void *)ptr);
|
||||
}
|
||||
|
||||
void afile_mapped_free(const void *ptr, int32_t sz) {
|
||||
if (need_to_be_free(ptr)) unmap_file(ptr, sz);
|
||||
}
|
||||
|
||||
|
|
|
@ -96,12 +96,18 @@ THANDLE_DATA *zneplatnit_block(int handle); //zneplatni data bloku
|
|||
THANDLE_DATA *get_handle(int handle); //vraci informace o rukojeti
|
||||
int find_handle(const char *name,ABLOCK_DECODEPROC decomp); //hleda mezi rukojeti stejnou definici
|
||||
int test_file_exist(int group,const char *filename); //testuje zda soubor existuje v ramci mmanageru
|
||||
int file_is_in_ddl(int group, const char *filename); //testuje zde je soubor v DDL
|
||||
const void *afile(const char *filename,int group,int32_t *blocksize); //nahraje do pameti soubor registrovany v ramci mmanageru
|
||||
void *afile_copy(const char *filename,int group,int32_t *blocksize); //nahraje do pameti soubor registrovany v ramci mmanageru
|
||||
int32_t get_handle_size(int handle);
|
||||
//void get_mem_info(MEMORYSTATUS *mem);
|
||||
void ablock_free(const void *ptr);
|
||||
|
||||
//access file using memory mapping feature
|
||||
const void *afile_mapped(const char *filename,int group,int32_t *blocksize);
|
||||
//free file memory opened by afile_mapped
|
||||
void afile_mapped_free(const void *ptr, int32_t sz);
|
||||
|
||||
|
||||
int read_group(int index);
|
||||
char add_patch_file(const char *filename); //pripojuje zaplatu
|
||||
|
|
|
@ -185,8 +185,7 @@ void BigPlayProc(MGIF_HEADER_T *hdr,int act,const void *data,int csize)
|
|||
void play_animation(const char *filename,char mode,int posy,char sound)
|
||||
{
|
||||
size_t sz;
|
||||
void *mgf=map_file_to_memory(file_icase_find(filename), &sz);
|
||||
change_music(NULL);
|
||||
const void *mgf=map_file_to_memory(file_icase_find(filename), &sz);
|
||||
if (mgf==NULL) return;
|
||||
game_display_disable_crt_effect_temporary(1);
|
||||
PlayMGFFile(mgf,BigPlayProc,posy,mode & 0x80);
|
||||
|
|
3
libs/minimp3.c
Normal file
3
libs/minimp3.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
#define MINIMP3_IMPLEMENTATION
|
||||
#define MINIMP3_ONLY_MP3
|
||||
#include "minimp3.h"
|
1865
libs/minimp3.h
Normal file
1865
libs/minimp3.h
Normal file
File diff suppressed because it is too large
Load diff
148
libs/music.cpp
148
libs/music.cpp
|
@ -1,8 +1,10 @@
|
|||
#include <string_view>
|
||||
|
||||
#include "music.h"
|
||||
#include "minimp3.h"
|
||||
#include <platform/platform.h>
|
||||
|
||||
#include "memman.h"
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
@ -10,30 +12,40 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
struct MResDeleter {
|
||||
TMUSIC_SOURCE src;
|
||||
void operator()(const void *ptr) {
|
||||
src.destructor(&src);
|
||||
}
|
||||
};
|
||||
|
||||
using MusicData = std::unique_ptr<const void, MResDeleter>;
|
||||
|
||||
|
||||
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(const TMUSIC_SOURCE *src)
|
||||
:_mdata(src->data, {*src}) {
|
||||
|
||||
if (_mdata) {
|
||||
_data = _mdata.get();
|
||||
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;
|
||||
|
||||
|
@ -60,8 +72,8 @@ public:
|
|||
|
||||
protected:
|
||||
bool _loaded = false;
|
||||
void *_data;
|
||||
std::size_t sz;
|
||||
MusicData _mdata;
|
||||
const void *_data;
|
||||
|
||||
std::int16_t chans;
|
||||
std::int32_t freq;
|
||||
|
@ -111,6 +123,85 @@ protected:
|
|||
|
||||
};
|
||||
|
||||
class MP3StreamParser: public IMusicStream {
|
||||
public:
|
||||
MP3StreamParser(const TMUSIC_SOURCE *src)
|
||||
:_data(src->data, {*src})
|
||||
,_mp3_size(src->size) {
|
||||
|
||||
|
||||
if (_data && find_music_info()) {
|
||||
_info.format = 2;
|
||||
_loaded = true;
|
||||
mp3dec_init(&dec);
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string_view read() override {
|
||||
if (!_put_back_buffer.empty() || _eof) return std::exchange(_put_back_buffer, {});
|
||||
while (_mp3_offset < _mp3_size) {
|
||||
mp3dec_frame_info_t frame_info;
|
||||
int samples = mp3dec_decode_frame(&dec,
|
||||
reinterpret_cast<const uint8_t *>(_data.get())+_mp3_offset,
|
||||
_mp3_size - _mp3_offset, pcm_buffer,&frame_info);
|
||||
|
||||
if (samples == 0) {
|
||||
// Nenalezen platný frame, posuneme se o 1 bajt a zkusíme dál
|
||||
_mp3_offset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
_mp3_offset += frame_info.frame_bytes ;
|
||||
|
||||
size_t data_size = samples* frame_info.channels * sizeof(mp3d_sample_t);
|
||||
|
||||
return std::string_view(reinterpret_cast<const char*>(pcm_buffer), data_size);
|
||||
}
|
||||
_eof = true;
|
||||
return {};
|
||||
}
|
||||
virtual void put_back(std::string_view data) override {
|
||||
_put_back_buffer = data;
|
||||
}
|
||||
virtual TMUSIC_STREAM_INFO get_info() const override {
|
||||
return _info;
|
||||
}
|
||||
|
||||
bool is_loaded() const {return _loaded;}
|
||||
|
||||
protected:
|
||||
MusicData _data;
|
||||
size_t _mp3_size;
|
||||
size_t _mp3_offset = 0;
|
||||
TMUSIC_STREAM_INFO _info = {};
|
||||
mp3dec_t dec{};
|
||||
|
||||
std::string_view _put_back_buffer = {};
|
||||
mp3d_sample_t pcm_buffer[MINIMP3_MAX_SAMPLES_PER_FRAME] = {};
|
||||
bool _eof = false;
|
||||
bool _loaded = false;
|
||||
|
||||
bool find_music_info() {
|
||||
mp3dec_t tmpdec{};
|
||||
mp3dec_init(&tmpdec);
|
||||
size_t ofs = 0;
|
||||
size_t max_ofs = std::min<size_t>(_mp3_size,4096);
|
||||
mp3dec_frame_info_t frame_info;
|
||||
while (ofs < max_ofs) {
|
||||
int samples = mp3dec_decode_frame(&tmpdec,
|
||||
reinterpret_cast<const uint8_t *>(_data.get())+ofs,
|
||||
_mp3_size-ofs, pcm_buffer, &frame_info);
|
||||
if (samples > 0) {
|
||||
_info.freq = frame_info.hz;
|
||||
_info.channels = frame_info.channels;
|
||||
return true;
|
||||
}
|
||||
++ofs;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void music_close(TMUSIC_STREAM *stream) {
|
||||
if (stream) {
|
||||
|
@ -119,13 +210,31 @@ void music_close(TMUSIC_STREAM *stream) {
|
|||
}
|
||||
}
|
||||
|
||||
TMUSIC_STREAM *music_open(const char *filename) {
|
||||
MUSStreamParser *player = new MUSStreamParser(filename);
|
||||
TMUSIC_STREAM *music_open_mus(const TMUSIC_SOURCE *src) {
|
||||
auto player = new MUSStreamParser(src);
|
||||
if (player->is_loaded()) return player;
|
||||
delete player;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TMUSIC_STREAM *music_open_mp3(const TMUSIC_SOURCE *src) {
|
||||
auto player = new MP3StreamParser(src);
|
||||
if (player->is_loaded()) return player;
|
||||
delete player;
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
TMUSIC_STREAM *music_open(const TMUSIC_SOURCE *source, TMUSIC_SOURCE_TYPE t) {
|
||||
if (t == MUSIC_SOURCE_MUS) {
|
||||
return music_open_mus(source);
|
||||
} else {
|
||||
return music_open_mp3(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TMUSIC_STREAM_CHUNK music_read(TMUSIC_STREAM *stream) {
|
||||
TMUSIC_STREAM_CHUNK r = {NULL,0};
|
||||
if (stream) {
|
||||
|
@ -157,4 +266,3 @@ TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream) {
|
|||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
libs/music.h
31
libs/music.h
|
@ -5,6 +5,17 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum music_source_type {
|
||||
MUSIC_SOURCE_MUS,
|
||||
MUSIC_SOURCE_MP3
|
||||
} TMUSIC_SOURCE_TYPE;
|
||||
|
||||
typedef struct music_source_t {
|
||||
const void *data;
|
||||
size_t size;
|
||||
void (*destructor)(struct music_source_t *me);
|
||||
} TMUSIC_SOURCE;
|
||||
|
||||
typedef struct music_stream_t {
|
||||
char dummy;
|
||||
} TMUSIC_STREAM;
|
||||
|
@ -23,8 +34,23 @@ typedef struct music_stream_chunk_t {
|
|||
|
||||
|
||||
|
||||
///open music stream
|
||||
TMUSIC_STREAM *music_open(const char *filename);
|
||||
|
||||
///open music stream MUS file
|
||||
/**
|
||||
* @param source source descriptor - it is copied into reader
|
||||
* @return music stream if valid, or NULL, if not
|
||||
*/
|
||||
TMUSIC_STREAM *music_open_mus(const TMUSIC_SOURCE *source);
|
||||
|
||||
///open music stream MP3 file
|
||||
/**
|
||||
* @param source source descriptor - it is copied into reader
|
||||
* @return music stream if valid, or NULL, if not
|
||||
*/
|
||||
TMUSIC_STREAM *music_open_mp3(const TMUSIC_SOURCE *source);
|
||||
|
||||
TMUSIC_STREAM *music_open(const TMUSIC_SOURCE *source, TMUSIC_SOURCE_TYPE type);
|
||||
|
||||
///retrieve information
|
||||
TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream);
|
||||
|
||||
|
@ -38,6 +64,7 @@ void music_put_back_chunk(TMUSIC_STREAM *stream, const TMUSIC_STREAM_CHUNK *chun
|
|||
void music_close(TMUSIC_STREAM *stream);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue