summaryrefslogtreecommitdiff
path: root/engine/voice_codecs/frame_encoder/voice_codec_frame.cpp
blob: 6de3dcd2cd759104521d8689085584e8ec3b87b6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#include "audio/public/ivoicecodec.h"
#include <string.h>
#include "tier0/dbg.h"
#include "iframeencoder.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"


#ifndef min
	#define min(a,b) ((a) < (b) ? (a) : (b))
#endif





// VoiceCodec_Frame can be used to wrap a frame encoder for the engine. As it gets sound data, it will queue it
// until it has enough for a frame, then it will compress it. Same thing for decompression.
class VoiceCodec_Frame : public IVoiceCodec
{
public:
	enum {MAX_FRAMEBUFFER_SAMPLES=1024};

					VoiceCodec_Frame(IFrameEncoder *pEncoder)
					{
						m_nEncodeBufferSamples = 0;
						m_nRawBytes = m_nRawSamples = m_nEncodedBytes = 0;
						m_pFrameEncoder = pEncoder;
					}
		
	virtual			~VoiceCodec_Frame()
	{
		if(m_pFrameEncoder)
			m_pFrameEncoder->Release();
	}
	
	virtual bool	Init( int quality )
	{
		if(m_pFrameEncoder && m_pFrameEncoder->Init(quality, m_nRawBytes, m_nEncodedBytes))
		{
			m_nRawSamples = m_nRawBytes >> 1;
			Assert(m_nRawBytes <= MAX_FRAMEBUFFER_SAMPLES && m_nEncodedBytes <= MAX_FRAMEBUFFER_SAMPLES);
			return true;
		}
		else
		{
			if(m_pFrameEncoder)
				m_pFrameEncoder->Release();

			m_pFrameEncoder = NULL;
			return false;
		}
	}

	virtual void	Release()
	{
		delete this;
	}

	virtual int		Compress(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal)
	{
		if(!m_pFrameEncoder)
			return 0;

		const short *pUncompressed = (const short*)pUncompressedBytes;

		int nCompressedBytes = 0;
		while((nSamples + m_nEncodeBufferSamples) >= m_nRawSamples && (maxCompressedBytes - nCompressedBytes) >= m_nEncodedBytes)
		{
			// Get the data block out.
			short samples[MAX_FRAMEBUFFER_SAMPLES];
			memcpy(samples, m_EncodeBuffer, m_nEncodeBufferSamples*BYTES_PER_SAMPLE);
			memcpy(&samples[m_nEncodeBufferSamples], pUncompressed, (m_nRawSamples - m_nEncodeBufferSamples) * BYTES_PER_SAMPLE);
			nSamples -= m_nRawSamples - m_nEncodeBufferSamples;
			pUncompressed += m_nRawSamples - m_nEncodeBufferSamples;
			m_nEncodeBufferSamples = 0;
			
			// Compress it.
			m_pFrameEncoder->EncodeFrame((const char*)samples, &pCompressed[nCompressedBytes]);
			nCompressedBytes += m_nEncodedBytes;
		}

		// Store the remaining samples.
		int nNewSamples = min(nSamples, min(m_nRawSamples-m_nEncodeBufferSamples, m_nRawSamples));
		if(nNewSamples)
		{
			memcpy(&m_EncodeBuffer[m_nEncodeBufferSamples], &pUncompressed[nSamples - nNewSamples], nNewSamples*BYTES_PER_SAMPLE);
			m_nEncodeBufferSamples += nNewSamples;
		}

		// If it must get the last data, just pad with zeros..
		if(bFinal && m_nEncodeBufferSamples && (maxCompressedBytes - nCompressedBytes) >= m_nEncodedBytes)
		{
			memset(&m_EncodeBuffer[m_nEncodeBufferSamples], 0, (m_nRawSamples - m_nEncodeBufferSamples) * BYTES_PER_SAMPLE);
			m_pFrameEncoder->EncodeFrame((const char*)m_EncodeBuffer, &pCompressed[nCompressedBytes]);
			nCompressedBytes += m_nEncodedBytes;
			m_nEncodeBufferSamples = 0;
		}

		return nCompressedBytes;
	}

	virtual int		Decompress(const char *pCompressed, int compressedBytes, char *pUncompressed, int maxUncompressedBytes)
	{
		if(!m_pFrameEncoder)
			return 0;

		Assert((compressedBytes % m_nEncodedBytes) == 0);
		int nDecompressedBytes = 0;
		int curCompressedByte = 0;
		while((compressedBytes - curCompressedByte)  >= m_nEncodedBytes && (maxUncompressedBytes - nDecompressedBytes) >= m_nRawBytes)
		{
			m_pFrameEncoder->DecodeFrame( pCompressed ? &pCompressed[curCompressedByte] : NULL, &pUncompressed[nDecompressedBytes]);
			curCompressedByte += m_nEncodedBytes;
			nDecompressedBytes += m_nRawBytes;
		}

		return nDecompressedBytes / BYTES_PER_SAMPLE;
	}

	virtual bool	ResetState()
	{
		if(m_pFrameEncoder)
			return m_pFrameEncoder->ResetState();
		else
			return false;
	}


public:
	// The codec encodes and decodes samples in fixed-size blocks, so we queue up uncompressed and decompressed data 
	// until we have blocks large enough to give to the codec.
	short				m_EncodeBuffer[MAX_FRAMEBUFFER_SAMPLES];
	int					m_nEncodeBufferSamples;

	IFrameEncoder		*m_pFrameEncoder;
	int					m_nRawBytes, m_nRawSamples;
	int					m_nEncodedBytes;
};


IVoiceCodec* CreateVoiceCodec_Frame(IFrameEncoder *pEncoder)
{
	return new VoiceCodec_Frame(pEncoder);
}