При одновременном воспроизведении 3-х звуковых файлов MP3 я получаю странное исключение - PullRequest
1 голос
/ 03 июня 2011

Я хотел бы сделать это:

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 ]

1 голос
/ 04 июня 2011

Похоже, вы используете старую версию NAudio.Начиная с версии 1.4, Mp3FileReader возвращает аудио в формате PCM из метода Read, устраняя необходимость в WaveFormatConversionStream и BlockAlignReductionStream.Я рекомендую вам обновить.

Кроме того, я не советую использовать обратные вызовы функций, если вы можете их избежать (например, WinForms и WPF), поскольку я обнаружил, что разные драйверы звуковых карт вызывают их в разное и неожиданное время,Жизнь становится намного проще, если вы не дадите неуправляемому коду указатель на управляемую функцию.Используйте конструктор WaveOut по умолчанию и позвольте ему использовать обратные вызовы Windowed.Это гораздо более надежный способ работы.См. Мой блог на Устройства вывода NAudio для получения дополнительной информации.

1 голос
/ 03 июня 2011

Объект 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();
}

Редактировать : нативный код требует какого-либо дескриптора для запуска. Это фактически означает, что дескриптор никогда не может исчезнуть , пока собственный код все еще работает.

В настоящее время проблема с этим кодом заключается в том, что трудно сказать, когда (если вообще когда-либо) вам потребуется создать НОВЫЙ информационный объект обратного вызова. Вы также можете попробовать:

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();
}
0 голосов
/ 02 июня 2013

Я сталкиваюсь с этой темой, где я на самом деле использую 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, похоже, не работает, поскольку он продолжает оставаться в состоянии воспроизведения.Проверка волнового потока делает свое дело, но имейте в виду, что если вы остановите его до того, как закончится воспроизведение аудио, код будет спать вечно, обязательно сделайте некоторую проверку там.

0 голосов
/ 06 июня 2011

На данный момент я использую это решение: WPF MediaPlayer: как играть в последовательности, синхронизация?

...