Skip to content

Commit

Permalink
More pleasant audio mix + cmixer stability update
Browse files Browse the repository at this point in the history
  • Loading branch information
jorio committed Jan 8, 2023
1 parent fa3b56a commit 8abfdf1
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 57 deletions.
8 changes: 3 additions & 5 deletions src/music.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,8 @@ static std::vector<char> LoadFile(char const* filename)

void ChooseMusic( short which )
{
if (s_musicChannel != NULL)
{
delete s_musicChannel;
s_musicChannel = NULL;
}
// Kill existing song first, if any
ShutdownMusic();

musicSelection = -1;

Expand Down Expand Up @@ -115,6 +112,7 @@ void ShutdownMusic()
{
if (s_musicChannel)
{
s_musicChannel->RemoveFromMixer();
delete s_musicChannel;
s_musicChannel = NULL;
}
Expand Down
35 changes: 22 additions & 13 deletions src/soundfx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
#include "support/cmixer.h"
#include <stdio.h>

static std::vector<cmixer::WavStream> soundBank;
MBoolean soundOn = true;
float playerStereoSeparation = 1.0;
MBoolean soundOn = true;

static std::vector<cmixer::WavStream> s_soundBank;
static constexpr float k_playerStereoSeparation = 0.5f;
static constexpr float k_soundEffectGain = 0.7f;

void InitSound()
{
Expand All @@ -23,14 +25,18 @@ void InitSound()
Error(path);
}

soundBank.emplace_back();
soundBank.back().InitFromWAVFile(path) ;
s_soundBank.emplace_back(cmixer::LoadWAVFromFile(path));
s_soundBank.back().SetInterpolation(true);
}
}

void ShutdownSound()
{
soundBank.clear();
for (auto& wavStream : s_soundBank)
{
wavStream.RemoveFromMixer();
}
s_soundBank.clear();
cmixer::ShutdownWithSDL();
}

