Skip to content

Commit

Permalink
Add voice chat
Browse files Browse the repository at this point in the history
Now you can press-and-hold F12 and transmit your voice to all
players on your bridge.  Or Ctrl-F12 to transmit your voice to all
players. libopus is used to compress the audio stream.

Signed-off-by: Stephen M. Cameron <stephenmcameron@gmail.com>
  • Loading branch information
smcameron committed Apr 1, 2020
1 parent 5d6674c commit d93c51e
Show file tree
Hide file tree
Showing 11 changed files with 567 additions and 22 deletions.
16 changes: 12 additions & 4 deletions Makefile
Expand Up @@ -522,7 +522,7 @@ _COMMONCLIENTOBJS= snis_ui_element.o snis_font.o snis_text_input.o \
snis_strip_chart.o material.o stl_parser.o entity.o matrix.o my_point.o liang-barsky.o joystick.o \
quat.o vec4.o thrust_attachment.o docking_port.o ui_colors.o snis_keyboard.o solarsystem_config.o \
pronunciation.o snis_preferences.o snis_pull_down_menu.o snis_client_debug.o starmap_adjacency.o \
shape_collision.o oriented_bounding_box.o xdg_base_dir_spec.o
shape_collision.o oriented_bounding_box.o xdg_base_dir_spec.o snis_voice_chat.o
COMMONCLIENTOBJS=${COMMONOBJS} ${OGGOBJ} ${SNDOBJS} $(patsubst %,$(OD)/%,${_COMMONCLIENTOBJS})

_CLIENTOBJS= shader.o graph_dev_opengl.o opengl_cap.o snis_graph.o snis_client.o joystick_config.o
Expand Down Expand Up @@ -642,9 +642,13 @@ VORBISFLAGS:=$(subst -I,-isystem ,$(shell $(PKG_CONFIG) --cflags vorbisfile))
ifeq (${WITHVOICECHAT},yes)
LIBOPUS=-L. -lopus
OPUSARCHIVE=libopus.a
VCHAT=-DWITHVOICECHAT=1
OPUSINCLUDE=-I./opus-1.3.1/include
else
LIBOPUS=
OPUSARCHIVE=
VCHAT=
OPUSINCLUDE=
endif

