diff --git a/src/sfxd.c b/src/sfxd.c index a5bc844..74abc41 100644 --- a/src/sfxd.c +++ b/src/sfxd.c @@ -1,5 +1,6 @@ #include "common.h" #include "miniaudio.h" +#include #include #include #include @@ -11,6 +12,7 @@ #define KEY_LENGTH 256 #define MAX_SOUNDS_PER_KEY 32 +#define MAX_SOURCE_DEPTH 32 struct msg_target { FILE *file_handle; @@ -31,7 +33,7 @@ struct audio_data { struct sfx_pool { struct sfx_pool_item { char key[KEY_LENGTH]; - ma_sound sounds[MAX_SOUNDS_PER_KEY]; + ma_sound *sounds; int last_index; float volume; @@ -43,6 +45,7 @@ struct sfx_pool { ssize_t send_data(struct msg_target tgt, const void *data, size_t len); ssize_t send_txt(struct msg_target tgt, const char *fmt, ...); void execute_command(struct msg_target tgt, const char *command, const char *params); +bool execute_file(struct msg_target tgt, const char *path, int depth); bool ipc_init(const char *sock_path); void ipc_loop(); @@ -59,14 +62,35 @@ struct ipc_data ipc = { 0 }; struct audio_data audio = { 0 }; struct sfx_pool sounds_pool = { 0, 0, 0 }; +void usage(int argc, char **argv) { + +} + int main(int argc, char **argv) { + char *sock_path = DAEMON_SOCKET_PATH; + sfx_pool_grow(32); - printf("audio_init()\n"); if (!audio_init()) { return EXIT_FAILURE; } - char *sock_path = DAEMON_SOCKET_PATH; + int c; + while ((c = getopt(argc, argv, "hC:S:")) != -1) { + switch (c) { + case 'h': + usage(argc, argv); + return EXIT_SUCCESS; + break; + case 'S': + sock_path = optarg; + break; + case 'C': + EXPECT(execute_file((struct msg_target) { stdout, 0, 0, 0 }, optarg, 0), == true); + break; + } + } + + if (!ipc_init(sock_path)) { unlink(sock_path); return EXIT_FAILURE; @@ -88,14 +112,14 @@ bool ipc_init(const char *sock_path) { return true; } +static char buffer_ipc_loop[BUFFER_SIZE]; void ipc_loop() { - static char buffer[BUFFER_SIZE]; ssize_t data_len; socklen_t sl_client = sizeof(ipc.sa_client); - while ((data_len = recvfrom(ipc.sock_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&ipc.sa_client, &sl_client)) != -1) { - buffer[data_len] = '\0'; + while ((data_len = recvfrom(ipc.sock_fd, buffer_ipc_loop, sizeof(buffer_ipc_loop) - 1, 0, (struct sockaddr *)&ipc.sa_client, &sl_client)) != -1) { + buffer_ipc_loop[data_len] = '\0'; struct sockaddr_un *sau_client = &ipc.sa_client; - char *command = buffer; + char *command = buffer_ipc_loop; char *params = strchr(command, ' '); if (params != NULL) { *params = '\0'; @@ -131,8 +155,8 @@ ssize_t send_data(struct msg_target tgt, const void *data, size_t len) { return written; } +static char txtbuf[BUFFER_SIZE]; ssize_t send_txt(struct msg_target tgt, const char *fmt, ...) { - static char txtbuf[BUFFER_SIZE]; va_list args; va_start(args, fmt); vsnprintf(txtbuf, BUFFER_SIZE - 1, fmt, args); @@ -141,18 +165,28 @@ ssize_t send_txt(struct msg_target tgt, const char *fmt, ...) { } void execute_command(struct msg_target tgt, const char *command, const char *params) { - send_txt(tgt, "Received '%s' with '%s'\n", command, params); + send_txt(tgt, "INF: Received '%s' with '%s'\n", command, params); if (0 == strcmp(command, "load")) { const char *key = params; char *path = strchr(params, ' '); *path = '\0'; path++; + + if (sounds_pool.use / (float)sounds_pool.cap >= 0.75) { + send_txt(tgt, "DBG: pool overflow: %d/%d (%.3f%%)\n", + sounds_pool.use, sounds_pool.cap, + 100.0 * sounds_pool.use / (float)sounds_pool.cap); + sfx_pool_grow(sounds_pool.cap + 32); + } + + send_txt(tgt, "Loading audio key=%s from %s\n", key, path); + struct sfx_pool_item *sound = sfx_pool_load(key, path); if (sound == NULL) { - send_txt(tgt, "Load failed\n"); + send_txt(tgt, "ERR: Load failed\n"); } else { - send_txt(tgt, "Loaded as %08x\n", adler32(key, strlen(key))); + send_txt(tgt, "OK: Loaded as %08x\n", adler32(key, strlen(key))); } } else if (0 == strcmp(command, "play")) { struct sfx_pool_item *sound = sfx_pool_lookup(params); @@ -165,9 +199,117 @@ void execute_command(struct msg_target tgt, const char *command, const char *par ma_sound_set_pitch(sfx, sound->pitch_min); ma_sound_set_volume(sfx, sound->volume); ma_sound_start(sfx); - } + } else if (0 == strcmp(command, "play:rs") || 0 == strcmp(command, "play_rs")) { + struct sfx_pool_item *sound = sfx_pool_lookup(params); + if (!sound) { + send_txt(tgt, "ERR: No such sound: '%s'\n", params); + return; + } + + ma_sound *sfx = &sound->sounds[(sound->last_index++) % MAX_SOUNDS_PER_KEY]; + float pitch = sound->pitch_min + (rand() / (float)RAND_MAX) * (sound->pitch_max - sound->pitch_min); + ma_sound_set_pitch(sfx, pitch); + ma_sound_set_volume(sfx, sound->volume); + ma_sound_start(sfx); + } else if (0 == strcmp(command, "set:pitch") || 0 == strcmp(command, "setpitchrange")) { + float min, max; + char *key; + sscanf(params, "%ms %f %f", &key, &min, &max); + + struct sfx_pool_item *sound = sfx_pool_lookup(key); + if (!sound) { + send_txt(tgt, "ERR: No such sound: '%s'\n", key); + return; + } + + if (min < 0) send_txt(tgt, "WARN: min pitch is too small: %f < 0\n", min); + if (max < 0) send_txt(tgt, "WARN: max pitch is too small: %f < 0\n", max); + if (max < min) { + send_txt(tgt, "WARN: max < min: %f < %f\n", max, min); + float tmp = max; + max = min; + min = tmp; + } + if (min < 0 || max < 0) { + send_txt(tgt, "ERR: either min or max are too small\n"); + return; + } + + sound->pitch_min = min; + sound->pitch_max = max; + } else if (0 == strcmp(command, "set:volume") || 0 == strcmp(command, "volume")) { + float vol; + char *key; + sscanf(params, "%ms %f", &key, &vol); + + struct sfx_pool_item *sound = sfx_pool_lookup(key); + if (!sound) { + send_txt(tgt, "ERR: No such sound: '%s'\n", key); + return; + } + + if (vol < 0 || vol > 1) { + send_txt(tgt, "ERR: Volume out of range: 0 <= %f < 1\n", vol); + } + + sound->volume = vol; + } else if (0 == strcmp(command, "dump")) { + for (int i = 0; i < sounds_pool.cap; i++) { + struct sfx_pool_item item = sounds_pool.sounds[i]; + if (item.key[0] != '\0') { + send_txt(tgt, "%3d vol=%7.5f pitch=%7.5f..%7.5f @%2d (0x%08x) \"%s\"\n", + i, item.volume, item.pitch_min, item.pitch_max, item.last_index, adler32(item.key, strlen(item.key)), item.key); + } + } + } else if (0 == strcmp(command, "source")) { + execute_file(tgt, params, 0); + } else if (0 == strcmp(command, "")) { + + } // commands } +static char buffer_exec_file[BUFFER_SIZE]; +bool execute_file(struct msg_target tgt, const char *path, int depth) { + send_txt(tgt, "DBG: soucing file %s at depth %d\n", path, depth); + char *newline; + FILE *fp = fopen(path, "rt"); + if (!fp) { + send_txt(tgt, "ERR: failed to open file: %d %s\n", errno, strerror(errno)); + return false; + } + + for (int lineno = 1; fgets(buffer_exec_file, BUFFER_SIZE, fp); lineno++) { + if ((newline = strchr(buffer_exec_file, '\n')) != NULL) { + *newline = '\0'; + } + + if (buffer_exec_file[0] == '#') { + send_txt(tgt, "DBG: comment: %s\n", buffer_exec_file); + continue; + } + + char *cmd = buffer_exec_file; + char *args = strchr(buffer_exec_file, ' '); + if (args != NULL) { + *args = '\0'; + args++; + } + + send_txt(tgt, "DBG: running: %s %s\n", cmd, args); + if (0 == strcmp(cmd, "source")) { + if (depth >= MAX_SOURCE_DEPTH) { + send_txt(tgt, "ERR: recursion error: %d deep in %s:%d\n", depth, path, lineno); + fclose(fp); + return false; + } + execute_file(tgt, args, depth + 1); + } else { + execute_command(tgt, cmd, args); + } + } + fclose(fp); + return true; +} void sfx_pool_clear() { for (int i = 0; i < sounds_pool.cap; i++) { @@ -176,7 +318,9 @@ void sfx_pool_clear() { ma_sound_uninit(&sounds_pool.sounds[i].sounds[j]); } } + free(sounds_pool.sounds[i].sounds); } + sounds_pool.use = 0; } void sfx_pool_grow(int size) { @@ -232,14 +376,12 @@ struct sfx_pool_item *sfx_pool_find_place_for(const char *key) { struct sfx_pool_item *sfx_pool_load(const char *key, const char *path) { if (key == NULL || path == NULL) return NULL; - if (sounds_pool.use / (float)sounds_pool.cap >= 0.75) { - sfx_pool_grow(sounds_pool.cap + 32); - } - struct sfx_pool_item *sound = sfx_pool_find_place_for(key); SOFT_EXPECT(sound == NULL, == false, NULL); ma_result res; + ma_sound *sounds = calloc(MAX_SOUNDS_PER_KEY, sizeof(ma_sound)); + sound->sounds = sounds; SOFT_EXPECT(res = ma_sound_init_from_file(&audio.engine, path, 0, 0, 0, &sound->sounds[0]), == MA_SUCCESS, NULL); bool keep_parameters = false; @@ -261,6 +403,7 @@ struct sfx_pool_item *sfx_pool_load(const char *key, const char *path) { } strncpy(sound->key, key, KEY_LENGTH); + sounds_pool.use++; return sound; }