LoginSignup
23
23

More than 3 years have passed since last update.

【Audio入門】音声変換してみる♬

Posted at

この記事は、以下のような人の助けになることを期待して記載します。
実は、昨日Twitterに以下のようなつぶやきを見ました。
「私は感音性難聴がヒドいので、常に音程が高く聴こえてます。
そこでお願いが有ります、補聴器に【音階調節】機能を取り入れて下さい。
正常な音程の演奏に合わせて歌いたいのです。
音割れ
音質がクリアに聴こえない
高音が聴こえない
日本語が100%理解出来ない
…これらは人工知能に期待します。」

ご本人にとっては切実な願いだと思いますし、何よりこれ、いまどきのテーマとして最適だと思いました。こういうことを解決するためにこそ、AIが使われるべきだと思います。

ということで、どこまでできるかわかりませんが、とりあえず、やってみようと思います。
第一弾がこの記事で、前半の【音階調節】というのをやってみました。
※一応PCでできれば、補聴器に取り入れられる日も近いし、たぶんスマホへの搭載は簡単な気がします

やったこと

・Audioの入出力を整理する
・音声を変換する
・実際の音声を再生してみる

・Audioの入出力を整理する

一番簡単なコードは以下のとおりだと思います。

# -*- coding:utf-8 -*-
import pyaudio
RATE=44100
p=pyaudio.PyAudio()
N=100
CHUNK=1024*N
stream=p.open(  format = pyaudio.paInt16,
        channels = 1,
        rate = RATE,
        frames_per_buffer = CHUNK,
        input = True,
        output = True) # inputとoutputを同時にTrueにする
while stream.is_active():
    input = stream.read(CHUNK)
    output = stream.write(input)

上記の説明は以下のとおりです。
1.変数として、重要なのはバッファとして使っているCHUNKです。
 この大きさによって、音声をサンプリングするサイズが変わるので応答時間が変わります。
 ただし、一度動き始めると連続的に途切れることなく再生してくれています。
2.二番目に重要なのがRATEという変数です。
 これがサンプリング速度を決めています。
3.input=True, output=Trueで音声の入出力がこのstreamでできます。
4.実際の入出力がwhile以下に記載されていますが、この二行で実行できます
5.while 文で継続的に実行します

・音声を変換する

実は直感的には、FFT→iFFTを使おうかと思っていましたが、以下のコードでできました。

# -*- coding:utf-8 -*-
import pyaudio
RATE=44100
p=pyaudio.PyAudio()
N=100
CHUNK=1024*N
r= 1.059463094
r12=r*r*r*r
stream=p.open(  format = pyaudio.paInt16,
        channels = 1,
        rate = RATE,
        frames_per_buffer = CHUNK,
        input = True,
        output = True) # inputとoutputを同時にTrueにする
stream1=p.open( format = pyaudio.paInt16,
        channels = 1,
        rate = int(RATE*r12),
        frames_per_buffer = CHUNK,
        input = True,
        output = True) # inputとoutputを同時にTrueにする
while stream.is_active():
    input = stream.read(CHUNK)
    output = stream1.write(input)

変更点は以下のとおりです。
6.r=1.059463094という昨夜の音階のパラメータを導入して音階計算する
7.適当な音階パラメータを使って、stream1の定義式のrateを変更する
  ちなみに、上記ではサンプリング周波数が高くなるので、出力は高くなります
  逆に、$int(RATE/r12)$とすると出力は低くなります
  ここでは$r^4$なのでドからドまでの12音階の4音階高い(あるいは低い)音が出ます
8.output=stream1.write(input)に変更しています
これでウワンの声は、高くも低くも自由に変えられました。
しかも、N=10程度(N=1でも可)だと遅延もほとんどなく、かつかなり鮮明に出力するのに驚きます。

・実際の音声を再生してみる

上のコードを実際に動かしてマイクをつなげればできると思いますが、一応コードでwavファイルを出力するところまで書いておこうと思います。
※以下に上記との差分だけ記載し、おまけに全体を掲載します
9.wavfileは以下のwrite_wave関数で保存します
  w=wave.Wave_write(wav_file)と定義して、信号sin_wawveをstruct.packしたbinwaveをw.writeframes(binwave)で書込みします。
10.wavfileは遅いのと速いのとオリジナルと3種類出力しました

def write_wave(i,sin_wave,fs,sig_name='sample'):#fs:サンプリング周波数
    sin_wave = [int(x * 32767.0) for x in sin_wave]    
    binwave = struct.pack("h" * len(sin_wave), *sin_wave)
    wav_file='./aiueo/sig_change/'+str(i)+'_'+sig_name+'.wav'
    w = wave.Wave_write(wav_file)
    p = (1, 2, fs, len(binwave), 'NONE', 'not compressed')
    w.setparams(p)
    w.writeframes(binwave)
    w.close()

while stream.is_active():
    input = stream.read(CHUNK)
    sig =[]
    sig = np.frombuffer(input, dtype="int16")  /32768.0
    write_wave(sk, sig, fs=RATE*r12, sig_name='-4x')
    write_wave(sk, sig, fs=RATE/r12, sig_name='4x')
    write_wave(sk, sig, fs=RATE, sig_name='original')
    output = stream1.write(input)
    sk += 1

できたwavfileを以下に置きました。また、合体したものもおいておきます。
※ダウンロードしてお聞きください
AudioAutoencoder/wavfile/
まとめたファイルは、

original_wav.wav
4x_wav.wav
-4x_wav.wav

の3つです。
※合成用のアプリをおまけ2に掲載する

まとめ

・音声変換してみたら、簡単に出来た
・ほぼリアルタイムで変換できる
・ウワンの声も女性化できた⇒なんか少し工夫が必要

・ノイズリダクションや音色変換までやろうと思う

おまけ

# -*- coding:utf-8 -*-
import pyaudio
import numpy as np
import wave
import struct

RATE=44100
p=pyaudio.PyAudio()
N=100
CHUNK=1024*N
r= 1.059463094
r12=r*r*r*r*r*r
sk=0

stream=p.open(  format = pyaudio.paInt16,
        channels = 1,
        rate = RATE,
        frames_per_buffer = CHUNK,
        input = True,
        output = True) # inputとoutputを同時にTrueにする
stream1=p.open( format = pyaudio.paInt16,
        channels = 1,
        rate = int(RATE*r12),
        frames_per_buffer = CHUNK,
        input = True,
        output = True) # inputとoutputを同時にTrueにする

def write_wave(i,sin_wave,fs,sig_name='sample'):#fs:サンプリング周波数
    sin_wave = [int(x * 32767.0) for x in sin_wave]    
    binwave = struct.pack("h" * len(sin_wave), *sin_wave)
    wav_file='./aiueo/sig_change/'+str(i)+'_'+sig_name+'.wav'
    w = wave.Wave_write(wav_file)
    p = (1, 2, fs, len(binwave), 'NONE', 'not compressed')
    w.setparams(p)
    w.writeframes(binwave)
    w.close()
    return wav_file

while stream.is_active():
    input = stream.read(CHUNK)
    sig =[]
    sig = np.frombuffer(input, dtype="int16")  /32768.0
    write_wave(sk, sig, fs=RATE*r12, sig_name='-4x')
    write_wave(sk, sig, fs=RATE/r12, sig_name='4x')
    write_wave(sk, sig, fs=RATE, sig_name='original')
    output = stream1.write(input)
    sk += 1

おまけ2

以下に、wavファイルの合成アプリを掲載する

# -*- coding:utf-8 -*-
import pyaudio
import numpy as np
import wave
import struct

RATE=44100
CHUNK = 22050
p=pyaudio.PyAudio()
r= 1.059463094
r12=r*r*r*r*r*r

stream1=p.open(format = pyaudio.paInt16,
        channels = 1,
        rate = int(RATE*r12),
        frames_per_buffer = CHUNK,
        input = True,
        output = True) # inputとoutputを同時にTrueにする

w = wave.Wave_write("./aiueo/sig_change//N100_3output/4x_wav.wav")
p = (1, 2, int(RATE*r12), CHUNK, 'NONE', 'not compressed')
w.setparams(p)

for i in range(6):
    wavfile = './aiueo/sig_change/N100_3output/{}_4x.wav'.format(i)
    print(wavfile)
    wr = wave.open(wavfile, "rb")
    input = wr.readframes(wr.getnframes())
    output = stream1.write(input)
    w.writeframes(input)
23
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
23