当同步播放3个MP3声音文件时,我收到一个奇怪的例外

时间:2011-06-03 18:57:18

标签: c# wpf naudio

我想这样做:

Sistema.Util.MP3Player(@"sound1.mp3");
Sistema.Util.MP3Player(@"sound2.mp3");

namespace Sistema.Util.TextToSpeech
{
    public class Player
    {
     static System.Windows.Media.MediaPlayer mp = new System.Windows.Media.MediaPlayer();

    public static void MP3Player(string FileName, bool Async = false)
    {
        if (Async)
        {
            //mp.MediaOpened += new EventHandler(mp_MediaOpened);
            //mp.MediaEnded += new EventHandler(mp_MediaEnded);
            mp.Open(FileName.ToUri());
            //mp.SpeedRatio = .2;
            mp.Play();
        }
        else
        {

            // 03-06-2011
            //using (var ms = System.IO.File.OpenRead(FileName)) // "test.mp3"
            using (var rdr = new Mp3FileReader(FileName))
            using (var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr))
            using (var baStream = new BlockAlignReductionStream(wavStream))
            using (var waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
            {
                //GC.KeepAlive(waveOut);

                waveOut.Init(baStream);
                waveOut.Play();
                //waveOut.PlaybackStopped += new EventHandler(waveOut_PlaybackStopped);
                while (waveOut.PlaybackState == PlaybackState.Playing)
                {
                    System.Threading.Thread.Sleep(100);
                }
            }
        }
    }
}
}

问题是我有时会尝试,它会抛出一个错误:

  

检测到CallbackOnCollectedDelegate   消息:在“NAudio!NAudio.Wave.WaveInterop + WaveCallback :: Invoke”类型的垃圾收集委托上进行了回调。这可能会导致应用程序崩溃,损坏和数据丢失。将委托传递给非托管代码时,托管应用程序必须将它们保持活动状态,直到确保它们永远不会被调用为止。

更新:我试过这个,但错误仍然发生在3次。你可以尝试阅读这段代码:

void play(string FileName)
    {
        var mre = new System.Threading.ManualResetEvent(false); // created unsignaled
        var callbackInfo = WaveCallbackInfo.FunctionCallback(); //lifetime outside using
        using (var rdr = new Mp3FileReader(FileName))
        using (var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr))
        using (var baStream = new BlockAlignReductionStream(wavStream))
        using (var waveOut = new WaveOut(callbackInfo))
        {
            waveOut.Init(baStream);
            waveOut.Play();
            waveOut.PlaybackStopped += (sender, e) => { mre.Set(); };
            mre.WaitOne();
        }
    }

play(@"C:\Users\Tony\AppData\Local\Temp\Sistema\Boa_Tarde(exclamacao).mp3");
play(@"C:\Users\Tony\AppData\Local\Temp\Sistema\Bem_vindo(exclamacao).mp3");
play(@"C:\Users\Tony\AppData\Local\Temp\Sistema\Boa_Tarde(exclamacao).mp3");
play(@"C:\Users\Tony\AppData\Local\Temp\Sistema\Bem_vindo(exclamacao).mp3");

4 个答案:

答案 0 :(得分:1)

“{1}}委托”拥有“WaveOut对象,在WaveCallbackInfo.FunctionCallback() - 块结束时被处理掉并收集垃圾。事实上你的using循环似乎没有提供对代理使用的保护(听起来像本机代码最终调用它,奇怪的架构)。

您可以使用ManualResetEvent来实现等待:

while

然后在你的方法中

// lifetime as long as your application
static WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback();

编辑:本机代码需要某种句柄才能运行。这有效意味着当本机代码仍在运行时句柄永远不会消失

此代码的当前问题是很难判断何时(如果有的话)您需要创建一个新的回调信息对象。您也可以尝试:

var mre = new ManualResetEvent(false); // created unsignaled
using (var rdr = new Mp3FileReader(FileName))
using (var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr))
using (var baStream = new BlockAlignReductionStream(wavStream))
using (var waveOut = new WaveOut(callbackInfo))
{
    waveOut.Init(baStream);
    waveOut.Play();
    waveOut.PlaybackStopped += (sender,e) => { mre.Set(); };
    mre.WaitOne();
}

然后在你的方法中(假设waveOut可以多次初始化):

static WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
static ManualResetEvent waveEvent = new ManualResetEvent(false);

static Player()
{
    waveOut.PlaybackStopped += (sender, e) => { waveEvent.Set(); };
}

答案 1 :(得分:1)

您看起来像是在使用旧版本的NAudio。从1.4开始,Mp3FileReader从Read方法返回PCM格式的音频,不再需要WaveFormatConversionStream和BlockAlignReductionStream。我建议你升级。

此外,我倾向于建议不要使用函数回调,如果你可以避免它们(即WinForms和WPF),因为我发现不同的音频卡驱动程序在不同的和意外的时间调用它们。如果不向非托管代码提供指向托管函数的函数指针,则生活会轻松许多。使用默认的WaveOut构造函数,让它使用Windowed回调。这是一种更可靠的工作方式。有关详情,请参阅NAudio output devices上的博文。

答案 2 :(得分:0)

目前我正在使用此解决方案:WPF MediaPlayer: How to play in sequence, sync?

答案 3 :(得分:0)

我遇到过这个话题,我实际上也在使用NAudio播放TTS mp3。所以我希望它们同步。经过多次尝试,这是我的解决方案。

var rdr = new Mp3FileReader(sFilePath);
var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr);
var waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
waveOut.Init(wavStream);
waveOut.Play();
while (wavStream.Position != wavStream.Length)
    Thread.Sleep(100);

希望它有所帮助,检查waveOut.PlaybackState == PlaybackState.Playing似乎不起作用,因为它一直保持在播放状态。检查波浪流可以解决这个问题但是如果你在播放完音频之前停止它,那么代码将会永远睡眠,请确保你在那里进行检查。