/// <summary> /// Decompress a single frame of MP3 /// </summary> public int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset) { // 1. copy into our DMO's input buffer inputMediaBuffer.LoadData(frame.RawData, frame.FrameLength); // 2. Give the input buffer to the DMO to process mp3Decoder.MediaObject.ProcessInput(0, inputMediaBuffer, DmoInputDataBufferFlags.None, 0, 0); outputBuffer.MediaBuffer.SetLength(0); outputBuffer.StatusFlags = DmoOutputDataBufferFlags.None; // 3. Now ask the DMO for some output data mp3Decoder.MediaObject.ProcessOutput(DmoProcessOutputFlags.None, 1, new[] {outputBuffer}); if (outputBuffer.Length == 0) { Debug.WriteLine("ResamplerDmoStream.Read: No output data available"); return 0; } // 5. Now get the data out of the output buffer outputBuffer.RetrieveData(dest, destOffset); Debug.Assert(!outputBuffer.MoreDataAvailable, "have not implemented more data available yet"); return outputBuffer.Length; }
public int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset) { Array.Copy(frame.RawData, conversionStream.SourceBuffer, frame.FrameLength); int sourceBytesConverted = 0; int converted = conversionStream.Convert(frame.FrameLength, out sourceBytesConverted); if (sourceBytesConverted != frame.FrameLength) { throw new InvalidOperationException("Couldn't convert the whole MP3 frame"); } Array.Copy(conversionStream.DestBuffer, 0, dest, destOffset, converted); return converted; }
/// <summary> /// Opens MP3 from a stream rather than a file /// Will not dispose of this stream itself /// </summary> /// <param name="inputStream"></param> public Mp3FileReader(Stream inputStream) { // Calculated as a double to minimize rounding errors double bitRate; mp3Stream = inputStream; id3v2Tag = Id3v2Tag.ReadTag(mp3Stream); dataStartPosition = mp3Stream.Position; var mp3Frame = new Mp3Frame(mp3Stream); sampleRate = mp3Frame.SampleRate; frameLengthInBytes = mp3Frame.FrameLength; bitRate = mp3Frame.BitRate; xingHeader = XingHeader.LoadXingHeader(mp3Frame); // If the header exists, we can skip over it when decoding the rest of the file if (xingHeader != null) dataStartPosition = mp3Stream.Position; mp3DataLength = mp3Stream.Length - dataStartPosition; // try for an ID3v1 tag as well mp3Stream.Position = mp3Stream.Length - 128; var tag = new byte[128]; mp3Stream.Read(tag, 0, 3); if (tag[0] == 'T' && tag[1] == 'A' && tag[2] == 'G') { id3v1Tag = tag; mp3DataLength -= 128; } mp3Stream.Position = dataStartPosition; CreateTableOfContents(); tocIndex = 0; // [Bit rate in Kilobits/sec] = [Length in kbits] / [time in seconds] // = [Length in bits ] / [time in milliseconds] // Note: in audio, 1 kilobit = 1000 bits. bitRate = (mp3DataLength*8.0/TotalSeconds()); mp3Stream.Position = dataStartPosition; Mp3WaveFormat = new Mp3WaveFormat(sampleRate, mp3Frame.ChannelMode == ChannelMode.Mono ? 1 : 2, frameLengthInBytes, (int) bitRate); decompressor = new AcmMp3FrameDecompressor(Mp3WaveFormat); // new DmoMp3FrameDecompressor(this.Mp3WaveFormat); waveFormat = decompressor.OutputFormat; bytesPerSample = (decompressor.OutputFormat.BitsPerSample)/8*decompressor.OutputFormat.Channels; // no MP3 frames have more than 1152 samples in them // some MP3s I seem to get double decompressBuffer = new byte[1152*bytesPerSample*2]; }
/// <summary> /// Reads the next mp3 frame /// </summary> /// <returns>Next mp3 frame, or null if EOF</returns> public Mp3Frame ReadNextFrame(bool readData) { if (mp3Stream.Position + frameLengthInBytes <= mp3DataLength + dataStartPosition) { var frame = new Mp3Frame(mp3Stream, readData); tocIndex++; return frame; } return null; }
/// <summary> /// Load Xing Header /// </summary> /// <param name="frame">Frame</param> /// <returns>Xing Header</returns> public static XingHeader LoadXingHeader(Mp3Frame frame) { var xingHeader = new XingHeader(); xingHeader.frame = frame; int offset = 0; if (frame.MpegVersion == MpegVersion.Version1) { if (frame.ChannelMode != ChannelMode.Mono) offset = 32 + 4; else offset = 17 + 4; } else if (frame.MpegVersion == MpegVersion.Version2) { if (frame.ChannelMode != ChannelMode.Mono) offset = 17 + 4; else offset = 9 + 4; } else { return null; // throw new FormatException("Unsupported MPEG Version"); } if ((frame.RawData[offset + 0] == 'X') && (frame.RawData[offset + 1] == 'i') && (frame.RawData[offset + 2] == 'n') && (frame.RawData[offset + 3] == 'g')) { xingHeader.startOffset = offset; offset += 4; } else { return null; } var flags = (XingHeaderOptions) ReadBigEndian(frame.RawData, offset); offset += 4; if ((flags & XingHeaderOptions.Frames) != 0) { xingHeader.framesOffset = offset; offset += 4; } if ((flags & XingHeaderOptions.Bytes) != 0) { xingHeader.bytesOffset = offset; offset += 4; } if ((flags & XingHeaderOptions.Toc) != 0) { xingHeader.tocOffset = offset; offset += 100; } if ((flags & XingHeaderOptions.VbrScale) != 0) { xingHeader.vbrScale = ReadBigEndian(frame.RawData, offset); offset += 4; } xingHeader.endOffset = offset; return xingHeader; }