mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
1) Support for user-defined external commandline codecs, tested on TAK
2) Better support for zip archives, which previously worked only with flac/wav files 3) More reliable tag handling, using taglib-sharp. Pictures are now preserved. 4) ALAC decoder bug fixed
This commit is contained in:
@@ -15,7 +15,7 @@ using System.Collections.Specialized;
|
||||
namespace CUETools.Processor
|
||||
{
|
||||
public static class AudioReadWrite {
|
||||
public static IAudioSource GetAudioSource(string path, Stream IO, string extension)
|
||||
public static IAudioSource GetAudioSource(string path, Stream IO, string extension, CUEConfig config)
|
||||
{
|
||||
switch (extension)
|
||||
{
|
||||
@@ -34,25 +34,27 @@ namespace CUETools.Processor
|
||||
return new TTAReader(path, IO);
|
||||
#endif
|
||||
default:
|
||||
if (extension == "." + config.udc1Extension && config.udc1Decoder != "")
|
||||
return new UserDefinedReader(path, IO, config.udc1Decoder, config.udc1Params, config.udc1APEv2);
|
||||
throw new Exception("Unsupported audio type: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
public static IAudioSource GetAudioSource(string path, Stream IO)
|
||||
public static IAudioSource GetAudioSource(string path, Stream IO, CUEConfig config)
|
||||
{
|
||||
string extension = Path.GetExtension(path).ToLower();
|
||||
string filename = Path.GetFileNameWithoutExtension(path);
|
||||
string secondExtension = Path.GetExtension(filename).ToLower();
|
||||
if (secondExtension != ".lossy" && secondExtension != ".lwcdf")
|
||||
return GetAudioSource(path, IO, extension);
|
||||
return GetAudioSource(path, IO, extension, config);
|
||||
|
||||
string lossyPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(filename) + ".lossy" + extension);
|
||||
string lwcdfPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(filename) + ".lwcdf" + extension);
|
||||
IAudioSource lossySource = GetAudioSource(lossyPath, null, extension);
|
||||
IAudioSource lossySource = GetAudioSource(lossyPath, null, extension, config);
|
||||
IAudioSource lwcdfSource = null;
|
||||
try
|
||||
{
|
||||
lwcdfSource = GetAudioSource(lwcdfPath, null, extension);
|
||||
lwcdfSource = GetAudioSource(lwcdfPath, null, extension, config);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -91,6 +93,11 @@ namespace CUETools.Processor
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (extension == "." + config.udc1Extension && config.udc1Encoder != "")
|
||||
{
|
||||
dest = new UserDefinedWriter(path, bitsPerSample, channelCount, sampleRate, null, config.udc1Encoder, config.udc1EncParams);
|
||||
break;
|
||||
}
|
||||
throw new Exception("Unsupported audio type: " + path);
|
||||
}
|
||||
dest.FinalSampleCount = finalSampleCount;
|
||||
|
||||
@@ -99,8 +99,14 @@
|
||||
<Compile Include="Processor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Tagging.cs" />
|
||||
<Compile Include="UserDefined.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\taglib-sharp\taglib-sharp.csproj">
|
||||
<Project>{4CC18776-125E-4318-9D24-D60110AD9697}</Project>
|
||||
<Name>taglib-sharp</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\CUETools.Codecs.ALAC\CUETools.Codecs.ALAC.csproj">
|
||||
<Project>{F2EC7193-D5E5-4252-9803-5CEB407E910F}</Project>
|
||||
<Name>CUETools.Codecs.ALAC</Name>
|
||||
|
||||
@@ -53,7 +53,8 @@ namespace CUETools.Processor
|
||||
WavPack,
|
||||
APE,
|
||||
TTA,
|
||||
NoAudio
|
||||
NoAudio,
|
||||
UDC1
|
||||
}
|
||||
|
||||
public enum AccurateRipMode
|
||||
@@ -74,7 +75,7 @@ namespace CUETools.Processor
|
||||
}
|
||||
|
||||
public static class General {
|
||||
public static string FormatExtension(OutputAudioFormat value)
|
||||
public static string FormatExtension(OutputAudioFormat value, CUEConfig config)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
@@ -84,6 +85,7 @@ namespace CUETools.Processor
|
||||
case OutputAudioFormat.TTA: return ".tta";
|
||||
case OutputAudioFormat.WAV: return ".wav";
|
||||
case OutputAudioFormat.NoAudio: return ".dummy";
|
||||
case OutputAudioFormat.UDC1: return "." + config.udc1Extension;
|
||||
}
|
||||
return ".wav";
|
||||
}
|
||||
@@ -270,6 +272,8 @@ namespace CUETools.Processor
|
||||
public bool lossyWAVHybrid;
|
||||
public bool decodeHDCDtoLW16;
|
||||
public bool decodeHDCDto24bit;
|
||||
public string udc1Extension, udc1Decoder, udc1Params, udc1Encoder, udc1EncParams;
|
||||
public bool udc1APEv2;
|
||||
|
||||
public CUEConfig()
|
||||
{
|
||||
@@ -316,6 +320,9 @@ namespace CUETools.Processor
|
||||
lossyWAVHybrid = true;
|
||||
decodeHDCDtoLW16 = false;
|
||||
decodeHDCDto24bit = true;
|
||||
|
||||
udc1Extension = udc1Decoder = udc1Params = udc1Encoder = udc1EncParams = "";
|
||||
udc1APEv2 = false;
|
||||
}
|
||||
|
||||
public void Save (SettingsWriter sw)
|
||||
@@ -363,6 +370,15 @@ namespace CUETools.Processor
|
||||
sw.Save("LossyWAVHybrid", lossyWAVHybrid);
|
||||
sw.Save("DecodeHDCDToLossyWAV16", decodeHDCDtoLW16);
|
||||
sw.Save("DecodeHDCDTo24bit", decodeHDCDto24bit);
|
||||
if (udc1Extension != "")
|
||||
{
|
||||
sw.Save("UDC1Extension", udc1Extension);
|
||||
sw.Save("UDC1Decoder", udc1Decoder);
|
||||
sw.Save("UDC1Params", udc1Params);
|
||||
sw.Save("UDC1Encoder", udc1Encoder);
|
||||
sw.Save("UDC1EncParams", udc1EncParams);
|
||||
sw.Save("UDC1APEv2", udc1APEv2);
|
||||
}
|
||||
}
|
||||
|
||||
public void Load(SettingsReader sr)
|
||||
@@ -410,6 +426,13 @@ namespace CUETools.Processor
|
||||
lossyWAVHybrid = sr.LoadBoolean("LossyWAVHybrid") ?? true;
|
||||
decodeHDCDtoLW16 = sr.LoadBoolean("DecodeHDCDToLossyWAV16") ?? false;
|
||||
decodeHDCDto24bit = sr.LoadBoolean("DecodeHDCDTo24bit") ?? true;
|
||||
|
||||
udc1Extension = sr.Load("UDC1Extension") ?? "";
|
||||
udc1Decoder = sr.Load("UDC1Decoder") ?? "";
|
||||
udc1Params = sr.Load("UDC1Params") ?? "";
|
||||
udc1Encoder = sr.Load("UDC1Encoder") ?? "";
|
||||
udc1EncParams = sr.Load("UDC1EncParams") ?? "";
|
||||
udc1APEv2 = sr.LoadBoolean("UDC1APEv2") ?? false;
|
||||
}
|
||||
|
||||
public string CleanseString (string s)
|
||||
@@ -484,7 +507,7 @@ namespace CUETools.Processor
|
||||
private string _mbReleaseId;
|
||||
private string _eacLog;
|
||||
private string _cuePath;
|
||||
private NameValueCollection _albumTags;
|
||||
private TagLib.File _fileInfo;
|
||||
private const int _arOffsetRange = 5 * 588 - 1;
|
||||
private HDCDDotNet.HDCDDotNet hdcdDecoder;
|
||||
private bool _outputLossyWAV = false;
|
||||
@@ -516,7 +539,6 @@ namespace CUETools.Processor
|
||||
_toc = new CDImageLayout();
|
||||
_sources = new List<SourceInfo>();
|
||||
_sourcePaths = new List<string>();
|
||||
_albumTags = new NameValueCollection();
|
||||
_stop = false;
|
||||
_pause = false;
|
||||
_cuePath = null;
|
||||
@@ -701,7 +723,7 @@ namespace CUETools.Processor
|
||||
bool seenFirstFileIndex = false;
|
||||
List<IndexInfo> indexes = new List<IndexInfo>();
|
||||
IndexInfo indexInfo;
|
||||
NameValueCollection _trackTags = null;
|
||||
TagLib.File _trackFileInfo = null;
|
||||
TextReader sr;
|
||||
|
||||
if (Directory.Exists(pathIn))
|
||||
@@ -712,6 +734,8 @@ namespace CUETools.Processor
|
||||
string[] audioExts = new string[] { "*.wav", "*.flac", "*.wv", "*.ape", "*.m4a", "*.tta" };
|
||||
for (i = 0; i < audioExts.Length && cueSheet == null; i++)
|
||||
cueSheet = CUESheet.CreateDummyCUESheet(pathIn, audioExts[i]);
|
||||
if (_config.udc1Extension != null && cueSheet == null)
|
||||
cueSheet = CUESheet.CreateDummyCUESheet(pathIn, "*." + _config.udc1Extension);
|
||||
if (cueSheet == null)
|
||||
throw new Exception("Input directory doesn't contain supported audio files.");
|
||||
sr = new StringReader(cueSheet);
|
||||
@@ -839,7 +863,7 @@ namespace CUETools.Processor
|
||||
sr = new StreamReader (pathIn, CUESheet.Encoding);
|
||||
|
||||
string logPath = Path.ChangeExtension(pathIn, ".log");
|
||||
if (File.Exists(logPath))
|
||||
if (System.IO.File.Exists(logPath))
|
||||
{
|
||||
StreamReader logReader = new StreamReader(logPath, CUESheet.Encoding);
|
||||
_eacLog = logReader.ReadToEnd();
|
||||
@@ -848,7 +872,7 @@ namespace CUETools.Processor
|
||||
else if (CUEToolsSelection != null)
|
||||
{
|
||||
CUEToolsSelectionEventArgs e = new CUEToolsSelectionEventArgs();
|
||||
e.choices = Directory.GetFiles(cueDir, "*.log");
|
||||
e.choices = Directory.GetFiles(cueDir == "" ? "." : cueDir, "*.log");
|
||||
if (e.choices.Length > 0)
|
||||
{
|
||||
CUEToolsSelection(this, e);
|
||||
@@ -862,18 +886,15 @@ namespace CUETools.Processor
|
||||
}
|
||||
} else
|
||||
{
|
||||
IAudioSource audioSource;
|
||||
NameValueCollection tags;
|
||||
string cuesheetTag = null;
|
||||
|
||||
audioSource = AudioReadWrite.GetAudioSource(pathIn,null);
|
||||
tags = audioSource.Tags;
|
||||
TagLib.File fileInfo;
|
||||
GetSampleLength(pathIn, out fileInfo);
|
||||
NameValueCollection tags = Tagging.Analyze(fileInfo);
|
||||
cuesheetTag = tags.Get("CUESHEET");
|
||||
_accurateRipId = tags.Get("ACCURATERIPID");
|
||||
_eacLog = tags.Get("LOG");
|
||||
if (_eacLog == null) _eacLog = tags.Get("LOGFILE");
|
||||
if (_eacLog == null) _eacLog = tags.Get("EACLOG");
|
||||
audioSource.Close();
|
||||
if (cuesheetTag == null)
|
||||
throw new Exception("Input file does not contain a .cue sheet.");
|
||||
sr = new StringReader (cuesheetTag);
|
||||
@@ -909,8 +930,8 @@ namespace CUETools.Processor
|
||||
}
|
||||
_sourcePaths.Add(pathAudio);
|
||||
absoluteFileStartTime += fileTimeLengthFrames;
|
||||
NameValueCollection tags;
|
||||
fileTimeLengthSamples = GetSampleLength(pathAudio, out tags);
|
||||
TagLib.File fileInfo;
|
||||
fileTimeLengthSamples = GetSampleLength(pathAudio, out fileInfo);
|
||||
if ((fileTimeLengthSamples % 588) == 492 && _config.truncate4608ExtraSamples)
|
||||
{
|
||||
_truncated4608 = true;
|
||||
@@ -918,9 +939,9 @@ namespace CUETools.Processor
|
||||
}
|
||||
fileTimeLengthFrames = (int)((fileTimeLengthSamples + 587) / 588);
|
||||
if (_hasEmbeddedCUESheet)
|
||||
_albumTags = tags;
|
||||
_fileInfo = fileInfo;
|
||||
else
|
||||
_trackTags = tags;
|
||||
_trackFileInfo = fileInfo;
|
||||
seenFirstFileIndex = false;
|
||||
}
|
||||
}
|
||||
@@ -947,8 +968,9 @@ namespace CUETools.Processor
|
||||
seenFirstFileIndex = true;
|
||||
if (isAudioTrack)
|
||||
{
|
||||
if (_tracks.Count > 0 && _trackTags != null && _trackTags.Count != 0)
|
||||
_tracks[_tracks.Count - 1]._trackTags = _trackTags;
|
||||
if (_tracks.Count > 0 && _trackFileInfo != null)
|
||||
_tracks[_tracks.Count - 1]._fileInfo = _trackFileInfo;
|
||||
_trackFileInfo = null;
|
||||
sourceInfo.Path = pathAudio;
|
||||
sourceInfo.Offset = 0;
|
||||
sourceInfo.Length = (uint)fileTimeLengthSamples;
|
||||
@@ -1116,30 +1138,46 @@ namespace CUETools.Processor
|
||||
}
|
||||
if (!_hasEmbeddedCUESheet && _hasSingleFilename)
|
||||
{
|
||||
_albumTags = _tracks[0]._trackTags;
|
||||
_tracks[0]._trackTags = new NameValueCollection();
|
||||
_fileInfo = _tracks[0]._fileInfo;
|
||||
_tracks[0]._fileInfo = null;
|
||||
}
|
||||
if (_config.fillUpCUE)
|
||||
{
|
||||
if ((_config.overwriteCUEData || General.FindCUELine(_attributes, "PERFORMER") == null) && GetCommonTag("ALBUM ARTIST") != null)
|
||||
General.SetCUELine(_attributes, "PERFORMER", GetCommonTag("ALBUM ARTIST"), true);
|
||||
if ((_config.overwriteCUEData || General.FindCUELine(_attributes, "PERFORMER") == null) && GetCommonTag("ARTIST") != null)
|
||||
General.SetCUELine(_attributes, "PERFORMER", GetCommonTag("ARTIST"), true);
|
||||
if ((_config.overwriteCUEData || General.FindCUELine(_attributes, "TITLE") == null) && GetCommonTag("ALBUM") != null)
|
||||
General.SetCUELine(_attributes, "TITLE", GetCommonTag("ALBUM"), true);
|
||||
if ((_config.overwriteCUEData || General.FindCUELine(_attributes, "REM", "DATE") == null) && GetCommonTag("DATE") != null)
|
||||
General.SetCUELine(_attributes, "REM", "DATE", GetCommonTag("DATE"), false);
|
||||
if ((_config.overwriteCUEData || General.FindCUELine(_attributes, "REM", "DATE") == null) && GetCommonTag("YEAR") != null)
|
||||
General.SetCUELine(_attributes, "REM", "DATE", GetCommonTag("YEAR"), false);
|
||||
if ((_config.overwriteCUEData || General.FindCUELine(_attributes, "REM", "GENRE") == null) && GetCommonTag("GENRE") != null)
|
||||
General.SetCUELine(_attributes, "REM", "GENRE", GetCommonTag("GENRE"), true);
|
||||
if (_config.overwriteCUEData || General.FindCUELine(_attributes, "PERFORMER") == null)
|
||||
{
|
||||
string value = GetCommonTag(delegate(TagLib.File file) { return file.Tag.JoinedAlbumArtists; });
|
||||
if (value == null)
|
||||
value = GetCommonTag(delegate(TagLib.File file) { return file.Tag.JoinedPerformers; });
|
||||
if (value != null)
|
||||
General.SetCUELine(_attributes, "PERFORMER", value, true);
|
||||
}
|
||||
if (_config.overwriteCUEData || General.FindCUELine(_attributes, "TITLE") == null)
|
||||
{
|
||||
string value = GetCommonTag(delegate(TagLib.File file) { return file.Tag.Album; });
|
||||
if (value != null)
|
||||
General.SetCUELine(_attributes, "TITLE", value, true);
|
||||
}
|
||||
if (_config.overwriteCUEData || General.FindCUELine(_attributes, "REM", "DATE") == null)
|
||||
{
|
||||
string value = GetCommonTag(delegate(TagLib.File file) { return file.Tag.Year != 0 ? file.Tag.Year.ToString() : null; });
|
||||
if (value != null)
|
||||
General.SetCUELine(_attributes, "REM", "DATE", value, false);
|
||||
}
|
||||
if (_config.overwriteCUEData || General.FindCUELine(_attributes, "REM", "GENRE") == null)
|
||||
{
|
||||
string value = GetCommonTag(delegate(TagLib.File file) { return file.Tag.JoinedGenres; });
|
||||
if (value != null)
|
||||
General.SetCUELine(_attributes, "REM", "GENRE", value, true);
|
||||
}
|
||||
for (i = 0; i < TrackCount; i++)
|
||||
{
|
||||
TrackInfo track = _tracks[i];
|
||||
string artist = _hasTrackFilenames ? track._trackTags.Get("ARTIST") :
|
||||
_hasEmbeddedCUESheet ? _albumTags.Get(String.Format("cue_track{0:00}_ARTIST", i + 1)) : null;
|
||||
string title = _hasTrackFilenames ? track._trackTags.Get("TITLE") :
|
||||
_hasEmbeddedCUESheet ? _albumTags.Get(String.Format("cue_track{0:00}_TITLE", i + 1)) : null;
|
||||
string artist = _hasTrackFilenames ? track._fileInfo.Tag.JoinedPerformers :
|
||||
_hasEmbeddedCUESheet ? Tagging.TagListToSingleValue(Tagging.GetMiscTag(_fileInfo, String.Format("cue_track{0:00}_ARTIST", i + 1))) :
|
||||
null;
|
||||
string title = _hasTrackFilenames ? track._fileInfo.Tag.Title :
|
||||
_hasEmbeddedCUESheet ? Tagging.TagListToSingleValue(Tagging.GetMiscTag(_fileInfo, String.Format("cue_track{0:00}_TITLE", i + 1))) :
|
||||
null;
|
||||
if ((_config.overwriteCUEData || track.Artist == "") && artist != null)
|
||||
track.Artist = artist;
|
||||
if ((_config.overwriteCUEData || track.Title == "") && title != null)
|
||||
@@ -1149,10 +1187,11 @@ namespace CUETools.Processor
|
||||
|
||||
CUELine cddbDiscIdLine = General.FindCUELine(_attributes, "REM", "DISCID");
|
||||
_cddbDiscIdTag = cddbDiscIdLine != null && cddbDiscIdLine.Params.Count == 3 ? cddbDiscIdLine.Params[2] : null;
|
||||
if (_cddbDiscIdTag == null) _cddbDiscIdTag = GetCommonTag("DISCID");
|
||||
if (_cddbDiscIdTag == null)
|
||||
_cddbDiscIdTag = GetCommonMiscTag("DISCID");
|
||||
|
||||
if (_accurateRipId == null)
|
||||
_accurateRipId = GetCommonTag("ACCURATERIPID");
|
||||
_accurateRipId = GetCommonMiscTag("ACCURATERIPID");
|
||||
|
||||
if (_accurateRipId == null)
|
||||
{
|
||||
@@ -1291,7 +1330,7 @@ namespace CUETools.Processor
|
||||
}
|
||||
}
|
||||
|
||||
private Stream OpenArchive(string fileName, bool showProgress)
|
||||
internal Stream OpenArchive(string fileName, bool showProgress)
|
||||
{
|
||||
#if !MONO
|
||||
if (Path.GetExtension(_archivePath).ToLower() == ".rar")
|
||||
@@ -1305,18 +1344,10 @@ namespace CUETools.Processor
|
||||
#endif
|
||||
if (Path.GetExtension(_archivePath).ToLower() == ".zip")
|
||||
{
|
||||
ZipInputStream zipStream = new ZipInputStream(File.OpenRead(_archivePath));
|
||||
ZipEntry theEntry = null;
|
||||
while ((theEntry = zipStream.GetNextEntry()) != null)
|
||||
if (theEntry.Name == fileName)
|
||||
break;
|
||||
if (theEntry == null)
|
||||
throw new Exception("Archive entry not found.");
|
||||
if (theEntry.IsCrypted)
|
||||
{
|
||||
unrar_PasswordRequired(this, new PasswordRequiredEventArgs());
|
||||
zipStream.Password = _archivePassword;
|
||||
}
|
||||
SeekableZipStream zipStream = new SeekableZipStream(_archivePath, fileName);
|
||||
zipStream.PasswordRequired += new PasswordRequiredHandler(unrar_PasswordRequired);
|
||||
if (showProgress)
|
||||
zipStream.ExtractionProgress += new ExtractionProgressHandler(unrar_ExtractionProgress);
|
||||
return zipStream;
|
||||
}
|
||||
throw new Exception("Unknown archive type.");
|
||||
@@ -1403,10 +1434,12 @@ namespace CUETools.Processor
|
||||
}
|
||||
#endif
|
||||
|
||||
public string GetCommonTag(string tagName)
|
||||
public delegate string GetStringTagProvider(TagLib.File file);
|
||||
|
||||
public string GetCommonTag(GetStringTagProvider provider)
|
||||
{
|
||||
if (_hasEmbeddedCUESheet || _hasSingleFilename)
|
||||
return _albumTags.Get(tagName);
|
||||
return General.EmptyStringToNull(provider(_fileInfo));
|
||||
if (_hasTrackFilenames)
|
||||
{
|
||||
string tagValue = null;
|
||||
@@ -1414,7 +1447,7 @@ namespace CUETools.Processor
|
||||
for (int i = 0; i < TrackCount; i++)
|
||||
{
|
||||
TrackInfo track = _tracks[i];
|
||||
string newValue = track._trackTags.Get (tagName);
|
||||
string newValue = General.EmptyStringToNull(provider(track._fileInfo));
|
||||
if (tagValue == null)
|
||||
tagValue = newValue;
|
||||
else
|
||||
@@ -1425,6 +1458,11 @@ namespace CUETools.Processor
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetCommonMiscTag(string tagName)
|
||||
{
|
||||
return GetCommonTag(delegate(TagLib.File file) { return Tagging.TagListToSingleValue(Tagging.GetMiscTag(file, tagName)); });
|
||||
}
|
||||
|
||||
private static string LocateFile(string dir, string file, List<string> contents) {
|
||||
List<string> dirList, fileList;
|
||||
string altDir;
|
||||
@@ -1446,7 +1484,7 @@ namespace CUETools.Processor
|
||||
for (int iDir = 0; iDir < dirList.Count; iDir++) {
|
||||
for (int iFile = 0; iFile < fileList.Count; iFile++) {
|
||||
string path = Path.Combine(dirList[iDir], fileList[iFile]);
|
||||
if ( (contents == null && File.Exists(path))
|
||||
if ((contents == null && System.IO.File.Exists(path))
|
||||
|| (contents != null && contents.Contains(path)))
|
||||
return path;
|
||||
path = dirList[iDir] + '/' + fileList[iFile];
|
||||
@@ -1464,7 +1502,7 @@ namespace CUETools.Processor
|
||||
_outputFormat = format;
|
||||
_cuePath = outputPath;
|
||||
|
||||
string extension = General.FormatExtension(format);
|
||||
string extension = General.FormatExtension(format, _config);
|
||||
List<string> find, replace;
|
||||
string filename;
|
||||
int iTrack;
|
||||
@@ -1551,16 +1589,17 @@ namespace CUETools.Processor
|
||||
}
|
||||
}
|
||||
|
||||
private int GetSampleLength(string path, out NameValueCollection tags)
|
||||
private int GetSampleLength(string path, out TagLib.File fileInfo)
|
||||
{
|
||||
IAudioSource audioSource;
|
||||
|
||||
ShowProgress("Analyzing input file...", 0.0, 0.0, path, null);
|
||||
if (_isArchive)
|
||||
audioSource = AudioReadWrite.GetAudioSource(path, OpenArchive(path, true));
|
||||
else
|
||||
audioSource = AudioReadWrite.GetAudioSource(path, null);
|
||||
|
||||
TagLib.UserDefined.AdditionalFileTypes.Config = _config;
|
||||
TagLib.File.IFileAbstraction file = _isArchive
|
||||
? (TagLib.File.IFileAbstraction) new ArchiveFileAbstraction(this, path)
|
||||
: (TagLib.File.IFileAbstraction) new TagLib.File.LocalFileAbstraction(path);
|
||||
fileInfo = TagLib.File.Create(file);
|
||||
|
||||
IAudioSource audioSource = AudioReadWrite.GetAudioSource(path, _isArchive ? OpenArchive(path, true) : null, _config);
|
||||
if ((audioSource.BitsPerSample != 16) ||
|
||||
(audioSource.ChannelCount != 2) ||
|
||||
(audioSource.SampleRate != 44100) ||
|
||||
@@ -1569,8 +1608,6 @@ namespace CUETools.Processor
|
||||
audioSource.Close();
|
||||
throw new Exception("Audio format is invalid.");
|
||||
}
|
||||
|
||||
tags = audioSource.Tags;
|
||||
audioSource.Close();
|
||||
return (int)audioSource.Length;
|
||||
}
|
||||
@@ -2021,13 +2058,12 @@ namespace CUETools.Processor
|
||||
{
|
||||
string logContents = LOGContents();
|
||||
string cueContents = CUESheetContents(style);
|
||||
bool needNewCRCs = _accurateRipMode != AccurateRipMode.None &&
|
||||
(_accurateRipMode == AccurateRipMode.VerifyAndConvert || _isCD) &&
|
||||
_config.writeArTagsOnConvert &&
|
||||
_arVerify.AccResult == HttpStatusCode.OK;
|
||||
uint tracksMatch = 0;
|
||||
int bestOffset = 0;
|
||||
if (needNewCRCs)
|
||||
|
||||
if (_accurateRipMode != AccurateRipMode.None &&
|
||||
_config.writeArTagsOnConvert &&
|
||||
_arVerify.AccResult == HttpStatusCode.OK)
|
||||
FindBestOffset(1, true, out tracksMatch, out bestOffset);
|
||||
|
||||
if (logContents != null)
|
||||
@@ -2035,51 +2071,70 @@ namespace CUETools.Processor
|
||||
else
|
||||
if (_eacLog != null && _config.extractLog)
|
||||
WriteText(Path.ChangeExtension(_cuePath, ".log"), _eacLog);
|
||||
if (style != CUEStyle.SingleFileWithCUE)
|
||||
|
||||
if (style == CUEStyle.SingleFileWithCUE || style == CUEStyle.SingleFile)
|
||||
{
|
||||
WriteText(_cuePath, cueContents);
|
||||
#if !MONO
|
||||
if (needNewCRCs && style != CUEStyle.SingleFile)
|
||||
if (style == CUEStyle.SingleFileWithCUE && _config.createCUEFileWhenEmbedded)
|
||||
WriteText(Path.ChangeExtension(_cuePath, ".cue"), cueContents);
|
||||
if (style == CUEStyle.SingleFile)
|
||||
WriteText(_cuePath, cueContents);
|
||||
if (_outputFormat != OutputAudioFormat.NoAudio)
|
||||
{
|
||||
for (int iTrack = 0; iTrack < TrackCount; iTrack++)
|
||||
NameValueCollection tags = GenerateAlbumTags(bestOffset, style == CUEStyle.SingleFileWithCUE);
|
||||
TagLib.UserDefined.AdditionalFileTypes.Config = _config;
|
||||
TagLib.File fileInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(destPaths[0]));
|
||||
if (Tagging.UpdateTags(fileInfo, tags, _config))
|
||||
{
|
||||
IAudioSource audioSource = AudioReadWrite.GetAudioSource(destPaths[iTrack + (htoaToFile ? 1 : 0)], null);
|
||||
CleanupTags(audioSource.Tags, "ACCURATERIP");
|
||||
GenerateAccurateRipTags(audioSource.Tags, 0, bestOffset, iTrack);
|
||||
audioSource.UpdateTags(false);
|
||||
audioSource.Close();
|
||||
audioSource = null;
|
||||
fileInfo.Tag.DiscCount = (_tracks[0]._fileInfo ?? _fileInfo).Tag.DiscCount; // TODO: GetCommonTag?
|
||||
fileInfo.Tag.Disc = (_tracks[0]._fileInfo ?? _fileInfo).Tag.Disc;
|
||||
//fileInfo.Tag.Title = null;
|
||||
//fileInfo.Tag.Performers = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Performers;
|
||||
//if (fileInfo.Tag.Performers.Length == 0) fileInfo.Tag.Performers = new string[] { _tracks[iTrack].Artist != "" ? _tracks[iTrack].Artist : Artist };
|
||||
fileInfo.Tag.AlbumArtists = (_tracks[0]._fileInfo ?? _fileInfo).Tag.AlbumArtists;
|
||||
if (fileInfo.Tag.AlbumArtists.Length == 0) fileInfo.Tag.AlbumArtists = new string[] { Artist };
|
||||
fileInfo.Tag.Album = (_tracks[0]._fileInfo ?? _fileInfo).Tag.Album ?? Title;
|
||||
uint year = (_tracks[0]._fileInfo ?? _fileInfo).Tag.Year;
|
||||
fileInfo.Tag.Year = year != 0 ? year : ("" != Year && uint.TryParse(Year, out year)) ? year : 0;
|
||||
fileInfo.Tag.Genres = (_tracks[0]._fileInfo ?? _fileInfo).Tag.Genres;
|
||||
if (fileInfo.Tag.Genres.Length == 0) fileInfo.Tag.Genres = new string[] { Genre };
|
||||
fileInfo.Tag.Pictures = (_tracks[0]._fileInfo ?? _fileInfo).Tag.Pictures;
|
||||
fileInfo.Save();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_config.createCUEFileWhenEmbedded)
|
||||
WriteText(Path.ChangeExtension(_cuePath, ".cue"), cueContents);
|
||||
#if !MONO
|
||||
if (needNewCRCs || _isCD)
|
||||
{
|
||||
IAudioSource audioSource = AudioReadWrite.GetAudioSource(destPaths[0], null);
|
||||
if (_isCD)
|
||||
WriteText(_cuePath, cueContents);
|
||||
if (_config.createM3U)
|
||||
WriteText(Path.ChangeExtension(_cuePath, ".m3u"), M3UContents(style));
|
||||
if (_outputFormat != OutputAudioFormat.NoAudio)
|
||||
for (int iTrack = 0; iTrack < TrackCount; iTrack++)
|
||||
{
|
||||
if (_accurateRipMode != AccurateRipMode.VerifyThenConvert)
|
||||
audioSource.Tags.Add("CUESHEET", cueContents);
|
||||
audioSource.Tags.Add("LOG", logContents);
|
||||
string path = destPaths[iTrack + (htoaToFile ? 1 : 0)];
|
||||
NameValueCollection tags = GenerateTrackTags(iTrack, bestOffset);
|
||||
TagLib.UserDefined.AdditionalFileTypes.Config = _config;
|
||||
TagLib.File fileInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(path));
|
||||
if (Tagging.UpdateTags(fileInfo, tags, _config))
|
||||
{
|
||||
fileInfo.Tag.TrackCount = (uint) TrackCount;
|
||||
fileInfo.Tag.Track = (uint) iTrack + 1;
|
||||
fileInfo.Tag.DiscCount = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.DiscCount;
|
||||
fileInfo.Tag.Disc = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Disc;
|
||||
fileInfo.Tag.Title = _tracks[iTrack]._fileInfo != null ? _tracks[iTrack]._fileInfo.Tag.Title : _tracks[iTrack].Title;
|
||||
fileInfo.Tag.Performers = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Performers;
|
||||
if (fileInfo.Tag.Performers.Length == 0) fileInfo.Tag.Performers = new string[] { _tracks[iTrack].Artist != "" ? _tracks[iTrack].Artist : Artist };
|
||||
fileInfo.Tag.AlbumArtists = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.AlbumArtists;
|
||||
if (fileInfo.Tag.AlbumArtists.Length == 0) fileInfo.Tag.AlbumArtists = new string[] { Artist };
|
||||
fileInfo.Tag.Album = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Album ?? Title;
|
||||
uint year = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Year;
|
||||
fileInfo.Tag.Year = year != 0 ? year : ("" != Year && uint.TryParse(Year, out year)) ? year : 0;
|
||||
fileInfo.Tag.Genres = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Genres;
|
||||
if (fileInfo.Tag.Genres.Length == 0) fileInfo.Tag.Genres = new string[] { Genre };
|
||||
fileInfo.Tag.Pictures = (_tracks[iTrack]._fileInfo ?? _fileInfo).Tag.Pictures;
|
||||
fileInfo.Save();
|
||||
}
|
||||
}
|
||||
if (needNewCRCs)
|
||||
{
|
||||
CleanupTags(audioSource.Tags, "ACCURATERIP");
|
||||
GenerateAccurateRipTags(audioSource.Tags, 0, bestOffset, -1);
|
||||
}
|
||||
audioSource.UpdateTags(false);
|
||||
audioSource.Close();
|
||||
audioSource = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (style != CUEStyle.SingleFileWithCUE && style != CUEStyle.SingleFile && _config.createM3U)
|
||||
WriteText(Path.ChangeExtension(_cuePath, ".m3u"), M3UContents(style));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2095,34 +2150,25 @@ namespace CUETools.Processor
|
||||
|
||||
if (_hasEmbeddedCUESheet)
|
||||
{
|
||||
IAudioSource audioSource = AudioReadWrite.GetAudioSource(_sourcePaths[0], null);
|
||||
NameValueCollection tags = audioSource.Tags;
|
||||
CleanupTags(tags, "ACCURATERIP");
|
||||
GenerateAccurateRipTags (tags, 0, bestOffset, -1);
|
||||
#if !MONO
|
||||
if (audioSource is FLACReader)
|
||||
audioSource.UpdateTags (true);
|
||||
#endif
|
||||
audioSource.Close();
|
||||
audioSource = null;
|
||||
if (_fileInfo is TagLib.Flac.File)
|
||||
{
|
||||
NameValueCollection tags = Tagging.Analyze(_fileInfo);
|
||||
CleanupTags(tags, "ACCURATERIP");
|
||||
GenerateAccurateRipTags(tags, 0, bestOffset, -1);
|
||||
if (Tagging.UpdateTags(_fileInfo, tags, _config))
|
||||
_fileInfo.Save();
|
||||
}
|
||||
} else if (_hasTrackFilenames)
|
||||
{
|
||||
for (int iTrack = 0; iTrack < TrackCount; iTrack++)
|
||||
{
|
||||
string src = _sourcePaths[iTrack + (_hasHTOAFilename ? 1 : 0)];
|
||||
IAudioSource audioSource = AudioReadWrite.GetAudioSource(src, null);
|
||||
#if !MONO
|
||||
if (audioSource is FLACReader)
|
||||
if (_tracks[iTrack]._fileInfo is TagLib.Flac.File)
|
||||
{
|
||||
NameValueCollection tags = audioSource.Tags;
|
||||
NameValueCollection tags = Tagging.Analyze(_tracks[iTrack]._fileInfo);
|
||||
CleanupTags(tags, "ACCURATERIP");
|
||||
GenerateAccurateRipTags (tags, 0, bestOffset, iTrack);
|
||||
audioSource.UpdateTags(true);
|
||||
GenerateAccurateRipTags(tags, 0, bestOffset, iTrack);
|
||||
if (Tagging.UpdateTags(_tracks[iTrack]._fileInfo, tags, _config))
|
||||
_tracks[iTrack]._fileInfo.Save();
|
||||
}
|
||||
#endif
|
||||
audioSource.Close();
|
||||
audioSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2145,76 +2191,74 @@ namespace CUETools.Processor
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTrackTags(IAudioDest audioDest, int iTrack, int bestOffset)
|
||||
private NameValueCollection GenerateTrackTags(int iTrack, int bestOffset)
|
||||
{
|
||||
NameValueCollection destTags = new NameValueCollection();
|
||||
|
||||
|
||||
if (_hasEmbeddedCUESheet)
|
||||
{
|
||||
string trackPrefix = String.Format ("cue_track{0:00}_", iTrack + 1);
|
||||
string[] keys = _albumTags.AllKeys;
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
string trackPrefix = String.Format("cue_track{0:00}_", iTrack + 1);
|
||||
NameValueCollection albumTags = Tagging.Analyze(_fileInfo);
|
||||
foreach (string key in albumTags.AllKeys)
|
||||
{
|
||||
if (keys[i].ToLower().StartsWith(trackPrefix)
|
||||
|| !keys[i].ToLower().StartsWith("cue_track"))
|
||||
if (key.ToLower().StartsWith(trackPrefix)
|
||||
|| !key.ToLower().StartsWith("cue_track"))
|
||||
{
|
||||
string name = keys[i].ToLower().StartsWith(trackPrefix) ?
|
||||
keys[i].Substring(trackPrefix.Length) : keys[i];
|
||||
string[] values = _albumTags.GetValues(keys[i]);
|
||||
string name = key.ToLower().StartsWith(trackPrefix) ?
|
||||
key.Substring(trackPrefix.Length) : key;
|
||||
string[] values = albumTags.GetValues(key);
|
||||
for (int j = 0; j < values.Length; j++)
|
||||
destTags.Add(name, values[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_hasTrackFilenames)
|
||||
destTags.Add(_tracks[iTrack]._trackTags);
|
||||
destTags.Add(Tagging.Analyze(_tracks[iTrack]._fileInfo));
|
||||
else if (_hasSingleFilename)
|
||||
{
|
||||
// TODO?
|
||||
}
|
||||
|
||||
destTags.Remove("CUESHEET");
|
||||
// these will be set explicitely
|
||||
destTags.Remove("ARTIST");
|
||||
destTags.Remove("TITLE");
|
||||
destTags.Remove("ALBUM");
|
||||
destTags.Remove("ALBUMARTIST");
|
||||
destTags.Remove("DATE");
|
||||
destTags.Remove("GENRE");
|
||||
destTags.Remove("TRACKNUMBER");
|
||||
destTags.Remove("TRACKTOTAL");
|
||||
destTags.Remove("TOTALTRACKS");
|
||||
destTags.Remove("DISCNUMBER");
|
||||
destTags.Remove("DISCTOTAL");
|
||||
destTags.Remove("TOTALDISCS");
|
||||
|
||||
destTags.Remove("LOG");
|
||||
destTags.Remove("LOGFILE");
|
||||
destTags.Remove("EACLOG");
|
||||
|
||||
// these are not valid
|
||||
destTags.Remove("CUESHEET");
|
||||
CleanupTags(destTags, "ACCURATERIP");
|
||||
CleanupTags(destTags, "REPLAYGAIN");
|
||||
|
||||
if (destTags.Get("TITLE") == null && "" != _tracks[iTrack].Title)
|
||||
destTags.Add("TITLE", _tracks[iTrack].Title);
|
||||
if (destTags.Get("ARTIST") == null && "" != _tracks[iTrack].Artist)
|
||||
destTags.Add("ARTIST", _tracks[iTrack].Artist);
|
||||
if (destTags.Get("ARTIST") == null && "" != Artist)
|
||||
destTags.Add("ARTIST", Artist);
|
||||
if (destTags.Get("ALBUM ARTIST") == null && "" != Artist)
|
||||
destTags.Add("ALBUM ARTIST", Artist);
|
||||
if (destTags.Get("ALBUM") == null && "" != Title)
|
||||
destTags.Add("ALBUM", Title);
|
||||
if (destTags.Get("DATE") == null && "" != Year)
|
||||
destTags.Add("DATE", Year);
|
||||
if (destTags.Get("GENRE") == null && "" != Genre)
|
||||
destTags.Add("GENRE", Genre);
|
||||
destTags.Add("TRACKNUMBER", (iTrack + 1).ToString("00"));
|
||||
destTags.Add("TOTALTRACKS", TrackCount.ToString("00"));
|
||||
if (_config.writeArTagsOnConvert)
|
||||
{
|
||||
if (!_isCD && _accurateRipMode == AccurateRipMode.VerifyThenConvert && _arVerify.AccResult == HttpStatusCode.OK)
|
||||
if (_accurateRipMode != AccurateRipMode.None && _arVerify.AccResult == HttpStatusCode.OK)
|
||||
GenerateAccurateRipTags(destTags, _writeOffset, bestOffset, iTrack);
|
||||
else
|
||||
destTags.Add("ACCURATERIPID", _accurateRipId);
|
||||
}
|
||||
audioDest.SetTags(destTags);
|
||||
return destTags;
|
||||
}
|
||||
|
||||
private void SetAlbumTags(IAudioDest audioDest, int bestOffset, bool fWithCUE)
|
||||
private NameValueCollection GenerateAlbumTags(int bestOffset, bool fWithCUE)
|
||||
{
|
||||
NameValueCollection destTags = new NameValueCollection();
|
||||
|
||||
if (_hasEmbeddedCUESheet || _hasSingleFilename)
|
||||
{
|
||||
destTags.Add(_albumTags);
|
||||
destTags.Add(Tagging.Analyze(_fileInfo));
|
||||
if (!fWithCUE)
|
||||
CleanupTags(destTags, "CUE_TRACK");
|
||||
}
|
||||
@@ -2222,32 +2266,45 @@ namespace CUETools.Processor
|
||||
{
|
||||
for (int iTrack = 0; iTrack < TrackCount; iTrack++)
|
||||
{
|
||||
string[] keys = _tracks[iTrack]._trackTags.AllKeys;
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
NameValueCollection trackTags = Tagging.Analyze(_tracks[iTrack]._fileInfo);
|
||||
foreach (string key in trackTags.AllKeys)
|
||||
{
|
||||
string singleValue = GetCommonTag (keys[i]);
|
||||
string singleValue = GetCommonMiscTag(key);
|
||||
if (singleValue != null)
|
||||
{
|
||||
if (destTags.Get(keys[i]) == null)
|
||||
destTags.Add(keys[i], singleValue);
|
||||
if (destTags.Get(key) == null)
|
||||
destTags.Add(key, singleValue);
|
||||
}
|
||||
else if (fWithCUE && keys[i].ToUpper() != "TRACKNUMBER")
|
||||
else if (fWithCUE && key.ToUpper() != "TRACKNUMBER")
|
||||
{
|
||||
string[] values = _tracks[iTrack]._trackTags.GetValues(keys[i]);
|
||||
string[] values = trackTags.GetValues(key);
|
||||
for (int j = 0; j < values.Length; j++)
|
||||
destTags.Add(String.Format("cue_track{0:00}_{1}", iTrack + 1, keys[i]), values[j]);
|
||||
destTags.Add(String.Format("cue_track{0:00}_{1}", iTrack + 1, key), values[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destTags.Remove("CUESHEET");
|
||||
// these will be set explicitely
|
||||
destTags.Remove("ARTIST");
|
||||
destTags.Remove("TITLE");
|
||||
destTags.Remove("ALBUM");
|
||||
destTags.Remove("ALBUMARTIST");
|
||||
destTags.Remove("DATE");
|
||||
destTags.Remove("GENRE");
|
||||
destTags.Remove("TRACKNUMBER");
|
||||
destTags.Remove("TRACKTOTAL");
|
||||
destTags.Remove("TOTALTRACKS");
|
||||
destTags.Remove("DISCNUMBER");
|
||||
destTags.Remove("DISCTOTAL");
|
||||
destTags.Remove("TOTALDISCS");
|
||||
|
||||
// these are not valid
|
||||
CleanupTags(destTags, "ACCURATERIP");
|
||||
CleanupTags(destTags, "REPLAYGAIN");
|
||||
|
||||
if (fWithCUE && (!_isCD || _accurateRipMode == AccurateRipMode.VerifyThenConvert))
|
||||
destTags.Remove("CUESHEET");
|
||||
if (fWithCUE)
|
||||
destTags.Add("CUESHEET", CUESheetContents(CUEStyle.SingleFileWithCUE));
|
||||
|
||||
if (_config.embedLog)
|
||||
@@ -2255,18 +2312,21 @@ namespace CUETools.Processor
|
||||
destTags.Remove("LOG");
|
||||
destTags.Remove("LOGFILE");
|
||||
destTags.Remove("EACLOG");
|
||||
if (_eacLog != null)
|
||||
string logContents = LOGContents();
|
||||
if (logContents != null)
|
||||
destTags.Add("LOG", logContents);
|
||||
else if (_eacLog != null)
|
||||
destTags.Add("LOG", _eacLog);
|
||||
}
|
||||
|
||||
if (_config.writeArTagsOnConvert)
|
||||
{
|
||||
if (fWithCUE && !_isCD && _accurateRipMode == AccurateRipMode.VerifyThenConvert && _arVerify.AccResult == HttpStatusCode.OK)
|
||||
if (fWithCUE && _accurateRipMode != AccurateRipMode.None && _arVerify.AccResult == HttpStatusCode.OK)
|
||||
GenerateAccurateRipTags(destTags, _writeOffset, bestOffset, -1);
|
||||
else
|
||||
destTags.Add("ACCURATERIPID", _accurateRipId);
|
||||
}
|
||||
audioDest.SetTags(destTags);
|
||||
return destTags;
|
||||
}
|
||||
|
||||
public void WriteAudioFilesPass(string dir, CUEStyle style, string[] destPaths, int[] destLengths, bool htoaToFile, bool noOutput)
|
||||
@@ -2324,11 +2384,6 @@ namespace CUETools.Processor
|
||||
_appliedWriteOffset = true;
|
||||
}
|
||||
|
||||
uint tracksMatch;
|
||||
int bestOffset = _writeOffset;
|
||||
if (!noOutput && _accurateRipMode == AccurateRipMode.VerifyThenConvert && _config.writeArTagsOnConvert && _arVerify.AccResult == HttpStatusCode.OK)
|
||||
FindBestOffset(1, true, out tracksMatch, out bestOffset);
|
||||
|
||||
if (_config.detectHDCD)
|
||||
{
|
||||
// currently broken verifyThenConvert on HDCD detection!!!! need to check for HDCD results higher
|
||||
@@ -2336,13 +2391,10 @@ namespace CUETools.Processor
|
||||
catch { }
|
||||
}
|
||||
|
||||
|
||||
if (style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE)
|
||||
{
|
||||
iDest++;
|
||||
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], hdcdDecoder != null && hdcdDecoder.Decoding ? hdcdDecoder.BitsPerSample : 16, noOutput);
|
||||
if (!noOutput)
|
||||
SetAlbumTags(audioDest, bestOffset, style == CUEStyle.SingleFileWithCUE);
|
||||
}
|
||||
|
||||
uint currentOffset = 0, previousOffset = 0;
|
||||
@@ -2371,8 +2423,6 @@ namespace CUETools.Processor
|
||||
if (audioDest != null)
|
||||
audioDest.Close();
|
||||
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], hdcdDecoder != null && hdcdDecoder.Decoding ? hdcdDecoder.BitsPerSample : 16, noOutput);
|
||||
if (!noOutput)
|
||||
SetTrackTags(audioDest, iTrack, bestOffset);
|
||||
}
|
||||
|
||||
for (iIndex = 0; iIndex <= _toc[_toc.FirstAudio + iTrack].LastIndex; iIndex++)
|
||||
@@ -2394,8 +2444,6 @@ namespace CUETools.Processor
|
||||
audioDest.Close();
|
||||
iDest++;
|
||||
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], hdcdDecoder != null && hdcdDecoder.Decoding ? hdcdDecoder.BitsPerSample : 16, noOutput);
|
||||
if (!noOutput)
|
||||
SetTrackTags(audioDest, iTrack, bestOffset);
|
||||
}
|
||||
|
||||
if ((style == CUEStyle.GapsAppended) && (iIndex == 0) && (iTrack == 0))
|
||||
@@ -2740,9 +2788,9 @@ namespace CUETools.Processor
|
||||
} else
|
||||
#endif
|
||||
if (_isArchive)
|
||||
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path, OpenArchive(sourceInfo.Path, false));
|
||||
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path, OpenArchive(sourceInfo.Path, false), _config);
|
||||
else
|
||||
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path, null);
|
||||
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path, null, _config);
|
||||
}
|
||||
|
||||
if (sourceInfo.Offset != 0)
|
||||
@@ -2957,6 +3005,155 @@ namespace CUETools.Processor
|
||||
}
|
||||
}
|
||||
|
||||
public class SeekableZipStream : Stream
|
||||
{
|
||||
ZipFile zipFile;
|
||||
ZipEntry zipEntry;
|
||||
Stream zipStream;
|
||||
long position;
|
||||
byte[] temp;
|
||||
|
||||
public SeekableZipStream(string path, string fileName)
|
||||
{
|
||||
zipFile = new ZipFile(path);
|
||||
zipEntry = zipFile.GetEntry(fileName);
|
||||
if (zipEntry == null)
|
||||
throw new Exception("Archive entry not found.");
|
||||
zipStream = zipFile.GetInputStream(zipEntry);
|
||||
temp = new byte[65536];
|
||||
position = 0;
|
||||
}
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return zipEntry.Size;
|
||||
}
|
||||
}
|
||||
public override long Position
|
||||
{
|
||||
get { return position; }
|
||||
set { Seek(value, SeekOrigin.Begin); }
|
||||
}
|
||||
public override void Close()
|
||||
{
|
||||
zipStream.Close();
|
||||
zipEntry = null;
|
||||
zipFile.Close();
|
||||
}
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (position == 0 && zipEntry.IsCrypted && ((ZipInputStream)zipStream).Password == null && PasswordRequired != null)
|
||||
{
|
||||
PasswordRequiredEventArgs e = new PasswordRequiredEventArgs();
|
||||
PasswordRequired(this, e);
|
||||
if (e.ContinueOperation && e.Password.Length > 0)
|
||||
((ZipInputStream)zipStream).Password = e.Password;
|
||||
}
|
||||
// TODO: always save to a local temp circular buffer for optimization of the backwards seek.
|
||||
int total = zipStream.Read(buffer, offset, count);
|
||||
position += total;
|
||||
if (ExtractionProgress != null)
|
||||
{
|
||||
ExtractionProgressEventArgs e = new ExtractionProgressEventArgs();
|
||||
e.BytesExtracted = position;
|
||||
e.FileName = zipEntry.Name;
|
||||
e.FileSize = zipEntry.Size;
|
||||
e.PercentComplete = 100.0 * position / zipEntry.Size;
|
||||
ExtractionProgress(this, e);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long seek_to;
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
seek_to = offset;
|
||||
break;
|
||||
case SeekOrigin.Current:
|
||||
seek_to = Position + offset;
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
seek_to = Length + offset;
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (seek_to < 0 || seek_to > Length)
|
||||
throw new IOException("Invalid seek");
|
||||
if (seek_to < position)
|
||||
{
|
||||
zipStream.Close();
|
||||
zipStream = zipFile.GetInputStream(zipEntry);
|
||||
position = 0;
|
||||
}
|
||||
while (seek_to > position)
|
||||
if (Read(temp, 0, (int) Math.Min(seek_to - position, (long) temp.Length)) <= 0)
|
||||
throw new IOException("Invalid seek");
|
||||
return position;
|
||||
}
|
||||
public override void Write(byte[] array, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public event PasswordRequiredHandler PasswordRequired;
|
||||
public event ExtractionProgressHandler ExtractionProgress;
|
||||
}
|
||||
|
||||
public class ArchiveFileAbstraction : TagLib.File.IFileAbstraction
|
||||
{
|
||||
private string name;
|
||||
private CUESheet _cueSheet;
|
||||
|
||||
public ArchiveFileAbstraction(CUESheet cueSheet, string file)
|
||||
{
|
||||
name = file;
|
||||
_cueSheet = cueSheet;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public System.IO.Stream ReadStream
|
||||
{
|
||||
get { return _cueSheet.OpenArchive(Name, true); }
|
||||
}
|
||||
|
||||
public System.IO.Stream WriteStream
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public void CloseStream(System.IO.Stream stream)
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public class CUELine {
|
||||
private List<String> _params;
|
||||
private List<bool> _quoted;
|
||||
@@ -3034,11 +3231,11 @@ namespace CUETools.Processor
|
||||
|
||||
public class TrackInfo {
|
||||
private List<CUELine> _attributes;
|
||||
public NameValueCollection _trackTags;
|
||||
public TagLib.File _fileInfo;
|
||||
|
||||
public TrackInfo() {
|
||||
_attributes = new List<CUELine>();
|
||||
_trackTags = new NameValueCollection();
|
||||
_fileInfo = null;
|
||||
}
|
||||
|
||||
public List<CUELine> Attributes {
|
||||
|
||||
178
CUETools.Processor/Tagging.cs
Normal file
178
CUETools.Processor/Tagging.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
|
||||
namespace CUETools.Processor
|
||||
{
|
||||
public class Tagging
|
||||
{
|
||||
public static bool UpdateTags(TagLib.File fileInfo, NameValueCollection tags, CUEConfig config)
|
||||
{
|
||||
if (fileInfo is TagLib.Flac.File)
|
||||
{
|
||||
TagLib.Ogg.XiphComment xiph = (TagLib.Ogg.XiphComment)fileInfo.GetTag(TagLib.TagTypes.Xiph);
|
||||
foreach (string tag in tags.AllKeys)
|
||||
xiph.SetField(tag, tags.GetValues(tag));
|
||||
return true;
|
||||
}
|
||||
if (fileInfo is TagLib.Riff.File)
|
||||
return false;
|
||||
if (fileInfo is TagLib.UserDefined.File && !(fileInfo as TagLib.UserDefined.File).SupportsAPEv2)
|
||||
return false;
|
||||
TagLib.Ape.Tag ape = (TagLib.Ape.Tag)fileInfo.GetTag(TagLib.TagTypes.Ape, true);
|
||||
foreach (string tag in tags.AllKeys)
|
||||
ape.SetValue(XiphTagNameToApe(tag), tags.GetValues(tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void UpdateTags(string path, NameValueCollection tags, CUEConfig config)
|
||||
{
|
||||
TagLib.UserDefined.AdditionalFileTypes.Config = config;
|
||||
TagLib.File fileInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(path));
|
||||
if (UpdateTags(fileInfo, tags, config))
|
||||
fileInfo.Save();
|
||||
//IAudioSource audioSource = AudioReadWrite.GetAudioSource(path, null, config);
|
||||
//audioSource.Tags = tags;
|
||||
//audioSource.UpdateTags(false);
|
||||
//audioSource.Close();
|
||||
//audioSource = null;
|
||||
}
|
||||
|
||||
public static string[] GetMiscTag(TagLib.File file, string name)
|
||||
{
|
||||
//TagLib.Mpeg4.AppleTag apple = (TagLib.Mpeg4.AppleTag)file.GetTag(TagLib.TagTypes.Apple);
|
||||
//TagLib.Id3v2.Tag id3v2 = (TagLib.Id3v2.Tag)file.GetTag(TagLib.TagTypes.Id3v2);
|
||||
TagLib.Ogg.XiphComment xiph = (TagLib.Ogg.XiphComment)file.GetTag(TagLib.TagTypes.Xiph);
|
||||
TagLib.Ape.Tag ape = (TagLib.Ape.Tag)file.GetTag(TagLib.TagTypes.Ape);
|
||||
|
||||
//if (apple != null)
|
||||
//{
|
||||
// string[] text = apple.GetText(name);
|
||||
// if (text.Length != 0)
|
||||
// return text;
|
||||
//}
|
||||
|
||||
//if (id3v2 != null)
|
||||
// foreach (TagLib.Id3v2.Frame f in id3v2.GetFrames())
|
||||
// if (f is TagLib.Id3v2.TextInformationFrame && ((TagLib.Id3v2.TextInformationFrame)f).Text != null)
|
||||
// return ((TagLib.Id3v2.TextInformationFrame)f).Text;
|
||||
|
||||
if (xiph != null)
|
||||
{
|
||||
string[] l = xiph.GetField(name);
|
||||
if (l != null && l.Length != 0)
|
||||
return l;
|
||||
}
|
||||
|
||||
if (ape != null)
|
||||
{
|
||||
TagLib.Ape.Item item = ape.GetItem(name);
|
||||
if (item != null)
|
||||
return item.ToStringArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string TagListToSingleValue(string[] list)
|
||||
{
|
||||
return list == null ? null :
|
||||
list.Length == 0 ? null :
|
||||
list.Length == 1 ? list[0] :
|
||||
null; // TODO: merge them?
|
||||
}
|
||||
|
||||
public static string ApeTagNameToXiph(string tag)
|
||||
{
|
||||
if (tag.ToUpper() == "YEAR")
|
||||
return "DATE";
|
||||
if (tag.ToUpper() == "TRACK")
|
||||
return "TRACKNUMBER";
|
||||
if (tag.ToUpper() == "DISC")
|
||||
return "DISCNUMBER";
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static string XiphTagNameToApe(string tag)
|
||||
{
|
||||
if (tag.ToUpper() == "DATE")
|
||||
return "Year";
|
||||
if (tag.ToUpper() == "TRACKNUMBER")
|
||||
return "Track";
|
||||
if (tag.ToUpper() == "DISCNUMBER")
|
||||
return "Disc";
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static NameValueCollection Analyze(string path)
|
||||
{
|
||||
return Analyze(new TagLib.File.LocalFileAbstraction(path));
|
||||
}
|
||||
|
||||
public static NameValueCollection Analyze(TagLib.File.IFileAbstraction file)
|
||||
{
|
||||
return Analyze(TagLib.File.Create(file));
|
||||
}
|
||||
|
||||
public static NameValueCollection Analyze(TagLib.File fileInfo)
|
||||
{
|
||||
NameValueCollection tags = new NameValueCollection();
|
||||
|
||||
TagLib.Ogg.XiphComment xiph = (TagLib.Ogg.XiphComment)fileInfo.GetTag(TagLib.TagTypes.Xiph);
|
||||
TagLib.Ape.Tag ape = (TagLib.Ape.Tag)fileInfo.GetTag(TagLib.TagTypes.Ape);
|
||||
|
||||
if (xiph != null)
|
||||
{
|
||||
foreach (string tag in xiph)
|
||||
foreach (string value in xiph.GetField(tag))
|
||||
tags.Add(tag, value);
|
||||
}
|
||||
else if (ape != null)
|
||||
{
|
||||
foreach (string tag in ape)
|
||||
foreach (string value in ape.GetItem(tag).ToStringArray())
|
||||
tags.Add(ApeTagNameToXiph(tag), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (audioSource is CUETools.Codecs.ALAC.ALACReader)
|
||||
//tags = (audioSource as CUETools.Codecs.ALAC.ALACReader).Tags;
|
||||
}
|
||||
|
||||
// TODO: enumerate dash atoms somehow?
|
||||
//TagLib.Mpeg4.AppleTag apple = (TagLib.Mpeg4.AppleTag)fileInfo.GetTag(TagLib.TagTypes.Apple);
|
||||
//if (apple != null)
|
||||
//{
|
||||
// tags = new NameValueCollection();
|
||||
// foreach (TagLib.Mpeg4.Box tag in apple)
|
||||
// if (tag.BoxType == "----")
|
||||
// foreach (string value in apple.GetDashBox(tag.)
|
||||
// tags.Add(tag, value);
|
||||
//}
|
||||
return tags;
|
||||
}
|
||||
|
||||
//public void SetTextField(TagLib.File file,
|
||||
// TagLib.ByteVector apple_name, TagLib.ByteVector id3v2_name,
|
||||
// string xiph_name, string ape_name, string[] values)
|
||||
//{
|
||||
// TagLib.Mpeg4.AppleTag apple = (TagLib.Mpeg4.AppleTag)file.GetTag(TagLib.TagTypes.Apple, true);
|
||||
// TagLib.Id3v2.Tag id3v2 = (TagLib.Id3v2.Tag)file.GetTag(TagLib.TagTypes.Id3v2, true);
|
||||
// TagLib.Ogg.XiphComment xiph = (TagLib.Ogg.XiphComment)file.GetTag(TagLib.TagTypes.Xiph, true);
|
||||
// TagLib.Ape.Tag ape = (TagLib.Ape.Tag)file.GetTag(TagLib.TagTypes.Ape, (file is TagLib.Mpc.File));
|
||||
|
||||
// if (apple != null)
|
||||
// apple.SetText(apple_name, values);
|
||||
|
||||
// if (id3v2 != null)
|
||||
// id3v2.SetTextFrame(id3v2_name, new TagLib.StringList(values));
|
||||
|
||||
// if (xiph != null)
|
||||
// xiph.AddFields(xiph_name, values);
|
||||
|
||||
// if (ape != null)
|
||||
// ape.AddValues(ape_name, values, true);
|
||||
//}
|
||||
}
|
||||
}
|
||||
319
CUETools.Processor/UserDefined.cs
Normal file
319
CUETools.Processor/UserDefined.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
//
|
||||
// File.cs: Provides tagging and properties support for WavPack files.
|
||||
//
|
||||
// Author:
|
||||
// Brian Nickel (brian.nickel@gmail.com)
|
||||
//
|
||||
// Original Source:
|
||||
// wvfile.cpp from libtunepimp
|
||||
//
|
||||
// Copyright (C) 2006-2007 Brian Nickel
|
||||
// Copyright (C) 2006 by Lukáš Lalinský (Original Implementation)
|
||||
// Copyright (C) 2004 by Allan Sandfeld Jensen (Original Implementation)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
using System;
|
||||
using TagLib;
|
||||
|
||||
namespace TagLib.UserDefined {
|
||||
/// <summary>
|
||||
/// This class extends <see cref="TagLib.NonContainer.File" /> to
|
||||
/// provide tagging and properties support for user defined format files.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A <see cref="TagLib.Ape.Tag" /> will be added automatically to
|
||||
/// any file that doesn't contain one. This change does not effect
|
||||
/// the file and can be reversed using the following method:
|
||||
/// <code>file.RemoveTags (file.TagTypes & ~file.TagTypesOnDisk);</code>
|
||||
/// </remarks>
|
||||
[SupportedMimeType("taglib/misc", "misc")]
|
||||
public class File : TagLib.NonContainer.File
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private bool _supportsAPEv2 = true;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified path in the local file
|
||||
/// system and specified read style.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// A <see cref="string" /> object containing the path of the
|
||||
/// file to use in the new instance.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="path" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public File (string path, ReadStyle propertiesStyle, bool supportsAPEv2)
|
||||
: base (path, propertiesStyle)
|
||||
{
|
||||
_supportsAPEv2 = supportsAPEv2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified path in the local file
|
||||
/// system with an average read style.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// A <see cref="string" /> object containing the path of the
|
||||
/// file to use in the new instance.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="path" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public File (string path, bool supportsAPEv2) : base (path)
|
||||
{
|
||||
_supportsAPEv2 = supportsAPEv2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified file abstraction and
|
||||
/// specified read style.
|
||||
/// </summary>
|
||||
/// <param name="abstraction">
|
||||
/// A <see cref="IFileAbstraction" /> object to use when
|
||||
/// reading from and writing to the file.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="abstraction" /> is <see langword="null"
|
||||
/// />.
|
||||
/// </exception>
|
||||
public File (File.IFileAbstraction abstraction,
|
||||
ReadStyle propertiesStyle, bool supportsAPEv2)
|
||||
: base (abstraction, propertiesStyle)
|
||||
{
|
||||
_supportsAPEv2 = supportsAPEv2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified file abstraction with an
|
||||
/// average read style.
|
||||
/// </summary>
|
||||
/// <param name="abstraction">
|
||||
/// A <see cref="IFileAbstraction" /> object to use when
|
||||
/// reading from and writing to the file.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="abstraction" /> is <see langword="null"
|
||||
/// />.
|
||||
/// </exception>
|
||||
public File (File.IFileAbstraction abstraction, bool supportsAPEv2)
|
||||
: base (abstraction)
|
||||
{
|
||||
_supportsAPEv2 = supportsAPEv2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public bool SupportsAPEv2
|
||||
{
|
||||
get
|
||||
{
|
||||
return _supportsAPEv2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a tag of a specified type from the current instance,
|
||||
/// optionally creating a new tag if possible.
|
||||
/// </summary>
|
||||
/// <param name="type">
|
||||
/// A <see cref="TagLib.TagTypes" /> value indicating the
|
||||
/// type of tag to read.
|
||||
/// </param>
|
||||
/// <param name="create">
|
||||
/// A <see cref="bool" /> value specifying whether or not to
|
||||
/// try and create the tag if one is not found.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="Tag" /> object containing the tag that was
|
||||
/// found in or added to the current instance. If no
|
||||
/// matching tag was found and none was created, <see
|
||||
/// langword="null" /> is returned.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If a <see cref="TagLib.Id3v2.Tag" /> is added to the
|
||||
/// current instance, it will be placed at the start of the
|
||||
/// file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
|
||||
/// <see cref="TagLib.Ape.Tag" /> will be added to the end of
|
||||
/// the file. All other tag types will be ignored.
|
||||
/// </remarks>
|
||||
public override TagLib.Tag GetTag (TagTypes type, bool create)
|
||||
{
|
||||
Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
|
||||
|
||||
if (t != null || !create)
|
||||
return t;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TagTypes.Id3v1:
|
||||
return EndTag.AddTag (type, Tag);
|
||||
|
||||
case TagTypes.Id3v2:
|
||||
return StartTag.AddTag (type, Tag);
|
||||
|
||||
case TagTypes.Ape:
|
||||
return EndTag.AddTag (type, Tag);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Reads format specific information at the start of the
|
||||
/// file.
|
||||
/// </summary>
|
||||
/// <param name="start">
|
||||
/// A <see cref="long" /> value containing the seek position
|
||||
/// at which the tags end and the media data begins.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
protected override void ReadStart (long start,
|
||||
ReadStyle propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads format specific information at the end of the
|
||||
/// file.
|
||||
/// </summary>
|
||||
/// <param name="end">
|
||||
/// A <see cref="long" /> value containing the seek position
|
||||
/// at which the media data ends and the tags begin.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
protected override void ReadEnd (long end,
|
||||
ReadStyle propertiesStyle)
|
||||
{
|
||||
// Make sure we have an APE tag.
|
||||
if (_supportsAPEv2)
|
||||
GetTag (TagTypes.Ape, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the audio properties from the file represented by
|
||||
/// the current instance.
|
||||
/// </summary>
|
||||
/// <param name="start">
|
||||
/// A <see cref="long" /> value containing the seek position
|
||||
/// at which the tags end and the media data begins.
|
||||
/// </param>
|
||||
/// <param name="end">
|
||||
/// A <see cref="long" /> value containing the seek position
|
||||
/// at which the media data ends and the tags begin.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="TagLib.Properties" /> object describing the
|
||||
/// media properties of the file represented by the current
|
||||
/// instance.
|
||||
/// </returns>
|
||||
protected override Properties ReadProperties (long start,
|
||||
long end,
|
||||
ReadStyle propertiesStyle)
|
||||
{
|
||||
return new Properties ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class AdditionalFileTypes
|
||||
{
|
||||
private static bool inited = false;
|
||||
private static CUETools.Processor.CUEConfig _config;
|
||||
|
||||
public static CUETools.Processor.CUEConfig Config
|
||||
{
|
||||
set
|
||||
{
|
||||
Init();
|
||||
_config = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static TagLib.File UserDefinedResolver(TagLib.File.IFileAbstraction abstraction, string mimetype, TagLib.ReadStyle style)
|
||||
{
|
||||
if (mimetype == "taglib/flac" || mimetype == "taglib/wv" || mimetype == "taglib/ape" || mimetype == "taglib/wav")
|
||||
return null;
|
||||
if (mimetype == "taglib/tta")
|
||||
return new File(abstraction, style, true);
|
||||
if (mimetype == "taglib/" + _config.udc1Extension)
|
||||
return new File(abstraction, style, _config.udc1APEv2);
|
||||
return null;
|
||||
}
|
||||
|
||||
static AdditionalFileTypes ()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
if (inited)
|
||||
return;
|
||||
TagLib.File.AddFileTypeResolver(new TagLib.File.FileTypeResolver(UserDefinedResolver));
|
||||
//FileTypes.Register(typeof(TagLib.NonContainer.File));
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user