1edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson/* tinyplay.c
2edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**
3edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** Copyright 2011, The Android Open Source Project
4edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**
5edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** Redistribution and use in source and binary forms, with or without
6edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** modification, are permitted provided that the following conditions are met:
7edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**     * Redistributions of source code must retain the above copyright
8edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**       notice, this list of conditions and the following disclaimer.
9edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**     * Redistributions in binary form must reproduce the above copyright
10edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**       notice, this list of conditions and the following disclaimer in the
11edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**       documentation and/or other materials provided with the distribution.
12edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**     * Neither the name of The Android Open Source Project nor the names of
13edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**       its contributors may be used to endorse or promote products derived
14edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**       from this software without specific prior written permission.
15edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson**
16edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson** DAMAGE.
27edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson*/
28edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
29edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <tinyalsa/asoundlib.h>
30edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <stdio.h>
31edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <stdlib.h>
32edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#include <stdint.h>
33edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
34edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_RIFF 0x46464952
35edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_WAVE 0x45564157
36edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_FMT  0x20746d66
37edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_DATA 0x61746164
38edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
3985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilsonstruct riff_wave_header {
40edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t riff_id;
41edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t riff_sz;
4285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    uint32_t wave_id;
4385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson};
4485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
4585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilsonstruct chunk_header {
4685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    uint32_t id;
4785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    uint32_t sz;
4885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson};
4985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
5085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilsonstruct chunk_fmt {
51edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t audio_format;
52edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t num_channels;
53edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t sample_rate;
54edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t byte_rate;
55edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t block_align;
56edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t bits_per_sample;
57edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson};
58edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
59daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonvoid play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
60daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int rate, unsigned int bits, unsigned int period_size,
61daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int period_count);
62edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
63edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint main(int argc, char **argv)
64edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{
65edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    FILE *file;
6685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    struct riff_wave_header riff_wave_header;
6785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    struct chunk_header chunk_header;
6885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    struct chunk_fmt chunk_fmt;
69621047309242d04a6186230e7a337de8642ec754Simon Wilson    unsigned int device = 0;
70daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    unsigned int card = 0;
71daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    unsigned int period_size = 1024;
72daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    unsigned int period_count = 4;
7385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    char *filename;
7485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    int more_chunks = 1;
75edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
76621047309242d04a6186230e7a337de8642ec754Simon Wilson    if (argc < 2) {
77daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]"
78daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                " [-n n_periods] \n", argv[0]);
79edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return 1;
80edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
81edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
8285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    filename = argv[1];
8385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    file = fopen(filename, "rb");
84edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (!file) {
8585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fprintf(stderr, "Unable to open file '%s'\n", filename);
8685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        return 1;
8785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    }
8885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
8985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
9085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    if ((riff_wave_header.riff_id != ID_RIFF) ||
9185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        (riff_wave_header.wave_id != ID_WAVE)) {
9285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
9385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fclose(file);
94edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return 1;
95edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
96edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
9785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    do {
9885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fread(&chunk_header, sizeof(chunk_header), 1, file);
9985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
10085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        switch (chunk_header.id) {
10185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        case ID_FMT:
10285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
10385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            /* If the format header is larger, skip the rest */
10485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            if (chunk_header.sz > sizeof(chunk_fmt))
10585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson                fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
10685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            break;
10785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        case ID_DATA:
10885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            /* Stop looking for chunks */
10985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            more_chunks = 0;
11085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            break;
11185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        default:
11285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            /* Unknown chunk, skip bytes */
11385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            fseek(file, chunk_header.sz, SEEK_CUR);
11485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        }
11585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    } while (more_chunks);
11685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
117621047309242d04a6186230e7a337de8642ec754Simon Wilson    /* parse command line arguments */
118621047309242d04a6186230e7a337de8642ec754Simon Wilson    argv += 2;
119e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson    while (*argv) {
120e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson        if (strcmp(*argv, "-d") == 0) {
121e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson            argv++;
122daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
123daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                device = atoi(*argv);
124e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson        }
125daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (strcmp(*argv, "-p") == 0) {
126daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
127daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
128daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                period_size = atoi(*argv);
129daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        }
130daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (strcmp(*argv, "-n") == 0) {
131daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
132daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
133daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                period_count = atoi(*argv);
134daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        }
135daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (strcmp(*argv, "-D") == 0) {
136daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
137daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
138daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                card = atoi(*argv);
139daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        }
140daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (*argv)
141daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
142621047309242d04a6186230e7a337de8642ec754Simon Wilson    }
143621047309242d04a6186230e7a337de8642ec754Simon Wilson
14485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
14585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson                chunk_fmt.bits_per_sample, period_size, period_count);
146edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
147edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    fclose(file);
148edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
149edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    return 0;
150edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}
151edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
152daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonvoid play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
153daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int rate, unsigned int bits, unsigned int period_size,
154daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int period_count)
155edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{
156edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    struct pcm_config config;
157edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    struct pcm *pcm;
158edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    char *buffer;
159edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    int size;
160edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    int num_read;
161edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
162edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    config.channels = channels;
163edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    config.rate = rate;
164daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    config.period_size = period_size;
165daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    config.period_count = period_count;
166edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (bits == 32)
167edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        config.format = PCM_FORMAT_S32_LE;
168edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    else if (bits == 16)
169edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        config.format = PCM_FORMAT_S16_LE;
170621047309242d04a6186230e7a337de8642ec754Simon Wilson    config.start_threshold = 0;
171621047309242d04a6186230e7a337de8642ec754Simon Wilson    config.stop_threshold = 0;
172621047309242d04a6186230e7a337de8642ec754Simon Wilson    config.silence_threshold = 0;
173edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
174daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    pcm = pcm_open(card, device, PCM_OUT, &config);
175edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (!pcm || !pcm_is_ready(pcm)) {
176621047309242d04a6186230e7a337de8642ec754Simon Wilson        fprintf(stderr, "Unable to open PCM device %u (%s)\n",
177621047309242d04a6186230e7a337de8642ec754Simon Wilson                device, pcm_get_error(pcm));
178edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return;
179edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
180edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
181daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
182edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    buffer = malloc(size);
183edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (!buffer) {
184edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        fprintf(stderr, "Unable to allocate %d bytes\n", size);
185edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        free(buffer);
186edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        pcm_close(pcm);
187edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return;
188edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
189edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
190edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
191edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
192edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    do {
193edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        num_read = fread(buffer, 1, size, file);
194edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        if (num_read > 0) {
195edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson            if (pcm_write(pcm, buffer, num_read)) {
196edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                fprintf(stderr, "Error playing sample\n");
197edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                break;
198edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson            }
199edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        }
200edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    } while (num_read > 0);
201edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
202edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    free(buffer);
203edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    pcm_close(pcm);
204edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}
205edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
206