Advertisement
Kitomas

work for 2024-11-21 (0/10)

Nov 22nd, 2024
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 35.95 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"2024-11-21\src\demo\callbacks.cpp":
  4. #include <include_all.hpp>
  5.  
  6. using namespace kit;
  7.  
  8.  
  9.  
  10.  
  11.  
  12. void watch_win_resize(Event& event, void* userdata){
  13.   if(event.type != KEVENT_WIN_SIZE_CHANGED) return;
  14.  
  15.   if(canvas_lock && win){
  16.     canvas_lock->lock();
  17.     win->renewSurface();
  18.  
  19.     if(canvas){
  20.       Surface* canvas_old = canvas;
  21.       Surface* canvas_new = new Surface(*win, PIXELFMT_ABGR8888);
  22.       canvas = canvas_new;
  23.       delete canvas_old;
  24.     }
  25.  
  26.     canvas_lock->unlock();
  27.  
  28.   }
  29.  
  30.   (void)userdata; //userdata isn't used here
  31.  
  32. }
  33.  
  34.  
  35.  
  36.  
  37.  
  38. s32 audio_callback(void* _buffer, const AudioDeviceInfo& info){
  39.   Stereo_f32* dst     = (Stereo_f32*)_buffer;
  40.   u16         dst_len = info.sampleFrames;
  41.  
  42.   if(music) music->mixTracks(dst, dst_len, info.timeStartMS);
  43.  
  44.   if(sfx) sfx->mixTracks(dst, dst_len, info.timeStartMS);
  45.  
  46.   //clamp all samples to a unit range, before setting volume to VOLUME
  47.   #define VOLUME 0.10f //10%
  48.   for(u16 i=0; i<dst_len; ++i){
  49.     dst[i].l = CLAMP(dst[i].l, -1.0f, 1.0f) * VOLUME;
  50.     dst[i].r = CLAMP(dst[i].r, -1.0f, 1.0f) * VOLUME;
  51.   }
  52.  
  53.   return 0;
  54.  
  55. }
  56. /******************************************************************************/
  57. /******************************************************************************/
  58. //"2024-11-21\src\demo\main.cpp":
  59. #include <stdlib.h>
  60. #include <windows.h>
  61.  
  62. #include <include_all.hpp>
  63.  
  64. using namespace kit;
  65.  
  66. DEF_GLOBAL_PTRS
  67.  
  68. colors::ABGR* pixels;
  69.  
  70. /*
  71.  
  72. sim speed should slow music and sfx down
  73.  
  74.   1 = 640, 480
  75.   2 = 320, 240
  76.   4 = 160, 120
  77.   5 = 128,  96
  78.   8 =  80,  60
  79.  10 =  64,  48
  80.  16 =  40,  30
  81.  20 =  32,  24
  82.  32 =  20,  15
  83.  40 =  16,  12
  84.  80 =   8,   6
  85. 160 =   4,   3
  86.  
  87. */
  88.  
  89.  
  90.  
  91.  
  92.  
  93. void watch_win_resize(Event& event, void* userdata);
  94.  
  95. s32 audio_callback(void* _buffer, const AudioDeviceInfo& info);
  96.  
  97.  
  98.  
  99.  
  100.  
  101. int user_main(int argc, char** argv);
  102.  
  103. int main(int argc, char** argv){
  104.   int returnStatus = -1; try {
  105.   u64 ticksStart = time::getTicks();
  106.   initSubsystems(KINIT_EVERYTHING);
  107.  
  108.  
  109.  
  110.   AudioDeviceInfo adev_info;
  111.   adev_info.numChannels = 2;
  112.   adev_info.zeroBuffer  = true;
  113.   adev_info.callback    = audio_callback;
  114.  
  115.   audio = new AudioDevice(nullptr, adev_info);
  116.   (void)adev_info;
  117.  
  118.   if(audio) audio->play();
  119.  
  120.   sfx   = new SoundEngine(64, audio->info.sampleRate);
  121.   music = new SoundEngine( 1, audio->info.sampleRate);
  122.   //setting this to false allows me to play music starting at 0% volume
  123.   music->stopIfVolumeIsZero = false;
  124.  
  125.  
  126.  
  127.   sfx_tick = new AudioData("audio/tick.wav", AudioDataLoadWAV);
  128.   sfx_tick->convertFormat(SMPFMT_F32);
  129.  
  130.   sfx_alert = new AudioData("audio/alert.ogg", AudioDataLoadOGG);
  131.   sfx_alert->convertFormat(SMPFMT_F32);
  132.  
  133.  
  134.  
  135.   AudioData music_data("audio/music.ogg", AudioDataLoadOGG);
  136.   music_data.convertFormat(SMPFMT_F32);
  137.   music_data.hdr->loopCount = 0xFFFF; //inf loop
  138.  
  139.   if(music->play(music_data, 0.0f, 0.0f, 1.0) == 0xFFFF)
  140.     throw "failed to queue music track"; //should be impossible
  141.  
  142.   music->setVolumeDelta(5.0f, 5.0f); //fade-in over 5 seconds
  143.  
  144.  
  145.  
  146.   //EventWatch watch_resize_events(watch_win_resize);
  147.  
  148.   canvas_lock = new Mutex;
  149.  
  150.   win = new Window("kit_sdl2 + Box2D-Lite demo", WIN_X, WIN_Y,
  151.                    WINFLAG_HIDDEN, WINPOS_CENTERED, WINPOS_CENTERED);
  152.  
  153.   canvas = new Surface(CNV_X, CNV_Y, PIXELFMT_ABGR8888);
  154.   pixels = (colors::ABGR*)canvas->getPixelData();
  155.  
  156.   #define GRAY(_hexvalue) 0xFF##_hexvalue##_hexvalue##_hexvalue
  157.   font0 = new BFont_Surface(nullptr, nullptr, GRAY(10), 4096);
  158.   font1 = new BFont_Surface(nullptr, nullptr, GRAY(FF), 4096);
  159.   font1->txt_bg.v = font0->txt_bg.v = GRAY(40);
  160.  
  161.  
  162.  
  163.   srand((u32)time::getTicks());
  164.   u64 ticksTaken = (time::getTicks()-ticksStart) * 1000; // *1000 to get milliseconds
  165.   kit_LogInfo("INITIALIZED IN: %fms", (f64)ticksTaken/time::getTicksPerSecond());
  166.  
  167.   //win->setVisibility(true);
  168.  
  169.   returnStatus = user_main(argc, argv);
  170.  
  171.  
  172.  
  173.   music->stop();
  174.   sfx  ->stop();
  175.   audio->pause();
  176.  
  177.   //watch_resize_events.setState(false); //stops the callback from being invoked
  178.  
  179.   NULLDELETE(win, Window);
  180.   NULLDELETE(canvas, Surface);
  181.   NULLDELETE(canvas_lock, Mutex);
  182.   NULLDELETE(font0, BFont_Surface);
  183.   NULLDELETE(font1, BFont_Surface);
  184.  
  185.   sfx->stop(0xFFFF, true);
  186.   NULLDELETE(sfx_tick, AudioData);
  187.   NULLDELETE(sfx_alert, AudioData);
  188.   NULLDELETE(sfx, SoundEngine);
  189.   audio->pauseAndWait();
  190.   NULLDELETE(audio, AudioDevice);
  191.   NULLDELETE(music, SoundEngine);
  192.  
  193.  
  194.  
  195.  
  196.  
  197. } catch(const char* errorText){
  198.   #ifdef _DEBUG
  199.     kit_LogError("FATAL EXCEPTION OCCURRED: \"%s\"\n", errorText);
  200.   #else
  201.     MessageBeep(MB_ICONERROR);
  202.     showMsgBox(errorText, "FATAL EXCEPTION OCCURRED!", MSGBOX_ERROR);
  203.   #endif /* _DEBUG */
  204.   //redundant, as quitSubsystems already does this when given KINIT_EVERYTHING
  205.   //freeThreadErrors();
  206.  
  207. }
  208.  
  209.   //can't error, and is also safe to call even if no subsystems are active!
  210.   quitSubsystems(KINIT_EVERYTHING);
  211.  
  212.   //a nonzero value indicates a memory leak, or the number of allocations
  213.    //was improperly inc/decremented internally!
  214.   if(memory::getNumAllocations())
  215.     kit_LogWarn("# OF ALLOCATIONS = %lli", (s64)memory::getNumAllocations());
  216.  
  217.   return returnStatus;
  218.  
  219. }
  220. /******************************************************************************/
  221. /******************************************************************************/
  222. //"2024-11-21\src\demo\user_main.cpp":
  223. #include <cstddef>
  224. #include <unistd.h>
  225.  
  226. #include <include_all.hpp>
  227.  
  228. using namespace kit;
  229.  
  230. #define CNV_POINT_EQ(_a, _b)
  231.  
  232.  
  233.  
  234.  
  235.  
  236. bool          dragging = false;
  237. shape::line   drag;
  238. shape::fpoint velocity;
  239.  
  240. #define VEL_MUL 10.0f
  241.  
  242. static inline f32 getVelScalar(f32 x0, f32 y0, f32 x1, f32 y1){
  243.   f32 dx = (x0-x1),  dy = (y0-y1);
  244.   return sqrtf(dx*dx + dy*dy)*VEL_MUL;
  245. }
  246.  
  247. static inline shape::fpoint getVel(f32 x0, f32 y0, f32 x1, f32 y1){
  248.   f32 dx = (x0-x1),  dy = (y0-y1);
  249.   //f32 len = sqrtf(dx*dx + dy*dy);
  250.   return {(dx)*VEL_MUL, (dy)*VEL_MUL};
  251. }
  252.  
  253. bool         mouse_focus = false;
  254. shape::point mouse;
  255.  
  256. f32 boxSize = 24.0f;
  257.  
  258. Body bodies[200];
  259. int  bodies_len = 0;
  260.  
  261. //(gravity_x, gravity_y), iterations
  262. World world(Vec2(0.0f, 300.0f), 10);
  263.  
  264.  
  265.  
  266.  
  267.  
  268. Body bodyFloor;
  269.  
  270. void worldInit(){
  271.   world.Clear();
  272.   bodies_len = 0;
  273.  
  274.   bodyFloor.Set(Vec2(CNV_X-16-2, 10), FLT_MAX);
  275.   bodyFloor.position.Set(CNV_X/2+8.0f, CNV_Y-6.0f);
  276.   world.Add(&bodyFloor);
  277.  
  278. }
  279.  
  280.  
  281.  
  282. void launchBox(){
  283.   if(bodies_len >= (sizeof(bodies)/sizeof(Body))) return; //can't add any more
  284.   Body& newbody = bodies[bodies_len++];
  285.  
  286.   newbody.Set(Vec2(boxSize, boxSize), boxSize*50);
  287.   newbody.position.Set(drag.x0, drag.y0);
  288.   newbody.velocity = Vec2(velocity.x, velocity.y);
  289.  
  290.   world.Add(&newbody);
  291.  
  292. }
  293.  
  294.  
  295.  
  296.  
  297.  
  298. extern colors::ABGR* pixels;
  299.  
  300. static inline void drawPoint(s32 x, s32 y, colors::ABGR pointColor){
  301.   if(x >= 0  &&  x < CNV_X  &&  y >= 0  &&  y < CNV_Y)
  302.     pixels[x + y*CNV_X].v = pointColor.v;
  303. }
  304.  
  305. void drawCursorTextCtx(){
  306.   if(!mouse_focus) return;
  307.  
  308.   if(mouse.x > 16){
  309.     if(!dragging){
  310.       drawPoint(mouse.x, mouse.y, 0xFFFFFFFF);
  311.  
  312.     } else {
  313.       f32 h = boxSize/2-1;
  314.       Box(drag.x0-h, drag.y0-h, boxSize, boxSize).draw();
  315.       drawCursorText(mouse.x, mouse.y, "vel: %.2f",
  316.                      getVelScalar(drag.x0, drag.y0, drag.x1, drag.y1));
  317.  
  318.     }
  319.  
  320.   } else {
  321.     drawCursorText(mouse.x, mouse.y, "simulation speed: %.2fx", simSpeed);
  322.  
  323.   }
  324. }
  325.  
  326.  
  327.  
  328.  
  329.  
  330. /******************************************************************************/
  331.  
  332.  
  333.  
  334.  
  335.  
  336. bool handleEvents(){
  337.   bool run = true;
  338.   bool launch = false;
  339.  
  340.   Event evt;
  341.   while(pollEvent(&evt))
  342.   switch(evt.type){
  343.     case KEVENT_QUIT: run = false; break;
  344.  
  345.     case KEVENT_MOUSE_DOWN: {
  346.       sfx->play(*sfx_tick, 1.0f, 1.0f, 1.5);
  347.       drag.x0 = evt.mouse.x/CNV_DIV;
  348.       drag.y0 = evt.mouse.y/CNV_DIV;
  349.       drag.x1 = drag.x0;
  350.       drag.y1 = drag.y0;
  351.       if(mouse.x <= 16) goto _set_sim_speed;
  352.       else              dragging = true;
  353.     } break;
  354.  
  355.     case KEVENT_MOUSE_UP: {
  356.       sfx->play(*sfx_tick, 1.0f, 1.0f, -1.0);
  357.       dragging = false;
  358.       if(mouse.x > 16){
  359.         launch = true;
  360.         _set_endpoint:
  361.         drag.x1 = mouse.x;
  362.         drag.y1 = mouse.y;
  363.         velocity = getVel(drag.x0, drag.y0, drag.x1, drag.y1);
  364.         if(launch) launchBox();
  365.       }
  366.     } break;
  367.  
  368.     case KEVENT_MOUSE_MOVED: {
  369.       mouse.x = evt.mouse.x/CNV_DIV;
  370.       mouse.y = evt.mouse.y/CNV_DIV;
  371.       if(mouse.x > 16){
  372.         if(evt.mouse.pressed && dragging) goto _set_endpoint;
  373.       } else {
  374.         if(evt.mouse.pressed) _set_sim_speed:
  375.           setSimSpeed((f64)(CNV_Y-mouse.y)/CNV_Y);
  376.       }
  377.     } break;
  378.  
  379.     case KEVENT_MOUSE_WHEEL: { //negative = scrolling down
  380.       boxSize += evt.mouse.pdy;
  381.       boxSize = CLAMP(boxSize, 1.0f, 178.0f);
  382.     } break;
  383.  
  384.     case KEVENT_WIN_MFOCUS_GAINED: mouse_focus = true;  break;
  385.     case KEVENT_WIN_MFOCUS_LOST  : mouse_focus = false; break;
  386.  
  387.     case KEVENT_KEY_DOWN: {
  388.       if(!evt.key.repeat){ //reset if a key is pressed
  389.         sfx->play(*sfx_alert, 1.0f, 1.0f, 1.5);
  390.         worldInit();
  391.       }
  392.     } break;
  393.  
  394.     default: kit_LogInfo("unhandled event = %s", getEventText(evt.type));
  395.   }
  396.  
  397.   return run;
  398.  
  399. }
  400.  
  401.  
  402.  
  403.  
  404.  
  405. /******************************************************************************/
  406.  
  407.  
  408.  
  409.  
  410.  
  411. int user_main(int argc, char** argv){
  412.   win->setVisibility(true);
  413.   worldInit();
  414.  
  415.   while(true){
  416.     u64 timeStart = time::getTicks();
  417.  
  418.     if(!handleEvents()) break;
  419.     clearCanvas();
  420.  
  421.     world.Step(timeStep);
  422.  
  423.     drawBody(bodyFloor);
  424.  
  425.     for(int i=0; i<bodies_len; ++i)
  426.       drawBody(bodies[i]);
  427.  
  428.     Box(CNV_X-1-boxSize, 1.0f, boxSize, boxSize).draw();
  429.     if(dragging && mouse.x > 16)
  430.       drawLine(drag.x0, drag.y0, drag.x1, drag.y1, 0xFF00FF);
  431.     drawSpeedBar();
  432.     drawCursorText(CNV_X-2-boxSize, 1, "box size: %.0fpx", boxSize);
  433.     drawCursorTextCtx();
  434.  
  435.     f64 timeDelta = (f64)(time::getTicks()-timeStart)/time::getTicksPerSecond();
  436.     font1->draw(*canvas, 17, 1, "rendered in %.0f\nmicroseconds", 0, timeDelta*1000000);
  437.  
  438.     presentCanvas();
  439.     time::sleep(16);
  440.  
  441.   }
  442.  
  443.   (void)argc, (void)argv; //begone, unused parameter warning
  444.   return 0;
  445.  
  446. }
  447. /******************************************************************************/
  448. /******************************************************************************/
  449. //"2024-11-21\src\demo\utils.cpp":
  450. #include <stdlib.h>
  451.  
  452. #include <include_all.hpp>
  453.  
  454. using namespace kit;
  455.  
  456.  
  457.  
  458.  
  459.  
  460. //assumes RAND_MAX is 32767
  461. #define GET_FRAND_VALUE(cast) ( (cast)(rand()<<15|rand())/0x3fffffff )
  462.  
  463. f64 frand(){ //0.0f -> 1.0f
  464.   return GET_FRAND_VALUE(f64);
  465. }
  466.  
  467. f64 frand2(){ //-1.0f -> 1.0f
  468.   return GET_FRAND_VALUE(f64)*2.0f - 1.0f;
  469. }
  470.  
  471. f32 frandf(){ //0.0f -> 1.0f
  472.   return GET_FRAND_VALUE(f32);
  473. }
  474.  
  475. f32 frandf2(){ //-1.0f -> 1.0f
  476.   return GET_FRAND_VALUE(f32)*2.0f - 1.0f;
  477. }
  478.  
  479.  
  480.  
  481.  
  482.  
  483. void presentCanvas(){
  484.   if(canvas_lock && win){
  485.     canvas_lock->lock();
  486.     win->renewSurface();
  487.  
  488.     if(canvas) canvas->blitScaled(*win);
  489.     win->updateSurface();
  490.  
  491.     canvas_lock->unlock();
  492.  
  493.   }
  494. }
  495.  
  496.  
  497.  
  498.  
  499.  
  500. extern colors::ABGR* pixels;
  501.  
  502. /*
  503. static inline void drawPoint(s32 x, s32 y, colors::ABGR pointColor){
  504.   if(x >= 0  &&  x < CNV_X  &&  y >= 0  &&  y < CNV_Y)
  505.     pixels[x + y*CNV_X].v = pointColor.v;
  506. }
  507.  
  508. void drawLine(s32 x_0, s32 y_0,
  509.               s32 x_1, s32 y_1,
  510.               colors::ABGR lineColor)
  511. {
  512.   lineColor.v |= 0xFF000000; //make sure alpha is always 255
  513.   s32 d_x =  abs(x_1 - x_0);
  514.   s32 d_y = -abs(y_1 - y_0);
  515.   s32 s_x = (x_0<x_1) ? 1 : -1;
  516.   s32 s_y = (y_0<y_1) ? 1 : -1;
  517.   s32 err = d_x + d_y;
  518.  
  519.   while(true){
  520.     drawPoint(x_0, y_0, lineColor);
  521.     if(x_0 == x_1  &&  y_0 == y_1) break;
  522.     s32 err2 = err<<1;
  523.  
  524.     if(err2 >= d_y){
  525.       err += d_y;
  526.       x_0 += s_x;
  527.     }
  528.  
  529.     if(err2 <= d_x){
  530.       err += d_x;
  531.       y_0 += s_y;
  532.     }
  533.  
  534.   }
  535. }
  536. */
  537. void drawLine(s32 x_0, s32 y_0,
  538.               s32 x_1, s32 y_1,
  539.               colors::ABGR lineColor)
  540. {
  541.   lineColor.v |= 0xFF000000; //make sure alpha is always 255
  542.   s32 d_x =  abs(x_1 - x_0);
  543.   s32 d_y = -abs(y_1 - y_0);
  544.   s32 s_x = (x_0<x_1) ? 1 : -1;
  545.   s32 s_y = (y_0<y_1) ? 1 : -1;
  546.   s32 err = d_x + d_y;
  547.  
  548.   s32 _s_x = s_x * sizeof(colors::ABGR);
  549.   s32 _s_y = s_y * sizeof(colors::ABGR) * CNV_X;
  550.   u8* _pixels = (u8*)&pixels[x_0 + y_0*CNV_X];
  551.  
  552.   while(true){
  553.     if((x_0 >= 0)  &&  (x_0 < CNV_X)  &&  (y_0 >= 0)  &&  (y_0 < CNV_Y))
  554.       ((colors::ABGR*)_pixels)->v = lineColor.v;
  555.  
  556.     if(x_0 == x_1  &&  y_0 == y_1) break;
  557.     s32 err2 = err<<1;
  558.  
  559.     if(err2 >= d_y){
  560.       err += d_y;
  561.       x_0 += s_x;
  562.       _pixels += _s_x;
  563.     }
  564.  
  565.     if(err2 <= d_x){
  566.       err += d_x;
  567.       y_0 += s_y;
  568.       _pixels += _s_y;
  569.     }
  570.  
  571.   }
  572. }
  573.  
  574.  
  575.  
  576.  
  577.  
  578. #define V2I(_p) (s32)((_p).x), (s32)((_p).y)
  579. #define DRAWLINE_VEC2(_a, _b, _color) \
  580.   drawLine(V2I(_a), V2I(_b), _color)
  581.  
  582. void drawBody(Body& body){
  583.   Mat22 rot(body.rotation);
  584.   Vec2  pos = body.position;
  585.   Vec2  siz = 0.5f * body.width;
  586.  
  587.   Vec2 p0 = pos + rot * Vec2(-siz.x, -siz.y);
  588.   Vec2 p1 = pos + rot * Vec2( siz.x, -siz.y);
  589.   Vec2 p2 = pos + rot * Vec2( siz.x,  siz.y);
  590.   Vec2 p3 = pos + rot * Vec2(-siz.x,  siz.y);
  591.  
  592.   colors::ABGR color = 0xFFFF00;
  593.  
  594.   color.a = 255;
  595.   DRAWLINE_VEC2(p0, p1, color);
  596.   DRAWLINE_VEC2(p1, p2, color);
  597.   DRAWLINE_VEC2(p2, p3, color);
  598.   DRAWLINE_VEC2(p3, p0, color);
  599.  
  600. }
  601.  
  602. #undef V2I
  603.  
  604.  
  605.  
  606.  
  607.  
  608. #include <stdarg.h>
  609.  
  610. #define BOXED_TEXT 1
  611.  
  612. //draws whatever is currently in the font's format buffer
  613. void drawCursorText(s32 x, s32 y, const char* fmt, ...){
  614.   bool npm0 = font0->neg_pos_margin;
  615.   bool npm1 = font1->neg_pos_margin;
  616.   font0->neg_pos_margin = false;
  617.   font1->neg_pos_margin = false;
  618.  
  619.  
  620.  
  621.   u8* buffer0     = font0->getFmtBuffer();
  622.   u32 buffer0_len = font0->getFmtBufferLen();
  623. //u8* buffer1     = font1->getFmtBuffer();
  624. //u32 buffer1_len = font1->getFmtBufferLen();
  625.  
  626.   va_list va;
  627.   va_start(va, fmt);
  628.   vsnPrintf((char*)buffer0, buffer0_len, fmt, va);
  629.   va_end(va);
  630.   font1->copy((char*)buffer0);
  631.  
  632.  
  633.  
  634. #if BOXED_TEXT == 1
  635.   shape::rect txtRect = font0->getRect(*canvas, x, y, true);
  636.  
  637.   if(!font0->box_padded) ++txtRect.w, ++txtRect.h;
  638.   if(txtRect.x >= txtRect.w) txtRect.x -= txtRect.w;
  639.   if(txtRect.y >= txtRect.h) txtRect.y -= txtRect.h;
  640.  
  641.   shape::point txtShift = font0->drawBoxNoText(*canvas, txtRect.x, txtRect.y,
  642.                                                         txtRect.w, txtRect.h);
  643.  
  644.   txtRect.x += txtShift.x + 1;
  645.   txtRect.y += txtShift.y + 1;
  646.   font0->draw(*canvas, txtRect.x, txtRect.y);
  647.  
  648.   font1->draw(*canvas, txtRect.x-1, txtRect.y-1);
  649.  
  650.  
  651. #else
  652.   shape::rect txtRect = font0->getRect(*canvas, x, y, false);
  653.  
  654.   if(txtRect.x >= (CNV_X/2)) txtRect.x -= txtRect.w;
  655.   if(txtRect.y >= txtRect.h) txtRect.y -= txtRect.h;
  656.  
  657.   font0->draw(*canvas, txtRect.x+1, txtRect.y+1);
  658.  
  659.   font1->draw(*canvas, txtRect.x, txtRect.y);
  660.  
  661.  
  662. #endif
  663.  
  664.  
  665.  
  666.   font0->neg_pos_margin = npm0;
  667.   font1->neg_pos_margin = npm1;
  668.  
  669. }
  670.  
  671.  
  672.  
  673.  
  674.  
  675. f64 simSpeed = 1.0;
  676.  
  677. f32 timeStep = 1.0f / (1000.0f/16);
  678.  
  679.  
  680.  
  681. void drawSpeedBar(f32 speed){
  682.   const static shape::rect bar(0,0, 16,CNV_Y);
  683.   const shape::rect slider(0, (2.0-speed*2.0)*(CNV_Y-1), 17,1);
  684.   canvas->fillRects(0xFF202020, &bar, 1);
  685.   canvas->fillRects(0xFFFFFFFF, &slider, 1);
  686.  
  687. }
  688.  
  689.  
  690.  
  691. void setSimSpeed(f64 newSpeed){
  692.   simSpeed = 0.5 + CLAMP(newSpeed,0.0,1.0)/2;
  693.   music->setSpeed(simSpeed);
  694.   timeStep = (simSpeed) / (1000.0f/16);
  695. }
  696. /******************************************************************************/
  697. /******************************************************************************/
  698. //"2024-11-21\src\kit_sdl2\kit_AudioCallbackWrapper.cpp":
  699. #include "_kit_common.hpp"
  700.  
  701.  
  702. namespace kit {
  703.  
  704.  
  705.  
  706.  
  707.  
  708. //workaround for having _AudioCallbackWrapper pause the device,
  709.  //without having to call SDL_PauseAudioDevice inside the callback itself
  710. //(this may potentially cut off playback before the current buffer plays, idk)
  711. static int _AudioPauseThread(void* data){
  712.   _AudioDeviceOpaque* opq = (_AudioDeviceOpaque*)data;
  713.   SDL_AudioDeviceID devID = opq->info_p->deviceID;
  714.  
  715.   //wait for the callback to exit before actually pausing
  716.   SDL_LockAudioDevice(devID);
  717.  
  718.   SDL_PauseAudioDevice(devID, 1);
  719.   opq->fadeOut = false;
  720.   opq->playing = false;
  721.  
  722.   SDL_UnlockAudioDevice(devID);
  723.  
  724.   return 0;
  725.  
  726. }
  727.  
  728. static inline bool startPauseThread(_AudioDeviceOpaque* opq){
  729.   SDL_Thread* pauseThread = SDL_CreateThread(_AudioPauseThread, "kADPause", opq);
  730.  
  731.   if(pauseThread){
  732.     SDL_DetachThread(pauseThread);
  733.  
  734.   } else {
  735.     opq->fadeDelay = KIT_U32_MAX;
  736.     opq->fadeOut   = false;
  737.     opq->playing   = false;
  738.  
  739.   }
  740.  
  741.   return pauseThread != nullptr;
  742.  
  743. }
  744.  
  745.  
  746.  
  747.  
  748.  
  749. //not inlined
  750.  //(also, stream_len is in total samples, not sample frames)
  751. static void apply_fade(_AudioDeviceOpaque* opq,
  752.                        f32* stream, u32 stream_len)
  753. {
  754.   f32 fadeDelta   = opq->fadeDelta;
  755.   f32 fadeVolume  = opq->fadeVolume;
  756.   u32 fadeDelay   = opq->fadeDelay;
  757.   u8  numChannels = opq->info_p->numChannels;
  758.  
  759.   u32 smp = 0; //this index is shared, as the loops can jump to others at will
  760.  
  761.  
  762.  
  763.   //FADING OUT
  764.   if(opq->fadeOut){
  765.     _FadeOut:;
  766.     for(; smp<stream_len; ++smp){
  767.       //if audio device starts fading in mid-fadeout, jump to the fade-in loop
  768.       if(!opq->fadeOut) goto _FadeIn;
  769.  
  770.       stream[smp] *= fadeVolume;
  771.  
  772.       //only change fadeVolume every numChannels samples,
  773.        //so that there's 1 fadeVolume state per sample frame
  774.       if(!((smp+1)%numChannels)) fadeVolume -= fadeDelta;
  775.  
  776.       //to enforce a minimum volume
  777.       if(fadeVolume < 0.0f) fadeVolume = 0.0f;
  778.  
  779.     }
  780.  
  781.     //trigger pause thread if fade out is complete
  782.     if(fadeVolume == 0.0f)
  783.       startPauseThread(opq);
  784.  
  785.  
  786.  
  787.   //FADING IN
  788.   } else if(fadeVolume < 1.0f){
  789.     //let stream warm up before fading in (if fadeDelay != 0)
  790.     for(; (fadeDelay)&&(smp<stream_len); ++smp){
  791.       stream[smp] = 0.0f;
  792.       if(!((smp+1)%numChannels)) --fadeDelay;
  793.  
  794.     }
  795.  
  796.  
  797.     _FadeIn:;
  798.     for(; smp<stream_len; ++smp){
  799.       if(opq->fadeOut) goto _FadeOut;
  800.  
  801.       stream[smp] *= fadeVolume;
  802.  
  803.       if(!((smp+1)%numChannels)) fadeVolume += fadeDelta;
  804.  
  805.       if(fadeVolume > 1.0f) fadeVolume = 1.0f;
  806.  
  807.     }
  808.  
  809.  
  810.   }
  811.  
  812.  
  813.  
  814.   //update relevant data in opq
  815.   opq->fadeVolume = fadeVolume;
  816.   opq->fadeDelay  = fadeDelay;
  817.  
  818. }
  819.  
  820.  
  821.  
  822.  
  823.  
  824. //for multiplying the by inverse of stuff
  825. #define INV_S8  0.0078125000000000000000000000000f // = 1.0f/0x80
  826. #define INV_S16 0.0000305175781250000000000000000f // = 1.0f/0x8000
  827. #define INV_S32 0.0000000004656612873077392578125f // = 1.0f/0x80000000
  828.  
  829. #define fmtconvloop for(u32 i=0; i<length; ++i) to
  830.  
  831.  
  832. //used for output devices
  833.  //(length should be in samples, not sample frames)
  834. static inline void fmt_to_f32(Mono_smp from, f32* to,
  835.                               u32 length, u16 from_fmt)
  836. {
  837.   switch(from_fmt){
  838.     case SMPFMT_U8 : fmtconvloop[i] = ((f32)from.u8_ [i]-0x80  )*INV_S8 ; break;
  839.     case SMPFMT_S8 : fmtconvloop[i] = ((f32)from.s8_ [i]       )*INV_S8 ; break;
  840.     case SMPFMT_U16: fmtconvloop[i] = ((f32)from.u16_[i]-0x8000)*INV_S16; break;
  841.     case SMPFMT_S16: fmtconvloop[i] = ((f32)from.s16_[i]       )*INV_S16; break;
  842.     case SMPFMT_S32: fmtconvloop[i] = ((f32)from.s32_[i]       )*INV_S32; break;
  843.     case SMPFMT_F32: memory::copy(to, from.f32_, length*sizeof(f32));     break;
  844.     //(f32 samples aren't hard-clipped! make sure to keep them -1.0f -> 1.0f)
  845.   }
  846. }
  847.  
  848.  
  849. //used for input devices
  850.  //(again, length should be in samples, not sample frames)
  851. static inline void f32_to_fmt(f32* from, Mono_smp to,
  852.                               u32 length, u16 to_fmt)
  853. {
  854.   //(input f32 samples aren't hard-clipped here either!)
  855.   switch(to_fmt){
  856.     case SMPFMT_U8 : fmtconvloop.u8_ [i] = (u8 )(from[i]*0x7F + 0x80    ); break;
  857.     case SMPFMT_S8 : fmtconvloop.s8_ [i] = (s8 )(from[i]*0x7F           ); break;
  858.     case SMPFMT_U16: fmtconvloop.u16_[i] = (u16)(from[i]*0x7FFF + 0x8000); break;
  859.     case SMPFMT_S16: fmtconvloop.s16_[i] = (s16)(from[i]*0x7FFF         ); break;
  860.     case SMPFMT_S32: fmtconvloop.s32_[i] = (s32)(from[i]*0x7FFFFFFF     ); break;
  861.     case SMPFMT_F32: memory::copy(to.f32_, from, length*sizeof(f32));
  862.   }
  863. }
  864.  
  865.  
  866.  
  867.  
  868.  
  869. void _AudioCallbackWrapper(void* userdata, u8* _stream, int len){
  870.   u64 timeStartTicks = SDL_GetPerformanceCounter();
  871.   u64 timeStartMS    = SDL_GetTicks64();
  872.  
  873.  
  874.   AudioDevice* device = (AudioDevice*)userdata;
  875.   f32*         stream = (f32*)_stream;
  876.   u32     stream_size = len;
  877.   u32      stream_len = len / sizeof(f32); //samples, not sample frames
  878.  
  879.  
  880.   _AudioDeviceOpaque* opq = (_AudioDeviceOpaque*)KIT_GET_CLASS_OPAQUE(device);
  881.   AudioDeviceInfo* info_p = opq->info_p;
  882.   info_p->timeStartTicks  = timeStartTicks;
  883.   info_p->timeStartMS     = timeStartMS;
  884.  
  885.  
  886.  
  887.   //if pause thread failed to start,
  888.    //simply write zeros to stream (if !isInput) and exit
  889.   if(opq->fadeDelay == KIT_U32_MAX){
  890.     if(!info_p->isInput) memory::set(stream, 0, stream_size);
  891.     return;
  892.  
  893.   }
  894.  
  895.  
  896.  
  897.   s32 callbackReturn = -1; //'abort playback' by default
  898.  
  899.   if(!info_p->isInput){ //buffer will be written to
  900.     //(memset 0 the _user_ buffer, not the sdl stream)
  901.     if(info_p->zeroBuffer) memory::set(opq->buffer, 0, opq->buffer_size);
  902.  
  903.     try { //attempt to call user callback
  904.       if(info_p->callback)
  905.         callbackReturn = info_p->callback(opq->buffer, *info_p);
  906.  
  907.     } catch(const char* errortext){
  908.       kit_LogError("IN AUDIO CALLBACK: \"%s\"", errortext);
  909.       freeThreadErrors();
  910.       //set back to 0, since used callback failed
  911.       memory::set(stream, 0, stream_size);
  912.  
  913.     }
  914.  
  915.     //copy buffer to stream (X to f32)
  916.     fmt_to_f32(opq->buffer, stream, stream_len, info_p->sampleFormat);
  917.  
  918.     //apply fade to output stream
  919.     apply_fade(opq, stream, stream_len);
  920.  
  921.  
  922.   } else { //buffer will be read from
  923.     //apply fade to input stream
  924.     apply_fade(opq, stream, stream_len);
  925.  
  926.     //copy stream to buffer (f32 to X)
  927.     f32_to_fmt(stream, opq->buffer, stream_len, info_p->sampleFormat);
  928.  
  929.     try { //attempt to call user callback
  930.       if(info_p->callback)
  931.         callbackReturn = info_p->callback(opq->buffer, *info_p);
  932.  
  933.     } catch(const char* errortext){
  934.       kit_LogError("IN AUDIO CALLBACK: \"%s\"", errortext);
  935.       freeThreadErrors();
  936.  
  937.     }
  938.  
  939.   }
  940.  
  941.  
  942.  
  943.   opq->fadeOut = callbackReturn != 0;
  944.  
  945.   if(callbackReturn < 0) startPauseThread(opq);
  946.  
  947. }
  948.  
  949.  
  950.  
  951.  
  952.  
  953. }; /* namespace kit */
  954. /******************************************************************************/
  955. /******************************************************************************/
  956. //"2024-11-21\src\kit_sdl2\kit_AudioData.cpp":
  957. #include "_kit_common.hpp"
  958.  
  959. #define ADATA_IS_INVALID (!_valid)
  960.  
  961.  
  962. namespace kit {
  963.  
  964. //#define DONT_USE_ALLOCSIMD //for debugging
  965.  
  966.  
  967.  
  968.  
  969.  
  970. void AudioDataHeader::printHeader(const char* name){
  971.   //(SDL_Log adds \n automatically, but ignores the last \n if added manually)
  972.   if(name != nullptr) kit_LogInfo("%s = {\n", name);
  973.   else                kit_LogInfo("%p = {\n", this);
  974. #ifdef _DEBUG
  975.   kit_LogInfo("  ->magic         = \"%.4s\"; (0x%08X)\n", (char*)&magic, magic);
  976.  
  977.  
  978.   const char* fmt_txt = "?";
  979.   switch(format){
  980.     case SMPFMT_U8    : fmt_txt = "U8";     break;
  981.     case SMPFMT_S8    : fmt_txt = "S8";     break;
  982.  
  983.     case SMPFMT_U16LSB: fmt_txt = "U16LSB"; break;
  984.     case SMPFMT_S16LSB: fmt_txt = "S16LSB"; break;
  985.     case SMPFMT_S24LSB: fmt_txt = "S24LSB"; break;
  986.     case SMPFMT_S32LSB: fmt_txt = "S32LSB"; break;
  987.     case SMPFMT_F32LSB: fmt_txt = "F32LSB"; break;
  988.     case SMPFMT_F64LSB: fmt_txt = "F64LSB"; break;
  989.  
  990.     case SMPFMT_U16MSB: fmt_txt = "U16MSB"; break;
  991.     case SMPFMT_S16MSB: fmt_txt = "S16MSB"; break;
  992.     case SMPFMT_S24MSB: fmt_txt = "S24MSB"; break;
  993.     case SMPFMT_S32MSB: fmt_txt = "S32MSB"; break;
  994.     case SMPFMT_F32MSB: fmt_txt = "F32MSB"; break;
  995.     case SMPFMT_F64MSB: fmt_txt = "F64MSB"; break;
  996.  
  997.     default           : fmt_txt = "UNKNOWN";
  998.   }
  999.  
  1000.   kit_LogInfo("  ->format        = SMPFMT_%s; (0x%04X)\n", fmt_txt, format);
  1001.  
  1002.  
  1003.   kit_LogInfo("  ->headerSize    = %u;\n", headerSize);
  1004.   kit_LogInfoS("  ->dataSize      = %llu;\n", dataSize);
  1005.   kit_LogInfoS("  ->loopStart     = %llu;\n", loopStart);
  1006.   kit_LogInfoS("  ->loopEnd       = %llu;\n", loopEnd);
  1007.   kit_LogInfoS("  ->numSamples    = %llu;\n", numSamples);
  1008.   kit_LogInfo("  ->sampleRate    = %u;\n", sampleRate);
  1009.   kit_LogInfo("  ->bitRate       = %u;\n", bitRate);
  1010.   kit_LogInfo("  ->loopCount     = %u;\n", loopCount);
  1011.   kit_LogInfo("  ->channels      = %u;\n", channels);
  1012.   kit_LogInfo("  ->_reserved     = %u;\n", _reserved);
  1013.   kit_LogInfo("  ->fmt_version   = %u;\n", fmt_version);
  1014.   kit_LogInfo("  ->mode          = %u;\n", mode);
  1015.   kit_LogInfo("  ->metadata_type = %u;\n", metadata_type);
  1016.   kit_LogInfo("  ->samples       = %p;\n", samples);
  1017.   kit_LogInfo("  ->userdata      = %p;\n", userdata);
  1018.   kit_LogInfo("};");
  1019.  
  1020.  
  1021. #else
  1022.   kit_LogInfo("  (AudioDataHeader::printHeader() is not available in release build!)\nINFO: }\n");
  1023.  
  1024.  
  1025. #endif /* _DEBUG */
  1026. }
  1027.  
  1028.  
  1029.  
  1030.  
  1031.  
  1032. void AudioData::_allocate_hdr(u16 headerSize, u64 dataSize,
  1033.                               const char* funcName)
  1034. {
  1035.   if(_valid) return;
  1036.   _type = KIT_CLASSTYPE_AUDIODATA;
  1037.  
  1038.   if(funcName == nullptr)
  1039.     funcName = "AudioData::AudioData(raw allocation)";
  1040.  
  1041.   if(headerSize < sizeof(AudioDataHeader))
  1042.     THROW_ERRORF("%s: headerSize < sizeof(AudioDataHeader)", funcName);
  1043.  
  1044.  
  1045. #ifndef DONT_USE_ALLOCSIMD
  1046.   hdr = (AudioDataHeader*)memory::allocSIMD(headerSize+dataSize);
  1047. #else
  1048.   hdr = (AudioDataHeader*)memory::alloc(headerSize+dataSize);
  1049. #endif /* DONT_USE_ALLOCSIMD */
  1050.   if(hdr == nullptr)
  1051.     THROW_ERRORF("%s: failed to allocate memory", funcName);
  1052.  
  1053.   memory::set(hdr, 0, headerSize+dataSize);
  1054.  
  1055.   hdr->headerSize = headerSize;
  1056.   hdr->dataSize   = dataSize;
  1057.   hdr->samples    = (u8*)hdr + hdr->headerSize;
  1058.  
  1059.  
  1060.  
  1061.   _valid = true;
  1062.   _constructing = false;
  1063.  
  1064. }
  1065.  
  1066.  
  1067.  
  1068.  
  1069.  
  1070. AudioData::AudioData(AudioSampleFormatEnum format,
  1071.                      u64 numSamples, u16 channels, u32 sampleRate)
  1072. {
  1073.   if(_valid) return;
  1074.   _type = KIT_CLASSTYPE_AUDIODATA;
  1075.  
  1076.   const u64 dataSize = KIT_AUDIO_BYTESIZE(format)*numSamples*channels;
  1077.  
  1078.   _allocate_hdr(sizeof(AudioDataHeader), dataSize,
  1079.                 "AudioData::AudioData(formatted allocation)");
  1080.  
  1081.  
  1082.  
  1083.   hdr->magic         = KIT_MAGIC_KPM;
  1084.   hdr->format        = format;
  1085.   hdr->headerSize    = sizeof(AudioDataHeader);
  1086.   hdr->dataSize      = dataSize;
  1087.  
  1088.   hdr->loopStart     = 0;
  1089.   hdr->loopEnd       = numSamples;
  1090.  
  1091.   hdr->numSamples    = numSamples;
  1092.   hdr->sampleRate    = sampleRate;
  1093.   hdr->bitRate       = KIT_AUDIO_BITSIZE(format)*sampleRate*channels;
  1094.  
  1095.   hdr->loopCount     = 0;
  1096.   hdr->channels      = channels;
  1097.   hdr->_reserved     = 0;
  1098.   hdr->fmt_version   = 1; //1 indicates the version kit_sdl2 uses
  1099.   hdr->mode          = 0; //PCM or float data
  1100.   hdr->metadata_type = 0; //no metadata
  1101.  
  1102.   hdr->samples       = (u8*)hdr + hdr->headerSize;
  1103.   hdr->userdata      = nullptr;
  1104.  
  1105.  
  1106.  
  1107.   _valid = true;
  1108.   _constructing = false;
  1109.  
  1110. }
  1111.  
  1112.  
  1113.  
  1114.  
  1115.  
  1116. #define KIT_MAGIC_KPCM 0x4D43506B //.kpm's old file signature
  1117.  
  1118. void AudioData::_construct_file(const char* filePath,
  1119.                                 AudioDataLoaderCallback callback,
  1120.                                 const char* funcName)
  1121. {
  1122.   if(_valid) return;
  1123.   _type = KIT_CLASSTYPE_AUDIODATA;
  1124.  
  1125.   if(funcName == nullptr)
  1126.     funcName = "AudioData::AudioData(specific file format)";
  1127.  
  1128.   if(filePath == nullptr)
  1129.       THROW_ERRORF("%s: filePath = nullptr", funcName);
  1130.  
  1131.   if(!fileio::exists(filePath))
  1132.     THROW_ERRORF("%s: \"%s\" doesn't exist", funcName, filePath);
  1133.  
  1134.  
  1135.  
  1136.   AudioDataHeader* _hdr;
  1137.  
  1138.   if(callback == nullptr){ //load .kpm file
  1139.     size_t fileSize;
  1140.     _hdr = (AudioDataHeader*)fileio::readAll(filePath, &fileSize);
  1141.     //(assumes that _hdr != nullptr after this point)
  1142.  
  1143.     //check for the current and old version of .kpm's file signature
  1144.      //(but only if the fileSize is enough for a u32)
  1145.     if(fileSize >= sizeof(u32)  &&
  1146.        (_hdr->magic != KIT_MAGIC_KPM  &&  _hdr->magic != KIT_MAGIC_KPCM))
  1147.     {
  1148.       memory::free(&_hdr);
  1149.       THROW_ERRORF("%s: AudioDataLoadKPM used, but file is not a .kpm", funcName);
  1150.     }
  1151.  
  1152.     if(fileSize < sizeof(AudioDataHeader)){
  1153.       memory::free(&_hdr);
  1154.       THROW_ERRORF("%s: size of .kpm < sizeof(AudioDataHeader)", funcName);
  1155.     }
  1156.  
  1157. /*
  1158.     if(_hdr->magic != KIT_MAGIC_KPM) throw "magic != KIT_MAGIC_KPM";
  1159.  
  1160.     if(!isFormatValid(_hdr->format)) throw "format is invalid";
  1161.  
  1162.     if(_hdr->headerSize < sizeof(AudioDataHeader)) throw "headerSize < sizeof(AudioDataHeader)";
  1163.  
  1164.     if(_hdr->dataSize != (fileSize-_hdr->headerSize)) throw "dataSize is invalid";
  1165.  
  1166.     //(channels are checked before numSamples to prevent divide-by-zero exceptions)
  1167.     if(_hdr->channels!=1 && _hdr->channels!=2) throw "audio is neither mono nor stereo";
  1168.  
  1169.     //(numSamples is checked before loopStart/loopEnd, as their checks rely upon numSamples)
  1170.     if(_hdr->numSamples != (_hdr->dataSize/KIT_ASTREAM_FMT_BYTESIZE(_hdr->format))/_hdr->channels) throw "numSamples is invalid";
  1171.  
  1172.     if(_hdr->loopStart >= _hdr->numSamples) throw "loopStart >= numSamples";
  1173.  
  1174.     if(_hdr->loopEnd > _hdr->numSamples) throw "loopEnd > numSamples";
  1175.  
  1176.     if(_hdr->sampleRate < 1000) throw "sampleRate < 1000";
  1177.  
  1178.     if(_hdr->bitRate != _hdr->sampleRate*_hdr->channels*KIT_ASTREAM_FMT_BITSIZE(_hdr->format)) throw "bitRate is invalid";
  1179.  
  1180.     if(_hdr->bitRemainder != 0) throw "bitRemainder != 0";
  1181.  
  1182.     if(_hdr->mode != 0) throw "only mode 0 kPCM files are currently supported";
  1183. */
  1184.  
  1185.     //the only difference between 0 and 1 is that bitsPerSample is offset by -1
  1186.     if(_hdr->fmt_version == 0) ++_hdr->format;
  1187.  
  1188.     _hdr->magic       = KIT_MAGIC_KPM; //if it was previously kPCM, now it is kPxM
  1189.     _hdr->fmt_version = 1;              //
  1190.  
  1191.  
  1192.   } else {
  1193.     _hdr = callback(filePath);
  1194.     if(_hdr == nullptr)
  1195.       THROW_ERRORF("%s: callback returned as nullptr", funcName);
  1196.  
  1197.  
  1198.   }
  1199.  
  1200.  
  1201.  
  1202.   size_t totalSize = _hdr->headerSize + _hdr->dataSize;
  1203.  
  1204. #ifndef DONT_USE_ALLOCSIMD
  1205.   hdr = (AudioDataHeader*)memory::allocSIMD(totalSize);
  1206. #else
  1207.   hdr = (AudioDataHeader*)memory::alloc(totalSize);
  1208. #endif /* DONT_USE_ALLOCSIMD */
  1209.   if(hdr == nullptr){
  1210.     memory::free(&_hdr);
  1211.     THROW_ERRORF("%s: failed to allocate memory", funcName);
  1212.   }
  1213.  
  1214.   memory::copy(hdr, _hdr, totalSize);
  1215.   memory::free(&_hdr);
  1216.  
  1217.   hdr->samples = (u8*)hdr + hdr->headerSize;
  1218.  
  1219.   _valid = true;
  1220.   _constructing = false;
  1221.  
  1222. }
  1223.  
  1224.  
  1225.  
  1226.  
  1227.  
  1228. //(the last constructor can be found in "kit_AudioData_LoadAllTypes.cpp")
  1229.  
  1230.  
  1231.  
  1232.  
  1233.  
  1234. AudioData::~AudioData(){
  1235.   if(!_valid) return;
  1236.   _valid = false;
  1237.  
  1238. #ifndef DONT_USE_ALLOCSIMD
  1239.   memory::freeSIMD(&hdr);
  1240. #else
  1241.   memory::free(&hdr);
  1242. #endif /* DONT_USE_ALLOCSIMD */
  1243.  
  1244. }
  1245.  
  1246.  
  1247.  
  1248.  
  1249.  
  1250. void AudioData::saveAudio(const char* filePath,
  1251.                           AudioDataSaverCallback callback)
  1252. {
  1253.   if(ADATA_IS_INVALID)
  1254.     THROW_ERROR("AudioData::saveAudio(): invalid AudioData");
  1255.  
  1256.   if(filePath == nullptr)
  1257.     THROW_ERROR("AudioData::saveAudio(): filePath = nullptr");
  1258.  
  1259.  
  1260.  
  1261.   if(callback == nullptr){ //save .kpm
  1262.     try {
  1263.       fileio::writeAll(filePath, hdr, hdr->headerSize+hdr->dataSize);
  1264.  
  1265.     } catch(const char* errorText){
  1266.       freeThreadErrors();
  1267.       THROW_ERRORF("AudioData::saveAudio(): \"%s\"", SDL_GetError());
  1268.  
  1269.     }
  1270.  
  1271.  
  1272.   } else {
  1273.     callback(filePath, *hdr);
  1274.  
  1275.  
  1276.   }
  1277.  
  1278. }
  1279.  
  1280.  
  1281.  
  1282.  
  1283.  
  1284. static bool _fmt_is_valid(u16 fmt){
  1285.   switch(fmt){
  1286.     case SMPFMT_U8 : SDL_FALLTHROUGH;
  1287.     case SMPFMT_S8 : SDL_FALLTHROUGH;
  1288.     case SMPFMT_U16: SDL_FALLTHROUGH;
  1289.     case SMPFMT_S16: SDL_FALLTHROUGH;
  1290.     case SMPFMT_S24: SDL_FALLTHROUGH;
  1291.     case SMPFMT_S32: SDL_FALLTHROUGH;
  1292.     case SMPFMT_F32: SDL_FALLTHROUGH;
  1293.     case SMPFMT_F64: return  true; //lol
  1294.     default:         return false;
  1295.   }
  1296. }
  1297.  
  1298. // = 2^(bits-1)
  1299. #define ABS_S8_MIN         (128) //ABS_Sx_MIN is a horrible name for this,
  1300. #define ABS_S16_MIN      (32768)  //but it's the name i chose within 10 seconds
  1301. #define ABS_S24_MIN    (8388608)  //(so i may or may not change it, idk)
  1302. #define ABS_S32_MIN (2147483648)
  1303.  
  1304. struct _s24 { u8 a, b, c; } //mostly for memory alignment purposes
  1305. __attribute__((packed)); //(explicitly pack just in case; probably unnecessary)
  1306.  
  1307. union _s24u {
  1308.   _s24 v;
  1309.    s32 n : 24;
  1310.   inline _s24u(_s24 _v) : v(_v) {}
  1311.   inline _s24u(s32  _n) : n(_n&KIT_S24_MAX) {}
  1312. };
  1313.  
  1314. static inline f64 frm_s24(_s24 x){ return (f64)_s24u(x).n/ABS_S24_MIN; }
  1315.  
  1316. static inline _s24 to_s24(f64 x){ return _s24u((s32)(x*KIT_S24_MAX)).v; }
  1317.  
  1318.  
  1319.  
  1320. void AudioData::convertFormat(AudioSampleFormatEnum format){
  1321.   if(ADATA_IS_INVALID)
  1322.     THROW_ERROR("AudioData::convertFormat(): invalid AudioData");
  1323.  
  1324.   if(hdr->format == format) return; //no need to convert anything; exit early
  1325.  
  1326.   if(!_fmt_is_valid(hdr->format))
  1327.     THROW_ERROR("AudioData::convertFormat(): unrecognized source format");
  1328.  
  1329.   if(!_fmt_is_valid(format))
  1330.     THROW_ERROR("AudioData::convertFormat(): unrecognized destination format");
  1331.  
  1332.  
  1333.  
  1334.   u64 totalSamples = hdr->numSamples*hdr->channels;
  1335.  
  1336.   u64    dataSize = KIT_AUDIO_BYTESIZE(format)*totalSamples;
  1337.   u32     bitRate = KIT_AUDIO_BITSIZE(format)*hdr->sampleRate*hdr->channels;
  1338.  
  1339.  
  1340.  
  1341.   try {
  1342.     memory::Wrapper temp_samples(totalSamples*sizeof(f64));
  1343.     f64*  tmp = (f64*)temp_samples.ptr;
  1344.     void* smp = hdr->samples;
  1345.  
  1346.     #define FOR_TS_BRK(x) for(u64 i=0; i<totalSamples; ++i){ x; } break
  1347.     #define SMPCAST(_type) (  ((_type*)smp)[i]  )
  1348.     #define FRM_CONV(_type, _scaling_factor, _modifier) \
  1349.       (  ((f64)SMPCAST(_type) _modifier) _scaling_factor  )
  1350.  
  1351.     //convert all source samples to 64-bit floats
  1352.     switch(hdr->format){
  1353.       case SMPFMT_U8 : FOR_TS_BRK(  tmp[i] = FRM_CONV(u8 , /ABS_S8_MIN ,   -128)  );
  1354.       case SMPFMT_S8 : FOR_TS_BRK(  tmp[i] = FRM_CONV(s8 , /ABS_S8_MIN ,       )  );
  1355.       case SMPFMT_U16: FOR_TS_BRK(  tmp[i] = FRM_CONV(u16, /ABS_S16_MIN, -32768)  );
  1356.       case SMPFMT_S16: FOR_TS_BRK(  tmp[i] = FRM_CONV(s16, /ABS_S16_MIN,       )  );
  1357.       case SMPFMT_S24: FOR_TS_BRK(  tmp[i] = frm_s24(SMPCAST(_s24))               );
  1358.       case SMPFMT_S32: FOR_TS_BRK(  tmp[i] = FRM_CONV(s32, /ABS_S32_MIN,       )  );
  1359.       case SMPFMT_F32: FOR_TS_BRK(  tmp[i] = FRM_CONV(f32,             ,       )  );
  1360.       case SMPFMT_F64: memory::copy(tmp, smp, hdr->dataSize); break;
  1361.     }
  1362.  
  1363.  
  1364.  
  1365.     //resize header to match destination format's dataSize
  1366. #ifndef DONT_USE_ALLOCSIMD
  1367.     if(!memory::reallocSIMD(&hdr, hdr->headerSize+dataSize))
  1368. #else
  1369.     if(!memory::realloc(&hdr, hdr->headerSize+dataSize))
  1370. #endif /* DONT_USE_ALLOCSIMD */
  1371.       THROW_ERROR("?"); //what is thrown doesn't matter as long as it's const char*
  1372.  
  1373.     //update relevant header values
  1374.     hdr->format       = format;
  1375.     hdr->dataSize     = dataSize;
  1376.     hdr->bitRate      = bitRate;
  1377.     hdr->samples      = (u8*)hdr + hdr->headerSize;
  1378.  
  1379.     smp = hdr->samples;
  1380.  
  1381.  
  1382.  
  1383.     #define TO_CONV(_type, _scl_fct, _mod) (  (_type)( tmp[i] _scl_fct _mod )  )
  1384.  
  1385.     //convert the f64 samples to the desired format
  1386.     switch(hdr->format){ //(hdr->format = format now)
  1387.       case SMPFMT_U8 : FOR_TS_BRK(  SMPCAST( u8 ) = TO_CONV(u8 , *KIT_S8_MAX ,   +128)  );
  1388.       case SMPFMT_S8 : FOR_TS_BRK(  SMPCAST( s8 ) = TO_CONV(s8 , *KIT_S8_MAX ,       )  );
  1389.       case SMPFMT_U16: FOR_TS_BRK(  SMPCAST( u16) = TO_CONV(u16, *KIT_S16_MAX, +32768)  );
  1390.       case SMPFMT_S16: FOR_TS_BRK(  SMPCAST( s16) = TO_CONV(s16, *KIT_S16_MAX,       )  );
  1391.       case SMPFMT_S24: FOR_TS_BRK(  SMPCAST(_s24) = to_s24(tmp[i])                      );
  1392.       case SMPFMT_S32: FOR_TS_BRK(  SMPCAST( s32) = TO_CONV(s32, *KIT_S32_MAX,       )  );
  1393.       case SMPFMT_F32: FOR_TS_BRK(  SMPCAST( f32) = TO_CONV(f32,             ,       )  );
  1394.       case SMPFMT_F64: memory::copy(smp, tmp, hdr->dataSize); break;
  1395.     }
  1396.  
  1397.  
  1398.  
  1399.   } catch(const char* errorText){
  1400.     //the only 2 errors that should occur in that try block are failures
  1401.      //to allocate heap memory, so i'm just changing the function
  1402.      //name to indicate it occurred in this constructor specifically
  1403.     freeThreadErrors();
  1404.     THROW_ERROR("AudioData::convertFormat(): failed to allocate memory");
  1405.  
  1406.  
  1407.  
  1408.   }
  1409.  
  1410. }
  1411.  
  1412.  
  1413.  
  1414.  
  1415.  
  1416. }; /* namespace kit */
  1417.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement