C++Builder によるマルチメディア操作

以下の記述はプログラム作成時にメモしていったものを並べたものです。今となると、意味不明のところもありますが後は皆さんの努力で補って下さい。

目次

第1部 基礎編
1、はじめに
2、Builderスタート
3、最も簡単なプログラム
4、描画とマウスの操作
5、カメラからの画像取り込み
6、画像ファイルの操作
7、コンピュータ間通信UDP
8、コンピュータ間通信TCP/IP
9、ジョイスティックの操作
10、ロボット制御(LEGO)
11、録音再生
12、音声スループログラム
13、カメラ画像の実時間操作(映像スルー)
14、音声処理の応用
 (1)音声スルー
 (2)インターネット電話もどき
 (3)スペクトルアナライザ
15、インタラクティブアート(映像スルーの応用)

1、C++BUILDERの世界
従来プログラムと言うとプログラムの頭から実行を開始し、一連の処理を行う1本の流れを表わすものだった。これに対しC++Builderのプログラムは、イベント(例えばボタンを押す)で動き出す複数のサブプログラムの集まりである。
プログラムの動きを管理するのはフォームと呼ばれるもので、大きなプラントの操作盤をイメージすればよい。(プラントの操作盤には様々な装置を動かすボタンやメーター、モニタ画面などが付いている)

C++Builderのフォーム(操作盤)にもボタンやモニタ画面を貼り付ける。これらフォームに貼り付けられるものはコンポーネントと呼ばれる。

コンポーネントは主に次の3つに分かれる
1)          サブプログラムを起動するためのもの:ボタン
2)          文字や画像を表示するためのもの:メモ、エディットボックス、イメージ


以上の2つは実行時にフォーム上の画面に表れる。これに対し、
3)          サブプログラム起動用であるが実行時に画面に表れない(プログラミング時には表れる)コンポーネント:コンピュータ間通信用ソケットなど

フォームと、これらコンポーネントは、それぞれ属性値表と発生可能イベント表を持っている。

C++Buliderのプログラミングは次の4ステップで行われる。
1)          フォームに必要なコンポーネントを配置する
2)          フォーム及びコンポーネントの属性、使用イベントを決める
3)          イベントを決めると(イベント空白欄のwクリック)イベント発生時のサブプログラム作成画面になるので、サブプログラムを作成
4)          実行

2、新しいプログラム(プロジェクト)を作るには

前準備
Builder内のprojectsフォルダに、新たに作成するプログラムを入れるフォルダを用意する。
黙っていると新プロジェクトはprojectsフォルダ内に出来てしまい、次々と出来るので混乱する。

Builder立ち上げ後
ステップ1:ファイル.すべて閉じるをし、次にファイル.新規作成でオブジェクトリポジトリが出るのでアプリケーションを選んでOKを打つ。
ステップ2:ファイル.プロジェクトに名前を付けて保存をすると、最初プログラム名を 聞いてくるので答えると、次にプロジェクト名を聞いてくるので記入。

3、最も簡単なプログラム作成 simpleフォルダ)

ステップ3:コンポーネントパレットから、ボタンをフォームにドラッグ。
  (最初に作られたボタンはプログラム内ではButton1となる)
オブジェクトインスペクタでボタン上文字を変えてもよいCaptionの右に書く。
ボタンを押したら何かするようにイベントのOnClickの右の空白をWクリック(これによりプログラム中にボタンが押されたときする仕事を書く所ができる。

フォームの上のボタンをWクリックすると、ボタンが押せれた時の処理を書く場所が開くので、今回とりあえず{ }の間にEdit1-> Text="PUSHED"; と書く。

次にまたコンポーネントパレットからEditボックスをフォームにドラッグ。(これはEdit1である)
メニューから実行.実行で実行する。

ステップ4:最後に保存
 ファイル.上書き保存

このプロジェクトを他の計算機にもっていくときは c++,~.cpp,~.exeをもっていけばよい。

プログラム例

//---------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit1->Text = "PUSHED";
}

//---------------------------------------------------------

4、マウスで指定した位置に丸を描く
goというフォルダに作った。Formにimage(additionalメニューから指定)作り、imageのイベントのOnMouseDownでマウスのクリック点を得て、丸を描く。

ボタン3つ用意
ボタン1を押すと碁盤もどきを描く
ボタン2を押すとユーザが作るGO()関数を実行
マウスをimage上でクリックするとそこに黒丸を描く
ボタン3を押すと描いた黒丸を消す

プログラム例
//-----------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "go.h"
//-----------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

void GO();
int GLX,GLY;

TForm1 *Form1;
//-----------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//-----------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  int i;
  Image1->Canvas -> Brush ->Color =clYellow;
 Image1->Canvas -> FillRect(Rect(10,10,300,300));
  Image1->Canvas -> Brush ->Color =clBlack;
 for(i=20; i<300; i=i+30) {
    Image1-> Canvas -> MoveTo(i,20);
   Image1-> Canvas -> LineTo(i,280);
    Image1-> Canvas -> MoveTo(20,i);
    Image1-> Canvas -> LineTo(280,i);
  }
}
//----------------------------------------------------------
void __fastcall TForm1::Image1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
 Image1->Canvas->Ellipse(X,Y,X+10,Y+10);
   GLX=X;
  GLY=Y;
}
//-----------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 GO();
}
//----------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 Image1->Canvas -> Brush ->Color =clYellow;
 Image1->Canvas->Ellipse(GLX,GLY,GLX+10,GLY+10);
 Image1->Canvas -> Brush ->Color =clBlack;
}
//--------------------------------------------------------
void GO()
{
};
//--------------------------------------------------------

5.カメラからの画像取り込み
ビデオキャプチャ画像を表示するプログラム(vcap)
(カメラからの画像をFormの上に垂れ流すプログラム。カメラはキャプチャーボードに付けてもよいし、USBポートにつけてもよい)

マニュアルはBuilder-Help-MS_SDKヘルプ-Microsoft_Multimedia_Programmer's_Ref
カメラ等マルチメディア機器を使わない時は〜.hファイルには触れないでよいが、使うときは〜.hファイルを呼び出し(~.cppが表示されている画面でファイル-開く-〜.hをやる)次の2点を行う
(1) #include を加える
(2)ユーザ宣言部に ハード名、ステータス、パラメータを宣言
次に、いつものイベントを指定してプログラムをする。ボタン等なにもないとき画像を表示するには、FormのOnCreateイベントの空白をwクリックして〜.cppファイルの
void __Fastcall Tform1::FormCreate(…)部をプログラム。

オンラインマニュアルにビデオキャプチャーを動かす(最低)3命令というのがあるので(但し、マニュアルの3命令目は画像をファイルに記憶させる方法なのでこれをプレビューに変える)これを参考にウインドウを用意し、コネクトし、プレビューすれば、画像が垂れ流される。

プログラム

Unit1.hは以下
//-------------------------------------------------------------
#ifndef vcapH
#define vcapH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//#include <Clipbrd.hpp>
//#include <ExtCtrls.hpp>
//#include <ExtDlgs.hpp>
#include <vfw.h>
//#include <Dialogs.hpp>
//-------------------------------------------------------------
class TForm1 : public TForm
{
__published:              // IDE 管理のコンポーネント
        void __fastcall FormCreate(TObject *Sender);

private:    // ユーザー宣言
HWND vcHWND;
CAPSTATUS CapStatus;
CAPTUREPARMS CaptureParms;
public:                   // ユーザー宣言
        __fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------
#endif
//--------Unit1.h END

Unit1.cppは以下
//---------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "vcap.h"
//---------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 vcHWND = capCreateCaptureWindow ( "My Own Capture Window",
     WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, Form1->Handle, 0);
 SendMessage (vcHWND, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L);
 capPreviewRate(vcHWND,33);
 capPreview(vcHWND,TRUE);
//SendMessage (vcHWND, WM_CAP_SEQUENCE, 0, 0L);オンラインマニュアルにある
//     カメラ画像のバッファーへの取り込み
}
//---------------------------------------------------------------

6、画像ファイルの操作
ファイルの取り扱いが主なのでイメージファイルの略でifというフォルダーに格納

(1)画像ファイル処理I
カメラからのプレビュー画像を画面に表示。
プログラムは基本的にvcapと同じで、ボタンを押した時、プレビュー画面をクリップボードに取り込み、それを表示するところを加えたもの。

画像表示と処理用にimageコンポを左下に配置。ここで注意がいるのはSendMessage(…)という命令。これはカメラ画像の扱いに頻繁に使われる。
FormCreatルーチンではカメラ(キャプチャー)を表示ウインドウに接続しているし、ボタンを押した時の処理ではプレビュー画像をクリップボードに取り込んでいる。
(注:画像を取り込むにはクリップボードしかなさそう)

プログラム
//------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "vcap.h"
//------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 vcHWND = capCreateCaptureWindow ( "My Own Capture Window",
     WS_CHILD | WS_VISIBLE , 0, 0, 240, 200, Form1->Handle, 0);
 SendMessage (vcHWND, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L); 
 capPreviewRate(vcHWND,33);

 capPreview(vcHWND,TRUE);
//SendMessage (vcHWND, WM_CAP_SEQUENCE, 0, 0L);
}
//----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 SendMessage(vcHWND,WM_CAP_EDIT_COPY,0,0L);//画像をClipboardに取り込み
 Image1->Picture->LoadFromClipboardFormat(CF_BITMAP,
   Clipboard()->GetAsHandle(CF_BITMAP),0);//取り込んだ画像表示
}
//----------------------------------------------------------------

(2)画像ファイル処理I

これは画像をいろいろな形態で使うことを前提にいろいろなボタンを用意した。
縦に3つ並んだ左側のボタンは上から

(1)        プレビューをクリップボードに取り、それを左下のimage1部に表示
(2)        image1に表示された画像をビットマップファイルに落としそれをimage2に表示
(3)        image1から1ピクセルづつimage2に表示(これは画像処理に役立つ)

右側の縦に2つ並んだボタンはプレビュー画像を画面に表示することなしにビットマップファイルに落とす処理。ボタンの意味は上から

(1)        プレビューをビットマップファイルに落とす。
(2)        それを確認のためにimage2に表示する。

コメント:クリップボードから(表示せずに)直接主メモリ配列に落とせると処理が速くなると思うがこれはやりかた不明。
//-------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "vcap.h"
//-------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//-------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 vcHWND = capCreateCaptureWindow ( "My Own Capture Window",
      WS_CHILD | WS_VISIBLE , 0, 0, 240, 200, Form1->Handle, 0);
                      //キャプチャウインドウ生成
 SendMessage (vcHWND, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L);
                      //カメラとキャプチャウインドウ接続
 capPreviewRate(vcHWND,33);   //表示速度指定
 capPreview(vcHWND,TRUE);    //プレビュー画面表示
}
//------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 SendMessage(vcHWND,WM_CAP_EDIT_COPY,0,0L);//画像をClipboardに取り込み
 Image1->Picture->LoadFromClipboardFormat(CF_BITMAP,   
  
  Clipboard()->GetAsHandle(CF_BITMAP),0);//取り込んだ画像表示

}
//---------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 Image1->Picture ->SaveToFile("gomi.bmp");     //Image1をファイルに保存
 Image2->Picture ->LoadFromFile("gomi.bmp");   //保存したファイルを表示
}
//----------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
int x,y;
unsigned int p;
for(x=0; x<200; x++)     //Image1から Image2  にピクセルごとにコピー
  for(y=0; y<200;y++)
  {
    p=(unsigned int)Image1->Canvas->Pixels[x][y];
    Image2->Canvas->Pixels[x][y]=(TColor)p;
  }
}
//--------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 //クリップボードをファイルに格納
 Graphics::TBitmap *Bitmap=new Graphics::TBitmap();
  SendMessage(vcHWND,WM_CAP_EDIT_COPY,0,0L);
  if(Clipboard()->HasFormat(CF_BITMAP)){
    Bitmap->LoadFromClipboardFormat(CF_BITMAP,
              Clipboard()->GetAsHandle(CF_BITMAP),0);
    Bitmap->SaveToFile("gomi.bmp");
  }
  Bitmap->Free();
}
//----------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
 Image2->Picture ->LoadFromFile("gomi.bmp");
}
//----------------------------------------------------------------

7.コンピュータ間通信UDP
コンピュータ間通信には、遅いが確実なTCP/IPと、速いが雑なUDPがある。

udp高速だが雑な転送
UDPではデータを送る側はSendBuffer又はSendStreamでデータを送り、受ける側は ???
これはhelpに有ったプログラム例でボタンが押されると文字kkkkを相手に送る。
送る側と受ける側は同じプログラムだがNMUDP1のプロパティのリモートアドレスが違う。
Internetタブにあるアイコンを右クリックしてヘルプを出しこれを頼りにプログラムする)

プログラム
//--------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  char C[5]={"kkkkk"};
  NMUDP1-> SendBuffer(C,5, 5);
}
//--------------------------------------------------------------------
void __fastcall TForm1::NMUDP1DataReceived(TComponent *Sender,
      int NumberBytes, AnsiString FromIP)
  {
    if(NumberBytes<6)
      {char Buff[6];
      int j;
      NMUDP1->ReadBuffer(Buff,6,j);
      Buff[6]=0;
      Memo1->Lines->Add("FromIP:"+FromIP +"Bytes"+NumberBytes+":"+Buff);
        }
   else
      Memo1->Lines->Add("overflow");
   }

