diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:58 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:58 -0800 |
commit | e16cb84e2324f05334d18dcf5956f20f44262b62 (patch) | |
tree | c87defa74181089778bc8e5671e2896d95d4e2b0 /sound | |
parent | 19ddb4b1680760e2d6863c3003976882ebd9d0fa (diff) | |
download | extras-e16cb84e2324f05334d18dcf5956f20f44262b62.tar.gz |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'sound')
-rw-r--r-- | sound/Android.mk | 8 | ||||
-rw-r--r-- | sound/playwav.c | 377 |
2 files changed, 385 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..bb37d4a9 --- /dev/null +++ b/sound/playwav.c @@ -0,0 +1,377 @@ +/* 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); + return 0; +} + +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; +} |