Skip to content

Commit d9aec6f

Browse files
committed
Use custom vorbis decoder (based on @lab313ru's work)
This also allows us to target netstandard2 by removing the dep on OggVorbisSharp
1 parent 1261397 commit d9aec6f

15 files changed

Lines changed: 980 additions & 442 deletions

Fmod5Sharp.Tests/Fmod5ImaAdPcmTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,19 @@ public void ImaAdPcmBanksCanBeRebuilt()
2727

2828
Assert.NotEmpty(bytes);
2929
}
30+
31+
[Fact]
32+
public void XboxImaAdPcmBanksCanBeRebuilt()
33+
{
34+
var rawData = this.LoadResource("xbox_imaad.fsb");
35+
36+
var fsb = FsbLoader.LoadFsbFromByteArray(rawData);
37+
38+
Assert.Equal(FmodAudioType.IMAADPCM, fsb.Header.AudioType);
39+
40+
var bytes = FmodImaAdPcmRebuilder.Rebuild(fsb.Samples[0]);
41+
42+
Assert.NotEmpty(bytes);
43+
}
3044
}
3145
}

Fmod5Sharp.Tests/Fmod5Sharp.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
<EmbeddedResource Include="TestResources\imaadpcm_short.fsb" />
4646
<None Remove="TestResources\imaadpcm_long.fsb" />
4747
<EmbeddedResource Include="TestResources\imaadpcm_long.fsb" />
48+
<None Remove="TestResources\broken_imaad.fsb" />
49+
<None Remove="TestResources\previously_unrecoverable_vorbis.fsb" />
50+
<EmbeddedResource Include="TestResources\previously_unrecoverable_vorbis.fsb" />
4851
</ItemGroup>
4952

5053
</Project>

Fmod5Sharp.Tests/Fmod5SharpVorbisTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,19 @@ public void LongerFilesWorkToo()
4545

4646
Assert.NotEmpty(oggBytes);
4747
}
48+
49+
[Fact]
50+
public void PreviouslyUnrecoverableVorbisFilesWorkWithOurCustomRebuilder()
51+
{
52+
var rawData = this.LoadResource("previously_unrecoverable_vorbis.fsb");
53+
54+
var samples = FsbLoader.LoadFsbFromByteArray(rawData).Samples;
55+
56+
var sample = samples[0];
57+
58+
var oggBytes = FmodVorbisRebuilder.RebuildOggFile(sample);
59+
60+
Assert.NotEmpty(oggBytes);
61+
}
4862
}
4963
}
Binary file not shown.
81.8 KB
Binary file not shown.

Fmod5Sharp/ChunkData/DspCoefficientsBlockData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void Read(BinaryReader reader, uint expectedSize)
2727
{
2828
//We can't use ReadInt16 here because BinaryReader is little-endian, and FSB5 encodes this data big-endian
2929
//So instead, read 2 bytes, reverse, then convert to short.
30-
ChannelData[ch].Add(BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray()));
30+
ChannelData[ch].Add(BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0));
3131
}
3232
//Extra 0xE = 14 bytes
3333
reader.ReadInt64();

Fmod5Sharp/Fmod5Sharp.csproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
54
<Nullable>enable</Nullable>
65
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
76
<Configurations>Release;Debug</Configurations>
@@ -13,16 +12,21 @@
1312
<Description>Decoder for FMOD 5 sound banks (FSB files)</Description>
1413
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1514
<PackageLicenseExpression>MIT</PackageLicenseExpression>
15+
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
16+
<LangVersion>10</LangVersion>
1617
</PropertyGroup>
1718

1819
<ItemGroup>
1920
<PackageReference Include="NAudio.Core" Version="2.0.0" />
20-
<PackageReference Include="OggVorbisSharp" Version="1.0.0" />
21+
<PackageReference Include="OggVorbisEncoder" Version="1.2.0" />
22+
<PackageReference Include="rubendal.BitStream" Version="1.2.0" />
23+
<PackageReference Include="System.Text.Json" Version="6.0.5" />
2124
</ItemGroup>
2225

2326
<ItemGroup>
2427
<None Remove="FmodVorbis\vorbis_headers.json" />
25-
<EmbeddedResource Include="FmodVorbis\vorbis_headers.json" />
28+
<None Remove="FmodVorbis\vorbis_headers_converted.json" />
29+
<EmbeddedResource Include="FmodVorbis\vorbis_headers_converted.json" />
2630
</ItemGroup>
2731

2832
</Project>

