Compare commits
19 Commits
exponentia
...
master
Author | SHA1 | Date |
---|---|---|
Casey | aa6b07d8ce | |
Casey | 11fc655bb7 | |
Casey | 26fb7bc8a5 | |
Casey | 4035b50128 | |
Casey | 6ca32e8d49 | |
Casey | 58010c8a07 | |
Casey | 470fd27fbb | |
Casey | 8787229004 | |
Casey | 68e933e08a | |
Casey | 2dfbf934f4 | |
Casey | 751dc4ee66 | |
Casey | 5dc7e334b2 | |
Casey | b68a4b7ccb | |
Casey | e9a2c969c0 | |
Casey | 664be76f30 | |
Casey | c080e62637 | |
Casey | 95bc6e7d33 | |
Casey | 30266e702b | |
Casey | df23dfb874 |
6
Makefile
6
Makefile
|
@ -1,5 +1,5 @@
|
||||||
CFLAGS +=
|
CFLAGS += -Wall -Wextra -Werror
|
||||||
LDFLAGS := -lm
|
LDFLAGS := -lm -ldl -lpthread
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: sfxd sfxc
|
all: sfxd sfxc
|
||||||
|
@ -14,4 +14,4 @@ sfxc: src/sfxc.c
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) sfxd sfxc
|
$(RM) sfxd sfxc miniaudio.o
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[Unit]
|
||||||
|
Description=SoundFX Daemon
|
||||||
|
After=network.target sound.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStartPre=/sbin/rm -fv /tmp/sfxd-socket
|
||||||
|
ExecStart=${HOME}/.local/bin/sfxd -C ${HOME}/.config/sfxd
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# XXX: CHANGEME
|
||||||
|
KEYBOARD_ID="AT Translated Set 2 keyboard"
|
||||||
|
|
||||||
|
b_isColon=false
|
||||||
|
|
||||||
|
xinput test "$KEYBOARD_ID" | while read -r line; do
|
||||||
|
if [[ "$line" =~ "key press" ]]; then
|
||||||
|
keycode="$(echo "$line" | awk '{print$3}')"
|
||||||
|
case "$keycode" in
|
||||||
|
36) # enter
|
||||||
|
b_isColon=false;
|
||||||
|
echo play typewriter/pc_on;; # OneShot:SE/pc_on.wav
|
||||||
|
22) # backspace
|
||||||
|
b_isColon=false;
|
||||||
|
echo play typewriter/bwomp;;
|
||||||
|
111 | 113 | 114 |116) # arrows
|
||||||
|
b_isColon=false;
|
||||||
|
echo play typewriter/arrow;; # OneShot:SE/menu_cursor.wav
|
||||||
|
112 | 117) # page up / page down
|
||||||
|
b_isColon=false;
|
||||||
|
echo play typewriter/page;; # OneShot:SE/page.wav
|
||||||
|
47) # ":"
|
||||||
|
b_isColon=true;
|
||||||
|
echo play typewriter/text;; # OneShot:SE/text.wav
|
||||||
|
12) # "3"
|
||||||
|
if [ "$b_isColon" = "true" ]; then
|
||||||
|
echo play typewriter/meow; # Minecraft:entity.cat.meow
|
||||||
|
else
|
||||||
|
echo play typewriter/text; # OneShot:SE/text.wav
|
||||||
|
fi;
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
b_isColon=false;
|
||||||
|
echo play typewriter/text;; # OneShot:SE/text.wav
|
||||||
|
esac;
|
||||||
|
fi;
|
||||||
|
done | sfxc
|
|
@ -0,0 +1,93 @@
|
||||||
|
.\" Generated by scdoc 1.11.3
|
||||||
|
.\" Complete documentation for this program is not available as a GNU info page
|
||||||
|
.ie \n(.g .ds Aq \(aq
|
||||||
|
.el .ds Aq '
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.\" Begin generated content:
|
||||||
|
.TH "sfxd" "5" "2024-04-30"
|
||||||
|
.PP
|
||||||
|
.SH NAME
|
||||||
|
.PP
|
||||||
|
sfxd - configuration file and client commands list
|
||||||
|
.PP
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
Both configuration file and sfxc(1) commands use the same set of instructions.\&
|
||||||
|
.PP
|
||||||
|
Configuration file is \fBnot\fR loaded automatically, unless specified with `-C [path]` flag, but it can be loaded at a run-time via \fBsource\fR command.\&
|
||||||
|
.PP
|
||||||
|
.SH COMMANDS LIST
|
||||||
|
.PP
|
||||||
|
\fBload NAME FILE\fR Loads specified sound file and stores it under given key.\&
|
||||||
|
.br
|
||||||
|
When given key is already in use, it SHOULD be replaced with the new sound file.\&
|
||||||
|
.br
|
||||||
|
\fB\fRNAME\fB\fR is a string with no spaces, does not support escaping and used as a primary key for accessing loaded sound later.\&
|
||||||
|
.br
|
||||||
|
\fB\fRFILE\fB\fR is an absolute path to a sound file to be loaded.\& Only WAV and MP3 files are supported.\&
|
||||||
|
.PP
|
||||||
|
\fBplay NAME\fR Plays the sound with default pitch and volume.\&
|
||||||
|
.br
|
||||||
|
\fB\fRNAME\fB\fR is a key, used when \fBload\fRing a file.\&
|
||||||
|
.PP
|
||||||
|
\fBplay:rs NAME\fR Plays the sound with randomized pitch.\&
|
||||||
|
.br
|
||||||
|
Pitch range is specified with \fBset:pitch\fR command.\&
|
||||||
|
.br
|
||||||
|
\fB\fRNAME\fB\fR is a key, used when \fBload\fRing a file.\&
|
||||||
|
.PP
|
||||||
|
\fBset:pitch NAME MIN MAX\fR Sets the pitch range for \fBplay:rs\fR
|
||||||
|
.br
|
||||||
|
\fB\fRNAME\fB\fR is a key, used when \fBload\fRing a file.\&
|
||||||
|
.br
|
||||||
|
\fB\fRMIN\fB\fR is the minimum pitch.\& Can'\&t be less than 0.\&
|
||||||
|
.br
|
||||||
|
\fB\fRMAX\fB\fR is the maximum pitch.\& Can'\&t be less than 0.\&
|
||||||
|
.br
|
||||||
|
NOTE: if MAX is smaller than MIN, they are swapped.\&
|
||||||
|
.PP
|
||||||
|
\fBset:volume NAME VOLUME\fR Changes loudness for \fBplay\fR and \fBplay:rs\fR commands.\&
|
||||||
|
.br
|
||||||
|
\fB\fRNAME\fB\fR is a key, used when \fBload\fRing a file.\&
|
||||||
|
.br
|
||||||
|
\fB\fRVOLUME\fB\fR is the volume to be set.\& Can'\&t be outside of 0.\&.\&1 range
|
||||||
|
.PP
|
||||||
|
\fBsource PATH\fR Loads instructions from the configuration file.\&
|
||||||
|
.br
|
||||||
|
\fB\fRPATH\fB\fR is the file path for the configuration file.\&
|
||||||
|
.br
|
||||||
|
NOTE: there is a limit on how deep inclusion can go.\& By default it'\&s set to 32.\&
|
||||||
|
.PP
|
||||||
|
\fBfind QUERY\fR Search for loaded sounds by a substring.\&
|
||||||
|
.br
|
||||||
|
\fB\fRQUERY\fB\fR is a substring to search for.\&
|
||||||
|
.PP
|
||||||
|
\fBlist [QUERY]\fR Prints out all loaded sounds that match an optional query.\&
|
||||||
|
.br
|
||||||
|
\fB\fRQUERY\fB\fR is a fnmatch-compatible pattern.\&
|
||||||
|
.PP
|
||||||
|
\fBls [QUERY]\fR - an alias for \fB\fRlist\fB\fR
|
||||||
|
.PP
|
||||||
|
.SH DEBUG COMMANDS
|
||||||
|
.PP
|
||||||
|
\fBdbg:dump\fR Dumps contents of the hashmap.\&
|
||||||
|
.br
|
||||||
|
Prints each position of allocated hashmap and corresponding contents.\&
|
||||||
|
.br
|
||||||
|
Empty areas are shown as `(unused)`, while used ones contain volume, pitch range, calculated hash, key and file path.\&
|
||||||
|
.PP
|
||||||
|
\fBdbg:counters\fR Shows internal counters.\& Can be disabled at the compile-time.\&
|
||||||
|
.br
|
||||||
|
- `.\&pool_read` - number of reads
|
||||||
|
.br
|
||||||
|
- `.\&pool_write` - number of writes
|
||||||
|
.br
|
||||||
|
- `.\&hash_misses_read` - number of missed reads due to collision
|
||||||
|
.br
|
||||||
|
- `.\&hash_misses_write` - number of collisions in the map
|
||||||
|
.br
|
||||||
|
- `.\&pool.\&use` - number of used cells
|
||||||
|
.br
|
||||||
|
- `.\&pool.\&cap` - hashmap capacity
|
||||||
|
.PP
|
|
@ -0,0 +1,62 @@
|
||||||
|
sfxd(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
sfxd - configuration file and client commands list
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Both configuration file and sfxc(1) commands use the same set of instructions.
|
||||||
|
|
||||||
|
Configuration file is *not* loaded automatically, unless specified with `-C [path]` flag, but it can be loaded at a run-time via *source* command.
|
||||||
|
|
||||||
|
# COMMANDS LIST
|
||||||
|
|
||||||
|
*load NAME FILE* Loads specified sound file and stores it under given key. ++
|
||||||
|
When given key is already in use, it SHOULD be replaced with the new sound file. ++
|
||||||
|
**NAME** is a string with no spaces, does not support escaping and used as a primary key for accessing loaded sound later. ++
|
||||||
|
**FILE** is an absolute path to a sound file to be loaded. Only WAV and MP3 files are supported.
|
||||||
|
|
||||||
|
*play NAME* Plays the sound with default pitch and volume. ++
|
||||||
|
**NAME** is a key, used when *load*ing a file.
|
||||||
|
|
||||||
|
*play:rs NAME* Plays the sound with randomized pitch. ++
|
||||||
|
Pitch range is specified with *set:pitch* command. ++
|
||||||
|
**NAME** is a key, used when *load*ing a file.
|
||||||
|
|
||||||
|
*set:pitch NAME MIN MAX* Sets the pitch range for *play:rs* ++
|
||||||
|
**NAME** is a key, used when *load*ing a file. ++
|
||||||
|
**MIN** is the minimum pitch. Can't be less than 0. ++
|
||||||
|
**MAX** is the maximum pitch. Can't be less than 0. ++
|
||||||
|
NOTE: if MAX is smaller than MIN, they are swapped.
|
||||||
|
|
||||||
|
*set:volume NAME VOLUME* Changes loudness for *play* and *play:rs* commands. ++
|
||||||
|
**NAME** is a key, used when *load*ing a file. ++
|
||||||
|
**VOLUME** is the volume to be set. Can't be outside of 0..1 range
|
||||||
|
|
||||||
|
*source PATH* Loads instructions from the configuration file. ++
|
||||||
|
**PATH** is the file path for the configuration file. ++
|
||||||
|
NOTE: there is a limit on how deep inclusion can go. By default it's set to 32.
|
||||||
|
|
||||||
|
*find QUERY* Search for loaded sounds by a substring. ++
|
||||||
|
**QUERY** is a substring to search for.
|
||||||
|
|
||||||
|
*list [QUERY]* Prints out all loaded sounds that match an optional query. ++
|
||||||
|
**QUERY** is a fnmatch-compatible pattern.
|
||||||
|
|
||||||
|
*ls [QUERY]* - an alias for **list**
|
||||||
|
|
||||||
|
# DEBUG COMMANDS
|
||||||
|
|
||||||
|
*dbg:dump* Dumps contents of the hashmap. ++
|
||||||
|
Prints each position of allocated hashmap and corresponding contents. ++
|
||||||
|
Empty areas are shown as `(unused)`, while used ones contain volume, pitch range, calculated hash, key and file path.
|
||||||
|
|
||||||
|
*dbg:counters* Shows internal counters. Can be disabled at the compile-time. ++
|
||||||
|
- `.pool_read` - number of reads ++
|
||||||
|
- `.pool_write` - number of writes ++
|
||||||
|
- `.hash_misses_read` - number of missed reads due to collision ++
|
||||||
|
- `.hash_misses_write` - number of collisions in the map ++
|
||||||
|
- `.pool.use` - number of used cells ++
|
||||||
|
- `.pool.cap` - hashmap capacity
|
||||||
|
|
77
src/sfxc.c
77
src/sfxc.c
|
@ -1,18 +1,36 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
struct sockaddr_un sa_server, sa_client;
|
||||||
|
|
||||||
|
void cleanup(void) {
|
||||||
|
fprintf(stderr, "[client:dbg] Removing receiving socket.\n");
|
||||||
|
if (sa_client.sun_path[0] != '\0') {
|
||||||
|
unlink(sa_client.sun_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_sigint(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
struct sockaddr_un sa_server, sa_client;
|
|
||||||
int sock_fd;
|
int sock_fd;
|
||||||
static char buffer[BUFFER_SIZE];
|
static char buffer[BUFFER_SIZE];
|
||||||
|
|
||||||
EXPECT(sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0), > 0);
|
EXPECT(sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0), > 0);
|
||||||
|
|
||||||
|
signal(SIGINT, on_sigint);
|
||||||
|
atexit(cleanup);
|
||||||
|
|
||||||
memset(&sa_server, 0, sizeof(sa_server));
|
memset(&sa_server, 0, sizeof(sa_server));
|
||||||
memset(&sa_client, 0, sizeof(sa_client));
|
memset(&sa_client, 0, sizeof(sa_client));
|
||||||
|
|
||||||
|
@ -22,21 +40,52 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
EXPECT(bind(sock_fd, (struct sockaddr *)&sa_client, sizeof(struct sockaddr_un)), == 0);
|
EXPECT(bind(sock_fd, (struct sockaddr *)&sa_client, sizeof(struct sockaddr_un)), == 0);
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
if (argc == 1) {
|
||||||
strncat(buffer, argv[i], BUFFER_SIZE - strlen(buffer) - 1);
|
fprintf(stderr, "[client:inf] You're in the interactive mode. Press Ctrl+C or Ctrl+D to exit.\n");
|
||||||
if (i != argc - 1) {
|
|
||||||
strncat(buffer, " ", BUFFER_SIZE - strlen(buffer) - 1);
|
struct timeval timeout;
|
||||||
|
fd_set fds;
|
||||||
|
int retval, data_len;
|
||||||
|
while (1) {
|
||||||
|
timeout.tv_sec = 1;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
FD_SET(sock_fd, &fds);
|
||||||
|
|
||||||
|
EXPECT(retval = select(sock_fd + 1, &fds, 0, 0, &timeout), != -1);
|
||||||
|
if (retval > 0) {
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||||
|
EXPECT(data_len = read(STDIN_FILENO, buffer, BUFFER_SIZE), != -1);
|
||||||
|
if (data_len == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[data_len - 1] = '\0';
|
||||||
|
EXPECT(sendto(sock_fd, buffer, strlen(buffer), 0, (struct sockaddr *)&sa_server, sizeof(sa_server)), == (ssize_t)strlen(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(sock_fd, &fds)) {
|
||||||
|
while ((data_len = recv(sock_fd, buffer, BUFFER_SIZE, 0)) > 0) {
|
||||||
|
printf("%.*s", data_len, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
strncat(buffer, argv[i], BUFFER_SIZE - strlen(buffer) - 1);
|
||||||
|
if (i != argc - 1) {
|
||||||
|
strncat(buffer, " ", BUFFER_SIZE - strlen(buffer) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("send: '%s'\n", buffer);
|
||||||
|
EXPECT(sendto(sock_fd, buffer, strlen(buffer), 0, (struct sockaddr *)&sa_server, sizeof(sa_server)), == (ssize_t)strlen(buffer));
|
||||||
|
int data_len;
|
||||||
|
while ((data_len = recv(sock_fd, buffer, BUFFER_SIZE, 0)) > 0) {
|
||||||
|
printf("%.*s", data_len, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("send: '%s'\n", buffer);
|
|
||||||
|
|
||||||
EXPECT(sendto(sock_fd, buffer, strlen(buffer), 0, (struct sockaddr *)&sa_server, sizeof(sa_server)), == strlen(buffer));
|
|
||||||
int data_len;
|
|
||||||
while ((data_len = recv(sock_fd, buffer, BUFFER_SIZE, 0)) > 0) {
|
|
||||||
printf("%.*s", data_len, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
unlink(sa_client.sun_path);
|
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
242
src/sfxd.c
242
src/sfxd.c
|
@ -1,5 +1,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "miniaudio.h"
|
#include "miniaudio.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
@ -9,16 +11,15 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
#define KEY_LENGTH 256
|
#define KEY_LENGTH 256
|
||||||
#define MAX_SOUNDS_PER_KEY 32
|
#define MAX_SOUNDS_PER_KEY 32
|
||||||
#define MAX_SOURCE_DEPTH 32
|
#define MAX_SOURCE_DEPTH 32
|
||||||
|
|
||||||
// Set initial offset to size to distinguish start and end
|
|
||||||
#define FOR_OFFSET_PROBE(base, offset, index, size) for (int offset = 0, index = (base) % (size); offset >= 0; offset = ((offset + 1) << 1L) % (((size) + 1) | 1) - 1, offset = offset == (size) ? (size) - 1 /* n * 2 = n - 1 (mod n + 1) */ : offset, index = ((base) + offset) % (size), offset = offset == 0 ? -1 : offset)
|
|
||||||
|
|
||||||
struct msg_target {
|
struct msg_target {
|
||||||
FILE *file_handle;
|
FILE *file_handle;
|
||||||
int sock_fd;
|
int sock_fd;
|
||||||
|
@ -49,10 +50,11 @@ struct sfx_pool {
|
||||||
int cap, use;
|
int cap, use;
|
||||||
};
|
};
|
||||||
|
|
||||||
ssize_t send_data(struct msg_target tgt, const void *data, size_t len);
|
ssize_t send_data(struct msg_target tgt, const void *data, ssize_t len);
|
||||||
ssize_t send_txt(struct msg_target tgt, const char *fmt, ...);
|
ssize_t send_txt(struct msg_target tgt, const char *fmt, ...);
|
||||||
void execute_command(struct msg_target tgt, const char *command, const char *params);
|
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 execute_file(struct msg_target tgt, const char *path, int depth);
|
||||||
|
void send_help(struct msg_target tgt);
|
||||||
|
|
||||||
bool ipc_init(const char *sock_path);
|
bool ipc_init(const char *sock_path);
|
||||||
void ipc_loop(void);
|
void ipc_loop(void);
|
||||||
|
@ -73,13 +75,23 @@ struct sfx_pool sounds_pool = { 0, 0, 0 };
|
||||||
struct global_counters {
|
struct global_counters {
|
||||||
uint64_t pool_read;
|
uint64_t pool_read;
|
||||||
uint64_t pool_write;
|
uint64_t pool_write;
|
||||||
uint64_t hash_misses_write;
|
|
||||||
uint64_t hash_misses_read;
|
uint64_t hash_misses_read;
|
||||||
|
uint64_t hash_misses_write;
|
||||||
|
uint64_t hash_misses_read_acc;
|
||||||
|
uint64_t hash_misses_write_acc;
|
||||||
} global_counters = { 0 };
|
} global_counters = { 0 };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int qsort_compare_index_of_key(const void *p1, const void *p2) {
|
||||||
|
struct sfx_pool_item *it1 = &sounds_pool.sounds[*(int *)p1];
|
||||||
|
struct sfx_pool_item *it2 = &sounds_pool.sounds[*(int *)p2];
|
||||||
|
return strcmp(it1->key, it2->key);
|
||||||
|
}
|
||||||
|
|
||||||
void usage(int argc, char **argv) {
|
void usage(int argc, char **argv) {
|
||||||
// TODO
|
// TODO
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
@ -106,6 +118,13 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
int status = stat(sock_path, &statbuf);
|
||||||
|
if (status == 0 && !S_ISSOCK(statbuf.st_mode)) {
|
||||||
|
PANIC("Specified sock_path is not a socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ipc_init(sock_path)) {
|
if (!ipc_init(sock_path)) {
|
||||||
unlink(sock_path);
|
unlink(sock_path);
|
||||||
|
@ -134,16 +153,17 @@ void ipc_loop(void) {
|
||||||
socklen_t sl_client = sizeof(ipc.sa_client);
|
socklen_t sl_client = sizeof(ipc.sa_client);
|
||||||
while ((data_len = recvfrom(ipc.sock_fd, buffer_ipc_loop, sizeof(buffer_ipc_loop) - 1, 0, (struct sockaddr *)&ipc.sa_client, &sl_client)) != -1) {
|
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';
|
buffer_ipc_loop[data_len] = '\0';
|
||||||
struct sockaddr_un *sau_client = &ipc.sa_client;
|
struct sockaddr *sa_client = (struct sockaddr *)&ipc.sa_client;
|
||||||
char *command = buffer_ipc_loop;
|
char *command = buffer_ipc_loop;
|
||||||
char *params = strchr(command, ' ');
|
char *params = strchr(command, ' ');
|
||||||
if (params != NULL) {
|
if (params != NULL) {
|
||||||
*params = '\0';
|
*params = '\0';
|
||||||
params++;
|
params++;
|
||||||
|
} else {
|
||||||
|
params = "";
|
||||||
}
|
}
|
||||||
execute_command((struct msg_target) { 0, ipc.sock_fd, (struct sockaddr *)&ipc.sa_client, sl_client }, command, params);
|
execute_command((struct msg_target) { 0, ipc.sock_fd, sa_client, sl_client }, command, params);
|
||||||
SOFT_EXPECT(sendto(ipc.sock_fd, "OK\n", 4, 0, (struct sockaddr *)&ipc.sa_client, sl_client), == 4,);
|
SOFT_EXPECT(sendto(ipc.sock_fd, "", 0, 0, sa_client, sl_client), == 0,);
|
||||||
SOFT_EXPECT(sendto(ipc.sock_fd, "", 0, 0, (struct sockaddr *)&ipc.sa_client, sl_client), == 0,);
|
|
||||||
sl_client = sizeof(ipc.sa_client);
|
sl_client = sizeof(ipc.sa_client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +174,7 @@ bool audio_init(void) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t send_data(struct msg_target tgt, const void *data, size_t len) {
|
ssize_t send_data(struct msg_target tgt, const void *data, ssize_t len) {
|
||||||
ssize_t written;
|
ssize_t written;
|
||||||
if (tgt.file_handle) {
|
if (tgt.file_handle) {
|
||||||
if ((written = fwrite(data, 1, len, tgt.file_handle)) != len) {
|
if ((written = fwrite(data, 1, len, tgt.file_handle)) != len) {
|
||||||
|
@ -182,10 +202,19 @@ ssize_t send_txt(struct msg_target tgt, const char *fmt, ...) {
|
||||||
|
|
||||||
void execute_command(struct msg_target tgt, const char *command, const char *params) {
|
void execute_command(struct msg_target tgt, const char *command, const char *params) {
|
||||||
send_txt(tgt, "INF: Received '%s' with '%s'\n", command, params);
|
send_txt(tgt, "INF: Received '%s' with '%s'\n", command, params);
|
||||||
|
send_txt((struct msg_target){ stdout, 0, 0, 0 }, "INF: Received '%s' with '%s'\n", command, params);
|
||||||
|
|
||||||
if (0 == strcmp(command, "load")) {
|
if (0 == strcmp(command, "load")) {
|
||||||
|
if (strlen(params) == 0) {
|
||||||
|
send_txt(tgt, "ERR: argument required: KEY\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const char *key = params;
|
const char *key = params;
|
||||||
char *path = strchr(params, ' ');
|
char *path = strchr(params, ' ');
|
||||||
|
if (path == NULL) {
|
||||||
|
send_txt(tgt, "ERR: argument required: PATH\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
*path = '\0';
|
*path = '\0';
|
||||||
path++;
|
path++;
|
||||||
|
|
||||||
|
@ -205,32 +234,48 @@ void execute_command(struct msg_target tgt, const char *command, const char *par
|
||||||
send_txt(tgt, "OK: 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")) {
|
} else if (0 == strcmp(command, "play")) {
|
||||||
struct sfx_pool_item *sound = sfx_pool_lookup(params);
|
if (strlen(params) == 0) {
|
||||||
if (!sound) {
|
send_txt(tgt, "ERR: argument required: KEY\n");
|
||||||
send_txt(tgt, "No such sound: '%s'\n", params);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_sound *sfx = &sound->sounds[(sound->last_index++) % MAX_SOUNDS_PER_KEY];
|
|
||||||
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);
|
struct sfx_pool_item *sound = sfx_pool_lookup(params);
|
||||||
if (!sound) {
|
if (!sound) {
|
||||||
send_txt(tgt, "ERR: No such sound: '%s'\n", params);
|
send_txt(tgt, "ERR: No such sound: '%s'\n", params);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_sound *sfx = &sound->sounds[(sound->last_index++) % MAX_SOUNDS_PER_KEY];
|
sound->last_index = (sound->last_index + 1) % MAX_SOUNDS_PER_KEY;
|
||||||
|
ma_sound *sfx = &sound->sounds[sound->last_index];
|
||||||
|
|
||||||
|
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")) {
|
||||||
|
if (strlen(params) == 0) {
|
||||||
|
send_txt(tgt, "ERR: argument required: KEY\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct sfx_pool_item *sound = sfx_pool_lookup(params);
|
||||||
|
if (!sound) {
|
||||||
|
send_txt(tgt, "ERR: No such sound: '%s'\n", params);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound->last_index = (sound->last_index + 1) % MAX_SOUNDS_PER_KEY;
|
||||||
|
ma_sound *sfx = &sound->sounds[sound->last_index];
|
||||||
|
|
||||||
float pitch = sound->pitch_min + (rand() / (float)RAND_MAX) * (sound->pitch_max - sound->pitch_min);
|
float pitch = sound->pitch_min + (rand() / (float)RAND_MAX) * (sound->pitch_max - sound->pitch_min);
|
||||||
ma_sound_set_pitch(sfx, pitch);
|
ma_sound_set_pitch(sfx, pitch);
|
||||||
ma_sound_set_volume(sfx, sound->volume);
|
ma_sound_set_volume(sfx, sound->volume);
|
||||||
ma_sound_start(sfx);
|
ma_sound_start(sfx);
|
||||||
} else if (0 == strcmp(command, "set:pitch") || 0 == strcmp(command, "setpitchrange")) {
|
} else if (0 == strcmp(command, "set:pitch")) {
|
||||||
float min, max;
|
float min, max;
|
||||||
char *key;
|
char *key;
|
||||||
sscanf(params, "%ms %f %f", &key, &min, &max);
|
int retval = sscanf(params, "%ms %f %f", &key, &min, &max);
|
||||||
|
if (retval != 3) {
|
||||||
|
send_txt(tgt, "ERR: not enough arguments: KEY MIN MAX needed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct sfx_pool_item *sound = sfx_pool_lookup(key);
|
struct sfx_pool_item *sound = sfx_pool_lookup(key);
|
||||||
if (!sound) {
|
if (!sound) {
|
||||||
|
@ -253,10 +298,14 @@ void execute_command(struct msg_target tgt, const char *command, const char *par
|
||||||
|
|
||||||
sound->pitch_min = min;
|
sound->pitch_min = min;
|
||||||
sound->pitch_max = max;
|
sound->pitch_max = max;
|
||||||
} else if (0 == strcmp(command, "set:volume") || 0 == strcmp(command, "volume")) {
|
} else if (0 == strcmp(command, "set:volume")) {
|
||||||
float vol;
|
float vol;
|
||||||
char *key;
|
char *key;
|
||||||
sscanf(params, "%ms %f", &key, &vol);
|
int retval = sscanf(params, "%ms %f", &key, &vol);
|
||||||
|
if (retval != 2) {
|
||||||
|
send_txt(tgt, "ERR: not enough arguments: KEY VOLUME\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct sfx_pool_item *sound = sfx_pool_lookup(key);
|
struct sfx_pool_item *sound = sfx_pool_lookup(key);
|
||||||
if (!sound) {
|
if (!sound) {
|
||||||
|
@ -265,22 +314,71 @@ void execute_command(struct msg_target tgt, const char *command, const char *par
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vol < 0 || vol > 1) {
|
if (vol < 0 || vol > 1) {
|
||||||
send_txt(tgt, "ERR: Volume out of range: 0 <= %f < 1\n", vol);
|
send_txt(tgt, "ERR: Volume out of range: 0 <= %f <= 1\n", vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->volume = vol;
|
sound->volume = vol;
|
||||||
} else if (0 == strcmp(command, "dump")) {
|
} else if (0 == strcmp(command, "find")) {
|
||||||
|
if (strlen(params) == 0) {
|
||||||
|
send_txt(tgt, "ERR: argument required: QUERY\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < sounds_pool.cap; i++) {
|
||||||
|
struct sfx_pool_item item = sounds_pool.sounds[i];
|
||||||
|
if (item.key[0] != '\0' && strstr(item.key, params)) {
|
||||||
|
send_txt(tgt, "%s\n", item.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (0 == strcmp(command, "list") || 0 == strcmp(command, "ls")) {
|
||||||
|
// TODO: find a better way to do this!
|
||||||
|
int *indices = calloc(sounds_pool.cap, sizeof(int));
|
||||||
|
for (int i = 0; i < sounds_pool.cap; i++) indices[i] = i;
|
||||||
|
qsort(indices, sounds_pool.cap, sizeof(int), qsort_compare_index_of_key);
|
||||||
|
for (int i = 0; i < sounds_pool.cap; i++) {
|
||||||
|
struct sfx_pool_item *item = &sounds_pool.sounds[indices[i]];
|
||||||
|
if (item->key[0] == '\0') continue;
|
||||||
|
if (0 == fnmatch(params, item->key, 0) || strlen(params) == 0) {
|
||||||
|
send_txt(tgt, "%s\n", item->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(indices);
|
||||||
|
} else if (0 == strcmp(command, "source")) {
|
||||||
|
if (strlen(params) == 0) {
|
||||||
|
send_txt(tgt, "ERR: argument required: PATH\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wordexp_t p;
|
||||||
|
int retval = wordexp(params, &p, 0);
|
||||||
|
#define _WORDEXP_ERROR(ERR_CODE) case ERR_CODE: send_txt(tgt, "ERR: wordexp: " #ERR_CODE); return;
|
||||||
|
switch (retval) {
|
||||||
|
case 0: break;
|
||||||
|
_WORDEXP_ERROR(WRDE_BADCHAR);
|
||||||
|
_WORDEXP_ERROR(WRDE_BADVAL);
|
||||||
|
_WORDEXP_ERROR(WRDE_CMDSUB);
|
||||||
|
_WORDEXP_ERROR(WRDE_NOSPACE);
|
||||||
|
_WORDEXP_ERROR(WRDE_SYNTAX);
|
||||||
|
default: send_txt(tgt, "ERR: wordexp: Unknown error: %d\n", retval); return;
|
||||||
|
}
|
||||||
|
#undef _WORDEXP_ERROR
|
||||||
|
for (size_t i = 0; i < p.we_wordc; i++) {
|
||||||
|
execute_file(tgt, p.we_wordv[i], 0);
|
||||||
|
}
|
||||||
|
wordfree(&p);
|
||||||
|
} else if (0 == strcmp(command, "help")) {
|
||||||
|
send_help(tgt);
|
||||||
|
} else if (0 == strcmp(command, "meow")) {
|
||||||
|
send_txt(tgt, "mrr~\n");
|
||||||
|
} else if (0 == strcmp(command, "dbg:dump")) {
|
||||||
for (int i = 0; i < sounds_pool.cap; i++) {
|
for (int i = 0; i < sounds_pool.cap; i++) {
|
||||||
struct sfx_pool_item item = sounds_pool.sounds[i];
|
struct sfx_pool_item item = sounds_pool.sounds[i];
|
||||||
if (item.key[0] == '\0') {
|
if (item.key[0] == '\0') {
|
||||||
send_txt(tgt, "%3d (unused)\n", i);
|
send_txt(tgt, "%3d (unused)\n", i);
|
||||||
} else {
|
} else {
|
||||||
send_txt(tgt, "%3d vol=%7.5f pitch=%7.5f..%7.5f @%2d (0x%08x) \"%s\"\n",
|
send_txt(tgt, "%3d vol=%7.5f pitch=%7.5f..%7.5f (0x%08x) \"%s\" from %s\n",
|
||||||
i, item.volume, item.pitch_min, item.pitch_max, item.last_index, adler32(item.key, strlen(item.key)), item.key);
|
i, item.volume, item.pitch_min, item.pitch_max, adler32(item.key, strlen(item.key)), item.key, item.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (0 == strcmp(command, "source")) {
|
|
||||||
execute_file(tgt, params, 0);
|
|
||||||
} else if (0 == strcmp(command, "dbg:counters")) {
|
} else if (0 == strcmp(command, "dbg:counters")) {
|
||||||
#ifdef NO_COUNTERS
|
#ifdef NO_COUNTERS
|
||||||
send_txt(tgt, "ERR: counters are disabled at compile-time with NO_COUNTERS feature flag.");
|
send_txt(tgt, "ERR: counters are disabled at compile-time with NO_COUNTERS feature flag.");
|
||||||
|
@ -289,15 +387,48 @@ void execute_command(struct msg_target tgt, const char *command, const char *par
|
||||||
send_txt(tgt, ".pool_write = %ld\n", global_counters.pool_write);
|
send_txt(tgt, ".pool_write = %ld\n", global_counters.pool_write);
|
||||||
send_txt(tgt, ".hash_misses_read = %ld\n", global_counters.hash_misses_read);
|
send_txt(tgt, ".hash_misses_read = %ld\n", global_counters.hash_misses_read);
|
||||||
send_txt(tgt, ".hash_misses_write = %ld\n", global_counters.hash_misses_write);
|
send_txt(tgt, ".hash_misses_write = %ld\n", global_counters.hash_misses_write);
|
||||||
|
send_txt(tgt, ".hash_misses_read_acc = %ld\n", global_counters.hash_misses_read_acc);
|
||||||
|
send_txt(tgt, ".hash_misses_write_acc = %ld\n", global_counters.hash_misses_write_acc);
|
||||||
send_txt(tgt, ".pool.use = %d\n", sounds_pool.use);
|
send_txt(tgt, ".pool.use = %d\n", sounds_pool.use);
|
||||||
send_txt(tgt, ".pool.cap = %d\n", sounds_pool.cap);
|
send_txt(tgt, ".pool.cap = %d\n", sounds_pool.cap);
|
||||||
#endif
|
#endif
|
||||||
|
} else if (0 == strcmp(command, "dbg:map")) {
|
||||||
|
int n_hits = 0, n_misses = 0;
|
||||||
|
for (int i = 0; i < sounds_pool.cap; i++) {
|
||||||
|
struct sfx_pool_item *item = &sounds_pool.sounds[i];
|
||||||
|
if ((i % 16) == 0) {
|
||||||
|
send_txt(tgt, "# ");
|
||||||
|
}
|
||||||
|
if (item->key[0] == '\0') {
|
||||||
|
send_txt(tgt, "-- ");
|
||||||
|
} else {
|
||||||
|
uint32_t hash = adler32(item->key, strlen(item->key));
|
||||||
|
int index = hash % sounds_pool.cap;
|
||||||
|
send_txt(tgt, "%02d ", i - index);
|
||||||
|
}
|
||||||
|
if ((i % 16) == 15) {
|
||||||
|
send_txt(tgt, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send_txt(tgt, ".used = %ld\n.size = %ld\n", sounds_pool.use, sounds_pool.cap);
|
||||||
|
send_txt(tgt, ".hits = %d\n.misses = %d\n", n_hits, n_misses);
|
||||||
} else if (0 == strcmp(command, "")) {
|
} else if (0 == strcmp(command, "")) {
|
||||||
} else {
|
} else {
|
||||||
send_txt(tgt, "ERR: unknown command: %s\n", command);
|
send_txt(tgt, "ERR: unknown command: %s\n", command);
|
||||||
} // commands
|
} // commands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void send_help(struct msg_target tgt) {
|
||||||
|
send_txt(tgt, "load NAME PATH load PATH sound as NAME\n");
|
||||||
|
send_txt(tgt, "play NAME play previously loaded NAME\n");
|
||||||
|
send_txt(tgt, "play:rs NAME play _ _ _ with random pitch\n");
|
||||||
|
send_txt(tgt, "set:pitch NAME MIN MAX set pitch range for NAME\n");
|
||||||
|
send_txt(tgt, "set:volume NAME VOLUME set loudness for NAME\n");
|
||||||
|
send_txt(tgt, "source PATH load instructions from PATH\n");
|
||||||
|
send_txt(tgt, "find QUERY search for keys by QUERY\n");
|
||||||
|
send_txt(tgt, "list [QUERY] basically ls\n");
|
||||||
|
}
|
||||||
|
|
||||||
static char buffer_exec_file[BUFFER_SIZE];
|
static char buffer_exec_file[BUFFER_SIZE];
|
||||||
bool execute_file(struct msg_target tgt, const char *path, int depth) {
|
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);
|
send_txt(tgt, "DBG: soucing file %s at depth %d\n", path, depth);
|
||||||
|
@ -332,7 +463,24 @@ bool execute_file(struct msg_target tgt, const char *path, int depth) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
execute_file(tgt, args, depth + 1);
|
wordexp_t p;
|
||||||
|
int retval = wordexp(args, &p, 0);
|
||||||
|
#define _WORDEXP_ERROR(ERR_CODE) case ERR_CODE: send_txt(tgt, "ERR: wordexp: " #ERR_CODE); return false;
|
||||||
|
switch (retval) {
|
||||||
|
case 0: break;
|
||||||
|
_WORDEXP_ERROR(WRDE_BADCHAR);
|
||||||
|
_WORDEXP_ERROR(WRDE_BADVAL);
|
||||||
|
_WORDEXP_ERROR(WRDE_CMDSUB);
|
||||||
|
_WORDEXP_ERROR(WRDE_NOSPACE);
|
||||||
|
_WORDEXP_ERROR(WRDE_SYNTAX);
|
||||||
|
default: send_txt(tgt, "ERR: wordexp: Unknown error: %d\n", retval); return false;
|
||||||
|
}
|
||||||
|
#undef _WORDEXP_ERROR
|
||||||
|
for (size_t i = 0; i < p.we_wordc; i++) {
|
||||||
|
send_txt(tgt, "DBG: executing file %s\n", p.we_wordv[i]);
|
||||||
|
execute_file(tgt, p.we_wordv[i], depth + 1);
|
||||||
|
}
|
||||||
|
wordfree(&p);
|
||||||
} else {
|
} else {
|
||||||
execute_command(tgt, cmd, args);
|
execute_command(tgt, cmd, args);
|
||||||
}
|
}
|
||||||
|
@ -364,14 +512,16 @@ void sfx_pool_grow(int size) {
|
||||||
for (int i = 0; i < sounds_pool.cap; i++) {
|
for (int i = 0; i < sounds_pool.cap; i++) {
|
||||||
if (sounds_pool.sounds[i].key[0] == '\0') continue;
|
if (sounds_pool.sounds[i].key[0] == '\0') continue;
|
||||||
uint32_t new_hash = adler32(sounds_pool.sounds[i].key, strlen(sounds_pool.sounds[i].key));
|
uint32_t new_hash = adler32(sounds_pool.sounds[i].key, strlen(sounds_pool.sounds[i].key));
|
||||||
FOR_OFFSET_PROBE (new_hash, offset, index, size) {
|
global_counters.pool_write++;
|
||||||
#ifndef NO_COUNTERS
|
for (int offset = 0; offset < size; offset++) {
|
||||||
global_counters.pool_write++;
|
int index = (new_hash + offset) % size;
|
||||||
if (offset != 0) {
|
|
||||||
global_counters.hash_misses_write++;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (new_items[index].key[0] == '\0') {
|
if (new_items[index].key[0] == '\0') {
|
||||||
|
#ifndef NO_COUNTERS
|
||||||
|
global_counters.hash_misses_write_acc++;
|
||||||
|
if (offset != 0) {
|
||||||
|
global_counters.hash_misses_write++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
memcpy(&new_items[index], &sounds_pool.sounds[i], sizeof(struct sfx_pool_item));
|
memcpy(&new_items[index], &sounds_pool.sounds[i], sizeof(struct sfx_pool_item));
|
||||||
used++;
|
used++;
|
||||||
break;
|
break;
|
||||||
|
@ -387,11 +537,14 @@ void sfx_pool_grow(int size) {
|
||||||
|
|
||||||
struct sfx_pool_item *sfx_pool_lookup(const char *key) {
|
struct sfx_pool_item *sfx_pool_lookup(const char *key) {
|
||||||
if (key == NULL) return NULL;
|
if (key == NULL) return NULL;
|
||||||
|
if (strlen(key) == 0) return NULL;
|
||||||
uint32_t hash = adler32(key, strlen(key));
|
uint32_t hash = adler32(key, strlen(key));
|
||||||
FOR_OFFSET_PROBE (hash, offset, index, sounds_pool.cap) {
|
global_counters.pool_read++;
|
||||||
|
for (int offset = 0; offset < sounds_pool.cap; offset++) {
|
||||||
|
int index = (hash + offset) % sounds_pool.cap;
|
||||||
if (0 == strncmp(key, sounds_pool.sounds[index].key, KEY_LENGTH)) {
|
if (0 == strncmp(key, sounds_pool.sounds[index].key, KEY_LENGTH)) {
|
||||||
#ifndef NO_COUNTERS
|
#ifndef NO_COUNTERS
|
||||||
global_counters.pool_read++;
|
global_counters.hash_misses_read_acc++;
|
||||||
if (offset != 0) {
|
if (offset != 0) {
|
||||||
global_counters.hash_misses_read++;
|
global_counters.hash_misses_read++;
|
||||||
}
|
}
|
||||||
|
@ -403,11 +556,15 @@ struct sfx_pool_item *sfx_pool_lookup(const char *key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sfx_pool_item *sfx_pool_find_place_for(const char *key) {
|
struct sfx_pool_item *sfx_pool_find_place_for(const char *key) {
|
||||||
|
if (key == NULL) return NULL;
|
||||||
|
if (strlen(key) == 0) return NULL;
|
||||||
uint32_t hash = adler32(key, strlen(key));
|
uint32_t hash = adler32(key, strlen(key));
|
||||||
FOR_OFFSET_PROBE (hash, offset, index, sounds_pool.cap) {
|
global_counters.pool_write++;
|
||||||
|
for (int offset = 0; offset < sounds_pool.cap; offset++) {
|
||||||
|
int index = (hash + offset) % sounds_pool.cap;
|
||||||
if (sounds_pool.sounds[index].key[0] == '\0' || 0 == strncmp(sounds_pool.sounds[index].key, key, KEY_LENGTH)) {
|
if (sounds_pool.sounds[index].key[0] == '\0' || 0 == strncmp(sounds_pool.sounds[index].key, key, KEY_LENGTH)) {
|
||||||
#ifndef NO_COUNTERS
|
#ifndef NO_COUNTERS
|
||||||
global_counters.pool_write++;
|
global_counters.hash_misses_write_acc++;
|
||||||
if (offset != 0) {
|
if (offset != 0) {
|
||||||
global_counters.hash_misses_write++;
|
global_counters.hash_misses_write++;
|
||||||
}
|
}
|
||||||
|
@ -420,6 +577,7 @@ 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) {
|
struct sfx_pool_item *sfx_pool_load(const char *key, const char *path) {
|
||||||
if (key == NULL || path == NULL) return NULL;
|
if (key == NULL || path == NULL) return NULL;
|
||||||
|
if (strlen(key) == 0 || strlen(path) == 0) return NULL;
|
||||||
|
|
||||||
struct sfx_pool_item *sound = sfx_pool_find_place_for(key);
|
struct sfx_pool_item *sound = sfx_pool_find_place_for(key);
|
||||||
SOFT_EXPECT(sound == NULL, == false, NULL);
|
SOFT_EXPECT(sound == NULL, == false, NULL);
|
||||||
|
|
Loading…
Reference in New Issue