5:マイク入力と録音 p5.sound.js サウンド

マイク入力

コンピュータのマイクから音声入力を取得し、それを使って円を浮かせます。

ノート:p5.AudioInは自分自身のp5.Amplitudeオブジェクトを含んでいるので、p5.Amplitudeを作成せずに、p5.AudioIn上からgetLevel()を呼び出すことができます。

// マイクからの入力を取得し、振幅値(音声レベル)を使って円を上下に動かす

let mic;
let isGetting = false;
let h;

function setup() {
    const canvas = createCanvas(300, 200);
    canvas.parent('sketch-holder');
    // AudioInオブジェクトを作成
    mic = new p5.AudioIn();
    // キャンバスのクリックで、マイク入力取得のオン/オフを切り替える
    canvas.mouseClicked(toggleMic);
    h = height;
    textSize(20);
}

function draw() {
    background(200);
    if (isGetting) {
        // 全体の音量を得る(0~1.0の間)
        const vol = mic.getLevel();
        // 音量にもとづく高さで円を描く
        h = map(vol, 0, 1, height, 0);
        // 取得した音量レベルをグラフで表示
        plot(frameCount, vol);
    }
    // 円を(h-25)の高さで描画
    fill(127);
    stroke(0);
    ellipse(width / 2, h - 25, 50, 50);

    // 文字を描画
    fill(255, 0, 0);
    noStroke();
    text('click to play/pause', 4, 20);
}

// マイク入力取得のオン/オフを切り替える
function toggleMic() {
    if (isGetting) {
        // AudioInオブジェクトをオフにする
        mic.stop();
        isGetting = false;
    }
    else {
        // AudioInオブジェクトをオンにする
        // デフォルトでは、.connect()しない(コンピュータのスピーカーに)
        // => スピーカーから音声は聞こえない
        mic.start();
        isGetting = true;
    }
}

function touchStarted() {
    // ユーザージェスチャでオーディオコンテキストを開始する
    userStartAudio();
}

下図は実行例です。下図をクリックすると、上記サンプルの実行画面が開きます。

解説

上記サンプルのようなWebアプリで、ユーザーのマイクを使用するには、いくつかクリアすべき問題があります。

まず、ユーザーがマイクをコンピュータに正しく接続し、マイクが使用可能状態である必要があります。これは当然ながらユーザー側の問題です。Windows10の場合には、[設定]->[システム]→[サウンド]の[入力]項で、使用するマイクが適切に設定されている必要があります。

次に、Webアプリがユーザーのマイクにアクセスすることを、ユーザーが許諾する必要があります。上記サンプルを(初めて)開き、キャンバスをクリックすると、ブラウザがユーザーに下図の許諾を求めます(左がChrome、右がFirefox)。ここでユーザーが許可するボタンをクリックすると、ブラウザはユーザーからの積極的な許諾を得たと判定して、マイクへのアクセスを実行します。

Chromeの場合には、1度許諾が得られるとChromeがそれを覚えるので、以降は得る必要はありません。この記憶は、Chromeの[設定]->[詳細設定]->[プライバシーとセキュリティ]->[サイトの設定]->[マイク]->[許可]にリストされているサイト名の右にあるゴミ箱のボタンをクリックすると、消去できます。

上記コードでは、p5.jsのコードに下記のコードを加えています。userStartAudio()関数は、ChromeやiOS Safariブラウザの自動再生ポリシーを解決します。

function touchStarted() {
    // ユーザージェスチャでオーディオコンテキストを開始する
    userStartAudio();
}
リファレンスメモ

userStartAudio()

説明

ユーザーに音声再生開始への制御を与えるのは優れたプラクティス(慣習)。このプラクティスは、Google Chromeのr70バージョンでの自動再生ポリシー(情報)とiOS Safari、そのほかのブラウザに強要される。

