[AI] 撰寫.NET打開簡報並呼叫Azure AI語言功能進行簡報

  • 39
  • 0
  • AI
  • 2025-01-09

結合操作PowerPoint及Azure AI Speech進行專業簡報

簡報要自己先作好, 建議可用Gamma, 備忘稿再自己補上(上圖黃框). 有配合動畫念稿的功能, 在稿內寫 § , 就切下一個動畫,
如果有動畫, 但沒寫§ , 就照念稿, 慢慢把動畫切完,
UI畫面自己拉和綁定事件就可以了~以下是.net framework程式碼, 專案屬性記得改x64 (換電腦才能跑), 會自動翻頁念出每頁備忘稿:

using Microsoft.CognitiveServices.Speech;
using Microsoft.Office.Interop.PowerPoint;
using ReadPPT.Properties;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using PP = Microsoft.Office.Interop.PowerPoint;

namespace ReadPPT
{
    public partial class Main : Form
    {
        #region field
        private PP.Application objPPT = null;
        private Presentation objPres = null;
        private bool IsStop = false;

        #endregion
        public Main()
        {
            InitializeComponent();
        }


        #region methods

        private async Task SpeechAsync(string text)
        {
            text = text.Trim();
            if (text.Length == 0) return;
            btnTest.Enabled = false;
            btnRead.Enabled = false;
            try
            {
                var speechConfig = SpeechConfig.FromSubscription(speechKey.Text, speechRegion.Text);
                speechConfig.SpeechSynthesisLanguage = "zh-TW"; //語系選台 灣 國
                speechConfig.SpeechSynthesisVoiceName = lstVoice.Text.Split('(')[0];
                using (var speechSynthesizer = new SpeechSynthesizer(speechConfig))
                using (var speechSynthesisResult = await speechSynthesizer.SpeakTextAsync(text))
                    if (speechSynthesisResult.Reason == ResultReason.Canceled)
                    {
                        var cancellation = SpeechSynthesisCancellationDetails.FromResult(speechSynthesisResult);
                        MessageBox.Show($"CANCELED: Reason={cancellation.Reason}");
                        if (cancellation.Reason == CancellationReason.Error)
                        {
                            MessageBox.Show($@"CANCELED: ErrorCode={cancellation.ErrorCode}
ErrorDetails=[{cancellation.ErrorDetails}]
key 或 region不正確");
                        }
                    }
            }
            finally
            {
                btnTest.Enabled = true;
                btnRead.Enabled = true;
            }
        }

        private void KillPowerPoint()
        {
            try
            {
                using (Process process = Process.Start("taskkill", "/F /IM POWERPNT.exe"))
                    process.WaitForExit();
            }
            catch (Exception ex)
            {
                MessageBox.Show("KillPowerPoint:" + ex.ToString());
            }
        }
        #endregion

        private void Main_Load(object sender, EventArgs e)
        {
            foreach (string str in Settings.Default.Voices.Split(';'))
                lstVoice.Items.Add(str);
            lstVoice.SelectedIndex = 0;
        }

        private void lnkOpen_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            searchPPT.ShowDialog();
        }

        private void searchPPT_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
        {
            txtPPT.Text = searchPPT.FileName;
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            if (!File.Exists(txtPPT.Text))
            {
                MessageBox.Show($"簡報路徑不正確");
                return;
            }
            KillPowerPoint();
            objPPT = new PP.Application();
            objPres = objPPT.Presentations.Open(txtPPT.Text);
        }

        private async void btnTest_Click(object sender, EventArgs e)
        {
            await SpeechAsync(txtTest.Text);
        }

        private async void btnRead_Click(object sender, EventArgs e)
        {
            if (objPres == null) btnOpen_Click(sender, e);
            chkPause.Checked = false;
            IsStop = false;
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            try
            {
                objPres.SlideShowSettings.Run();
                SlideShowView view = null;
                Slide slide = null;
                Task speech = null;
                while (true)
                {
                    #region 到下張Slide
                    #region check stop & pause
                    while (chkPause.Checked)
                    {
                        stopwatch.Stop();
                        await Task.Delay(2000);
                    }
                    if (IsStop) throw new SystemException("stop");
                    stopwatch.Start();
                    #endregion

                    if (speech != null) await speech;
                    speech = null;
                    if (view == null) view = objPPT.SlideShowWindows[1].View;
                    else view.Next();

                    try { slide = view.Slide; }
                    catch { break; }
                    #endregion

                    int idx = slide.SlideIndex;
                    string notesText = slide.NotesPage.Shapes[2].TextFrame.TextRange.Text;
                    var texts = notesText.Split('§');//備忘稿內以§作為切動畫的提示
                    int count = view.GetClickCount();
                    for (int i = 0; i < Math.Max(texts.Length, count); i++)
                    {
                        if (i < texts.Length)//讀出
                        {
                            string text = texts[i];
                            speech = SpeechAsync(text);
                            if (i < texts.Length - 1)
                                await speech;
                        }

                        #region check stop & pause
                        while (chkPause.Checked)
                        {
                            stopwatch.Stop();
                            await Task.Delay(2000);
                        }
                        if (IsStop) throw new SystemException("stop");
                        stopwatch.Start();
                        #endregion

                        if (i < count)//跑動畫
                            view.Next();
                    }
                }
            }
            catch (SystemException) { }
            catch (Exception ex)
            {
                stopwatch.Stop();
                MessageBox.Show(ex.ToString());
            }
            finally
            {
                stopwatch.Stop();
                TimeSpan ts = stopwatch.Elapsed;
                string hour = (ts.Hours > 0 ? $"{ts.Hours} 小時 " : "");
                MessageBox.Show($"共進行 {hour}{ts.Minutes} 分鐘 {ts.Seconds} 秒");
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            if (objPPT == null) return;
            IsStop = true;
            chkPause.Checked = false;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            objPres.Close();
            Marshal.ReleaseComObject(objPres);
            objPres = null;
            objPPT.Quit();
            Marshal.ReleaseComObject(objPPT);
            objPPT = null;
        }
    }
}

作成下拉清單選擇聲音, 由於常改版, 所以寫在Setting (=App.config):

            <setting name="Voices" serializeAs="String">
                <value>zh-TW-HsiaoChenNeural(女,快);zh-TW-YunJheNeural(男,快);zh-TW-HsiaoYuNeural(女,慢)</value>
            </setting>

Taiwan is a country. 臺灣是我的國家