Raspberry Piを使ってオーディオレコーダーを自作する方法

※この記事はPRリンクを含みます。

マイクアンプやADCにこだわりのある方にとってはレコーダー部分はなんでもよかったりします。

今回は当スタジオが現場でもよく採用している96kHz/24bitのオーディオレコーダーをRaspberry Piで作成するためのガイドを提供。

この記事で学べるスキル

基本となる録音アプリをPyAudioで自作し、使いやすく改造していきます。
Raspberry Piで動かすことによってコンパクト省電力でロケ収録できるようにします。

簡易紹介:こうたろう

1986年生まれ
音大卒業後日本、スウェーデン、ドイツにて音楽活動
その後金田式DC録音のスタジオに弟子入り
プログラミング(C)を株式会社ジオセンスのCEO小林一英氏よりを学ぶ
現在はヒーリングサウンド専門のピアニスト、またスタジオでは音響エンジニア、フォトグラファーなどマルチメディアクリエーターとして活動中
当記事ではプログラマー、音響エンジニアとして知識とスキルをシェアしていきます

基本準備とステップの確認

環境設定

  1. Raspberry Piのセットアップ: Raspberry Piに最新のRaspberry Pi OSをインストールし、必要なアップデートを実施します。
  2. Pythonの確認: Raspberry PiにPythonがインストールされていることを確認し、バージョンが最新であることを確認します。
  3. ライブラリのインストール: PyAudioや他の必要なPythonライブラリをインストールします。
  4. ステップ 2: オーディオインターフェースのセットアップ
  5. オーディオインターフェースの接続: Raspberry Piに96kHz/24ビット対応のUSBオーディオインターフェースを接続します。
  6. ドライバーの確認: オーディオインターフェースがRaspberry Piに認識されていることを確認します。
  7. ステップ 3: 基本的なオーディオ録音スクリプトの作成
  8. オーディオ録音機能の実装: PyAudioを使用して、96kHz/24ビットのオーディオ録音機能を実装します。これには、オーディオインターフェースからのデータストリームのキャプチャとファイルへの保存が含まれます。
  9. 録音パラメータの設定: サンプルレートを96kHz、ビット深度を24ビットに設定します。
  10. 録音ループの実装: オーディオデータを連続的に読み取り、ファイルに書き込む録音ループを実装します。

PyAudioの録音スクリプト

PyAudioライブラリを使用して、基本的な録音機能を実装します。

まず、PyAudioをインストールする必要があります。これは通常、以下のコマンドで行うことができます。

pip install pyaudio

PyAudioは音響やオーディオにとってはよく使うライブラリになりますので、他にもできることをいろいろアイディア出してみてもいいですね。

Pythonライブラリ〜Pydubを使ってオーディオファイルを変換するコード

ここからは実際のスクリプトをシェアしていきます。

デバッグ

プログラミング開発は全体の8割はデバッグ作業です。
環境によっても違いますし、なんらかのエラーは必ず出るという前提で全体のガイド的な役割としてみるくらいに思っておきましょう。

import pyaudio
import wave

# 録音パラメータの設定
FORMAT = pyaudio.paInt24  # 24ビットフォーマット
CHANNELS = 2  # ステレオ
RATE = 96000  # サンプルレート: 96kHz
CHUNK = 1024  # バッファサイズ
RECORD_SECONDS = 5  # 録音時間(秒)
WAVE_OUTPUT_FILENAME = "output.wav"  # 出力ファイル名

# PyAudioインスタンスの作成
audio = pyaudio.PyAudio()

# 録音用ストリームの開始
stream = audio.open(format=FORMAT, channels=CHANNELS,
                    rate=RATE, input=True,
                    frames_per_buffer=CHUNK)

print("録音を開始します...")

frames = []

# 指定された秒数だけ録音を行う
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("録音が終了しました。")

# ストリームを停止しクローズ
stream.stop_stream()
stream.close()
audio.terminate()

