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>
33da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson#include <string.h>
34da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson#include <signal.h>
35edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
36edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_RIFF 0x46464952
37edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_WAVE 0x45564157
38edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_FMT  0x20746d66
39edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson#define ID_DATA 0x61746164
40edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
4185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilsonstruct riff_wave_header {
42edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t riff_id;
43edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t riff_sz;
4485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    uint32_t wave_id;
4585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson};
4685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
4785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilsonstruct chunk_header {
4885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    uint32_t id;
4985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    uint32_t sz;
5085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson};
5185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
5285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilsonstruct chunk_fmt {
53edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t audio_format;
54edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t num_channels;
55edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t sample_rate;
56edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint32_t byte_rate;
57edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t block_align;
58edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    uint16_t bits_per_sample;
59edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson};
60edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
61da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilsonstatic int close = 0;
62da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson
63daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonvoid play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
64daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int rate, unsigned int bits, unsigned int period_size,
65daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int period_count);
66edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
67da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilsonvoid stream_close(int sig)
68da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson{
69da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson    /* allow the stream to be closed gracefully */
70da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson    signal(sig, SIG_IGN);
71da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson    close = 1;
72da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson}
73da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson
74edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilsonint main(int argc, char **argv)
75edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{
76edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    FILE *file;
7785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    struct riff_wave_header riff_wave_header;
7885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    struct chunk_header chunk_header;
7985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    struct chunk_fmt chunk_fmt;
80621047309242d04a6186230e7a337de8642ec754Simon Wilson    unsigned int device = 0;
81daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    unsigned int card = 0;
82daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    unsigned int period_size = 1024;
83daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    unsigned int period_count = 4;
8485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    char *filename;
8585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    int more_chunks = 1;
86edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
87621047309242d04a6186230e7a337de8642ec754Simon Wilson    if (argc < 2) {
88daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]"
89daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                " [-n n_periods] \n", argv[0]);
90edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return 1;
91edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
92edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
9385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    filename = argv[1];
9485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    file = fopen(filename, "rb");
95edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (!file) {
9685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fprintf(stderr, "Unable to open file '%s'\n", filename);
9785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        return 1;
9885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    }
9985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
10085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
10185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    if ((riff_wave_header.riff_id != ID_RIFF) ||
10285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        (riff_wave_header.wave_id != ID_WAVE)) {
10385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
10485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fclose(file);
105edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return 1;
106edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
107edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
10885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    do {
10985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        fread(&chunk_header, sizeof(chunk_header), 1, file);
11085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
11185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        switch (chunk_header.id) {
11285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        case ID_FMT:
11385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
11485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            /* If the format header is larger, skip the rest */
11585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            if (chunk_header.sz > sizeof(chunk_fmt))
11685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson                fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
11785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            break;
11885dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        case ID_DATA:
11985dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            /* Stop looking for chunks */
12085dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            more_chunks = 0;
12185dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            break;
12285dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        default:
12385dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            /* Unknown chunk, skip bytes */
12485dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson            fseek(file, chunk_header.sz, SEEK_CUR);
12585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson        }
12685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    } while (more_chunks);
12785dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson
128621047309242d04a6186230e7a337de8642ec754Simon Wilson    /* parse command line arguments */
129621047309242d04a6186230e7a337de8642ec754Simon Wilson    argv += 2;
130e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson    while (*argv) {
131e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson        if (strcmp(*argv, "-d") == 0) {
132e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson            argv++;
133daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
134daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                device = atoi(*argv);
135e9942c8b1fab1cea4836b5af2dd59a1bf0ad411dSimon Wilson        }
136daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (strcmp(*argv, "-p") == 0) {
137daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
138daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
139daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                period_size = atoi(*argv);
140daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        }
141daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (strcmp(*argv, "-n") == 0) {
142daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
143daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
144daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                period_count = atoi(*argv);
145daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        }
146daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (strcmp(*argv, "-D") == 0) {
147daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
148daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            if (*argv)
149daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                card = atoi(*argv);
150daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        }
151daa83291944318d8face12c780dfb69ae96b0723Simon Wilson        if (*argv)
152daa83291944318d8face12c780dfb69ae96b0723Simon Wilson            argv++;
153621047309242d04a6186230e7a337de8642ec754Simon Wilson    }
154621047309242d04a6186230e7a337de8642ec754Simon Wilson
15585dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson    play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
15685dc38f5bee79c184260e8c665cc34143be12bd4Simon Wilson                chunk_fmt.bits_per_sample, period_size, period_count);
157edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
158edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    fclose(file);
159edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
160edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    return 0;
161edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}
162edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
1639673f5717d824137d64320d0bc98a6461a9383a8Simon Wilsonint check_param(struct pcm_params *params, unsigned int param, unsigned int value,
1649673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson                 char *param_name, char *param_unit)
1659673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson{
1669673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    unsigned int min;
1679673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    unsigned int max;
1689673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    int is_within_bounds = 1;
1699673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
1709673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    min = pcm_params_get_min(params, param);
1719673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    if (value < min) {
1729673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
1739673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson                param_unit, min, param_unit);
1749673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        is_within_bounds = 0;
1759673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    }
1769673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
1779673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    max = pcm_params_get_max(params, param);
1789673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    if (value > max) {
1799673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
1809673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson                param_unit, max, param_unit);
1819673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        is_within_bounds = 0;
1829673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    }
1839673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
1849673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    return is_within_bounds;
1859673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson}
1869673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
1879673f5717d824137d64320d0bc98a6461a9383a8Simon Wilsonint sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
1889673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson                        unsigned int rate, unsigned int bits, unsigned int period_size,
1899673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson                        unsigned int period_count)
1909673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson{
1919673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    struct pcm_params *params;
1929673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    int can_play;
1939673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
1949673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    params = pcm_params_get(card, device, PCM_OUT);
1959673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    if (params == NULL) {
1969673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        fprintf(stderr, "Unable to open PCM device %u.\n", device);
1979673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        return 0;
1989673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    }
1999673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
2009673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
2019673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
2029673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
2039673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
2049673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");
2059673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
2069673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    pcm_params_free(params);
2079673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
2089673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    return can_play;
2099673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson}
2109673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
211daa83291944318d8face12c780dfb69ae96b0723Simon Wilsonvoid play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
212daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int rate, unsigned int bits, unsigned int period_size,
213daa83291944318d8face12c780dfb69ae96b0723Simon Wilson                 unsigned int period_count)
214edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson{
215edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    struct pcm_config config;
216edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    struct pcm *pcm;
217edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    char *buffer;
218edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    int size;
219edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    int num_read;
220edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
221edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    config.channels = channels;
222edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    config.rate = rate;
223daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    config.period_size = period_size;
224daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    config.period_count = period_count;
225edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (bits == 32)
226edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        config.format = PCM_FORMAT_S32_LE;
227edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    else if (bits == 16)
228edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        config.format = PCM_FORMAT_S16_LE;
229621047309242d04a6186230e7a337de8642ec754Simon Wilson    config.start_threshold = 0;
230621047309242d04a6186230e7a337de8642ec754Simon Wilson    config.stop_threshold = 0;
231621047309242d04a6186230e7a337de8642ec754Simon Wilson    config.silence_threshold = 0;
232edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
2339673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
2349673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson        return;
2359673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson    }
2369673f5717d824137d64320d0bc98a6461a9383a8Simon Wilson
237daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    pcm = pcm_open(card, device, PCM_OUT, &config);
238edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (!pcm || !pcm_is_ready(pcm)) {
239621047309242d04a6186230e7a337de8642ec754Simon Wilson        fprintf(stderr, "Unable to open PCM device %u (%s)\n",
240621047309242d04a6186230e7a337de8642ec754Simon Wilson                device, pcm_get_error(pcm));
241edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return;
242edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
243edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
244daa83291944318d8face12c780dfb69ae96b0723Simon Wilson    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
245edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    buffer = malloc(size);
246edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    if (!buffer) {
247edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        fprintf(stderr, "Unable to allocate %d bytes\n", size);
248edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        free(buffer);
249edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        pcm_close(pcm);
250edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        return;
251edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    }
252edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
253edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
254edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
255da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson    /* catch ctrl-c to shutdown cleanly */
256da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson    signal(SIGINT, stream_close);
257da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson
258edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    do {
259edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        num_read = fread(buffer, 1, size, file);
260edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        if (num_read > 0) {
261edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson            if (pcm_write(pcm, buffer, num_read)) {
262edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                fprintf(stderr, "Error playing sample\n");
263edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson                break;
264edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson            }
265edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson        }
266da39e0b09eb0a1b559a96e2108160d1d8dccf314Simon Wilson    } while (!close && num_read > 0);
267edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
268edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    free(buffer);
269edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson    pcm_close(pcm);
270edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson}
271edff708e3129d7d8e05bbdfb624af97a3264a332Simon Wilson
272