//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include "quakedef.h" #include "iframeencoder.h" #include "interface.h" #include "milesbase.h" #include "tier0/dbg.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" void Con_Printf( const char *pMsg, ... ) { } class FrameEncoder_Miles : public IFrameEncoder { protected: virtual ~FrameEncoder_Miles(); public: FrameEncoder_Miles(); virtual bool Init(int quality, int &rawFrameSize, int &encodedFrameSize); virtual void Release(); virtual void EncodeFrame(const char *pUncompressed, char *pCompressed); virtual void DecodeFrame(const char *pCompressed, char *pDecompressed); virtual bool ResetState(); public: void Shutdown(); static S32 AILCALLBACK EncodeStreamCB( UINTa user, // User value passed to ASI_open_stream() void *dest, // Location to which stream data should be copied by app S32 bytes_requested, // # of bytes requested by ASI codec S32 offset // If not -1, application should seek to this point in stream ); static S32 AILCALLBACK DecodeStreamCB( UINTa user, // User value passed to ASI_open_stream() void *dest, // Location to which stream data should be copied by app S32 bytes_requested, // # of bytes requested by ASI codec S32 offset // If not -1, application should seek to this point in stream ); void FigureOutFrameSizes(); private: // Encoder stuff. ASISTRUCT m_Encoder; // Decoder stuff. ASISTRUCT m_Decoder; // Destination for encoding and decoding. const char *m_pSrc; int m_SrcLen; int m_CurSrcPos; // Frame sizes.. int m_nRawBytes; int m_nEncodedBytes; }; // ------------------------------------------------------------------------ // // Helper functions. // ------------------------------------------------------------------------ // void Convert16UnsignedToSigned(short *pDest, int nSamples) { for(int i=0; i < nSamples; i++) { int val = *((unsigned short*)&pDest[i]) - (1 << 15); pDest[i] = (short)val; } } void Convert16SignedToUnsigned(short *pDest, int nSamples) { for(int i=0; i < nSamples; i++) { int val = *((short*)&pDest[i]) + (1 << 15); *((unsigned short*)&pDest[i]) = (unsigned short)val; } } // ------------------------------------------------------------------------ // // FrameEncoder_Miles functions. // ------------------------------------------------------------------------ // FrameEncoder_Miles::FrameEncoder_Miles() { } FrameEncoder_Miles::~FrameEncoder_Miles() { Shutdown(); } bool FrameEncoder_Miles::Init(int quality, int &rawFrameSize, int &encodedFrameSize) { Shutdown(); // This tells what protocol we're using. C8 suffix[128] = ".v12"; // (.v12, .v24, .v29, or .raw) // encoder converts from RAW to v12 if ( !m_Encoder.Init( (void *)this, ".RAW", suffix, &FrameEncoder_Miles::EncodeStreamCB ) ) { Con_Printf("(FrameEncoder_Miles): Can't initialize ASI encoder.\n"); Shutdown(); return false; } // decoder converts from v12 to RAW if ( !m_Decoder.Init( (void *)this, suffix, ".RAW", &FrameEncoder_Miles::DecodeStreamCB ) ) { Con_Printf("(FrameEncoder_Miles): Can't initialize ASI decoder.\n"); Shutdown(); return false; } FigureOutFrameSizes(); // Output.. rawFrameSize = m_nRawBytes * 2; // They'll be using 16-bit samples and we're quantizing to 8-bit. encodedFrameSize = m_nEncodedBytes; return true; } void FrameEncoder_Miles::Release() { delete this; } void FrameEncoder_Miles::EncodeFrame(const char *pUncompressedBytes, char *pCompressed) { char samples[1024]; if(!m_Encoder.IsActive() || m_nRawBytes > sizeof(samples)) return; const short *pUncompressed = (const short*)pUncompressedBytes; for(int i=0; i < m_nRawBytes; i++) samples[i] = (char)(pUncompressed[i] >> 8); m_pSrc = samples; m_SrcLen = m_nRawBytes; m_CurSrcPos = 0; U32 len = m_Encoder.Process( pCompressed, m_nEncodedBytes ); if ( len != (U32)m_nEncodedBytes ) { Assert(0); } } void FrameEncoder_Miles::DecodeFrame(const char *pCompressed, char *pDecompressed) { if(!m_Decoder.IsActive()) return; m_pSrc = pCompressed; m_SrcLen = m_nEncodedBytes; m_CurSrcPos = 0; U32 outputSize = m_nRawBytes*2; U32 len = m_Decoder.Process( pDecompressed, outputSize ); if (len != outputSize) { Assert(0); } } void FrameEncoder_Miles::Shutdown() { m_Decoder.Shutdown(); m_Encoder.Shutdown(); } bool FrameEncoder_Miles::ResetState() { if(!m_Decoder.IsActive() || !m_Encoder.IsActive()) return true; for(int i=0; i < 2; i++) { char data[2048], compressed[2048]; memset(data, 0, sizeof(data)); m_pSrc = data; m_SrcLen = m_nRawBytes; m_CurSrcPos = 0; U32 len = m_Encoder.Process( compressed, m_nEncodedBytes ); if ( len != (U32)m_nEncodedBytes ) { Assert(0); } m_pSrc = compressed; m_SrcLen = m_nEncodedBytes; m_CurSrcPos = 0; m_Decoder.Process( data, m_nRawBytes * 2 ); } // Encode and decode a couple frames of zeros. return true; } S32 AILCALLBACK FrameEncoder_Miles::EncodeStreamCB( UINTa user, // User value passed to ASI_open_stream() void *dest, // Location to which stream data should be copied by app S32 bytes_requested, // # of bytes requested by ASI codec S32 offset // If not -1, application should seek to this point in stream ) { FrameEncoder_Miles *pThis = (FrameEncoder_Miles*)user; Assert(pThis && offset == -1); // Figure out how many samples we can safely give it. int maxSamples = pThis->m_SrcLen - pThis->m_CurSrcPos; int samplesToGive = MIN(maxSamples, bytes_requested/2); // Convert to 16-bit signed mono. short *pOut = (short*)dest; for(int i=0; i < samplesToGive; i++) { pOut[i] = pThis->m_pSrc[pThis->m_CurSrcPos+i] << 8; } pThis->m_CurSrcPos += samplesToGive; return samplesToGive * 2; } S32 AILCALLBACK FrameEncoder_Miles::DecodeStreamCB( UINTa user, // User value passed to ASI_open_stream() void *dest, // Location to which stream data should be copied by app S32 bytes_requested, // # of bytes requested by ASI codec S32 offset // If not -1, application should seek to this point in stream ) { FrameEncoder_Miles *pThis = (FrameEncoder_Miles*)user; Assert(pThis && offset == -1); int maxBytes = pThis->m_SrcLen - pThis->m_CurSrcPos; int bytesToGive = MIN(maxBytes, bytes_requested); memcpy(dest, &pThis->m_pSrc[pThis->m_CurSrcPos], bytesToGive); pThis->m_CurSrcPos += bytesToGive; return bytesToGive; } void FrameEncoder_Miles::FigureOutFrameSizes() { // Figure out the frame sizes. It is probably not prudent in general to assume fixed frame sizes with Miles codecs // but it works with the voxware codec right now and simplifies things a lot. m_nRawBytes = (int)m_Encoder.GetProperty( m_Encoder.INPUT_BLOCK_SIZE ); char uncompressed[1024]; char compressed[1024]; Assert(m_nRawBytes <= sizeof(uncompressed)); m_pSrc = uncompressed; m_SrcLen = m_nRawBytes; m_CurSrcPos = 0; m_nEncodedBytes = (int)m_Encoder.Process( compressed, sizeof(compressed) ); } class IVoiceCodec; extern IVoiceCodec* CreateVoiceCodec_Frame(IFrameEncoder *pEncoder); void* CreateVoiceCodec_Miles() { IFrameEncoder *pEncoder = new FrameEncoder_Miles; if(!pEncoder) return NULL; return CreateVoiceCodec_Frame(pEncoder); } EXPOSE_INTERFACE_FN(CreateVoiceCodec_Miles, IVoiceCodec, "vaudio_miles")