aboutsummaryrefslogtreecommitdiff
path: root/tinyplay.c
diff options
context:
space:
mode:
authorSimon Wilson <simonwilson@google.com>2012-05-01 23:26:25 -0700
committerSimon Wilson <simonwilson@google.com>2012-05-04 16:06:38 -0700
commit9eba533b82d1bb6aa8924f4da132151f82e6475c (patch)
tree128fc0f88e8aa6a671a77aba54043382c1b22d24 /tinyplay.c
parent8fe2c93b75112bce4de1a08f61eca9c509d770c1 (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
Diffstat (limited to 'tinyplay.c')
-rw-r--r--tinyplay.c73
1 files changed, 48 insertions, 25 deletions
diff --git a/tinyplay.c b/tinyplay.c
index 4d257e7..3f76cc6 100644
--- a/tinyplay.c
+++ b/tinyplay.c
@@ -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);