This commit is contained in:
Grigory Chudov
2018-10-21 14:12:25 -04:00
30 changed files with 1470 additions and 121 deletions

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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)
{

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
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; }
[Browsable(false)]
public int? Title { get; set; }
}
}

View File

@@ -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

View File

@@ -5,19 +5,15 @@ using System.IO;
namespace CUETools.Codecs.MPEG.MPLS
{
public class AudioDecoder : IAudioSource
public class AudioDecoder : IAudioSource, IAudioTitleSet
{
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;
_path = path;
_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000);
int length = (int)_IO.Length;
if (length > 0x100000) throw new Exception("File too big");
contents = new byte[length];
if (_IO.Read(contents, 0, length) != length) throw new Exception("");
fixed (byte* ptr = &contents[0])
@@ -33,7 +29,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 +63,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;
}
@@ -387,15 +384,30 @@ namespace CUETools.Codecs.MPEG.MPLS
}
}
public List<uint> Chapters
public List<IAudioTitle> AudioTitles
{
get
{
var titles = new List<IAudioTitle>();
foreach (var item in hdr_m.play_item)
foreach (var audio in item.audio)
{
//if (audio.coding_type != 0x80 /* LPCM */) continue;
titles.Add(new AudioTitle(this, audio.pid));
}
return titles;
}
}
public List<TimeSpan> Chapters
{
get
{
//settings.IgnoreShortItems
var res = new List<uint>();
var res = new List<TimeSpan>();
if (hdr_m.play_mark.Count < 1) return res;
if (hdr_m.play_item.Count < 1) return res;
res.Add(0);
res.Add(TimeSpan.Zero);
for (int i = 0; i < hdr_m.mark_count; i++)
{
ushort mark_item = hdr_m.play_mark[i].play_item_ref;
@@ -410,7 +422,7 @@ namespace CUETools.Codecs.MPEG.MPLS
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);
res.Add(TimeSpan.FromSeconds((hdr_m.play_mark[i].time - item_in_time + item_offset) / 45000.0));
}
uint end_offset = 0;
for (int j = 0; j < hdr_m.play_item.Count; j++)
@@ -420,9 +432,9 @@ namespace CUETools.Codecs.MPEG.MPLS
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);
res.Add(TimeSpan.FromSeconds(end_offset / 45000.0));
while (res.Count > 1 && res[1] - res[0] < TimeSpan.FromSeconds(1.0)) res.RemoveAt(1);
while (res.Count > 1 && res[res.Count - 1] - res[res.Count - 2] < TimeSpan.FromSeconds(1.0)) res.RemoveAt(res.Count - 2);
return res;
}
}
@@ -434,12 +446,64 @@ 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;
}
public class AudioTitle : IAudioTitle
{
public AudioTitle(AudioDecoder source, int pid)
{
this.source = source;
this.pid = pid;
}
public List<TimeSpan> Chapters => source.Chapters;
public AudioPCMConfig PCM
{
get
{
var s = FirstStream;
int channelCount = s.format == 1 ? 1 : s.format == 3 ? 2 : s.format == 6 ? 5 : 0;
int sampleRate = s.rate == 1 ? 48000 : s.rate == 4 ? 96000 : s.rate == 5 ? 192000 : s.rate == 12 ? 192000 : s.rate == 14 ? 96000 : 0;
int bitsPerSample = 0;
return new AudioPCMConfig(bitsPerSample, channelCount, sampleRate);
}
}
public string Codec
{
get
{
var s = FirstStream;
return s != null ? s.CodecString : "?";
}
}
public string Language
{
get
{
var s = FirstStream;
return s != null ? s.LanguageString : "?";
}
}
public int StreamId => pid;
MPLSStream FirstStream
{
get
{
MPLSStream result = null;
source.MPLSHeader.play_item.ForEach(i => i.audio.FindAll(a => a.pid == pid).ForEach(x => result = x));
return result;
}
}
AudioDecoder source;
int pid;
}
public struct MPLSPlaylistMark
{
public byte mark_id;
@@ -450,7 +514,7 @@ namespace CUETools.Codecs.MPEG.MPLS
public uint duration;
}
public struct MPLSStream
public class MPLSStream
{
public byte stream_type;
public byte coding_type;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -13,7 +13,7 @@ namespace CUETools.Codecs.ffmpegdll
#region IAudioDecoderSettings implementation
[Browsable(false)]
public string Name => "ffmpeg";
public string Name => "ffmpeg.dll";
[Browsable(false)]
public Type DecoderType => typeof(AudioDecoder);

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -32,7 +32,51 @@
KSAUDIO_SPEAKER_5POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
KSAUDIO_SPEAKER_5POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
KSAUDIO_SPEAKER_7POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER),
KSAUDIO_SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
KSAUDIO_SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
DVDAUDIO_GR1_0 = SPEAKER_FRONT_CENTER,
DVDAUDIO_GR1_1 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_2 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_3 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_4 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_5 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_6 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_7 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_8 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_9 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_10 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_11 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_12 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
DVDAUDIO_GR1_13 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
DVDAUDIO_GR1_14 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
DVDAUDIO_GR1_15 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
DVDAUDIO_GR1_16 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
DVDAUDIO_GR1_17 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
DVDAUDIO_GR1_18 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR1_19 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR1_20 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_0 = 0,
DVDAUDIO_GR2_1 = 0,
DVDAUDIO_GR2_2 = SPEAKER_BACK_CENTER,
DVDAUDIO_GR2_3 = SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_4 = SPEAKER_LOW_FREQUENCY,
DVDAUDIO_GR2_5 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
DVDAUDIO_GR2_6 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_7 = SPEAKER_FRONT_CENTER,
DVDAUDIO_GR2_8 = SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER,
DVDAUDIO_GR2_9 = SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_10 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY,
DVDAUDIO_GR2_11 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
DVDAUDIO_GR2_12 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_13 = SPEAKER_BACK_CENTER,
DVDAUDIO_GR2_14 = SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_15 = SPEAKER_LOW_FREQUENCY,
DVDAUDIO_GR2_16 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
DVDAUDIO_GR2_17 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
DVDAUDIO_GR2_18 = SPEAKER_LOW_FREQUENCY,
DVDAUDIO_GR2_19 = SPEAKER_FRONT_CENTER,
DVDAUDIO_GR2_20 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY,
}
private int _bitsPerSample;
@@ -46,6 +90,17 @@
public int BlockAlign { get { return _channelCount * ((_bitsPerSample + 7) / 8); } }
public SpeakerConfig ChannelMask { get { return _channelMask; } }
public bool IsRedBook { get { return _bitsPerSample == 16 && _channelCount == 2 && _sampleRate == 44100; } }
public static int ChannelsInMask(SpeakerConfig mask)
{
int count = 0;
while (mask != 0)
{
count++;
mask &= (mask - 1);
}
return count;
}
public static SpeakerConfig GetDefaultChannelMask(int channelCount)
{
switch (channelCount)

View File

@@ -54,6 +54,8 @@ namespace CUETools.Codecs
}
}
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
public long Length
{
get

View File

@@ -52,18 +52,18 @@ namespace CUETools.Codecs
if (Type.GetType("Mono.Runtime", false) == null)
{
encoders.Add(new CommandLine.EncoderSettings("flake", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11 12", "8", "flake.exe", "-%M - -o %O -p %P"));
encoders.Add(new CommandLine.EncoderSettings("takc", "tak", true, "0 1 2 2e 2m 3 3e 3m 4 4e 4m", "2", "takc.exe", "-e -p%M -overwrite - %O"));
encoders.Add(new CommandLine.EncoderSettings("ffmpeg alac", "m4a", true, "", "", "ffmpeg.exe", "-i - -f ipod -acodec alac -y %O"));
encoders.Add(new CommandLine.EncoderSettings("VBR (lame.exe)", "mp3", false, "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2", "lame.exe", "--vbr-new -%M - %O"));
encoders.Add(new CommandLine.EncoderSettings("CBR (lame.exe)", "mp3", false, "96 128 192 256 320", "256", "lame.exe", "-m s -q 0 -b %M --noreplaygain - %O"));
encoders.Add(new CommandLine.EncoderSettings("oggenc", "ogg", false, "-1 -0.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8", "3", "oggenc.exe", "-q %M - -o %O"));
encoders.Add(new CommandLine.EncoderSettings("opusenc", "opus", false, "6 16 32 48 64 96 128 192 256", "128", "opusenc.exe", "--bitrate %M - %O"));
encoders.Add(new CommandLine.EncoderSettings("nero aac", "m4a", false, "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9", "0.4", "neroAacEnc.exe", "-q %M -if - -of %O"));
encoders.Add(new CommandLine.EncoderSettings("qaac tvbr", "m4a", false, "10 20 30 40 50 60 70 80 90 100 110 127", "80", "qaac.exe", "-s -V %M -q 2 - -o %O"));
encoders.Add(new CommandLine.EncoderSettings("flake.exe", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11 12", "8", "flake.exe", "-%M - -o %O -p %P"));
encoders.Add(new CommandLine.EncoderSettings("takc.exe", "tak", true, "0 1 2 2e 2m 3 3e 3m 4 4e 4m", "2", "takc.exe", "-e -p%M -overwrite - %O"));
encoders.Add(new CommandLine.EncoderSettings("ffmpeg.exe", "m4a", true, "", "", "ffmpeg.exe", "-i - -f ipod -acodec alac -y %O"));
encoders.Add(new CommandLine.EncoderSettings("lame.exe (VBR)", "mp3", false, "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2", "lame.exe", "--vbr-new -%M - %O"));
encoders.Add(new CommandLine.EncoderSettings("lame.exe (CBR)", "mp3", false, "96 128 192 256 320", "256", "lame.exe", "-m s -q 0 -b %M --noreplaygain - %O"));
encoders.Add(new CommandLine.EncoderSettings("oggenc.exe", "ogg", false, "-1 -0.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8", "3", "oggenc.exe", "-q %M - -o %O"));
encoders.Add(new CommandLine.EncoderSettings("opusenc.exe", "opus", false, "6 16 32 48 64 96 128 192 256", "128", "opusenc.exe", "--bitrate %M - %O"));
encoders.Add(new CommandLine.EncoderSettings("neroAacEnc.exe", "m4a", false, "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9", "0.4", "neroAacEnc.exe", "-q %M -if - -of %O"));
encoders.Add(new CommandLine.EncoderSettings("qaac.exe (tvbr)", "m4a", false, "10 20 30 40 50 60 70 80 90 100 110 127", "80", "qaac.exe", "-s -V %M -q 2 - -o %O"));
decoders.Add(new CommandLine.DecoderSettings("takc", "tak", "takc.exe", "-d %I -"));
decoders.Add(new CommandLine.DecoderSettings("ffmpeg alac", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -"));
decoders.Add(new CommandLine.DecoderSettings("takc.exe", "tak", "takc.exe", "-d %I -"));
decoders.Add(new CommandLine.DecoderSettings("ffmpeg.exe", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -"));
}
else
{

View File

@@ -27,6 +27,15 @@ namespace CUETools.Codecs.CommandLine
}
}
public TimeSpan Duration
{
get
{
Initialize();
return rdr.Duration;
}
}
public long Length
{
get

View File

@@ -1,4 +1,7 @@
namespace CUETools.Codecs
using System;
using System.Collections.Generic;
namespace CUETools.Codecs
{
public interface IAudioSource
{
@@ -7,11 +10,73 @@
AudioPCMConfig PCM { get; }
string Path { get; }
long Length { get; }
TimeSpan Duration { get; }
long Length { get; }
long Position { get; set; }
long Remaining { get; }
int Read(AudioBuffer buffer, int maxLength);
void Close();
}
public interface IAudioTitle
{
List<TimeSpan> Chapters { get; }
AudioPCMConfig PCM { get; }
string Codec { get; }
string Language { get; }
int StreamId { get; }
//IAudioSource Open { get; }
}
public interface IAudioTitleSet
{
List<IAudioTitle> AudioTitles { get; }
}
public static class IAudioTitleExtensions
{
public static TimeSpan GetDuration(this IAudioTitle title)
{
var chapters = title.Chapters;
return chapters[chapters.Count - 1];
}
public static string GetRateString(this IAudioTitle title)
{
var sr = title.PCM.SampleRate;
if (sr % 1000 == 0) return $"{sr / 1000}KHz";
if (sr % 100 == 0) return $"{sr / 100}.{(sr / 100) % 10}KHz";
return $"{sr}Hz";
}
public static string GetFormatString(this IAudioTitle title)
{
switch (title.PCM.ChannelCount)
{
case 1: return "mono";
case 2: return "stereo";
default: return "multi-channel";
}
}
}
public class SingleAudioTitle : IAudioTitle
{
public SingleAudioTitle(IAudioSource source) { this.source = source; }
public List<TimeSpan> Chapters => new List<TimeSpan> { TimeSpan.Zero, source.Duration };
public AudioPCMConfig PCM => source.PCM;
public string Codec => source.Settings.Extension;
public string Language => "";
public int StreamId => 0;
IAudioSource source;
}
public class SingleAudioTitleSet : IAudioTitleSet
{
public SingleAudioTitleSet(IAudioSource source) { this.source = source; }
public List<IAudioTitle> AudioTitles => new List<IAudioTitle> { new SingleAudioTitle(source) };
IAudioSource source;
}
}

View File

@@ -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; }

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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(); }

View File

@@ -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; }

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -4,6 +4,7 @@ using CUETools.CTDB;
using CUETools.Processor;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
@@ -49,6 +50,7 @@ namespace CUETools.eac3to
string encoderName = null;
string encoderFormat = null;
AudioEncoderType audioEncoderType = AudioEncoderType.NoAudio;
var decoderOptions = new Dictionary<string, string>();
bool queryMeta = false;
for (int arg = 0; arg < args.Length; arg++)
@@ -61,14 +63,20 @@ namespace CUETools.eac3to
encoderFormat = args[arg];
else if ((args[arg] == "-p" || args[arg] == "--padding") && ++arg < args.Length)
ok = int.TryParse(args[arg], out padding);
else if ((args[arg] == "-m" || args[arg] == "--mode") && ++arg < args.Length)
encoderMode = args[arg];
else if ((args[arg] == "-m" || args[arg] == "--mode") && arg + 1 < args.Length)
encoderMode = args[++arg];
else if (args[arg] == "--lossy")
audioEncoderType = AudioEncoderType.Lossy;
else if (args[arg] == "--lossless")
audioEncoderType = AudioEncoderType.Lossless;
else if (args[arg] == "--ctdb")
queryMeta = true;
else if (args[arg] == "--decoder-option" && arg + 2 < args.Length)
{
var optionName = args[++arg];
var optionValue = args[++arg];
decoderOptions.Add(optionName, optionValue);
}
else if ((args[arg][0] != '-' || args[arg] == "-") && sourceFile == null)
sourceFile = args[arg];
else if ((args[arg][0] != '-' || args[arg] == "-") && sourceFile != null && destFile == null)
@@ -122,11 +130,11 @@ namespace CUETools.eac3to
#endif
{
IAudioSource audioSource = null;
IAudioTitleSet audioContainer = null;
IAudioDest audioDest = null;
var videos = new List<Codecs.MPEG.MPLS.MPLSStream>();
var audios = new List<Codecs.MPEG.MPLS.MPLSStream>();
List<uint> chapters;
TimeSpan duration;
List<IAudioTitle> audios = null;
List<TimeSpan> chapters = null;
TagLib.UserDefined.AdditionalFileTypes.Config = config;
#if !DEBUG
@@ -135,19 +143,43 @@ namespace CUETools.eac3to
{
if (true)
{
var mpls = new Codecs.MPEG.MPLS.AudioDecoder(new Codecs.MPEG.MPLS.DecoderSettings(), sourceFile, null);
audioSource = mpls;
IAudioDecoderSettings decoderSettings = null;
if (Path.GetExtension(sourceFile) == ".mpls")
{
decoderSettings = new Codecs.MPEG.MPLS.DecoderSettings();
} else
{
decoderSettings = new Codecs.MPEG.ATSI.DecoderSettings();
}
foreach (var decOpt in decoderOptions)
{
var property = TypeDescriptor.GetProperties(decoderSettings).Find(decOpt.Key, true);
if (property == null)
throw new Exception($"{decoderSettings.Name} {decoderSettings.Extension} decoder settings object (of type {decoderSettings.GetType().FullName}) doesn't have a property named {decOpt.Key}.");
property.SetValue(decoderSettings,
TypeDescriptor.GetConverter(property.PropertyType).ConvertFromString(decOpt.Value));
}
audioSource = decoderSettings.Open(sourceFile);
audioContainer = audioSource as IAudioTitleSet;
if (audioContainer == null) audioContainer = new SingleAudioTitleSet(audioSource);
Console.ForegroundColor = ConsoleColor.White;
int frameRate = 0;
bool interlaced = false;
chapters = mpls.Chapters;
mpls.MPLSHeader.play_item.ForEach(i => i.video.ForEach(v => { if (!videos.Exists(v1 => v1.pid == v.pid)) videos.Add(v); }));
mpls.MPLSHeader.play_item.ForEach(i => i.audio.ForEach(v => { if (!audios.Exists(v1 => v1.pid == v.pid)) audios.Add(v); }));
audios = audioContainer.AudioTitles;
audios.ForEach(t => chapters = t.Chapters);
if (audioSource is Codecs.MPEG.MPLS.AudioDecoder)
{
var mpls = audioSource as Codecs.MPEG.MPLS.AudioDecoder;
mpls.MPLSHeader.play_item.ForEach(i => i.video.ForEach(v => { if (!videos.Exists(v1 => v1.pid == v.pid)) videos.Add(v); }));
}
videos.ForEach(v => { frameRate = v.FrameRate; interlaced = v.Interlaced; });
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");
Console.Error.Write($@"M2TS, {
videos.Count} video track{(videos.Count != 1 ? "s" : "")}, {
audios.Count} audio track{(audios.Count != 1 ? "s" : "")}, {
CDImageLayout.TimeToString(audios[0].GetDuration(), "{0:0}:{1:00}:{2:00}")}, {
(frameRate * (interlaced ? 2 : 1))}{
(interlaced ? "i" : "p")}");
Console.Error.WriteLine();
//foreach (var item in mpls.MPLSHeader.play_item)
//Console.Error.WriteLine("{0}.m2ts", item.clip_id);
{
@@ -175,11 +207,9 @@ namespace CUETools.eac3to
Console.Error.Write(id++);
Console.Error.Write(": ");
Console.ForegroundColor = ConsoleColor.Gray;
Console.Error.WriteLine("{0}, {1}, {2}, {3}", audio.CodecString, audio.LanguageString, audio.FormatString, audio.RateString);
Console.Error.WriteLine("{0}, {1}, {2}, {3}", audio.Codec, audio.Language, audio.GetFormatString(), audio.GetRateString());
}
}
duration = mpls.Duration;
}
if (destFile == null)
@@ -187,7 +217,7 @@ namespace CUETools.eac3to
string strtoc = "";
for (int i = 0; i < chapters.Count; i++)
strtoc += string.Format(" {0}", chapters[i] / 600);
strtoc += string.Format(" {0}", (int)Math.Round((chapters[i].TotalSeconds * 75)));
strtoc = strtoc.Substring(1);
CDImageLayout toc = new CDImageLayout(strtoc);
CTDBResponseMeta meta = null;
@@ -238,9 +268,9 @@ namespace CUETools.eac3to
for (int i = 0; i < chapters.Count - 1; i++)
{
sw.WriteLine("CHAPTER{0:00}={1}", i + 1,
CDImageLayout.TimeToString(TimeSpan.FromSeconds(chapters[i] / 45000.0)));
if (meta != null && meta.track.Length >= toc[i+1].Number)
sw.WriteLine("CHAPTER{0:00}NAME={1}", i + 1, meta.track[(int)toc[i+1].Number - 1].name);
CDImageLayout.TimeToString(chapters[i]));
if (meta != null && meta.track.Length >= toc[i + 1].Number)
sw.WriteLine("CHAPTER{0:00}NAME={1}", i + 1, meta.track[(int)toc[i + 1].Number - 1].name);
else
sw.WriteLine("CHAPTER{0:00}NAME=", i + 1);
}
@@ -309,21 +339,21 @@ namespace CUETools.eac3to
throw new Exception("Unknown encoder format: " + destFile);
}
if (stream - chapterStreams <= videos.Count)
throw new Exception("Video extraction not supported.");
if (stream - chapterStreams - videos.Count > audios.Count)
throw new Exception(string.Format("The source file doesn't contain a track with the number {0}.", stream));
int streamId = audios[stream - chapterStreams - videos.Count - 1].StreamId;
if (audioSource is Codecs.MPEG.MPLS.AudioDecoder)
{
if (stream - chapterStreams <= videos.Count)
throw new Exception("Video extraction not supported.");
if (stream - chapterStreams - videos.Count > audios.Count)
throw new Exception(string.Format("The source file doesn't contain a track with the number {0}.", stream));
int pid = audios[stream - chapterStreams - videos.Count - 1].pid;
(audioSource.Settings as Codecs.MPEG.MPLS.DecoderSettings).StreamId = pid;
(audioSource.Settings as Codecs.MPEG.MPLS.DecoderSettings).StreamId = streamId;
}
}
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 +427,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;
}

View File

@@ -125,7 +125,7 @@ namespace BluTools
{
string strtoc = "";
for (int i = 0; i < chapters.Count; i++)
strtoc += string.Format(" {0}", chapters[i] / 600);
strtoc += string.Format(" {0}", (int) Math.Round((chapters[i].TotalSeconds * 75)));
strtoc = strtoc.Substring(1);
CDImageLayout toc = new CDImageLayout(strtoc);
ctdb = new CUEToolsDB(toc, null);
@@ -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));

View File

@@ -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);