# WAVファイルへの書き込み
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(audio.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

print(f"録音データは {WAVE_OUTPUT_FILENAME} に保存されました。")

スクリプトの説明

  • pyaudio.paInt24: 24ビットのオーディオフォーマットを指定します。
  • CHANNELS = 2: ステレオ録音を意味します。
  • RATE = 96000: サンプルレートを96kHzに設定します。
  • CHUNK = 1024: バッファサイズを1024に設定します。これはストリームから一度に読み取るフレームの数です。
  • RECORD_SECONDS = 5: 録音する秒数を設定します。
  • WAVE_OUTPUT_FILENAME: 保存するファイルの名前です。

このスクリプトは、指定されたフォーマットでオーディオを5秒間録音し、”output.wav”というファイル名で保存します。録音時間やファイル名は必要に応じて変更してください。

また、実際の録音デバイス(オーディオインターフェース)がPyAudioと互換性があることを確認してください。

録音タイミングをキーボードで操作

自然界の収録であればタイマーで録音するのもいいかもしれませんが、セッション録音やロケ収録になるとそういうわけにもいきません。

import pyaudio
import wave
import threading
import sys

# 録音パラメータの設定
FORMAT = pyaudio.paInt24  # 24ビットフォーマット
CHANNELS = 2  # ステレオ
RATE = 96000  # サンプルレート: 96kHz
CHUNK = 1024  # バッファサイズ
WAVE_OUTPUT_FILENAME = "output.wav"  # 出力ファイル名

# 録音のフラグ
is_recording = False

def record():
    global is_recording

    # PyAudioインスタンスの作成
    audio = pyaudio.PyAudio()

    # 録音用ストリームの開始
    stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)

    print("録音を開始するには 's' を、停止するには 'q' を押してください。")

    frames = []

    while is_recording:
        data = stream.read(CHUNK)
        frames.append(data)

    # ストリームを停止しクローズ
    stream.stop_stream()
    stream.close()
    audio.terminate()

    # WAVファイルへの書き込み
    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(audio.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()

    print(f"録音データは {WAVE_OUTPUT_FILENAME} に保存されました。")

# 録音を制御するスレッド
def record_control():
    global is_recording
    while True:
        cmd = input()
        if cmd == 's':
            if not is_recording:
                is_recording = True
                threading.Thread(target=record).start()
            else:
                print("すでに録音中です。")
        elif cmd == 'q':
            if is_recording:
                is_recording = False
                break
            else:
                print("録音は開始されていません。")

# 録音コントロールスレッドの開始
record_control_thread = threading.Thread(target=record_control)
record_control_thread.start()
  • このスクリプトは、threadingモジュールを使用して、録音制御と録音処理を別々のスレッドで実行します。
  • ユーザーがキーボードで’s’を入力すると録音が開始し、’q’を入力すると録音が停止します。
  • is_recording変数は録音の状態を制御します。録音中はTrue、停止中はFalseです。

ファイルの保存先

ラズベリーパイで開発している場合、録音されたデータ(output.wav)は、スクリプトが実行されているディレクトリに保存されます。

通常、これはスクリプトファイルが存在する同じフォルダーです。

保存場所を変更したい場合は

  • 保存先を変更したい場合は、WAVE_OUTPUT_FILENAME の値をフルパスで指定します。例えば、/home/pi/recordings/output.wav とすると、recordings フォルダにファイルが保存されます。
WAVE_OUTPUT_FILENAME = "/home/pi/recordings/output.wav"

この場合、recordings フォルダは予め作成しておく必要があります。

また、パスはラズベリーパイ上での実際のディレクトリ構造に合わせて調整してください。

アクセスと再生

  • 保存されたファイルにアクセスするには、ラズベリーパイのファイルマネージャーを使用するか、コマンドラインから直接アクセスします。
  • 録音されたファイルを再生するには、ラズベリーパイで対応するオーディオプレーヤーを使用します。例えば、コマンドラインから aplay output.wav のようにコマンドを実行することで再生できます(ただし、aplayはPCMフォーマットのファイルのみサポートします)。

APIでDropboxに自動保存

現場で一番困ることといえばデータの保管です。

安全が確保されたスタジオであればいいですが、ロケや海外での収録などは、ハードで保管して持ち歩いているとハイリスク。

治安の悪い地域などでしたら盗難や強盗のリスクも忘れないでください。

アルゼンチンでタンゴを録音する場合は必ず何らかのバックアップは必要です。

アルゼンチンタンゴの録音(スタジオ関連サイト:青いタンゴ礁)

  1. Dropbox Appの作成:
    • Dropboxの開発者ページにアクセスし、「Create App」を選択します。
    • 必要なアクセス権限(通常はApp folderかFull Dropbox)を選択し、アプリに名前を付けます。
  2. アクセストークンの生成:
    • アプリの設定ページで、「Generated access token」を選択してトークンを生成します。このトークンは後でPythonスクリプトで使用します。

2. 必要なライブラリのインストール

PythonのDropbox SDKをインストールします。

pip install dropbox
import dropbox
import os

# Dropboxアクセストークンの設定
ACCESS_TOKEN = 'あなたのアクセストークン'

# Dropboxクライアントの初期化
dbx = dropbox.Dropbox(ACCESS_TOKEN)

def upload_file_to_dropbox(file_path, dropbox_path):
    """ Dropboxにファイルをアップロードする関数 """
    with open(file_path, "rb") as f:
        dbx.files_upload(f.read(), dropbox_path, mode=dropbox.files.WriteMode("overwrite"))

# 録音ファイルのパス
local_file = "output.wav"

# Dropbox上のパス(ファイル名を含む)
dropbox_file = "/output.wav"

# ファイルをアップロード
upload_file_to_dropbox(local_file, dropbox_file)

このスクリプトは、指定されたローカルファイルをDropboxにアップロードします。

アップロードするファイルのパスとDropbox上の目的のパスを適切に設定してください。

録音したファイルを読み込み、-0.2 dBにノーマライズ

ノーマライズ作業もPythonで終わらせておくと、編集の際に便利です。

Pythonでは、pydub ライブラリがこの種の処理に適しています。まずは pydub をインストールする必要があります。

pip install pydub
from pydub import AudioSegment
from pydub.effects import normalize

def normalize_audio(input_file, output_file, target_dBFS=-0.2):
    """ 指定されたdBFSにオーディオファイルをノーマライズする """
    audio = AudioSegment.from_file(input_file)
    normalized_audio = normalize(audio, headroom=target_dBFS)
    normalized_audio.export(output_file, format="wav")

# 録音ファイルのパス
input_file = "output.wav"

# ノーマライズ後のファイル名
output_file = "normalized_output.wav"

# ノーマライズ処理
normalize_audio(input_file, output_file)
  • normalize_audio 関数は、指定されたファイルを読み込み、-0.2 dBにノーマライズして別のファイル名で保存します。
  • AudioSegment.from_file でオーディオファイルを読み込みます。
  • normalize 関数でオーディオをノーマライズします。headroom パラメータで目標のdBFSを指定します。
  • normalized_audio.export でノーマライズされたオーディオを指定したファイル名で保存します。

ファイル名を自動で生成

ファイル名も自動でつけてしまいましょう。

日付に加えて時刻をつけておくことで、take番号よりも把握しやすいケースが筆者は多かったので、時刻を取得してつけておきます。

“今日の日付”と”録音終了時の時刻”を含めるには、Pythonの datetime ライブラリを使用して現在の日付と時刻を取得し、それをファイル名の一部として組み込むことができます。

from datetime import datetime
import pyaudio
import wave

# 録音パラメータの設定
FORMAT = pyaudio.paInt24  # 24ビットフォーマット
CHANNELS = 2  # ステレオ
RATE = 96000  # サンプルレート: 96kHz
CHUNK = 1024  # バッファサイズ
RECORD_SECONDS = 5  # 録音時間(秒)

# 録音の開始
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)

