I have a test program that loads an AIFF or WAV file with libsndfile and
plays then using libao. After running around in circles for a while, I
found and fixed the problem, but I'm still confused as to why the solution
works. Would someone please explain this to me so I don't trip over this
again later in my project?
I used sf_open_fd() to get a SNDFILE* pointer and an AF_INFO struct. In
that struct I find out what bit resolution the samples are. I put that
number into ao_sample_format format.bits, fill a buffer using
sf_read_int() and then call ao_play(). Doing this with a file containing
8-bit samples, the result was the file playing at at half-pitch and twice
as long as normal. But, if I manually plug in format.bits = 32, the files
play fine.
Here's the program:
/* compile with "gcc -o foo foo.c -lao -ldl -lm -lsndfile" */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ao/ao.h>
#include <sndfile.h>
#include <math.h>
ao_device *device;
ao_sample_format format;
int main(int argc, char *argv[])
{
int default_driver;
int frames_read;
int *buffer;
SNDFILE *sndfile;
SF_INFO sf_info;
FILE *fp;
if (argc != 2) {
printf("usage: %s <filename>\n", argv[0]);
exit(1);
}
ao_initialize();
default_driver = ao_default_driver_id();
fp = fopen(argv[1], "rb");
if (fp == NULL) {
printf("Cannot open %s.\n", argv[1]);
exit(2);
}
sf_info.format = 0;
sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 1);
memset(&format, 0, sizeof(format));
if ((sf_info.format & SF_ENDIAN_LITTLE) == SF_ENDIAN_LITTLE)
format.byte_format = AO_FMT_LITTLE;
if ((sf_info.format & SF_ENDIAN_BIG) == SF_ENDIAN_BIG)
format.byte_format = AO_FMT_BIG;
if ((sf_info.format & SF_FORMAT_PCM_S8) == SF_FORMAT_PCM_S8)
format.bits = 8;
if ((sf_info.format & SF_FORMAT_PCM_16) == SF_FORMAT_PCM_16)
format.bits = 16;
if ((sf_info.format & SF_FORMAT_PCM_24) == SF_FORMAT_PCM_24)
format.bits = 24;
if ((sf_info.format & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32)
format.bits = 32;
if ((sf_info.format & SF_FORMAT_PCM_U8) == SF_FORMAT_PCM_U8)
format.bits = 8;
// format.bits = 32;
printf("%x\n", sf_info.format);
printf("bits: %d\n", format.bits);
format.channels = sf_info.channels;
format.rate = sf_info.samplerate;
device = ao_open_live(default_driver, &format, NULL /* no options */);
if (device == NULL) {
printf("Error opening sound device.\n");
exit(1);
}
buffer = malloc(sizeof(int) * sf_info.frames * sf_info.channels);
frames_read = sf_read_int(sndfile, buffer, sf_info.frames * sizeof(int));
ao_play(device, (char *)buffer, sf_info.frames * sizeof(int));
ao_close(device);
ao_shutdown();
sf_close(sndfile);
return 0;
}