How to fast-forward or backward an mp3 file while playing it?
<p>I have a class to help me to play mp3 files from URL sources. It works good when playing, pausing and resuming. But I am confused about to fast-forward or backward.</p>

<p>I am using temporary files to store mp3 data and I want to re-position the <code>FileStream</code> according to position the user selected. But there is a problem for it.</p>

<p><strong>Problem:</strong> If the position doesn't exist yet. (Not downloaded yet)<a href="https://i.stack.imgur.com/dqIl6.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/dqIl6.png" alt="enter image description here"></a></p>

<p>This can be solved using <code>WebRequest.AddRange()</code> but in this case, we have to open a new FileStream to store bytes separately and calling <code>AddRange()</code> method every time that user wants to go forward or backward means that the file is going to be re-downloaded from that position. However, if this is done too often, we have to download the file as many as the number of the forward or backward.</p>

<p>So, if there is a simple and quota-friendly solution please let me know it. I can't figure out how to do it. Help please!</p>

<p><strong>My code:</strong></p>

<pre><code>public class NAudioPlayer
{
    HttpWebRequest req;
    HttpWebResponse resp;
    Stream stream;
    WaveOut waveOut;
    Mp3WaveFormat format;
    AcmMp3FrameDecompressor decompressor;
    BufferedWaveProvider provider;
    FileStream tempFileStream;
    System.Windows.Forms.Timer ticker;
    private int bufferedDuration;   

    string url, path;
    long size, streamPos;
    int timeOffset, timePosition, avgBytes, duration;
    bool formatKnown, waitinloop, exitloop;

    State currentState;

    public NAudioPlayer(string mp3Url)
    {
        this.url = mp3Url;
        this.currentState = State.Stopped;
        this.size = -1;
        this.timeOffset = 0;
        this.timePosition = 0;
        this.avgBytes = 0;
        this.duration = 0;
        this.format = null;
        this.ticker = new System.Windows.Forms.Timer();
        this.waveOut = new WaveOut();
        this.waitinloop = false;

        ticker.Interval = 250;
        ticker.Tick += ticker_Tick;

    }
    int target = 0;
    void ticker_Tick(object sender, EventArgs e)
    {
        if (waveOut.PlaybackState == PlaybackState.Playing)
        {
            timePosition = timeOffset + (int)(waveOut.GetPosition() * 1d / waveOut.OutputWaveFormat.AverageBytesPerSecond);
            Debug.WriteLine(timePosition);
        }
        if (duration != 0 &amp;&amp; timePosition &gt;= duration) 
        {
            waveOut.Stop();
            ticker.Stop();
        }

        if (timePosition == target &amp;&amp; timePosition &lt; duration - 5 &amp;&amp; 
            provider != null &amp;&amp; provider.BufferedDuration.TotalSeconds &lt; 5)
        {
            waveOut.Pause();
            currentState = State.Buffering;
            target = timePosition + 5;
        }
        if (currentState == State.Buffering &amp;&amp; provider != null &amp;&amp; provider.BufferedDuration.TotalSeconds &gt;= 5)
        {
            waveOut.Play();
        }
    }

    public void Play()
    {
        int range = avgBytes &lt;= 0 ? 0 : timeOffset * avgBytes;
        int readBytes = 0;
        long pos = 0;
        this.streamPos = 0;
        exitloop = false;
        disposeAllResources();
        ticker.Start();

        Task.Run(() =&gt;
        {

            //Crate WebRequest using AddRange to enable repositioning the mp3
            req = WebRequest.Create(url) as HttpWebRequest;
            req.AllowAutoRedirect = true;
            req.ServicePoint.ConnectionLimit = 100;
            req.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0";
            req.AddRange(range);
            resp = req.GetResponse() as HttpWebResponse;
            stream = resp.GetResponseStream();
            size = resp.ContentLength;

            //Create a unique file to store data
            path = Path.GetTempPath() + Guid.NewGuid().ToString() + ".mp3";
            tempFileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

            waveOut.Stop();
            waveOut = new WaveOut();
            if (provider != null)
                waveOut.Init(provider);

            byte[] buffer = new byte[17 * 1024];

            while ((readBytes = stream.Read(buffer, 0, buffer.Length)) &gt; 0 ||
                    timePosition &lt;= duration)
            {
                while (waitinloop)
                    Thread.Sleep(500);

                if (exitloop)
                    break;

                Mp3Frame frame = null;
                tempFileStream.Write(buffer, 0, readBytes);
                tempFileStream.Flush();

                //Read the stream starting from the point 
                //where we were at the last reading
                using (MemoryStream ms = new MemoryStream(ReadStreamPartially(tempFileStream, streamPos, 1024 * 10)))
                {
                    ms.Position = 0;
                    try
                    {
                        frame = Mp3Frame.LoadFromStream(ms);
                    }
                    catch { continue; } //Sometimes it throws Unexpected End of Stream exception
                    //Couldn't find the problem out, try catch is working for now

                    if (frame == null)
                        continue;

                    pos = ms.Position;
                    streamPos += pos;
                }

                if (!formatKnown)
                {
                    format = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2,
                                                                frame.FrameLength, frame.BitRate);
                    duration = (int)(Math.Ceiling(resp.ContentLength * 1d / format.AverageBytesPerSecond));

                    avgBytes = format.AverageBytesPerSecond;
                    formatKnown = true;
                }

                if (decompressor == null)
                {
                    decompressor = new AcmMp3FrameDecompressor(format);
                    provider = new BufferedWaveProvider(decompressor.OutputFormat);
                    provider.BufferDuration = TimeSpan.FromSeconds(20);
                    waveOut.Init(provider);
                    waveOut.Play();
                }

                int decompressed = decompressor.DecompressFrame(frame, buffer, 0);

                if (IsBufferNearlyFull(provider))
                {
                    Thread.Sleep(500);
                }


                provider.AddSamples(buffer, 0, decompressed);
            }
        });
    }


    void disposeAllResources()
    {
        if (resp != null)
            resp.Close();
        if (stream != null)
            stream.Close();
        if (provider != null)
            provider.ClearBuffer();
    }

    public void Pause()
    {
        if (waveOut.PlaybackState == PlaybackState.Playing &amp;&amp; !waitinloop)
        {
            waitinloop = true;
            waveOut.Pause();
            Thread.Sleep(200);
        }
    }
    public void Resume()
    {
        if (waveOut.PlaybackState == PlaybackState.Paused &amp;&amp; waitinloop)
        {
            waitinloop = false;
            waveOut.Play();
            Thread.Sleep(200);
        }
    }
    public void ForwardOrBackward(int targetTimePos)
    {
        waitinloop = false;
        exitloop = true;
        timeOffset = targetTimePos;
        Thread.Sleep(100);
        waveOut.Stop();
        ticker.Stop();
        this.Play();
    }
    public static byte[] ReadStreamPartially(System.IO.Stream stream, long offset, long count)
    {
        long originalPosition = 0;

        if (stream.CanSeek)
        {
            originalPosition = stream.Position;
            stream.Position = offset;
        }

        try
        {
            byte[] readBuffer = new byte[4096];
            byte[] total = new byte[count];
            int totalBytesRead = 0;
            int byteRead;

            while ((byteRead = stream.ReadByte()) != -1)
            {
                Buffer.SetByte(total, totalBytesRead, (byte)byteRead);
                totalBytesRead++;
                if (totalBytesRead == count)
                {
                    stream.Position = originalPosition;
                    break;
                }
            }
            if (totalBytesRead &lt; count)
            {
                byte[] temp = new byte[totalBytesRead];
                Buffer.BlockCopy(total, 0, temp, 0, totalBytesRead);
                stream.Position = originalPosition;
                return temp;
            }
            return total;
        }
        finally
        {
            if (stream.CanSeek)
            {
                stream.Position = originalPosition;
            }
        }
    }
    private bool IsBufferNearlyFull(BufferedWaveProvider bufferedWaveProvider)
    {
        return bufferedWaveProvider != null &amp;&amp;
               bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes
               &lt; bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4;
    }

    public int Duration
    {
        get
        {
            return duration;
        }
    }
    public int TimePosition
    {
        get
        {
            return timePosition;
        }
    }
    public int BufferedDuration
    {
        get { return (int)provider.BufferedDuration.TotalSeconds; }
    }
    public int TimeOffset
    {
        get
        {
            return timeOffset;
        }
    }
}
public enum State
{
    Paused,
    Playing,
    Stopped,
    Buffering
}
</code></pre>