iT邦幫忙

0

C# NET6 NAudio 重複播放問題

  • 分享至 

  • xImage

RePlay 後 回圈內的第二次Play()
waveOut.Init(reader); 這行中斷點 執行後會跳出程式不執行
然後 程式還在運行沒有跳任何錯誤 但是後續的NAudio功能會停住

public void RePlay(int times)
{
    Play();
    int i = 1;
    while (true)
    {
        if (i >= times) break;
        if (waveOut.PlaybackState == PlaybackState.Stopped)
        {
            //這邊Play() 中斷點設 waveOut.Init(reader); 會錯誤
            //Play();
            i++;
        }
        Thread.Sleep(100);
    }
}

public void Play()
{
    DirectoryInfo directory = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Media"));
    if (!File.Exists($"{directory}\\{Mp3Name}"))
    {
        MessageBox.Show("查無檔案,重新擷取媒體庫");
        infos = directory.GetFiles("*.mp3");
        infoIndex = 0;
        dataHolder.Mp3FileName = infos[infoIndex].Name;
        return;
    }
    if (waveOut != null)
    {
        if (waveOut.PlaybackState == PlaybackState.Playing)
        {
            waveOut.Pause();
        }
        else if (waveOut.PlaybackState == PlaybackState.Paused)
        {
            waveOut.Play();
        }
        else if (waveOut.PlaybackState == PlaybackState.Stopped)
        {
            waveOut = new WaveOut();
            reader = new Mp3FileReader($"{directory}\\{Mp3Name}");
            waveOut.Init(reader);
            waveOut.Play();
            waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
        }
        Task.Run(() =>
        {
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                TimeSpan currentTime = reader.CurrentTime;
                TimeSpan totalTime = reader.TotalTime;
                double progress = (double)currentTime.Ticks / totalTime.Ticks;
                int progressBarValue = (int)(progress * 100);
                Mp3Progress = progressBarValue;
                NotifyOfPropertyChange(() => Mp3Progress);
                Thread.Sleep(500);
            }
        });
    }
}
private void WaveOut_PlaybackStopped(object? sender, StoppedEventArgs e)
{
    Mp3Progress = 0;
    NotifyOfPropertyChange(() => Mp3Progress);
    reader.Dispose();
    waveOut.Dispose();
}
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

0
deh
iT邦研究生 1 級 ‧ 2023-03-28 13:31:36

第二次調用 RePlay() 方法時,waveOut.PlaybackState 的值為 Stopped,因此程式將嘗試執行 waveOut.Init(reader); 這一行,但由於 reader 已在 WaveOut_PlaybackStopped() 方法中被釋放,所以會引發異常。

要解決此問題,每次調用 Play() 方法時創建一個新的 Mp3FileReader 物件。確保在 waveOut 物件上調用 Stop() 時,reader 物件不會被釋放。

public void Play()
{
    DirectoryInfo directory = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Media"));
    if (!File.Exists($"{directory}\\{Mp3Name}"))
    {
        MessageBox.Show("查無檔案,重新擷取媒體庫");
        infos = directory.GetFiles("*.mp3");
        infoIndex = 0;
        dataHolder.Mp3FileName = infos[infoIndex].Name;
        return;
    }
    if (waveOut != null)
    {
        if (waveOut.PlaybackState == PlaybackState.Playing)
        {
            waveOut.Pause();
        }
        else if (waveOut.PlaybackState == PlaybackState.Paused)
        {
            waveOut.Play();
        }
        else if (waveOut.PlaybackState == PlaybackState.Stopped)
        {
            waveOut = new WaveOut();
            reader = new Mp3FileReader($"{directory}\\{Mp3Name}");
            waveOut.Init(reader);
            waveOut.Play();
            waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
        }
        Task.Run(() =>
        {
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                TimeSpan currentTime = reader.CurrentTime;
                TimeSpan totalTime = reader.TotalTime;
                double progress = (double)currentTime.Ticks / totalTime.Ticks;
                int progressBarValue = (int)(progress * 100);
                Mp3Progress = progressBarValue;
                NotifyOfPropertyChange(() => Mp3Progress);
                Thread.Sleep(500);
            }
        });
        reader.Dispose(); // 釋放上一個 Mp3FileReader 物件的資源
    }
}

柯柯 iT邦新手 3 級 ‧ 2023-03-28 14:11:55 檢舉

這個測試後 不行
因為我是用排程去call replay 我在想是不是 執行緒的問題

Quartz.cs
public async Task Execute(IJobExecutionContext context)
{
    var dataMap = context.JobDetail.JobDataMap;
    string musicName = dataMap.GetString("Music") ?? "";
    string LoopTimes = dataMap.GetString("LoopTimes") ?? "0";

    await Task.Run(() =>
    {
        if (!NPDay())
        {
            _mainVM.Mp3Name = musicName;
            _mainVM.Replay(int.Parse(LoopTimes));
        }
    });
}

改成這種方式用按鈕是可以正常replay 但排程一樣播完第一次就卡死了

public async Task Replay(int times)
{
    for (int i = 0; i < times; i++)
    {
        await Play();
    }
}

public async Task Play()
{
    .....
    await Task.Run(() =>
    {
        .....
    }
}
deh iT邦研究生 1 級 ‧ 2023-03-28 14:22:12 檢舉

這樣是同步跟非同步混用 講起來有點複雜 等其他大大回答吧/images/emoticon/emoticon06.gif

0
rain_yu
iT邦新手 1 級 ‧ 2023-03-29 11:28:06

可能是因為在第二次執行時,已經有一個 waveOut 實例正在運行,但是在初始化 reader 時出現了錯誤。因此,可以嘗試在第二次執行之前關閉 waveOut 和 reader,並重新初始化。修改後的程式碼如下:

public void RePlay(int times)
{
Play();
int i = 1;
while (true)
{
if (i >= times) break;
if (waveOut.PlaybackState == PlaybackState.Stopped)
{
waveOut.Stop(); // 停止播放
reader.Dispose(); // 關閉 reader
waveOut.Dispose(); // 關閉 waveOut
waveOut = new WaveOut(); // 重新初始化 waveOut
reader = new Mp3FileReader(Path.Combine(Media, Mp3Name)); // 重新初始化 reader
waveOut.Init(reader);
waveOut.Play();
waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
i++;
}
Thread.Sleep(100);
}
}

注意在重新初始化 reader 時,應該使用 Path.Combine(Media, Mp3Name) 來取代 ${directory}\{Mp3Name},因為 Mp3Name 已經是相對於 Media 的路徑了。

我要發表回答

立即登入回答