LoginSignup
0
0

More than 1 year has passed since last update.

PythonとPydubとSqlite3を用いてデータベースに音データを格納しデータベースから音データを復元してみた

Last updated at Posted at 2022-02-05

こんにちは。

wavファイルやmp3ファイルといったオーディオファイルをデータベースに格納したいと考えました。
また、データベースで格納されたオーディオ情報を取り出して、オーディオファイルに復元したいと思いました。

今回のブログでは、『Pythonによってオーディオファイルからオーディオ情報を作成し、SQLを用いてデータベースに格納する』、『データベースからSQLを用いてオーディオ情報を取り出し、それをオーディオファイルに復元する』、この二点について述べていきます。

この記事で得られるサブ情報は以下の通りです。

・Pythonスクリプト上でSqlite3を用いたデータベースの接続方法
・Pythonスクリプト上でSqlite3を用いたテーブルの操作
・バイナリデータをBLOB型として直接データベースに格納する方法
・Pydubを用いたオーディオ情報の取得方法、オーディオファイルの作成方法

それでは早速手順を追っていきましょう。

0. 作成したスクリプトの概要

今回作成したスクリプトの概要は以下のようになります。

image.png

audio_to_db.pyでは、オーディオファイルをオーディオ情報に変換したのちに、オーディオ情報をデータベースに格納しています。
audio_to_db.pyの解説をセクション1で行っています。

また、db_to_audio.pyでは、データベースからオーディオ情報を取り出し、オーディオファイルに復元しています。
db_to_audio.pyの解説をセクション2で行っています。

オーディオファイルとオーディオ情報のやり取りをPydubモジュールを用いて行いました。
また、データベースとのやりとりをSqlite3モジュールを用いて行いました。

1. オーディオファイルからオーディオ情報を作成し、データベースに格納する

オーディオファイルをデータベースに格納していきます。
データベース格納までの手順としては、次のような概要になります。

1.1. オーディオファイルをPydubモジュールを用いてオーディオ情報に変換
1.2. Sqlite3モジュールを用いてテーブルを作成し、オーディオ情報を格納

オーディオファイルを変換するのにPythonのPydubモジュールを用います。
それに合わせて、今回はPythonスクリプトでSQLを記述することで、ファイルの変換からデータベース格納の流れをPythonスクリプト一個でまとめたいと思います。
PythonでSQLを記述するにはSqlite3モジュールを用います。

1.0. 全体のソースコード

オーディオファイルをデータベースに格納するソースコードは以下の通りです。

audio_to_db.py
import sqlite3
from contextlib import closing
from pydub import AudioSegment
import re
import sys

filepath = sys.argv[1] #データベースに格納するオーディオファイルのパスをコマンドライン引数で指定
extension = re.sub(r'.*[.]', '', filepath) #オーディオファイルの拡張子

#AudioSegmentオブジェクトとしてオーディオファイルを読み込む
#拡張子によって読み込み方法を指定(WAV, MP3)
audio = AudioSegment.from_wav(filepath) if extension == 'wav'\
    else AudioSegment.from_mp3(filepath)

#オーディオ情報を取得
#バイナリデータ, ビット数(16bit->2, 24bit->3, ...)
#サンプリング周波数(44100Hzになりがち), チャネル(1ch, 2ch)
audio_data = AudioSegment.get_array_of_samples(audio)
audio_sample_width = audio.sample_width
audio_frame_rate = audio.frame_rate
audio_channels = audio.channels

# print(type(audio_data), audio_sample_width, audio_frame_rate, audio_channels)

#データベースと接続
dbname = 'sample_audio.db' #DB名
with closing(sqlite3.connect(dbname)) as connection: #closing書いておくといいとのこと。
    cursor = connection.cursor() #データベースとの接続で、作業スペースcursorを確保

    #audioテーブルが無い場合に新たに作成する。
    #オーディオ情報のバイナリデータdataはblob型で格納する。
    sql = '''
        CREATE TABLE IF NOT EXISTS audio (id integer primary key autoincrement, filepath text, data blob, sample_width integer, frame_rate integer, channels integer)
    '''
    cursor.execute(sql) #sql構文をcursorに反映

    #audioテーブルにオーディオ情報のレコードを追加する。
    #sql内で(?,?,?,?,?)とすることで、cursor.execute()の第二引数が?の変数になる。
    sql = '''
        INSERT INTO audio (filepath, data, sample_width, frame_rate, channels) values (?,?,?,?,?)
    '''
    data = [filepath, audio_data, audio_sample_width, audio_frame_rate, audio_channels]
    cursor.execute(sql, data)

    #データベースに変更を保存->接続を閉じる。
    connection.commit()
    connection.close()

以降では、ソースコードの詳細について解説していきます。

1.1. オーディオファイルをPydubモジュールを用いてオーディオ情報に変換

ソースコードを元に、ここではオーディオファイルをオーディオ情報に変換する方法について、詳細を記述していきます。

1.1.1. Pydubモジュールのインストール

