Beispiel #1
0
        /// <summary>
        /// Reads the header part of a WAV file from a stream
        /// </summary>
        /// <param name="stream">The stream, positioned at the start of audio data</param>
        /// <param name="format">The format found</param>
        /// <param name="dataChunkPosition">The position of the data chunk</param>
        /// <param name="dataChunkLength">The length of the data chunk</param>
        /// <param name="chunks">Additional chunks found</param>
        public static void ReadWaveHeader(Stream stream, out WaveFormat format, out long dataChunkPosition, out int dataChunkLength, List <RiffChunk> chunks)
        {
            dataChunkPosition = -1;
            format            = null;
            BinaryReader br = new BinaryReader(stream);

            if (br.ReadInt32() != WaveInterop.mmioStringToFOURCC("RIFF", 0))
            {
                throw new FormatException("Not a WAVE file - no RIFF header");
            }
            uint fileSize = br.ReadUInt32(); // read the file size (minus 8 bytes)

            if (br.ReadInt32() != WaveInterop.mmioStringToFOURCC("WAVE", 0))
            {
                throw new FormatException("Not a WAVE file - no WAVE header");
            }

            int dataChunkID   = WaveInterop.mmioStringToFOURCC("data", 0);
            int formatChunkId = WaveInterop.mmioStringToFOURCC("fmt ", 0);

            dataChunkLength = 0;

            // sometimes a file has more data than is specified after the RIFF header
            long stopPosition = Math.Min(fileSize + 8, stream.Length);

            // this -8 is so we can be sure that there are at least 8 bytes for a chunk id and length
            while (stream.Position <= stopPosition - 8)
            {
                Int32 chunkIdentifier = br.ReadInt32();
                Int32 chunkLength     = br.ReadInt32();
                if (chunkIdentifier == dataChunkID)
                {
                    dataChunkPosition = stream.Position;
                    dataChunkLength   = chunkLength;
                    stream.Position  += chunkLength;
                }
                else if (chunkIdentifier == formatChunkId)
                {
                    format = WaveFormat.FromFormatChunk(br, chunkLength);
                }
                else
                {
                    if (chunks != null)
                    {
                        chunks.Add(new RiffChunk(chunkIdentifier, chunkLength, stream.Position));
                    }
                    stream.Position += chunkLength;
                }
            }

            if (format == null)
            {
                throw new FormatException("Invalid WAV file - No fmt chunk found");
            }
            if (dataChunkPosition == -1)
            {
                throw new FormatException("Invalid WAV file - No data chunk found");
            }
        }
        /// <summary>
        /// Creates a cue list from the cue RIFF chunk and the list RIFF chunk
        /// </summary>
        /// <param name="cueChunkData">The data contained in the cue chunk</param>
        /// <param name="listChunkData">The data contained in the list chunk</param>
        internal CueList(byte[] cueChunkData, byte[] listChunkData)
        {
            int cueCount = BitConverter.ToInt32(cueChunkData, 0);
            Dictionary <int, int> cueIndex = new Dictionary <int, int>();

            int[] positions = new int[cueCount];
            int   cue       = 0;

            for (int p = 4; cueChunkData.Length - p >= 24; p += 24, cue++)
            {
                cueIndex[BitConverter.ToInt32(cueChunkData, p)] = cue;
                positions[cue] = BitConverter.ToInt32(cueChunkData, p + 20);
            }

            string[] labels      = new string[cueCount];
            int      labelLength = 0;
            int      cueID       = 0;

            Int32 labelChunkID = WaveInterop.mmioStringToFOURCC("labl", 0);

            for (int p = 4; listChunkData.Length - p >= 16; p += labelLength + labelLength % 2 + 12)
            {
                if (BitConverter.ToInt32(listChunkData, p) == labelChunkID)
                {
                    labelLength = BitConverter.ToInt32(listChunkData, p + 4) - 4;
                    cueID       = BitConverter.ToInt32(listChunkData, p + 8);
                    cue         = cueIndex[cueID];
                    labels[cue] = Encoding.Default.GetString(listChunkData, p + 12, labelLength - 1);
                }
            }

            for (int i = 0; i < cueCount; i++)
            {
                cues.Add(new Cue(positions[i], labels[i]));
            }
        }
        /// <summary>
        /// Reads the header part of a WAV file from a stream
        /// </summary>
        /// <param name="stream">The stream, positioned at the start of audio data</param>
        /// <param name="format">The format found</param>
        /// <param name="dataChunkPosition">The position of the data chunk</param>
        /// <param name="dataChunkLength">The length of the data chunk</param>
        /// <param name="chunks">Additional chunks found</param>
        public static void ReadWaveHeader(Stream stream, out WaveFormat format, out long dataChunkPosition, out int dataChunkLength, List <RiffChunk> chunks)
        {
            dataChunkPosition = -1;
            format            = null;
            BinaryReader br = new BinaryReader(stream);

            if (br.ReadInt32() != WaveInterop.mmioStringToFOURCC("RIFF", 0))
            {
                throw new FormatException("Not a WAVE file - no RIFF header");
            }
            uint fileSize = br.ReadUInt32(); // read the file size (minus 8 bytes)

            if (br.ReadInt32() != WaveInterop.mmioStringToFOURCC("WAVE", 0))
            {
                throw new FormatException("Not a WAVE file - no WAVE header");
            }

            int dataChunkID   = WaveInterop.mmioStringToFOURCC("data", 0);
            int formatChunkId = WaveInterop.mmioStringToFOURCC("fmt ", 0);

            dataChunkLength = 0;

            // sometimes a file has more data than is specified after the RIFF header
            long stopPosition = Math.Min(fileSize + 8, stream.Length);

            // this -8 is so we can be sure that there are at least 8 bytes for a chunk id and length
            while (stream.Position <= stopPosition - 8)
            {
                Int32 chunkIdentifier = br.ReadInt32();
                Int32 chunkLength     = br.ReadInt32();
                if (chunkIdentifier == dataChunkID)
                {
                    dataChunkPosition = stream.Position;
                    dataChunkLength   = chunkLength;
                    stream.Position  += chunkLength;
                }
                else if (chunkIdentifier == formatChunkId)
                {
                    format = WaveFormat.FromFormatChunk(br, chunkLength);
                }
                else
                {
                    // check for invalid chunk length
                    if (chunkLength < 0 || chunkLength > stream.Length - stream.Position)
                    {
                        Debug.Assert(false, String.Format("Invalid chunk length {0}, pos: {1}. length: {2}",
                                                          chunkLength, stream.Position, stream.Length));
                        // an exception will be thrown further down if we haven't got a format and data chunk yet,
                        // otherwise we will tolerate this file despite it having corrupt data at the end
                        break;
                    }
                    if (chunks != null)
                    {
                        chunks.Add(new RiffChunk(chunkIdentifier, chunkLength, stream.Position));
                    }
                    stream.Position += chunkLength;
                }
            }

            if (format == null)
            {
                throw new FormatException("Invalid WAV file - No fmt chunk found");
            }
            if (dataChunkPosition == -1)
            {
                throw new FormatException("Invalid WAV file - No data chunk found");
            }
        }
        /// <summary>
        /// Gets the cues as the concatenated cue and list RIFF chunks.
        /// </summary>
        /// <returns>RIFF chunks containing the cue data</returns>
        internal byte[] GetRIFFChunks()
        {
            if (this.Count == 0)
            {
                return(null);
            }
            else
            {
                int cueChunkLength   = 12 + 24 * this.Count;
                int listChunkLength  = 12;
                int labelChunkLength = 0;
                for (int i = 0; i < this.Count; i++)
                {
                    labelChunkLength = this[i].Label.Length + 1;
                    listChunkLength += labelChunkLength + labelChunkLength % 2 + 12;
                }

                byte[] chunks       = new byte[cueChunkLength + listChunkLength];
                Int32  cueChunkID   = WaveInterop.mmioStringToFOURCC("cue ", 0);
                Int32  dataChunkID  = WaveInterop.mmioStringToFOURCC("data", 0);
                Int32  listChunkID  = WaveInterop.mmioStringToFOURCC("LIST", 0);
                Int32  adtlTypeID   = WaveInterop.mmioStringToFOURCC("adtl", 0);
                Int32  labelChunkID = WaveInterop.mmioStringToFOURCC("labl", 0);

                using (MemoryStream stream = new MemoryStream(chunks))
                {
                    using (BinaryWriter writer = new BinaryWriter(stream))
                    {
                        writer.Write(cueChunkID);
                        writer.Write(cueChunkLength - 8);
                        writer.Write(this.Count);
                        for (int cue = 0; cue < this.Count; cue++)
                        {
                            writer.Write(cue);
                            writer.Seek(4, SeekOrigin.Current);
                            writer.Write(dataChunkID);
                            writer.Seek(8, SeekOrigin.Current);
                            writer.Write(this[cue].Position);
                        }
                        writer.Write(listChunkID);
                        writer.Write(listChunkLength - 8);
                        writer.Write(adtlTypeID);
                        for (int cue = 0; cue < this.Count; cue++)
                        {
                            writer.Write(labelChunkID);
                            writer.Write(this[cue].Label.Length + 1 + 4);
                            writer.Write(cue);
                            writer.Write(Encoding.Default.GetBytes(this[cue].Label.ToCharArray()));
                            if (this[cue].Label.Length % 2 == 0)
                            {
                                writer.Seek(2, SeekOrigin.Current);
                            }
                            else
                            {
                                writer.Seek(1, SeekOrigin.Current);
                            }
                        }
                    }
                }
                return(chunks);
            }
        }