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

这篇具有很好参考价值的文章主要介绍了Python 从零开始制作自己的声音 - wave模块读写wav文件详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

计算机经常被用于处理音频这种真实世界中的数据。声音经过采样,量化和编码后,存储在音频文件,如wav文件中。
文章首先介绍wave模块的基础用法; 再通过生成一定频率声波的算法实现,来深入讲解wave库的使用。

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('test.wav','w')
<wave.Wave_write object at 0x0355E810>

以读模式打开的文件会返回Wave_read 对象, 写模式打开时会返回Wave_write 对象。

Wave_read

Wave_read 对象通过wave.open() 函数创建。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 对象也通过wave.open() 函数创建。

Wave_write.setnchannels(n):设置声道数。

Wave_write.setsampwidth(n):设置采样字节长度为 n。

Wave_write.setframerate(n):设置采样频率为 n。

Wave_write.setnframes(n):设置总帧数为 n。(后来发现调用writeframes()时,wave模块会自动更新总帧数,实际上不需要调用这个函数)

Wave_write.setcomptype(type, name):设置压缩格式。(目前只支持 NONE 即无压缩格式。)

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

Wave_write.writeframes(data)(或writeframesraw(data)
写入bytes格式的音频帧,并更新 nframes。

Wave_write.close()
确保 nframes 是正确的,并在文件被 wave 打开时关闭它。 此方法会在对象收集时被调用。 如果输出流不可查找且 nframes 与实际写入的帧数不匹配时引发异常。

初步: 拼接音频

程序先将两段音频中的数据读入data1data2中,再将读取的数据拼接,写入result.wav。注意两段音频的采样频率、采样字节长度需要一致。

import wave

sampwidth = 1
framerate = 22050

with wave.open('音乐1.wav','rb') as f1:
    sampwidth = f1.getsampwidth()
    framerate = f1.getframerate()
    nframes1=f1.getnframes()
    data1=f1.readframes(nframes1)

with wave.open('音乐2.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_FILENAME

file = '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)))

PlaySound(file,SND_FILENAME) # 播放生成的wav

再次实现

上述程序生成的是方波,并有一些缺陷,如para0b00000000b11111111的长度是整数且相同,导致生成的声音频率不精确,等等。
这里合成一段200Hz,长度为1.8秒的正弦波。

import wave,math
from winsound import PlaySound,SND_FILENAME

def generate(T,total,volume,sine=False):
    # T: 周期, total 总长度, 都以帧为单位
    if not sine:
        h = T / 2
        for i in range(total):
            if i % T >= h:
                yield volume
            else:
                yield 0
    else:
        # 计算方法: sin 的 T = 2*pi / w
        w = 2 * math.pi  / T; r = volume / 2
        for 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 / 2
        for i in range(total):
            if i % T >= h:
                yield volume
            else:
                yield 0
    else:
        w = 2 * math.pi  / T; r = volume / 2
        for i in range(total):
            # T = 2*pi / w
            yield 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()

Python 从零开始制作自己的声音 - wave模块读写wav文件详解
写在最后:
程序还可再做改进, 例如模拟各种乐器的音色, 也就是细微改变生成的声波形状。如果程序中加入共振峰, 还可实现简单的语音合成?
但是, Windows系统已经自带了语音合成, 何必再开发一个呢?
下篇: Python 调用Windows内置的语音合成,并生成wav文件文章来源地址https://www.toymoban.com/news/detail-408294.html

到了这里,关于Python 从零开始制作自己的声音 - wave模块读写wav文件详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python爬虫进阶(1),Django+Selenium+Mysql+SimpleUI,从零开始搭建自己的爬虫后台管理系统

    如果爬虫做完的话都会发现每个文件要么保存到csv或者是其他格式的文件中,这样做多少会有些麻烦,所以需要将这些内容保存起来方便自己管理和查看内容。 相对于flask而言Django有着相对成熟的一个后台管理系统配合上其他一些插件就可以做到即插即用的效果而不用自己再

    2024年02月08日
    浏览(43)
  • 详细步骤 | 打包制作自己的 Python 模块包并上传到 Pip 源 PyPI

    本文介绍如何打包一个简单的 Python 项目,以及如何将其上传到Python包索引平台( PyPI ),供他人使用 pip install xxx 指令安装。 文件目录结构: 说明: packaging_tutorial 可以是任意的目录名, pyproject.toml 是打包配置文件, src 里面存放的就是你的程序代码, example_package_YOUR_USERNAM

    2024年03月24日
    浏览(38)
  • 从零开始快速构建自己的Flink应用

    本文介绍如何在 mac 下快速构建属于自己的 Flink 应用。 在 mac 上使用homebrew安装 flink: 查看安装的位置: 进入安装目录,启动 flink 集群: 进入 web 页面:http://localhost:8081/ 基于模板直接构建一个项目: 在项目的 DataStreamJob 类实现如下计数的功能: 在上面的例子中,我们使用

    2024年02月20日
    浏览(51)
  • 从零开始制作婚礼策划展示小程序

    随着移动互联网的发展,小程序已经成为各行各业展示和推广自己的重要工具之一。对于婚礼策划行业来说,制作一个专属的婚礼策划展示小程序,不仅能提升服务的专业性和便利性,还能吸引更多的客户。下面将介绍从零开始制作婚礼策划展示小程序的步骤。 第一步是进入

    2024年02月16日
    浏览(43)
  • LeetCode 刷题记录——从零开始记录自己一些不会的

    1. 最多可以摧毁的敌人城堡数目 题意 思路 两层循环,太low了 用一个变量记录前一个位置 代码 2. 到达终点的数字 题意 思路 代码 3. 单词的压缩编码 题意 思路 代码 思路2 去找到是否不同的单词具有相同的后缀,我们可以将其反序之后插入字典树中。例如,我们有 “time”

    2024年02月09日
    浏览(67)
  • 小程序制作教程:从零开始搭建企业小程序

    在如今的数字化时代,企业介绍小程序成为了企业展示与推广的重要工具。通过企业介绍小程序,企业可以向用户展示自己的品牌形象、产品服务以及企业文化等内容,进而提高用户对企业的认知度和信任度。本文将介绍如何从零开始搭建一个企业介绍小程序。 首先,进入乔

    2024年02月12日
    浏览(47)
  • 一篇SEO指南:新手如何从零开始优化自己的网站

    在如今的数字时代,拥有一个优化良好的网站对于任何企业或个人来说都是至关重要的。但是,对于SEO新手来说,如何从零开始优化自己的网站可能是一项看似艰巨的任务。在本文中,我们将为您提供一份SEO指南,帮助您了解从零开始优化自己的网站的过程。 确定您的目标关

    2024年02月02日
    浏览(112)
  • 从零开始画自己的DAG作业依赖图(一)--前期准备

    背景: 由于业务场景需要展示作业之间的依赖关系,由于一些开源的插件和当前的业务逻辑有一些冲突,个人打算尝试从零开始,一点点实现自己的DAG图。同时用博客记录自己实现过程和总结,不正确的地方,欢迎指正提升。 场景分析: 数据开发中常常有作业之间的依赖,

    2024年02月05日
    浏览(52)
  • 构建自己的ChatGPT:从零开始构建个性化语言模型

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月15日
    浏览(48)
  • UE4从零开始制作数字孪生道路监测平台

    UE4集成Cesium for Unreal和WebSocket,后端使用NodeJs搭建服务器进行数据模拟和真实数据实时转发。 1:新建UE4项目并集成Cesium for Unreal Cesium for UE4插件解锁了虚幻引擎中的3D地理空间生态系统。通过将高精度全尺寸WGS84 globe、开放API和空间索引开放标准(如3D图块)以及基于云的真实

    2024年02月02日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包