TOP > プログラミング関連 > SFML非公式翻訳トップ > 【非公式翻訳】SFML2.4チュートリアル > オーディオモジュール > 録音

[原文(公式)]

◆◆ 録音 ◆◆
SFML2.4 非公式日本語翻訳
サウンドバッファへ録音する
さぁ、オーディオデータをキャプチャーするぞ!
データをどこに入れようか? サウンドバッファ(= sf::SoundBuffer)だ!
つまり、サウンドバッファの使い道は2つあるのですね。再生、と、ファイルへの保存。

目的はとてもシンプルなインターフェイスで達成できるぞ。
さぁ、sf::SoundBufferRecorder クラスのお出ましだ:
// まずはオーディオ入力デバイスが使用可能かどうかを確認
if (!sf::SoundBufferRecorder::isAvailable())
{
    // エラー:このマシンでは録音できない。
    ...
}

// レコーダーを作成
sf::SoundBufferRecorder recorder;

// キャプチャー開始
recorder.start();

// 録音中……

// キャプチャー終了
recorder.stop();

// キャプチャーしたオーディオデータが入っているバッファを取り出す
const sf::SoundBuffer& buffer = recorder.getBuffer();
スタティック関数 SoundBufferRecorder::isAvailable は、 お使いのマシンで音声のキャプチャーができるかどうかを調べます。 false と言われたら、sf::SoundBufferRecorder クラスは使えません。

start() と stop() は、わかりますよね?

キャプチャー処理は裏側で独自のスレッドが走って、そこで行われます。 なので、start() と stop() の間で、他の処理を好きなように書くことができます。
キャプチャーが終了すると、サウンドバッファを取り出せるようになります。 中に音声データが入っているぞ。getBuffer() 関数を使うのだ。

さぁ、音声データが手に入った。
今こそ、キミの夢を叶えるのだ↓
サウンドバッファをコピーしておくのを忘れるな! 録音した音声データは、レコーダーオブジェクトを破棄したり再利用したりすると消えてしまうぞ!
入力デバイスを選択
音声を入力するデバイスがいくつもマシンにつながっている場合ってありますよね? たとえばマイクを接続しているとか、サウンドカードを増設しているとか、ウェブカメラのマイクとか。 そういう場合、どれを録音に使うのかを指定することができます。

音声の入力デバイスは名前で区別されます。 スタティック関数の SoundBufferRecorder::getAvailableDevices() を実行すると、 戻り値として std::vector<std::string> で文字列のリストが得られます。この中に、使用可能なデバイスの名前が入ってます。 その中から、使いたいデバイスを好きなように選んで、名前を setDevice() 関数に渡します。 デバイスの変更もいつでもできます。なんと、録音中にだってできちゃいます。

getDevice() を実行すると、現時点で使用しているデバイス名を確認できます。
自分でデバイスを指定しない場合はデフォルトのものが使われます。
何がデフォルトなのか知りたいときはスタティック関数の SoundBufferRecorder::getDefaultDevice() で取得だ。

ではサンプルをどうぞ:
// 使用できるサウンド入力デバイスの名前を取得
std::vector<std::string> availableDevices = sf::SoundRecorder::getAvailableDevices();

// デバイスを選択
std::string inputDevice = availableDevices[0];

// レコーダーを作成
sf::SoundBufferRecorder recorder;

// レコーダーにデバイスをセット
if (!recorder.setDevice(inputDevice))
{
    // エラー:デバイスを選択できませんでした……
    /*
    	エラー処理を書いたり書かなかったり
    */
}

// あとはいつも通りに録音。
/*
	録音処理を書く
*/

録音方法をカスタマイズ
サウンドバッファに溜め込む形式での録音がキミの望みではないならば、独自のレコーダークラスを作ることもできるぞ。 すると、音声を録音しながら処理することができるようになるのだ。 たとえばネットワークにストリーミングしたり、リアルタイムでの音声解析などが可能になるぞ。

独自のレコーダークラスを作るには、抽象クラス sf::SoundRecorder を継承するのだ。 何を隠そう、sf::SoundBufferRecorder は このクラスの使用例に過ぎないのだ。

このクラスを継承して、オーバーライドする必要がある仮想関数は1つだけ。onProcessSamples() だ。 この関数は音声サンプルのチャンクがキャプチャーされるたびに呼び出される。 つまり、この関数の内部こそ、キミの独自の処理を記述する場所なのだ。

デフォルトでは 100ms ごとのタイミングで音声サンプルが onProcessSamples() 関数に送られます。 この間隔は setProcessingInterval() 関数で変更できます。 たとえば、録音データをリアルタイムで処理したい場合はもっと短い間隔にするといいかもしれません。
ただし、この設定値はあくまでも「目安」です。 正確に設定値通りに処理されるとは限りません。実際にはブレます。あんまりアテにはしないでね。
あと2つ、必須ではないけれどオーバーライドできる仮想関数があるぞ。 onStart() と onStop() だ。
それぞれ、キャプチャーの開始時と終了時に呼ばれる。 初期化と後処理を記述しておくのに便利なのだ。
class MyRecorder : public sf::SoundRecorder
{
    virtual bool onStart() // オーバーライドは必須ではありません。
    {
        // キャプチャー開始前に必要な前処理を記述
        ...

        // true を返すとキャプチャー開始。false を返すとキャンセルされる。
        return true;
    }

    virtual bool onProcessSamples(const sf::Int16* samples, std::size_t sampleCount)
    {
        // 送られてきた音声サンプルのチャンクを、好きなように処理
        // ...

        // true を返すとキャプチャーを続行。false を返すと停止します。
        return true;
    }

    virtual void onStop() // オーバーライドは必須ではありません。
    {
        // キャプチャー終了後に必要な後処理を記述
        // ...
    }
}
isAvailable()、start()、stop() は ベースクラス sf::SoundRecorder に定義されてます。 なので、子クラスにも継承されます。 つまり、sf::SoundRecorder を継承しているならば、使い方は上で説明した sf::SoundBufferRecorder と同じなのだ。
if (!MyRecorder::isAvailable())
{
    // error...
}

MyRecorder recorder;
recorder.start();
...
recorder.stop();
スレッドにまつわる問題
録音は別スレッドで処理されます。 実際のところ、どこでどんな処理が行われているのかな?

onStart()関数は start() 関数から直接呼ばれます。 つまり、この関数は start()を呼んだのと同じスレッド内で実行されます。
一方、onProcessSample() と onStop() は SFML が内部的に作るスレッドから呼ばれます。

ということは……キミが作ったレコーダークラスが start()を呼んだスレッドからも、録音スレッドからも、 同時にアクセスされる可能性のあるデータを使っている場合は、 アクセスが衝突しないようにプロテクトしておく必要があります(ミューテックスか何かで)。 さもないと、おかしなことが起きてしまうかもしれません。データが壊れるとか、クラッシュするとか。

スレッドのことをよく知らないってお友達は、こちらのチュートリアルを読んでね。