1
0
mirror of https://github.com/SineVector241/VoiceCraft-MCBE_Proximity_Chat.git synced 2024-11-24 14:36:13 +00:00
VoiceCraft-MCBE_Proximity_Chat/ATL/AudioData/IO/AAC.cs
2024-07-13 11:16:08 +10:00

265 lines
9.1 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using Commons;
using static ATL.ChannelsArrangements;
namespace ATL.AudioData.IO
{
/// <summary>
/// Class for Advanced Audio Coding files manipulation (extensions : .AAC)
///
/// Implementation notes
///
/// 1. LATM and LOAS/LATM support is missing
///
/// </summary>
class AAC : IAudioDataIO
{
// Header type codes
public const byte AAC_HEADER_TYPE_UNKNOWN = 0; // Unknown
public const byte AAC_HEADER_TYPE_ADIF = 1; // ADIF
public const byte AAC_HEADER_TYPE_ADTS = 2; // ADTS
// Header type names
public static readonly string[] AAC_HEADER_TYPE = { "Unknown", "ADIF", "ADTS" };
// MPEG version codes
public const byte AAC_MPEG_VERSION_UNKNOWN = 0; // Unknown
public const byte AAC_MPEG_VERSION_2 = 1; // MPEG-2
public const byte AAC_MPEG_VERSION_4 = 2; // MPEG-4
// MPEG version names
public static readonly string[] AAC_MPEG_VERSION = { "Unknown", "MPEG-2", "MPEG-4" };
// Profile codes
public const byte AAC_PROFILE_UNKNOWN = 0; // Unknown
public const byte AAC_PROFILE_MAIN = 1; // Main
public const byte AAC_PROFILE_LC = 2; // LC
public const byte AAC_PROFILE_SSR = 3; // SSR
public const byte AAC_PROFILE_LTP = 4; // LTP
// Profile names
public static readonly string[] AAC_PROFILE = { "Unknown", "AAC Main", "AAC LC", "AAC SSR", "AAC LTP" };
// Bit rate type codes
public const byte AAC_BITRATE_TYPE_UNKNOWN = 0; // Unknown
public const byte AAC_BITRATE_TYPE_CBR = 1; // CBR
public const byte AAC_BITRATE_TYPE_VBR = 2; // VBR
// Bit rate type names
public static readonly string[] AAC_BITRATE_TYPE = { "Unknown", "CBR", "VBR" };
// Sample rate values
private static readonly int[] SAMPLE_RATE = { 96000, 88200, 64000, 48000, 44100, 32000,
24000, 22050, 16000, 12000, 11025, 8000,
0, 0, 0, 0 };
private byte headerTypeID;
private byte bitrateTypeID;
private double bitrate;
private AudioDataManager.SizeInfo sizeInfo;
// ---------- INFORMATIVE INTERFACE IMPLEMENTATIONS & MANDATORY OVERRIDES
// IAudioDataIO
/// <inheritdoc/>
public bool IsVBR => AAC_BITRATE_TYPE_VBR == bitrateTypeID;
/// <inheritdoc/>
public Format AudioFormat
{
get;
}
/// <inheritdoc/>
public int CodecFamily => AudioDataIOFactory.CF_LOSSY;
/// <inheritdoc/>
public double BitRate => bitrate / 1000.0;
/// <inheritdoc/>
public double Duration => getDuration();
/// <inheritdoc/>
public int SampleRate { get; private set; }
/// <inheritdoc/>
public int BitDepth => -1; // Irrelevant for lossy formats
/// <inheritdoc/>
public string FileName { get; }
/// <inheritdoc/>
public List<MetaDataIOFactory.TagType> GetSupportedMetas()
{
return new List<MetaDataIOFactory.TagType> { MetaDataIOFactory.TagType.ID3V2, MetaDataIOFactory.TagType.APE, MetaDataIOFactory.TagType.NATIVE, MetaDataIOFactory.TagType.ID3V1 };
}
/// <inheritdoc/>
public ChannelsArrangement ChannelsArrangement { get; private set; }
/// <inheritdoc/>
public long AudioDataOffset { get; set; }
/// <inheritdoc/>
public long AudioDataSize { get; set; }
// ---------- CONSTRUCTORS & INITIALIZERS
protected void resetData()
{
headerTypeID = AAC_HEADER_TYPE_UNKNOWN;
bitrateTypeID = AAC_BITRATE_TYPE_UNKNOWN;
bitrate = 0;
SampleRate = 0;
AudioDataOffset = -1;
AudioDataSize = 0;
}
public AAC(string fileName, Format format)
{
this.FileName = fileName;
AudioFormat = format;
resetData();
}
// ********************** Private functions & procedures *********************
// Calculate duration time
private double getDuration()
{
if (Utils.ApproxEquals(bitrate, 0)) return 0;
return 8.0 * (sizeInfo.FileSize - sizeInfo.TotalTagSize) * 1000 / bitrate;
}
public static bool IsValidHeader(byte[] data)
{
return recognizeHeaderType(data) != AAC_BITRATE_TYPE_UNKNOWN;
}
private static byte recognizeHeaderType(byte[] data)
{
if (data.Length < 4) return AAC_BITRATE_TYPE_UNKNOWN;
if ("ADIF".Equals(Utils.Latin1Encoding.GetString(data, 0, 4)))
{
return AAC_HEADER_TYPE_ADIF;
}
if (0xFF == data[0] && 0xF0 == (data[1] & 0xF0)) return AAC_HEADER_TYPE_ADTS;
return AAC_BITRATE_TYPE_UNKNOWN;
}
// Get header type of the file
private byte recognizeHeaderType(Stream source)
{
byte[] header = new byte[4];
source.Seek(sizeInfo.ID3v2Size, SeekOrigin.Begin);
source.Read(header, 0, header.Length);
byte result = recognizeHeaderType(header);
if (result != AAC_BITRATE_TYPE_UNKNOWN)
{
AudioDataOffset = source.Position - 4;
AudioDataSize = sizeInfo.FileSize - sizeInfo.APESize - sizeInfo.ID3v1Size - AudioDataOffset;
}
return result;
}
// Read ADIF header data
private void readADIF(Stream Source)
{
int Position;
Position = (int)(sizeInfo.ID3v2Size * 8 + 32);
if (0 == StreamUtils.ReadBEBits(Source, Position, 1)) Position += 3;
else Position += 75;
if (0 == StreamUtils.ReadBEBits(Source, Position, 1)) bitrateTypeID = AAC_BITRATE_TYPE_CBR;
else bitrateTypeID = AAC_BITRATE_TYPE_VBR;
Position++;
bitrate = (int)StreamUtils.ReadBEBits(Source, Position, 23);
if (AAC_BITRATE_TYPE_CBR == bitrateTypeID) Position += 51;
else Position += 31;
Position += 2;
uint channels = 1;
SampleRate = SAMPLE_RATE[StreamUtils.ReadBEBits(Source, Position, 4)];
Position += 4;
channels += StreamUtils.ReadBEBits(Source, Position, 4);
Position += 4;
channels += StreamUtils.ReadBEBits(Source, Position, 4);
Position += 4;
channels += StreamUtils.ReadBEBits(Source, Position, 4);
Position += 4;
channels += StreamUtils.ReadBEBits(Source, Position, 2);
ChannelsArrangement = GuessFromChannelNumber((int)channels);
}
// Read ADTS header data
private void readADTS(Stream source)
{
int frames = 0;
int totalSize = 0;
int position;
do
{
frames++;
position = (int)(sizeInfo.ID3v2Size + totalSize) * 8;
if (StreamUtils.ReadBEBits(source, position, 12) != 0xFFF) break;
position += 18;
SampleRate = SAMPLE_RATE[StreamUtils.ReadBEBits(source, position, 4)];
position += 5;
uint channels = StreamUtils.ReadBEBits(source, position, 3);
ChannelsArrangement = GuessFromChannelNumber((int)channels);
position += 7;
totalSize += (int)StreamUtils.ReadBEBits(source, position, 13);
position += 13;
if (0x7FF == StreamUtils.ReadBEBits(source, position, 11))
bitrateTypeID = AAC_BITRATE_TYPE_VBR;
else
bitrateTypeID = AAC_BITRATE_TYPE_CBR;
if (AAC_BITRATE_TYPE_CBR == bitrateTypeID) break;
}
while (source.Length > sizeInfo.ID3v2Size + totalSize);
bitrate = (int)Math.Round(8 * totalSize / 1024.0 / frames * SampleRate);
}
// Read data from file
public bool Read(Stream source, AudioDataManager.SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams)
{
this.sizeInfo = sizeInfo;
return read(source, readTagParams);
}
protected bool read(Stream source, MetaDataIO.ReadTagParams readTagParams)
{
bool result = true;
resetData();
headerTypeID = recognizeHeaderType(source);
// Read header data
if (AAC_HEADER_TYPE_ADIF == headerTypeID) readADIF(source);
else if (AAC_HEADER_TYPE_ADTS == headerTypeID) readADTS(source);
else result = false;
return result;
}
}
}