C# 在同步播放3个MP3声音文件时,我收到一个奇怪的异常
我想这样做:C# 在同步播放3个MP3声音文件时,我收到一个奇怪的异常,c#,wpf,naudio,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(); p
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");
WaveOut
对象,它“拥有”WaveCallbackInfo.FunctionCallback()委托,正在使用-block在结束时进行处理和垃圾收集。看起来您的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();
}
编辑:本机代码需要某种句柄才能运行。这实际上意味着,当本机代码仍在运行时,句柄永远不会消失
这段代码当前的问题是很难判断何时(如果有)需要创建一个新的回调信息对象。您也可以尝试:
static WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
static ManualResetEvent waveEvent = new ManualResetEvent(false);
static Player()
{
waveOut.PlaybackStopped += (sender, e) => { waveEvent.Set(); };
}
然后在方法中(假设waveOut可以多次初始化):
你看起来像是在使用老版本的NAudio。由于1.4,Mp3FileReader从Read方法返回PCM格式的音频,因此不需要波形转换流和BlockAlignReductionStream。我建议你升级
此外,如果可以避免使用函数回调(例如WinForms和WPF),我倾向于建议不要使用函数回调,因为我发现不同的声卡驱动程序在不同和意外的时间调用它们。如果不给非托管代码一个指向托管函数的函数指针,生活就会简单得多。使用默认的WaveOut构造函数,让它使用窗口回调。这是一种更可靠的工作方式。有关更多信息,请参阅我的博客文章 现在我正在使用这个解决方案:我遇到了这个主题,我实际上也在使用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。播放似乎不起作用,因为它一直保持播放状态。检查wave stream可以做到这一点,但请记住,如果您在wave stream播放完音频之前停止它,代码将永远休眠,请确保您在那里进行了一些检查。非常感谢!我第一次尝试了你的代码,它运行正常,但是如果我再次单击按钮,第一个mp3播放,然后第二个抛出问题中提到的异常。@托尼:我一直在阅读NAudio库,在WaveCallbackInfo
上发现了一个Disconnect
方法。你能试试我更新的代码吗?是的,我用你的例子再试了一次,但现在出现了错误:“NAudio.Wave.WaveCallbackInfo”不包含“Disconnect”的定义,并且找不到接受第一个参数类型为“NAudio.Wave.WaveCallbackInfo”的扩展方法“Disconnect”(是否缺少using指令或程序集引用?)@托尼:我在他们的源代码管理中看到的代码似乎与发行版不匹配。我发布了一些新代码。我相信如果waveOut
被处理掉,它会破坏callbackInfo
。你需要一个静态waveOut
的策略。很抱歉,我无法让它工作。你能试试我上次遇到的吗hod并查看错误,看看是否有解决方案?我正在尝试的另一件事是在C#上使用MediaPlayer,但它只播放异步。我如何使用MediaPlayer来播放同步?谢谢你的回答。你能用一个线程安全的函数(如Playm3(字符串文件名))生成一个C#WPF代码吗,播放并在播放完成后返回。此函数将按顺序调用多次,不能返回问题中提到的奇怪异常。我将调用Playm3(“file1.mp3”);Playm3(“file2.mp3”);Playm3(“file3.mp3”);Playm3(“file4.mp3”);我没有在WPF中为NAudio生成阻塞播放示例的原因是,这是一种非常糟糕的用户体验。通常,你将WaveOut保存在一个私有变量中,这样你的GUI就可以保持响应,并且可以随时取消播放。你应该进行阻塞播放。如果我想不阻塞UI,我可以将播放包装在一个n中电子战线程。
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);