//-----------------------------------------------------------------
void __fastcall TForm1::NMUDP1DataSend(TObject *Sender)
{
 Memo2->Lines->Add("sent");
}
//-----------------------------------------------------------------
void __fastcall TForm1::NMUDP1Status(TComponent *Sender, AnsiString status)
{
 Memo2->Lines->Add(status);
}
//-----------------------------------------------------------------
void __fastcall TForm1::NMUDP1InvalidHost(bool &handled)
{
 AnsiString S;
 S=NMUDP1->RemoteHost;
 if(InputQuery("Invalid host","Specify valid host name",S))
  {
   NMUDP1->RemoteHost=S;
   handled = TRUE;
 }
}
//----------------------------------------------------------------

Internetタブにあるudpを使用。InternetタブにあるNMMsg,NMStrm,NMUDPを使うとソケットは不要。
*ボタンやエディットボックスのようなコンポはフォームに張り付いて実行時に見えるので分かりやすいが、InternetのUDPコンポ等(ソケットやNMMsgは実行時には見えないコンポなので最初は不安。
もし、ボタンとエディットボックスとメモを用意し、ボタンを押すとエディットボックスの中身がudpで送られるものを作るには、フォームにメモ、ボタン、エディットボックス、NMUDPを用意し、ボタンのonclickイベントをwクリックしてSendBufferを書き、NMUDPの受信イベントをwクリックして受信プログラムRecieveBufferを書けばよい。
ただしNMUDPのプロパティのリモートホスト、ローカルポート6668、リモートポート6668
(プロパティのreportlevelはヘルプの例とおりに動かない、このためイベントのOnBufferInvaridも動作せず)
これを参考にSendBufferとSendStreamを使った簡単な例を作ったudp1,udp2

簡易例題1 udp1
ボタンを押すとsBuffの中身が相手計算機に送られる
0.    ファイル0−全て閉じる
1.          ファイルーアプリケーションの新規作成
2.          FormにMemoを持ってくる。
3.          FormにButtonをもってくる。
4.          ボタンのイベントでOnClickの白箱をwクリックするとクリックで動き出すプログラム入力になる。;
   void __fastcall Tform1::Tform1(…)
5.              InternetからFormへNMUDPをもってくる(Formのあいている場所ならどこでもよい)。
6.              NMUDPのプロパティ設定
   LocalPort6668,RemoteHost202.16.209.84,RemotePort6668
7.              NMUDPのイベント作成OnDataReceivedをwクリックしてプログラム作成
   Void __fastcall Tform1::NMUDP1DataReceived(…)部
8.実行

//--------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "udp1.h"
//--------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{ }
//--------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  char sBuff[10]={"senddata//"};
  NMUDP1->SendBuffer(sBuff,10,10);
}
//-------------------------------------------------------------------
void __fastcall TForm1::NMUDP1DataReceived(TComponent *Sender,
      int NumberBytes, AnsiString FromIP)
{
   char rBuff[11];
   int j;
   NMUDP1->ReadBuffer(rBuff,11,j);
   rBuff[j]=0;
   Memo1->Lines->Add(rBuff);
}
//------------------------------------------------------
簡易例題2 udp2
ボタンを押すとエディットボックスの中身が相手計算機に送られる
0.          ファイル0−全て閉じる
1.          ファイルーアプリケーションの新規作成
2.FormにEditboxをもってくる。
3.FormにMemoを持ってくる。
4.FormにButtonをもってくる。
5.FormにInternetからNMUDPをもってくる。
6.ボタンをクリックしイベントタブでOnClickの白箱をwクリックするとクリックで動き出すプログラム入力になる。;
void __fastcall Tform1::Tform1(…)部作成
(ヘルプのSendStream例参照)
7.          MUDPのプロパティ設定
   LocalPort6668,RemoteHost202.16.209.84,RemotePort6668
8.              NMUDPのイベント作成OnDataReceivedをwクリックしてプログラム作成
 Void __fastcall Tform1::NMUDP1DataReceived(…)部
9.          実行
(ここはStreamの扱い方がわかる。Editboxの中身は可変長なのでAnsiStringで取り出す。
これをchar配列に入れるにはWideCharがあるようだが使い方不明。上のプログラムでC[1]が何なのかは不明)
//-----------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//-----------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-----------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 TMemoryStream *MyStream= new TMemoryStream();
 int I;
 AnsiString C;
 C=Edit1->Text;       //Editboxの中身をCに入れる
 I=C.Length();
 MyStream->Write(&C[1],I);
 NMUDP1->SendStream(MyStream);
}
//-----------------------------------------------------------------

void __fastcall TForm1::NMUDP1DataReceived(TComponent *Sender,
      int NumberBytes, AnsiString FromIP)
{
  AnsiString C;
  TMemoryStream *MyStream= new TMemoryStream();
  NMUDP1-> ReadStream(MyStream);
  C.SetLength(NumberBytes);
  MyStream->Read(&C[1],NumberBytes);
  Memo1->Lines->Add(C);
}
//-----------------------------------------------------------------

8.コンピュータ間通信TCP/IP
2台のコンピュータ間で通信するには、2台にそれぞれ、仕事を頼む側のクライアントと、頼まれる側のサーバという役割を持たせる。クライアントがデータを送り、サーバが受け取る。以下の例では、1台のコンピュータにクライアントとサーバの機能を用意しているが、これは双方向からデータを送るためである。
msg

C++Builderによるインターネットプログラムという本にあったもの。これはTCP/IPによるチャットプログラム。

簡易版

例題msg1
ボタンを押すとcの中身が相手計算機に送られる
0.ファイル0−全て閉じる
1.ファイルーアプリケーションの新規作成
2.          FormにMemoを持ってくる。
3.          FormにButtonをもってくる。
4.          Form にInternetタブからNMMsgとNMMSGServを持ってくる。
Formのあいている場所ならどこでもよい)。
(チャットは双方向なので両方必要)
5.          NMMsgとNMMSGServのプロパティ設定
NMMsgはPort6711,Host202.16.209.84,〈通信相手〉
NMMSGServはPort6711,Host空白
6.              NMMSGServのイベント作成OnMSGをwクリックしてメッセージ到着時に動く

プログラム作成
 Void __fastcall Tform1::NMMSGServ1MSG(…)部
6.5  NMMsgのイベントはなし。
7.          Buttonのイベント設定
PostItの引数は原則ストリングだが、この例は配列を使用した。
ただし配列の場合最後にNULLを入れないとごみが送られる。
8.          実行

プログラム
//--------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//--------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 char c[3]={"333"};
 c[2]=NULL;
 NMMsg1->PostIt(c);
}
//--------------------------------------------------------------
void __fastcall TForm1::NMMSGServ1MSG(TComponent *Sender,
      const AnsiString sFrom, const AnsiString sMsg)
{
 Memo1->Lines->Add(sMsg);
}
//----------------------------------------------------

例題msg2
ボタンを押すとエディットボックスの中身が送られる。

エディットボックスを持ってきた以外上と同じ。

(これと同様のことをソケットを使ってやれる。その時クライアントとサーバそれぞれにソケットを用意し、それぞれOpenし、クライアント側でClientSocket->Socket->SendText("CharData");で送り出し、サーバ側では(一寸変な感じだが)OnClientReadイベントでReceiveTextで受ける。OnClientReadはテキストが送られてくると動き出すらしい。自分で確認はしてない。)

//------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//------------------------------------------------------
_fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//-------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 NMMsg1->PostIt(Edit1->Text);
}
//-------------------------------------------------------
void __fastcall TForm1::NMMSGServ1MSG(TComponent *Sender,
      const AnsiString sFrom, const AnsiString sMsg)
{
 Memo1->Lines->Add(sMsg);
}
//-------------------------------------------------------

9.ジョイスティックによる入力(joyフォルダー)

先ずジョイスティックをUBSポートに接続し、ドライバのインストールを行う。

下下準備
1)〜.hファイルに#include を入れる。
2)〜.hファイルのユーザ宣言部に次の2つの変数宣言を入れる
JOYCAPS joycaps;
JOYINFOEDX joyinfoex;
joycapsはジョイスティックの色々な属性を示す変数の集合である。例えば dwXmin、dwXmaxがあり、前者にはX方向の最小値、後者はX方向の最大値が入っている。
これらは、joyGetDevCapsで取り込まれる。
joyinfoexは、ジョイスティックの現在の位置を記憶する構造体で、joyGetPosEx命令で値がセットされる。

下準備
ジョイスティックを使用するにはjoyinfoexに初期設定が必要。これはフォームクリエイト時にJoyint()という関数を用意して行うとよい。これはコンポーネントの属性をいじるので、〜.hで関数宣言し、〜.cppでもTform1の下の関数と言う風に宣言する

Joyint()での仕事は
1)joyinfoexのサイズ指定
joyinfoex.dwSize=sizeof(JOYINFOEX);
2)ジョイスティックの機能指定joyinfoex.dwFlags=JOY_RETURNBUTTONS|JOY_RETURNX|JOY_RETURNY;
3)ジョイスティック数の確認(接続されているかのチェックも含む)

if(joyGetNumDevs()==0)MessageBox(Handle,"NoStick","TITLE",MB_OK);
joyGetDevCaps()で特徴パラメータを取り込む
4)中心点(midlex)の設定(余裕度(flexx)の設定を含む)

これより本番
ジョイスティックの情報を計算機に取り込むには、定期的に取り込む方法と、変化時に取り込む方法がある。ここでは定期的に取り込む方法を記述。フォームにシステムタブを開いてタイマーを持ってくる。タイマーイベントをwクリックしてプログラムを作成(単位ms)。
ジョイスティックの各変数値は
joyGetPosEx(JOYSTICKID1,&joyinfoex);
joyinfoexに取り込み
joyinfoex.dwYpos等で取り込んだ値を使用する。
(注:Exが付くのは6軸高機能スティック用)

FAQ:

変数の値をエディットボックスに出すには。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 int fromdata=10;
 int todata;
 AnsiString S;
 Edit1-> Text = IntToStr(fromdata);
 S=Edit1->Text;
 todata= StrToInt(S);
//参照C++Builder4オフィシャルコースウエアp.64
}

プログラム
//-------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//-------------------------------------------------------------------
#pragma package(smart_init)

#pragma resource "*.dfm"
TForm1 *Form1;
unsigned int midlex=0,flexx=0;
//--------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)

        : TForm(Owner)
{
}
//--------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 JoyInit();
}
//--------------------------------------------------------------------
 void __fastcall TForm1::JoyInit()
 {
   joyinfoex.dwSize=sizeof(JOYINFOEX);
   joyinfoex.dwFlags=JOY_RETURNBUTTONS|JOY_RETURNX;
   if(joyGetNumDevs()==0)MessageBox(Handle,"NoJoyStick","TITLE",MB_OK);
  joyGetDevCaps(JOYSTICKID1,&joycaps,sizeof(JOYCAPS));
   midlex=(joycaps.wXmax+joycaps.wXmin)/2;
   flexx=midlex/10;
 }

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
   joyGetPosEx(JOYSTICKID1,&joyinfoex);
   if(joyinfoex.dwXpos > midlex+flexx) Edit1->Text ="RIGHT"; //midle=32K
   if(joyinfoex.dwXpos < midlex-flexx) Edit1->Text ="LEFT";  
if(joyinfoex.dwButtons==JOY_BUTTON1) Edit1->Text="BUTTON"; //buttonA

}
//------------------------------------------------------------------

10.ロボット制御

レゴを走らせるには、朱色のLEGOマークのコンポーネントがC++Builderのメニュー(ActiveX)に必要。これがないマシンは以下の下準備を行う。(日経ソフトウエア2000年5月号参照)。
ただしこの記事はVB用なので少し処理が異なるので注意。

下準備
1.コンピュータにROBOTICS INVENTION SYSTEM 1.5をインストール(これ1枚でOK)
注:私のコンピュータでは(Dirext X がインストールできなかったという警告が出た)
2.ロボット(RCX)にファームウエアをダウンロード
(すでにやってあったのでやってない)
3.C++Builder でSpirit.ocx を使えるようにする。
  (1)先ず、C++Buliderを立ち上げる
  (2)コンポーネント-ActiveXコントロールの取り込み、を行うと
     日経ソフト2000.5、図5に近いダイアログボックスが登場する。
  (3)LEGO PbrickControl,OLE Control moduleをハイライトし下のほうにある
     インストールを押す。
  (4)コンパイルが必要といってくるのでコンパイルするとエラーがでる。
     エラーを起こしているところが2重定義してあるようなので、
10行位 #undef …で打ち消すようにする。
(注)私の場合、ProjectフォルダにLib,Bplフォルダがないのでエラーになったので作成
  (5)修正すると、裏の方にコンパイルマークが左上に付いたウインドウがあるので
     ここで再コンパイル。
  (6)(5)と同じウインドウでインストールを指示するとC++Builder-ActiveXメニューの
LEGOマークができる。
  (7)〜.ocxは保存が必要。

プログラム

VbとC++で記法が違うので要注意)
startボタンで発進し、stopボタンで停止するとういう簡単なもの。

フォームが開いたときInitComm()でCOM1を開設
  (COM2を使うにはSpirit1->ComPortNo=COM2;としておいてからInitComm()を出す)
     "02"はモータ番号
RCXはオンにするだけで正しく動いた。

プログラム

//------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SPIRITLib_OCX"
#pragma resource "*.dfm"
TForm1 *Form1;
//------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
{
}

//------------------------------------------------------------------

void __fastcall TForm1::stopClick(TObject *Sender)
{
  Spirit1->Off (WideString("02"));
}
//-------------------------------------------------------------------
void __fastcall TForm1::startClick(TObject *Sender)
{
  Spirit1->On (WideString("02"));
}
//-------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  Spirit1->InitComm();
}
//-------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  Spirit1->CloseComm();   
}
//-------------------------------------------------------------------

11.録音再生

音声録音再生には次の3つの方法がある。
1.超簡易型(高レベルオーディオサービスと呼ばれる)
2.簡易型(MCIウエーブフォームオーディオサービス)
3.基本型(低レベルウエーブフォームオーディオサービス)

3の低レベルは入力音声をメモリ上に保存するのを基本にしている。
他の1.2.は音声データをディスクに置いて処理する。

(1)低レベルウエーブフォームオーディオサービス
動くまでに大変苦労した。不明点も多いが一応動く(後は自分で勉強してください)
先ず準備しなくてはいけないもの。

<初期化ボタン>
1.音声を録音するメモリ領域
  これをGlobalAllocで確保するが、これだけではメモリ領域が用意されハンドルが返されるだけなので、GlobalLockで確保した領域へのポインタを得る。
  (LPSTRは char * と同じである)
  (最後にGlobalUnlockを忘れぬように)
2.入出力デバイスオープン時にはデバイスの特性を記述した表(構造体LPWAVEFORMATEX型)を用意する。そしてデバイスには普通WAVE_MAPPERというものを指定する。これが何者かよくわからない。WAVE_MAPPERがインストールされてないマシンもあるらしいが私が調べた(ブートストラップ誌No.5の3節Echo.exeを走らせると入出力デバイス一覧が出て、そこで WAVE_MAPPERがサポートするデバイスの一覧が見られる。
 ここで、大切なのは1サンプルあたりのバイト数)

  プログラムではpcmwfとなっている。
3.IOデバイスの動作状況やデバイスが音声を録音するメモリ領域(バッファと呼ばれる)を示す構造体WAVEHDR(ヘッダー)を準備する。

本番

<録音ボタン>  
  waveInPrepareHeader()でヘッダーを準備し、
  waveInAddBuffer()でデバイスにバッファーを送り
  waveInStart()で録音開始

<再生ボタン>
  ヘッダーを入力用から出力用に変換
  入力デバイスをストップ
  再生開始
<終了ボタン>
  後始末 

プログラム
//-------------------------------------------------------------
#include <vcl.h>

#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------
#pragma package(smart_init)

#pragma resource "*.dfm"
#include <mmsystem.h> //...
//----------
#define  N_SAMPLES_PER_SEC             22050L                // 1秒あたりのサンプル数
#define  N_CHANNELS                        1            // チャネル数(2はステレオ)
#define  N_BYTES_PER_SAMPLE                2
#define  N_BYTES_PER_DATA_BLOCK        4096L
#define  N_BITS_PER_CHANNEL             16

HGLOBAL h;
LPSTR lpBuffer;
WAVEHDR wavehdr;                                       // データブロック
HWAVEIN hwi;                                                                            // ウェーブオーディオ入力用ハンドルHWAVEOUT hwo;                                                                      // ウェーブオーディオ出力用ハンドルPCMWAVEFORMAT pcmwf = {                                               // Waveデータの形式
              {
                            WAVE_FORMAT_PCM,                            // フォーマット:PCM
                          N_CHANNELS,                                       // チャネル数
                N_SAMPLES_PER_SEC,                         // 1秒あたりのサンプル数
                N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8),   // 1秒あたりのバイト数                N_BYTES_PER_SAMPLE                            // 1サンプルあたりのバイト数
              },
                   N_BITS_PER_CHANNEL                            // 1サンプル・1チャネルあたりのバイト数
                };
//-----------
TForm1 *Form1;
//----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
{
}
//-----------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
   wavehdr.dwFlags= 0;                           // 処理終了フラグをリセット
   wavehdr.dwBufferLength = N_BYTES_PER_DATA_BLOCK;                  // バッファサイズを設定
   waveInOpen(&hwi,
              WAVE_MAPPER,
              (LPWAVEFORMATEX)&pcmwf,
               0,
                NULL,
                  0);
   waveOutOpen(&hwo,
                     WAVE_MAPPER,
                    (LPWAVEFORMATEX)&pcmwf,
                      0,
                      NULL,
                      0);
   h= GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, N_BYTES_PER_DATA_BLOCK);
   lpBuffer= (char *) GlobalLock(h);
   // 確保する
   wavehdr.lpData= lpBuffer;
   waveInPrepareHeader(hwi, &wavehdr, sizeof(wavehdr));              // 準備のための API を呼び出す
}
//-------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   waveInAddBuffer(hwi, &wavehdr, sizeof(wavehdr));
   waveInStart( hwi );       
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
   waveInUnprepareHeader( hwi, &wavehdr, sizeof(wavehdr));
   waveInStop(hwi);
   waveInReset(hwi);
   waveOutPrepareHeader( hwo, &wavehdr, sizeof(wavehdr));
   waveOutWrite( hwo, &wavehdr, sizeof(wavehdr));
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
   waveOutUnprepareHeader( hwo, &wavehdr, sizeof(wavehdr));
   GlobalUnlock(h);
   GlobalFree(lpBuffer);
   waveInClose(hwi);
   waveOutReset(hwo);
   waveOutClose(hwo);
}
//----------------------------------------------------------------------

(2)低レベルウエーブフォームオーディオ(ファイルセーブロード)
上記プログラムにファイルセーブとファイルロードボタンを付加
<セーブ>でメモリの音声データをファイルに落とす
<ロード>でファイルからメモリへ持ってくる
注意:このプログラムは参考文献(ブートストラップNo.5)をもとに作った。
参考文献には誤りがあるので注意

//--------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------
#pragma package(smart_init)

#pragma resource "*.dfm"
#include <mmsystem.h> //...
//-----------
#define  N_SAMPLES_PER_SEC             22050L                // 1秒あたりのサンプル数
#define  N_CHANNELS                        1            // チャネル数(2はステレオ)
#define  N_BYTES_PER_SAMPLE                2
#define  N_BYTES_PER_DATA_BLOCK        4096L
#define  N_BITS_PER_CHANNEL             16

HGLOBAL h;
LPSTR lpBuffer;
WAVEHDR wavehdr;                                       // データブロック
HWAVEIN hwi;                                                                            // ウェーブオーディオ入力用ハンドルHWAVEOUT hwo;                                                                      // ウェーブオーディオ出力用ハンドルPCMWAVEFORMAT pcmwf = {                                               // Waveデータの形式
             {
                  WAVE_FORMAT_PCM,                            // フォーマット:PCM
                   N_CHANNELS,                                       // チャネル数
         N_SAMPLES_PER_SEC,                         // 1秒あたりのサンプル数
                  N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8),   // 1秒あたりのバイト数
                  N_BYTES_PER_SAMPLE                            // 1サンプルあたりのバイト数
              },
                      N_BITS_PER_CHANNEL                            // 1サンプル・1チャネルあたりのバイト数                };
//-----------
TForm1 *Form1;
//---------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)

        : TForm(Owner)
{
}
//---------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
   wavehdr.dwFlags= 0;                           // 処理終了フラグをリセット
   wavehdr.dwBufferLength = N_BYTES_PER_DATA_BLOCK;                  // バッファサイズを設定
   waveInOpen(&hwi,

             WAVE_MAPPER,
              (LPWAVEFORMATEX)&pcmwf,
             0,
              NULL,
              0);

   waveOutOpen(&hwo,
                     WAVE_MAPPER,
                     (LPWAVEFORMATEX)&pcmwf,
                      0,
                      NULL,
                      0);
   h= GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, N_BYTES_PER_DATA_BLOCK);
   lpBuffer= (char *) GlobalLock(h);
   // 確保する
   wavehdr.lpData= lpBuffer;
   waveInPrepareHeader(hwi, &wavehdr, sizeof(wavehdr));              // 準備のための API を呼び出す
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   waveInAddBuffer(hwi, &wavehdr, sizeof(wavehdr));
   waveInStart( hwi );       
}

//------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
   waveInUnprepareHeader( hwi, &wavehdr, sizeof(wavehdr));
   waveInStop(hwi);
   waveInReset(hwi);
   waveOutPrepareHeader( hwo, &wavehdr, sizeof(wavehdr));
   waveOutWrite( hwo, &wavehdr, sizeof(wavehdr));
}
//-------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
   waveOutUnprepareHeader( hwo, &wavehdr, sizeof(wavehdr));
   GlobalUnlock(h);
   GlobalFree(lpBuffer);
   waveInClose(hwi);
   waveOutReset(hwo);
   waveOutClose(hwo);
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
HMMIO hmmio;
hmmio=mmioOpen("voice.wav", NULL,MMIO_WRITE | MMIO_CREATE);
MMCKINFO mmckinfoParent;
mmckinfoParent.fccType=mmioFOURCC('W','A','V','E');
mmioCreateChunk(hmmio,&mmckinfoParent,MMIO_CREATERIFF);
MMCKINFO mmckinfoSubchunk;
mmckinfoSubchunk.ckid=mmioFOURCC('f','m','t',' ');
mmioCreateChunk(hmmio,&mmckinfoSubchunk,0);
mmioWrite(hmmio, (char *)&pcmwf,sizeof(pcmwf));
mmioAscend(hmmio,&mmckinfoSubchunk,0);
mmckinfoSubchunk.ckid=mmioFOURCC('d','a','t','a');
mmioCreateChunk(hmmio,&mmckinfoSubchunk,0);
mmioWrite(hmmio,wavehdr.lpData,wavehdr.dwBufferLength);
mmioAscend(hmmio,&mmckinfoParent,0);
mmioClose(hmmio,0);
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button6Click(TObject *Sender)
{
HMMIO hmmio;
hmmio=mmioOpen("voice.wav", NULL,MMIO_READ);
MMCKINFO mmckinfoParent;
mmckinfoParent.fccType=mmioFOURCC('W','A','V','E');
mmioDescend(hmmio,&mmckinfoParent,NULL,MMIO_FINDRIFF);
MMCKINFO mmckinfoSubchunk;
mmckinfoSubchunk.ckid=mmioFOURCC('f','m','t',' ');
mmioDescend(hmmio,&mmckinfoSubchunk,&mmckinfoParent,MMIO_FINDCHUNK);
mmioRead(hmmio, (char *)&pcmwf,sizeof(pcmwf));
mmioAscend(hmmio,&mmckinfoSubchunk,0);
mmckinfoSubchunk.ckid=mmioFOURCC('d','a','t','a');mmioDescend(hmmio,&mmckinfoSubchunk,&mmckinfoParent,MMIO_FINDCHUNK);
mmioRead(hmmio, lpBuffer,wavehdr.dwBufferLength);
mmioClose(hmmio,0);}
//-----------------------------------------------------------------

(3)MCIウエーブフォームオーディオ
record,stop,playbuffer,save,playfile,stopという6個のボタンを並べた。
recordボタンで録音開始、record命令で終了時刻を指定しない時は、stopボタンで録音をとめる。recordした内容はsample1に入っている(sample1はファイルらしいが、ここではbufferと呼ぶ。本当はopen命令にbufferがあり、正式にはこれと異なるが)playbufferボタンで、sample1を再生。
save ボタンでsample1をsample2.wavに移す。playfileボタンでsample2.wavを再生。endボタンでsample1をクローズ(これを忘れると悲惨)。
注意:
1:record 命令には waitを指定できる。そのため、 record直後にcloseすると録音できない。
2:playfileでは再生直後にクローズした方がよい。
(そうしないと原因不明の実行停止不能になることがあった)
3:play命令ではfrom 0を入れないといけない。

//-------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <mmsystem.h>
#include <windows.h>
#include "Unit1.h"
//--------------------------------------------------------------------
#pragma package(smart_init)

#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)

        : TForm(Owner)
{
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)

{
//play buffer sample1
mciSendString("play sample1 from 0 wait",NULL,0,NULL);
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)

{
//record
mciSendString("open new type waveaudio  alias sample1",NULL,0,NULL);
mciSendString("set sample1 time format ms",NULL,0,NULL);
mciSendString("set sample1 samplespersec 22050 channels 1 bitspersample 8"
              ,NULL,0,NULL);
mciSendString("record sample1 from 0 to 200",NULL,0,NULL); //need not stop
//mciSendString("record sample1",NULL,0,NULL); //need stop
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)

