質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1264閲覧

音声の離散フーリエ変換後にノイズが入ってしまう

Yuki0

総合スコア5

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2023/03/14 19:21

実現したいこと

マイクの入力にハイパスフィルタをかけたいです。

前提

Pythonを用いてマイクの入力をpyaudioで受けて、numpyを用いて離散フーリエ変換する事で、
ローパスフィルタをかけようとしています。

発生している問題

後述のように、フーリエ変換を少しでも変更するとノイズが発生します。
これは離散フーリエ変換の性質なのか、問題が発生しているのかわかりません。
フーリエ変換の性質の場合、音声にフィルタをかけるためには何が必要でしょうか。

該当のソースコード

Python

1import pyaudio 2import numpy as np 3import math 4import matplotlib as mpl 5import matplotlib.pyplot as plt 6 7 8def audio_trans(inp): 9 """ 10 w = np.zeros(512, dtype=float) 11 for i in range(512): 12 w[i] = (math.cos(i / 512 * math.pi * 2 - math.pi) + 1) * 0.5 13 """ 14 # data size 15 aaa = 512 * 2 * 2 16 # passing frequency 17 freq = 0 18 19 ''' 20 f = np.fft.rfftfreq(2048, 1 / 44100) 21 x = np.arange(1025) 22 y = np.arange(512) 23 plt.plot(x, cv_data.imag, color="cyan", alpha=1) 24 plt.plot(x, cv_data.real, color="red", alpha=0.5) 25 plt.show() 26 ''' 27 # fourier transforming 28 fft_data = np.fft.rfft(inp, aaa) 29 30 # editing coefficients 31 cv_data = np.zeros(aaa // 2 + 1, dtype=np.complex_) 32 for i in range(aaa // 2 + 1 - freq): 33 cv_data[i] = fft_data[i] 34 35 # invert fourier transforming 36 fft_data2 = np.fft.irfft(cv_data, aaa) 37 38 # era-kaihi 39 fft_data3 = np.real(fft_data2) 40 fft_data4 = np.zeros(aaa, dtype=int) 41 for num in range(aaa): 42 fft_data4[num] = int(fft_data3[num]) 43 44 ret = np.array(fft_data4) 45 46 return ret 47 48 49def main(): 50 CHUNK = 1024 51 RATE = 44100 52 p = pyaudio.PyAudio() 53 54 stream = p.open(format=pyaudio.paInt16, 55 channels=1, 56 rate=RATE, 57 frames_per_buffer=CHUNK, 58 input_device_index=1, 59 output_device_index=10, 60 input=True, 61 output=True) 62 63 while stream.is_active(): 64 st_input = stream.read(CHUNK) 65 data = np.frombuffer(st_input, dtype='int32') 66 67 # to editor 68 data2 = audio_trans(data) 69 70 input2 = np.frombuffer(data2, dtype='int32') 71 output = stream.write(input2) 72 73 stream.stop_stream() 74 stream.close() 75 p.terminate() 76 77 print 78 'Stop Streaming' 79 80 81if __name__ == '__main__': 82 main()

試したこと

freqを0にする、つまりフーリエ変換に変更を加えないと、望んだ音が返ってきました。
フーリエ変換の値の一部を0にすると、周波数に依存するノイズが発生しました。
値を1000000+1000000j減算してもノイズが発生しました。
値を2で除算してもノイズが発生しました。
有意に変更を加えると正しい音声にならない?

補足情報(FW/ツールのバージョンなど)

IntelliJ IDEA 2021.3.3 (Community Edition)
Build #IC-213.7172.25, built on March 16, 2022
Runtime version: 11.0.14.1+1-b1751.46 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2023/03/15 00:26

マイク入力だとテストしづらいので、カットされない周波数の正弦波を入力に与えるようにして 入出力のグラフを貼ってください。
kikukiku

2023/03/15 00:47

高い周波数のみ抽出しているということなので その結果がノイズのようになるのは正しい結果なのではないでしょうか? その結果が正しくなく、ノイズだとおっしゃっているのは どのような判断基準で言っているのでしょうか? 感覚で判断せず、上記、ozwkさんのコメントのように、 低い周波数と、高い周波数を入れてみて、 高い周波数のみ通ることを確認されるのが良いと思います。
jbpb0

2023/03/15 01:24 編集

