Beispiel #1
0
        public static Boolean IsDataEligible(MetaDataIO meta)
        {
            if (meta.Title.Length > 0)
            {
                return(true);
            }
            if (meta.Artist.Length > 0)
            {
                return(true);
            }
            if (meta.Comment.Length > 0)
            {
                return(true);
            }
            if (meta.Genre.Length > 0)
            {
                return(true);
            }
            if (meta.Copyright.Length > 0)
            {
                return(true);
            }

            foreach (var key in meta.AdditionalFields.Keys)
            {
                if (key.StartsWith("info."))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #2
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, uint chunkSize)
        {
            long   position = source.Position;
            long   initialPos = position;
            string key, value;
            int    size;

            byte[] data = new byte[chunkSize];

            while (source.Position < initialPos + chunkSize - 4) // 4 being the "INFO" purpose that belongs to the chunk
            {
                // Key
                source.Read(data, 0, 4);
                key = Utils.Latin1Encoding.GetString(data, 0, 4);
                // Size
                source.Read(data, 0, 4);
                size = StreamUtils.DecodeInt32(data);
                if (size > 0)
                {
                    source.Read(data, 0, size);
                    // Manage parasite zeroes at the end of data
                    if (source.ReadByte() != 0)
                    {
                        source.Seek(-1, SeekOrigin.Current);
                    }
                    value = Utils.Latin1Encoding.GetString(data, 0, size);
                    meta.SetMetaField("info." + key, Utils.StripEndingZeroChars(value), readTagParams.ReadAllMetaFrames);
                }
            }
        }
Beispiel #3
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, UInt32 chunkSize)
        {
            var    position = source.Position;
            var    initialPos = position;
            String key, value;
            Int32  size;
            var    data = new Byte[256];

            while (source.Position < initialPos + chunkSize - 4) // 4 being the "INFO" purpose that belongs to the chunk
            {
                // Key
                source.Read(data, 0, 4);
                key = Utils.Latin1Encoding.GetString(data, 0, 4);
                // Size
                source.Read(data, 0, 4);
                size = StreamUtils.DecodeInt32(data);
                // Value
                value = StreamUtils.ReadNullTerminatedString(source, Utils.Latin1Encoding);

                if (value.Length > 0)
                {
                    meta.SetMetaField("info." + key, value, readTagParams.ReadAllMetaFrames);
                }

                position = source.Position;
            }
        }
Beispiel #4
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, uint chunkSize)
        {
            long   position = source.Position;
            long   initialPos = position;
            string key, value;
            int    size;

            byte[] data   = new byte[chunkSize];
            long   maxPos = initialPos + chunkSize - 4; // 4 being the "INFO" purpose that belongs to the chunk

            while (source.Position < maxPos)
            {
                // Key
                source.Read(data, 0, 4);
                key = Utils.Latin1Encoding.GetString(data, 0, 4);
                // Size
                source.Read(data, 0, 4);
                size = StreamUtils.DecodeInt32(data);
                // Do _NOT_ use StreamUtils.ReadNullTerminatedString because non-textual fields may be found here (e.g. NITR)
                if (size > 0)
                {
                    source.Read(data, 0, size);
                    // Manage parasite zeroes at the end of data
                    if (source.Position < maxPos && source.ReadByte() != 0)
                    {
                        source.Seek(-1, SeekOrigin.Current);
                    }
                    value = Utils.Latin1Encoding.GetString(data, 0, size);
                    meta.SetMetaField("info." + key, Utils.StripEndingZeroChars(value), readTagParams.ReadAllMetaFrames);
                }
            }
        }
Beispiel #5
0
        private static int readInt32(Stream source, MetaDataIO meta, string fieldName, byte[] buffer, bool readAllMetaFrames)
        {
            source.Read(buffer, 0, 4);
            int value = StreamUtils.DecodeInt32(buffer);

            meta.SetMetaField(fieldName, value.ToString(), readAllMetaFrames);
            return(value);
        }
