LoginSignup
2
5

More than 5 years have passed since last update.

Cythonで音声ファイルを読み込み、無音部分ごとに分ける

Posted at

はじめに

Cython初心者です。備忘録として。

やりたいこと

特定のディレクトリ内にある複数の音声データ(.wavや.mp3など)を読み込み、それぞれのファイルについて無音部分ごとに分割する。読み込まれた音声データや出力される音声データは別のディレクトリに移動させる。

何故Cythonなのか

大きな音声データを扱うための前処理として行う際に、Pythonだと速度が足りないため。

実行環境

OS : Windows10
ディレクトリ構成は以下のような感じ。

sample
  |- datas_input     # 読み込む音声ファイル
  |- datas_output    # 出力される音声ファイル
  |     |- outputs   # 分割された後の音声ファイルが入る
  |- main.py         # 実行ファイル
  |- sounds.pyx      # Cythonによるモジュール
  |- setup.py        # Cythonコードをコンパイルするためのファイル

必要なライブラリ・モジュール

事前にpydubをインストールしておく。
参考はこちら
また、忘れずにpip install cythonもしておく。

from glob import glob
from pydub import AudioSegment
from pydub.silence import split_on_silence
from os.path import basename
import shutil

処理内容

sounds.pyx
cdef split(data):
    cdef int n = 0
    cdef str file_path = ''
    cdef list data_list = []

    sound = AudioSegment.from_file(data)
    chunks = split_on_silence(sound, min_silence_len = 1000, silence_thresh = -50, keep_silence = 100)

    for n, chunk in enumerate(chunks):
        file_path = 'datas_output/outputs/output' + str(n) + '_' + basename(data)
        data_list.append(file_path)
        chunk.export(file_path)

    return data_list


cpdef load():
    cdef list sounds = []
    cdef list sound_list = []
    cdef list new_sound_list = []
    cdef str data = ''

    sounds = glob('datas_input/*')
    if len(sounds) == 0:
        print('There is no data')
        return None

    for data in sounds:
        sound_list.append(shutil.move(data, 'datas_output/'))

    for data in sound_list:
        new_sound_list.extend(split(data))

    return new_sound_list

簡単な説明

  1. glob('datas_input/*')datas_inputにある全てのファイル名のリストを一括で取得。
  2. shutil.move(data, 'datas_output/')datadatas_output/に移動。
  3. AudioSegment.from_file(data)dataを読み込み。
  4. split_on_silence(sound, min_silence_len = 1000, silence_thresh = -50, keep_silence = 100)soundを無音部分ごとにカット。参考はこちら
  5. chunk.export(file_path)file_pathに出力。

Cythonを使うための処理

setupのためのファイル。

setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext' : build_ext},
    ext_modules = [Extension('sounds', ['sounds.pyx'])]
)

呼び出し側のPythonコード

こちらはCythonで作られたモジュールをimportする側。

main.py
import sounds

def main():
    sound_list = sounds.load()
    if sound_list == None:
        exit()
    for data in sound_list:
        print('[data] : ' + data)

    # ...処理...


if __name__ == '__main__':
    main()

実行

main.pyを実行すると、datas_inputに入っていた音声データが全てdatas_outputに移動する。さらに、そこにsplitされた後の音声データも保存されているはずである。テスト用データは、こちらからダウンロードしておくとよい。上手くいけば、以下のような結果が得られる。

$ python setup.py build_ext --inplace
$ python main.py
[data] : datas_output/outputs/output0_〇〇.mp3
[data] : datas_output/outputs/output1_〇〇.mp3
[data] : datas_output/outputs/output2_〇〇.mp3
[data] : datas_output/outputs/output0_△△.mp3
                   ・
                   ・
                   ・

エラーが出たら

同じディレクトリ内にffmpeg.exeffmprobe.exeがあるかどうかチェックする。それ以外の場合はエラーメッセージをググる。

2
5
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
2
5