日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python 从零开始制作自己的声音 - wave模块读写wav文件详解

發布時間:2023/12/14 python 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python 从零开始制作自己的声音 - wave模块读写wav文件详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章介紹wave模塊的用法、生成一定頻率音頻的算法實現。

目錄

    • wave模塊
        • wave.open() 函數
        • Wave_read 對象
        • Wave_write 對象
        • 初步: 拼接音頻
    • 初次實現
    • 再次實現

wave模塊

wave模塊提供了一個處理 wav 聲音格式的便利接口, 可獲取wav文件頭信息, 從文件讀取數據, 也可直接將bytes格式的數據寫入wav文件。

wave.open() 函數

wave.open(file, mode=None)
函數接收兩個參數, file為文件名或文件對象, mode可取"r",“rb”,“w”,“wb"四個值, 其中"r"和"rb”, "w"和"wb"效果完全相同, 如下:

>>> wave.open('音樂.wav','r') <wave.Wave_read object at 0x0355E810> >>> wave.open('音樂.wav','rb') <wave.Wave_read object at 0x0358C5D0> >>> wave.open('test.wav','w') <wave.Wave_write object at 0x0355E810> >>> wave.open('test.wav','wb') <wave.Wave_write object at 0x0358C5B0>

以讀模式打開的文件會返回Wave_read 對象, 寫模式打開時會返回Wave_write 對象。

Wave_read 對象

wave文件由許多幀組成, 每一幀長度為1或2字節。
Wave_read.getnchannels()
返回聲道數量(1 為單聲道,2 為立體聲)

Wave_read.getsampwidth()
返回采樣字節長度 (每一幀的字節長度)。

Wave_read.getframerate()
返回采樣頻率。

Wave_read.getnframes()
返回音頻總幀數。

Wave_read.getcomptype()和Wave_read.getcompname()
返回壓縮類型。

Wave_read.readframes(n)
讀取并返回以 bytes 對象表示的最多 n 幀音頻。

Wave_read.tell()
返回當前文件指針位置。

Wave_read.setpos(pos)
設置文件指針到指定位置。

Wave_write 對象

Wave_write.setnchannels(n)
設置聲道數。

Wave_write.setsampwidth(n)
設置采樣字節長度為 n。

Wave_write.setframerate(n)
設置采樣頻率為 n。

Wave_write.setnframes(n)
設置總幀數為 n。 如果與之后實際寫入的幀數不一致此值將會被更改( 如果輸出流不可查找則此更改嘗試將引發錯誤)。

Wave_write.setcomptype(type, name)
設置壓縮格式。目前只支持 NONE 即無壓縮格式。

Wave_write.tell()
返回當前文件指針,其指針含義和 Wave_read.tell() 以及 Wave_read.setpos() 是一致的。

Wave_write.writeframesraw(data)
寫入bytes格式的音頻數據但不更新 nframes。data的長度必須不大于setnframes設定的總幀數, 否則會引發錯誤。

Wave_write.writeframes(data)
寫入bytes格式的音頻幀并確保 nframes 是正確的。 這里data的長度可以大于setnframes設定的總幀數。

Wave_write.close()
確保 nframes 是正確的,并在文件被 wave 打開時關閉它。 此方法會在對象收集時被調用。 如果輸出流不可查找且 nframes 與實際寫入的幀數不匹配時引發異常。

初步: 拼接音頻

程序先將兩段音頻中的數據讀入data1和data2中, 再將讀取的數據寫入result.wav。兩段音頻的采樣頻率、采樣字節長度需要一致。

import wavesampwidth = 1 framerate = 22050with wave.open('音樂.wav','rb') as f1:sampwidth = f1.getsampwidth()framerate = f1.getframerate()nframes1=f1.getnframes()data1=f1.readframes(nframes1)with wave.open('音樂.wav','rb') as f2:nframes2=f2.getnframes()data2=f2.readframes(nframes2)with wave.open('result.wav','wb') as fw:fw.setnchannels(1)fw.setsampwidth(sampwidth)fw.setframerate(framerate)#fw.setnframes(nframes1+nframes2)fw.writeframesraw(data1)fw.writeframesraw(data2)

初次實現

現在開始制作自己的聲音。程序生成一段頻率為200Hz, 長度為1.8秒的蜂鳴聲。

