注意
单击此处下载完整的示例代码
音频 I/O¶
作者: Moto Hira
本教程介绍如何使用 TorchAudio 的基本 I/O API 来检查音频数据。 将它们加载到 PyTorch 张量中并保存 PyTorch 张量。
警告
在最近的版本中,计划/对音频 I/O 进行了多项更改。 有关这些更改的详细信息,请参阅 Dispatcher 简介。
import torch
import torchaudio
print(torch.__version__)
print(torchaudio.__version__)
2.5.0
2.5.0
制备¶
首先,我们导入模块并下载我们在本教程中使用的音频资源。
注意
在 Google Colab 中运行本教程时,请安装所需的软件包 替换为以下内容:
!pip install boto3
import io
import os
import tarfile
import tempfile
import boto3
import matplotlib.pyplot as plt
import requests
from botocore import UNSIGNED
from botocore.config import Config
from IPython.display import Audio
from torchaudio.utils import download_asset
SAMPLE_GSM = download_asset("tutorial-assets/steam-train-whistle-daniel_simon.gsm")
SAMPLE_WAV = download_asset("tutorial-assets/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav")
SAMPLE_WAV_8000 = download_asset("tutorial-assets/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042-8000hz.wav")
def _hide_seek(obj):
class _wrapper:
def __init__(self, obj):
self.obj = obj
def read(self, n):
return self.obj.read(n)
return _wrapper(obj)
0%| | 0.00/7.99k [00:00<?, ?B/s]
100%|##########| 7.99k/7.99k [00:00<00:00, 14.5MB/s]
0%| | 0.00/53.2k [00:00<?, ?B/s]
100%|##########| 53.2k/53.2k [00:00<00:00, 48.4MB/s]
查询音频元数据¶
函数获取音频元数据。
您可以提供类似路径的对象或类似文件的对象。
metadata = torchaudio.info(SAMPLE_WAV)
print(metadata)
AudioMetaData(sample_rate=16000, num_frames=54400, num_channels=1, bits_per_sample=16, encoding=PCM_S)
哪里
sample_rate
是音频的采样率num_channels
是通道数num_frames
是每个通道的帧数bits_per_sample
是位深度encoding
是示例编码格式
encoding
可以采用以下值之一:
"PCM_S"
: 有符号整数线性 PCM"PCM_U"
:无符号整数线性 PCM"PCM_F"
: 浮点线性 PCM"FLAC"
: Flac,免费无损音频 编 解码 器"ULAW"
: 穆罗, [维基百科]"ALAW"
: A-law [维基百科]"MP3"
:MP3、MPEG-1 音频层 III"VORBIS"
: OGG Vorbis [xiph.org]"AMR_NB"
:自适应多速率 [维基百科]"AMR_WB"
: 自适应多速率宽带 [维基百科]"OPUS"
: 作品 [opus-codec.org]"GSM"
: GSM-FR [维基百科]"HTK"
:单通道 16 位 PCM"UNKNOWN"
以上都不是
注意
bits_per_sample
可用于具有压缩和/或 可变比特率(如 MP3)。0
num_frames
可以是 GSM-FR 格式。0
metadata = torchaudio.info(SAMPLE_GSM)
print(metadata)
AudioMetaData(sample_rate=8000, num_frames=39680, num_channels=1, bits_per_sample=0, encoding=GSM)
查询类文件对象¶
AudioMetaData(sample_rate=44100, num_frames=109368, num_channels=2, bits_per_sample=16, encoding=PCM_S)
注意
当传递一个类似文件的对象时,不会读取
所有基础数据;相反,它只读取一部分
的数据。
因此,对于给定的音频格式,它可能无法检索
正确的元数据,包括格式本身。在这种情况下,您
可以传递参数来指定音频的格式。info
format
加载音频数据¶
此函数接受 path-like object 或 file-like object 作为 input。
返回值是 waveform () 和 sample rate 的元组
().Tensor
int
默认情况下,生成的 tensor 对象具有 和
其取值范围为 。dtype=torch.float32
[-1.0, 1.0]
支持的格式列表请参考 torchaudio 文档。
waveform, sample_rate = torchaudio.load(SAMPLE_WAV)
def plot_waveform(waveform, sample_rate):
waveform = waveform.numpy()
num_channels, num_frames = waveform.shape
time_axis = torch.arange(0, num_frames) / sample_rate
figure, axes = plt.subplots(num_channels, 1)
if num_channels == 1:
axes = [axes]
for c in range(num_channels):
axes[c].plot(time_axis, waveform[c], linewidth=1)
axes[c].grid(True)
if num_channels > 1:
axes[c].set_ylabel(f"Channel {c+1}")
figure.suptitle("waveform")
plot_waveform(waveform, sample_rate)
def plot_specgram(waveform, sample_rate, title="Spectrogram"):
waveform = waveform.numpy()
num_channels, num_frames = waveform.shape
figure, axes = plt.subplots(num_channels, 1)
if num_channels == 1:
axes = [axes]
for c in range(num_channels):
axes[c].specgram(waveform[c], Fs=sample_rate)
if num_channels > 1:
axes[c].set_ylabel(f"Channel {c+1}")
figure.suptitle(title)
plot_specgram(waveform, sample_rate)
Audio(waveform.numpy()[0], rate=sample_rate)
从类文件对象加载¶
I/O 函数支持类似文件的对象。 这允许从位置获取和解码音频数据 在本地文件系统内部和外部。 以下示例对此进行了说明。
# Load audio data as HTTP request
url = "https://download.pytorch.org/torchaudio/tutorial-assets/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav"
with requests.get(url, stream=True) as response:
waveform, sample_rate = torchaudio.load(_hide_seek(response.raw))
plot_specgram(waveform, sample_rate, title="HTTP datasource")
# Load audio from tar file
tar_path = download_asset("tutorial-assets/VOiCES_devkit.tar.gz")
tar_item = "VOiCES_devkit/source-16k/train/sp0307/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav"
with tarfile.open(tar_path, mode="r") as tarfile_:
fileobj = tarfile_.extractfile(tar_item)
waveform, sample_rate = torchaudio.load(fileobj)
plot_specgram(waveform, sample_rate, title="TAR file")
0%| | 0.00/110k [00:00<?, ?B/s]
100%|##########| 110k/110k [00:00<00:00, 43.5MB/s]
# Load audio from S3
bucket = "pytorch-tutorial-assets"
key = "VOiCES_devkit/source-16k/train/sp0307/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav"
client = boto3.client("s3", config=Config(signature_version=UNSIGNED))
response = client.get_object(Bucket=bucket, Key=key)
waveform, sample_rate = torchaudio.load(_hide_seek(response["Body"]))
plot_specgram(waveform, sample_rate, title="From S3")
切片提示¶
提供和参数限制
decoding 到 input 的相应段。num_frames
frame_offset
使用 vanilla Tensor 切片可以获得相同的结果,
(即 )。然而
提供和参数更多
有效。waveform[:, frame_offset:frame_offset+num_frames]
num_frames
frame_offset
这是因为该函数将结束数据采集和解码 一旦它完成对请求的帧的解码。这是有利的 当音频数据通过网络传输时,数据传输将 stop 一旦获取到必要的数据量。
下面的示例对此进行了说明。
# Illustration of two different decoding methods.
# The first one will fetch all the data and decode them, while
# the second one will stop fetching data once it completes decoding.
# The resulting waveforms are identical.
frame_offset, num_frames = 16000, 16000 # Fetch and decode the 1 - 2 seconds
url = "https://download.pytorch.org/torchaudio/tutorial-assets/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav"
print("Fetching all the data...")
with requests.get(url, stream=True) as response:
waveform1, sample_rate1 = torchaudio.load(_hide_seek(response.raw))
waveform1 = waveform1[:, frame_offset : frame_offset + num_frames]
print(f" - Fetched {response.raw.tell()} bytes")
print("Fetching until the requested frames are available...")
with requests.get(url, stream=True) as response:
waveform2, sample_rate2 = torchaudio.load(
_hide_seek(response.raw), frame_offset=frame_offset, num_frames=num_frames
)
print(f" - Fetched {response.raw.tell()} bytes")
print("Checking the resulting waveform ... ", end="")
assert (waveform1 == waveform2).all()
print("matched!")
Fetching all the data...
- Fetched 108844 bytes
Fetching until the requested frames are available...
- Fetched 108844 bytes
Checking the resulting waveform ... matched!
将音频保存到文件¶
要将音频数据保存为常见应用程序可解释的格式,
您可以使用 .
此函数接受 path-like object 或 file-like object。
在传递类似文件的对象时,你还需要提供参数,以便函数知道它应该使用哪种格式。在
对于路径类对象,该函数将从
扩展。如果要保存到没有扩展名的文件,则需要
以提供参数 。format
format
保存 WAV 格式的数据时,Tensor 的默认编码
是 32 位浮点 PCM。您可以提供参数并更改此行为。例如,要保存数据
在 16 位有符号整数 PCM 中,您可以执行以下操作。float32
encoding
bits_per_sample
注意
以较低位深度的编码保存数据会减少 生成的文件大小以及精度。
waveform, sample_rate = torchaudio.load(SAMPLE_WAV)
def inspect_file(path):
print("-" * 10)
print("Source:", path)
print("-" * 10)
print(f" - File size: {os.path.getsize(path)} bytes")
print(f" - {torchaudio.info(path)}")
print()
保存时不带任何编码选项。 该函数将获取 提供的数据拟合
with tempfile.TemporaryDirectory() as tempdir:
path = f"{tempdir}/save_example_default.wav"
torchaudio.save(path, waveform, sample_rate)
inspect_file(path)
----------
Source: /tmp/tmpu65i6inj/save_example_default.wav
----------
- File size: 108878 bytes
- AudioMetaData(sample_rate=16000, num_frames=54400, num_channels=1, bits_per_sample=16, encoding=PCM_S)
另存为 16 位有符号整数线性 PCM 生成的文件占用了一半的存储空间,但会丢失精度
with tempfile.TemporaryDirectory() as tempdir:
path = f"{tempdir}/save_example_PCM_S16.wav"
torchaudio.save(path, waveform, sample_rate, encoding="PCM_S", bits_per_sample=16)
inspect_file(path)
----------
Source: /tmp/tmp9xxqlowd/save_example_PCM_S16.wav
----------
- File size: 108878 bytes
- AudioMetaData(sample_rate=16000, num_frames=54400, num_channels=1, bits_per_sample=16, encoding=PCM_S)
formats = [
"flac",
# "vorbis",
# "sph",
# "amb",
# "amr-nb",
# "gsm",
]
waveform, sample_rate = torchaudio.load(SAMPLE_WAV_8000)
with tempfile.TemporaryDirectory() as tempdir:
for format in formats:
path = f"{tempdir}/save_example.{format}"
torchaudio.save(path, waveform, sample_rate, format=format)
inspect_file(path)
----------
Source: /tmp/tmpjayiu4jz/save_example.flac
----------
- File size: 45262 bytes
- AudioMetaData(sample_rate=8000, num_frames=27200, num_channels=1, bits_per_sample=16, encoding=FLAC)
存储为类似文件的对象¶
与其他 I/O 功能类似,您可以将音频保存为类似 file 的
对象。当保存到类文件对象时,参数为
必填。format
waveform, sample_rate = torchaudio.load(SAMPLE_WAV)
# Saving to bytes buffer
buffer_ = io.BytesIO()
torchaudio.save(buffer_, waveform, sample_rate, format="wav")
buffer_.seek(0)
print(buffer_.read(16))
b'RIFFF\xa9\x01\x00WAVEfmt '
脚本总运行时间:(0 分 1.906 秒)