kikukikuさん > 高い周波数のみ抽出しているということなので その結果がノイズのようになるのは正しい結果なのではないでしょうか? 質問の「実現したいこと」には「ハイパスフィルタをかけたい」とありますが、「前提」には「ローパスフィルタをかけようと」とあります コードを見ると、「fft_data」の先頭の方だけ「cv_data」にコピーしてるので、「ローパスフィルタ」ですよね for i in range(aaa // 2 + 1 - freq): cv_data[i] = fft_data[i]
kikukiku

2023/03/15 04:43 編集

あら、確かにそうですね。 ローパスフィルタを実現したいのであるならば、 撤回します。 どちらにしても、同じようにテストをした方が良いと思います。 ※当方はコードはわかりませんが、音声処理の基礎理論はすべて理解しているものです。
guest

回答3

0

入力信号を一定時間ごとに分割してフーリエ変換し、
適当な周波数領域を削って逆フーリエ変換しているものと思います。

質問で問題としているノイズの原因かはわかりませんが、とりあえず一般的な話をしますと、
この方法だと主に分割点付近で信号が不連続になったりノイズが発生します。

フーリエ変換というのは変換する信号が無限に繰り返すことを想定するので、
分割によって分割前にはなかった周波数成分が現れます。

簡単な例を挙げますと、平均値が0の正弦波を入力として、周波数0の成分を除くことを考えます。
当然、出力はなにも変化がないことを期待します。

1周期で分割した場合は信号の平均値は0なので
周波数が0の成分は当然0です。したがって周波数が0の成分を除いてもなにも変化を加えてませんので、
期待通りの出力が得られます。

一方で半周期で2分割した場合は信号の平均値が前半部分は正、後半部分は負になります。
なので周波数が0の成分が0になりません。それぞれ周波数が0の成分を除いてしまったら
信号に変化を加えたことになりますので、期待する出力になりません。


イメージ説明
赤が入力信号、入力信号を前半と後半に分けてそれぞれ低周波成分を除くと入力信号から変化してしまう。


例では周波数が0の部分だけ扱いましたが、他の周波数領域でも同じことが起こります。
分割により意図しない周波数成分が現れて、それを削ってしまうことでノイズが入ります。

フィルタを実現する場合はフーリエ変換せずにFIRやIIRなどで実現するのが普通かと思います。

投稿2023/03/15 01:01

編集2023/03/15 01:15
ozwk

総合スコア13521

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jbpb0

2023/03/15 02:17

質問のコードは、 for i in range(aaa // 2 + 1 - freq): cv_data[i] = fft_data[i] で、「fft_data」の先頭の方だけ「cv_data」にコピーしてるので、「ローパスフィルタ」だから、 > 周波数0の成分を除くことを考えます。 は起きないと思います フィルタ後に、(周波数0のデータを除去しなくても)つなぎ目が不連続になる影響はあるのでしょうけど
ozwk

2023/03/15 02:27

書いた後にハイパスじゃなくてローパスの話だったことに気づいたんで、例としてはあまりよろしくなかったとは思いますが、原理的には「分割により意図しない周波数成分が現れて、それを削ってしまうことでノイズが入る」が言いたいことなのでまあいいかと思ってます。
guest

0

自己解決

解決しました。
結論としては、pyaudioのストリームを汲む時・に流す時のデータ型が間違っていました。

間違った方法では、
ストリームから"バイト型"で汲んだ後、それをnumpyで"int32"で読み取り、"int32"で流していました。
読み取ったデータには1文字おきに0が入っていましたので、小音量でしたので振幅の上位データが溢れたものと考えました。

よって、
numpyで読み取る時、
data = np.frombuffer(st_input, dtype='int16')
とし、流す前に
input2 = np.frombuffer(data2, dtype='int8')
とバイトとして与えた
ら、audio_transを挟んでも挟まなくても正常に音声が流れ、audio_transで窓をかけた上で周波数をカットし、オーバーラップしてつなげた時にも正常に聞こえました。

投稿2023/03/26 23:08

Yuki0

総合スコア5

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

フーリエ変換を少しでも変更するとノイズが発生します。
これは離散フーリエ変換の性質なのか

過去のダウンロード音素材
の「0332 小型船」からダウンロードして解凍した「0332.wav」を入力に、下記のコードを実行して作成した「0332lpf.wav」を聞いてみましたが、私には「ノイズ」に感じるものは聞こえませんでした
(私の聴覚が鈍感なのかもしれませんが)

python

1import numpy as np 2from scipy.io.wavfile import read, write 3 4 5readfilename = "./0332.wav" 6writefilename = "./0332lpf.wav" 7fs, inp = read(readfilename) 8 9# data size 10aaa = len(inp) 11# passing frequency 12freq = int((aaa // 2 + 1) * 0.9) 13 14# fourier transforming 15fft_data = np.fft.rfft(inp, aaa) 16 17# editing coefficients 18cv_data = np.zeros(aaa // 2 + 1, dtype=np.complex_) 19for i in range(aaa // 2 + 1 - freq): 20 cv_data[i] = fft_data[i] 21 22# invert fourier transforming 23fft_data2 = np.fft.irfft(cv_data, aaa) 24 25# era-kaihi 26fft_data3 = np.real(fft_data2) 27fft_data4 = np.zeros(aaa, dtype=int) 28for num in range(aaa): 29 fft_data4[num] = int(fft_data3[num]) 30 31ret = np.array(fft_data4).astype("int16") 32write(writefilename, rate=fs, data=ret)

なお、上記のコードは、原因が「離散フーリエ変換」にあるかどうかを切り分けるために、質問のコードの「def audio_trans(inp):」の内容を取り出したものです

 
【追記】
ozwkさんも回答に書いてますが、フィルタ後のデータのつなぎ目が不連続になって、それが「ノイズ」として聞こえるのではないですかね

フィルタの窓関数設計
とかに書かれてるように、データに窓関数を適用させてからフィルタ処理すれば、つなぎ目の不連続性が緩和されると思います
ただし、元データを一定の長さに切り出す際にオーバーラップさせて切り出して、フィルタ後のデータを単純につなげるのではなく、オーバーラップ部分をミックスさせて、つないだところで音量が変わらないようにする必要があります

投稿2023/03/15 02:06

編集2023/03/15 03:10
jbpb0

総合スコア7651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問