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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
/** (c) Nullsoft, Inc. C O N F I D E N T I A L
** Filename:
** Project:
** Description:
** Author: Ben Allison benski@nullsoft.com
** Created:
**/
#include "main.h"
#include "Resampler.h"
#include <ks.h>
#include <ksmedia.h>
static int GetChannelMask(size_t channels)
{
switch(channels)
{
case 1: return KSAUDIO_SPEAKER_MONO;
case 2: return KSAUDIO_SPEAKER_STEREO;
case 4: return KSAUDIO_SPEAKER_SURROUND;
case 6: return KSAUDIO_SPEAKER_5POINT1;
case 8: return KSAUDIO_SPEAKER_7POINT1;
default: return 0;
}
}
static void FillWFX_float(WAVEFORMATIEEEFLOATEX *format, size_t channels, size_t bits, size_t sampleRate)
{
ZeroMemory(format, sizeof(WAVEFORMATIEEEFLOATEX));
size_t padded_bits = (bits + 7) & (~7);
format->Format.nChannels = (WORD)channels;
format->Format.nSamplesPerSec = (DWORD)sampleRate;
format->Format.nAvgBytesPerSec = (DWORD)(sampleRate * channels * (padded_bits >> 3));
format->Format.nBlockAlign = (WORD)(channels * (padded_bits >> 3));
format->Format.wBitsPerSample = (WORD)padded_bits;
format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format->Format.cbSize = sizeof(WAVEFORMATIEEEFLOATEX) - sizeof(WAVEFORMATEX);
format->Samples.wValidBitsPerSample = (WORD)bits;
format->dwChannelMask = GetChannelMask(channels);
format->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
static void FillWFX(WAVEFORMATEXTENSIBLE *format, size_t channels, size_t bits, size_t sampleRate)
{
ZeroMemory(format, sizeof(WAVEFORMATEXTENSIBLE));
size_t padded_bits = (bits + 7) & (~7);
format->Format.nChannels = (WORD)channels;
format->Format.nSamplesPerSec = (DWORD)sampleRate;
format->Format.nAvgBytesPerSec = (DWORD)(sampleRate * channels * (padded_bits >> 3));
format->Format.nBlockAlign = (WORD)(channels * (padded_bits >> 3));
format->Format.wBitsPerSample = (WORD)padded_bits;
if (channels > 2 || padded_bits != bits)
{
format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
format->Samples.wValidBitsPerSample = (WORD)bits;
format->dwChannelMask = GetChannelMask(channels);
format->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else
{
format->Format.wFormatTag = WAVE_FORMAT_PCM;
format->Format.cbSize = 0;
}
}
Resampler::Resampler(size_t inputBits, size_t inputChannels, size_t inputSampleRate,
size_t outputBits, size_t outputChannels, size_t outputSampleRate, bool floatingPoint)
: sizeFactor(1.), hStream(0), buffer(0), eof(false)
{
if (floatingPoint)
{
WAVEFORMATIEEEFLOATEX inputFormat;
FillWFX_float(&inputFormat, inputChannels, inputBits, inputSampleRate);
WAVEFORMATIEEEFLOATEX outputFormat;
FillWFX_float(&outputFormat, outputChannels, outputBits, outputSampleRate);
acmStreamOpen(&hStream, 0, &inputFormat.Format, &outputFormat.Format, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME);
}
else
{
WAVEFORMATEXTENSIBLE inputFormat = {0};
FillWFX(&inputFormat, inputChannels, inputBits, inputSampleRate);
WAVEFORMATEXTENSIBLE outputFormat = {0};
FillWFX(&outputFormat, outputChannels, outputBits, outputSampleRate);
acmStreamOpen(&hStream, 0, &inputFormat.Format, &outputFormat.Format, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME);
}
if (!hStream)
{
// error - what should we do?
return ;
}
bufferAlloc = MulDiv(1024, (int) (outputBits * outputChannels), 8); // TODO: use acmStreamSize
buffer = (__int8 *)calloc(bufferAlloc, sizeof(__int8));
bufferValid = 0;
sizeFactor = (outputChannels * outputBits * outputSampleRate) * (inputChannels * inputBits * inputSampleRate);
}
Resampler::~Resampler()
{
if (hStream)
{
acmStreamClose(hStream, 0);
}
free(buffer);
}
size_t Resampler::UseInternalBuffer(void *output, size_t outputBytes)
{
if (bufferValid)
{
size_t writeSize = min(outputBytes, bufferValid);
memcpy(output, buffer, writeSize);
if (writeSize)
memmove(buffer, buffer + writeSize, bufferValid - writeSize);
bufferValid -= writeSize;
return writeSize;
}
else
return 0;
}
void Resampler::Flush()
{
eof = true;
}
size_t Resampler::Convert(void *input, size_t *inputBytes, void *output, size_t outputBytes)
{
size_t x = UseInternalBuffer(output, outputBytes);
if (x)
return x;
ACMSTREAMHEADER streamHeader;
ZeroMemory(&streamHeader, sizeof(streamHeader));
streamHeader.cbStruct = sizeof(streamHeader);
streamHeader.pbSrc = reinterpret_cast<LPBYTE>(input);
streamHeader.cbSrcLength = (DWORD)*inputBytes;
streamHeader.pbDst = reinterpret_cast<LPBYTE>(buffer);
streamHeader.cbDstLength = (DWORD)bufferAlloc;
if (acmStreamPrepareHeader(hStream, &streamHeader, 0))
return 0; // m_error = 1;
if (input && *inputBytes && !eof)
acmStreamConvert(hStream, &streamHeader, ACM_STREAMCONVERTF_BLOCKALIGN);
else
acmStreamConvert(hStream, &streamHeader, ACM_STREAMCONVERTF_END);
*inputBytes -= streamHeader.cbSrcLengthUsed;
bufferValid = streamHeader.cbDstLengthUsed;
acmStreamUnprepareHeader(hStream, &streamHeader, 0);
return UseInternalBuffer(output, outputBytes);
}
bool Resampler::OK()
{
if (hStream)
return true;
else
return false;
}
|