Skip to content
wo80 edited this page May 3, 2021 · 4 revisions

The decoder was tested on the .NET 5.0 platform using the NAudio.Core and NAudio.WinMM NuGet packages.

namespace AcoustID.Examples
{
    using NAudio.Wave;
    using System;
    using System.Buffers;
    using System.IO;

    /// <summary>
    /// Decode using the NAudio library.
    /// </summary>
    public class NAudioDecoder : AudioDecoder
    {
        string file;

        public NAudioDecoder(string file)
        {
            this.file = file;

            Initialize();
        }

        public override bool Decode(IAudioConsumer consumer, int maxLength)
        {
            using (var reader = OpenWaveStream(file))
            {
                if (reader.WaveFormat.BitsPerSample != 16)
                {
                    return false;
                }

                int remaining, length, size;

                var buffer = ArrayPool<byte>.Shared.Rent(2 * BUFFER_SIZE);
                var data = ArrayPool<short>.Shared.Rent(BUFFER_SIZE);

                // Samples to read to get maxLength seconds of audio
                remaining = maxLength * this.Format.Channels * this.sampleRate;

                // Bytes to read
                length = 2 * Math.Min(remaining, BUFFER_SIZE);

                while ((size = reader.Read(buffer, 0, length)) > 0)
                {
                    Buffer.BlockCopy(buffer, 0, data, 0, size);

                    consumer.Consume(data, size / 2);

                    remaining -= size / 2;
                    if (remaining <= 0)
                    {
                        break;
                    }

                    length = 2 * Math.Min(remaining, BUFFER_SIZE);
                }

                ArrayPool<byte>.Shared.Return(buffer);
                ArrayPool<short>.Shared.Return(data);

                return true;
            }
        }

        private bool Initialize()
        {
            using (var reader = OpenWaveStream(file))
            {
                var format = reader.WaveFormat;

                this.sampleRate = format.SampleRate;
                this.channels = format.Channels;

                this.Format = new AudioProperties(format.SampleRate, format.BitsPerSample,
                    format.Channels, (int)reader.TotalTime.TotalSeconds);

                return format.BitsPerSample != 16;
            }
        }

        private WaveStream OpenWaveStream(string file)
        {
            var extension = Path.GetExtension(file).ToLowerInvariant();

            if (extension.Equals(".mp3"))
            {
                return new Mp3FileReader(file);
            }

            // Try open as WAV (will throw an exception, if not supported).
            return new WaveFileReader(file);
        }
    }

Additional code:

    /// <summary>
    /// Interface for audio decoders.
    /// </summary>
    public interface IAudioDecoder : IDecoder, IDisposable
    {
        AudioProperties Format { get; }
    }

    /// <summary>
    /// Abstract base class for audio decoders
    /// </summary>
    public abstract class AudioDecoder : IAudioDecoder
    {
        protected static readonly int BUFFER_SIZE = 2 * 192000;

        protected int sampleRate;
        protected int channels;

        public int SampleRate
        {
            get { return sampleRate; }
        }

        public int Channels
        {
            get { return channels; }
        }

        ~AudioDecoder() => Dispose(false);

        public AudioProperties Format { get; protected set; }

        public abstract bool Decode(IAudioConsumer consumer, int maxLength);

        public virtual void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
        }
    }
}
Clone this wiki locally