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