Advertisement
Kitomas

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

Jun 27th, 2025
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 26.26 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"opengl_metaballs_2025-06-26\src\kit_utils\sound\SoundEngine.cpp":
  4. // DO NOT USE THIS AS REFERENCE FOR A NEW PROJECT
  5. // THIS IS A HACKY PORT OF KIT_SDL2'S SOUNDENGINE!!!!
  6. #include <public_stuff.hpp>
  7.  
  8. #ifdef SOUND_STUFF_USED
  9.  
  10. #include <windows.h>
  11.  
  12.  
  13.  
  14.  
  15.  
  16. #define SNDENGINE_IS_INVALID (_tracks_len == 0)
  17. #define SNDENGINE_IS_TRACK_OOB (track >= _tracks_len)
  18.  
  19.  
  20.  
  21. #define MUTEX_PTR ((CRITICAL_SECTION*)&_lock)
  22.  
  23. #define LOCK_MUTEX \
  24.   bool locked = false; \
  25.   if(MUTEX_PTR != nullptr){ EnterCriticalSection(MUTEX_PTR); locked = true; }
  26.  
  27. #define UNLOCK_MUTEX                      { \
  28.   if(locked){ LeaveCriticalSection(MUTEX_PTR); locked = false; } }
  29.  
  30.  
  31.  
  32.  
  33.  
  34. SoundEngine::SoundEngine(u16 numTracks, f32 _sampleRate){
  35.   if(numTracks < 1) return;
  36.  
  37.   _tracks_len = numTracks;
  38.  
  39.   mem_set(_tracks, 0, sizeof(SoundEngineTrack)*_tracks_len);
  40.  
  41.   InitializeCriticalSectionAndSpinCount(MUTEX_PTR, 4096);
  42.  
  43.   sampleRate = _sampleRate;
  44.  
  45. }
  46.  
  47.  
  48.  
  49.  
  50.  
  51. SoundEngine::~SoundEngine(){
  52.   if(_tracks_len == 0) return;
  53.  
  54.   LOCK_MUTEX;
  55.  
  56.   for(u16 t=0; t<_tracks_len; ++t)
  57.     _tracks[t].audio = nullptr;
  58.  
  59.   _tracks_len = 0;
  60.  
  61.   UNLOCK_MUTEX;
  62.  
  63.   DeleteCriticalSection(MUTEX_PTR);
  64.  
  65. }
  66.  
  67.  
  68.  
  69.  
  70.  
  71. bool SoundEngine::isTrackPlaying(u16 track){
  72.   if(SNDENGINE_IS_INVALID  ) return false;
  73.   if(SNDENGINE_IS_TRACK_OOB) return false;
  74.  
  75.   return _tracks[track].audio != nullptr;
  76.  
  77. }
  78.  
  79.  
  80.  
  81.  
  82.  
  83. u16 SoundEngine::getActiveTracks(){
  84.   if(SNDENGINE_IS_INVALID) return 0;
  85.  
  86.   u16 activeTracks = 0;
  87.  
  88.   SoundEngineTrack* tracks = _tracks;
  89.  
  90.   LOCK_MUTEX;
  91.  
  92.   for(u16 t=0; t<_tracks_len; ++t){
  93.     if(tracks[t].audio != nullptr) ++activeTracks;
  94.   }
  95.  
  96.   UNLOCK_MUTEX;
  97.  
  98.   return activeTracks;
  99.  
  100. }
  101.  
  102.  
  103.  
  104.  
  105.  
  106. bool SoundEngine::setVolume(f32 volumeL, f32 volumeR,
  107.                             u16 track, bool forced)
  108. {
  109.   if(SNDENGINE_IS_INVALID) return false;
  110.  
  111.   // Normally, anything at or below 0 will cause the clip
  112.   // to stop immediately, but this check couldn't hurt
  113.   StereoF32 volumeNew = { MAX(volumeL, 0.0f), MAX(volumeR, 0.0f) };
  114.  
  115.  
  116.  
  117.   if(track != 0xFFFF){
  118.     if(SNDENGINE_IS_TRACK_OOB) return false;
  119.  
  120.     SoundEngineTrack& _track = _tracks[track];
  121.     if(_track.audio == nullptr) return true; // Track is empty; exit early
  122.  
  123.     LOCK_MUTEX;
  124.  
  125.     if(forced) _track.vol_old = volumeNew;
  126.     _track.vol_new = volumeNew;
  127.  
  128.     UNLOCK_MUTEX;
  129.  
  130.  
  131.  
  132.   } else {
  133.     SoundEngineTrack* tracks = _tracks;
  134.  
  135.     LOCK_MUTEX;
  136.  
  137.     if(!forced){
  138.       for(u16 t=0; t<_tracks_len; ++t)
  139.         tracks[t].vol_new = volumeNew;
  140.  
  141.     } else {
  142.       for(u16 t=0; t<_tracks_len; ++t){
  143.         tracks[t].vol_old = volumeNew;
  144.         tracks[t].vol_new = volumeNew;
  145.       }
  146.  
  147.     }
  148.  
  149.     UNLOCK_MUTEX;
  150.  
  151.  
  152.  
  153.   }
  154.  
  155.  
  156.  
  157.   return true;
  158.  
  159. }
  160.  
  161.  
  162.  
  163.  
  164.  
  165. // Used to modify speed values to account for different sample rates
  166. //
  167. // (Volume doesn't need this modifier, since it operates
  168. //  on the destination buffer, NOT the source buffer!)
  169. #define SMPRATE_MOD_RAW(_src_samplerate) \
  170.   (  (f64)(_src_samplerate)/sampleRate  )
  171.  
  172. // (This one assumes .audio != nullptr, so just be aware of that)
  173. #define SMPRATE_MOD(_track_lvalue) \
  174.   SMPRATE_MOD_RAW(_track_lvalue.audio->sampleRate)
  175.  
  176. bool SoundEngine::setSpeed(f64 speedNew,
  177.                            u16 track, bool forced)
  178. {
  179.   if(SNDENGINE_IS_INVALID) return false;
  180.  
  181.   // Normally, anything at or below 0 will cause the clip
  182.   // to stop immediately, but this check couldn't hurt
  183.   if(speedNew < 0.0) speedNew = 0.0;
  184.  
  185.  
  186.  
  187.   if(track != 0xFFFF){
  188.     if(SNDENGINE_IS_TRACK_OOB) return false;
  189.  
  190.     SoundEngineTrack& _track = _tracks[track];
  191.     if(_track.audio == nullptr) return true; // Track is empty; exit early
  192.  
  193.     LOCK_MUTEX;
  194.  
  195.     speedNew *= SMPRATE_MOD(_track);
  196.  
  197.     if(forced) _track.spd_old = speedNew;
  198.     _track.spd_new = speedNew;
  199.  
  200.     UNLOCK_MUTEX;
  201.  
  202.  
  203.  
  204.   } else {
  205.     SoundEngineTrack* tracks = _tracks;
  206.  
  207.     LOCK_MUTEX;
  208.  
  209.     if(!forced){
  210.       for(u16 t=0; t<_tracks_len; ++t){
  211.         if(tracks[t].audio == nullptr) continue; // Track is empty; skip
  212.         tracks[t].spd_new = speedNew*SMPRATE_MOD(tracks[t]);
  213.       }
  214.  
  215.     } else {
  216.       for(u16 t=0; t<_tracks_len; ++t){
  217.         if(tracks[t].audio == nullptr) continue; // Track is empty; skip
  218.         f64 _speedNew = speedNew*SMPRATE_MOD(tracks[t]);
  219.         tracks[t].spd_old = _speedNew;
  220.         tracks[t].spd_new = _speedNew;
  221.       }
  222.  
  223.     }
  224.  
  225.     UNLOCK_MUTEX;
  226.  
  227.  
  228.  
  229.   }
  230.  
  231.  
  232.  
  233.   return true;
  234.  
  235. }
  236.  
  237.  
  238.  
  239.  
  240.  
  241. bool SoundEngine::setVolumeDelta(f32 deltaSecondsL, f32 deltaSecondsR,
  242.                                  u16 track)
  243. {
  244.   if(SNDENGINE_IS_INVALID) return false;
  245.  
  246.   // Calculate delta, with a divide-by zero check
  247.   StereoF32 delta = { 0.0f, 0.0f };
  248.  
  249.   if(sampleRate){
  250.     if(deltaSecondsL) delta.l = (1.0f/deltaSecondsL) / sampleRate;
  251.     if(deltaSecondsR) delta.r = (1.0f/deltaSecondsR) / sampleRate;
  252.   }
  253.  
  254.  
  255.  
  256.   if(track != 0xFFFF){
  257.     if(SNDENGINE_IS_TRACK_OOB) return false;
  258.     _tracks[track].volDelta = delta;
  259.  
  260.   } else {
  261.     SoundEngineTrack* tracks = _tracks;
  262.     for(u16 t=0; t<_tracks_len; ++t)
  263.       tracks[t].volDelta = delta;
  264.  
  265.   }
  266.  
  267.   return true;
  268.  
  269. }
  270.  
  271.  
  272.  
  273.  
  274.  
  275. bool SoundEngine::setSpeedDelta(f64 deltaSeconds,
  276.                                 u16 track)
  277. {
  278.   if(SNDENGINE_IS_INVALID) return false;
  279.  
  280.   // Calculate delta, with a divide-by zero check
  281.   f64 delta = (deltaSeconds != 0)  ?  ((1.0/deltaSeconds) / sampleRate)  :  0.0;
  282.  
  283.  
  284.  
  285.   if(track != 0xFFFF){
  286.     if(SNDENGINE_IS_TRACK_OOB) return false;
  287.  
  288.     SoundEngineTrack& _track = _tracks[track];
  289.     if(_track.audio == nullptr) return true; // Track is empty; skip
  290.  
  291.     _track.spdDelta = delta*SMPRATE_MOD(_track);
  292.  
  293.  
  294.   } else {
  295.     SoundEngineTrack* tracks = _tracks;
  296.  
  297.     for(u16 t=0; t<_tracks_len; ++t){
  298.       if(tracks[t].audio == nullptr) continue; // Track is empty; skip
  299.       tracks[t].spdDelta = delta*SMPRATE_MOD(tracks[t]);
  300.  
  301.     }
  302.  
  303.  
  304.   }
  305.  
  306.   return true;
  307.  
  308. }
  309.  
  310.  
  311.  
  312.  
  313.  
  314. #define PLAY_ASSERT(_success, _err_msg) \
  315.   if(!(_success)){ _printf("ERROR: %s\n", _err_msg); return 0xFFFE; }
  316.  
  317. u16 SoundEngine::play(const AudioData& audio,
  318.                       f32 volumeL, f32 volumeR, f64 speed)
  319. {
  320.   if(SNDENGINE_IS_INVALID) return 0xFFFE;
  321.  
  322.   PLAY_ASSERT(audio.hdr, "audio.hdr = nullptr");
  323.   PLAY_ASSERT(audio.hdr->format == SMPFMT_F32, "audio.hdr->format != SMPFMT_F32");
  324.   PLAY_ASSERT(audio.hdr->channels == 1  ||  audio.hdr->channels == 2,
  325.               "audio.hdr->channels is neither mono nor stereo")
  326.  
  327.  
  328.   f64 timestamp = timeGetSeconds();
  329.  
  330.  
  331.   // Will remain and return as 0xFFFF if no available track was found
  332.   u16 queuedTrackNum = 0xFFFF;
  333.  
  334.   SoundEngineTrack* tracks = _tracks;
  335.  
  336.  
  337.  
  338.   // (Is locking actually necessary here?)
  339.   LOCK_MUTEX; // TBD: If timing problems start to occur, remove the lock
  340.  
  341.   for(u16 t=0; t<_tracks_len; ++t){
  342.     SoundEngineTrack& track = tracks[t];
  343.  
  344.  
  345.     if(track.audio == nullptr){
  346.       track.timestamp = timestamp;
  347.  
  348.     //track.position  = <set inside callback>;
  349.  
  350.       track.spd_old   = MAX(speed*SMPRATE_MOD_RAW(audio.hdr->sampleRate), 0.0);
  351.       track.spd_new   = track.spd_old;
  352.       track.spdDelta  = 0.0;
  353.  
  354.       track.vol_old   = {volumeL, volumeR};
  355.       track.vol_new   = {volumeL, volumeR};
  356.       track.volDelta  = {   0.0f,    0.0f};
  357.       track.volMaster = audio.volume;
  358.  
  359.       track.loops     = audio.hdr->loopCount;
  360.       track.stopping  = false;
  361.  
  362.       //set last to make sure that all relevant members
  363.        //of the track are set before beginning
  364.       track.audio     = audio.hdr;
  365.  
  366.       queuedTrackNum = t; break;
  367.  
  368.     }
  369.  
  370.   }
  371.  
  372.   UNLOCK_MUTEX;
  373.  
  374.   return queuedTrackNum;
  375.  
  376. }
  377.  
  378.  
  379.  
  380.  
  381.  
  382. bool SoundEngine::stop(u16 track, bool forced){
  383.   if(SNDENGINE_IS_INVALID) return false;
  384.  
  385.   // If !forced, 'stop' the track by setting a fade-out of 10ms,
  386.   // whereupon the track will *actually* stop automatically
  387.   f32 _fadeoutDelta = (1.0f/-0.010f) / sampleRate;
  388.   StereoF32 fadeoutDelta = {_fadeoutDelta, _fadeoutDelta};
  389.  
  390.  
  391.  
  392.   if(track != 0xFFFF){
  393.     if(SNDENGINE_IS_TRACK_OOB) return false;
  394.  
  395.     SoundEngineTrack& _track = _tracks[track];
  396.     if(_track.audio == nullptr) return true; // Track is empty; skip
  397.  
  398.     if(!forced) _track.volDelta = fadeoutDelta;
  399.     else        _track.audio    = nullptr;
  400.  
  401.  
  402.  
  403.   } else {
  404.     SoundEngineTrack* tracks = _tracks;
  405.  
  406.     if(!forced){
  407.       for(u16 t=0; t<_tracks_len; ++t)
  408.         tracks[t].volDelta = fadeoutDelta;
  409.  
  410.     } else {
  411.       for(u16 t=0; t<_tracks_len; ++t)
  412.         tracks[t].audio = nullptr;
  413.  
  414.     }
  415.  
  416.  
  417.  
  418.   }
  419.  
  420.  
  421.  
  422.   return true;
  423.  
  424. }
  425.  
  426.  
  427.  
  428.  
  429.  
  430. // Returns false if timeout is reached, true otherwise
  431. static inline bool _waitForTrack(const AudioDataHeader*& audio,
  432.                                  f64 timestampSec, f64 timeoutSec)
  433. {
  434.   while(audio != nullptr){
  435.     timeSleep(5);
  436.     if((timeGetSeconds()-timestampSec) > timeoutSec) return false;
  437.   }
  438.  
  439.   return true;
  440.  
  441. }
  442.  
  443.  
  444.  
  445. bool SoundEngine::waitForTrack(f64 timeoutSeconds, u16 track){
  446.   if(SNDENGINE_IS_INVALID) return false;
  447.  
  448.   f64 timestampSeconds = timeGetSeconds();
  449.  
  450.   if(timeoutSeconds == 0.0) timeoutSeconds = 1e30; // Effectively infinite
  451.  
  452.  
  453.  
  454.   if(track != 0xFFFF){
  455.     if(SNDENGINE_IS_TRACK_OOB) return false;
  456.  
  457.     if(!_waitForTrack(_tracks[track].audio,
  458.                       timestampSeconds, timeoutSeconds))
  459.     {
  460.       return false;
  461.     }
  462.  
  463.  
  464.  
  465.   } else {
  466.     SoundEngineTrack* tracks = _tracks;
  467.  
  468.     for(u16 t=0; t<_tracks_len; ++t){
  469.       if(!_waitForTrack(tracks[t].audio,
  470.                         timestampSeconds, timeoutSeconds))
  471.       {
  472.         return false;
  473.       }
  474.  
  475.     }
  476.  
  477.     // (alternate method)
  478.     //
  479.     //while(getActiveTracks() > 0){
  480.     //  timeSleep(10);
  481.     //  if((timeGetSeconds()-timestampSeconds) > timeoutSeconds) return false;
  482.     //}
  483.  
  484.  
  485.  
  486.   }
  487.  
  488.  
  489.  
  490.   return true; // Will return true only if track(s) finished before timeout
  491.  
  492. }
  493.  
  494.  
  495.  
  496.  
  497.  
  498.  
  499. #endif
  500. /******************************************************************************/
  501. /******************************************************************************/
  502. //"opengl_metaballs_2025-06-26\src\kit_utils\sound\SoundEngine_mixTracks.cpp":
  503. // DO NOT USE THIS AS REFERENCE FOR A NEW PROJECT
  504. // THIS IS A HACKY PORT OF KIT_SDL2'S SOUNDENGINE!!!!
  505. #include <public_stuff.hpp>
  506.  
  507. #ifdef SOUND_STUFF_USED
  508.  
  509. #include <windows.h>
  510.  
  511.  
  512.  
  513. //lower clip's volume to 0 within 10ms if speed reaches 0
  514. #define speed0VolDeltaMS (10.0f)
  515.  
  516.  
  517. #define MUTEX_PTR ((CRITICAL_SECTION*)&_lock)
  518.  
  519. #define LOCK_MUTEX \
  520.   bool locked = false; \
  521.   if(MUTEX_PTR != nullptr){ EnterCriticalSection(MUTEX_PTR); locked = true; }
  522.  
  523. #define UNLOCK_MUTEX                      { \
  524.   if(locked){ LeaveCriticalSection(MUTEX_PTR); locked = false; } }
  525.  
  526.  
  527.  
  528. /******************************************************************************/
  529.  
  530.  
  531.  
  532. #define HANDLE_LOOP \
  533.   /* loop handling (or end-of-clip handling i guess) */                                 \
  534.   if(trk.position >= loopEnd){ /* audio clip finished loop */                           \
  535.     if(!trk.loops){ trk.stopping = true; break; } /* mark as inactive and break */      \
  536.     if(trk.loops != 65535) --trk.loops; /* decrement loops (unless infinite) */   \
  537.     while(trk.position >= loopEnd)                                                      \
  538.       trk.position -= loopDifference; /* jump back to the loop's starting point */      \
  539.                                                                                         \
  540.   } else if(trk.position < 0){ /* if clip has yet to start playing */                   \
  541.     if(trk.position < -trk.spd_old){                                                    \
  542.       trk.position += trk.spd_old; /* step forward by current speed */                  \
  543.       continue;                                                                         \
  544.     } else { /* if position >= -speed, the clip should start on this sample */          \
  545.       trk.position = 0.0; /* make sure clip starts at exactly 0 */                      \
  546.     }                                                                                   \
  547.                                                                                         \
  548.   }
  549.  
  550.  
  551.  
  552. #define HANDLE_SPEED_UPDATE \
  553.   /* change old and new speed by speedDelta */                                  \
  554.   f64 currentSpeedDelta = track.spdDelta;                                       \
  555.   trk.spd_old += currentSpeedDelta;                                             \
  556.   trk.spd_new += currentSpeedDelta;                                             \
  557.                                                                                 \
  558.   t += t_inc; /* raise interpolation t value */                                 \
  559.                                                                                 \
  560.   /* start rapid fade out if clip's new speed <= 0 */                           \
  561.   if(LERP2(trk.spd_old, trk.spd_new, t) <= 0.0){                                \
  562.     trk.spd_old = trk.spd_new = 0.0;                                            \
  563.     /* volDelta is sorta volatile, so that might need to be accounted for... */ \
  564.     track.volDelta = speed0VDelta;                                              \
  565.   }
  566.  
  567.  
  568.  
  569. /******************************************************************************/
  570.  
  571.  
  572.  
  573. static inline f32 linearSmpMono(const f32* src, f64 position,
  574.                                 u64 loopEnd = 0xFFFFFFFFFFFFFFFF)
  575. {
  576.   const u64 intPosition = (u64)position;
  577.   const f32 modPosition = (f32)(position-intPosition); //the position's fraction
  578.  
  579.   const f32 smpA = src[ intPosition           ];
  580.   const f32 smpB = src[(intPosition+1)%loopEnd];
  581.  
  582.   return LERP2(smpA, smpB, modPosition);
  583.  
  584. }
  585.  
  586.  
  587.  
  588. static inline StereoF32 linearSmpStereo(const StereoF32* src, f64 position,
  589.                                         u64 loopEnd = 0xFFFFFFFFFFFFFFFF)
  590. {
  591.   const u64 intPosition = (u64)position;
  592.   const f32 modPosition = (f32)(position-intPosition); //the position's fraction
  593.  
  594.   const StereoF32 smpA = src[ intPosition           ];
  595.   const StereoF32 smpB = src[(intPosition+1)%loopEnd];
  596.  
  597.   return {LERP2(smpA.l, smpB.l, modPosition),
  598.           LERP2(smpA.r, smpB.r, modPosition)};
  599.  
  600. }
  601.  
  602.  
  603.  
  604. /******************************************************************************/
  605.  
  606.  
  607.  
  608. static inline bool mixTrack(SoundEngineTrack& track, f64 refTimestamp,
  609.                             bool sIVIZ, f32 sampleRate, StereoF32 volumeMaster,
  610.                             StereoF32* dst, size_t dst_len)
  611. {
  612.  
  613.   //the value volumeDelta is set to if the track hits 0x speed
  614.   const f32 _speed0VDelta = -( (1000.0/speed0VolDeltaMS) / sampleRate );
  615.   const StereoF32 speed0VDelta = {_speed0VDelta, _speed0VDelta};
  616.  
  617.   //create stack copy of track data
  618.   //(all but a track's deltas are to be referenced with this!)
  619.   SoundEngineTrack trk = track;
  620.  
  621.   if(trk.audio == nullptr) return false; //just in case
  622.  
  623.   //make copies of relevant data from trk.audio
  624.   const bool       stereo    = trk.audio->channels==2;
  625.   const u64        loopStart = trk.audio->loopStart;
  626.   const u64        loopEnd   = trk.audio->loopEnd; //(doubles as src_len)
  627.   const f32*       src_m     = (      f32*)trk.audio->samples;
  628.   const StereoF32* src_s     = (StereoF32*)trk.audio->samples;
  629.  
  630.   u64 loopDifference = loopEnd - loopStart;
  631.  
  632.   //calculate position based on the difference between
  633.    //the reference and the queued audio clip's timestamps
  634.   if(trk.timestamp > 0.0){
  635.     f64 difference = refTimestamp-trk.timestamp; //seconds
  636.     difference *= sampleRate; //seconds -> samples
  637.  
  638.     //(starts playing when position reaches 0)
  639.     trk.position = -(difference*trk.spd_old);
  640.     //(alternate calculation; try this if timing feels wrong or something)
  641.     //trk.position = difference*trk.spd_old - dst_len;
  642.  
  643.     track.timestamp = 0.0; //ensures that this only occurs once per clip queued
  644.  
  645.   }
  646.  
  647.   //used for interpolating speed and volume settings from old to new
  648.   f32       t     = 0.0f;
  649.   const f32 t_inc = 1.0f/dst_len; //t_inc[rement]
  650.  
  651.  
  652.  
  653.   if(!stereo) //source is mono
  654.   for(size_t i=0; i<dst_len; ++i){
  655.     HANDLE_LOOP;
  656.  
  657.     //get sample
  658.     f32 smp = linearSmpMono(src_m, trk.position, loopEnd);
  659.  
  660.     //apply volumes while interpolating between its old and new state
  661.     smp *= volumeMaster.l * LERP2(trk.vol_old.l, trk.vol_new.l, t);
  662.  
  663.     //mix sample to dst (which is always stereo, so mix smp into both channels)
  664.     dst[i].l += smp;
  665.     dst[i].r += smp;
  666.  
  667.     //update position by speed, while interpolating between speed's old and new state
  668.     trk.position += LERP2(trk.spd_old, trk.spd_new, t);
  669.  
  670.     //change old and new volume by volumeDelta
  671.      //(deltas are referenced directly with track instead of trk)
  672.     const f32 currentVolumeDelta = track.volDelta.l;
  673.     trk.vol_old.l += currentVolumeDelta;
  674.     trk.vol_new.l += currentVolumeDelta;
  675.  
  676.     //make sure both volumes are always between 0.0f and 1.0f
  677.     trk.vol_old.l = CLAMP(trk.vol_old.l, 0.0f, 1.0f);
  678.     trk.vol_new.l = CLAMP(trk.vol_new.l, 0.0f, 1.0f);
  679.  
  680.     HANDLE_SPEED_UPDATE
  681.  
  682.   }
  683.  
  684.  
  685.  
  686.   else //source is stereo
  687.   for(size_t i=0; i<dst_len; ++i){
  688.     HANDLE_LOOP;
  689.  
  690.     //get sample
  691.     StereoF32 smp = linearSmpStereo(src_s, trk.position, loopEnd);
  692.  
  693.     //apply volumes while interpolating between its old and new state
  694.     smp.l *= volumeMaster.l * LERP2(trk.vol_old.l, trk.vol_new.l, t);
  695.     smp.r *= volumeMaster.r * LERP2(trk.vol_old.r, trk.vol_new.r, t);
  696.  
  697.     //mix sample to dst
  698.     dst[i].l += smp.l;
  699.     dst[i].r += smp.r;
  700.  
  701.     //update position by speed, while interpolating between speed's old and new state
  702.     trk.position += LERP2(trk.spd_old, trk.spd_new, t);
  703.  
  704.     //change old and new volume by volumeDelta
  705.      //(deltas are referenced directly with track instead of trk)
  706.     const StereoF32 currentVolumeDelta = track.volDelta;
  707.     trk.vol_old.l += currentVolumeDelta.l;
  708.     trk.vol_old.r += currentVolumeDelta.r;
  709.     trk.vol_new.l += currentVolumeDelta.l;
  710.     trk.vol_new.r += currentVolumeDelta.r;
  711.  
  712.     //make sure both volumes are always between 0.0f and 1.0f
  713.     trk.vol_old.l = CLAMP(trk.vol_old.l, 0.0f, 1.0f);
  714.     trk.vol_old.r = CLAMP(trk.vol_old.r, 0.0f, 1.0f);
  715.     trk.vol_new.l = CLAMP(trk.vol_new.l, 0.0f, 1.0f);
  716.     trk.vol_new.r = CLAMP(trk.vol_new.r, 0.0f, 1.0f);
  717.  
  718.     HANDLE_SPEED_UPDATE
  719.  
  720.   }
  721.  
  722.  
  723.  
  724.   //set relevant old values to the new ones
  725.    //(deltas don't need to be set)
  726.   track.position = trk.position;
  727.   track.spd_old  = trk.spd_new; //both old *and* new speeds
  728.   track.spd_new  = trk.spd_new;  //must be set to their new values
  729.   track.vol_old  = trk.vol_new; //both old *and* new volumes
  730.   track.vol_new  = trk.vol_new;  //must be set to their new values
  731.   track.loops    = trk.loops;
  732.   track.stopping = trk.stopping;
  733.  
  734.   //if stopIfVolumeIsZero while volume is <=0,
  735.    //mark the track as stopping (if it wasn't already)
  736.   //(sIVIZ is short for stopIfVolumeIsZero)
  737.   if((!stereo  &&  sIVIZ  &&  trk.vol_new.l <= 0) ||
  738.      ( stereo  &&  sIVIZ  &&  trk.vol_new.l <= 0  &&  trk.vol_new.r <= 0))
  739.   {
  740.     track.stopping = true;
  741.   }
  742.  
  743.   //a track is marked as complete if either the clip finishes,
  744.    //or if the volume goes at or below 0 while stopIfVolumeIsZero is true
  745.   if(track.stopping) track.audio = nullptr;
  746.  
  747.   return true;
  748.  
  749. }
  750.  
  751.  
  752.  
  753. /******************************************************************************/
  754.  
  755.  
  756.  
  757. //returns referenceTimestamp, or 0 on error
  758. f64 SoundEngine::mixTracks(StereoF32* buffer, size_t buffer_len,
  759.                            f64 referenceTimestamp)
  760. {
  761.   //if(MUTEX_PTR == nullptr) return 0.0;
  762.  
  763.   LOCK_MUTEX;
  764.  
  765.   if(false /*_tracks == nullptr*/){
  766.     referenceTimestamp = 0.0;
  767.     _unlock_mutex_and_return:
  768.     UNLOCK_MUTEX;
  769.     return referenceTimestamp;
  770.   }
  771.  
  772.   //this will occur if you accidentally use the desired audio device's info
  773.    //while sampleRate is set to 0 (which indicates that the default should
  774.    //be used), instead of the audio device's actual sample rate.
  775.    //this is because the desired info is a const that is not modified,
  776.    //and thus keeping .sampleRate as 0 will set this to 0 as well.
  777.   //(TL;DR: use AudioDevice.info.sampleRate after construction instead
  778.    //of using the AudioDeviceInfo you fed to the constructor)
  779.   if(sampleRate == 0.0f){ UNLOCK_MUTEX; return 0.0; }
  780.  
  781.  
  782.  
  783.   SoundEngineTrack* tracks = _tracks;
  784.   bool               sIVIZ = stopIfVolumeIsZero;
  785.   f32              smpRate = sampleRate;
  786.   StereoF32      volMaster = volumeMaster;
  787.  
  788.   for(u16 t=0; t<_tracks_len; ++t){
  789.     if(tracks[t].audio == nullptr) continue;
  790.  
  791.     //modify SoundEngine's master volume by the track's master volume
  792.     StereoF32 _volMaster = volMaster;
  793.     _volMaster.l *= tracks[t].volMaster.l;
  794.     _volMaster.r *= tracks[t].volMaster.r;
  795.  
  796.     if(!mixTrack(tracks[t], referenceTimestamp, sIVIZ,
  797.                  smpRate, _volMaster, buffer, buffer_len))
  798.     {
  799.       return 0;
  800.     }
  801.  
  802.   }
  803.  
  804.  
  805.  
  806.   goto _unlock_mutex_and_return;
  807.  
  808. }
  809.  
  810.  
  811.  
  812.  
  813.  
  814. #endif
  815. /******************************************************************************/
  816. /******************************************************************************/
  817. //"opengl_metaballs_2025-06-26\src\win32\audio.cpp":
  818. #include <win32/audio.hpp>
  819.  
  820. #include <public_stuff.hpp>
  821.  
  822.  
  823.  
  824.  
  825.  
  826. void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp);
  827.  
  828.  
  829.  
  830.  
  831.  
  832. #define _chunk_count 3
  833. #define fade_delta_len 0.010f //for 10ms
  834.  
  835. static StereoF32 samples_in[AUDIO_SAMPLES_MAX];
  836. static StereoS16 _chunk_data[_chunk_count][AUDIO_SAMPLES_MAX];
  837. static WAVEHDR   _chunk_header[_chunk_count];
  838. static int       _chunk_which;
  839.  
  840. static HWAVEOUT wave_out;
  841.  
  842. static bool fade_in     = true;
  843. static f32  fade_volume = 0.0f;
  844.  
  845. u32 audio_samples = 0;
  846. u32 sample_rate   = 0;
  847.  
  848.  
  849.  
  850.  
  851.  
  852. static inline void call_user_audio(int which){
  853.   // Zero out user buffer, before invoking user function
  854.   memSet(samples_in, 0, audio_samples*sizeof(StereoF32));
  855.   user_audio(samples_in, audio_samples, sample_rate, timeGetSeconds());
  856.  
  857.   // Convert stereo f32 (-1.0f to 1.0f) to stereo s16 (-32768 to 32767)
  858.   StereoS16* samples_out = _chunk_data[which];
  859.  
  860.   // 'By how much does the fade volume change every frame?'
  861.   const f32 fade_delta = 1.0f / (fade_delta_len*sample_rate);
  862.  
  863.  
  864.  
  865.   if(fade_in && fade_volume>=1.0f){ // Fully faded in
  866.     for(u32 i=0; i<audio_samples; ++i){
  867.       samples_out[i].l  =  (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
  868.       samples_out[i].r  =  (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
  869.  
  870.     }
  871.  
  872.   } else if(fade_in){ // Currently fading in
  873.     for(u32 i=0; i<audio_samples; ++i){
  874.       f32 smp_l  =  (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
  875.       f32 smp_r  =  (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
  876.  
  877.       smp_l *= fade_volume;
  878.       smp_r *= fade_volume;
  879.       fade_volume = MIN(fade_volume+fade_delta, 1.0f);
  880.  
  881.       samples_out[i].l = smp_l;
  882.       samples_out[i].r = smp_r;
  883.  
  884.     }
  885.  
  886.   } else { // if(!fade_in)
  887.     for(u32 i=0; i<audio_samples; ++i){
  888.       f32 smp_l  =  (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
  889.       f32 smp_r  =  (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
  890.  
  891.       smp_l *= fade_volume;
  892.       smp_r *= fade_volume;
  893.       fade_volume = MAX(fade_volume-fade_delta, 0.0f);
  894.  
  895.       samples_out[i].l = smp_l;
  896.       samples_out[i].r = smp_r;
  897.  
  898.     }
  899.  
  900.   }
  901.  
  902. }
  903.  
  904.  
  905.  
  906.  
  907.  
  908. void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT msg, DWORD_PTR inst,
  909.                           DWORD_PTR param1, DWORD_PTR param2)
  910. {
  911.   if(msg == WOM_DONE){
  912.     call_user_audio(_chunk_which);
  913.  
  914.     waveOutWrite(hwo, &_chunk_header[_chunk_which], sizeof(WAVEHDR));
  915.  
  916.     _chunk_which += 1;
  917.     _chunk_which %= _chunk_count;
  918.  
  919.   }
  920.  
  921. }
  922.  
  923.  
  924.  
  925.  
  926.  
  927. int WaveOutInit(){
  928.   MMRESULT err;
  929.   WAVEOUTCAPS devcaps;
  930.  
  931.   if((err = waveOutGetDevCapsA(WAVE_MAPPER, &devcaps, sizeof(WAVEOUTCAPS))))
  932.     return -1;
  933.  
  934.   if(devcaps.dwFormats & WAVE_FORMAT_4S16) // 44100Hz, Stereo, s16
  935.   {
  936.     audio_samples = AUDIO_SAMPLES_MAX/2;
  937.     sample_rate   = 44100;
  938.   }
  939.   else if(devcaps.dwFormats & WAVE_FORMAT_96S16) // 96000Hz, Stereo, s16
  940.   {
  941.     audio_samples = AUDIO_SAMPLES_MAX;
  942.     sample_rate   = 96000;
  943.   }
  944.   else if(devcaps.dwFormats & WAVE_FORMAT_2S16) // 22050Hz, Stereo, s16
  945.   {
  946.     audio_samples = AUDIO_SAMPLES_MAX/4;
  947.     sample_rate   = 22050;
  948.   }
  949.   else if(devcaps.dwFormats & WAVE_FORMAT_1S16) // 11025Hz, Stereo, s16
  950.   {
  951.     audio_samples = AUDIO_SAMPLES_MAX/8;
  952.     sample_rate   = 11025;
  953.   }
  954.   else // No valid 16-bit stereo sample rate is supported!
  955.   {
  956.     return -2;
  957.   }
  958.  
  959.  
  960.  
  961.   _chunk_which = 0;
  962.  
  963.  
  964.  
  965.   WAVEFORMATEX fmt;
  966.   fmt.wFormatTag     = WAVE_FORMAT_PCM;
  967.   fmt.nChannels      =  2;
  968.   fmt.nSamplesPerSec = sample_rate;
  969.   fmt.wBitsPerSample = 16;
  970.   fmt.cbSize         =  0;
  971.  
  972.   fmt.nBlockAlign     = fmt.nChannels      * fmt.wBitsPerSample / 8;
  973.   fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
  974.  
  975.  
  976.  
  977.   if(waveOutOpen(&wave_out, WAVE_MAPPER, &fmt,
  978.                  (DWORD_PTR)WaveOutProc, 0, CALLBACK_FUNCTION))
  979.   {
  980.     return -3;
  981.   }
  982.  
  983.   #define WO_VOLUME(_left, _right) ( ((_right)<<16) | (_left) )
  984.  
  985.   err = waveOutSetVolume(wave_out, WO_VOLUME(0xffff, 0xffff));
  986.  
  987.   // Even if the function isn't supported, hopefully it'll just default to 100%
  988.   if(err && err != MMSYSERR_NOTSUPPORTED) return -4;
  989.  
  990.  
  991.  
  992.   // Set header info and prime the audio buffers
  993.   for(u32 i=0; i<_chunk_count; ++i){
  994.     _chunk_header[i].lpData          = (CHAR*)_chunk_data[i];
  995.     _chunk_header[i].dwBufferLength  = audio_samples*sizeof(s16)*fmt.nChannels;
  996.     _chunk_header[i].dwBytesRecorded = 0;
  997.     _chunk_header[i].dwUser          = 0;
  998.     _chunk_header[i].dwFlags         = 0;
  999.     _chunk_header[i].dwLoops         = 0;
  1000.     _chunk_header[i].lpNext          = nullptr;
  1001.     _chunk_header[i].reserved        = 0;
  1002.  
  1003.     if(waveOutPrepareHeader(wave_out, &_chunk_header[i], sizeof(WAVEHDR)))
  1004.       return -5;
  1005.  
  1006.     memSet(_chunk_data[i], 0, _chunk_header[i].dwBufferLength);
  1007.     if(waveOutWrite(wave_out, &_chunk_header[i], sizeof(WAVEHDR)))
  1008.       return -6;
  1009.  
  1010.   }
  1011.  
  1012.  
  1013.  
  1014.   return 0;
  1015.  
  1016. }
  1017.  
  1018.  
  1019.  
  1020.  
  1021.  
  1022. void WaveOutQuit(){
  1023.   // Fade audio out smoothly before pausing
  1024.   fade_in = false;
  1025.   while(fade_volume > 0.0f) Sleep(10);
  1026.  
  1027.   // Also make sure all buffers have played
  1028.   #define chunk_time ((f64)(_chunk_count*audio_samples)/sample_rate)
  1029.   Sleep((DWORD)(chunk_time*1000));
  1030.  
  1031.   waveOutPause(wave_out);
  1032.  
  1033. }
  1034.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement