OSDN Git Service

#39474 Riff chunked oggを使った曲を再生しようとすると、DTXManiaが強制終了する問題を修正。
[dtxmania/dtxmania.git] / NVorbis / VorbisReader.cs
diff --git a/NVorbis/VorbisReader.cs b/NVorbis/VorbisReader.cs
new file mode 100644 (file)
index 0000000..1e67655
--- /dev/null
@@ -0,0 +1,344 @@
+/****************************************************************************
+ * NVorbis                                                                  *
+ * Copyright (C) 2014, Andrew Ward <afward@gmail.com>                       *
+ *                                                                          *
+ * See COPYING for license terms (Ms-PL).                                   *
+ *                                                                          *
+ ***************************************************************************/
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Diagnostics;
+
+namespace NVorbis
+{
+    public class VorbisReader : IDisposable
+    {
+        int _streamIdx;
+
+        IContainerReader _containerReader;
+        List<VorbisStreamDecoder> _decoders;
+        List<int> _serials;
+
+        VorbisReader()
+        {
+            ClipSamples = true;
+
+            _decoders = new List<VorbisStreamDecoder>();
+            _serials = new List<int>();
+
+        }
+
+        public VorbisReader(string fileName)
+            : this(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), true)
+        {
+        }
+
+        public VorbisReader(Stream stream, bool closeStreamOnDispose)
+            : this()
+        {
+            var bufferedStream = new BufferedReadStream(stream);
+            bufferedStream.CloseBaseStream = closeStreamOnDispose;
+
+                       // try Ogg first
+                       var oggContainer = new Ogg.ContainerReader(bufferedStream, closeStreamOnDispose);
+            if (!LoadContainer(oggContainer))
+            {
+               // oops, not Ogg!
+                // we don't support any other container types yet, so error out
+                // TODO: Add Matroska fallback
+                bufferedStream.Close();
+                throw new InvalidDataException("Could not determine container type!!");
+            }
+           _containerReader = oggContainer;
+
+            if (_decoders.Count == 0) throw new InvalidDataException("No Vorbis data found!");
+        }
+
+        public VorbisReader(IContainerReader containerReader)
+            : this()
+        {
+            if (!LoadContainer(containerReader))
+            {
+                throw new InvalidDataException("Container did not initialize!");
+            }
+            _containerReader = containerReader;
+
+            if (_decoders.Count == 0) throw new InvalidDataException("No Vorbis data found!");
+        }
+
+        public VorbisReader(IPacketProvider packetProvider)
+            : this()
+        {
+            var ea = new NewStreamEventArgs(packetProvider);
+            NewStream(this, ea);
+            if (ea.IgnoreStream) throw new InvalidDataException("No Vorbis data found!");
+        }
+
+        bool LoadContainer(IContainerReader containerReader)
+        {
+            containerReader.NewStream += NewStream;
+            if (!containerReader.Init())
+            {
+                containerReader.NewStream -= NewStream;
+                return false;
+            }
+            return true;
+        }
+
+        void NewStream(object sender, NewStreamEventArgs ea)
+        {
+            var packetProvider = ea.PacketProvider;
+            var decoder = new VorbisStreamDecoder(packetProvider);
+            if (decoder.TryInit())
+            {
+                _decoders.Add(decoder);
+                _serials.Add(packetProvider.StreamSerial);
+            }
+            else
+            {
+                // This is almost certainly not a Vorbis stream
+                ea.IgnoreStream = true;
+            }
+        }
+
+        public void Dispose()
+        {
+            if (_decoders != null)
+            {
+                foreach (var decoder in _decoders)
+                {
+                    decoder.Dispose();
+                }
+                _decoders.Clear();
+                _decoders = null;
+            }
+
+            if (_containerReader != null)
+            {
+                _containerReader.NewStream -= NewStream;
+                _containerReader.Dispose();
+                _containerReader = null;
+            }
+        }
+
+        VorbisStreamDecoder ActiveDecoder
+        {
+            get
+            {
+                if (_decoders == null) throw new ObjectDisposedException("VorbisReader");
+                return _decoders[_streamIdx];
+            }
+        }
+
+        #region Public Interface
+
+        /// <summary>
+        /// Gets the number of channels in the current selected Vorbis stream
+        /// </summary>
+        public int Channels { get { return ActiveDecoder._channels; } }
+
+        /// <summary>
+        /// Gets the sample rate of the current selected Vorbis stream
+        /// </summary>
+        public int SampleRate { get { return ActiveDecoder._sampleRate; } }
+
+        /// <summary>
+        /// Gets the encoder's upper bitrate of the current selected Vorbis stream
+        /// </summary>
+        public int UpperBitrate { get { return ActiveDecoder._upperBitrate; } }
+
+        /// <summary>
+        /// Gets the encoder's nominal bitrate of the current selected Vorbis stream
+        /// </summary>
+        public int NominalBitrate { get { return ActiveDecoder._nominalBitrate; } }
+
+        /// <summary>
+        /// Gets the encoder's lower bitrate of the current selected Vorbis stream
+        /// </summary>
+        public int LowerBitrate { get { return ActiveDecoder._lowerBitrate; } }
+
+        /// <summary>
+        /// Gets the encoder's vendor string for the current selected Vorbis stream
+        /// </summary>
+        public string Vendor { get { return ActiveDecoder._vendor; } }
+
+        /// <summary>
+        /// Gets the comments in the current selected Vorbis stream
+        /// </summary>
+        public string[] Comments { get { return ActiveDecoder._comments; } }
+
+        /// <summary>
+        /// Gets whether the previous short sample count was due to a parameter change in the stream.
+        /// </summary>
+        public bool IsParameterChange { get { return ActiveDecoder.IsParameterChange; } }
+
+        /// <summary>
+        /// Gets the number of bits read that are related to framing and transport alone
+        /// </summary>
+        public long ContainerOverheadBits { get { return ActiveDecoder.ContainerBits; } }
+
+        /// <summary>
+        /// Gets or sets whether to automatically apply clipping to samples returned by <see cref="VorbisReader.ReadSamples"/>.
+        /// </summary>
+        public bool ClipSamples { get; set; }
+
+        /// <summary>
+        /// Gets stats from each decoder stream available
+        /// </summary>
+        public IVorbisStreamStatus[] Stats
+        {
+            get { return _decoders.Select(d => d).Cast<IVorbisStreamStatus>().ToArray(); }
+        }
+
+        /// <summary>
+        /// Gets the currently-selected stream's index
+        /// </summary>
+        public int StreamIndex
+        {
+            get { return _streamIdx; }
+        }
+
+        /// <summary>
+        /// Reads decoded samples from the current logical stream
+        /// </summary>
+        /// <param name="buffer">The buffer to write the samples to</param>
+        /// <param name="offset">The offset into the buffer to write the samples to</param>
+        /// <param name="count">The number of samples to write</param>
+        /// <returns>The number of samples written</returns>
+        public int ReadSamples(float[] buffer, int offset, int count)
+        {
+            if (offset < 0) throw new ArgumentOutOfRangeException("offset");
+            if (count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("count");
+
+            count = ActiveDecoder.ReadSamples(buffer, offset, count);
+
+            if (ClipSamples)
+            {
+                var decoder = _decoders[_streamIdx];
+                for (int i = 0; i < count; i++, offset++)
+                {
+                    buffer[offset] = Utils.ClipValue(buffer[offset], ref decoder._clipped);
+                }
+            }
+
+            return count;
+        }
+
+        /// <summary>
+        /// Clears the parameter change flag so further samples can be requested.
+        /// </summary>
+        public void ClearParameterChange()
+        {
+            ActiveDecoder.IsParameterChange = false;
+        }
+
+        /// <summary>
+        /// Returns the number of logical streams found so far in the physical container
+        /// </summary>
+        public int StreamCount
+        {
+            get { return _decoders.Count; }
+        }
+
+        /// <summary>
+        /// Searches for the next stream in a concatenated file
+        /// </summary>
+        /// <returns><c>True</c> if a new stream was found, otherwise <c>false</c>.</returns>
+        public bool FindNextStream()
+        {
+            if (_containerReader == null) return false;
+            return _containerReader.FindNextStream();
+        }
+
+        /// <summary>
+        /// Switches to an alternate logical stream.
+        /// </summary>
+        /// <param name="index">The logical stream index to switch to</param>
+        /// <returns><c>True</c> if the properties of the logical stream differ from those of the one previously being decoded. Otherwise, <c>False</c>.</returns>
+        public bool SwitchStreams(int index)
+        {
+            if (index < 0 || index >= StreamCount) throw new ArgumentOutOfRangeException("index");
+
+            if (_decoders == null) throw new ObjectDisposedException("VorbisReader");
+
+            if (_streamIdx == index) return false;
+
+            var curDecoder = _decoders[_streamIdx];
+            _streamIdx = index;
+            var newDecoder = _decoders[_streamIdx];
+
+            return curDecoder._channels != newDecoder._channels || curDecoder._sampleRate != newDecoder._sampleRate;
+        }
+
+        /// <summary>
+        /// Gets or Sets the current timestamp of the decoder.  Is the timestamp before the next sample to be decoded
+        /// </summary>
+        public TimeSpan DecodedTime
+        {
+            get
+            {
+                return TimeSpan.FromSeconds((double)ActiveDecoder.CurrentPosition / SampleRate);
+            }
+            set
+            {
+                ActiveDecoder.SeekTo((long)(value.TotalSeconds * SampleRate));
+            }
+
+        }
+
+        /// <summary>
+        /// Gets or Sets the current position of the next sample to be decoded.
+        /// </summary>
+        public long DecodedPosition
+        {
+            get 
+            {
+                return ActiveDecoder.CurrentPosition;
+            }
+            set
+            {
+                ActiveDecoder.SeekTo(value);
+            }
+        }
+
+        /// <summary>
+        /// Gets the total length of the current logical stream
+        /// </summary>
+        public TimeSpan TotalTime
+        {
+            get
+            {
+                var decoder = ActiveDecoder;
+                if (decoder.CanSeek)
+                {
+                    return TimeSpan.FromSeconds((double)decoder.GetLastGranulePos() / decoder._sampleRate);
+                }
+                else
+                {
+                    return TimeSpan.MaxValue;
+                }
+            }
+        }
+
+        public long TotalSamples
+        {
+            get
+            {
+                var decoder = ActiveDecoder;
+                if (decoder.CanSeek)
+                {
+                    return decoder.GetLastGranulePos();
+                }
+                else
+                {
+                    return long.MaxValue;
+                }
+            }
+        }
+        
+        #endregion
+    }
+}