AVFoundation 文字轉語音和音訊錄製 播放

張炫赫發表於2019-04-19

現在你應該對AVFoundation有了比較深入的瞭解,並且對數字媒體的細節也有了一定認識,下面介紹一下 AVFoundation的文字轉語音功能

AVSpeechSynthesizer

開發者可以使用AVFoundation中的AVSpeechSynthesizer類向iOS應用程式中新增類似功能,這個類用來播放一個或多個語音內容,這些語音內容都是名為AVSpeechUtterance的類的例項。具體的實現程式碼如下所示:

let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(AVSpeechUtterance(string: "Hello AV Foundation. How are you?"))
複製程式碼

就兩行程式碼解決了文字轉語音功能。當然很多人會有自己的需求,那麼還需要對具體對話中用到的聲音和語音字串定義屬性。

AVSpeechUtterance

如果我們想定義聲音的語速和語種那麼我們就需要用AVSpeechUtterance類來設定一下自定義的屬性

let synthesizer = AVSpeechSynthesizer()
//如果播放語音內容 需要用到 AVSpeechUtterance 類
let utterance = AVSpeechUtterance(string: "Hello AV Foundation. How are you?")
//定義播放的語音語種
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
//定義播放語音內容的速率
utterance.rate = 0.5
//可在播放特定語句時改變聲音的音調 pitchMultiplier 的允許值一般介於0.5(低音調)和2.0(高音調)之間
utterance.pitchMultiplier = 1.0
//讓語音合成器在播放下一語句之前有短暫時間暫停
utterance.postUtteranceDelay = 0.5
//播放
synthesizer.speak(utterance)
複製程式碼

強調一下AVSpeechUtterance中的voice屬性

Arabic (ar-SA)
Chinese (zh-CN, zh-HK, zh-TW)
Czech (cs-CZ)
Danish (da-DK)
Dutch (nl-BE, nl-NL)
English (en-AU, en-GB, en-IE, en-US, en-ZA)
Finnish (fi-FI)
French (fr-CA, fr-FR)
German (de-DE)
Greek (el-GR)
Hebrew (he-IL)
Hindi (hi-IN)
Hungarian (hu-HU)
Indonesian (id-ID)
Italian (it-IT)
Japanese (ja-JP)
Korean (ko-KR)
Norwegian (no-NO)
Polish (pl-PL)
Portuguese (pt-BR, pt-PT)
Romanian (ro-RO)
Russian (ru-RU)
Slovak (sk-SK)
Spanish (es-ES, es-MX)
Swedish (sv-SE)
Thai (th-TH)
Turkish (tr-TR)
複製程式碼

AVSpeechSynthesizer 常用的delegate

  //開始朗讀
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
    }
    
    //結束朗讀
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        
    }
    
    //暫停朗讀
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) {
        
    }
    
    //繼續朗讀
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didContinue utterance: AVSpeechUtterance) {
        
    }
    
    //將要播放的語音文字
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
        
    }
複製程式碼

常用的文字轉語音功能介紹完了 接下來介紹下常用的音訊錄製和播放功能

所有iOS應用程式都具有音訊會話,無論其是否使用。預設音訊會話來自於以下一些預配置:

  • 啟用了音訊播放,但是音訊錄音未啟用
  • 當使用者切換響鈴/靜音開光到“靜音”模式時,應用程式播放的所有音訊都會消失
  • 當裝置顯示解鎖螢幕時,應用程式的音訊處於靜音狀態
  • 當應用程式播放音訊時,所有後臺播放的音訊都會處於靜音狀態

AVFoundation定義了7種分類來描述應用程式所使用的音訊行為。

分類 作用 是否允許混音 音訊輸入 音訊輸出
Ambient 遊戲 效率應用程式
Solo Ambient (預設) 遊戲 效率應用程式
Playback 音訊和視訊播放器 可選
Record 錄音機 音訊捕捉
Play and Record VoIP 語音聊天 可選
Audio Processing 離線會話和處理
Multi-Route 使用外部硬體的高階A/V應用程式

