summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit7341494707810f709855ea85ce03a8ec3ac8dbaf (patch)
tree3bfd77f6c3acddfd222b1b0f4cf1a3452bcc1863 /sound
downloadextras-7341494707810f709855ea85ce03a8ec3ac8dbaf.tar.gz
Diffstat (limited to 'sound')
-rw-r--r--sound/Android.mk8
-rw-r--r--sound/playwav.c376
2 files changed, 384 insertions, 0 deletions
diff --git a/sound/Android.mk b/sound/Android.mk
new file mode 100644
index 00000000..7a58998b
--- /dev/null
+++ b/sound/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := sound
+LOCAL_SRC_FILES := playwav.c
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_EXECUTABLE)
+
diff --git a/sound/playwav.c b/sound/playwav.c
new file mode 100644
index 00000000..6d6bce75
--- /dev/null
+++ b/sound/playwav.c
@@ -0,0 +1,376 @@
+/* Copyright (C) 2008 The Android Open Source Project
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include <linux/ioctl.h>
+
+#define AUDIO_IOCTL_MAGIC 'a'
+
+#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned)
+#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned)
+#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned)
+#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned)
+#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned)
+#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned)
+
+struct msm_audio_config {
+ uint32_t buffer_size;
+ uint32_t buffer_count;
+ uint32_t channel_count;
+ uint32_t sample_rate;
+ uint32_t codec_type;
+ uint32_t unused[3];
+};
+
+struct msm_audio_stats {
+ uint32_t out_bytes;
+ uint32_t unused[3];
+};
+
+int pcm_play(unsigned rate, unsigned channels,
+ int (*fill)(void *buf, unsigned sz, void *cookie),
+ void *cookie)
+{
+ struct msm_audio_config config;
+ struct msm_audio_stats stats;
+ unsigned sz, n;
+ char buf[8192];
+ int afd;
+
+ afd = open("/dev/msm_pcm_out", O_RDWR);
+ if (afd < 0) {
+ perror("pcm_play: cannot open audio device");
+ return -1;
+ }
+
+ if(ioctl(afd, AUDIO_GET_CONFIG, &config)) {
+ perror("could not get config");
+ return -1;
+ }
+
+ config.channel_count = channels;
+ config.sample_rate = rate;
+ if (ioctl(afd, AUDIO_SET_CONFIG, &config)) {
+ perror("could not set config");
+ return -1;
+ }
+ sz = config.buffer_size;
+ if (sz > sizeof(buf)) {
+ fprintf(stderr,"too big\n");
+ return -1;
+ }
+
+ fprintf(stderr,"prefill\n");
+ for (n = 0; n < config.buffer_count; n++) {
+ if (fill(buf, sz, cookie))
+ break;
+ if (write(afd, buf, sz) != sz)
+ break;
+ }
+
+ fprintf(stderr,"start\n");
+ ioctl(afd, AUDIO_START, 0);
+
+ for (;;) {
+#if 0
+ if (ioctl(afd, AUDIO_GET_STATS, &stats) == 0)
+ fprintf(stderr,"%10d\n", stats.out_bytes);
+#endif
+ if (fill(buf, sz, cookie))
+ break;
+ if (write(afd, buf, sz) != sz)
+ break;
+ }
+
+done:
+ close(afd);
+ return 0;
+}
+
+/* http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */
+ uint16_t block_align; /* num_channels * bps / 8 */
+ uint16_t bits_per_sample;
+ uint32_t data_id;
+ uint32_t data_sz;
+};
+
+
+static char *next;
+static unsigned avail;
+
+int fill_buffer(void *buf, unsigned sz, void *cookie)
+{
+ if (sz > avail)
+ return -1;
+ memcpy(buf, next, sz);
+ next += sz;
+ avail -= sz;
+ return 0;
+}
+
+void play_file(unsigned rate, unsigned channels,
+ int fd, unsigned count)
+{
+ next = malloc(count);
+ if (!next) {
+ fprintf(stderr,"could not allocate %d bytes\n", count);
+ return;
+ }
+ if (read(fd, next, count) != count) {
+ fprintf(stderr,"could not read %d bytes\n", count);
+ return;
+ }
+ avail = count;
+ pcm_play(rate, channels, fill_buffer, 0);
+}
+
+int wav_play(const char *fn)
+{
+ struct wav_header hdr;
+ unsigned rate, channels;
+ int fd;
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "playwav: cannot open '%s'\n", fn);
+ return -1;
+ }
+ if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ fprintf(stderr, "playwav: cannot read header\n");
+ return -1;
+ }
+ fprintf(stderr,"playwav: %d ch, %d hz, %d bit, %s\n",
+ hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample,
+ hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown");
+
+ if ((hdr.riff_id != ID_RIFF) ||
+ (hdr.riff_fmt != ID_WAVE) ||
+ (hdr.fmt_id != ID_FMT)) {
+ fprintf(stderr, "playwav: '%s' is not a riff/wave file\n", fn);
+ return -1;
+ }
+ if ((hdr.audio_format != FORMAT_PCM) ||
+ (hdr.fmt_sz != 16)) {
+ fprintf(stderr, "playwav: '%s' is not pcm format\n", fn);
+ return -1;
+ }
+ if (hdr.bits_per_sample != 16) {
+ fprintf(stderr, "playwav: '%s' is not 16bit per sample\n", fn);
+ return -1;
+ }
+
+ play_file(hdr.sample_rate, hdr.num_channels,
+ fd, hdr.data_sz);
+
+ return 0;
+}
+
+int wav_rec(const char *fn, unsigned channels, unsigned rate)
+{
+ struct wav_header hdr;
+ unsigned char buf[8192];
+ struct msm_audio_config cfg;
+ unsigned sz, n;
+ int fd, afd;
+ unsigned total = 0;
+ unsigned char tmp;
+
+ hdr.riff_id = ID_RIFF;
+ hdr.riff_sz = 0;
+ hdr.riff_fmt = ID_WAVE;
+ hdr.fmt_id = ID_FMT;
+ hdr.fmt_sz = 16;
+ hdr.audio_format = FORMAT_PCM;
+ hdr.num_channels = channels;
+ hdr.sample_rate = rate;
+ hdr.byte_rate = hdr.sample_rate * hdr.num_channels * 2;
+ hdr.block_align = hdr.num_channels * 2;
+ hdr.bits_per_sample = 16;
+ hdr.data_id = ID_DATA;
+ hdr.data_sz = 0;
+
+ fd = open(fn, O_CREAT | O_RDWR, 0666);
+ if (fd < 0) {
+ perror("cannot open output file");
+ return -1;
+ }
+ write(fd, &hdr, sizeof(hdr));
+
+ afd = open("/dev/msm_pcm_in", O_RDWR);
+ if (afd < 0) {
+ perror("cannot open msm_pcm_in");
+ close(fd);
+ return -1;
+ }
+
+ /* config change should be a read-modify-write operation */
+ if (ioctl(afd, AUDIO_GET_CONFIG, &cfg)) {
+ perror("cannot read audio config");
+ goto fail;
+ }
+
+ cfg.channel_count = hdr.num_channels;
+ cfg.sample_rate = hdr.sample_rate;
+ if (ioctl(afd, AUDIO_SET_CONFIG, &cfg)) {
+ perror("cannot write audio config");
+ goto fail;
+ }
+
+ if (ioctl(afd, AUDIO_GET_CONFIG, &cfg)) {
+ perror("cannot read audio config");
+ goto fail;
+ }
+
+ sz = cfg.buffer_size;
+ fprintf(stderr,"buffer size %d x %d\n", sz, cfg.buffer_count);
+ if (sz > sizeof(buf)) {
+ fprintf(stderr,"buffer size %d too large\n", sz);
+ goto fail;
+ }
+
+ if (ioctl(afd, AUDIO_START, 0)) {
+ perror("cannot start audio");
+ goto fail;
+ }
+
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ fprintf(stderr,"\n*** RECORDING * HIT ENTER TO STOP ***\n");
+
+ for (;;) {
+ while (read(0, &tmp, 1) == 1) {
+ if ((tmp == 13) || (tmp == 10)) goto done;
+ }
+ if (read(afd, buf, sz) != sz) {
+ perror("cannot read buffer");
+ goto fail;
+ }
+ if (write(fd, buf, sz) != sz) {
+ perror("cannot write buffer");
+ goto fail;
+ }
+ total += sz;
+
+ }
+done:
+ close(afd);
+
+ /* update lengths in header */
+ hdr.data_sz = total;
+ hdr.riff_sz = total + 8 + 16 + 8;
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &hdr, sizeof(hdr));
+ close(fd);
+ return 0;
+
+fail:
+ close(afd);
+ close(fd);
+ unlink(fn);
+ return -1;
+}
+
+int mp3_play(const char *fn)
+{
+ char buf[64*1024];
+ int r;
+ int fd, afd;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ perror("cannot open mp3 file");
+ return -1;
+ }
+
+ afd = open("/dev/msm_mp3", O_RDWR);
+ if (afd < 0) {
+ close(fd);
+ perror("cannot open mp3 output device");
+ return -1;
+ }
+
+ fprintf(stderr,"MP3 PLAY\n");
+ ioctl(afd, AUDIO_START, 0);
+
+ for (;;) {
+ r = read(fd, buf, 64*1024);
+ if (r <= 0) break;
+ r = write(afd, buf, r);
+ if (r < 0) break;
+ }
+
+ close(fd);
+ close(afd);
+}
+
+int main(int argc, char **argv)
+{
+ const char *fn = 0;
+ int play = 1;
+ unsigned channels = 1;
+ unsigned rate = 44100;
+
+ argc--;
+ argv++;
+ while (argc > 0) {
+ if (!strcmp(argv[0],"-rec")) {
+ play = 0;
+ } else if (!strcmp(argv[0],"-play")) {
+ play = 1;
+ } else if (!strcmp(argv[0],"-stereo")) {
+ channels = 2;
+ } else if (!strcmp(argv[0],"-mono")) {
+ channels = 1;
+ } else if (!strcmp(argv[0],"-rate")) {
+ argc--;
+ argv++;
+ if (argc == 0) {
+ fprintf(stderr,"playwav: -rate requires a parameter\n");
+ return -1;
+ }
+ rate = atoi(argv[0]);
+ } else {
+ fn = argv[0];
+ }
+ argc--;
+ argv++;
+ }
+
+ if (fn == 0) {
+ fn = play ? "/data/out.wav" : "/data/rec.wav";
+ }
+
+ if (play) {
+ const char *dot = strrchr(fn, '.');
+ if (dot && !strcmp(dot,".mp3")) {
+ return mp3_play(fn);
+ } else {
+ return wav_play(fn);
+ }
+ } else {
+ return wav_rec(fn, channels, rate);
+ }
+ return 0;
+}