/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