private static void ReadAudioMetaFields(StreamBuffer sb, MusicMatchTag tag) { if (sb == null) throw new ArgumentNullException("sb"); // Single-line text fields tag.SongTitle = ReadTextField(sb); tag.AlbumTitle = ReadTextField(sb); tag.ArtistName = ReadTextField(sb); tag.Genre = ReadTextField(sb); tag.Tempo = ReadTextField(sb); tag.Mood = ReadTextField(sb); tag.Situation = ReadTextField(sb); tag.Preference = ReadTextField(sb); // Non-text fields tag.SongDuration = ReadTextField(sb); tag.CreationDate = DateTime.FromOADate(sb.ReadDouble()); tag.PlayCounter = sb.ReadInt32(); tag.OriginalFilename = ReadTextField(sb); tag.SerialNumber = ReadTextField(sb); tag.TrackNumber = sb.ReadInt16(); // Multi-line text fields tag.Notes = ReadTextField(sb); tag.ArtistBio = ReadTextField(sb); tag.Lyrics = ReadTextField(sb); // Internet addresses tag.ArtistUrl = ReadTextField(sb); tag.BuyCdUrl = ReadTextField(sb); tag.ArtistEmail = ReadTextField(sb); }
////------------------------------------------------------------------------------------------------------------------------------ /// <inheritdoc/> public IAudioTagOffset ReadFromStream(Stream stream, TagOrigin tagOrigin) { if (stream == null) throw new ArgumentNullException("stream"); if (!stream.CanRead) throw new InvalidOperationException("stream can not be read"); if (!stream.CanSeek) throw new InvalidOperationException("stream can not be seeked"); StreamBuffer sb = stream as StreamBuffer ?? new StreamBuffer(stream); MusicMatchTag tag = new MusicMatchTag(); // Should we read the header or footer? MusicMatchHeader headerOrFooter = (tagOrigin == TagOrigin.Start) ? ReadHeader(sb, tagOrigin) : ReadFooter(sb, tagOrigin); if (headerOrFooter == null) return null; long startOffset = 0, endOffset = 0, streamLength = sb.Length; MusicMatchHeader header = null, footer = null, versionInformation = null; if (tagOrigin == TagOrigin.Start) { header = headerOrFooter; tag.UseHeader = true; startOffset = header.Position; } else if (tagOrigin == TagOrigin.End) { footer = headerOrFooter; endOffset = Math.Min(footer.Position + MusicMatchTag.FooterSize, streamLength); // Seek to the Audio metadata offset struct sb.Position = Math.Max(endOffset - MusicMatchTag.FooterSize - MusicMatchTag.DataOffsetFieldsSize, 0); // Read the Audio Meta-data Offsets MusicMatchDataOffsets dataOffsets = ReadDataOffsets(sb); long startPosition = Math.Max(sb.Position + MusicMatchTag.FooterSize, 0); foreach (int audioMetaDataOffset in AudioMetaDataSizes.OrderBy(a => a)) { // Seek to the start of the version information, based on the audioMetaDataOffset long startPositionHeader = Math.Max(startPosition - audioMetaDataOffset - MusicMatchTag.HeaderSize, 0); long endPositionHeader = startPositionHeader + HeaderIdentifierBytes.Length; // Read the version information versionInformation = ReadHeaderFooter(sb, startPositionHeader, endPositionHeader, HeaderIdentifierBytes, TagOrigin.Start); if (versionInformation == null) continue; startOffset = (versionInformation.Position + MusicMatchTag.HeaderSize) - dataOffsets.TotalDataOffsetsSize; sb.Position = Math.Max(startOffset - MusicMatchTag.HeaderSize, 0); header = ReadHeader(sb, TagOrigin.Start); break; } // We've got a problem, Houston. if (versionInformation == null) return null; // Header found? set new startPosition if (header != null) { if (header.Position == versionInformation.Position) { header = null; } else { tag.UseHeader = true; startOffset = header.Position; } } } sb.Position = startOffset + (tag.UseHeader ? MusicMatchTag.HeaderSize : 0); // Read image tag.Image = MusicMatchImage.ReadFromStream(sb); // Unused, null padding byte[] nullPadding = new byte[4]; sb.Read(nullPadding, nullPadding.Length); // Read version info if (versionInformation == null) { versionInformation = ReadHeader(sb, TagOrigin.Start); if (versionInformation == null) { #if DEBUG throw new InvalidDataException("Version information not found in the tag."); #else return null; #endif } } else { // version information has the same size as the header size. sb.Position += MusicMatchTag.HeaderSize; } if (tag.UseHeader && !versionInformation.Equals(header)) { #if DEBUG throw new InvalidDataException("Version information field(s) mismatch the header fields."); #else return null; #endif } tag.XingEncoderVersion = versionInformation.XingEncoderVersion; tag.Version = versionInformation.MusicMatchVersion; // Read the Audio meta-data ReadAudioMetaFields(sb, tag); // Padding sb.Position += 16; if (tagOrigin == TagOrigin.Start) { long startPosition = sb.Position; foreach (int audioMetaDataOffset in AudioMetaDataSizes.OrderBy(a => a)) { //Look for the start of the footer. long startPositionFooter = Math.Max(startPosition + (audioMetaDataOffset - (sb.Position - startOffset) - (tag.UseHeader ? MusicMatchTag.HeaderSize : 0)), 0); long endPositionFooter = startPositionFooter + FooterIdentifierBytes.Length; // Read the footer. footer = ReadHeaderFooter(sb, startPositionFooter, endPositionFooter, FooterIdentifierBytes, TagOrigin.End); if (footer != null) break; } // We've got a problem, Houston. if (footer == null) return null; } return new AudioTagOffset(tagOrigin, startOffset, endOffset, tag); }
/// <summary> /// Equals the specified <see cref="MusicMatchTag"/>. /// </summary> /// <param name="tag">The <see cref="MusicMatchTag"/>.</param> /// <returns>true if equal; false otherwise.</returns> public bool Equals(MusicMatchTag tag) { if (ReferenceEquals(null, tag)) return false; if (ReferenceEquals(this, tag)) return true; return true; }