2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/multi_voice.cpp $
16 * Revision 1.7 2005/10/02 09:30:10 taylor
17 * sync up rest of big-endian network changes. it should at least be as good as what's in FS2_Open now, only better :)
19 * Revision 1.6 2004/09/20 01:31:44 theoddone33
22 * Revision 1.5 2004/06/11 01:46:06 tigital
23 * byte-swapping changes for bigendian systems
25 * Revision 1.4 2003/08/03 16:10:29 taylor
26 * cleanup; compile warning fixes
28 * Revision 1.3 2002/06/09 04:41:24 relnev
29 * added copyright header
31 * Revision 1.2 2002/05/07 03:16:47 theoddone33
32 * The Great Newline Fix
34 * Revision 1.1.1.1 2002/05/03 03:28:10 root
38 * 9 3/10/99 6:50p Dave
39 * Changed the way we buffer packets for all clients. Optimized turret
40 * fired packets. Did some weapon firing optimizations.
42 * 8 3/09/99 6:24p Dave
43 * More work on object update revamping. Identified several sources of
44 * unnecessary bandwidth.
46 * 7 1/08/99 4:42p Anoop
47 * Fixed memory overwrite problem.
49 * 6 11/19/98 4:19p Dave
50 * Put IPX sockets back in psnet. Consolidated all multiplayer config
53 * 5 11/19/98 8:03a Dave
54 * Full support for D3-style reliable sockets. Revamped packet lag/loss
55 * system, made it receiver side and at the lowest possible level.
57 * 4 11/17/98 11:12a Dave
58 * Removed player identification by address. Now assign explicit id #'s.
60 * 3 11/05/98 5:55p Dave
61 * Big pass at reducing #includes
63 * 2 10/07/98 10:53a Dave
66 * 1 10/07/98 10:50a Dave
68 * 43 6/16/98 4:22p Allender
69 * fix Net_player reference problem
71 * 42 6/13/98 9:32p Mike
72 * Kill last character in file which caused "Find in Files" to report the
73 * file as "not a text file."
75 * 41 6/13/98 6:01p Hoffoss
76 * Externalized all new (or forgot to be added) strings to all the code.
78 * 40 6/13/98 3:19p Hoffoss
79 * NOX()ed out a bunch of strings that shouldn't be translated.
81 * 39 6/12/98 2:49p Dave
84 * 38 5/27/98 4:14p Dave
85 * Spiffed up text display for voice status.
87 * 37 5/27/98 1:15p Dave
88 * Change pxo login failure popup text. Put in message display of player
89 * who is currently doing voice.
91 * 36 5/24/98 3:45a Dave
92 * Minor object update fixes. Justify channel information on PXO. Add a
93 * bunch of configuration stuff for the standalone.
95 * 35 5/24/98 12:12a Frank
96 * Make sure recording clients don't ever send/store/handle packets past
97 * the limit of the accum buffer.
99 * 34 5/22/98 9:35p Dave
100 * Put in channel based support for PXO. Put in "shutdown" button for
101 * standalone. UI tweaks for TvT
103 * 33 5/20/98 2:24a Dave
104 * Fixed server side voice muting. Tweaked multi debrief/endgame
105 * sequencing a bit. Much friendlier for stats tossing/accepting now.
107 * 32 4/25/98 2:02p Dave
108 * Put in multiplayer context help screens. Reworked ingame join ship
109 * select screen. Fixed places where network timestamps get hosed.
111 * 31 4/21/98 4:44p Dave
112 * Implement Vasudan ships in multiplayer. Added a debug function to bash
113 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
114 * problem in options screen.
116 * 30 4/17/98 5:27p Dave
117 * More work on the multi options screen. Fixed many minor ui todo bugs.
119 * 29 4/09/98 11:01p Dave
120 * Put in new multi host options screen. Tweaked multiplayer options a
123 * 28 4/08/98 10:11a Dave
124 * Fixed a voice packet indexing problem.
126 * 27 4/07/98 5:42p Dave
127 * Put in support for ui display of voice system status (recording,
128 * playing back, etc). Make sure main hall music is stopped before
129 * entering a multiplayer game via ingame join.
131 * 26 4/04/98 4:22p Dave
132 * First rev of UDP reliable sockets is done. Seems to work well if not
135 * 25 4/03/98 4:22p Allender
136 * Fixed a bug where voice recording was not cut off after MAX_TIME.
138 * 24 4/03/98 1:03a Dave
139 * First pass at unreliable guaranteed delivery packets.
141 * 23 3/30/98 6:27p Dave
142 * Put in a more official set of multiplayer options, including a system
143 * for distributing netplayer and settings.
145 * 22 3/27/98 9:46a Dave
146 * Made default quality of sound 10.
148 * 21 3/26/98 6:01p Dave
149 * Put in file checksumming routine in cfile. Made pilot pic xferring more
150 * robust. Cut header size of voice data packets in half. Put in
151 * restricted game host query system.
153 * 20 3/25/98 8:36p Dave
154 * Removed potential bug spots from voice code.
156 * 19 3/25/98 2:16p Dave
157 * Select random default image for newly created pilots. Fixed several
158 * multi-pause messaging bugs. Begin work on online help for multiplayer
161 * 18 3/24/98 5:00p Dave
162 * Fixed several ui bugs. Put in pre and post voice stream playback sound
163 * fx. Put in error specific popups for clients getting dropped from games
164 * through actions other than their own.
166 * 17 3/23/98 7:18p Dave
167 * Converted voice to use streaming of packets as they are recorded.
169 * 16 3/22/98 7:13p Lawrance
170 * Get streaming of recording voice working
172 * 15 3/20/98 1:01p Dave
173 * Made the token system for multi_voice more reliable. Fixed a bug where
174 * standalones and other server who can't play sound don't route
175 * multi_voice correctly.
177 * 14 3/19/98 5:05p Dave
178 * Put in support for targeted multiplayer text and voice messaging (all,
179 * friendly, hostile, individual).
181 * 13 3/18/98 5:52p Dave
182 * Put in netgame password popup. Numerous ui changes. Laid groundwork for
183 * streamed multi_voice data.
185 * 12 3/18/98 3:32p Dave
186 * Put in a hook for streamed rtvoice data from the rtvoice system.
188 * 11 3/18/98 9:43a Dave
189 * Removed an uninitialized data warning.
191 * 10 3/17/98 12:30a Dave
192 * Put in hud support for rtvoice. Several ui interface changes.
194 * 9 3/16/98 2:35p Dave
195 * Numerous bug fixes. Made the "cue sound" sound play before incoming
198 * 8 3/15/98 4:17p Dave
199 * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size
200 * of network orientation matrices.
202 * 7 3/04/98 4:36p Dave
203 * Fixed a compiler warning.
205 * 6 2/26/98 6:01p Dave
206 * Made voice playback work correctly under NT.
208 * 5 2/26/98 5:01p Dave
209 * Fixed a multi_voice_reset() bug.
211 * 4 2/26/98 4:21p Dave
212 * More robust multiplayer voice.
214 * 3 2/25/98 8:58p Dave
215 * Got first full implementation of multi voice done.
217 * 2 2/24/98 11:56p Lawrance
218 * Change real-time voice code to provide the uncompressed size on decode.
220 * 1 2/24/98 10:12p Dave
221 * Initial pass at multiplayer voice streaming.
228 #include "freespace.h"
229 #include "gamesequence.h"
230 #include "multimsgs.h"
231 #include "multiutil.h"
232 #include "multi_voice.h"
233 #include "multi_pmsg.h"
237 #include "optionsmenumulti.h"
238 #include "stand_gui.h"
242 // --------------------------------------------------------------------------------------------------
243 // MULTI VOICE DEFINES/VARS
246 // #define MULTI_VOICE_POST_DECOMPRESS // when we're _not_ using streaming
247 #define MULTI_VOICE_PRE_DECOMPRESS // when we _are_ using streaming
249 #define MULTI_VOICE_VERBOSE // keep this defined for verbose debug output
251 #define MULTI_VOICE_LOCAL_ECHO // keep this defined for local echo of recorded network voice
253 // flag indicating the status of the multi voice system
254 int Multi_voice_inited = 0;
255 int Multi_voice_can_record = 0;
256 int Multi_voice_can_play = 0;
257 int Multi_voice_send_mode = MULTI_MSG_NONE; // gotten from the multi_msg system when we start recording
259 // packet code defines
260 #define MV_CODE_GIVE_TOKEN 0 // received player side - he now has the token to speak
261 #define MV_CODE_DENY_TOKEN 1 // received player side - server has denied this request
262 #define MV_CODE_TAKE_TOKEN 2 // received player side - the server is forcibly taking his token
263 #define MV_CODE_RELEASE_TOKEN 3 // received server side - player is relinquishing token
264 #define MV_CODE_REQUEST_TOKEN 4 // received server side - player is requesting token
265 #define MV_CODE_PLAYER_PREFS 5 // received server side - player bitflags for who he'll receive from
266 #define MV_CODE_DATA 6 // sound data
267 #define MV_CODE_DATA_DUMMY 7 // in place of a packet which has been deemed too large, so that receivers don't time out early
269 // default quality of sound
270 #define MV_DEFAULT_QOS 10 // default quality of sound
271 int Multi_voice_qos; // default quality of sound
273 // sounds added to the front and end of a playing voice stream (set to -1 if none are wanted)
274 #define MULTI_VOICE_PRE_SOUND SND_CUE_VOICE
275 #define MULTI_VOICE_POST_SOUND SND_END_VOICE
276 int Multi_voice_pre_sound_size = 0;
280 // NOTE : the following 2 defines should be used for reference only. they represent the worst case situation,
281 // sending voice to a specific target under IPX. you should use multi_voice_max_chunk_size(...) when
282 // determining if a given chunk will fit into an individual freespace packet
283 // max size of a data packet header (note, this changes as the code itself changes - should probably never use this except for reference)
284 #define MULTI_VOICE_MAX_HEADER_SIZE 22
285 // size of an individual chunk (CHUNK == block of data stuck into a packet), in the worst case of header size (see above)
286 #define MULTI_VOICE_MAX_CHUNK_SIZE 488
288 // total max size of an incoming or an outgoing uncompressed buffer (note this is probably too big, but we won't worry about that for now)
289 #define MULTI_VOICE_MAX_BUFFER_SIZE ((1<<16)+(1<<14)) // 80k
291 // overall size of an total accum buffer for a stream
292 #define MULTI_VOICE_ACCUM_BUFFER_SIZE (1<<14) // 16k
294 // how many accum buffers need to be in a total accum buffer
295 // NOTE : we reference MULTI_VOICE_MAX_CHUNK_SIZE here because it is worst case. ie, we'll always have enough
296 // accum buffers in anything better than the worst case if we use MULTI_VOICE_MAX_CHUNK_SIZE
297 #define MULTI_VOICE_ACCUM_BUFFER_COUNT (MULTI_VOICE_ACCUM_BUFFER_SIZE / MULTI_VOICE_MAX_CHUNK_SIZE)
299 int Multi_voice_max_time; // current maximum recording time
300 char *Multi_voice_record_buffer = NULL; // buffer for recording back voice
301 char *Multi_voice_playback_buffer = NULL; // buffer for processing the accum buffer and playing the result
304 #ifdef MULTI_VOICE_POST_DECOMPRESS
305 char Multi_voice_unpack_buffer[MULTI_VOICE_MAX_BUFFER_SIZE];
308 // the max amount of tokens we want to be floating about (max sound streams)
309 #define MULTI_VOICE_MAX_STREAMS 1
311 // voice algorithm stuff
312 // it would probably be good to base the timeout time on some multiple of our average ping to the server
313 #define MV_ALG_TIMEOUT 500 // if start get new data for a window then a pause this long, play the window
314 int Multi_voice_stamps[MULTI_VOICE_MAX_STREAMS];
316 // NOTE : this should be > then MULTI_VOICE_MAX_TIME + the time for the data to come over a network connection!!
317 #define MULTI_VOICE_TOKEN_TIMEOUT 7000 // timeout - server will take the token back if he does not hear from the guy in this amount of time
319 #define MULTI_VOICE_TOKEN_RELEASE_WAIT (1.0f) // wait 1 second
321 // the token index of a voice stream is set to one of these values, or the index of the player who has the token
322 #define MULTI_VOICE_TOKEN_INDEX_FREE -1 // the token (and the stream are free)
323 #define MULTI_VOICE_TOKEN_INDEX_RELEASED 0xDEADBEAD // the token has been released but the stream is still active
325 typedef struct voice_stream {
326 int token_status; // status of the token (player index if a player has it) or one of the above defines
327 int token_stamp; // timestamp for the MULTI_VOICE_TOKEN_TIMEOUT
329 short stream_from; // id of the player the stream is coming from
331 ubyte *accum_buffer[MULTI_VOICE_ACCUM_BUFFER_COUNT]; // accum buffer
332 ubyte accum_buffer_flags[MULTI_VOICE_ACCUM_BUFFER_COUNT]; // flag indicating the existence of a given accum (sub)buffer
333 ushort accum_buffer_usize[MULTI_VOICE_ACCUM_BUFFER_COUNT]; // uncompressed size of the corresponding (sub)buffer
334 ushort accum_buffer_csize[MULTI_VOICE_ACCUM_BUFFER_COUNT]; // compressed size of the corresponding (sub)buffer
335 double accum_buffer_gain[MULTI_VOICE_ACCUM_BUFFER_COUNT]; // gain of the corresponding (sub)buffer
337 ubyte stream_id; // stream id #
338 fix stream_last_heard; // last time we heard from this stream
340 fix stream_start_time; // time the stream started playing
341 int stream_snd_handle; // sound playing instance handle
342 int stream_rtvoice_handle; // rtvoice buffer handle
344 voice_stream Multi_voice_stream[MULTI_VOICE_MAX_STREAMS]; // voice streams themselves
347 #define MULTI_VOICE_KEY SDLK_BACKQUOTE // key used for realtime voice
348 int Multi_voice_keydown = 0; // is the record key currently being pressed
349 int Multi_voice_recording = 0; // flag indicating if we're currently recording or not
350 int Multi_voice_token = 0; // if we currently have a token or not
351 int Multi_voice_recording_stamp = -1; // how long we've been recording
352 ubyte Multi_voice_stream_id = 0; // stream id for the stream we're currently sending
353 int Multi_voice_current_stream_index = 0; // packet index of the currently recodring stream
354 int Multi_voice_current_stream_sent = -1; // index of packet we've sent up to
357 ubyte Multi_voice_next_stream_id = 0; // kept on the server - given to the next valid token requester
358 int Multi_voice_player_prefs[MAX_PLAYERS]; // player bitflag preferences
360 // voice status data - used for determing the result of multi_voice_status
361 #define MULTI_VOICE_DENIED_TIME 1000 // how long to display the "denied" status
362 int Multi_voice_denied_stamp = -1; // timestamp for when we got denied a token
364 // local muting preferences
365 int Multi_voice_local_prefs = 0xffffffff;
368 // --------------------------------------------------------------------------------------------------
369 // MULTI VOICE FORWARD DECLARATIONS
372 // process voice details as the server
373 void multi_voice_server_process();
375 // process voice details as a player (may also be the server)
376 void multi_voice_player_process();
378 // determine if the voice key is down this frame
379 int multi_voice_keydown();
381 // find the voice stream index by token player index
382 int multi_voice_find_token(int player_index);
384 // <server> gives the token to a given player
385 void multi_voice_give_token(int stream_index,int player_index);
387 // <server> takes the token from a given stream entry
388 void multi_voice_take_token(int stream_index);
390 // <server> tells the client he's been denied on this request
391 void multi_voice_deny_token(int player_index);
393 // <player> releases the token back to the server
394 void multi_voice_release_token();
396 // <player> requests the token from the server
397 void multi_voice_request_token();
399 // <server> process a request for the token
400 void multi_voice_process_token_request(int player_index);
402 // free up any memory which may have been malloced
403 void multi_voice_free_all();
405 // <player> send the currently recorded sound
406 void multi_voice_player_send_stream();
408 // process incoming sound data, return bytes processed
409 int multi_voice_process_data(ubyte *data, int player_index,int msg_mode,net_player *target);
411 // <server> increment the current stream id#
412 void multi_voice_inc_stream_id();
414 // flush any old sound stream data because we've started to receive data for a new stream
415 void multi_voice_flush_old_stream(int stream_index);
417 // route sound data through the server to all appropriate players
418 void multi_voice_route_data(ubyte *data, int packet_size,int player_index,int mode,net_player *target);
420 // find the stream to apply incoming sound data to, freeing up old ones as necessary
421 int multi_voice_get_stream(int stream_id);
423 // NOTE : these 4 functions can be arbitrarily written to perform in any way necessary. This way the algorithm is
424 // completely seperate from the transport and token layers
425 // initialize the smart algorithm
426 void multi_voice_alg_init();
428 // process incoming sound data in whatever way necessary (this function should take care of playing data when necessary)
429 void multi_voice_alg_process_data(int player_index,int stream_index,ushort chunk_index,ushort chunk_size);
431 // process existing streams
432 void multi_voice_alg_process_streams();
434 // we are going to flush the current stream because we have started to receive data for a new one. do something first
435 void multi_voice_alg_flush_old_stream(int stream_index);
437 // is the given sound stream playing (compares uncompressed sound size with current playback position)
438 int multi_voice_stream_playing(int stream_index);
440 // tack on a post voice sound (pass -1 for none)
441 // return final buffer size
442 int multi_voice_mix(int post_sound,char *data,int cur_size,int max_size);
444 // send a dummy packet in the place of a too-large data packet
445 void multi_voice_send_dummy_packet();
447 // process a dummy data packet
448 int multi_voice_process_data_dummy(ubyte *data);
450 // max size of a sound chunk which we can fit into a packet
451 int multi_voice_max_chunk_size(int msg_mode);
453 // process a player preferences packet, return bytes processed
454 int multi_voice_process_player_prefs(ubyte *data,int player_index);
456 // process and play the current window of sound stream data we have. reset the window for the next incoming data as well
457 void multi_voice_alg_play_window(int stream_index);
459 // send all pending voice packets
460 void multi_voice_client_send_pending();
463 // --------------------------------------------------------------------------------------------------
464 // MULTI VOICE FUNCTIONS
467 // initialize the multiplayer voice system
468 void multi_voice_init()
470 int idx,s_idx,pre_size,pre_sound;
472 // if the voice system is already initialized, just reset some stuff
473 if(Multi_voice_inited){
478 // set the default quality of sound
479 Multi_voice_qos = MV_DEFAULT_QOS;
481 // if we're the standalone server, we can't record _or_ playback, but we can still route data and manage tokens
482 if(Game_mode & GM_STANDALONE_SERVER){
483 Multi_voice_can_record = 0;
484 Multi_voice_can_play = 0;
486 // initialize the realtime voice module
487 if(rtvoice_init_recording(Multi_voice_qos)){
488 nprintf(("Network","MULTI VOICE : Error initializing rtvoice - recording will not be possible\n"));
489 Multi_voice_can_record = 0;
491 Multi_voice_can_record = 1;
494 if(rtvoice_init_playback()){
495 nprintf(("Network","MULTI VOICE : Error initializing rtvoice - playback will not be possible\n"));
496 Multi_voice_can_play = 0;
498 Multi_voice_can_play = 1;
501 // _always_ set the quality of server
502 multi_voice_set_vars(MV_DEFAULT_QOS,MULTI_VOICE_MAX_TIME);
505 // initialize player-side data
506 Multi_voice_token = 0;
507 Multi_voice_keydown = 0;
508 Multi_voice_recording = 0;
509 Multi_voice_stream_id = 0;
510 Multi_voice_recording_stamp = -1;
511 Multi_voice_current_stream_index = 0;
512 Multi_voice_current_stream_sent = -1;
514 // initialize server-side data
515 memset(Multi_voice_player_prefs,0xff,sizeof(int)*MAX_PLAYERS);
516 Multi_voice_next_stream_id = 0;
518 Multi_voice_local_prefs = 0xffffffff;
520 // initialize the sound buffers
521 Multi_voice_record_buffer = NULL;
523 Multi_voice_playback_buffer = NULL;
524 Multi_voice_pre_sound_size = 0;
525 if(Multi_voice_can_play){
526 // attempt to allocate the buffer
527 Multi_voice_playback_buffer = (char*)malloc(MULTI_VOICE_MAX_BUFFER_SIZE);
528 if(Multi_voice_playback_buffer == NULL){
529 nprintf(("Network","MULTI VOICE : Error allocating playback buffer - playback will not be possible\n"));
530 Multi_voice_can_play = 0;
533 // attempt to copy in the "pre" voice sound
534 pre_sound = snd_load(&Snds[MULTI_VOICE_PRE_SOUND]);
535 Multi_voice_pre_sound_size = 0;
537 // get the pre-sound size
538 if((snd_size(pre_sound,&pre_size) != -1) && (pre_size < MULTI_VOICE_MAX_BUFFER_SIZE)){
539 if ( !snd_get_data(pre_sound,Multi_voice_playback_buffer) ) {
540 Multi_voice_pre_sound_size = pre_size;
546 // initialize the streams
547 memset(Multi_voice_stream,0,sizeof(voice_stream) * MULTI_VOICE_MAX_STREAMS);
548 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
549 Multi_voice_stream[idx].token_status = MULTI_VOICE_TOKEN_INDEX_FREE;
550 Multi_voice_stream[idx].token_stamp = -1;
551 Multi_voice_stream[idx].stream_snd_handle = -1;
553 // get a playback buffer handle
554 if(Multi_voice_can_play){
555 Multi_voice_stream[idx].stream_rtvoice_handle = -1;
556 Multi_voice_stream[idx].stream_rtvoice_handle = rtvoice_create_playback_buffer();
557 if(Multi_voice_stream[idx].stream_rtvoice_handle == -1){
558 nprintf(("Network","MULTI VOICE : Error getting rtvoice buffer handle - playback will not be possible!\n"));
559 multi_voice_free_all();
561 Multi_voice_can_play = 0;
564 // allocate the accum buffer
565 for(s_idx=0;s_idx<MULTI_VOICE_ACCUM_BUFFER_COUNT;s_idx++){
566 Multi_voice_stream[idx].accum_buffer[s_idx] = NULL;
567 Multi_voice_stream[idx].accum_buffer[s_idx] = (ubyte*)malloc(MULTI_VOICE_ACCUM_BUFFER_SIZE);
568 if(Multi_voice_stream[idx].accum_buffer[s_idx] == NULL){
569 nprintf(("Network","MULTI VOICE : Error allocating accum buffer - playback will not be possible\n"));
570 multi_voice_free_all();
572 Multi_voice_can_play = 0;
578 // initialize the default max time
579 Multi_voice_max_time = MULTI_VOICE_MAX_TIME;
581 // initialize voice status data
582 Multi_voice_denied_stamp = -1;
584 // initialize the smart algorithm
585 multi_voice_alg_init();
587 Multi_voice_inited = 1;
590 // shutdown the multiplayer voice system
591 void multi_voice_close()
595 // if the voice system isn't already initialized, don't do anything
596 if(!Multi_voice_inited){
601 multi_voice_free_all();
603 // release all the rtvoice buffers
604 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
605 if(Multi_voice_stream[idx].stream_rtvoice_handle != -1){
606 rtvoice_free_playback_buffer(Multi_voice_stream[idx].stream_rtvoice_handle);
607 Multi_voice_stream[idx].stream_rtvoice_handle = -1;
608 Multi_voice_stream[idx].stream_snd_handle = -1;
612 // close the realtime voice module
613 rtvoice_close_recording();
614 rtvoice_close_playback();
616 Multi_voice_inited = 0;
619 // reset between levels
620 void multi_voice_reset()
624 #ifdef MULTI_VOICE_VERBOSE
625 nprintf(("Network","MULTI VOICE : Resetting\n"));
628 SDL_assert(Multi_voice_inited);
630 // if we're the standalone server, we can't record _or_ playback, but we can still route data and manage tokens
631 if(Game_mode & GM_STANDALONE_SERVER){
632 Multi_voice_can_record = 0;
633 Multi_voice_can_play = 0;
636 // initialize player-side data
637 Multi_voice_token = 0;
638 Multi_voice_keydown = 0;
639 Multi_voice_recording = 0;
640 Multi_voice_stream_id = 0;
641 Multi_voice_recording_stamp = -1;
643 // initialize server-side data
644 memset(Multi_voice_player_prefs,0xff,sizeof(int)*MAX_PLAYERS);
645 Multi_voice_local_prefs = 0xffffffff;
646 Multi_voice_next_stream_id = 0;
648 // initialize the sound buffers
649 Multi_voice_record_buffer = NULL;
651 // initialize the streams
652 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
653 Multi_voice_stream[idx].token_status = MULTI_VOICE_TOKEN_INDEX_FREE;
654 Multi_voice_stream[idx].token_stamp = -1;
657 // initialize the smart algorithm
658 multi_voice_alg_init();
661 // process all voice details
662 void multi_voice_process()
666 // don't do anything if the voice module is not initialized
667 if((!Multi_voice_inited) || !(Net_player->flags & NETINFO_FLAG_CONNECTED)){
671 // send all pending voice packets
672 multi_voice_client_send_pending();
674 // find any playing sound streams which have finished and unmark them
675 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
676 if((Multi_voice_stream[idx].stream_snd_handle != -1) && !multi_voice_stream_playing(idx)){
677 Multi_voice_stream[idx].stream_snd_handle = -1;
681 // process seperately as player or server
682 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
683 multi_voice_server_process();
686 // all "players" do this, except the standalone who isn't a real player by definition
687 if(!(Game_mode & GM_STANDALONE_SERVER)){
688 multi_voice_player_process();
691 // everyont calls the general algorithm process function
692 multi_voice_alg_process_streams();
695 // voice settings debug console function
696 void multi_voice_dcf()
698 dc_get_arg(ARG_STRING);
700 // set the quality of sound
701 if (strcmp(Dc_arg, NOX("qos")) == 0) {
703 if(Dc_arg_type & ARG_INT){
704 if((Dc_arg_int >= 1) && (Dc_arg_int <= 10) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
705 multi_voice_set_vars(Dc_arg_int,-1);
706 dc_printf("Quality of sound : %d\n",Dc_arg_int);
712 // the status of the voice system - use this to determine what bitmaps to display, etc see above MULTI_VOICE_STATUS_* defines
713 int multi_voice_status()
719 // if the "denied" timestamp is set, return that as the status
720 if(Multi_voice_denied_stamp != -1){
721 return MULTI_VOICE_STATUS_DENIED;
724 // if we're currently recording (has precedence over playing back a sound from somebody)
725 if(Multi_voice_recording){
726 return MULTI_VOICE_STATUS_RECORDING;
729 // find the stream which started playing the farthest back (if any)
732 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
733 // if we found a playing stream
734 if(Multi_voice_stream[idx].stream_snd_handle != -1){
735 if((earliest == -1) || (Multi_voice_stream[idx].stream_start_time < earliest_time)){
737 earliest_time = Multi_voice_stream[idx].stream_start_time;
741 // if we found a stream
743 return MULTI_VOICE_STATUS_PLAYING;
747 return MULTI_VOICE_STATUS_IDLE;
750 // update the qos if the current setting is different from the passed in value
751 void multi_voice_maybe_update_vars(int new_qos,int new_duration)
753 // if the current qos is different from the passed qos, set it
754 if((new_qos != Multi_voice_qos) || (new_duration != Multi_voice_max_time)){
755 multi_voice_set_vars(new_qos,new_duration);
760 // --------------------------------------------------------------------------------------------------
761 // MULTI VOICE FORWARD DECLARATIONS
764 // process voice details as the server
765 void multi_voice_server_process()
769 // process all the tokens for all the available streams
770 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
771 switch(Multi_voice_stream[idx].token_status){
772 // if the token is free, so is the stream - don't do anything
773 case MULTI_VOICE_TOKEN_INDEX_FREE:
776 // if the token has been released - check to see if the stream is "done" (ie, can be marked as FREE once again)
777 case MULTI_VOICE_TOKEN_INDEX_RELEASED:
778 // if the stream_last_heard var is -1, it means we never got sound from this guy so free the token up immediately
779 if(Multi_voice_stream[idx].stream_last_heard == -1){
780 Multi_voice_stream[idx].token_status = MULTI_VOICE_TOKEN_INDEX_FREE;
782 #ifdef MULTI_VOICE_VERBOSE
783 nprintf(("Network","MULTI VOICE : freeing released token (no packets)\n"));
786 // if a sufficiently long amount of time has elapsed since he released the token, free it up
789 t1 = f2fl(Multi_voice_stream[idx].stream_last_heard);
790 t2 = f2fl(timer_get_fixed_seconds());
791 if((t2 - t1) >= MULTI_VOICE_TOKEN_RELEASE_WAIT){
792 Multi_voice_stream[idx].token_status = MULTI_VOICE_TOKEN_INDEX_FREE;
794 #ifdef MULTI_VOICE_VERBOSE
795 nprintf(("Network","MULTI VOICE : freeing released token (time elapsed)\n"));
801 // if the token is still being held by a player
803 // if the token timestamp has elapsed, take the token back
804 if((Multi_voice_stream[idx].token_stamp != -1) && timestamp_elapsed(Multi_voice_stream[idx].token_stamp)){
805 SDL_assert(Multi_voice_stream[idx].token_status != MULTI_VOICE_TOKEN_INDEX_FREE);
806 multi_voice_take_token(idx);
812 // for each netplayer, if his token wait timestamp is running, see if it has popped yet
813 for(idx=0;idx<MAX_PLAYERS;idx++){
814 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.voice_token_timestamp != -1) && timestamp_elapsed(Net_players[idx].s_info.voice_token_timestamp)){
815 // unset it so that he can have the token again
816 Net_players[idx].s_info.voice_token_timestamp = -1;
821 // process voice details as a player (may also be the server)
822 void multi_voice_player_process()
824 // if the voice key is down for the first time this frame, send a request for the token
825 if(!Multi_voice_keydown && multi_voice_keydown() && Multi_voice_can_record && !(Netgame.options.flags & MSO_FLAG_NO_VOICE)){
826 // mark the key as being down
827 Multi_voice_keydown = 1;
829 // send a request for a token
830 multi_voice_request_token();
832 #ifdef MULTI_VOICE_VERBOSE
833 nprintf(("Network","MULTI VOICE : Request\n"));
837 // if the key is still being pressed
838 if(Multi_voice_keydown && multi_voice_keydown() && Multi_voice_can_record){
839 // if we have the token
840 if(Multi_voice_token){
841 // if we're not already recording, start recording
842 if(!Multi_voice_recording){
843 #ifdef MULTI_VOICE_VERBOSE
844 nprintf(("Network","MULTI VOICE : RECORD %d\n",(int)Multi_voice_stream_id));
846 // flush the old stream
847 multi_voice_flush_old_stream(0);
849 // start the recording process with the appropriate callback function
850 if(rtvoice_start_recording(multi_voice_process_next_chunk)){
851 nprintf(("Network","MULTI VOICE : Error initializing recording!\n"));
855 // set myself to be recording
856 Multi_voice_recording = 1;
858 // set the time when I started recording
859 Multi_voice_recording_stamp = timestamp(Multi_voice_max_time);
861 // set the current packet/chunk index to 0
862 Multi_voice_current_stream_index = 0;
863 Multi_voice_current_stream_sent = 0;
865 // get the proper messaging mode
866 if(Game_mode & GM_IN_MISSION){
867 // in mission, paused
868 if(gameseq_get_state() == GS_STATE_MULTI_PAUSED){
869 Multi_voice_send_mode = MULTI_MSG_ALL;
871 // in mission, unpaused
873 Multi_voice_send_mode = multi_msg_mode();
876 Multi_voice_send_mode = MULTI_MSG_ALL;
880 // if we've recorded the max time allowed, send the data
881 if((Multi_voice_recording_stamp != -1) && timestamp_elapsed(Multi_voice_recording_stamp)){
882 #ifdef MULTI_VOICE_VERBOSE
883 nprintf(("Network","MULTI VOICE : timestamp popped"));
885 // mark me as no longer recording
886 Multi_voice_recording = 0;
887 Multi_voice_current_stream_sent = -1;
889 // stop the recording process
890 rtvoice_stop_recording();
892 #ifdef MULTI_VOICE_POST_DECOMPRESS
893 multi_voice_player_send_stream();
896 // play my sound locally as well
897 #ifdef MULTI_VOICE_LOCAL_ECHO
898 multi_voice_alg_play_window(0);
900 // release the token back to the server
901 multi_voice_release_token();
905 // if the key has been released
906 else if(Multi_voice_keydown && !multi_voice_keydown() && Multi_voice_can_record){
907 #ifdef MULTI_VOICE_VERBOSE
908 nprintf(("Network","MULTI VOICE : Release\n"));
911 // mark the kay as not being down
912 Multi_voice_keydown = 0;
914 // if we were recording, send the data
915 if(Multi_voice_recording){
916 // mark me as no longer recording
917 Multi_voice_recording = 0;
919 Multi_voice_current_stream_sent = -1;
921 // stop the recording process
922 rtvoice_stop_recording();
924 #ifdef MULTI_VOICE_POST_DECOMPRESS
925 multi_voice_player_send_stream();
928 // play my sound locally as well
929 #ifdef MULTI_VOICE_LOCAL_ECHO
930 multi_voice_alg_play_window(0);
933 // release the token back to the server
934 multi_voice_release_token();
938 // if the "denied" timestamp is set, but has elapsed or the user has let up on the key, set it to -1
939 if((Multi_voice_denied_stamp != -1) && (timestamp_elapsed(Multi_voice_denied_stamp) || !multi_voice_keydown())){
940 Multi_voice_denied_stamp = -1;
944 // determine if the voice key is down this frame
945 int multi_voice_keydown()
947 // if we're in the options screen, we should never allow the button to be pressed
948 if(gameseq_get_state() == GS_STATE_OPTIONS_MENU){
952 // if we're pre-game, we should just be checking the keyboard bitflags
953 if(!(Game_mode & GM_IN_MISSION)){
954 return (key_pressed(MULTI_VOICE_KEY) && !(key_pressed(SDLK_LSHIFT) || key_pressed(SDLK_RSHIFT))) ? 1 : 0;
957 // in-mission, paused - treat just like any other "chattable" screen.
958 if(gameseq_get_state() == GS_STATE_MULTI_PAUSED){
959 return (key_pressed(MULTI_VOICE_KEY) && !(key_pressed(SDLK_LSHIFT) || key_pressed(SDLK_RSHIFT))) ? 1 : 0;
962 // ingame, unpaused, rely on the multi-messaging system (ingame)
963 return multi_msg_voice_record();
966 // find the voice stream index by token player index
967 int multi_voice_find_token(int player_index)
971 // look through all the existing streams
972 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
973 if(Multi_voice_stream[idx].token_status == player_index){
982 // <server> gives the token to a given player
983 void multi_voice_give_token(int stream_index,int player_index)
988 // only the server should ever be here
989 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
991 // set this player as having the token
992 Multi_voice_stream[stream_index].token_status = player_index;
994 // set the token timeout
995 Multi_voice_stream[stream_index].token_stamp = timestamp(MULTI_VOICE_TOKEN_TIMEOUT);
997 // set the stream id and increment the count
998 Multi_voice_stream[stream_index].stream_id = Multi_voice_next_stream_id;
999 multi_voice_inc_stream_id();
1001 // set the last heard from time to -1 to indicate we've heard no sound from this guy
1002 Multi_voice_stream[stream_index].stream_last_heard = -1;
1004 #ifdef MULTI_VOICE_VERBOSE
1005 nprintf(("Network","MULTI VOICE : GIVE TOKEN %d\n",(int)Multi_voice_next_stream_id));
1008 // if we're giving to ourself, don't send any data
1009 if(Net_player == &Net_players[player_index]){
1010 Multi_voice_token = 1;
1012 Multi_voice_stream_id = Multi_voice_stream[stream_index].stream_id;
1014 // send the "give" packet to the guy
1015 BUILD_HEADER(VOICE_PACKET);
1016 code = MV_CODE_GIVE_TOKEN;
1019 // add the current stream id#
1020 ADD_DATA(Multi_voice_stream[stream_index].stream_id);
1023 multi_io_send_reliable(&Net_players[player_index], data, packet_size);
1027 // <server> takes the token from a given player
1028 void multi_voice_take_token(int stream_index)
1030 ubyte data[10],code;
1031 int packet_size = 0;
1033 // only the server should ever be here
1034 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1036 // if the index is -1, the token has probably been released to us "officially" already
1037 if((Multi_voice_stream[stream_index].token_status == (int)MULTI_VOICE_TOKEN_INDEX_FREE) || (Multi_voice_stream[stream_index].token_status == (int)MULTI_VOICE_TOKEN_INDEX_RELEASED)){
1038 Multi_voice_stream[stream_index].token_stamp = -1;
1042 // if i'm taking from myself, don't send any data
1043 if(Net_player == &Net_players[Multi_voice_stream[stream_index].token_status]){
1044 Multi_voice_token = 0;
1046 // timestamp this guy so that he can't get the token back immediately
1047 Net_player->s_info.voice_token_timestamp = timestamp(Netgame.options.voice_token_wait);
1049 // send the "take" packet to the guy
1050 BUILD_HEADER(VOICE_PACKET);
1051 code = MV_CODE_TAKE_TOKEN;
1055 multi_io_send_reliable(&Net_players[Multi_voice_stream[stream_index].token_status], data, packet_size);
1057 // timestamp this guy so that he can't get the token back immediately
1058 Net_players[Multi_voice_stream[stream_index].token_status].s_info.voice_token_timestamp = timestamp(Netgame.options.voice_token_wait);
1061 // take the token back from the dude
1062 Multi_voice_stream[stream_index].token_status = MULTI_VOICE_TOKEN_INDEX_RELEASED;
1063 Multi_voice_stream[stream_index].token_stamp = -1;
1066 // <server> tells the client he's been denied on this request
1067 void multi_voice_deny_token(int player_index)
1069 ubyte data[10],code;
1070 int packet_size = 0;
1072 // only the server should ever be here
1073 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1076 // if i'm denying myself, set the denied timestamp
1077 if(Net_player == &Net_players[player_index]){
1078 Multi_voice_denied_stamp = timestamp(MULTI_VOICE_DENIED_TIME);
1080 // send the "deny" packet to the guy
1081 BUILD_HEADER(VOICE_PACKET);
1082 code = MV_CODE_DENY_TOKEN;
1086 multi_io_send_reliable(&Net_players[player_index], data, packet_size);
1090 // <player> releases the token back to the server
1091 void multi_voice_release_token()
1093 ubyte data[10],code;
1094 int packet_size = 0;
1096 // I don't have the token anymore
1097 Multi_voice_token = 0;
1099 // if i'm the server, don't send any data
1100 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1101 // mark the token as being released
1102 int stream_index = multi_voice_find_token(MY_NET_PLAYER_NUM);
1103 Multi_voice_stream[stream_index].token_status = MULTI_VOICE_TOKEN_INDEX_RELEASED;
1105 // timestamp this guy so that he can't get the token back immediately
1106 Net_player->s_info.voice_token_timestamp = timestamp(Netgame.options.voice_token_wait);
1108 // send the "release" packet to the server
1109 BUILD_HEADER(VOICE_PACKET);
1110 code = MV_CODE_RELEASE_TOKEN;
1114 multi_io_send_reliable(Net_player, data, packet_size);
1118 // <player> requests the token from the server
1119 void multi_voice_request_token()
1121 ubyte data[10],code;
1122 int packet_size = 0;
1124 // if i'm the server, process the request right now
1125 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1126 multi_voice_process_token_request(MY_NET_PLAYER_NUM);
1128 // send the "request" packet to the server
1129 BUILD_HEADER(VOICE_PACKET);
1130 code = MV_CODE_REQUEST_TOKEN;
1134 multi_io_send_reliable(Net_player, data, packet_size);
1138 // <player> sends hit bitflag settings (who he'll receive sound from, etc)
1139 void multi_voice_set_prefs(int pref_flags)
1141 ubyte data[MAX_PACKET_SIZE],code;
1143 int packet_size = 0;
1145 // set the local flags
1146 Multi_voice_local_prefs = pref_flags;
1148 // if i'm the server, set the sound prefs right now
1149 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1150 Multi_voice_player_prefs[MY_NET_PLAYER_NUM] = pref_flags;
1152 // send the prefs to the server
1153 BUILD_HEADER(VOICE_PACKET);
1154 code = MV_CODE_PLAYER_PREFS;
1157 // add the address of all players being ignored
1158 for(idx=0;idx<MAX_PLAYERS;idx++){
1159 if(!(pref_flags & (1<<idx))){
1163 // add the player's id
1164 ADD_SHORT(Net_players[idx].player_id);
1167 // add final stop byte
1172 multi_io_send_reliable(Net_player, data, packet_size);
1176 // set the default voice quality and duration (if server passes -1, he just broadcasts the qos to all clients)
1177 void multi_voice_set_vars(int qos,int duration)
1179 int need_update = 0;
1181 // make sure its in the right range
1182 if((qos > 0) && (qos <= 10)){
1183 #ifdef MULTI_VOICE_VERBOSE
1184 nprintf(("Network","MULTI VOICE : SETTING QOS %d\n",qos));
1187 // set the default value
1188 Multi_voice_qos = qos;
1190 // set the value in the rtvoice module
1191 rtvoice_set_qos(Multi_voice_qos);
1193 // update the netgame settings
1194 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1195 Netgame.options.voice_qos = (ubyte)Multi_voice_qos;
1200 // set the maximum duration
1201 if((duration > 0) && (duration <= MULTI_VOICE_MAX_TIME)){
1202 #ifdef MULTI_VOICE_VERBOSE
1203 nprintf(("Network","MULTI VOICE : SETTING MAX RECORD TIME %d\n",duration));
1205 // set the default value
1206 Multi_voice_max_time = duration;
1208 // update the netgame settings
1209 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1210 Netgame.options.voice_record_time = duration;
1215 // send an options update if necessary
1216 if(need_update && !(Game_mode & GM_STANDALONE_SERVER)){
1217 multi_options_update_netgame();
1221 // <server> process a request for the token
1222 void multi_voice_process_token_request(int player_index)
1226 // if we're not doing voice on this server, return now
1227 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
1231 // if the player's token timestamp is not -1, can't give him the token
1232 if(Net_players[player_index].s_info.voice_token_timestamp != -1){
1233 #ifdef MULTI_VOICE_VERBOSE
1234 nprintf(("Network","MULTI VOICE : Not giving token because player %s's timestamp hasn't elapsed yet!\n",Net_players[player_index].player->callsign));
1235 nprintf(("Network","MULTI VOICE : token status %d\n",Multi_voice_stream[0].token_status));
1238 multi_voice_deny_token(player_index);
1242 // attempt to find a free token token
1243 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
1244 if(Multi_voice_stream[idx].token_status == MULTI_VOICE_TOKEN_INDEX_FREE){
1245 multi_voice_give_token(idx,player_index);
1251 // free up any memory which may have been malloced
1252 void multi_voice_free_all()
1256 // free up the playback buffer
1257 if(Multi_voice_playback_buffer != NULL){
1258 free(Multi_voice_playback_buffer);
1259 Multi_voice_playback_buffer = NULL;
1262 // free up the accum buffers
1263 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
1264 for(s_idx=0;s_idx<MULTI_VOICE_ACCUM_BUFFER_COUNT;s_idx++){
1265 if(Multi_voice_stream[idx].accum_buffer[s_idx] != NULL){
1266 free(Multi_voice_stream[idx].accum_buffer[s_idx]);
1267 Multi_voice_stream[idx].accum_buffer[s_idx] = NULL;
1273 // <player> send the currently recorded sound
1274 void multi_voice_player_send_stream()
1276 ubyte data[MAX_PACKET_SIZE],code,*rbuf,msg_mode,chunk_index;
1277 ushort chunk_size,uc_size;
1278 int packet_size = 0;
1279 int sound_size,size_sent,uncompressed_size,target_index,max_chunk_size;
1283 // we'd better not ever get here as we can't record voice
1284 SDL_assert(Multi_voice_can_record);
1287 rtvoice_get_data((unsigned char**)&Multi_voice_record_buffer,&sound_size,&uncompressed_size,&d_gain);
1288 gain = (float)d_gain;
1290 msg_mode = (ubyte)Multi_voice_send_mode;
1291 // get the specific target if we're in MSG_TARGET mode
1293 if(msg_mode == MULTI_MSG_TARGET){
1294 if(Player_ai->target_objnum != -1){
1295 target_index = multi_find_player_by_object(&Objects[Player_ai->target_objnum]);
1296 if(target_index == -1){
1304 // get the max chunk size
1305 max_chunk_size = multi_voice_max_chunk_size(Multi_voice_send_mode);
1307 // go through the data and send all of it
1308 code = MV_CODE_DATA;
1311 rbuf = (unsigned char*)Multi_voice_record_buffer;
1312 while(size_sent < sound_size){
1313 // build the header and add the opcode
1314 BUILD_HEADER(VOICE_PACKET);
1316 // add the packet code type
1319 // add the routing data and any necessary targeting information
1321 if(msg_mode == MULTI_MSG_TARGET){
1322 ADD_USHORT(Objects[Net_players[target_index].player->objnum].net_signature);
1326 ADD_SHORT(Net_player->player_id);
1328 // add the current stream id#
1329 ADD_DATA(Multi_voice_stream_id);
1331 SDL_assert(uncompressed_size < MULTI_VOICE_MAX_BUFFER_SIZE);
1332 uc_size = (ushort)uncompressed_size;
1333 ADD_USHORT(uc_size);
1335 // add the chunk index
1336 ADD_DATA(chunk_index);
1338 // determine how much we are going to send in this packet
1339 if((sound_size - size_sent) >= max_chunk_size){
1340 chunk_size = (ushort)max_chunk_size;
1342 chunk_size = (ushort)(sound_size - size_sent);
1344 ADD_USHORT(chunk_size);
1349 // add the chunk of data
1350 memcpy(data+packet_size, rbuf,chunk_size);
1351 packet_size += chunk_size;
1353 // send to the server or rebroadcast if I _am_ the server
1354 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1355 multi_voice_route_data(data,packet_size,MY_NET_PLAYER_NUM,(int)msg_mode,(target_index == -1) ? NULL : &Net_players[target_index]);
1357 multi_io_send(Net_player, data, packet_size);
1360 // increment the chunk_index
1363 // increment bytes sent and the buffer
1364 size_sent += (int)chunk_size;
1369 // process incoming sound data, return bytes processed
1370 int multi_voice_process_data(ubyte *data, int player_index,int msg_mode,net_player *target)
1372 ubyte stream_id,chunk_index;
1373 ushort chunk_size,uc_size;
1379 // read in all packet data except for the sound chunk itself
1380 GET_SHORT(who_from);
1381 GET_DATA(stream_id);
1382 GET_USHORT(uc_size);
1383 GET_DATA(chunk_index);
1384 GET_USHORT(chunk_size);
1387 // if our netgame options are currently set for no voice, ignore the packet
1388 if((Netgame.options.flags & MSO_FLAG_NO_VOICE) || !Multi_options_g.std_voice){
1389 offset += chunk_size;
1393 // get a handle to a valid stream to be using, freeing old streams as necessary
1394 stream_index = multi_voice_get_stream((int)stream_id);
1396 // if this index is too high, flush the stream
1397 if(chunk_index >= MULTI_VOICE_ACCUM_BUFFER_COUNT){
1398 #ifdef MULTI_VOICE_VERBOSE
1399 nprintf(("Network","MULTI VOICE : flushing stream because packet index is too high!!\n"));
1403 multi_voice_flush_old_stream(stream_index);
1405 // return bytes processed
1406 offset += chunk_size;
1410 // if we found a stream to work with
1411 if(stream_index != -1){
1412 // set the id of where it came from
1413 Multi_voice_stream[stream_index].stream_from = who_from;
1415 // set the stream id#
1416 Multi_voice_stream[stream_index].stream_id = stream_id;
1419 Multi_voice_stream[stream_index].accum_buffer_gain[chunk_index] = (double)gain;
1421 // set the stream uncompressed size size
1422 Multi_voice_stream[stream_index].accum_buffer_usize[chunk_index] = uc_size;
1424 // set the token timestamp
1425 Multi_voice_stream[stream_index].token_stamp = timestamp(MULTI_VOICE_TOKEN_TIMEOUT);
1427 // set the last heard time
1428 Multi_voice_stream[stream_index].stream_last_heard = timer_get_fixed_seconds();
1430 // copy the data and setup any other accum buffer data necessary
1431 // ignore data if we can't play sounds
1432 if(Multi_voice_can_play){
1433 memcpy(Multi_voice_stream[stream_index].accum_buffer[chunk_index],data+offset,(int)chunk_size);
1436 Multi_voice_stream[stream_index].accum_buffer_flags[chunk_index] = 1;
1437 Multi_voice_stream[stream_index].accum_buffer_csize[chunk_index] = chunk_size;
1439 // pass the data into the smart voice algorithm
1440 if(player_index != -1){
1441 multi_voice_alg_process_data(player_index,stream_index,chunk_index,chunk_size);
1445 // increment the offset
1446 offset += (int)chunk_size;
1451 // <server> increment the current stream id#
1452 void multi_voice_inc_stream_id()
1454 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1456 if(Multi_voice_next_stream_id == 0xff){
1457 Multi_voice_next_stream_id = 0;
1459 Multi_voice_next_stream_id++;
1463 // flush any old sound stream data because we've started to receive data for a new stream
1464 void multi_voice_flush_old_stream(int stream_index)
1466 #ifdef MULTI_VOICE_VERBOSE
1467 nprintf(("Network","MULTI VOICE : old stream flush\n"));
1470 // call the smart algorithm for flushing streams
1471 multi_voice_alg_flush_old_stream(stream_index);
1473 // clear all the accum buffer flags
1474 memset(Multi_voice_stream[stream_index].accum_buffer_flags,0,MULTI_VOICE_ACCUM_BUFFER_COUNT);
1477 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1478 multi_voice_take_token(stream_index);
1481 Multi_voice_stream[stream_index].token_stamp = -1;
1482 Multi_voice_stream[stream_index].token_status = MULTI_VOICE_TOKEN_INDEX_FREE;
1484 // timestamp the player
1485 Net_player->s_info.voice_token_timestamp = timestamp(Netgame.options.voice_token_wait);
1488 // route sound data through the server to all appropriate players
1489 void multi_voice_route_data(ubyte *data, int packet_size,int player_index,int mode,net_player *target)
1493 // route the data to all other players
1496 for(idx=0;idx<MAX_PLAYERS;idx++){
1497 if(MULTI_CONNECTED( Net_players[idx] ) && // player is connected
1498 ( &Net_players[idx] != &Net_players[player_index] ) && // not the sending player
1499 ( Net_player != &Net_players[idx] ) && // not me
1500 ( Multi_voice_player_prefs[idx] & (1 << player_index) ) && // is accepting sound from this player
1501 !( Net_players[idx].p_info.options.flags & MLO_FLAG_NO_VOICE ) ){ // is accepting sound periods
1503 multi_io_send(&Net_players[idx], data, packet_size);
1508 case MULTI_MSG_FRIENDLY:
1509 for(idx=0;idx<MAX_PLAYERS;idx++){
1510 if(MULTI_CONNECTED( Net_players[idx] ) && // player is connected
1511 ( &Net_players[idx] != &Net_players[player_index] ) && // not the sending player
1512 ( Net_player != &Net_players[idx] ) && // not me
1513 ( Net_players[idx].p_info.team == Net_players[player_index].p_info.team ) &&// on the same team
1514 ( Multi_voice_player_prefs[idx] & (1 << player_index) ) && // is accepting sound from the sender
1515 !( Net_players[idx].p_info.options.flags & MLO_FLAG_NO_VOICE) ){ // is accepting sound periods
1517 multi_io_send(&Net_players[idx], data, packet_size);
1521 case MULTI_MSG_HOSTILE:
1522 for(idx=0;idx<MAX_PLAYERS;idx++){
1523 if(MULTI_CONNECTED( Net_players[idx] ) && // player is connected
1524 ( &Net_players[idx] != &Net_players[player_index] ) && // not the sending player
1525 ( Net_player != &Net_players[idx] ) && // not me
1526 ( Net_players[idx].p_info.team != Net_players[player_index].p_info.team ) &&// on the opposite team
1527 ( Multi_voice_player_prefs[idx] & (1 << player_index) ) && // is accepting sound from the sender
1528 !( Net_players[idx].p_info.options.flags & MLO_FLAG_NO_VOICE ) ){ // is accepting sound periods
1530 multi_io_send(&Net_players[idx], data, packet_size);
1535 case MULTI_MSG_TARGET:
1536 SDL_assert(target != NULL);
1537 if(!(target->p_info.options.flags & MLO_FLAG_NO_VOICE)){
1538 multi_io_send(target, data, packet_size);
1544 // find the stream to apply incoming sound data to, freeing up old ones as necessary
1545 int multi_voice_get_stream(int stream_id)
1547 int idx,max_diff_index;
1548 fix cur_time,max_diff;
1550 // first check to see if this stream exists
1551 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
1552 if(Multi_voice_stream[idx].stream_id == (ubyte)stream_id){
1557 // if we got to this point, we didn't find the matching stream, so we should try and find an empty stream
1558 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
1559 if(Multi_voice_stream[idx].token_stamp == -1){
1564 #ifdef MULTI_VOICE_VERBOSE
1565 nprintf(("Network","MULTI VOICE : going to blast old voice stream while looking for a free one - beware!!\n"));
1568 // if we got to this point, we should free up the oldest stream we have
1569 cur_time = timer_get_fixed_seconds();
1570 max_diff_index = -1;
1572 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
1573 if(((max_diff_index == -1) || ((cur_time - Multi_voice_stream[idx].stream_last_heard) > max_diff)) && (Multi_voice_stream[idx].token_stamp != -1)){
1574 max_diff_index = idx;
1575 max_diff = cur_time - Multi_voice_stream[idx].stream_last_heard;
1579 // if we found the oldest
1580 if(max_diff_index != -1){
1581 // flush the old stream
1582 multi_voice_flush_old_stream(max_diff_index);
1584 return max_diff_index;
1587 // some other fail condition
1591 // is the given sound stream playing (compares uncompressed sound size with current playback position)
1592 int multi_voice_stream_playing(int stream_index)
1594 // if the handle is invalid, it can't be playing
1596 if(Multi_voice_stream[stream_index].stream_snd_handle < 0){
1600 // if the sound is playing and the buffer is past the uncompressed size, its effectively done
1601 if(ds_get_play_position(ds_get_channel(Multi_voice_stream[stream_index].stream_snd_handle)) >= (DWORD)Multi_voice_stream[stream_index].stream_uc_size){
1610 // tack on pre and post sounds to a sound stream (pass -1 for either if no sound is wanted)
1611 // return final buffer size
1612 int multi_voice_mix(int post_sound,char *data,int cur_size,int max_size)
1616 // if the user passed -1 for both pre and post sounds, don't do a thing
1617 if(post_sound == -1){
1621 // get the sizes of the additional sounds
1624 if(post_sound >= 0){
1625 post_sound = snd_load(&Snds[post_sound]);
1626 if(post_sound >= 0){
1627 if(snd_size(post_sound,&post_size) == -1){
1637 // if we have a "post" sound to add
1639 if((max_size - cur_size) > post_size){
1640 // copy in the sound
1641 if ( !snd_get_data(post_sound,data + cur_size) ) {
1642 // increment the cur_size
1643 cur_size += post_size;
1648 // return the size of the new buffer
1652 // max size of a sound chunk which we can fit into a packet
1653 int multi_voice_max_chunk_size(int msg_mode)
1657 // all headers contain the following data
1658 header_size = 1 + // messaging mode
1660 2 + // packet uncompressed size
1661 2 + // compressed size
1664 // if we're targeting a specific player
1665 if(msg_mode == MULTI_MSG_TARGET){
1666 header_size += 2; // targeted player's object net_signature
1669 // if we're in IPX mode
1670 if(Psnet_my_addr.type == NET_IPX){
1671 header_size += 10; // my address (10 bytes in IPX)
1673 // if we're in TCP mode
1675 header_size += 4; // my address (4 bytes in TCP)
1678 // calculate max chunk size
1679 return (MAX_PACKET_SIZE - // max freespace packet size
1681 1 - // voice packet code subtype
1682 header_size); // calculated header size
1685 // --------------------------------------------------------------------------------------------------
1686 // MULTI VOICE / RTVOICE INTERFACE
1689 // process the "next" chunk of standalone valid sound data from the rtvoice system
1690 void multi_voice_process_next_chunk()
1692 int sound_size,uncompressed_size;
1696 // we'd better not ever get here is we can't record voice
1697 SDL_assert(Multi_voice_can_record);
1700 rtvoice_get_data((unsigned char**)&Multi_voice_record_buffer,&sound_size,&uncompressed_size,&d_gain);
1702 // if we've reached the max # of packets for this stream, bail
1703 if(Multi_voice_current_stream_index >= (MULTI_VOICE_ACCUM_BUFFER_COUNT - 1)){
1704 nprintf(("Network","MULTI VOICE : Forcing stream to stop on the record size!!!\n"));
1706 // mark me as no longer recording
1707 Multi_voice_recording = 0;
1709 Multi_voice_current_stream_sent = -1;
1711 // stop the recording process
1712 rtvoice_stop_recording();
1714 #ifdef MULTI_VOICE_POST_DECOMPRESS
1715 multi_voice_player_send_stream();
1718 // play my sound locally as well
1719 #ifdef MULTI_VOICE_LOCAL_ECHO
1720 multi_voice_alg_play_window(0);
1722 // release the token back to the server
1723 multi_voice_release_token();
1725 // unset the timestamp so we don't still think we're still recording
1726 Multi_voice_recording_stamp = -1;
1731 // pack the data locally as well (so I can hear myself)
1732 str = &Multi_voice_stream[0];
1733 memcpy(str->accum_buffer[Multi_voice_current_stream_index],Multi_voice_record_buffer,sound_size);
1734 str->stream_from = Net_player->player_id;
1735 str->accum_buffer_flags[Multi_voice_current_stream_index] = 1;
1736 str->accum_buffer_usize[Multi_voice_current_stream_index] = (ushort)uncompressed_size;
1737 str->accum_buffer_csize[Multi_voice_current_stream_index] = (ushort)sound_size;
1738 str->accum_buffer_gain[Multi_voice_current_stream_index] = d_gain;
1740 // increment the stream index
1741 Multi_voice_current_stream_index++;
1745 // --------------------------------------------------------------------------------------------------
1746 // MULTI VOICE PACKET HANDLERS
1749 // send a dummy packet in the place of a too-large data packet
1750 void multi_voice_send_dummy_packet()
1752 ubyte data[10],code,msg_mode;
1753 int packet_size,target_index;
1755 // build the header and add the opcode
1756 BUILD_HEADER(VOICE_PACKET);
1757 code = (ubyte)MV_CODE_DATA_DUMMY;
1760 msg_mode = (ubyte)Multi_voice_send_mode;
1761 // get the specific target if we're in MSG_TARGET mode
1763 if(msg_mode == MULTI_MSG_TARGET){
1764 if(Player_ai->target_objnum != -1){
1765 target_index = multi_find_player_by_object(&Objects[Player_ai->target_objnum]);
1766 if(target_index == -1){
1774 if(msg_mode == MULTI_MSG_TARGET){
1775 ADD_USHORT(Objects[Net_players[target_index].player->objnum].net_signature);
1778 // add the voice stream id
1779 ADD_DATA(Multi_voice_stream_id);
1781 // send to the server or rebroadcast if I _am_ the server
1782 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1783 multi_voice_route_data(data,packet_size,MY_NET_PLAYER_NUM,(int)msg_mode,(target_index == -1) ? NULL : &Net_players[target_index]);
1785 multi_io_send(Net_player, data, packet_size);
1789 // process a dummy data packet
1790 int multi_voice_process_data_dummy(ubyte *data)
1796 // get the stream id
1797 GET_DATA(stream_id);
1799 // get the proper stream index
1800 stream_index = multi_voice_get_stream((int)stream_id);
1802 // set the token timestamp
1803 Multi_voice_stream[stream_index].token_stamp = timestamp(MULTI_VOICE_TOKEN_TIMEOUT);
1805 // set the last heard time
1806 Multi_voice_stream[stream_index].stream_last_heard = timer_get_fixed_seconds();
1808 // set the timeout timestamp
1809 Multi_voice_stamps[stream_index] = timestamp(MV_ALG_TIMEOUT);
1811 // return bytes processed
1815 // process a player preferences packet, return bytes processed
1816 int multi_voice_process_player_prefs(ubyte *data,int player_index)
1823 // set all channels active
1824 Multi_voice_player_prefs[player_index] = 0xffffffff;
1826 // get all muted players
1831 // get the player to mute
1832 mute_index = find_player_id(mute_id);
1833 if(mute_index != -1){
1834 #ifdef MULTI_VOICE_VERBOSE
1835 nprintf(("Network","Player %s muting player %s\n",Net_players[player_index].player->callsign,Net_players[mute_index].player->callsign));
1838 Multi_voice_player_prefs[player_index] &= ~(1<<mute_index);
1841 // get the next stop value
1845 // return bytes processed
1849 // process an incoming voice packet of some kind or another
1850 void multi_voice_process_packet(ubyte *data, header *hinfo)
1852 ubyte code,msg_mode;
1854 int player_index,stream_index,target_index;
1855 int offset = HEADER_LENGTH;
1857 // find out who is sending this data
1858 player_index = find_player_id(hinfo->id);
1863 // process the packet
1865 // I don't have the token anymore
1866 case MV_CODE_TAKE_TOKEN:
1867 // we should never have the token if we cannot record
1868 if(!Multi_voice_can_record){
1872 Multi_voice_token = 0;
1875 // I have been denied the token
1876 case MV_CODE_DENY_TOKEN:
1877 // set the "denied" timestamp
1878 Multi_voice_denied_stamp = timestamp(MULTI_VOICE_DENIED_TIME);
1881 // I now have the token
1882 case MV_CODE_GIVE_TOKEN:
1883 GET_DATA(Multi_voice_stream_id);
1885 // we should never get the token if we cannot record
1886 if(!Multi_voice_can_record){
1890 // if we no longer have the keydown, automatically release the token
1891 if(!Multi_voice_keydown){
1892 multi_voice_release_token();
1894 Multi_voice_token = 1;
1898 // a request for the token from a player
1899 case MV_CODE_REQUEST_TOKEN:
1900 if(player_index >= 0){
1901 multi_voice_process_token_request(player_index);
1905 // a player gave up the token
1906 case MV_CODE_RELEASE_TOKEN:
1907 if(player_index >= 0){
1908 stream_index = multi_voice_find_token(player_index);
1913 if(stream_index >= 0){
1914 // set the token as having been released
1915 Multi_voice_stream[stream_index].token_status = MULTI_VOICE_TOKEN_INDEX_RELEASED;
1917 // timestamp this guy so that he can't get the token back immediately
1918 Net_players[player_index].s_info.voice_token_timestamp = timestamp(Netgame.options.voice_token_wait);
1922 // a player has set prefs for himself
1923 case MV_CODE_PLAYER_PREFS:
1924 SDL_assert(player_index != -1);
1925 offset += multi_voice_process_player_prefs(data+offset,player_index);
1930 #ifdef MULTI_VOICE_VERBOSE
1931 nprintf(("Network","VOICE : PROC DATA\n"));
1933 // get routing information
1936 if(msg_mode == MULTI_MSG_TARGET){
1937 GET_USHORT(target_sig);
1938 target_index = multi_find_player_by_net_signature(target_sig);
1939 SDL_assert(target_index != -1);
1942 offset += multi_voice_process_data(data+offset,player_index,msg_mode,(target_index == -1) ? NULL : &Net_players[target_index]);
1944 // if we're the server of the game, we should also route this data to all other players
1945 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1946 multi_voice_route_data(data,offset,player_index,msg_mode,(target_index == -1) ? NULL : &Net_players[target_index]);
1950 // a data dummy packet
1951 case MV_CODE_DATA_DUMMY:
1952 #ifdef MULTI_VOICE_VERBOSE
1953 nprintf(("Network","VOICE : PROC DATA DUMMY\n"));
1955 // get routing information
1958 if(msg_mode == MULTI_MSG_TARGET){
1959 GET_USHORT(target_sig);
1960 target_index = multi_find_player_by_net_signature(target_sig);
1961 SDL_assert(target_index != -1);
1964 offset += multi_voice_process_data_dummy(data+offset);
1966 // if we're the server of the game, we should also route this data to all other players
1967 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1968 multi_voice_route_data(data,offset,player_index,msg_mode,(target_index == -1) ? NULL : &Net_players[target_index]);
1975 // send all pending voice packets
1976 void multi_voice_client_send_pending()
1978 ubyte data[MAX_PACKET_SIZE],code;
1979 ubyte msg_mode,chunk_index;
1980 ushort uc_size,chunk_size;
1981 int max_chunk_size,sent,target_index;
1986 // if we're not recording
1987 if(!Multi_voice_recording || (Multi_voice_current_stream_sent < 0) || (Multi_voice_current_stream_sent > Multi_voice_current_stream_index)){
1991 // stream all buffered up packets
1992 str = &Multi_voice_stream[0];
1993 while(Multi_voice_current_stream_sent < Multi_voice_current_stream_index){
1994 sent = Multi_voice_current_stream_sent++;
1996 // get the current messaging mode
1997 msg_mode = (ubyte)Multi_voice_send_mode;
1999 // if the size of this voice chunk will fit in the packet
2000 max_chunk_size = multi_voice_max_chunk_size(Multi_voice_send_mode);
2001 if(str->accum_buffer_csize[sent] > max_chunk_size){
2002 #ifdef MULTI_VOICE_VERBOSE
2003 nprintf(("Network","MULTI VOICE : streamed packet size too large!!\n"));
2006 Multi_voice_current_stream_sent++;
2008 // send a dummy data packet instead
2009 multi_voice_send_dummy_packet();
2014 #ifdef MULTI_VOICE_VERBOSE
2015 nprintf(("Network","MULTI VOICE : PACKET %d %d\n",(int)str->accum_buffer_csize[sent],(int)str->accum_buffer_usize[sent]));
2018 // get the specific target if we're in MSG_TARGET mode
2020 if(msg_mode == MULTI_MSG_TARGET){
2021 if(Player_ai->target_objnum != -1){
2022 target_index = multi_find_player_by_object(&Objects[Player_ai->target_objnum]);
2023 if(target_index == -1){
2031 // go through the data and send all of it
2032 code = MV_CODE_DATA;
2035 // if this packet is small enough to fit within a psnet data packet
2036 BUILD_HEADER(VOICE_PACKET);
2038 // add the packet code type
2041 // add the routing data and any necessary targeting information
2043 if(msg_mode == MULTI_MSG_TARGET){
2044 SDL_assert(Game_mode & GM_IN_MISSION);
2045 ADD_USHORT(Objects[Net_players[target_index].player->objnum].net_signature);
2049 ADD_SHORT(Net_player->player_id);
2051 // add the current stream id#
2052 ADD_DATA(Multi_voice_stream_id);
2054 //SDL_assert(str->accum_buffer_usize[sent] < MULTI_VOICE_MAX_BUFFER_SIZE); // always true
2055 uc_size = (ushort)str->accum_buffer_usize[sent];
2056 ADD_USHORT(uc_size);
2058 // add the chunk index
2059 chunk_index = (ubyte)sent;
2060 ADD_DATA(chunk_index);
2062 // size of the sound data
2063 chunk_size = (ushort)str->accum_buffer_csize[sent];
2064 ADD_USHORT(chunk_size);
2067 gain = (float)str->accum_buffer_gain[sent];
2070 // add the chunk of data
2071 memcpy(data+packet_size, str->accum_buffer[sent],chunk_size);
2072 packet_size += chunk_size;
2074 // send to the server or rebroadcast if I _am_ the server
2075 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2076 multi_voice_route_data(data,packet_size,MY_NET_PLAYER_NUM,(int)msg_mode,(target_index == -1) ? NULL : &Net_players[target_index]);
2078 multi_io_send(Net_player, data, packet_size);
2084 // --------------------------------------------------------------------------------------------------
2085 // MULTI VOICE ALGORITHM stuff
2088 // process and play the current window of sound stream data we have. reset the window for the next incoming data as well
2089 void multi_voice_alg_play_window(int stream_index)
2091 int idx,buffer_offset;
2094 #ifdef MULTI_VOICE_VERBOSE
2095 nprintf(("Network","MULTI VOICE : PLAYING STREAM %d\n",stream_index));
2098 // get a pointer to the stream
2099 st = &Multi_voice_stream[stream_index];
2101 // don't play anything back if we can't hear sound
2102 if(Multi_voice_can_play){
2103 // first, pack all the accum buffers into the playback buffer
2104 #ifdef MULTI_VOICE_PRE_DECOMPRESS
2105 buffer_offset = Multi_voice_pre_sound_size;
2106 nprintf(("Network","VOICE : pre sound size %d\n",Multi_voice_pre_sound_size));
2107 for(idx=0;idx<MULTI_VOICE_ACCUM_BUFFER_COUNT;idx++){
2108 // if the flag is set, uncompress the data into the playback buffer
2109 if(st->accum_buffer_flags[idx]){
2110 // first, uncompress the data
2111 rtvoice_uncompress(st->accum_buffer[idx],(int)st->accum_buffer_csize[idx],st->accum_buffer_gain[idx],(ubyte*)Multi_voice_playback_buffer+buffer_offset,st->accum_buffer_usize[idx]);
2113 // increment the buffer offset
2114 buffer_offset += st->accum_buffer_usize[idx];
2118 #ifdef MULTI_VOICE_POST_DECOMPRESS
2120 for(idx=0;idx<MULTI_VOICE_ACCUM_BUFFER_COUNT;idx++){
2121 // if the flag is set, copy the data
2122 if(st->accum_buffer_flags[idx]){
2123 memcpy(Multi_voice_unpack_buffer+buffer_offset,st->accum_buffer[idx],st->accum_buffer_csize[idx]);
2124 buffer_offset += st->accum_buffer_csize[idx];
2128 // decompress the whole shebang
2129 rtvoice_uncompress((ubyte*)Multi_voice_unpack_buffer,buffer_offset,st->accum_buffer_gain[0],(ubyte*)Multi_voice_playback_buffer,st->accum_buffer_usize[0]);
2130 buffer_offset = st->accum_buffer_usize[0];
2133 // mix in the SND_CUE_VOICE and the SND_END_VOICE game sounds
2134 buffer_offset = multi_voice_mix(MULTI_VOICE_POST_SOUND,Multi_voice_playback_buffer,buffer_offset,MULTI_VOICE_MAX_BUFFER_SIZE);
2136 SDL_assert(Multi_voice_stream[stream_index].stream_rtvoice_handle != -1);
2138 // kill any previously playing sounds
2139 rtvoice_stop_playback(Multi_voice_stream[stream_index].stream_rtvoice_handle);
2140 Multi_voice_stream[stream_index].stream_snd_handle = -1;
2142 // if we can play sound and we know who this is from, display it
2143 if(Multi_voice_can_play){
2144 char voice_msg[256];
2145 int player_index = find_player_id(Multi_voice_stream[stream_index].stream_from);
2147 if(player_index != -1){
2148 SDL_snprintf(voice_msg, sizeof(voice_msg), XSTR("<%s is speaking>", 712), Net_players[player_index].player->callsign);
2150 // display a chat message (write to the correct spot - hud, standalone gui, chatbox, etc)
2151 multi_display_chat_msg(voice_msg,player_index,0);
2155 // now call the rtvoice playback functions
2156 Multi_voice_stream[stream_index].stream_snd_handle = rtvoice_play_uncompressed(Multi_voice_stream[stream_index].stream_rtvoice_handle,(unsigned char*)Multi_voice_playback_buffer,buffer_offset);
2157 Multi_voice_stream[stream_index].stream_start_time = timer_get_fixed_seconds();
2160 // unset the stamp so that its not "free"
2161 Multi_voice_stamps[stream_index] = -1;
2163 // flush the stream (will also grab the token back, if the server)
2164 multi_voice_flush_old_stream(stream_index);
2167 // decision function which decides if we should play the current block of sound we have
2168 int multi_voice_alg_should_play(int stream_index)
2170 // if the timestamp has expired, play the sound
2171 if((Multi_voice_stamps[stream_index] != -1) && timestamp_elapsed(Multi_voice_stamps[stream_index])){
2172 #ifdef MULTI_VOICE_VERBOSE
2173 nprintf(("Network","MULTI VOICE : DECIDE, TIMEOUT\n"));
2181 // process incoming sound data in whatever way necessary (this function should take care of playing data when necessary)
2182 void multi_voice_alg_process_data(int player_index,int stream_index,ushort chunk_index,ushort chunk_size)
2184 // do this so we don't get compiler warnings
2189 // update the timestamp for this window
2190 Multi_voice_stamps[stream_index] = timestamp(MV_ALG_TIMEOUT);
2193 // process existing streams
2194 void multi_voice_alg_process_streams()
2199 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
2200 // determine if we should play this window of data
2201 if((Multi_voice_stamps[idx] != -1) && multi_voice_alg_should_play(idx)){
2202 // determine who this stream came from
2203 player_index = find_player_id(Multi_voice_stream[idx].stream_from);
2205 // server should check his own settings here
2206 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && ((Net_player->p_info.options.flags & MLO_FLAG_NO_VOICE) || (player_index == -1) || !(Multi_voice_player_prefs[MY_NET_PLAYER_NUM] & (1<<player_index))) ){
2207 // unset the stamp so that its not "free"
2208 Multi_voice_stamps[idx] = -1;
2210 // flush the stream (will also grab the token back, if the server)
2211 multi_voice_flush_old_stream(idx);
2213 nprintf(("Network","Server not playing sound because of set options!\n"));
2215 // play the current sound
2217 multi_voice_alg_play_window(idx);
2223 // we are going to flush the current stream because we have started to receive data for a new one. do something first
2224 void multi_voice_alg_flush_old_stream(int stream_index)
2226 // just unset the heard from timestamp for now
2227 Multi_voice_stamps[stream_index] = -1;
2230 // initialize the smart algorithm
2231 void multi_voice_alg_init()
2235 for(idx=0;idx<MULTI_VOICE_MAX_STREAMS;idx++){
2236 Multi_voice_stamps[idx] = -1;
2241 // --------------------------------------------------------------------------------------------------
2242 // MULTI VOICE TESTING FUNCTIONS
2245 #define MV_TEST_RECORD_TIME 3000 // recording time in ms for testing voice
2246 int Multi_voice_test_record_stamp = -1;
2247 int Multi_voice_test_packet_tossed = 0;
2249 // process the next chunk of voice data
2250 void multi_voice_test_process_next_chunk()
2252 unsigned char *outbuf,*outbuf_raw;
2253 int compressed_size,uncompressed_size,raw_size;
2256 // if the test recording stamp is -1, we should stop
2257 if(Multi_voice_test_record_stamp == -1){
2258 rtvoice_stop_recording();
2262 // if the recording timestamp has elapsed, stop the whole thing
2263 if(timestamp_elapsed(Multi_voice_test_record_stamp)){
2264 nprintf(("Network","Stopping voice test recording\n"));
2266 rtvoice_stop_recording();
2268 Multi_voice_test_record_stamp = -1;
2269 Multi_voice_test_packet_tossed = 0;
2273 // otherwise get the compressed and uncompressed data and do something interesting with it
2274 rtvoice_get_data(&outbuf,&compressed_size,&uncompressed_size,&gain,&outbuf_raw,&raw_size);
2276 // determine whether the packet would have been dropped
2277 if(compressed_size > multi_voice_max_chunk_size(MULTI_MSG_ALL)){
2278 Multi_voice_test_packet_tossed = 1;
2280 Multi_voice_test_packet_tossed = 0;
2283 // send the raw output buffer to the voice options screen
2284 options_multi_set_voice_data(outbuf_raw,raw_size,outbuf,compressed_size,uncompressed_size,gain);
2287 // start recording voice locally for playback testing
2288 void multi_voice_test_record_start()
2290 // if there is test recording going on already, don't do anything
2291 if(Multi_voice_test_record_stamp != -1){
2295 // stop any playback which may be occuring
2296 rtvoice_stop_playback_all();
2298 // stop any recording which may be occuring
2299 rtvoice_stop_recording();
2301 // set the timestamp
2302 Multi_voice_test_record_stamp = timestamp(MV_TEST_RECORD_TIME);
2304 // start the recording of voice
2305 rtvoice_start_recording(multi_voice_test_process_next_chunk);
2308 // force stop any recording voice test
2309 void multi_voice_test_record_stop()
2311 Multi_voice_test_record_stamp = -1;
2312 Multi_voice_test_packet_tossed = 0;
2313 rtvoice_stop_recording();
2316 // return if the test recording is going on
2317 int multi_voice_test_recording()
2319 return (Multi_voice_test_record_stamp == -1) ? 0 : 1;
2322 // call this function if multi_voice_test_recording() is true to process various odds and ends of the test recording
2323 void multi_voice_test_process()
2325 // if we're not recording, do nothing
2326 if(Multi_voice_test_record_stamp == -1){
2330 // check to see if the timestamp has elapsed
2331 if(timestamp_elapsed(Multi_voice_test_record_stamp)){
2332 Multi_voice_test_record_stamp = -1;
2333 Multi_voice_test_packet_tossed = 0;
2337 // get a playback buffer handle (return -1 if none exist - bad)
2338 int multi_voice_test_get_playback_buffer()
2340 // return voice stream 0
2341 SDL_assert(Multi_voice_stream[0].stream_snd_handle == -1);
2342 SDL_assert(Multi_voice_stream[0].stream_rtvoice_handle != -1);
2344 return Multi_voice_stream[0].stream_rtvoice_handle;
2347 // return whether the last sampled chunk would have been too large to fit in a packet
2348 int multi_voice_test_packet_tossed()
2350 return Multi_voice_test_packet_tossed;