Beispiel #6
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, uint chunkSize)
        {
            IList <string> position    = new List <string>();
            bool           inList      = false;
            int            listDepth   = 0;
            int            listCounter = 1;

            position.Add("ixml");

            using (MemoryStream mem = new MemoryStream((int)chunkSize))
            {
                StreamUtils.CopyStream(source, mem, (int)chunkSize); // Isolate XML structure in a clean memory chunk
                mem.Seek(0, SeekOrigin.Begin);

                using (XmlReader reader = XmlReader.Create(mem))
                {
                    while (reader.Read())
                    {
                        switch (reader.NodeType)
                        {
                        case XmlNodeType.Element:     // Element start
                            string key = reader.Name;
                            if (inList && reader.Depth == listDepth + 1 && !key.EndsWith("COUNT", StringComparison.OrdinalIgnoreCase))
                            {
                                key = key + "[" + listCounter + "]";
                                listCounter++;
                            }
                            if (!key.Equals("BWFXML", StringComparison.OrdinalIgnoreCase))
                            {
                                position.Add(key);
                            }
                            if (!inList && reader.Name.EndsWith("LIST", StringComparison.OrdinalIgnoreCase))
                            {
                                inList      = true;
                                listDepth   = reader.Depth;
                                listCounter = 1;
                            }
                            break;

                        case XmlNodeType.Text:
                            if (reader.Value != null && reader.Value.Length > 0)
                            {
                                meta.SetMetaField(getPosition(position), reader.Value, readTagParams.ReadAllMetaFrames);
                            }
                            break;

                        case XmlNodeType.EndElement:     // Element end
                            position.RemoveAt(position.Count - 1);
                            if (inList && reader.Name.EndsWith("LIST", StringComparison.OrdinalIgnoreCase))
                            {
                                inList = false;
                            }
                            break;
                        }
                    }
                }
            }
        }
