Exemple #1
0
 internal bool FlushPacket(out OggPacket packet) =>
 SafeNativeMethods.VorbisBitrateFlushPacket(DspState, out packet) == 1;
        public unsafe AudioMetadata ReadMetadata(Stream stream)
        {
            OggStream? oggStream = null;
            SafeNativeMethods.VorbisCommentInit(out var vorbisComment);
#if NETSTANDARD2_0
            var buffer = ArrayPool<byte>.Shared.Rent(4096);
#else
            Span<byte> buffer = stackalloc byte[4096];
#endif

            try
            {
                using (var sync = new OggSync())
                using (var decoder = new VorbisDecoder())
                {
                    OggPage page;

                    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);
                        }

                        oggStream ??= new(SafeNativeMethods.OggPageSerialNo(page));
                        oggStream.PageIn(page);

                        while (oggStream.PacketOut(out var packet))
                        {
                            decoder.HeaderIn(vorbisComment, packet);

                            if (packet.PacketNumber == 1)
                                return new VorbisCommentToMetadataAdapter(vorbisComment);
                        }
                    } while (!SafeNativeMethods.OggPageEos(page));

                    throw new AudioInvalidException("The end of the Ogg stream was reached without finding a header.");
                }
            }
            finally
            {
#if NETSTANDARD2_0
                ArrayPool<byte>.Shared.Return(buffer);
#endif
                SafeNativeMethods.VorbisCommentClear(ref vorbisComment);
                oggStream?.Dispose();
            }
        }
Exemple #3
0
 internal void Analysis(IntPtr packet) => SafeNativeMethods.VorbisAnalysis(_block, packet);
Exemple #4
0
 internal void AddBlock() => SafeNativeMethods.VorbisBitrateAddBlock(_block);
Exemple #5
0
 internal void Wrote(int samples) => SafeNativeMethods.VorbisAnalysisWrote(DspState, samples);
Exemple #6
0
 internal bool BlockOut() => SafeNativeMethods.VorbisAnalysisBlockOut(DspState, _block) == 1;
Exemple #7
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();
                }
            }
        }
Exemple #8
0
 internal IntPtr GetBuffer(int samples) => SafeNativeMethods.VorbisAnalysisBuffer(DspState, samples);
Exemple #9
0
 void FreeUnmanaged()
 {
     SafeNativeMethods.OggStreamClear(_state);
     Marshal.FreeHGlobal(_state);
 }
Exemple #10
0
 internal VorbisDecoder()
 {
     SafeNativeMethods.VorbisInfoInit(_info);
 }
Exemple #11
0
 internal bool Flush(out OggPage page) => SafeNativeMethods.OggStreamFlush(_state, out page) != 0;
Exemple #12
0
 internal bool PacketOut(out OggPacket packet) => SafeNativeMethods.OggStreamPacketOut(_state, out packet) == 1;
Exemple #13
0
 internal bool PageOut(out OggPage page) => SafeNativeMethods.OggStreamPageOut(_state, out page) != 0;
Exemple #14
0
        internal OggStream(long serialNumber)
#endif
        {
            _state = Marshal.AllocHGlobal(Marshal.SizeOf <OggStreamState>());
            SafeNativeMethods.OggStreamInit(_state, serialNumber);
        }
Exemple #15
0
        public bool Handle()
        {
            var logger = LoggerManager.LoggerFactory.CreateLogger <VorbisLibHandler>();

#if WINDOWS
            var libPath = Path.Combine(
                // ReSharper disable once AssignNullToNotNullAttribute
                Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
                Environment.Is64BitProcess ? "win-x64" : "win-x86");

#if NETSTANDARD2_0
            // On Full Framework, AssemblyLoadContext isn't available, so we add the directory to PATH
            if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.Ordinal))
            {
                Environment.SetEnvironmentVariable("PATH",
                                                   $"{libPath}{Path.PathSeparator}{Environment.GetEnvironmentVariable("PATH")}");
            }
            else
            {
                AddUnmanagedLibraryPath(libPath);
            }
#else
            AddUnmanagedLibraryPath(libPath);
#endif
#elif OSX
            var osVersion = GetOSVersion();

            AddUnmanagedLibraryPath(Path.Combine(
                                        // ReSharper disable once AssignNullToNotNullAttribute
                                        Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
                                        osVersion.StartsWith("10.12", StringComparison.Ordinal) ? "osx.10.12-x64" :
                                        osVersion.StartsWith("10.13", StringComparison.Ordinal) ? "osx.10.13-x64" :
                                        "osx.10.14-x64"));
#else // LINUX
            var release = GetRelease();

            if (release.StartsWith("Ubuntu 16.04", StringComparison.OrdinalIgnoreCase))
            {
                AddUnmanagedLibraryPath(Path.Combine(
                                            // ReSharper disable once AssignNullToNotNullAttribute
                                            Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
                                            release.StartsWith("Ubuntu 16.04", StringComparison.OrdinalIgnoreCase)
                        ? "ubuntu.16.04-x64"
                        : "ubuntu.18.04-x64"));
            }

            if (!VerifyLibrary("libogg.so.0"))
            {
                logger.LogWarning(
                    release.StartsWith("Ubuntu", StringComparison.OrdinalIgnoreCase)
                        ? "Missing libogg.so.0. Run 'sudo apt-get install -y libogg0 && sudo updatedb' then restart AudioWorks."
                        : "Missing libogg.so.0.");
                return(false);
            }
#endif

            logger.LogInformation("Using libvorbis version {0}.",
                                  Marshal.PtrToStringAnsi(SafeNativeMethods.VorbisVersion()));

            return(true);
        }