Search code examples
c#audiosfmln-layersfml.net

NLayer MpegFile to SFML.Net SoundStream


I'm currently working on SFML.Net to expand with mp3 support. Therefore I wrote a Stream class which uses NLayer MpegFile to decode the mp3.

public class Mp3StreamSFML : SoundStream
{
    private MpegFile mp3file;
    private int currentBufferSize;
    private short[] currentBuffer;

    public Mp3StreamSFML(String _filename)
    {
        mp3file = new MpegFile(_filename);
        Initialize((uint)mp3file.Channels, (uint)mp3file.SampleRate);
        currentBufferSize = 0;
        currentBuffer = new short[currentBufferSize];
    }

    #region implemented abstract members of SoundStream

    protected override bool OnGetData(out short[] samples)
    {
        if (currentBufferSize <= mp3file.Position)
        {
            byte[] buffer = new byte[512];
            if (mp3file.ReadSamples(buffer, 0, buffer.Length) > 0)
            {
                Array.Resize(ref currentBuffer, currentBuffer.Length + (buffer.Length / 2));
                Buffer.BlockCopy(buffer, 0, currentBuffer, currentBufferSize, buffer.Length);
                currentBufferSize = currentBuffer.Length;
            }
            samples = currentBuffer;
            return true;
        } 
        else
        {
            samples = currentBuffer;
            return false;
        }
    }

    protected override void OnSeek(TimeSpan timeOffset)
    {
        mp3file.Position = (long)timeOffset.TotalSeconds;
    }

    #endregion
}

I use it this way:

                try
                {
                    stream = new Mp3StreamSFML(this.objProgram.getObjCuesheet().getAudiofilePath(true));
                    stream.Play();
                    log.debug("samplerate = " + stream.SampleRate);
                }
                catch(Exception ex)
                {
                    log.fatal(ex.ToString());
                }

Unfortunately, there is not the correct sound played, its just "juttering" and sound really weird. What I'm doing wrong? Seems like a problem between the 2 Frameworks.

Thanks for your help. Sven


Solution

  • Solved the problem this way:

    using System;
    using SFML.Audio;
    using NLayer;
    using System.Threading;
    
    namespace AudioCuesheetEditor.AudioBackend.SFML
    {
        /// <summary>
        /// Class for mp3 decoded audio files to use in SFML as Soundstream, since SFML doesn't support mp3 decoding (for legal reasons).
        /// </summary>
        public class Mp3StreamSFML : SoundStream
        {
            private static readonly Logfile log = Logfile.getLogfile(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            private MpegFile mp3file;
            private Mutex mutex;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="AudioCuesheetEditor.AudioBackend.SFML.Mp3StreamSFML"/> class.
            /// </summary>
            /// <param name="_filename">Full path to the file</param>
            public Mp3StreamSFML(String _filename)
            {
                log.debug("Constructor called with " + _filename);
                this.mp3file = new MpegFile(_filename);
                this.Initialize((uint)this.mp3file.Channels, (uint)this.mp3file.SampleRate);
                this.mutex = new Mutex();
            }
    
            public TimeSpan Duration
            {
                get
                {
                    log.debug("Duration = " + this.mp3file.Duration);
                    return this.mp3file.Duration;
                }
            }
    
            #region implemented abstract members of SoundStream
    
            protected override bool OnGetData(out short[] samples)
            {
                log.debug("OnGetData called");
                this.mutex.WaitOne();
                //Buffer data for about 1 second
                float[] normalizedaudiodata = new float[48000];
                int readSamples = this.mp3file.ReadSamples(normalizedaudiodata, 0, normalizedaudiodata.Length);
                short[] pcmaudiodata;
                if (readSamples > 0)
                {
                    pcmaudiodata = new short[readSamples]; // converted data
                    for (int i = 0; i < readSamples; i++)
                    {
                        // clip the data
                        if (normalizedaudiodata[i] > 1.0f)
                        {
                            normalizedaudiodata[i] = 1.0f;
                        }
                        else
                        {
                            if (normalizedaudiodata[i] < -1.0f)
                            {
                                normalizedaudiodata[i] = -1.0f;
                            }
                        }
                        // convert to pcm data
                        pcmaudiodata[i] = (short)(normalizedaudiodata[i] * short.MaxValue);
                    }
                    samples = pcmaudiodata;
                    this.mutex.ReleaseMutex();
                    return true;
                }
                else
                {
                    samples = null;
                    this.mutex.ReleaseMutex();
                    return false;
                }
            }
    
            protected override void OnSeek(TimeSpan timeOffset)
            {
                log.debug("OnSeek called with " + timeOffset);
                this.mutex.WaitOne();
                if ((timeOffset <= this.mp3file.Duration) && (timeOffset >= TimeSpan.Zero))
                {
                    this.mp3file.Time = timeOffset;
                }
                this.mutex.ReleaseMutex();
            }
    
            #endregion
        }
    }