{
//stop
mciSendString("stop sample1",NULL,0,NULL);
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button6Click(TObject *Sender)

{
//end
 mciSendString("close sample1",NULL,0,NULL);
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)

{
//save
 mciSendString("save sample1 sample2.wav",NULL,0,NULL);
}
//--------------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)

{
//play file
mciSendString("open sample2.wav type waveaudio alias sample2",NULL,0,NULL);
mciSendString("play sample2 from 0 wait",NULL,0,NULL);
mciSendString("close sample2",NULL,0,NULL);
}
//----------------------------------------------------------------------

12.音声スループログラム
ボタンが2つ(スタートボタンとストップボタン)あり、スタートボタンを押すと、マイクからの音声がそのままスピーカから聞こえる。コールバック関数(割り込み処理関数)を使っているので、多分最速でと思う。
Unit1.h
//-------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <mmsystem.h>


#define N_SAMPLES_PER_SEC 22050L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 4096L
#define N_BITS_PER_CHANNEL 16
#define N_DATA_BLOCK 3 //バッファ数


LPSTR lpBuffer;
WAVEHDR wavehdr[N_DATA_BLOCK]; // データブロック
HWAVEIN hwi; // ウェーブオーディオ入力用ハンドル
HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル

WAVEFORMATEX wfex = { // Waveデータの形式
WAVE_FORMAT_PCM, // フォーマット:PCM
N_CHANNELS, // チャネル数
N_SAMPLES_PER_SEC, // 1秒あたりのサンプル数
N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8), // 1秒あたりのバイト数
N_BYTES_PER_SAMPLE, // 1サンプルあたりのバイト数
N_BITS_PER_CHANNEL, // 1サンプル・1チャネルあたりのバイト数
sizeof(WAVEFORMATEX)
};

//------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button3;
TButton *Button4;
void __fastcall Button3Click(TObject *Sender);
void __fastcall Button4Click(TObject *Sender);
private: // ユーザー宣言

static void CALLBACK weProc(HWND hwnd,UINT message,DWORD dwins, LPWAVEHDR wparam, DWORD lparam)
{ switch(message){
case MM_WIM_DATA:
waveInUnprepareHeader( hwi, wparam, sizeof(WAVEHDR));
wparam->dwBufferLength= wparam->dwBytesRecorded;
wparam->dwFlags &= ~MHDR_DONE;
waveOutPrepareHeader( hwo, wparam, sizeof(WAVEHDR));
waveOutWrite( hwo, wparam, sizeof(WAVEHDR));break;
case MM_WOM_DONE:
waveOutUnprepareHeader( hwo, wparam, sizeof(WAVEHDR));
wparam->dwBufferLength= N_BYTES_PER_DATA_BLOCK;
wparam->dwFlags &= ~MHDR_DONE;
waveInPrepareHeader( hwi, wparam, sizeof(WAVEHDR));
waveInAddBuffer( hwi, wparam, sizeof(WAVEHDR));break;
default: break;
}
}

public: // ユーザー宣言

__fastcall TForm1(TComponent* Owner);

};
//-----------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//----------------------------------------------------------
#endif

Unit1.cpp
//-----------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//-----------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

// furuya追加
#include <mmsystem.h>
#include <windows.h>
#include <windowsx.h>

//-----------
TForm1 *Form1;
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//-------------------------------------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
int i;

waveInOpen(&hwi,
WAVE_MAPPER,
&wfex,
(DWORD)TForm1::weProc,
NULL,
CALLBACK_FUNCTION);

waveOutOpen(&hwo,
WAVE_MAPPER,
&wfex,
(DWORD)TForm1::weProc,
NULL,
CALLBACK_FUNCTION);

for(i=0; i<N_DATA_BLOCK;++i) {
lpBuffer=(LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, N_BYTES_PER_DATA_BLOCK);
wavehdr[i].lpData= lpBuffer;
wavehdr[i].dwBufferLength= N_BYTES_PER_DATA_BLOCK;
wavehdr[i].dwFlags &= ~MHDR_DONE;
waveInPrepareHeader(hwi, &wavehdr[i], sizeof(WAVEHDR));
waveInAddBuffer(hwi, &wavehdr[i], sizeof(WAVEHDR));
}

waveInStart( hwi );
}
//-----------------------------------------------------------

void __fastcall TForm1::Button4Click(TObject *Sender)
{

int i;

for(i=0; i<N_DATA_BLOCK;++i)
waveOutUnprepareHeader( hwo, &wavehdr[i], sizeof(WAVEHDR));

waveInStop(hwi);
waveInReset(hwi);
waveInClose(hwi);
waveOutReset(hwo);
waveOutClose(hwo);

}
//-----------------------------------------------------------

13、カメラ画像の実時間操作(画像スルー)
カメラからの画像を実時間で操作して表示する。インタラクティブアートの素。
次のプログラムはカメラの特性を(120x160,1ピクセル16ビットに、IO Data Video Recorderでセット)
Unit1.h
//-----------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//-----------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>

//#include <ExtCtrls.hpp>
//#include <mmsystem.h>
//#include <windows.h>
//#include <windowsx.h>
//-------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button1;

void __fastcall Button1Click(TObject *Sender);
private: // ユーザー宣言



public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//----------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//-----------------------------------------------------------
#endif


Unit1.cpp
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"

#include <vfw.h> //for CCD
//--------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

#define ww 160
#define wh 120

HWND vcHWND; //for CCD

BITMAPINFO bmi;
char pixels[ww*wh*2];

LRESULT CALLBACK FrameCallbackProc(HWND vcHWND, LPVIDEOHDR lpVHdr)
{
int i;
// CopyMemory(&pixels, lpVHdr->lpData, ww*wh*2);次の2行と同じ機能

for(i=0; i<wh*ww*2; i++)
pixels[i]= lpVHdr->lpData[i];


StretchDIBits(Form1->Canvas->Handle,0,0,ww,wh,0,0,ww,wh,
&pixels,&bmi,DIB_RGB_COLORS,SRCCOPY);
return 0;
}

//----------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//----------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=ww;
bmi.bmiHeader.biHeight=wh;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=16;
bmi.bmiHeader.biCompression=BI_RGB;
bmi.bmiHeader.biSizeImage=0;

vcHWND=capCreateCaptureWindow("My Window",WS_CHILD |
WS_VISIBLE,0,240,ww,wh,Form1->Handle,0);
capDriverConnect(vcHWND,0);
capPreviewRate(vcHWND,30);
capPreview(vcHWND,TRUE);
//capOverlay(vcHWND,TRUE);
capSetCallbackOnFrame(vcHWND,FrameCallbackProc);
//ShowWindow(vcHWND,SW_HIDE);

}
//----------------------------------------------------------
---------------------------------------------------------


参考資料
CQ出版社ブートストラップNo.5

+*+*+*+*+*+*+*+*以下、宝の山又はゴミの山+*+*

(以下は、上記と作成時期が異なり一部重複する部分がある)

14、音声処理の応用

音声の実時間処理をウインドウズ上で行う次の3つのプログラムを紹介する。

(1)スループログラム
(2)ネットワークを介した音声伝送
(3)スペクトル表示

約束事
最初に、ウインドウズで音声処理プログラムの作成を行う際の、いくつかの決まり事を整理する。

(I)道具立て
音声処理プログラムに不可欠な変数と領域:
(1)WAVEFORMATEXというタイプの変数:これにはサンプリング速度、ステレオか否かなど、音声入力デバイスに関する情報が書き込まれている。(付録1参照)
(2)音声信号格納バッファ:メモリ上の領域
(3)バッファヘッダ:各バッファに一つくっついているヘッダ(名札というか荷札というか):バッファサイズやメモリ上のバッファ位置などが記されている。

以上3つはプログラマが陽に意識してプログラム中に記述なくてはいけない。

(II)知っておく必要のあるこれに対し、裏構造
(4)リクエストキュー:入力デバイスにはリクエストキューなるものが付いていて、音声入力を行う時はこのリクエストキューにバッファを入れた後入力命令を出す。
(5)WAVEMAPPER:ウインドウズに登録されているデフォールトのウエーブデバイス指定。
(6)バッファのモード切り替え:バッファは入力デバイスによって音声を入力された後処理が行われる。場合によってはそんそまま出力デバイスに送られて出力されることもある(スループログラムと呼ばれる)。この時、バッファは入力に使ったものと同じものなのだからそのまま出力装置に渡せばよいのに、入力の際は入力用、出力の際は出力用、とモード切り替えをやらなくてはいけない。具体的にはバッファヘッダ(名札)内に、どのモードかを書きこむ。

(III)ウインドウズプログラミング
これは付録として最後にまとめる。

第1部 
スループログラム

(IV)プログラム例
6つのプログラム例を紹介する。プログラムは全て自作であるが、参考書を参考に作成しているため、各参考書の方式を引き継いでいるところが多い。例えばバッファの確保法などは、参考書によって方式が異なる。ここでは、あえて統一することをせず、各プログラム作成時に使った参考書の方式を使っている。本コーナーの読者には、色々な方法があることが分かって、参考になると思う。なお、以下の説明に現れる命令の詳細は付録1を参照のこと。

プログラム1:(基本音声入出力)
このプログラムは本ホームページの”C++Builderによるマルチメディア処理”の中でとりあげている(ただし、かつて使っていたPCMWAVEFORMは旧式なのでC++Builder6.0用にWAVEFORMEXに変更している)。このプログラムは音声入出力に関して最小限の機能を実現している。フォームには次の4つのボタンが付いている。ボタンを左から1つずつ押して処理を進めると、デバイスの初期化、録音、再生、後始末、が行われ一連の処理が完了する(録音時間は一瞬なので、再生ボタンを押す前から音を出しておかないといけない)。

(1)初期化:WAVEFORMATEXに値を設定し、入出力デバイスのOpen処理を行い、入出力用バッファの領域確保を行っている。このバッファをwaveInPrepareで入力モードにする。

(2)録音:1バッファ分の録音を行う。録音法は、先ずwaveAddBufferでバッファを入力デバイスに送っておいて、waveInStartで入力作業を指示する。バッファを送るのに何故AddBufferなのかというと、入力デバイスに複数のバッファを送っておくことができるためである。送られたバッファは入力デバイスのキューにつながれ、入力データは先頭のバッファから順に音声データが格納されていく。waveInSatrt指令は、バッファがいっぱいになった時点で終了する。

(3)再生:録音でバッファがいっぱいになると、waveOutWriteでバッファの中身をスピーカから音にする。この際音声入力に使ったバッファをwaveInUnpreparHeaderで入力モードを解除した後、waveOutPrepareHeader命令で出力モードに設定する必要がある。この間、入力デバイスを停止させたりもしている。

(5)後始末:入出力デバイスに動作終了を指示し、バッファ領域を開放し、デバイスをクローズする。バッファ領域の開放は、プログラム2以下の領域確保法では必要ない。

^^^^^^^^^^^^^^^^^^^^^^^^^^^^Unit1.cpp^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//-------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//-------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
#include <mmsystem.h> //忘れないで
//-----------
#define N_SAMPLES_PER_SEC 22050L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 4096L
#define N_BITS_PER_CHANNEL 16

HGLOBAL h;
LPSTR lpBuffer;
WAVEHDR wavehdr; // データブロック
HWAVEIN hwi; // ウェーブオーディオ入力用ハンドル
HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル
WAVEFORMATEX wfex;

TForm1 *Form1;
//-----------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//-----------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// wfexの設定 CurBuff=0;PlayPos=0;
wfex.wFormatTag=WAVE_FORMAT_PCM;
wfex.nChannels=1;
wfex.nSamplesPerSec=22050L;
wfex.nAvgBytesPerSec=22050*2;
wfex.nBlockAlign=2;
wfex.wBitsPerSample=16;
wfex.cbSize=sizeof(WAVEFORMATEX);

//ヘッダ設定
wavehdr.dwFlags= 0; // 処理終了フラグをリセット
wavehdr.dwBufferLength = N_BYTES_PER_DATA_BLOCK; // バッファサイズを設定
//入出力デバイスオープン
waveInOpen(&hwi, WAVE_MAPPER, &wfex, 0, NULL, 0);
waveOutOpen(&hwo, WAVE_MAPPER, &wfex, 0, NULL, 0);
h= GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, N_BYTES_PER_DATA_BLOCK);
lpBuffer= (char *) GlobalLock(h);
wavehdr.lpData= lpBuffer;
waveInPrepareHeader(hwi, &wavehdr, sizeof(wavehdr)); // 準備のための API を呼び出す
}
//----------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
waveInAddBuffer(hwi, &wavehdr, sizeof(wavehdr));
waveInStart( hwi );
}
//----------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
waveInUnprepareHeader( hwi, &wavehdr, sizeof(wavehdr));
waveInStop(hwi);
waveInReset(hwi);
waveOutPrepareHeader( hwo, &wavehdr, sizeof(wavehdr));
waveOutWrite( hwo, &wavehdr, sizeof(wavehdr));
}
//---------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
waveOutUnprepareHeader( hwo, &wavehdr, sizeof(wavehdr));
GlobalUnlock(h);
GlobalFree(lpBuffer);
waveInClose(hwi);
waveOutReset(hwo);
waveOutClose(hwo);
}
//--------------------------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^

