diff options
author | Simon Wilson <simonwilson@google.com> | 2012-05-01 23:26:25 -0700 |
---|---|---|
committer | Simon Wilson <simonwilson@google.com> | 2012-05-04 16:06:38 -0700 |
commit | 9eba533b82d1bb6aa8924f4da132151f82e6475c (patch) | |
tree | 128fc0f88e8aa6a671a77aba54043382c1b22d24 | |
parent | 8fe2c93b75112bce4de1a08f61eca9c509d770c1 (diff) |
tinyplay: add multichannel support
Improve the reading of the RIFF WAVE header so that unrecognised
chunks are skipped, which means that extra chunks are not played
as if they were audio data. This ensures that all channels for
multichannel playback line up correctly.
Change-Id: Ifdb3cb73b3c0bf41a1e271068d263cd01116616c
-rw-r--r-- | tinyplay.c | 73 |
1 files changed, 48 insertions, 25 deletions
@@ -36,22 +36,24 @@ #define ID_FMT 0x20746d66 #define ID_DATA 0x61746164 -#define FORMAT_PCM 1 - -struct wav_header { +struct riff_wave_header { uint32_t riff_id; uint32_t riff_sz; - uint32_t riff_fmt; - uint32_t fmt_id; - uint32_t fmt_sz; + uint32_t wave_id; +}; + +struct chunk_header { + uint32_t id; + uint32_t sz; +}; + +struct chunk_fmt { uint16_t audio_format; uint16_t num_channels; uint32_t sample_rate; uint32_t byte_rate; uint16_t block_align; uint16_t bits_per_sample; - uint32_t data_id; - uint32_t data_sz; }; void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, @@ -61,11 +63,15 @@ void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned in int main(int argc, char **argv) { FILE *file; - struct wav_header header; + struct riff_wave_header riff_wave_header; + struct chunk_header chunk_header; + struct chunk_fmt chunk_fmt; unsigned int device = 0; unsigned int card = 0; unsigned int period_size = 1024; unsigned int period_count = 4; + char *filename; + int more_chunks = 1; if (argc < 2) { fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]" @@ -73,12 +79,41 @@ int main(int argc, char **argv) return 1; } - file = fopen(argv[1], "rb"); + filename = argv[1]; + file = fopen(filename, "rb"); if (!file) { - fprintf(stderr, "Unable to open file '%s'\n", argv[1]); + fprintf(stderr, "Unable to open file '%s'\n", filename); + return 1; + } + + fread(&riff_wave_header, sizeof(riff_wave_header), 1, file); + if ((riff_wave_header.riff_id != ID_RIFF) || + (riff_wave_header.wave_id != ID_WAVE)) { + fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename); + fclose(file); return 1; } + do { + fread(&chunk_header, sizeof(chunk_header), 1, file); + + switch (chunk_header.id) { + case ID_FMT: + fread(&chunk_fmt, sizeof(chunk_fmt), 1, file); + /* If the format header is larger, skip the rest */ + if (chunk_header.sz > sizeof(chunk_fmt)) + fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR); + break; + case ID_DATA: + /* Stop looking for chunks */ + more_chunks = 0; + break; + default: + /* Unknown chunk, skip bytes */ + fseek(file, chunk_header.sz, SEEK_CUR); + } + } while (more_chunks); + /* parse command line arguments */ argv += 2; while (*argv) { @@ -106,20 +141,8 @@ int main(int argc, char **argv) argv++; } - fread(&header, sizeof(struct wav_header), 1, file); - - if ((header.riff_id != ID_RIFF) || - (header.riff_fmt != ID_WAVE) || - (header.fmt_id != ID_FMT) || - (header.audio_format != FORMAT_PCM) || - (header.fmt_sz != 16)) { - fprintf(stderr, "Error: '%s' is not a PCM riff/wave file\n", argv[1]); - fclose(file); - return 1; - } - - play_sample(file, card, device, header.num_channels, header.sample_rate, - header.bits_per_sample, period_size, period_count); + play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate, + chunk_fmt.bits_per_sample, period_size, period_count); fclose(file); |