Expand All @@ -48,19 +54,22 @@ void PlayStereoFrequency( short player, short which, short freq )
{
if (soundOn)
{
auto& effect = soundBank.at(which);
auto& effect = s_soundBank.at(which);

double pan;
switch (player) {
case 0: pan = -playerStereoSeparation; break;
case 1: pan = +playerStereoSeparation; break;
switch (player)
{
case 0: pan = -k_playerStereoSeparation; break;
case 1: pan = +k_playerStereoSeparation; break;
default: pan = 0.0; break;
}


effect.Stop();
effect.SetGain(k_soundEffectGain);
effect.SetPan(pan);
effect.SetPitch(1.0 + freq/16.0);
effect.Play();

UpdateSound();
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/soundfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,4 @@ enum
kNumSounds
};

namespace FMOD { class System; }

extern MBoolean soundOn;
extern FMOD::System *g_fmod;
157 changes: 126 additions & 31 deletions src/support/cmixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,30 @@ using namespace cmixer;

#define BUFFER_MASK (BUFFER_SIZE - 1)

static inline int16_t UnpackI16BE(const void* data)
{
#if __BIG_ENDIAN__
// no-op on big-endian systems
return *(const uint16_t*) data;
#else
const uint8_t* p = (uint8_t*) data;
return ( p[0] << 8 )
| ( p[1] );
#endif
}

static inline int16_t UnpackI16LE(const void* data)
{
#if __BIG_ENDIAN__
const uint8_t* p = (uint8_t*) data;
return ( p[0] )
| ( p[1] << 8 );
#else
// no-op on little-endian systems
return *(const uint16_t*) data;
#endif
}

//-----------------------------------------------------------------------------
// Global mixer

Expand Down Expand Up @@ -86,11 +110,12 @@ void cmixer::InitWithSDL()
// Init SDL audio
SDL_AudioSpec fmt = {};
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.format = AUDIO_S16SYS;
fmt.channels = 2;
fmt.samples = 1024;
fmt.callback = [](void* udata, Uint8* stream, int size)
{
(void) udata;
gMixer.Process((int16_t*) stream, size / 2);
};

Expand Down Expand Up @@ -161,7 +186,7 @@ void Mixer::SetMasterGain(double newGain)
{
if (newGain < 0)
newGain = 0;
gain = FX_FROM_FLOAT(newGain);
gain = (int) FX_FROM_FLOAT(newGain);
}

void Mixer::Process(int16_t* dst, int len)
Expand Down Expand Up @@ -245,23 +270,37 @@ void Source::Init(int theSampleRate, int theLength)
{
this->samplerate = theSampleRate;
this->length = theLength;
this->sustainOffset = 0;
SetGain(1);
SetPan(0);
SetPitch(1);
SetLoop(false);
Stop();
}

Source::~Source()
void Source::RemoveFromMixer()
{
gMixer.Lock();
if (active)
{
gMixer.sources.remove(this);
active = false;
}
gMixer.Unlock();
}

Source::~Source()
{
if (active)
{
// You MUST call RemoveFromMixer before destroying a source. If you get here, your program is incorrect.
fprintf(stderr, "Source wasn't removed from mixer prior to destruction!\n");
#if _DEBUG
std::terminate();
#endif
}
}

void Source::Rewind()
{
RewindImplementation();
Expand Down Expand Up @@ -395,8 +434,8 @@ void Source::RecalcGains()
{
double l = this->gain * (pan <= 0. ? 1. : 1. - pan);
double r = this->gain * (pan >= 0. ? 1. : 1. + pan);
this->lgain = FX_FROM_FLOAT(l);
this->rgain = FX_FROM_FLOAT(r);
this->lgain = (int) FX_FROM_FLOAT(l);
this->rgain = (int) FX_FROM_FLOAT(r);
}

void Source::SetGain(double newGain)
Expand All @@ -422,7 +461,7 @@ void Source::SetPitch(double newPitch)
{
newRate = 0.001;
}
rate = FX_FROM_FLOAT(newRate);
rate = (int) FX_FROM_FLOAT(newRate);
}

void Source::SetLoop(bool newLoop)
Expand All @@ -437,6 +476,13 @@ void Source::SetInterpolation(bool newInterpolation)

void Source::Play()
{
if (length == 0)
{
// Don't attempt to play an empty source as this would result
// in instant starvation when filling mixer buffer
return;
}

gMixer.Lock();
state = CM_STATE_PLAYING;
if (!active)
Expand Down Expand Up @@ -488,36 +534,88 @@ void WavStream::ClearImplementation()
bitdepth = 0;
channels = 0;
idx = 0;

#if __BIG_ENDIAN__ // default to native endianness
bigEndian = true;
#else
bigEndian = false;
#endif

userBuffer.clear();
}

void WavStream::Init(
int theSampleRate,
int theBitDepth,
int theNChannels,
bool theBigEndian,
std::span<char> theSpan)
{
Clear();
Source::Init(theSampleRate, int((theSpan.size() / (theBitDepth / 8)) / theNChannels));
this->bitdepth = theBitDepth;
this->channels = theNChannels;
this->idx = 0;
this->span = theSpan;
this->bigEndian = theBigEndian;
}

std::span<char> WavStream::GetBuffer(int nBytesOut)
{
userBuffer.clear();
userBuffer.reserve(nBytesOut);
return std::span(userBuffer.data(), nBytesOut);
}

std::span<char> WavStream::SetBuffer(std::vector<char>&& data)
{
userBuffer = std::move(data);
return std::span(userBuffer.data(), userBuffer.size());
}

void WavStream::RewindImplementation()
{
idx = 0;
}

void WavStream::FillBuffer(int16_t* dst, int len)
void WavStream::FillBuffer(int16_t* dst, int fillLength)
{
int x, n;

len /= 2;
fillLength /= 2;

while (len > 0)
while (fillLength > 0)
{
n = MIN(len, length - idx);
len -= n;
if (bitdepth == 16 && channels == 1)
n = MIN(fillLength, length - idx);

fillLength -= n;

if (bigEndian && bitdepth == 16 && channels == 1)
{
WAV_PROCESS_LOOP({
dst[0] = dst[1] = UnpackI16BE(&data16()[idx]);
});
}
else if (bigEndian && bitdepth == 16 && channels == 2)
{
WAV_PROCESS_LOOP({
dst[0] = dst[1] = data16()[idx];
x = idx * 2;
dst[0] = UnpackI16BE(&data16()[x]);
dst[1] = UnpackI16BE(&data16()[x + 1]);
});
}
else if (bitdepth == 16 && channels == 1)
{
WAV_PROCESS_LOOP({
dst[0] = dst[1] = UnpackI16LE(&data16()[idx]);
});
}
else if (bitdepth == 16 && channels == 2)
{
WAV_PROCESS_LOOP({
x = idx * 2;
dst[0] = data16()[x];
dst[1] = data16()[x + 1];
dst[0] = UnpackI16LE(&data16()[x]);
dst[1] = UnpackI16LE(&data16()[x + 1]);
});
}
else if (bitdepth == 8 && channels == 1)
Expand All @@ -535,15 +633,15 @@ void WavStream::FillBuffer(int16_t* dst, int len)
});
}
// Loop back and continue filling buffer if we didn't fill the buffer
if (len > 0)
if (fillLength > 0)
{
idx = 0;
idx = sustainOffset;
}
}
}

//-----------------------------------------------------------------------------
// LoadWAVFromFile for testing
// LoadWAVFromFile

static std::vector<char> LoadFile(char const* filename)
{
Expand All @@ -570,7 +668,7 @@ static const char* FindChunk(const char* data, int len, const char* id, int* siz
return p + 8;
}

void cmixer::WavStream::InitFromWAVFile(const char* path)
WavStream cmixer::LoadWAVFromFile(const char* path)
{
int sz;
auto filebuf = LoadFile(path);
Expand All @@ -581,7 +679,7 @@ void cmixer::WavStream::InitFromWAVFile(const char* path)
// Check header
if (memcmp(p, "RIFF", 4) || memcmp(p + 8, "WAVE", 4))
throw std::invalid_argument("bad wav header");

// Find fmt subchunk
p = FindChunk(data, len, "fmt ", &sz);
if (!p)
Expand All @@ -602,15 +700,12 @@ void cmixer::WavStream::InitFromWAVFile(const char* path)
if (!p)
throw std::invalid_argument("no data subchunk");

Clear();
Init(samplerate, sz / (channels * bitdepth / 8));

this->bitdepth = bitdepth;
this->channels = channels;
this->idx = 0;
this->interpolate = true;

userBuffer.reserve(sz);
for (int i = 0; i < sz; i++)
userBuffer.push_back(p[i]);
WavStream wavStream;
wavStream.Init(
samplerate,
bitdepth,
channels,
false,
wavStream.SetBuffer(std::vector<char>(p, p + sz)));
return wavStream;
}
Loading

0 comments on commit 8abfdf1

Please sign in to comment.