Question

In my previous question here, I managed to get a working .wav file as the output. However, when I throw this .wav file into my encoder (tested with other .wav files and works perfectly), my encoder throws back an error back at me right before it finishes.

Here is my output:

$ ./capture 2
Capture device is plughw:1,0    
Finished writing to /tmp/filevXDDX6.wav
Starting encode to /tmp/filevXDDX6.flac
Wrote 3641 bytes, 4096/88200 samples, 2/22 frames
Wrote 6132 bytes, 8192/88200 samples, 2/22 frames
Wrote 8748 bytes, 12288/88200 samples, 3/22 frames
Wrote 11253 bytes, 16384/88200 samples, 4/22 frames
Wrote 13697 bytes, 20480/88200 samples, 5/22 frames
Wrote 16222 bytes, 24576/88200 samples, 6/22 frames
Wrote 18811 bytes, 28672/88200 samples, 7/22 frames
Wrote 21900 bytes, 32768/88200 samples, 8/22 frames
Wrote 24681 bytes, 36864/88200 samples, 9/22 frames
Wrote 27408 bytes, 40960/88200 samples, 10/22 frames
Wrote 30494 bytes, 45056/88200 samples, 11/22 frames
Wrote 34107 bytes, 49152/88200 samples, 12/22 frames
Wrote 37447 bytes, 53248/88200 samples, 13/22 frames
Wrote 40719 bytes, 57344/88200 samples, 14/22 frames
Wrote 45257 bytes, 61440/88200 samples, 15/22 frames
Wrote 48735 bytes, 65536/88200 samples, 16/22 frames
Wrote 52842 bytes, 69632/88200 samples, 17/22 frames
Wrote 56529 bytes, 73728/88200 samples, 18/22 frames
Wrote 60185 bytes, 77824/88200 samples, 19/22 frames
Wrote 63906 bytes, 81920/88200 samples, 20/22 frames
ERROR: reading from WAVE file
Wrote 67687 bytes, 86016/88200 samples, 21/22 frames
Encoding: FAILED
   State: FLAC__STREAM_ENCODER_UNINITIALIZED

I'm not sure why it's cutting off early, though I'm pretty sure it has to do with how I'm trying to record my own .wav file (since my encoder worked fine with other files).

