* src/AudioFileIO.cs, src/Util/CustomAttribute.cs, src/Util/AudioFileReader.cs: Drop the concept of extensions and pass mimetypes to the individual file readers. * File Readers: Updates for new AudioFileReader interface * src/Tracker/TrackerFileReader.cs, src/Tracker/TrackerTagReader.cs, src/Makefile.am: New audio file reader for amiga tracker files based on work by Boris Peterbarg. * tests/EntaggedTest.cs, tests/samples: Add tracker tests Index: tests/EntaggedTest.cs =================================================================== --- tests/EntaggedTest.cs (revision 48577) +++ tests/EntaggedTest.cs (working copy) @@ -256,3 +256,71 @@ } } +[TestFixture] +public class TrackerS3mTest { + private AudioFileWrapper afw; + + [TestFixtureSetUp] + public void Init() + { + afw = new AudioFileWrapper("samples/sample.s3m"); + } + + [Test] + public void ReadTag() + { + Assert.AreEqual("64-Mania", afw.Title); + } +} + +[TestFixture] +public class TrackerModTest { + private AudioFileWrapper afw; + + [TestFixtureSetUp] + public void Init() + { + afw = new AudioFileWrapper("samples/sample.mod"); + } + + [Test] + public void ReadTag() + { + Assert.AreEqual("Crazy Cow Level-1", afw.Title); + } +} + +[TestFixture] +public class TrackerXmTest { + private AudioFileWrapper afw; + + [TestFixtureSetUp] + public void Init() + { + afw = new AudioFileWrapper("samples/sample.xm"); + } + + [Test] + public void ReadTag() + { + Assert.AreEqual(".midnight.", afw.Title); + } +} + +[TestFixture] +public class TrackerItTest { + private AudioFileWrapper afw; + + [TestFixtureSetUp] + public void Init() + { + afw = new AudioFileWrapper("samples/sample.it"); + } + + [Test] + public void ReadTag() + { + Assert.AreEqual("Brass Funk", afw.Title); + } +} + Index: tests/samples/sample.mod =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: tests/samples/sample.mod ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: tests/samples/sample.s3m =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: tests/samples/sample.s3m ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: tests/samples/sample.xm =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: tests/samples/sample.xm ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: tests/samples/sample.it =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: tests/samples/sample.it ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: tests/ConsoleUi.cs =================================================================== --- tests/ConsoleUi.cs (revision 48576) +++ tests/ConsoleUi.cs (working copy) @@ -80,7 +80,8 @@ try { ConsoleUi consoleUi = new ConsoleUi(); - return consoleUi.Execute( options ); + consoleUi.Execute( options ); + return 0; } catch( FileNotFoundException ex ) { Index: src/Mpc/MpcFileReader.cs =================================================================== --- src/Mpc/MpcFileReader.cs (revision 48576) +++ src/Mpc/MpcFileReader.cs (working copy) @@ -36,19 +36,18 @@ using Entagged.Audioformats.Ape.Util; namespace Entagged.Audioformats.Mpc { - [SupportedExtension ("mpc")] - [SupportedExtension ("mp+")] [SupportedMimeType ("entagged/mpc")] + [SupportedMimeType ("entagged/mp+")] public class MpcFileReader : AudioFileReader { private MpcInfoReader ir = new MpcInfoReader(); private ApeTagReader tr = new ApeTagReader(); - protected override EncodingInfo GetEncodingInfo( Stream raf ) { + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) { return ir.Read(raf); } - protected override Tag GetTag( Stream raf ) { + protected override Tag GetTag(Stream raf, string mime) { return tr.Read(raf); } } Index: src/M4a/M4aFileReader.cs =================================================================== --- src/M4a/M4aFileReader.cs (revision 48576) +++ src/M4a/M4aFileReader.cs (working copy) @@ -33,20 +33,19 @@ namespace Entagged.Audioformats.M4a { - [SupportedExtension ("m4a")] - [SupportedExtension ("m4p")] [SupportedMimeType ("audio/x-m4a")] [SupportedMimeType ("entagged/m4a")] + [SupportedMimeType ("entagged/m4p")] public class M4aFileReader : AudioFileReader { private M4aTagReader tr = new M4aTagReader(); - protected override EncodingInfo GetEncodingInfo(Stream raf) + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) { return tr.ReadEncodingInfo(raf); } - protected override Tag GetTag(Stream raf) + protected override Tag GetTag(Stream raf, string mime) { return tr.ReadTags(raf); } Index: src/Tracker/TrackerFileReader.cs =================================================================== --- src/Tracker/TrackerFileReader.cs (revision 0) +++ src/Tracker/TrackerFileReader.cs (revision 0) @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright 2005 Daniel Drake + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.IO; +using Entagged.Audioformats.Util; +using Entagged.Audioformats.Tracker.Util; + +namespace Entagged.Audioformats.Tracker { + + public enum TrackerFormat { + It, + Mod, + S3m, + Xm, + } + + [SupportedMimeType ("audio/x-s3m")] + [SupportedMimeType ("audio/x-mod")] + [SupportedMimeType ("audio/x-xm")] + [SupportedMimeType ("audio/x-xm")] + [SupportedMimeType ("entagged/s3m")] + [SupportedMimeType ("entagged/mod")] + [SupportedMimeType ("entagged/xm")] + [SupportedMimeType ("entagged/it")] + public class TrackerFileReader : AudioFileReader { + + private TrackerTagReader tr = new TrackerTagReader(); + + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) + { + return new EncodingInfo(); + } + + protected override Tag GetTag(Stream raf, string mime) + { + return tr.Read(raf, GetTrackerFormat(mime)); + } + + private TrackerFormat GetTrackerFormat(string mime) + { + string segment = mime.Substring(mime.IndexOf('/') + 1); + if (segment.StartsWith ("x-")) + segment = segment.Substring(2); + + try { + return (TrackerFormat) Enum.Parse(typeof(TrackerFormat), segment, true); + } catch(Exception e) { + throw new Exception("Unrecognised tracker file format: " + mime); + } + } + + } +} Index: src/Tracker/Util/TrackerTagReader.cs =================================================================== --- src/Tracker/Util/TrackerTagReader.cs (revision 0) +++ src/Tracker/Util/TrackerTagReader.cs (revision 0) @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright 2005 Daniel Drake + * Copyright 2005 Boris Peterbarg + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System.IO; +using System.Text; +using Entagged.Audioformats.Tracker; + +namespace Entagged.Audioformats.Tracker.Util { + + public class TrackerTagReader { + + public Tag Read(Stream fs, TrackerFormat format) + { + Tag tag = new Tag(); + int start_offset = 0; + int end_offset = 0; + string sig = null; + + switch (format) { + case TrackerFormat.S3m: + end_offset = 28; + break; + case TrackerFormat.Mod: + end_offset = 20; + break; + case TrackerFormat.Xm: + start_offset = 17; + end_offset = 20; + break; + case TrackerFormat.It: + sig = "IMPM"; + end_offset = 26; + break; + } + + //Parse the tag -)------------------------------------------------ + fs.Seek(start_offset, SeekOrigin.Begin); + byte[] b; + + if (sig != null) { + b = new byte[sig.Length]; + fs.Read(b, 0, sig.Length); + if (Encoding.ASCII.GetString(b) != sig) + return tag; + } + + b = new byte[end_offset]; + fs.Read(b, 0, end_offset); + + string content = Encoding.ASCII.GetString(b, 0, end_offset); + int term = content.IndexOf('\0'); + if (term == -1) + term = end_offset; + content = content.Substring(0, term).Trim(); + tag.AddTitle(content); + + return tag; + } + + } + +} Index: src/AudioFileIO.cs =================================================================== --- src/AudioFileIO.cs (revision 48576) +++ src/AudioFileIO.cs (working copy) @@ -49,8 +49,7 @@ namespace Entagged.Audioformats { public class AudioFileIO { //These tables contains all the readers writers associated with extensions/mimetypes - private static Hashtable extensions = new Hashtable(); - private static Hashtable mimetypes = new Hashtable(); + private static Hashtable readers = new Hashtable(); //Initialize the different readers/writers using reflection static AudioFileIO() { @@ -61,41 +60,32 @@ continue; AudioFileReader reader = (AudioFileReader) Activator.CreateInstance(type); - Attribute [] attrs = Attribute.GetCustomAttributes(type, typeof(SupportedExtension)); - foreach (SupportedExtension attr in attrs) - extensions.Add (attr.Extension, reader); - - attrs = Attribute.GetCustomAttributes (type, typeof(SupportedMimeType)); + Attribute [] attrs = Attribute.GetCustomAttributes (type, typeof(SupportedMimeType)); foreach (SupportedMimeType attr in attrs) - mimetypes.Add (attr.MimeType, reader); + readers.Add (attr.MimeType, reader); } } public static AudioFile Read(string f) { - string ext = Utils.GetExtension(f); - - object afr = extensions[ext]; - if( afr == null) - throw new CannotReadException("No Reader associated to this extension: "+ext); - - return (afr as AudioFileReader).Read(f); + string mimetype = "entagged/" + Utils.GetExtension(f); + return Read(f, mimetype); } public static AudioFile Read(string f, string mimetype) { - object afr = mimetypes[mimetype]; + object afr = readers[mimetype]; if( afr == null) throw new CannotReadException("No Reader associated to this MimeType: "+mimetype); - return (afr as AudioFileReader).Read(f); + return (afr as AudioFileReader).Read(f, mimetype); } public static AudioFile Read(Stream stream, string mimetype) { - object afr = mimetypes[mimetype]; + object afr = readers[mimetype]; if ( afr == null) throw new CannotReadException("No Reader associated to this MimeType: "+mimetype); - return (afr as AudioFileReader).Read(stream); + return (afr as AudioFileReader).Read(stream, mimetype); } } Index: src/Mp3/Mp3FileReader.cs =================================================================== --- src/Mp3/Mp3FileReader.cs (revision 48576) +++ src/Mp3/Mp3FileReader.cs (working copy) @@ -40,7 +40,6 @@ using Entagged.Audioformats.Mp3.Util; namespace Entagged.Audioformats.Mp3 { - [SupportedExtension ("mp3")] [SupportedMimeType ("audio/x-mp3")] [SupportedMimeType ("entagged/mp3")] public class Mp3FileReader : AudioFileReader { @@ -49,11 +48,11 @@ private Id3v2TagReader idv2tr = new Id3v2TagReader(); private Id3v1TagReader idv1tr = new Id3v1TagReader(); - protected override EncodingInfo GetEncodingInfo( Stream raf ) { + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) { return ir.Read(raf); } - protected override Tag GetTag( Stream raf ) { + protected override Tag GetTag(Stream raf, string mime) { Id3Tag tag = new Id3Tag(); idv2tr.Read(tag, raf); Index: src/Flac/FlacFileReader.cs =================================================================== --- src/Flac/FlacFileReader.cs (revision 48576) +++ src/Flac/FlacFileReader.cs (working copy) @@ -35,7 +35,6 @@ using Entagged.Audioformats.Util; namespace Entagged.Audioformats.Flac { - [SupportedExtension ("flac")] [SupportedMimeType ("audio/x-flac")] [SupportedMimeType ("entagged/flac")] public class FlacFileReader : AudioFileReader { @@ -43,11 +42,11 @@ private FlacInfoReader ir = new FlacInfoReader(); private FlacTagReader tr = new FlacTagReader(); - protected override EncodingInfo GetEncodingInfo( Stream raf ) { + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) { return ir.Read(raf); } - protected override Tag GetTag( Stream raf ) { + protected override Tag GetTag(Stream raf, string mime) { return tr.Read(raf); } } Index: src/Ape/MonkeyFileReader.cs =================================================================== --- src/Ape/MonkeyFileReader.cs (revision 48576) +++ src/Ape/MonkeyFileReader.cs (working copy) @@ -35,18 +35,17 @@ using Entagged.Audioformats.Ape.Util; namespace Entagged.Audioformats.Ape { - [SupportedExtension ("ape")] [SupportedMimeType ("entagged/ape")] public class MonkeyFileReader : AudioFileReader { private ApeTagReader ape = new ApeTagReader(); private MonkeyInfoReader ir = new MonkeyInfoReader(); - protected override EncodingInfo GetEncodingInfo( Stream raf ) { + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) { return ir.Read(raf); } - protected override Tag GetTag( Stream raf ) { + protected override Tag GetTag(Stream raf, string mime) { return ape.Read(raf); } } Index: src/Makefile.am =================================================================== --- src/Makefile.am (revision 48577) +++ src/Makefile.am (working copy) @@ -55,6 +55,10 @@ $(srcdir)/M4a/Util/M4aInfoReader.cs \ $(srcdir)/M4a/Util/M4aTagReader.cs +TRACKER_CSFILES = \ + $(srcdir)/Tracker/TrackerFileReader.cs \ + $(srcdir)/Tracker/Util/TrackerTagReader.cs + ASSEMBLY_SOURCES = \ $(srcdir)/AssemblyInfo.cs \ $(srcdir)/Exceptions/CannotReadException.cs \ @@ -73,7 +77,8 @@ $(OGG_CSFILES) \ $(MPC_CSFILES) \ $(MP3_CSFILES) \ - $(M4A_CSFILES) + $(M4A_CSFILES) \ + $(TRACKER_CSFILES) all: $(ASSEMBLY) Index: src/Ogg/OggFileReader.cs =================================================================== --- src/Ogg/OggFileReader.cs (revision 48576) +++ src/Ogg/OggFileReader.cs (working copy) @@ -35,7 +35,6 @@ using Entagged.Audioformats.Ogg.Util; namespace Entagged.Audioformats.Ogg { - [SupportedExtension ("ogg")] [SupportedMimeType ("application/ogg")] [SupportedMimeType ("entagged/ogg")] public class OggFileReader : AudioFileReader { @@ -43,11 +42,11 @@ private OggInfoReader ir = new OggInfoReader(); private VorbisTagReader otr = new VorbisTagReader(); - protected override EncodingInfo GetEncodingInfo( Stream raf ) { + protected override EncodingInfo GetEncodingInfo(Stream raf, string mime) { return ir.Read(raf); } - protected override Tag GetTag( Stream raf ) { + protected override Tag GetTag(Stream raf, string mime) { return otr.Read(raf); } } Index: src/Util/CustomAttributes.cs =================================================================== --- src/Util/CustomAttributes.cs (revision 48576) +++ src/Util/CustomAttributes.cs (working copy) @@ -3,19 +3,6 @@ namespace Entagged.Audioformats.Util { [AttributeUsage (AttributeTargets.Class, AllowMultiple=true)] - public class SupportedExtension : Attribute { - private string extension; - public string Extension { - get { return extension; } - } - - public SupportedExtension (string extension) - { - this.extension = extension; - } - } - - [AttributeUsage (AttributeTargets.Class, AllowMultiple=true)] public class SupportedMimeType : Attribute { private string mime_type; public string MimeType { Index: src/Util/AudioFileReader.cs =================================================================== --- src/Util/AudioFileReader.cs (revision 48576) +++ src/Util/AudioFileReader.cs (working copy) @@ -37,10 +37,10 @@ namespace Entagged.Audioformats.Util { public abstract class AudioFileReader { - protected abstract EncodingInfo GetEncodingInfo( Stream raf ); - protected abstract Tag GetTag( Stream raf ); + protected abstract EncodingInfo GetEncodingInfo(Stream raf, string mime); + protected abstract Tag GetTag(Stream raf, string mime); - public AudioFile Read(string f) { + public AudioFile Read(string f, string mime) { FileStream stream; try { @@ -49,10 +49,10 @@ throw new CannotReadException("\""+f+"\": "+e.Message); } - return Read (stream); + return Read (stream, mime); } - public AudioFile Read(Stream stream) { + public AudioFile Read(Stream stream, string mime) { if (! stream.CanSeek) throw new CannotReadException("Stream not seekable"); @@ -62,12 +62,12 @@ try{ stream.Seek( 0, SeekOrigin.Begin ); - EncodingInfo info = GetEncodingInfo(stream); + EncodingInfo info = GetEncodingInfo(stream, mime); Tag tag = null; try { stream.Seek( 0, SeekOrigin.Begin ); - tag = GetTag(stream); + tag = GetTag(stream, mime); } catch (CannotReadException e) { // Do nothing }