はじめに
Unity の Microphone API Microphone.Start()
を利用して AudioClip を作成する際、録音の途中で Microphone.End()
を実行しても、AudioClip が Microphone.Start()
メソッドの第3引数で指定した lengthSec (録音するオーディオクリップの長さ) の秒数で生成されていることに気が付きました。
class Microphone
{
public static AudioClip Start (string deviceName, bool loop, int lengthSec, int frequency)
{
..
};
}
引数 | 型 | 概要 |
---|---|---|
deviceName | string | 録音するデバイス名 (マイク) |
loop | bool | ループ録音を行うかどうか、true の場合は lengthSec に到達した時にクリップの最初に戻って録音を継続する |
lengthSec | int | 録音するオーディオクリップの長さ |
frequency | int | 録音するオーディオクリップの周波数 |
Microphone.End
をコールすることで録音自体は停止するのですが、AudioClip は Microphone.Start()
メソッドコール時の第三引数の秒数 (int) で生成されてしまっているようです。
例えば Microphone.Start(deviceName, false, 60, 44100)
を実行した後、10秒後に Microphone.End()
を実行すると、60秒の AudioClip が生成され、0~10秒間の間のみ、録音されており、11秒~60秒までは無音の状態が続くといったような状況です。
今回は録音されている部分のみを新たな AudioClip として新規作成する方法をご紹介したいと思います。
基礎知識
まず最初に Microphone クラス や AudioClip クラスを扱う上で、確実に理解しておきたい用語のご紹介をしたいと思います。
用語 | 解説 |
---|---|
サンプリング周波数 (frequency) | 人の声などのアナログの音声信号をデジタル信号へ変換する際に、1秒間にサンプリング(標本化)を行う頻度のこと。1秒間に実行されるサンプリング回数はヘルツ (Hz) の単位で表すことができます。サンプリング周波数が 44100 Hz の場合は、1秒間に 44100 回サンプリングが行われることを示します。 |
サンプル数 (samples) | サンプリング周波数に応じて取得された標本の数。サンプリング周波数 (Hz) × 音声データの秒数 (sec) = サンプル数 (標本数) となります。 |
チャンネル (channels) | 音の出力先となるスピーカーのこと。チャンネル数は、スピーカーの数を表しており、「スピーカー◯本分の音を収録している」と同時に「この音声を完全に再生するためには◯本のスピーカーが必要」ということでもある。 |
録音した部分だけを AudioClip として切り出す方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit.UI;
/// <summary>
/// HoloLens 2 で音声録音を行うサンプル
/// </summary>
[RequireComponent(typeof(AudioSource))]
public class RecorderSample : MonoBehaviour
{
// オーディオクリップの再生を行うための AudioSource
private AudioSource audioSource;
// 録音したデータを保存する AudioClip
private AudioClip audioClip;
// 録音したデータをトリミングした AudioClip
private AudioClip audioClip2;
// 録音を終了した際のサンプル位置
private int position;
[Header("Buttons")]
[SerializeField]
private Interactable startAndStopButton;
void Start()
{
// 同一オブジェクトにアタッチされている AudioSource を取得
audioSource = GetComponent<AudioSource>();
startAndStopButton.OnClick.AddListener( delegate { recording(); });
}
private void recording()
{
if (!Microphone.IsRecording(Microphone.devices[0]))
{
startRecord();
}
else
{
stopRecord();
}
}
/// <summary>
/// レコーディングを開始する
/// </summary>
private void startRecord()
{
// 録音の開始
audioClip = Microphone.Start(Microphone.devices[0], false, 60, 44100);
}
/// <summary>
/// レコーディングを終了する
/// </summary>
private void stopRecord()
{
// 録音を終了した際のサンプリングの数 (終了位置)
position = Microphone.GetPosition(Microphone.devices[0]);
Microphone.End(Microphone.devices[0]);
// Microphone.Start で録音したデータのサンプルを取得する
// まずサンプル数と同じ要素数のfloat配列を初期化する
// サンプルのデータ数は audioClip.samples * audioClip.channels で計算
float[] audioData = new float[audioClip.samples * audioClip.channels];
// audioClip.GetData で audioData の配列要素数だけサンプルを取得する
// 第二引数はオフセットサンプル数
// (1秒後から取得したい場合は、サンプリング周波数×1秒で 44100 が第二引数となる)
audioClip.GetData(audioData, 0);
// audioData から録音終了時までのサンプルデータを抜き出す
// まずサンプルデータを保存する配列を初期化する
float[] audioData2 = new float[position * audioClip.channels];
// audioData2 配列の要素数だけ audioData のデータをコピーする
for (var i = 0; i < audioData2.Length; i++)
{
audioData2[i] = audioData[i];
}
// 新しい AudioClip を生成する
audioClip2 = AudioClip.Create("audioClip2", position, audioClip.channels, 44100, false);
// 生成した AudioClip にサンプルデータを格納する
audioClip2.SetData(audioData2, 0);
// 生成した AudioClip2 を再生する
audioSource.clip = audioClip2;
Play();
}
/// <summary>
/// AudioSource にセットされている AudioClip を再生する
private void Play()
{
audioSource.Play();
}
}
空の GameObject を追加して、RecorderSample.cs
をアタッチします。
また、録音の開始/終了をするためのボタンを追加します。
今回は MRTK Prefabs の PressableButtonHoloLens2Toggle_48×48
を使用しています。
インスペクタービューで RecorderSample コンポーネントの StartAndStopButton
プロパティにボタンの GameObject をドラッグ&ドロップで追加したら、完了です。
実際に HoloLens 2 で動かしてみた動画は以下からご確認ください。
Refs
本記事を作成するにあたり、参考にさせていただいた Web サイト一覧です。
あわせてご覧いただけると理解が深まると思います。