Skip to content

Commit 9916221

Browse files
authored
Fix generating audio files that are a teeny tiny bit longer than the original (#16)
* Respect SampleCount metadata when rebuilding ogg files Since Vorbis blocks are always one of two possible sizes, a Vorbis container needs some other field to hold the actual number of samples in the audio file. This change essentially copies the SampleCount FSB metadata field to the ogg output. * Respect SampleCount metadata when rebuilding wav files FSB PCM files appear to be padded with zeroes, meaning the correct number of samples to copy is the SampleCount value instead of just the rest of the file.
1 parent e860b35 commit 9916221

7 files changed

Lines changed: 43 additions & 23 deletions

File tree

Fmod5Sharp.Tests/Fmod5Sharp.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
<ItemGroup>
1919
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
20+
<PackageReference Include="NAudio.Core" Version="2.1.0"/>
21+
<PackageReference Include="NVorbis" Version="0.10.5" />
2022
<PackageReference Include="xunit" Version="2.4.1" />
2123
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
2224
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

Fmod5Sharp.Tests/Fmod5SharpPcmTests.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using Fmod5Sharp.CodecRebuilders;
22
using Fmod5Sharp.FmodTypes;
3+
using NAudio.Wave;
4+
using System.IO;
35
using Xunit;
46

57
namespace Fmod5Sharp.Tests
@@ -23,9 +25,11 @@ public void PcmFilesCanBeReconstructed()
2325

2426
var fsb = FsbLoader.LoadFsbFromByteArray(rawData);
2527

26-
var wavFile = FmodPcmRebuilder.Rebuild(fsb.Samples[0], fsb.Header.AudioType);
27-
28-
Assert.NotEmpty(wavFile);
28+
var sample = fsb.Samples[0];
29+
30+
var wavFile = FmodPcmRebuilder.Rebuild(sample, fsb.Header.AudioType);
31+
32+
Assert.Equal(sample.Metadata.SampleCount, new WaveFileReader(new MemoryStream(wavFile)).SampleCount);
2933
}
3034
}
3135
}

Fmod5Sharp.Tests/Fmod5SharpVorbisTests.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using Fmod5Sharp.CodecRebuilders;
2+
using Fmod5Sharp.FmodTypes;
3+
using NVorbis;
4+
using System.IO;
25
using Xunit;
36

47
namespace Fmod5Sharp.Tests
@@ -27,9 +30,7 @@ public void VorbisAudioCanBeRestoredWithoutExceptions()
2730

2831
var oggBytes = FmodVorbisRebuilder.RebuildOggFile(sample);
2932

30-
Assert.NotEmpty(oggBytes);
31-
32-
//Cannot assert on length output bytes because it changes with the version of libvorbis you use.
33+
CheckSampleCount(sample, oggBytes);
3334
}
3435

3536
[Fact]
@@ -43,7 +44,7 @@ public void LongerFilesWorkToo()
4344

4445
var oggBytes = FmodVorbisRebuilder.RebuildOggFile(sample);
4546

46-
Assert.NotEmpty(oggBytes);
47+
CheckSampleCount(sample, oggBytes);
4748
}
4849

4950
[Fact]
@@ -57,7 +58,7 @@ public void PreviouslyUnrecoverableVorbisFilesWorkWithOurCustomRebuilder()
5758

5859
var oggBytes = FmodVorbisRebuilder.RebuildOggFile(sample);
5960

60-
Assert.NotEmpty(oggBytes);
61+
CheckSampleCount(sample, oggBytes);
6162
}
6263

6364
[Fact]
@@ -71,7 +72,12 @@ public void VorbisFilesThatPreviouslyThrewExceptionsDoNot()
7172

7273
var oggBytes = FmodVorbisRebuilder.RebuildOggFile(sample);
7374

74-
Assert.NotEmpty(oggBytes);
75+
CheckSampleCount(sample, oggBytes);
76+
}
77+
78+
private bool CheckSampleCount(FmodSample sample, byte[] oggBytes)
79+
{
80+
Assert.Equal(sample.Metadata.SampleCount, new VorbisReader(new MemoryStream(oggBytes)).TotalSamples);
7581
}
7682
}
7783
}

Fmod5Sharp/CodecRebuilders/FmodPcmRebuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using System.IO;
2-
using Fmod5Sharp.FmodTypes;
1+
using Fmod5Sharp.FmodTypes;
32
using NAudio.Wave;
3+
using System.IO;
44

