Blame src/sound.c

09c823
09c823
#include <sdl.h></sdl.h>
09c823
#include <sdl_mixer.h></sdl_mixer.h>
09c823
09c823
#include "private.h"
ca6bde
#include "sound.h"
09c823
09c823
09c823
#define HELI_SOUND_CHANNELS 64
09c823
09c823
09c823
typedef struct _HeliSoundInstance {
09c823
	char *path;
09c823
	Mix_Chunk *chunk;
09c823
	int refcount;
09c823
} HeliSoundInstance;
09c823
09c823
typedef struct _HeliSoundTrashItem {
09c823
	Mix_Chunk *chunk;
a20939
	unsigned int time;
09c823
} HeliSoundTrashItem;
09c823
09c823
struct _Sound {
09c823
	HeliSoundInstance *instance;
09c823
};
09c823
09c823
09c823
static int initialized;
09c823
static int ready;
09c823
09c823
static HeliArray cache;
09c823
static Sound channels[HELI_SOUND_CHANNELS];
09c823
09c823
static HeliSoundTrashItem *trash;
09c823
static size_t trashSize;
ca6bde
static int memSoundIndex = 0;
09c823
09c823
a20939
static int less(unsigned int a, unsigned int b)
a20939
	{ return (b - a) < (1u << 31); }
