Domanda

Running my program, I appear to not be writing the correct amount of frames according to the index.

$ ./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;
}

I'm not quite sure where I am going wrong, I know I need to be writing more frames. Any suggestions?

È stato utile?

Soluzione

There seems to be something wrong with your assumptions about the sample format. In the callback you are using char * (single bytes) for the sample format, but in your libsndfile call you're opening a 16 bit file with SF_FORMAT_PCM_16.

This is not so good:

data->format_type = 1;

I recommend using one of the symbolic constants in the PortAudio library for sample formatting. Maybe you want a 16 bit one? And if so, you want to be using short* not char* in the PA callback.

Finally, if your channel count is not 1, the copy loops are incorrect:

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

A "frame" contains data for all channels, so for example, if it's a stereo input your iteration needs to deal with both left and right channels like this:

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

Same for the other loops.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top