Beispiel #7
0
        public static bool IsDataEligible(MetaDataIO meta)
        {
            foreach (string key in meta.AdditionalFields.Keys)
            {
                if (key.StartsWith("ixml."))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #8
0
        public static bool IsDataEligible(MetaDataIO meta)
        {
            if (meta.GeneralDescription.Length > 0)
            {
                return(true);
            }

            foreach (string key in meta.AdditionalFields.Keys)
            {
                if (key.StartsWith("bext."))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #9
0
        public static Int32 ToStream(BinaryWriter w, Boolean isLittleEndian, MetaDataIO meta)
        {
            var additionalFields = meta.AdditionalFields;

            w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_LIST));

            var sizePos = w.BaseStream.Position;

            w.Write((Int32)0); // Placeholder for chunk size that will be rewritten at the end of the method

            w.Write(Utils.Latin1Encoding.GetBytes(PURPOSE_INFO));

            // 'Classic' fields (NB : usually done within a loop by accessing MetaDataIO.tagData)
            IDictionary <String, String> writtenFields = new Dictionary <String, String>();
            // Title
            var value = Utils.ProtectValue(meta.Title);

            if (0 == value.Length && additionalFields.Keys.Contains("info.INAM"))
            {
                value = additionalFields["info.INAM"];
            }
            if (value.Length > 0)
            {
                writeSizeAndNullTerminatedString("INAM", value, w, writtenFields);
            }
            // Artist
            value = Utils.ProtectValue(meta.Artist);
            if (0 == value.Length && additionalFields.Keys.Contains("info.IART"))
            {
                value = additionalFields["info.IART"];
            }
            if (value.Length > 0)
            {
                writeSizeAndNullTerminatedString("IART", value, w, writtenFields);
            }
            // Copyright
            value = Utils.ProtectValue(meta.Copyright);
            if (0 == value.Length && additionalFields.Keys.Contains("info.ICOP"))
            {
                value = additionalFields["info.ICOP"];
            }
            if (value.Length > 0)
            {
                writeSizeAndNullTerminatedString("ICOP", value, w, writtenFields);
            }
            // Genre
            value = Utils.ProtectValue(meta.Genre);
            if (0 == value.Length && additionalFields.Keys.Contains("info.IGNR"))
            {
                value = additionalFields["info.IGNR"];
            }
            if (value.Length > 0)
            {
                writeSizeAndNullTerminatedString("IGNR", value, w, writtenFields);
            }
            // Comment
            value = Utils.ProtectValue(meta.Comment);
            if (0 == value.Length && additionalFields.Keys.Contains("info.ICMT"))
            {
                value = additionalFields["info.ICMT"];
            }
            if (value.Length > 0)
            {
                writeSizeAndNullTerminatedString("ICMT", value, w, writtenFields);
            }

            String shortKey;

            foreach (var key in additionalFields.Keys)
            {
                if (key.StartsWith("info."))
                {
                    shortKey = key.Substring(5, key.Length - 5).ToUpper();
                    if (!writtenFields.ContainsKey(key))
                    {
                        if (additionalFields[key].Length > 0)
                        {
                            writeSizeAndNullTerminatedString(shortKey, additionalFields[key], w, writtenFields);
                        }
                    }
                }
            }

            var finalPos = w.BaseStream.Position;

            w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
            if (isLittleEndian)
            {
                w.Write((Int32)(finalPos - sizePos - 4));
            }
            else
            {
                w.Write(StreamUtils.EncodeBEInt32((Int32)(finalPos - sizePos - 4)));
            }

            return(14);
        }
Beispiel #10
0
        public static int ToStream(BinaryWriter w, bool isLittleEndian, MetaDataIO meta)
        {
            IDictionary <string, string> additionalFields = meta.AdditionalFields;

            w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_IXML));

            long sizePos = w.BaseStream.Position;

            w.Write(0); // Placeholder for chunk size that will be rewritten at the end of the method


            XmlWriterSettings settings = new XmlWriterSettings();

            settings.CloseOutput = false;
            settings.Encoding    = Encoding.UTF8;

            XmlWriter writer = XmlWriter.Create(w.BaseStream, settings);

            //writer.Formatting = Formatting.None;


            writer.WriteStartDocument();
            writer.WriteStartElement("BWFXML");

            string[] path;
            string[] previousPath = null;
            bool     first        = true;
            string   subkey;

            foreach (string key in additionalFields.Keys)
            {
                if (key.StartsWith("ixml."))
                {
                    path = key.Split('.');
                    if (first)
                    {
                        previousPath = path;
                        first        = false;
                    }

                    // Closes all terminated paths
                    for (int i = previousPath.Length - 2; i >= 0; i--)
                    {
                        if ((path.Length <= i) || (path.Length > i && !path[i].Equals(previousPath[i])))
                        {
                            writer.WriteEndElement();
                        }
                    }

                    // Opens all new paths
                    for (int i = 0; i < path.Length - 1; i++)
                    {
                        if (previousPath.Length <= i || !path[i].Equals(previousPath[i]))
                        {
                            subkey = path[i];
                            if (subkey.Contains("["))
                            {
                                subkey = subkey.Substring(0, subkey.IndexOf("["));                       // Remove [x]'s
                            }
                            writer.WriteStartElement(subkey.ToUpper());
                        }
                    }

                    writer.WriteElementString(path[path.Length - 1], additionalFields[key]);

                    previousPath = path;
                }
            }

            // Closes all terminated paths
            if (previousPath != null)
            {
                for (int i = previousPath.Length - 2; i >= 0; i--)
                {
                    writer.WriteEndElement();
                }
            }
            writer.Close();


            long finalPos = w.BaseStream.Position;

            w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
            if (isLittleEndian)
            {
                w.Write((int)(finalPos - sizePos - 4));
            }
            else
            {
                w.Write(StreamUtils.EncodeBEInt32((int)(finalPos - sizePos - 4)));
            }

            return(14);
        }
