在PortAudio中连续录制(来自麦克风或输出)

时间:2013-03-28 19:38:40

标签: c audio 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

4 个答案:

答案 0 :(得分:4)

更新

此代码的目的是什么?

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

在我看来,这会导致您的(最新)问题。为什么需要检索(然后丢弃)可以从流中读取的帧数而不等待,因为stream是回调流,所以可能会为零?

<小时/> 上一个回答:

这似乎非常可疑:

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的变量?那真是太傻了......你能想到更合适的标识符吗?

其次,您将&data(a void **)传递给Pa_OpenStream。据推测,Pa_OpenStream将相同的值传递给您的回调函数,您将该指针视为void *,就好像它指向testData *一样。这是未定义的行为。

删除 static void* data; 。这不是必要的。在main中声明一个新的testData data = { 0 };,在最顶部。现在您将testData *(转换为void *)传递给Pa_OpenStream,Pa_OpenStream会将其传递给您的回调,您可以安全地将其转换回testData *。您可能希望在调用Pa_OpenStream ...

之前设置data的成员

答案 1 :(得分:1)

要实时与数据流交互,您需要一种机制,可以在帧周期内休眠(在Windows上忙等待)(采样率/每帧采样数)或使用线程同步原语来触发您的线程{ {1}}已准备好处理音频。这将使您可以访问PortAudio在其回调期间提供的每个数据帧(从PortAudio线程调用)。以下是使用int mainboost::condition概念的概念。

boost::mutex

通常,此技术是跨平台的,但此特定实现可能仅适用于Linux。在Windows上使用//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; } 代替SetEvent(eventVar)condition::notify_one()而不是WaitForSingleObject(eventVar, duration)

答案 2 :(得分:0)

您在第一次迭代中关闭了流err = Pa_CloseStream(stream);。在第二次迭代中,频道已经关闭。在所有迭代之后尝试操作err = Pa_CloseStream(stream);

答案 3 :(得分: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();
    }
}