userStartAudio()はユーザージェスチャでAudioContextを開始する。この関数は、Yotam MannのStartAudioContextライブラリ(MIT Licence, 2016)を利用している。詳細はこちら(https://github.com/tambien/StartAudioContext)

ユーザージェスチャによるオーディオコンテキストの開始はuserStartAudio()を使えば簡単。オプションのパラメータを使用すると、オーディオコンテキストを開始する特定の要素を決めたり、オーディオコンテキストが開始された後に関数を呼び出すことができる。

シンタックス

userStartAudio([element(s)], [callback])

パラメータ

element(s) 要素|配列: この引数には、Element、Selector String、NodeList、p5.Element、jQuery Element、またはこれらを含む配列が指定できる(オプション)
callback 関数: AudioContextが開始したときに呼び出すコールバック(オプション)

戻り

Promise: AudioContextが’running’状態のときに解決されるPromiseを返す

p5.AudioIn

説明

入力、つまりコンピュータのマイクから音声を取得する。

マイクのオン/オフは、start()とstop()メソッドで切り替える。マイクがオンのとき、その音量はgetLevel()や、FFTオブジェクトの接続によって計測できる。

AudioInを聞きたい場合には、.connect()メソッドを使用する。AudioInはデフォルトで、フィードバック(ハウリング)を回避するため、p5.sound出力に接続しない。

ノート:AudioInは、特定のブラウザでサポートサービスえていないgetUserMedia/ Stream APIを使用する。Chromeブラウザでのアクセスはlocalhostとhttpsに制限されるが、http越しのアクセスも制限されるかもしれない。

シンタックス

new p5.AudioIn([errorCallback])

パラメータ

errorCallback 関数: AudioInへのアクセスにエラーが発生した場合に呼び出す関数。たとえば、SafariとiOSデバイスは現在、マイクへのアクセスを許可しない(オプション)

フィールド

input:
output:
stream:
mediaStream:
currentSource:
enabled:マイク/audioinソースにアクセスするには、クライアントの許諾が必要。デフォルトは。クライアントがアクセスを有効にするとtrueになる。
amplitude:入力振幅。

メソッド

start()

説明

オーディオ入力処理を開始する。これにより、getLevel()などのそのほかのAudioInメソッドが使用できるようになる。AudioInはデフォルトで、p5.soundの出力に接続しないことに注意。connect()メソッドを使用しない限り、何も聞こえない。

特定のブラウザはユーザーのマイクへのアクセスを制限する。たとえばChromeはlocalhostとhttps越しからのアクセスだけを許可する。このため、ブラウザがマイクへのアクセスを提供しない場合に呼び出されるerrorCallback関数を含めた方がよいかもしれない。

シンタックス

start([successCallback], [errorCallback])

パラメータ

successCallback 関数: 成功時に呼び出す関数の名前(オプション)
errorCallback 関数: エラーが生じた場合に呼び出す関数の名前。たとえばブラウザの中には、getUserMedia()をサポートしないものがある(オプション)

stop()

説明

AudioInをオフにする。AudioInが停止している場合には、getLevel()できない。ユーザーは再スタート時、アクセス許可を求められるかもしれない。

getLevel()

説明

AudioInのamplitude(音量レベル)を読み取る。AudioInクラスは、マイクの音量レベルの取得を容易にするために、独自のAmplitudeクラスのインスタンスを含んでいる。オプションのsmoothing値(0.0 < 1.0)も受け取る。注意:.getLevel()を使用するには、.getLevel()の前に.start()を呼び出しておくこと。

周波数スペクトル

リアルタイムでの音声入力の周波数スペクトルを視覚化します。

let mic, fft;
let isUserStarted = false;

function setup() {
    createCanvas(710, 400);
    noFill();

    // AudioInオブジェクトを作成してスタート
    mic = new p5.AudioIn();
    // mic.start();

    // FFTオブジェクトを作成して、入力ソースをマイクに設定
    fft = new p5.FFT();
    fft.setInput(mic);
}

function draw() {
    background(200);
    // FFTによるスペクトル解析
    let spectrum = fft.analyze();
    // spectrum配列を使って周波数スペクトルを描く
    beginShape();
    for (i = 0; i < spectrum.length; i++) {
        vertex(i, map(spectrum[i], 0, 255, height, 0));
    }
    endShape();
}

function touchStarted() {
    if (!isUserStarted) {
        // Promise解決後にAudioIn.start()を呼び出す
        userStartAudio().then(() => {
            print('Promise解決');
            mic.start();
        });
        isUserStarted = true;
    }
}

画面のクリックでスタートします。

解説

行っていることは、前の「4:サウンドの加工、作成 p5.sound.js サウンド」で見た「ノート エンベロープ」サンプルと同じで、解析対象が異なるだけです。

ブラウザに画面が表示され、ユーザーが画面をクリックすると、下図に示すようなマイクの許可ダイアログボックスが表示されます。

ユーザーが[許可]ボタンをクリックすることで、マイクへのアクセスを積極的に許可すると、ブラウザはマイクにアクセスできるようになり、画面にマイクからの音声入力の周波数スペクトルが描画されます。

userStartAudio()のPromiseについて

p5.AudioInのstart()は、userStartAudio()が(ユーザーのクリックなどで)呼び出されるまで実行されず、userStartAudio()が返すPromiseが解決されるのを待っています。したがってsetup()関数には次の2行が記述できますが、分かりやすい方法とは言えません。

mic = new p5.AudioIn();
mic.start();

通常は、touchStarted()関数内で次のようにするのが適切でしょう。これは、userStartAudio()関数が返すPromiseオブジェクトが解決されたら、Promiseのthen()メソッドの引数に指定された関数が呼び出される、という意味です。

userStartAudio().then(() => {
    mic.start();
});

変数promiseを作成しuserStartAudio()が返すPromiseを追ってみます。

function draw() {
  background(200);
  ...
  print(promise);
}

let promise;
function touchStarted() {
  if (!isUserStarted) {
    promise = userStartAudio().then(() => {
      print('Promise解決');
      mic.start();
    })
    isUserStarted = true;
  }
}

変数promiseは初めは未定義ですが、touchStarted()でuserStartAudio()が呼び出されると、ペンディング状態に入り、すぐに解決されていることが分かります。

この状態でユーザーがマイクの許可ダイアログで[許可]をクリックすると、マイクにアクセスできるようになります。ただし、ユーザーが[ブロック]ボタンをクリックしてアクセスを認めないと、下図下部の赤字(DOM例外:アクセス拒否)が発生し、アクセスできなくなります。

リファレンスメモ

setInput()

説明

FFT解析用の入力ソースを設定する。ソースが与えられない場合、FFTはスケッチ内のすべてのサウンドを解析する。

シンタックス

setInput([source])

パラメータ

source オブジェクト: p5.soundオブジェクト(またはWebAudio API ソースノード) (オプション)

touchStarted()

説明

touchStarted()関数は、タッチが登録される(画面が指などでタッチされる)たびに呼び出される。touchStarted()が定義されていない場合には、mousePressed()関数が定義されていればこれが代わりに呼び出される。

ブラウザには、さまざまなタッチイベントに関連付けられた異なるデフォルト動作がある。このイベントのデフォルト動作を回避したい場合には、メソッドの最後に”return false”を加える。

シンタックス

touchStarted([event])

パラメータ

event オブジェクト: オプションのTouchEventコールバック引数(オプション)

マイクのしきい値

音声入力の音量がしきい値を超えたら、イベント(矩形の描画)を引き起こします。

// Daniel Shiffmanの「Learning Processing」より
// learningprocessing.com
let input;
let analyzer;
let isUserStarted = false;

function setup() {
    createCanvas(710, 200);
    background(255);

    // AudioInオブジェクトを作成
    input = new p5.AudioIn();
}

function draw() {

    // スケッチ全体の音量を取得(0~1.0)
    let volume = input.getLevel();

    // しきい値
    let threshold = 0.1;
    // volume > 0.1なら、矩形をランダムな位置に描く
    // 音量が大きいほど、矩形のサイズは大きくなる
    if (volume > threshold) {
        stroke(0);
        fill(0, 100);
        rect(random(40, width), random(height), volume * 50, volume * 50);
    }

    // 音量としきい値をいっしょにグラフに表示する準備
    let y = map(volume, 0, 1, height, 0);
    let ythreshold = map(threshold, 0, 1, height, 0);

    noStroke();
    fill(175);
    // 棒グラフの背景部
    rect(0, 0, 20, height);
    // 棒グラフの音量部
    fill(0);
    rect(0, y, 20, y);
    // しきい値の横線
    stroke(0);
    line(0, ythreshold, 19, ythreshold);
}

function touchStarted() {
    if (!isUserStarted) {
        userStartAudio().then(() => {
            input.start();
        });
        isUserStarted = true;
    }
}

マイクへのアクセスを許可し、画面をクリックしてマイクの前で音を立てます。

解説

しきい値(閾値、threshold)とは「一般に境界線、境目のことを指し、ある値以上で効果が現れ、それ以下では効果が現れないこと」をいます(コトバンク:「しきい値」)。

上記サンプルでは、draw()関数内の次のコードで使われています。変数thresholdには0.1という比較的小さな値が使われていますが、大きな音のときに反応させたい場合には、0.5や0.8などをthresholdに割り当てます。

// しきい値
let threshold = 0.1;
if (volume > threshold) {
    // 音量がしきい値を超えているときに実行したい事柄
}

音声の録音と保存

音を録音し、それを再生して、クライアントのコンピュータに.wavファイルで保存します。そのためには、 p5.AudioIn(マイク/音源)とp5.SoundRecorder(録音)、 p5.SoundFile(再生と保存)の3つのオブジェクトが必要です。

let mic, recorder, soundFile;
let isUserStarted = false;
let canvas;

// 録音、停止、再生の状態を表す
let state = 0;

function setup() {
    canvas = createCanvas(400, 400);
    background(200);
    fill(0);
    text('Enable mic and click the mouse to begin recording', 20, 20);

    // p5.AudioInオブジェクトを作成
    mic = new p5.AudioIn();
    // p5.SoundRecorderオブジェクトを作成
    recorder = new p5.SoundRecorder();
    // 空のp5.SoundFileオブジェクトを作成。録音した音の再生に使用する
    soundFile = new p5.SoundFile();
}

function touchStarted() {
    if (!isUserStarted) {
        userStartAudio().then(() => {
            // オーディオ入力処理を開始
            mic.start();
            // マイクをレコーダーに接続
            recorder.setInput(mic);
            canvas.mousePressed(recordAndSave)
        });
        isUserStarted = true;
    }
}

function recordAndSave() {
    // p5.AudioInオブジェクトのenabledプロパティを使って、ユーザーがマイクを有効にしていることを確認
    // (有効担っていない場合には無音が録音される)
    if (state === 0 && mic.enabled) {
        // この後再生に使用するp5.SoundFileオブジェクトに録音するよう、レコーダーに伝える
        recorder.record(soundFile);
        background(255, 0, 0);
        text('Recording now! Click to stop.', 20, 20);
        // 状態が0から1に進む
        state++;
    }
    else if (state === 1) {
        // レコーダーを停止し、結果をsoundFileに送る
        recorder.stop();

        background(0, 255, 0);
        text('Recording stopped. Click to play & save', 20, 20);
        // 状態が1から2に進む
        state++;
    }
    else if (state === 2) {
        // 結果を再生する
        soundFile.play();
        // ファイルを保存する
        saveSound(soundFile, 'mySound.wav');
        // 状態が2から3に進む => 終了
        state++;
    }
}
解説
リファレンスメモ

p5.SoundRecorder

説明

再生するサウンドを録音し、.wavファイルとして保存する。p5.SoundRecorderはスケッチからのすべてのサウンドを録音するが、setInput()メソッドを使って特定の音源を割り当てることもできる。

record()メソッドはパラメータとしてp5.SoundFileを取る。再生が終了すると(指定された時間が経過するか、stop()メソッドによって)、p5.SoundRecorderはその録音内容を再生用のp5.SoundFileに送る。

メソッド

setInput()

説明

指定されたデバイスをp5.SoundRecorderに接続する。パラメータが与えられない場合、p5.SoundRecorerはスケッチから聞こえるすべてのp5.soundを録音する。

シンタックス

setInput([unit])

パラメータ

unit オブジェクト: p5.soundオブジェクトか、サウンドを出力するウェブオーディオユニット(オプション)

record()

説明

録音を開始する。その録音にアクセスするには、最初のパラメータにp5.SoundFileを指定する。録音が終わると、p5.SoundRecorderはその録音を再生用のp5.SoundFileに送る。オプションのパラメータには、録音の長さ(秒単位)と、p5.SoundFileへの送信が終わったときに呼び出されるコールバック関数が指定できる。

シンタックス

record(soundFile, [duration], [callback])

パラメータ

soundFile p5.SoundFile: p5.SoundFile
duration 数値: 時間(秒単位) (オプション)
callback 関数: 録音が完了したときに呼び出される関数の名前(オプション)

stop()

説明

録音を停止する。録音の停止時には、その結果が、.record()で与えられたp5.SoundFileに送られ、コールバック関数が指定されている場合にはその関数が呼び出される。

関数

saveSound()

説明

p5.SoundFileを.wavファイルとして保存する。ブラウザはユーザーに対し、自分のデバイスにファイルをダウンロードするよううながす。オーディオのサーバーへのアップロードには、p5.SoundFile.getBlobを使用する。

シンタックス

saveSound(soundFile, fileName)

パラメータ

soundFile p5.SoundFile: 保存したいp5.SoundFile
fileName 文字列: 保存する.wavファイルの名前

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA