175 lines
4.3 KiB
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]);
|
|
}
|
|
}
|
|
|