shithub: cstory

Download patch

ref: 732d3bbc5cb24a1e43eea16163455c0cab104f02
parent: 4e58457e1d0059c36107e55f12cf60f049b94b88
author: Clownacy <Clownacy@users.noreply.github.com>
date: Tue Mar 31 08:14:20 EDT 2020

Split software audio mixer to its own file

This will be shared between the SDL2 and miniaudio backends,
eliminating all that digusting duplicate code.

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -296,7 +296,7 @@
 if(BACKEND_AUDIO MATCHES "SDL2")
 	target_sources(CSE2 PRIVATE "src/Backends/Audio/SDL2.cpp")
 elseif(BACKEND_AUDIO MATCHES "miniaudio")
-	target_sources(CSE2 PRIVATE "src/Backends/Audio/miniaudio.cpp")
+	target_sources(CSE2 PRIVATE "src/Backends/Audio/miniaudio.cpp" "src/Backends/Audio/SoftwareMixer.cpp")
 
 	# Link libdl, libm, and libpthread
 	include(CheckLibraryExists)
--- /dev/null
+++ b/src/Backends/Audio/SoftwareMixer.cpp
@@ -1,0 +1,185 @@
+#include "SoftwareMixer.h"
+
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define CLAMP(x, y, z) MIN(MAX((x), (y)), (z))
+
+#ifdef __GNUC__
+#define ATTR_HOT __attribute__((hot))
+#else
+#define ATTR_HOT
+#endif
+
+struct Mixer_Sound
+{
+	unsigned char *samples;
+	size_t frames;
+	double position;
+	double advance_delta;
+	bool playing;
+	bool looping;
+	unsigned int frequency;
+	float volume;
+	float pan_l;
+	float pan_r;
+	float volume_l;
+	float volume_r;
+
+	struct Mixer_Sound *next;
+};
+
+static Mixer_Sound *sound_list_head;
+
+static unsigned long output_frequency;
+
+static double MillibelToScale(long volume)
+{
+	// Volume is in hundredths of a decibel, from 0 to -10000
+	volume = CLAMP(volume, -10000, 0);
+	return pow(10.0, volume / 2000.0);
+}
+
+void Mixer_Init(unsigned long frequency)
+{
+	output_frequency = frequency;
+}
+
+Mixer_Sound* Mixer_CreateSound(unsigned int frequency, size_t frames)
+{
+	Mixer_Sound *sound = (Mixer_Sound*)malloc(sizeof(Mixer_Sound));
+
+	if (sound == NULL)
+		return NULL;
+
+	sound->samples = (unsigned char*)malloc(frames + 1);
+
+	if (sound->samples == NULL)
+	{
+		free(sound);
+		return NULL;
+	}
+
+	sound->frames = frames;
+	sound->playing = false;
+	sound->position = 0.0;
+
+	Mixer_SetSoundFrequency(sound, frequency);
+	Mixer_SetSoundVolume(sound, 0);
+	Mixer_SetSoundPan(sound, 0);
+
+	sound->next = sound_list_head;
+	sound_list_head = sound;
+
+	return sound;
+}
+
+void Mixer_DestroySound(Mixer_Sound *sound)
+{
+	for (Mixer_Sound **sound_pointer = &sound_list_head; *sound_pointer != NULL; sound_pointer = &(*sound_pointer)->next)
+	{
+		if (*sound_pointer == sound)
+		{
+			*sound_pointer = sound->next;
+			free(sound->samples);
+			free(sound);
+			break;
+		}
+	}
+}
+
+unsigned char* Mixer_LockSound(Mixer_Sound *sound, size_t *size)
+{
+	if (size != NULL)
+		*size = sound->frames;
+
+	return sound->samples;
+}
+
+void Mixer_PlaySound(Mixer_Sound *sound, bool looping)
+{
+	sound->playing = true;
+	sound->looping = looping;
+
+	sound->samples[sound->frames] = looping ? sound->samples[0] : 0x80;	// For the linear interpolator
+}
+
+void Mixer_StopSound(Mixer_Sound *sound)
+{
+	sound->playing = false;
+	sound->position = 0.0;
+}
+
+void Mixer_RewindSound(Mixer_Sound *sound)
+{
+	sound->position = 0.0;
+}
+
+void Mixer_SetSoundFrequency(Mixer_Sound *sound, unsigned int frequency)
+{
+	sound->frequency = frequency;
+	sound->advance_delta = (double)frequency / (double)output_frequency;
+}
+
+void Mixer_SetSoundVolume(Mixer_Sound *sound, long volume)
+{
+	sound->volume = (float)MillibelToScale(volume);
+
+	sound->volume_l = sound->pan_l * sound->volume;
+	sound->volume_r = sound->pan_r * sound->volume;
+}
+
+void Mixer_SetSoundPan(Mixer_Sound *sound, long pan)
+{
+	sound->pan_l = (float)MillibelToScale(-pan);
+	sound->pan_r = (float)MillibelToScale(pan);
+
+	sound->volume_l = sound->pan_l * sound->volume;
+	sound->volume_r = sound->pan_r * sound->volume;
+}
+
+// Most CPU-intensive function in the game (2/3rd CPU time consumption in my experience), so marked with attrHot so the compiler considers it a hot spot (as it is) when optimizing
+ATTR_HOT void Mixer_MixSounds(float *stream, unsigned int frames_total)
+{
+	for (Mixer_Sound *sound = sound_list_head; sound != NULL; sound = sound->next)
+	{
+		if (sound->playing)
+		{
+			float *steam_pointer = stream;
+
+			for (unsigned int frames_done = 0; frames_done < frames_total; ++frames_done)
+			{
+				// Get two samples, and normalise them to 0-1
+				const float sample1 = (sound->samples[(size_t)sound->position] - 128.0f) / 128.0f;
+				const float sample2 = (sound->samples[(size_t)sound->position + 1] - 128.0f) / 128.0f;
+
+				// Perform linear interpolation
+				const float interpolated_sample = sample1 + ((sample2 - sample1) * fmod((float)sound->position, 1.0f));
+
+				*steam_pointer++ += interpolated_sample * sound->volume_l;
+				*steam_pointer++ += interpolated_sample * sound->volume_r;
+
+				sound->position += sound->advance_delta;
+
+				if (sound->position >= sound->frames)
+				{
+					if (sound->looping)
+					{
+						sound->position = fmod(sound->position, (double)sound->frames);
+					}
+					else
+					{
+						sound->playing = false;
+						sound->position = 0.0;
+						break;
+					}
+				}
+			}
+		}
+	}
+}
--- /dev/null
+++ b/src/Backends/Audio/SoftwareMixer.h
@@ -1,0 +1,17 @@
+#pragma once
+
+#include <stddef.h>
+
+typedef struct Mixer_Sound Mixer_Sound;
+
+void Mixer_Init(unsigned long frequency);
+Mixer_Sound* Mixer_CreateSound(unsigned int frequency, size_t frames);
+void Mixer_DestroySound(Mixer_Sound *sound);
+unsigned char* Mixer_LockSound(Mixer_Sound *sound, size_t *size);
+void Mixer_PlaySound(Mixer_Sound *sound, bool looping);
+void Mixer_StopSound(Mixer_Sound *sound);
+void Mixer_RewindSound(Mixer_Sound *sound);
+void Mixer_SetSoundFrequency(Mixer_Sound *sound, unsigned int frequency);
+void Mixer_SetSoundVolume(Mixer_Sound *sound, long volume);
+void Mixer_SetSoundPan(Mixer_Sound *sound, long pan);
+void Mixer_MixSounds(float *stream, unsigned int frames_total);
--- a/src/Backends/Audio/miniaudio.cpp
+++ b/src/Backends/Audio/miniaudio.cpp
@@ -1,11 +1,5 @@
 #include "../Audio.h"
 