Beispiel #11
0
        public static int ToStream(BinaryWriter w, bool isLittleEndian, MetaDataIO meta)
        {
            IDictionary <string, string> additionalFields = meta.AdditionalFields;

            w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_BEXT));

            long sizePos = w.BaseStream.Position;

            w.Write((int)0); // Placeholder for chunk size that will be rewritten at the end of the method

            // Text values
            string description = Utils.ProtectValue(meta.GeneralDescription);

            if (0 == description.Length && additionalFields.Keys.Contains("bext.description"))
            {
                description = additionalFields["bext.description"];
            }

            writeFixedTextValue(description, 256, w);
            writeFixedFieldTextValue("bext.originator", 32, additionalFields, w);
            writeFixedFieldTextValue("bext.originatorReference", 32, additionalFields, w);
            writeFixedFieldTextValue("bext.originationDate", 10, additionalFields, w);
            writeFixedFieldTextValue("bext.originationTime", 8, additionalFields, w);

            // Int values
            writeFieldIntValue("bext.timeReference", additionalFields, w, (ulong)0);
            writeFieldIntValue("bext.version", additionalFields, w, (ushort)0);

            // UMID
            if (additionalFields.Keys.Contains("bext.UMID"))
            {
                if (Utils.IsHex(additionalFields["bext.UMID"]))
                {
                    int usedValues = (int)Math.Floor(additionalFields["bext.UMID"].Length / 2.0);
                    for (int i = 0; i < usedValues; i++)
                    {
                        w.Write(Convert.ToByte(additionalFields["bext.UMID"].Substring(i * 2, 2), 16));
                    }
                    // Complete the field to 64 bytes
                    for (int i = 0; i < 64 - usedValues; i++)
                    {
                        w.Write((byte)0);
                    }
                }
                else
                {
                    LogDelegator.GetLogDelegate()(Log.LV_WARNING, "'bext.UMID' : error writing field - hexadecimal notation required; " + additionalFields["bext.UMID"] + " found");
                    for (int i = 0; i < 64; i++)
                    {
                        w.Write((byte)0);
                    }
                }
            }
            else
            {
                for (int i = 0; i < 64; i++)
                {
                    w.Write((byte)0);
                }
            }


            // Float values
            writeField100DecimalValue("bext.loudnessValue", additionalFields, w, (short)0);
            writeField100DecimalValue("bext.loudnessRange", additionalFields, w, (short)0);
            writeField100DecimalValue("bext.maxTruePeakLevel", additionalFields, w, (short)0);
            writeField100DecimalValue("bext.maxMomentaryLoudness", additionalFields, w, (short)0);
            writeField100DecimalValue("bext.maxShortTermLoudness", additionalFields, w, (short)0);

            // Reserved
            for (int i = 0; i < 180; i++)
            {
                w.Write((byte)0);
            }

            // CodingHistory
            byte[] textData = new byte[0];
            if (additionalFields.Keys.Contains("bext.codingHistory"))
            {
                textData = Utils.Latin1Encoding.GetBytes(additionalFields["bext.codingHistory"]);
                w.Write(textData);
            }
            w.Write(new byte[2] {
                13, 10
            } /* CR LF */);

            // Emulation of the BWFMetaEdit padding behaviour (256 characters)
            for (int i = 0; i < 256 - ((textData.Length + 2) % 256); i++)
            {
                w.Write((byte)0);
            }


            long finalPos = w.BaseStream.Position;

            w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
            if (isLittleEndian)
            {
                w.Write((int)(finalPos - sizePos - 4));
            }
            else
            {
                w.Write(StreamUtils.EncodeBEInt32((int)(finalPos - sizePos - 4)));
            }

            return(14);
        }