import wave from winsound import PlaySound,SND_FILENAMEfile = 'test.wav' len_= 1.8 # 秒 frequency = 200 sampwidth = 1 #每一幀寬度(采樣字節長度) framerate = 22050 # 采樣頻率 (越大音質越好) length = int(framerate * len_ * sampwidth) para = [0b00000000]*(framerate//frequency//2*sampwidth)\+[0b11111111]*(framerate//frequency//2*sampwidth) # 音頻的一小段 data=bytes(para)# 生成wav文件 with wave.open(file,'wb') as f:f.setnchannels(1)f.setsampwidth(sampwidth)f.setframerate(framerate)#f.setnframes(length)f.writeframes(data * (length // len(data))) # 調用writeframes函數無需預先設置nframes, 但調用writeframesraw函數需要PlaySound(file,SND_FILENAME) # 播放生成的wav

再次實現

上述程序有缺點, 如para中0b0000000和0b11111111的長度是整數且相同, 導致生成的聲音頻率不精確; 缺乏對正弦波的支持等。
這里增加了生成器, 用于生成音頻幀。

import wave,math from winsound import PlaySound,SND_FILENAMEdef generate(T,total,volume,sine=False):# T: 周期, total 總長度, 都以幀為單位if not sine:h = T / 2for i in range(total):if i % T >= h:yield volumeelse:yield 0else:# 計算方法: sin 的 T = 2*pi / ww = 2 * math.pi / T; r = volume / 2for i in range(total):yield int(math.sin(w * i) * r + r)file = 'test.wav' len_= 1.8 # 秒 frequency = 200 sampwidth = 1 framerate = 22050 sine=True volume = 255 # 音量, 0 - 255 data = bytes(generate(framerate / frequency, int(framerate*len_),volume,sine)) # bytes能接收0-255整數型的迭代器with wave.open(file,'wb') as f:f.setnchannels(1)f.setsampwidth(sampwidth)f.setframerate(framerate)f.writeframes(data)PlaySound(file,SND_FILENAME)

與Python內置的音頻合成對比:

import winsound # Beep(freq,duration),參數分別是頻率和毫秒為單位的持續時間 winsound.Beep(200,1800)

發現, 前述程序很好地仿真了調用內置的Beep函數發聲。
但音質有區別, 這是采樣字節長度為1(只有8位)導致的, 還需要加大采樣字節長度。
最終的程序如下:

import wave,math,struct from winsound import PlaySound,SND_FILENAME def generate(T,total,volume,sampwidth,sine=False):# T: 周期, total 總長度, 以幀為單位volume = min(volume * 2**(sampwidth*8),2**(sampwidth*8) - 1)if not sine:h = T / 2for i in range(total):if i % T >= h:yield volumeelse:yield 0else:w = 2 * math.pi / T; r = volume / 2for i in range(total):# T = 2*pi / wyield int(math.sin(w * i) * r + r)file = 'test.wav' len_= 1.8 # 秒 frequency = 200 sampwidth = 2 framerate = 22050 sine=True volume = 255 # 8位的wav文件的一幀是無符號8位整數, 而16位的一幀是有符號的整數(-32768至32767)。 if sampwidth == 1: # 8位lst = list(generate(framerate / frequency, int(framerate*len_),volume,sampwidth,sine))data = bytes(lst) elif sampwidth == 2:data = b'' # 16位lst = list(generate(framerate/frequency,int(framerate*len_),volume,sampwidth,sine))for digit in lst:data += struct.pack('<h',digit - 32768)with wave.open(file,'wb') as f:# --snip-- (省略)PlaySound(file,SND_FILENAME)

使用matplotlib庫查看生成的聲波:

import matplotlib.pyplot as plt # --snip-- plt.plot(range(len(lst)),lst) plt.show()


寫在最后:
程序還可再做改進, 例如模擬各種樂器的音色, 也就是細微改變生成的聲波形狀。如果程序中加入共振峰, 還可實現簡單的語音合成?
但是, Windows系統已經自帶了語音合成, 何必再開發一個呢?
下篇: Python 調用Windows內置的語音合成,并生成wav文件

總結

以上是生活随笔為你收集整理的Python 从零开始制作自己的声音 - wave模块读写wav文件详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。