-#include <math.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #define MINIAUDIO_IMPLEMENTATION
 #define MA_NO_DECODING
 #define MA_API static
@@ -14,35 +8,10 @@
 #include "../../Organya.h"
 #include "../../WindowsWrapper.h"
 
+#include "SoftwareMixer.h"
+
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define CLAMP(x, y, z) MIN(MAX((x), (y)), (z))
 
-#ifdef __GNUC__
-#define ATTR_HOT __attribute__((hot))
-#else
-#define ATTR_HOT
-#endif
-
-struct AudioBackend_Sound
-{
-	unsigned char *samples;
-	size_t frames;
-	double position;
-	double advance_delta;
-	BOOL playing;
-	BOOL looping;
-	unsigned int frequency;
-	float volume;
-	float pan_l;
-	float pan_r;
-	float volume_l;
-	float volume_r;
-
-	struct AudioBackend_Sound *next;
-};
-
-static AudioBackend_Sound *sound_list_head;
 static ma_device device;
 static ma_mutex mutex;
 static ma_mutex organya_mutex;
@@ -51,81 +20,6 @@
 
 static unsigned short organya_timer;
 
-static double MillibelToScale(long volume)
-{
-	// Volume is in hundredths of decibels, from 0 to -10000
-	volume = CLAMP(volume, -10000, 0);
-	return pow(10.0, volume / 2000.0);
-}
-
-static void SetSoundFrequency(AudioBackend_Sound *sound, unsigned int frequency)
-{
-	sound->frequency = frequency;
-	sound->advance_delta = (double)frequency / (double)output_frequency;
-}
-
-static void SetSoundVolume(AudioBackend_Sound *sound, long volume)
-{
-	sound->volume = (float)MillibelToScale(volume);
-
-	sound->volume_l = sound->pan_l * sound->volume;
-	sound->volume_r = sound->pan_r * sound->volume;
-}
-
-static void SetSoundPan(AudioBackend_Sound *sound, long pan)
-{
-	sound->pan_l = (float)MillibelToScale(-pan);
-	sound->pan_r = (float)MillibelToScale(pan);
-
-	sound->volume_l = sound->pan_l * sound->volume;
-	sound->volume_r = sound->pan_r * sound->volume;
-}
-
-// Most CPU-intensive function in the game (2/3rd CPU time consumption in my experience), so marked with attrHot so the compiler considers it a hot spot (as it is) when optimizing
-ATTR_HOT static void MixSounds(float *stream, unsigned int frames_total)
-{
-	ma_mutex_lock(&mutex);
-
-	for (AudioBackend_Sound *sound = sound_list_head; sound != NULL; sound = sound->next)
-	{
-		if (sound->playing)
-		{
-			float *steam_pointer = stream;
-
-			for (unsigned int frames_done = 0; frames_done < frames_total; ++frames_done)
-			{
-				// Get two samples, and normalise them to 0-1
-				const float sample1 = (sound->samples[(size_t)sound->position] - 128.0f) / 128.0f;
-				const float sample2 = (sound->samples[(size_t)sound->position + 1] - 128.0f) / 128.0f;
-
-				// Perform linear interpolation
-				const float interpolated_sample = sample1 + ((sample2 - sample1) * fmod((float)sound->position, 1.0f));
-
-				*steam_pointer++ += interpolated_sample * sound->volume_l;
-				*steam_pointer++ += interpolated_sample * sound->volume_r;
-
-				sound->position += sound->advance_delta;
-
-				if (sound->position >= sound->frames)
-				{
-					if (sound->looping)
-					{
-						sound->position = fmod(sound->position, (double)sound->frames);
-					}
-					else
-					{
-						sound->playing = FALSE;
-						sound->position = 0.0;
-						break;
-					}
-				}
-			}
-		}
-	}
-
-	ma_mutex_unlock(&mutex);
-}
-
 static void Callback(ma_device *device, void *output_stream, const void *input_stream, ma_uint32 frames_total)
 {
 	(void)device;
@@ -137,7 +31,9 @@
 
 	if (organya_timer == 0)
 	{
-		MixSounds(stream, frames_total);
+		ma_mutex_lock(&mutex);
+		Mixer_MixSounds(stream, frames_total);
+		ma_mutex_unlock(&mutex);
 	}
 	else
 	{
@@ -160,7 +56,9 @@
 
 			const unsigned int frames_to_do = MIN(organya_countdown, frames_total - frames_done);
 
-			MixSounds(stream + frames_done * 2, frames_to_do);
+			ma_mutex_lock(&mutex);
+			Mixer_MixSounds(stream + frames_done * 2, frames_to_do);
+			ma_mutex_unlock(&mutex);
 
 			frames_done += frames_to_do;
 			organya_countdown -= frames_to_do;
@@ -182,14 +80,18 @@
 
 	if (ma_device_init(NULL, &config, &device) == MA_SUCCESS)
 	{
-		output_frequency = device.sampleRate;
-
 		if (ma_mutex_init(device.pContext, &mutex) == MA_SUCCESS)
 		{
 			if (ma_mutex_init(device.pContext, &organya_mutex) == MA_SUCCESS)
 			{
 				if (ma_device_start(&device) == MA_SUCCESS)
+				{
+					output_frequency = device.sampleRate;
+
+					Mixer_Init(device.sampleRate);
+
 					return TRUE;
+				}
 
 				ma_mutex_uninit(&organya_mutex);
 			}
@@ -216,35 +118,13 @@
 
 AudioBackend_Sound* AudioBackend_CreateSound(unsigned int frequency, size_t frames)
 {
-	AudioBackend_Sound *sound = (AudioBackend_Sound*)malloc(sizeof(AudioBackend_Sound));
-
-	if (sound == NULL)
-		return NULL;
-
-	sound->samples = (unsigned char*)malloc(frames + 1);
-
-	if (sound->samples == NULL)
-	{
-		free(sound);
-		return NULL;
-	}
-
-	sound->frames = frames;
-	sound->playing = FALSE;
-	sound->position = 0.0;
-
-	SetSoundFrequency(sound, frequency);
-	SetSoundVolume(sound, 0);
-	SetSoundPan(sound, 0);
-
 	ma_mutex_lock(&mutex);
 
-	sound->next = sound_list_head;
-	sound_list_head = sound;
+	Mixer_Sound *sound = Mixer_CreateSound(frequency, frames);
 
 	ma_mutex_unlock(&mutex);
 
-	return sound;
+	return (AudioBackend_Sound*)sound;
 }
 
 void AudioBackend_DestroySound(AudioBackend_Sound *sound)
@@ -254,16 +134,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	for (AudioBackend_Sound **sound_pointer = &sound_list_head; *sound_pointer != NULL; sound_pointer = &(*sound_pointer)->next)
-	{
-		if (*sound_pointer == sound)
-		{
-			*sound_pointer = sound->next;
-			free(sound->samples);
-			free(sound);
-			break;
-		}
-	}
+	Mixer_DestroySound((Mixer_Sound*)sound);
 
 	ma_mutex_unlock(&mutex);
 }
@@ -275,10 +146,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	if (size != NULL)
-		*size = sound->frames;
-
-	return sound->samples;
+	return Mixer_LockSound((Mixer_Sound*)sound, size);
 }
 
 void AudioBackend_UnlockSound(AudioBackend_Sound *sound)
@@ -296,11 +164,8 @@
 
 	ma_mutex_lock(&mutex);
 
-	sound->playing = TRUE;
-	sound->looping = looping;
+	Mixer_PlaySound((Mixer_Sound*)sound, looping);
 
-	sound->samples[sound->frames] = looping ? sound->samples[0] : 0x80;	// For the linear interpolator
-
 	ma_mutex_unlock(&mutex);
 }
 
@@ -311,8 +176,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	sound->playing = FALSE;
-	sound->position = 0.0;
+	Mixer_StopSound((Mixer_Sound*)sound);
 
 	ma_mutex_unlock(&mutex);
 }
@@ -324,7 +188,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	sound->position = 0.0;
+	Mixer_RewindSound((Mixer_Sound*)sound);
 
 	ma_mutex_unlock(&mutex);
 }
@@ -336,7 +200,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	SetSoundFrequency(sound, frequency);
+	Mixer_SetSoundFrequency((Mixer_Sound*)sound, frequency);
 
 	ma_mutex_unlock(&mutex);
 }
@@ -348,7 +212,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	SetSoundVolume(sound, volume);
+	Mixer_SetSoundVolume((Mixer_Sound*)sound, volume);
 
 	ma_mutex_unlock(&mutex);
 }
@@ -360,7 +224,7 @@
 
 	ma_mutex_lock(&mutex);
 
-	SetSoundPan(sound, pan);
+	Mixer_SetSoundPan((Mixer_Sound*)sound, pan);
 
 	ma_mutex_unlock(&mutex);
 }
--