mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 10:04:24 +00:00
Implement IAudioSource.Duration property and IAudioDecoderSettings.Open extension method.
This commit is contained in:
@@ -165,7 +165,9 @@ namespace CUETools.Codecs.ALAC
|
||||
_IO.Close();
|
||||
}
|
||||
|
||||
public long Length
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -127,7 +127,9 @@ namespace CUETools.Codecs.Flake
|
||||
_IO.Close();
|
||||
}
|
||||
|
||||
public long Length
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -12,13 +12,9 @@ namespace CUETools.Codecs.LossyWAV
|
||||
|
||||
public IAudioDecoderSettings Settings => null;
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _audioSource.Length;
|
||||
}
|
||||
}
|
||||
public TimeSpan Duration => _audioSource.Duration;
|
||||
|
||||
public long Length => _audioSource.Length;
|
||||
|
||||
public long Position
|
||||
{
|
||||
@@ -33,29 +29,11 @@ namespace CUETools.Codecs.LossyWAV
|
||||
}
|
||||
}
|
||||
|
||||
public long Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return _audioSource.Remaining;
|
||||
}
|
||||
}
|
||||
public long Remaining => _audioSource.Remaining;
|
||||
|
||||
public AudioPCMConfig PCM
|
||||
{
|
||||
get
|
||||
{
|
||||
return _audioSource.PCM;
|
||||
}
|
||||
}
|
||||
public AudioPCMConfig PCM => _audioSource.PCM;
|
||||
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return _audioSource.Path;
|
||||
}
|
||||
}
|
||||
public string Path => _audioSource.Path;
|
||||
|
||||
public LossyWAVReader(IAudioSource audioSource, IAudioSource lwcdfSource)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,8 @@ namespace CUETools.Codecs.MACLib
|
||||
|
||||
public string Path => _path;
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length => _sampleCount;
|
||||
|
||||
internal long SamplesInBuffer => _bufferLength - _bufferOffset;
|
||||
|
||||
657
CUETools.Codecs.MPEG/ATSI/AudioDecoder.cs
Normal file
657
CUETools.Codecs.MPEG/ATSI/AudioDecoder.cs
Normal file
@@ -0,0 +1,657 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace CUETools.Codecs.MPEG.ATSI
|
||||
{
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
public unsafe AudioDecoder(DecoderSettings settings, string path, Stream IO)
|
||||
{
|
||||
m_settings = settings;
|
||||
_path = path;
|
||||
_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000);
|
||||
int length = (int)_IO.Length;
|
||||
contents = new byte[length];
|
||||
if (_IO.Read(contents, 0, length) != length) throw new Exception("");
|
||||
fixed (byte* ptr = &contents[0])
|
||||
{
|
||||
var fr = new FrameReader(ptr, length);
|
||||
hdr_m = parseHeader(fr);
|
||||
fr = new FrameReader(ptr + hdr_m.list_pos, length - hdr_m.list_pos);
|
||||
parsePlaylist(fr);
|
||||
fr = new FrameReader(ptr + hdr_m.mark_pos, length - hdr_m.mark_pos);
|
||||
parsePlaylistMarks(fr);
|
||||
}
|
||||
}
|
||||
|
||||
void openEntries()
|
||||
{
|
||||
readers = new List<IAudioSource>();
|
||||
var pids = new List<int>();
|
||||
foreach (var item in hdr_m.play_item)
|
||||
foreach (var audio in item.audio)
|
||||
{
|
||||
if (audio.coding_type != 0x80 /* LPCM */) continue;
|
||||
pids.Add(audio.pid);
|
||||
}
|
||||
int chosenPid;
|
||||
if (m_settings.StreamId.HasValue)
|
||||
{
|
||||
if (!pids.Contains(m_settings.StreamId.Value))
|
||||
throw new Exception("StreamId can be " +
|
||||
string.Join(", ", pids.ConvertAll(pid => pid.ToString()).ToArray()));
|
||||
chosenPid = m_settings.StreamId.Value;
|
||||
}
|
||||
else if (m_settings.Stream.HasValue)
|
||||
{
|
||||
if (m_settings.Stream.Value < 0 || m_settings.Stream.Value >= pids.Count)
|
||||
throw new Exception("Stream can be 0.." + (pids.Count - 1).ToString());
|
||||
chosenPid = pids[m_settings.Stream.Value];
|
||||
}
|
||||
else throw new Exception("multiple streams present, please specify StreamId or Stream");
|
||||
foreach (var item in hdr_m.play_item)
|
||||
foreach (var audio in item.audio)
|
||||
{
|
||||
if (audio.coding_type != 0x80 /* LPCM */) continue;
|
||||
if (m_settings.IgnoreShortItems && item.out_time - item.in_time < shortItemDuration) continue;
|
||||
if (audio.pid == chosenPid)
|
||||
{
|
||||
var parent = Directory.GetParent(System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(_path)));
|
||||
var m2ts = System.IO.Path.Combine(
|
||||
System.IO.Path.Combine(parent.FullName, "STREAM"),
|
||||
item.clip_id + ".m2ts");
|
||||
var settings = new BDLPCM.DecoderSettings() { StreamId = chosenPid };
|
||||
var entry = settings.Open(m2ts);
|
||||
readers.Add(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentReader = readers[0];
|
||||
pcm = currentReader.PCM;
|
||||
}
|
||||
|
||||
MPLSHeader parseHeader(FrameReader fr)
|
||||
{
|
||||
var hdr = new MPLSHeader();
|
||||
hdr.play_item = new List<MPLSPlaylistItem>();
|
||||
hdr.play_mark = new List<MPLSPlaylistMark>();
|
||||
long length = fr.Length;
|
||||
hdr.type_indicator = fr.read_uint();
|
||||
hdr.type_indicator2 = fr.read_uint();
|
||||
hdr.list_pos = fr.read_uint();
|
||||
hdr.mark_pos = fr.read_uint();
|
||||
hdr.ext_pos = fr.read_uint();
|
||||
if (hdr.type_indicator != 0x4d504c53 /*MPLS*/) throw new NotSupportedException();
|
||||
if (hdr.type_indicator2 != 0x30313030 && hdr.type_indicator2 != 0x30323030) throw new NotSupportedException();
|
||||
if (hdr.list_pos > length || hdr.mark_pos > length || hdr.ext_pos > length) throw new NotSupportedException();
|
||||
return hdr;
|
||||
}
|
||||
|
||||
void parsePlaylist(FrameReader parentFr)
|
||||
{
|
||||
uint len = parentFr.read_uint();
|
||||
var fr = new FrameReader(parentFr, len);
|
||||
parentFr.skip(len);
|
||||
ushort reserved = fr.read_ushort();
|
||||
hdr_m.list_count = fr.read_ushort();
|
||||
hdr_m.sub_count = fr.read_ushort();
|
||||
for (int i = 0; i < hdr_m.list_count; i++)
|
||||
hdr_m.play_item.Add(parsePlaylistItem(fr));
|
||||
}
|
||||
|
||||
void parsePlaylistMarks(FrameReader parentFr)
|
||||
{
|
||||
uint len = parentFr.read_uint();
|
||||
var fr = new FrameReader(parentFr, len);
|
||||
parentFr.skip(len);
|
||||
hdr_m.mark_count = fr.read_ushort();
|
||||
for (int ii = 0; ii < hdr_m.mark_count; ii++)
|
||||
hdr_m.play_mark.Add(parsePlaylistMark(fr));
|
||||
}
|
||||
|
||||
MPLSPlaylistItem parsePlaylistItem(FrameReader parentFr)
|
||||
{
|
||||
var item = new MPLSPlaylistItem();
|
||||
item.video = new List<MPLSStream>();
|
||||
item.audio = new List<MPLSStream>();
|
||||
item.pg = new List<MPLSStream>();
|
||||
|
||||
// PlayItem Length
|
||||
ushort len = parentFr.read_ushort();
|
||||
var fr = new FrameReader(parentFr, len);
|
||||
parentFr.skip(len);
|
||||
|
||||
// Primary Clip identifer
|
||||
item.clip_id = fr.read_string(5);
|
||||
|
||||
// skip the redundant "M2TS" CodecIdentifier
|
||||
uint codecId = fr.read_uint();
|
||||
if (codecId != 0x4D325453) throw new NotSupportedException("Incorrect CodecIdentifier");
|
||||
|
||||
ushort flags = fr.read_ushort();
|
||||
bool is_multi_angle = ((flags >> 4) & 1) != 0;
|
||||
item.connection_condition = (byte)(flags & 15);
|
||||
if (item.connection_condition != 0x01 &&
|
||||
item.connection_condition != 0x05 &&
|
||||
item.connection_condition != 0x06)
|
||||
throw new NotSupportedException("Unexpected connection condition");
|
||||
|
||||
item.stc_id = fr.read_byte();
|
||||
item.in_time = fr.read_uint();
|
||||
item.out_time = fr.read_uint();
|
||||
|
||||
// Skip UO_mask_table, random_access_flag, reserved, still_mode
|
||||
// and still_time
|
||||
fr.skip(12);
|
||||
|
||||
if (is_multi_angle)
|
||||
{
|
||||
byte num_angles = fr.read_byte();
|
||||
// skip reserved, is_different_audio, is_seamless_angle_change
|
||||
fr.skip(1);
|
||||
for (int ii = 1; ii < num_angles; ii++)
|
||||
{
|
||||
// Drop clip_id, clip_codec_id, stc_id
|
||||
fr.skip(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip STN len
|
||||
fr.skip(2);
|
||||
|
||||
// Skip 2 reserved bytes
|
||||
fr.skip(2);
|
||||
|
||||
item.num_video = fr.read_byte();
|
||||
item.num_audio = fr.read_byte();
|
||||
item.num_pg = fr.read_byte();
|
||||
item.num_ig = fr.read_byte();
|
||||
item.num_secondary_audio = fr.read_byte();
|
||||
item.num_secondary_video = fr.read_byte();
|
||||
item.num_pip_pg = fr.read_byte();
|
||||
|
||||
// 5 reserve bytes
|
||||
fr.skip(5);
|
||||
|
||||
for (int ii = 0; ii < item.num_video; ii++)
|
||||
item.video.Add(parseStream(fr));
|
||||
for (int ii = 0; ii < item.num_audio; ii++)
|
||||
item.audio.Add(parseStream(fr));
|
||||
for ( int ii = 0; ii < item.num_pg; ii++)
|
||||
item.pg.Add(parseStream(fr));
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
MPLSStream parseStream(FrameReader parentFr)
|
||||
{
|
||||
MPLSStream s = new MPLSStream();
|
||||
|
||||
byte len = parentFr.read_byte();
|
||||
var fr = new FrameReader(parentFr, len);
|
||||
parentFr.skip(len);
|
||||
|
||||
s.stream_type = fr.read_byte();
|
||||
switch (s.stream_type)
|
||||
{
|
||||
case 1:
|
||||
s.pid = fr.read_ushort();
|
||||
break;
|
||||
case 2:
|
||||
case 4:
|
||||
s.subpath_id = fr.read_byte();
|
||||
s.subclip_id = fr.read_byte();
|
||||
s.pid = fr.read_ushort();
|
||||
break;
|
||||
case 3:
|
||||
s.subpath_id = fr.read_byte();
|
||||
s.pid = fr.read_ushort();
|
||||
break;
|
||||
default:
|
||||
throw new Exception("unrecognized stream type");
|
||||
};
|
||||
|
||||
len = parentFr.read_byte();
|
||||
fr = new FrameReader(parentFr, len);
|
||||
parentFr.skip(len);
|
||||
|
||||
s.coding_type = fr.read_byte();
|
||||
if (s.coding_type == 0x01
|
||||
|| s.coding_type == 0x02
|
||||
|| s.coding_type == 0xea
|
||||
|| s.coding_type == 0x1b)
|
||||
{
|
||||
// Video
|
||||
byte fmt = fr.read_byte();
|
||||
s.format = (byte)(fmt >> 4);
|
||||
s.rate = (byte)(fmt & 15);
|
||||
}
|
||||
else if (s.coding_type == 0x03
|
||||
|| s.coding_type == 0x04
|
||||
|| s.coding_type == 0x80
|
||||
|| s.coding_type == 0x81
|
||||
|| s.coding_type == 0x82
|
||||
|| s.coding_type == 0x83
|
||||
|| s.coding_type == 0x84
|
||||
|| s.coding_type == 0x85
|
||||
|| s.coding_type == 0x86)
|
||||
{
|
||||
// Audio
|
||||
byte fmt = fr.read_byte();
|
||||
s.format = (byte)(fmt >> 4);
|
||||
s.rate = (byte)(fmt & 15);
|
||||
s.lang = fr.read_string(3);
|
||||
}
|
||||
else if (s.coding_type == 0x90
|
||||
|| s.coding_type == 0x91)
|
||||
{
|
||||
s.lang = fr.read_string(3);
|
||||
}
|
||||
else if (s.coding_type == 0x92)
|
||||
{
|
||||
s.char_code = fr.read_byte();
|
||||
s.lang = fr.read_string(3);
|
||||
}
|
||||
else throw new Exception("unrecognized stream type");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
MPLSPlaylistMark parsePlaylistMark(FrameReader fr)
|
||||
{
|
||||
var mark = new MPLSPlaylistMark();
|
||||
mark.mark_id = fr.read_byte();
|
||||
mark.mark_type = fr.read_byte();
|
||||
mark.play_item_ref = fr.read_ushort();
|
||||
mark.time = fr.read_uint();
|
||||
mark.entry_es_pid = fr.read_ushort();
|
||||
mark.duration = fr.read_uint();
|
||||
return mark;
|
||||
}
|
||||
|
||||
public IAudioDecoderSettings Settings => m_settings;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (readers != null)
|
||||
foreach (var rdr in readers)
|
||||
{
|
||||
rdr.Close();
|
||||
}
|
||||
readers = null;
|
||||
currentReader = null;
|
||||
_IO = null;
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
uint totalLength = 0;
|
||||
foreach (var item in hdr_m.play_item)
|
||||
{
|
||||
if (item.num_audio == 0) continue;
|
||||
uint item_duration = item.out_time - item.in_time;
|
||||
if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue;
|
||||
totalLength += item_duration;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(totalLength / 45000.0);
|
||||
}
|
||||
}
|
||||
|
||||
public long Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
long res = 0;
|
||||
foreach (var rdr in readers)
|
||||
{
|
||||
res += rdr.Position;
|
||||
if (rdr == currentReader) break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe AudioPCMConfig PCM
|
||||
{
|
||||
get {
|
||||
if (readers == null) openEntries();
|
||||
return pcm;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path { get { return _path; } }
|
||||
|
||||
public unsafe int Read(AudioBuffer buff, int maxLength)
|
||||
{
|
||||
if (readers == null) openEntries();
|
||||
int res = currentReader.Read(buff, maxLength);
|
||||
if (res == 0)
|
||||
{
|
||||
bool nextOne = false;
|
||||
foreach (var rdr in readers)
|
||||
{
|
||||
if (nextOne)
|
||||
{
|
||||
currentReader = rdr;
|
||||
return currentReader.Read(buff, maxLength);
|
||||
}
|
||||
nextOne = (rdr == currentReader);
|
||||
}
|
||||
currentReader = null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.IO.Path.GetFileName(_path);
|
||||
}
|
||||
}
|
||||
|
||||
public MPLSHeader MPLSHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return hdr_m;
|
||||
}
|
||||
}
|
||||
|
||||
public List<uint> Chapters
|
||||
{
|
||||
get
|
||||
{
|
||||
//settings.IgnoreShortItems
|
||||
var res = new List<uint>();
|
||||
if (hdr_m.play_mark.Count < 1) return res;
|
||||
if (hdr_m.play_item.Count < 1) return res;
|
||||
res.Add(0);
|
||||
for (int i = 0; i < hdr_m.mark_count; i++)
|
||||
{
|
||||
ushort mark_item = hdr_m.play_mark[i].play_item_ref;
|
||||
uint item_in_time = hdr_m.play_item[mark_item].in_time;
|
||||
uint item_out_time = hdr_m.play_item[mark_item].out_time;
|
||||
if (m_settings.IgnoreShortItems && item_out_time - item_in_time < shortItemDuration) continue;
|
||||
uint item_offset = 0;
|
||||
for (int j = 0; j < mark_item; j++)
|
||||
{
|
||||
if (hdr_m.play_item[j].num_audio == 0) continue;
|
||||
uint item_duration = hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time;
|
||||
if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue;
|
||||
item_offset += item_duration;
|
||||
}
|
||||
res.Add(hdr_m.play_mark[i].time - item_in_time + item_offset);
|
||||
}
|
||||
uint end_offset = 0;
|
||||
for (int j = 0; j < hdr_m.play_item.Count; j++)
|
||||
{
|
||||
if (hdr_m.play_item[j].num_audio == 0) continue;
|
||||
uint item_duration = hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time;
|
||||
if (m_settings.IgnoreShortItems && item_duration < shortItemDuration) continue;
|
||||
end_offset += hdr_m.play_item[j].out_time - hdr_m.play_item[j].in_time;
|
||||
}
|
||||
res.Add(end_offset);
|
||||
while (res.Count > 1 && res[1] - res[0] < 45000) res.RemoveAt(1);
|
||||
while (res.Count > 1 && res[res.Count - 1] - res[res.Count - 2] < 45000) res.RemoveAt(res.Count - 2);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
readonly static int shortItemDuration = 45000 * 30;
|
||||
|
||||
string _path;
|
||||
Stream _IO;
|
||||
byte[] contents;
|
||||
|
||||
AudioPCMConfig pcm;
|
||||
List<IAudioSource> readers;
|
||||
IAudioSource currentReader;
|
||||
MPLSHeader hdr_m;
|
||||
DecoderSettings m_settings;
|
||||
}
|
||||
|
||||
public struct MPLSPlaylistMark
|
||||
{
|
||||
public byte mark_id;
|
||||
public byte mark_type;
|
||||
public ushort play_item_ref;
|
||||
public uint time;
|
||||
public ushort entry_es_pid;
|
||||
public uint duration;
|
||||
}
|
||||
|
||||
public struct MPLSStream
|
||||
{
|
||||
public byte stream_type;
|
||||
public byte coding_type;
|
||||
public ushort pid;
|
||||
public byte subpath_id;
|
||||
public byte subclip_id;
|
||||
public byte format;
|
||||
public byte rate;
|
||||
public byte char_code;
|
||||
public string lang;
|
||||
|
||||
public string FormatString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (coding_type == 0x01
|
||||
|| coding_type == 0x02
|
||||
|| coding_type == 0xea
|
||||
|| coding_type == 0x1b)
|
||||
switch (format)
|
||||
{
|
||||
case 0: return "reserved0";
|
||||
case 1: return "480i";
|
||||
case 2: return "576i";
|
||||
case 3: return "480p";
|
||||
case 4: return "1080i";
|
||||
case 5: return "720p";
|
||||
case 6: return "1080p";
|
||||
case 7: return "576p";
|
||||
default: return format.ToString();
|
||||
}
|
||||
switch (format)
|
||||
{
|
||||
case 0: return "reserved0";
|
||||
case 1: return "mono";
|
||||
case 2: return "reserved2";
|
||||
case 3: return "stereo";
|
||||
case 4: return "reserved4";
|
||||
case 5: return "reserved5";
|
||||
case 6: return "multi-channel";
|
||||
case 12: return "combo";
|
||||
default: return format.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int FrameRate
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (rate)
|
||||
{
|
||||
case 1: return 24;
|
||||
case 2: return 24;
|
||||
case 3: return 25;
|
||||
case 4: return 30;
|
||||
case 6: return 50;
|
||||
case 7: return 60;
|
||||
default: throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Interlaced
|
||||
{
|
||||
get
|
||||
{
|
||||
return format == 1 || format == 2 || format == 4;
|
||||
}
|
||||
}
|
||||
|
||||
public string RateString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (coding_type == 0x01
|
||||
|| coding_type == 0x02
|
||||
|| coding_type == 0xea
|
||||
|| coding_type == 0x1b)
|
||||
switch (rate)
|
||||
{
|
||||
case 0: return "reserved0";
|
||||
case 1: return "23.976";
|
||||
case 2: return "24";
|
||||
case 3: return "25";
|
||||
case 4: return "29.97";
|
||||
case 5: return "reserved5";
|
||||
case 6: return "50";
|
||||
case 7: return "59.94";
|
||||
default: return rate.ToString();
|
||||
}
|
||||
switch (rate)
|
||||
{
|
||||
case 0: return "reserved0";
|
||||
case 1: return "48KHz";
|
||||
case 2: return "reserved2";
|
||||
case 3: return "reserved3";
|
||||
case 4: return "96KHz";
|
||||
case 5: return "192KHz";
|
||||
//case 12: return "48/192KHz"; (core/hd)
|
||||
case 12: return "192KHz";
|
||||
//case 14: return "48/96KHz"; (core/hd)
|
||||
case 14: return "96KHz";
|
||||
default: return rate.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string CodecString
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (coding_type)
|
||||
{
|
||||
case 0x01: return "MPEG-1 Video";
|
||||
case 0x02: return "MPEG-2 Video";
|
||||
case 0x03: return "MPEG-1 Audio";
|
||||
case 0x04: return "MPEG-2 Audio";
|
||||
//case 0x80: return "LPCM";
|
||||
case 0x80: return "RAW/PCM";
|
||||
case 0x81: return "AC-3";
|
||||
case 0x82: return "DTS";
|
||||
case 0x83: return "TrueHD";
|
||||
case 0x84: return "AC-3 Plus";
|
||||
case 0x85: return "DTS-HD";
|
||||
//case 0x86: return "DTS-HD Master";
|
||||
case 0x86: return "DTS Master Audio";
|
||||
case 0xea: return "VC-1";
|
||||
case 0x1b: return "h264/AVC";
|
||||
case 0x90: return "Presentation Graphics";
|
||||
case 0x91: return "Interactive Graphics";
|
||||
case 0x92: return "Text Subtitle";
|
||||
default: return coding_type.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte CodingType
|
||||
{
|
||||
get
|
||||
{
|
||||
return coding_type;
|
||||
}
|
||||
}
|
||||
|
||||
public byte FormatType
|
||||
{
|
||||
get
|
||||
{
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
public string LanguageString
|
||||
{
|
||||
get
|
||||
{
|
||||
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||
foreach (var culture in cultures)
|
||||
{
|
||||
// Exclude custom cultures.
|
||||
if ((culture.CultureTypes & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture)
|
||||
continue;
|
||||
|
||||
if (culture.ThreeLetterISOLanguageName == lang)
|
||||
return culture.EnglishName;
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct MPLSPlaylistItem
|
||||
{
|
||||
public string clip_id;
|
||||
public byte connection_condition;
|
||||
public byte stc_id;
|
||||
public uint in_time;
|
||||
public uint out_time;
|
||||
|
||||
public byte num_video;
|
||||
public byte num_audio;
|
||||
public byte num_pg;
|
||||
public byte num_ig;
|
||||
public byte num_secondary_audio;
|
||||
public byte num_secondary_video;
|
||||
public byte num_pip_pg;
|
||||
public List<MPLSStream> video;
|
||||
public List<MPLSStream> audio;
|
||||
public List<MPLSStream> pg;
|
||||
}
|
||||
|
||||
public struct MPLSHeader
|
||||
{
|
||||
public uint type_indicator;
|
||||
public uint type_indicator2;
|
||||
public uint list_pos;
|
||||
public uint mark_pos;
|
||||
public uint ext_pos;
|
||||
|
||||
public ushort list_count;
|
||||
public ushort sub_count;
|
||||
public ushort mark_count;
|
||||
|
||||
public List<MPLSPlaylistItem> play_item;
|
||||
public List<MPLSPlaylistMark> play_mark;
|
||||
}
|
||||
}
|
||||
45
CUETools.Codecs.MPEG/ATSI/DecoderSettings.cs
Normal file
45
CUETools.Codecs.MPEG/ATSI/DecoderSettings.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CUETools.Codecs.MPEG.ATSI
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class DecoderSettings : IAudioDecoderSettings
|
||||
{
|
||||
#region IAudioDecoderSettings implementation
|
||||
[Browsable(false)]
|
||||
public string Extension => "ifo";
|
||||
|
||||
[Browsable(false)]
|
||||
public string Name => "cuetools";
|
||||
|
||||
[Browsable(false)]
|
||||
public Type DecoderType => typeof(AudioDecoder);
|
||||
|
||||
[Browsable(false)]
|
||||
public int Priority => 2;
|
||||
|
||||
public IAudioDecoderSettings Clone()
|
||||
{
|
||||
return MemberwiseClone() as IAudioDecoderSettings;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public DecoderSettings()
|
||||
{
|
||||
this.Init();
|
||||
}
|
||||
|
||||
[DefaultValue(true)]
|
||||
public bool IgnoreShortItems { get; set; }
|
||||
|
||||
[Browsable(false)]
|
||||
public int? Stream { get; set; }
|
||||
|
||||
[Browsable(false)]
|
||||
public int? StreamId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,6 @@ namespace CUETools.Codecs.MPEG.BDLPCM
|
||||
{
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
public unsafe AudioDecoder(string path, Stream IO, int pid)
|
||||
: this(new DecoderSettings() { StreamId = pid }, path, IO)
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe AudioDecoder(DecoderSettings settings, string path, Stream IO)
|
||||
{
|
||||
_path = path;
|
||||
@@ -38,6 +33,8 @@ namespace CUETools.Codecs.MPEG.BDLPCM
|
||||
_IO = null;
|
||||
}
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
|
||||
@@ -7,11 +7,6 @@ namespace CUETools.Codecs.MPEG.MPLS
|
||||
{
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
public unsafe AudioDecoder(string path, Stream IO, ushort pid)
|
||||
: this(new DecoderSettings() { StreamId = pid }, path, IO)
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe AudioDecoder(DecoderSettings settings, string path, Stream IO)
|
||||
{
|
||||
m_settings = settings;
|
||||
@@ -33,7 +28,7 @@ namespace CUETools.Codecs.MPEG.MPLS
|
||||
|
||||
void openEntries()
|
||||
{
|
||||
readers = new List<BDLPCM.AudioDecoder>();
|
||||
readers = new List<IAudioSource>();
|
||||
var pids = new List<int>();
|
||||
foreach (var item in hdr_m.play_item)
|
||||
foreach (var audio in item.audio)
|
||||
@@ -67,7 +62,8 @@ namespace CUETools.Codecs.MPEG.MPLS
|
||||
var m2ts = System.IO.Path.Combine(
|
||||
System.IO.Path.Combine(parent.FullName, "STREAM"),
|
||||
item.clip_id + ".m2ts");
|
||||
var entry = new BDLPCM.AudioDecoder(m2ts, null, chosenPid);
|
||||
var settings = new BDLPCM.DecoderSettings() { StreamId = chosenPid };
|
||||
var entry = settings.Open(m2ts);
|
||||
readers.Add(entry);
|
||||
break;
|
||||
}
|
||||
@@ -434,8 +430,8 @@ namespace CUETools.Codecs.MPEG.MPLS
|
||||
byte[] contents;
|
||||
|
||||
AudioPCMConfig pcm;
|
||||
List<BDLPCM.AudioDecoder> readers;
|
||||
BDLPCM.AudioDecoder currentReader;
|
||||
List<IAudioSource> readers;
|
||||
IAudioSource currentReader;
|
||||
MPLSHeader hdr_m;
|
||||
DecoderSettings m_settings;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,11 @@ namespace TTA {
|
||||
}
|
||||
}
|
||||
|
||||
virtual property TimeSpan Duration {
|
||||
TimeSpan get() {
|
||||
return Length < 0 ? TimeSpan::Zero : TimeSpan::FromSeconds((double)Length / PCM->SampleRate);
|
||||
}
|
||||
}
|
||||
virtual property Int64 Length {
|
||||
Int64 get() {
|
||||
return _sampleCount;
|
||||
|
||||
@@ -252,6 +252,8 @@ namespace CUETools.Codecs.WMA
|
||||
m_syncReader = null;
|
||||
}
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace CUETools.Codecs.ffmpegdll
|
||||
//if (stream->duration > 0)
|
||||
// _sampleCount = stream->duration;
|
||||
//else
|
||||
_sampleCount = -1;
|
||||
_sampleCount = -1;
|
||||
|
||||
int bps = stream->codecpar->bits_per_raw_sample != 0 ?
|
||||
stream->codecpar->bits_per_raw_sample :
|
||||
@@ -279,6 +279,21 @@ namespace CUETools.Codecs.ffmpegdll
|
||||
|
||||
public string Path => _path;
|
||||
|
||||
public TimeSpan Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
// Sadly, duration is unreliable for most codecs.
|
||||
if (stream->codecpar->codec_id == AVCodecID.AV_CODEC_ID_MLP)
|
||||
return TimeSpan.Zero;
|
||||
if (stream->duration > 0)
|
||||
return TimeSpan.FromSeconds((double)stream->duration / stream->codecpar->sample_rate);
|
||||
if (fmt_ctx->duration > 0)
|
||||
return TimeSpan.FromSeconds((double)fmt_ctx->duration / ffmpeg.AV_TIME_BASE);
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public long Length => _sampleCount;
|
||||
|
||||
public long Position
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace CUETools.Codecs.libFLAC
|
||||
public string Name => "libFLAC";
|
||||
|
||||
[Browsable(false)]
|
||||
public Type DecoderType => typeof(Reader);
|
||||
public Type DecoderType => typeof(AudioDecoder);
|
||||
|
||||
[Browsable(false)]
|
||||
public int Priority => 1;
|
||||
@@ -37,9 +37,9 @@ namespace CUETools.Codecs.libFLAC
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe class Reader : IAudioSource
|
||||
public unsafe class AudioDecoder : IAudioSource
|
||||
{
|
||||
public Reader(DecoderSettings settings, string path, Stream IO)
|
||||
public AudioDecoder(DecoderSettings settings, string path, Stream IO)
|
||||
{
|
||||
m_settings = settings;
|
||||
|
||||
@@ -306,6 +306,8 @@ namespace CUETools.Codecs.libFLAC
|
||||
|
||||
public string Path => m_path;
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length => m_sampleCount;
|
||||
|
||||
private int SamplesInBuffer => m_bufferLength - m_bufferOffset;
|
||||
|
||||
@@ -99,6 +99,8 @@ namespace CUETools.Codecs.libwavpack
|
||||
|
||||
public string Path => _path;
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length => _sampleCount;
|
||||
|
||||
public long Position
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
|
||||
namespace CUETools.Codecs
|
||||
{
|
||||
@@ -44,5 +45,10 @@ namespace CUETools.Codecs
|
||||
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
|
||||
property.ResetValue(settings);
|
||||
}
|
||||
|
||||
public static IAudioSource Open(this IAudioDecoderSettings settings, string path, Stream IO = null)
|
||||
{
|
||||
return Activator.CreateInstance(settings.DecoderType, settings, path, IO) as IAudioSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace CUETools.Codecs
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
|
||||
@@ -27,6 +27,15 @@ namespace CUETools.Codecs.CommandLine
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
Initialize();
|
||||
return rdr.Duration;
|
||||
}
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace CUETools.Codecs
|
||||
using System;
|
||||
|
||||
namespace CUETools.Codecs
|
||||
{
|
||||
public interface IAudioSource
|
||||
{
|
||||
@@ -7,7 +9,8 @@
|
||||
AudioPCMConfig PCM { get; }
|
||||
string Path { get; }
|
||||
|
||||
long Length { get; }
|
||||
TimeSpan Duration { get; }
|
||||
long Length { get; }
|
||||
long Position { get; set; }
|
||||
long Remaining { get; }
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace CUETools.Codecs.NULL
|
||||
using System;
|
||||
|
||||
namespace CUETools.Codecs.NULL
|
||||
{
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
@@ -8,6 +10,8 @@
|
||||
|
||||
public IAudioDecoderSettings Settings => null;
|
||||
|
||||
public TimeSpan Duration => TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get { return _sampleCount; }
|
||||
|
||||
@@ -55,6 +55,8 @@ namespace CUETools.Codecs.WAV
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace CUETools.Converter
|
||||
|
||||
AudioBuffer buff = new AudioBuffer(audioSource, 0x10000);
|
||||
Console.Error.WriteLine("Filename : {0}", sourceFile);
|
||||
Console.Error.WriteLine("File Info : {0}kHz; {1} channel; {2} bit; {3}", audioSource.PCM.SampleRate, audioSource.PCM.ChannelCount, audioSource.PCM.BitsPerSample, TimeSpan.FromSeconds(audioSource.Length * 1.0 / audioSource.PCM.SampleRate));
|
||||
Console.Error.WriteLine("File Info : {0}kHz; {1} channel; {2} bit; {3}", audioSource.PCM.SampleRate, audioSource.PCM.ChannelCount, audioSource.PCM.BitsPerSample, audioSource.Duration);
|
||||
|
||||
CUEToolsFormat fmt;
|
||||
if (encoderFormat == null)
|
||||
@@ -226,15 +226,16 @@ namespace CUETools.Converter
|
||||
TimeSpan elapsed = DateTime.Now - start;
|
||||
if ((elapsed - lastPrint).TotalMilliseconds > 60)
|
||||
{
|
||||
long length = audioSource.Length;
|
||||
if (length < 0 && sourceInfo != null) length = (long)(sourceInfo.Properties.Duration.TotalMilliseconds * audioSource.PCM.SampleRate / 1000);
|
||||
if (length < audioSource.Position) length = audioSource.Position;
|
||||
if (length < 1) length = 1;
|
||||
var duration = audioSource.Duration;
|
||||
var position = TimeSpan.FromSeconds((double)audioSource.Position / audioSource.PCM.SampleRate);
|
||||
if (duration == TimeSpan.Zero && sourceInfo != null) duration = sourceInfo.Properties.Duration;
|
||||
if (duration < position) duration = position;
|
||||
if (duration < TimeSpan.FromSeconds(1)) duration = TimeSpan.FromSeconds(1);
|
||||
Console.Error.Write("\rProgress : {0:00}%; {1:0.00}x; {2}/{3}",
|
||||
100.0 * audioSource.Position / length,
|
||||
audioSource.Position / elapsed.TotalSeconds / audioSource.PCM.SampleRate,
|
||||
100.0 * position.TotalSeconds / duration.TotalSeconds,
|
||||
position.TotalSeconds / elapsed.TotalSeconds,
|
||||
elapsed,
|
||||
TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / audioSource.Position * length)
|
||||
TimeSpan.FromSeconds(elapsed.TotalSeconds / position.TotalSeconds * duration.TotalSeconds)
|
||||
);
|
||||
lastPrint = elapsed;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ namespace CUETools.DSP.Mixer
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public TimeSpan Duration
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace CUETools.Processor
|
||||
|
||||
public IAudioDecoderSettings Settings => null;
|
||||
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get { return _sampleLen; }
|
||||
|
||||
@@ -1149,7 +1149,9 @@ namespace CUETools.Ripper.SCSI
|
||||
return buff.Length;
|
||||
}
|
||||
|
||||
public long Length
|
||||
public TimeSpan Duration => TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -56,7 +56,9 @@ namespace CUETools.TestHelpers
|
||||
{
|
||||
}
|
||||
|
||||
public long Length
|
||||
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -126,7 +126,6 @@ namespace CUETools.eac3to
|
||||
var videos = new List<Codecs.MPEG.MPLS.MPLSStream>();
|
||||
var audios = new List<Codecs.MPEG.MPLS.MPLSStream>();
|
||||
List<uint> chapters;
|
||||
TimeSpan duration;
|
||||
TagLib.UserDefined.AdditionalFileTypes.Config = config;
|
||||
|
||||
#if !DEBUG
|
||||
@@ -147,7 +146,7 @@ namespace CUETools.eac3to
|
||||
Console.Error.WriteLine("M2TS, {0} video track{1}, {2} audio track{3}, {4}, {5}{6}",
|
||||
videos.Count, videos.Count > 1 ? "s" : "",
|
||||
audios.Count, audios.Count > 1 ? "s" : "",
|
||||
CDImageLayout.TimeToString(mpls.Duration, "{0:0}:{1:00}:{2:00}"), frameRate * (interlaced ? 2 : 1), interlaced ? "i" : "p");
|
||||
CDImageLayout.TimeToString(audioSource.Duration, "{0:0}:{1:00}:{2:00}"), frameRate * (interlaced ? 2 : 1), interlaced ? "i" : "p");
|
||||
//foreach (var item in mpls.MPLSHeader.play_item)
|
||||
//Console.Error.WriteLine("{0}.m2ts", item.clip_id);
|
||||
{
|
||||
@@ -178,8 +177,6 @@ namespace CUETools.eac3to
|
||||
Console.Error.WriteLine("{0}, {1}, {2}, {3}", audio.CodecString, audio.LanguageString, audio.FormatString, audio.RateString);
|
||||
}
|
||||
}
|
||||
|
||||
duration = mpls.Duration;
|
||||
}
|
||||
|
||||
if (destFile == null)
|
||||
@@ -323,7 +320,7 @@ namespace CUETools.eac3to
|
||||
AudioBuffer buff = new AudioBuffer(audioSource, 0x10000);
|
||||
Console.Error.WriteLine("Filename : {0}", sourceFile);
|
||||
Console.Error.WriteLine("File Info : {0}kHz; {1} channel; {2} bit; {3}", audioSource.PCM.SampleRate, audioSource.PCM.ChannelCount, audioSource.PCM.BitsPerSample,
|
||||
duration);
|
||||
audioSource.Duration);
|
||||
|
||||
CUEToolsFormat fmt;
|
||||
if (encoderFormat == null)
|
||||
@@ -397,14 +394,15 @@ namespace CUETools.eac3to
|
||||
TimeSpan elapsed = DateTime.Now - start;
|
||||
if ((elapsed - lastPrint).TotalMilliseconds > 60)
|
||||
{
|
||||
long length = (long)(duration.TotalSeconds * audioSource.PCM.SampleRate);
|
||||
if (length < audioSource.Position) length = audioSource.Position;
|
||||
if (length < 1) length = 1;
|
||||
var duration = audioSource.Duration;
|
||||
var position = TimeSpan.FromSeconds((double)audioSource.Position / audioSource.PCM.SampleRate);
|
||||
if (duration < position) duration = position;
|
||||
if (duration < TimeSpan.FromSeconds(1)) duration = TimeSpan.FromSeconds(1);
|
||||
Console.Error.Write("\rProgress : {0:00}%; {1:0.00}x; {2}/{3}",
|
||||
100.0 * audioSource.Position / length,
|
||||
audioSource.Position / elapsed.TotalSeconds / audioSource.PCM.SampleRate,
|
||||
100.0 * position.TotalSeconds / duration.TotalSeconds,
|
||||
position.TotalSeconds / elapsed.TotalSeconds,
|
||||
elapsed,
|
||||
TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / audioSource.Position * length)
|
||||
TimeSpan.FromSeconds(elapsed.TotalSeconds / position.TotalSeconds * duration.TotalSeconds)
|
||||
);
|
||||
lastPrint = elapsed;
|
||||
}
|
||||
|
||||
@@ -228,10 +228,11 @@ namespace BluTools
|
||||
|
||||
void workerExtract_DoWork(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
CUETools.Codecs.MPEG.MPLS.AudioDecoder reader = null;
|
||||
IAudioSource reader = null;
|
||||
try
|
||||
{
|
||||
reader = new CUETools.Codecs.MPEG.MPLS.AudioDecoder(chosenReader.Path, null, pid);
|
||||
var decoderSettings = new CUETools.Codecs.MPEG.MPLS.DecoderSettings() { StreamId = pid };
|
||||
reader = decoderSettings.Open(chosenReader.Path);
|
||||
Directory.CreateDirectory(outputFolderPath);
|
||||
if (File.Exists(outputCuePath)) throw new Exception(string.Format("File \"{0}\" already exists", outputCuePath));
|
||||
if (File.Exists(outputAudioPath)) throw new Exception(string.Format("File \"{0}\" already exists", outputAudioPath));
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace CUETools.TestCodecs
|
||||
[TestMethod()]
|
||||
public void SeekTest()
|
||||
{
|
||||
var r = new CUETools.Codecs.libFLAC.Reader(new Codecs.libFLAC.DecoderSettings(), "test.flac", null);
|
||||
var r = new Codecs.libFLAC.DecoderSettings().Open("test.flac", null);
|
||||
var buff1 = new AudioBuffer(r, 16536);
|
||||
var buff2 = new AudioBuffer(r, 16536);
|
||||
Assert.AreEqual(0, r.Position);
|
||||
|
||||
Reference in New Issue
Block a user