1

我想这样做:

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

4 回答 4

1

WaveOut“拥有”委托的对象将在-blockWaveCallbackInfo.FunctionCallback()的末尾被处理并收集垃圾。using看来您的while循环在事后无法防止使用委托(听起来像本机代码最终调用它,奇怪的架构)。

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

// 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 可以多次初始化):

waveEvent.Reset();
using (var rdr = new Mp3FileReader(FileName))
using (var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr))
using (var baStream = new BlockAlignReductionStream(wavStream))
{
    waveOut.Init(baStream);
    waveOut.Play();

    waveEvent.WaitOne();
}
于 2011-06-03T19:10:19.423 回答
1

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

此外,我倾向于建议不要使用函数回调,如果可以避免它们(即 WinForms 和 WPF),因为我发现不同的声卡驱动程序会在不同的和意外的时间调用它们。当您不为非托管代码提供指向托管函数的函数指针时,生活会容易得多。使用默认 WaveOut 构造函数并让它使用窗口回调。这是一种更可靠的工作方式。有关更多信息,请参阅我关于NAudio 输出设备的博客文章。

于 2011-06-03T20:39:42.740 回答
0

现在我正在使用这个解决方案:WPF MediaPlayer:如何按顺序播放,同步?

于 2011-06-06T14:08:51.490 回答
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 似乎不起作用,因为它一直保持在 Playing 状态。检查波流可以解决问题,但请记住,如果您在它完成播放音频之前停止它,代码将永远休眠,请确保您在那里进行一些检查。

于 2013-06-02T04:54:26.350 回答