shithub: candycrisis

Download patch

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);
 
 }