• F#奇妙游(29):PPTX注释变音频插入页面


    突然想做点有用的东西

    天天学F#,感觉又没有动力了。还是要做点好玩点的、有用的东西才会更加有积极性。因为F#活在.NET平台中,做有用的东西简直太简单了。结合到最近知乎和别的平台一直在叽叽咕咕发视频,我没有怎么做过视频,但是PPT做得不少。我就想为啥不能把PPT直接搞成视频呢?

    这不思路一下子打开了,我平常上的那门课,也可以改成视频教学啊,把PPT一转换,上课就开始播放,我就可以坐在旁边喝茶了,多美啊,小朋友们不懂了慢慢来问就好!就是这样!

    强力论点转视频
    老师无需费舌唇
    课堂上就能播放
    学生不懂慢慢问

    嗯嗯,那就这样吧!

    PPT转视频总共分几步

    视频和PPT一比较,就多个音频讲解,PPT本身就是视觉材料,所以视频就是把PPT和音频结合起来。

    1. 确定每页PPT的讲解词,一般我们就写在注释里,以前我们写得很不走心,现在写详细一点;
    2. 把每页的讲解词变成音频,可以自己去录,大佬的自播放那必须用大佬的声音,萌妹子也是,但我是糙汉子,不用自己录,用TTS;
    3. 把每页的音频插入到PPT中,这样就有了PPT+音频的PPT;
    4. 设置音频为自动播放,播放时隐藏;
    5. PPT导出为MP4文件,打完收工。

    这样一看起来,相当简单。

    F#的实现

    输出讲解词的音频

    #r "nuget: System.Speech, 7.0.0"
    
    open System.Speech.Synthesis
    
    let produceAudio (s:string) (fn:string) = 
        use synthesizer: SpeechSynthesizer = new SpeechSynthesizer()
        synthesizer.SetOutputToWaveFile fn
        synthesizer.Speak s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里只需要用到System.Speech这个库,然后就是SpeechSynthesizer这个类,这个类有一个Speak方法,可以把字符串转换成音频,然后用SetOutputToWaveFile方法把音频输出到文件中。

    这里唯一需要注意的就是use关键词,这个关键词可以让我们在使用完对象后(到表达式体的范围之外)自动释放对象,这样就不用我们自己去释放了。这里的对象必须是实现了IDisposable接口的对象,这个接口只有一个方法Dispose,释放资源,例如文件句柄、数据库连接等等。下面还会反复用到这个功能。

    有一个问题就是一般的wav文件适配性不够好,我们把wav文件转换成mp3文件。

    #r "nuget: NAudio, 2.2.1"
    
    open NAudio.MediaFoundation
    open NAudio.Wave
    let wav2mp3 (source:string) =
        if source.EndsWith ".wav" then
            do MediaFoundationApi.Startup()
            use reader = new WaveFileReader(source)
            let outputFile = source.Replace(".wav", ".mp3")
            MediaFoundationEncoder.EncodeToMp3(reader, outputFile)
            do MediaFoundationApi.Shutdown()
            outputFile
        else
            source
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这用了另外一个包NAudio,这个包功能很多,我们这里用得很少。只需要把wav文件转换成mp3文件就可以了。我们用if构造一个验证文件是否是wav文件,如果是,就用MediaFoundationEncoder这个类把wav文件转换成mp3文件,然后返回mp3文件的路径。

    把音频插入到PPT中

    操作PPT的库有很多,最权威的当然是Open-XML-SDK。但是这个太难了……帮助就来个1000页,不停引用标准ISO/IEC 29500-1:2008,那个标准也不长,只有5560页。包装好的访问PPT的工具部大部分都是收费软件,我不想列名字。

    开源的软件中,我们在Open-XML-SDK的主页上找到了推荐的ShapeCrawler。这里也给找软件包的人提示一个方法,到GitHub上找到一个你需要的软件包,然后看看它的主页,一般都会有推荐的软件包。

    #r "nuget: ShapeCrawler, 0.48.0"
    
    
    open System.IO
    let insertAudio (slide:ShapeCrawler.ISlide) (fn:string) =
        use stream =  File.Open(fn, FileMode.Open)
        let audio = slide.Shapes.AddAudio(10, 10, stream)
        slide
    
    
    let producePPTWithAudioFromNote (inputFile:string) (outputFile:string) = 
        use p = ShapeCrawler.SCPresentation.Open(inputFile)
    
        p.Slides
        |> Seq.iter (fun o -> 
            let notes = o.SDKSlidePart.NotesSlidePart.NotesSlide.InnerText
            let [n; num] = 
                match removeLastNumber notes with
                | Some [note;number] -> [note; number]
                | _ -> [""; "0"]
            let fn = sprintf $"note-{num}.wav" 
            do produceAudio n fn
            let mp3fn = wav2mp3 fn
            if mp3fn = fn then
                failwith $"Error produce mp3 file from {fn}"
            else
                insertAudio o mp3fn |> ignore
            )
    
        p.SaveAs(outputFile)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    上面的函数producePPTWithAudioFromNote就是我们最终想使用的功能,打开一个PPT文件,然后把每页的讲解词转换成音频,然后插入到PPT中,最后保存为新的PPT文件。这里面,打开了文件后,属性Slides就对应了所有的幻灯片页,而每页的SDKSlidePart.NotesSlidePart.NotesSlide.InnerText就是对应的注释内容。我们打开后发现挺好玩的一件事情,每个注释最后都被加了一个数字,这个数字就是这个注释在这个幻灯片页中的序号。

    我们只好又编了一个函数把这个数字提取出来。这里用到了正则表达式,正则表达式在F#中也是很好用的,我们可以用Regex这个类来处理正则表达式。

    open System.Text.RegularExpressions
    let removeLastNumber note = 
        let reg = Regex("(.*?)(\d+)$").Match(note)
        if reg.Success
        then Some (List.tail [for x in reg.Groups -> x.Value])
        else None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样我们就可以把每个注释对应的音频文件命名为note-数字.wav,这样就可以把音频文件和注释对应起来了。

    插入音频的代码也很简单:

    open System.IO
    let insertAudio (slide:ShapeCrawler.ISlide) (fn:string) =
        use stream =  File.Open(fn, FileMode.Open)
        let audio = slide.Shapes.AddAudio(10, 10, stream)
        slide
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里还是用到了use的语法,这样才能在函数结束后自动释放文件句柄。

    尚未实现的功能

    这个玩意我从昨晚开始打主意,没动手。早上开始整,10点不到就整完了,还是很有效率。

    还有几个功能不太满意,找了一下没有轮子,就没有实现。

    1. 自动把音频设置为自动播放;
    2. 自动把音频设置为隐藏;
    3. 自动把PPT导出为MP4文件。

    这几个功能应该可以用Office.Interop来实现,那就要求机器上装了Office,是不是还得看版本,算了,就当活动手臂,自己点吧。

    总结

    1. .NET平台人才济济,个个说话都很好听;
    2. 我们先来把这篇文章的内容做个PPT,然后转成视频吧……
  • 相关阅读:
    多肽介导PEG磷脂——靶向功能材料之DSPE-PEG-RGD/TAT/NGR/APRPG
    网上花店网页代码 html静态花店网页设计制作 dw静态鲜花网页成品模板素材网页 web前端网页设计与制作 div静态网页设计
    小程序游戏、App游戏与H5游戏:三种不同的游戏开发与体验方式
    C++标准模板(STL)- 类型支持 (数值极限,C 数值极限接口)
    【C/C++】2024春晚刘谦春晚魔术步骤模拟+暴力破解
    java毕业生设计沧州雄狮足球俱乐部管理系统计算机源码+系统+mysql+调试部署+lw
    行业洞察 | 谁动了艺术家的奶酪?
    Unittest框架--自动化
    PMO、EMPO、P3O分别是什么
    1.python基础
  • 原文地址:https://blog.csdn.net/withstand/article/details/132712590