This project has moved. For the latest updates, please go here.

Noise in playback start

Oct 24, 2012 at 10:32 AM

Hi all! 

Sorry if this is already discussed question, I tried but couldn't find an answer here.

I need to allow user to listen some part of MP3 file. To do this I basically follow example from documentation, my code looks like this:

private Mp3FileReader _mp3Reader; 
private WaveOut _wavePlayer; 
private Timer _timer;
private double? _endSec;


		public void Play( double startSec, double? endSec )
		{
			_endSec = endSec;

			_mp3Reader.CurrentTime = new TimeSpan( (long)( startSec * 10000000 ) );

			_wavePlayer.Play();

			_timer.Start();
		}


		private void Timer_Tick( object sender, EventArgs e )
		{
			if( _endSec.HasValue && _mp3Reader.CurrentTime.TotalSeconds >= _endSec.Value )
				Stop();
		}

Everething is great at first time. But when I click Play next time I hear a distinct noise during the first miliseconds of playback. If at the end of the previous playback there was silence, then there will be no noise next time, if at the end of the previous playback was loud sound, then noise will be loud.

It looks like some internal sound buffer is left with a few milliseconds of sound from previous playback, and so next playback first plays that leftover of previous playback. Is there some way to clear these internal buffers? have you met such problem and what are solutions for it?

Thanks!

Sergey P.

Oct 24, 2012 at 11:10 AM

Hi,

I have the same problem. Not only with the MP3 file (wav too) and all type of drivers (Wave, WASAPI, ASIO).

More generally, it remains a part of the buffer already read.

The number of remaining sample is proportional to the latency.

To make a simple test must be PLAY (listen to the song to a highlight). PAUSE and then move to the position (quiet time) and then PLAY. there is data in the buffer.

 

Coordinator
Oct 24, 2012 at 11:13 AM

If you reposition, you should stop. Pause is supposed to keep things in the buffer, and WaveOut has no knowledge that you have repositioned.

Stopping however should flush the buffers, and if it isn't doing that there is a bug somewhere.

Oct 24, 2012 at 11:15 AM

Same problem with a Stop :-((

Oct 24, 2012 at 11:32 AM
Edited Oct 24, 2012 at 11:34 AM

I think this is not really a bug, euhmmm

But one way to do it.

In your demo after every STOP,  you unload fileStream waveOut and reset with each PLAY.

The problem arises when initializing the beginning, and playing with Play, Pause, Stop

I do not know the structure of SergeyPo but I think it is the same problem.

Oct 24, 2012 at 12:15 PM

Hi!

I don't use Pause, only Play and Stop -- exactly as in my code excerpt. So, obviously there is something with Stop method - it doesn't clear buffer.

Coordinator
Oct 24, 2012 at 12:48 PM

Hi, I've found the bug. It's not in any of the output devices. It is in the Mp3FileReader class. The MP3 decoder needs to be alerted that you have repositioned. I've prototyped a fix, but it is a breaking change as it modifies the IMp3FrameDecompressor interface, so I'll need to think about how best to introdduce the change.

Coordinator
Oct 24, 2012 at 1:32 PM

I've checked in a fix. If you build the very latest NAudio you should be able to test it out for yourself. I'll hopefully get a new build up on NuGet soon.

Oct 24, 2012 at 3:00 PM

Mark, thank you for such a quick response!

I've downloaded and compiled the latest version of source codes but the bug is still here. :(

Can you check fix again?

Coordinator
Oct 24, 2012 at 3:03 PM

Can you try the Simple Audio Playback demo in NAudio Demo and click the MP3 test button. It will ask you to select an MP3 file and then bring up a very simple form with Play Stop and Rewind. Click Play, then Stop, then Rewind, and then Play again. You shouldn't hear any left-over buffer sound if my fix worked properly. Let me know how you get on with that, and then we'll see if there is another unresolved issue.

Oct 24, 2012 at 3:32 PM

Yes congratulations to speed for the update.

For me the problem is gone :-))

Oct 24, 2012 at 4:06 PM

Hi, Mark!

I tried Simple Audio Playback demo -- the left-over is still here. You can try this file if you like http://www.filedropper.com/testaudio but actually I can reproduce it on any MP3 file, just you need to press Stop at the moment when sound is loud.

Coordinator
Oct 24, 2012 at 4:30 PM

that's strange. It was a very reproducable problem before my fix, and I couldn't hear it at all afterwards. I'll try with your file if I get a chance..

Coordinator
Oct 24, 2012 at 4:35 PM

I should also ask what soundcard and OS you are using. NAudio calls waveOutReset when you press stop which the soundcard drivers are supposed to respond to by immediately releasing all the buffers. Sometimes cheap soundcards don't behave quite how you expect though.