Beispiel #12
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams)
        {
            string str;

            byte[] data = new byte[256];

            // Description
            source.Read(data, 0, 256);
            str = Utils.StripEndingZeroChars(Utils.Latin1Encoding.GetString(data).Trim());
            if (str.Length > 0)
            {
                meta.SetMetaField("bext.description", str, readTagParams.ReadAllMetaFrames);
            }

            // Originator
            source.Read(data, 0, 32);
            str = Utils.StripEndingZeroChars(Utils.Latin1Encoding.GetString(data, 0, 32).Trim());
            if (str.Length > 0)
            {
                meta.SetMetaField("bext.originator", str, readTagParams.ReadAllMetaFrames);
            }

            // OriginatorReference
            source.Read(data, 0, 32);
            str = Utils.StripEndingZeroChars(Utils.Latin1Encoding.GetString(data, 0, 32).Trim());
            if (str.Length > 0)
            {
                meta.SetMetaField("bext.originatorReference", str, readTagParams.ReadAllMetaFrames);
            }

            // OriginationDate
            source.Read(data, 0, 10);
            str = Utils.StripEndingZeroChars(Utils.Latin1Encoding.GetString(data, 0, 10).Trim());
            if (str.Length > 0)
            {
                meta.SetMetaField("bext.originationDate", str, readTagParams.ReadAllMetaFrames);
            }

            // OriginationTime
            source.Read(data, 0, 8);
            str = Utils.StripEndingZeroChars(Utils.Latin1Encoding.GetString(data, 0, 8).Trim());
            if (str.Length > 0)
            {
                meta.SetMetaField("bext.originationTime", str, readTagParams.ReadAllMetaFrames);
            }

            // TimeReference
            source.Read(data, 0, 8);
            ulong timeReference = StreamUtils.DecodeUInt64(data);

            meta.SetMetaField("bext.timeReference", timeReference.ToString(), readTagParams.ReadAllMetaFrames);

            // BEXT version
            source.Read(data, 0, 2);
            int intData = StreamUtils.DecodeUInt16(data);

            meta.SetMetaField("bext.version", intData.ToString(), readTagParams.ReadAllMetaFrames);

            // UMID
            source.Read(data, 0, 64);
            str = "";

            int usefulLength = 32; // "basic" UMID

            if (data[12] > 19)
            {
                usefulLength = 64;                // data[12] gives the size of remaining UMID
            }
            for (int i = 0; i < usefulLength; i++)
            {
                str = str + data[i].ToString("X2");
            }

            meta.SetMetaField("bext.UMID", str, readTagParams.ReadAllMetaFrames);

            // LoudnessValue
            source.Read(data, 0, 2);
            intData = StreamUtils.DecodeInt16(data);
            meta.SetMetaField("bext.loudnessValue", (intData / 100.0).ToString(), readTagParams.ReadAllMetaFrames);

            // LoudnessRange
            source.Read(data, 0, 2);
            intData = StreamUtils.DecodeInt16(data);
            meta.SetMetaField("bext.loudnessRange", (intData / 100.0).ToString(), readTagParams.ReadAllMetaFrames);

            // MaxTruePeakLevel
            source.Read(data, 0, 2);
            intData = StreamUtils.DecodeInt16(data);
            meta.SetMetaField("bext.maxTruePeakLevel", (intData / 100.0).ToString(), readTagParams.ReadAllMetaFrames);

            // MaxMomentaryLoudness
            source.Read(data, 0, 2);
            intData = StreamUtils.DecodeInt16(data);
            meta.SetMetaField("bext.maxMomentaryLoudness", (intData / 100.0).ToString(), readTagParams.ReadAllMetaFrames);

            // MaxShortTermLoudness
            source.Read(data, 0, 2);
            intData = StreamUtils.DecodeInt16(data);
            meta.SetMetaField("bext.maxShortTermLoudness", (intData / 100.0).ToString(), readTagParams.ReadAllMetaFrames);

            // Reserved
            source.Seek(180, SeekOrigin.Current);

            // CodingHistory
            long initialPos = source.Position;

            if (StreamUtils.FindSequence(source, new byte[2] {
                13, 10
            } /* CR LF */))
            {
                long endPos = source.Position - 2;
                source.Seek(initialPos, SeekOrigin.Begin);

                if (data.Length < (int)(endPos - initialPos))
                {
                    data = new byte[(int)(endPos - initialPos)];
                }
                source.Read(data, 0, (int)(endPos - initialPos));

                str = Utils.StripEndingZeroChars(Utils.Latin1Encoding.GetString(data, 0, (int)(endPos - initialPos)).Trim());
                if (str.Length > 0)
                {
                    meta.SetMetaField("bext.codingHistory", str, readTagParams.ReadAllMetaFrames);
                }
            }
        }
