はじめに
映画『メタモルフォーゼの縁側』めっちゃよかった…、みんな観て🙏、nikkieです。
今回は、私の熱い『アイの歌声を聴かせて』ファン活動(アイカツ!)の様子をお届けします1。
前後編とアウトプットした「声をPythonに聴かせて」の続編(新編)です2。
※この投稿はアイうた円盤発売・配信開始アドカレ(後述)への投稿ではありません(本日18日目の投稿はひしさんです!)。
ですが、20日目以降のアドカレへの投稿と、後日扱いを変えるかもしれません。
目次
- はじめに
- 目次
- これまでの「声をPythonに聴かせて」
- TL; DR (シュッとした実装)
- インメモリストリームで一時ファイルが不要と気づく
- AudioDataからwav形式のbytesが作れることに気づく
- PyCon JP 2020 「インメモリー ストリーム活用術」
- 終わりに
- P.S. アイうた円盤発売・配信開始アドベントカレンダー賑わってます!
これまでの「声をPythonに聴かせて」
『アイの歌声を聴かせて』のシオン v0.0.1実装プロジェクトの中で、「音声を認識してテキストに変換する」(=音声認識)機能を実装しました。
スタンドアローンで動かすために機械学習モデルを導入したところ、マイクから入力した音声の書き起こしがイマイチという事象が発生。
前回の後編でこれを解決しました。
解決法はWorkaround(一時的な解決方法)だったんですが、このたび余計な処理を削ぎ落としてシュッとした実装にできたのでアウトプットします。
動作環境については前編を参照ください。
TL; DR (シュッとした実装)
from io import BytesIO import numpy as np import soundfile as sf import speech_recognition as sr from espnet2.bin.asr_inference import Speech2Text speech2text = Speech2Text.from_pretrained( "kan-bayashi/csj_asr_train_asr_transformer_raw_char_sp_valid.acc.ave" ) r = sr.Recognizer() with sr.Microphone(sample_rate=16_000) as source: print("なにか話してください") audio_data = r.listen(source) # -- リファクタリングした箇所 -- wav_bytes = audio_data.get_wav_data() # (1) wav_stream = BytesIO(wav_bytes) # (2) audio_array, sampling_rate = sf.read(wav_stream) # (3) # -- リファクタリングした箇所 終わり -- nbests = speech2text(audio_array) text, tokens, *_ = nbests[0] print(text)
- ライブラリ
SpeechRecognition
のAudioData
に音声をwav形式のbytesに変換するメソッドを発見:(1) - インメモリストリームを介して、wav形式のbytesを
soundfile.read
:(2) & (3)
インメモリストリームで一時ファイルが不要と気づく
from scipy.io import wavfile # リファクタリングした箇所をまず以下のように変えました frame_bytes = audio_data.get_raw_data() speech_array = np.frombuffer(frame_bytes, dtype=np.int16) speech_stream = BytesIO() # (4) wavfile.write(speech_stream, audio_data.sample_rate, speech_array) audio_array, sampling_rate = sf.read(speech_stream)
ファイルの読み書きは遅いと認識しています(ディスクアクセスのため)。
(PyCon APAC 2022の発表準備3の中で)ふと「インメモリストリームでいけそうじゃん!」と気付きました。
scipy.io.wavfile.write
とsf.read
はどちらもインメモリストリームを扱えることを確認したうえで、書き換えを試します。
scipy.io.wavfile.write
の第1引数には文字列だけでなく、open file handleも渡せます4(file-like objectと理解しました)sf.read
の第1引数も文字列だけでなく、file-like objectも渡せます5
上記コードは動作し、脱tempfileできました!🙌
AudioData
からwav形式のbytesが作れることに気づく
WorkaroundはライブラリSpeechRecognition
のAudioData
のget_raw_data
メソッドで音声をbytesに変えるのですが、「もしかして形式を指定できる?」とリファレンスを覗いてみました。
そこで見つかったのがget_wav_data
メソッド!
Returns a byte string representing the contents of a WAV file containing the audio represented by the AudioData instance.
このメソッドでwav形式のbytesが取得できたので、「これをインメモリストリームにして6sf.read
で読み込めば…」と実装したところ、動作しました!7
get_wav_data
メソッドとインメモリストリームによりscipy.io.wavfile
も不要になり、スッキリしましたね。
PyCon JP 2020 「インメモリー ストリーム活用術」
https://pycon.jp/2020/timetable/?id=203893
これはリアルタイムで聴講して記憶に残っていたのですが、(おそらく発表準備のプレッシャーによる火事場の馬鹿力で)シオン v0.0.1の音声認識のworkaroundカイゼンに結びつきました。
今回特に参考になったのは「PNGをBytesIOで取り扱う例」です。
終わりに
シオン v0.0.1の音声認識機能(スタンドアローン対応)は、workaroundを脱して動作する最小限の実装となりました!🎉
KISS原則(Keep it Short and Simple)8にもかないます!Spark joy!!
締切の力は偉大ですね。
それではこの辺で発表準備に戻りますー ノシ
P.S. アイうた円盤発売・配信開始アドベントカレンダー賑わってます!
7/27のBlu-ray & DVD 発売、そしてレンタル配信 & デジタルセル配信 開始をカウントダウンする企画です。
Blu-ray&DVD|映画『アイの歌声を聴かせて』公式サイト
直近は
というラインナップです👏
企画したnikkieの出番がこんなに回ってこない(=たくさんの参加者で賑わう)というのは全く想定していませんでした。
アドカレに関してご参加やいいね・RTで盛り上げていただき、誠にありがとうございます!!
本アドベントカレンダーは、どなたのご参加も大歓迎です!
-
映画(10th STORY)いいらしいので、観に行きたいなー↩
-
聞いて聞いて! #アイの歌声を聴かせて のシオン v0.0.1実装プロジェクトを海外のカンファレンスで発表します - nikkie-ftnextの日記↩
-
https://pysoundfile.readthedocs.io/en/latest/#soundfile.read↩
-
(2)のコードにありますが、bytesを渡して
BytesIO
インスタンスを初期化できます。ref: https://docs.python.org/ja/3/library/io.html#binary-i-o↩ -
get_raw_data
メソッドのときと異なり、np.frombuffer
に渡すと「ValueError: buffer size must be a multiple of element size」が送出されます。追っていませんが、wav形式に変換されたbytesのためかと理解しています(先頭を少し見比べました)↩ -
過去にこんなエントリも書きました。KISS原則も登場します:「あってもなくても同じなら捨てる」という片付けの考え方が(こんまりメソッドと)プログラミングに通じていました - nikkie-ftnextの日記↩