2013-03-28 47 views
6

私はPortAudioで音楽ビジュアライザーアプリケーションを作成しようとしています。私はいくつかの基礎研究を行い、マイクから(一時的な)ファイルへの録音方法の例を見出しました。しかし、記録中にデータがランタイムに使用されない例はありませんでした。PortAudioでの連続録音(マイクまたは出力から)

したがって、現在の「フレーム」からデータを取得できる連続オーディオストリームを開始するにはどうすればよいですか?

#include <stdio.h> 
#include <unistd.h> 
#include <time.h> 
#include <stdlib.h> 

#include "portaudio.h" 

#define SAMPLE_RATE (44100) 

typedef struct{ 
    int frameIndex; 
    int maxFrameIndex; 
    char* recordedSamples; 
} 
testData; 

PaStream* stream; 

static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){ 
    testData* data = (testData*)userData; 
    const char* buffer_ptr = (const char*)inputBuffer; 
    char* index_ptr = &data->recordedSamples[data->frameIndex]; 

    long framesToCalc; 
    long i; 
    int finished; 
    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; 

    if(framesLeft < frameCount){ 
     framesToCalc = framesLeft; 
     finished = paComplete; 
    }else{ 
     framesToCalc = frameCount; 
     finished = paContinue; 
    } 

    if(inputBuffer == NULL){ 
     for(i = 0; i < framesToCalc; i++){ 
      *index_ptr++ = 0; 
     } 
    }else{ 
     for(i = 0; i < framesToCalc; i++){ 
      *index_ptr++ = *buffer_ptr++; 
     } 
    } 

    data->frameIndex += framesToCalc; 
    return finished; 
} 

int setup(testData streamData){ 
    PaError err; 

    err = Pa_Initialize(); 
    if(err != paNoError){ 
     fprintf(stderr, "Pa_Initialize error: %s\n", Pa_GetErrorText(err)); 
     return 1; 
    } 

    PaStreamParameters inputParameters; 
    inputParameters.device = Pa_GetDefaultInputDevice(); 
    if (inputParameters.device == paNoDevice) { 
     fprintf(stderr, "Error: No default input device.\n"); 
     return 1; 
    } 

    inputParameters.channelCount = 1; 
    inputParameters.sampleFormat = paInt8; 
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; 
    inputParameters.hostApiSpecificStreamInfo = NULL; 

    err = Pa_OpenStream(&stream, &inputParameters, NULL, SAMPLE_RATE, 256, paClipOff, recordCallback, &streamData); 
    if(err != paNoError){ 
     fprintf(stderr, "Pa_OpenDefaultStream error: %s\n", Pa_GetErrorText(err)); 
     return 1; 
    } 

    err = Pa_StartStream(stream); 
    if(err != paNoError){ 
     fprintf(stderr, "Pa_StartStream error: %s\n", Pa_GetErrorText(err)); 
     return 1; 
    } 

    return 0; 
} 

void quit(testData streamData){ 
    PaError err; 
    err = Pa_Terminate(); 
    if(err != paNoError){ 
     fprintf(stderr, "Pa_Terminate error: %s\n", Pa_GetErrorText(err)); 
    } 

    if(streamData.recordedSamples) 
     free(streamData.recordedSamples); 
} 

int main(){ 
    int i; 
    PaError err; 
    testData streamData = {0}; 

    streamData.frameIndex = 0; 
    streamData.maxFrameIndex = SAMPLE_RATE; 
    streamData.recordedSamples = (char*)malloc(SAMPLE_RATE * sizeof(char)); 
    if(streamData.recordedSamples == NULL) 
     printf("Could not allocate record array.\n"); 

    for(i=0; i<SAMPLE_RATE; i++) 
     streamData.recordedSamples[i] = 0; 

    //int totalFrames = SAMPLE_RATE; 

    if(!setup(streamData)){ 
     printf("Opened\n"); 

     int i = 0; 

     while(i++ < 500){ 

      if((err = Pa_GetStreamReadAvailable(stream)) != paNoError) 
       break; 

      while((err = Pa_IsStreamActive(stream)) == 1){ 
       Pa_Sleep(1000); 
      } 

      err = Pa_CloseStream(stream); 
      if(err != paNoError) 
       break; 

      streamData.frameIndex = 0; 
      for(i=0; i<SAMPLE_RATE; i++) 
       streamData.recordedSamples[i] = 0; 
     } 

     if(err != paNoError){ 
      fprintf(stderr, "Active stream error: %s\n", Pa_GetErrorText(err)); 
     } 

     quit(streamData); 
    }else{ 
     puts("Couldn't open\n"); 
    } 
    return 0; 
} 

