mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-20 04:09:16 +00:00
Hoist MOULOpenSourceClientPlugin/Plasma20/* to top level
to match H'uru layout and make patching/cherry-picking easier.
This commit is contained in:
714
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp
Normal file
714
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp
Normal file
@ -0,0 +1,714 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#include "hsTypes.h"
|
||||
#include "hsWindows.h"
|
||||
#include "hsTimer.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "al.h"
|
||||
#include "alc.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "speex.h"
|
||||
#include "speex_bits.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "plVoiceChat.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "../plAudible/plWinAudible.h"
|
||||
#include "../plNetMessage/plNetMessage.h"
|
||||
#include "../plPipeline/plPlates.h"
|
||||
#include "hsConfig.h"
|
||||
#include "../plAvatar/plAvatarMgr.h"
|
||||
#include "../plAvatar/plArmatureMod.h"
|
||||
#include "hsQuat.h"
|
||||
#include "../plAudioCore/plAudioCore.h"
|
||||
|
||||
// DEBUG for printing to the console
|
||||
#include "../plMessage/plConsoleMsg.h"
|
||||
#include "../plPipeline/plDebugText.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
#define MICROPHONE "ui_microphone.png"
|
||||
#define TALKING "ui_speaker.png"
|
||||
#define NUM_CHANNELS 1
|
||||
#define VOICE_STOP_MS 2000
|
||||
#define MAX_DATA_SIZE 1024 * 4 // 4 KB
|
||||
|
||||
hsBool plVoiceRecorder::fCompress = true;
|
||||
hsBool plVoiceRecorder::fRecording = true;
|
||||
hsBool plVoiceRecorder::fNetVoice = false;
|
||||
short plVoiceRecorder::fSampleRate = FREQUENCY;
|
||||
hsScalar plVoiceRecorder::fRecordThreshhold = 200.0f;
|
||||
hsBool plVoiceRecorder::fShowIcons = true;
|
||||
hsBool plVoiceRecorder::fMicAlwaysOpen = false;
|
||||
hsBool plVoicePlayer::fEnabled = true;
|
||||
|
||||
plVoiceRecorder::plVoiceRecorder()
|
||||
{
|
||||
plPlateManager::Instance().CreatePlate( &fDisabledIcon );
|
||||
fDisabledIcon->CreateFromResource( MICROPHONE );
|
||||
fDisabledIcon->SetPosition(-0.90, -0.90);
|
||||
fDisabledIcon->SetSize(0.0675, 0.09);
|
||||
fDisabledIcon->SetVisible(false);
|
||||
|
||||
plPlateManager::Instance().CreatePlate( &fTalkIcon );
|
||||
fTalkIcon->CreateFromResource( TALKING );
|
||||
fTalkIcon->SetPosition(-0.9,-0.9);
|
||||
fTalkIcon->SetSize(0.0675, 0.09);
|
||||
fTalkIcon->SetVisible(false);
|
||||
}
|
||||
|
||||
plVoiceRecorder::~plVoiceRecorder()
|
||||
{
|
||||
if(fDisabledIcon)
|
||||
plPlateManager::Instance().DestroyPlate( fDisabledIcon);
|
||||
fDisabledIcon = nil;
|
||||
|
||||
if (fTalkIcon)
|
||||
plPlateManager::Instance().DestroyPlate( fTalkIcon );
|
||||
fTalkIcon = nil;
|
||||
}
|
||||
|
||||
void plVoiceRecorder::IncreaseRecordingThreshhold()
|
||||
{
|
||||
fRecordThreshhold += (100 * hsTimer::GetDelSysSeconds());
|
||||
if (fRecordThreshhold >= 10000.0f)
|
||||
fRecordThreshhold = 10000.0f;
|
||||
|
||||
plDebugText &txt = plDebugText::Instance();
|
||||
char str[256];
|
||||
sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold);
|
||||
txt.DrawString(400,300,str);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::DecreaseRecordingThreshhold()
|
||||
{
|
||||
fRecordThreshhold -= (100 * hsTimer::GetDelSysSeconds());
|
||||
if (fRecordThreshhold <= 50.0f)
|
||||
fRecordThreshhold = 50.0f;
|
||||
|
||||
plDebugText &txt = plDebugText::Instance();
|
||||
char str[256];
|
||||
sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold);
|
||||
txt.DrawString(400,300,str);
|
||||
}
|
||||
|
||||
// Set the quality of speex encoder
|
||||
void plVoiceRecorder::SetQuality(int quality)
|
||||
{
|
||||
char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive";
|
||||
if(quality < 1 || quality > 10)
|
||||
{
|
||||
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str );
|
||||
plgDispatch::MsgSend( cMsg );
|
||||
return;
|
||||
}
|
||||
|
||||
if(plSpeex::GetInstance()->IsUsingVBR())
|
||||
{
|
||||
// Sets average bit rate between 4kb and 13kb
|
||||
int AverageBitrate = quality * 1000 + 3000;
|
||||
plSpeex::GetInstance()->SetABR(AverageBitrate);
|
||||
}
|
||||
else
|
||||
{
|
||||
plSpeex::GetInstance()->SetQuality(quality);
|
||||
}
|
||||
}
|
||||
|
||||
// toggle variable bit rate
|
||||
void plVoiceRecorder::SetVBR(bool vbr)
|
||||
{
|
||||
plSpeex::GetInstance()->VBR(vbr);
|
||||
SetQuality(plSpeex::GetInstance()->GetQuality()); // update proper quality param
|
||||
}
|
||||
|
||||
void plVoiceRecorder::SetComplexity(int c)
|
||||
{
|
||||
char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive";
|
||||
if(c < 1 || c > 10)
|
||||
{
|
||||
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str );
|
||||
plgDispatch::MsgSend( cMsg );
|
||||
return;
|
||||
}
|
||||
plSpeex::GetInstance()->SetComplexity((UInt8) c);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::SetENH(hsBool b)
|
||||
{
|
||||
plSpeex::GetInstance()->SetENH(b);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::SetMikeOpen(hsBool b)
|
||||
{
|
||||
ALCdevice *device = plgAudioSys::GetCaptureDevice();
|
||||
if (fRecording && device)
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
alcCaptureStart(device);
|
||||
}
|
||||
else
|
||||
{
|
||||
alcCaptureStop(device);
|
||||
}
|
||||
DrawTalkIcon(b);
|
||||
fMikeOpen = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawDisabledIcon(b); // voice recording is unavailable or disabled
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceRecorder::DrawDisabledIcon(hsBool b)
|
||||
{
|
||||
if (!fDisabledIcon)
|
||||
{
|
||||
// at least try and make one here...
|
||||
plPlateManager::Instance().CreatePlate( &fDisabledIcon );
|
||||
if (fDisabledIcon)
|
||||
{
|
||||
fDisabledIcon->CreateFromResource( MICROPHONE );
|
||||
fDisabledIcon->SetPosition(-0.90, -0.90);
|
||||
fDisabledIcon->SetSize(0.0675, 0.09);
|
||||
fDisabledIcon->SetVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (fDisabledIcon)
|
||||
fDisabledIcon->SetVisible(b);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::DrawTalkIcon(hsBool b)
|
||||
{
|
||||
if (!fTalkIcon)
|
||||
{
|
||||
plPlateManager::Instance().CreatePlate( &fTalkIcon );
|
||||
if (fTalkIcon)
|
||||
{ fTalkIcon->CreateFromResource( TALKING );
|
||||
fTalkIcon->SetPosition(-0.9,-0.9);
|
||||
fTalkIcon->SetSize(0.0675, 0.09);
|
||||
fTalkIcon->SetVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (fTalkIcon)
|
||||
{
|
||||
fTalkIcon->SetVisible(b);
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceRecorder::Update(double time)
|
||||
{
|
||||
if(!fRecording)
|
||||
return;
|
||||
|
||||
int EncoderFrameSize = plSpeex::GetInstance()->GetFrameSize();
|
||||
if(EncoderFrameSize == -1)
|
||||
return;
|
||||
|
||||
ALCdevice *captureDevice = plgAudioSys::GetCaptureDevice();
|
||||
if(!captureDevice)
|
||||
return;
|
||||
|
||||
unsigned minSamples = EncoderFrameSize * 10;
|
||||
|
||||
ALCint samples;
|
||||
alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples );
|
||||
|
||||
if (samples > 0)
|
||||
{
|
||||
if (samples >= minSamples)
|
||||
{
|
||||
int numFrames = (int)(samples / EncoderFrameSize); // the number of frames that have been captured
|
||||
int totalSamples = numFrames * EncoderFrameSize;
|
||||
|
||||
// cap uncompressed data
|
||||
if(totalSamples > MAX_DATA_SIZE)
|
||||
totalSamples = MAX_DATA_SIZE;
|
||||
|
||||
// convert to correct units:
|
||||
short *buffer = TRACKED_NEW short[totalSamples];
|
||||
|
||||
alcCaptureSamples(captureDevice, buffer, totalSamples);
|
||||
|
||||
if (!CompressionEnabled())
|
||||
{
|
||||
plNetMsgVoice pMsg;
|
||||
pMsg.SetNetProtocol(kNetProtocolCli2Game);
|
||||
pMsg.SetVoiceData((char *)buffer, totalSamples * sizeof(short));
|
||||
// set frame size here;
|
||||
pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
|
||||
//if (false) //plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice))
|
||||
// pMsg.SetBit(plNetMessage::kEchoBackToSender);
|
||||
plNetClientApp::GetInstance()->SendMsg(&pMsg);
|
||||
|
||||
}
|
||||
else // use the speex voice compression lib
|
||||
{
|
||||
UInt8 *packet = TRACKED_NEW UInt8[totalSamples]; // packet to send encoded data in
|
||||
int packedLength = 0; // the size of the packet that will be sent
|
||||
hsRAMStream ram; // ram stream to hold output data from speex
|
||||
UInt8 numFrames = totalSamples / EncoderFrameSize; // number of frames to be encoded
|
||||
|
||||
// encode the data using speex
|
||||
plSpeex::GetInstance()->Encode(buffer, numFrames, &packedLength, &ram);
|
||||
|
||||
if (packedLength)
|
||||
{
|
||||
// extract data from ram stream into packet
|
||||
ram.Rewind();
|
||||
ram.Read(packedLength, packet);
|
||||
plNetMsgVoice pMsg;
|
||||
pMsg.SetNetProtocol(kNetProtocolCli2Game);
|
||||
|
||||
pMsg.SetVoiceData((char *)packet, packedLength);
|
||||
pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
|
||||
pMsg.SetFlag(VOICE_ENCODED); // Set encoded flag
|
||||
pMsg.SetNumFrames(numFrames);
|
||||
if (plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice))
|
||||
pMsg.SetBit(plNetMessage::kEchoBackToSender);
|
||||
|
||||
plNetClientApp::GetInstance()->SendMsg(&pMsg);
|
||||
}
|
||||
delete[] packet;
|
||||
}
|
||||
delete[] buffer;
|
||||
}
|
||||
else if(!fMikeOpen)
|
||||
{
|
||||
short *buffer = TRACKED_NEW short[samples];
|
||||
// the mike has since closed, and there isn't enough data to meet our minimum, so throw this data out
|
||||
alcCaptureSamples(captureDevice, buffer, samples);
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plVoicePlayer::plVoicePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
plVoicePlayer::~plVoicePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void plVoicePlayer::PlaybackUncompressedVoiceMessage(void* data, unsigned size)
|
||||
{
|
||||
if(fEnabled)
|
||||
{
|
||||
if(!fSound.IsPlaying())
|
||||
{
|
||||
fSound.Play();
|
||||
}
|
||||
fSound.AddVoiceData(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
void plVoicePlayer::PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer)
|
||||
{
|
||||
if(fEnabled)
|
||||
{
|
||||
int numBytes; // the number of bytes that speex decompressed the data to.
|
||||
int bufferSize = numFramesInBuffer * plSpeex::GetInstance()->GetFrameSize();
|
||||
short *nBuff = TRACKED_NEW short[bufferSize];
|
||||
memset(nBuff, 0, bufferSize);
|
||||
|
||||
// Decode the encoded voice data using speex
|
||||
if(!plSpeex::GetInstance()->Decode((UInt8 *)data, size, numFramesInBuffer, &numBytes, nBuff))
|
||||
{
|
||||
delete[] nBuff;
|
||||
return;
|
||||
}
|
||||
|
||||
BYTE* newBuff;
|
||||
newBuff = (BYTE*)nBuff; // Convert to byte data
|
||||
PlaybackUncompressedVoiceMessage(newBuff, numBytes); // playback uncompressed data
|
||||
delete[] nBuff;
|
||||
}
|
||||
}
|
||||
|
||||
void plVoicePlayer::SetVelocity(const hsVector3 vel)
|
||||
{
|
||||
fSound.SetVelocity(vel);
|
||||
}
|
||||
|
||||
void plVoicePlayer::SetPosition(const hsPoint3 pos)
|
||||
{
|
||||
fSound.SetPosition(pos);
|
||||
}
|
||||
|
||||
void plVoicePlayer::SetOrientation(const hsPoint3 pos)
|
||||
{
|
||||
fSound.SetConeOrientation(pos.fX, pos.fY, pos.fZ);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* plVoiceSound
|
||||
*
|
||||
***/
|
||||
unsigned plVoiceSound::fCount = 0;
|
||||
|
||||
plVoiceSound::plVoiceSound()
|
||||
{
|
||||
fInnerCone = 90;
|
||||
fOuterCone = 240;
|
||||
fOuterVol = -2000;
|
||||
|
||||
fMinFalloff = 15;
|
||||
fMaxFalloff = 75;
|
||||
|
||||
fProperties = 0;
|
||||
fCurrVolume = 1.0;
|
||||
fDesiredVol = 1.0;
|
||||
|
||||
fPriority = 1;
|
||||
fType = plgAudioSys::kVoice;
|
||||
|
||||
fEAXSettings.SetRoomParams(-1200, -100, 0, 0);
|
||||
fLastUpdate = 0;
|
||||
|
||||
char keyName[32];
|
||||
StrPrintf(keyName, arrsize(keyName), "VoiceSound_%d", fCount);
|
||||
fCount++;
|
||||
hsgResMgr::ResMgr()->NewKey(keyName, this, plLocation::kGlobalFixedLoc);
|
||||
}
|
||||
|
||||
plVoiceSound::~plVoiceSound()
|
||||
{
|
||||
}
|
||||
|
||||
hsBool plVoiceSound::LoadSound( hsBool is3D )
|
||||
{
|
||||
if( fFailed )
|
||||
return false;
|
||||
if( !plgAudioSys::Active() || fDSoundBuffer )
|
||||
return false;
|
||||
|
||||
if( fPriority > plgAudioSys::GetPriorityCutoff() )
|
||||
return false; // Don't set the failed flag, just return
|
||||
|
||||
plWAVHeader header;
|
||||
header.fFormatTag = WAVE_FORMAT_PCM;
|
||||
header.fBitsPerSample = 16;
|
||||
header.fNumChannels = 1;
|
||||
header.fNumSamplesPerSec = FREQUENCY;
|
||||
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 2;
|
||||
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
|
||||
|
||||
fDSoundBuffer = TRACKED_NEW plDSoundBuffer(0, header, true, false, false, true);
|
||||
if(!fDSoundBuffer)
|
||||
return false;
|
||||
fDSoundBuffer->SetupVoiceSource();
|
||||
|
||||
IRefreshParams();
|
||||
IRefreshEAXSettings( true );
|
||||
fDSoundBuffer->SetScalarVolume(1.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void plVoiceSound::Play()
|
||||
{
|
||||
fPlaying = true;
|
||||
if( IWillBeAbleToPlay() )
|
||||
{
|
||||
IRefreshParams();
|
||||
SetVolume( fDesiredVol );
|
||||
IActuallyPlay();
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceSound::IDerivedActuallyPlay( void )
|
||||
{
|
||||
if( !fReallyPlaying )
|
||||
{
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceSound::AddVoiceData(void *data, unsigned bytes)
|
||||
{
|
||||
unsigned size;
|
||||
unsigned bufferId;
|
||||
if(!fDSoundBuffer)
|
||||
{
|
||||
if(!LoadSound(true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fDSoundBuffer->UnQueueVoiceBuffers(); // attempt to unque any buffers that have finished
|
||||
while(bytes > 0)
|
||||
{
|
||||
size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
|
||||
if(!fDSoundBuffer->GetAvailableBufferId(&bufferId))
|
||||
break; // if there isn't any room for the data, it is currently thrown out
|
||||
|
||||
fDSoundBuffer->VoiceFillBuffer(data, size, bufferId);
|
||||
bytes -= size;
|
||||
}
|
||||
fLastUpdate = hsTimer::GetMilliSeconds();
|
||||
}
|
||||
|
||||
void plVoiceSound::Update()
|
||||
{
|
||||
if(IsPlaying())
|
||||
{
|
||||
if((hsTimer::GetMilliSeconds() - fLastUpdate) > VOICE_STOP_MS)
|
||||
{
|
||||
Stop(); // terminating case for playback. Wait for x number of milliseconds, and stop.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceSound::IRefreshParams()
|
||||
{
|
||||
plSound::IRefreshParams();
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Speex Voice Encoding/Decoding
|
||||
*
|
||||
***/
|
||||
|
||||
plSpeex::plSpeex() :
|
||||
fBits(nil),
|
||||
fEncoderState(nil),
|
||||
fDecoderState(nil),
|
||||
fSampleRate(plVoiceRecorder::GetSampleRate()),
|
||||
fFrameSize(-1),
|
||||
fQuality(7),
|
||||
fVBR(true), // variable bit rate on
|
||||
fAverageBitrate(8000), // 8kb bitrate
|
||||
fComplexity(3),
|
||||
fENH(false),
|
||||
fInitialized(false)
|
||||
{
|
||||
fBits = TRACKED_NEW SpeexBits;
|
||||
Init(kNarrowband); // if no one initialized us initialize using a narrowband encoder
|
||||
}
|
||||
|
||||
plSpeex::~plSpeex()
|
||||
{
|
||||
Shutdown();
|
||||
delete fBits;
|
||||
fBits = nil;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Init(Mode mode)
|
||||
{
|
||||
int enh = 1;
|
||||
|
||||
// setup speex
|
||||
speex_bits_init(fBits);
|
||||
fBitsInit = true;
|
||||
|
||||
if(mode == kNarrowband)
|
||||
{
|
||||
fEncoderState = speex_encoder_init(&speex_nb_mode); // narrowband
|
||||
fDecoderState = speex_decoder_init(&speex_nb_mode);
|
||||
}
|
||||
else if(mode == kWideband)
|
||||
{
|
||||
fEncoderState = speex_encoder_init(&speex_wb_mode);
|
||||
fDecoderState = speex_decoder_init(&speex_wb_mode);
|
||||
}
|
||||
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_GET_FRAME_SIZE, &fFrameSize); // get frame size
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity); // 3
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_SAMPLING_RATE, &fSampleRate); // 8 khz
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR_QUALITY, &fQuality); // 7
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR); // use variable bit rate
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate); // default to 8kb
|
||||
|
||||
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH); // perceptual enhancement
|
||||
|
||||
fInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Shutdown()
|
||||
{
|
||||
//shutdown speex
|
||||
if(fDecoderState)
|
||||
{
|
||||
speex_decoder_destroy(fDecoderState);
|
||||
fDecoderState = nil;
|
||||
}
|
||||
|
||||
if(fEncoderState)
|
||||
{
|
||||
speex_encoder_destroy(fEncoderState);
|
||||
fEncoderState = nil;
|
||||
}
|
||||
|
||||
if(fBitsInit)
|
||||
{
|
||||
speex_bits_destroy(fBits);
|
||||
fBitsInit = false;
|
||||
}
|
||||
fInitialized = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out)
|
||||
{
|
||||
*packedLength = 0;
|
||||
|
||||
short *pData = data; // pointer to input data
|
||||
float *input = TRACKED_NEW float[fFrameSize]; // input to speex - used as am intermediate array since speex requires float data
|
||||
BYTE frameLength; // number of bytes speex compressed frame to
|
||||
BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds one frame of encoded data
|
||||
|
||||
// encode data
|
||||
for( int i = 0; i < numFrames; i++ )
|
||||
{
|
||||
// convert input data to floats
|
||||
for( int j = 0; j < fFrameSize; j++ )
|
||||
{
|
||||
input[j] = pData[j];
|
||||
}
|
||||
|
||||
speex_bits_reset(fBits); // reset bit structure
|
||||
|
||||
// encode data using speex
|
||||
speex_encode(fEncoderState, input, fBits);
|
||||
frameLength = speex_bits_write(fBits, (char *)frameData, fFrameSize);
|
||||
|
||||
// write data - length and bytes
|
||||
out->WriteSwap(frameLength);
|
||||
*packedLength += sizeof(frameLength); // add length of encoded frame
|
||||
out->Write(frameLength, frameData);
|
||||
*packedLength += frameLength; // update length
|
||||
|
||||
pData += fFrameSize; // move input pointer
|
||||
}
|
||||
|
||||
delete[] frameData;
|
||||
delete[] input;
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out)
|
||||
{
|
||||
if(!fInitialized) return false;
|
||||
*numOutputBytes = 0;
|
||||
|
||||
hsReadOnlyStream stream( size, data );
|
||||
float *speexOutput = TRACKED_NEW float[fFrameSize]; // holds output from speex
|
||||
short *pOut = out; // pointer to output short buffer
|
||||
|
||||
// create buffer for input data
|
||||
BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds the current frames data to be decoded
|
||||
BYTE frameLen; // holds the length of the current frame being decoded.
|
||||
|
||||
|
||||
// Decode data
|
||||
for (int i = 0; i < numFrames; i++)
|
||||
{
|
||||
stream.ReadSwap( &frameLen ); // read the length of the current frame to be decoded
|
||||
stream.Read( frameLen, frameData ); // read the data
|
||||
|
||||
memset(speexOutput, 0, fFrameSize * sizeof(float));
|
||||
speex_bits_read_from(fBits, (char *)frameData, frameLen); // give data to speex
|
||||
speex_decode(fDecoderState, fBits, speexOutput); // decode data
|
||||
|
||||
for(int j = 0; j < fFrameSize; j++)
|
||||
{
|
||||
pOut[j] = (short)(speexOutput[j]); // convert floats to shorts
|
||||
}
|
||||
|
||||
pOut += fFrameSize;
|
||||
}
|
||||
|
||||
delete[] frameData;
|
||||
delete[] speexOutput;
|
||||
|
||||
*numOutputBytes = (numFrames * fFrameSize) * sizeof(short); // length of decoded voice data(out) in bytes
|
||||
if(*numOutputBytes == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets variable bit rate on/off
|
||||
void plSpeex::VBR(hsBool b)
|
||||
{
|
||||
fVBR = b;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR);
|
||||
}
|
||||
|
||||
|
||||
// Sets the average bit rate
|
||||
void plSpeex::SetABR(UInt32 abr)
|
||||
{
|
||||
fAverageBitrate = abr;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate);
|
||||
}
|
||||
|
||||
// Sets the quality of encoding
|
||||
void plSpeex::SetQuality(UInt32 quality)
|
||||
{
|
||||
fQuality = quality;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_QUALITY, &fQuality);
|
||||
}
|
||||
|
||||
void plSpeex::SetENH(hsBool b)
|
||||
{
|
||||
fENH = b;
|
||||
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH);
|
||||
}
|
||||
|
||||
void plSpeex::SetComplexity(UInt8 c)
|
||||
{
|
||||
fComplexity = c;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity);
|
||||
}
|
Reference in New Issue
Block a user