]> icculus.org git repositories - taylor/freespace2.git/blob - src/sound/rtvoice.cpp
plug demo upsells into the game event/state system
[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 "oal.h"
115 #include "oal_capture.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 buf_handle;         // handle to sound 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 SDL_TimerID 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 Uint32 TimeProc(Uint32 interval, void *param)
186 {
187         if ( !Rtv_callback ) {
188                 SDL_RemoveTimer(Rtv_record_timer_id);
189                 Rtv_record_timer_id = 0;
190
191                 return 0;
192         }
193
194         nprintf(("Alan","In callback\n"));
195         Rtv_callback();
196
197         if (Rtv_callback_time) {
198                 return interval;
199         } else {
200                 SDL_RemoveTimer(Rtv_record_timer_id);
201                 Rtv_record_timer_id = 0;
202
203                 return 0;
204         }
205 }
206
207 // Try to pick the most appropriate recording format
208 //
209 // exit:        0       =>              success
210 //                      !0      =>              failure
211 int rtvoice_pick_record_format()
212 {
213         int i;
214
215         for (i=0; i<MAX_RTV_FORMATS; i++) {
216                 if ( oal_capture_create_buffer(Rtv_formats[i].frequency, Rtv_formats[i].bits_per_sample, 1, RTV_BUFFER_TIME) == 0 ) {
217                         oal_capture_release_buffer();
218                         Rtv_recording_format=i;
219                         break;
220                 }
221         }
222
223         if ( i == MAX_RTV_FORMATS ) {
224                 return -1;
225         }
226
227         return 0;
228 }
229
230 // input:       qos => new quality of service (1..10)
231 void rtvoice_set_qos(int qos)
232 {
233         InitEncoder(e_cCodec1, qos, Encode_buffer1, Encode_buffer2);
234 }
235
236 // Init the recording portion of the real-time voice system
237 // input:       qos     => quality of service (1..10) 1 is highest compression, 10 is highest quality
238 //      exit:   0       =>      success
239 //                      !0      =>      failure, recording not possible
240 int rtvoice_init_recording(int qos)
241 {
242         if ( !Rtv_recording_inited ) {
243                 if ( rtvoice_pick_record_format() ) {
244                         return -1;
245                 }
246
247                 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);
248
249                 if ( Encode_buffer1 ) {
250                         free(Encode_buffer1);
251                         Encode_buffer1=NULL;
252                 }
253
254                 if ( oal_capture_create_buffer(Rtv_formats[Rtv_recording_format].frequency, Rtv_formats[Rtv_recording_format].bits_per_sample, 1, RTV_BUFFER_TIME) ) {
255                         return -1;
256                 }
257
258                 Encode_buffer1 = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
259                 SDL_assert(Encode_buffer1);
260
261                 if ( Encode_buffer2 ) {
262                         free(Encode_buffer2);
263                         Encode_buffer2=NULL;
264                 }
265                 Encode_buffer2 = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
266                 SDL_assert(Encode_buffer2);
267
268                 // malloc out the voice data buffer for raw (uncompressed) recorded sound
269                 if ( Rtv_capture_raw_buffer ) {
270                         free(Rtv_capture_raw_buffer);
271                         Rtv_capture_raw_buffer=NULL;
272                 }
273                 Rtv_capture_raw_buffer = (unsigned char*)malloc(Rtv_capture_raw_buffer_size);
274
275                 // malloc out voice data buffer for compressed recorded sound
276                 if ( Rtv_capture_compressed_buffer ) {
277                         free(Rtv_capture_compressed_buffer);
278                         Rtv_capture_compressed_buffer=NULL;
279                 }
280                 Rtv_capture_compressed_buffer_size=Rtv_capture_raw_buffer_size; // be safe and allocate same as uncompressed
281                 Rtv_capture_compressed_buffer = (unsigned char*)malloc(Rtv_capture_compressed_buffer_size);
282
283                 InitEncoder(e_cCodec1, qos, Encode_buffer1, Encode_buffer2);
284
285                 Rtv_recording_inited=1;
286         }
287         return 0;
288 }
289
290 // Stop a stream from recording
291 void rtvoice_stop_recording()
292 {
293         if ( !Rtv_recording ) {
294                 return;
295         }
296
297         oal_capture_stop_record();
298
299         if ( Rtv_record_timer_id ) {
300                 SDL_RemoveTimer(Rtv_record_timer_id);
301                 Rtv_record_timer_id = 0;
302         }
303
304         Rtv_recording=0;
305 }
306
307 // Close down the real-time voice recording system
308 void rtvoice_close_recording()
309 {
310         if ( Rtv_recording ) {
311                 rtvoice_stop_recording();
312         }
313
314         if ( Encode_buffer1 ) {
315                 free(Encode_buffer1);
316                 Encode_buffer1=NULL;
317         }
318
319         if ( Encode_buffer2 ) {
320                 free(Encode_buffer2);
321                 Encode_buffer2=NULL;
322         }
323
324         if ( Rtv_capture_raw_buffer ) {
325                 free(Rtv_capture_raw_buffer);
326                 Rtv_capture_raw_buffer=NULL;
327         }
328
329         if ( Rtv_capture_compressed_buffer ) {
330                 free(Rtv_capture_compressed_buffer);
331                 Rtv_capture_compressed_buffer=NULL;
332         }
333
334         oal_capture_release_buffer();
335
336         Rtv_recording_inited=0;
337 }
338
339 // Open a stream for recording (recording begins immediately)
340 // exit:        0       =>      success
341 //                      !0      =>      failure
342 int rtvoice_start_recording( void (*user_callback)(), int callback_time ) 
343 {
344         if ( !oal_capture_supported() ) {
345                 return -1;
346         }
347
348         SDL_assert(Rtv_recording_inited);
349
350         if ( Rtv_recording ) {
351                 return -1;
352         }
353
354         if ( oal_capture_start_record() ) {
355                 return -1;
356         }
357
358         if ( user_callback ) {
359                 Rtv_record_timer_id = SDL_AddTimer(callback_time, TimeProc, NULL);
360
361                 if ( !Rtv_record_timer_id ) {
362                         oal_capture_stop_record();
363                         return -1;
364                 }
365                 Rtv_callback = user_callback;
366                 Rtv_callback_time = callback_time;
367         } else {
368                 Rtv_callback = NULL;
369                 Rtv_record_timer_id = 0;
370         }
371
372         Rtv_recording=1;
373         return 0;
374 }
375
376 // compress voice data using specialized codec
377 int rtvoice_compress(unsigned char *data_in, int size_in, unsigned char *data_out, int size_out)
378 {
379         int             compressed_size;
380         
381         Rtv_code_info.Code = e_cCodec1;
382         Rtv_code_info.Gain = 0;
383
384         compressed_size = 0;
385         if(size_in <= 0){
386                 nprintf(("Network","RTVOICE => 0 bytes size in !\n"));          
387         } else {
388                 compressed_size = Encode(data_in, data_out, size_in, size_out, &Rtv_code_info);
389
390                 nprintf(("SOUND","RTVOICE => Sound compressed to %d bytes (%0.2f percent)\n", compressed_size, (compressed_size*100.0f)/size_in));
391         }
392
393         return compressed_size;
394 }
395
396 // For 8-bit formats (unsigned, 0 to 255)
397 // For 16-bit formats (signed, -32768 to 32767)
398 int rtvoice_16to8(unsigned char *data, int size)
399 {
400         int i;
401         unsigned short  sample16;
402         unsigned char   sample8, *dest, *src;
403
404         SDL_assert(size%2 == 0);
405
406         dest = data;
407         src = data;
408
409         for (i=0; i<size; i+=2) {
410                 sample16  = src[0];
411                 sample16 |= src[1] << 8;
412
413                 sample16 += 32768;
414                 sample8 = (unsigned char)(sample16>>8);
415
416                 *dest++ = sample8;
417                 src += 2;
418         }
419
420         return (size>>1);
421 }
422
423 // Convert voice sample from 22KHz to 11KHz
424 int rtvoice_22to11(unsigned char *data, int size)
425 {
426         int i, new_size=0;
427         unsigned char *dest, *src;
428
429         dest=data;
430         src=data;
431
432         for (i=0; i<size; i+=2) {
433                 *(dest+new_size) = *(src+i);
434                 new_size++;
435         }
436
437         return new_size; 
438 }
439
440 // Convert voice data to 8bit, 11KHz if necessary
441 int rtvoice_maybe_convert_data(unsigned char *data, int size)
442 {
443         int new_size=size;
444         switch ( Rtv_recording_format ) {
445         case 0:
446                 // do nothing
447                 break;
448         case 1:
449                 // convert samples to 8 bit from 16 bit
450                 new_size = rtvoice_16to8(data,new_size);
451                 break;
452         case 2:
453                 // convert to 11KHz
454                 new_size = rtvoice_22to11(data,new_size);
455                 break;
456         case 3:
457                 // convert to 11Khz, 8 bit
458                 new_size = rtvoice_16to8(data,new_size);
459                 new_size = rtvoice_22to11(data,new_size);
460                 break;
461         default:
462                 Int3(); // should never happen
463                 break;
464         }
465
466         return new_size;
467 }
468
469 // Retrieve the recorded voice data
470 // input:       outbuf                                  =>              output parameter, recorded voice stored here
471 //                              compressed_size         =>              output parameter, size in bytes of recorded voice after compression
472 //                              uncompressed_size               =>              output parameter, size in bytes of recorded voice before compression
473 //                              gain                                            =>              output parameter, gain value which must be passed to decoder
474 //                              outbuf_raw                              =>              output optional parameter, pointer to the raw sound data making up the compressed chunk
475 //                              outbuf_size_raw         =>              output optional parameter, size of the outbuf_raw buffer
476 //
477 // NOTE: function converts voice data into compressed format
478 void rtvoice_get_data(unsigned char **outbuf, int *compressed_size, int *uncompressed_size, double *gain, unsigned char **outbuf_raw, int *outbuf_size_raw)
479 {
480         int max_size, raw_size, csize;
481         max_size = oal_capture_max_buffersize();
482
483         *compressed_size=0;
484         *uncompressed_size=0;
485         *outbuf=NULL;
486
487         if ( max_size < 0 ) {
488                 return;
489         }       
490         
491         raw_size = oal_capture_get_raw_data(Rtv_capture_raw_buffer, max_size);
492
493         // convert data to 8bit, 11KHz if necessary
494         raw_size = rtvoice_maybe_convert_data(Rtv_capture_raw_buffer, raw_size);
495         *uncompressed_size=raw_size;
496
497         // compress voice data
498         if ( Rtv_do_compression ) {
499                 csize = rtvoice_compress(Rtv_capture_raw_buffer, raw_size, Rtv_capture_compressed_buffer, Rtv_capture_compressed_buffer_size);
500                 *gain = Rtv_code_info.Gain;
501                 *compressed_size = csize;
502                 *outbuf = Rtv_capture_compressed_buffer;                
503         } else {
504                 *gain = Rtv_code_info.Gain;
505                 *compressed_size = raw_size;
506                 *outbuf = Rtv_capture_raw_buffer;
507         }
508
509         // NOTE : if we are not doing compression, then the raw buffer and size are going to be the same as the compressed
510         //        buffer and size
511
512         // assign the raw buffer and size if necessary
513         if(outbuf_raw != NULL){
514                 *outbuf_raw = Rtv_capture_raw_buffer;
515         }
516         if(outbuf_size_raw != NULL){
517                 *outbuf_size_raw = raw_size;
518         }
519 }
520
521 /////////////////////////////////////////////////////////////////////////////////////////////////
522 // DECODE/PLAYBACK
523 /////////////////////////////////////////////////////////////////////////////////////////////////
524
525 // return the size that the decode buffer should be
526 int rtvoice_get_decode_buffer_size()
527 {
528         return Decode_buffer_size;
529 }
530
531 // uncompress the data into PCM format
532 void rtvoice_uncompress(unsigned char *data_in, int size_in, double gain, unsigned char *data_out, int size_out)
533 {
534         Rtv_code_info.Gain = gain;
535         Decode(&Rtv_code_info, data_in, data_out, size_in, size_out);
536 }
537
538 // Close down the real-time voice playback system
539 void rtvoice_close_playback()
540 {
541         if ( Decode_buffer ) {
542                 free(Decode_buffer);
543                 Decode_buffer=NULL;
544         }
545
546         if ( Rtv_playback_uncompressed_buffer ) {
547                 free(Rtv_playback_uncompressed_buffer);
548                 Rtv_playback_uncompressed_buffer=NULL;
549         }
550
551         Rtv_playback_inited=0;
552 }
553
554 // Clear out the Rtv_output_buffers[] array
555 void rtvoice_reset_out_buffers()
556 {
557         int i;
558         
559         for ( i=0; i<MAX_RTV_OUT_BUFFERS; i++ ) {
560                 Rtv_output_buffers[i].flags=0;
561                 Rtv_output_buffers[i].buf_handle=-1;
562         }
563 }
564
565 // Init the playback portion of the real-time voice system
566 //      exit:   0       =>      success
567 //                      !0      =>      failure, playback not possible
568 int rtvoice_init_playback()
569 {
570         rtv_format      *rtvf=NULL;
571
572         if ( !Rtv_playback_inited ) {
573
574                 rtvoice_reset_out_buffers();
575
576                 Rtv_playback_format=0;
577                 rtvf = &Rtv_formats[Rtv_playback_format];
578                 Decode_buffer_size = rtvf->frequency * (RTV_BUFFER_TIME) * fl2i(rtvf->bits_per_sample/8.0f);
579
580                 if ( Decode_buffer ) {
581                         free(Decode_buffer);
582                         Decode_buffer=NULL;
583                 }
584
585                 Decode_buffer = (unsigned char*)malloc(Decode_buffer_size);
586                 SDL_assert(Decode_buffer);
587
588                 if ( Rtv_playback_uncompressed_buffer ) {
589                         free(Rtv_playback_uncompressed_buffer);
590                         Rtv_playback_uncompressed_buffer=NULL;
591                 }
592
593                 Rtv_playback_uncompressed_buffer_size=Decode_buffer_size;
594                 Rtv_playback_uncompressed_buffer = (unsigned char*)malloc(Rtv_playback_uncompressed_buffer_size);
595                 SDL_assert(Rtv_playback_uncompressed_buffer);
596
597                 InitDecoder(1, Decode_buffer); 
598
599                 Rtv_playback_inited=1;
600         }
601
602         return 0;
603 }
604
605 int rtvoice_find_free_output_buffer()
606 {
607         int i;
608
609         for ( i=0; i<MAX_RTV_OUT_BUFFERS; i++ ) {
610                 if ( !(Rtv_output_buffers[i].flags & RTV_OUT_FLAG_USED) ) {
611                         break;
612                 }
613         }
614
615         if ( i == MAX_RTV_OUT_BUFFERS ) {
616                 return -1;
617         }
618
619         Rtv_output_buffers[i].flags |= RTV_OUT_FLAG_USED;
620         return i;
621          
622 }
623
624 // Open a stream for real-time voice output
625 int rtvoice_create_playback_buffer()
626 {
627         int                     index;
628         rtv_format      *rtvf=NULL;
629
630         rtvf = &Rtv_formats[Rtv_playback_format];
631         index = rtvoice_find_free_output_buffer();
632
633         if ( index == -1 ) {
634                 Int3();
635                 return -1;
636         }
637         
638         Rtv_output_buffers[index].buf_handle = oal_create_buffer(rtvf->frequency, rtvf->bits_per_sample, 1, RTV_BUFFER_TIME);
639         if ( Rtv_output_buffers[index].buf_handle == -1 ) {
640                 return -1;
641         }
642
643         return 0;
644 }
645
646 void rtvoice_stop_playback(int index)
647 {
648         SDL_assert(index >=0 && index < MAX_RTV_OUT_BUFFERS);
649
650         if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) {
651                 if ( Rtv_output_buffers[index].buf_handle != -1 ) {
652                         oal_stop_buffer(Rtv_output_buffers[index].buf_handle);
653                 }
654         }
655 }
656
657 void rtvoice_stop_playback_all()
658 {
659         int i;
660
661         for ( i = 0; i < MAX_RTV_OUT_BUFFERS; i++ ) {
662                 rtvoice_stop_playback(i);
663         }
664 }
665
666 // Close a stream that was opened for real-time voice output
667 void rtvoice_free_playback_buffer(int index)
668 {
669         SDL_assert(index >=0 && index < MAX_RTV_OUT_BUFFERS);
670
671         if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) {
672                 Rtv_output_buffers[index].flags=0;
673                 if ( Rtv_output_buffers[index].buf_handle != -1 ) {
674                         oal_stop_buffer(Rtv_output_buffers[index].buf_handle);
675                         oal_unload_buffer(Rtv_output_buffers[index].buf_handle);
676                 }
677                 Rtv_output_buffers[index].buf_handle=-1;
678         }
679 }
680
681 // Play compressed sound data
682 // exit:        >=0     =>      handle to playing sound
683 //                      -1              =>      error, voice not played
684 int rtvoice_play_compressed(int index, unsigned char *data, int size, int uncompressed_size, double gain)
685 {
686         int buf_handle, rval;
687
688         buf_handle = Rtv_output_buffers[index].buf_handle;
689
690         // Stop any currently playing voice output
691         oal_stop_buffer(buf_handle);
692
693         SDL_assert(uncompressed_size <= Rtv_playback_uncompressed_buffer_size);
694
695         // uncompress the data into PCM format
696         if ( Rtv_do_compression ) {
697                 rtvoice_uncompress(data, size, gain, Rtv_playback_uncompressed_buffer, uncompressed_size);
698         }
699
700         // lock the data in
701         if ( oal_lock_data(buf_handle, Rtv_playback_uncompressed_buffer, uncompressed_size) ) {
702                 return -1;
703         }
704
705         // play the voice
706         rval = oal_play(buf_handle, -1, SND_PRIORITY_MUST_PLAY, Master_voice_volume, 0.0f, 0);
707         return rval;
708 }
709
710 // Play uncompressed (raw) sound data
711 // exit:        >=0     =>      handle to playing sound
712 //                      -1              =>      error, voice not played
713 int rtvoice_play_uncompressed(int index, unsigned char *data, int size)
714 {
715         int buf_handle, rval;
716
717         buf_handle = Rtv_output_buffers[index].buf_handle;
718
719         // Stop any currently playing voice output
720         oal_stop_buffer(buf_handle);
721
722         // lock the data in
723         if ( oal_lock_data(buf_handle, data, size) ) {
724                 return -1;
725         }
726
727         // play the voice
728         rval = oal_play(buf_handle, -1, SND_PRIORITY_MUST_PLAY, Master_voice_volume, 0.0f, 0);
729         return rval;
730 }
731