上述分類所提供的幾種常見行為可以滿足大部分應用程式的需要,不過如果開發者需要更復雜的功能,其中一些分類可以通過使用options和modes方法進一步自定義開發。

音訊會話在應用程式的生命週期中是可以修改的,但通常我們只對其配置一次,就是在應用程式啟動時。


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        let session = AVAudioSession.sharedInstance()
        do {
            /*AVAudioSession.Category:
             .ambient         混合播放,會把後臺播放的音樂混合起來播放
             .soloAmbient     進入後臺,先會把之前的後臺音樂停止,在播放自己的
             .playback        進入後臺的時候播放音樂 不會隨著靜音鍵和螢幕關閉而靜音
             .record          用於需要錄音的應用,除了來電鈴聲,鬧鐘或日曆提醒之外的其它系統聲音都不會被播放
             .playAndRecord   用於既需要播放聲音又需要錄音的應用 該Category提供錄音和播放功能。如果你的應用需要用到iPhone上的聽筒,該category是你唯一的選擇,在該Category下聲音的預設出口為聽筒(在沒有外接裝置的情況下)
             .audioProcessing 主要用於音訊格式處理,一般可以配合AudioUnit進行使用
             .multiRoute      這個類別可以支援多個裝置輸入輸出。
             
             
             上面介紹的這個七大類別,可以認為是設定了七種主場景,而這七類肯定是不能滿足開發者所有的需求的。CoreAudio提供的方法是,首先定下七種的一種基調,然後在進行微調。CoreAudio為每種Category都提供了些許選項來進行微調。在設定完類別後,可以通過 AVAudioSession.CategoryOptions屬性 檢視當前類別設定了哪些選項
             AVAudioSession.CategoryOptions:
             .mixWithOthers     是否可以和其他後臺APP進行混音
             .duckOthers        是否壓低其他APP聲音
             .allowBluetooth    是否支援藍芽耳機
             .defaultToSpeaker  是否預設用擴音聲音
             除此之外,在iOS9還提供了.interruptSpokenAudioAndMixWithOthers iOS10又新加了兩個.allowBluetoothA2DP 和 .allowAirPlay用來支援藍芽A2DP耳機和AirPlay
             
             
             通過上面的七大類別,我們基本覆蓋了常用的主場景,在每個主場景中可以通過Option進行微調。為此CoreAudio提供了七大比較常見微調後的子場景。叫做各個類別的模式。
             AVAudioSession.Mode:
             .default        每種類別預設的就是這個模式,所有要想還原的話,就設定成這個模式。
             .voiceChat      主要用於VoIP場景,此時系統會選擇最佳的輸入裝置,比如插上耳機就使用耳機上的麥克風進行採集。此時有個副作用,他會設定類別的選項為".allowBluetooth"從而支援藍芽耳機。  適用於 .playAndRecord
             .gameChat       適用於遊戲App的採集和播放,比如“GKVoiceChat”物件,一般不需要手動設定 適用於 .playAndRecord
             .videoRecording 錄製視訊時  適用於 .playAndRecord .record
             .measurement    最小系統    適用於 .playAndRecord .record .playback
             .moviePlayback  視訊播放    適用於 .playback
             .videoChat      主要用於視訊通話,比如QQ視訊、FaceTime。時系統也會選擇最佳的輸入裝置,比如插上耳機就使用耳機上的麥克風進行採集並且會設定類別的選項為".allowBluetooth" 和 ".defaultToSpeaker"。 適用於 .playAndRecord
            */
            try session.setCategory(.playback, mode: .default, options: .mixWithOthers)
            
            try session.setActive(true, options: .notifyOthersOnDeactivation)
        } catch let error as Error {
            print(error)
        }

        return true
    }
複製程式碼

使用AVAudionPlayer 播放音訊

AVAudioPlayer構建於Core Audio中的C-based Audio Qucue Serics的最頂層。所以它可以提供所有你在 Audio Oucue Services中所能找到的核心功能, 比如播放、迴圈甚至音訊計量。除非你需要從網路流中播放音訊、需要訪問原始音訊樣本或者需要非常低的時延,否則AVAudioPlayer都能勝任。