まずは、必要となるモジュールをインストールしましょう。
オーディオファイルをオーディオ情報に変換するのに、今回はPydubモジュールをインストールします。
同時に、Pydubモジュールを用いるために必要な、ffmpegをインストールします。

#ffmpegをインストール
apt install ffmpeg

#Pydubをインストール
pip3 install pydub

1.1.2. PydubでオーディオファイルからAudioSegmentオブジェクトを作成

Pydubモジュールを用いて、オーディオファイルをオーディオ情報に変換してみます。

filepath = sys.argv[1]
extension = re.sub(r'.*[.]', '', filepath)
audio = AudioSegment.from_wav(filepath) if extension == 'wav'\
    else AudioSegment.from_mp3(filepath)

AudioSegmentオブジェクトとしてオーディオ情報を格納します。
audio = AudioSegment.from_wav(filepath)では、WAVファイルfilepathからオーディオ形成に必要となる情報を格納します。
MP3ファイルの場合はfrom_mp3(filepath)となります。

1.1.3. AudioSegmentオブジェクトからオーディオ情報を取得

次に、AudioSegmentからオーディオ情報を取り出しましょう。

audio_data = AudioSegment.get_array_of_samples(audio)
audio_sample_width = audio.sample_width
audio_frame_rate = audio.frame_rate
audio_channels = audio.channels

AudioSegmentから取り出すことができるオーディオ情報は以下の通りです。

data: オーディオのバイナリデータ
sample_width: ビット数, 2->16bit, 3->24bit,...
frame_rate: サンプリング周波数
channels: チャネル, ステレオ or モノラル

オーディオのバイナリデータは、AudioSegment.get_array_of_samples(audio)で入手できます。
これらの4つの情報は、データベースからオーディオファイルを復元する際にも、必要となる情報になります。

1.2. Sqlite3モジュールを用いてテーブルを作成し、オーディオ情報を格納

オーディオ情報を格納するためのデータベースおよびテーブルを作成し、オーディオファイルを格納していきます。

1.2.1. Sqlite3モジュールのインストール

まずは、データベースに接続するためにSQL構文を使いたいので、今回はSqlite3モジュールをインストールします。

#Sqlite3をインストール
pip3 install sqlite3

1.2.2. Sqlite3を用いてデータベースと接続する方法

次に、Sqlite3モジュールを用いて、オーディオ情報を格納するデータベースと接続してみます。

dbname = 'sample_audio.db' #DB名
with closing(sqlite3.connect(dbname)) as connection:
    cursor = connection.cursor()
    sql = '''
        SQLを記述
    '''
    cursor.execute(sql)
    sql = ''' #複数SQL構文がある場合は、cursor.execute()を繰り返す。
        SQLを記述
    '''
    cursor.execute(sql)

    connection.commit()
    connection.close()

with closing(sqlite3.connect(dbname)) as connectionとすることで、データベースと接続します。
dbnameというデータベースのパスがない場合は、自動生成してくれるようです。

次に、データベースとの接続connectionを作成した後に、connection.cursor()でcursorオブジェクトを作成します。
このcursorオブジェクトでSQLの操作を行います。
cursor.execute(sql)で、コメントアウトした構文sqlを、cursor上で反映します。

ここで注意ですが、cursorオブジェクト上でsqlを反映させただけでは、データベース上のテーブル本体には反映されません。
connection.commit()とすることで、初めてデータベース上のテーブルに反映されます。

最後にconnection.close()でデータベースとの接続を切ります。

1.2.2. SQL構文を記述してaudioテーブルを作成

次に、オーディオ情報を格納するためのテーブルを作成するSQL構文の内容を説明します。

sql = '''
        CREATE TABLE IF NOT EXISTS audio (id integer primary key autoincrement, filepath text, data blob, sample_width integer, frame_rate integer, channels integer)
'''

CREATE TABLE IF NOT EXISTS audioを記述して、audioテーブルがデータベース上に無い場合に作成するようにしています。
そのため、データベースやaudioテーブルを自ら削除しない限り、原則的にはこのSQLはスルーされます。

オーディオ情報を格納するカラムの定義は次になります。

id: 主キー, INT
filepath: オーディオファイルの名前, TEXT
data: オーディオのバイナリデータ, BLOB
sample_width: ビット数, INT
frame_rate: サンプリング周波数, INT
channels: チャネル, INT

また、メディアデータをデータベースに格納するとき、格納方法は二通り存在すると考えます。

・メディアデータのバイナリデータをBLOB型としてデータベースに直接格納する。
・メディアデータのパスをデータベースに格納し、ローカルの指定されたパスにメディアデータを配置する。

今回のテーブルでは、オーディオのバイナリデータはBLOB型を用いてデータベースの中に直接格納しています。

1.2.3. SQL構文を記述してaudioテーブルにオーディオ情報を格納

次に、作成したaudioテーブルにオーディオ情報を格納していきます。

sql = '''
        INSERT INTO audio (filepath, data, sample_width, frame_rate, channels) values (?,?,?,?,?)
    '''
data = [filepath, audio_data, audio_sample_width, audio_frame_rate, audio_channels]
cursor.execute(sql, data)

