/dev/audio is vaguely
standard, except that
it requires fills of DMA buffer size (4608 bytes) and is locked at
44.1khz stereo. The buffer fill bit is a bug, but it's likely to stay locked
at that rate - we can't actually change the rate (the player does
interpolation & flash stuff for lower bitrates).
The mixer calls are pretty standard I believe, though there are lots of other bits for controlling the DSP - many of which we simply can't document as we're under NDA with the docs on the DSP.
Contributed by Hugo FiennesHere is a program which reads pairs of signed 16-bit little-endian PCM
samples from standard input and plays them at 44.1kHz stereo using
/dev/audio. It takes care always to write 4608 bytes at a
time.
Some of the ioctl()s probably aren't strictly necessary on the
empeg but they illustrate the API.
pcmplay.c
# include <stdio.h>
# include <string.h>
# include <errno.h>
# include <fcntl.h>
# include <unistd.h>
# include <sys/ioctl.h>
# include <sys/soundcard.h>
# define AUDIO_DEVICE "/dev/audio"
# define AUDIO_FILLSZ 4608
static
int output(int fd, void const *buf, unsigned int len)
{
char const *ptr = buf;
int wrote;
while (len) {
wrote = write(fd, ptr, len);
if (wrote == -1) {
if (errno == EINTR)
continue;
else
return -1;
}
ptr += wrote;
len -= wrote;
}
return 0;
}
static
int audio_buffer(int fd, void const *buf, unsigned int len)
{
char const *ptr = buf;
static char hold[AUDIO_FILLSZ];
static unsigned int held;
unsigned int left, grab;
if (len == 0) {
if (held) {
memset(&hold[held], 0, &hold[AUDIO_FILLSZ] - &hold[held]);
held = 0;
return output(fd, hold, AUDIO_FILLSZ);
}
return 0;
}
if (held == 0 && len == AUDIO_FILLSZ)
return output(fd, ptr, len);
left = AUDIO_FILLSZ - held;
while (len) {
grab = len < left ? len : left;
memcpy(&hold[held], ptr, grab);
held += grab;
left -= grab;
ptr += grab;
len -= grab;
if (left == 0) {
if (output(fd, hold, AUDIO_FILLSZ) == -1)
return -1;
held = 0;
left = AUDIO_FILLSZ;
}
}
return 0;
}
# define audio_flush(fd) audio_buffer((fd), 0, 0)
static
int audio_init(int fd)
{
int format, stereo, speed;
format = AFMT_S16_LE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1) {
perror("ioctl(SNDCTL_DSP_SETFMT)");
return -1;
}
if (format != AFMT_S16_LE) {
fprintf(stderr, "AFMT_S16_LE not available\n");
return -1;
}
stereo = 1;
if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
perror("ioctl(SNDCTL_DSP_STEREO)");
return -1;
}
if (!stereo) {
fprintf(stderr, "stereo selection failed\n");
return -1;
}
speed = 44100;
if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {
perror("ioctl(SNDCTL_DSP_SPEED)");
return -1;
}
if (speed != 44100) {
fprintf(stderr, "sample speed 44100 not available (closest %u)\n", speed);
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
static char buffer[AUDIO_FILLSZ];
int fd;
unsigned int len;
fd = open(AUDIO_DEVICE, O_WRONLY);
if (fd == -1) {
perror(AUDIO_DEVICE);
return 1;
}
if (audio_init(fd) == -1) {
close(fd);
return 2;
}
while ((len = fread(buffer, 4, AUDIO_FILLSZ / 4, stdin))) {
if (audio_buffer(fd, buffer, 4 * len) == -1) {
perror("write");
return 3;
}
}
if (ferror(stdin)) {
perror("read");
return 4;
}
if (audio_flush(fd) == -1) {
perror("write");
return 3;
}
if (close(fd) == -1) {
perror("close");
return 5;
}
return 0;
}
You will need a source of PCM data to try this program. If you have a 44.1kHz stereo WAV file, you can usually generate an appropriate PCM file by stripping away the 44-byte RIFF header:
dd if=Contributed by Rob Leslie$file.wav of=$file.pcm ibs=44 skip=1