iOS PCM轉(zhuǎn)成WAV

最近做錄音的時(shí)候?yàn)榱撕桶沧慷私y(tǒng)一离唐,需要將pcm格式的錄音文件轉(zhuǎn)為wav格式的文件族沃。

背景知識(shí)

PCM

PCM (Pulse Code Modulation----脈沖調(diào)制錄音)摹量。所謂PCM錄音就是 將聲音等模擬信號(hào)變成符號(hào)化的脈沖列泰演,再予以記錄。PCM信號(hào)時(shí)有[1]止剖、[0]等結(jié)構(gòu)符號(hào)構(gòu)成的數(shù)字信號(hào)亮靴,而未經(jīng)過(guò)任何編碼和壓縮處理馍盟。與模擬信號(hào)比,它不易受傳送系統(tǒng)的雜波及失真的影響茧吊。動(dòng)態(tài)范圍寬贞岭,可得到音質(zhì)想到好的效果。PCM是無(wú)損的音頻格式搓侄。

WAV

WAV全稱(chēng)WAVE, .wav是其拓展名瞄桨,它是一種無(wú)損的音頻格式文件,WAV符合RIFF(Resource Interchange File Format)規(guī)范讶踪。所有的WAV都有一個(gè)文件頭芯侥,這個(gè)文件頭是音頻流的編碼參數(shù)。WAV對(duì)音頻的編碼沒(méi)有硬性規(guī)定乳讥,除了PCM之外柱查,還有幾乎所有支持ACM規(guī)范的編碼都可以為WAV音頻流進(jìn)行編碼。

PCM和WAV的關(guān)系

PCM是無(wú)損WAV文件中音頻數(shù)據(jù)的一種編碼方式云石,pcm加上文件頭就可以轉(zhuǎn)為wav格式唉工,但wav還可以用其他方式編碼。

Swift代碼如下:


class ConvertPcmToWaveTool {
   
   // 緩存的音頻大小
   private var mBufferSize = 44100 * 1 * 2
   
   // 采樣率
   private var mSampleRate = 44100
   
   // 聲道
   private var mChannel = 1
   
   func pcmToWav(inFileName: String, outFileName: String) {
       
       guard let input: URL = URL(string: inFileName) else { return }
       guard let output: URL = URL(string: outFileName) else { return }
       
       var totalAudioLen: Int64
       var totalDataLen: Int64
       let longSampleRate: Int64 = Int64(mSampleRate)
       
       let channels = 1
       let byteRate: Int64 = Int64(16 * mSampleRate * channels / 8)
       
       totalAudioLen = Int64(inFileName.getFileSize())
       print("---------totalAudioLen---------\(totalAudioLen)")
       totalDataLen = totalAudioLen + 36
       
       writewaveFileHeader(output: output, totalAudioLen: totalAudioLen, totalDataLen: totalDataLen, longSampleRate: longSampleRate, channels: channels, byteRate: byteRate)
       
       guard let readHandler = try? FileHandle(forReadingFrom: input) else { return }
       guard let writeHandler = try? FileHandle(forWritingTo: output) else { return }
       
        // 文件頭占44字節(jié)留晚,偏移后才寫(xiě)入pcm數(shù)據(jù)
       writeHandler.seek(toFileOffset: 44)
       
       let data = readHandler.readDataToEndOfFile()
       
       writeHandler.write(data)
   
       print("---getFileSize----\(outFileName.getFileSize())---")
   }
   
   func writewaveFileHeader(output: URL, totalAudioLen: Int64, totalDataLen: Int64, longSampleRate: Int64, channels: Int, byteRate: Int64) {
       var header: [UInt8] = Array(repeating: 0, count: 44)
       
       // RIFF/WAVE header
       header[0] = UInt8(ascii: "R")
       header[1] = UInt8(ascii: "I")
       header[2] = UInt8(ascii: "F")
       header[3] = UInt8(ascii: "F")
       header[4] = (UInt8)(totalDataLen & 0xff)
       header[5] = (UInt8)((totalDataLen >> 8) & 0xff)
       header[6] = (UInt8)((totalDataLen >> 16) & 0xff)
       header[7] = (UInt8)((totalDataLen >> 24) & 0xff)
       
       //WAVE
       header[8] = UInt8(ascii: "W")
       header[9] = UInt8(ascii: "A")
       header[10] = UInt8(ascii: "V")
       header[11] = UInt8(ascii: "E")
       
       // 'fmt' chunk
       header[12] = UInt8(ascii: "f")
       header[13] = UInt8(ascii: "m")
       header[14] = UInt8(ascii: "t")
       header[15] = UInt8(ascii: " ")
       
       // 4 bytes: size of 'fmt ' chunk
       header[16] = 16
       header[17] = 0
       header[18] = 0
       header[19] = 0
       
       // format = 1
       header[20] = 1
       header[21] = 0
       header[22] = UInt8(channels)
       header[23] = 0
       
       header[24] = (UInt8)(longSampleRate & 0xff)
       header[25] = (UInt8)((longSampleRate >> 8) & 0xff)
       header[26] = (UInt8)((longSampleRate >> 16) & 0xff)
       header[27] = (UInt8)((longSampleRate >> 24) & 0xff)
       
       header[28] = (UInt8)(byteRate & 0xff)
       header[29] = (UInt8)((byteRate >> 8) & 0xff)
       header[30] = (UInt8)((byteRate >> 16) & 0xff)
       header[31] = (UInt8)((byteRate >> 24) & 0xff)
       
       // block align
       header[32] = UInt8(2 * 16 / 8)
       header[33] = 0
       
       // bits per sample
       header[34] = 16
       header[35] = 0
       
       //data
       header[36] = UInt8(ascii: "d")
       header[37] = UInt8(ascii: "a")
       header[38] = UInt8(ascii: "t")
       header[39] = UInt8(ascii: "a")
       header[40] = UInt8(totalAudioLen & 0xff)
       header[41] = UInt8((totalAudioLen >> 8) & 0xff)
       header[42] = UInt8((totalAudioLen >> 16) & 0xff)
       header[43] = UInt8((totalAudioLen >> 24) & 0xff)
       
       guard let writeHandler = try? FileHandle(forWritingTo: output) else { return }
       let data = Data(bytes: header)
       writeHandler.write(data)
       
   }

}