Here is my code (sorry it's a bit long, I cut down as much as possible):

// Compile with "g++ test.ccp -o test -lasound"

// Use the newer ALSA API
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

struct WaveHeader
{
        char RIFF_marker[4];
        uint32_t file_size;
        char filetype_header[4];
        char format_marker[4];
        uint32_t data_header_length;
        uint16_t format_type;
        uint16_t number_of_channels;
        uint32_t sample_rate;
        uint32_t bytes_per_second;
        uint16_t bytes_per_frame;
        uint16_t bits_per_sample;
};

struct WaveHeader *genericWAVHeader(uint32_t sample_rate, uint16_t bit_depth, uint16_t channels)
{
    struct WaveHeader *hdr;
    hdr = (WaveHeader*) malloc(sizeof(*hdr));
    if (!hdr)
        return NULL;

    memcpy(&hdr->RIFF_marker, "RIFF", 4);
    memcpy(&hdr->filetype_header, "WAVE", 4);
    memcpy(&hdr->format_marker, "fmt ", 4);
    hdr->data_header_length = 16;
    hdr->format_type = 1;
    hdr->number_of_channels = channels;
    hdr->sample_rate = sample_rate;
    hdr->bytes_per_second = sample_rate * channels * bit_depth / 8;
    hdr->bytes_per_frame = channels * bit_depth / 8;
    hdr->bits_per_sample = bit_depth;

    return hdr;
}

int writeWAVHeader(int fd, struct WaveHeader *hdr)
{
    if (!hdr)
        return -1;

    write(fd, &hdr->RIFF_marker, 4);
    write(fd, &hdr->file_size, 4);
    write(fd, &hdr->filetype_header, 4);
    write(fd, &hdr->format_marker, 4);
    write(fd, &hdr->data_header_length, 4);
    write(fd, &hdr->format_type, 2);
    write(fd, &hdr->number_of_channels, 2);
    write(fd, &hdr->sample_rate, 4);
    write(fd, &hdr->bytes_per_second, 4);
    write(fd, &hdr->bytes_per_frame, 2);
    write(fd, &hdr->bits_per_sample, 2);
    write(fd, "data", 4);

    uint32_t data_size = hdr->file_size + 8 - 44;
    write(fd, &data_size, 4);

    return 0;
}

int recordWAV(const char *fileName, struct WaveHeader *hdr, uint32_t duration)
{
    int err;
    int size;
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    unsigned int sampleRate = hdr->sample_rate;
    int dir;
    snd_pcm_uframes_t frames = 32;
    char *device = (char*) "plughw:1,0";
    char *buffer;
    int filedesc;

    printf("Capture device is %s\n", device);

    /* Open PCM device for recording (capture). */
    err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0);
    if (err)
    {
        fprintf(stderr, "Unable to open PCM device: %s\n", snd_strerror(err));
        return err;
    }

    /* Allocate a hardware parameters object. */
    snd_pcm_hw_params_alloca(&params);

    /* Fill it in with default values. */
    snd_pcm_hw_params_any(handle, params);

    /* ### Set the desired hardware parameters. ### */

    /* Interleaved mode */
    err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err)
    {
        fprintf(stderr, "Error setting interleaved mode: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }
    /* Signed 16-bit little-endian format */
    if (hdr->bits_per_sample == 16) err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
    else err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_U8);
    if (err)
    {
        fprintf(stderr, "Error setting format: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }
    /* Two channels (stereo) */
    err = snd_pcm_hw_params_set_channels(handle, params, hdr->number_of_channels);
    if (err)
    {
        fprintf(stderr, "Error setting channels: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }
    /* 44100 bits/second sampling rate (CD quality) */
    sampleRate = hdr->sample_rate;
    err = snd_pcm_hw_params_set_rate_near(handle, params, &sampleRate, &dir);
    if (err)
    {
        fprintf(stderr, "Error setting sampling rate (%d): %s\n", sampleRate, snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }
    hdr->sample_rate = sampleRate;
    /* Set period size*/
    err = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
    if (err)
    {
        fprintf(stderr, "Error setting period size: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }
    /* Write the parameters to the driver */
    err = snd_pcm_hw_params(handle, params);
    if (err < 0)
    {
        fprintf(stderr, "Unable to set HW parameters: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }

    /* Use a buffer large enough to hold one period */
    err = snd_pcm_hw_params_get_period_size(params, &frames, &dir);
    if (err)
    {
        fprintf(stderr, "Error retrieving period size: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        return err;
    }

    size = frames * hdr->bits_per_sample / 8 * hdr->number_of_channels; /* 2 bytes/sample, 2 channels */
    buffer = (char *) malloc(size);
    if (!buffer)
    {
        fprintf(stdout, "Buffer error.\n");
        snd_pcm_close(handle);
        return -1;
    }

    err = snd_pcm_hw_params_get_period_time(params, &sampleRate, &dir);
    if (err)
    {
        fprintf(stderr, "Error retrieving period time: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        free(buffer);
        return err;
    }

    uint32_t pcm_data_size = hdr->sample_rate * hdr->bytes_per_frame * duration / 1000;
    hdr->file_size = pcm_data_size + 44 - 8;

    filedesc = open(fileName, O_WRONLY | O_CREAT, 0644);
    err = writeWAVHeader(filedesc, hdr);
    if (err)
    {
        fprintf(stderr, "Error writing .wav header.");
        snd_pcm_close(handle);
        free(buffer);
        close(filedesc);
        return err;
    }
    fprintf(stdout, "Channels: %d\n", hdr->number_of_channels);
    for(int i = duration * 1000 / sampleRate; i > 0; i--)
    {
        err = snd_pcm_readi(handle, buffer, frames);
        if (err == -EPIPE) fprintf(stderr, "Overrun occurred: %d\n", err);
        if (err < 0) err = snd_pcm_recover(handle, err, 0);

        // Still an error, need to exit.
        if (err < 0)
        {
            fprintf(stderr, "Error occured while recording: %s\n", snd_strerror(err));
            snd_pcm_close(handle);
            free(buffer);
            close(filedesc);
            return err;
        }
        write(filedesc, buffer, size);
    }

    close(filedesc);
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);

    printf("Finished writing to %s\n", fileName);
    return 0;
}

int main(int argc, char *argv[]) 
{

    if(argc != 2)
    {
        fprintf(stderr, "Usage: %s (record duration)\n", argv[0]);
        return -1;
    }

    int err;
    struct WaveHeader *hdr;

    // Creates a temporary file in /tmp

    char wavFile[L_tmpnam + 5];
    char *tempFilenameStub = tmpnam(NULL);
    sprintf(wavFile, "%s.wav", tempFilenameStub);
    hdr = genericWAVHeader(44000, 16, 2);
    if (!hdr)
    {
        fprintf(stderr, "Error allocating WAV header.\n");
        return -1;
    }

    err = recordWAV(wavFile, hdr, 1000 * strtod(argv[1], NULL));
    if (err)
    {
            fprintf(stderr, "Error recording WAV file: %d\n", err);
            return err;
    }

    free(hdr);
    return 0;
}

Any suggestions?


Edit - I was told to compare the headers of my .wav file to that of one generated by arecord. Here are the results:

$ stat -c %s arecord.wav
352844

$ stat -c %s /tmp/filevXDDX6.wav
345004

$ xxd -g1 arecord.wav | head
0000000: 52 49 46 46 44 62 05 00 57 41 56 45 66 6d 74 20  RIFFDb..WAVEfmt
0000010: 10 00 00 00 01 00 02 00 44 ac 00 00 10 b1 02 00  ........D.......
0000020: 04 00 10 00 64 61 74 61 20 62 05 00 08 00 08 00  ....data b......
0000030: 08 00 08 00 07 00 07 00 fe ff fe ff f7 ff f7 ff  ................
0000040: f6 ff f6 ff ee ff ee ff ee ff ee ff f6 ff f6 ff  ................
0000050: f0 ff f0 ff e7 ff e7 ff ee ff ee ff f4 ff f4 ff  ................
0000060: f4 ff f4 ff f7 ff f7 ff fe ff fe ff fc ff fc ff  ................
0000070: fa ff fa ff f5 ff f5 ff ed ff ed ff ee ff ee ff  ................
0000080: f8 ff f8 ff f4 ff f4 ff ed ff ed ff ee ff ee ff  ................
0000090: f8 ff f8 ff f6 ff f6 ff f0 ff f0 ff ee ff ee ff  ................

$ xxd -g1 /tmp/filevXDDX6.wav | head
0000000: 52 49 46 46 44 62 05 00 57 41 56 45 66 6d 74 20  RIFFDb..WAVEfmt
0000010: 10 00 00 00 01 00 02 00 44 ac 00 00 10 b1 02 00  ........D.......
0000020: 04 00 10 00 64 61 74 61 20 62 05 00 71 00 71 00  ....data b..q.q.
0000030: 6f 00 6f 00 79 00 79 00 75 00 75 00 63 00 63 00  o.o.y.y.u.u.c.c.
0000040: 3e 00 3e 00 1b 00 1b 00 07 00 07 00 fb ff fb ff  >.>.............
0000050: 00 00 00 00 0c 00 0c 00 0f 00 0f 00 1c 00 1c 00  ................
0000060: 30 00 30 00 31 00 31 00 24 00 24 00 1e 00 1e 00  0.0.1.1.$.$.....
0000070: 24 00 24 00 31 00 31 00 2c 00 2c 00 28 00 28 00  $.$.1.1.,.,.(.(.
0000080: 31 00 31 00 3c 00 3c 00 36 00 36 00 31 00 31 00  1.1.<.<.6.6.1.1.
0000090: 39 00 39 00 40 00 40 00 3d 00 3d 00 30 00 30 00  9.9.@.@.=.=.0.0.

$ file arecord.wav
test.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz

$ file /tmp/filevXDDX6.wav                        
/tmp/filevXDDX6.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz
Was it helpful?

Solution

Your program does not write the correct number of samples to the .wav file.

snd_pcm_readi returns the number of frames that was actually read. You can only write that many frames to the output.

The integer division in duration * 1000 / sampleRate might be rounded. It is likely that duration * 1000 / sampleRate * frames is not exactly the same as the number of frames you actually want to read.

You should restructure your loop to count the total number of frames.

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