]> icculus.org git repositories - taylor/freespace2.git/blob - src/sound/rtvoice.cpp
added copyright header
[taylor/freespace2.git] / src / sound / rtvoice.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Sound/rtvoice.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module file for real-time voice
16  *
17  * $Log$
18  * Revision 1.4  2002/06/09 04:41:27  relnev
19  * added copyright header
20  *
21  * Revision 1.3  2002/05/27 04:04:43  relnev
22  * 155 undefined references left
23  *
24  * Revision 1.2  2002/05/07 03:16:52  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:10  root
28  * Initial import.
29  *
30  * 
31  * 2     10/07/98 10:54a Dave
32  * Initial checkin.
33  * 
34  * 1     10/07/98 10:51a Dave
35  * 
36  * 24    4/24/98 2:17a Lawrance
37  * Clear out record buffer when recording begins
38  * 
39  * 23    4/21/98 4:44p Dave
40  * Implement Vasudan ships in multiplayer. Added a debug function to bash
41  * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
42  * problem in options screen. 
43  * 
44  * 22    4/21/98 10:30a Dave
45  * allocate 8 second buffers for rtvoice
46  * 
47  * 21    4/17/98 5:27p Dave
48  * More work on the multi options screen. Fixed many minor ui todo bugs.
49  * 
50  * 20    4/17/98 10:38a Lawrance
51  * reduce num output streams to 1
52  * 
53  * 19    3/25/98 9:56a Dave
54  * Increase buffer size to handle 8 seconds of voice data.
55  * 
56  * 18    3/22/98 7:13p Lawrance
57  * Get streaming of recording voice working
58  * 
59  * 17    3/09/98 5:22p Dave
60  * Fixed a rtvoice bug which caused bogus output when given size 0 input.
61  * 
62  * 16    2/26/98 2:54p Lawrance
63  * Don't recreate capture buffer each time recording starts... just use
64  * one.
65  * 
66  * 15    2/24/98 11:56p Lawrance
67  * Change real-time voice code to provide the uncompressed size on decode.
68  * 
69  * 14    2/24/98 10:13p Dave
70  * Put in initial support for multiplayer voice streaming.
71  * 
72  * 13    2/24/98 10:47a Lawrance
73  * Play voice through normal channels
74  * 
75  * 12    2/23/98 6:54p Lawrance
76  * Make interface to real-time voice more generic and useful.
77  * 
78  * 11    2/19/98 12:47a Lawrance
79  * Use a global code_info
80  * 
81  * 10    2/16/98 7:31p Lawrance
82  * get compression/decompression of voice working
83  * 
84  * 9     2/15/98 11:59p Lawrance
85  * Change the order of some code when opening a stream
86  * 
87  * 8     2/15/98 11:10p Lawrance
88  * more work on real-time voice system
89  * 
90  * 7     2/15/98 4:43p Lawrance
91  * work on real-time voice
92  * 
93  * 6     2/09/98 8:07p Lawrance
94  * get buffer create working
95  * 
96  * 5     2/04/98 6:08p Lawrance
97  * Read function pointers from dsound.dll, further work on
98  * DirectSoundCapture.
99  * 
100  * 4     2/03/98 11:53p Lawrance
101  * Adding support for DirectSoundCapture
102  * 
103  * 3     2/03/98 4:07p Lawrance
104  * check return codes from waveIn calls
105  * 
106  * 2     1/31/98 5:48p Lawrance
107  * Start on real-time voice recording
108  *
109  * $NoKeywords: $
110  */
111
112 #include "pstypes.h"
113 #include "sound.h"
114 #include "ds.h"
115 #include "dscap.h"
116 #include "codec1.h"
117 #include "rtvoice.h"
118
119 typedef struct rtv_format
120 {
121         int nchannels;
122         int bits_per_sample;
123         int frequency;
124 } rtv_format;
125
126
127 #define MAX_RTV_FORMATS 5
128 static rtv_format Rtv_formats[MAX_RTV_FORMATS] = 
129 {
130         {1,     8,              11025},
131         {1,     16,     11025},
132         {1,     8,              22050},
133         {1,     16,     22050},
134         {1,     8,              44100},
135 };
136
137 static int Rtv_do_compression=1;                                        // flag to indicate whether compression should be done          
138 static int Rtv_recording_format;                                        // recording format, index into Rtv_formats[]
139 static int Rtv_playback_format;                                 // playback format, index into Rtv_formats[]
140
141 #define RTV_BUFFER_TIME         8                                               // length of buffer in seconds  
142
143 static int Rtv_recording_inited=0;                              // The input stream has been inited
144 static int Rtv_playback_inited=0;                               // The output stream has been inited
145
146 static int Rtv_recording=0;                                             // Voice is currently being recorded
147
148 #define MAX_RTV_OUT_BUFFERS     1
149 #define RTV_OUT_FLAG_USED               (1<<0)
150 typedef struct rtv_out_buffer
151 {
152         int ds_handle;          // handle to directsound buffer
153         int flags;                      // see RTV_OUT_FLAG_ #defines above
154 } rtv_out_buffer;
155 static rtv_out_buffer Rtv_output_buffers[MAX_RTV_OUT_BUFFERS];          // data for output buffers
156
157 static struct   t_CodeInfo Rtv_code_info;               // Parms will need to be transmitted with packets
158
159 // recording timer data
160 static int Rtv_record_timer_id;         // unique id for callback timer
161 static int Rtv_callback_time;                   // callback time in ms
162
163 void (*Rtv_callback)();
164
165 // recording/encoding buffers
166 static unsigned char *Rtv_capture_raw_buffer;
167 static unsigned char *Rtv_capture_compressed_buffer;
168 static int Rtv_capture_compressed_buffer_size;
169 static int Rtv_capture_raw_buffer_size;
170
171 static unsigned char    *Encode_buffer1=NULL;
172 static unsigned char    *Encode_buffer2=NULL;
173
174 // playback/decoding buffers
175 static unsigned char *Rtv_playback_uncompressed_buffer;
176 static int Rtv_playback_uncompressed_buffer_size;
177
178 static unsigned char *Decode_buffer=NULL;
179 static int Decode_buffer_size;
180
181 /////////////////////////////////////////////////////////////////////////////////////////////////
182 // RECORD/ENCODE
183 /////////////////////////////////////////////////////////////////////////////////////////////////
184
185 void CALLBACK TimeProc(unsigned int id, unsigned int msg, unsigned long userdata, unsigned long dw1, unsigned long dw2)
186 {
187         if ( !Rtv_callback ) {
188                 return;
189         }
190
191         nprintf(("Alan","In callback\n"));
192         Rtv_callback();
193 }
194
195 // Try to pick the most appropriate recording format
196 //
197 // exit:        0       =>              success
198 //                      !0      =>              failure
199 int rtvoice_pick_record_format()
200 {
201         int i;
202
203         for (i=0; i<MAX_RTV_FORMATS; i++) {
204                 if ( dscap_create_buffer(Rtv_formats[i].frequency, Rtv_formats[i].bits_per_sample, 1, RTV_BUFFER_TIME) == 0 ) {
205                         dscap_release_buffer();
206                         Rtv_recording_format=i;
207                         break;
208                 }
209         }
210
211         if ( i == MAX_RTV_FORMATS ) {
212                 return -1;
213         }
214
215         return 0;
216 }
217
218 // input:       qos => new quality of service (1..10)
219 void rtvoice_set_qos(int qos)
220 {
221         InitEncoder(e_cCodec1, qos, Encode_buffer1, Encode_buffer2);
222 }
223
224 // Init the recording portion of the real-time voice system
225 // input:       qos     => quality of service (1..10) 1 is highest compression, 10 is highest quality
226 //      exit:   0       =>      success
227 //                      !0      =>      failure, recording not possible
228 int rtvoice_init_recording(int qos)
229 {
230         if ( !Rtv_recording_inited ) {
231                 if ( rtvoice_pick_record_format() ) {
232                         return -1;
233                 }
234
235                 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);
236
237                 if ( Encode_buffer1 ) {
238                         free(Encode_buffer1);
239                         Encode_buffer1=NULL;
240                 }
241
242                 if ( dscap_create_buffer(Rtv_formats[Rtv_recording_format].frequency, Rtv_formats[Rtv_recording_format].bits_per_sample, 1, RTV_BUFFER_TIME) ) {
243                         return -1;
244                 }
245
246                 Encode_buffer1 = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
247                 Assert(Encode_buffer1);
248
249                 if ( Encode_buffer2 ) {
250                         free(Encode_buffer2);
251                         Encode_buffer2=NULL;
252                 }
253                 Encode_buffer2 = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
254                 Assert(Encode_buffer2);
255
256                 // malloc out the voice data buffer for raw (uncompressed) recorded sound
257                 if ( Rtv_capture_raw_buffer ) {
258                         free(Rtv_capture_raw_buffer);
259                         Rtv_capture_raw_buffer=NULL;
260                 }
261                 Rtv_capture_raw_buffer = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
262
263                 // malloc out voice data buffer for compressed recorded sound
264                 if ( Rtv_capture_compressed_buffer ) {
265                         free(Rtv_capture_compressed_buffer);
266                         Rtv_capture_compressed_buffer=NULL;
267                 }
268                 Rtv_capture_compressed_buffer_size=Rtv_capture_raw_buffer_size; // be safe and allocate same as uncompressed
269                 Rtv_capture_compressed_buffer = (unsigned char*)malloc(Rtv_capture_compressed_buffer_size);
270
271                 InitEncoder(e_cCodec1, qos, Encode_buffer1, Encode_buffer2);
272
273                 Rtv_recording_inited=1;
274         }
275         return 0;
276 }
277
278 // Stop a stream from recording
279 void rtvoice_stop_recording()
280 {
281         if ( !Rtv_recording ) {
282                 return;
283         }
284
285         dscap_stop_record();
286
287         if ( Rtv_record_timer_id ) {
288 #ifndef PLAT_UNIX
289                 timeKillEvent(Rtv_record_timer_id);
290 #else
291                 STUB_FUNCTION;
292 #endif          
293                 Rtv_record_timer_id = 0;
294         }
295
296         Rtv_recording=0;
297 }
298
299 // Close down the real-time voice recording system
300 void rtvoice_close_recording()
301 {
302         if ( Rtv_recording ) {
303                 rtvoice_stop_recording();
304         }
305
306         if ( Encode_buffer1 ) {
307                 free(Encode_buffer1);
308                 Encode_buffer1=NULL;
309         }
310
311         if ( Encode_buffer2 ) {
312                 free(Encode_buffer2);
313                 Encode_buffer2=NULL;
314         }
315
316         if ( Rtv_capture_raw_buffer ) {
317                 free(Rtv_capture_raw_buffer);
318                 Rtv_capture_raw_buffer=NULL;
319         }
320
321         if ( Rtv_capture_compressed_buffer ) {
322                 free(Rtv_capture_compressed_buffer);
323                 Rtv_capture_compressed_buffer=NULL;
324         }
325
326         Rtv_recording_inited=0;
327 }
328
329 // Open a stream for recording (recording begins immediately)
330 // exit:        0       =>      success
331 //                      !0      =>      failure
332 int rtvoice_start_recording( void (*user_callback)(), int callback_time ) 
333 {
334         if ( !dscap_supported() ) {
335                 return -1;
336         }
337
338         Assert(Rtv_recording_inited);
339
340         if ( Rtv_recording ) {
341                 return -1;
342         }
343
344         if ( dscap_start_record() ) {
345                 return -1;
346         }
347
348         if ( user_callback ) {
349 #ifndef PLAT_UNIX
350                 Rtv_record_timer_id = timeSetEvent(callback_time, callback_time, TimeProc, 0, TIME_PERIODIC);
351 #else
352                 STUB_FUNCTION;
353                 
354                 return -1;
355 #endif          
356                 if ( !Rtv_record_timer_id ) {
357                         dscap_stop_record();
358                         return -1;
359                 }
360                 Rtv_callback = user_callback;
361                 Rtv_callback_time = callback_time;
362         } else {
363                 Rtv_callback = NULL;
364                 Rtv_record_timer_id = 0;
365         }
366
367         Rtv_recording=1;
368         return 0;
369 }
370
371 // compress voice data using specialized codec
372 int rtvoice_compress(unsigned char *data_in, int size_in, unsigned char *data_out, int size_out)
373 {
374         int             compressed_size;
375         
376         Rtv_code_info.Code = e_cCodec1;
377         Rtv_code_info.Gain = 0;
378
379         compressed_size = 0;
380         if(size_in <= 0){
381                 nprintf(("Network","RTVOICE => 0 bytes size in !\n"));          
382         } else {
383                 compressed_size = Encode(data_in, data_out, size_in, size_out, &Rtv_code_info);
384
385                 nprintf(("SOUND","RTVOICE => Sound compressed to %d bytes (%0.2f percent)\n", compressed_size, (compressed_size*100.0f)/size_in));
386         }
387
388         return compressed_size;
389 }
390
391 // For 8-bit formats (unsigned, 0 to 255)
392 // For 16-bit formats (signed, -32768 to 32767)
393 int rtvoice_16to8(unsigned char *data, int size)
394 {
395         int i;
396         unsigned short  sample16;
397         unsigned char   sample8, *dest, *src;
398
399         Assert(size%2 == 0);
400
401         dest = data;
402         src = data;
403
404         for (i=0; i<size; i+=2) {
405                 sample16  = src[0];
406                 sample16 |= src[1] << 8;
407
408                 sample16 += 32768;
409                 sample8 = (unsigned char)(sample16>>8);
410
411                 *dest++ = sample8;
412                 src += 2;
413         }
414
415         return (size>>1);
416 }
417
418 // Convert voice sample from 22KHz to 11KHz
419 int rtvoice_22to11(unsigned char *data, int size)
420 {
421         int i, new_size=0;
422         unsigned char *dest, *src;
423
424         dest=data;
425         src=data;
426
427         for (i=0; i<size; i+=2) {
428                 *(dest+new_size) = *(src+i);
429                 new_size++;
430         }
431
432         return new_size; 
433 }
434
435 // Convert voice data to 8bit, 11KHz if necessary
436 int rtvoice_maybe_convert_data(unsigned char *data, int size)
437 {
438         int new_size=size;
439         switch ( Rtv_recording_format ) {
440         case 0:
441                 // do nothing
442                 break;
443         case 1:
444                 // convert samples to 8 bit from 16 bit
445                 new_size = rtvoice_16to8(data,new_size);
446                 break;
447         case 2:
448                 // convert to 11KHz
449                 new_size = rtvoice_22to11(data,new_size);
450                 break;
451         case 3:
452                 // convert to 11Khz, 8 bit
453                 new_size = rtvoice_16to8(data,new_size);
454                 new_size = rtvoice_22to11(data,new_size);
455                 break;
456         default:
457                 Int3(); // should never happen
458                 break;
459         }
460
461         return new_size;
462 }
463
464 // Retrieve the recorded voice data
465 // input:       outbuf                                  =>              output parameter, recorded voice stored here
466 //                              compressed_size         =>              output parameter, size in bytes of recorded voice after compression
467 //                              uncompressed_size               =>              output parameter, size in bytes of recorded voice before compression
468 //                              gain                                            =>              output parameter, gain value which must be passed to decoder
469 //                              outbuf_raw                              =>              output optional parameter, pointer to the raw sound data making up the compressed chunk
470 //                              outbuf_size_raw         =>              output optional parameter, size of the outbuf_raw buffer
471 //
472 // NOTE: function converts voice data into compressed format
473 void rtvoice_get_data(unsigned char **outbuf, int *compressed_size, int *uncompressed_size, double *gain, unsigned char **outbuf_raw, int *outbuf_size_raw)
474 {
475         int max_size, raw_size, csize;
476         max_size = dscap_max_buffersize();
477
478         *compressed_size=0;
479         *uncompressed_size=0;
480         *outbuf=NULL;
481
482         if ( max_size < 0 ) {
483                 return;
484         }       
485         
486         raw_size = dscap_get_raw_data(Rtv_capture_raw_buffer, max_size);
487
488         // convert data to 8bit, 11KHz if necessary
489         raw_size = rtvoice_maybe_convert_data(Rtv_capture_raw_buffer, raw_size);
490         *uncompressed_size=raw_size;
491
492         // compress voice data
493         if ( Rtv_do_compression ) {
494                 csize = rtvoice_compress(Rtv_capture_raw_buffer, raw_size, Rtv_capture_compressed_buffer, Rtv_capture_compressed_buffer_size);
495                 *gain = Rtv_code_info.Gain;
496                 *compressed_size = csize;
497                 *outbuf = Rtv_capture_compressed_buffer;                
498         } else {
499                 *gain = Rtv_code_info.Gain;
500                 *compressed_size = raw_size;
501                 *outbuf = Rtv_capture_raw_buffer;
502         }
503
504         // NOTE : if we are not doing compression, then the raw buffer and size are going to be the same as the compressed
505         //        buffer and size
506
507         // assign the raw buffer and size if necessary
508         if(outbuf_raw != NULL){
509                 *outbuf_raw = Rtv_capture_raw_buffer;
510         }
511         if(outbuf_size_raw != NULL){
512                 *outbuf_size_raw = raw_size;
513         }
514 }
515
516 /////////////////////////////////////////////////////////////////////////////////////////////////
517 // DECODE/PLAYBACK
518 /////////////////////////////////////////////////////////////////////////////////////////////////
519
520 // return the size that the decode buffer should be
521 int rtvoice_get_decode_buffer_size()
522 {
523         return Decode_buffer_size;
524 }
525
526 // uncompress the data into PCM format
527 void rtvoice_uncompress(unsigned char *data_in, int size_in, double gain, unsigned char *data_out, int size_out)
528 {
529         Rtv_code_info.Gain = gain;
530         Decode(&Rtv_code_info, data_in, data_out, size_in, size_out);
531 }
532
533 // Close down the real-time voice playback system
534 void rtvoice_close_playback()
535 {
536         if ( Decode_buffer ) {
537                 free(Decode_buffer);
538                 Decode_buffer=NULL;
539         }
540
541         if ( Rtv_playback_uncompressed_buffer ) {
542                 free(Rtv_playback_uncompressed_buffer);
543                 Rtv_playback_uncompressed_buffer=NULL;
544         }
545
546         Rtv_playback_inited=0;
547 }
548
549 // Clear out the Rtv_output_buffers[] array
550 void rtvoice_reset_out_buffers()
551 {
552         int i;
553         
554         for ( i=0; i<MAX_RTV_OUT_BUFFERS; i++ ) {
555                 Rtv_output_buffers[i].flags=0;
556                 Rtv_output_buffers[i].ds_handle=-1;
557         }
558 }
559
560 // Init the playback portion of the real-time voice system
561 //      exit:   0       =>      success
562 //                      !0      =>      failure, playback not possible
563 int rtvoice_init_playback()
564 {
565         rtv_format      *rtvf=NULL;
566
567         if ( !Rtv_playback_inited ) {
568
569                 rtvoice_reset_out_buffers();
570
571                 Rtv_playback_format=0;
572                 rtvf = &Rtv_formats[Rtv_playback_format];
573                 Decode_buffer_size = rtvf->frequency * (RTV_BUFFER_TIME) * fl2i(rtvf->bits_per_sample/8.0f);
574
575                 if ( Decode_buffer ) {
576                         free(Decode_buffer);
577                         Decode_buffer=NULL;
578                 }
579
580                 Decode_buffer = (unsigned char*)malloc(Decode_buffer_size);
581                 Assert(Decode_buffer);
582
583                 if ( Rtv_playback_uncompressed_buffer ) {
584                         free(Rtv_playback_uncompressed_buffer);
585                         Rtv_playback_uncompressed_buffer=NULL;
586                 }
587
588                 Rtv_playback_uncompressed_buffer_size=Decode_buffer_size;
589                 Rtv_playback_uncompressed_buffer = (unsigned char*)malloc(Rtv_playback_uncompressed_buffer_size);
590                 Assert(Rtv_playback_uncompressed_buffer);
591
592                 InitDecoder(1, Decode_buffer); 
593
594                 Rtv_playback_inited=1;
595         }
596
597         return 0;
598 }
599
600 int rtvoice_find_free_output_buffer()
601 {
602         int i;
603
604         for ( i=0; i<MAX_RTV_OUT_BUFFERS; i++ ) {
605                 if ( !(Rtv_output_buffers[i].flags & RTV_OUT_FLAG_USED) ) {
606                         break;
607                 }
608         }
609
610         if ( i == MAX_RTV_OUT_BUFFERS ) {
611                 return -1;
612         }
613
614         Rtv_output_buffers[i].flags |= RTV_OUT_FLAG_USED;
615         return i;
616          
617 }
618
619 // Open a stream for real-time voice output
620 int rtvoice_create_playback_buffer()
621 {
622         int                     index;
623         rtv_format      *rtvf=NULL;
624
625         rtvf = &Rtv_formats[Rtv_playback_format];
626         index = rtvoice_find_free_output_buffer();
627
628         if ( index == -1 ) {
629                 Int3();
630                 return -1;
631         }
632         
633         Rtv_output_buffers[index].ds_handle = ds_create_buffer(rtvf->frequency, rtvf->bits_per_sample, 1, RTV_BUFFER_TIME);
634         if ( Rtv_output_buffers[index].ds_handle == -1 ) {
635                 return -1;
636         }
637
638         return 0;
639 }
640
641 void rtvoice_stop_playback(int index)
642 {
643         Assert(index >=0 && index < MAX_RTV_OUT_BUFFERS);
644
645         if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) {
646                 if ( Rtv_output_buffers[index].ds_handle != -1 ) {
647                         ds_stop_easy(Rtv_output_buffers[index].ds_handle);
648                 }
649         }
650 }
651
652 void rtvoice_stop_playback_all()
653 {
654         int i;
655
656         for ( i = 0; i < MAX_RTV_OUT_BUFFERS; i++ ) {
657                 rtvoice_stop_playback(i);
658         }
659 }
660
661 // Close a stream that was opened for real-time voice output
662 void rtvoice_free_playback_buffer(int index)
663 {
664         Assert(index >=0 && index < MAX_RTV_OUT_BUFFERS);
665
666         if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) {
667                 Rtv_output_buffers[index].flags=0;
668                 if ( Rtv_output_buffers[index].ds_handle != -1 ) {
669                         ds_stop_easy(Rtv_output_buffers[index].ds_handle);
670                         ds_unload_buffer(Rtv_output_buffers[index].ds_handle, -1);
671                 }
672                 Rtv_output_buffers[index].ds_handle=-1;
673         }
674 }
675
676 // Play compressed sound data
677 // exit:        >=0     =>      handle to playing sound
678 //                      -1              =>      error, voice not played
679 int rtvoice_play_compressed(int index, unsigned char *data, int size, int uncompressed_size, double gain)
680 {
681         int ds_handle, rval;
682
683         ds_handle = Rtv_output_buffers[index].ds_handle;
684
685         // Stop any currently playing voice output
686         ds_stop_easy(ds_handle);
687
688         Assert(uncompressed_size <= Rtv_playback_uncompressed_buffer_size);
689
690         // uncompress the data into PCM format
691         if ( Rtv_do_compression ) {
692                 rtvoice_uncompress(data, size, gain, Rtv_playback_uncompressed_buffer, uncompressed_size);
693         }
694
695         // lock the data in
696         if ( ds_lock_data(ds_handle, Rtv_playback_uncompressed_buffer, uncompressed_size) ) {
697                 return -1;
698         }
699
700         // play the voice
701         rval = ds_play(ds_handle, -1, -100, DS_MUST_PLAY, ds_convert_volume(Master_voice_volume), 0, 0);
702         return rval;
703 }
704
705 // Play uncompressed (raw) sound data
706 // exit:        >=0     =>      handle to playing sound
707 //                      -1              =>      error, voice not played
708 int rtvoice_play_uncompressed(int index, unsigned char *data, int size)
709 {
710         int ds_handle, rval;
711
712         ds_handle = Rtv_output_buffers[index].ds_handle;
713
714         // Stop any currently playing voice output
715         ds_stop_easy(ds_handle);
716
717         // lock the data in
718         if ( ds_lock_data(ds_handle, data, size) ) {
719                 return -1;
720         }
721
722         // play the voice
723         rval = ds_play(ds_handle, -1, -100, DS_MUST_PLAY, ds_convert_volume(Master_voice_volume), 0, 0);
724         return rval;
725 }
726