Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"opengl_metaballs_2025-06-26\src\kit_utils\sound\SoundEngine.cpp":
- // DO NOT USE THIS AS REFERENCE FOR A NEW PROJECT
- // THIS IS A HACKY PORT OF KIT_SDL2'S SOUNDENGINE!!!!
- #include <public_stuff.hpp>
- #ifdef SOUND_STUFF_USED
- #include <windows.h>
- #define SNDENGINE_IS_INVALID (_tracks_len == 0)
- #define SNDENGINE_IS_TRACK_OOB (track >= _tracks_len)
- #define MUTEX_PTR ((CRITICAL_SECTION*)&_lock)
- #define LOCK_MUTEX \
- bool locked = false; \
- if(MUTEX_PTR != nullptr){ EnterCriticalSection(MUTEX_PTR); locked = true; }
- #define UNLOCK_MUTEX { \
- if(locked){ LeaveCriticalSection(MUTEX_PTR); locked = false; } }
- SoundEngine::SoundEngine(u16 numTracks, f32 _sampleRate){
- if(numTracks < 1) return;
- _tracks_len = numTracks;
- mem_set(_tracks, 0, sizeof(SoundEngineTrack)*_tracks_len);
- InitializeCriticalSectionAndSpinCount(MUTEX_PTR, 4096);
- sampleRate = _sampleRate;
- }
- SoundEngine::~SoundEngine(){
- if(_tracks_len == 0) return;
- LOCK_MUTEX;
- for(u16 t=0; t<_tracks_len; ++t)
- _tracks[t].audio = nullptr;
- _tracks_len = 0;
- UNLOCK_MUTEX;
- DeleteCriticalSection(MUTEX_PTR);
- }
- bool SoundEngine::isTrackPlaying(u16 track){
- if(SNDENGINE_IS_INVALID ) return false;
- if(SNDENGINE_IS_TRACK_OOB) return false;
- return _tracks[track].audio != nullptr;
- }
- u16 SoundEngine::getActiveTracks(){
- if(SNDENGINE_IS_INVALID) return 0;
- u16 activeTracks = 0;
- SoundEngineTrack* tracks = _tracks;
- LOCK_MUTEX;
- for(u16 t=0; t<_tracks_len; ++t){
- if(tracks[t].audio != nullptr) ++activeTracks;
- }
- UNLOCK_MUTEX;
- return activeTracks;
- }
- bool SoundEngine::setVolume(f32 volumeL, f32 volumeR,
- u16 track, bool forced)
- {
- if(SNDENGINE_IS_INVALID) return false;
- // Normally, anything at or below 0 will cause the clip
- // to stop immediately, but this check couldn't hurt
- StereoF32 volumeNew = { MAX(volumeL, 0.0f), MAX(volumeR, 0.0f) };
- if(track != 0xFFFF){
- if(SNDENGINE_IS_TRACK_OOB) return false;
- SoundEngineTrack& _track = _tracks[track];
- if(_track.audio == nullptr) return true; // Track is empty; exit early
- LOCK_MUTEX;
- if(forced) _track.vol_old = volumeNew;
- _track.vol_new = volumeNew;
- UNLOCK_MUTEX;
- } else {
- SoundEngineTrack* tracks = _tracks;
- LOCK_MUTEX;
- if(!forced){
- for(u16 t=0; t<_tracks_len; ++t)
- tracks[t].vol_new = volumeNew;
- } else {
- for(u16 t=0; t<_tracks_len; ++t){
- tracks[t].vol_old = volumeNew;
- tracks[t].vol_new = volumeNew;
- }
- }
- UNLOCK_MUTEX;
- }
- return true;
- }
- // Used to modify speed values to account for different sample rates
- //
- // (Volume doesn't need this modifier, since it operates
- // on the destination buffer, NOT the source buffer!)
- #define SMPRATE_MOD_RAW(_src_samplerate) \
- ( (f64)(_src_samplerate)/sampleRate )
- // (This one assumes .audio != nullptr, so just be aware of that)
- #define SMPRATE_MOD(_track_lvalue) \
- SMPRATE_MOD_RAW(_track_lvalue.audio->sampleRate)
- bool SoundEngine::setSpeed(f64 speedNew,
- u16 track, bool forced)
- {
- if(SNDENGINE_IS_INVALID) return false;
- // Normally, anything at or below 0 will cause the clip
- // to stop immediately, but this check couldn't hurt
- if(speedNew < 0.0) speedNew = 0.0;
- if(track != 0xFFFF){
- if(SNDENGINE_IS_TRACK_OOB) return false;
- SoundEngineTrack& _track = _tracks[track];
- if(_track.audio == nullptr) return true; // Track is empty; exit early
- LOCK_MUTEX;
- speedNew *= SMPRATE_MOD(_track);
- if(forced) _track.spd_old = speedNew;
- _track.spd_new = speedNew;
- UNLOCK_MUTEX;
- } else {
- SoundEngineTrack* tracks = _tracks;
- LOCK_MUTEX;
- if(!forced){
- for(u16 t=0; t<_tracks_len; ++t){
- if(tracks[t].audio == nullptr) continue; // Track is empty; skip
- tracks[t].spd_new = speedNew*SMPRATE_MOD(tracks[t]);
- }
- } else {
- for(u16 t=0; t<_tracks_len; ++t){
- if(tracks[t].audio == nullptr) continue; // Track is empty; skip
- f64 _speedNew = speedNew*SMPRATE_MOD(tracks[t]);
- tracks[t].spd_old = _speedNew;
- tracks[t].spd_new = _speedNew;
- }
- }
- UNLOCK_MUTEX;
- }
- return true;
- }
- bool SoundEngine::setVolumeDelta(f32 deltaSecondsL, f32 deltaSecondsR,
- u16 track)
- {
- if(SNDENGINE_IS_INVALID) return false;
- // Calculate delta, with a divide-by zero check
- StereoF32 delta = { 0.0f, 0.0f };
- if(sampleRate){
- if(deltaSecondsL) delta.l = (1.0f/deltaSecondsL) / sampleRate;
- if(deltaSecondsR) delta.r = (1.0f/deltaSecondsR) / sampleRate;
- }
- if(track != 0xFFFF){
- if(SNDENGINE_IS_TRACK_OOB) return false;
- _tracks[track].volDelta = delta;
- } else {
- SoundEngineTrack* tracks = _tracks;
- for(u16 t=0; t<_tracks_len; ++t)
- tracks[t].volDelta = delta;
- }
- return true;
- }
- bool SoundEngine::setSpeedDelta(f64 deltaSeconds,
- u16 track)
- {
- if(SNDENGINE_IS_INVALID) return false;
- // Calculate delta, with a divide-by zero check
- f64 delta = (deltaSeconds != 0) ? ((1.0/deltaSeconds) / sampleRate) : 0.0;
- if(track != 0xFFFF){
- if(SNDENGINE_IS_TRACK_OOB) return false;
- SoundEngineTrack& _track = _tracks[track];
- if(_track.audio == nullptr) return true; // Track is empty; skip
- _track.spdDelta = delta*SMPRATE_MOD(_track);
- } else {
- SoundEngineTrack* tracks = _tracks;
- for(u16 t=0; t<_tracks_len; ++t){
- if(tracks[t].audio == nullptr) continue; // Track is empty; skip
- tracks[t].spdDelta = delta*SMPRATE_MOD(tracks[t]);
- }
- }
- return true;
- }
- #define PLAY_ASSERT(_success, _err_msg) \
- if(!(_success)){ _printf("ERROR: %s\n", _err_msg); return 0xFFFE; }
- u16 SoundEngine::play(const AudioData& audio,
- f32 volumeL, f32 volumeR, f64 speed)
- {
- if(SNDENGINE_IS_INVALID) return 0xFFFE;
- PLAY_ASSERT(audio.hdr, "audio.hdr = nullptr");
- PLAY_ASSERT(audio.hdr->format == SMPFMT_F32, "audio.hdr->format != SMPFMT_F32");
- PLAY_ASSERT(audio.hdr->channels == 1 || audio.hdr->channels == 2,
- "audio.hdr->channels is neither mono nor stereo")
- f64 timestamp = timeGetSeconds();
- // Will remain and return as 0xFFFF if no available track was found
- u16 queuedTrackNum = 0xFFFF;
- SoundEngineTrack* tracks = _tracks;
- // (Is locking actually necessary here?)
- LOCK_MUTEX; // TBD: If timing problems start to occur, remove the lock
- for(u16 t=0; t<_tracks_len; ++t){
- SoundEngineTrack& track = tracks[t];
- if(track.audio == nullptr){
- track.timestamp = timestamp;
- //track.position = <set inside callback>;
- track.spd_old = MAX(speed*SMPRATE_MOD_RAW(audio.hdr->sampleRate), 0.0);
- track.spd_new = track.spd_old;
- track.spdDelta = 0.0;
- track.vol_old = {volumeL, volumeR};
- track.vol_new = {volumeL, volumeR};
- track.volDelta = { 0.0f, 0.0f};
- track.volMaster = audio.volume;
- track.loops = audio.hdr->loopCount;
- track.stopping = false;
- //set last to make sure that all relevant members
- //of the track are set before beginning
- track.audio = audio.hdr;
- queuedTrackNum = t; break;
- }
- }
- UNLOCK_MUTEX;
- return queuedTrackNum;
- }
- bool SoundEngine::stop(u16 track, bool forced){
- if(SNDENGINE_IS_INVALID) return false;
- // If !forced, 'stop' the track by setting a fade-out of 10ms,
- // whereupon the track will *actually* stop automatically
- f32 _fadeoutDelta = (1.0f/-0.010f) / sampleRate;
- StereoF32 fadeoutDelta = {_fadeoutDelta, _fadeoutDelta};
- if(track != 0xFFFF){
- if(SNDENGINE_IS_TRACK_OOB) return false;
- SoundEngineTrack& _track = _tracks[track];
- if(_track.audio == nullptr) return true; // Track is empty; skip
- if(!forced) _track.volDelta = fadeoutDelta;
- else _track.audio = nullptr;
- } else {
- SoundEngineTrack* tracks = _tracks;
- if(!forced){
- for(u16 t=0; t<_tracks_len; ++t)
- tracks[t].volDelta = fadeoutDelta;
- } else {
- for(u16 t=0; t<_tracks_len; ++t)
- tracks[t].audio = nullptr;
- }
- }
- return true;
- }
- // Returns false if timeout is reached, true otherwise
- static inline bool _waitForTrack(const AudioDataHeader*& audio,
- f64 timestampSec, f64 timeoutSec)
- {
- while(audio != nullptr){
- timeSleep(5);
- if((timeGetSeconds()-timestampSec) > timeoutSec) return false;
- }
- return true;
- }
- bool SoundEngine::waitForTrack(f64 timeoutSeconds, u16 track){
- if(SNDENGINE_IS_INVALID) return false;
- f64 timestampSeconds = timeGetSeconds();
- if(timeoutSeconds == 0.0) timeoutSeconds = 1e30; // Effectively infinite
- if(track != 0xFFFF){
- if(SNDENGINE_IS_TRACK_OOB) return false;
- if(!_waitForTrack(_tracks[track].audio,
- timestampSeconds, timeoutSeconds))
- {
- return false;
- }
- } else {
- SoundEngineTrack* tracks = _tracks;
- for(u16 t=0; t<_tracks_len; ++t){
- if(!_waitForTrack(tracks[t].audio,
- timestampSeconds, timeoutSeconds))
- {
- return false;
- }
- }
- // (alternate method)
- //
- //while(getActiveTracks() > 0){
- // timeSleep(10);
- // if((timeGetSeconds()-timestampSeconds) > timeoutSeconds) return false;
- //}
- }
- return true; // Will return true only if track(s) finished before timeout
- }
- #endif
- /******************************************************************************/
- /******************************************************************************/
- //"opengl_metaballs_2025-06-26\src\kit_utils\sound\SoundEngine_mixTracks.cpp":
- // DO NOT USE THIS AS REFERENCE FOR A NEW PROJECT
- // THIS IS A HACKY PORT OF KIT_SDL2'S SOUNDENGINE!!!!
- #include <public_stuff.hpp>
- #ifdef SOUND_STUFF_USED
- #include <windows.h>
- //lower clip's volume to 0 within 10ms if speed reaches 0
- #define speed0VolDeltaMS (10.0f)
- #define MUTEX_PTR ((CRITICAL_SECTION*)&_lock)
- #define LOCK_MUTEX \
- bool locked = false; \
- if(MUTEX_PTR != nullptr){ EnterCriticalSection(MUTEX_PTR); locked = true; }
- #define UNLOCK_MUTEX { \
- if(locked){ LeaveCriticalSection(MUTEX_PTR); locked = false; } }
- /******************************************************************************/
- #define HANDLE_LOOP \
- /* loop handling (or end-of-clip handling i guess) */ \
- if(trk.position >= loopEnd){ /* audio clip finished loop */ \
- if(!trk.loops){ trk.stopping = true; break; } /* mark as inactive and break */ \
- if(trk.loops != 65535) --trk.loops; /* decrement loops (unless infinite) */ \
- while(trk.position >= loopEnd) \
- trk.position -= loopDifference; /* jump back to the loop's starting point */ \
- \
- } else if(trk.position < 0){ /* if clip has yet to start playing */ \
- if(trk.position < -trk.spd_old){ \
- trk.position += trk.spd_old; /* step forward by current speed */ \
- continue; \
- } else { /* if position >= -speed, the clip should start on this sample */ \
- trk.position = 0.0; /* make sure clip starts at exactly 0 */ \
- } \
- \
- }
- #define HANDLE_SPEED_UPDATE \
- /* change old and new speed by speedDelta */ \
- f64 currentSpeedDelta = track.spdDelta; \
- trk.spd_old += currentSpeedDelta; \
- trk.spd_new += currentSpeedDelta; \
- \
- t += t_inc; /* raise interpolation t value */ \
- \
- /* start rapid fade out if clip's new speed <= 0 */ \
- if(LERP2(trk.spd_old, trk.spd_new, t) <= 0.0){ \
- trk.spd_old = trk.spd_new = 0.0; \
- /* volDelta is sorta volatile, so that might need to be accounted for... */ \
- track.volDelta = speed0VDelta; \
- }
- /******************************************************************************/
- static inline f32 linearSmpMono(const f32* src, f64 position,
- u64 loopEnd = 0xFFFFFFFFFFFFFFFF)
- {
- const u64 intPosition = (u64)position;
- const f32 modPosition = (f32)(position-intPosition); //the position's fraction
- const f32 smpA = src[ intPosition ];
- const f32 smpB = src[(intPosition+1)%loopEnd];
- return LERP2(smpA, smpB, modPosition);
- }
- static inline StereoF32 linearSmpStereo(const StereoF32* src, f64 position,
- u64 loopEnd = 0xFFFFFFFFFFFFFFFF)
- {
- const u64 intPosition = (u64)position;
- const f32 modPosition = (f32)(position-intPosition); //the position's fraction
- const StereoF32 smpA = src[ intPosition ];
- const StereoF32 smpB = src[(intPosition+1)%loopEnd];
- return {LERP2(smpA.l, smpB.l, modPosition),
- LERP2(smpA.r, smpB.r, modPosition)};
- }
- /******************************************************************************/
- static inline bool mixTrack(SoundEngineTrack& track, f64 refTimestamp,
- bool sIVIZ, f32 sampleRate, StereoF32 volumeMaster,
- StereoF32* dst, size_t dst_len)
- {
- //the value volumeDelta is set to if the track hits 0x speed
- const f32 _speed0VDelta = -( (1000.0/speed0VolDeltaMS) / sampleRate );
- const StereoF32 speed0VDelta = {_speed0VDelta, _speed0VDelta};
- //create stack copy of track data
- //(all but a track's deltas are to be referenced with this!)
- SoundEngineTrack trk = track;
- if(trk.audio == nullptr) return false; //just in case
- //make copies of relevant data from trk.audio
- const bool stereo = trk.audio->channels==2;
- const u64 loopStart = trk.audio->loopStart;
- const u64 loopEnd = trk.audio->loopEnd; //(doubles as src_len)
- const f32* src_m = ( f32*)trk.audio->samples;
- const StereoF32* src_s = (StereoF32*)trk.audio->samples;
- u64 loopDifference = loopEnd - loopStart;
- //calculate position based on the difference between
- //the reference and the queued audio clip's timestamps
- if(trk.timestamp > 0.0){
- f64 difference = refTimestamp-trk.timestamp; //seconds
- difference *= sampleRate; //seconds -> samples
- //(starts playing when position reaches 0)
- trk.position = -(difference*trk.spd_old);
- //(alternate calculation; try this if timing feels wrong or something)
- //trk.position = difference*trk.spd_old - dst_len;
- track.timestamp = 0.0; //ensures that this only occurs once per clip queued
- }
- //used for interpolating speed and volume settings from old to new
- f32 t = 0.0f;
- const f32 t_inc = 1.0f/dst_len; //t_inc[rement]
- if(!stereo) //source is mono
- for(size_t i=0; i<dst_len; ++i){
- HANDLE_LOOP;
- //get sample
- f32 smp = linearSmpMono(src_m, trk.position, loopEnd);
- //apply volumes while interpolating between its old and new state
- smp *= volumeMaster.l * LERP2(trk.vol_old.l, trk.vol_new.l, t);
- //mix sample to dst (which is always stereo, so mix smp into both channels)
- dst[i].l += smp;
- dst[i].r += smp;
- //update position by speed, while interpolating between speed's old and new state
- trk.position += LERP2(trk.spd_old, trk.spd_new, t);
- //change old and new volume by volumeDelta
- //(deltas are referenced directly with track instead of trk)
- const f32 currentVolumeDelta = track.volDelta.l;
- trk.vol_old.l += currentVolumeDelta;
- trk.vol_new.l += currentVolumeDelta;
- //make sure both volumes are always between 0.0f and 1.0f
- trk.vol_old.l = CLAMP(trk.vol_old.l, 0.0f, 1.0f);
- trk.vol_new.l = CLAMP(trk.vol_new.l, 0.0f, 1.0f);
- HANDLE_SPEED_UPDATE
- }
- else //source is stereo
- for(size_t i=0; i<dst_len; ++i){
- HANDLE_LOOP;
- //get sample
- StereoF32 smp = linearSmpStereo(src_s, trk.position, loopEnd);
- //apply volumes while interpolating between its old and new state
- smp.l *= volumeMaster.l * LERP2(trk.vol_old.l, trk.vol_new.l, t);
- smp.r *= volumeMaster.r * LERP2(trk.vol_old.r, trk.vol_new.r, t);
- //mix sample to dst
- dst[i].l += smp.l;
- dst[i].r += smp.r;
- //update position by speed, while interpolating between speed's old and new state
- trk.position += LERP2(trk.spd_old, trk.spd_new, t);
- //change old and new volume by volumeDelta
- //(deltas are referenced directly with track instead of trk)
- const StereoF32 currentVolumeDelta = track.volDelta;
- trk.vol_old.l += currentVolumeDelta.l;
- trk.vol_old.r += currentVolumeDelta.r;
- trk.vol_new.l += currentVolumeDelta.l;
- trk.vol_new.r += currentVolumeDelta.r;
- //make sure both volumes are always between 0.0f and 1.0f
- trk.vol_old.l = CLAMP(trk.vol_old.l, 0.0f, 1.0f);
- trk.vol_old.r = CLAMP(trk.vol_old.r, 0.0f, 1.0f);
- trk.vol_new.l = CLAMP(trk.vol_new.l, 0.0f, 1.0f);
- trk.vol_new.r = CLAMP(trk.vol_new.r, 0.0f, 1.0f);
- HANDLE_SPEED_UPDATE
- }
- //set relevant old values to the new ones
- //(deltas don't need to be set)
- track.position = trk.position;
- track.spd_old = trk.spd_new; //both old *and* new speeds
- track.spd_new = trk.spd_new; //must be set to their new values
- track.vol_old = trk.vol_new; //both old *and* new volumes
- track.vol_new = trk.vol_new; //must be set to their new values
- track.loops = trk.loops;
- track.stopping = trk.stopping;
- //if stopIfVolumeIsZero while volume is <=0,
- //mark the track as stopping (if it wasn't already)
- //(sIVIZ is short for stopIfVolumeIsZero)
- if((!stereo && sIVIZ && trk.vol_new.l <= 0) ||
- ( stereo && sIVIZ && trk.vol_new.l <= 0 && trk.vol_new.r <= 0))
- {
- track.stopping = true;
- }
- //a track is marked as complete if either the clip finishes,
- //or if the volume goes at or below 0 while stopIfVolumeIsZero is true
- if(track.stopping) track.audio = nullptr;
- return true;
- }
- /******************************************************************************/
- //returns referenceTimestamp, or 0 on error
- f64 SoundEngine::mixTracks(StereoF32* buffer, size_t buffer_len,
- f64 referenceTimestamp)
- {
- //if(MUTEX_PTR == nullptr) return 0.0;
- LOCK_MUTEX;
- if(false /*_tracks == nullptr*/){
- referenceTimestamp = 0.0;
- _unlock_mutex_and_return:
- UNLOCK_MUTEX;
- return referenceTimestamp;
- }
- //this will occur if you accidentally use the desired audio device's info
- //while sampleRate is set to 0 (which indicates that the default should
- //be used), instead of the audio device's actual sample rate.
- //this is because the desired info is a const that is not modified,
- //and thus keeping .sampleRate as 0 will set this to 0 as well.
- //(TL;DR: use AudioDevice.info.sampleRate after construction instead
- //of using the AudioDeviceInfo you fed to the constructor)
- if(sampleRate == 0.0f){ UNLOCK_MUTEX; return 0.0; }
- SoundEngineTrack* tracks = _tracks;
- bool sIVIZ = stopIfVolumeIsZero;
- f32 smpRate = sampleRate;
- StereoF32 volMaster = volumeMaster;
- for(u16 t=0; t<_tracks_len; ++t){
- if(tracks[t].audio == nullptr) continue;
- //modify SoundEngine's master volume by the track's master volume
- StereoF32 _volMaster = volMaster;
- _volMaster.l *= tracks[t].volMaster.l;
- _volMaster.r *= tracks[t].volMaster.r;
- if(!mixTrack(tracks[t], referenceTimestamp, sIVIZ,
- smpRate, _volMaster, buffer, buffer_len))
- {
- return 0;
- }
- }
- goto _unlock_mutex_and_return;
- }
- #endif
- /******************************************************************************/
- /******************************************************************************/
- //"opengl_metaballs_2025-06-26\src\win32\audio.cpp":
- #include <win32/audio.hpp>
- #include <public_stuff.hpp>
- void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp);
- #define _chunk_count 3
- #define fade_delta_len 0.010f //for 10ms
- static StereoF32 samples_in[AUDIO_SAMPLES_MAX];
- static StereoS16 _chunk_data[_chunk_count][AUDIO_SAMPLES_MAX];
- static WAVEHDR _chunk_header[_chunk_count];
- static int _chunk_which;
- static HWAVEOUT wave_out;
- static bool fade_in = true;
- static f32 fade_volume = 0.0f;
- u32 audio_samples = 0;
- u32 sample_rate = 0;
- static inline void call_user_audio(int which){
- // Zero out user buffer, before invoking user function
- memSet(samples_in, 0, audio_samples*sizeof(StereoF32));
- user_audio(samples_in, audio_samples, sample_rate, timeGetSeconds());
- // Convert stereo f32 (-1.0f to 1.0f) to stereo s16 (-32768 to 32767)
- StereoS16* samples_out = _chunk_data[which];
- // 'By how much does the fade volume change every frame?'
- const f32 fade_delta = 1.0f / (fade_delta_len*sample_rate);
- if(fade_in && fade_volume>=1.0f){ // Fully faded in
- for(u32 i=0; i<audio_samples; ++i){
- samples_out[i].l = (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
- samples_out[i].r = (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
- }
- } else if(fade_in){ // Currently fading in
- for(u32 i=0; i<audio_samples; ++i){
- f32 smp_l = (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
- f32 smp_r = (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
- smp_l *= fade_volume;
- smp_r *= fade_volume;
- fade_volume = MIN(fade_volume+fade_delta, 1.0f);
- samples_out[i].l = smp_l;
- samples_out[i].r = smp_r;
- }
- } else { // if(!fade_in)
- for(u32 i=0; i<audio_samples; ++i){
- f32 smp_l = (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
- f32 smp_r = (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
- smp_l *= fade_volume;
- smp_r *= fade_volume;
- fade_volume = MAX(fade_volume-fade_delta, 0.0f);
- samples_out[i].l = smp_l;
- samples_out[i].r = smp_r;
- }
- }
- }
- void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT msg, DWORD_PTR inst,
- DWORD_PTR param1, DWORD_PTR param2)
- {
- if(msg == WOM_DONE){
- call_user_audio(_chunk_which);
- waveOutWrite(hwo, &_chunk_header[_chunk_which], sizeof(WAVEHDR));
- _chunk_which += 1;
- _chunk_which %= _chunk_count;
- }
- }
- int WaveOutInit(){
- MMRESULT err;
- WAVEOUTCAPS devcaps;
- if((err = waveOutGetDevCapsA(WAVE_MAPPER, &devcaps, sizeof(WAVEOUTCAPS))))
- return -1;
- if(devcaps.dwFormats & WAVE_FORMAT_4S16) // 44100Hz, Stereo, s16
- {
- audio_samples = AUDIO_SAMPLES_MAX/2;
- sample_rate = 44100;
- }
- else if(devcaps.dwFormats & WAVE_FORMAT_96S16) // 96000Hz, Stereo, s16
- {
- audio_samples = AUDIO_SAMPLES_MAX;
- sample_rate = 96000;
- }
- else if(devcaps.dwFormats & WAVE_FORMAT_2S16) // 22050Hz, Stereo, s16
- {
- audio_samples = AUDIO_SAMPLES_MAX/4;
- sample_rate = 22050;
- }
- else if(devcaps.dwFormats & WAVE_FORMAT_1S16) // 11025Hz, Stereo, s16
- {
- audio_samples = AUDIO_SAMPLES_MAX/8;
- sample_rate = 11025;
- }
- else // No valid 16-bit stereo sample rate is supported!
- {
- return -2;
- }
- _chunk_which = 0;
- WAVEFORMATEX fmt;
- fmt.wFormatTag = WAVE_FORMAT_PCM;
- fmt.nChannels = 2;
- fmt.nSamplesPerSec = sample_rate;
- fmt.wBitsPerSample = 16;
- fmt.cbSize = 0;
- fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
- fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
- if(waveOutOpen(&wave_out, WAVE_MAPPER, &fmt,
- (DWORD_PTR)WaveOutProc, 0, CALLBACK_FUNCTION))
- {
- return -3;
- }
- #define WO_VOLUME(_left, _right) ( ((_right)<<16) | (_left) )
- err = waveOutSetVolume(wave_out, WO_VOLUME(0xffff, 0xffff));
- // Even if the function isn't supported, hopefully it'll just default to 100%
- if(err && err != MMSYSERR_NOTSUPPORTED) return -4;
- // Set header info and prime the audio buffers
- for(u32 i=0; i<_chunk_count; ++i){
- _chunk_header[i].lpData = (CHAR*)_chunk_data[i];
- _chunk_header[i].dwBufferLength = audio_samples*sizeof(s16)*fmt.nChannels;
- _chunk_header[i].dwBytesRecorded = 0;
- _chunk_header[i].dwUser = 0;
- _chunk_header[i].dwFlags = 0;
- _chunk_header[i].dwLoops = 0;
- _chunk_header[i].lpNext = nullptr;
- _chunk_header[i].reserved = 0;
- if(waveOutPrepareHeader(wave_out, &_chunk_header[i], sizeof(WAVEHDR)))
- return -5;
- memSet(_chunk_data[i], 0, _chunk_header[i].dwBufferLength);
- if(waveOutWrite(wave_out, &_chunk_header[i], sizeof(WAVEHDR)))
- return -6;
- }
- return 0;
- }
- void WaveOutQuit(){
- // Fade audio out smoothly before pausing
- fade_in = false;
- while(fade_volume > 0.0f) Sleep(10);
- // Also make sure all buffers have played
- #define chunk_time ((f64)(_chunk_count*audio_samples)/sample_rate)
- Sleep((DWORD)(chunk_time*1000));
- waveOutPause(wave_out);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement