2017-11-04 24 views
2

현재 녹음하는 응용 프로그램과 함께 portaudio를 사용하고 있는데 샘플을 수집하는 데 문제가있는 것 같습니다. 내가 볼 수있는 것은 단지 하나의 샘플 만 저장된다는 것이고, 콜백은 한번만 호출된다는 것인데, 변수 NUM_OF_SECONDS이 30 초로 설정되어 있더라도.portaudio는 하나의 샘플만을 사용합니까?

현재 내가 테스트 할 수있는 아이디어가 부족하고 어떻게 디버깅 할 수 있습니까? 그래서 여기에 문제를 디버깅 할 수있는 방법을 제안했습니다.

MAIN.CPP :

#include <record.h> 

int main() 
{ 
    record somethis; 
    somethis.start_record(); 
    return 0; 
} 

record.h

#pragma once 
#include <iostream> // Functionality: COUT 
#include "portaudio.h" 
#include <stdio.h> 
#include <stdlib.h> 
#include <chrono> //Functionality: Sleep 
#include <thread> //Functionality: Sleep 
#include <algorithm> //Functionality: fill_n 
#define SAMPLE_RATE (44100) 

typedef float SAMPLE; 

#define NUM_SECONDS 30 
#define NUM_CHANNELS 2 
#define SAMPLE_SILENCE 0.0f 
#define PA_SAMPLE_TYPE paFloat32 
#define FRAMES_PER_BUFFER (512) 
#define TRUE (1==1) 
#define FALSE (!TRUE) 
#define WRITE_TO_FILE TRUE 


typedef struct 
{ 
    int  frameIndex; 
    int  maxFrameindex; 
    SAMPLE *recordedSamples; 
} 
paTestData; 

class record { 
public: 
    record(); 
    void start_record(); 
private: 
    PaStreamParameters inputParameters, 
         outputParameters; 
    PaStream*   stream; 
    PaError    err = paNoError; 
    paTestData   data; 
    int     totalFrames; 
    int     numSamples; 
    int     numBytes; 
    SAMPLE    max, val; 
    double    average; 
    int recordCallback(const void *inputBuffer, void *outputBuffer, 
         unsigned long framesPerBuffer, 
         const PaStreamCallbackTimeInfo* timeInfo, 
         PaStreamCallbackFlags statusFlags, void *userData); 

    static int recordCallbackSub(const void *inputBuffer, void *outputBuffer, 
         unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, 
         PaStreamCallbackFlags statusFlags, void *userData) 
    { 
     auto pThis = reinterpret_cast<record*>(userData); // get back the this pointer. 
     return pThis->recordCallback(inputBuffer, outputBuffer,framesPerBuffer, timeInfo,statusFlags, nullptr); 

    } 
}; 

record.cpp

#include "record.h" 


record::record() 
{ 
    std::cout << "Record object made" << std::endl; 
    std::cout << "Portaudio Version: " << Pa_GetVersion() << std::endl; 
    this->data.maxFrameindex = this->totalFrames = NUM_SECONDS * SAMPLE_RATE; 
    this->data.frameIndex = 0; 
    this->numSamples = this->totalFrames * NUM_CHANNELS; 
    numBytes = numSamples * sizeof(SAMPLE); 
    this->data.recordedSamples = new SAMPLE[numSamples]; /* From now on, recordedSamples is initialised. */ 
    if(this->data.recordedSamples == NULL) 
    { 
     std::cout << "Could not allocate record array" << std::endl; 
     exit(1); 
    } 

    for(int i=0; i<numSamples; i++) 
    { 
     this->data.recordedSamples[i] = 0; 

    } 

    int err = Pa_Initialize(); 

    if(err == paNoError) 
    { 
     std::cout << "No error in init" << std::endl; 
     std::cout << "PortAudio init: "<< Pa_GetErrorText(err) << std::endl; 
    } 
    else 
    { 
     printf( "PortAudio error: %s\n", Pa_GetErrorText(err)); 
     exit(1); 
    } 

    this->inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ 


    if (this->inputParameters.device == paNoDevice) { 
     std::cout << "Error: No default input device" << std::endl; 
     exit(1); 
    } 

    this->inputParameters.channelCount = 1;     /* stereo input */ 
    this->inputParameters.sampleFormat = PA_SAMPLE_TYPE; 
    this->inputParameters.suggestedLatency = Pa_GetDeviceInfo(this->inputParameters.device)->defaultLowInputLatency; 
    this->inputParameters.hostApiSpecificStreamInfo = NULL; 

    std::cout << "Device name: " <<Pa_GetDeviceInfo(this->inputParameters.device)->name << std::endl; 
    std::cout << "Max inputChannels: " <<Pa_GetDeviceInfo(this->inputParameters.device)->maxInputChannels << std::endl; 

} 

int record::recordCallback(const void *inputBuffer, void *outputBuffer, 
          unsigned long framesPerBuffer, 
          const PaStreamCallbackTimeInfo* timeInfo, 
          PaStreamCallbackFlags statusFlags, void *userData) 
{ 
    std::cout << "Callback called" << std::endl; 
    this->data = (paTestData&) userData; 
    const SAMPLE *rptr = (const SAMPLE*)inputBuffer; 
    SAMPLE *wptr = &this->data.recordedSamples[this->data.frameIndex * NUM_CHANNELS]; 
    long framesToCalc; 
    long i; 
    int finished; 
    unsigned long framesLeft = this->data.maxFrameindex - this->data.frameIndex; 

    (void) outputBuffer; /* Prevent unused variable warnings. */ 
    (void) timeInfo; 
    (void) statusFlags; 
    //(void) userData; 

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

    if(inputBuffer == NULL) 
    { 
     for(int i=0; i<framesToCalc; i++) 
     { 
      *wptr++ = SAMPLE_SILENCE; /* left */ 
      if(NUM_CHANNELS == 2) *wptr++ = SAMPLE_SILENCE; /* right */ 
     } 
    } 
    else 
    { 
     for(int i=0; i<framesToCalc; i++) 
     { 
      *wptr++ = *rptr++; /* left */ 
      if(NUM_CHANNELS == 2) *wptr++ = *rptr++; /* right */ 
     } 
    } 
    this->data.frameIndex += framesToCalc; 
    return finished; 
} 

void record::start_record() 
{ 

    err = Pa_OpenStream(
       &this->stream, 
       &this->inputParameters, 
       NULL,     /* &outputParameters, */ 
       SAMPLE_RATE, 
       FRAMES_PER_BUFFER, 
       paClipOff,  /* we won't output out of range samples so don't bother clipping them */ 
       &record::recordCallbackSub, 
       this); 
    if(err != paNoError) 
    { 
     std::cout << "Something wrong - open_stream check" << std::endl; 
     std::cout << "PortAudio error: "<< Pa_GetErrorText(err) << std::endl; 
     exit(1); 
    } 

    this->err = Pa_StartStream(this->stream); 

    if(err != paNoError) 
    { 
     std::cout << "Something wrong in stream check" << std::endl; 
     std::cout << "PortAudio error: "<< Pa_GetErrorText(err) << std::endl; 
     exit(1); 
    } 

    std::cout << "Waiting for playback to finish" << std::endl; 

    while((err = Pa_IsStreamActive(stream)) == 1) 
    { 
     Pa_Sleep(1000); 
     printf("index = %d\n", this->data.frameIndex); fflush(stdout); 

    } 
    if(err < 0) 
    { 
     std::cout << "error check with isStreamActive - something wrong" << std::endl; 
     std::cout << "PortAudio error: "<< Pa_GetErrorText(err) << std::endl; 
     exit(1); 
    } 

    err = Pa_CloseStream(stream); 
    if(err != paNoError) 
    { 
     std::cout << "error check with close_stream- something wrong" << std::endl; 
     std::cout << "PortAudio error: "<< Pa_GetErrorText(err) << std::endl; 
     exit(1); 
    } 

    std::cout << "Number of entries: " << sizeof(this->data.recordedSamples)/sizeof(this->data.recordedSamples[0]) << std::endl; 

    /* Measure maximum peak amplitude. */ 
    max = 0; 
    average = 0.0; 
    for(int i=0; i<numSamples; i++) 
    { 

     val = this->data.recordedSamples[i]; 
     std::cout << "i: " << i << " : "<< val << std::endl; 
     std::this_thread::sleep_for(std::chrono::milliseconds(100)); 

     if(val < 0) val = -val; /* ABS */ 
     if(val > max) 
     { 
      max = val; 
     } 
     average += val; 
    } 

    average = average/(double)numSamples; 

    std::cout<<"sample max amplitude = " << max << std::endl; 
    std::cout<<"sample average = " << average << std::endl; 

    if (WRITE_TO_FILE) 
    { 
     FILE *fid; 
     fid = fopen("recorded.wav", "wb"); 
     if(fid == NULL) 
     { 
      printf("Could not open file."); 
     } 
     else 
     { 
      fwrite(data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid); 
      fclose(fid); 
      printf("Wrote data to 'recorded.raw'\n"); 
     } 
    } 



    std::cout << "Everythin done!" << std::endl; 



} 

