How to fast forward or rewind an mp3 file during playback?

I have a class that will help me play mp3 files from URL sources. It works well when it plays, pauses and resumes. But I'm embarrassed to skip forward or backward.

I use temporary files to store mp3 data and I want to move the FileStream according to the position selected by the user. But there is a problem for this.

Problem: If the line item does not exist yet. (Not loaded) enter image description here

This can be solved using WebRequest.AddRange() , but in this case we must open a new FileStream to store bytes separately and call the AddRange() method each time the user wants to go forward or backward, which means that the file will be reloaded with this position. However, if this is done too often, we must download the file as much as the number forward or backward.

So, if there is a simple and quota solution, let me know. I can’t figure out how to do this. Help me please!

My code is:

 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 && timePosition >= duration) { waveOut.Stop(); ticker.Stop(); } if (timePosition == target && timePosition < duration - 5 && provider != null && provider.BufferedDuration.TotalSeconds < 5) { waveOut.Pause(); currentState = State.Buffering; target = timePosition + 5; } if (currentState == State.Buffering && provider != null && provider.BufferedDuration.TotalSeconds >= 5) { waveOut.Play(); } } public void Play() { int range = avgBytes <= 0 ? 0 : timeOffset * avgBytes; int readBytes = 0; long pos = 0; this.streamPos = 0; exitloop = false; disposeAllResources(); ticker.Start(); Task.Run(() => { //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)) > 0 || timePosition <= 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 && !waitinloop) { waitinloop = true; waveOut.Pause(); Thread.Sleep(200); } } public void Resume() { if (waveOut.PlaybackState == PlaybackState.Paused && 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 < 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 && bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes < 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 } 
+7
c # audio-streaming
source share
1 answer

I can show you how I would try to do this - assuming that the waveOut buffer is not completely different from the secondary DirectSound buffer.

Stream playback can behave as follows:

The way it should be reproduced.

There is data that has already been downloaded, and possibly reproduced and not loaded, data between. To save data broken down by fractions, we need to add additional information to it - time \ playorder.

To make this easier, we divide the file / stream into atomic subhanks of a fixed size, for example. 100kByte. If the file has a size of 5001 kb β†’ 51 sub-clones.

You can save them to a file in the loaded order and search for the identifier you need, and then reload the sub-cell in your playbuffer. To do this, you need to use this version of AddRange to load the sublanguage:

public void AddRange (int from, int to) https://msdn.microsoft.com/de-de/library/7fy67z6d(v=vs.110).aspx

enter image description here

I hope you understand.

  • Download in another way and save the old stream

  • Let the test player check if it needs to restart its queue.

  • Only upload a subware if it has not been fully stored in memory or file.

You can read the way to read the file:

File description

+5
source share

All Articles