Files
Aaru/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioEncoder.cs

182 lines
5.0 KiB
C#
Raw Normal View History

2022-12-16 18:20:23 +00:00
using System;
using System.Collections.Generic;
using System.IO;
2024-05-01 04:39:38 +01:00
namespace CUETools.Codecs.WAV;
public class AudioEncoder : IAudioDest
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
readonly EncoderSettings m_settings;
BinaryWriter _bw;
List<uint> _chunkFCCs;
List<byte[]> _chunks;
long _finalSampleCount = -1;
bool _headersWritten;
Stream _IO;
long hdrLen;
public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
{
m_settings = settings;
Path = path;
_IO = IO ?? new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
_bw = new BinaryWriter(_IO);
}
public long Position { get; private set; }
#region IAudioDest Members
public long FinalSampleCount
{
set => _finalSampleCount = value;
}
public IAudioEncoderSettings Settings => m_settings;
public string Path { get; }
public void Close()
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
if(_finalSampleCount <= 0 && _IO.CanSeek)
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
long dataLen = Position * Settings.PCM.BlockAlign;
long dataLenPadded = dataLen + (dataLen & 1);
if(dataLenPadded + hdrLen - 8 < 0xffffffff)
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
if((dataLen & 1) == 1) _bw.Write((byte)0);
_bw.Seek(4, SeekOrigin.Begin);
_bw.Write((uint)(dataLenPadded + hdrLen - 8));
_bw.Seek((int)hdrLen - 4, SeekOrigin.Begin);
_bw.Write((uint)dataLen);
2022-12-16 18:20:23 +00:00
}
}
2024-05-01 04:39:38 +01:00
_bw.Close();
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
_bw = null;
_IO = null;
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
if(_finalSampleCount > 0 && Position != _finalSampleCount)
throw new Exception("Samples written differs from the expected sample count.");
}
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
public void Delete()
{
_bw.Close();
_bw = null;
_IO = null;
if(Path != "") File.Delete(Path);
}
public void Write(AudioBuffer buff)
{
if(buff.Length == 0) return;
buff.Prepare(this);
if(!_headersWritten) WriteHeaders();
_IO.Write(buff.Bytes, 0, buff.ByteLength);
Position += buff.Length;
}
#endregion
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
public void WriteChunk(uint fcc, byte[] data)
{
if(Position > 0) throw new Exception("data already written, no chunks allowed");
if(_chunks == null)
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
_chunks = [];
_chunkFCCs = [];
2022-12-16 18:20:23 +00:00
}
2024-05-01 04:39:38 +01:00
_chunkFCCs.Add(fcc);
_chunks.Add(data);
hdrLen += 8 + data.Length + (data.Length & 1);
}
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
void WriteHeaders()
{
const uint fccRIFF = 0x46464952;
const uint fccWAVE = 0x45564157;
const uint fccFormat = 0x20746D66;
const uint fccData = 0x61746164;
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
bool wavex = Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24 ||
Settings.PCM.ChannelMask != AudioPCMConfig.GetDefaultChannelMask(Settings.PCM.ChannelCount);
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
hdrLen += 36 + (wavex ? 24 : 0) + 8;
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
var dataLen = (uint)(_finalSampleCount * Settings.PCM.BlockAlign);
uint dataLenPadded = dataLen + (dataLen & 1);
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
_bw.Write(fccRIFF);
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
if(_finalSampleCount <= 0)
_bw.Write(0xffffffff);
else
_bw.Write((uint)(dataLenPadded + hdrLen - 8));
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
_bw.Write(fccWAVE);
_bw.Write(fccFormat);
2022-12-16 18:20:23 +00:00
2024-05-01 04:39:38 +01:00
if(wavex)
{
_bw.Write((uint)40);
_bw.Write((ushort)0xfffe); // WAVEX follows
}
else
{
_bw.Write((uint)16);
_bw.Write((ushort)1); // PCM
2022-12-16 18:20:23 +00:00
}
2024-05-01 04:39:38 +01:00
_bw.Write((ushort)Settings.PCM.ChannelCount);
_bw.Write((uint)Settings.PCM.SampleRate);
_bw.Write((uint)(Settings.PCM.SampleRate * Settings.PCM.BlockAlign));
_bw.Write((ushort)Settings.PCM.BlockAlign);
_bw.Write((ushort)((Settings.PCM.BitsPerSample + 7) / 8 * 8));
if(wavex)
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
_bw.Write((ushort)22); // length of WAVEX structure
_bw.Write((ushort)Settings.PCM.BitsPerSample);
_bw.Write((uint)Settings.PCM.ChannelMask);
_bw.Write((ushort)1); // PCM Guid
_bw.Write((ushort)0);
_bw.Write((ushort)0);
_bw.Write((ushort)0x10);
_bw.Write((byte)0x80);
_bw.Write((byte)0x00);
_bw.Write((byte)0x00);
_bw.Write((byte)0xaa);
_bw.Write((byte)0x00);
_bw.Write((byte)0x38);
_bw.Write((byte)0x9b);
_bw.Write((byte)0x71);
2022-12-16 18:20:23 +00:00
}
2024-05-01 04:39:38 +01:00
if(_chunks != null)
2022-12-16 18:20:23 +00:00
{
2024-05-01 04:39:38 +01:00
for(var i = 0; i < _chunks.Count; i++)
{
_bw.Write(_chunkFCCs[i]);
_bw.Write((uint)_chunks[i].Length);
_bw.Write(_chunks[i]);
if((_chunks[i].Length & 1) != 0) _bw.Write((byte)0);
}
2022-12-16 18:20:23 +00:00
}
2024-05-01 04:39:38 +01:00
_bw.Write(fccData);
if(_finalSampleCount <= 0)
_bw.Write(0xffffffff);
else
_bw.Write(dataLen);
_headersWritten = true;
2022-12-16 18:20:23 +00:00
}
2024-05-01 04:39:38 +01:00
}