ifeq (${V},1)
Expand All @@ -665,7 +669,7 @@ SNISSERVERDBGCOMPILE=$(ECHO) ' COMPILE' $< && $(CC) -DSNIS_SERVER_DATA ${MYCFLA
SNISCLIENTDBGCOMPILE=$(ECHO) ' COMPILE' $< && $(CC) -DSNIS_CLIENT_DATA ${MYCFLAGS} ${LUACFLAGS} -c -o $(OD)/snis_client_debug.o $<

CLIENTLINK=$(ECHO) ' LINK' $@ && $(CC) ${MYCFLAGS} ${SNDFLAGS} -o $@ ${GTKCFLAGS} ${GLEXTCFLAGS} ${CLIENTOBJS} ${GTKLDFLAGS} ${GLEXTLDFLAGS} ${LIBS} ${SNDLIBS} $(LDFLAGS) ${LIBOPUS}
LIMCLIENTLINK=$(ECHO) ' LINK' $@ && $(CC) ${MYCFLAGS} ${SNDFLAGS} -o $@ ${GTKCFLAGS} ${LIMCLIENTOBJS} ${GLEXTLDFLAGS} ${LIBS} ${SNDLIBS} $(LDFLAGS)
LIMCLIENTLINK=$(ECHO) ' LINK' $@ && $(CC) ${MYCFLAGS} ${SNDFLAGS} -o $@ ${GTKCFLAGS} ${LIMCLIENTOBJS} ${GLEXTLDFLAGS} ${LIBS} ${SNDLIBS} $(LDFLAGS) ${LIBOPUS}
SDLCLIENTLINK=$(ECHO) ' LINK' $@ && $(CC) ${MYCFLAGS} ${SNDFLAGS} -o $@ ${SDLCFLAGS} ${SDLCLIENTOBJS} ${SDLLIBS} ${LIBS} ${SNDLIBS} $(LDFLAGS)
SERVERLINK=$(ECHO) ' LINK' $@ && $(CC) ${MYCFLAGS} -o $@ ${SERVEROBJS} ${SERVERLIBS} $(LDFLAGS)
MULTIVERSELINK=$(ECHO) ' LINK' $@ && $(CC) ${MYCFLAGS} -o $@ ${MULTIVERSEOBJS} ${MULTIVERSELIBS} $(LDFLAGS)
Expand Down Expand Up @@ -856,11 +860,11 @@ $(OD)/snis_server_tracker.o: snis_server_tracker.c snis_server_tracker.h pthread
$(Q)$(COMPILE)

$(OD)/snis_client.o: snis_client.c Makefile build_info.h ui_colors.h ${ODT}
$(Q)$(GLEXTCOMPILE)
$(Q)$(GLEXTCOMPILE) ${VCHAT}

$(OD)/snis_limited_client.o: snis_client.c Makefile build_info.h ${ODT}
@echo -n " (limited client) "
$(Q)$(LIMCOMPILE)
$(Q)$(LIMCOMPILE) ${VCHAT}

$(OD)/mesh_viewer.o: mesh_viewer.c Makefile build_info.h ${ODT}
$(Q)$(SDLCOMPILE)
Expand Down Expand Up @@ -1039,6 +1043,9 @@ $(OD)/matrix.o: matrix.c Makefile ${ODT}
$(OD)/starmap_adjacency.o: starmap_adjacency.c starmap_adjacency.h $(OD)/quat.o $(OD)/vec4.o ${ODT}
$(Q)$(COMPILE)

$(OD)/snis_voice_chat.o: snis_voice_chat.c snis_voice_chat.h pthread_util.h wwviaudio.h ${OPUSARCHIVE}
$(Q)$(COMPILE) ${VCHAT} ${OPUSINCLUDE}

$(OD)/replacement_assets.o: replacement_assets.c replacement_assets.h ${ODT}
$(Q)$(COMPILE)

Expand Down Expand Up @@ -1421,6 +1428,7 @@ scan-build:
scan-build -o /tmp/snis-scan-build-output make CC=clang
xdg-open /tmp/snis-scan-build-output/*/index.html

# opus stuff for voice chat
opus-1.3.1.tar.gz:
wget https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz

Expand Down
2 changes: 1 addition & 1 deletion snis.h
Expand Up @@ -41,7 +41,7 @@
#include "shape_collision.h"

#define DEFAULT_SOLAR_SYSTEM "default"
#define SNIS_PROTOCOL_VERSION "SNIS050"
#define SNIS_PROTOCOL_VERSION "SNIS051"
#define COMMON_MTWIST_SEED 97872
/* dimensions of the "known" universe */
#define XKNOWN_DIM 600000.0
Expand Down
45 changes: 42 additions & 3 deletions snis_client.c
Expand Up @@ -132,6 +132,7 @@
#include "planetary_properties.h"
#include "xdg_base_dir_spec.h"
#include "open-simplex-noise.h"
#include "snis_voice_chat.h"

#define SHIP_COLOR CYAN
#define STARBASE_COLOR RED
Expand Down Expand Up @@ -835,7 +836,6 @@ static double quat_to_heading(const union quat *q)
return atan2(-v.v.z, v.v.x);
}

