/// <summary> /// copy construct AudioFrame for derived classes /// </summary> /// <param name="other"></param> protected AudioFrame(AudioFrame other) { _frameBuffer = other._frameBuffer; _header = other._header; _headerBytes = other._headerBytes; }
/// <summary> /// construct AudioFrame from a larger portion of the stream; don't rewind stream when done /// </summary> /// <param name="stream">source stream</param> /// <param name="header">parsed header</param> /// <param name="frameSize">size from header, or scanning for second frame of free bitrate file</param> /// <param name="remainingBytes">number of bytes in audio block, as reported by the caller</param> public AudioFrame(Stream stream, AudioFrameHeader header, uint frameSize, uint remainingBytes) { Debug.Assert(header != null); _header = header; _frameBuffer = new byte[frameSize]; _headerBytes = _header.HeaderSize; int numgot = stream.Read(_frameBuffer, 0, (int)frameSize); if( numgot < (int)frameSize ) throw new InvalidAudioFrameException( string.Format("MPEG Audio AudioFrame: only {0} bytes of frame found when {1} bytes declared", numgot, frameSize)); }
/// <summary> /// compare headers /// </summary> // return true if identical or related // return false if no similarities // (from an idea in CMPAHeader, http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx) bool IsCompatible( AudioFrameHeader destHeader ) { // version change never possible if (destHeader.RawMpegVersion != RawMpegVersion) return false; // layer change never possible if (destHeader.RawLayer != RawLayer) return false; // sampling rate change never possible if (destHeader.RawSampleFreq != RawSampleFreq) return false; // from mono to stereo never possible if (destHeader.IsMono != IsMono) return false; if (destHeader.RawEmphasis != RawEmphasis) return false; return true; }
/// <summary> /// construct AudioFrame from supplied bytes /// </summary> /// <param name="frameBuffer"></param> /// <remarks>buffer is correct size, even for free bitrate files</remarks> public AudioFrame(byte[] frameBuffer) { // find and parse frame header, rewind stream to start, or throw. _header = new AudioFrameHeader(frameBuffer); _headerBytes = _header.HeaderSize; _frameBuffer = frameBuffer; }
/// <summary> /// Creates Header /// </summary> /// <remarks> /// n.b. doesn't rewind the stream to the start of the frame. /// If the caller wants to read the entire frame in one block, they'll have to rewind it themselves. /// </remarks> /// <param name="stream"></param> /// <param name="remainingBytes"></param> /// <returns>valid audio header, or null</returns> static AudioFrameHeader CreateHeader( Stream stream, uint remainingBytes ) { // save the start of the skip operation, for error messages long streamStartpos = stream.Position; int framesSkipped = 0; // apply an upper limit of 64k to the number of bytes it will skip, // to prevent it trying to read the whole of a corrupt 5meg file // (MPAHeaderInfo skips 3 frames but I think it's worth checking further than that) if( remainingBytes > 65536 ) remainingBytes = 65536; long endPos = streamStartpos + (long)remainingBytes; do { long remaining = endPos - (uint)stream.Position; int numskipped = Seek( stream, remaining ); if( numskipped < 0 ) { //throw new InvalidAudioFrameException( // string.Format("MPEG Audio Frame: No header found from offset {0} to the end", streamStartpos)); return null; } //else if( numskipped > 0 ) // Trace.WriteLine( string.Format( "{0} bytes skipped to start of frame at offset {1}", // numskipped, stream.Position - numskipped ) ); // save the start of the real frame, i.e. after the rubbish is skipped long frameStartPos = stream.Position; AudioFrameHeader parsedHeader = new AudioFrameHeader( ReadHeader( stream ) ); if( parsedHeader.Valid ) { if( frameStartPos > streamStartpos ) { if( framesSkipped > 0 ) Trace.WriteLine(string.Format("total {0} bytes and {1} invalid frame headers skipped to get to the start of a valid frame at stream offset {2}", frameStartPos - streamStartpos, framesSkipped, frameStartPos)); else Trace.WriteLine(string.Format("total {0} bytes skipped to get to the start of a valid frame at stream offset {1}", frameStartPos - streamStartpos, frameStartPos)); } return parsedHeader; } // header is invalid mp3 frame, so skip a char and look again stream.Position = frameStartPos; ++framesSkipped; /*byte skipchar = (byte)*/stream.ReadByte(); //Trace.WriteLine(string.Format(" Invalid MP3 frame; skipping 0x{0:X} at {1}", skipchar, frameStartPos + 1)); } while( true ); }