ref: 4f1fde5ba764c2a25f6f0653a77e301f1a368204
parent: 3ba2487fcb31c6a1cbe5387fe9cd63fc9a90973a
author: Iliyas Jorio <iliyas@jor.io>
date: Wed Feb 2 11:39:20 EST 2022
Update cmixer
binary files a/src/main.cpp b/src/main.cpp differ
--- a/src/music.cpp
+++ b/src/music.cpp
@@ -92,7 +92,7 @@
if (which >= 0 && which <= k_songs)
{
- printf("Music: %d\n" , which + 128);
+ //printf("Music: %d\n" , which + 128);
auto qrn = QuickResourceName("mod", which+128, ".mod");
if (!FileExists(qrn)) {
@@ -110,5 +110,14 @@
musicSelection = which;
s_musicPaused = 0;
+ }
+}
+
+void ShutdownMusic()
+{
+ if (s_musicChannel)
+ {
+ delete s_musicChannel;
+ s_musicChannel = NULL;
}
}
--- a/src/music.h
+++ b/src/music.h
@@ -7,6 +7,7 @@
void FastMusic( void );
void SlowMusic( void );
void ChooseMusic( short which );
+void ShutdownMusic();
extern MBoolean musicOn;
--- a/src/soundfx.cpp
+++ b/src/soundfx.cpp
@@ -12,16 +12,28 @@
MBoolean soundOn = true;
float playerStereoSeparation = 1.0;
-void InitSound( void )
+void InitSound()
{
cmixer::InitWithSDL();
for (int index=0; index<kNumSounds; index++)
{
- soundBank.emplace_back(cmixer::LoadWAVFromFile(QuickResourceName("snd", index+128, ".wav")));
+ const char* path = QuickResourceName("snd", index+128, ".wav");
+ if (!FileExists(path))
+ {
+ Error(path);
+ }
+
+ soundBank.emplace_back();
+ soundBank.back().InitFromWAVFile(path) ;
}
}
+void ShutdownSound()
+{
+ soundBank.clear();
+ cmixer::ShutdownWithSDL();
+}
void PlayMono( short which )
{
@@ -37,7 +49,7 @@
{
if (soundOn)
{
- auto& effect = soundBank[which];
+ auto& effect = soundBank.at(which);
double pan;
switch (player) {
--- a/src/soundfx.h
+++ b/src/soundfx.h
@@ -1,9 +1,8 @@
// soundfx.h
-void FMOD_ERRCHECK(int result);
-
-void InitSound( void );
+void InitSound();
+void ShutdownSound();
void PlayStereo( short player, short which );
void PlayStereoFrequency( short player, short which, short freq );
void PlayMono( short which );
--- a/src/support/ModStream.cpp
+++ b/src/support/ModStream.cpp
@@ -5,7 +5,7 @@
using namespace cmixer;
ModStream::ModStream(std::vector<char> &&rawModuleData)
- : Source(44100, INT_MAX)
+ : Source()
, moduleFile(rawModuleData)
, replayBuffer(2048*8)
, rbOffset(0)
@@ -12,7 +12,8 @@
, rbLength(0)
, playbackSpeedMult(1.0)
{
- ibxm::data d;
+ Init(44100, INT_MAX);
+ ibxm::data d = {};
d.buffer = moduleFile.data();
d.length = moduleFile.size();
char errors[256];
@@ -20,11 +21,6 @@
this->module = ibxm::module_load(&d, errors);
this->replay = ibxm::new_replay(this->module, 44100, 0);
//printf("%p IBXM Error: %s\n", this->module, errors);
-}
-
-void ModStream::Rewind2()
-{
- printf("Rewind not supported\n");
}
void ModStream::SetPlaybackSpeed(double f)
--- a/src/support/ModStream.h
+++ b/src/support/ModStream.h
@@ -18,8 +18,9 @@
int rbLength;
double playbackSpeedMult;
- void Rewind2();
- void FillBuffer(int16_t* buffer, int length);
+ void ClearImplementation() override {};
+ void RewindImplementation() override {};
+ void FillBuffer(int16_t* buffer, int length) override;
public:
ModStream(std::vector<char>&& rawModule);
--- a/src/support/cmixer.cpp
+++ b/src/support/cmixer.cpp
@@ -47,7 +47,8 @@
//-----------------------------------------------------------------------------
// Global mixer
-static struct Mixer {
+static struct Mixer
+{
SDL_mutex* sdlAudioMutex;
std::list<Source*> sources; // Linked list of active (playing) sources
@@ -56,11 +57,15 @@
int gain; // Master gain (fixed point)
void Init(int samplerate);
+
void Process(int16_t* dst, int len);
+
void Lock();
+
void Unlock();
+
void SetMasterGain(double newGain);
-} gMixer;
+} gMixer = {};
//-----------------------------------------------------------------------------
// Global init/shutdown
@@ -84,8 +89,9 @@
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 1024;
- fmt.callback = [](void* udata, Uint8* stream, int size) {
- gMixer.Process((int16_t*)stream, size / 2);
+ fmt.callback = [](void* udata, Uint8* stream, int size)
+ {
+ gMixer.Process((int16_t*) stream, size / 2);
};
SDL_AudioSpec got;
@@ -103,11 +109,18 @@
void cmixer::ShutdownWithSDL()
{
- if (sdlDeviceID) {
+ if (sdlDeviceID)
+ {
SDL_CloseAudioDevice(sdlDeviceID);
sdlDeviceID = 0;
}
- if (sdlAudioSubSystemInited) {
+ if (gMixer.sdlAudioMutex)
+ {
+ SDL_DestroyMutex(gMixer.sdlAudioMutex);
+ gMixer.sdlAudioMutex = nullptr;
+ }
+ if (sdlAudioSubSystemInited)
+ {
SDL_QuitSubSystem(SDL_INIT_AUDIO);
sdlAudioSubSystemInited = false;
}
@@ -154,7 +167,8 @@
void Mixer::Process(int16_t* dst, int len)
{
// Process in chunks of BUFFER_SIZE if `len` is larger than BUFFER_SIZE
- while (len > BUFFER_SIZE) {
+ while (len > BUFFER_SIZE)
+ {
Process(dst, BUFFER_SIZE);
dst += BUFFER_SIZE;
len -= BUFFER_SIZE;
@@ -165,15 +179,18 @@
// Process active sources
Lock();
- for (auto si = sources.begin(); si != sources.end(); ) {
+ for (auto si = sources.begin(); si != sources.end();)
+ {
auto& s = **si;
s.Process(len);
// Remove source from list if it is no longer playing
- if (s.state != CM_STATE_PLAYING) {
+ if (s.state != CM_STATE_PLAYING)
+ {
s.active = false;
si = sources.erase(si);
}
- else {
+ else
+ {
++si;
}
}
@@ -180,7 +197,8 @@
Unlock();
// Copy internal buffer to destination and clip
- for (int i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++)
+ {
int x = (pcmmixbuf[i] * gain) >> FX_BITS;
dst[i] = CLAMP(x, -32768, 32767);
}
@@ -189,15 +207,48 @@
//-----------------------------------------------------------------------------
// Source implementation
-Source::Source(int theSampleRate, int theLength)
+Source::Source()
{
- memset(this, 0, sizeof(*this));
- length = theLength;
- samplerate = theSampleRate;
+ ClearPrivate();
+ active = false;
+}
+
+void Source::ClearPrivate()
+{
+ samplerate = 0;
+ length = 0;
+ end = 0;
+ state = CM_STATE_STOPPED;
+ position = 0;
+ lgain = 0;
+ rgain = 0;
+ rate = 0;
+ nextfill = 0;
+ loop = false;
+ rewind = true;
+ interpolate = false;
+ // DON'T touch active. The source may still be in gMixer!
+ gain = 0;
+ pan = 0;
+ onComplete = nullptr;
+}
+
+void Source::Clear()
+{
+ gMixer.Lock();
+ ClearPrivate();
+ ClearImplementation();
+ gMixer.Unlock();
+}
+
+void Source::Init(int theSampleRate, int theLength)
+{
+ this->samplerate = theSampleRate;
+ this->length = theLength;
SetGain(1);
SetPan(0);
SetPitch(1);
- SetLoop(0);
+ SetLoop(false);
Stop();
}
@@ -204,19 +255,16 @@
Source::~Source()
{
gMixer.Lock();
- if (active) {
+ if (active)
+ {
gMixer.sources.remove(this);
}
gMixer.Unlock();
- //CMEvent e;
- //e.type = CM_EVENT_DESTROY;
- //e.udata = udata;
- //handler(&e);
}
void Source::Rewind()
{
- Rewind2();
+ RewindImplementation();
position = 0;
rewind = false;
end = length;
@@ -223,9 +271,9 @@
nextfill = 0;
}
-void Source::FillBuffer(int offset, int length)
+void Source::FillBuffer(int offset, int fillLength)
{
- FillBuffer(pcmbuf + offset, length);
+ FillBuffer(pcmbuf + offset, fillLength);
}
void Source::Process(int len)
@@ -233,34 +281,40 @@
int32_t* dst = gMixer.pcmmixbuf;
// Do rewind if flag is set
- if (rewind) {
+ if (rewind)
+ {
Rewind();
}
// Don't process if not playing
- if (state != CM_STATE_PLAYING) {
+ if (state != CM_STATE_PLAYING)
+ {
return;
}
// Process audio
- while (len > 0) {
+ while (len > 0)
+ {
// Get current position frame
int frame = int(position >> FX_BITS);
// Fill buffer if required
- if (frame + 3 >= nextfill) {
+ if (frame + 3 >= nextfill)
+ {
FillBuffer((nextfill * 2) & BUFFER_MASK, BUFFER_SIZE / 2);
nextfill += BUFFER_SIZE / 4;
}
// Handle reaching the end of the playthrough
- if (frame >= end) {
+ if (frame >= end)
+ {
// As streams continiously fill the raw buffer in a loop we simply
// increment the end idx by one length and continue reading from it for
// another play-through
end = frame + this->length;
// Set state and stop processing if we're not set to loop
- if (!loop) {
+ if (!loop)
+ {
state = CM_STATE_STOPPED;
if (onComplete != nullptr)
onComplete();
@@ -276,10 +330,12 @@
len -= count * 2;
// Add audio to master buffer
- if (rate == FX_UNIT) {
+ if (rate == FX_UNIT)
+ {
// Add audio to buffer -- basic
n = frame * 2;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++)
+ {
dst[0] += (pcmbuf[(n ) & BUFFER_MASK] * lgain) >> FX_BITS;
dst[1] += (pcmbuf[(n + 1) & BUFFER_MASK] * rgain) >> FX_BITS;
n += 2;
@@ -287,9 +343,11 @@
}
this->position += count * FX_UNIT;
}
- else {
- // Add audio to buffer -- interpolated
- for (int i = 0; i < count; i++) {
+ else if (interpolate)
+ {
+ // Resample audio (with linear interpolation) and add to buffer
+ for (int i = 0; i < count; i++)
+ {
n = int(position >> FX_BITS) * 2;
int p = position & FX_MASK;
int a = pcmbuf[(n ) & BUFFER_MASK];
@@ -303,18 +361,29 @@
dst += 2;
}
}
-
+ else
+ {
+ // Resample audio (without interpolation) and add to buffer
+ for (int i = 0; i < count; i++)
+ {
+ n = int(position >> FX_BITS) * 2;
+ dst[0] += (pcmbuf[(n ) & BUFFER_MASK] * lgain) >> FX_BITS;
+ dst[1] += (pcmbuf[(n + 1) & BUFFER_MASK] * rgain) >> FX_BITS;
+ position += rate;
+ dst += 2;
+ }
+ }
}
}
double Source::GetLength() const
{
- return length / (double)samplerate;
+ return length / (double) samplerate;
}
double Source::GetPosition() const
{
- return ((position >> FX_BITS) % length) / (double)samplerate;
+ return ((position >> FX_BITS) % length) / (double) samplerate;
}
int Source::GetState() const
@@ -345,10 +414,12 @@
void Source::SetPitch(double newPitch)
{
double newRate;
- if (newPitch > 0.) {
- newRate = samplerate / (double)gMixer.samplerate * newPitch;
+ if (newPitch > 0.)
+ {
+ newRate = samplerate / (double) gMixer.samplerate * newPitch;
}
- else {
+ else
+ {
newRate = 0.001;
}
rate = FX_FROM_FLOAT(newRate);
@@ -359,11 +430,17 @@
loop = newLoop;
}
+void Source::SetInterpolation(bool newInterpolation)
+{
+ interpolate = newInterpolation;
+}
+
void Source::Play()
{
gMixer.Lock();
state = CM_STATE_PLAYING;
- if (!active) {
+ if (!active)
+ {
active = true;
gMixer.sources.push_front(this);
}
@@ -381,9 +458,6 @@
Play();
else if (state == CM_STATE_PLAYING)
Pause();
- else {
- ;
- }
}
void Source::Stop()
@@ -396,32 +470,32 @@
// WavStream implementation
#define WAV_PROCESS_LOOP(X) \
- while (n--) { \
- X \
- dst += 2; \
- idx++; \
- }
+ while (n--) \
+ { \
+ X \
+ dst += 2; \
+ idx++; \
+ }
-WavStream::WavStream(
- int theSampleRate,
- int theBitDepth,
- int nChannels,
- std::vector<char>&& data
-)
- :Source(theSampleRate, int((data.size() / (theBitDepth / 8)) / nChannels))
- ,udata(data)
- ,idx(0)
- ,bitdepth(theBitDepth)
- ,channels(nChannels)
+WavStream::WavStream()
+ : Source()
{
- bigEndian = false;
+ ClearImplementation();
}
-void WavStream::Rewind2()
+void WavStream::ClearImplementation()
{
+ bitdepth = 0;
+ channels = 0;
idx = 0;
+ userBuffer.clear();
}
+void WavStream::RewindImplementation()
+{
+ idx = 0;
+}
+
void WavStream::FillBuffer(int16_t* dst, int len)
{
int x, n;
@@ -428,38 +502,41 @@
len /= 2;
- while (len > 0) {
+ while (len > 0)
+ {
n = MIN(len, length - idx);
len -= n;
- if (bitdepth == 16 && channels == 1) {
+ if (bitdepth == 16 && channels == 1)
+ {
WAV_PROCESS_LOOP({
dst[0] = dst[1] = data16()[idx];
- });
+ });
}
- else if (bitdepth == 16 && channels == 2) {
+ else if (bitdepth == 16 && channels == 2)
+ {
WAV_PROCESS_LOOP({
x = idx * 2;
dst[0] = data16()[x];
dst[1] = data16()[x + 1];
- });
+ });
}
- else if (bitdepth == 8 && channels == 1) {
+ else if (bitdepth == 8 && channels == 1)
+ {
WAV_PROCESS_LOOP({
dst[0] = dst[1] = (data8()[idx] - 128) << 8;
- });
+ });
}
- else if (bitdepth == 8 && channels == 2) {
+ else if (bitdepth == 8 && channels == 2)
+ {
WAV_PROCESS_LOOP({
x = idx * 2;
dst[0] = (data8()[x] - 128) << 8;
dst[1] = (data8()[x + 1] - 128) << 8;
- });
+ });
}
- else {
- throw std::invalid_argument("big endian clips not supported");
- }
// Loop back and continue filling buffer if we didn't fill the buffer
- if (len > 0) {
+ if (len > 0)
+ {
idx = 0;
}
}
@@ -493,7 +570,7 @@
return p + 8;
}
-WavStream cmixer::LoadWAVFromFile(const char* path)
+void cmixer::WavStream::InitFromWAVFile(const char* path)
{
int sz;
auto filebuf = LoadFile(path);
@@ -525,10 +602,14 @@
if (!p)
throw std::invalid_argument("no data subchunk");
- return WavStream(
- samplerate,
- bitdepth,
- channels,
- std::vector<char>(p, p + sz));
-}
+ Clear();
+ Init(samplerate, sz / (channels * bitdepth / 8));
+ this->bitdepth = bitdepth;
+ this->channels = channels;
+ this->idx = 0;
+
+ userBuffer.reserve(sz);
+ for (int i = 0; i < sz; i++)
+ userBuffer.push_back(p[i]);
+}
--- a/src/support/cmixer.h
+++ b/src/support/cmixer.h
@@ -30,87 +30,120 @@
#define BUFFER_SIZE (512)
-namespace cmixer {
+namespace cmixer
+{
-enum {
- CM_STATE_STOPPED,
- CM_STATE_PLAYING,
- CM_STATE_PAUSED
-};
+ enum
+ {
+ CM_STATE_STOPPED,
+ CM_STATE_PLAYING,
+ CM_STATE_PAUSED
+ };
-struct Source {
- int16_t pcmbuf[BUFFER_SIZE]; // Internal buffer with raw stereo PCM
- int samplerate; // Stream's native samplerate
- int length; // Stream's length in frames
- int end; // End index for the current play-through
- int state; // Current state (playing|paused|stopped)
- int64_t position; // Current playhead position (fixed point)
- int lgain, rgain; // Left and right gain (fixed point)
- int rate; // Playback rate (fixed point)
- int nextfill; // Next frame idx where the buffer needs to be filled
- bool loop; // Whether the source will loop when `end` is reached
- bool rewind; // Whether the source will rewind before playing
- bool active; // Whether the source is part of `sources` list
- double gain; // Gain set by `cm_set_gain()`
- double pan; // Pan set by `cm_set_pan()`
- std::function<void()> onComplete; // Callback
+ struct Source
+ {
+ int16_t pcmbuf[BUFFER_SIZE]; // Internal buffer with raw stereo PCM
+ int samplerate; // Stream's native samplerate
+ int length; // Stream's length in frames
+ int end; // End index for the current play-through
+ int state; // Current state (playing|paused|stopped)
+ int64_t position; // Current playhead position (fixed point)
+ int lgain, rgain; // Left and right gain (fixed point)
+ int rate; // Playback rate (fixed point)
+ int nextfill; // Next frame idx where the buffer needs to be filled
+ bool loop; // Whether the source will loop when `end` is reached
+ bool rewind; // Whether the source will rewind before playing
+ bool active; // Whether the source is part of `sources` list
+ bool interpolate; // Interpolated resampling when played back at a non-native rate
+ double gain; // Gain set by `cm_set_gain()`
+ double pan; // Pan set by `cm_set_pan()`
+ std::function<void()> onComplete; // Callback
-protected:
- Source(int theSampleRate, int theLength);
+ void ClearPrivate();
- virtual void Rewind2() = 0;
- virtual void FillBuffer(int16_t* buffer, int length) = 0;
+ protected:
+ Source();
-public:
- void Rewind();
- void RecalcGains();
- void FillBuffer(int offset, int length);
- void Process(int len);
+ void Init(int samplerate, int length);
-public:
- ~Source();
- double GetLength() const;
- double GetPosition() const;
- int GetState() const;
- void SetGain(double gain);
- void SetPan(double pan);
- void SetPitch(double pitch);
- void SetLoop(bool loop);
- void Play();
- void Pause();
- void TogglePause();
- void Stop();
-};
+ virtual void RewindImplementation() = 0;
-class WavStream : public Source {
- int bitdepth;
- int channels;
- int idx;
+ virtual void ClearImplementation() = 0;
- std::vector<char> udata;
+ virtual void FillBuffer(int16_t* buffer, int length) = 0;
- void Rewind2();
- void FillBuffer(int16_t* buffer, int length);
+ public:
+ virtual ~Source();
- inline uint8_t* data8() { return (uint8_t*)udata.data(); }
- inline int16_t* data16() { return (int16_t*)udata.data(); }
+ void Clear();
-public:
- bool bigEndian;
+ void Rewind();
- WavStream(
- int theSampleRate,
- int theBitDepth,
- int nChannels,
- std::vector<char>&& data
- );
-};
+ void RecalcGains();
+ void FillBuffer(int offset, int length);
-void InitWithSDL();
-void ShutdownWithSDL();
-double GetMasterGain();
-void SetMasterGain(double);
-WavStream LoadWAVFromFile(const char* path);
+ void Process(int len);
+
+ double GetLength() const;
+
+ double GetPosition() const;
+
+ int GetState() const;
+
+ void SetGain(double gain);
+
+ void SetPan(double pan);
+
+ void SetPitch(double pitch);
+
+ void SetLoop(bool loop);
+
+ void SetInterpolation(bool interpolation);
+
+ void Play();
+
+ void Pause();
+
+ void TogglePause();
+
+ void Stop();
+ };
+
+ class WavStream : public Source
+ {
+ int bitdepth;
+ int channels;
+ int idx;
+ std::vector<char> userBuffer;
+
+ void ClearImplementation() override;
+
+ void RewindImplementation() override;
+
+ void FillBuffer(int16_t* buffer, int length) override;
+
+ inline const uint8_t* data8() const
+ { return reinterpret_cast<const uint8_t*>(userBuffer.data()); }
+
+ inline const int16_t* data16() const
+ { return reinterpret_cast<const int16_t*>(userBuffer.data()); }
+
+ public:
+ WavStream();
+
+ void InitFromWAVFile(const char* path);
+ };
+
+
+ void InitWithSDL();
+
+ void ShutdownWithSDL();
+
+ double GetMasterGain();
+
+ void SetMasterGain(double);
+
+ WavStream LoadWAVFromFile(const char* path);
}