使用PortAudio写出不正确的帧数?

时间:2014-04-16 17:35:05

标签: c file audio audio-recording portaudio

运行我的程序,我似乎没有根据索引编写正确数量的帧。

$ ./test
Now recording!! Please speak into the microphone.
index = 0
Writing to: test.flac

audio.h

#include <stdint.h>
#include <string.h>

typedef struct
{
    uint32_t duration;
    uint16_t format_type;
    uint16_t number_of_channels;
    uint32_t sample_rate;
    uint32_t frameIndex;  /* Index into sample array. */
    uint32_t maxFrameIndex;
    char* recordedSamples;
} AudioData;

int recordFLAC(AudioData* data, const char *fileName);
AudioData* initAudioData(uint32_t sample_rate, uint16_t channels, uint32_t duration);

capture.c

#include <stdio.h>
#include <stdlib.h>
#include <portaudio.h>
#include <sndfile.h>
#include "audio.h"

AudioData* initAudioData(uint32_t sample_rate, uint16_t channels, uint32_t duration)
{
    AudioData* data = malloc(sizeof(*data));
    if (!data) return NULL;
    data->duration = duration;
    data->format_type = 1;
    data->number_of_channels = channels;
    data->sample_rate = sample_rate;
    data->frameIndex = 0;
    data->maxFrameIndex = sample_rate * duration;
    data->recordedSamples = malloc(sizeof(data->maxFrameIndex));
    if(!data->maxFrameIndex) return NULL;
    return data;
}

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

    (void) outputBuffer;
    (void) timeInfo;
    (void) statusFlags;

    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){
        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 recordFLAC(AudioData* data, const char *fileName)
{
    PaStreamParameters inputParameters;
    PaStream* stream;
    int err = 0;
    int totalFrames = data->maxFrameIndex;
    int numSamples;
    int numBytes;
    char max, val;
    double average;

    numSamples = totalFrames * data->number_of_channels;
    numBytes = numSamples;
    data->recordedSamples = malloc(numBytes);
    if(!data->recordedSamples)
    {
        printf("Could not allocate record array.\n");
        goto done;
    }
    for(int i = 0; i < numSamples; i++) data->recordedSamples[i] = 0;

    if((err = Pa_Initialize())) goto done;

    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
    if (inputParameters.device == paNoDevice) {
        fprintf(stderr,"Error: No default input device.\n");
        goto done;
    }
    inputParameters.channelCount = data->number_of_channels;                    /* stereo input */
    inputParameters.sampleFormat = data->format_type;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;

    /* Record some audio. -------------------------------------------- */
    err = Pa_OpenStream(&stream, &inputParameters, NULL, data->sample_rate, paFramesPerBufferUnspecified, paClipOff, recordCallback, &data);
    if(err) goto done;
    if((err = Pa_StartStream(stream))) goto done;
    puts("Now recording!! Please speak into the microphone.");

    while((err = Pa_IsStreamActive(stream)) == 1)
    {
        Pa_Sleep(1000);
        printf("index = %d\n", data->frameIndex);
    }
    if( err < 0 ) goto done;

    err = Pa_CloseStream(stream);
    if(err) goto done;

    /* Measure maximum peak amplitude. */
    max = 0;
    average = 0.0;
    for(int i = 0; i < numSamples; i++)
    {
        val = data->recordedSamples[i];
        val = abs(val);
        if( val > max )
        {
            max = val;
        }
        average += val;
    }

    average /= (double)numSamples;

done:
    Pa_Terminate();
    if(err)
    {
        fprintf(stderr, "An error occured while using the portaudio stream\n");
        fprintf(stderr, "Error number: %d\n", err);
        fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
        err = 1;          /* Always return 0 or 1, but no other return codes. */
    }
    else
    {
        SF_INFO sfinfo;
        sfinfo.channels = 1;
        sfinfo.samplerate = data->sample_rate;
        sfinfo.format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;

        // open to file
        printf("Writing to: %s\n", fileName);
        SNDFILE * outfile = sf_open(fileName, SFM_WRITE, &sfinfo);
        if (!outfile) return -1;

        // prepare a 3 second long buffer (sine wave)
        const int size = data->sample_rate * 3;

        // write the entire buffer to the file
        sf_write_raw(outfile, data->recordedSamples, size);

        // force write to disk
        sf_write_sync(outfile);
        // don't forget to close the file
        sf_close(outfile);
    }
    return err;
}

我不太确定我哪里出错了,我知道我需要写更多帧。有什么建议吗?

1 个答案:

答案 0 :(得分:2)

您对样本格式的假设似乎有问题。在回调中,您使用char *(单个字节)作为样本格式,但在libsndfile调用中,您使用SF_FORMAT_PCM_16打开一个16位文件。

这不太好:

data->format_type = 1;

我建议使用PortAudio库中的一个符号常量进行样本格式化。也许你想要一个16位的?如果是这样,您希望在PA回调中使用short*而不是char*

最后,如果您的频道数不是1,则复制循环不正确:

    for(i = 0; i < framesToCalc; i++){
        *index_ptr++ = 0;
    }

A&#34;框架&#34;包含所有通道的数据,例如,如果它是一个立体声输入,您的迭代需要处理左右声道,如下所示:

    for(i = 0; i < framesToCalc; i++){
        *index_ptr++ = 0; // left
        *index_ptr++ = 0; // right
    }

其他循环相同。