-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ff46f73
commit 1c73f60
Showing
28 changed files
with
568 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using BenchmarkDotNet.Attributes; | ||
|
||
namespace benchmarks { | ||
public class CastingValues { | ||
private const int n = 100000000; | ||
|
||
[Benchmark] | ||
public unsafe void ViaPointer() { | ||
for (var i = 0; i < n; i++) { | ||
ulong value = 123456; | ||
double castedValue = *(double*) (&value); | ||
} | ||
} | ||
|
||
[Benchmark] | ||
public void ViaBitConverter() { | ||
for (var i = 0; i < n; i++) { | ||
ulong value = 123456; | ||
double castedValue = BitConverter.UInt64BitsToDouble(value); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
FinModelUtility/Fin/Fin/src/audio/io/exporters/AudioExporterInterfaces.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using fin.io; | ||
|
||
namespace fin.audio.io.exporters { | ||
public interface IAudioExporter { | ||
void ExportAudio(IAudioBuffer<short> audioBuffer, ISystemFile outputFile); | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
FinModelUtility/Fin/Fin/src/audio/io/exporters/ogg/OggAudioExporter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
using System; | ||
using System.IO; | ||
|
||
using fin.io; | ||
using fin.util.asserts; | ||
|
||
using OggVorbisEncoder; | ||
|
||
namespace fin.audio.io.exporters.ogg { | ||
/// <summary> | ||
/// Based on example at: | ||
/// https://github.com/SteveLillis/.NET-Ogg-Vorbis-Encoder/blob/master/OggVorbisEncoder.Example/Encoder.cs | ||
/// </summary> | ||
public class OggAudioExporter : IAudioExporter { | ||
private const int WRITE_BUFFER_SIZE = 512; | ||
|
||
public void ExportAudio(IAudioBuffer<short> audioBuffer, | ||
ISystemFile outputFile) { | ||
Asserts.SequenceEqual(".ogg", outputFile.FileType.ToLower()); | ||
|
||
using var outputData = outputFile.OpenWrite(); | ||
|
||
var channelCount = audioBuffer.AudioChannelsType switch { | ||
AudioChannelsType.MONO => 1, | ||
AudioChannelsType.STEREO => 2, | ||
}; | ||
|
||
var oggStream = new OggStream(new Random().Next()); | ||
|
||
// ========================================================= | ||
// HEADER | ||
// ========================================================= | ||
// Vorbis streams begin with three headers; the initial header (with | ||
// most of the codec setup parameters) which is mandated by the Ogg | ||
// bitstream spec. The second header holds any comment fields. The | ||
// third header holds the bitstream codebook. | ||
var info = VorbisInfo.InitVariableBitRate(channelCount, | ||
audioBuffer.Frequency, | ||
1f); | ||
var infoPacket = HeaderPacketBuilder.BuildInfoPacket(info); | ||
var commentsPacket | ||
= HeaderPacketBuilder.BuildCommentsPacket(new Comments()); | ||
var booksPacket = HeaderPacketBuilder.BuildBooksPacket(info); | ||
|
||
oggStream.PacketIn(infoPacket); | ||
oggStream.PacketIn(commentsPacket); | ||
oggStream.PacketIn(booksPacket); | ||
|
||
// Flush to force audio data onto its own page per the spec | ||
FlushPages_(oggStream, outputData, true); | ||
|
||
// ========================================================= | ||
// BODY (Audio Data) | ||
// ========================================================= | ||
var processingState = ProcessingState.Create(info); | ||
|
||
var lengthInSamples = audioBuffer.LengthInSamples; | ||
|
||
var floatSamples = new float[channelCount][]; | ||
for (var c = 0; c < channelCount; ++c) { | ||
var channelSamples = floatSamples[c] = new float[lengthInSamples]; | ||
|
||
var channel = channelCount switch { | ||
1 => AudioChannelType.MONO, | ||
2 => c switch { | ||
0 => AudioChannelType.STEREO_LEFT, | ||
1 => AudioChannelType.STEREO_RIGHT | ||
} | ||
}; | ||
|
||
for (var i = 0; i < lengthInSamples; ++i) { | ||
var shortSample = audioBuffer.GetPcm(channel, i); | ||
channelSamples[i] = (shortSample / (1f * short.MaxValue)); | ||
} | ||
} | ||
|
||
for (int readIndex = 0; | ||
readIndex <= lengthInSamples; | ||
readIndex += WRITE_BUFFER_SIZE) { | ||
if (readIndex == lengthInSamples) { | ||
processingState.WriteEndOfStream(); | ||
} else { | ||
var writeLength | ||
= Math.Min(lengthInSamples - readIndex, WRITE_BUFFER_SIZE); | ||
processingState.WriteData(floatSamples, writeLength, readIndex); | ||
} | ||
|
||
while (!oggStream.Finished && | ||
processingState.PacketOut(out OggPacket packet)) { | ||
oggStream.PacketIn(packet); | ||
|
||
FlushPages_(oggStream, outputData, false); | ||
} | ||
} | ||
|
||
FlushPages_(oggStream, outputData, true); | ||
} | ||
|
||
private static void FlushPages_(OggStream oggStream, | ||
Stream output, | ||
bool force) { | ||
while (oggStream.PageOut(out OggPage page, force)) { | ||
output.Write(page.Header, 0, page.Header.Length); | ||
output.Write(page.Body, 0, page.Body.Length); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using fin.io; | ||
using fin.io.bundles; | ||
|
||
namespace fin.testing { | ||
public abstract class BGoldenTests<TFileBundle> | ||
where TFileBundle : IFileBundle { | ||
public abstract TFileBundle GetFileBundleFromDirectory( | ||
IFileHierarchyDirectory directory); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
|
||
using CommunityToolkit.HighPerformance; | ||
|
||
using fin.io; | ||
using fin.util.asserts; | ||
using fin.util.strings; | ||
|
||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
namespace fin.testing { | ||
public static class GoldenAssert { | ||
private const string TMP_NAME = "tmp"; | ||
|
||
public static ISystemDirectory GetRootGoldensDirectory( | ||
Assembly executingAssembly) { | ||
var assemblyName = | ||
executingAssembly.ManifestModule.Name.SubstringUpTo(".dll"); | ||
|
||
var executingAssemblyDll = new FinFile(executingAssembly.Location); | ||
var executingAssemblyDir = executingAssemblyDll.AssertGetParent(); | ||
|
||
var currentDir = executingAssemblyDir; | ||
while (currentDir.Name != assemblyName) { | ||
currentDir = currentDir.AssertGetParent(); | ||
} | ||
|
||
Assert.IsNotNull(currentDir); | ||
|
||
var gloTestsDir = currentDir; | ||
var goldensDirectory = gloTestsDir.AssertGetExistingSubdir("goldens"); | ||
|
||
return goldensDirectory; | ||
} | ||
|
||
public static IEnumerable<IFileHierarchyDirectory> GetGoldenDirectories( | ||
ISystemDirectory rootGoldenDirectory) { | ||
var hierarchy = FileHierarchy.From(rootGoldenDirectory); | ||
return hierarchy.Root.GetExistingSubdirs() | ||
.Where( | ||
subdir => subdir.Name != TMP_NAME); | ||
} | ||
|
||
public static IEnumerable<IFileHierarchyDirectory> | ||
GetGoldenInputDirectories(ISystemDirectory rootGoldenDirectory) | ||
=> GetGoldenDirectories(rootGoldenDirectory) | ||
.Select(subdir => subdir.AssertGetExistingSubdir("input")); | ||
|
||
public static void RunInTestDirectory( | ||
IFileHierarchyDirectory goldenSubdir, | ||
Action<ISystemDirectory> handler) { | ||
var tmpDirectory = goldenSubdir.Impl.GetOrCreateSubdir(TMP_NAME); | ||
tmpDirectory.DeleteContents(); | ||
|
||
try { | ||
handler(tmpDirectory); | ||
} finally { | ||
tmpDirectory.DeleteContents(); | ||
tmpDirectory.Delete(); | ||
} | ||
} | ||
|
||
public static void AssertFilesInDirectoriesAreIdentical( | ||
IReadOnlyTreeDirectory lhs, | ||
IReadOnlyTreeDirectory rhs) { | ||
var lhsFiles = lhs.GetExistingFiles() | ||
.ToDictionary(file => file.Name); | ||
var rhsFiles = rhs.GetExistingFiles() | ||
.ToDictionary(file => file.Name); | ||
|
||
Assert.IsTrue(lhsFiles.Keys.ToHashSet() | ||
.SetEquals(rhsFiles.Keys.ToHashSet())); | ||
|
||
foreach (var (name, lhsFile) in lhsFiles) { | ||
var rhsFile = rhsFiles[name]; | ||
try { | ||
AssertFilesAreIdentical_(lhsFile, rhsFile); | ||
} catch (Exception ex) { | ||
throw new Exception($"Found a change in file {name}: ", ex); | ||
} | ||
} | ||
} | ||
|
||
private static void AssertFilesAreIdentical_( | ||
IReadOnlyTreeFile lhs, | ||
IReadOnlyTreeFile rhs) { | ||
using var lhsStream = lhs.OpenRead(); | ||
using var rhsStream = rhs.OpenRead(); | ||
|
||
Assert.AreEqual(lhsStream.Length, rhsStream.Length); | ||
|
||
var bytesToRead = sizeof(long); | ||
int iterations = | ||
(int) Math.Ceiling((double) lhsStream.Length / bytesToRead); | ||
|
||
long lhsLong = 0; | ||
long rhsLong = 0; | ||
|
||
var lhsSpan = new Span<long>(ref lhsLong).AsBytes(); | ||
var rhsSpan = new Span<long>(ref rhsLong).AsBytes(); | ||
|
||
for (int i = 0; i < iterations; i++) { | ||
lhsStream.Read(lhsSpan); | ||
rhsStream.Read(rhsSpan); | ||
|
||
if (lhsLong != rhsLong) { | ||
Asserts.Fail( | ||
$"Files with name \"{lhs.Name}\" are different around byte #: {i * bytesToRead}"); | ||
} | ||
} | ||
} | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
FinModelUtility/Fin/Fin/src/testing/audio/AudioGoldenAssert.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
|
||
using fin.audio.io; | ||
using fin.audio.io.exporters.ogg; | ||
using fin.audio.io.importers; | ||
using fin.model.io.exporters; | ||
using fin.model.io.exporters.assimp.indirect; | ||
using fin.io; | ||
using fin.testing.audio.stubbed; | ||
|
||
namespace fin.testing.audio { | ||
public static class AudioGoldenAssert { | ||
public static IEnumerable<TAudioBundle> GetGoldenAudioBundles<TAudioBundle>( | ||
ISystemDirectory rootGoldenDirectory, | ||
Func<IFileHierarchyDirectory, TAudioBundle> | ||
gatherAudioBundleFromInputDirectory) | ||
where TAudioBundle : IAudioFileBundle { | ||
foreach (var goldenSubdir in | ||
GoldenAssert.GetGoldenDirectories(rootGoldenDirectory)) { | ||
var inputDirectory = goldenSubdir.AssertGetExistingSubdir("input"); | ||
var audioBundle = gatherAudioBundleFromInputDirectory(inputDirectory); | ||
|
||
yield return audioBundle; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Asserts model goldens. Assumes that directories will be stored as the following: | ||
/// | ||
/// - {goldenDirectory} | ||
/// - {goldenName1} | ||
/// - input | ||
/// - {raw golden files} | ||
/// - output | ||
/// - {exported files} | ||
/// - {goldenName2} | ||
/// ... | ||
/// </summary> | ||
public static void AssertExportGoldens<TAudioBundle>( | ||
ISystemDirectory rootGoldenDirectory, | ||
IAudioImporter<TAudioBundle> audioImporter, | ||
Func<IFileHierarchyDirectory, TAudioBundle> | ||
gatherAudioBundleFromInputDirectory) | ||
where TAudioBundle : IAudioFileBundle { | ||
foreach (var goldenSubdir in | ||
GoldenAssert.GetGoldenDirectories(rootGoldenDirectory)) { | ||
AudioGoldenAssert.AssertGolden(goldenSubdir, | ||
audioImporter, | ||
gatherAudioBundleFromInputDirectory); | ||
} | ||
} | ||
|
||
private static string EXTENSION = ".ogg"; | ||
|
||
public static void AssertGolden<TAudioBundle>( | ||
IFileHierarchyDirectory goldenSubdir, | ||
IAudioImporter<TAudioBundle> audioImporter, | ||
Func<IFileHierarchyDirectory, TAudioBundle> | ||
gatherAudioBundleFromInputDirectory) | ||
where TAudioBundle : IAudioFileBundle { | ||
using var audioManager = new StubbedAudioManager(); | ||
|
||
var inputDirectory = goldenSubdir.AssertGetExistingSubdir("input"); | ||
var audioBundle = gatherAudioBundleFromInputDirectory(inputDirectory); | ||
|
||
var outputDirectory = goldenSubdir.AssertGetExistingSubdir("output"); | ||
var hasGoldenExport = | ||
outputDirectory.GetFilesWithFileType(EXTENSION).Any(); | ||
|
||
GoldenAssert.RunInTestDirectory( | ||
goldenSubdir, | ||
tmpDirectory => { | ||
var targetDirectory = | ||
hasGoldenExport ? tmpDirectory : outputDirectory.Impl; | ||
|
||
var audioBuffer | ||
= audioImporter.ImportAudio(audioManager, audioBundle); | ||
new OggAudioExporter() | ||
.ExportAudio( | ||
audioBuffer, | ||
new FinFile( | ||
Path.Combine(targetDirectory.FullPath, | ||
$"{audioBundle.MainFile.NameWithoutExtension}{EXTENSION}"))); | ||
|
||
if (hasGoldenExport) { | ||
GoldenAssert.AssertFilesInDirectoriesAreIdentical( | ||
tmpDirectory, | ||
outputDirectory.Impl); | ||
} | ||
}); | ||
} | ||
} | ||
} |
Oops, something went wrong.