建立 AVAudionPlayer


            let fileurl = Bundle.main.url(forResource: "rock", withExtension: "mp3")
            
            let player = try AVAudioPlayer.init(contentsOf: fileurl!)
            
            if player != nil {
                player.prepareToPlay()
            }
複製程式碼

如果返回一個有效的播放例項,建議開發者呼叫 prepareToPlay 方法。這樣做會取得需要的音訊硬體並預載入Audio Queue 的緩衝區。呼叫 prepareToPlay這個動作是可選的,當呼叫Play方法時會隱形啟用,不過在建立時準備播放器可以降低呼叫Play方法和聽到聲音之間的延時

AVAudioPlayer常用屬性

            //設定聲音的大小 範圍為(0到1)
            player?.volume = 0.5
            //設定迴圈次數,如果為負數,就是無限迴圈
            player?.numberOfLoops = -1
            //設定播放進度
            player?.currentTime = 0
            //調整播放率 範圍 0.5 - 2.0
            player?.rate = 0.5
            //允許使用立體聲播放聲音 範圍從 -1.0(極左)到 1.0(極右) 預設值為0.0(居中)
            player?.pan = 1.0

複製程式碼

pausestop方法的區別: pausestop方法在應用程式外面看來實現的功能都是停止當前播放行為,這兩者最主要的區別在底層處理上。掉用stop方法會撤銷掉用prepareToPlay時所做的設定,而呼叫pause方法則不會。

使用AVAudionRecorder 播放音訊

AVAudionRecorder同其於播放音訊的兄弟類一樣,構建於Audio Qucue Serics之上,是一個功能強大且程式碼簡單易用的類。我們可以在Mac機器和iOS裝置上使用這個類來從內建的麥克風錄製視訊,也可從外部音訊裝置進行錄製,比如數字音訊介面或USB麥克風

建立 AVAudionRecorder

        let tmpDir = NSTemporaryDirectory()
        
        let fileURL = URL.init(string: tmpDir)!.appendingPathComponent("memo.caf")
        
                      /*AVEncoderAudioQualityKey:聲音質量 需要的引數是一個列舉 :
                        AVAudioQualityMin    最小的質量
                        AVAudioQualityLow    比較低的質量
                        AVAudioQualityMedium 中間的質量
                        AVAudioQualityHigh   高的質量
                        AVAudioQualityMax    最好的質量
                        AVLinearPCMBitDepthKey:位元率  8 16 32
                    */ 
        let settings = [AVFormatIDKey : [kAudioFormatAppleIMA4],
                        AVSampleRateKey : "44100.0",
                        AVNumberOfChannelsKey : "1",
                        AVEncoderBitDepthHintKey : "16",
                        AVEncoderAudioQualityKey : [kRenderQuality_Medium]
                        ] as [String : Any]
        
        
        do {
            
            try recorder = AVAudioRecorder.init(url: fileURL, settings: settings)
            
            recorder?.delegate = self
            recorder?.isMeteringEnabled = true
            recorder?.prepareToRecord()
        } catch _ {
            
        }
複製程式碼

成功建立AVAudioRecorder 例項,建議呼叫期prepareToRecord 方法,與AVPlayerprepareToPlay方法類似。這個方法執行底層Audio Queue初始化的必要過程。該方法還在URL引數指定的位置一個檔案,將錄製啟動時的延遲降到最小。

在設定字典中指定的鍵值資訊也值得討論一番,開發者可以使用的完整可用鍵資訊在<ACFoundation/AVAudioSettings.h>中定義。大部分的鍵都專門定義了特有的各式,不過下面介紹的都是一些通用的音訊格式

1.音訊格式 AVFormatIDKey 鍵定義了寫入內容的音訊格式,下面的常量都是音訊格式所支援的值:

kAudioFormatLinearPCM               = 'lpcm',
kAudioFormatAC3                     = 'ac-3',
kAudioFormat60958AC3                = 'cac3',
kAudioFormatAppleIMA4               = 'ima4',
kAudioFormatMPEG4AAC                = 'aac ',
kAudioFormatMPEG4CELP               = 'celp',
kAudioFormatMPEG4HVXC               = 'hvxc',
kAudioFormatMPEG4TwinVQ             = 'twvq',
kAudioFormatMACE3                   = 'MAC3',
kAudioFormatMACE6                   = 'MAC6',
kAudioFormatULaw                    = 'ulaw',
kAudioFormatALaw                    = 'alaw',
kAudioFormatQDesign                 = 'QDMC',
kAudioFormatQDesign2                = 'QDM2',
kAudioFormatQUALCOMM                = 'Qclp',
kAudioFormatMPEGLayer1              = '.mp1',
kAudioFormatMPEGLayer2              = '.mp2',
kAudioFormatMPEGLayer3              = '.mp3',
kAudioFormatTimeCode                = 'time',
kAudioFormatMIDIStream              = 'midi',
kAudioFormatParameterValueStream    = 'apvs',
kAudioFormatAppleLossless           = 'alac',
kAudioFormatMPEG4AAC_HE             = 'aach',
kAudioFormatMPEG4AAC_LD             = 'aacl',
kAudioFormatMPEG4AAC_ELD            = 'aace',
kAudioFormatMPEG4AAC_ELD_SBR        = 'aacf',
kAudioFormatMPEG4AAC_ELD_V2         = 'aacg',    
kAudioFormatMPEG4AAC_HE_V2          = 'aacp',
kAudioFormatMPEG4AAC_Spatial        = 'aacs',
kAudioFormatAMR                     = 'samr',
kAudioFormatAMR_WB                  = 'sawb',
kAudioFormatAudible                 = 'AUDB',
kAudioFormatiLBC                    = 'ilbc',
kAudioFormatDVIIntelIMA             = 0x6D730011,
kAudioFormatMicrosoftGSM            = 0x6D730031,
kAudioFormatAES3                    = 'aes3',
kAudioFormatEnhancedAC3             = 'ec-3',
kAudioFormatFLAC                    = 'flac',
kAudioFormatOpus                    = 'opus'
複製程式碼

指定kAudioFormatLinearPCM會將未壓縮的音訊流寫入到檔案中。這種格式的保真度最高,不過相應的檔案也最大。選擇諸如AACApple IMA4的壓縮格式會顯著縮小檔案,還能保證高質量的音訊內容

2.取樣率 AVSampleRateKey用於定義錄音器的取樣率,取樣率定義了對輸入的模擬音訊訊號每一秒內的取樣數。在錄製音訊的質量及最終檔案大小方面,取樣率扮演著至關重要的角色。使用低取樣率,比如8kHz, 會導致粗粒度、 AM廣播型別的錄製效果,不過檔案會比較小,使用44.1kHz的取樣率(CD質量的取樣率)會得到非常高質量的內容,不過檔案就比較大。對於使用什麼取樣率最好 沒有一個明確的定義,不過開發者應該儘量使用標準的取樣率,比如80001600022 05044 100。最終是我們的耳朵在進行判斷。

3.通道數 AVNumberOfChannelsKey用於定義記錄音訊內容的通道數。指定預設值1意味著使用單聲道錄製,設定為2意味著使用立體聲錄製。除非使用外部硬體進行錄製,否則通常應該建立單聲道錄音。

4.指定格式的鍵 處理Linear PCM或壓縮音訊格式時,可以定義一些其他指定格式的鍵。 可在Xcode幫助文件中的AV Foundation Audio Sttings Constants引用中找到完整的列表。

AVAudionRecorder常用的API

open func prepareToRecord() -> Bool    準備錄音, 這個在錄音之前要呼叫一下, 底層可能會給你做一些事

open func record() -> Bool    錄音

@available(iOS 6.0, *)
open func record(atTime time: TimeInterval) -> Bool    在未來的某個時刻開始錄音

open func record(forDuration duration: TimeInterval) -> Bool  錄音, 控制時長

@available(iOS 6.0, *)
open func record(atTime time: TimeInterval, forDuration duration: TimeInterval) -> Bool  在未來的某段時間錄多少時間的音訊

open func pause()  暫停

open func stop()  停止