Fmod5Sharp/FmodImaAdPcmRebuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ private static void ExpandNibble(MemoryStream stream, long byteOffset, int nibbl
4343
if ((sampleNibble & 8) != 0) delta = -delta;
4444
sampleDecoded += delta;
4545

46-
hist = Math.Clamp(sampleDecoded, short.MinValue, short.MaxValue);
46+
hist = Utils.Clamp((short)sampleDecoded, short.MinValue, short.MaxValue);
4747
stepIndex += IMA_IndexTable[sampleNibble];
48-
stepIndex = Math.Clamp(stepIndex, 0, 88);
48+
stepIndex = Utils.Clamp((short)stepIndex, 0, 88);
4949
}
5050

5151
private static short[] GetPcm(FmodSample sample)
@@ -77,7 +77,7 @@ private static short[] GetPcm(FmodSample sample)
7777
stream.Seek(headerIndex + 2, SeekOrigin.Begin);
7878
int stepIndex = reader.ReadByte();
7979

80-
stepIndex = Math.Clamp(stepIndex, 0, 88);
80+
stepIndex = Utils.Clamp((short)stepIndex, 0, 88);
8181
ret[sampleIndex] = (short)hist;
8282
sampleIndex += numChannels;
8383

Fmod5Sharp/FmodPcmRebuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static byte[] Rebuild(FmodSample sample, FmodAudioType type)
2727
using var stream = new MemoryStream();
2828
using var writer = new WaveFileWriter(stream, format);
2929

30-
writer.Write(sample.SampleBytes);
30+
writer.Write(sample.SampleBytes, 0, sample.SampleBytes.Length);
3131

3232
return stream.GetBuffer();
3333
}

Fmod5Sharp/FmodSample.cs

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,49 @@
44

55
namespace Fmod5Sharp
66
{
7-
public class FmodSample
8-
{
9-
public FmodSampleMetadata Metadata;
10-
public byte[] SampleBytes;
11-
internal FmodSoundBank? MyBank;
7+
public class FmodSample
8+
{
9+
public FmodSampleMetadata Metadata;
10+
public byte[] SampleBytes;
11+
internal FmodSoundBank? MyBank;
1212

13-
public FmodSample(FmodSampleMetadata metadata, byte[] sampleBytes)
14-
{
15-
Metadata = metadata;
16-
SampleBytes = sampleBytes;
17-
}
13+
public FmodSample(FmodSampleMetadata metadata, byte[] sampleBytes)
14+
{
15+
Metadata = metadata;
16+
SampleBytes = sampleBytes;
17+
}
1818

19+
#if NET6_0
1920
public bool RebuildAsStandardFileFormat([NotNullWhen(true)] out byte[]? data, [NotNullWhen(true)] out string? fileExtension)
20-
{
21-
switch(MyBank!.Header.AudioType)
22-
{
23-
case FmodAudioType.VORBIS:
24-
data = FmodVorbisRebuilder.RebuildOggFile(this);
25-
fileExtension = "ogg";
26-
return data.Length > 0;
27-
case FmodAudioType.PCM8:
28-
case FmodAudioType.PCM16:
29-
case FmodAudioType.PCM32:
30-
data = FmodPcmRebuilder.Rebuild(this, MyBank.Header.AudioType);
31-
fileExtension = "wav";
32-
return data.Length > 0;
33-
case FmodAudioType.GCADPCM:
34-
data = FmodGcadPcmRebuilder.Rebuild(this);
35-
fileExtension = "wav";
36-
return data.Length > 0;
37-
case FmodAudioType.IMAADPCM:
38-
data = FmodImaAdPcmRebuilder.Rebuild(this);
39-
fileExtension = "wav";
40-
return data.Length > 0;
41-
default:
42-
data = null;
43-
fileExtension = null;
44-
return false;
45-
}
46-
}
47-
}
21+
#else
22+
public bool RebuildAsStandardFileFormat(out byte[]? data, out string? fileExtension)
23+
#endif
24+
{
25+
switch (MyBank!.Header.AudioType)
26+
{
27+
case FmodAudioType.VORBIS:
28+
data = FmodVorbisRebuilder.RebuildOggFile(this);
29+
fileExtension = "ogg";
30+
return data.Length > 0;
31+
case FmodAudioType.PCM8:
32+
case FmodAudioType.PCM16:
33+
case FmodAudioType.PCM32:
34+
data = FmodPcmRebuilder.Rebuild(this, MyBank.Header.AudioType);
35+
fileExtension = "wav";
36+
return data.Length > 0;
37+
case FmodAudioType.GCADPCM:
38+
data = FmodGcadPcmRebuilder.Rebuild(this);
39+
fileExtension = "wav";
40+
return data.Length > 0;
41+
case FmodAudioType.IMAADPCM:
42+
data = FmodImaAdPcmRebuilder.Rebuild(this);
43+
fileExtension = "wav";
44+
return data.Length > 0;
45+
default:
46+
data = null;
47+
fileExtension = null;
48+
return false;
49+
}
50+
}
51+
}
4852
}

0 commit comments

Comments
 (0)