0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[MR Dev Tips #7] Unity Microphone クラスで正確な録音時間の AudioClip を生成する方法

Last updated at Posted at 2023-08-04

はじめに

Unity の Microphone API Microphone.Start() を利用して AudioClip を作成する際、録音の途中で Microphone.End() を実行しても、AudioClip が Microphone.Start() メソッドの第3引数で指定した lengthSec (録音するオーディオクリップの長さ) の秒数で生成されていることに気が付きました。

Microphone.cs
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 として切り出す方法

RecorderSample.cs
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 をドラッグ&ドロップで追加したら、完了です。

image.png

実際に HoloLens 2 で動かしてみた動画は以下からご確認ください。

Refs

本記事を作成するにあたり、参考にさせていただいた Web サイト一覧です。
あわせてご覧いただけると理解が深まると思います。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?