public void Initialize(Stream stream, AudioInfo info, AudioMetadata metadata, SettingDictionary settings)
        {
            InitializeReplayGainFilter(info, metadata, settings);

            _oggStream = new OggStream(settings.TryGetValue("SerialNumber", out int serialNumber)
                ? serialNumber
                : new Random().Next());

            // Default to a quality setting of 5
            if (settings.TryGetValue("BitRate", out int bitRate))
            {
                if (settings.TryGetValue("ForceCBR", out bool cbr) && cbr)
                {
                    _encoder = new VorbisEncoder(info.Channels, info.SampleRate,
                                                 bitRate * 1000, bitRate * 1000, bitRate * 1000);
                }
                else
                {
                    _encoder = new VorbisEncoder(info.Channels, info.SampleRate,
                                                 -1, bitRate * 1000, -1);
                }
            }
            else
            {
                _encoder = new VorbisEncoder(info.Channels, info.SampleRate,
                                             settings.TryGetValue("Quality", out int quality)
                        ? quality / 10f
                        : 0.5f);
            }

            // Generate the header
            using (var comment = new MetadataToVorbisCommentAdapter(metadata))
            {
                comment.HeaderOut(_encoder.DspState, out var first, out var second, out var third);
                _oggStream.PacketIn(first);
                _oggStream.PacketIn(second);
                _oggStream.PacketIn(third);
            }

            // Buffer the header in memory
            using (var tempStream = new MemoryStream())
            {
                _outputStream = tempStream;

                // ReSharper disable once PossibleNullReferenceException
                while (_oggStream.Flush(out var page))
                {
                    WritePage(page);
                }

                // Pre-allocate the whole stream (estimate worst case of 500kbps, plus the header)
                stream.SetLength(0xFA00 * (long)info.PlayLength.TotalSeconds + tempStream.Length);

                // Flush the headers to the output stream
                tempStream.WriteTo(stream);
            }

            _outputStream = stream;
        }
        public void Initialize(Stream stream, AudioInfo info, AudioMetadata metadata, SettingDictionary settings)
        {
            InitializeReplayGainFilter(info, metadata, settings);

            if (!settings.TryGetValue("SerialNumber", out int serialNumber))
#if NETSTANDARD2_0
            { using (var random = RandomNumberGenerator.Create())
              {
                  var buffer = new byte[sizeof(int)];
                  random.GetNonZeroBytes(buffer);
                  serialNumber = BitConverter.ToInt32(buffer, 0);
              } }
#else
            { serialNumber = RandomNumberGenerator.GetInt32(int.MaxValue); }
#endif

            _oggStream = new(serialNumber);

            // Default to a quality setting of 5
            if (settings.TryGetValue("BitRate", out int bitRate))
            {
                if (settings.TryGetValue("ForceCBR", out bool cbr) && cbr)
                {
                    _encoder = new(info.Channels, info.SampleRate, bitRate * 1000, bitRate * 1000, bitRate * 1000);
                }
                else
                {
                    _encoder = new(info.Channels, info.SampleRate, -1, bitRate * 1000, -1);
                }
            }
            else
            {
                _encoder = new(info.Channels, info.SampleRate,
                               settings.TryGetValue("Quality", out int quality) ? quality / 10f : 0.5f);
            }

            // Generate the header
            using (var comment = new MetadataToVorbisCommentAdapter(metadata))
            {
                comment.HeaderOut(_encoder.DspState, out var first, out var second, out var third);
                _oggStream.PacketIn(first);
                _oggStream.PacketIn(second);
                _oggStream.PacketIn(third);
            }

            // Buffer the header in memory
            using (var tempStream = new MemoryStream())
            {
                _outputStream = tempStream;

                while (_oggStream.Flush(out var page))
                {
                    WritePage(page);
                }

                // Pre-allocate the whole stream (estimate worst case of 500kbps, plus the header)
                stream.SetLength(0xFA00 * (long)info.PlayLength.TotalSeconds + tempStream.Length);

                // Flush the headers to the output stream
                tempStream.WriteTo(stream);
            }

            _outputStream = stream;
        }
Exemple #3
0
        public unsafe void WriteMetadata(Stream stream, AudioMetadata metadata, SettingDictionary settings)
        {
            using (var tempStream = new TempFileStream())
            {
                OggStream inputOggStream  = null;
                OggStream outputOggStream = null;
#if NETSTANDARD2_0
                var buffer = ArrayPool <byte> .Shared.Rent(4096);
#else
                Span <byte> buffer = stackalloc byte[4096];
#endif

                try
                {
                    using (var sync = new OggSync())
                    {
                        var     headerWritten = false;
                        OggPage page;
                        var     pagesWritten = 0u;

                        do
                        {
                            // Read from the buffer into a page
                            while (!sync.PageOut(out page))
                            {
#if NETSTANDARD2_0
                                var bytesRead = stream.Read(buffer, 0, buffer.Length);
#else
                                var bytesRead = stream.Read(buffer);
#endif
                                if (bytesRead == 0)
                                {
                                    throw new AudioInvalidException("No Ogg stream was found.");
                                }

                                var nativeBuffer = new Span <byte>(sync.Buffer(bytesRead).ToPointer(), bytesRead);
#if NETSTANDARD2_0
                                buffer.AsSpan().Slice(0, bytesRead).CopyTo(nativeBuffer);
#else
                                buffer.Slice(0, bytesRead).CopyTo(nativeBuffer);
#endif
                                sync.Wrote(bytesRead);
                            }

                            if (inputOggStream == null)
                            {
                                inputOggStream = new OggStream(SafeNativeMethods.OggPageSerialNo(page));
                            }
                            if (outputOggStream == null)
                            {
                                outputOggStream = new OggStream(inputOggStream.SerialNumber);
                            }

                            // Write new header page(s) using a modified comment packet
                            if (!headerWritten)
                            {
                                inputOggStream.PageIn(page);

                                while (inputOggStream.PacketOut(out var packet))
                                {
                                    switch (packet.PacketNumber)
                                    {
                                    case 0:
                                        outputOggStream.PacketIn(packet);
                                        break;

                                    // Substitute the new comment packet
                                    case 1:
                                        using (var adapter = new MetadataToVorbisCommentAdapter(metadata))
                                        {
                                            adapter.HeaderOut(out var commentPacket);
                                            outputOggStream.PacketIn(commentPacket);
                                        }

                                        break;

                                    // Flush at the end of the header
                                    case 2:
                                        outputOggStream.PacketIn(packet);
                                        while (outputOggStream.Flush(out var outPage))
                                        {
                                            WritePage(outPage, tempStream);
                                            pagesWritten++;
                                        }

                                        headerWritten = true;
                                        break;

                                    default:
                                        throw new AudioInvalidException("Missing header packet.");
                                    }
                                }
                            }
                            else
                            {
                                // Copy the existing data pages verbatim, with updated sequence numbers
                                UpdateSequenceNumber(ref page, pagesWritten);
                                WritePage(page, tempStream);
                                pagesWritten++;
                            }
                        } while (!SafeNativeMethods.OggPageEos(page));

                        // Once the end of the input is reached, overwrite the original file and return
                        stream.Position = 0;
                        stream.SetLength(tempStream.Length);
                        tempStream.Position = 0;
                        tempStream.CopyTo(stream);
                    }
                }
                finally
                {
#if NETSTANDARD2_0
                    ArrayPool <byte> .Shared.Return(buffer);
#endif
                    inputOggStream?.Dispose();
                    outputOggStream?.Dispose();
                }
            }
        }