a20939
ca6bde
09c823
static void trashAdd(Mix_Chunk *chunk) {
09c823
	if (!chunk) return;
09c823
	
a20939
	unsigned int time = SDL_GetTicks() + 1000u;
09c823
	for(int i = 0; i < trashSize; ++i)
09c823
		if (!trash[i].chunk) 
09c823
			{ trash[i].chunk = chunk; trash[i].time = time; return; }
09c823
	
09c823
	size_t prevSize = trashSize;
09c823
	trashSize += trashSize/4 + 32;
1015c5
	trash = realloc(trash, trashSize*sizeof(*trash));
09c823
	memset(&trash[prevSize], 0, (trashSize - prevSize)*sizeof(*trash));
09c823
	
09c823
	trash[prevSize].chunk = chunk;
09c823
	trash[prevSize].time = time;
09c823
}
09c823
ca6bde
09c823
static void trashProcess() {
a20939
	unsigned int time = SDL_GetTicks();
09c823
	for(int i = 0; i < trashSize; ++i)
a20939
		if (trash[i].chunk && !less(time, trash[i].time))
09c823
			{ Mix_FreeChunk(trash[i].chunk); trash[i].chunk = NULL; }
09c823
}
09c823
ca6bde
09c823
static void trashDestroy() {
09c823
	for(int i = 0; i < trashSize; ++i)
09c823
		if (trash[i].chunk) Mix_FreeChunk(trash[i].chunk);
09c823
	trashSize = 0;
09c823
	free(trash);
09c823
	trash = NULL;
09c823
}
09c823
09c823
09c823
static void init() {
09c823
	if (initialized) return;
09c823
	if (SDL_Init(SDL_INIT_AUDIO) == 0) {
09c823
		int loaded = Mix_Init(MIX_INIT_OGG | MIX_INIT_FLAC | MIX_INIT_MP3);
09c823
		if (!(loaded & MIX_INIT_OGG )) fprintf(stderr, "helianthus: cannot initialize OGG  support\n");
09c823
		if (!(loaded & MIX_INIT_FLAC)) fprintf(stderr, "helianthus: cannot initialize FLAC support\n");
09c823
		if (!(loaded & MIX_INIT_MP3 )) fprintf(stderr, "helianthus: cannot initialize MP3  support\n");
09c823
		ready = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == 0;
09c823
		if (!ready) fprintf(stderr, "helianthus: cannot open audio device\n");
09c823
	}
09c823
	initialized = TRUE;
09c823
}
09c823
ca6bde
09c823
static void deinit() {
09c823
	if (!initialized) return;
09c823
	heliArrayDestroy(&cache);
09c823
	if (ready) Mix_CloseAudio();
09c823
	trashDestroy();
09c823
	Mix_Quit();
09c823
	SDL_Quit();
09c823
	for(int i = 0; i < HELI_SOUND_CHANNELS; ++i) channels[i] = NULL;
09c823
	ready = FALSE;
09c823
	initialized = FALSE;
09c823
}
09c823
09c823
09c823
static HeliSoundInstance* load(const char *path) {
09c823
	HeliSoundInstance *s = calloc(1, sizeof(*s));
09c823
	s->path = heliStringCopy(path);
09c823
	if (ready) {
ca6bde
		if (strncmp(path, "helimem:", 8) == 0) {
ca6bde
			void *data = NULL;
ca6bde
			int size = 0;
ca6bde
			s->chunk = NULL;
ca6bde
			if (sscanf(path, "helimem:%p:%d:", &data, &size) != EOF && data && size > 0) {
ca6bde
				SDL_RWops *rw = SDL_RWFromMem((void*)data, size);
ca6bde
				if (rw) s->chunk = Mix_LoadWAV_RW(rw, SDL_TRUE);
ca6bde
			}
ca6bde
			if (!s->chunk)
ca6bde
				fprintf(stderr, "helianthus: cannot load sound from memory\n");
ca6bde
		} else {
ca6bde
			s->chunk = Mix_LoadWAV(path);
ca6bde
			if (!s->chunk)
ca6bde
				fprintf(stderr, "helianthus: cannot load sound file: %s\n", path);
ca6bde
		}
09c823
	}
09c823
	return s;
09c823
}
09c823
ca6bde
09c823
static void unload(HeliSoundInstance *s) {
09c823
	assert(!s->refcount);
09c823
	free(s->path);
09c823
	trashAdd(s->chunk);
09c823
	free(s);
09c823
}
09c823
09c823
09c823
Sound createSound(const char *path) {
8eb855
	if (!heliInitialized) return NULL;
09c823
	init();
09c823
	HeliPair *item = heliStringmapGet(&cache, path);
09c823
	if (!item) item = heliStringmapAdd(&cache, path, load(path), (HeliFreeCallback)&unload);
09c823
	HeliSoundInstance *s = (HeliSoundInstance*)item->value;
09c823
	++s->refcount;
09c823
	
09c823
	Sound sound = calloc(1, sizeof(*sound));
09c823
	sound->instance = s;
8eb855
	
8eb855
	heliObjectRegister(sound, (HeliFreeCallback)&soundDestroy);
09c823
	return sound;
09c823
}
09c823
ca6bde
ca6bde
Sound createSoundFromMemory(const void *data, int size) {
ca6bde
	char buffer[1024] = {};
ca6bde
	sprintf(buffer, "helimem:%p:%d:%d", data, size, ++memSoundIndex);
ca6bde
	return createSound(buffer);
ca6bde
}
ca6bde
ca6bde
ca6bde
Sound soundClone(Sound sound) {
ca6bde
	if (!sound) return NULL;
ca6bde
	return createSound(sound->instance ? sound->instance->path : NULL);
ca6bde
}
ca6bde
ca6bde
09c823
void soundDestroy(Sound sound) {
8eb855
	heliObjectUnregister(sound);
09c823
	soundStop(sound);
09c823
	if (--sound->instance->refcount <= 0)
09c823
		heliStringmapRemove(&cache, sound->instance->path);
8935bc
	free(sound);
09c823
}
09c823
09c823
09c823
void soundPlay(Sound sound, int loop) {
09c823
	if (!ready) return;
09c823
	if (sound->instance->chunk) {
09c823
		int channel = Mix_PlayChannel(-1, sound->instance->chunk, loop ? -1 : 0);
09c823
		assert(channel < HELI_SOUND_CHANNELS);
09c823
		if (channel >= 0 && channel < HELI_SOUND_CHANNELS)
09c823
			channels[channel] = sound;
09c823
	}
09c823
}
09c823
ca6bde
09c823
void soundStop(Sound sound) {
09c823
	for(int i = 0; i < HELI_SOUND_CHANNELS; ++i) {
09c823
		if (channels[i] == sound) {
09c823
			Mix_Pause(i);
09c823
			Mix_HaltChannel(i);
09c823
			channels[i] = NULL;
09c823
		}
09c823
	}
09c823
}
09c823
09c823
09c823
void heliSoundUpdate()
09c823
	{ trashProcess(); }
09c823
ca6bde
09c823
void heliSoundFinish()
09c823
	{ deinit(); }
09c823