Beispiel #13
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams)
        {
            string str;

            byte[] data = new byte[256];

            // Manufacturer
            readInt32(source, meta, "sample.manufacturer", data, readTagParams.ReadAllMetaFrames);

            // Product
            readInt32(source, meta, "sample.product", data, readTagParams.ReadAllMetaFrames);

            // Period
            readInt32(source, meta, "sample.period", data, readTagParams.ReadAllMetaFrames);

            // MIDI unity note
            readInt32(source, meta, "sample.MIDIUnityNote", data, readTagParams.ReadAllMetaFrames);

            // MIDI pitch fraction
            readInt32(source, meta, "sample.MIDIPitchFraction", data, readTagParams.ReadAllMetaFrames);

            // SMPTE format
            readInt32(source, meta, "sample.SMPTEFormat", data, readTagParams.ReadAllMetaFrames);

            // SMPTE offsets
            source.Read(data, 0, 1);
            sbyte sByteData = StreamUtils.DecodeSignedByte(data);

            meta.SetMetaField("sample.SMPTEOffset.Hours", sByteData.ToString(), readTagParams.ReadAllMetaFrames);
            source.Read(data, 0, 1);
            byte byteData = StreamUtils.DecodeUByte(data);

            meta.SetMetaField("sample.SMPTEOffset.Minutes", byteData.ToString(), readTagParams.ReadAllMetaFrames);
            source.Read(data, 0, 1);
            byteData = StreamUtils.DecodeUByte(data);
            meta.SetMetaField("sample.SMPTEOffset.Seconds", byteData.ToString(), readTagParams.ReadAllMetaFrames);
            source.Read(data, 0, 1);
            byteData = StreamUtils.DecodeUByte(data);
            meta.SetMetaField("sample.SMPTEOffset.Frames", byteData.ToString(), readTagParams.ReadAllMetaFrames);

            // Num sample loops
            int numSampleLoops = readInt32(source, meta, "sample.NumSampleLoops", data, readTagParams.ReadAllMetaFrames);

            // Sample loops size (not useful here)
            source.Seek(4, SeekOrigin.Current);

            for (int i = 0; i < numSampleLoops; i++)
            {
                // Cue point ID
                readInt32(source, meta, "sample.SampleLoop[" + i + "].CuePointId", data, readTagParams.ReadAllMetaFrames);

                // Type
                readInt32(source, meta, "sample.SampleLoop[" + i + "].Type", data, readTagParams.ReadAllMetaFrames);

                // Start
                readInt32(source, meta, "sample.SampleLoop[" + i + "].Start", data, readTagParams.ReadAllMetaFrames);

                // End
                readInt32(source, meta, "sample.SampleLoop[" + i + "].End", data, readTagParams.ReadAllMetaFrames);

                // Fraction
                readInt32(source, meta, "sample.SampleLoop[" + i + "].Fraction", data, readTagParams.ReadAllMetaFrames);

                // Play count
                readInt32(source, meta, "sample.SampleLoop[" + i + "].PlayCount", data, readTagParams.ReadAllMetaFrames);
            }
        }
