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

古いバージョンの NAudio を使用しているようです。1.4 以降、Mp3FileReader は Read メソッドから PCM 形式でオーディオを返し、WaveFormatConversionStream と BlockAlignReductionStream の必要性を取り除きます。アップグレードすることをお勧めします。

また、関数コールバック (WinForms や WPF) を避けることができる場合は、使用しないようにアドバイスする傾向があります。これは、さまざまなオーディオ カード ドライバーがさまざまな予期しないタイミングで関数コールバックを呼び出すことを発見したためです。アンマネージ コードにマネージ関数への関数ポインターを指定しないと、作業がずっと楽になります。デフォルトの WaveOut コンストラクターを使用して、ウィンドウ化されたコールバックを使用できるようにします。これは、はるかに信頼性の高い作業方法です。詳細については、 NAudio 出力デバイスに関する私のブログ投稿を参照してください。

于 2011-06-03T20:39:42.740 に答える
1

WaveOutデリゲートを「所有」するオブジェクトは、ブロックのWaveCallbackInfo.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();
}

編集: ネイティブ コードを実行するには、何らかのハンドルが必要です。これが実質的に意味することは、ネイティブ コードがまだ実行されている間はハンドルが消えないということです。

このコードの現在の問題は、NEW コールバック情報オブジェクトをいつ作成する必要があるかを判断するのが難しいことです。あなたも試してみてください:

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 に答える
0

今のところ、私はこのソリューションを使用しています: WPF MediaPlayer: How to play in sequence, sync?

于 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 に答える