using Amphora;
using System;
using System.IO;
using static StbVorbisSharp.StbVorbis;
namespace Susurrus.UnitTests
{
public static class TestSounds
{
public static Stream OpenFixture(string name)
{
Stream returnMe =
typeof(TestSounds).Assembly.GetManifestResourceStream("Susurrus.UnitTests.Fixtures." + name);
if (returnMe == null)
{
throw new InvalidOperationException($"There's no embedded fixture called {name}.");
}
return returnMe;
}
public static byte[] LoadFixture(string name)
{
using (Stream stream = OpenFixture(name))
using (MemoryStream copy = new MemoryStream())
{
stream.CopyTo(copy);
return copy.ToArray();
}
}
public static int LastPageStart(byte[] file)
{
for (int start = file.Length - 4; start >= 0; --start)
{
if (file[start] == 'O' && file[start + 1] == 'g' && file[start + 2] == 'g' && file[start + 3] == 'S')
{
return start;
}
}
return -1;
}
///
/// Rewrites the checksum of the Ogg page at to match its current bytes.
///
public static void FixPageChecksum(byte[] file, int pageStart)
{
int segmentCount = file[pageStart + 26];
int pageSize = 27 + segmentCount;
for (int i = 0; i < segmentCount; ++i)
{
pageSize += file[pageStart + 27 + i];
}
uint checksum = OggPages.Checksum(file, pageStart, pageSize);
Array.Copy(BitConverter.GetBytes(checksum), 0, file, pageStart + 22, 4);
}
///
/// Returns the start of the Ogg page that holds the byte at , reading the file's pages
/// from its first.
///
public static int PageStartHolding(byte[] file, int index)
{
int start = 0;
while (OggPages.TryRead(file, start, file.Length, out OggPage page) && start + page.Size <= index)
{
start += page.Size;
}
return start;
}
///
/// Builds an Ogg page with the given header fields and a patterned body of bytes.
///
public static byte[] MakePage(long granule, byte flags, byte[] lacing, int bodySize)
{
byte[] returnMe = new byte[OggPages.HeaderSize + lacing.Length + bodySize];
Array.Copy(System.Text.Encoding.ASCII.GetBytes("OggS"), returnMe, 4);
returnMe[5] = flags;
Array.Copy(BitConverter.GetBytes(granule), 0, returnMe, 6, 8);
returnMe[26] = (byte)lacing.Length;
Array.Copy(lacing, 0, returnMe, OggPages.HeaderSize, lacing.Length);
for (int i = 0; i < bodySize; ++i)
{
returnMe[OggPages.HeaderSize + lacing.Length + i] = (byte)(i % 251);
}
FixPageChecksum(returnMe, 0);
return returnMe;
}
public static FailureKind KindOf(StatusOr result)
{
return result.Status.Kind;
}
///
/// Returns the error code of an Ogg failure, or -1 for anything else, so a test comparing it fails either way.
///
public static STBVorbisError OggErrorOf(StatusOr result)
{
Status status = result.Status;
if (status.Kind != SoundFailures.OggFailed)
{
return (STBVorbisError)(-1);
}
return SoundFailures.OggFailed.Read(in status);
}
public static short SampleAt(DecodedSound sound, int frame, int channel)
{
int index = (frame * sound.ChannelCount + channel) * sizeof(short);
return (short)(sound.Samples[index] | sound.Samples[index + 1] << 8);
}
public static StatusOr DecodeWav(byte[] file)
{
return WavDecoder.Decode(new ByteView(file));
}
public static StatusOr DecodeOgg(byte[] file)
{
return OggDecoder.Decode(new ByteView(file));
}
public static StatusOr DecodeAny(byte[] file)
{
return SoundDecoder.Decode(new ByteView(file));
}
public static ByteView Silence(int byteCount)
{
return new ByteView(new byte[byteCount]);
}
public static byte[] BytesOf(ByteView view)
{
byte[] returnMe = new byte[view.Length];
view.CopyTo(returnMe, destinationIndex: 0);
return returnMe;
}
}
}