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