9

我正在尝试在 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

4 回答 4

5

更新

这段代码的目的是什么?

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

其次,您将&data(a void **) 传递给 Pa_OpenStream。据推测,Pa_OpenStream 将相同的值传递给您的回调函数,在那里您将该指针void *视为指向一个testData *. 那是未定义的行为。

删除static void* data;. 没必要,这里。testData data = { 0 };在最顶部声明​​一个新的内部主干。现在您将 a testData *(转换为void *)传递给 Pa_OpenStream,Pa_OpenStream 会将其传递给您的回调,您可以在回调中安全地将其转换回testData *原样。您可能希望data在调用 Pa_OpenStream 之前设置成员...

于 2013-04-02T12:36:30.743 回答
2

要与数据流实时交互,您需要一种机制,该机制可以在帧周期(采样率/每帧样本)内休眠(Windows 上的忙等待)或使用线程同步原语来触发int main有音频的线程准备处理。这将使您能够访问 PortAudio 在其回调期间传递的每一帧数据(从 PortAudio 线程调用)。以下是使用boost::conditionand的概念boost::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 上使用SetEvent(eventVar)代替condition::notify_one()WaitForSingleObject(eventVar, duration)代替condition::timed_wait(lock, duration).

于 2013-05-25T21:34:14.770 回答
0

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

于 2013-04-13T01:24:23.907 回答
-1

我是这样解决的:

    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();
        }
    }
于 2015-04-23T17:22:53.720 回答