void audio_mgr_startup(void) { g_engine->audio_mgr.sound_count = NULL_HANDLE+1; } void set_listener(vec3 position, vec3 front, vec3 up) { g_engine->audio_mgr.listener.position = position; g_engine->audio_mgr.listener.front = front; g_engine->audio_mgr.listener.up = up; } handle_t play_sound(audio_source_t sound) { handle_t handle = NULL_HANDLE; if (g_engine->audio_mgr.sound_count < MAX_AUDIO_SOURCE_COUNT) { // Find free handle for (int i = NULL_HANDLE+1; i < MAX_AUDIO_SOURCE_COUNT; i++) { if (g_engine->audio_mgr.handle_to_index_map[i] == NULL_HANDLE) { handle = i; break; } } size_t new_index = g_engine->audio_mgr.sound_count; g_engine->audio_mgr.handle_to_index_map[handle] = new_index; g_engine->audio_mgr.index_to_handle_map[new_index] = handle; g_engine->audio_mgr.sources[new_index] = sound; g_engine->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_engine->audio_mgr.handle_to_index_map[handle]; size_t last_element_index = g_engine->audio_mgr.sound_count - 1; g_engine->audio_mgr.sources[stopped_audio_index] = g_engine->audio_mgr.sources[last_element_index]; // Update maps to point to new locations int last_element_handle = g_engine->audio_mgr.index_to_handle_map[last_element_index]; g_engine->audio_mgr.handle_to_index_map[last_element_handle] = stopped_audio_index; g_engine->audio_mgr.index_to_handle_map[stopped_audio_index] = last_element_handle; // Mark invalid g_engine->audio_mgr.sources[last_element_index].type = AUDIO_TYPE_NULL; g_engine->audio_mgr.handle_to_index_map[handle] = NULL_HANDLE; g_engine->audio_mgr.index_to_handle_map[last_element_index] = NULL_HANDLE; g_engine->audio_mgr.sound_count--; } void update_sound_position(int handle, vec3 position) { (void)handle;(void)position; int index = g_engine->audio_mgr.handle_to_index_map[handle]; transform_t* transform = get_transform(g_engine->audio_mgr.sources[index].transform); transform->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_handles[MAX_AUDIO_SOURCE_COUNT] = {NULL_HANDLE}; for (int i = NULL_HANDLE+1; i < g_engine->audio_mgr.sound_count; i++) { audio_source_t* source = &g_engine->audio_mgr.sources[i]; sound_t* sound = get_sound(source->sound); LOG_ASSERT(sound->byte_count >= source->cursor, "Audio overflow"); int audio_bytes_remaining = sound->byte_count - source->cursor; int bytes_to_fill = (len < audio_bytes_remaining) ? len : audio_bytes_remaining; int samples_to_fill = bytes_to_fill / sizeof(*sound->data); float* data = (float*)((uint8_t*)sound->data + source->cursor); float* buf = (float*)buffer; // Calculate angle from listener to source float right_gain = 1.0f; float left_gain = 1.0f; transform_t* transform = get_transform(source->transform); switch (source->type) { case AUDIO_TYPE_MUSIC: { right_gain = source->volume; left_gain = source->volume; } break; case AUDIO_TYPE_SFX: { vec3 listener_right = vec3_cross(g_engine->audio_mgr.listener.front, g_engine->audio_mgr.listener.up); vec3 listener_left = vec3_negate(listener_right); vec3 target = vec3_sub(transform->position, g_engine->audio_mgr.listener.position); float distance = vec3_length(target); if (distance > 0.0f) { float attenuation = 1.0f / distance; if (attenuation < 0.1f) { attenuation = 0.0f; } else if (attenuation > 1.0f) { attenuation = 1.0f; } // TODO: Make this better. This is probably very wrong. And I don't fully understand it. float angle_left = RAD_TO_DEG(acosf(vec3_dot(listener_left, target)/distance)); float angle_right = RAD_TO_DEG(acosf(vec3_dot(listener_right, target)/distance)); left_gain = source->volume * (angle_right/180.0f) * attenuation; right_gain = source->volume* (angle_left/180.0f) * attenuation; } } break; default: break; } 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++; } source->cursor += bytes_to_fill; if (source->cursor >= sound->byte_count) { if (source->type == AUDIO_TYPE_MUSIC) { source->cursor = 0; } else if (source->type == AUDIO_TYPE_SFX) { stop_handles[stop_count++] = g_engine->audio_mgr.index_to_handle_map[i]; } } } for (int i = 0; i < stop_count; i++) { stop_sound(stop_handles[i]); } }