1
0
mirror of https://github.com/SineVector241/VoiceCraft-MCBE_Proximity_Chat.git synced 2024-11-20 10:27:45 +00:00
VoiceCraft-MCBE_Proximity_Chat/ATL/AudioData/IO/DSF.cs
2024-07-13 11:16:08 +10:00

215 lines
7.1 KiB
C#

using ATL.Logging;
using System;
using System.IO;
using static ATL.AudioData.AudioDataManager;
using Commons;
using static ATL.ChannelsArrangements;
using System.Collections.Generic;
namespace ATL.AudioData.IO
{
/// <summary>
/// Class for DSD Stream File files manipulation (extension : .DSF)
/// </summary>
class DSF : IAudioDataIO, IMetaDataEmbedder
{
// Headers ID
private static readonly byte[] DSD_ID = Utils.Latin1Encoding.GetBytes("DSD ");
private static readonly byte[] FMT_ID = Utils.Latin1Encoding.GetBytes("fmt ");
// Private declarations
private ChannelsArrangement channelsArrangement;
private uint bits;
private uint sampleRate;
private bool isValid;
private SizeInfo sizeInfo;
private long id3v2Offset;
private readonly FileStructureHelper id3v2StructureHelper = new FileStructureHelper();
// Public declarations
public double CompressionRatio => getCompressionRatio();
// ---------- INFORMATIVE INTERFACE IMPLEMENTATIONS & MANDATORY OVERRIDES
public int SampleRate => (int)sampleRate;
public bool IsVBR => false;
public Format AudioFormat
{
get;
}
public int CodecFamily => AudioDataIOFactory.CF_LOSSLESS;
public string FileName { get; }
public double BitRate { get; private set; }
public int BitDepth => (int)bits;
public double Duration { get; private set; }
public ChannelsArrangement ChannelsArrangement => channelsArrangement;
public List<MetaDataIOFactory.TagType> GetSupportedMetas()
{
return new List<MetaDataIOFactory.TagType> { MetaDataIOFactory.TagType.ID3V2 };
}
// IMetaDataEmbedder
public long HasEmbeddedID3v2 => id3v2Offset;
public uint ID3v2EmbeddingHeaderSize => 0;
public FileStructureHelper.Zone Id3v2Zone => id3v2StructureHelper.GetZone(FileStructureHelper.DEFAULT_ZONE_NAME);
public long AudioDataOffset { get; set; }
public long AudioDataSize { get; set; }
// ---------- CONSTRUCTORS & INITIALIZERS
protected void resetData()
{
bits = 0;
sampleRate = 0;
Duration = 0;
BitRate = 0;
isValid = false;
id3v2Offset = -1;
id3v2StructureHelper.Clear();
AudioDataOffset = -1;
AudioDataSize = 0;
}
/// <summary>
/// Constructor
/// </summary>
public DSF(string filePath, Format format)
{
this.FileName = filePath;
AudioFormat = format;
resetData();
}
// ---------- SUPPORT METHODS
// Get compression ratio
private double getCompressionRatio()
{
if (isValid) return sizeInfo.FileSize / (Duration / 1000.0 * sampleRate * (channelsArrangement.NbChannels * bits / 8.0) + 44) * 100;
return 0;
}
public static bool IsValidHeader(byte[] data)
{
return StreamUtils.ArrBeginsWith(data, DSD_ID);
}
/// <inheritdoc/>
public bool Read(Stream source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams)
{
this.sizeInfo = sizeInfo;
bool result = false;
byte[] buffer = new byte[8];
resetData();
source.Seek(0, SeekOrigin.Begin);
source.Read(buffer, 0, 4);
if (StreamUtils.ArrBeginsWith(buffer, DSD_ID))
{
source.Seek(16, SeekOrigin.Current); // Chunk size and file size
source.Read(buffer, 0, 8);
id3v2Offset = StreamUtils.DecodeInt64(buffer);
source.Read(buffer, 0, 4);
if (StreamUtils.ArrBeginsWith(buffer, FMT_ID))
{
source.Seek(8, SeekOrigin.Current); // Chunk size
source.Read(buffer, 0, 4);
int formatVersion = StreamUtils.DecodeInt32(buffer);
if (formatVersion > 1)
{
LogDelegator.GetLogDelegate()(Log.LV_ERROR, "DSF format version " + formatVersion + " not supported");
return result;
}
isValid = true;
source.Seek(8, SeekOrigin.Current); // Format ID (4), Channel type (4)
source.Read(buffer, 0, 4);
uint channels = StreamUtils.DecodeUInt32(buffer);
channelsArrangement = channels switch
{
1 => MONO,
2 => STEREO,
3 => ISO_3_0_0,
4 => QUAD,
5 => LRCLFE,
6 => ISO_3_2_0,
7 => ISO_3_2_1,
_ => UNKNOWN
};
source.Read(buffer, 0, 4);
sampleRate = StreamUtils.DecodeUInt32(buffer);
source.Read(buffer, 0, 4);
bits = StreamUtils.DecodeUInt32(buffer);
source.Read(buffer, 0, 8);
ulong sampleCount = StreamUtils.DecodeUInt64(buffer);
Duration = sampleCount * 1000.0 / sampleRate;
BitRate = Math.Round(((double)(sizeInfo.FileSize - source.Position)) * 8 / Duration); //time to calculate average bitrate
AudioDataOffset = source.Position + 8;
if (id3v2Offset > 0)
AudioDataSize = id3v2Offset - AudioDataOffset;
else
AudioDataSize = sizeInfo.FileSize - AudioDataOffset;
result = true;
}
// Load tag if exists
if (id3v2Offset > 0)
{
if (readTagParams.PrepareForWriting)
{
id3v2StructureHelper.AddZone(id3v2Offset, (int)(source.Length - id3v2Offset));
id3v2StructureHelper.AddSize(12, source.Length);
id3v2StructureHelper.AddIndex(20, id3v2Offset);
}
}
else
{
id3v2Offset = 0; // Switch status to "tried to read, but nothing found"
if (readTagParams.PrepareForWriting)
{
// Add EOF zone for future tag writing
id3v2StructureHelper.AddZone(source.Length, 0);
id3v2StructureHelper.AddSize(12, source.Length);
id3v2StructureHelper.AddIndex(20, source.Length);
}
}
}
return result;
}
/// <inheritdoc/>
public void WriteID3v2EmbeddingHeader(Stream s, long tagSize)
{
// Nothing to do here; DSF format defines no frame header for its embedded ID3v2 tag
}
public void WriteID3v2EmbeddingFooter(Stream s, long tagSize)
{
// Nothing to do here
}
}
}