bluetooth マイクからの音声入力

解決


shun  2009-09-19 01:40:33  No: 70955

windows xp、VC++6.0の環境でwin32 console application プロジェクトを用いてbluetoothヘッドセットマイクからの入力音声を録音するプログラムを作成したのですが、音声の録音が開始されません。エラーなどは何もなくビルド出来ているのですが、調べた結果、waveinstart関数が指定した時間ではなく瞬間的に終了しています。これはbluetoothが無線だから上手くスタート出来ていないのでしょうか?まったく対策も思い浮かばず困り果てていますのでどなたかアドバイスいただけますでしょうか?宜しくお願い致します。
ちなみにbluetoothヘッドセットはハードウェアテストで正常に動作することは確認しております。


オショウ  2009-09-19 03:29:27  No: 70956

waveInOpenで、録音する際のコールバック関数waveInProcを登録して
いると思いますが、waveInOpen以降で録音用のバッファを割り当て、
waveInStartすることになりますが、バッファ容量が少ない為、録音
が終了してしまっているのでは?

ここは非同期コールバックなので、延々録音したいのであれば、
waveInProcが呼び出された際、バッファの内容を保存し、再度、
バッファをwaveInAddBufferで割り付け録音を行う・・・

この辺、なかなかに大変な部分なので、頑張って下さい!

以上。参考まで


shun  2009-09-24 18:31:06  No: 70957

オショウさん、早いレスポンスありがとうございます。
返信が遅くなってしまい、申し訳ありません。
録音時間も2秒と短く、バッファ容量も同じだけ与えているのですが、録音が即座に終了してしまっています。
原因としてbluetoothを使用するときにはプロファイルを介さないと録音も再生もできないのではないかと考え始めていますが、通常の優先マイクでもbluetoothヘッドセットからでも同じように録音・再生できるものなのでしょうか?
質問がbluetoothの話に及んでしまって申し訳ありませんが、宜しくお願いします。


オショウ  2009-09-24 19:09:44  No: 70958

一般的に区別はありません。
因みに、マイク端子に直接マイクを接続した場合は、正常に録音
できていますか?
これができていて、bluetoothのマイクがダメなら、APIの呼び出
し順番や各API呼び出し時のリターンコード等調べないといけない
かと。

bluetoothで携帯電話と通信させた場合、bluetoothが機器と接続
状態になっても即オープンできませんでした。若干のタイムラグ
があると言うか、機器と正常に通信できるようになるまで時間が
かかるようで、待ってやらないと成功しませんでした。

●  即座に終了する・・・と言うのは
    waveInProcを登録せず、waveInStartしたら、即APIから返ってくる
    と言う状態を指し示しているように思いました。

    録音の手順は・・・

1.  waveInGetNumDevs で、使用可能な入力デバイスがあるか確認
2.  waveInGetDevCaps で、乳りょきデバイスの情報取得
3.  waveInOpen で、入力デバイスをオープン
4.  録音用データバッファの容量を計算し確保
    waveInOpenの際に取得したデバイス情報から算出
    wfx.nSamplesPerSec * wfx.wBitsPerSample / 8 * 2;  // 2秒間の録音サイズ
5.  waveInPrepareHeader で、録音開始準備
6.  waveInStart で、録音開始
7.  waveInProcが呼び出される
    受信バッファの物理デバイスへの保存
    長時間録音の場合は、waveInAddBuffer で、再度録音用バッファを
    登録する。
    ※  本来この時点で行った場合、間に合わないので、受信用バッファの
        容量やバッファ数は予め用意(確保)して行うのが好ましい。
8.  録音停止を判断したら、waveInStop で、録音停止
9.  waveInReset で、入力デバイスのリセット
10. waveInUnprepareHeader で、録音終了
11. waveInClose で、入力デバイスのクローズ
12. 受信バッファ等動的に確保したメモリの開放

こんな流れになろうかと。

この流れと違うならば・・・
録音はできていないはずです。

以上。参考まで


オショウ  2009-09-24 19:10:51  No: 70959

追伸

DirectX のDirectSound使った方が、楽かもヨ・・・
私は古い人間なので、DirectXは使ったことないですが。

以上。


shun  2009-09-24 20:05:45  No: 70960

オショウさん、丁寧なレスポンスありがとうございます。
直接マイクを接続して試していないので試してみます。
プログラムのは、
http://www.codeproject.com/KB/audio-video/VoiceRecording.aspx
からダウンロードしたクラスを使用しています。この場合、waveInGetNumDevsおよびwaveInGetDevCapsを使用してないのですが、エラーなくオープン出来ているみたいなので必要ないのかと思っていました。
最後のクローズ・メモリの解放はしてなかったので、記述したいと思います。
最終的にはほぼリアルタイムで入力と再生を行うプログラムを作成したいので、DirectSoundで実現できるのでしょうか。
初心者でわからないことばかりのため脈絡のない文章になってしまってすいません。