open func deleteRecording() -> Bool  刪除錄音

open func updateMeters()   更新音量等資料

open func peakPower(forChannel channelNumber: Int) -> Float  最高音量

open func averagePower(forChannel channelNumber: Int) -> Float  平均音量

open var isRecording: Bool { get }   是否正在錄音

open var url: URL { get }  錄音存放的url

open var settings: [String : Any] { get } 錄音格式配置字典

@available(iOS 10.0, *)
open var format: AVAudioFormat { get }  錄音格式配置

unowned(unsafe) open var delegate: AVAudioRecorderDelegate? 代理

open var currentTime: TimeInterval { get }  當前錄音的時長

@available(iOS 6.0, *)
open var deviceCurrentTime: TimeInterval { get }  裝置當前時間

@available(iOS 7.0, *)
open var channelAssignments: [AVAudioSessionChannelDescription]? 每個聲音通道描述陣列

複製程式碼

AVAudioRecorderDelegate

@available(iOS 3.0, *)
optional public func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool)  錄音成功的回撥

@available(iOS 3.0, *)
optional public func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) 錄音發生錯誤的的回撥

@available(iOS, introduced: 2.2, deprecated: 8.0)
optional public func audioRecorderBeginInterruption(_ recorder: AVAudioRecorder)    錄音開始中斷的回撥

@available(iOS, introduced: 6.0, deprecated: 8.0)
optional public func audioRecorderEndInterruption(_ recorder: AVAudioRecorder, withOptions flags: Int) 錄音結束中斷的回撥

複製程式碼

使用Audio Metering

AVAudioRecorderAVAudioPlayer中最強大和最實用的功能就是對音訊進行測量。Audio Metering可讓開發者讀取音訊的平均分貝和峰值分貝資料,並使用這些資料以視覺化方式將聲音的大小呈現給終端使用者。

這兩個類使用的方法都是**averagePowerForChannelpeakPowerForChannel**。兩個方法都會返回一個用於表示聲音分貝(dB)等級的浮點值。這個值的範圍從表示最大分貝的0Db(fullscale)到表示最小分貝或靜音的-160dB。

在可以讀取這些值之前,首先要通過設定錄音器的**isMeteringEnabled = true才可以支援對音訊進行測量。這就使得錄音器可以對捕捉到的音訊樣本進行分貝計算。每當需要讀取值時,首頁需要呼叫updateMeters()**方法才能獲取最新的值。

建立一個新的CADisplayLink 定時器 來實時重新整理 獲取分貝 定時器方法內部實現:

       recorder?.updateMeters()
       var level = 0.0
        
        //獲取分貝
        let peakPower = recorder?.peakPower(forChannel: 0)
        
        //設定一個最低分貝
        let minDecibels:Float = -60
        
        if peakPower < minDecibels {
            level = 0.0
        }else if peakPower >= 0.0 {
            level = 1.0
        }else {
            let root            = 2.0;
            //最小分貝的波形幅度值 公式: db 分貝 A 波形幅度值   dB=20∗log(A)→A=pow(10,(db/20.0))
            let minAmp          = powf(10.0, 0.05 * minDecibels);
            let inverseAmpRange = 1.0 / (1.0 - minAmp);
            //實時獲取到波形幅度值 公式同上
            let amp             = powf(10.0, 0.05 * peakPower);
            //(實時資料 - 最小資料)/(最大資料 - 最小資料)  應該計算的一個比例值吧
            let adjAmp          = (amp - minAmp) * inverseAmpRange;
            level = powf(adjAmp, 1.0 / root);
        }
複製程式碼

本章我們見識了AVFoundation的音訊類所能提供的強大功能。AVAudionSession作為應用程式和更在的iOS音訊環境的中間環節,可通過使用分類在語義上定義應用程式的行為,並且提供工具來觀察中斷和線路變化。AVAudionPlayerAVAudioRecorder提供了一種簡單但功能強大的介面,用於處理音訊的播放和錄製。這兩個類都構建與Core Audio框架之上,但為在應用程式中實現音訊錄製和播放提供了一種更便捷的方法。

相關文章