| 7 |
|
{ |
| 8 |
|
#region Private data |
| 9 |
|
private bool convert_to_PCM; |
| 10 |
+ |
private bool do_pc_demo_test; |
| 11 |
|
|
| 12 |
|
private const int fcc_RIFF = 0x46464952; |
| 13 |
|
private const int fcc_WAVE = 0x45564157; |
| 86 |
|
}; |
| 87 |
|
#endregion |
| 88 |
|
|
| 89 |
< |
public WavExporter(InstanceFileManager fileManager, string outputDirPath, bool convertToPCM = false) |
| 89 |
> |
public WavExporter(InstanceFileManager fileManager, string outputDirPath, bool convertToPCM = false, bool noDemo = false) |
| 90 |
|
: base(fileManager, outputDirPath) |
| 91 |
|
{ |
| 92 |
|
convert_to_PCM = convertToPCM; |
| 93 |
+ |
do_pc_demo_test = !noDemo; |
| 94 |
|
} |
| 95 |
|
|
| 96 |
|
private static void ClampToRange(ref int value, int lower, int upper) |
| 148 |
|
|
| 149 |
|
protected override void ExportInstance(InstanceDescriptor descriptor) |
| 150 |
|
{ |
| 151 |
< |
var sound = SoundData.Read(descriptor); |
| 151 |
> |
var sound = SoundData.Read(descriptor, do_pc_demo_test); |
| 152 |
|
|
| 153 |
|
using (var stream = File.Create(Path.Combine(OutputDirPath, descriptor.FullName + ".wav"))) |
| 154 |
|
using (var writer = new BinaryWriter(stream)) |
| 155 |
|
{ |
| 156 |
< |
var blockSizeADPCM = 512 * sound.ChannelCount * sound.SampleRate / 22050; |
| 155 |
< |
int wholeBlocks = sound.Data.Length / blockSizeADPCM; |
| 156 |
< |
int leftoverBytes = sound.Data.Length - (wholeBlocks * blockSizeADPCM); |
| 157 |
< |
int leftoverSamples = 8 * (leftoverBytes - 7 * sound.ChannelCount) |
| 158 |
< |
/ 4 / sound.ChannelCount + 2; // 4 bits per sample |
| 159 |
< |
int paddingBytes = 0; |
| 160 |
< |
if (leftoverBytes > 0) // incomplete trailing block |
| 161 |
< |
paddingBytes = blockSizeADPCM - leftoverBytes; |
| 162 |
< |
var samplesPerBlock = 2 + (blockSizeADPCM - sound.ChannelCount * 7) * 8 / sound.ChannelCount / 4; |
| 163 |
< |
|
| 164 |
< |
Int32 sampleCount = sampleCount = wholeBlocks * samplesPerBlock + leftoverSamples; |
| 165 |
< |
|
| 156 |
> |
int blockSizeADPCM, samplesPerBlock, sampleCount, paddingBytes = 0; |
| 157 |
|
if (sound.IsIMA4) // IMA4 ADPCM format |
| 158 |
|
{ |
| 159 |
|
blockSizeADPCM = 34 * sound.ChannelCount; |
| 160 |
|
samplesPerBlock = 64; |
| 161 |
|
sampleCount = (sound.Data.Length / blockSizeADPCM) * samplesPerBlock; |
| 162 |
|
} |
| 163 |
< |
|
| 163 |
> |
else |
| 164 |
> |
{ |
| 165 |
> |
blockSizeADPCM = sound.BlockAlignment; |
| 166 |
> |
int wholeBlocks = sound.Data.Length / blockSizeADPCM; |
| 167 |
> |
int leftoverBytes = sound.Data.Length - (wholeBlocks * blockSizeADPCM); |
| 168 |
> |
int leftoverSamples = 0; |
| 169 |
> |
if (leftoverBytes > 7 * sound.ChannelCount) |
| 170 |
> |
leftoverSamples = 2 + (leftoverBytes - 7 * sound.ChannelCount) |
| 171 |
> |
* 8 / sound.BitsPerSample / sound.ChannelCount; |
| 172 |
> |
else |
| 173 |
> |
Console.Error.WriteLine("Improper trailing bytes/samples!"); |
| 174 |
> |
if (leftoverBytes > 0) // incomplete trailing block |
| 175 |
> |
paddingBytes = blockSizeADPCM - leftoverBytes; |
| 176 |
> |
samplesPerBlock = 2 + (blockSizeADPCM - sound.ChannelCount * 7) * 8 / sound.ChannelCount / sound.BitsPerSample; |
| 177 |
> |
sampleCount = wholeBlocks * samplesPerBlock + leftoverSamples; |
| 178 |
> |
} |
| 179 |
|
if (!convert_to_PCM) |
| 180 |
|
{ |
| 181 |
|
if (sound.IsIMA4) |
| 182 |
|
{ |
| 183 |
< |
throw new NotSupportedException("Transcoding from Mac/demo ADPCM to PC ADPCM not supported! Please use -extract:pcm"); |
| 183 |
> |
throw new NotSupportedException("Transcoding from IMA4 ADPCM (Mac) to MS ADPCM (PC) not supported! Please use -extract:pcm"); |
| 184 |
|
} |
| 185 |
|
var format = (byte[])formatTemplate_ADPCM.Clone(); |
| 186 |
|
var fact = (byte[])factTemplate.Clone(); // needed for ADPCM (to specify the actual sample count) |
| 276 |
|
if (predictorL > 32767) predictorL -= 65536; |
| 277 |
|
} |
| 278 |
|
stepIndexL = headerLoL & 0x7f; |
| 279 |
+ |
if (stepIndexL > 88) |
| 280 |
+ |
Console.WriteLine("Block {0} (L): Initial IMA4 step index is {1}, clamping to 88.", block, stepIndexL); |
| 281 |
+ |
ClampToRange(ref stepIndexL, 0, 88); |
| 282 |
+ |
|
| 283 |
|
for (int b = 0; b < 32; b++) |
| 284 |
|
{ |
| 285 |
|
Byte nibblesL = sound.Data[pos++]; |
| 300 |
|
if (predictorR > 32767) predictorR -= 65536; |
| 301 |
|
} |
| 302 |
|
stepIndexR = headerLoR & 0x7f; |
| 303 |
+ |
if (stepIndexR > 88) |
| 304 |
+ |
Console.WriteLine("Block {0} (R): Initial IMA4 step index is {1}, clamping to 88.", block, stepIndexR); |
| 305 |
+ |
ClampToRange(ref stepIndexR, 0, 88); |
| 306 |
|
|
| 307 |
|
for (int b = 0; b < 32; b++) |
| 308 |
|
{ |
| 372 |
|
samplesR[iSampleR++] = sample1R; |
| 373 |
|
} |
| 374 |
|
} |
| 375 |
< |
// read pair of nibbles |
| 376 |
< |
Byte nibbles = sound.Data[pos++]; |
| 377 |
< |
Byte nibbleHi = (Byte)(nibbles >> 4); |
| 378 |
< |
Byte nibbleLo = (Byte)(nibbles & 0xF); |
| 379 |
< |
samplesL[iSampleL++] = NibbletoSampleMSADPCM(ref sample1L, ref sample2L, ref deltaL, pred_indexL, nibbleHi); |
| 380 |
< |
if (sound.ChannelCount == 2) |
| 381 |
< |
samplesR[iSampleR++] = NibbletoSampleMSADPCM(ref sample1R, ref sample2R, ref deltaR, pred_indexR, nibbleLo); |
| 382 |
< |
else |
| 383 |
< |
samplesL[iSampleL++] = NibbletoSampleMSADPCM(ref sample1L, ref sample2L, ref deltaL, pred_indexL, nibbleLo); |
| 375 |
> |
else // read pair of nibbles |
| 376 |
> |
{ |
| 377 |
> |
Byte nibbles = sound.Data[pos++]; |
| 378 |
> |
Byte nibbleHi = (Byte)(nibbles >> 4); |
| 379 |
> |
Byte nibbleLo = (Byte)(nibbles & 0xF); |
| 380 |
> |
samplesL[iSampleL++] = NibbletoSampleMSADPCM(ref sample1L, ref sample2L, ref deltaL, pred_indexL, nibbleHi); |
| 381 |
> |
if (sound.ChannelCount == 2) |
| 382 |
> |
samplesR[iSampleR++] = NibbletoSampleMSADPCM(ref sample1R, ref sample2R, ref deltaR, pred_indexR, nibbleLo); |
| 383 |
> |
else |
| 384 |
> |
samplesL[iSampleL++] = NibbletoSampleMSADPCM(ref sample1L, ref sample2L, ref deltaL, pred_indexL, nibbleLo); |
| 385 |
> |
} |
| 386 |
|
} |
| 387 |
|
} |
| 388 |
|
writer.Write(fcc_data); |