Beispiel #14
0
        public static int ToStream(BinaryWriter w, bool isLittleEndian, MetaDataIO meta)
        {
            IDictionary <string, string> additionalFields = meta.AdditionalFields;

            w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_SAMPLE));

            long sizePos = w.BaseStream.Position;

            w.Write(0); // Placeholder for chunk size that will be rewritten at the end of the method

            // Int values
            writeFieldIntValue("sample.manufacturer", additionalFields, w, 0);
            writeFieldIntValue("sample.product", additionalFields, w, 0);
            writeFieldIntValue("sample.period", additionalFields, w, 1);
            writeFieldIntValue("sample.MIDIUnityNote", additionalFields, w, 0);
            writeFieldIntValue("sample.MIDIPitchFraction", additionalFields, w, 0);
            writeFieldIntValue("sample.SMPTEFormat", additionalFields, w, 0);

            // SMPTE offset
            writeFieldIntValue("sample.SMPTEOffset.Hours", additionalFields, w, (sbyte)0);
            writeFieldIntValue("sample.SMPTEOffset.Minutes", additionalFields, w, (byte)0);
            writeFieldIntValue("sample.SMPTEOffset.Seconds", additionalFields, w, (byte)0);
            writeFieldIntValue("sample.SMPTEOffset.Frames", additionalFields, w, (byte)0);

            // == Sample loops

            // How many of them do we have ? -> count distinct indexes
            IList <string> keys = new List <string>();

            foreach (string s in additionalFields.Keys)
            {
                if (s.StartsWith("sample.SampleLoop"))
                {
                    string key = s.Substring(0, s.IndexOf("]") + 1);
                    if (!keys.Contains(key))
                    {
                        keys.Add(key);
                    }
                }
            }
            w.Write(keys.Count);

            // Sample loops data size
            long sampleLoopsPos = w.BaseStream.Position;

            w.Write(0); // Placeholder for data size that will be rewritten at the end of the method

            // Sample loops data
            foreach (string key in keys)
            {
                writeFieldIntValue(key + ".CuePointId", additionalFields, w, 0);
                writeFieldIntValue(key + ".Type", additionalFields, w, 0);
                writeFieldIntValue(key + ".Start", additionalFields, w, 0);
                writeFieldIntValue(key + ".End", additionalFields, w, 0);
                writeFieldIntValue(key + ".Fraction", additionalFields, w, 0);
                writeFieldIntValue(key + ".PlayCount", additionalFields, w, 0);
            }

            // Write actual sample loops data size
            long finalPos = w.BaseStream.Position;

            w.BaseStream.Seek(sampleLoopsPos, SeekOrigin.Begin);
            w.Write((int)(finalPos - sampleLoopsPos - 4));

            // Write actual tag size
            w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
            if (isLittleEndian)
            {
                w.Write((int)(finalPos - sizePos - 4));
            }
            else
            {
                w.Write(StreamUtils.EncodeBEInt32((int)(finalPos - sizePos - 4)));
            }

            return(10);
        }
        public static int ToStream(BinaryWriter w, bool isLittleEndian, MetaDataIO meta)
        {
            IDictionary <string, string> additionalFields = meta.AdditionalFields;

            w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_IXML));

            long sizePos = w.BaseStream.Position;

            w.Write(0); // Placeholder for chunk size that will be rewritten at the end of the method


            XmlWriterSettings settings = new XmlWriterSettings();

            settings.CloseOutput = false;
            settings.Encoding    = Encoding.UTF8;

            XmlWriter writer = XmlWriter.Create(w.BaseStream, settings);

            //writer.Formatting = Formatting.None;


            writer.WriteStartDocument();
            writer.WriteStartElement("BWFXML");

            // Path notes : key = node path; value = node name
            Dictionary <string, string> pathNodes = new Dictionary <string, string>();
            List <string> previousPathNodes       = new List <string>();
            string        subkey;

            foreach (string key in additionalFields.Keys)
            {
                if (key.StartsWith("ixml."))
                {
                    // Create the list of path nodes
                    List <string> singleNodes = new List <string>(key.Split('.'));
                    singleNodes.RemoveAt(0); // Remove the "ixml" node

                    StringBuilder nodePrefix = new StringBuilder();
                    pathNodes.Clear();
                    foreach (string nodeName in singleNodes)
                    {
                        nodePrefix.Append(".").Append(nodeName);
                        pathNodes.Add(nodePrefix.ToString(), nodeName);
                    }

                    // Close all terminated (i.e. non present in current path) nodes in reverse order
                    for (int i = previousPathNodes.Count - 2; i >= 0; i--)
                    {
                        if (!pathNodes.ContainsKey(previousPathNodes[i]))
                        {
                            writer.WriteEndElement();
                        }
                    }

                    // Opens all new (i.e. non present in previous path) nodes
                    foreach (string nodePath in pathNodes.Keys)
                    {
                        if (!previousPathNodes.Contains(nodePath))
                        {
                            subkey = pathNodes[nodePath];
                            if (subkey.Equals(singleNodes.Last()))
                            {
                                continue;                                    // Last node is a leaf, not a node
                            }
                            if (subkey.Contains("["))
                            {
                                subkey = subkey.Substring(0, subkey.IndexOf("["));                       // Remove [x]'s
                            }
                            writer.WriteStartElement(subkey.ToUpper());
                        }
                    }

                    // Write the last node (=leaf) as a proper value
                    writer.WriteElementString(singleNodes.Last(), additionalFields[key]);

                    previousPathNodes = pathNodes.Keys.ToList();
                }
            }

            // Closes all terminated paths
            if (previousPathNodes != null)
            {
                for (int i = previousPathNodes.Count - 2; i >= 0; i--)
                {
                    writer.WriteEndElement();
                }
            }
            writer.Close();


            long finalPos = w.BaseStream.Position;

            w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
            if (isLittleEndian)
            {
                w.Write((int)(finalPos - sizePos - 4));
            }
            else
            {
                w.Write(StreamUtils.EncodeBEInt32((int)(finalPos - sizePos - 4)));
            }

            return(14);
        }