INSERT INTO audio (columns) values (?)によって、audioテーブルにレコードを挿入することができます。
また、SQLコメントアウトの中に(?)と記述することで、cursor.execute(sql, data)の第二引数リスト型dataが(?)の変数になります。

これによって、audioテーブルにオーディオ情報を格納することができます。

以上によって、オーディオファイルからオーディオ情報を作成し、データベースに格納することが出来ました。

2. データベースからオーディオ情報を取り出し、それをオーディオファイルに復元する

次にデータベースからオーディオファイルを復元してみましょう。手順としては、次のような概要になります。

2.1. Sqlite3モジュールを用いてデータベースからオーディオ情報を取得
2.2. Pydubモジュールを用いてオーディオファイルに復元

こちらも、Pythonスクリプト一個でまとめていきます。

2.0. 全体のソースコード

データベースからオーディオファイルに復元するソースコードは以下の通りです。

db_to_audio.py
import sqlite3
from contextlib import closing
from pydub import AudioSegment
import re
import sys

dbname = 'sample_audio.db'

#データベースと接続
with closing(sqlite3.connect(dbname)) as connection:
    cursor = connection.cursor()

    #audioテーブルから、指定したidのオーディオデータのレコードを取り出したい。
    sql = '''
            SELECT id, filepath, data, sample_width, frame_rate, channels FROM audio Where id=(?)
        '''
    cursor.execute(sql, sys.argv[1]) #コマンドライン引数でidを指定し、sql構文をcursorに反映
    output = cursor.fetchone() #取り出した1個のレコードをOUTPUTに格納
    connection.close()

#オーディオ情報を取り出す。
#id, パス, バイナリデータ, ビット数, サンプリングレート, チャネルを各変数に格納
[audio_id, audio_filepath, audio_data, audio_sample_width, audio_frame_rate, audio_channels] = output
print(audio_id, audio_filepath, type(audio_data), audio_sample_width, audio_frame_rate, audio_channels)

#バイナリデータ, ビット数, サンプリングレート, チャネルからAudioSegmentを作成
audio = AudioSegment(audio_data, sample_width=audio_sample_width, frame_rate=audio_frame_rate, channels=audio_channels)

extension = re.sub(r'.*[.]', '', audio_filepath) #ファイルの拡張子
audio.export('result_'+audio_filepath, format=extension) #拡張子を指定してオーディオファイルを出力

次の見出しから、ソースコードの主要部分について解説していきます。

2.1. Sqlite3モジュールを用いてデータベースからオーディオ情報を取得

データベースからオーディオファイルの復元に必要となるオーディオ情報を取得していきます。

sql = '''
            SELECT id, filepath, data, sample_width, frame_rate, channels FROM audio Where id=(?)
        '''
cursor.execute(sql, sys.argv[1])
output = cursor.fetchone()
[audio_id, audio_filepath, audio_data, audio_sample_width, audio_frame_rate, audio_channels] = output

SELECT columns FROM audio where id=(?)と記述することによって、audioテーブルからidが変数(?)と一致するレコードを取得し、cursorオブジェクトに格納します。
cursor.execute(sql, sys.argv[1])によって、変数(?)をコマンドライン引数で指定できるようにしました。

cursor.fetchone()によって、SELECT構文によって取得したオーディオ情報を、cursorオブジェクトから取得することができます。
ちなみに、レコードを複数まとめて取得したい場合、cursor.fetchall()によって複数取得することができます。

2.2. Pydubモジュールを用いてオーディオファイルに復元

データベースから取得したオーディオ情報をオーディオファイルに復元していきます。

audio = AudioSegment(audio_data, sample_width=audio_sample_width, frame_rate=audio_frame_rate, channels=audio_channels)

extension = re.sub(r'.*[.]', '', audio_filepath)
audio.export('result_'+audio_filepath, format=extension)

AudioSegment(data, sample_width, frame_rate, channels)に、データベースから取得したオーディオ情報を格納して、AudioSegmentオブジェクトを作成します。

最後に、AudioSegmentオブジェクトのexport(path, format)メソッドを用いて、オーディオファイルを出力します。
formatにはオーディオファイルの拡張子を渡します。

以上によって、データベースからSQLを用いてオーディオ情報を取り出し、それをオーディオファイルに復元することが出来ました。

3.終わりに・感想

今回のブログでは、『Pythonによってオーディオファイルからオーディオ情報を作成し、SQLを用いてデータベースに格納する』、『データベースからSQLを用いてオーディオ情報を取り出し、それをオーディオファイルに復元する』、この二点について手順を述べてきました。

今回は、データベースをローカル上に保存する形となりましたが、AWS等クラウド上にデータベースを格納して、アクセスするのがトレンドなのでしょうね。
また、オーディオのバイナリデータをBLOB型としてデータベースに直接保存しましたが、この方法は多数派なのでしょうか。。。
実務だと、クラウド上だと、メディアデータをどうやって保存するのが現実的でしょうか。。。
さらなる勉強が必要そうです。

こちらのブログが少しでもお役に立つことが出来れば幸いです。
それでは、最後までお読みいただきありがとうございました。

0
0
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
0
0