1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "wav-source.h"
6
7 #include <fcntl.h>
8 #include <stdio.h>
9
10 #include <zircon/assert.h>
11 #include <fbl/auto_call.h>
12 #include <fbl/algorithm.h>
13 #include <lib/fdio/io.h>
14
Initialize(const char * filename)15 zx_status_t WAVSource::Initialize(const char* filename) {
16 zx_status_t res = WAVCommon::Initialize(filename, InitMode::SOURCE);
17 if (res != ZX_OK) return res;
18
19 RIFFChunkHeader riff_hdr;
20 WAVHeader wav_info;
21
22 auto cleanup = fbl::MakeAutoCall([&]() {
23 Close();
24 payload_len_ = 0;
25 });
26
27 // Read and sanity check the top level RIFF header
28 res = Read(&riff_hdr, sizeof(riff_hdr));
29 if (res != ZX_OK) {
30 printf("Failed to read top level RIFF header!\n");
31 return res;
32 }
33 riff_hdr.FixupEndian();
34
35 if (riff_hdr.four_cc != RIFF_FOUR_CC) {
36 printf("Missing expected 'RIFF' 4CC (expected 0x%08x got 0x%08x)\n",
37 RIFF_FOUR_CC, riff_hdr.four_cc);
38 return ZX_ERR_INVALID_ARGS;
39 }
40
41 // Read the WAVE header along with its required format chunk.
42 res = Read(&wav_info, sizeof(wav_info));
43 if (res != ZX_OK) {
44 printf("Failed to read top level WAVE header!\n");
45 return res;
46 }
47 wav_info.FixupEndian();
48
49 if (wav_info.wave_four_cc != WAVE_FOUR_CC) {
50 printf("Missing expected 'RIFF' 4CC (expected 0x%08x got 0x%08x)\n",
51 WAVE_FOUR_CC, wav_info.wave_four_cc);
52 return ZX_ERR_INVALID_ARGS;
53 }
54
55 if (wav_info.fmt_four_cc != FMT_FOUR_CC) {
56 printf("Missing expected 'RIFF' 4CC (expected 0x%08x got 0x%08x)\n",
57 FMT_FOUR_CC, wav_info.fmt_four_cc);
58 return ZX_ERR_INVALID_ARGS;
59 }
60
61 if (!wav_info.frame_size) {
62 printf("Bad frame size (%hu)\n", wav_info.frame_size);
63 return ZX_ERR_INVALID_ARGS;
64 }
65
66 // Sanity check the format of the wave file. This test app only supports a
67 // limited subset of the possible formats.
68 if (wav_info.format != FORMAT_LPCM) {
69 printf("Unsupported format (0x%08hx) must be LPCM (0x%08hx)\n",
70 wav_info.format, FORMAT_LPCM);
71 return ZX_ERR_INVALID_ARGS;
72 }
73
74 switch (wav_info.bits_per_sample) {
75 case 8: audio_format_.sample_format = AUDIO_SAMPLE_FORMAT_8BIT; break;
76 case 16: audio_format_.sample_format = AUDIO_SAMPLE_FORMAT_16BIT; break;
77 default:
78 printf("Unsupported bits per sample (%hu)\n", wav_info.bits_per_sample);
79 return ZX_ERR_INVALID_ARGS;
80 };
81
82 audio_format_.frame_rate = wav_info.frame_rate;
83 audio_format_.channels = wav_info.channel_count;
84
85 // Skip any extra data in the format chunk
86 size_t total_wav_hdr_size = wav_info.fmt_chunk_len + offsetof(WAVHeader, format);
87 if (total_wav_hdr_size < sizeof(WAVHeader)) {
88 printf("Bad format chunk length in WAV header (%u)\n", wav_info.fmt_chunk_len);
89 return ZX_ERR_INVALID_ARGS;
90 }
91
92 if (total_wav_hdr_size > sizeof(WAVHeader)) {
93 off_t delta = total_wav_hdr_size - sizeof(WAVHeader);
94 if (::lseek(fd_, delta, SEEK_CUR) < 0) {
95 printf("Error while attempt to skip %zu bytes of extra WAV header\n",
96 static_cast<size_t>(delta));
97 return ZX_ERR_INVALID_ARGS;
98 }
99 }
100
101 // Read and skip chunks until we find the data chunk.
102 RIFFChunkHeader data_hdr;
103 while (true) {
104 res = Read(&data_hdr, sizeof(data_hdr));
105 if (res != ZX_OK) {
106 printf("Failed to find DATA chunk header\n");
107 return res;
108 }
109 data_hdr.FixupEndian();
110
111 if (data_hdr.four_cc == DATA_FOUR_CC)
112 break;
113
114 if (::lseek(fd_, data_hdr.length, SEEK_CUR) < 0) {
115 printf("Error while attempt to skip %u bytes of 0x%08x chunk\n",
116 data_hdr.length, data_hdr.four_cc);
117 return ZX_ERR_INVALID_ARGS;
118 }
119 }
120
121 // If the length of the data chunk is not a multiple of the frame size, log a
122 // warning and truncate the length.
123 uint16_t leftover;
124 payload_len_ = data_hdr.length;
125 leftover = static_cast<uint16_t>(payload_len_ % wav_info.frame_size);
126 if (leftover) {
127 printf("WARNING: Data chunk length (%u) not a multiple of frame size (%hu)\n",
128 payload_len_, wav_info.frame_size);
129 payload_len_ -= leftover;
130 }
131
132 cleanup.cancel();
133 return ZX_OK;
134 }
135
GetFormat(Format * out_format)136 zx_status_t WAVSource::GetFormat(Format* out_format) {
137 if (fd_ < 0)
138 return ZX_ERR_BAD_STATE;
139
140 *out_format = audio_format_;
141 return ZX_OK;
142 }
143
GetFrames(void * buffer,uint32_t buf_space,uint32_t * out_packed)144 zx_status_t WAVSource::GetFrames(void* buffer, uint32_t buf_space, uint32_t* out_packed) {
145 if ((buffer == nullptr) || (out_packed == nullptr))
146 return ZX_ERR_INVALID_ARGS;
147
148 if ((fd_ < 0) || finished())
149 return ZX_ERR_BAD_STATE;
150
151 ZX_DEBUG_ASSERT(payload_played_ < payload_len_);
152 uint32_t todo = fbl::min(buf_space, payload_len_ - payload_played_);
153 zx_status_t res = Read(buffer, todo);
154 if (res == ZX_OK) {
155 payload_played_ += todo;
156 *out_packed = todo;
157 }
158
159 return res;
160 }
161