音声データの取得について
これはご質問者さんのおっしゃるAudioSource.GetOutputData
やMonoBehaviour.OnAudioFilterRead
が妥当ではないかと思います。
AudioSource.GetOutputData
のデータについて
すでにリファレンス をご覧になったかと思いますが、おそらくここで言及されている「ブロック」は単にデータの塊ということだろうと思います。サイズ1024の配列で受け取れば、今再生されている点周辺の1024個のデータ(不確かですが、「今まさに再生されている瞬間から直近1024点」とは言い切れないと思いますが...未調査です)が得られるはずです。
MonoBehaviour.OnAudioFilterRead
のタイミングについて
リファレンス によると...
OnAudioFilterRead is called every time a chunk of audio is sent to the filter (this happens frequently, every ~20ms depending on the sample rate and platform).
OnAudioFilterReadはオーディオのチャンクがフィルターへ送られるたびに実行されます(サンプルレートやプラットフォームにもよりますが、~20ミリ秒おきに頻繁に実行されます)。
※「チャンク」も「データの一塊」のようなとらえ方でいいように思います。オーディオファイルフォーマットの中の、各種データのためのそれぞれの領域を「チャンク」と呼んだりもしますが、そちらは文脈的にそぐわないように感じます。
Also note that OnAudioFilterRead is called on a different thread from the main thread (namely the audio thread) so calling into many Unity functions from this function is not allowed (if you try, a warning shows up at run time).
また、OnAudioFilterReadはメインスレッドとは異なるスレッド(オーディオスレッド)上で呼び出されることにご注意ください。よって、この関数内では大部分のUnity関数の呼び出しは許されません(行おうとすれば実行時に警告が表示されます)。
とのことで、Update
とは異なる周期のようですね。
波形の記録はこんな感じでいかがでしょうか?
C#
1 using System . Collections . Generic ;
2 using UnityEngine ;
3
4 [ RequireComponent ( typeof ( AudioSource ) ) ]
5 [ RequireComponent ( typeof ( LineRenderer ) ) ]
6 public class Sound_Mic : MonoBehaviour
7 {
8 public int ChannelToRead ;
9
10 [ HideInInspector ] private int count ;
11 private readonly object lockObject = new object ( ) ;
12 private LineRenderer lr ;
13 private bool playing ;
14 private readonly List < float > recordedData = new List < float > ( ) ;
15
16 private void Start ( )
17 {
18 this . lr = this . GetComponent < LineRenderer > ( ) ;
19 this . count = 0 ;
20 this . lr . positionCount = this . count ;
21 this . playing = true ;
22 }
23
24 // OnAudioFilterReadは別スレッドで動作するようなので、こちらのメソッドでは
25 // データの記録のみ行い、LineRendererの操作は行わない
26 private void OnAudioFilterRead ( float [ ] data , int channels )
27 {
28 // dataには各チャンネルのデータがインターリーブ状態で入ってくる
29 // ステレオ音声ならLRLRLRLR...
30 // (ChannelToRead >= channelsにはならないものとして範囲チェックは省略)
31 lock ( this . lockObject )
32 {
33 if ( this . playing )
34 {
35 for ( var i = 0 ; i < data . Length ; i += channels )
36 {
37 this . recordedData . Add ( data [ i + this . ChannelToRead ] ) ;
38 }
39 }
40 }
41 }
42
43 // Update内でrecordedDataを見て、新たなデータが追記されていればラインを延ばす
44 private void Update ( )
45 {
46 lock ( this . lockObject )
47 {
48 if ( ! this . playing )
49 {
50 return ;
51 }
52
53 // 無制限にラインを延ばし続けると、じきにハングアップするので
54 // 適当な時間でやめる
55 if ( Time . timeSinceLevelLoad > 0.1f )
56 {
57 this . playing = false ;
58 }
59 var newCount = this . recordedData . Count ;
60 if ( newCount > this . count )
61 {
62 this . lr . positionCount = newCount + 1 ;
63 for ( var i = this . count ; i < newCount ; i ++ )
64 {
65 this . lr . SetPosition ( i , new Vector3 ( i * 0.1f , this . recordedData [ i ] * 10 , 5 ) ) ;
66 }
67
68 this . lr . SetPosition ( newCount , new Vector3 ( newCount * 0.1f , 100 , 5 ) ) ;
69 }
70 }
71 }
72 }
ですが、なにぶん毎秒数万点のデータがやってきますので、LineRenderer
による描画では頂点数が多すぎて、すぐに動かなくなってしまうでしょう。データを間引くなどの対策が必要かと思います。
また、パフォーマンスは考慮していませんので、再生中の音にプツプツとノイズが入るかもしれません。多少改良しても、そもそも大量のデータをリアルタイムに操作するのはやっかいですので、解消しきれない可能性があります。
それよりも、もし「今再生中の音の記録」ではなくてもいいのなら、元のオーディオクリップからデータを持ってきてグラフを描くというのも一案かと思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/02 16:40
2019/01/02 19:39 編集
2019/01/03 05:18