Files

169 lines
4.8 KiB
C#

using System.Buffers.Binary;
namespace Lagrange.OneBot.Utility;
public static class AudioHelper
{
public enum AudioFormat
{
Unknown,
Wav,
Mp3,
SilkV3,
TenSilkV3,
Amr,
Ogg,
}
/// <summary>
/// Detect audio type
/// </summary>
/// <param name="data"></param>
/// <param name="type"></param>
/// <returns></returns>
[Obsolete("public static AudioFormat DetectAudio(byte[] data)")]
public static bool DetectAudio(byte[] data, out AudioFormat type)
{
return (type = DetectAudio(data)) != AudioFormat.Unknown;
}
// WAV
// R I F F W A V E f m t
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |52|49|46|46| | | | |57|41|56|45|66|6D|74|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// 0 4 8 12 15
private const ulong WAV_HEAD_LOWER = 0x5249464600000000UL;
private const ulong WAV_HEAD_UPPER = 0x57415645666D7400UL;
// TENSILKV3
// # ! S I L K
// +--+--+--+--+--+--+--+
// |02|23|21|53|49|4C|4B|
// +--+--+--+--+--+--+--+
// 0 4 7
private const ulong TENSILKV3_HEAD_LOWER = 0x02232153494C4B00UL;
// AMR
// # ! A M R
// +--+--+--+--+--+
// |23|21|41|4D|52|
// +--+--+--+--+--+
// 0 4 5
private const ulong AMR_HEAD_LOWER = 0x2321414D52000000UL;
// SILKV3
// # ! S I L K
// +--+--+--+--+--+--+
// |23|21|53|49|4C|4B|
// +--+--+--+--+--+--+
// 0 4 6
private const ulong SILKV3_HEAD_LOWER = 0x232153494C4B0000UL;
// Ogg
// O g g S
// +--+--+--+--+
// |4F|67|67|53|
// +--+--+--+--+
// 0 4
private const ulong OGG_HEAD_LOWER = 0x4F67675300000000UL;
// MP3 ID3
// I D 3
// +--+--+--+
// |49|44|33|
// +--+--+--+
// 0 3
private const ulong MP3ID3_HEAD_LOWER = 0x4944330000000000UL;
// MP3 no ID3
// ÿ û
// +--+--+
// |FF|F2|
// +--+--+
// 0 2
private const ulong MP3FFF2_HEAD_LOWER = 0xFFF2000000000000UL;
// MP3 no ID3
// ÿ û
// +--+--+
// |FF|F3|
// +--+--+
// 0 2
private const ulong MP3FFF3_HEAD_LOWER = 0xFFF3000000000000UL;
// MP3 no ID3
// ÿ û
// +--+--+
// |FF|FB|
// +--+--+
// 0 2
private const ulong MP3FFFB_HEAD_LOWER = 0xFFFB000000000000UL;
/// <summary>
/// Detect audio type
/// </summary>
/// <param name="data"></param>
/// <param name="type"></param>
/// <returns></returns>
public static AudioFormat DetectAudio(byte[] data)
{
ulong lower = BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(0, sizeof(ulong)));
ulong upper = BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(8, sizeof(ulong)));
if ((lower & WAV_HEAD_LOWER) == WAV_HEAD_LOWER &&
(upper & WAV_HEAD_UPPER) == WAV_HEAD_UPPER
) return AudioFormat.Wav;
if ((lower & TENSILKV3_HEAD_LOWER) == TENSILKV3_HEAD_LOWER) return AudioFormat.TenSilkV3;
if ((lower & AMR_HEAD_LOWER) == AMR_HEAD_LOWER) return AudioFormat.Amr;
if ((lower & SILKV3_HEAD_LOWER) == SILKV3_HEAD_LOWER) return AudioFormat.SilkV3;
if ((lower & OGG_HEAD_LOWER) == OGG_HEAD_LOWER) return AudioFormat.Ogg;
if ((lower & MP3ID3_HEAD_LOWER) == MP3ID3_HEAD_LOWER) return AudioFormat.Mp3;
if ((lower & MP3FFF2_HEAD_LOWER) == MP3FFF2_HEAD_LOWER) return AudioFormat.Mp3;
if ((lower & MP3FFF3_HEAD_LOWER) == MP3FFF3_HEAD_LOWER) return AudioFormat.Mp3;
if ((lower & MP3FFFB_HEAD_LOWER) == MP3FFFB_HEAD_LOWER) return AudioFormat.Mp3;
return AudioFormat.Unknown;
}
private static readonly byte[] _silk_end = [0xFF, 0xFF];
public static double GetSilkTime(byte[] data, int offset = 0)
{
int count = 0;
for (int i = 9 + offset; i < data.Length && !data.AsSpan(i, 2).SequenceEqual(_silk_end); i += 2 + BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(i, 2)))
{
count++;
}
// Because the silk encoder encodes each 20ms sample as a block,
// So that we can calculate the total time easily.
return count * 0.02;
}
/// <summary>
/// Get silk total time
/// </summary>
/// <param name="data"></param>
/// <param name="offset"></param>
/// <returns></returns>
public static double GetTenSilkTime(byte[] data)
{
int count = 0;
for (int i = 10; i < data.Length; i += 2 + BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(i, 2)))
{
count++;
}
// Because the silk encoder encodes each 20ms sample as a block,
// So that we can calculate the total time easily.
return count * 0.02;
}
}