55
namespace Fmod5Sharp.CodecRebuilders
66
{
@@ -28,7 +28,7 @@ public static byte[] Rebuild(FmodSample sample, FmodAudioType type)
2828
using var stream = new MemoryStream();
2929
using var writer = new WaveFileWriter(stream, format);
3030

31-
writer.Write(sample.SampleBytes, 0, sample.SampleBytes.Length);
31+
writer.Write(sample.SampleBytes, 0, numChannels * width * (int) sample.Metadata.SampleCount);
3232

3333
return stream.GetBuffer();
3434
}

Fmod5Sharp/CodecRebuilders/FmodVorbisRebuilder.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public static byte[] RebuildOggFile(FmodSample sample)
6060

6161
oggStream.FlushAndCopyTo(outputStream, true);
6262

63-
CopySampleData(vorbisData, sample.SampleBytes, oggStream, outputStream);
63+
CopySampleData(vorbisData, sample.Metadata.SampleCount, sample.SampleBytes, oggStream, outputStream);
6464

6565
return outputStream.ToArray();
6666
}
@@ -130,16 +130,16 @@ private static OggPacket BuildCommentPacket(string vendor)
130130
return new(oggMs.ToArray(), false, 0, 1);
131131
}
132132

133-
private static void CopySampleData(FmodVorbisData vorbisData, byte[] sampleBytes, OggStream oggStream, Stream outputStream)
133+
private static void CopySampleData(FmodVorbisData vorbisData, uint sampleCount, byte[] sampleBytes, OggStream oggStream, Stream outputStream)
134134
{
135135
using var inputStream = new MemoryStream(sampleBytes);
136136
using var inputReader = new BinaryReader(inputStream);
137137

138138
ReadSamplePackets(inputReader, out var packetLength, out var packets);
139139

140140
var packetNum = 1;
141-
var granulePos = 0;
142-
var previousBlockSize = 0;
141+
uint granulePos = 0;
142+
uint previousBlockSize = 0;
143143

144144
var finalPacketNum = packetLength.Count - 1;
145145

@@ -158,16 +158,24 @@ private static void CopySampleData(FmodVorbisData vorbisData, byte[] sampleBytes
158158
else
159159
granulePos += (blockSize + previousBlockSize) / 4;
160160

161+
//Make sure to end the stream once we reach the sample count in the metadata.
162+
if (granulePos > sampleCount)
163+
granulePos = sampleCount;
164+
161165
//Set previous block size
162166
previousBlockSize = blockSize;
163167

164168
//Write the packet to the stream
165-
oggStream.PacketIn(new(packet, isLast, granulePos, packetNum));
169+
oggStream.PacketIn(new(packet, isLast, (int) granulePos, packetNum));
166170
oggStream.FlushAndCopyTo(outputStream, isLast);
171+
172+
//Early end to the stream if this is going past the sample count in the metadata.
173+
if (granulePos == sampleCount)
174+
break;
167175
}
168176
}
169177

170-
private static void ReadSamplePackets(BinaryReader inputReader, out List<int> packetLengths, out List<byte[]> packets)
178+
private static void ReadSamplePackets(BinaryReader inputReader, out List<ushort> packetLengths, out List<byte[]> packets)
171179
{
172180
packetLengths = new();
173181
packets = new();

Fmod5Sharp/FmodTypes/FmodSampleMetadata.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ public class FmodSampleMetadata : IBinaryReadable
99
{
1010
internal bool HasAnyChunks;
1111
internal uint FrequencyId;
12-
internal ulong DataOffset;
12+
internal uint DataOffset;
1313
internal List<FmodSampleChunk> Chunks = new();
1414
internal int NumChannels;
1515

1616
public bool IsStereo;
17-
public ulong SampleCount;
17+
public uint SampleCount;
1818

1919
public int Frequency => FsbLoader.Frequencies.TryGetValue(FrequencyId, out var actualFrequency) ? actualFrequency : (int)FrequencyId; //If set by FREQUENCY chunk, id is actual frequency
2020
public uint Channels => (uint)NumChannels;
@@ -38,8 +38,8 @@ void IBinaryReadable.Read(BinaryReader reader)
3838

3939
IsStereo = NumChannels == 2;
4040

41-
DataOffset = encoded.Bits(7, 27) * 32;
42-
SampleCount = encoded.Bits(34, 30);
41+
DataOffset = (uint) encoded.Bits(7, 27) * 32;
42+
SampleCount = (uint) encoded.Bits(34, 30);
4343
}
4444
}
4545
}

Fmod5Sharp/Util/FmodVorbisData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ internal void InitBlockFlags()
5959
}).ToArray();
6060
}
6161

62-
public int GetPacketBlockSize(byte[] packetBytes)
62+
public uint GetPacketBlockSize(byte[] packetBytes)
6363
{
6464
var bitStream = new BitStream(packetBytes);
6565

0 commit comments

Comments
 (0)