print("録音を開始します...")
frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("録音が終了しました。")

# ストリームを停止しクローズ
stream.stop_stream()
stream.close()
audio.terminate()

# ファイル名を生成(日付と時刻)
now = datetime.now()
filename = now.strftime("%Y%m%d_%H%M%S") + "_recording.wav"

# WAVファイルへの書き込み
wf = wave.open(filename, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(audio.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

print(f"録音データは {filename} に保存されました。")

コードの説明

  • datetime.now() を使用して現在の日付と時刻を取得します。
  • strftime("%Y%m%d_%H%M%S") を使用して日付と時刻を “YYYYMMDD_HHMMSS” の形式にフォーマットします。
  • この文字列をファイル名の一部として使用し、ファイル名を YYYYMMDD_HHMMSS_recording.wav の形式に設定します。

このスクリプトは、録音が終了すると、現在の日付と時刻をファイル名に含んだWAVファイルを保存します。

すべての統合コード

それでは、録音スクリプト(キーボードでスタートとストップを操作)に加えて録音が終了したら自動でノーマライズし、”今日の日付”と”録音終了時の時刻”のファイル名でDropboxとオフラインのパス(一般的なパス名にしています)に保存する統合コードを書いていきましょう。

import pyaudio
import wave
import threading
from datetime import datetime
from pydub import AudioSegment
from pydub.effects import normalize
import dropbox

# Dropbox APIの設定
ACCESS_TOKEN = 'YOUR_DROPBOX_ACCESS_TOKEN'  # ここにDropboxのアクセストークンを設定
dbx = dropbox.Dropbox(ACCESS_TOKEN)

# 録音パラメータ
FORMAT = pyaudio.paInt24
CHANNELS = 2
RATE = 96000
CHUNK = 1024
is_recording = False

# 録音処理関数
def record():
    global is_recording

    p = pyaudio.PyAudio()
    stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)

    print("録音を開始します。'q' を押して録音を停止してください。")

    frames = []
    while is_recording:
        data = stream.read(CHUNK)
        frames.append(data)

    # 録音停止
    stream.stop_stream()
    stream.close()
    p.terminate()

    # ファイル名の生成
    now = datetime.now()
    original_filename = now.strftime("%Y%m%d_%H%M%S") + "_recording.wav"

    # ファイルの保存
    wf = wave.open(original_filename, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()

    print(f"録音データは {original_filename} に保存されました。ノーマライズを開始します。")

    # ノーマライズ
    normalized_filename = now.strftime("%Y%m%d_%H%M%S") + "_normalized_recording.wav"
    normalize_audio(original_filename, normalized_filename)

    # Dropboxにアップロード
    upload_file_to_dropbox(normalized_filename, f'/{normalized_filename}')

def normalize_audio(input_file, output_file, target_dBFS=-0.2):
    audio = AudioSegment.from_file(input_file)
    normalized_audio = normalize(audio, headroom=target_dBFS)
    normalized_audio.export(output_file, format="wav")
    print(f"ノーマライズされたファイル {output_file} が保存されました。")

def upload_file_to_dropbox(file_path, dropbox_path):
    with open(file_path, "rb") as f:
        dbx.files_upload(f.read(), dropbox_path, mode=dropbox.files.WriteMode("overwrite"))
    print(f"ファイル {file_path} がDropboxにアップロードされました。")

def record_control():
    global is_recording
    while True:
        cmd = input()
        if cmd == 's':
            if not is_recording:
                is_recording = True
                threading.Thread(target=record).start()
            else:
                print("すでに録音中です。")
        elif cmd == 'q':
            if is_recording:
                is_recording = False
                break
            else:
                print("録音は開始されていません。")

# 録音コントロールスレッドの開始
record_control_thread = threading.Thread(target=record_control)
record_control_thread.start()

コードの説明

  • キーボードで s を押すと録音が開始し、q を押すと録音が停止します。
  • 録音が終了すると、ファイルは指定されたフォーマットで保存され、その後、ノーマライズされます。
  • ノーマライズされたファイルは、”YYYYMMDD_HHMMSS_normalized_recording.wav” の形式でローカルに保存され、Dropboxにもアップロードされます。

オーディオレベルの調整(Linux)

alsamixeramixer は、Linuxシステム(ラズベリーパイを含む)でオーディオ設定を行うためのコマンドラインツールです。

これらを使用してオーディオレベルや入力デバイスを設定する方法を以下に説明します。

alsamixerの使用方法

alsamixer は、ユーザーフレンドリーなテキストベースのグラフィカルインターフェースを提供します。

alsamixerを開くにはターミナルで以下のコマンドを実行します。

alsamixer

オーディオデバイスの選択

  • F6 を押して利用可能なサウンドカード(オーディオデバイス)のリストを開きます。
  • 使用したいオーディオデバイス(例えば、外部USBオーディオインターフェイス)を選択します。

オーディオレベルの調整

  • 矢印キー(←/→)を使って、調整したいチャンネル(例えば「Master」や「PCM」)を選択します。
  • 矢印キー(↑/↓)を使って、音量を調整します。

設定の保存と終了

  • Esc キーを押して alsamixer を終了します。
  • ターミナルで以下のコマンドを実行して、設定を保存します。
sudo alsactl store

amixerの使用方法

amixer は、コマンドラインから直接オーディオ設定を行うためのツールです。

オーディオデバイスの情報を表示

amixer

特定のコントロールの音量を設定

  • 音量を設定したいコントロール(例:PCM)の音量を変更するには、以下のようなコマンドを使用します。
amixer set PCM 50%

マイク入力の有効化/無効化

  • マイクのミュート(無効化)やアンミュート(有効化)を行うには、以下のようにします。
amixer set Mic nocap  # マイクをミュート
amixer set Mic cap    # マイクをアンミュート

tkinterでGUIを調整

tkinter を使用してラズベリーパイで録音アプリケーションのGUIを作成するには、以下のステップに従って進めます。

ここでは、基本的な録音コントロール(録音開始、停止)とステータス表示を含むGUIの例を示します。

tkinterのインポート

tkinter はPythonに標準で含まれているGUIツールキットです。

これを使用してGUIを作成します。

GUIの基本構造の作成

GUIのウィンドウ、ボタン、ラベルなどのウィジェットを作成し、レイアウトします。

録音の機能との統合

録音機能をGUIに統合し、ボタン操作で録音を開始・停止できるようにします。

以下は、録音と停止のボタンを持つ簡単なGUIの例です。

import tkinter as tk
from threading import Thread
import pyaudio
import wave

class AudioRecorder:
    def __init__(self, root):
        self.root = root
        self.is_recording = False
        self.frames = []

        # PyAudioインスタンス
        self.audio = pyaudio.PyAudio()

        # GUIの構成
        self.status_label = tk.Label(root, text="準備完了")
        self.status_label.pack()

        self.record_button = tk.Button(root, text="録音開始", command=self.start_recording)
        self.record_button.pack()

        self.stop_button = tk.Button(root, text="録音停止", command=self.stop_recording, state=tk.DISABLED)
        self.stop_button.pack()

    def start_recording(self):
        self.is_recording = True
        self.record_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        self.status_label.config(text="録音中...")

        # 録音スレッドの開始
        self.record_thread = Thread(target=self.record)
        self.record_thread.start()

    def stop_recording(self):
        self.is_recording = False
        self.stop_button.config(state=tk.DISABLED)

    def record(self):
        stream = self.audio.open(format=pyaudio.paInt16, channels=2, rate=44100, input=True, frames_per_buffer=1024)
        self.frames.clear()

        while self.is_recording:
            data = stream.read(1024)
            self.frames.append(data)

        # 録音終了
        stream.stop_stream()
        stream.close()

        # ファイル保存
        with wave.open("recording.wav", 'wb') as wf:
            wf.setnchannels(2)
            wf.setsampwidth(self.audio.get_sample_size(pyaudio.paInt16))
            wf.setframerate(44100)
            wf.writeframes(b''.join(self.frames))

        self.record_button.config(state=tk.NORMAL)
        self.status_label.config(text="録音完了")

root = tk.Tk()
app = AudioRecorder(root)
root.mainloop()
  • AudioRecorder クラスは録音の機能とGUIを管理します。
  • 録音は別のスレッドで行われ、GUIの応答性を保持します。
  • 「録音開始」と「録音停止」のボタンをクリックすることで、録音を制御します。
  • 録音状態はラベルで表示され、録音が完了するとファイルに保存されます。

ラズベリーパイで開発したPythonベースのオーディオ録音アプリケーションをデプロイするには、いくつかのステップを踏む必要があります。

ここでは、アプリケーションをラズベリーパイにインストールし、実行可能な形で配置するための基本的な手順を説明します。

1. コードの最終化

  • アプリケーションのコードを最終確認し、必要に応じて最後の修正やテストを行います。

2. 必要なライブラリのインストール

  • アプリケーションで使用している外部ライブラリ(PyAudio, PyDub, Dropbox SDK など)がラズベリーパイにインストールされていることを確認します。

3. スクリプトの転送

  • 開発環境からラズベリーパイにスクリプトを転送します。これはSCP(Secure Copy Protocol)、FTP(File Transfer Protocol)、または直接USBドライブを介して行うことができます。

4. 実行権限の付与

  • スクリプトファイルが実行可能であることを確認します。ターミナルで以下のコマンドを実行します。
chmod +x my_script.py

自動起動の設定(オプション)

  • アプリケーションをラズベリーパイの起動時に自動的に実行したい場合は、crontabsystemd サービスを設定します。
    • Crontabの例:
crontab -e

起動時にスクリプトを実行するための行を追加:

@reboot python3 /path/to/my_script.py

Systemdサービスの例: /etc/systemd/system/myapp.service というサービスファイルを作成し、以下の内容を記述:

[Unit]
Description=My Audio Recorder App

[Service]
ExecStart=/usr/bin/python3 /path/to/my_script.py

[Install]
WantedBy=multi-user.target

その後、以下のコマンドでサービスを有効化:

sudo systemctl enable myapp.service sudo systemctl start myapp.service

まとめ

今回はラズベリーパイを使ってハイレゾレコーダーを自作するサンプルコードを紹介しました。

保存場所や、WIFIを使った機能の追加など、市販品ではなかなか難しいレコーダーを自作して、現場の業務を安全かつ円滑に進められるように工夫していってください。