しかし、それは次のように出力できます:

は、これは私がそれを実行しようとしました方法です

ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear 
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe 
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side 
ALSA lib pcm_dmix.c:957:(snd_pcm_dmix_open) The dmix plugin supports only playback stream 
Active stream error: Can't read from a callback stream 
+0

あなたが作業しているどのようなプラットフォーム? –

+0

私はDebian Linux(64 bit)に取り組んでいます – tversteeg

答えて

4

更新

このコードの目的は何ですが?

 if((err = Pa_GetStreamReadAvailable(stream)) != paNoError) 
      break; 

これはあなたの(最新の)問題を引き起こしているようです。なぜストリームがコールバックストリームなので、待たずにストリームから読み込むことができるフレーム数を取得(そして破棄)する必要がありますか?


前の答え:

これは非常に疑わしいと思われる:

まず
static void* data; 
/* ... */ 
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){ 
    testData* data = (testData*)userData; 
    /* ... */ 
} 

、なぜdataという名前の2つの変数があるのですか?それはちょうど愚かなことです...より適切な識別子について考えることができますか?

第2に、&datavoid **)をPa_OpenStreamに渡しています。おそらく、Pa_OpenStreamはその同じ値をコールバック関数に渡します。ここでは、そのポインタをvoid *のように扱い、あたかもそれがtestData *を指しているかのように扱います。それは未定義の動作です。

削除 static void* data; それはここでは必要ではありません。新しくtestData data = { 0 };をメインの内側に宣言してください。今度はtestData *void *に変換)をPa_OpenStreamに渡しています。Pa_OpenStreamはそのコールバックに渡します。ここで安全にtestData *に戻すことができます。 Pa_OpenStreamを呼び出す前にdataのメンバーを設定するとよいでしょう。

+0

これらのエラーを指摘していただき、ありがとうございます。私はメモリ割り当てとは関係ないと思います(更新された質問を参照)。私はまだ 'Active stream error:コールバックストリームから読み込めません'というエラーが出ます。だから、 'Pa_GetStreamReadAvailable'呼び出しが壊れますが、私は使用可能なマイクを自分のコンピュータに接続しています。私は自分のコンピュータが出力として送るサウンドを使用する代わりの方法も好きです。 – tversteeg

+0

@ThomasVersteeg私の更新を見て... – Sebivor

+0

私はあなたの答えを確認する時間がなかったが、ポイントの時間がほとんど終了して以来、私はそれをあなたに与えます。私はこの日後にそれをチェックします! – tversteeg

0

最初の繰り返しでストリームerr = Pa_CloseStream(stream);を閉じました。 2番目の反復では、チャネルはすでに閉じられていました。すべての反復の後で操作err = Pa_CloseStream(stream);を試してください。

+0

あなたが指摘した行を削除すると、私はまだデータを取得しません。 – tversteeg

1

リアルタイムでデータストリームと対話するには、フレーム期間(サンプルレート/フレームあたりのサンプル)でスリープ(Windowsでビジー状態)するか、スレッド同期プリミティブを使用してスレッドをトリガーするメカニズムが必要ですint mainに処理可能なオーディオがあることを示します。これにより、PortAudioがコールバック中に(PortAudioスレッドから呼び出された)、各データフレームのデータにアクセスできるようになります。 boost::conditionboost::mutexを使ってコンセプトがどのように動作するかを示します。

//CAUTION THIS SNIPPET IS ONLY INTENDED TO DEMONSTRATE HOW ONE MIGHT 
//SYNCHRONIZE WITH THE PORTAUDIO THREAD 

#include <stdio.h> 
#include <unistd.h> 
#include <time.h> 
#include <stdlib.h> 

#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/thread/condition.hpp> 
#include <boost/interprocess/sync/scoped_lock.hpp> 

#include "portaudio.h" 

