1/* Copyright (C) 2008 The Android Open Source Project
2 */
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <fcntl.h>
7#include <stdint.h>
8#include <sys/mman.h>
9#include <sys/ioctl.h>
10
11#include <linux/ioctl.h>
12
13#define AUDIO_IOCTL_MAGIC 'a'
14
15#define AUDIO_START        _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned)
16#define AUDIO_STOP         _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned)
17#define AUDIO_FLUSH        _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned)
18#define AUDIO_GET_CONFIG   _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned)
19#define AUDIO_SET_CONFIG   _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned)
20#define AUDIO_GET_STATS    _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned)
21
22struct msm_audio_config {
23    uint32_t buffer_size;
24    uint32_t buffer_count;
25    uint32_t channel_count;
26    uint32_t sample_rate;
27    uint32_t codec_type;
28    uint32_t unused[3];
29};
30
31struct msm_audio_stats {
32    uint32_t out_bytes;
33    uint32_t unused[3];
34};
35
36int pcm_play(unsigned rate, unsigned channels,
37             int (*fill)(void *buf, unsigned sz, void *cookie),
38             void *cookie)
39{
40    struct msm_audio_config config;
41    struct msm_audio_stats stats;
42    unsigned sz, n;
43    char buf[8192];
44    int afd;
45
46    afd = open("/dev/msm_pcm_out", O_RDWR);
47    if (afd < 0) {
48        perror("pcm_play: cannot open audio device");
49        return -1;
50    }
51
52    if(ioctl(afd, AUDIO_GET_CONFIG, &config)) {
53        perror("could not get config");
54        return -1;
55    }
56
57    config.channel_count = channels;
58    config.sample_rate = rate;
59    if (ioctl(afd, AUDIO_SET_CONFIG, &config)) {
60        perror("could not set config");
61        return -1;
62    }
63    sz = config.buffer_size;
64    if (sz > sizeof(buf)) {
65        fprintf(stderr,"too big\n");
66        return -1;
67    }
68
69    fprintf(stderr,"prefill\n");
70    for (n = 0; n < config.buffer_count; n++) {
71        if (fill(buf, sz, cookie))
72            break;
73        if (write(afd, buf, sz) != sz)
74            break;
75    }
76
77    fprintf(stderr,"start\n");
78    ioctl(afd, AUDIO_START, 0);
79
80    for (;;) {
81#if 0
82        if (ioctl(afd, AUDIO_GET_STATS, &stats) == 0)
83            fprintf(stderr,"%10d\n", stats.out_bytes);
84#endif
85        if (fill(buf, sz, cookie))
86            break;
87        if (write(afd, buf, sz) != sz)
88            break;
89    }
90
91done:
92    close(afd);
93    return 0;
94}
95
96/* http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
97
98#define ID_RIFF 0x46464952
99#define ID_WAVE 0x45564157
100#define ID_FMT  0x20746d66
101#define ID_DATA 0x61746164
102
103#define FORMAT_PCM 1
104
105struct wav_header {
106	uint32_t riff_id;
107	uint32_t riff_sz;
108	uint32_t riff_fmt;
109	uint32_t fmt_id;
110	uint32_t fmt_sz;
111	uint16_t audio_format;
112	uint16_t num_channels;
113	uint32_t sample_rate;
114	uint32_t byte_rate;       /* sample_rate * num_channels * bps / 8 */
115	uint16_t block_align;     /* num_channels * bps / 8 */
116	uint16_t bits_per_sample;
117	uint32_t data_id;
118	uint32_t data_sz;
119};
120
121
122static char *next;
123static unsigned avail;
124
125int fill_buffer(void *buf, unsigned sz, void *cookie)
126{
127    if (sz > avail)
128        return -1;
129    memcpy(buf, next, sz);
130    next += sz;
131    avail -= sz;
132    return 0;
133}
134
135void play_file(unsigned rate, unsigned channels,
136               int fd, unsigned count)
137{
138    next = malloc(count);
139    if (!next) {
140        fprintf(stderr,"could not allocate %d bytes\n", count);
141        return;
142    }
143    if (read(fd, next, count) != count) {
144        fprintf(stderr,"could not read %d bytes\n", count);
145        return;
146    }
147    avail = count;
148    pcm_play(rate, channels, fill_buffer, 0);
149}
150
151int wav_play(const char *fn)
152{
153	struct wav_header hdr;
154    unsigned rate, channels;
155	int fd;
156	fd = open(fn, O_RDONLY);
157	if (fd < 0) {
158        fprintf(stderr, "playwav: cannot open '%s'\n", fn);
159		return -1;
160	}
161	if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
162        fprintf(stderr, "playwav: cannot read header\n");
163		return -1;
164	}
165    fprintf(stderr,"playwav: %d ch, %d hz, %d bit, %s\n",
166            hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample,
167            hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown");
168
169    if ((hdr.riff_id != ID_RIFF) ||
170        (hdr.riff_fmt != ID_WAVE) ||
171        (hdr.fmt_id != ID_FMT)) {
172        fprintf(stderr, "playwav: '%s' is not a riff/wave file\n", fn);
173        return -1;
174    }
175    if ((hdr.audio_format != FORMAT_PCM) ||
176        (hdr.fmt_sz != 16)) {
177        fprintf(stderr, "playwav: '%s' is not pcm format\n", fn);
178        return -1;
179    }
180    if (hdr.bits_per_sample != 16) {
181        fprintf(stderr, "playwav: '%s' is not 16bit per sample\n", fn);
182        return -1;
183    }
184
185    play_file(hdr.sample_rate, hdr.num_channels,
186              fd, hdr.data_sz);
187
188    return 0;
189}
190
191int wav_rec(const char *fn, unsigned channels, unsigned rate)
192{
193    struct wav_header hdr;
194    unsigned char buf[8192];
195    struct msm_audio_config cfg;
196    unsigned sz, n;
197    int fd, afd;
198    unsigned total = 0;
199    unsigned char tmp;
200
201    hdr.riff_id = ID_RIFF;
202    hdr.riff_sz = 0;
203    hdr.riff_fmt = ID_WAVE;
204    hdr.fmt_id = ID_FMT;
205    hdr.fmt_sz = 16;
206    hdr.audio_format = FORMAT_PCM;
207    hdr.num_channels = channels;
208    hdr.sample_rate = rate;
209    hdr.byte_rate = hdr.sample_rate * hdr.num_channels * 2;
210    hdr.block_align = hdr.num_channels * 2;
211    hdr.bits_per_sample = 16;
212    hdr.data_id = ID_DATA;
213    hdr.data_sz = 0;
214
215    fd = open(fn, O_CREAT | O_RDWR, 0666);
216    if (fd < 0) {
217        perror("cannot open output file");
218        return -1;
219    }
220    write(fd, &hdr, sizeof(hdr));
221
222    afd = open("/dev/msm_pcm_in", O_RDWR);
223    if (afd < 0) {
224        perror("cannot open msm_pcm_in");
225        close(fd);
226        return -1;
227    }
228
229        /* config change should be a read-modify-write operation */
230    if (ioctl(afd, AUDIO_GET_CONFIG, &cfg)) {
231        perror("cannot read audio config");
232        goto fail;
233    }
234
235    cfg.channel_count = hdr.num_channels;
236    cfg.sample_rate = hdr.sample_rate;
237    if (ioctl(afd, AUDIO_SET_CONFIG, &cfg)) {
238        perror("cannot write audio config");
239        goto fail;
240    }
241
242    if (ioctl(afd, AUDIO_GET_CONFIG, &cfg)) {
243        perror("cannot read audio config");
244        goto fail;
245    }
246
247    sz = cfg.buffer_size;
248    fprintf(stderr,"buffer size %d x %d\n", sz, cfg.buffer_count);
249    if (sz > sizeof(buf)) {
250        fprintf(stderr,"buffer size %d too large\n", sz);
251        goto fail;
252    }
253
254    if (ioctl(afd, AUDIO_START, 0)) {
255        perror("cannot start audio");
256        goto fail;
257    }
258
259    fcntl(0, F_SETFL, O_NONBLOCK);
260    fprintf(stderr,"\n*** RECORDING * HIT ENTER TO STOP ***\n");
261
262    for (;;) {
263        while (read(0, &tmp, 1) == 1) {
264            if ((tmp == 13) || (tmp == 10)) goto done;
265        }
266        if (read(afd, buf, sz) != sz) {
267            perror("cannot read buffer");
268            goto fail;
269        }
270        if (write(fd, buf, sz) != sz) {
271            perror("cannot write buffer");
272            goto fail;
273        }
274        total += sz;
275
276    }
277done:
278    close(afd);
279
280        /* update lengths in header */
281    hdr.data_sz = total;
282    hdr.riff_sz = total + 8 + 16 + 8;
283    lseek(fd, 0, SEEK_SET);
284    write(fd, &hdr, sizeof(hdr));
285    close(fd);
286    return 0;
287
288fail:
289    close(afd);
290    close(fd);
291    unlink(fn);
292    return -1;
293}
294
295int mp3_play(const char *fn)
296{
297    char buf[64*1024];
298    int r;
299    int fd, afd;
300
301    fd = open(fn, O_RDONLY);
302    if (fd < 0) {
303        perror("cannot open mp3 file");
304        return -1;
305    }
306
307    afd = open("/dev/msm_mp3", O_RDWR);
308    if (afd < 0) {
309        close(fd);
310        perror("cannot open mp3 output device");
311        return -1;
312    }
313
314    fprintf(stderr,"MP3 PLAY\n");
315    ioctl(afd, AUDIO_START, 0);
316
317    for (;;) {
318        r = read(fd, buf, 64*1024);
319        if (r <= 0) break;
320        r = write(afd, buf, r);
321        if (r < 0) break;
322    }
323
324    close(fd);
325    close(afd);
326    return 0;
327}
328
329int main(int argc, char **argv)
330{
331    const char *fn = 0;
332    int play = 1;
333    unsigned channels = 1;
334    unsigned rate = 44100;
335
336    argc--;
337    argv++;
338    while (argc > 0) {
339        if (!strcmp(argv[0],"-rec")) {
340            play = 0;
341        } else if (!strcmp(argv[0],"-play")) {
342            play = 1;
343        } else if (!strcmp(argv[0],"-stereo")) {
344            channels = 2;
345        } else if (!strcmp(argv[0],"-mono")) {
346            channels = 1;
347        } else if (!strcmp(argv[0],"-rate")) {
348            argc--;
349            argv++;
350            if (argc == 0) {
351                fprintf(stderr,"playwav: -rate requires a parameter\n");
352                return -1;
353            }
354            rate = atoi(argv[0]);
355        } else {
356            fn = argv[0];
357        }
358        argc--;
359        argv++;
360    }
361
362    if (fn == 0) {
363        fn = play ? "/data/out.wav" : "/data/rec.wav";
364    }
365
366    if (play) {
367        const char *dot = strrchr(fn, '.');
368        if (dot && !strcmp(dot,".mp3")) {
369            return mp3_play(fn);
370        } else {
371            return wav_play(fn);
372        }
373    } else {
374        return wav_rec(fn, channels, rate);
375    }
376	return 0;
377}
378