/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "globalincs/pstypes.h" #include "sound/ds.h" #include "sound/dscap.h" #include "sound/rtvoice.h" #include "sound/sound.h" #ifdef WIN32 #define WIN32_LEAN_AN_MEAN #include #endif typedef struct rtv_format { int nchannels; int bits_per_sample; int frequency; } rtv_format; #define MAX_RTV_CHANNELS 15 #define MAX_RTV_FORMATS 5 static rtv_format Rtv_formats[MAX_RTV_FORMATS] = { {1, 8, 11025}, {1, 16, 11025}, {1, 8, 22050}, {1, 16, 22050}, {1, 8, 44100}, }; static int Rtv_recording_format; // recording format, index into Rtv_formats[] static int Rtv_playback_format; // playback format, index into Rtv_formats[] #define RTV_BUFFER_TIME 8 // length of buffer in seconds static int Rtv_recording_inited=0; // The input stream has been inited static int Rtv_playback_inited=0; // The output stream has been inited static int Rtv_recording=0; // Voice is currently being recorded #define MAX_RTV_OUT_BUFFERS 1 #define RTV_OUT_FLAG_USED (1<<0) typedef struct rtv_out_buffer { int ds_handle; // handle to directsound buffer int flags; // see RTV_OUT_FLAG_ #defines above } rtv_out_buffer; static rtv_out_buffer Rtv_output_buffers[MAX_RTV_OUT_BUFFERS]; // data for output buffers //static struct t_CodeInfo Rtv_code_info; // Parms will need to be transmitted with packets // recording timer data #ifdef _WIN32 static int Rtv_record_timer_id; // unique id for callback timer #else static SDL_TimerID Rtv_record_timer_id; #endif static int Rtv_callback_time; // callback time in ms void (*Rtv_callback)(); // recording/encoding buffers static unsigned char *Rtv_capture_raw_buffer; static unsigned char *Rtv_capture_compressed_buffer; //static int Rtv_capture_compressed_buffer_size; static int Rtv_capture_raw_buffer_size; // playback/decoding buffers static unsigned char *Rtv_playback_uncompressed_buffer; static int Rtv_playback_uncompressed_buffer_size; ///////////////////////////////////////////////////////////////////////////////////////////////// // RECORD/ENCODE ///////////////////////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 void CALLBACK TimeProc(unsigned int /*id*/, unsigned int /*msg*/, DWORD_PTR /*userdata*/, DWORD_PTR /*dw1*/, DWORD_PTR /*dw2*/) { if ( !Rtv_callback ) { return; } nprintf(("Alan","In callback\n")); Rtv_callback(); } #else Uint32 TimeProc(void * /* userdata */, SDL_TimerID /* timerID */, Uint32 interval) { if ( !Rtv_callback ) { SDL_RemoveTimer(Rtv_record_timer_id); Rtv_record_timer_id = 0; return 0; } mprintf(("In callback\n")); Rtv_callback(); if (Rtv_callback_time) { return interval; } else { SDL_RemoveTimer(Rtv_record_timer_id); Rtv_record_timer_id = 0; return 0; } } #endif // Try to pick the most appropriate recording format // // exit: 0 => success // !0 => failure int rtvoice_pick_record_format() { int i; for (i=0; i new quality of service (1..10) void rtvoice_set_qos(int /*qos*/) { // TODO: Speex stuff } // Init the recording portion of the real-time voice system // input: qos => quality of service (1..10) 1 is highest compression, 10 is highest quality // exit: 0 => success // !0 => failure, recording not possible int rtvoice_init_recording(int /*qos*/) { #if 0 if ( !Rtv_recording_inited ) { if ( rtvoice_pick_record_format() ) { return -1; } 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); if ( dscap_create_buffer(Rtv_formats[Rtv_recording_format].frequency, Rtv_formats[Rtv_recording_format].bits_per_sample, 1, RTV_BUFFER_TIME) ) { return -1; } // malloc out the voice data buffer for raw (uncompressed) recorded sound if ( Rtv_capture_raw_buffer ) { vm_free(Rtv_capture_raw_buffer); Rtv_capture_raw_buffer=NULL; } Rtv_capture_raw_buffer = (unsigned char*)vm_malloc(Rtv_capture_raw_buffer_size); // malloc out voice data buffer for compressed recorded sound if ( Rtv_capture_compressed_buffer ) { vm_free(Rtv_capture_compressed_buffer); Rtv_capture_compressed_buffer=NULL; } Rtv_capture_compressed_buffer_size=Rtv_capture_raw_buffer_size; // be safe and allocate same as uncompressed Rtv_capture_compressed_buffer = (unsigned char*)vm_malloc(Rtv_capture_compressed_buffer_size); Rtv_recording_inited=1; } return 0; #else return -1; #endif } // Stop a stream from recording void rtvoice_stop_recording() { if ( !Rtv_recording ) { return; } dscap_stop_record(); if ( Rtv_record_timer_id ) { #ifndef _WIN32 SDL_RemoveTimer(Rtv_record_timer_id); #else timeKillEvent(Rtv_record_timer_id); #endif Rtv_record_timer_id = 0; } Rtv_recording=0; } // Close down the real-time voice recording system void rtvoice_close_recording() { if ( Rtv_recording ) { rtvoice_stop_recording(); } if ( Rtv_capture_raw_buffer ) { vm_free(Rtv_capture_raw_buffer); Rtv_capture_raw_buffer=NULL; } if ( Rtv_capture_compressed_buffer ) { vm_free(Rtv_capture_compressed_buffer); Rtv_capture_compressed_buffer=NULL; } dscap_release_buffer(); Rtv_recording_inited=0; } // Open a stream for recording (recording begins immediately) // exit: 0 => success // !0 => failure int rtvoice_start_recording( void (*user_callback)(), int callback_time ) { if ( !dscap_supported() ) { return -1; } Assert(Rtv_recording_inited); if ( Rtv_recording ) { return -1; } if ( dscap_start_record() ) { return -1; } if ( user_callback ) { #ifndef _WIN32 Rtv_record_timer_id = SDL_AddTimer(callback_time, TimeProc, NULL); #else Rtv_record_timer_id = timeSetEvent(callback_time, callback_time, TimeProc, 0, TIME_PERIODIC); #endif if ( !Rtv_record_timer_id ) { dscap_stop_record(); return -1; } Rtv_callback = user_callback; Rtv_callback_time = callback_time; } else { Rtv_callback = NULL; Rtv_record_timer_id = 0; } Rtv_recording=1; return 0; } // Retrieve the recorded voice data // input: outbuf => output parameter, recorded voice stored here // compressed_size => output parameter, size in bytes of recorded voice after compression // uncompressed_size => output parameter, size in bytes of recorded voice before compression // gain => output parameter, gain value which must be passed to decoder // outbuf_raw => output optional parameter, pointer to the raw sound data making up the compressed chunk // outbuf_size_raw => output optional parameter, size of the outbuf_raw buffer // // NOTE: function converts voice data into compressed format void rtvoice_get_data(unsigned char **outbuf, int *size, double * /*gain*/) { int max_size __UNUSED, raw_size; max_size = dscap_max_buffersize(); *outbuf=NULL; raw_size = dscap_get_raw_data(Rtv_capture_raw_buffer, Rtv_capture_raw_buffer_size); // TODO: compress voice data // *gain = Rtv_code_info.Gain; *size = raw_size; *outbuf = Rtv_capture_raw_buffer; } ///////////////////////////////////////////////////////////////////////////////////////////////// // DECODE/PLAYBACK ///////////////////////////////////////////////////////////////////////////////////////////////// // uncompress the data into PCM format void rtvoice_uncompress(unsigned char *data_in, int size_in, double /*gain*/, unsigned char * /*data_out*/, int /*size_out*/) { if ( (data_in == NULL) || (size_in <= 0) ) { return; } } // Close down the real-time voice playback system void rtvoice_close_playback() { if ( Rtv_playback_uncompressed_buffer ) { vm_free(Rtv_playback_uncompressed_buffer); Rtv_playback_uncompressed_buffer=NULL; } Rtv_playback_inited=0; } // Clear out the Rtv_output_buffers[] array void rtvoice_reset_out_buffers() { int i; for ( i=0; i success // !0 => failure, playback not possible int rtvoice_init_playback() { rtv_format *rtvf=NULL; if ( !Rtv_playback_inited ) { rtvoice_reset_out_buffers(); Rtv_playback_format=0; rtvf = &Rtv_formats[Rtv_playback_format]; if ( Rtv_playback_uncompressed_buffer ) { vm_free(Rtv_playback_uncompressed_buffer); Rtv_playback_uncompressed_buffer=NULL; } Rtv_playback_uncompressed_buffer_size = rtvf->frequency * (RTV_BUFFER_TIME) * fl2i(rtvf->bits_per_sample/8.0f); Rtv_playback_uncompressed_buffer = (unsigned char*)vm_malloc(Rtv_playback_uncompressed_buffer_size); Assert(Rtv_playback_uncompressed_buffer); Rtv_playback_inited=1; } return 0; } int rtvoice_find_free_output_buffer() { int i; for ( i=0; ifrequency, rtvf->bits_per_sample, 1, RTV_BUFFER_TIME); if ( Rtv_output_buffers[index].ds_handle == -1 ) { return -1; } return index; } void rtvoice_stop_playback(int index) { Assert(index >=0 && index < MAX_RTV_OUT_BUFFERS); if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) { if ( Rtv_output_buffers[index].ds_handle != -1 ) { ds_stop_easy(Rtv_output_buffers[index].ds_handle); } } } void rtvoice_stop_playback_all() { int i; for ( i = 0; i < MAX_RTV_OUT_BUFFERS; i++ ) { rtvoice_stop_playback(i); } } // Close a stream that was opened for real-time voice output void rtvoice_free_playback_buffer(int index) { Assert(index >=0 && index < MAX_RTV_OUT_BUFFERS); if ( Rtv_output_buffers[index].flags & RTV_OUT_FLAG_USED ) { Rtv_output_buffers[index].flags=0; if ( Rtv_output_buffers[index].ds_handle != -1 ) { ds_stop_easy(Rtv_output_buffers[index].ds_handle); ds_unload_buffer(Rtv_output_buffers[index].ds_handle); } Rtv_output_buffers[index].ds_handle=-1; } } // Play sound data // exit: >=0 => handle to playing sound // -1 => error, voice not played sound_handle rtvoice_play(int index, unsigned char* data, int size) { int ds_handle; ds_handle = Rtv_output_buffers[index].ds_handle; // Stop any currently playing voice output ds_stop_easy(ds_handle); // TODO: uncompress the data into PCM format // lock the data in if ( ds_lock_data(ds_handle, data, size) ) { return sound_handle::invalid(); } // play the voice EnhancedSoundData enhanced_sound_data(SND_ENHANCED_PRIORITY_MUST_PLAY, MAX_RTV_CHANNELS); return ds_play(ds_handle, -100, DS_MUST_PLAY, &enhanced_sound_data, Master_voice_volume, 0, 0); }