オショウ  2009-09-24 21:12:32  No: 70961

codeprojectのコードですが・・・
CVoiceBase::PrepareBuffer を呼び出していますか?
ここで録音したい秒数分のバッファを確保してその情報
を設定しています。

要は、VoiceRecording.cppのみでは録音動作はしない。
と言うことです。(設定情報不足・・・)

以上。


shun  2009-09-24 22:35:42  No: 70962

オショウさん、レスありがとうございます。
メイン関数の中はこんな感じです。クラスなどは何一つ変えていません。
_______________________________
CVoiceRecording m_Record;
CVoicePlaying m_Play;

m_Record.PrepareBuffer(10);    //prepare buffer for recording 10 seconds.

m_Record.Open();
    
m_Play.PrepareBuffer(10);    //prepare buffer for playing of 10 seconds of data

m_Play.Open();    

if (m_Record.IsOpen())
{
  printf("Please speak at the microphone. \n");
    m_Record.Record();
  printf("The voice will be announced soon. \n");
}

//after finishing the record scenario,

//play the buffer, first copy recorded buffer to m_Play buffer

m_Play.CopyBuffer(m_Record.buffer, 10);
        
if (m_Play.IsOpen())
{
    m_Play.Play();
}

m_Record.Close();
m_Play.Close();
m_Record.DestroyBuffer();
m_Play.DestroyBuffer();
____________________

クローズ・メモリの解放も加えてましたが、やはり即座にプログラムが終了してしまいます。
おしょうさんのおっしゃる通り、waveInProcやstartあたりがおかしいのかもしれません。そのあたりについて調べてみたいと思います。
また、有線マイクなら問題なくできるかについては手元に通常のマイクがないので購入して試してみたいと思います。


shun  2009-09-24 22:40:20  No: 70963

追記ですが、codeprojectのコードはページの上の方にあるダウンロードボタンで再生まで含めたコードを入手できます。
ページ上に記載しているのは、あくまでサンプルみたいです。


オショウ  2009-09-24 23:37:53  No: 70964

codeprojectは、私もよく利用するので・・・

で、そのサンプルコードですが・・・
あくまでサンプルで、うまく動作するように自分で改造
しないとダメです。

例えば・・・
void CVoiceRecording::RecordFinished
この関数、同期化させる何か機能を追加しないと・・・
要は、VoiceWaveInProcが呼ばれた時点で、
pVoice->RecordFinished();
が呼ばれるので、録音終了に伴うメインスレッド側での
待ち合わせを同期するような追加機能が必要です。

※  動作する完成された機能がいつも提供されるとは
    思わない方がよいです。

因みに再生側も同様で、m_Play.Play();よコールした後
再生完了を待ち合わせる機能が必要です。

その辺細工したら、一応、有線マイクですが、録音・再生
できました。

※  録音しながら再生されるには、もう少し細工が必要かも
    しれません。
    こちらでは、10秒録音して、10秒再生にしました。

以上。参考まで


shun  2009-09-25 00:28:42  No: 70965

オショウさん、レスありがとうございます。
そんなにすぐにできるなんてすごいですね。私は関数についてはMSDNの簡単な説明だけしか調べてなかったので、それで動くものだと考えてしまっていました。
上記でアドバイス頂いている内容ですが、初心者の私にはこのコールバック関数なぜ必要なのかどの様にwaveInOpen関数に関わってくるのかわからないので、RecordFinished関数に何を記述したらよいかわかりません。
申し訳ないのですが、もう少しヒントを頂けないでしょうか?
こんな初心者につきあっって頂いてありがとうございます。


オショウ  2009-09-25 02:53:35  No: 70966

// VoiceRecording.hの追加修正(部分のみ)

class CVoiceRecording : public CVoiceBase  
{
public:
  virtual void RecordFinished();
  BOOL IsOpen();
  BOOL Close();
  BOOL Open();
    BOOL Record();
    HWAVEIN hWaveIn;
  BOOL  bRecorded;
  BOOL  IsRecorded();
  CVoiceRecording();
  virtual ~CVoiceRecording();

};

// VoiceRecording.cppの追加修正(部分のみ)

CVoiceRecording::CVoiceRecording()
{
  hWaveIn=NULL;
  bRecorded = FALSE;
}

