protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { HeaderInfo Header = new HeaderInfo(); resetData(); source.BaseStream.Seek(sizeInfo.ID3v2Size, SeekOrigin.Begin); bool result = readHeader(source, ref Header); // Process data if loaded and header valid if (result && StreamUtils.StringEqualsArr(TWIN_ID, Header.ID)) { isValid = true; // Fill properties with header data channelsArrangement = getChannelArrangement(Header); bitrate = getBitRate(Header); sampleRate = GetSampleRate(Header); duration = getDuration(Header); // Get tag information and fill properties readTag(source, Header, readTagParams); AudioDataOffset = source.BaseStream.Position; AudioDataSize = sizeInfo.FileSize - sizeInfo.APESize - sizeInfo.ID3v1Size - AudioDataOffset; } return(result); }
private void testGenericAudio( string resource, int duration, int bitrate, int samplerate, bool isVbr, int codecFamily, ChannelsArrangement channelsArrangement, string formatName, int alternate = 0) { ConsoleLogger log = new ConsoleLogger(); string theResource = TestUtils.GetResourceLocationRoot() + resource; IAudioDataIO theReader = AudioDataIOFactory.GetInstance().GetFromPath(theResource, alternate); Assert.IsNotInstanceOfType(theReader, typeof(ATL.AudioData.IO.DummyReader)); AudioDataManager manager = new AudioDataManager(theReader); manager.ReadFromFile(); Assert.AreEqual(duration, (int)Math.Round(theReader.Duration)); Assert.AreEqual(bitrate, (int)Math.Round(theReader.BitRate)); Assert.AreEqual(samplerate, theReader.SampleRate); Assert.AreEqual(theReader.IsVBR, isVbr); Assert.AreEqual(codecFamily, theReader.CodecFamily); Assert.AreEqual(channelsArrangement, theReader.ChannelsArrangement); Assert.AreEqual(formatName, theReader.AudioFormat.Name); }
public bool Read(BinaryReader source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { this.sizeInfo = sizeInfo; resetData(); source.BaseStream.Seek(sizeInfo.ID3v2Size, SeekOrigin.Begin); bool result = false; if (TTA_SIGNATURE.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { isValid = true; audioFormat = source.ReadUInt16(); channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(source.ReadUInt16()); bitsPerSample = source.ReadUInt16(); sampleRate = source.ReadUInt32(); samplesSize = source.ReadUInt32(); cRC32 = source.ReadUInt32(); bitrate = (double)(sizeInfo.FileSize - sizeInfo.TotalTagSize) * 8.0 / ((double)samplesSize * 1000.0 / sampleRate); duration = (double)samplesSize * 1000.0 / sampleRate; result = true; } return(result); }
public bool Read(BinaryReader source, ReadTagParams readTagParams) { bool result = false; BufferedBinaryReader reader = new BufferedBinaryReader(source.BaseStream); if (readTagParams.ReadTag && null == vorbisTag) { vorbisTag = new VorbisTag(true, true, true); } info.Reset(); if (getInfo(reader, info, readTagParams)) { if (contents.Equals(CONTENTS_VORBIS)) { channelsArrangement = getArrangementFromCode(info.VorbisParameters.ChannelMode); sampleRate = info.VorbisParameters.SampleRate; bitRateNominal = (ushort)(info.VorbisParameters.BitRateNominal / 1000); // Integer division } else if (contents.Equals(CONTENTS_OPUS)) { channelsArrangement = getArrangementFromCode(info.OpusParameters.OutputChannelCount); sampleRate = (int)info.OpusParameters.InputSampleRate; // No nominal bitrate for OPUS } samples = info.Samples; result = true; } return(result); }
private void readChannelLayoutChunk(BinaryReader source) { uint channelLayout = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); // we don't need anything else if (channelsMapping.ContainsKey(channelLayout)) { channelsArrangement = channelsMapping[channelLayout]; } }
// ---------- CONSTRUCTORS & INITIALIZERS protected void resetData() { sampleRate = 0; duration = 0; bitrate = 0; isVbr = false; codecFamily = 0; channelsPerFrame = 0; channelsArrangement = null; secondsPerByte = 0; }
// Read ADIF header data private void readADIF(BinaryReader Source) { int Position; Position = (int)(sizeInfo.ID3v2Size * 8 + 32); if (0 == StreamUtils.ReadBits(Source, Position, 1)) { Position += 3; } else { Position += 75; } if (0 == StreamUtils.ReadBits(Source, Position, 1)) { bitrateTypeID = AAC_BITRATE_TYPE_CBR; } else { bitrateTypeID = AAC_BITRATE_TYPE_VBR; } Position++; bitrate = (int)StreamUtils.ReadBits(Source, Position, 23); if (AAC_BITRATE_TYPE_CBR == bitrateTypeID) { Position += 51; } else { Position += 31; } Position += 2; uint channels = 1; sampleRate = SAMPLE_RATE[StreamUtils.ReadBits(Source, Position, 4)]; Position += 4; channels += StreamUtils.ReadBits(Source, Position, 4); Position += 4; channels += StreamUtils.ReadBits(Source, Position, 4); Position += 4; channels += StreamUtils.ReadBits(Source, Position, 4); Position += 4; channels += StreamUtils.ReadBits(Source, Position, 2); channelsArrangement = ChannelsArrangements.GuessFromChannelNumber((int)channels); }
// Read ADTS header data private void readADTS(BinaryReader Source) { int frames = 0; int totalSize = 0; int position; do { frames++; position = (int)(sizeInfo.ID3v2Size + totalSize) * 8; if (StreamUtils.ReadBits(Source, position, 12) != 0xFFF) { break; } position += 18; sampleRate = SAMPLE_RATE[StreamUtils.ReadBits(Source, position, 4)]; position += 5; uint channels = StreamUtils.ReadBits(Source, position, 3); channelsArrangement = ChannelsArrangements.GuessFromChannelNumber((int)channels); position += 7; totalSize += (int)StreamUtils.ReadBits(Source, position, 13); position += 13; if (0x7FF == StreamUtils.ReadBits(Source, position, 11)) { bitrateTypeID = AAC_BITRATE_TYPE_VBR; } else { bitrateTypeID = AAC_BITRATE_TYPE_CBR; } if (AAC_BITRATE_TYPE_CBR == bitrateTypeID) { break; } }while (Source.BaseStream.Length > sizeInfo.ID3v2Size + totalSize); bitrate = (int)Math.Round(8 * totalSize / 1024.0 / frames * sampleRate); }
protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { fileData = new FileData(); resetData(); bool result = readData(source, readTagParams); // Process data if loaded and valid if (result && isValid(fileData)) { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(fileData.Channels); sampleRate = fileData.SampleRate; bitrate = (sizeInfo.FileSize - sizeInfo.TotalTagSize - fileData.HeaderSize) * 8.0 / duration; isVBR = (WMA_GSM_VBR_ID == fileData.FormatTag); isLossless = (WMA_LOSSLESS_ID == fileData.FormatTag); } return(result); }
// TODO : support for CUESHEET block public bool Read(BinaryReader source, ReadTagParams readTagParams) { bool result = false; if (readTagParams.ReadTag && null == vorbisTag) { vorbisTag = new VorbisTag(false, false, false, false); } initialPaddingOffset = -1; initialPaddingSize = 0; byte[] aMetaDataBlockHeader; long position; uint blockLength; byte blockType; int blockIndex; bool isLast; bool paddingFound = false; long blockEndOffset = -1; readHeader(source); // Process data if loaded and header valid if (header.IsValid()) { int channels = (header.Info[12] >> 1) & 0x7; switch (channels) { case 0b0000: channelsArrangement = MONO; break; case 0b0001: channelsArrangement = STEREO; break; case 0b0010: channelsArrangement = ISO_3_0_0; break; case 0b0011: channelsArrangement = QUAD; break; case 0b0100: channelsArrangement = ISO_3_2_0; break; case 0b0101: channelsArrangement = ISO_3_2_1; break; case 0b0110: channelsArrangement = LRCLFECrLssRss; break; case 0b0111: channelsArrangement = LRCLFELrRrLssRss; break; case 0b1000: channelsArrangement = JOINT_STEREO_LEFT_SIDE; break; case 0b1001: channelsArrangement = JOINT_STEREO_RIGHT_SIDE; break; case 0b1010: channelsArrangement = JOINT_STEREO_MID_SIDE; break; default: channelsArrangement = UNKNOWN; break; } sampleRate = header.Info[10] << 12 | header.Info[11] << 4 | header.Info[12] >> 4; bitsPerSample = (byte)(((header.Info[12] & 1) << 4) | (header.Info[13] >> 4) + 1); samples = header.Info[14] << 24 | header.Info[15] << 16 | header.Info[16] << 8 | header.Info[17]; if (0 == (header.MetaDataBlockHeader[1] & FLAG_LAST_METADATA_BLOCK)) // metadata block exists { blockIndex = 0; vorbisTag.Clear(); if (readTagParams.PrepareForWriting) { if (null == zones) { zones = new List <Zone>(); } else { zones.Clear(); } blockEndOffset = source.BaseStream.Position; } do // Read all metadata blocks { aMetaDataBlockHeader = source.ReadBytes(4); isLast = (aMetaDataBlockHeader[0] & FLAG_LAST_METADATA_BLOCK) > 0; // last flag ( first bit == 1 ) blockIndex++; blockLength = StreamUtils.DecodeBEUInt24(aMetaDataBlockHeader, 1); blockType = (byte)(aMetaDataBlockHeader[0] & 0x7F); // decode metablock type position = source.BaseStream.Position; if (blockType == META_VORBIS_COMMENT) // Vorbis metadata { if (readTagParams.PrepareForWriting) { zones.Add(new Zone(blockType + "", position - 4, (int)blockLength + 4, new byte[0], blockType)); } vorbisTag.Read(source, readTagParams); } else if ((blockType == META_PADDING) && (!paddingFound)) // Padding block (skip any other padding block) { if (readTagParams.PrepareForWriting) { zones.Add(new Zone(PADDING_ZONE_NAME, position - 4, (int)blockLength + 4, new byte[0], blockType)); } initialPaddingSize = blockLength; initialPaddingOffset = position; paddingFound = true; source.BaseStream.Seek(blockLength, SeekOrigin.Current); } else if (blockType == META_PICTURE) // Picture (NB: as per FLAC specs, pictures must be embedded at the FLAC level, not in the VorbisComment !) { if (readTagParams.PrepareForWriting) { zones.Add(new Zone(blockType + "", position - 4, (int)blockLength + 4, new byte[0], blockType)); } vorbisTag.ReadPicture(source.BaseStream, readTagParams); } else // Unhandled block; needs to be zoned anyway to be able to manage the 'isLast' flag at write-time { if (readTagParams.PrepareForWriting) { zones.Add(new Zone(blockType + "", position - 4, (int)blockLength + 4, new byte[0], blockType)); } } if (blockType < 7) { source.BaseStream.Seek(position + blockLength, SeekOrigin.Begin); blockEndOffset = position + blockLength; } else { // Abnormal header : incorrect size and/or misplaced last-metadata-block flag break; } }while (!isLast); if (readTagParams.PrepareForWriting) { bool vorbisTagFound = false; bool pictureFound = false; foreach (Zone zone in zones) { if (zone.Flag == META_PICTURE) { pictureFound = true; } else if (zone.Flag == META_VORBIS_COMMENT) { vorbisTagFound = true; } } if (!vorbisTagFound) { zones.Add(new Zone(META_VORBIS_COMMENT + "", blockEndOffset, 0, new byte[0], META_VORBIS_COMMENT)); } if (!pictureFound) { zones.Add(new Zone(META_PICTURE + "", blockEndOffset, 0, new byte[0], META_PICTURE)); } // Padding must be the last block for it to correctly absorb size variations of the other blocks if (!paddingFound && Settings.AddNewPadding) { zones.Add(new Zone(PADDING_ZONE_NAME, blockEndOffset, 0, new byte[0], META_PADDING)); } } } } if (isValid()) { audioOffset = source.BaseStream.Position; // we need that to calculate the bitrate result = true; } return(result); }
public bool Read(BinaryReader source, AudioDataManager.SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { this.sizeInfo = sizeInfo; bool result = false; resetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); if (DSD_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { source.BaseStream.Seek(16, SeekOrigin.Current); // Chunk size and file size id3v2Offset = source.ReadInt64(); if (FMT_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { source.BaseStream.Seek(8, SeekOrigin.Current); // Chunk size formatVersion = source.ReadInt32(); if (formatVersion > 1) { LogDelegator.GetLogDelegate()(Log.LV_ERROR, "DSF format version " + formatVersion + " not supported"); return(result); } isValid = true; source.BaseStream.Seek(8, SeekOrigin.Current); // Format ID (4), Channel type (4) uint channels = source.ReadUInt32(); switch (channels) { case 1: channelsArrangement = MONO; break; case 2: channelsArrangement = STEREO; break; case 3: channelsArrangement = ISO_3_0_0; break; case 4: channelsArrangement = QUAD; break; case 5: channelsArrangement = LRCLFE; break; case 6: channelsArrangement = ISO_3_2_0; break; case 7: channelsArrangement = ISO_3_2_1; break; default: channelsArrangement = UNKNOWN; break; } sampleRate = source.ReadUInt32(); bits = source.ReadUInt32(); ulong sampleCount = source.ReadUInt64(); duration = (double)sampleCount * 1000.0 / sampleRate; bitrate = Math.Round(((double)(sizeInfo.FileSize - source.BaseStream.Position)) * 8 / duration); //time to calculate average bitrate result = true; } // Load tag if exists if (id3v2Offset > 0) { if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(id3v2Offset, (int)(source.BaseStream.Length - id3v2Offset)); id3v2StructureHelper.AddSize(12, source.BaseStream.Length); id3v2StructureHelper.AddIndex(20, id3v2Offset); } } else { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { // Add EOF zone for future tag writing id3v2StructureHelper.AddZone(source.BaseStream.Length, 0); id3v2StructureHelper.AddSize(12, source.BaseStream.Length); id3v2StructureHelper.AddIndex(20, source.BaseStream.Length); } } } return(result); }
protected override bool read(BinaryReader source, ReadTagParams readTagParams) { ResetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); bool result = readFileHeader(source); long cursorPos = source.BaseStream.Position; long audioChunkSize = 0; // Iterate through chunks while (cursorPos < source.BaseStream.Length) { string chunkType = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); long chunkSize = StreamUtils.DecodeBEInt64(source.ReadBytes(8)); if (readTagParams.PrepareForWriting) { structureHelper.AddZone(cursorPos, chunkSize + 12, chunkType); } switch (chunkType) { case CHUNK_AUDIO_DESC: readAudioDescriptionChunk(source); break; case CHUNK_CHANNEL_LAYOUT: readChannelLayoutChunk(source); break; case CHUNK_COOKIE: case CHUNK_UMID: if (readTagParams.PrepareForWriting || readTagParams.ReadAllMetaFrames) { readStringChunk(source, chunkType, chunkSize); } break; case CHUNK_STRINGS: if (readTagParams.PrepareForWriting || readTagParams.ReadAllMetaFrames) { readStringsChunk(source); } break; case CHUNK_INFO: readInfoChunk(source, readTagParams.PrepareForWriting || readTagParams.ReadAllMetaFrames); break; case CHUNK_AUDIO: AudioDataOffset = cursorPos; audioChunkSize = chunkSize; AudioDataSize = chunkSize + 12; if (secondsPerByte > 0) { duration = chunkSize * secondsPerByte * 1000; } break; case CHUNK_PACKET_TABLE: if (0 == secondsPerByte) { readPaktChunk(source); } break; } source.BaseStream.Seek(cursorPos + chunkSize + 12, SeekOrigin.Begin); cursorPos = source.BaseStream.Position; } bitrate = audioChunkSize * 8d / duration; if (null == channelsArrangement) { if (1 == channelsPerFrame) { channelsArrangement = MONO; } else if (2 == channelsPerFrame) { channelsArrangement = STEREO; } else { channelsArrangement = new ChannelsArrangement((int)channelsPerFrame, "Custom layout (" + channelsPerFrame + " channels)"); } } return(result); }
protected void Update(bool readEmbeddedPictures = false) { if ((null == Path) || (0 == Path.Length)) { return; } // TODO when tag is not available, customize by naming options // tracks (...) if (null == stream) { fileIO = new AudioFileIO(Path, readEmbeddedPictures, Settings.ReadAllMetaFrames); } else { fileIO = new AudioFileIO(stream, mimeType, readEmbeddedPictures, Settings.ReadAllMetaFrames); } Title = fileIO.Title; if (Settings.UseFileNameWhenNoTitle && (null == Title || "" == Title)) { Title = System.IO.Path.GetFileNameWithoutExtension(Path); } Artist = Utils.ProtectValue(fileIO.Artist); Composer = Utils.ProtectValue(fileIO.Composer); Comment = Utils.ProtectValue(fileIO.Comment); Genre = Utils.ProtectValue(fileIO.Genre); OriginalArtist = Utils.ProtectValue(fileIO.OriginalArtist); OriginalAlbum = Utils.ProtectValue(fileIO.OriginalAlbum); Description = Utils.ProtectValue(fileIO.GeneralDescription); Copyright = Utils.ProtectValue(fileIO.Copyright); Publisher = Utils.ProtectValue(fileIO.Publisher); AlbumArtist = Utils.ProtectValue(fileIO.AlbumArtist); Conductor = Utils.ProtectValue(fileIO.Conductor); Date = fileIO.Date; Year = fileIO.IntYear; Album = fileIO.Album; TrackNumber = fileIO.Track; TrackTotal = fileIO.TrackTotal; DiscNumber = fileIO.Disc; DiscTotal = fileIO.DiscTotal; ChaptersTableDescription = Utils.ProtectValue(fileIO.ChaptersTableDescription); Bitrate = fileIO.IntBitRate; CodecFamily = fileIO.CodecFamily; DurationMs = fileIO.Duration; #pragma warning disable CS0618 // Obsolete Rating = fileIO.Rating; #pragma warning restore CS0618 // Obsolete Popularity = fileIO.Popularity; IsVBR = fileIO.IsVBR; SampleRate = fileIO.SampleRate; ChannelsArrangement = fileIO.ChannelsArrangement; Chapters = fileIO.Chapters; Lyrics = fileIO.Lyrics; AdditionalFields = fileIO.AdditionalFields; initialAdditionalFields = fileIO.AdditionalFields.Keys; PictureTokens = new List <PictureInfo>(fileIO.PictureTokens); if (readEmbeddedPictures) { foreach (PictureInfo picInfo in fileIO.EmbeddedPictures) { picInfo.ComputePicHash(); currentEmbeddedPictures.Add(picInfo); PictureInfo initialPicInfo = new PictureInfo(picInfo, false); initialEmbeddedPictures.Add(initialPicInfo); } } if (!readEmbeddedPictures && currentEmbeddedPictures != null) { currentEmbeddedPictures.Clear(); initialEmbeddedPictures.Clear(); currentEmbeddedPictures = null; initialEmbeddedPictures = null; } }
private bool _ReadV4(BinaryReader r) { WavPackHeader4 wvh4 = new WavPackHeader4(); byte[] EncBuf = new byte[4096]; int tempo; byte encoderbyte; bool result = false; wvh4.Reset(); wvh4.ckID = r.ReadChars(4); wvh4.ckSize = r.ReadUInt32(); wvh4.version = r.ReadUInt16(); wvh4.track_no = r.ReadByte(); wvh4.index_no = r.ReadByte(); wvh4.total_samples = r.ReadUInt32(); wvh4.block_index = r.ReadUInt32(); wvh4.block_samples = r.ReadUInt32(); wvh4.flags = r.ReadUInt32(); wvh4.crc = r.ReadUInt32(); if (StreamUtils.StringEqualsArr("wvpk", wvh4.ckID)) // wavpack header found -- TODO handle exceptions better { result = true; version = (wvh4.version >> 8); channelsArrangement = ChannelsArrangements.GuessFromChannelNumber((int)(2 - (wvh4.flags & 4))); bits = (int)((wvh4.flags & 3) * 16); // bytes stored flag samples = wvh4.total_samples; bSamples = wvh4.block_samples; sampleRate = (int)((wvh4.flags & (0x1F << 23)) >> 23); if ((sampleRate > 14) || (sampleRate < 0)) { sampleRate = 44100; } else { sampleRate = sample_rates[sampleRate]; } if (8 == (wvh4.flags & 8)) // hybrid flag { encoder = "hybrid lossy"; codecFamily = AudioDataIOFactory.CF_LOSSY; } else { //if (2 == (wvh4.flags & 2) ) { // lossless flag encoder = "lossless"; codecFamily = AudioDataIOFactory.CF_LOSSLESS; } /* * if ((wvh4.flags & 0x20) > 0) // MODE_HIGH * { * FEncoder = FEncoder + " (high)"; * end * else if ((wvh4.flags & 0x40) > 0) // MODE_FAST * { * FEncoder = FEncoder + " (fast)"; * } */ duration = (double)wvh4.total_samples * 1000.0 / sampleRate; if (duration > 0) { bitrate = (sizeInfo.FileSize - tagSize) * 8 / (double)(samples * 1000.0 / (double)sampleRate); } Array.Clear(EncBuf, 0, 4096); EncBuf = r.ReadBytes(4096); for (tempo = 0; tempo < 4096; tempo++) { if (0x65 == EncBuf[tempo]) { if (0x02 == EncBuf[tempo + 1]) { encoderbyte = EncBuf[tempo + 2]; switch (encoderbyte) { case 8: encoder = encoder + " (high)"; break; case 0: encoder = encoder + " (normal)"; break; case 2: encoder = encoder + " (fast)"; break; case 6: encoder = encoder + " (very fast)"; break; } break; } } } } return(result); }
public bool Read(BinaryReader source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { ApeHeaderOld APE_OLD = new ApeHeaderOld(); // old header <= 3.97 ApeHeaderNew APE_NEW = new ApeHeaderNew(); // new header >= 3.98 ApeDescriptor APE_DESC = new ApeDescriptor(); // extra header >= 3.98 int BlocksPerFrame; bool LoadSuccess; bool result = false; this.sizeInfo = sizeInfo; resetData(); // reading data from file LoadSuccess = false; readCommonHeader(source); if (StreamUtils.StringEqualsArr("MAC ", header.cID)) { isValid = true; version = header.nVersion; versionStr = ((double)version / 1000).ToString().Substring(0, 4); //Str(FVersion / 1000 : 4 : 2, FVersionStr); // Load New Monkey's Audio Header for version >= 3.98 if (header.nVersion >= 3980) { APE_DESC.padded = 0; APE_DESC.nDescriptorBytes = 0; APE_DESC.nHeaderBytes = 0; APE_DESC.nSeekTableBytes = 0; APE_DESC.nHeaderDataBytes = 0; APE_DESC.nAPEFrameDataBytes = 0; APE_DESC.nAPEFrameDataBytesHigh = 0; APE_DESC.nTerminatingDataBytes = 0; Array.Clear(APE_DESC.cFileMD5, 0, APE_DESC.cFileMD5.Length); APE_DESC.padded = source.ReadUInt16(); APE_DESC.nDescriptorBytes = source.ReadUInt32(); APE_DESC.nHeaderBytes = source.ReadUInt32(); APE_DESC.nSeekTableBytes = source.ReadUInt32(); APE_DESC.nHeaderDataBytes = source.ReadUInt32(); APE_DESC.nAPEFrameDataBytes = source.ReadUInt32(); APE_DESC.nAPEFrameDataBytesHigh = source.ReadUInt32(); APE_DESC.nTerminatingDataBytes = source.ReadUInt32(); APE_DESC.cFileMD5 = source.ReadBytes(16); // seek past description header if (APE_DESC.nDescriptorBytes != 52) { source.BaseStream.Seek(APE_DESC.nDescriptorBytes - 52, SeekOrigin.Current); } // load new ape_header if (APE_DESC.nHeaderBytes > 24 /*sizeof(APE_NEW)*/) { APE_DESC.nHeaderBytes = 24 /*sizeof(APE_NEW)*/; } APE_NEW.nCompressionLevel = 0; APE_NEW.nFormatFlags = 0; APE_NEW.nBlocksPerFrame = 0; APE_NEW.nFinalFrameBlocks = 0; APE_NEW.nTotalFrames = 0; APE_NEW.nBitsPerSample = 0; APE_NEW.nChannels = 0; APE_NEW.nSampleRate = 0; APE_NEW.nCompressionLevel = source.ReadUInt16(); APE_NEW.nFormatFlags = source.ReadUInt16(); APE_NEW.nBlocksPerFrame = source.ReadUInt32(); APE_NEW.nFinalFrameBlocks = source.ReadUInt32(); APE_NEW.nTotalFrames = source.ReadUInt32(); APE_NEW.nBitsPerSample = source.ReadUInt16(); APE_NEW.nChannels = source.ReadUInt16(); APE_NEW.nSampleRate = source.ReadUInt32(); // based on MAC SDK 3.98a1 (APEinfo.h) sampleRate = (int)APE_NEW.nSampleRate; channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(APE_NEW.nChannels); formatFlags = APE_NEW.nFormatFlags; bits = APE_NEW.nBitsPerSample; compressionMode = APE_NEW.nCompressionLevel; // calculate total uncompressed samples if (APE_NEW.nTotalFrames > 0) { totalSamples = (long)(APE_NEW.nBlocksPerFrame) * (long)(APE_NEW.nTotalFrames - 1) + (long)(APE_NEW.nFinalFrameBlocks); } LoadSuccess = true; } else { // Old Monkey <= 3.97 APE_OLD.nCompressionLevel = 0; APE_OLD.nFormatFlags = 0; APE_OLD.nChannels = 0; APE_OLD.nSampleRate = 0; APE_OLD.nHeaderBytes = 0; APE_OLD.nTerminatingBytes = 0; APE_OLD.nTotalFrames = 0; APE_OLD.nFinalFrameBlocks = 0; APE_OLD.nInt = 0; APE_OLD.nCompressionLevel = source.ReadUInt16(); APE_OLD.nFormatFlags = source.ReadUInt16(); APE_OLD.nChannels = source.ReadUInt16(); APE_OLD.nSampleRate = source.ReadUInt32(); APE_OLD.nHeaderBytes = source.ReadUInt32(); APE_OLD.nTerminatingBytes = source.ReadUInt32(); APE_OLD.nTotalFrames = source.ReadUInt32(); APE_OLD.nFinalFrameBlocks = source.ReadUInt32(); APE_OLD.nInt = source.ReadInt32(); compressionMode = APE_OLD.nCompressionLevel; sampleRate = (int)APE_OLD.nSampleRate; channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(APE_OLD.nChannels); formatFlags = APE_OLD.nFormatFlags; bits = 16; if ((APE_OLD.nFormatFlags & MONKEY_FLAG_8_BIT) != 0) { bits = 8; } if ((APE_OLD.nFormatFlags & MONKEY_FLAG_24_BIT) != 0) { bits = 24; } hasSeekElements = ((APE_OLD.nFormatFlags & MONKEY_FLAG_PEAK_LEVEL) != 0); wavNotStored = ((APE_OLD.nFormatFlags & MONKEY_FLAG_SEEK_ELEMENTS) != 0); hasPeakLevel = ((APE_OLD.nFormatFlags & MONKEY_FLAG_WAV_NOT_STORED) != 0); if (hasPeakLevel) { peakLevel = (uint)APE_OLD.nInt; peakLevelRatio = (peakLevel / (1 << bits) / 2.0) * 100.0; } // based on MAC_SDK_397 (APEinfo.cpp) if (version >= 3950) { BlocksPerFrame = 73728 * 4; } else if ((version >= 3900) || ((version >= 3800) && (MONKEY_COMPRESSION_EXTRA_HIGH == APE_OLD.nCompressionLevel))) { BlocksPerFrame = 73728; } else { BlocksPerFrame = 9216; } // calculate total uncompressed samples if (APE_OLD.nTotalFrames > 0) { totalSamples = (long)(APE_OLD.nTotalFrames - 1) * (long)(BlocksPerFrame) + (long)(APE_OLD.nFinalFrameBlocks); } LoadSuccess = true; } if (LoadSuccess) { // compression profile name if ((0 == (compressionMode % 1000)) && (compressionMode <= 6000)) { compressionModeStr = MONKEY_COMPRESSION[compressionMode / 1000]; // int division } else { compressionModeStr = compressionMode.ToString(); } // length if (sampleRate > 0) { duration = ((double)totalSamples * 1000.0 / sampleRate); } // average bitrate if (duration > 0) { bitrate = 8 * (sizeInfo.FileSize - sizeInfo.TotalTagSize) / (duration); } // some extra sanity checks isValid = ((bits > 0) && (sampleRate > 0) && (totalSamples > 0) && (channelsArrangement.NbChannels > 0)); result = isValid; } } return(result); }
private bool _ReadV3(BinaryReader r) { RiffChunk chunk = new RiffChunk(); char[] wavchunk; FormatChunk fmt; bool hasfmt; WavpackHeader3 wvh3 = new WavpackHeader3(); bool result = false; hasfmt = false; // read and evaluate header chunk.Reset(); chunk.id = r.ReadChars(4); chunk.size = r.ReadUInt32(); wavchunk = r.ReadChars(4); if (!StreamUtils.StringEqualsArr("WAVE", wavchunk)) { return(result); } // start looking for chunks chunk.Reset(); while (r.BaseStream.Position < r.BaseStream.Length) { chunk.id = r.ReadChars(4); chunk.size = r.ReadUInt32(); if (chunk.size <= 0) { break; } if (StreamUtils.StringEqualsArr("fmt ", chunk.id)) // Format chunk found read it { if (chunk.size >= 16 /*sizeof(fmt_chunk)*/) { fmt.wformattag = r.ReadUInt16(); fmt.wchannels = r.ReadUInt16(); fmt.dwsamplespersec = r.ReadUInt32(); fmt.dwavgbytespersec = r.ReadUInt32(); fmt.wblockalign = r.ReadUInt16(); fmt.wbitspersample = r.ReadUInt16(); hasfmt = true; result = true; formatTag = fmt.wformattag; channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(fmt.wchannels); sampleRate = (int)fmt.dwsamplespersec; bits = fmt.wbitspersample; bitrate = (double)fmt.dwavgbytespersec * 8; } else { break; } } else { if ((StreamUtils.StringEqualsArr("data", chunk.id)) && hasfmt) { wvh3.Reset(); wvh3.ckID = r.ReadChars(4); wvh3.ckSize = r.ReadUInt32(); wvh3.version = r.ReadUInt16(); wvh3.bits = r.ReadUInt16(); wvh3.flags = r.ReadUInt16(); wvh3.shift = r.ReadUInt16(); wvh3.total_samples = r.ReadUInt32(); wvh3.crc = r.ReadUInt32(); wvh3.crc2 = r.ReadUInt32(); wvh3.extension = r.ReadChars(4); wvh3.extra_bc = r.ReadByte(); wvh3.extras = r.ReadChars(3); if (StreamUtils.StringEqualsArr("wvpk", wvh3.ckID)) // wavpack header found { result = true; version = wvh3.version; channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(2 - (wvh3.flags & 1)); samples = wvh3.total_samples; codecFamily = AudioDataIOFactory.CF_LOSSLESS; // Encoder guess if (wvh3.bits > 0) { if ((wvh3.flags & NEW_HIGH_FLAG_v3) > 0) { encoder = "hybrid"; if ((wvh3.flags & WVC_FLAG_v3) > 0) { encoder += " lossless"; } else { encoder += " lossy"; codecFamily = AudioDataIOFactory.CF_LOSSY; } if ((wvh3.flags & EXTREME_DECORR_v3) > 0) { encoder = encoder + " (high)"; } } else { if ((wvh3.flags & (HIGH_FLAG_v3 | FAST_FLAG_v3)) == 0) { encoder = (wvh3.bits + 3).ToString() + "-bit lossy"; codecFamily = AudioDataIOFactory.CF_LOSSY; } else { encoder = (wvh3.bits + 3).ToString() + "-bit lossy"; codecFamily = AudioDataIOFactory.CF_LOSSY; if ((wvh3.flags & HIGH_FLAG_v3) > 0) { encoder += " high"; } else { encoder += " fast"; } } } } else { if ((wvh3.flags & HIGH_FLAG_v3) == 0) { encoder = "lossless (fast mode)"; } else { if ((wvh3.flags & EXTREME_DECORR_v3) > 0) { encoder = "lossless (high mode)"; } else { encoder = "lossless"; } } } if (sampleRate <= 0) { sampleRate = 44100; } duration = (double)wvh3.total_samples * 1000.0 / sampleRate; if (duration > 0) { bitrate = 8.0 * (sizeInfo.FileSize - tagSize - (double)wvh3.ckSize) / duration; } } break; } else // not a wv file { break; } } } // while return(result); }
// ---------- SUPPORT METHODS /* Unused for now * private double getCompressionRatio() * { * // Get compression ratio * if (isValid) * return (double)sizeInfo.FileSize / ((duration * sampleRate) * (channels * bits / 8) + 44) * 100; * else * return 0; * } */ public bool Read(BinaryReader source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { ushort signatureChunk; byte aByte; this.sizeInfo = sizeInfo; resetData(); bool result = false; source.BaseStream.Seek(0, SeekOrigin.Begin); signatureChunk = source.ReadUInt16(); if (30475 == signatureChunk) { source.BaseStream.Seek(2, SeekOrigin.Current); aByte = source.ReadByte(); switch (aByte & 0xC0) { case 0: sampleRate = 48000; break; case 0x40: sampleRate = 44100; break; case 0x80: sampleRate = 32000; break; default: sampleRate = 0; break; } bitrate = BITRATES[(aByte & 0x3F) >> 1]; source.BaseStream.Seek(1, SeekOrigin.Current); aByte = source.ReadByte(); switch (aByte & 0xE0) { case 0: channelsArrangement = DUAL_MONO; break; case 0x20: channelsArrangement = MONO; break; case 0x40: channelsArrangement = STEREO; break; case 0x60: channelsArrangement = ISO_3_0_0; break; case 0x80: channelsArrangement = ISO_2_1_0; break; case 0xA0: channelsArrangement = ISO_3_1_0; break; case 0xC0: channelsArrangement = ISO_2_2_0; break; case 0xE0: channelsArrangement = ISO_3_2_0; break; default: channelsArrangement = UNKNOWN; break; } duration = sizeInfo.FileSize * 8.0 / bitrate; result = true; } return(result); }
// ---------- SUPPORT METHODS private bool readWAV(Stream source, ReadTagParams readTagParams) { bool result = true; uint riffChunkSize; long riffChunkSizePos; byte[] data = new byte[4]; source.Seek(0, SeekOrigin.Begin); // Read header source.Read(data, 0, 4); string str = Utils.Latin1Encoding.GetString(data); if (str.Equals(HEADER_RIFF)) { _isLittleEndian = true; } else if (str.Equals(HEADER_RIFX)) { _isLittleEndian = false; } else { return(false); } // Force creation of FileStructureHelper with detected endianness structureHelper = new FileStructureHelper(isLittleEndian); id3v2StructureHelper = new FileStructureHelper(isLittleEndian); riffChunkSizePos = source.Position; source.Read(data, 0, 4); if (isLittleEndian) { riffChunkSize = StreamUtils.DecodeUInt32(data); } else { riffChunkSize = StreamUtils.DecodeBEUInt32(data); } // Format code source.Read(data, 0, 4); str = Utils.Latin1Encoding.GetString(data); if (!str.Equals(FORMAT_WAVE)) { return(false); } string subChunkId; uint chunkSize; long chunkDataPos; bool foundSample = false; bool foundBext = false; bool foundInfo = false; bool foundIXml = false; // Sub-chunks loop while (source.Position < riffChunkSize + 8) { // Chunk ID source.Read(data, 0, 4); if (0 == data[0]) // Sometimes data segment ends with a parasite null byte { source.Seek(-3, SeekOrigin.Current); source.Read(data, 0, 4); } subChunkId = Utils.Latin1Encoding.GetString(data); // Chunk size source.Read(data, 0, 4); if (isLittleEndian) { chunkSize = StreamUtils.DecodeUInt32(data); } else { chunkSize = StreamUtils.DecodeBEUInt32(data); } chunkDataPos = source.Position; if (subChunkId.Equals(CHUNK_FORMAT)) { source.Read(data, 0, 2); if (isLittleEndian) { formatId = StreamUtils.DecodeUInt16(data); } else { formatId = StreamUtils.DecodeBEUInt16(data); } source.Read(data, 0, 2); if (isLittleEndian) { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeUInt16(data)); } else { channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeBEUInt16(data)); } source.Read(data, 0, 4); if (isLittleEndian) { sampleRate = StreamUtils.DecodeUInt32(data); } else { sampleRate = StreamUtils.DecodeBEUInt32(data); } source.Read(data, 0, 4); if (isLittleEndian) { bytesPerSecond = StreamUtils.DecodeUInt32(data); } else { bytesPerSecond = StreamUtils.DecodeBEUInt32(data); } source.Seek(2, SeekOrigin.Current); // BlockAlign source.Read(data, 0, 2); if (isLittleEndian) { bitsPerSample = StreamUtils.DecodeUInt16(data); } else { bitsPerSample = StreamUtils.DecodeBEUInt16(data); } } else if (subChunkId.Equals(CHUNK_DATA)) { headerSize = riffChunkSize - chunkSize; } else if (subChunkId.Equals(CHUNK_FACT)) { source.Read(data, 0, 4); if (isLittleEndian) { sampleNumber = StreamUtils.DecodeInt32(data); } else { sampleNumber = StreamUtils.DecodeBEInt32(data); } } else if (subChunkId.Equals(CHUNK_SAMPLE)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundSample = true; tagExists = true; SampleTag.FromStream(source, this, readTagParams); } else if (subChunkId.Equals(CHUNK_BEXT)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundBext = true; tagExists = true; BextTag.FromStream(source, this, readTagParams); } else if (subChunkId.Equals(CHUNK_INFO)) { // Purpose of the list should be INFO source.Read(data, 0, 4); string purpose = Utils.Latin1Encoding.GetString(data, 0, 4); if (purpose.Equals(InfoTag.PURPOSE_INFO)) { structureHelper.AddZone(source.Position - 12, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundInfo = true; tagExists = true; InfoTag.FromStream(source, this, readTagParams, chunkSize); } } else if (subChunkId.Equals(CHUNK_IXML)) { structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); foundIXml = true; tagExists = true; IXmlTag.FromStream(source, this, readTagParams, chunkSize); } else if (subChunkId.Equals(CHUNK_ID3)) { id3v2Offset = source.Position; // Zone is already added by Id3v2.Read id3v2StructureHelper.AddZone(id3v2Offset - 8, (int)(chunkSize + 8), subChunkId); id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId); } source.Seek(chunkDataPos + chunkSize, SeekOrigin.Begin); } // Add zone placeholders for future tag writing if (readTagParams.PrepareForWriting) { if (!foundSample) { structureHelper.AddZone(source.Position, 0, CHUNK_SAMPLE); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_SAMPLE); } if (!foundBext) { structureHelper.AddZone(source.Position, 0, CHUNK_BEXT); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_BEXT); } if (!foundInfo) { structureHelper.AddZone(source.Position, 0, CHUNK_INFO); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_INFO); } if (!foundIXml) { structureHelper.AddZone(source.Position, 0, CHUNK_IXML); structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_IXML); } } // ID3 zone should be set as the very last one for Windows to be able to read the LIST INFO zone properly if (-1 == id3v2Offset) { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(source.Position, 0, CHUNK_ID3); id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_ID3); } } return(result); }
public bool Read(BinaryReader source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { uint signatureChunk; ushort aWord; byte[] specDTS; bool result = false; this.sizeInfo = sizeInfo; resetData(); signatureChunk = source.ReadUInt32(); if (/*0x7FFE8001*/ 25230975 == signatureChunk) { AudioDataOffset = source.BaseStream.Position - 4; AudioDataSize = sizeInfo.FileSize - sizeInfo.APESize - sizeInfo.ID3v1Size - AudioDataOffset; source.BaseStream.Seek(3, SeekOrigin.Current); specDTS = source.ReadBytes(8); isValid = true; aWord = (ushort)(specDTS[1] | (specDTS[0] << 8)); switch ((aWord & 0x0FC0) >> 6) { case 0: channelsArrangement = MONO; break; case 1: channelsArrangement = DUAL_MONO; break; case 2: channelsArrangement = STEREO; break; case 3: channelsArrangement = STEREO_SUM_DIFFERENCE; break; case 4: channelsArrangement = STEREO_LEFT_RIGHT_TOTAL; break; case 5: channelsArrangement = ISO_3_0_0; break; case 6: channelsArrangement = ISO_2_1_0; break; case 7: channelsArrangement = LRCS; break; case 8: channelsArrangement = QUAD; break; case 9: channelsArrangement = ISO_3_2_0; break; case 10: channelsArrangement = CLCRLRSLSR; break; case 11: channelsArrangement = CLRLRRRO; break; case 12: channelsArrangement = CFCRLFRFLRRR; break; case 13: channelsArrangement = CLCCRLRSLSR; break; case 14: channelsArrangement = CLCRLRSL1SL2SR1SR2; break; case 15: channelsArrangement = CLCCRLRSLSSR; break; default: channelsArrangement = UNKNOWN; break; } switch ((aWord & 0x3C) >> 2) { case 1: sampleRate = 8000; break; case 2: sampleRate = 16000; break; case 3: sampleRate = 32000; break; case 6: sampleRate = 11025; break; case 7: sampleRate = 22050; break; case 8: sampleRate = 44100; break; case 11: sampleRate = 12000; break; case 12: sampleRate = 24000; break; case 13: sampleRate = 48000; break; default: sampleRate = 0; break; } aWord = (ushort)(specDTS[2] | (specDTS[1] << 8)); bitrate = (ushort)BITRATES[(aWord & 0x03E0) >> 5]; aWord = (ushort)(specDTS[7] | (specDTS[6] << 8)); switch ((aWord & 0x01C0) >> 6) { case 0: case 1: bits = 16; break; case 2: case 3: bits = 20; break; case 4: case 5: bits = 24; break; default: bits = 16; break; } duration = sizeInfo.FileSize * 8.0 / bitrate; result = true; } return(result); }
public bool Read(BinaryReader source, ReadTagParams readTagParams) { bool result = false; if (readTagParams.ReadTag && null == vorbisTag) { vorbisTag = new VorbisTag(false, false, false); } byte[] aMetaDataBlockHeader; long position; uint blockLength; int blockType; int blockIndex; bool isLast; bool bPaddingFound = false; readHeader(source); // Process data if loaded and header valid if (header.IsValid()) { int channels = (header.Info[12] >> 1) & 0x7; switch (channels) { case 0b0000: channelsArrangement = MONO; break; case 0b0001: channelsArrangement = STEREO; break; case 0b0010: channelsArrangement = ISO_3_0_0; break; case 0b0011: channelsArrangement = QUAD; break; case 0b0100: channelsArrangement = ISO_3_2_0; break; case 0b0101: channelsArrangement = ISO_3_2_1; break; case 0b0110: channelsArrangement = LRCLFECrLssRss; break; case 0b0111: channelsArrangement = LRCLFELrRrLssRss; break; case 0b1000: channelsArrangement = JOINT_STEREO_LEFT_SIDE; break; case 0b1001: channelsArrangement = JOINT_STEREO_RIGHT_SIDE; break; case 0b1010: channelsArrangement = JOINT_STEREO_MID_SIDE; break; default: channelsArrangement = UNKNOWN; break; } sampleRate = (header.Info[10] << 12 | header.Info[11] << 4 | header.Info[12] >> 4); bitsPerSample = (byte)(((header.Info[12] & 1) << 4) | (header.Info[13] >> 4) + 1); samples = (header.Info[14] << 24 | header.Info[15] << 16 | header.Info[16] << 8 | header.Info[17]); if (0 == (header.MetaDataBlockHeader[1] & 0x80)) // metadata block exists { blockIndex = 0; vorbisTag.Clear(); if (readTagParams.PrepareForWriting) { if (null == zones) { zones = new List <Zone>(); } else { zones.Clear(); } firstBlockPosition = source.BaseStream.Position; } do // read more metadata blocks if available { aMetaDataBlockHeader = source.ReadBytes(4); isLast = ((aMetaDataBlockHeader[0] & 0x80) > 0); // last flag ( first bit == 1 ) blockIndex++; blockLength = StreamUtils.DecodeBEUInt24(aMetaDataBlockHeader, 1); blockType = (aMetaDataBlockHeader[0] & 0x7F); // decode metablock type position = source.BaseStream.Position; if (blockType == META_VORBIS_COMMENT) // Vorbis metadata { if (readTagParams.PrepareForWriting) { zones.Add(new Zone(ZONE_VORBISTAG, position - 4, (int)blockLength + 4, new byte[0], (byte)(isLast ? 1 : 0))); } vorbisTag.Read(source, readTagParams); } else if ((blockType == META_PADDING) && (!bPaddingFound)) // Padding block { padding = blockLength; // if we find more skip & put them in metablock array paddingLast = ((aMetaDataBlockHeader[0] & 0x80) != 0); paddingIndex = blockIndex; bPaddingFound = true; source.BaseStream.Seek(padding, SeekOrigin.Current); // advance into file till next block or audio data start } else if (blockType == META_PICTURE) { if (readTagParams.PrepareForWriting) { zones.Add(new Zone(ZONE_PICTURE, position - 4, (int)blockLength + 4, new byte[0], (byte)(isLast ? 1 : 0))); } vorbisTag.ReadPicture(source.BaseStream, readTagParams); } // TODO : support for CUESHEET block if (blockType < 7) { source.BaseStream.Seek(position + blockLength, SeekOrigin.Begin); } else { // Abnormal header : incorrect size and/or misplaced last-metadata-block flag break; } }while (!isLast); if (readTagParams.PrepareForWriting) { bool vorbisTagFound = false; bool pictureFound = false; foreach (Zone zone in zones) { if (zone.Name.Equals(ZONE_PICTURE)) { pictureFound = true; } else if (zone.Name.Equals(ZONE_VORBISTAG)) { vorbisTagFound = true; } } if (!vorbisTagFound) { zones.Add(new Zone(ZONE_VORBISTAG, firstBlockPosition, 0, new byte[0])); } if (!pictureFound) { zones.Add(new Zone(ZONE_PICTURE, firstBlockPosition, 0, new byte[0])); } } } } if (isValid()) { audioOffset = source.BaseStream.Position; // we need that to rebuild the file if nedeed result = true; } return(result); }
protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams) { bool result = false; long position; resetData(); source.BaseStream.Seek(0, SeekOrigin.Begin); if (AIFF_CONTAINER_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { // Container chunk size long containerChunkPos = source.BaseStream.Position; int containerChunkSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4)); if (containerChunkPos + containerChunkSize + 4 != source.BaseStream.Length) { LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Header size is incoherent with file size"); } // Form type format = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); if (format.Equals(FORMTYPE_AIFF) || format.Equals(FORMTYPE_AIFC)) { isValid = true; StringBuilder commentStr = new StringBuilder(""); long soundChunkPosition = 0; long soundChunkSize = 0; // Header size included bool nameFound = false; bool authorFound = false; bool copyrightFound = false; bool commentsFound = false; long limit = Math.Min(containerChunkPos + containerChunkSize + 4, source.BaseStream.Length); int annotationIndex = 0; int commentIndex = 0; while (source.BaseStream.Position < limit) { ChunkHeader header = seekNextChunkHeader(source, limit); position = source.BaseStream.Position; if (header.ID.Equals(CHUNKTYPE_COMMON)) { short channels = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); switch (channels) { case 1: channelsArrangement = MONO; break; case 2: channelsArrangement = STEREO; break; case 3: channelsArrangement = ISO_3_0_0; break; case 4: channelsArrangement = ISO_2_2_0; break; // Specs actually allow both 2/2.0 and LRCS case 6: channelsArrangement = LRLcRcCS; break; default: channelsArrangement = UNKNOWN; break; } numSampleFrames = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); sampleSize = (uint)StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // This sample size is for uncompressed data only byte[] byteArray = source.ReadBytes(10); Array.Reverse(byteArray); double aSampleRate = StreamUtils.ExtendedToDouble(byteArray); if (format.Equals(FORMTYPE_AIFC)) { compression = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); } else // AIFF <=> no compression { compression = COMPRESSION_NONE; } if (aSampleRate > 0) { sampleRate = (int)Math.Round(aSampleRate); duration = (double)numSampleFrames * 1000.0 / sampleRate; if (!compression.Equals(COMPRESSION_NONE)) // Sample size is specific to selected compression method { if (compression.ToLower().Equals("fl32")) { sampleSize = 32; } else if (compression.ToLower().Equals("fl64")) { sampleSize = 64; } else if (compression.ToLower().Equals("alaw")) { sampleSize = 8; } else if (compression.ToLower().Equals("ulaw")) { sampleSize = 8; } } if (duration > 0) { bitrate = sampleSize * numSampleFrames * channelsArrangement.NbChannels / duration; } } } else if (header.ID.Equals(CHUNKTYPE_SOUND)) { soundChunkPosition = source.BaseStream.Position - 8; soundChunkSize = header.Size + 8; } else if (header.ID.Equals(CHUNKTYPE_NAME) || header.ID.Equals(CHUNKTYPE_AUTHOR) || header.ID.Equals(CHUNKTYPE_COPYRIGHT)) { structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID); tagExists = true; if (header.ID.Equals(CHUNKTYPE_NAME)) { nameFound = true; } if (header.ID.Equals(CHUNKTYPE_AUTHOR)) { authorFound = true; } if (header.ID.Equals(CHUNKTYPE_COPYRIGHT)) { copyrightFound = true; } SetMetaField(header.ID, Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size)), readTagParams.ReadAllMetaFrames); } else if (header.ID.Equals(CHUNKTYPE_ANNOTATION)) { annotationIndex++; structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + annotationIndex); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + annotationIndex); if (commentStr.Length > 0) { commentStr.Append(Settings.InternalValueSeparator); } commentStr.Append(Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size))); tagExists = true; } else if (header.ID.Equals(CHUNKTYPE_COMMENTS)) { commentIndex++; structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + commentIndex); structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + commentIndex); tagExists = true; commentsFound = true; ushort numComs = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)); for (int i = 0; i < numComs; i++) { CommentData cmtData = new CommentData(); cmtData.Timestamp = StreamUtils.DecodeBEUInt32(source.ReadBytes(4)); cmtData.MarkerId = StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // Comments length ushort comLength = StreamUtils.DecodeBEUInt16(source.ReadBytes(2)); MetaFieldInfo comment = new MetaFieldInfo(getImplementedTagType(), header.ID + commentIndex); comment.Value = Utils.Latin1Encoding.GetString(source.ReadBytes(comLength)); comment.SpecificData = cmtData; tagData.AdditionalFields.Add(comment); // Only read general purpose comments, not those linked to a marker if (0 == cmtData.MarkerId) { if (commentStr.Length > 0) { commentStr.Append(Settings.InternalValueSeparator); } commentStr.Append(comment.Value); } } } else if (header.ID.Equals(CHUNKTYPE_ID3TAG)) { id3v2Offset = source.BaseStream.Position; // Zone is already added by Id3v2.Read id3v2StructureHelper.AddZone(id3v2Offset - 8, header.Size + 8, CHUNKTYPE_ID3TAG); id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG); } source.BaseStream.Position = position + header.Size; if (header.ID.Equals(CHUNKTYPE_SOUND) && header.Size % 2 > 0) { source.BaseStream.Position += 1; // Sound chunk size must be even } } tagData.IntegrateValue(TagData.TAG_FIELD_COMMENT, commentStr.ToString().Replace("\0", " ").Trim()); if (-1 == id3v2Offset) { id3v2Offset = 0; // Switch status to "tried to read, but nothing found" if (readTagParams.PrepareForWriting) { id3v2StructureHelper.AddZone(soundChunkPosition + soundChunkSize, 0, CHUNKTYPE_ID3TAG); id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG); } } // Add zone placeholders for future tag writing if (readTagParams.PrepareForWriting) { if (!nameFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_NAME); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_NAME); } if (!authorFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_AUTHOR); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_AUTHOR); } if (!copyrightFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COPYRIGHT); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COPYRIGHT); } if (!commentsFound) { structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COMMENTS); structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COMMENTS); } } result = true; } } return(result); }
public bool Read(BinaryReader source, SizeInfo sizeInfo, MetaDataIO.ReadTagParams readTagParams) { bool result = false; bool doLoop = true; long position; UInt16 readData16; UInt32 readData32; UInt32 metaType; UInt32 metaSize; long sampleCount = 0; int frameSizeType = -1; this.sizeInfo = sizeInfo; resetData(); source.BaseStream.Seek(sizeInfo.ID3v2Size, SeekOrigin.Begin); if (TAK_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4)))) { result = true; position = source.BaseStream.Position; source.BaseStream.Seek(position, SeekOrigin.Begin); do // Loop metadata { readData32 = source.ReadUInt32(); metaType = readData32 & 0x7F; metaSize = readData32 >> 8; position = source.BaseStream.Position; if (0 == metaType) { doLoop = false; // End of metadata } else if (0x01 == metaType) // Stream info { readData16 = source.ReadUInt16(); frameSizeType = readData16 & 0x003C; // bits 11 to 14 readData32 = source.ReadUInt32(); uint restOfData = source.ReadUInt32(); sampleCount = (readData16 >> 14) + (readData32 << 2) + ((restOfData & 0x00000080) << 34); sampleRate = ((restOfData >> 4) & 0x03ffff) + 6000; // bits 4 to 21 channelsArrangement = ChannelsArrangements.GuessFromChannelNumber((int)((restOfData >> 27) & 0x0F) + 1); // bits 28 to 31 if (sampleCount > 0) { duration = (double)sampleCount * 1000.0 / sampleRate; bitrate = Math.Round(((double)(sizeInfo.FileSize - source.BaseStream.Position)) * 8 / duration); //time to calculate average bitrate } } else if (0x04 == metaType) // Encoder info { readData32 = source.ReadUInt32(); formatVersion = 100 * ((readData32 & 0x00ff0000) >> 16); formatVersion += 10 * ((readData32 & 0x0000ff00) >> 8); formatVersion += (readData32 & 0x000000ff); } source.BaseStream.Seek(position + metaSize, SeekOrigin.Begin); } while (doLoop); // End of metadata loop } return(result); }