You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
8.2 KiB
318 lines
8.2 KiB
#include "sound.h"
|
|
#include "../common/log.h"
|
|
#include "../common/array.h"
|
|
#include "../common/hashmap.h"
|
|
#include "../common/variant.h"
|
|
#include "../common/string_utils.h"
|
|
#include "file_io.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include <soloud_c.h>
|
|
|
|
struct Sound_Source_Buffer
|
|
{
|
|
int type;
|
|
union
|
|
{
|
|
Wav* wav;
|
|
WavStream* wavstream;
|
|
};
|
|
};
|
|
|
|
struct Sound_State
|
|
{
|
|
Soloud* soloud_context;
|
|
float volume;
|
|
};
|
|
|
|
static struct Sound_State sound_state =
|
|
{
|
|
.soloud_context = NULL,
|
|
.volume = 1.f
|
|
};
|
|
|
|
static struct Hashmap* sound_sources = NULL;
|
|
|
|
bool sound_init(void)
|
|
{
|
|
sound_sources = hashmap_new();
|
|
|
|
sound_state.soloud_context = Soloud_create();
|
|
|
|
if(!sound_state.soloud_context)
|
|
{
|
|
log_error("sound:init", "Failed to create sound context");
|
|
return false;
|
|
}
|
|
|
|
Soloud_initEx(sound_state.soloud_context, SOLOUD_CLIP_ROUNDOFF | SOLOUD_ENABLE_VISUALIZATION, SOLOUD_AUTO, SOLOUD_AUTO, SOLOUD_AUTO, SOLOUD_AUTO);
|
|
Soloud_setGlobalVolume(sound_state.soloud_context, 4);
|
|
|
|
Soloud_set3dListenerParameters(sound_state.soloud_context,
|
|
0.f, 0.f, 0.f, // Position
|
|
0.f, 0.f, -1.f, // At
|
|
0.f, 1.f, 0.f); // Up
|
|
|
|
log_message("Sound initialized with %s", Soloud_getBackendString(sound_state.soloud_context));
|
|
return true;
|
|
}
|
|
|
|
void sound_listener_update(float apos_x, float apos_y, float apos_z,
|
|
float afwd_x, float afwd_y, float afwd_z,
|
|
float aup_x, float aup_y, float aup_z)
|
|
{
|
|
Soloud_set3dListenerParameters(sound_state.soloud_context,
|
|
apos_x, apos_y, apos_z, // Position
|
|
afwd_x, afwd_y, afwd_z, // At
|
|
aup_x, aup_y, aup_z); // Up
|
|
}
|
|
|
|
void sound_volume_set(float volume)
|
|
{
|
|
if(volume < 0.f) volume = 0.f;
|
|
sound_state.volume = volume;
|
|
Soloud_setGlobalVolume(sound_state.soloud_context, sound_state.volume);
|
|
}
|
|
|
|
void sound_update_3d(void)
|
|
{
|
|
Soloud_update3dAudio(sound_state.soloud_context);
|
|
}
|
|
|
|
void sound_cleanup(void)
|
|
{
|
|
char* key = NULL;
|
|
struct Variant* value = NULL;
|
|
HASHMAP_FOREACH(sound_sources, key, value)
|
|
{
|
|
struct Sound_Source_Buffer* source = value->val_voidptr;
|
|
sound_source_stop_all(source);
|
|
switch(source->type)
|
|
{
|
|
case ST_WAV: Wav_destroy(source->wav); break;
|
|
case ST_WAV_STREAM: WavStream_destroy(source->wavstream); break;
|
|
}
|
|
free(source);
|
|
}
|
|
|
|
hashmap_free(sound_sources);
|
|
Soloud_deinit(sound_state.soloud_context);
|
|
Soloud_destroy(sound_state.soloud_context);
|
|
|
|
sound_state.volume = 0.f;
|
|
sound_state.soloud_context = NULL;
|
|
}
|
|
|
|
void sound_source_instance_destroy(uint source_instance)
|
|
{
|
|
Soloud_stop(sound_state.soloud_context, source_instance);
|
|
}
|
|
|
|
void sound_source_instance_update_position(uint source_instance, float apos_x, float apos_y, float apos_z)
|
|
{
|
|
Soloud_set3dSourceParameters(sound_state.soloud_context, source_instance, apos_x, apos_y, apos_z);
|
|
}
|
|
|
|
uint sound_source_instance_create(struct Sound_Source_Buffer* source, bool is3d)
|
|
{
|
|
assert(source);
|
|
uint source_instance = 0;
|
|
if(is3d)
|
|
{
|
|
source_instance = Soloud_play3dEx(sound_state.soloud_context,
|
|
source->type == ST_WAV ? source->wav : source->wavstream,
|
|
0.f, 0.f, 0.f,
|
|
0.f, 0.f, 0.f,
|
|
1.f,
|
|
true,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
source_instance = Soloud_playEx(sound_state.soloud_context, source->type == ST_WAV ? source->wav : source->wavstream, 1.f, 0.0f, true, 0);
|
|
}
|
|
return source_instance;
|
|
}
|
|
|
|
void sound_source_instance_volume_set(uint source_instance, float volume)
|
|
{
|
|
if(volume < 0.f) volume = 0.f;
|
|
Soloud_setVolume(sound_state.soloud_context, source_instance, volume);
|
|
}
|
|
|
|
void sound_source_instance_loop_set(uint source_instance, bool loop)
|
|
{
|
|
Soloud_setLooping(sound_state.soloud_context, source_instance, loop);
|
|
}
|
|
|
|
void sound_source_instance_play(uint source_instance)
|
|
{
|
|
Soloud_setPause(sound_state.soloud_context, source_instance, false);
|
|
}
|
|
|
|
void sound_source_instance_pause(uint source_instance)
|
|
{
|
|
Soloud_setPause(sound_state.soloud_context, source_instance, true);
|
|
}
|
|
|
|
void sound_source_instance_rewind(uint source_instance)
|
|
{
|
|
Soloud_seek(sound_state.soloud_context, source_instance, 0.0);
|
|
}
|
|
|
|
void sound_source_instance_stop(uint source_instance)
|
|
{
|
|
Soloud_stop(sound_state.soloud_context, source_instance);
|
|
}
|
|
|
|
void sound_source_instance_min_max_distance_set(uint source_instance, float min_distance, float max_distance)
|
|
{
|
|
Soloud_set3dSourceMinMaxDistance(sound_state.soloud_context, source_instance, min_distance, max_distance);
|
|
}
|
|
|
|
void sound_source_instance_attenuation_set(uint source_instance, int attenuation_type, float rolloff_factor)
|
|
{
|
|
Soloud_set3dSourceAttenuation(sound_state.soloud_context, source_instance, attenuation_type, rolloff_factor);
|
|
}
|
|
|
|
float sound_source_instance_volume_get(uint source_instance)
|
|
{
|
|
return Soloud_getVolume(sound_state.soloud_context, source_instance);
|
|
}
|
|
|
|
bool sound_source_instance_loop_get(uint source_instance)
|
|
{
|
|
return Soloud_getLooping(sound_state.soloud_context, source_instance);
|
|
}
|
|
|
|
bool sound_source_instance_is_paused(uint source_instance)
|
|
{
|
|
return Soloud_getPause(sound_state.soloud_context, source_instance);
|
|
}
|
|
|
|
struct Sound_Source_Buffer* sound_source_create(const char* filename, int type)
|
|
{
|
|
if(!filename) return NULL;
|
|
|
|
struct Sound_Source_Buffer* source = NULL;
|
|
|
|
long size = 0L;
|
|
char* memory = io_file_read(DIRT_INSTALL, filename, "rb", &size);
|
|
|
|
//See if we've already loaded this file
|
|
if(hashmap_value_exists(sound_sources, filename))
|
|
{
|
|
source = (struct Sound_Source_Buffer*)hashmap_ptr_get(sound_sources, filename);
|
|
return source;
|
|
}
|
|
|
|
source = malloc(sizeof(*source));
|
|
if(!source)
|
|
{
|
|
log_error("sound:source_create", "Out of memory!");
|
|
return NULL;
|
|
}
|
|
|
|
switch(type)
|
|
{
|
|
case ST_WAV:
|
|
{
|
|
Wav* wave = Wav_create();
|
|
int rc = Wav_loadMem(wave, memory, (uint)size);
|
|
if(rc != 0)
|
|
{
|
|
log_error("sound:source_create", "Failed to load %s, Soloud: %s", filename, Soloud_getErrorString(sound_state.soloud_context, rc));
|
|
free(source);
|
|
return 0;
|
|
}
|
|
source->type = ST_WAV;
|
|
source->wav = wave;
|
|
}
|
|
break;
|
|
case ST_WAV_STREAM:
|
|
{
|
|
WavStream* wave_stream = WavStream_create();
|
|
int rc = WavStream_loadMem(wave_stream, memory, (uint)size);
|
|
if(rc != 0)
|
|
{
|
|
log_error("sound:source_create", "Failed to load %s, Soloud: %s", filename, Soloud_getErrorString(sound_state.soloud_context, rc));
|
|
free(source);
|
|
return 0;
|
|
}
|
|
source->type = ST_WAV_STREAM;
|
|
}
|
|
break;
|
|
default: log_error("sound:source_create", "Invalid source type %d", type); return 0;
|
|
}
|
|
|
|
hashmap_ptr_set(sound_sources, filename, (void*)source);
|
|
return source;
|
|
}
|
|
|
|
struct Sound_Source_Buffer* sound_source_get(const char* name)
|
|
{
|
|
struct Sound_Source_Buffer* source = NULL;
|
|
if(hashmap_value_exists(sound_sources, name))
|
|
{
|
|
source = (struct Sound_Source_Buffer*)hashmap_ptr_get(sound_sources, name);
|
|
}
|
|
return source;
|
|
}
|
|
|
|
void sound_source_destroy(const char* name)
|
|
{
|
|
struct Sound_Source_Buffer* source = sound_source_get(name);
|
|
if(source)
|
|
{
|
|
sound_source_stop_all(source);
|
|
switch(source->type)
|
|
{
|
|
case ST_WAV: Wav_destroy(source->wav); break;
|
|
case ST_WAV_STREAM: WavStream_destroy(source->wavstream); break;
|
|
}
|
|
free(source);
|
|
}
|
|
hashmap_value_remove(sound_sources, name);
|
|
}
|
|
|
|
void sound_source_volume_set(struct Sound_Source_Buffer* source, float volume)
|
|
{
|
|
assert(source);
|
|
switch(source->type)
|
|
{
|
|
case ST_WAV: Wav_setVolume(source->wav, volume); break;
|
|
case ST_WAV_STREAM: WavStream_setVolume(source->wavstream, volume); break;
|
|
}
|
|
}
|
|
|
|
void sound_source_loop_set(struct Sound_Source_Buffer* source, bool loop)
|
|
{
|
|
assert(source);
|
|
switch(source->type)
|
|
{
|
|
case ST_WAV: Wav_setLooping(source->wav, loop); break;
|
|
case ST_WAV_STREAM: WavStream_setLooping(source->wavstream, loop); break;
|
|
}
|
|
}
|
|
|
|
void sound_source_stop_all(struct Sound_Source_Buffer* source)
|
|
{
|
|
assert(source);
|
|
switch(source->type)
|
|
{
|
|
case ST_WAV: Soloud_stopAudioSource(sound_state.soloud_context, source->wav); break;
|
|
case ST_WAV_STREAM: Soloud_stopAudioSource(sound_state.soloud_context, source->wavstream); break;
|
|
}
|
|
}
|
|
|
|
void sound_source_min_max_distance_set(struct Sound_Source_Buffer* source, float min_distance, float max_distance)
|
|
{
|
|
assert(source);
|
|
switch(source->type)
|
|
{
|
|
case ST_WAV: Wav_set3dMinMaxDistance(source->wav, min_distance, max_distance); break;
|
|
case ST_WAV_STREAM: WavStream_set3dMinMaxDistance(source->wavstream, min_distance, max_distance); break;
|
|
}
|
|
} |