static void print_demon_console_msg(const char *fmt, ...);
static void print_demon_console_color_msg(int color, const char *fmt, ...);
static void set_object_location(struct snis_entity *o, double x, double y, double z)
{
Expand Down Expand Up @@ -3381,7 +3381,7 @@ static void scale_points(struct my_point_t *points, int npoints,

static void wakeup_gameserver_writer(void);

static void queue_to_server(struct packed_buffer *pb)
void queue_to_server(struct packed_buffer *pb)
{
if (!pb) {
stacktrace("snis_client: NULL packed_buffer in queue_to_server()");
Expand Down Expand Up @@ -4397,6 +4397,12 @@ static gint key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
return TRUE;
}

if (event->keyval == GDK_F12) {
if (control_key_pressed)
voice_chat_start_recording(VOICE_CHAT_DESTINATION_ALL, 0);
else
voice_chat_start_recording(VOICE_CHAT_DESTINATION_CREW, 0);
}
#if 0
printf("event->keyval = 0x%08x, GDK_z = %08x, GDK_space = %08x\n", event->keyval, GDK_z, GDK_space);
#endif
Expand Down Expand Up @@ -4641,6 +4647,9 @@ static gint key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
if (ka > 0 && ka < NKEYSTATES)
kbstate.pressed[ka] = 0;

if (event->keyval == GDK_F12)
voice_chat_stop_recording();

return FALSE;
}

Expand Down Expand Up @@ -7687,6 +7696,30 @@ static int process_request_oneshot_sound(void)
return 0;
}

static int process_opus_audio_data(void)
{
int rc;
uint8_t buffer[4008];
uint8_t destination;
uint32_t radio_channel;
uint16_t datalen;

rc = read_and_unpack_buffer(buffer, "bwh",
&destination, &radio_channel, &datalen);
if (rc)
return rc;
if (datalen > 4000) {
fprintf(stderr, "%s: Unexpected opus audio data length of %u\n",
"snis_client", datalen);
return -1;
}
rc = snis_readsocket(gameserver_sock, buffer, datalen);
if (rc)
return rc;
voice_chat_play_opus_packet(buffer, datalen);
return 0;
}

static int process_custom_button(void);
static int process_console_op(void);
static int process_client_config(void);
Expand Down Expand Up @@ -7997,6 +8030,11 @@ static void *gameserver_reader(__attribute__((unused)) void *arg)
if (rc)
goto protocol_error;
break;
case OPCODE_OPUS_AUDIO_DATA:
rc = process_opus_audio_data();
if (rc)
goto protocol_error;
break;
default:
goto protocol_error;
}
Expand Down Expand Up @@ -17763,7 +17801,7 @@ static void print_demon_console_msg_helper(char *buffer, int color)
}
}

static void print_demon_console_msg(const char *fmt, ...)
void print_demon_console_msg(const char *fmt, ...)
{
va_list arg_ptr;
char buffer[256];
Expand Down Expand Up @@ -23255,6 +23293,7 @@ int main(int argc, char *argv[])
setup_natural_language_fifo();
setup_demon_fifo();
setup_text_to_speech_thread();
voice_chat_setup_threads();
ecx = entity_context_new(5000, 5000);

snis_slider_mouse_position_query(&mouse.x, &mouse.y);
Expand Down
2 changes: 2 additions & 0 deletions snis_opcode_def.c
Expand Up @@ -222,6 +222,8 @@ int snis_opcode_def_init(void)
rc |= init_opcode_def(OPCODE_LOAD_SKYBOX, "n/a");
rc |= init_opcode_def(OPCODE_ROBOT_AUTO_MANUAL, "bb");
rc |= init_opcode_def(OPCODE_ADD_WARP_EFFECT, "bwSSSSSS");
/* rc |= init_opcode_def(OPCODE_OPUS_AUDIO_DATA, "bbwhr"); */
/* ^^^ opcode, destination, channel, bytecount, audio data */
rc |= init_opcode_def(OPCODE_REQUEST_PITCH, "bb");
rc |= init_opcode_def(OPCODE_REQUEST_ROLL, "bb");
rc |= init_opcode_def(OPCODE_REQUEST_SCIBALL_YAW, "bb");
Expand Down
2 changes: 1 addition & 1 deletion snis_packet.h
Expand Up @@ -169,7 +169,7 @@
#define OPCODE_LOAD_SKYBOX 185
#define OPCODE_ROBOT_AUTO_MANUAL 186
#define OPCODE_ADD_WARP_EFFECT 187
/* UNUSED OPCODE 188 */
#define OPCODE_OPUS_AUDIO_DATA 188
#define OPCODE_REQUEST_PITCH 189
#define OPCODE_REQUEST_ROLL 190
#define OPCODE_REQUEST_SCIBALL_YAW 191
Expand Down
55 changes: 53 additions & 2 deletions snis_server.c
Expand Up @@ -121,6 +121,7 @@
#include "shape_collision.h"
#include "planetary_ring_data.h"
#include "scipher.h"
#include "snis_voice_chat.h"

#define CLIENT_UPDATE_PERIOD_NSECS 500000000
#define MAXCLIENTS 100
Expand Down Expand Up @@ -2006,7 +2007,6 @@ static void send_packet_to_all_clients_on_a_bridge_except(uint32_t shipid, struc
if (skip)
continue;
}

pbc = packed_buffer_copy(pb);
pb_queue_to_client(c, pbc);
}
Expand Down Expand Up @@ -2256,7 +2256,7 @@ static void send_packet_to_requestor_plus_role_on_a_bridge(struct game_client *r
client_unlock();
}

