【发布时间】:2018-05-08 04:22:21
【问题描述】:
我是一名音乐家,我正在编写一个读取 .wav 文件的 Python 脚本,使用快速傅立叶变换将其转换为一堆正弦波,然后将这些正弦波调谐到最接近的谐波频率。如果所有这些听起来都是胡言乱语,那没关系,我的问题无需任何音乐知识即可回答。
当我在一个相当长的 .wav 文件上运行我的脚本时,需要几个小时来处理脚本的以下部分:
filtered_data_fft = np.zeros(data_fft.size)
for f in data_fft:
if f > 1:
valid_frequency = (np.abs(valid_frequencies - i)).argmin()
filtered_data_fft[valid_frequency] += data_fft[i]
i += 1
以 fft 结尾的两个数组都是索引对应于频率的数组,valid_frequencies 数组是对应于所述索引的频率列表。我最初并没有对所有东西都使用 numpy 数组,而且运行时间太长,以至于我无法在合理的时间内处理一个简短的声音文件,但是使用 numpy 会快很多。谁能想出比这更快的方法?我会把完整的脚本放在下面。
此外,关于将复数转换为实数的两个已知警告会丢弃复数,但我认为它们不是问题。 FFT 返回一个元组数组,其中第一个值是频率,第二个值是一个复数,表示我不太了解的东西,但根据我学习这一点的页面,这并不重要。这是我学到这些东西的地方:https://pythonforengineers.com/audio-and-digital-signal-processingdsp-in-python/
诚然,我并不完全理解我在这里所做的很多 DSP 工作,所以如果我在某些方面有严重错误,请告诉我!我只是想用一种有趣的方式将噪音变成音乐,用于我正在进行的项目。
这是我正在测试的音频示例: https://my.mixtape.moe/iltlos.wav (重命名为missile.wav)
这是完整的脚本(更新为正确):
import struct
import wave
import numpy as np
import matplotlib.pyplot as plt
# import data from wave
wav_file = wave.open("missile.wav", 'r')
num_samples = wav_file.getnframes()
sampling_rate = wav_file.getframerate() / 2
data = wav_file.readframes(num_samples)
wav_file.close()
data = struct.unpack('{n}h'.format(n=num_samples), data)
data = np.array(data)
# fast fourier transform makes an array of the frequencies of sine waves that comprise the sound
data_fft = np.fft.rfft(data)
# generate list of ratios that can be used for tuning (not octave reduced)
MAX_HARMONIC = 5
valid_ratios = []
for i in range(1, MAX_HARMONIC + 1):
for j in range(1, MAX_HARMONIC + 1):
if i % 2 != 0 and j % 2 != 0:
valid_ratios.append(i/float(j))
valid_ratios.append(j/float(i))
# remove dupes
valid_ratios = list(set(valid_ratios))
# find all the frequencies with the valid ratios
valid_frequencies = []
multiple = 2
while(multiple < num_samples / 2):
multiple *= 2
for ratio in valid_ratios:
frequency = ratio * multiple
if frequency < num_samples / 2:
valid_frequencies.append(frequency)
# remove dupes and sort and turn into a numpy array
valid_frequencies = np.sort(np.array(list(set(valid_frequencies))))
# bin the data_fft into the nearest valid frequency
valid_frequencies = valid_frequencies.astype(int)
boundaries = np.concatenate([[0], np.round(np.sqrt(0.25 + valid_frequencies[:-1] * valid_frequencies[1:])).astype(int)])
select = np.abs(data_fft) > 1
filtered_data_fft = np.zeros_like(data_fft)
filtered_data_fft[valid_frequencies] = np.add.reduceat(np.where(select, data_fft, 0), boundaries)
# do the inverse fourier transform to get a sound wave back
recovered_signal = np.fft.irfft(filtered_data_fft)
# write sound wave to wave file
comptype="NONE"
compname="not compressed"
nchannels=1
sampwidth=2
wav_file=wave.open("missile_output.wav", 'w')
wav_file.setparams((nchannels, sampwidth, int(sampling_rate), num_samples, comptype, compname))
for s in recovered_signal:
wav_file.writeframes(struct.pack('h', s))
wav_file.close()
【问题讨论】:
标签: python arrays numpy optimization signal-processing