2 * $Logfile: /Freespace2/code/Sound/rtvoice.cpp $
7 * C module file for real-time voice
10 * Revision 1.3 2002/05/27 04:04:43 relnev
11 * 155 undefined references left
13 * Revision 1.2 2002/05/07 03:16:52 theoddone33
14 * The Great Newline Fix
16 * Revision 1.1.1.1 2002/05/03 03:28:10 root
20 * 2 10/07/98 10:54a Dave
23 * 1 10/07/98 10:51a Dave
25 * 24 4/24/98 2:17a Lawrance
26 * Clear out record buffer when recording begins
28 * 23 4/21/98 4:44p Dave
29 * Implement Vasudan ships in multiplayer. Added a debug function to bash
30 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
31 * problem in options screen.
33 * 22 4/21/98 10:30a Dave
34 * allocate 8 second buffers for rtvoice
36 * 21 4/17/98 5:27p Dave
37 * More work on the multi options screen. Fixed many minor ui todo bugs.
39 * 20 4/17/98 10:38a Lawrance
40 * reduce num output streams to 1
42 * 19 3/25/98 9:56a Dave
43 * Increase buffer size to handle 8 seconds of voice data.
45 * 18 3/22/98 7:13p Lawrance
46 * Get streaming of recording voice working
48 * 17 3/09/98 5:22p Dave
49 * Fixed a rtvoice bug which caused bogus output when given size 0 input.
51 * 16 2/26/98 2:54p Lawrance
52 * Don't recreate capture buffer each time recording starts... just use
55 * 15 2/24/98 11:56p Lawrance
56 * Change real-time voice code to provide the uncompressed size on decode.
58 * 14 2/24/98 10:13p Dave
59 * Put in initial support for multiplayer voice streaming.
61 * 13 2/24/98 10:47a Lawrance
62 * Play voice through normal channels
64 * 12 2/23/98 6:54p Lawrance
65 * Make interface to real-time voice more generic and useful.
67 * 11 2/19/98 12:47a Lawrance
68 * Use a global code_info
70 * 10 2/16/98 7:31p Lawrance
71 * get compression/decompression of voice working
73 * 9 2/15/98 11:59p Lawrance
74 * Change the order of some code when opening a stream
76 * 8 2/15/98 11:10p Lawrance
77 * more work on real-time voice system
79 * 7 2/15/98 4:43p Lawrance
80 * work on real-time voice
82 * 6 2/09/98 8:07p Lawrance
83 * get buffer create working
85 * 5 2/04/98 6:08p Lawrance
86 * Read function pointers from dsound.dll, further work on
89 * 4 2/03/98 11:53p Lawrance
90 * Adding support for DirectSoundCapture
92 * 3 2/03/98 4:07p Lawrance
93 * check return codes from waveIn calls
95 * 2 1/31/98 5:48p Lawrance
96 * Start on real-time voice recording
108 typedef struct rtv_format
116 #define MAX_RTV_FORMATS 5
117 static rtv_format Rtv_formats[MAX_RTV_FORMATS] =
126 static int Rtv_do_compression=1; // flag to indicate whether compression should be done
127 static int Rtv_recording_format; // recording format, index into Rtv_formats[]
128 static int Rtv_playback_format; // playback format, index into Rtv_formats[]
130 #define RTV_BUFFER_TIME 8 // length of buffer in seconds
132 static int Rtv_recording_inited=0; // The input stream has been inited
133 static int Rtv_playback_inited=0; // The output stream has been inited
135 static int Rtv_recording=0; // Voice is currently being recorded
137 #define MAX_RTV_OUT_BUFFERS 1
138 #define RTV_OUT_FLAG_USED (1<<0)
139 typedef struct rtv_out_buffer
141 int ds_handle; // handle to directsound buffer
142 int flags; // see RTV_OUT_FLAG_ #defines above
144 static rtv_out_buffer Rtv_output_buffers[MAX_RTV_OUT_BUFFERS]; // data for output buffers
146 static struct t_CodeInfo Rtv_code_info; // Parms will need to be transmitted with packets
148 // recording timer data
149 static int Rtv_record_timer_id; // unique id for callback timer
150 static int Rtv_callback_time; // callback time in ms
152 void (*Rtv_callback)();
154 // recording/encoding buffers
155 static unsigned char *Rtv_capture_raw_buffer;
156 static unsigned char *Rtv_capture_compressed_buffer;
157 static int Rtv_capture_compressed_buffer_size;
158 static int Rtv_capture_raw_buffer_size;
160 static unsigned char *Encode_buffer1=NULL;
161 static unsigned char *Encode_buffer2=NULL;
163 // playback/decoding buffers
164 static unsigned char *Rtv_playback_uncompressed_buffer;
165 static int Rtv_playback_uncompressed_buffer_size;
167 static unsigned char *Decode_buffer=NULL;
168 static int Decode_buffer_size;
170 /////////////////////////////////////////////////////////////////////////////////////////////////
172 /////////////////////////////////////////////////////////////////////////////////////////////////
174 void CALLBACK TimeProc(unsigned int id, unsigned int msg, unsigned long userdata, unsigned long dw1, unsigned long dw2)
176 if ( !Rtv_callback ) {
180 nprintf(("Alan","In callback\n"));
184 // Try to pick the most appropriate recording format
186 // exit: 0 => success
188 int rtvoice_pick_record_format()
192 for (i=0; i<MAX_RTV_FORMATS; i++) {
193 if ( dscap_create_buffer(Rtv_formats[i].frequency, Rtv_formats[i].bits_per_sample, 1, RTV_BUFFER_TIME) == 0 ) {
194 dscap_release_buffer();
195 Rtv_recording_format=i;
200 if ( i == MAX_RTV_FORMATS ) {
207 // input: qos => new quality of service (1..10)
208 void rtvoice_set_qos(int qos)
210 InitEncoder(e_cCodec1, qos, Encode_buffer1, Encode_buffer2);
213 // Init the recording portion of the real-time voice system
214 // input: qos => quality of service (1..10) 1 is highest compression, 10 is highest quality
215 // exit: 0 => success
216 // !0 => failure, recording not possible
217 int rtvoice_init_recording(int qos)
219 if ( !Rtv_recording_inited ) {
220 if ( rtvoice_pick_record_format() ) {
224 Rtv_capture_raw_buffer_size = Rtv_formats[Rtv_recording_format].frequency * (RTV_BUFFER_TIME) * fl2i(Rtv_formats[Rtv_recording_format].bits_per_sample/8.0f);
226 if ( Encode_buffer1 ) {
227 free(Encode_buffer1);
231 if ( dscap_create_buffer(Rtv_formats[Rtv_recording_format].frequency, Rtv_formats[Rtv_recording_format].bits_per_sample, 1, RTV_BUFFER_TIME) ) {
235 Encode_buffer1 = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
236 Assert(Encode_buffer1);
238 if ( Encode_buffer2 ) {
239 free(Encode_buffer2);
242 Encode_buffer2 = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
243 Assert(Encode_buffer2);
245 // malloc out the voice data buffer for raw (uncompressed) recorded sound
246 if ( Rtv_capture_raw_buffer ) {
247 free(Rtv_capture_raw_buffer);
248 Rtv_capture_raw_buffer=NULL;
250 Rtv_capture_raw_buffer = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
252 // malloc out voice data buffer for compressed recorded sound
253 if ( Rtv_capture_compressed_buffer ) {
254 free(Rtv_capture_compressed_buffer);
255 Rtv_capture_compressed_buffer=NULL;
257 Rtv_capture_compressed_buffer_size=Rtv_capture_raw_buffer_size; // be safe and allocate same as uncompressed
258 Rtv_capture_compressed_buffer = (unsigned char*)malloc(Rtv_capture_compressed_buffer_size);
260 InitEncoder(e_cCodec1, qos, Encode_buffer1, Encode_buffer2);
262 Rtv_recording_inited=1;
267 // Stop a stream from recording
268 void rtvoice_stop_recording()
270 if ( !Rtv_recording ) {
276 if ( Rtv_record_timer_id ) {
278 timeKillEvent(Rtv_record_timer_id);
282 Rtv_record_timer_id = 0;
288 // Close down the real-time voice recording system
289 void rtvoice_close_recording()
291 if ( Rtv_recording ) {
292 rtvoice_stop_recording();
295 if ( Encode_buffer1 ) {
296 free(Encode_buffer1);
300 if ( Encode_buffer2 ) {
301 free(Encode_buffer2);
305 if ( Rtv_capture_raw_buffer ) {
306 free(Rtv_capture_raw_buffer);
307 Rtv_capture_raw_buffer=NULL;
310 if ( Rtv_capture_compressed_buffer ) {
311 free(Rtv_capture_compressed_buffer);
312 Rtv_capture_compressed_buffer=NULL;
315 Rtv_recording_inited=0;
318 // Open a stream for recording (recording begins immediately)
319 // exit: 0 => success
321 int rtvoice_start_recording( void (*user_callback)(), int callback_time )
323 if ( !dscap_supported() ) {
327 Assert(Rtv_recording_inited);
329 if ( Rtv_recording ) {
333 if ( dscap_start_record() ) {
337 if ( user_callback ) {
339 Rtv_record_timer_id = timeSetEvent(callback_time, callback_time, TimeProc, 0, TIME_PERIODIC);
345 if ( !Rtv_record_timer_id ) {
349 Rtv_callback = user_callback;
350 Rtv_callback_time = callback_time;
353 Rtv_record_timer_id = 0;
360 // compress voice data using specialized codec
361 int rtvoice_compress(unsigned char *data_in, int size_in, unsigned char *data_out, int size_out)
365 Rtv_code_info.Code = e_cCodec1;
366 Rtv_code_info.Gain = 0;
370 nprintf(("Network","RTVOICE => 0 bytes size in !\n"));
372 compressed_size = Encode(data_in, data_out, size_in, size_out, &Rtv_code_info);
374 nprintf(("SOUND","RTVOICE => Sound compressed to %d bytes (%0.2f percent)\n", compressed_size, (compressed_size*100.0f)/size_in));
377 return compressed_size;
380 // For 8-bit formats (unsigned, 0 to 255)
381 // For 16-bit formats (signed, -32768 to 32767)
382 int rtvoice_16to8(unsigned char *data, int size)
385 unsigned short sample16;
386 unsigned char sample8, *dest, *src;
393 for (i=0; i<size; i+=2) {
395 sample16 |= src[1] << 8;
398 sample8 = (unsigned char)(sample16>>8);
407 // Convert voice sample from 22KHz to 11KHz
408 int rtvoice_22to11(unsigned char *data, int size)
411 unsigned char *dest, *src;
416 for (i=0; i<size; i+=2) {
417 *(dest+new_size) = *(src+i);
424 // Convert voice data to 8bit, 11KHz if necessary
425 int rtvoice_maybe_convert_data(unsigned char *data, int size)
428 switch ( Rtv_recording_format ) {
433 // convert samples to 8 bit from 16 bit
434 new_size = rtvoice_16to8(data,new_size);
438 new_size = rtvoice_22to11(data,new_size);
441 // convert to 11Khz, 8 bit
442 new_size = rtvoice_16to8(data,new_size);
443 new_size = rtvoice_22to11(data,new_size);
446 Int3(); // should never happen
453 // Retrieve the recorded voice data
454 // input: outbuf => output parameter, recorded voice stored here
455 // compressed_size => output parameter, size in bytes of recorded voice after compression
456 // uncompressed_size => output parameter, size in bytes of recorded voice before compression
457 // gain => output parameter, gain value which must be passed to decoder
458 // outbuf_raw => output optional parameter, pointer to the raw sound data making up the compressed chunk
459 // outbuf_size_raw => output optional parameter, size of the outbuf_raw buffer
461 // NOTE: function converts voice data into compressed format
462 void rtvoice_get_data(unsigned char **outbuf, int *compressed_size, int *uncompressed_size, double *gain, unsigned char **outbuf_raw, int *outbuf_size_raw)
464 int max_size, raw_size, csize;
465 max_size = dscap_max_buffersize();
468 *uncompressed_size=0;
471 if ( max_size < 0 ) {
475 raw_size = dscap_get_raw_data(Rtv_capture_raw_buffer, max_size);
477 // convert data to 8bit, 11KHz if necessary
478 raw_size = rtvoice_maybe_convert_data(Rtv_capture_raw_buffer, raw_size);
479 *uncompressed_size=raw_size;
481 // compress voice data
482 if ( Rtv_do_compression ) {
483 csize = rtvoice_compress(Rtv_capture_raw_buffer, raw_size, Rtv_capture_compressed_buffer, Rtv_capture_compressed_buffer_size);
484 *gain = Rtv_code_info.Gain;
485 *compressed_size = csize;
486 *outbuf = Rtv_capture_compressed_buffer;
488 *gain = Rtv_code_info.Gain;
489 *compressed_size = raw_size;
490 *outbuf = Rtv_capture_raw_buffer;
493 // NOTE : if we are not doing compression, then the raw buffer and size are going to be the same as the compressed
496 // assign the raw buffer and size if necessary
497 if(outbuf_raw != NULL){
498 *outbuf_raw = Rtv_capture_raw_buffer;
500 if(outbuf_size_raw != NULL){
501 *outbuf_size_raw = raw_size;
505 /////////////////////////////////////////////////////////////////////////////////////////////////
507 /////////////////////////////////////////////////////////////////////////////////////////////////
509 // return the size that the decode buffer should be
510 int rtvoice_get_decode_buffer_size()
512 return Decode_buffer_size;
515 // uncompress the data into PCM format
516 void rtvoice_uncompress(unsigned char *data_in, int size_in, double gain, unsigned char *data_out, int size_out)
518 Rtv_code_info.Gain = gain;
519 Decode(&Rtv_code_info, data_in, data_out, size_in, size_out);
522 // Close down the real-time voice playback system
523 void rtvoice_close_playback()
525 if ( Decode_buffer ) {
530 if ( Rtv_playback_uncompressed_buffer ) {
531 free(Rtv_playback_uncompressed_buffer);
532 Rtv_playback_uncompressed_buffer=NULL;
535 Rtv_playback_inited=0;
538 // Clear out the Rtv_output_buffers[] array
539 void rtvoice_reset_out_buffers()
543 for ( i=0; i<MAX_RTV_OUT_BUFFERS; i++ ) {
544 Rtv_output_buffers[i].flags=0;
545 Rtv_output_buffers[i].ds_handle=-1;
549 // Init the playback portion of the real-time voice system
550 // exit: 0 => success
551 // !0 => failure, playback not possible
552 int rtvoice_init_playback()
554 rtv_format *rtvf=NULL;
556 if ( !Rtv_playback_inited ) {
558 rtvoice_reset_out_buffers();
560 Rtv_playback_format=0;
561 rtvf = &Rtv_formats[Rtv_playback_format];
562 Decode_buffer_size = rtvf->frequency * (RTV_BUFFER_TIME) * fl2i(rtvf->bits_per_sample/8.0f);
564 if ( Decode_buffer ) {
569 Decode_buffer = (unsigned char*)malloc(Decode_buffer_size);
570 Assert(Decode_buffer);
572 if ( Rtv_playback_uncompressed_buffer ) {
573 free(Rtv_playback_uncompressed_buffer);
574 Rtv_playback_uncompressed_buffer=NULL;
577 Rtv_playback_uncompressed_buffer_size=Decode_buffer_size;
578 Rtv_playback_uncompressed_buffer = (unsigned char*)malloc(Rtv_playback_uncompressed_buffer_size);
579 Assert(Rtv_playback_uncompressed_buffer);
581 InitDecoder(1, Decode_buffer);
583 Rtv_playback_inited=1;
589 int rtvoice_find_free_output_buffer()
593 for ( i=0; i<MAX_RTV_OUT_BUFFERS; i++ ) {
594 if ( !(Rtv_output_buffers[i].flags & RTV_OUT_FLAG_USED) ) {
599 if ( i == MAX_RTV_OUT_BUFFERS ) {
603 Rtv_output_buffers[i].flags |= RTV_OUT_FLAG_USED;
608 // Open a stream for real-time voice output
609 int rtvoice_create_playback_buffer()
612 rtv_format *rtvf=NULL;
614 rtvf = &Rtv_formats[Rtv_playback_format];
615 index = rtvoice_find_free_output_buffer();
622 Rtv_output_buffers[index].ds_handle = ds_create_buffer(rtvf->frequency, rtvf->bits_per_sample, 1, RTV_BUFFER_TIME);
623 if ( Rtv_output_buffers[index].ds_handle == -1 ) {
630 void rtvoice_stop_playback(int index)
632 Assert(index >=0 && index < MAX_RTV_OUT_BUFFERS);
634 if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) {
635 if ( Rtv_output_buffers[index].ds_handle != -1 ) {
636 ds_stop_easy(Rtv_output_buffers[index].ds_handle);
641 void rtvoice_stop_playback_all()
645 for ( i = 0; i < MAX_RTV_OUT_BUFFERS; i++ ) {
646 rtvoice_stop_playback(i);
650 // Close a stream that was opened for real-time voice output
651 void rtvoice_free_playback_buffer(int index)
653 Assert(index >=0 && index < MAX_RTV_OUT_BUFFERS);
655 if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) {
656 Rtv_output_buffers[index].flags=0;
657 if ( Rtv_output_buffers[index].ds_handle != -1 ) {
658 ds_stop_easy(Rtv_output_buffers[index].ds_handle);
659 ds_unload_buffer(Rtv_output_buffers[index].ds_handle, -1);
661 Rtv_output_buffers[index].ds_handle=-1;
665 // Play compressed sound data
666 // exit: >=0 => handle to playing sound
667 // -1 => error, voice not played
668 int rtvoice_play_compressed(int index, unsigned char *data, int size, int uncompressed_size, double gain)
672 ds_handle = Rtv_output_buffers[index].ds_handle;
674 // Stop any currently playing voice output
675 ds_stop_easy(ds_handle);
677 Assert(uncompressed_size <= Rtv_playback_uncompressed_buffer_size);
679 // uncompress the data into PCM format
680 if ( Rtv_do_compression ) {
681 rtvoice_uncompress(data, size, gain, Rtv_playback_uncompressed_buffer, uncompressed_size);
685 if ( ds_lock_data(ds_handle, Rtv_playback_uncompressed_buffer, uncompressed_size) ) {
690 rval = ds_play(ds_handle, -1, -100, DS_MUST_PLAY, ds_convert_volume(Master_voice_volume), 0, 0);
694 // Play uncompressed (raw) sound data
695 // exit: >=0 => handle to playing sound
696 // -1 => error, voice not played
697 int rtvoice_play_uncompressed(int index, unsigned char *data, int size)
701 ds_handle = Rtv_output_buffers[index].ds_handle;
703 // Stop any currently playing voice output
704 ds_stop_easy(ds_handle);
707 if ( ds_lock_data(ds_handle, data, size) ) {
712 rval = ds_play(ds_handle, -1, -100, DS_MUST_PLAY, ds_convert_volume(Master_voice_volume), 0, 0);