__attribute__((unused)) static void send_packet_to_all_clients_except(struct packed_buffer *pb, uint32_t roles, struct except_clients *except)
static void send_packet_to_all_clients_except(struct packed_buffer *pb, uint32_t roles, struct except_clients *except)
{
send_packet_to_all_clients_on_a_bridge_except(ANY_SHIP_ID, pb, roles, except);
}
Expand Down Expand Up @@ -20046,6 +20046,52 @@ static int process_comms_crypto(struct game_client *c)
return 0;
}

static int process_opus_audio_data(struct game_client *c)
{
int rc;
uint8_t buffer[4008];
uint8_t destination;
uint32_t radio_channel;
uint16_t datalen;
struct packed_buffer *pb;
struct except_clients except;

rc = read_and_unpack_buffer(c, buffer, "bwh",
&destination, &radio_channel, &datalen);
if (rc)
return rc;
if (datalen > 4000) {
fprintf(stderr, "%s: Unexpected opus audio data length of %hu\n",
logprefix(), datalen);
return -1;
}
rc = snis_readsocket(c->socket, buffer, datalen);
if (rc)
return rc;
pb = packed_buffer_allocate(8 + datalen);
packed_buffer_append(pb, "bbwhr", OPCODE_OPUS_AUDIO_DATA,
destination, radio_channel, datalen, buffer, datalen);

/* Don't send a client's own audio back at him. */
except.nclients = 1;
except.client[0] = c - &client[0];
except.shipid[0] = c->shipid;

switch (destination) {
case VOICE_CHAT_DESTINATION_CREW:
send_packet_to_all_clients_on_a_bridge_except(c->shipid, pb, ROLE_ALL, &except);
break;
case VOICE_CHAT_DESTINATION_ALL:
case VOICE_CHAT_DESTINATION_CHANNEL: /* TODO: implement radio channels */
send_packet_to_all_clients_except(pb, ROLE_ALL, &except);
break;
default:
fprintf(stderr, "Unexpected destination code %hhu in opus audio packet\n", destination);
return -1;
}
return 0;
}

static int process_toggle_demon_ai_debug_mode(struct game_client *c)
{
c->debug_ai = !c->debug_ai;
Expand Down Expand Up @@ -22780,6 +22826,11 @@ static void process_instructions_from_client(struct game_client *c)
if (rc)
goto protocol_error;
break;
case OPCODE_OPUS_AUDIO_DATA:
rc = process_opus_audio_data(c);
if (rc)
goto protocol_error;
break;
default:
goto protocol_error;
}
Expand Down

0 comments on commit d93c51e

Please sign in to comment.