DDSDチュートリアル(4)

録音もできます

 ここでは、TDDSDCaptureの使い方をご説明します。

 マイクなどの音声入力機器からの音声の波形を、リアルタイムで画面に表示するプログラムを、ここでは作成しましょう。音声に合わせてラインが「みよみよ〜」と踊るのは、案外見てて気持ちのいいものです(^^;)

まず下準備

 まずはいつもどおり、Delphi IDEから、「ファイル−アプリケーションの新規作成」を選び、空のプロジェクトを作成しましょう。

 そうしたら、DDSDコンポーネントと、ApplicationEventsコンポーネントを貼り付けましょう。ここでは、DDSDコンポーネントの名前は、DDSD1として、ApplicationEventsコンポーネントはApplicationEvents1として貼り付けましたので、以下、それにしたがって解説を続けます。

 さらに、フォームのサイズを600x300にし、DDDDコンポーネントの設定も、それに合わせてInitialScreenWidth = 600, InitialScreenHeight = 300 という事にします。

 以上を行うと、こんな感じになるでしょう。

 次に、一度に表示するサンプル(波形を構成するデータ)の数を決めておきますか。 ここでは、画面の横幅と同じ、600サンプルという事にしましょう。

 とりあえずは、このサンプル数を定数として宣言しましょう。

 場所は関数や手続きの外側なら何処でもいいのですが、とりあえず、Formの宣言の終わった下、var文のさらに真下に書き入れました。

var
  Form1: TForm1;

const
  NUM_SAMPLE = 600; //600サンプルを一度に表示

 

 録音に使う、TDDSDCaptureオブジェクトと、波形を描画するためのTBitmapオブジェクトを用意し、使えるようにしときます。

 フォームのCanvasに直接グラフを描くと、チラついて見づらいグラフになるので、TBitmapに一旦描きこんで、グラフが全部出来たらフォームに転送するようにするわけです。

type
TForm1 = class(TForm)
  DDSD1: TDDSD;
  procedure FormCreate(Sender: TObject);
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
  { Private 宣言 }
  public
  { Public 宣言 }
    Capture:TDDSDCapture;
    OffBMP:TBitmap;
end;

 まずは、こんな感じで、TDDSDCaptureオブジェクト変数を、フォームの定義に書き加えましょう。

 そうしたら、今度はTForm.OnCreateイベントで初期化します。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Capture:=TDDSDCapture.Create(DDSD1);
  OffBMP:=TBitmap.Create;
  OffBMP.Width:=ClientWidth;
  OffBMP.Height:=ClientHeight;

  //サポートしてる録音フォーマットなのか、チェックしてから
  if Capture.IsSupportedFormat(44100, 16, False) then begin
    //16ビット・44.1KHz・モノラルで録音、バッファは一度に表示するぶんだけ
    Capture.SetupCaptureBuffer(44100,16,False, NUM_SAMPLE * 2);
  end else if Capture.IsSupportedFormat(22050, 16, False) then begin
    //16ビット・22.05KHz・モノラルで妥協
    Capture.SetupCaptureBuffer(22050,16,False, NUM_SAMPLE * 2);
  end else begin
    //悪態をついて終わる
    MessageDlg('録音環境がヘボいです',mtWarning, [mbOK], 0);
    Capture.Free;
    Application.Terminate;
  end;

end;

 環境によって特定のサンプリングレートでの録音をサポートしていないことがあるので、一応チェックしておきましょう。そのための関数が、IsSupportedFormatメソッドです。このメソッドの返り値がTrueならば、そのフォーマットはサポートされているので、SetupCaptureBufferメソッドを呼んで、録音した結果を溜めておくバッファを確保させます。

 今回は、意地でもビットレートは16です(^^;) 44.1KHzで録音できないなら、22.05KHzで妥協するという事にしています。

 それから、終了時には録音を中止して、録音用オブジェクトを解放することにしましょう。

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Capture.Stop;
  Capture.Free;
end;

 

録音せよ!

 では、録音を開始しましょう。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Capture:=TDDSDCapture.Create(DDSD1);

  //サポートしてる録音フォーマットなのか、チェックしてから
  if Capture.IsSupportedFormat(44100, 16, False) then begin
    //16ビット・44.1KHz・モノラルで録音、バッファは一度に表示するぶんだけ
    Capture.SetupCaptureBuffer(44100,16,False, NUM_SAMPLE * 2);
    Capture.StartLoop;

  end else if Capture.IsSupportedFormat(22050, 16, False) then begin
    //16ビット・22.05KHz・モノラルで妥協
    Capture.SetupCaptureBuffer(22050,16,False, NUM_SAMPLE * 2);
    Capture.StartLoop;

  end else begin
    //悪態をついて終わる
    MessageDlg('録音環境がヘボいです',mtWarning, [mbOK], 0);
    Capture.Free;
    OffBMP.Free;
    Application.Terminate;
  end;

end;

 StartLoopメソッドで、録音を開始します。録音はバックグラウンドで常時行われる事になります。

 今回は取り扱いませんが、一定の時間だけ録音して、録音する間だけ処理を止めたい、という場合には、Startメソッドを使います。

 とりあえず、これでF9キーを押せば、録音している事にはなるのですが、ホントに録音しているのか、全く分かりませんね(^^;) まだ波形の表示ルーチンを作ってないからです。

 波形はいつでも表示されていて、音声入力に合わせてどんどん書き換わって欲しいので、Application.OnIdleイベントを利用します。

 

表示せよ

 そういうわけで、ApplicationEvents1.OnIdleイベントを書き換えます。
 
 波形を表示させるような手続きという事にしましょう。

procedure TForm1.ApplicationEvents1Idle(Sender: TObject;
  var Done: Boolean);
var
  i:Integer;
  Buf:Array[0..NUM_SAMPLE-1] of SmallInt;
  Points:Array[0..NUM_SAMPLE-1] of TPoint;
begin

  //波形の表示
  with OffBmp.Canvas do begin

    //キャンバスを塗りつぶす
    Brush.Color:=clBlack;
    Pen.Color:=clLime;
    FillRect(Rect(0,0, 600,300));

    Capture.BlockCopy(Capture.Position, @Buf[0], NUM_SAMPLE * 2); //Buf配列にコピー(1/10秒分)

    //グラフを描く…150とかは、グラフを描く位置を調節するための値です。
    For i:=0 to NUM_SAMPLE-1 do begin
      Points[i]:= Point(i, 150 - (Buf[i] div 256));
    end;
    Polyline(Points);
  end;

  Canvas.CopyRect(ClientRect, OffBmp.Canvas, ClientRect);

  //おかわり!
  done:=False;
end;


 グラフの描き方そのものはDelphiのTCanvasの説明を見ていただくとして、ここで重要なのは、水色の一行。 BlockCopyメソッドで、Buf配列変数に波形データを転送している部分だけです。

 BlockCopyで波形データを頂いたら、後はその値を元にグラフを描くだけ、FFTすればスペクトラムアナライザとかができます。

以上!

 

 で、DirectSoundCaptureを使用した、オシロスコープ(!?) が出来ました!

 結構、面白い…でしょ?

 これを利用して、音声入力をサポートするゲームやなんかが出来てしまうわけなんですね。言うのは簡単ですが、本格的にやるとなるとなかなか険しい道のりとなりそうです(^^;)