boost::condition waitForAudio; 
boost::mutex waitForAudioMutex; 
boost::mutex audioBufferMutex; 
bool trigger = false; 

std::deque<char> audioBuffer; 

static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){ 

    const char* buffer_ptr = (const char*)inputBuffer; 

    //Lock mutex to block user thread from modifying data buffer 
    audioBufferMutex.lock(); 

    //Copy data to user buffer 
    for(i = 0; i < frameCount; ++i) { 
     audioBuffer.push_back(buffer_ptr + i); 
    } 

    //Unlock mutex, allow user to manipulate buffer 
    audioBufferMutex.unlock(); 

    //Signal user thread to process audio 
    waitForAudioMutex.lock(); 
    trigger= true; 
    waitForAudio.notify_one(); 
    waitForAudioMutex.unlock(); 

    return finished; 
} 

int main(){ 
     Pa_Initialize(); 
     //OPEN AND START PORTAUDIO STREAM 
     while(true){ //Catch signal (Ctrl+C) or some other mechanism to interrupt this loop 
      boost::xtime duration; 
      boost::xtime_get(&duration, boost::TIME_UTC); 
      boost::interprocess::scoped_lock<boost::mutex> lock(waitForAudioMutex); 
      if(!trigger) { 
       if(!waitForAudio.timed_wait(lock, duration)) { 
        //Condition timed out -- assume audio stream failed 
        break; 
       } 
      } 
      trigger= false; 

      audioBufferMutex.lock(); 
      //VISUALIZE AUDIO HERE 
      //JUST MAKE SURE TO FINISH BEFORE PORTAUDIO MAKES ANOTHER CALLBACK 
      audioBufferMutex.unlock(); 
     } 
     //STOP AND CLOSE PORTAUDIO STEAM 
     Pa_Terminate(); 
     return 0; 
    } 

一般的に、この手法はクロスプラットフォームですが、この特定の実装はLinuxでのみ機能します。 Windowsでは、condition::timed_wait(lock, duration)の代わりにcondition::notify_one()WaitForSingleObject(eventVar, duration)の代わりにSetEvent(eventVar)を使用してください。

+0

@AshishKあなたの疑問は何ですか? – trukvl

+0

こんにちは@trukvlでお願いします(https://stackoverflow.com/questions/44645466/portaudio-real-time-audio-processing-for-continuous-input-stream) –

0

私はそれをこのように解決:

PaStreamParameters inputParameters ,outputParameters; 
PaStream*   stream; 
PaError    err; 
paTestData   data; 
int     i; 
int     totalFrames; 
int     numSamples; 
int     numBytes; 

err      = paNoError; 
inputParameters.device = 4; 

data.maxFrameIndex  = totalFrames = NUM_SECONDS * SAMPLE_RATE; 
data.frameIndex   = 0; 
numSamples    = totalFrames * NUM_CHANNELS; 
numBytes    = numSamples * sizeof(SAMPLE); 
data.recordedSamples = (SAMPLE *) malloc(numBytes); 

std::ofstream arch; 
arch.open("signal.csv"); 

err = Pa_Initialize(); 
inputParameters.channelCount = 1;      
inputParameters.sampleFormat = PA_SAMPLE_TYPE; 
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; 
inputParameters.hostApiSpecificStreamInfo = NULL; 

int contador = 0; 
bool strec = true; 
while (strec) 
{ 
    err = Pa_OpenStream(
      &stream, 
      &inputParameters, 
      NULL,     
      SAMPLE_RATE, 
      FRAMES_PER_BUFFER, 
      paClipOff,  
      recordCallback, 
      &data); 

    err = Pa_StartStream(stream); 

    printf("\n===Grabando.... ===\n"); fflush(stdout); 
    Pa_Sleep(3000); 
    while((err = Pa_IsStreamActive(stream)) == 1) 
    { 

    } 

    err = Pa_CloseStream(stream); 

    for(i=0; i<numSamples; i++) 

    { 

     val = data.recordedSamples[i]; 

     arch << val << std::endl; 
     std::cout << std::endl << "valor : " << val; 
    } 

    data.frameIndex = 0; 
    contador++; 
    if (contador >= 100) //if you delete this condition continuously recorded this audio 
    { 
     strec = false; 
     arch.close(); 
    } 
} 
関連する問題