업데이트 :

,691 여기

코드입니다

일부 디버깅 공지에서 콜백이 한 번만 호출되고 반환 된 후에는 스트림이 비활성 상태가되어 콜백 함수를 호출 할 수 없습니다. 스트림이 비활성 상태 인 이유는 무엇입니까?

+0

질문에 답할 수있는 내용은 안에 포함 된 정보로만 작성하십시오. – bolov

+0

당신은 어떤 언어로 글을 쓰고 있습니까? 질문에 대답하기 위해 외부 링크를 방문 할 필요는 없습니다. 그것은'C' 또는'C++'인가? 하나 골라주세요. – bolov

+0

코드를 추가하고 태그를 변경했습니다. @bolov – Lamda

답변

2

첫 번째 콜백

static int recordCallbackSub(const void *inputBuffer, void *outputBuffer, 
        unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, 
        PaStreamCallbackFlags statusFlags, void *userData) 
{ 
    auto pThis = reinterpret_cast<record*>(userData); // get back the this pointer. 
    return pThis->recordCallback(inputBuffer, outputBuffer,framesPerBuffer, timeInfo,statusFlags, nullptr); 

} 

는 두 번째 콜백 nullptr로 설정 userData 매개 변수

int record::recordCallback(const void *inputBuffer, void *outputBuffer, 
          unsigned long framesPerBuffer, 
          const PaStreamCallbackTimeInfo* timeInfo, 
          PaStreamCallbackFlags statusFlags, void *userData) 
{ 
    std::cout << "Callback called" << std::endl; 
    this->data = (paTestData&) userData; 

    .... 
} 

호출합니다. 그런 다음 nullptrpaTestData&으로 전송하고 data 회원 변수를 결과로 설정합니다. 사용자가 의도하지 않은 것으로 의심됩니다.

this->data = (paTestData&) userData; 행을 삭제하십시오.

+0

고정되어있는 것 같습니다. portaudio가 제공 한 paex_record.c 예제 스크립트를 사용하고있었습니다. 나는/여전히 약간의 userData 실제로 무엇을 확신 할 수 있습니까? – Lamda

+1

userData 매개 변수는 Port Audio와 같은 C API의 일반적인 기능으로, 사용자가 정의한 임의의 정보를 콜백에 전달할 수 있습니다. 클래스에 대한 포인터를'recordCallbackSub' 함수에 전달하면 정적 함수에서 멤버 함수로 이동할 수 있습니다. 'void * userData' 매개 변수가 없으면 간단하게 불가능합니다. paex_record.c 예제는이 메커니즘을 사용하여 콜백에 데이터 구조체를 전달하지만 이미 클래스 안에 있으므로 멤버 변수를 직접 대신 사용할 수 있습니다. – t0m