プログラム2:(スルー:CALLBACK関数付加)
スループログラムは、音声入力をメモリに一旦格納し、それを再生(出力)する、ということを繰り返すもので、音声処理の基礎である。スルーの基本操作は、バッファを入力デバイスに送り、バッファがいっぱいになったら、それを出力デバイスに送って音にする、ことを繰り返すことである。ここで問題になるのは、出力を行っている間にも入ってくる音声をどこにしまっておくかである。これを解決するには複数のバッファを用意し、一つのバッファを出力デバイスに送った後は別のバッファに入力データを保存する。そして出力を終わったバッファは再び入力デバイスに送って入力に用いる、というサイクルを用意する。

プログラム2は各入出力命令の終了をメッセージ(IO命令終了時のIO割り込みに相当する)としてCALLBACK関数で受け取り処理する方式である。C++Builderでは、主なイベントメッセージ(例えば“マウスをクリックしたら“というような)はBuilderで分別され、ユーザはメッセージに対応した処理だけを__fastcall関数として書けばよい。しかし、音声処理の”音声入力でバッファが一杯になった(MM_WIM_DATA“とか”バッファを全て再生した(MM_WOM_DONE)“といったマルチメディア関係のメッセージは対応してくれない。そこで、これらを受け取るCALLBACK関数をフォームに張りつける必要がある。このCALLBACK関数はフォームにststicメソッドとして宣言する。

プログラムはフォーム上に2つのボタンを用意している。初期化ボタンを押すと、入出力デバイスのOpenが行われ、バッファが確保される。バッファは上記の理由で複数用意する。次にバッファを全て入力デバイスのリクエストキューに送りこみ(waveAddBuffer)、入力デバイスを起動する(waveStartInput)。ここで忘れないで欲しいのはデバイスOpen命令のパラメータがCALLBACK用(waveInOpenで最後の3つのパラメーターを(DWORD)TForm1::waveInProc,0,CALLBACK_FUNCTION )にする点である。
最初のバッファが入力音声データでいっぱいになると、WindowsOSはCALLBACK関数weProcにMM_WIM_DATAメッセージを送ってくる。そこでcaseMM_WIM_DATA部が動き出す。そこではバッファを出力用にモード変更(waveInUnprepareHeaderrに続きwavaOutPrepareHeaderを行う)し、出力命令(waveOutWrite)をだす。

一方、出力命令で動き出した出力デバイスであるが、バッファ分の出力を終えるとWindowsOSからCALLBACK関数にMM_WOM_DONEというメッセージが送られる。CALLBACK関数は、このメッセージを受けると、caseMM_WOM_DONE部の処理に移る。ここでは出力を終えたバッファを入力用に戻し(waveOutUnprepareBufferに続きwavaInPrepareBufferを行う)、入力デバイスに送る。入力命令は再度出す必要はない。バッファが送られると(waveInAddBuffer)、入力デバイスは自動的にスタートする。
以下、この処理が繰り返される。右ボタンは終了処理である。

この処理とほぼ同じC言語のプログラムは付録3に掲載してある(BuilderC++とC言語のプログラムの違いを理会するのに役立つだろうVC++5.0のWin32Applicationでは動くが、他では確認していない)。

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Unit1.h^^^^^^^^
//-------------------------------
#ifndef Unit1H
#define Unit1H
//-------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <mmsystem.h>

#define N_SAMPLES_PER_SEC 22050L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 4096L
#define N_BITS_PER_CHANNEL 16
#define N_DATA_BLOCK 3 //バッファ数

LPSTR lpBuffer;
WAVEHDR wavehdr[N_DATA_BLOCK]; // データブロック
HWAVEIN hwi; // ウェーブオーディオ入力用ハンドル
HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル
WAVEFORMATEX wfex = { // Waveデータの形式
WAVE_FORMAT_PCM, // フォーマット:PCM
N_CHANNELS, // チャネル数
N_SAMPLES_PER_SEC, // 1秒あたりのサンプル数
N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8),
// 1秒あたりのバイト数
N_BYTES_PER_SAMPLE, // 1サンプルあたりのバイト数
N_BITS_PER_CHANNEL,  // 1サンプル・1チャネルあたりのバイト数
sizeof(WAVEFORMATEX)
};

//--------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button3;
TButton *Button4;
void __fastcall Button3Click(TObject *Sender);
void __fastcall Button4Click(TObject *Sender);
private: // ユーザー宣言
static void CALLBACK weProc(HWND hwnd,UINT message,DWORD dwins,
LPWAVEHDR wparam, DWORD lparam)
{ switch(message){
case MM_WIM_DATA:
waveInUnprepareHeader( hwi, wparam, sizeof(WAVEHDR));
wparam->dwBufferLength= wparam->dwBytesRecorded;
wparam->dwFlags &= ~MHDR_DONE;
waveOutPrepareHeader( hwo, wparam, sizeof(WAVEHDR));
waveOutWrite( hwo, wparam, sizeof(WAVEHDR));break;
case MM_WOM_DONE:
waveOutUnprepareHeader( hwo, wparam, sizeof(WAVEHDR));
wparam->dwBufferLength= N_BYTES_PER_DATA_BLOCK;
wparam->dwFlags &= ~MHDR_DONE;
waveInPrepareHeader( hwi, wparam, sizeof(WAVEHDR));
waveInAddBuffer( hwi, wparam, sizeof(WAVEHDR));break;
default: break;
}
}

public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//------------------------------
extern PACKAGE TForm1 *Form1;
//------------------------------
#endif

^^^^^^^^^^^^^^^^^^^^^Unit1.cpp^^^^^^^^^^^^^^^^
//-------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//-------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
// furuya追加
#include <mmsystem.h>
#include <windows.h>
#include <windowsx.h>
//-----------
TForm1 *Form1;
//--------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
int i;
waveInOpen(&hwi,
WAVE_MAPPER,
&wfex,
(DWORD)TForm1::weProc,
NULL,
CALLBACK_FUNCTION);

waveOutOpen(&hwo,
WAVE_MAPPER,
&wfex,
(DWORD)TForm1::weProc,
NULL,
CALLBACK_FUNCTION);

for(i=0; i<N_DATA_BLOCK;++i) {
lpBuffer=(LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
N_BYTES_PER_DATA_BLOCK);
wavehdr[i].lpData= lpBuffer;
wavehdr[i].dwBufferLength= N_BYTES_PER_DATA_BLOCK;
wavehdr[i].dwFlags &= ~MHDR_DONE;
waveInPrepareHeader(hwi, &wavehdr[i], sizeof(WAVEHDR));
waveInAddBuffer(hwi, &wavehdr[i], sizeof(WAVEHDR));
}
waveInStart( hwi );
}
//---------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
int i;
for(i=0; i<N_DATA_BLOCK;++i)
waveOutUnprepareHeader( hwo, &wavehdr[i], sizeof(WAVEHDR));
waveInStop(hwi);
waveInReset(hwi);
waveInClose(hwi);
waveOutReset(hwo);
waveOutClose(hwo);
}
//----------------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
プログラム3:(スルー:スレッド方式)(近日公開)
プログラム4:(通信:TCP/IP)(近日公開)
プログラム5:(通信:UDP)(近日公開)
プログラム6:(FFT)(近日公開)
=−=−=−=−=−=−
付録1:主な音声処理用命令
(1)WAVEFORMATEX
typedef struct {
WORD wFormatTag(WAVE_FORMAT_PCMとする);
WORD nCannels(モノラルは1、ステレオは2);
DWORD nSamplesPerSec(サンプル数/秒);
DWORD nAvgBytesPerSec(平均データ転送バイト/秒);
WORD nBlockAlign(ブロックの境界をバイト単位で示す);
WORD wBitsPerSample(サンプルのビット数);
WORD cbSize(PCMの場合0);
} WAVEFORMAT;

(2)ウエーブヘッダ
typedef struct {
LPSTR lpData(音声バッファへのポインタ);
DWORD dwBufferLength(音声バッファサイズ);
DWORD dwBytesRecorded;(入力中のデータ数)
DWORD dwUser(プログラマが自由に使える);
DWORD dwFlags(バッファ満、バッファ再生済、バッファ準備OKなどの情報);
DWORD dwLoops(ループ再生回数);
Struct wavehdr_tag * lpNext(システムが使用);
DWORD reserved(未使用);
} WAVEHDR, *LPWAVEHDR;
dwFlagsの詳細

(3)入力デバイスオープン命令
UNIT waveInOpen(
LPHWAVEIN lphWaveIn(HWAVEINハンドルを指すポインタ),
UINT wDeviceID(WAVE_MAPPERと記入),
LPWAVEFORMAT lpFormat(WAVEFOEMAT 構造体へのポインタ),
DWORD dwCallback(コールバック関数のアドレス);
DWORD dwCallbackInstance(未使用);
DWORD dwFlags(コールバック先がウインドウズか関数かなど));

(4)出力デバイスオープン命令
UNIT waveOutOpen(
KPHWAVEOUT lphWaveOut(HWAVEOUTハンドルを指すポインタ),
UINT wDeviceID(WAVE_MAPPERと記入),
LPWAVEFORMAT lpFormat(WAVEFOEMAT 構造体へのポインタ),
DWORD dwCallback(コールバック関数のアドレス);
DWORD dwCallbackInstance(未使用);
DWORD dwFlags(コールバック先がウインドウズか関数かなど));

(5)入力デバイスのクローズ
waveInClose(HWAVEIN hWaveIn);

(6)出力デバイスのクローズ
waveOutClose(HWAVEOUT hWaveOut);

(7)入力デバイスのバッファを送る

waveInAddBuffer(
HWAVEIN hWaveIn(入力デバイスハンドル),
LPWAVEHDR lpWaveInHdr(バッファヘッダへのポインタ),
UINT wSize(WAVEHDRサイズ));

(7)録音開始
waveInStart(HWAVEIN hWaveIn);

(8)録音停止
waveInStop(HWAVEIN hWaveIn);

(9)再生(音声出力)
waveOutWrite(
HWAVEOUT hWaveOut(出力デバイスハンドル),
LPWAVEHDR lpWaveOutHdr(バッファヘッダへのポインタ),
UINT wSize(WAVEHDRサイズ));

(10)再生停止
waveOutReset(HWAVEOUT hWaveOut);

(11)バッファを再生用に準備
waveOutPrepareHeader(
HWAVEOUT hWaveout(出力デバイスハンドル),
LPWAVEHDR lpWaveHeader(バッファヘッダへのポインタ),
UNIT wSize(WAVEHDRサイズ));

(12)再生用バッファの後始末
waveOutPrepareHeader(
HWAVEOUT hWaveout(出力デバイスハンドル),
LPWAVEHDR lpWaveHeader(バッファヘッダへのポインタ),
UNIT wSize(WAVEHDRサイズ));

(13)バッファを入力用に準備
waveInPrepareHeader(
HWAVEOUT hWaveout(入力デバイスハンドル),
LPWAVEHDR lpWaveHeader(バッファヘッダへのポインタ),
UNIT wSize(WAVEHDRサイズ));

(12)再生用バッファの後始末
waveInPrepareHeader(
HWAVEOUT hWaveout(入力デバイスハンドル),
LPWAVEHDR lpWaveHeader(バッファヘッダへのポインタ),
UNIT wSize(WAVEHDRサイズ));

=−=−=−=−=−=−

付録2:ウィンドウズプログラミングの基礎

C言語に慣れている学生もウインドウズ用C言語は少しUNIX上のものと違うので勉強が必要である。ここでは音声処理に必要な最小限の知識をまとめておく。

<前置き>
(I) C?VisualC++?C++Builder?
ここではC++Builderを使った音声処理を紹介している。VisualC++の方がメジャーな気がするがC++Builderの方が使いやすいのと、判官びいきも手伝って、C++Builderにこだわっている。
C++BulderでもVisualC++でも、基本はウインドウズCであり、これを理解しないとウィンドウズプログラムの本質が分からない。そこで先ず、ウインドウズCプログラミングの構造を紹介する。ウインドウズCプログラムはVC++のWin32Applicationで実行できる。

<ウインドウズC++プログラム>
ウインドウズプログラムが従来のCプログラムと最も異なる点はイベント駆動だ、ということである。UNIXのCプログラムはmain()の先頭から実行を開始し、最後の命令に到達すると処理が終わる。これに対しウィンドウズC++プログラムはWinmain()の先頭からプログラムが開始し、(一般には)メインウインドウを開いた後、WindowsOSからの(イベント)メッセージを待ち状態になる。
WindowsOSからのイベントとは、プログラム利用者がマウスボタンを押した、とかキーを打ったとか、(メイン)ウインドウのボタンをクリックしたとかいうものである。このイベントはウインドウプロシージャ(CALLBACK関数と呼ばれる)にメッセージとして送られる。CALLBACK関数はWindowsOSから送られてくるメッセージに従って、各メッセージ(イベント)に対応した処理ルーチンに制御を渡す(イベントに対応する関数にジャンプする)。

C++プログラムの構造は要点だけを書けば、
WinMain()
{
(ほとんどおまじない的にきまりきったもの)
メインウインドウの生成;
イベント待ちループ;
}

CALLBACK Window_Procedura(message) {
switch(message) {
case WM_DESTROY:
…….
Break;
case WM_COMMAND:
……
break;
….
}
}

極端に言えば、ウインドウC++プログラムはWinMain()とウインドウプロシージャから出来ている。
WindowOSからのメッセージとしては、”マウスボタンが押された”等といったハードウエア操作に由来するものが多くWM_ではじまるものが多い。しかし中には入力デバイスがバッファへデータを入れ終わった(MM_WIM_DATA)などどいうものもある。
そしてC++Builderは、ユーザがコールバック関数のswitch-case構造(上図参照)を見えないようにしている。即ち、プログラマは”主な”イベント対応ルーチン(case文の : 以下)を記述すればよい。主なイベントしか使わないときはプログラムが簡単になるが、逆に主なイベント以外の対応ルーチンを書こうとすると、面倒なことになる。

フォームにCALLBACK関数を作るには、
フォームのクラス内staticメソッドとして作成する。
例えば、
class TForm1 : public TForm {
/*(省略)*/
static void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD
dwInstance,DWORD dwParam1,DWORD dwParam2) {
switch(uMsg) {
case WIM_DATA: /*処理*/break;
}
}
}

ハンドルって何だ?
=−=−=−=−=−=−

付録3:プログラム2の Windows C版(下記、^^^^で区切られた4つのファイルよりなる)
ウインドウズプロシージャを全て書いたもので、C++Builderとの比較に便利。ただし、上記プログラム2はボタンのクリックで起動するのに対し、このプログラムはウインドウ上ででのマウスクリックで動作が開始する。

^^^^^^winmain.c^^^^^^^^
/*WinMain関数(音声スループログラム)*/
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include "wndproc.h"
#include "thru.h"
#include <mmsystem.h>

/* アプリケーションエントリーポイント */
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR CmdLine,
int CmdShow)
{
HWND hwnd; /*メインウインドウのウインドウハンドル*/
MSG msg; /*メッセージキューから取得したメッセージ */
WNDCLASS wc; /*ウインドウクラス登録用の構造体 */

wc.style =0;
wc.lpfnWndProc =WindowProc;
wc.cbClsExtra =0;
wc.cbWndExtra =0;
wc.hInstance =hInstance;
wc.hIcon =LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor =LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName =NULL;
wc.lpszClassName="MainWindowClass";

if(RegisterClass(&wc)==0) /*ウインドウクラス登録*/
return 0;
hwnd=CreateWindow( "MainWindowClass", /*ウインドウ作成*/
"ThruFuru",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
0);

if(hwnd==NULL)
return 0;
ShowWindow(hwnd,CmdShow); /* ウインドウの表示*/
UpdateWindow(hwnd); /* ウインドウの最初の更新*/

while(GetMessage(&msg,NULL,0,0)) /* メッセージループ*/
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

^^^^^^^^^thru.h^^^^^^^^^^^^^^
#define N_SAMPLES_PER_SEC 22050L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 8820L
#define N_BITS_PER_CHANNEL 16
#define N_DATA_BLOCK 3 //バッファ数

LPSTR lpBuffer;
WAVEHDR wavehdr[N_DATA_BLOCK]; // データブロック
HWAVEIN hwi; // ウェーブオーディオ入力用ハンドル
HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル
PCMWAVEFORMAT pcmwf = { // Waveデータの形式
{
WAVE_FORMAT_PCM, // フォーマット:PCM
N_CHANNELS, // チャネル数
N_SAMPLES_PER_SEC, // 1秒あたりのサンプル数
N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8),
// 1秒あたりのバイト数
N_BYTES_PER_SAMPLE // 1サンプルあたりのバイト数
},
N_BITS_PER_CHANNEL // 1サンプル・1チャネルあたりのバイト数
};

^^^^^^^^^^windproc.c^^^^^^^^^^^^^^^^^
/*メインウインドウのウインドウプロシージャ*/
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <mmsystem.h>
#include "wndproc.h"
#include "thru.h"

/*このファイル内でのみ用いられる関数のプロトタイプ宣言*/
static LRESULT Wm_DestroyProc(void);
static LRESULT
Wm_LButtonDownProc(HWND,WPARAM,WORD,WORD);
static LRESULT
Wm_RButtonDownProc(HWND,WPARAM,WORD,WORD);
static LRESULT Mm_WimProc( LPARAM );
static LRESULT Mm_WomProc( LPARAM );

/* メインウインドウのウインドウプロシージャ */
LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM
wparam,LPARAM lparam)
{
switch(message)
{
case WM_DESTROY: /*ウインドウの破壊後処理 */
return Wm_DestroyProc();
case WM_LBUTTONDOWN: /*マウスの左ボタンが押された時*/
return
Wm_LButtonDownProc(hwnd,wparam,LOWORD(lparam),HIWORD(lparam));
case WM_RBUTTONDOWN: /*マウスが右ボタンが押された時*/
return
Wm_RButtonDownProc(hwnd,wparam,LOWORD(lparam),HIWORD(lparam));
case MM_WIM_DATA:
return Mm_WimProc(lparam);
case MM_WOM_DONE:
return Mm_WomProc(lparam);
}
return DefWindowProc(hwnd,message,wparam,lparam);
}

/* このファイル内でのみ用いられる変数定義 */
static LRESULT Wm_DestroyProc(void)
{
PostQuitMessage(0);
return 0;
}

static LRESULT Wm_LButtonDownProc(HWND hwnd,WPARAM
fwKeys,WORD xPos,WORD yPos)
{ int i;
SetCapture(hwnd); /* マウスをキャプチャする */
waveInOpen(&hwi,
WAVE_MAPPER,
(LPWAVEFORMATEX)&pcmwf,
(UINT)hwnd,
NULL,
CALLBACK_WINDOW);
waveOutOpen(&hwo,
WAVE_MAPPER,
(LPWAVEFORMATEX)&pcmwf,
(UINT)hwnd,
NULL,
CALLBACK_WINDOW);

for(i=0; i<N_DATA_BLOCK;++i) {
lpBuffer= GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
N_BYTES_PER_DATA_BLOCK);
wavehdr[i].lpData= lpBuffer;
wavehdr[i].dwBufferLength= N_BYTES_PER_DATA_BLOCK;
wavehdr[i].dwFlags &= ~MHDR_DONE;
waveInPrepareHeader(hwi, &wavehdr[i], sizeof(wavehdr[i]));
waveInAddBuffer(hwi, &wavehdr[i], sizeof(wavehdr[i]));
}

waveInStart( hwi );
return 0;
}

static LRESULT Mm_WimProc( LPWAVEHDR lpwh)

{

waveInUnprepareHeader( hwi, lpwh, sizeof(*lpwh));
lpwh->dwBufferLength= lpwh->dwBytesRecorded;
lpwh->dwFlags &= ~MHDR_DONE;
waveOutPrepareHeader( hwo, lpwh, sizeof(*lpwh));
waveOutWrite( hwo, lpwh, sizeof(*lpwh));
return 0;
}
static LRESULT Mm_WomProc( LPWAVEHDR lpwh)
{
waveOutUnprepareHeader( hwo, lpwh, sizeof(*lpwh));
lpwh->dwBufferLength= N_BYTES_PER_DATA_BLOCK;
lpwh->dwFlags &= ~MHDR_DONE;
waveInPrepareHeader( hwi, lpwh, sizeof(*lpwh));
waveInAddBuffer( hwi, lpwh, sizeof(*lpwh) );
return 0;
}

static LRESULT Wm_RButtonDownProc(HWND hwnd,WPARAM
fwKeys,WORD xPos,WORD yPos)
{
int i;
if(GetCapture()==hwnd) {/* マウスをキャプチャしているのが、このウインドウである事を確認して*/

for(i=0; i<N_DATA_BLOCK;++i)
waveOutUnprepareHeader( hwo, &wavehdr[i], sizeof(wavehdr[i]));
ReleaseCapture(); /* マウスをレリースする */
waveInStop(hwi);
waveInReset(hwi);
waveInClose(hwi);
waveOutReset(hwo);
waveOutClose(hwo);
}
return 0;
}

^^^^^^^^^^^wndproc.h^^^^^^^^^^^^^^^^^^^^
/* CALLBACKの宣言 */
extern LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

参考資料と謝辞
音声プログラムに関して
(1)羽毛田:ウエーブフォームオーディオ機能の応用、ブートストラップProject3,No-5,CQ出版(一寸古い部分もあるが、分かりやすい)。
(2)田辺:Windowsサウンドプログラミング、翔泳社
ウィンドウズプログラミング
(1)近藤:http://web.kyoto-inet.or.jp/people/ysskondo/
“C言語で始めるWindowsプログラミング”(大変よくできているが、最新のVC++で実行すると、そのままでは動かない)
++++VC++で強引にコンパイル.リンクする裏技++++
リンクエラーが出るとき:
プロジェクト->設定->リンクタブ  と、たどり、
カテゴリ窓を ”カスタマイズ” にして ターゲット強制 に レ印を入れる。
また、音声の場合上の リンクタブで、カテゴリを ”一般”  にして、オブジェクトライブラリモジュールに
winmm.lib を追加
++++++++++++++++++++++++++
謝辞:フォームへのCALLBACK関数付加法は中澤さん
http://market.agr.tottori-u.ac.jp/ken/ に教えていただいた。感謝いたしま
す。

第2部
音声のネットワーク転送(インターネット電話の基本型)
TCP/IPのクライアントサーバー方式で音声を送るプログラム。ネットワークにつながった2台の計算機(tcpsendとtcpreceive)を用意する。両計算機とも、フォーム上に2つのボタン(通信開始ボタン、通信終了ボタン)を持つ。送信側計算機tcpsendはクライアント計算機であり、音声を(短時間)収録しては受信側計算機tcpreceiveに送る、という操作を繰り返す。tcpreceiveはサーバと呼ばれ、クライアントからの音声が送られてくると動き出し、送られてきた音声をスピーカに出力する。このクライアント・サーバ機構を実現するため、tcpsendのフォームには(BuilderのInternetメニューにある)クライアントソケットを、tcpreceiveにはサーバソケットを張りつける。

+++++一寸道草+++++
フォームにソケットを張りつけるとプロパティとイベントを設定することになる。サーバソケットの場合プロパティはポート番号(今回は6668を使用)のみを設定すればよい。サーバは、どの(クライアント)計算機からの要求にも応えられる必要があるため相手の計算機指定はしない。一方クライアントソケットのプロパティは“address”にサーバのIPアドレスを指定すると共に、サーバのポート番号を指定する(6668)。イベントの設定は、クライアントはSendBif命令で音声送るだけなので何も設定しない。これに対し、サーバはクライアントからの音声到着イベントで動き出す必要がある。そのため、OnClientReadというイベントを用意し、そこで送られて来る音声を受け取る(ReceiveBuf命令)。くれぐれもOnClientWriteでないことに注意。

+++++++++++++++
(1)送信側プログラム
送信側のプログラムはスループログラムとよく似ている。音声入力バッファがいっぱいになるとウインドウプロシージャ(callback関数)が呼ばれ、バッファの中身を受信側に送る。この際、ヘッダー(Unit1.h)内で送ろうとするとうまくいかないのでUnit1.cppの方に一度戻って送るようにしている(もっとうまい方法があるかも)。

~~~~~~~~~~~~~~Unit1.h~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//--------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ScktComp.hpp>
#include <mmsystem.h>
static void back(LPWAVEHDR);
#define N_SAMPLES_PER_SEC 22050L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 4096L
#define N_BITS_PER_CHANNEL 16
#define N_IN_DATA_BLOCK 3 //入力バッファ数
//#define N_OUT_DATA_BLOCK 1 //出力バッファ数
LPSTR lpBuffer;
WAVEHDR waveInhdr[N_IN_DATA_BLOCK]; // データブロック
HWAVEIN hwi; // ウェーブオーディオ入力用ハンドル
HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル
WAVEFORMATEX wfex = { // Waveデータの形式
WAVE_FORMAT_PCM, // フォーマット:PCM
N_CHANNELS, // チャネル数
N_SAMPLES_PER_SEC, // 1秒あたりのサンプル数
N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8),
// 1秒あたりのバイト数
N_BYTES_PER_SAMPLE, // 1サンプルあたりのバイト数
N_BITS_PER_CHANNEL, // 1サンプル・1チャネルあたりのバイト数
sizeof(WAVEFORMATEX)
};

//-----------------------------------------------------------

class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button1;
TButton *Button2;
TClientSocket *ClientSocket1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
static void CALLBACK weProc(HWND hwnd,UINT message,DWORD dwins,
LPWAVEHDR wparam, DWORD lparam)
{
switch(message){
case MM_WIM_DATA:
waveInUnprepareHeader( hwi, wparam, sizeof(WAVEHDR));
wparam->dwBufferLength=
N_BYTES_PER_DATA_BLOCK;//dwBytesRecorded;
wparam->dwFlags &= ~MHDR_DONE;
back(wparam);
waveInPrepareHeader( hwi, wparam, sizeof(WAVEHDR));
waveInAddBuffer( hwi, wparam, sizeof(WAVEHDR));break;
default: break;
}
}
__fastcall TForm1(TComponent* Owner);
};
//-----------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//-----------------------------------------------------------
#endif
~~~~~~~~~~~~~~~~~~~~~~~~Unit1.h End~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~Unit1.cpp~~~~~~~~~~~~~~~~~~~
//-----------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//-----------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
#include <mmsystem.h>
#include <windows.h>
#include <windowsx.h>

TForm1 *Form1;
//--------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
void back(LPWAVEHDR wparam);
}
//--------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int i;
waveInOpen(&hwi,
WAVE_MAPPER,
&wfex,
(DWORD)TForm1::weProc,
NULL,
CALLBACK_FUNCTION);

ClientSocket1->Open();

for(i=0; i<N_IN_DATA_BLOCK;++i) {
lpBuffer=(LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
N_BYTES_PER_DATA_BLOCK);
waveInhdr[i].lpData= lpBuffer;
waveInhdr[i].dwBufferLength= N_BYTES_PER_DATA_BLOCK;
waveInhdr[i].dwFlags &= ~MHDR_DONE;
waveInPrepareHeader(hwi, &waveInhdr[i], sizeof(WAVEHDR));
waveInAddBuffer(hwi, &waveInhdr[i], sizeof(WAVEHDR));
}

waveInStart( hwi );
}
//------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
int i;
for(i=0; i<N_IN_DATA_BLOCK;++i)
waveInUnprepareHeader( hwi, &waveInhdr[i], sizeof(WAVEHDR));
waveInStop(hwi);
waveInReset(hwi);
waveInClose(hwi);

ClientSocket1->Close();
}
//----------------------------------------------------------
void back(LPWAVEHDR wparam)
{
Form1->ClientSocket1->Socket->SendBuf(wparam->lpData,N_BYTES_PER_DATA_BLOCK);
};
//----------------------------------------------------------

(3)受信側プログラム
受信側の考え方は、受信バッファを複数用意し、バッファがいっぱいになったらスピーカへ送ることを繰り返す。ここで注意して欲しいのは、スピーカへ垂れ流すことである。CallBack関数などで終了を待たず、ただただ垂れ流すこと。

~~~~~~~~~~~~~~Unit1.h~~~~~~~~~~~~~~~~~~~~~~~

//-------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//-------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ScktComp.hpp>
#include <mmsystem>
#include <ScktComp.hpp>
//------------------------------------------------------
#define N_SAMPLES_PER_SEC 22050L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 4096L
#define N_BITS_PER_CHANNEL 16
//#define N_IN_DATA_BLOCK 3 //入力バッファ数
#define N_OUT_DATA_BLOCK 3 //出力バッファ数

LPSTR lpBuffer;
WAVEHDR waveOuthdr[N_OUT_DATA_BLOCK]; // データブロック
HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル
WAVEFORMATEX wfex = { // Waveデータの形式
WAVE_FORMAT_PCM, // フォーマット:PCM
N_CHANNELS, // チャネル数
N_SAMPLES_PER_SEC, // 1秒あたりのサンプル数
N_SAMPLES_PER_SEC*(N_BITS_PER_CHANNEL*N_CHANNELS/8), // 1秒あたりのバイト数
N_BYTES_PER_SAMPLE, // 1サンプルあたりのバイト数
N_BITS_PER_CHANNEL, // 1サンプル・1チャネルあたりのバイト数
sizeof(WAVEFORMATEX)
};
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button1;
TButton *Button2;
TServerSocket *ServerSocket1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall ServerSocket1ClientRead(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall Button2Click(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言

__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------

#endif

~~~~~~~~~~~Unit1.h End~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~Unit1.cpp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//--------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <mmsystem.h>
#include <windows.h>
#include <windowsx.h>
//--------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int OBP=0;
//--------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int i;
waveOutOpen(&hwo,
WAVE_MAPPER,
&wfex,
0,
NULL,
0);

ServerSocket1->Open();

for(i=0; i<N_OUT_DATA_BLOCK;++i) {
lpBuffer=(LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
N_BYTES_PER_DATA_BLOCK);
waveOuthdr[i].lpData= lpBuffer;
waveOuthdr[i].dwBufferLength= N_BYTES_PER_DATA_BLOCK;
waveOuthdr[i].dwFlags &= ~MHDR_DONE;
waveOutPrepareHeader(hwo, &waveOuthdr[i], sizeof(WAVEHDR));
}
}
//---------------------------------------------------------
void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,

TCustomWinSocket *Socket)
{
ServerSocket1->Socket->Connections[0]->ReceiveBuf(waveOuthdr[OBP].lpData,N_BYTES_PER_DATA_BLOCK);
waveOutUnprepareHeader(hwo,&waveOuthdr[OBP],sizeof(WAVEHDR));
waveOutPrepareHeader(hwo,&waveOuthdr[OBP],sizeof(WAVEHDR));
waveOutWrite(hwo,&waveOuthdr[OBP],sizeof(WAVEHDR));
OBP++;OBP%=N_OUT_DATA_BLOCK;
}
//---------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
int i;
for(i=0;i<N_OUT_DATA_BLOCK; i++)
waveOutUnprepareHeader(hwo, &waveOuthdr[i],sizeof(WAVEHDR));
waveOutClose(hwo);
ServerSocket1->Close();
}
//--------------------------------------------------------
~~~~~~~~~~~~~~~~~Unit1.cpp End~~~~~~~~~~~~~~~~~~~~~~~~~~~
<おまけの付録>スレッドプログラムの作り方

スレッドの走らせ方

やらせたい事:プログラムの本流の他に、beep()という関数をスレッドとして定義し並走させる。

やる事:
ステップ1 : 関数beep() を Unit1.cpp に書くと共に、Unit1.h にプロトタイプを書く。
Unit1.cpp は以下。
//----------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//----------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//----------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{  //ステップ5でここに記入
}
//----------------------------------------------------
void __fastcall TForm1::beep() //記入
{ Beep(); } //記入(ビープ音発生)
//----------------------------------------------------
Unit1.h は以下の”追加記入部”を追加
class TForm1 : public TForm
{
friend class thread;
__published: // IDE 管理のコンポーネント
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
void __fastcall beep();   //ここに追加記入
};
//----------------------------------------------------

ステップ2:スレッドクラスを作る準備
step2.1:メニューの ファイル->新規作成->その他、と辿るとウインドウが開くので、その中からスレッドオブジェクトをWClick。
step2.2:スレッドオブジェクトの作成というウインドウが開くので、クラス名窓にthreadと記入しOKボタンを押すと、Unit2.cpp が現れる。

ステップ3:Unit2.cppの記入
はじめ、以下のようになっているので、//TODO部にコードを書いてもよいが、
//----------------------------------------------------
void __fastcall thread::Execute()
{
// TODO : スレッドとして実行したいコードを以下に記述 */
}
//----------------------------------------------------
普通次のようにするのが慣例。
//----------------------------------------------------
void __fastcall thread::Execute()
{
// TODO : スレッドとして実行したいコードを以下に記述 */
while(!Terminated) {
Synchronize(run);
}
}
//----------------------------------------------------
void __fastcall thread::run()
{ Form1->beep(); }
//-----------------------------------------------------
このプログラムの意味はSynchronize(run);で下のrun()という関数を呼び、run()の中でUnit1.ccに書いたbeep()という関数を呼んでいる。
* この時、Unit2.cpp に #include "Unit1.h" を付加する。//忘れないで

ステップ4: Unit2.h の修正
以下のように、 void __fastcall run(); を追加記入。
//-----------------------------------------------------
class thread : public TThread
{
private:
protected:
void __fastcall Execute();
public:
__fastcall thread(bool CreateSuspended);
void __fastcall run(); //追加記入
};
//------------------------------------------------------
ステップ5:(Unit1.cpp に戻って)スレッドを走らせる部分を追加
//------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
thread1 = new thread(true);  //この部分
thread1->Resume(); //この部分
}
//------------------------------------------------------
ステップ6:Unit1.h への大幅追加
step6.1: #include "Unit2.h" の追加
step6.2: おまじない的2行追加。
class TForm1部は以下のようになる。
//------------------------------------------------------
class TForm1 : public TForm
{
friend class thread;   //おまじない1
__published: // IDE 管理のコンポーネント
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
void __fastcall test();
thread *thread1;};  //おまじない2
//------------------------------------------------------
ステップ7:プロジェクト->再構築->実行

ステップ8:スレッドの動作タイミングを変えるには。 

Unit2.cpp(スレッド)の中に”Sleep()命令”を追加。

//--------------------------
while(!Terminated) {
Synchronize(run);
Sleep(5000); //これを追加
}
//--------------------------
以上。

第3部
スペクトル表示 (スペクトラムアナライザ)
マイクからの音にFFTをかけて周波数成分を実時間で表示するプログラム。フォーム上のボタン押すとスペクトル表示を開始する。毎秒4Kサンプル採っているので、2Kヘルツまでの表示が可能。実行終了は “Esc” キーを押す。
~~~~~~~~~~~~Unit1.h~~~~~~~~~~~~~~~~~~~~~~~
#ifndef Unit1H
#define Unit1H
//-------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#define N_SAMPLES_PER_SEC 4096L // 1秒あたりのサンプル数
#define N_CHANNELS 1 // チャネル数(2はステレオ)
#define N_BYTES_PER_SAMPLE 2
#define N_BYTES_PER_DATA_BLOCK 1024L
#define N_BITS_PER_CHANNEL 16
#define N_2 N_BYTES_PER_DATA_BLOCK/2/2
//-------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
void __fastcall fft();
void __fastcall show();
__fastcall TForm1(TComponent* Owner);
Graphics::TBitmap *bmp;
double level[100];
double data[N_BYTES_PER_DATA_BLOCK/2],data2[N_BYTES_PER_DATA_BLOCK/2];
};
//--------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------
#endif
~~~~~~~~~Unit1.h End~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~Unit1.cpp~~~~~~~~~~~~~~~~~~~~~~~~~
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
#include <mmsystem.h> //忘れないで
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <winuser.h>
//-----------
int mm;
HGLOBAL h;
LPSTR lpBuffer;
WAVEHDR wavehdr; // データブロック
HWAVEIN hwi; // ウェーブオーディオ入力用ハンドル
//HWAVEOUT hwo; // ウェーブオーディオ出力用ハンドル
WAVEFORMATEX wfex;

TForm1 *Form1;
//-----------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//-----------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ int i,j,k;
waveInAddBuffer(hwi, &wavehdr, sizeof(wavehdr));
waveInStart( hwi );
while (GetAsyncKeyState(VK_ESCAPE)==0) {
loop: if((wavehdr.dwFlags & MHDR_DONE)!=MHDR_DONE) goto loop;
for(int i=0;i<N_BYTES_PER_DATA_BLOCK;i+=2)
data[i/2]=wavehdr.lpData[i]+wavehdr.lpData[i+1]*0x100;
wavehdr.dwFlags &= ~MHDR_DONE;

//窓関数もどき
data[0]/=64; data[1]/=32; data[2]/=16;data[3]/=8; data[4]/=4; data[5]/=2;
data[506]/=2;data[507]/=4;data[508]/=8;data[509]/=16;data[510]/=32;data[511]/=64;
waveInUnprepareHeader( hwi, &wavehdr, sizeof(wavehdr));
waveInPrepareHeader( hwi, &wavehdr, sizeof(wavehdr));
waveInAddBuffer(hwi, &wavehdr, sizeof(wavehdr));
fft();
show();
}
waveInClose(hwi);
}
//--------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// wfexの設定 CurBuff=0;PlayPos=0;
wfex.wFormatTag=WAVE_FORMAT_PCM;
wfex.nChannels=1;
wfex.nSamplesPerSec=4096L;
wfex.nAvgBytesPerSec=4096*2;
wfex.nBlockAlign=2;
wfex.wBitsPerSample=16;
wfex.cbSize=sizeof(WAVEFORMATEX);
//ヘッダ設定
wavehdr.dwFlags= 0; // 処理終了フラグをリセット
wavehdr.dwBufferLength = N_BYTES_PER_DATA_BLOCK; // バッファサイズを設定
//入出力デバイスオープン
waveInOpen(&hwi, WAVE_MAPPER, &wfex, 0, NULL, 0);
h= GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, N_BYTES_PER_DATA_BLOCK);
lpBuffer= (char *) GlobalLock(h);
// 領域を確保する
wavehdr.lpData= lpBuffer;
waveInPrepareHeader(hwi, &wavehdr, sizeof(wavehdr)); // 準備のための API を呼び出す
bmp = new Graphics::TBitmap();
bmp->Width=300;
bmp->Height=250;
}
//-----------------------------------------------------
void __fastcall TForm1::fft(){
int i,j,k,m,h,kp;
double w[N_BYTES_PER_DATA_BLOCK/2],t1,t2;
double w1,w2,v1,v2,pi=3.141592653;
for(j=0;j<N_2;j++){
w[j]=cos(2.0*j*pi/512); // 512点
w[j+N_2]=-1.0*sin(2.0*j*pi/512); // 512点
}

// for(i=0; i<512; i++)
// data[i]= sin((double)i*2.0*pi*256.0/4096.0);
for(i=0; i<512; i++)
data2[i]=0.0;
j=0;
for(i=0;i<=510;i++){ //N-2
if(i < j){
t1=data[j];data[j]=data[i];data[i]=t1;
}
k=N_2;
while(k<=j){
j=j-k;k=k/2;
}
j=j+k;
}
for(i=1;i<=9;i++){ // 2のべき
m=pow(2.0,(double)i);
h=m/2;
for(j=0;j<h;j++){
w1=w[j*(N_BYTES_PER_DATA_BLOCK/m/2)];
w2=w[j*(N_BYTES_PER_DATA_BLOCK/m/2)+(int)N_2];
for(k=j;k<N_BYTES_PER_DATA_BLOCK/2;k+=m){
kp=k+h;
v1=data[kp]*w1-data2[kp]*w2;
v2=data[kp]*w2+data2[kp]*w1;
t1=data[k]+v1;
data[kp]=data[k]-v1;
data[k]=t1;
t2=data2[k]+v2;
data2[kp]=data2[k]-v2;
data2[k]=t2;
}
}
}
}
//-------------------------------------------------------
void __fastcall TForm1::show(){
int i,j;
double l0,l1;
bmp->Canvas->Brush->Color=clWhite;
bmp->Canvas->FillRect(bmp->Canvas->ClipRect);

for(int i=0;i<100;i++){
bmp->Canvas->Brush->Color=clLime;
bmp->Canvas->FillRect(Rect(i*2+15,-level[i]/1000+100,i*2+17,100));
}
for(int i=0;i<100;i++){
l0=sqrt(pow(data[i*2],2)+pow(data2[i*2],2));
l1=sqrt(pow(data[i*2+1],2)+pow(data2[i*2+1],2));
if(l0>l1)level[i]=l0;
else level[i]=l1;
}

for(int i=0;i<100;i++){
bmp->Canvas->Brush->Color=clGreen;
bmp->Canvas->FillRect(Rect(i*2+15,-level[i]/1000+100,i*2+17,100));
}
BitBlt(Canvas->Handle,0,0,300,250,bmp->Canvas->Handle,0,0,SRCCOPY);
}



+*+*+*+*+*

15、インタラクティブアート
(1)ボタンが1つ付いている。タイマーを使わず、映像変換している。

Unit1.h
//------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//-------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//------------------------------------------------------------
#endif

Unit1.cpp
//-----------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <vfw.h> //forCCD
//-----------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;


#define ww 320
#define wh 240
#define slice 120
#define data ww*wh*2/slice

HWND vcHWND; //forCCD
BITMAPINFO bmi;
char pixels[ww*wh*2];
char f[240][ww*wh*2];
//char f2[ww*wh*2];

int n=0;

LRESULT CALLBACK FrameCallbackProc(HWND vcHWND, LPVIDEOHDR lpVHdr)
{
//CopyMemory(&pixels, lpVHdr->lpData, ww*wh*2);


int i,j;
for(i=n,j=0; j<slice; i=(i+1)%slice,j++)
//CopyMemory(&f[i][((n%24)*ww*wh*2)/24], &pixels[((n%24)*ww*wh*2)/24], (ww*wh*2)/24);
CopyMemory(&f[i][(slice-1-n%slice)*data], &lpVHdr->lpData[(slice-1-n%slice)*data], data);

StretchDIBits(Form1->Canvas->Handle,0,0,ww,wh,0,0,ww,wh,
&f[n],&bmi,DIB_RGB_COLORS,SRCCOPY);

n++;
n=n%slice;
return 0;
// StretchDIBits(Form1->Canvas->Handle,0,0,ww,wh,0,0,ww,wh,
// &pixels,&bmi,DIB_RGB_COLORS,SRCCOPY);

}
//---------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{

bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=ww;
bmi.bmiHeader.biHeight=wh;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=16;
bmi.bmiHeader.biCompression=BI_RGB;
bmi.bmiHeader.biSizeImage=0;

vcHWND=capCreateCaptureWindow("My Window",WS_CHILD |
WS_VISIBLE,0,240,ww,wh,Form1->Handle,0);
capDriverConnect(vcHWND,0);
capPreviewRate(vcHWND,30);
capPreview(vcHWND,TRUE);
capSetCallbackOnFrame(vcHWND,FrameCallbackProc);

}
//--------------------------------------------------------

(2)本格的インタラクティブアート(映像スルーの応用)
カメラからの映像を横(縦の場合もある)にスライスし、各スライスの表示タイミングを変えることにより、様々な映像ができる。これは2003年オープンキャンパスで紹介したもの。
プログラムの実行を開始すると、Optionをいうウインドウが現れ、ここで画面スライス数と表示速度を入力する。後は、F1キーを押すたびに6種類の映像が表示される。
このプログラムはタイマーの扱い(Wait.h)が分かりにくい。一見C++Builderのタイマで割り込みを起こしフレームを取っていると思うが、実は時間を正確に刻むため、CPUのクロックを利用している。C++Builderのタイマー割り込みは常に起こるようにし、StartWait()とEndWait()で時間を計ってフレームを取りにいっている。

Option.h
//---------------------------------------------------------
#ifndef OptionH
#define OptionH
//---------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------
class TForm2 : public TForm
{
__published: // IDE 管理のコンポーネント
TLabel *Label1;
TEdit *Edit1;
TButton *Button1;
TLabel *Label2;
TEdit *Edit2;
void __fastcall Button1Click(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm2(TComponent* Owner);
};
//----------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//----------------------------------------------------------
#endif

Option.cpp
//----------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Option.h"
//----------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//----------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//----------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
ModalResult = -1;
}
//----------------------------------------------------------


Wait.h
//-------------------------------------------------------
#ifndef _WAIT_H_
#define _WAIT_H_

//#include <windows.h>
#include <mmsystem.h>

//-------------------------------------------------------
class TWait
{
private:
DWORD ltime, ptime, wtime;

public:
void __fastcall SetWait(DWORD time);
void __fastcall StartWait();
void __fastcall EndWait();
void __fastcall FreeWait();
int __fastcall RateWait() { return (int)ptime; }
};
//-------------------------------------------------------
void __fastcall TWait::SetWait(DWORD time)
{
wtime = time;
timeBeginPeriod(1);
}
//-------------------------------------------------------
void __fastcall TWait::FreeWait()
{
timeEndPeriod(1);
}
//-------------------------------------------------------
void __fastcall TWait::StartWait()
{
ltime = timeGetTime();
}
//-------------------------------------------------------
void __fastcall TWait::EndWait()
{
ptime = timeGetTime() - ltime;

if(ptime < wtime){
Sleep(wtime - ptime);

ptime = wtime;
}
}
//--------------------------------------------------------

#endif

Unit1.h
//--------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>

#include "Wait.h"
//--------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TTimer *Timer1;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall FormResize(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormShow(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
int ww, wh;
int state;
int m_slice,m_data,wait;
HWND vcHWND;
TWait m_wait;
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------
#endif

Unit1.cpp
//--------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <vfw.h> //forCCD
#include "Option.h"
//--------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;


#define WIDTH 320
#define HEIGHT 240
#define wslice 80
#define slice 60
#define data WIDTH*HEIGHT*2
#define WAIT 60
#define size 5

//int slice;
HWND vcHWND; //forCCD
BITMAPINFO bmi;
char f[240][WIDTH*HEIGHT*2];
int n=0;

LRESULT CALLBACK FrameCallbackProc(HWND vcHWND, LPVIDEOHDR lpVHdr)
{
if(GetAsyncKeyState(VK_F1)!=0) Form1->state++;
if(Form1->state ==7) Form1->state=0;
switch(Form1->state){
case 0:
for(int i = 0; i < (Form1->m_slice); i++)
CopyMemory(&f[i], lpVHdr->lpData, Form1->m_data * (Form1->m_slice - i)); //data*(Form1->slice - i));
Form1->state = 1;
StretchDIBits(Form1->Canvas->Handle, 0, 0, Form1->ww, Form1->wh,
0, 0, WIDTH, HEIGHT, &f[n], &bmi, DIB_RGB_COLORS, SRCCOPY);
n++;
n %= Form1->m_slice;
break;
case 1:
for(int i=n, j=Form1->m_slice-1; j>=0; i=(i+1)%Form1->m_slice, j--)
CopyMemory(&f[i][(j % Form1->m_slice) * Form1->m_data],
&lpVHdr->lpData[(j % Form1->m_slice) * Form1->m_data], Form1->m_data);
StretchDIBits(Form1->Canvas->Handle, 0, 0, Form1->ww, Form1->wh,
0, 0, WIDTH, HEIGHT, &f[n], &bmi, DIB_RGB_COLORS, SRCCOPY);
n++;
n %= Form1->m_slice;
break;
case 2:
for(int i=0; i<WIDTH*HEIGHT; i++)
for(int j=0; j<2; j++)
f[0][i*2+j]= lpVHdr->lpData[WIDTH*HEIGHT*2-i*2+j];
StretchDIBits(Form1->Canvas->Handle, 0, 0, Form1->ww, Form1->wh,
0, 0, WIDTH, HEIGHT, &f[0], &bmi, DIB_RGB_COLORS, SRCCOPY);
break;
case 3:
for(int i=0; i<WIDTH*(HEIGHT-1); i++)
if(rand()%100>70)
for(int j=0; j<size; j++)
f[0][i*2+j]= lpVHdr->lpData[i*2+j];
StretchDIBits(Form1->Canvas->Handle, 0, 0, Form1->ww,Form1->wh,
0, 0, WIDTH, HEIGHT, &f[0], &bmi, DIB_RGB_COLORS, SRCCOPY);
break;
case 4:
for(int i=0; i<HEIGHT; i++)
for(int j=0; j<wslice; j++)
CopyMemory(&f[(n+j)%wslice][((WIDTH*2)/wslice)*j+i*WIDTH*2],
&lpVHdr->lpData[((WIDTH*2)/wslice)*j+i*WIDTH*2],(WIDTH*2)/wslice);
StretchDIBits(Form1->Canvas->Handle, 0, 0, Form1->ww, Form1->wh,
0, 0, WIDTH, HEIGHT, &f[n], &bmi, DIB_RGB_COLORS, SRCCOPY);
n++;
n=n%wslice;
break;
case 5:
for(int i=0; i<12; i++)
{
CopyMemory(&f[n][((i%12)*WIDTH*HEIGHT*2)/12], &lpVHdr->lpData[((i%12)*WIDTH*HEIGHT*2)/12], (WIDTH*HEIGHT*2)/12);
StretchDIBits(Form1->Canvas->Handle,0,0,Form1->ww,Form1->wh,0,0,WIDTH,HEIGHT,
&f[n],&bmi,DIB_RGB_COLORS,SRCCOPY);
}
n++;
n=n%12;
break;
case 6:
for(int i=0; i<slice; i++) {
if((i%4)==0)
CopyMemory(&f[(n+30)%slice][((WIDTH*HEIGHT*2)/slice)*i],
&lpVHdr->lpData[((WIDTH*HEIGHT*2)/slice)*i],(WIDTH*HEIGHT*2)/slice);
else
CopyMemory(&f[(n)%slice][((WIDTH*HEIGHT*2)/slice)*i],
&lpVHdr->lpData[((WIDTH*HEIGHT*2)/slice)*i],(WIDTH*HEIGHT*2)/slice);
}

StretchDIBits(Form1->Canvas->Handle, 0, 0, Form1->ww, Form1->wh,
0, 0, WIDTH, HEIGHT, &f[n], &bmi, DIB_RGB_COLORS, SRCCOPY);
n++;
n=n%slice;
break;

}

return 0;
}
//-------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
state = 0;
ww = Form1->ClientWidth;
wh = Form1->ClientHeight;
Timer1->Enabled = false;
}
//--------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if(state)
capDriverDisconnect(vcHWND);
}
//--------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
m_wait.StartWait();

capGrabFrame(vcHWND);

m_wait.EndWait();

Form1->Text = "IntArt : " + IntToStr(m_wait.RateWait());
}
//--------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
ww = Form1->ClientWidth;
wh = Form1->ClientHeight;
}
//--------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
Form1->ClientWidth = 320;
Form1->ClientHeight = 240;
}
//--------------------------------------------------------

void __fastcall TForm1::FormShow(TObject *Sender)
{
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = ww;
bmi.bmiHeader.biHeight = wh;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 16;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;

vcHWND=capCreateCaptureWindow("My Window",WS_CHILD |
WS_VISIBLE,0,240,ww,wh,Form1->Handle,0);
capDriverConnect(vcHWND,0);
capPreviewRate(vcHWND,30);
capPreview(vcHWND,TRUE);
capSetCallbackOnFrame(vcHWND,FrameCallbackProc);
ShowWindow(vcHWND,SW_HIDE);

Form2->ShowModal();

m_slice = StrToInt(Form2->Edit1->Text);
wait = StrToInt(Form2->Edit2->Text);
m_data = data / m_slice;
m_wait.SetWait(wait);
Timer1->Enabled = true; //Form2->ShowModal();
}
//----------------------------------------------------------

謝辞: プログラムの作成に当たっては院生の捧君や村田君のプログラムを参考にしました。感謝いたします。