BOOL CVoiceRecording::Record()
{
  bRecorded = FALSE;

  res=waveInPrepareHeader(hWaveIn,&WaveHeader,sizeof(WAVEHDR));
  GetMMResult(res);
  if (res!=MMSYSERR_NOERROR)
    return FALSE;

  res=waveInAddBuffer(hWaveIn,&WaveHeader,sizeof(WAVEHDR));
  GetMMResult(res);
  if (res!=MMSYSERR_NOERROR)
    return FALSE;
    
  res=waveInStart(hWaveIn) ;
  GetMMResult(res);
  if (res!=MMSYSERR_NOERROR)
    return FALSE;
  else
    return TRUE;
    
}

BOOL CVoiceRecording::IsRecorded()
{
  return bRecorded;
}

void CVoiceRecording::RecordFinished()
{
  //write your handler here
  bRecorded = TRUE;

  //or create your own classes that derived from this class
  //and override this virtual function
}

// VoicePlaying.hの追加修正(部分のみ)

class CVoicePlaying : public CVoiceBase  
{
public:
  void PlayFinished();
  BOOL IsOpen();
  BOOL Close();
  BOOL Open();
    BOOL Play();
  HWAVEOUT hWaveOut;
  BOOL  bPlayed;
  BOOL  IsPlayed();
  CVoicePlaying();
  virtual ~CVoicePlaying();

};

// VoicePlaying.cppの追加修正(部分のみ)

CVoicePlaying::CVoicePlaying()
{
  hWaveOut=NULL;
  bPlayed=FALSE;
}

BOOL CVoicePlaying::Play()
{
  bPlayed = FALSE;

  res=waveOutPrepareHeader (hWaveOut,&WaveHeader,sizeof(WAVEHDR));
    GetMMResult(res);
  if (res!=MMSYSERR_NOERROR)
    return FALSE;
    
  res=waveOutWrite(hWaveOut,&WaveHeader,sizeof(WAVEHDR));  
    GetMMResult(res);
  if (res!=MMSYSERR_NOERROR)
    return FALSE;
  else
    return TRUE;
}

BOOL CVoicePlaying::IsPlayed()
{

  return bPlayed;

}

void CVoicePlaying::PlayFinished()
{
  //write your own handler here
  bPlayed = TRUE;

  //or simply create your own class and override this virtual function
}

// MAIN

int _tmain(int argc, _TCHAR* argv[])
{
  BOOL  bRet;

  CVoiceRecording m_Record;
  CVoicePlaying m_Play;

  bRet = m_Record.Open();
  if(bRet){
    m_Record.PrepareBuffer(10);    //prepare buffer for recording 10 seconds.

    if (m_Record.IsOpen()){
      printf("Please speak at the microphone. \n");
      bRet = m_Record.Record();
      printf("The voice will be announced soon. \n");
    }

    do{
      ::Sleep(100);
    }while(!m_Record.IsRecorded());
    
    m_Record.Close();
  }

  if(bRet){
    bRet = m_Play.Open();
    if (bRet){
      bRet = m_Play.PrepareBuffer(10);    //prepare buffer for playing of 10 seconds of data
      if(bRet){
        m_Play.CopyBuffer(m_Record.buffer, 10);

        if (m_Play.IsOpen()){
          m_Play.Play();
        }
      }
    }

    do{
      ::Sleep(100);
    }while(!m_Play.IsPlayed());

    m_Play.Close();
  }

  m_Record.DestroyBuffer();
  m_Play.DestroyBuffer();

  return 0;
}

こんなもんかナ〜
最低限の動作できるようにしか改造してません。
あしからず。(決してうまい方法だとは思わないんです)

フラグ処理ではなく、イベント処理で行うのがCPU負荷
食わないでいいのですが・・・

※  録音時のボリューム調整が、このサンプルにはありません。
    再生時も・・・
    ミキサーデバイスをオープして・・・
    ボリューム調整をプログラムから行えるようにすれば、一応
    完成かナ〜

    頑張って下さい!

以上。参考まで


shun  2009-09-25 03:59:02  No: 70967

オショウさん、こんなに手取り足取り教えて頂きありがとうございます。
bluetoothヘッドセットからでも録音・再生ができました。
CPU負荷などのことは全く分かりませんので、今回改良頂いたプログラムとともに勉強したいと思います。
本当に助かりました。私もオショウさんのように熟練者になれるようにがんばっていきます。
これからリアルタイム性を考慮した細工を施していきたいと思いますが、うまくいかないときはまた宜しくお願いします。


shun  2009-09-28 19:13:21  No: 70968

解決チェックを忘れていました。
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加