Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top