Advertisement
Kitomas

code for 2025-06-26 (0/4)

Jun 27th, 2025
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 36.24 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"opengl_metaballs_2025-06-26\src\user_audio.cpp":
  4. #include <public_stuff.hpp>
  5.  
  6.  
  7.  
  8. //SoundEngine sfx;
  9.  
  10.  
  11.  
  12. void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp)
  13. {
  14.   // This plays a normal 440Hz sine wave (commented!)
  15. /*
  16.   // The static keyword here just makes the variable persistent, in that
  17.   // its state is preserved between each call to user_audio
  18.   static u64 t = 0; // u[nsigned] 64-bit integer
  19.  
  20.  
  21.   for(u32 i=0; i<len; ++i){ //For every sample to be filled in the audio buffer
  22.  
  23.     // t is incremented every sample, and is divided by the sample rate to
  24.     // basically make a percentage of how far it is through a second.
  25.     // This is then multiplied by 2pi, which effectively makes it oscillate
  26.     // once every second if you fed cycle into sin directly
  27.     f32 cycle = ((f32)(t++)/sampleRate) * M_2PIf;
  28.  
  29.     // cycle is multiplied by 440 so that it oscillates 440 times a second
  30.     // instead of just once.
  31.     // (Also, sinF is just sin but it uses 32-bit f[loats] instead of 64-bit,
  32.     //  and unlike the actual sinf, this is a custom implementation)
  33.     f32 smp = sinF(cycle*440); // This ofc returns a number from -1.0 to 1.0
  34.  
  35.     // 20% volume
  36.     smp *= 0.20f;
  37.  
  38.     // Since the audio stream is stereo, the sample is applied to
  39.     // both 'ears' so-to-speak.
  40.     stream[i].l = smp;
  41.     stream[i].r = smp;
  42.  
  43.   }
  44.  
  45.  
  46.   // Unnecessary, since a u64 is so big, but this makes it so that
  47.   // even if it was a smaller integer size, it will never overflow.
  48.   // (The % operator does a division, but gets the remainder instead)
  49.   t %= sampleRate;
  50. */
  51.  
  52.   /*
  53.   sfx.sampleRate = sample_rate;
  54.   if(!sfx.mixTracks(stream, len, timeStamp)){
  55.     _printf("ERROR: sfx.mixTracks failed!\n");
  56.   }
  57.   */
  58.  
  59. }
  60. /******************************************************************************/
  61. /******************************************************************************/
  62. //"opengl_metaballs_2025-06-26\src\user_main.cpp":
  63. #include <win32/opengl.hpp>
  64.  
  65. #include <public_stuff.hpp>
  66.  
  67. char infoLogPostfix[512-64];
  68. char infoLogBuffer[512];
  69.  
  70. #define MAX_SPHERE_COUNT 256*256
  71. #define MAX_METABALL_SPEED 5000.0f
  72.  
  73. union r16 { u16 u; s16 s; };
  74.  
  75. struct sphere {
  76.   f32 x, y;
  77.   r16 velX, velY; // Indexes into a lookup table of velocities
  78.   f32 m;
  79. };
  80.  
  81.  
  82.  
  83.  
  84.  
  85. /*
  86. //Returns the normal of the surface at the given position.
  87. vec3 Normal(vec3 pos)
  88. {
  89.   vec3 offset = vec3(NORMAL_SMOOTHNESS, 0, 0);
  90.  
  91.     vec3 normal = vec3
  92.     (
  93.         Scene(pos - offset.xyz) - Scene(pos + offset.xyz),
  94.         Scene(pos - offset.zxy) - Scene(pos + offset.zxy),
  95.         Scene(pos - offset.yzx) - Scene(pos + offset.yzx)
  96.     );
  97.  
  98.     return normalize(normal);
  99. }
  100.  
  101.  
  102.  
  103. vec3 hsv2rgb(vec3 c)
  104. {
  105.     vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  106.     vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  107.     return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
  108. }
  109.  
  110. //Scene shading.
  111. //position, lightPos
  112. vec3 Shade(vec3 position, vec3 normal, vec3 rayOrigin,vec3 rayDirection,vec2 pixel)
  113. {
  114.     float ang = iTime * 2.0;
  115.     vec3 lightPos = vec3(cos(ang), cos(ang*2.0), sin(ang)) * 2.0;
  116.  
  117.     //float shade = (32.0-length(position-rayOrigin))/32.0;
  118.     float shade = (32.0-length(position-lightPos))/32.0;
  119.     shade = clamp(shade, 0.0, 1.0);
  120.  
  121.     // best seems to be (30/1024)*3
  122.     float GAMMA = (iMouse.x/iResolution.x)*4.0;
  123.     shade = pow(shade, 1.0/(GAMMA));
  124.  
  125.  
  126.  
  127.     //vec3 color = vec3(0);
  128.  
  129.     //float ang = iTime * 2.0;
  130.     //vec3 lightPos = vec3(cos(ang), cos(ang*2.0), sin(ang)) * 2.0;
  131.  
  132.     //Normal shading
  133.     //float shade = 0.4 * max(0.0, normal, normalize(-lightPos)));
  134.  
  135.     //Specular highlight
  136.     //shade += 0.6 * max(0.0, dot(-reflect(normalize(position - lightPos), normal), rayDirection));
  137.  
  138.     //Linear falloff
  139.     //shade *= (16.0-distance(position, lightPos))/16.0;
  140.  
  141.     //Apply palette
  142.     //color = GetDitheredPalette(shade, pixel);
  143.  
  144.  
  145.     vec3 color = vec3( hsv2rgb(vec3(shade, shade, shade)).rgb );
  146.  
  147.     //color = mix(color, vec3(0.1), step(22.0, length(position)));
  148.  
  149.     return color;
  150. }
  151. */
  152.  
  153.  
  154.  
  155.  
  156.  
  157. const char* shader_vertex = R"GLSL(
  158. #version 330 core
  159.  
  160. // This way, the entire canvas is covered with a single triangle
  161. const vec2 verts[3] = vec2[](
  162.  vec2(-1.0f, -1.0f), // Bottom-left
  163.  vec2( 3.0f, -1.0f), // Bottom-right (horizontally past NDC)
  164.  vec2(-1.0f,  3.0f)  // Top-left (vertically past NDC)
  165. );
  166.  
  167. out vec2 fragNDC;
  168. out vec2 fragCoords;
  169.  
  170. uniform vec3 iResolution; // viewport resolution (in pixels)
  171.  
  172. void main(){
  173.  vec2 pos = verts[gl_VertexID];
  174.  fragNDC = pos;
  175.  fragCoords = ((pos+1.0)/2.0) * iResolution.xy;
  176.  gl_Position = vec4(pos, 0.0f, 1.0f);
  177. }
  178.  
  179. )GLSL";
  180.  
  181.  
  182.  
  183.  
  184.  
  185. // maybe use the velX and velY bits for something?
  186.  
  187. const char* shader_fragment = R"GLSL(
  188. #version 330 core
  189.  
  190. #define METABALL_THRESHHOLD 75.0
  191.  
  192.  
  193.  
  194. in vec2 fragNDC;
  195. in vec2 fragCoords;
  196. out vec4 fragColor;
  197.  
  198. uniform samplerBuffer spheres;
  199. uniform int spheres_len;
  200.  
  201. uniform float threshhold;
  202.  
  203. uniform vec3  iResolution; // viewport resolution (in pixels)
  204. uniform vec4  iMouse;      // mouse pixel coords. xy: current (if MLB down), zw: click
  205. uniform float iTime;       // shader playback time (in seconds)
  206.  
  207.  
  208.  
  209. // When input is < 1.0, output is v
  210. // When input is 1.0-1.99..., output is 2.0-v
  211. // (Its output basically looks like a triangle wave)
  212. float oscillate(float v){
  213.  return 1.0 - abs(1.0-mod(v, 2.0));
  214. }
  215.  
  216. // All values are normalized
  217. vec3 hsv2rgb(vec3 c){
  218.  vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
  219.  vec3 p = abs(fract(c.xxx+K.xyz) * 6.0  -  K.www);
  220.  return c.z  *  mix(K.xxx, clamp(p-K.xxx, 0.0, 1.0), c.y);
  221. }
  222.  
  223. float distMag(vec2 p0, vec2 p1, float mag){
  224.  p0 -= p1;
  225.  float result = sqrt(p0.x*p0.x + p0.y*p0.y);
  226.  return mag / (result*result);
  227. }
  228.  
  229. // Returns a multiplier of how far past the threshhold the sum is
  230. float sumMetaballs(){
  231.  float sum = 0.0;
  232.  for(int i=0; i<spheres_len; ++i){
  233.    vec4 metaball = texelFetch(spheres, i);
  234.    sum += distMag(fragCoords, metaball.xy, metaball.w);
  235.  }
  236.  return sum/threshhold;
  237. }
  238.  
  239. void main(){
  240.  float shade      = oscillate(sumMetaballs()/4.0);
  241.  float shadeTimed = oscillate(sumMetaballs()/4.0 + iTime/10.0);
  242.  
  243.  vec3 hsv = vec3(shadeTimed, 1.0/shadeTimed, float(shade>0.05));
  244.  
  245.  vec3 rgb = hsv2rgb(hsv);
  246.  fragColor = vec4(rgb, 1.0);
  247. }
  248.  
  249. )GLSL";
  250.  
  251.  
  252.  
  253.  
  254.  
  255. //maybe make color proportional to its size?
  256. /*
  257. const char* shader_fragment = R"GLSL(
  258. #version 330 core
  259.  
  260. #define ENABLE_DITHER 0
  261. #define MAX_STEPS 200
  262. #define MIN_DIST 0.002
  263.  
  264. in vec2 fragNDC;
  265. out vec4 fragColor;
  266.  
  267. uniform samplerBuffer spheres;
  268. uniform int spheres_len;
  269. uniform float fractTime; // 0.0 -> 0.99...
  270. uniform vec2 camAngle;
  271. uniform vec3 camPos;
  272.  
  273. // When input is < 1.0, output is v
  274. // when input is 1.0-1.99..., output is 2.0-v
  275. float oscillate(float v){
  276.   return 1.0 - abs(1.0-mod(v, 2.0));
  277. }
  278.  
  279. mat2 rotate(float angleRadians){
  280.   return mat2( cos(angleRadians), sin(angleRadians),
  281.               -sin(angleRadians), cos(angleRadians));
  282. }
  283.  
  284. vec3 hsv2rgb(vec3 c){
  285.   vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
  286.   vec3 p = abs(fract(c.xxx+K.xyz) * 6.0  -  K.www);
  287.   return c.z  *  mix(K.xxx, clamp(p-K.xxx, 0.0, 1.0), c.y);
  288. }
  289.  
  290. float sdfSphere(vec3 pos, vec4 sphere){
  291.   return length(pos-sphere.xyz) - sphere.w; // (.w is its radius)
  292. }
  293.  
  294. float sdfScene(vec3 pos){
  295.   float dist = 1e20; // Big starting distance
  296.   for(int i=0; i<spheres_len; ++i){
  297.     vec4 sphere = texelFetch(spheres, i);
  298.     dist = min(dist, sdfSphere(pos, sphere));
  299.   }
  300.   return dist;
  301. }
  302.  
  303. // Returns point of contact, or a max distance ray if nothing was hit
  304. vec3 rayMarch(vec3 origin, vec3 direction){
  305.   float hitDist = 0.0;
  306.   for(int i=0; i<MAX_STEPS; ++i){
  307.     float sceneDist = sdfScene(origin + direction*hitDist);
  308.     if(sceneDist < MIN_DIST) break;
  309.     hitDist += sceneDist;
  310.   }
  311.   return origin + direction*hitDist;
  312. }
  313.  
  314. #define LIGHT_MAX_DIST 32.0
  315. float getShade(vec3 pos, vec3 posLight){
  316.   float dist = length(pos-posLight);
  317.  
  318.   //float shade = 1.0f * float(dist<LIGHT_MAX_DIST);
  319.  
  320.   float shade = ((LIGHT_MAX_DIST)-dist)/(LIGHT_MAX_DIST);
  321.   shade = clamp(shade, 0.0, 1.0);
  322.  
  323.   #define GAMMA 0.087890625
  324.   shade = pow(shade, 1.0/(GAMMA));
  325.   shade = 1.0f/dist;
  326.  
  327.   return shade;
  328. }
  329.  
  330. void main(){
  331.   vec2 aspectRatio = vec2(16.0/9.0, 1.0);
  332.   vec2 fragCoords = (fragNDC+1.0)/2.0;
  333.  
  334.  
  335.   vec2 xy = fragCoords; // * tan(fovRadians * 0.5);
  336.  
  337.   vec3 rayOrigin = camPos;
  338.   vec3 rayDir    = normalize(vec3(xy - aspectRatio/2.0, 1.0));
  339.  
  340.   mat2 camRotX = rotate(camAngle.x);
  341.   mat2 camRotY = rotate(camAngle.y);
  342.  
  343.   rayOrigin.yz *= camRotX;
  344.   rayOrigin.xz *= camRotY;
  345.   rayDir.yz *= camRotX;
  346.   rayDir.xz *= camRotY;
  347.  
  348.  
  349.  
  350.   vec3 contactPoint = rayMarch(rayOrigin, rayDir);
  351.   float shade = getShade(contactPoint, rayOrigin);
  352.   fragColor = vec4(shade, shade, shade, 1.0);
  353.  
  354. }
  355.  
  356. )GLSL";
  357. */
  358.  
  359.  
  360.  
  361.  
  362.  
  363. // Assumes prefix has already been copied to infoLogBuffer
  364. void concatenateInfoBuffer(const char* prefix_str){
  365.   strnCpy(infoLogBuffer, prefix_str, sizeof(infoLogBuffer)-1);
  366.  
  367.   strnCat(infoLogBuffer, ": ", sizeof(infoLogBuffer)-1);
  368.  
  369.   strnCat(infoLogBuffer, infoLogPostfix, sizeof(infoLogBuffer)-1);
  370.  
  371.   infoLogBuffer[sizeof(infoLogBuffer)-1] = 0; // Just in case
  372.  
  373. }
  374.  
  375.  
  376.  
  377. void assertShaderComp(GLuint shader_id, const char* prefix_str){
  378.   int success;
  379.   glGetShaderiv(shader_id, GL_COMPILE_STATUS, &success);
  380.  
  381.   if(!success){
  382.     glGetShaderInfoLog(shader_id,sizeof(infoLogPostfix),nullptr,infoLogPostfix);
  383.  
  384.     concatenateInfoBuffer(prefix_str);
  385.  
  386.     throw (const char*)infoLogBuffer;
  387.  
  388.   }
  389.  
  390. }
  391.  
  392.  
  393.  
  394. void assertShaderProg(GLuint program_id, const char* prefix_str){
  395.   int success;
  396.   glGetProgramiv(program_id, GL_LINK_STATUS, &success);
  397.  
  398.   if(!success){
  399.     glGetProgramInfoLog(program_id, sizeof(infoLogPostfix),
  400.                         nullptr, infoLogPostfix);
  401.  
  402.     concatenateInfoBuffer(prefix_str);
  403.  
  404.     throw (const char*)infoLogBuffer;
  405.  
  406.   }
  407.  
  408. }
  409.  
  410.  
  411.  
  412.  
  413.  
  414. GLuint createShaderProg(const char** SRC_vert_shader_p,
  415.                         const char** SRC_frag_shader_p,
  416.                         const char* failure_string = nullptr)
  417. {
  418.   if(SRC_vert_shader_p == nullptr)
  419.     throw "createShaderProg(): SRC_vert_shader_p = nullptr";
  420.  
  421.   if(SRC_frag_shader_p == nullptr)
  422.     throw "createShaderProg(): SRC_frag_shader_p = nullptr";
  423.  
  424.   if(failure_string == nullptr)
  425.     failure_string = "Failed to create shader program";
  426.  
  427.  
  428.  
  429.   // Make vertex shader
  430.  
  431.   GLuint ID_vert_shader = glCreateShader(GL_VERTEX_SHADER);
  432.   if(!ID_vert_shader) throw "\"glCreateShader(GL_VERTEX_SHADER)\" failed";
  433.   glShaderSource(ID_vert_shader, 1, SRC_vert_shader_p, nullptr);
  434.   glCompileShader(ID_vert_shader);
  435.  
  436.   assertShaderComp(ID_vert_shader, "Failed to compile vertex shader");
  437.  
  438.  
  439.  
  440.   // Make fragment shader
  441.  
  442.   GLuint ID_frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
  443.   if(!ID_frag_shader) throw "\"glCreateShader(GL_FRAGMENT_SHADER)\" failed";
  444.  
  445.   glShaderSource(ID_frag_shader, 1, SRC_frag_shader_p, nullptr);
  446.   glCompileShader(ID_frag_shader);
  447.  
  448.   assertShaderComp(ID_frag_shader, "Failed to compile fragment shader");
  449.  
  450.  
  451.  
  452.   // Make program
  453.  
  454.   GLuint ID_shader_program = glCreateProgram();
  455.   if(!ID_frag_shader) throw "\"glCreateProgram()\" failed";
  456.  
  457.   glAttachShader(ID_shader_program, ID_vert_shader);
  458.   glAttachShader(ID_shader_program, ID_frag_shader);
  459.   glLinkProgram(ID_shader_program);
  460.  
  461.   assertShaderProg(ID_shader_program, failure_string);
  462.  
  463.   glDeleteShader(ID_vert_shader);
  464.   glDeleteShader(ID_frag_shader);
  465.  
  466.  
  467.  
  468.   return ID_shader_program;
  469.  
  470. }
  471.  
  472.  
  473.  
  474.  
  475.  
  476. f32 velocities[65536]; // LUT of velocities
  477.  
  478. sphere* spheres;
  479. GLint   spheres_len;
  480. Point2d resolution;
  481.  
  482. #define THRESHHOLD_RATIO (75.0f/400)
  483. f32 threshhold = 0.0001f;
  484.  
  485. #define MBCOUNT_FRACT(x,y) ((f32)spheres_len*((f32)(x)/(y)))
  486.  
  487. void rem_metaball(){
  488.   //spheres_len = MAX(spheres_len-1, 0);
  489.   spheres_len = MAX(spheres_len-1, 1);
  490.  
  491.   //for(GLint i=0; i<spheres_len; ++i)
  492.   //  spheres[i].m = MBCOUNT_FRACT(1.0f,4) + MBCOUNT_FRACT(3.0f,4)*frandf();
  493.  
  494.   //threshhold = ((f32)spheres_len)*THRESHHOLD_RATIO;
  495.  
  496. }
  497.  
  498. void add_metaball(f32 x, f32 y){
  499.   if(spheres_len >= MAX_SPHERE_COUNT) return;
  500.  
  501.   u16 velX_new = frandf()*65535;
  502.   u16 velY_new = frandf()*65535;
  503.  
  504.   // y is inverted, due to how OpenGL NDCs work
  505.   spheres[spheres_len].x      = x;
  506.   spheres[spheres_len].y      = resolution.y-y;
  507.   spheres[spheres_len].velX.u = (velX_new!=32768) ? velX_new : 32769;
  508.   spheres[spheres_len].velY.u = (velY_new!=32768) ? velY_new : 32769;
  509.  
  510.   //for(GLint i=0; i<spheres_len; ++i)
  511.   //  spheres[i].m = MBCOUNT_FRACT(1.0f,4) + MBCOUNT_FRACT(3.0f,4)*frandf();
  512.  
  513.   spheres[spheres_len].m = MBCOUNT_FRACT(1.0f,4)+MBCOUNT_FRACT(3.0f,4)*frandf();
  514.  
  515.   ++spheres_len;
  516.  
  517.   //threshhold = ((f32)spheres_len)*THRESHHOLD_RATIO;
  518.  
  519. }
  520.  
  521. void animate_metaballs(){
  522.   #define OPTIMAL_FRAME (1000.0/62.5) // 16ms
  523.   #define COEFFICIENT_DIV (1.0/OPTIMAL_FRAME)
  524.  
  525.   #define LASTTIME_START 0xFFFFFFFFFFFFFFFF // = 2^64 - 1
  526.   static u64 timeLast = LASTTIME_START;
  527.  
  528.   const u64 timeThis = timeGetPerfCounter();
  529.   if(timeLast == LASTTIME_START) timeLast = timeThis;
  530.  
  531.   // Note: convert to f64 if too many rounding errors start happening
  532.   const f32 timeDelta = (f32)(timeThis-timeLast)/timeGetPerfFreq();
  533.   timeLast = timeThis;
  534.  
  535.  
  536.  
  537.   for(int i=0; i<spheres_len; ++i){
  538.     // Note: convert to f64 if too many rounding errors start happening
  539.     const f32 coefficient = timeDelta*COEFFICIENT_DIV;
  540.     sphere& metaball = spheres[i];
  541.  
  542.     metaball.x += velocities[metaball.velX.u]*coefficient;
  543.     metaball.y += velocities[metaball.velY.u]*coefficient;
  544.  
  545.     if(metaball.x<0 || metaball.x>=resolution.x){
  546.       metaball.velX.s = -metaball.velX.s;
  547.       metaball.x = CLAMP(metaball.x, 0, resolution.x-1);
  548.     }
  549.  
  550.     if(metaball.y<0 || metaball.y>=resolution.y){
  551.       metaball.velY.s = -metaball.velY.s;
  552.       metaball.y = CLAMP(metaball.y, 0, resolution.y-1);
  553.     }
  554.  
  555.   }
  556.  
  557. }
  558.  
  559.  
  560.  
  561.  
  562.  
  563. #define SET_TICKCOUNT (tickCount=((s64)timeGetPerfCounter()))
  564.  
  565. int user_main(int argc, char** argv){
  566.   s64 tickCount; SET_TICKCOUNT;
  567.   s64 ticksPerSecond = (s64)timeGetPerfFreq();
  568.   f64 timeStart = (f64)tickCount/ticksPerSecond; // Doesn't change
  569.  
  570.   // Calculate velocities LUT
  571.   for(s32 i=0; i<65536; ++i){
  572.     r16 j; j.u = i&65535;
  573.     if(j.s==-32768) continue;
  574.     velocities[j.u] = MAX_METABALL_SPEED*((f32)j.s/32767);
  575.   }
  576.   // Otherwise (signed) index -32768 would stay 0
  577.   velocities[32768] = velocities[32769];
  578.  
  579.   // Set up viewport, and compile and link shaders
  580.   resolution = get_win_size();
  581.   glViewport(0, 0, resolution.x, resolution.y);
  582.   u32 ID_prg_raymarch = createShaderProg(&shader_vertex, &shader_fragment,
  583.                                         "Failed to create main shader program");
  584.  
  585.  
  586.  
  587.   // mem_Wrapper should free automatically, even if an exception is thrown
  588.   mem_Wrapper sceneBuffer((MAX_SPHERE_COUNT) * sizeof(sphere));
  589.  
  590.   spheres     = (sphere*)sceneBuffer.ptr;
  591.   spheres_len = 0;
  592.  
  593.   if(spheres == nullptr) throw "Failed to allocate scene data buffer";
  594.  
  595.  
  596.  
  597.   u32 ID_VAO, ID_TBO, ID_TEX;
  598.  
  599.   glGenVertexArrays(1, &ID_VAO);
  600.   glBindVertexArray(ID_VAO); // Never unbound
  601.  
  602.   // Create the actual Texture-Buffer-Object
  603.   glGenBuffers(1, &ID_TBO);
  604.   glBindBuffer(GL_TEXTURE_BUFFER, ID_TBO);
  605.   // Allocate buffer with GL_DYNAMIC_DRAW usage hint
  606.   // (without copying anything initially!)
  607.   glBufferData(GL_TEXTURE_BUFFER, (MAX_SPHERE_COUNT) * sizeof(sphere),
  608.                nullptr, GL_DYNAMIC_DRAW);
  609.  
  610.   // Associate a texture to that TBO...
  611.   glGenBuffers(1, &ID_TEX);
  612.   glBindTexture(GL_TEXTURE_BUFFER, ID_TEX);
  613.   // ...while specifying its format, GL_RGBA32F
  614.   glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, ID_TBO);
  615.  
  616.  
  617.  
  618.   GLint UNI_spheres_len = glGetUniformLocation(ID_prg_raymarch, "spheres_len");
  619.   GLint UNI_threshhold  = glGetUniformLocation(ID_prg_raymarch, "threshhold");
  620.  
  621.   GLint UNI_iTime       = glGetUniformLocation(ID_prg_raymarch, "iTime");
  622.   GLint UNI_iResolution = glGetUniformLocation(ID_prg_raymarch, "iResolution");
  623.   GLint UNI_iMouse      = glGetUniformLocation(ID_prg_raymarch, "iMouse");
  624.  
  625.  
  626.  
  627.   glUseProgram(ID_prg_raymarch);
  628.  
  629.   #ifdef _DEBUG
  630.     s64 timeDeltaTicks = ((s64)timeGetPerfCounter())-tickCount;
  631.     f64 timeDeltaSecs  = (f64)timeDeltaTicks/ticksPerSecond;
  632.     _printf("Setting up OpenGL shaders & buffers took an additional: %.4f seconds!\n", timeDeltaSecs);
  633.   #endif
  634.  
  635.   //trap_cursor(true, true);
  636.  
  637.   Fpoint2d mpos = {0.0f, 0.0f};
  638.   add_metaball(frand()*(resolution.x-1), frand()*(resolution.y-1));
  639.  
  640.   while(!is_win_closed()){
  641.     SET_TICKCOUNT;
  642.     s64 truncTimeTicks = tickCount/ticksPerSecond;
  643.     s64 fractTimeTicks = tickCount%ticksPerSecond;
  644.     f64 timeSeconds = ((f64)truncTimeTicks)+((f32)fractTimeTicks/ticksPerSecond);
  645.     timeSeconds -= timeStart;
  646.     f32 timeSeconds32 = (f32)timeSeconds;
  647.  
  648.     //Handle events
  649.     Event evt;
  650.     while(pollEvent(&evt))
  651.     switch(evt.type){
  652.       case EVENT_KEY_DOWN: {
  653.         if(evt.key.vkey == VKEY_ESCAPE) close_window();
  654.       } break;
  655.  
  656.       case EVENT_MOUSE_MOVED: {
  657.         mpos.x = evt.mouse.x;
  658.         mpos.y = evt.mouse.y;
  659.         //_printf("%f, %f\n", mpos.x, mpos.y);
  660.       } break;
  661.  
  662.       case EVENT_MOUSE_DOWN: {
  663.         if(evt.mouse.button&MBUTTON_LEFT)
  664.           add_metaball(evt.mouse.x, evt.mouse.y);
  665.         else
  666.           rem_metaball();
  667.       } break;
  668.  
  669.       case EVENT_MOUSE_VWHEEL: {
  670.         threshhold = MAX(threshhold+(evt.mouse.dy/700000),
  671.                          0.00000000004843059082f); // Small, but not 0
  672.         _printf("%f\n", threshhold);
  673.       } break;
  674.  
  675.     }
  676.  
  677.     if(is_win_closed()) break;
  678.  
  679.  
  680.  
  681.     // Update uniform values
  682.     glUniform1i(UNI_spheres_len, spheres_len);
  683.     glUniform1f(UNI_threshhold, threshhold);
  684.     glUniform1f(UNI_iTime, timeSeconds32);
  685.     glUniform3f(UNI_iResolution, (f32)resolution.x, (f32)resolution.y, 1.0f);
  686.     glUniform4f(UNI_iMouse, mpos.x, mpos.y, mpos.x, mpos.y);
  687.  
  688.     // Upload the scene data to the TBO
  689.     glBindBuffer(GL_TEXTURE_BUFFER, ID_TBO);
  690.     glBufferSubData(GL_TEXTURE_BUFFER, 0, sizeof(sphere)*spheres_len, spheres);
  691.  
  692.     // Draw stuff (only if the window is in focus)
  693.     if(is_win_focused()){
  694.       // 3 vertex indices for a single triangle, starting at index 0
  695.       // (using glClear should be redundant here, given that
  696.       //  the only triangle used completely covers the screen!)
  697.       glDrawArrays(GL_TRIANGLES, 0, 3);
  698.       present_window();
  699.  
  700.     }
  701.  
  702.     animate_metaballs();
  703.  
  704.     // Waits for about 16ms to approximate 60fps
  705.     timeSleep(16);
  706.  
  707.   }
  708.  
  709.  
  710.  
  711.   // Optional de-allocation
  712.   //glDeleteTextures(1, &ID_TEX);
  713.   glDeleteBuffers(1, &ID_TBO);
  714.   glDeleteVertexArrays(1, &ID_VAO);
  715.   glDeleteProgram(ID_prg_raymarch);
  716.  
  717.   return 0;
  718.  
  719. }
  720. /******************************************************************************/
  721. /******************************************************************************/
  722. //"opengl_metaballs_2025-06-26\src\kit_utils\misc\is_bit_set.cpp":
  723. #include <public_stuff.hpp>
  724.  
  725. // Union type punning in C++ is technically undefined behavior,
  726. // however GCC (if I recall correctly) explicitly allows it as an extension.
  727. #define USE_UNION_TYPE_PUNNING 1
  728.  
  729.  
  730.  
  731.  
  732.  
  733. #if USE_UNION_TYPE_PUNNING == 1
  734. bool is_bit_set(const void* buffer, u32 which_bit){
  735.   union {
  736.     u32 value;
  737.     struct {
  738.       u32 bit  :  3;
  739.       u32 byte : 29;
  740.     };
  741.   } _which_bit;
  742.  
  743.   _which_bit.value = which_bit;
  744.  
  745.   return 1&(  (((u8*)buffer)[_which_bit.byte])  >>  _which_bit.bit  );
  746.  
  747. }
  748.  
  749.  
  750.  
  751.  
  752.  
  753. #else
  754. bool is_bit_set(const void* buffer, u32 which_bit) {
  755.     const u8* bytes = static_cast<const u8*>(buffer);
  756.  
  757.     u32 byte_index = which_bit / 8;
  758.     u32 bit_index  = which_bit % 8;
  759.  
  760.     return (bytes[byte_index] >> bit_index) & 1;
  761. }
  762.  
  763.  
  764.  
  765.  
  766.  
  767. #endif
  768. /******************************************************************************/
  769. /******************************************************************************/
  770. //"opengl_metaballs_2025-06-26\src\kit_utils\misc\kit_misc.cpp":
  771. #include <public_stuff.hpp>
  772.  
  773.  
  774.  
  775. #ifdef FRAND_USED
  776. #include <stdlib.h>
  777.  
  778.  
  779.  
  780. #if RAND_MAX == 32767
  781.   #define GET_FRAND_VALUE() ( (double)(rand()<<15|rand())/0x3FFFFFFF )
  782.  
  783. #elif RAND_MAX == 2147483647
  784.   #define GET_FRAND_VALUE() ( (double)rand()/0x7FFFFFFF )
  785.  
  786. #else
  787.   #error "No definition of GET_FRAND_VALUE() available for current RAND_MAX"
  788.  
  789. #endif
  790.  
  791.  
  792.  
  793. f64 frand(){ //0.0f -> 1.0f
  794.   return GET_FRAND_VALUE();
  795. }
  796.  
  797. f64 frand2(){ //-1.0f -> 1.0f
  798.   return GET_FRAND_VALUE()*2.0f - 1.0f;
  799. }
  800.  
  801. f32 frandf(){ //0.0f -> 1.0f
  802.   return (f32)GET_FRAND_VALUE();
  803. }
  804.  
  805. f32 frandf2(){ //-1.0f -> 1.0f
  806.   return (f32)GET_FRAND_VALUE()*2.0f - 1.0f;
  807. }
  808.  
  809. #endif
  810.  
  811.  
  812.  
  813. //...
  814. /******************************************************************************/
  815. /******************************************************************************/
  816. //"opengl_metaballs_2025-06-26\src\kit_utils\misc\print_event.cpp":
  817. #include <public_stuff.hpp>
  818. #ifdef PRINT_EVENT_USED
  819. #ifdef _DEBUG
  820.  
  821.  
  822.  
  823. static char key_evt_text[][5] = { // EVENT_KEY_?
  824.   "NULL",
  825.   "CHAR",
  826.   "UP",
  827.   "DOWN",
  828. };
  829.  
  830. static char mouse_evt_text[][7] = { // EVENT_MOUSE_?
  831.   "NULL",
  832.   "MOVED",
  833.   "HWHEEL",
  834.   "VWHEEL",
  835.   "UP",
  836.   "DOWN",
  837. };
  838.  
  839. static char cursor_evt_text[][9] = { // EVENT_CURSOR_?
  840.   "NULL",
  841.   "TRAPPED",
  842.   "RELEASED",
  843. };
  844.  
  845.  
  846.  
  847.  
  848.  
  849. void print_event(Event& evt){
  850.   u16 subevent = SUBEVENT_ID(evt.type);
  851.  
  852.  
  853.  
  854.   switch(EVENT_ID(evt.type)){
  855.     case EVENT_NULL:
  856.     {
  857.       _printf("EVENT_NULL = {\n");
  858.       _printf("  .type      = EVENT_NULL,\n");
  859.       _printf("  .timestamp = %llu,\n", evt.common.timestamp);
  860.     } break;
  861.  
  862.  
  863.  
  864.     case EVENT_COMMON:
  865.     {
  866.       _printf("EVENT_COMMON = {\n");
  867.       _printf("  .type      = EVENT_COMMON,\n");
  868.       _printf("  .timestamp = %llu,\n", evt.common.timestamp);
  869.     } break;
  870.  
  871.  
  872.  
  873.     case EVENT_QUIT:
  874.     {
  875.       _printf("EVENT_COMMON = {\n");
  876.       _printf("  .type      = EVENT_COMMON,\n");
  877.       _printf("  .timestamp = %llu,\n", evt.common.timestamp);
  878.     } break;
  879.  
  880.  
  881.  
  882.     case EVENT_KEY:
  883.     {
  884.       _printf("EVENT_KEY = {\n");
  885.       _printf("  .type      = EVENT_KEY_%s,\n", key_evt_text[subevent]);
  886.       _printf("  .timestamp = %llu,\n\n", evt.key.timestamp);
  887.       _printf("  .kmods     = 0x%04X = {\n", evt.key.kmods);
  888.       _printf("    .lshift    = %i,\n", evt.key.sym.kmod.lshift);
  889.       _printf("    .rshift    = %i,\n", evt.key.sym.kmod.rshift);
  890.       _printf("    .lctrl     = %i,\n", evt.key.sym.kmod.lctrl);
  891.       _printf("    .rctrl     = %i,\n", evt.key.sym.kmod.rctrl);
  892.       _printf("    .lalt      = %i,\n", evt.key.sym.kmod.lalt);
  893.       _printf("    .ralt      = %i,\n", evt.key.sym.kmod.ralt);
  894.       _printf("    .lgui      = %i,\n", evt.key.sym.kmod.lgui);
  895.       _printf("    .rgui      = %i,\n", evt.key.sym.kmod.rgui);
  896.       _printf("    .numlock   = %i,\n", evt.key.sym.kmod.numlock);
  897.       _printf("    .capslock  = %i,\n", evt.key.sym.kmod.capslock);
  898.       _printf("    .altgraph  = %i,\n", evt.key.sym.kmod.altgraph);
  899.       _printf("    .scrollock = %i,\n", evt.key.sym.kmod.scrollock);
  900.       _printf("  }\n\n");
  901.       _printf("  .pkey    = 0x%02X,\n", evt.key.pkey);
  902.       _printf("  .vkey    = 0x%02X,\n", evt.key.vkey);
  903.       _printf("  .pressed = %s,\n", BOOL_STR(evt.key.pressed));
  904.       _printf("  .ischar  = %s,\n", BOOL_STR(evt.key.ischar));
  905.       _printf("  .repeat  = %s,\n", BOOL_STR(evt.key.repeat));
  906.     } break;
  907.  
  908.  
  909.  
  910.     case EVENT_MOUSE:
  911.     {
  912.       _printf("EVENT_MOUSE = {\n");
  913.       _printf("  .type      = EVENT_MOUSE_%s,\n\n", mouse_evt_text[subevent]);
  914.       _printf("  .button    = 0x%02X,\n", evt.mouse.button);
  915.       _printf("  .pressed   = %s,\n", BOOL_STR(evt.mouse.pressed));
  916.       _printf("  .dblClick  = %s,\n\n", BOOL_STR(evt.mouse.dblClick));
  917.       _printf("  .timestamp = %llu,\n\n", evt.mouse.timestamp);
  918.       _printf("  .x         = %5.2f,\n", evt.mouse.x);
  919.       _printf("  .y         = %5.2f,\n", evt.mouse.y);
  920.       _printf("  .dx        = %5.2f,\n", evt.mouse.dx);
  921.       _printf("  .dy        = %5.2f,\n", evt.mouse.dy);
  922.     } break;
  923.  
  924.  
  925.  
  926.     case EVENT_CURSOR:
  927.     {
  928.       _printf("EVENT_CURSOR = {\n");
  929.       _printf("  .type      = EVENT_CURSOR_%s,\n", cursor_evt_text[subevent]);
  930.       _printf("  .timestamp = %llu,\n", evt.common.timestamp);
  931.     } break;
  932.  
  933.  
  934.  
  935.     default:
  936.     {
  937.       _printf("EVENT_UNKNOWN = {\n");
  938.       _printf("  .type      = 0x%08X,\n", evt.type);
  939.       _printf("  .timestamp = %llu,\n", evt.common.timestamp);
  940.     }
  941.  
  942.  
  943.  
  944.   }
  945.  
  946.  
  947.  
  948.   _printf("}\n");
  949.  
  950. }
  951.  
  952.  
  953.  
  954.  
  955.  
  956. #endif /* _DEBUG */
  957. #endif /* PRINT_EVENT_USED */
  958. /******************************************************************************/
  959. /******************************************************************************/
  960. //"opengl_metaballs_2025-06-26\src\kit_utils\misc\snPrintf.cpp":
  961. #include <public_stuff.hpp>
  962.  
  963. #undef snprintf
  964. #undef vsnprintf
  965.  
  966. #define STB_SPRINTF_IMPLEMENTATION
  967. #include "../../../dep/stb_sprintf.h"
  968.  
  969.  
  970.  
  971.  
  972.  
  973. s32 snPrintf(char* str_dst, size_t len_max, const char* str_fmt, ...){
  974.   va_list va;
  975.   va_start(va, str_fmt);
  976.  
  977.   s32 result = 0;
  978.  
  979.   if(len_max > 0  &&  len_max <= INT_MAX){
  980.     result = stbsp_vsnprintf(str_dst, (int)len_max, str_fmt, va);
  981.   } else {
  982.     result = stbsp_vsprintf(str_dst, str_fmt, va);
  983.   }
  984.  
  985.   va_end(va);
  986.  
  987.   return result;
  988.  
  989. }
  990.  
  991.  
  992.  
  993. s32 vsnPrintf(char* str_dst, size_t len_max, const char* str_fmt, va_list va){
  994.   if(len_max > 0  &&  len_max <= INT_MAX){
  995.     return stbsp_vsnprintf(str_dst, (int)len_max, str_fmt, va);
  996.  
  997.   } else {
  998.     return stbsp_vsprintf(str_dst, str_fmt, va);
  999.  
  1000.   }
  1001.  
  1002. }
  1003. /******************************************************************************/
  1004. /******************************************************************************/
  1005. //"opengl_metaballs_2025-06-26\src\kit_utils\sound\AudioData.cpp":
  1006. #include <public_stuff.hpp>
  1007.  
  1008. #ifdef SOUND_STUFF_USED
  1009.  
  1010.  
  1011.  
  1012.  
  1013.  
  1014. void AudioDataHeader::printHeader(const char* name){
  1015.   if(name != nullptr){ _printf("%s = {\n", name); }
  1016.   else               { _printf("%p = {\n", this); }
  1017. #ifdef _DEBUG
  1018.   _printf("  ->magic         = \"%.4s\"; (0x%08X)\n", (char*)&magic, magic);
  1019.  
  1020.  
  1021.   const char* fmt_txt = "?";
  1022.   switch(format){
  1023.     case SMPFMT_U8    : fmt_txt = "U8";     break;
  1024.     case SMPFMT_S8    : fmt_txt = "S8";     break;
  1025.  
  1026.     case SMPFMT_U16LSB: fmt_txt = "U16LSB"; break;
  1027.     case SMPFMT_S16LSB: fmt_txt = "S16LSB"; break;
  1028.     case SMPFMT_S24LSB: fmt_txt = "S24LSB"; break;
  1029.     case SMPFMT_S32LSB: fmt_txt = "S32LSB"; break;
  1030.     case SMPFMT_F32LSB: fmt_txt = "F32LSB"; break;
  1031.     case SMPFMT_F64LSB: fmt_txt = "F64LSB"; break;
  1032.  
  1033.     case SMPFMT_U16MSB: fmt_txt = "U16MSB"; break;
  1034.     case SMPFMT_S16MSB: fmt_txt = "S16MSB"; break;
  1035.     case SMPFMT_S24MSB: fmt_txt = "S24MSB"; break;
  1036.     case SMPFMT_S32MSB: fmt_txt = "S32MSB"; break;
  1037.     case SMPFMT_F32MSB: fmt_txt = "F32MSB"; break;
  1038.     case SMPFMT_F64MSB: fmt_txt = "F64MSB"; break;
  1039.  
  1040.     default           : fmt_txt = "UNKNOWN";
  1041.   }
  1042.  
  1043.   _printf("  ->format        = SMPFMT_%s; (0x%04X)\n", fmt_txt, format);
  1044.  
  1045.  
  1046.   _printf("  ->headerSize    = %u;\n", headerSize);
  1047.   _printf("  ->dataSize      = %llu;\n", dataSize);
  1048.   _printf("  ->loopStart     = %llu;\n", loopStart);
  1049.   _printf("  ->loopEnd       = %llu;\n", loopEnd);
  1050.   _printf("  ->numSamples    = %llu;\n", numSamples);
  1051.   _printf("  ->sampleRate    = %u;\n", sampleRate);
  1052.   _printf("  ->bitRate       = %u;\n", bitRate);
  1053.   _printf("  ->loopCount     = %u;\n", loopCount);
  1054.   _printf("  ->channels      = %u;\n", channels);
  1055.   _printf("  ->_reserved     = %u;\n", _reserved);
  1056.   _printf("  ->fmt_version   = %u;\n", fmt_version);
  1057.   _printf("  ->mode          = %u;\n", mode);
  1058.   _printf("  ->metadata_type = %u;\n", metadata_type);
  1059.   _printf("  ->samples       = %p;\n", samples);
  1060.   _printf("  ->userdata      = %p;\n", userdata);
  1061.   _printf("};");
  1062.  
  1063.  
  1064. #else
  1065.   _printf("  (AudioDataHeader::printHeader() is not available in release build!)\n}\n");
  1066.  
  1067.  
  1068. #endif /* _DEBUG */
  1069. }
  1070.  
  1071.  
  1072.  
  1073.  
  1074.  
  1075. bool AudioData::_allocate_hdr(u16 headerSize, u64 dataSize){
  1076.   if(hdr) return false;
  1077.  
  1078.   if(headerSize < sizeof(AudioDataHeader)) return false;
  1079.  
  1080.   hdr = (AudioDataHeader*)mem_alloc(headerSize+dataSize);
  1081.  
  1082.   if(hdr == nullptr) return false;
  1083.  
  1084.   mem_set(hdr, 0, headerSize+dataSize);
  1085.  
  1086.  
  1087.   hdr->magic      = KPM_FILE_SIG;
  1088.  
  1089.   hdr->headerSize = headerSize;
  1090.   hdr->dataSize   = dataSize;
  1091.  
  1092.   hdr->samples    = (u8*)hdr + hdr->headerSize;
  1093.  
  1094.  
  1095.   return true;
  1096.  
  1097. }
  1098.  
  1099.  
  1100.  
  1101.  
  1102.  
  1103. AudioData::AudioData(AudioSampleFormatEnum format,
  1104.                      u64 numSamples, u16 channels, u32 sampleRate)
  1105. {
  1106.   if(hdr) return;
  1107.  
  1108.   const u64 dataSize = GET_AUDIO_BYTESIZE(format)*numSamples*channels;
  1109.  
  1110.   if(!_allocate_hdr(sizeof(AudioDataHeader), dataSize)) return;
  1111.  
  1112.   // (Redundant assignments are commented out here)
  1113.  
  1114. //hdr->magic         = KPM_FILE_SIG;
  1115.   hdr->format        = format;
  1116. //hdr->headerSize    = sizeof(AudioDataHeader);
  1117. //hdr->dataSize      = dataSize;
  1118.  
  1119. //hdr->loopStart     = 0;
  1120.   hdr->loopEnd       = numSamples;
  1121.  
  1122.   hdr->numSamples    = numSamples;
  1123.   hdr->sampleRate    = sampleRate;
  1124.   hdr->bitRate       = GET_AUDIO_BITSIZE(format)*sampleRate*channels;
  1125.  
  1126. //hdr->loopCount     = 0;
  1127.   hdr->channels      = channels;
  1128. //hdr->_reserved     = 0;
  1129.   hdr->fmt_version   = 1; // 1 indicates the version kit_sdl2 uses
  1130. //hdr->mode          = 0; // PCM or float data
  1131. //hdr->metadata_type = 0; // No metadata
  1132.  
  1133. //hdr->samples       = (u8*)hdr + hdr->headerSize;
  1134. //hdr->userdata      = nullptr;
  1135.  
  1136. }
  1137.  
  1138.  
  1139.  
  1140.  
  1141.  
  1142. #ifdef AUDIODATA_FILELOAD_USED
  1143.  
  1144. #define KPCM_FILE_SIG 0x4D43506B //.kpm's old file signature
  1145.  
  1146. AudioData::AudioData(const char* filePath,
  1147.                      AudioDataLoaderCallback callback)
  1148. {
  1149.   if(hdr) return;
  1150.  
  1151.   if(filePath == nullptr) return;
  1152.  
  1153.   if(!fileio_exists(filePath)) return;
  1154.  
  1155.  
  1156.  
  1157.   AudioDataHeader* _hdr;
  1158.  
  1159.   if(callback == nullptr){ // Load .kpm file
  1160.     size_t fileSize;
  1161.     _hdr = (AudioDataHeader*)fileio_read(filePath, &fileSize);
  1162.     if(_hdr == nullptr) return;
  1163.  
  1164.     // Check for the current and old version of .kpm's file signature
  1165.     // (but only if the fileSize is enough for a u32)
  1166.     if(fileSize >= sizeof(u32)  &&
  1167.        (_hdr->magic != KPM_FILE_SIG  &&  _hdr->magic != KPCM_FILE_SIG))
  1168.     {
  1169.       _free_hdr_copy: mem_free(&_hdr); return;
  1170.     }
  1171.     else if(fileSize < sizeof(AudioDataHeader)) goto _free_hdr_copy;
  1172.  
  1173. /*
  1174.     if(_hdr->magic != KIT_MAGIC_KPM) throw "magic != KIT_MAGIC_KPM";
  1175.  
  1176.     if(!isFormatValid(_hdr->format)) throw "format is invalid";
  1177.  
  1178.     if(_hdr->headerSize < sizeof(AudioDataHeader)) throw "headerSize < sizeof(AudioDataHeader)";
  1179.  
  1180.     if(_hdr->dataSize != (fileSize-_hdr->headerSize)) throw "dataSize is invalid";
  1181.  
  1182.     //(channels are checked before numSamples to prevent divide-by-zero exceptions)
  1183.     if(_hdr->channels!=1 && _hdr->channels!=2) throw "audio is neither mono nor stereo";
  1184.  
  1185.     //(numSamples is checked before loopStart/loopEnd, as their checks rely upon numSamples)
  1186.     if(_hdr->numSamples != (_hdr->dataSize/KIT_ASTREAM_FMT_BYTESIZE(_hdr->format))/_hdr->channels) throw "numSamples is invalid";
  1187.  
  1188.     if(_hdr->loopStart >= _hdr->numSamples) throw "loopStart >= numSamples";
  1189.  
  1190.     if(_hdr->loopEnd > _hdr->numSamples) throw "loopEnd > numSamples";
  1191.  
  1192.     if(_hdr->sampleRate < 1000) throw "sampleRate < 1000";
  1193.  
  1194.     if(_hdr->bitRate != _hdr->sampleRate*_hdr->channels*KIT_ASTREAM_FMT_BITSIZE(_hdr->format)) throw "bitRate is invalid";
  1195.  
  1196.     if(_hdr->bitRemainder != 0) throw "bitRemainder != 0";
  1197.  
  1198.     if(_hdr->mode != 0) throw "only mode 0 kPCM files are currently supported";
  1199. */
  1200.  
  1201.     // The only difference between 0 and 1 is that bitsPerSample is offset by -1
  1202.     if(_hdr->fmt_version == 0) ++_hdr->format;
  1203.  
  1204.     _hdr->magic       = KPM_FILE_SIG; // If it was previously kPCM, now it is kPxM
  1205.     _hdr->fmt_version = 1;
  1206.  
  1207.  
  1208.   } else {
  1209.     _hdr = callback(filePath);
  1210.     if(_hdr == nullptr) return;
  1211.  
  1212.  
  1213.   }
  1214.  
  1215.  
  1216.  
  1217.   size_t totalSize = _hdr->headerSize + _hdr->dataSize;
  1218.   hdr = (AudioDataHeader*)mem_alloc(totalSize);
  1219.   if(hdr == nullptr){ mem_free(&_hdr); return; }
  1220.  
  1221.   mem_copy(hdr, _hdr, totalSize);
  1222.   mem_free(&_hdr);
  1223.  
  1224.   hdr->samples = (u8*)hdr + hdr->headerSize;
  1225.  
  1226. }
  1227.  
  1228. #endif /* AUDIODATA_FILELOAD_USED */
  1229.  
  1230.  
  1231.  
  1232.  
  1233.  
  1234. AudioData::~AudioData(){
  1235.   mem_free(&hdr);
  1236. }
  1237.  
  1238.  
  1239.  
  1240.  
  1241.  
  1242. #ifdef AUDIODATA_SAVEAUDIO_USED
  1243.  
  1244. bool AudioData::saveAudio(const char* filePath,
  1245.                           AudioDataSaverCallback callback)
  1246. {
  1247.   if(hdr == nullptr) return false;
  1248.   if(filePath == nullptr) return false;
  1249.  
  1250.   if(callback == nullptr){ //save .kpm
  1251.     if(!fileio_write(filePath, hdr, hdr->headerSize+hdr->dataSize))
  1252.       return false;
  1253.   } else {
  1254.     if(!callback(filePath, *hdr))
  1255.       return false;
  1256.   }
  1257.  
  1258.   return true;
  1259.  
  1260. }
  1261.  
  1262. #endif
  1263.  
  1264.  
  1265.  
  1266.  
  1267.  
  1268. #ifdef AUDIODATA_CONVERTFORMAT_USED
  1269.  
  1270. static bool _fmt_is_valid(u16 fmt){
  1271.   switch(fmt){
  1272.     case SMPFMT_U8 :
  1273.     case SMPFMT_S8 :
  1274.     case SMPFMT_U16:
  1275.     case SMPFMT_S16:
  1276.     case SMPFMT_S24:
  1277.     case SMPFMT_S32:
  1278.     case SMPFMT_F32:
  1279.     case SMPFMT_F64: return  true; // Lol
  1280.     default:         return false;
  1281.   }
  1282. }
  1283.  
  1284. // = 2^(bits-1)
  1285. #define ABS_S8_MIN         (128) //ABS_Sx_MIN is a horrible name for this,
  1286. #define ABS_S16_MIN      (32768)  //but it's the name i chose within 10 seconds
  1287. #define ABS_S24_MIN    (8388608)  //(so i may or may not change it, idk)
  1288. #define ABS_S32_MIN (2147483648)
  1289.  
  1290. struct _s24 { u8 a, b, c; } //mostly for memory alignment purposes
  1291. ATTR_PACKED; //(explicitly pack just in case; probably unnecessary)
  1292.  
  1293. union _s24u {
  1294.   _s24 v;
  1295.    s32 n : 24;
  1296.   inline _s24u(_s24 _v) : v(_v) {}
  1297.   inline _s24u(s32  _n) : n(_n&0x7FFFFF) {}
  1298. };
  1299.  
  1300. static inline f64 frm_s24(_s24 x){ return (f64)_s24u(x).n/ABS_S24_MIN; }
  1301.  
  1302. static inline _s24 to_s24(f64 x){ return _s24u((s32)(x*0x7FFFFF)).v; }
  1303.  
  1304.  
  1305.  
  1306. bool AudioData::convertFormat(AudioSampleFormatEnum format){
  1307.   if(hdr == nullptr) return false;
  1308.  
  1309.   if(hdr->format == format) return true;
  1310.  
  1311.   if(!_fmt_is_valid(hdr->format)) return false;
  1312.  
  1313.   if(!_fmt_is_valid(format)) return false;
  1314.  
  1315.  
  1316.  
  1317.   u64 totalSamples = hdr->numSamples*hdr->channels;
  1318.  
  1319.   u64    dataSize = GET_AUDIO_BYTESIZE(format)*totalSamples;
  1320.   u32     bitRate = GET_AUDIO_BITSIZE(format)*hdr->sampleRate*hdr->channels;
  1321.  
  1322.  
  1323.  
  1324.   mem_Wrapper temp_samples(totalSamples*sizeof(f64));
  1325.   f64*  tmp = (f64*)temp_samples.ptr;
  1326.   void* smp = hdr->samples;
  1327.  
  1328.   #define FOR_TS_BRK(x) for(u64 i=0; i<totalSamples; ++i){ x; } break
  1329.   #define SMPCAST(_type) (  ((_type*)smp)[i]  )
  1330.   #define FRM_CONV(_type, _scaling_factor, _modifier) \
  1331.     (  ((f64)SMPCAST(_type) _modifier) _scaling_factor  )
  1332.  
  1333.   // Convert all source samples to 64-bit floats
  1334.   switch(hdr->format){
  1335.     case SMPFMT_U8 : FOR_TS_BRK(  tmp[i] = FRM_CONV(u8 , /ABS_S8_MIN ,   -128)  );
  1336.     case SMPFMT_S8 : FOR_TS_BRK(  tmp[i] = FRM_CONV(s8 , /ABS_S8_MIN ,       )  );
  1337.     case SMPFMT_U16: FOR_TS_BRK(  tmp[i] = FRM_CONV(u16, /ABS_S16_MIN, -32768)  );
  1338.     case SMPFMT_S16: FOR_TS_BRK(  tmp[i] = FRM_CONV(s16, /ABS_S16_MIN,       )  );
  1339.     case SMPFMT_S24: FOR_TS_BRK(  tmp[i] = frm_s24(SMPCAST(_s24))               );
  1340.     case SMPFMT_S32: FOR_TS_BRK(  tmp[i] = FRM_CONV(s32, /ABS_S32_MIN,       )  );
  1341.     case SMPFMT_F32: FOR_TS_BRK(  tmp[i] = FRM_CONV(f32,             ,       )  );
  1342.     case SMPFMT_F64: mem_copy(tmp, smp, hdr->dataSize); break;
  1343.   }
  1344.  
  1345.  
  1346.  
  1347.   //resize header to match destination format's dataSize
  1348.   if(!mem_realloc(&hdr, hdr->headerSize+dataSize)) return false;
  1349.  
  1350.   //update relevant header values
  1351.   hdr->format       = format;
  1352.   hdr->dataSize     = dataSize;
  1353.   hdr->bitRate      = bitRate;
  1354.   hdr->samples      = (u8*)hdr + hdr->headerSize;
  1355.  
  1356.   smp = hdr->samples;
  1357.  
  1358.  
  1359.  
  1360.   #define TO_CONV(_type, _scl_fct, _mod) (  (_type)( tmp[i] _scl_fct _mod )  )
  1361.  
  1362.   //convert the f64 samples to the desired format
  1363.   switch(hdr->format){ //(hdr->format = format now)
  1364.     case SMPFMT_U8 : FOR_TS_BRK(  SMPCAST( u8 ) = TO_CONV(u8 , *  127,   +128)  );
  1365.     case SMPFMT_S8 : FOR_TS_BRK(  SMPCAST( s8 ) = TO_CONV(s8 , *  127,       )  );
  1366.     case SMPFMT_U16: FOR_TS_BRK(  SMPCAST( u16) = TO_CONV(u16, *32767, +32768)  );
  1367.     case SMPFMT_S16: FOR_TS_BRK(  SMPCAST( s16) = TO_CONV(s16, *32767,       )  );
  1368.     case SMPFMT_S24: FOR_TS_BRK(  SMPCAST(_s24) = to_s24(tmp[i])                );
  1369.     case SMPFMT_S32: FOR_TS_BRK(  SMPCAST( s32) = TO_CONV(s32, *0x7FFFFFFF,  )  );
  1370.     case SMPFMT_F32: FOR_TS_BRK(  SMPCAST( f32) = TO_CONV(f32,            ,  )  );
  1371.     case SMPFMT_F64: mem_copy(smp, tmp, hdr->dataSize); break;
  1372.   }
  1373.  
  1374.  
  1375.  
  1376.   return true;
  1377.  
  1378. }
  1379.  
  1380. #endif
  1381.  
  1382.  
  1383.  
  1384.  
  1385.  
  1386. #endif
  1387.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement