Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"kw32g\src\save.cpp":
- #include <save.hpp>
- #include <player.hpp>
- #include <tile.hpp>
- #include <object.hpp>
- using namespace kit;
- //since this is in a code block, data will be gc'd pretty much immediately
- #define READ_DATA(_path, _thing_ptr, _thing_size, _err_text) { \
- if(!fileio::fileExists(_path)) throw _err_text; \
- BinaryData data(_path); \
- if(data.getSize() != _thing_size) throw _err_text; \
- memory::copy(_thing_ptr, data.getData(), _thing_size); }
- static const char fpath_gl_player[] = "save/gl_player.bin";
- static const char fpath_gl_scene_indices[] = "save/gl_scene_indices.bin";
- static const char fpath_gl_scene[] = "save/gl_scene.bin";
- static const char fpath_gl_objcache[] = "save/gl_objsCache_%u.bin";
- static const char fpath_system_states[] = "dat/system_states.bin";
- #define FPATH_GL_PLAYER fpath_gl_player
- #define FPATH_GL_SCENE_INDICES fpath_gl_scene_indices
- #define FPATH_GL_SCENE fpath_gl_scene
- #define FPATH_GL_OBJCACHE fpath_gl_objcache
- #define FPATH_SYSTEM_STATES fpath_system_states
- #define SCENE_MIN_OFFSET (sizeof(Bitmap*) + sizeof(gl_scene.pat_mg) + sizeof(gl_scene.pat_fg))
- #define SCENE_MIN_SIZE (sizeof(gl_scene) - SCENE_MIN_OFFSET)
- #define SCENE_MIN PTR_OFFSET(&gl_scene, SCENE_MIN_OFFSET, Scene)
- //assumes object pointer is called "obj"
- #define OBJCAST_UNINIT(_type) ((_type*)obj)->init = false
- #define DELETE_SAVE_FILE(_path) { if(fileio::fileExists(_path)) fileio::deleteFile(_path); }
- struct SystemStates {
- bool fullscreen;
- };
- void saveReadSystem(){
- SystemStates system_states;
- {
- if(!fileio::fileExists(FPATH_SYSTEM_STATES)) return;
- BinaryData data(FPATH_SYSTEM_STATES);
- if(data.getSize() != sizeof(system_states))
- throw "failed to read system save data";
- memory::copy(&system_states, data.getData(), sizeof(system_states));
- }
- //fullscreen
- system_states.fullscreen &= 1; //just in case
- if(system_states.fullscreen != gl_fullscreen){
- gl_fullscreen = system_states.fullscreen;
- gl_win->setFullscreen(gl_fullscreen);
- }
- }
- void saveWriteSystem(){
- SystemStates system_states;
- system_states.fullscreen = gl_fullscreen;
- fileio::writeToFile(FPATH_SYSTEM_STATES, &system_states, sizeof(system_states));
- }
- void saveWipeSystem(){
- DELETE_SAVE_FILE(FPATH_SYSTEM_STATES);
- }
- static inline void uninit_object(Object* obj){
- switch(obj->type){
- case 1 : OBJCAST_UNINIT(_obj_1lever ); break;
- case 2 : OBJCAST_UNINIT(_obj_ability ); break;
- case 3 : OBJCAST_UNINIT(_obj_ability_remover); break;
- case 4 : OBJCAST_UNINIT(_obj_generic_sprite ); break;
- case 5 : OBJCAST_UNINIT(_obj_msgbox ); break;
- case 6 : OBJCAST_UNINIT(_obj_save_interface ); break;
- case 7 : OBJCAST_UNINIT(_obj_teleporter ); break;
- case 8 : OBJCAST_UNINIT(_obj_death_zone ); break;
- case 9 : OBJCAST_UNINIT(_obj_death_ball ); break;
- default:;
- }
- }
- //returns whether or not there is a save to load (just checks if gl_player.bin exists lol)
- //(make sure pointers aren't being used when loading relevant data; includes sfx)
- bool saveRead(){
- if(!fileio::fileExists(FPATH_GL_PLAYER)) return false;
- if(gl_player.dying) return false;
- READ_DATA(FPATH_GL_PLAYER, &gl_player, sizeof(gl_player), "failed to read player save data");
- gl_player.sfx_footstep = gl_sfx[1];
- gl_player.sfx_jumping = gl_sfx[2];
- gl_player.sfx_landing = gl_sfx[3];
- gl_player.going_left = false;
- gl_player.going_right = false;
- READ_DATA(FPATH_GL_SCENE_INDICES, gl_scene_indices, sizeof(gl_scene_indices),
- "failed to read scene index data");
- BinaryData _scene_indices(gl_scene_indices, sizeof(gl_scene_indices));
- u16* _scene_indices_ptr = (u16*)_scene_indices.getData();
- for(u32 i=1; i<(gl_scenes_len+1); ++i){
- delete gl_scenes[i];
- gl_scenes[i] = new SceneDescriptor(i);
- swapScene(gl_scene_indices[i], _scene_indices_ptr[i]);
- }
- READ_DATA(FPATH_GL_SCENE, SCENE_MIN, SCENE_MIN_SIZE, "failed to read current scene data");
- loadScene(gl_scene.scene);
- //for every scene
- for(u32 i=1; i<(gl_scenes_len+1); ++i){
- u16 objs_len = gl_scenes[i]->objs_len;
- //if scene actually has objects in it
- if(objs_len){
- size_t cache_size = sizeof(Object) * objs_len;
- READ_DATA(gl_fstr->fmt(FPATH_GL_OBJCACHE, i), gl_objsCache[i], cache_size,
- gl_fstr->fmt("failed to read object cache %u", i));
- //for each object in scene, uninitialize them so their pointers refresh
- Object* obj_cache = gl_objsCache[i];
- for(u16 o=0; o<objs_len; ++o){ //(object cache is zero-indexed)
- Object* obj = &obj_cache[o];
- obj->update = gl_objCallbacks[obj->type]; //(also update the callback ptr)
- uninit_object(obj);
- }
- }
- }
- gl_saveRead_called = true;
- return true;
- }
- void saveWrite(){
- fileio::writeToFile(FPATH_GL_PLAYER, &gl_player, sizeof(gl_player));
- fileio::writeToFile(FPATH_GL_SCENE_INDICES, gl_scene_indices,
- sizeof(gl_scene_indices));
- fileio::writeToFile(FPATH_GL_SCENE, SCENE_MIN, SCENE_MIN_SIZE);
- for(u32 i=1; i<(gl_scenes_len+1); ++i){
- size_t cache_size = sizeof(Object) * gl_scenes[i]->objs_len;
- if(cache_size){
- fileio::writeToFile(gl_fstr->fmt(FPATH_GL_OBJCACHE, i),
- gl_objsCache[i], cache_size);
- }
- }
- }
- void saveWipe(){
- DELETE_SAVE_FILE(FPATH_GL_PLAYER);
- DELETE_SAVE_FILE(FPATH_GL_SCENE_INDICES);
- DELETE_SAVE_FILE(FPATH_GL_SCENE);
- char* fstr_ptr = gl_fstr->ptr(); //valid for as long as gl_fstr exists
- for(u32 i=1; i<(gl_scenes_len+1); ++i){
- gl_fstr->fmt(FPATH_GL_OBJCACHE, i);
- DELETE_SAVE_FILE(fstr_ptr);
- }
- }
- /******************************************************************************/
- /******************************************************************************/
- //"kw32g\src\tile.cpp":
- #include <tile.hpp>
- #include <object.hpp>
- using namespace kit;
- void Scene::drawBg(){
- //don't draw background at all if scene = 0
- if(!scene) return;
- //if background is nullptr, use the 'missing tileset' bitmap instead
- //(since an error would occur during init if the background is invalid,
- // it should be impossible for bmp_bg to be nullptr in the first place)
- if(bmp_bg == nullptr){ gl_tileset_missing->blitRect(nullptr, nullptr); return; }
- shape::point sizeBmp = bmp_bg->getSize();
- if(sizeBmp.x<1 || sizeBmp.y<1) return;
- if(!repeat_bg){ //stretch entire background to entire canvas
- bmp_bg->blitRect();
- } else { //otherwise, repeat background bitmap in a tile pattern
- shape::rect dst; //destination rectangle
- dst.w = sizeBmp.x;
- dst.h = sizeBmp.y;
- shape::point sizeCanvas = gl_win->getCanvasSize();
- //increase x, then y until the entire canvas is drawn to
- for(dst.y = 0; dst.y < sizeCanvas.y; dst.y += dst.h)
- for(dst.x = 0; dst.x < sizeCanvas.x; dst.x += dst.w)
- {
- bmp_bg->blitRect(&dst, nullptr); //whole bitmap is used when src = nullptr
- }
- }
- }
- //note: if slowdown gets to a point where it's noticable,
- //implement a scene pattern cache
- //(as in, bake the 2 layers as bitmaps when loading a new
- // scene, before blitting those bitmaps every draw call)
- void Scene::drawTiles(bool drawForeground){
- if(!scene) return; //don't draw any tiles for scene 0
- Tile* tiles = pat_mg;
- if(drawForeground) tiles = pat_fg;
- if(tileset_a > gl_tilesets_len || tileset_b > gl_tilesets_len)
- throw "tileset index is out of bounds";
- Bitmap* tileset[2];
- tileset[0] = gl_tilesets[tileset_a];
- tileset[1] = gl_tilesets[tileset_b];
- shape::rect src(0, 0, 24, 24); //determines section of tileset to copy from
- shape::rect dst(0, 0, 24, 24); //determines section of canvas to paste to
- shape::point sizeCanvas = gl_win->getCanvasSize();
- //for accessing each tile independent of the 2 for loops
- u32 tileindex = -1; //(set to -1 so i can use preincrement while indexing)
- //increase x, then y until the entire canvas is drawn to
- for(dst.y = 0; dst.y < sizeCanvas.y; dst.y += dst.h)
- for(dst.x = 0; dst.x < sizeCanvas.x; dst.x += dst.w)
- {
- Tile tile = tiles[++tileindex];
- if(!tile.id) continue; //tile 0 is always transparent, so it can be skipped
- src.x = ( tile.id &0b1111) * src.w; //bits 0-3 are the tileset atlas' x
- src.y = ((tile.id>>4)& 0b111) * src.h; //bits 4-6 are the tileset atlas' y
- Bitmap* _tileset = tileset[tile.tileset]; //(tile.tileset is 1 bit in size)
- if(_tileset != nullptr){
- _tileset->blitRect(&dst, &src, 0xff00ff); //transparency color is magenta
- } else { //tileset doesn't exist; draw 'missing tileset' tile
- gl_tileset_missing->blitRect(&dst, nullptr); //transparency is not used
- }
- }
- }
- //(also used by both SceneDescriptor's constructor and destructor!)
- void SceneDescriptor::unload(){
- if(fileData == nullptr) return;
- BinaryData* _fileData = fileData;
- fileData = nullptr;
- delete _fileData;
- }
- #define etThrow(_text, _label) { errorText = _text; goto _label; }
- #define dfdThrow(_text) etThrow(_text, _DelFD)
- SceneDescriptor::SceneDescriptor(u16 scene_id){
- const char* errorText = "(no error)";
- if(0){ //entered into in the event of an exception
- _DelFD: NULLDELETE(fileData);
- throw errorText;
- }
- if(scene_id > gl_scenes_len)
- throw "scene_id > gl_scenes_len";
- unload(); //clean up any previous data, if any exists
- if(!fileio::fileExists(gl_fstr->fmt("dat/scene/scene_%u.ksd", scene_id)))
- dfdThrow((const char*)gl_fstr->fmt("\"dat/scene/scene_%u.ksd\" doesn't exist", scene_id));
- fileData = new BinaryData(gl_fstr->ptr());
- char* _fileData = fileData->getData();
- size_t fileSize = fileData->getSize();
- //handle header data (-sizeof(BinaryData*) used here as to not overwrite fileData!)
- memory::copy(this, _fileData, sizeof(SceneDescriptor)-sizeof(BinaryData*));
- char* data_end = PTR_OFFSET(_fileData, fileSize, char);
- char* objs_end = PTR_OFFSET(data_end, -(s64)sizeof(Object), char);
- char* pat_end = PTR_OFFSET(data_end, -(s64)sizeof(Tile)*2, char); //*2 for rle data + tile
- if(magic != KSD_MAGIC)
- dfdThrow("file is not a scene descriptor");
- if(dataSize != (fileSize-sizeof(SceneDescriptor)))
- dfdThrow("data size is inconsistent with actual file size");
- //convert file offsets to their actual pointer value (only if offset != 0)
- if(objs ) objs = PTR_OFFSET(_fileData, objs, Object); //(these will remain
- if(pat_mg) pat_mg = PTR_OFFSET(_fileData, pat_mg, Tile); //as nullptr if they
- if(pat_fg) pat_fg = PTR_OFFSET(_fileData, pat_fg, Tile); //were already)
- if(((char*)objs ) > objs_end) dfdThrow("objs goes past file data");
- if(((char*)pat_mg) > pat_end) dfdThrow("pat_mg goes past file data");
- if(((char*)pat_fg) > pat_end) dfdThrow("pat_fg goes past file data");
- if(bmp_bg > gl_backgrounds_len)
- dfdThrow("bmp_bg is out of range");
- if( (objs == nullptr) != (objs_len == 0) )
- dfdThrow("objs_len is inconsistent with obj file offset");
- //set objs' funcUpdate based on its type id
- for(u32 i=0; i<objs_len; ++i){ //(loop will not run at all if objs_len is 0)
- //objects of type 0 are technically allowed,
- //but they just don't do anything (lol)
- if(objs[i].type > gl_objCallbacks_len) dfdThrow("invalid object type");
- objs[i].update = gl_objCallbacks[objs[i].type];
- if(objs[i].update == nullptr) dfdThrow("object callback was null");
- }
- if(tileset_a > gl_tilesets_len) dfdThrow("tileset_a out of range");
- if(tileset_b > gl_tilesets_len) dfdThrow("tileset_b out of range");
- if(edge_n > gl_scenes_len) dfdThrow("edge_n out of range");
- if(edge_s > gl_scenes_len) dfdThrow("edge_s out of range");
- if(edge_w > gl_scenes_len) dfdThrow("edge_w out of range");
- if(edge_e > gl_scenes_len) dfdThrow("edge_e out of range");
- if(scene != scene_id ) dfdThrow("scene id in file inconsistent with actual id");
- if(music != 0xFFFF && music > gl_music_len ) dfdThrow("music out of range");
- if(ambience_a != 0xFFFF && ambience_a > gl_ambience_len) dfdThrow("ambience_a out of range");
- if(ambience_b != 0xFFFF && ambience_b > gl_ambience_len) dfdThrow("ambience_b out of range");
- }
- void loadScene(u16 scene_id){
- if(!scene_id) return; //0 is a special case for most, if not all asset arrays
- if(scene_id > gl_scenes_len) throw "scene_id > gl_scenes_len";
- if(gl_scenes[scene_id] == nullptr) throw "gl_scenes[scene_id] = nullptr";
- //(former bug: sceneNew used to be a stack object set to *gl_scenes[scene_id],
- //but doing so causes its destructor to trigger, making .fileData invalid,
- //which breaks things during the program's uninitialization phase.)
- SceneDescriptor* sceneNew = gl_scenes[scene_id];
- //gl_scene.bmp_bg = gl_backgrounds[sceneNew->bmp_bg]; //handled later
- gl_scene.scene = sceneNew->scene;
- gl_scene.music = sceneNew->music;
- gl_scene.ambience_a = sceneNew->ambience_a;
- gl_scene.ambience_b = sceneNew->ambience_b;
- gl_scene.tileset_a = sceneNew->tileset_a;
- gl_scene.tileset_b = sceneNew->tileset_b;
- //gl_scene.obj_reset = 3; //this is handled separately
- gl_scene.repeat_bg = sceneNew->repeat_bg;
- struct rle_data {
- u16 run;
- Tile tile;
- rle_data() : run(0), tile(0) {}
- };
- char* file_start = sceneNew->fileData->getData();
- size_t file_size = sceneNew->fileData->getSize();
- char* data_start = PTR_OFFSET(file_start, sizeof(SceneDescriptor), char);
- char* data_end = PTR_OFFSET(file_start, file_size, char);
- //copy bg and tile data to the scene
- if(sceneNew->fileData == nullptr) return;
- gl_scene.bmp_bg = nullptr;
- if(sceneNew->bmp_bg <= gl_backgrounds_len)
- gl_scene.bmp_bg = gl_backgrounds[sceneNew->bmp_bg];
- rle_data tile_mg, tile_fg;
- rle_data* rle_mg = (rle_data*)sceneNew->pat_mg;
- rle_data* rle_fg = (rle_data*)sceneNew->pat_fg;
- #define rle_end ((rle_data*)data_end)
- rle_data* rle_last = PTR_OFFSET(rle_end, -(s64)sizeof(rle_data), rle_data);
- //^^data_last is the location of the last element of the rle
- if(rle_mg != nullptr){
- for(u32 i=0; i<PATTERN_LEN; ++i){
- if(tile_mg.run == 0){
- if(rle_mg == rle_end) break;
- if(rle_mg > rle_last) throw "rle_mg went past file data";
- tile_mg = *(rle_mg++);
- }
- gl_scene.pat_mg[i] = tile_mg.tile;
- --tile_mg.run;
- }
- } else {
- memset0(gl_scene.pat_mg);
- }
- if(rle_fg != nullptr){
- for(u32 i=0; i<PATTERN_LEN; ++i){
- if(tile_fg.run == 0){
- if(rle_fg == rle_end) break;
- if(rle_fg > rle_last) throw "rle_fg went past file data";
- tile_fg = *(rle_fg++);
- }
- gl_scene.pat_fg[i] = tile_fg.tile;
- --tile_fg.run;
- }
- } else {
- memset0(gl_scene.pat_fg);
- }
- }
- void swapScene(u16 scene_id_old, u16 scene_id_new){
- if(!scene_id_old || !scene_id_new) return;
- if(scene_id_old > gl_scenes_len) throw "scene_id_old > gl_scenes_len";
- if(scene_id_new > gl_scenes_len) throw "scene_id_new > gl_scenes_len";
- if(gl_scenes[scene_id_old] == nullptr) throw "gl_scenes[scene_id_old] = nullptr";
- if(gl_scenes[scene_id_new] == nullptr) throw "gl_scenes[scene_id_new] = nullptr";
- SceneDescriptor* scene_old = gl_scenes[scene_id_old];
- SceneDescriptor* scene_new = gl_scenes[scene_id_new];
- //swap object information to prevent scene identity mismatches
- //(as in, scene 1's objects should always refer to the obj cache at index 1 )
- Object* objs_old = scene_old->objs;
- Object* objs_new = scene_new->objs;
- u16 objs_len_old = scene_old->objs_len;
- u16 objs_len_new = scene_new->objs_len;
- scene_old->objs = objs_new;
- scene_new->objs = objs_old;
- scene_old->objs_len = objs_len_new;
- scene_new->objs_len = objs_len_old;
- //swap the scene ids
- scene_old->scene = scene_id_new;
- scene_new->scene = scene_id_old;
- gl_scene_indices[scene_id_old] = scene_id_new;
- gl_scene_indices[scene_id_new] = scene_id_old;
- //swap the scene pointers
- gl_scenes[scene_id_old] = scene_new;
- gl_scenes[scene_id_new] = scene_old;
- //if current scene is being swapped, load the new scene
- if(gl_scene.scene == scene_id_old) loadScene(gl_scene.scene);
- }
- /******************************************************************************/
- /******************************************************************************/
- //"kit_xmp_sfx\src\kit_xmp_sfx\kit_qoa.h":
- /*
- Copyright (c) 2023, Dominic Szablewski - https://phoboslab.org
- SPDX-License-Identifier: MIT
- QOA - The "Quite OK Audio" format for fast, lossy audio compression
- */
- /* -----------------------------------------------------------------------------
- Header - Public functions */
- #ifndef QOA_H
- #define QOA_H
- #ifdef __cplusplus
- extern "C" {
- #endif
- #define QOA_MIN_FILESIZE 16
- #define QOA_MAX_CHANNELS 8
- #define QOA_SLICE_LEN 20
- #define QOA_SLICES_PER_FRAME 256
- #define QOA_FRAME_LEN (QOA_SLICES_PER_FRAME * QOA_SLICE_LEN)
- #define QOA_LMS_LEN 4
- #define QOA_MAGIC 0x716f6166 /* 'qoaf' */
- #define QOA_FRAME_SIZE(channels, slices) \
- (8 + QOA_LMS_LEN * 4 * channels + 8 * slices * channels)
- typedef struct {
- s32 history[QOA_LMS_LEN];
- s32 weights[QOA_LMS_LEN];
- } qoa_lms_t;
- typedef struct {
- u32 channels;
- u32 samplerate;
- u32 samples;
- qoa_lms_t lms[QOA_MAX_CHANNELS];
- #ifdef QOA_RECORD_TOTAL_ERROR
- f64 error;
- #endif
- } qoa_desc;
- u32 qoa_max_frame_size(qoa_desc *qoa);
- u32 qoa_decode_header(const u8* bytes, s32 size, qoa_desc* qoa);
- u32 qoa_decode_frame(const u8* bytes, u32 size, qoa_desc* qoa, s16* sample_data, u32* frame_len);
- s16* qoa_decode(const u8* bytes, s32 size, qoa_desc *file);
- #ifdef __cplusplus
- }
- #endif
- #endif /* QOA_H */
- /* -----------------------------------------------------------------------------
- Implementation */
- #ifdef QOA_IMPLEMENTATION
- #ifndef QOA_MALLOC
- #define QOA_MALLOC(sz) memory::alloc(sz);
- #define QOA_FREE(p) memory::free(&(p));
- #endif
- typedef unsigned long long qoa_uint64_t;
- /* The quant_tab provides an index into the dequant_tab for residuals in the
- range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at
- the higher end. Note that the residual zero is identical to the lowest positive
- value. This is mostly fine, since the qoa_div() function always rounds away
- from zero. */
- static const int qoa_quant_tab[17] = {
- 7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */
- 0, /* 0 */
- 0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */
- };
- /* We have 16 different scalefactors. Like the quantized residuals these become
- less accurate at the higher end. In theory, the highest scalefactor that we
- would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we
- rely on the LMS filter to predict samples accurately enough that a maximum
- residual of one quarter of the 16 bit range is sufficient. I.e. with the
- scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14.
- The scalefactor values are computed as:
- scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */
- static const int qoa_scalefactor_tab[16] = {
- 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048
- };
- /* The reciprocal_tab maps each of the 16 scalefactors to their rounded
- reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in
- the encoder with just one multiplication instead of an expensive division. We
- do this in .16 fixed point with integers, instead of floats.
- The reciprocal_tab is computed as:
- reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */
- static const int qoa_reciprocal_tab[16] = {
- 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32
- };
- /* The dequant_tab maps each of the scalefactors and quantized residuals to
- their unscaled & dequantized version.
- Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4
- instead of 1. The dequant_tab assumes the following dequantized values for each
- of the quant_tab indices and is computed as:
- float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7};
- dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q])
- The rounding employed here is "to nearest, ties away from zero", i.e. positive
- and negative values are treated symmetrically.
- */
- static const int qoa_dequant_tab[16][8] = {
- { 1, -1, 3, -3, 5, -5, 7, -7},
- { 5, -5, 18, -18, 32, -32, 49, -49},
- { 16, -16, 53, -53, 95, -95, 147, -147},
- { 34, -34, 113, -113, 203, -203, 315, -315},
- { 63, -63, 210, -210, 378, -378, 588, -588},
- { 104, -104, 345, -345, 621, -621, 966, -966},
- { 158, -158, 528, -528, 950, -950, 1477, -1477},
- { 228, -228, 760, -760, 1368, -1368, 2128, -2128},
- { 316, -316, 1053, -1053, 1895, -1895, 2947, -2947},
- { 422, -422, 1405, -1405, 2529, -2529, 3934, -3934},
- { 548, -548, 1828, -1828, 3290, -3290, 5117, -5117},
- { 696, -696, 2320, -2320, 4176, -4176, 6496, -6496},
- { 868, -868, 2893, -2893, 5207, -5207, 8099, -8099},
- {1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933},
- {1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005},
- {1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336},
- };
- /* The Least Mean Squares Filter is the heart of QOA. It predicts the next
- sample based on the previous 4 reconstructed samples. It does so by continuously
- adjusting 4 weights based on the residual of the previous prediction.
- The next sample is predicted as the sum of (weight[i] * history[i]).
- The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or
- subtracts the residual to each weight, based on the corresponding sample from
- the history. This, surprisingly, is sufficient to get worthwhile predictions.
- This is all done with fixed point integers. Hence the right-shifts when updating
- the weights and calculating the prediction. */
- static int qoa_lms_predict(qoa_lms_t *lms) {
- int prediction = 0;
- for (int i = 0; i < QOA_LMS_LEN; i++) {
- prediction += lms->weights[i] * lms->history[i];
- }
- return prediction >> 13;
- }
- static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) {
- int delta = residual >> 4;
- for (int i = 0; i < QOA_LMS_LEN; i++) {
- lms->weights[i] += lms->history[i] < 0 ? -delta : delta;
- }
- for (int i = 0; i < QOA_LMS_LEN-1; i++) {
- lms->history[i] = lms->history[i+1];
- }
- lms->history[QOA_LMS_LEN-1] = sample;
- }
- /* qoa_div() implements a rounding division, but avoids rounding to zero for
- small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still
- returns as 0, which is handled in the qoa_quant_tab[].
- qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an
- argument, so it can do the division with a cheaper integer multiplication. */
- static inline int qoa_div(int v, int scalefactor) {
- int reciprocal = qoa_reciprocal_tab[scalefactor];
- int n = (v * reciprocal + (1 << 15)) >> 16;
- n = n + ((v > 0) - (v < 0)) - ((n > 0) - (n < 0)); /* round away from 0 */
- return n;
- }
- static inline int qoa_clamp(int v, int min, int max) {
- if (v < min) { return min; }
- if (v > max) { return max; }
- return v;
- }
- /* This specialized clamp function for the signed 16 bit range improves decode
- performance quite a bit. The extra if() statement works nicely with the CPUs
- branch prediction as this branch is rarely taken. */
- static inline int qoa_clamp_s16(int v) {
- if ((unsigned int)(v + 32768) > 65535) {
- if (v < -32768) { return -32768; }
- if (v > 32767) { return 32767; }
- }
- return v;
- }
- static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) {
- bytes += *p;
- *p += 8;
- return
- ((qoa_uint64_t)(bytes[0]) << 56) | ((qoa_uint64_t)(bytes[1]) << 48) |
- ((qoa_uint64_t)(bytes[2]) << 40) | ((qoa_uint64_t)(bytes[3]) << 32) |
- ((qoa_uint64_t)(bytes[4]) << 24) | ((qoa_uint64_t)(bytes[5]) << 16) |
- ((qoa_uint64_t)(bytes[6]) << 8) | ((qoa_uint64_t)(bytes[7]) << 0);
- }
- static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) {
- bytes += *p;
- *p += 8;
- bytes[0] = (v >> 56) & 0xff;
- bytes[1] = (v >> 48) & 0xff;
- bytes[2] = (v >> 40) & 0xff;
- bytes[3] = (v >> 32) & 0xff;
- bytes[4] = (v >> 24) & 0xff;
- bytes[5] = (v >> 16) & 0xff;
- bytes[6] = (v >> 8) & 0xff;
- bytes[7] = (v >> 0) & 0xff;
- }
- /* -----------------------------------------------------------------------------
- Decoder */
- unsigned int qoa_max_frame_size(qoa_desc *qoa) {
- return QOA_FRAME_SIZE(qoa->channels, QOA_SLICES_PER_FRAME);
- }
- unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa) {
- unsigned int p = 0;
- if (size < QOA_MIN_FILESIZE) {
- return 0;
- }
- /* Read the file header, verify the magic number ('qoaf') and read the
- total number of samples. */
- qoa_uint64_t file_header = qoa_read_u64(bytes, &p);
- if ((file_header >> 32) != QOA_MAGIC) {
- return 0;
- }
- qoa->samples = file_header & 0xffffffff;
- if (!qoa->samples) {
- return 0;
- }
- /* Peek into the first frame header to get the number of channels and
- the samplerate. */
- qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
- qoa->channels = (frame_header >> 56) & 0x0000ff;
- qoa->samplerate = (frame_header >> 32) & 0xffffff;
- if (qoa->channels == 0 || qoa->samples == 0 || qoa->samplerate == 0) {
- return 0;
- }
- return 8;
- }
- unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len) {
- unsigned int p = 0;
- *frame_len = 0;
- if (size < 8 + QOA_LMS_LEN * 4 * qoa->channels) {
- return 0;
- }
- /* Read and verify the frame header */
- qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
- int channels = (frame_header >> 56) & 0x0000ff;
- int samplerate = (frame_header >> 32) & 0xffffff;
- int samples = (frame_header >> 16) & 0x00ffff;
- u32 frame_size = (frame_header ) & 0x00ffff;
- int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
- int num_slices = data_size / 8;
- int max_total_samples = num_slices * QOA_SLICE_LEN;
- if (
- channels != qoa->channels ||
- samplerate != qoa->samplerate ||
- frame_size > size ||
- samples * channels > max_total_samples
- ) {
- return 0;
- }
- /* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
- for (int c = 0; c < channels; c++) {
- qoa_uint64_t history = qoa_read_u64(bytes, &p);
- qoa_uint64_t weights = qoa_read_u64(bytes, &p);
- for (int i = 0; i < QOA_LMS_LEN; i++) {
- qoa->lms[c].history[i] = ((signed short)(history >> 48));
- history <<= 16;
- qoa->lms[c].weights[i] = ((signed short)(weights >> 48));
- weights <<= 16;
- }
- }
- /* Decode all slices for all channels in this frame */
- for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
- for (int c = 0; c < channels; c++) {
- qoa_uint64_t slice = qoa_read_u64(bytes, &p);
- int scalefactor = (slice >> 60) & 0xf;
- int slice_start = sample_index * channels + c;
- int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c;
- for (int si = slice_start; si < slice_end; si += channels) {
- int predicted = qoa_lms_predict(&qoa->lms[c]);
- int quantized = (slice >> 57) & 0x7;
- int dequantized = qoa_dequant_tab[scalefactor][quantized];
- int reconstructed = qoa_clamp_s16(predicted + dequantized);
- sample_data[si] = reconstructed;
- slice <<= 3;
- qoa_lms_update(&qoa->lms[c], reconstructed, dequantized);
- }
- }
- }
- *frame_len = samples;
- return p;
- }
- short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) {
- unsigned int p = qoa_decode_header(bytes, size, qoa);
- if (!p) {
- return NULL;
- }
- /* Calculate the required size of the sample buffer and allocate */
- int total_samples = qoa->samples * qoa->channels;
- short *sample_data = (s16*)QOA_MALLOC(total_samples * sizeof(short));
- unsigned int sample_index = 0;
- unsigned int frame_len;
- unsigned int frame_size;
- /* Decode all frames */
- do {
- short *sample_ptr = sample_data + sample_index * qoa->channels;
- frame_size = qoa_decode_frame(bytes + p, size - p, qoa, sample_ptr, &frame_len);
- p += frame_size;
- sample_index += frame_len;
- } while (frame_size && sample_index < qoa->samples);
- qoa->samples = sample_index;
- return sample_data;
- }
- #endif /* QOA_IMPLEMENTATION */
- /******************************************************************************/
- /******************************************************************************/
- //"kit_w32\include\kit\all.hpp":
- #ifndef _KIT_INC_ALL_HPP
- #define _KIT_INC_ALL_HPP
- #include "commondef.hpp"
- #include "misc.hpp"
- #include "video.hpp"
- #include "audio.hpp"
- #endif /* _KIT_INC_ALL_HPP */
- /******************************************************************************/
- /******************************************************************************/
- //"kit_w32\include\kit\audio.hpp":
- #ifndef _KIT_INC_AUDIO_HPP
- #define _KIT_INC_AUDIO_HPP
- #include "commondef.hpp"
- #include "misc.hpp"
- #include "_audio_AudioStream.hpp"
- #include "_audio_func.hpp"
- #endif /* _KIT_INC_AUDIO_HPP */
- /******************************************************************************/
- /******************************************************************************/
- //"kit_w32\include\kit\commondef.hpp":
- #ifndef _KIT_INC_COMMONDEF_HPP
- #define _KIT_INC_COMMONDEF_HPP
- namespace kit {
- #ifndef MIN
- #define MIN(a,b) ( ((a)<(b)) ? (a) : (b) )
- #endif /* MIN(a,b) */
- #ifndef MAX
- #define MAX(a,b) ( ((a)>(b)) ? (a) : (b) )
- #endif /* MAX(a,b) */
- #ifndef CLAMP
- #define CLAMP(n, mn, mx) MIN(MAX(n,mn),mx)
- #endif /* CLAMP(n, mn, mx) */
- // integer bounds
- #define KIT_U8_MAX (0xFF)
- #define KIT_U16_MAX (0xFFFF)
- #define KIT_U32_MAX (0xFFFFFFFF)
- #define KIT_U64_MAX (0xFFFFFFFFFFFFFFFF)
- //
- #define KIT_S8_MIN (0x80)
- #define KIT_S8_MAX (0x7F)
- #define KIT_S16_MIN (0x8000)
- #define KIT_S16_MAX (0x7FFF)
- #define KIT_S32_MIN (0x80000000)
- #define KIT_S32_MAX (0x7FFFFFFF)
- #define KIT_S64_MIN (0x8000000000000000)
- #define KIT_S64_MAX (0x7FFFFFFFFFFFFFFF)
- // most significant bits/Bytes
- #define KIT_MSb_8 (0x80)
- #define KIT_MSb_16 (0x8000)
- #define KIT_MSb_32 (0x80000000)
- #define KIT_MSb_64 (0x8000000000000000)
- //
- #define KIT_MSB_8 (0xFF)
- #define KIT_MSB_16 (0xFF00)
- #define KIT_MSB_32 (0xFF000000)
- #define KIT_MSB_64 (0xFF00000000000000)
- #if defined(_STDINT) || defined(_CSTDINT_)
- typedef uint8_t u8 ;
- typedef uint16_t u16;
- typedef uint32_t u32;
- typedef uint64_t u64;
- typedef int8_t s8 ;
- typedef int16_t s16;
- typedef int32_t s32;
- typedef int64_t s64;
- #else
- typedef unsigned char u8 ;
- typedef unsigned short u16;
- typedef unsigned int u32;
- typedef unsigned long long u64;
- typedef signed char s8 ;
- typedef signed short s16;
- typedef signed int s32;
- typedef signed long long s64;
- #endif
- // for consistency
- typedef float f32;
- typedef double f64;
- typedef void* _GenericOpaquePtr;
- };
- #endif /* _KIT_INC_COMMONDEF_HPP */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement