// Returns 'true' if it still looks valid, 'false' if it doesn't public bool Process(byte b) { processedBytes++; if (skip > 0) { skip--; return(true); } if (state == MP3State.ID3v2) // Check magic bytes and get ID3v2 length { if (progress < 3) { if (b == MP3Detector.MagicStart[progress]) { progress++; return(true); } return(false); } else if (progress < 6) // TODO We can perform extra verification on 'version' and 'flags' here { progress++; return(true); } else if (progress < 9) { lengthBytes[progress - 6] = b; progress++; return(true); } else { lengthBytes[progress - 6] = b; progress++; // Determine length // This is given by the 4 length bytes, where the first bit in each of them is ignored (28 bits) // Note that since ID3v2 is LE, the 'first' bit is bit 7 // Convert to integers in system-endian if (!BitConverter.IsLittleEndian) { Array.Reverse(lengthBytes); } // Bit 0 is _never_ high in the ID3v2 size field foreach (byte lb in lengthBytes) { if ((lb & 0b10000000) == 0b10000000) { return(false); } } int[] lengthInts = new int[4]; lengthInts[0] = (int)lengthBytes[0]; lengthInts[1] = (int)lengthBytes[1]; lengthInts[2] = (int)lengthBytes[2]; lengthInts[3] = (int)lengthBytes[3]; // Consider the length ints to be A B C and D // The length is then given by A*2^21 + B*2^14 + C^*2^7 + D // We use << for powers, which gives us this expression // int len = lengthInts[0] * (2 << 20) + lengthInts[1] * (2 << 13) + lengthInts[2] * (2 << 6) + lengthInts[3]; //Console.WriteLine("ID3v2 Tag Length: {0}", len); progress = 0; state = MP3State.Frame; skip = len; // -1 to account for the current byte return(true); } } else if (state == MP3State.Frame) // Keep parsing MP3 frame headers { if (progress < 3) { frameBytes[progress] = b; progress++; return(true); } else { frameBytes[progress] = b; progress = 0; // Reset ////// Verify header //string bits1 = Convert.ToString(frameBytes[0], 2); //string bits2 = Convert.ToString(frameBytes[1], 2); //string bits3 = Convert.ToString(frameBytes[2], 2); //string bits4 = Convert.ToString(frameBytes[3], 2); //Console.WriteLine("Global index: {0}", detectedStartIdx+processedBytes-1); //Console.WriteLine("{0} {1} {2} {3}", bits1, bits2, bits3, bits4); //Console.WriteLine("0x{0:X} 0x{1:X} 0x{2:X} 0x{3:X}", frameBytes[0], frameBytes[1], frameBytes[2], frameBytes[3]); // Check frame sync // First 11 bits are all 1 if (!(frameBytes[0] == 0xFF && (frameBytes[1] & 0b11100000) == 0b11100000)) { if (potentiallyValid) { state = MP3State.ID3; return(true); } else { //Console.WriteLine("Reject: sync"); return(false); } } // Check version // This is either 11 for v1 or 10 for v2 // 01 is reserved and 00 is unofficial int version = (frameBytes[1] & 0b00011000) >> 3; if (version != 0b11 && version != 0b10) { if (potentiallyValid) { state = MP3State.ID3; return(true); } else { //Console.WriteLine("Reject: version"); return(false); } } // Check layer int layer = (frameBytes[1] & 0b00000110) >> 1; if (layer == 0b00) // 00 is reserved; 01, 10 and 11 are valid layers { if (potentiallyValid) { state = MP3State.ID3; return(true); } else { //Console.WriteLine("Reject: layer"); return(false); } } // Get bitrate index byte bitrateIndex = (byte)((frameBytes[2] & 0b11110000) >> 4); // Get sampling rate index byte samplingIndex = (byte)((frameBytes[2] & 0b00001100) >> 2); int bitrate = MP3Detector.GetBitrate(version, layer, bitrateIndex) * 1000; // Multiply by 1000 (convert kbps -> bps) int samplingRate = MP3Detector.GetSamplingRate(version, samplingIndex); if (bitrate == -1) { if (potentiallyValid) { state = MP3State.ID3; return(true); } else { //Console.WriteLine("Reject: bitrate"); return(false); } } if (samplingRate == -1) { if (potentiallyValid) { state = MP3State.ID3; return(true); } else { //Console.WriteLine("Reject: sampling"); //Console.WriteLine("{0} - {1}", samplingIndex, version); return(false); } } //Console.WriteLine("Got bitrate: {0}", bitrate); //Console.WriteLine("Got sam: {0}", samplingRate); // Get padding bit // This is either 0 (not padded) or 1 (padded) int paddingBit = (frameBytes[2] & 0b00000010) >> 1; //Console.WriteLine("Padding: {0}", paddingBit); // Compute length int length; if (layer == 0b11) { length = (12 * bitrate / samplingRate + paddingBit * 4) * 4; // 4 byte pad } else { length = 144 * bitrate / samplingRate + paddingBit; // 1 byte pad } //Console.WriteLine("Computed length: {0}", length); skip = length - 4; potentiallyValid = true; return(true); } } else // MP3State.ID3; // NOTICE: BY THE TIME WE SWITCH HERE WE'VE ALREADY READ 4 BYTES!!! // Those 4 bytes were read in the 'Frame' state which was found invalid // Thus, we already have all the bytes needed to check for ID3v1 'TAG' { if (frameBytes[0] == 0x54 && frameBytes[1] == 0x41 && frameBytes[2] == 0x47) { processedBytes += 123; // ID3v1 tag is always 128; -4 we already read; -1 the current byte we just read } else { processedBytes--; // Substract 1 for the byte we just read, since it isn't used } done = true; return(true); } }
public PotentialMP3(int start_idx, MP3State startState) { detectedStartIdx = start_idx; state = startState; }