mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
462 lines
11 KiB
C++
462 lines
11 KiB
C++
/*
|
|
* 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 <windows.h>
|
|
#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<MAX_RTV_FORMATS; i++) {
|
|
if ( dscap_create_buffer(Rtv_formats[i].frequency, Rtv_formats[i].bits_per_sample, 1, RTV_BUFFER_TIME) == 0 ) {
|
|
dscap_release_buffer();
|
|
Rtv_recording_format=i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i == MAX_RTV_FORMATS ) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// input: qos => 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<MAX_RTV_OUT_BUFFERS; i++ ) {
|
|
Rtv_output_buffers[i].flags=0;
|
|
Rtv_output_buffers[i].ds_handle=-1;
|
|
}
|
|
}
|
|
|
|
// Init the playback portion of the real-time voice system
|
|
// exit: 0 => 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; i<MAX_RTV_OUT_BUFFERS; i++ ) {
|
|
if ( !(Rtv_output_buffers[i].flags & RTV_OUT_FLAG_USED) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i == MAX_RTV_OUT_BUFFERS ) {
|
|
return -1;
|
|
}
|
|
|
|
Rtv_output_buffers[i].flags |= RTV_OUT_FLAG_USED;
|
|
return i;
|
|
|
|
}
|
|
|
|
// Open a stream for real-time voice output
|
|
int rtvoice_create_playback_buffer()
|
|
{
|
|
int index;
|
|
rtv_format *rtvf=NULL;
|
|
|
|
rtvf = &Rtv_formats[Rtv_playback_format];
|
|
index = rtvoice_find_free_output_buffer();
|
|
|
|
if ( index == -1 ) {
|
|
Int3();
|
|
return -1;
|
|
}
|
|
|
|
Rtv_output_buffers[index].ds_handle = ds_create_buffer(rtvf->frequency, 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);
|
|
}
|
|
|