extension String {
   
   /// 計(jì)算文件夾大小(有單文件計(jì)算)
   func getFileSize() -> UInt64  {
       var size: UInt64 = 0
       let fileManager = FileManager.default
       var isDir: ObjCBool = false
       let isExists = fileManager.fileExists(atPath: self, isDirectory: &isDir)
       // 判斷文件存在
       if isExists {
           // 是否為文件夾
           if isDir.boolValue {
               // 迭代器 存放文件夾下的所有文件名
               let enumerator = fileManager.enumerator(atPath: self)
               for subPath in enumerator! {
                   // 獲得全路徑
                   let fullPath = self.appending("/\(subPath)")
                   do {
                       let attr = try fileManager.attributesOfItem(atPath: fullPath)
                       size += attr[FileAttributeKey.size] as! UInt64
                   } catch  {
                       print("error :\(error)")
                   }
               }
           } else {    // 單文件
               do {
                   let attr = try fileManager.attributesOfItem(atPath: self)
                   size += attr[FileAttributeKey.size] as! UInt64
                   
               } catch  {
                   print("error :\(error)")
               }
           }
       }
       return size
   }
}


如果處理正常酵紫,打印出來(lái)最終輸出的wav文件大小比pcm文件大44字節(jié)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末告嘲,一起剝皮案震驚了整個(gè)濱河市错维,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌橄唬,老刑警劉巖赋焕,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異仰楚,居然都是意外死亡隆判,警方通過(guò)查閱死者的電腦和手機(jī)犬庇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)侨嘀,“玉大人臭挽,你說(shuō)我怎么就攤上這事∫螅” “怎么了欢峰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)涨共。 經(jīng)常有香客問(wèn)我纽帖,道長(zhǎng),這世上最難降的妖魔是什么举反? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任懊直,我火速辦了婚禮,結(jié)果婚禮上火鼻,老公的妹妹穿的比我還像新娘室囊。我一直安慰自己,他們只是感情好魁索,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布波俄。 她就那樣靜靜地躺著,像睡著了一般蛾默。 火紅的嫁衣襯著肌膚如雪懦铺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天支鸡,我揣著相機(jī)與錄音冬念,去河邊找鬼。 笑死牧挣,一個(gè)胖子當(dāng)著我的面吹牛急前,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瀑构,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼裆针,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了寺晌?” 一聲冷哼從身側(cè)響起世吨,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呻征,沒(méi)想到半個(gè)月后耘婚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陆赋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年沐祷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嚷闭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赖临,死狀恐怖胞锰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兢榨,我是刑警寧澤胜蛉,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站色乾,受9級(jí)特大地震影響誊册,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暖璧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一案怯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧澎办,春花似錦嘲碱、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至琅绅,卻和暖如春扶欣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背千扶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工料祠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澎羞。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓髓绽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親妆绞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子顺呕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言: 記載資料多為網(wǎng)絡(luò)搜集,侵刪括饶。 根據(jù)最近接觸的整機(jī)項(xiàng)目做了一些整機(jī)音頻相關(guān)基礎(chǔ)知識(shí)的總結(jié)株茶,如有不足或表述問(wèn)題...
    Gawain_Knowknow閱讀 8,154評(píng)論 0 4
  • 前言 本篇開(kāi)始講解在Android平臺(tái)上進(jìn)行的音頻編輯開(kāi)發(fā),首先需要對(duì)音頻相關(guān)概念有基礎(chǔ)的認(rèn)識(shí)巷帝。所以本篇要講解以下...
    Ihesong閱讀 7,773評(píng)論 2 18
  • 1忌卤、最近在項(xiàng)目遇到上傳音頻到服務(wù)端處理錯(cuò)誤問(wèn)題扫夜;當(dāng)然一般情況下如果雙端商量好格式楞泼,通過(guò)iOS系統(tǒng)的錄音框架驰徊,上傳A...
    浮海_2015閱讀 1,446評(píng)論 4 2
  • 文/金紫緣(南京) 清明的雨 祭祀的淚 紙錢(qián)的青煙飄渺 帶走無(wú)盡的追思 墳前的祈禱 化作佛前的蓮花 為你常開(kāi)祈福 ...
    金紫緣閱讀 283評(píng)論 1 5
  • 知道你們都有高顏值棍厂,但是了解自己的體型,越穿越好看呀超陆! 生活中有很多胖子牺弹,也有很多瘦子,但是胖的各不相同时呀,瘦的也各...
    一道美學(xué)閱讀 988評(píng)論 0 7