1
0
Fork 0
2022-untitled-game/code/src/engine/audio_mgr.c

175 lines
4.3 KiB
C

enum { MAX_SOUND_COUNT = 10 };
const int INVALID_INDEX = -1;
const int INVALID_HANDLE = -1;
typedef struct audio_source {
vec3 position;
unsigned int cursor;
bool stereo;
bool is_looping;
wav_t* wav;
} audio_source_t;
typedef struct audio_listener {
vec3 position;
vec3 front;
vec3 up;
} audio_listener_t;
typedef struct audio_mgr {
int sound_count;
audio_listener_t listener;
audio_source_t sources[MAX_SOUND_COUNT];
int index_to_handle_map[MAX_SOUND_COUNT];
int handle_to_index_map[MAX_SOUND_COUNT];
} audio_mgr_t;
void
audio_mgr_startup(void)
{
for (int i = 0; i < MAX_SOUND_COUNT; i++)
{
g_audio_mgr->handle_to_index_map[i] = INVALID_INDEX;
g_audio_mgr->index_to_handle_map[i] = INVALID_HANDLE;
}
}
int
play_sound(audio_source_t sound)
{
int handle = INVALID_HANDLE;
if (g_audio_mgr->sound_count < MAX_SOUND_COUNT)
{
// Find free handle
for (int i = 0; i < MAX_SOUND_COUNT; i++)
{
if (g_audio_mgr->handle_to_index_map[i] == INVALID_INDEX)
{
handle = i;
break;
}
}
// Put new entry at end and update the maps
if (handle != INVALID_HANDLE)
{
size_t new_index = g_audio_mgr->sound_count;
g_audio_mgr->handle_to_index_map[handle] = new_index;
g_audio_mgr->index_to_handle_map[new_index] = handle;
g_audio_mgr->sources[new_index] = sound;
g_audio_mgr->sound_count++;
}
}
return handle;
}
void
stop_sound(int handle)
{
// Copy element at end into deleted element's place to maintain density
int stopped_audio_index = g_audio_mgr->handle_to_index_map[handle];
size_t last_element_index = g_audio_mgr->sound_count - 1;
g_audio_mgr->sources[stopped_audio_index] = g_audio_mgr->sources[last_element_index];
// Update maps to point to new locations
int last_element_handle = g_audio_mgr->index_to_handle_map[last_element_index];
g_audio_mgr->handle_to_index_map[last_element_handle] = stopped_audio_index;
g_audio_mgr->index_to_handle_map[stopped_audio_index] = last_element_handle;
// Mark invalid
g_audio_mgr->handle_to_index_map[handle] = INVALID_INDEX;
g_audio_mgr->index_to_handle_map[last_element_index] = INVALID_HANDLE;
g_audio_mgr->sound_count--;
}
void
update_sound_position(int handle, vec3 position)
{
int index = g_audio_mgr->handle_to_index_map[handle];
if (index != INVALID_INDEX)
{
g_audio_mgr->sources[index].position = position;
}
}
void
fill_audio_buffer(uint8_t* buffer, int len)
{
// Fill with silence ahead of time
memset(buffer, 0, len);
int stop_count = 0;
int stop_sources[MAX_SOUND_COUNT] = {INVALID_HANDLE};
for (int i = 0; i < g_audio_mgr->sound_count; i++)
{
audio_source_t* sound = &g_audio_mgr->sources[i];
int audio_bytes_remaining = sound->wav->byte_count - sound->cursor;
int bytes_to_fill = (len < audio_bytes_remaining) ? len : audio_bytes_remaining;
int samples_to_fill = bytes_to_fill / sizeof(*sound->wav->data);
float* data = (float*)((uint8_t*)sound->wav->data + sound->cursor);
float* buf = (float*)buffer;
// Calculate angle from listener to source
float right_gain = sound->wav->volume;
float left_gain = sound->wav->volume;
if (sound->stereo)
{
vec3 listener_right = vec3_cross(g_audio_mgr->listener.front, g_audio_mgr->listener.up);
vec3 listener_left = vec3_negate(listener_right);
vec3 target = vec3_sub(sound->position, g_audio_mgr->listener.position);
float attenuation = 1.0f / vec3_length(target);
if (attenuation < 0.1f) {
attenuation = 0.0f;
} else if (attenuation > 1.0f) {
attenuation = 1.0f;
}
float length_squared = vec3_length(listener_left) * vec3_length(target);
float angle_left = RAD_TO_DEG(acosf(vec3_dot(listener_left, target)/length_squared));
float angle_right = RAD_TO_DEG(acosf(vec3_dot(listener_right, target)/length_squared));
left_gain *= (angle_right/180.0f) * attenuation;
right_gain *= (angle_left/180.0f) * attenuation;
}
for (int j = 0; j < samples_to_fill; j+=2)
{
// Left channel
*buf += *data * left_gain;
buf++;
data++;
// Right channel
*buf += *data * right_gain;
buf++;
data++;
}
sound->cursor += bytes_to_fill;
if (sound->cursor >= sound->wav->byte_count)
{
if (sound->is_looping) {
sound->cursor = 0;
} else {
stop_sources[stop_count++] = g_audio_mgr->index_to_handle_map[i];
}
}
}
for (int i = 0; i < stop_count; i++) {
stop_sound(stop_sources[i]);
}
}