Oct 24, 2012 at 5:18 PM

OS: Windows XP SP3

Audio chipset is on motherboard.

Motherboard: ASUS P5KPL-AM

Audio: VIA High Definition Audio

I also updated to newest drivers from ASUS web site (not so new actually) -- this didn't help. So drivers are -- version 6.0.1.7600, date 21/10/2009.

Hope this will help. 

Coordinator
Oct 24, 2012 at 5:31 PM

OK, well to diagnose the problem further, we'll need to break it down. For example, instead of using WaveOut, try WaveOutEvent and DirectSoundOut. Also, what does your constructor for WaveOut look like?

Another test is to play WAV files instead of MP3s. Does the same issue happen?

And if you have any other soundcard, e.g. a USB headset, see if the problem occurs with that instead.

Oct 25, 2012 at 7:17 AM

Hi, Mark!

Here is results:

  • Mp3FileReader + WaveOut or WaveOutEvent or DirectSoundOut = plays but with left-over noise
  • WaveFileReader + WaveOut or WaveOutEvent = plays good!
  • WaveFileReader + DirectSoundOut = silence

I'll try to test code on another computer tomorrow.

Here is the code that I used for these tests -- the simplest one:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NAudio.Wave;

namespace NAudioTest
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private Mp3FileReader _reader;
		//private WaveFileReader _reader;

		//private WaveOut _wavePlayer;
		//private WaveOutEvent _wavePlayer;
		private DirectSoundOut _wavePlayer;

		private void Form1_Load( object sender, EventArgs e )
		{
			_reader = new Mp3FileReader( @"C:\Temp\NAudioTest\test2.mp3" );
			//_reader = new WaveFileReader( @"C:\Temp\NAudioTest\test2.wav" );

			//_wavePlayer = new WaveOut();
			//_wavePlayer = new WaveOutEvent();
			_wavePlayer = new DirectSoundOut();

			_wavePlayer.Init( _reader );
		}

		private void btPlay_Click( object sender, EventArgs e )
		{
			_reader.CurrentTime = new TimeSpan();

			_wavePlayer.Play();
		}

		private void btStop_Click( object sender, EventArgs e )
		{
			_wavePlayer.Stop();
		}
	}
}

Coordinator
Oct 25, 2012 at 7:25 AM

OK, are you 100% sure you have my latest checkin. You need to go to the "Source Code" tab above or download the change I uploaded yesterday here.

Oct 25, 2012 at 8:38 AM

I was sure but I'have done this right now again :) - noise is still here.

Here is http://www.filedropper.com/naudiotest

I put this small test project together with test files and compiled NAudio (in Release mode) - on your computer it should work without nouse as I understand.

Coordinator
Oct 25, 2012 at 8:49 AM

Hi Sergey, I've tried your app and I can't hear any problems. You're saying you hear some noise at the start when you press play?

Let me know how you get on with testing on other computers.

Another test you can do is create a new output device every time you press play. i.e.

 

private void btPlay_Click( object sender, EventArgs e )
{
    if (_wavePlayer != null)
        _wavePlayer.Dispose();            
    _wavePlayer = new DirectSoundOut();
    _wavePlayer.Init( _reader );

    _reader.CurrentTime = new TimeSpan();
    _wavePlayer.Play();
}
This will help us narrow down whether it is the WavePlayer or the MP3 reader that is the source of your issue.

 

Oct 25, 2012 at 10:46 AM

> You're saying you hear some noise at the start when you press play?

Yes, correct. And this is "left-over" sound, because I don't hear it first time and I don't hear it if I pressed Stop when there was silence in the sound, but I hear it when I pressed Stop and there was loud sound at this moment. The louder a sound was when I pressed Stop, the louder will be noise at next Play.

I tried your workaround with recreating of _wavePlayer -- this didn't help.

I tried code that recreates everething at every Play:

		private void btPlay_Click( object sender, EventArgs e )
		{
			if( _wavePlayer != null )
				_wavePlayer.Dispose();
			if( _reader != null )
				_reader.Dispose();

			_reader = new Mp3FileReader( @"C:\Temp\NAudioTest\test2.mp3" );
			_wavePlayer = new DirectSoundOut();
			_wavePlayer.Init( _reader );

			_reader.CurrentTime = new TimeSpan();
			_wavePlayer.Play();
		}

This code plays sound correctly. But I can't use this as workaround because your Mp3FileReader (as far as I understand) scans through all frame headers in MP3 file and this can take some noticeable time for really big MP3 files (by the way, it's great that you implemented Mp3FileReader this way, because this allows correct positioning inside MP3 -- something that Windows Media Player or WinAmp don't do correctly -- this fact really was big surprise for me).

I'll test on another computer tomorrow. Thanks for all your efforts to resolve this issue!

