4coder-non-source/test_data/lots_of_files/handmade_audio.cpp

350 lines
14 KiB
C++
Raw Normal View History

2023-09-30 01:17:40 +00:00
/* ========================================================================
$File: $
$Date: $
$Revision: $
$Creator: Casey Muratori $
$Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $
======================================================================== */
internal void
OutputTestSineWave(game_state *GameState, game_sound_output_buffer *SoundBuffer, int ToneHz)
{
int16 ToneVolume = 3000;
int WavePeriod = SoundBuffer->SamplesPerSecond/ToneHz;
int16 *SampleOut = SoundBuffer->Samples;
for(int SampleIndex = 0;
SampleIndex < SoundBuffer->SampleCount;
++SampleIndex)
{
// TODO(casey): Draw this out for people
#if 1
real32 SineValue = sinf(GameState->tSine);
int16 SampleValue = (int16)(SineValue * ToneVolume);
#else
int16 SampleValue = 0;
#endif
*SampleOut++ = SampleValue;
*SampleOut++ = SampleValue;
#if 1
GameState->tSine += Tau32*1.0f/(real32)WavePeriod;
if(GameState->tSine > Tau32)
{
GameState->tSine -= Tau32;
}
#endif
}
}
internal playing_sound *
PlaySound(audio_state *AudioState, sound_id SoundID)
{
TIMED_FUNCTION();
if(!AudioState->FirstFreePlayingSound)
{
AudioState->FirstFreePlayingSound = PushStruct(AudioState->PermArena, playing_sound);
AudioState->FirstFreePlayingSound->Next = 0;
}
playing_sound *PlayingSound = AudioState->FirstFreePlayingSound;
AudioState->FirstFreePlayingSound = PlayingSound->Next;
PlayingSound->SamplesPlayed = 0;
// TODO(casey): Should these default to 0.5f/0.5f for centerred?
PlayingSound->CurrentVolume = PlayingSound->TargetVolume = V2(1.0f, 1.0f);
PlayingSound->dCurrentVolume = V2(0, 0);
PlayingSound->ID = SoundID;
PlayingSound->dSample = 1.0f;
PlayingSound->Next = AudioState->FirstPlayingSound;
AudioState->FirstPlayingSound = PlayingSound;
return(PlayingSound);
}
internal void
ChangeVolume(audio_state *AudioState, playing_sound *Sound, real32 FadeDurationInSeconds, v2 Volume)
{
if(Sound)
{
if(FadeDurationInSeconds <= 0.0f)
{
Sound->CurrentVolume = Sound->TargetVolume = Volume;
}
else
{
real32 OneOverFade = 1.0f / FadeDurationInSeconds;
Sound->TargetVolume = Volume;
Sound->dCurrentVolume = OneOverFade*(Sound->TargetVolume - Sound->CurrentVolume);
}
}
}
internal void
ChangePitch(audio_state *AudioState, playing_sound *Sound, real32 dSample)
{
if(Sound)
{
Sound->dSample = dSample;
}
}
internal void
OutputPlayingSounds(audio_state *AudioState,
game_sound_output_buffer *SoundBuffer, game_assets *Assets,
memory_arena *TempArena)
{
TIMED_FUNCTION();
temporary_memory MixerMemory = BeginTemporaryMemory(TempArena);
u32 GenerationID = BeginGeneration(Assets);
Assert((SoundBuffer->SampleCount & 3) == 0);
u32 ChunkCount = SoundBuffer->SampleCount / 4;
__m128 *RealChannel0 = PushArray(TempArena, ChunkCount, __m128, 16);
__m128 *RealChannel1 = PushArray(TempArena, ChunkCount, __m128, 16);
real32 SecondsPerSample = 1.0f / (real32)SoundBuffer->SamplesPerSecond;
#define AudioStateOutputChannelCount 2
__m128 One = _mm_set1_ps(1.0f);
__m128 Zero = _mm_set1_ps(0.0f);
// NOTE(casey): Clear out the mixer channels
{
__m128 *Dest0 = RealChannel0;
__m128 *Dest1 = RealChannel1;
for(u32 SampleIndex = 0;
SampleIndex < ChunkCount;
++SampleIndex)
{
_mm_store_ps((float *)Dest0++, Zero);
_mm_store_ps((float *)Dest1++, Zero);
}
}
// NOTE(casey): Sum all sounds
for(playing_sound **PlayingSoundPtr = &AudioState->FirstPlayingSound;
*PlayingSoundPtr;
)
{
playing_sound *PlayingSound = *PlayingSoundPtr;
bool32 SoundFinished = false;
uint32 TotalChunksToMix = ChunkCount;
__m128 *Dest0 = RealChannel0;
__m128 *Dest1 = RealChannel1;
while(TotalChunksToMix && !SoundFinished)
{
loaded_sound *LoadedSound = GetSound(Assets, PlayingSound->ID, GenerationID);
if(LoadedSound)
{
sound_id NextSoundInChain = GetNextSoundInChain(Assets, PlayingSound->ID);
PrefetchSound(Assets, NextSoundInChain);
v2 Volume = PlayingSound->CurrentVolume;
v2 dVolume = SecondsPerSample*PlayingSound->dCurrentVolume;
v2 dVolumeChunk = 4.0f*dVolume;
real32 dSample = PlayingSound->dSample;
// TODO(casey): If you use the 1.9f version, it can clearly repro a bug
// in the accuracy of the samples played calcs!
// real32 dSample = PlayingSound->dSample*1.9f;
real32 dSampleChunk = 4.0f*dSample;
// NOTE(casey): Channel 0
__m128 MasterVolume0 = _mm_set1_ps(AudioState->MasterVolume.E[0]);
__m128 Volume0 = _mm_setr_ps(Volume.E[0] + 0.0f*dVolume.E[0],
Volume.E[0] + 1.0f*dVolume.E[0],
Volume.E[0] + 2.0f*dVolume.E[0],
Volume.E[0] + 3.0f*dVolume.E[0]);
__m128 dVolume0 = _mm_set1_ps(dVolume.E[0]);
__m128 dVolumeChunk0 = _mm_set1_ps(dVolumeChunk.E[0]);
// NOTE(casey): Channel 1
__m128 MasterVolume1 = _mm_set1_ps(AudioState->MasterVolume.E[1]);
__m128 Volume1 = _mm_setr_ps(Volume.E[1] + 0.0f*dVolume.E[1],
Volume.E[1] + 1.0f*dVolume.E[1],
Volume.E[1] + 2.0f*dVolume.E[1],
Volume.E[1] + 3.0f*dVolume.E[1]);
__m128 dVolume1 = _mm_set1_ps(dVolume.E[1]);
__m128 dVolumeChunk1 = _mm_set1_ps(dVolumeChunk.E[1]);
Assert(PlayingSound->SamplesPlayed >= 0.0f);
u32 ChunksToMix = TotalChunksToMix;
r32 RealChunksRemainingInSound =
(LoadedSound->SampleCount - RoundReal32ToInt32(PlayingSound->SamplesPlayed)) / dSampleChunk;
u32 ChunksRemainingInSound = RoundReal32ToInt32(RealChunksRemainingInSound);
if(ChunksToMix > ChunksRemainingInSound)
{
ChunksToMix = ChunksRemainingInSound;
}
u32 VolumeEndsAt[AudioStateOutputChannelCount] = {};
for(u32 ChannelIndex = 0;
ChannelIndex < ArrayCount(VolumeEndsAt);
++ChannelIndex)
{
if(dVolumeChunk.E[ChannelIndex] != 0.0f)
{
real32 DeltaVolume = (PlayingSound->TargetVolume.E[ChannelIndex] -
Volume.E[ChannelIndex]);
u32 VolumeChunkCount = (u32)(((DeltaVolume / dVolumeChunk.E[ChannelIndex]) + 0.5f));
if(ChunksToMix > VolumeChunkCount)
{
ChunksToMix = VolumeChunkCount;
VolumeEndsAt[ChannelIndex] = VolumeChunkCount;
}
}
}
// TODO(casey): Handle stereo!
real32 BeginSamplePosition = PlayingSound->SamplesPlayed;
real32 EndSamplePosition = BeginSamplePosition + ChunksToMix*dSampleChunk;
real32 LoopIndexC = (EndSamplePosition - BeginSamplePosition) / (r32)ChunksToMix;
for(u32 LoopIndex = 0;
LoopIndex < ChunksToMix;
++LoopIndex)
{
real32 SamplePosition = BeginSamplePosition + LoopIndexC*(r32)LoopIndex;
// TODO(casey): Move volume up here to explicit.
#if 1
__m128 SamplePos = _mm_setr_ps(SamplePosition + 0.0f*dSample,
SamplePosition + 1.0f*dSample,
SamplePosition + 2.0f*dSample,
SamplePosition + 3.0f*dSample);
__m128i SampleIndex = _mm_cvttps_epi32(SamplePos);
__m128 Frac = _mm_sub_ps(SamplePos, _mm_cvtepi32_ps(SampleIndex));
__m128 SampleValueF = _mm_setr_ps(LoadedSound->Samples[0][((int32 *)&SampleIndex)[0]],
LoadedSound->Samples[0][((int32 *)&SampleIndex)[1]],
LoadedSound->Samples[0][((int32 *)&SampleIndex)[2]],
LoadedSound->Samples[0][((int32 *)&SampleIndex)[3]]);
__m128 SampleValueC = _mm_setr_ps(LoadedSound->Samples[0][((int32 *)&SampleIndex)[0] + 1],
LoadedSound->Samples[0][((int32 *)&SampleIndex)[1] + 1],
LoadedSound->Samples[0][((int32 *)&SampleIndex)[2] + 1],
LoadedSound->Samples[0][((int32 *)&SampleIndex)[3] + 1]);
__m128 SampleValue = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(One, Frac), SampleValueF),
_mm_mul_ps(Frac, SampleValueC));
#else
__m128 SampleValue = _mm_setr_ps(LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 0.0f*dSample)],
LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 1.0f*dSample)],
LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 2.0f*dSample)],
LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 3.0f*dSample)]);
#endif
__m128 D0 = _mm_load_ps((float *)&Dest0[0]);
__m128 D1 = _mm_load_ps((float *)&Dest1[0]);
D0 = _mm_add_ps(D0, _mm_mul_ps(_mm_mul_ps(MasterVolume0, Volume0), SampleValue));
D1 = _mm_add_ps(D1, _mm_mul_ps(_mm_mul_ps(MasterVolume1, Volume1), SampleValue));
_mm_store_ps((float *)&Dest0[0], D0);
_mm_store_ps((float *)&Dest1[0], D1);
++Dest0;
++Dest1;
Volume0 = _mm_add_ps(Volume0, dVolumeChunk0);
Volume1 = _mm_add_ps(Volume1, dVolumeChunk1);
}
PlayingSound->CurrentVolume.E[0] = ((real32 *)&Volume0)[0];
PlayingSound->CurrentVolume.E[1] = ((real32 *)&Volume1)[1];
for(u32 ChannelIndex = 0;
ChannelIndex < ArrayCount(VolumeEndsAt);
++ChannelIndex)
{
if(ChunksToMix == VolumeEndsAt[ChannelIndex])
{
PlayingSound->CurrentVolume.E[ChannelIndex] =
PlayingSound->TargetVolume.E[ChannelIndex];
PlayingSound->dCurrentVolume.E[ChannelIndex] = 0.0f;
}
}
PlayingSound->SamplesPlayed = EndSamplePosition;
Assert(TotalChunksToMix >= ChunksToMix);
TotalChunksToMix -= ChunksToMix;
if(ChunksToMix == ChunksRemainingInSound)
{
if(IsValid(NextSoundInChain))
{
PlayingSound->ID = NextSoundInChain;
Assert(PlayingSound->SamplesPlayed >= LoadedSound->SampleCount);
PlayingSound->SamplesPlayed -= (real32)LoadedSound->SampleCount;
if(PlayingSound->SamplesPlayed < 0)
{
PlayingSound->SamplesPlayed = 0.0f;
}
}
else
{
SoundFinished = true;
}
}
}
else
{
LoadSound(Assets, PlayingSound->ID);
break;
}
}
if(SoundFinished)
{
*PlayingSoundPtr = PlayingSound->Next;
PlayingSound->Next = AudioState->FirstFreePlayingSound;
AudioState->FirstFreePlayingSound = PlayingSound;
}
else
{
PlayingSoundPtr = &PlayingSound->Next;
}
}
// NOTE(casey): Convert to 16-bit
{
__m128 *Source0 = RealChannel0;
__m128 *Source1 = RealChannel1;
__m128i *SampleOut = (__m128i *)SoundBuffer->Samples;
for(u32 SampleIndex = 0;
SampleIndex < ChunkCount;
++SampleIndex)
{
__m128 S0 = _mm_load_ps((float *)Source0++);
__m128 S1 = _mm_load_ps((float *)Source1++);
__m128i L = _mm_cvtps_epi32(S0);
__m128i R = _mm_cvtps_epi32(S1);
__m128i LR0 = _mm_unpacklo_epi32(L, R);
__m128i LR1 = _mm_unpackhi_epi32(L, R);
__m128i S01 = _mm_packs_epi32(LR0, LR1);
*SampleOut++ = S01;
}
}
EndGeneration(Assets, GenerationID);
EndTemporaryMemory(MixerMemory);
}
internal void
InitializeAudioState(audio_state *AudioState, memory_arena *PermArena)
{
AudioState->PermArena = PermArena;
AudioState->FirstPlayingSound = 0;
AudioState->FirstFreePlayingSound = 0;
AudioState->MasterVolume = V2(1.0f, 1.0f);
}