Coordinator
Oct 25, 2012 at 10:50 AM

OK, so its definitely the MP3FileReader, and clearly the changes I made to AcmMp3FrameDecompressor aren't working for you.

I am now wondering if maybe you have a different ACM MP3 codec installed on your machine. Can you use the NAudioDemo utility to examine what codecs you have installed on your system, and see what is there. Also, are you running your executable as x86 or x64?

Oct 25, 2012 at 11:51 AM

Application runs as 32 bit. Below is list from "NAudioDemo.exe \ ACM Format Conversion":

===========================

Long Name: Кодек IMA ADPCM (Microsoft)

Short Name: Microsoft IMA ADPCM

Driver ID: 1527664

---

Long Name: Кодек ADPCM (Microsoft)

Short Name: MS-ADPCM

Driver ID: 1611616

---

Long Name: Кодек CCITT G.711 A-Law и u-Law (Microsoft)

Short Name: Microsoft CCITT G.711

Driver ID: 1885552

---

Long Name: Кодек GSM 6.10 (Microsoft)

Short Name: Microsoft GSM 6.10

Driver ID: 1885960

---

Long Name: Кодек DSP Group TrueSpeech™

Short Name: TrueSpeech(TM)

Driver ID: 1886368

---

Long Name: Microsoft G.723.1 CODEC

Short Name: Microsoft G.723.1

Driver ID: 1886776

---

Long Name: Windows Media Audio

Short Name: WM-AUDIO

Driver ID: 1887184

---

Long Name: Sipro Lab Telecom ACELP.net audio codec

Short Name: ACELP.net

Driver ID: 1887592

---

Long Name: Indeo® audio software

Short Name: Indeo® audio software

Driver ID: 1888000

---

Long Name: Fraunhofer IIS MPEG Layer-3 Codec (advanced)

Short Name: MPEG Layer-3 Codec 

Driver ID: 1888408

---

Long Name: Конвертор PCM Microsoft

Short Name: MS-PCM

Driver ID: 1857096

=========================

Do you need details from bottom text box of "NAudioDemo.exe \ ACM Format Conversion"?

Coordinator
Oct 25, 2012 at 12:45 PM

OK, one more thing to try. The constructor for Mp3FileReader lets you pass in a factory object that creates a frame decompressor. Create a class like this:

class CustomMp3FrameDecompressor : IMp3FrameDecompressor
{
    private AcmMp3FrameDecompressor decompressor;
    private WaveFormat waveFormat;

    public CustomMp3FrameDecompressor(WaveFormat waveFormat)
    {
        decompressor = new AcmMp3FrameDecompressor(waveFormat);
        this.waveFormat = waveFormat;
    }

    public int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset)
    {  
        return decompressor.DecompressFrame(frame,dest,destOffset);
    }

    public void Reset()
    {
        decompressor.Dispose();
        decompressor = new AcmMp3FrameDecompressor(waveFormat);
    }

    public void Dispose()
    {
        decompressor.Dispose();
    }

    public WaveFormat OutputFormat 
    {
        get { return decompressor.OutputFormat; }
    }                     
}

And then when you create a new Mp3FileReader, pass it in

new Mp3FileReader(fileName, (wf) => new CustomMp3FrameDecompressor(wf));

Oct 25, 2012 at 1:47 PM
Edited Oct 25, 2012 at 1:48 PM

Wow! Great, this works! :)

Do you need some more information to track why simple approach doesn't work?

And what does this mean that we need to create CustomMp3FrameDecompressor -- does this has any drawbacks?

Coordinator
Oct 25, 2012 at 2:00 PM

Well the fix I made before passed a flag into the codec that is supposed to tell the codec that we have repositioned and it should flush its internal state. However, it seems that your codec was ignoring that flag, so this solution closes the codec and reopens it when you reposition. Ideally I'd like to find a fix inside AcmMp3FrameDecompressor itself as this might not perform too well if you are repositioning during playback.

Mark

Oct 25, 2012 at 2:34 PM

I tried this workaround on big MP3 file (187 MB, 4 hours) -- no noticable delay -- so this works well for me.

Write if you'll need any more information or testing from my side, I will be glad to help you fix this problem completely, if you'll decide to do this.

You do a great job -- NAudio is very easy to use and it calculates duration of MP3 correctly (something that Windows Media Player control does wrong). Thanks!

Coordinator
Oct 25, 2012 at 2:39 PM

thanks SergeyPo,

The way I'm trying to do this with the AcmMp3FrameConverter is calling into acmStreamConvert with the ACM_STREAMCONVERTF_START flag set. As far as I can see, that is how you are supposed to do a reposition, and I'm surprised the codec on your machine doesn't seem to be responding to it correctly.

Mark

ACM_STREAMCONVERTF_START