1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // container-au.c - a parser/builder for a container of Sun Audio File.
4 //
5 // Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6 //
7 // Licensed under the terms of the GNU General Public License, version 2.
8
9 #include "container.h"
10 #include "misc.h"
11
12 // Not portable to all of UNIX platforms.
13 #include <endian.h>
14
15 // Reference:
16 // * http://pubs.opengroup.org/external/auformat.html
17
18 #define AU_MAGIC ".snd"
19 #define UNKNOWN_SIZE UINT32_MAX
20
21 enum code_id {
22 CODE_ID_CCIT_MU_LAW_BE = 0x01,
23 CODE_ID_GENERIC_MBLA_S8 = 0x02,
24 CODE_ID_GENERIC_MBLA_S16_BE = 0x03,
25 CODE_ID_GENERIC_MBLA_S32_BE = 0x05,
26 CODE_ID_IEEE754_FLOAT_S32_BE = 0x06,
27 CODE_ID_IEEE754_DOUBLE_S64_BE = 0x07,
28 CODE_ID_CCIT_ADPCM_G721_4BIT_BE = 0x17,
29 CODE_ID_CCIT_ADPCM_G723_3BIT_BE = 0x19,
30 CODE_ID_CCIT_A_LAW_BE = 0x1b,
31 };
32
33 struct format_map {
34 enum code_id code_id;
35 snd_pcm_format_t format;
36 };
37
38 static const struct format_map format_maps[] = {
39 {CODE_ID_GENERIC_MBLA_S8, SND_PCM_FORMAT_S8},
40 {CODE_ID_GENERIC_MBLA_S16_BE, SND_PCM_FORMAT_S16_BE},
41 {CODE_ID_GENERIC_MBLA_S32_BE, SND_PCM_FORMAT_S32_BE},
42 {CODE_ID_IEEE754_FLOAT_S32_BE, SND_PCM_FORMAT_FLOAT_BE},
43 {CODE_ID_IEEE754_DOUBLE_S64_BE, SND_PCM_FORMAT_FLOAT64_BE},
44 // CODE_ID_CCIT_ADPCM_G721_4BIT_BE is not supported by ALSA.
45 // CODE_ID_CCIT_ADPCM_G723_3BIT_BE is not supported due to width of
46 // its sample.
47 {CODE_ID_CCIT_A_LAW_BE, SND_PCM_FORMAT_A_LAW},
48 {CODE_ID_CCIT_MU_LAW_BE, SND_PCM_FORMAT_MU_LAW},
49 };
50
51 struct container_header {
52 uint8_t magic[4];
53 uint32_t hdr_size;
54 uint32_t data_size;
55 uint32_t code_id;
56 uint32_t frames_per_second;
57 uint32_t samples_per_frame;
58 };
59
60 struct container_annotation {
61 uint32_t chunks[0];
62 };
63
64 struct parser_state {
65 enum code_id code_id;
66 unsigned int samples_per_frame;
67 unsigned int bytes_per_sample;
68 };
69
au_parser_pre_process(struct container_context * cntr,snd_pcm_format_t * format,unsigned int * samples_per_frame,unsigned int * frames_per_second,uint64_t * byte_count)70 static int au_parser_pre_process(struct container_context *cntr,
71 snd_pcm_format_t *format,
72 unsigned int *samples_per_frame,
73 unsigned int *frames_per_second,
74 uint64_t *byte_count)
75 {
76 struct parser_state *state = cntr->private_data;
77 struct container_header header;
78 enum code_id code_id;
79 int i;
80 int err;
81
82 // Parse header. 4 bytes are enough to detect supported containers.
83 memcpy(&header.magic, cntr->magic, sizeof(cntr->magic));
84 err = container_recursive_read(cntr,
85 (char *)&header + sizeof(cntr->magic),
86 sizeof(header) - sizeof(cntr->magic));
87 if (err < 0)
88 return err;
89 if (cntr->eof)
90 return 0;
91
92 if (memcmp(header.magic, AU_MAGIC, sizeof(header.magic)) != 0)
93 return -EINVAL;
94 if (be32toh(header.hdr_size) != sizeof(struct container_header))
95 return -EINVAL;
96
97 code_id = be32toh(header.code_id);
98 for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
99 if (format_maps[i].code_id == code_id)
100 break;
101 }
102 if (i == ARRAY_SIZE(format_maps))
103 return -EINVAL;
104 *format = format_maps[i].format;
105 *frames_per_second = be32toh(header.frames_per_second);
106 *samples_per_frame = be32toh(header.samples_per_frame);
107
108 state->code_id = code_id;
109 state->samples_per_frame = *samples_per_frame;
110 state->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
111
112 *byte_count = be32toh(header.data_size);
113
114 return 0;
115 }
116
117 struct builder_state {
118 unsigned int bytes_per_sample;
119 unsigned int samples_per_frame;
120 unsigned int frames_per_second;
121 enum code_id code_id;
122 };
123
build_container_header(struct builder_state * state,struct container_header * header,unsigned int frames_per_second,uint64_t byte_count)124 static void build_container_header(struct builder_state *state,
125 struct container_header *header,
126 unsigned int frames_per_second,
127 uint64_t byte_count)
128 {
129 memcpy(header->magic, AU_MAGIC, sizeof(header->magic));
130 header->hdr_size = htobe32(sizeof(struct container_header));
131 header->data_size = htobe32(byte_count);
132 header->code_id = htobe32(state->code_id);
133 header->frames_per_second = htobe32(frames_per_second);
134 header->samples_per_frame = htobe32(state->samples_per_frame);
135 }
136
write_container_header(struct container_context * cntr,uint64_t byte_count)137 static int write_container_header(struct container_context *cntr,
138 uint64_t byte_count)
139 {
140 struct builder_state *state = cntr->private_data;
141 struct container_header header;
142
143 build_container_header(state, &header, state->frames_per_second,
144 byte_count);
145
146 return container_recursive_write(cntr, &header, sizeof(header));
147 }
148
au_builder_pre_process(struct container_context * cntr,snd_pcm_format_t * format,unsigned int * samples_per_frame,unsigned int * frames_per_second,uint64_t * byte_count)149 static int au_builder_pre_process(struct container_context *cntr,
150 snd_pcm_format_t *format,
151 unsigned int *samples_per_frame,
152 unsigned int *frames_per_second,
153 uint64_t *byte_count)
154 {
155 struct builder_state *status = cntr->private_data;
156 int i;
157
158 for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
159 if (format_maps[i].format == *format)
160 break;
161 }
162 if (i == ARRAY_SIZE(format_maps))
163 return -EINVAL;
164
165 status->code_id = format_maps[i].code_id;
166 status->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
167 status->frames_per_second = *frames_per_second;
168 status->samples_per_frame = *samples_per_frame;
169
170 return write_container_header(cntr, *byte_count);
171 }
172
au_builder_post_process(struct container_context * cntr,uint64_t handled_byte_count)173 static int au_builder_post_process(struct container_context *cntr,
174 uint64_t handled_byte_count)
175 {
176 int err;
177
178 err = container_seek_offset(cntr, 0);
179 if (err < 0)
180 return err;
181
182 return write_container_header(cntr, handled_byte_count);
183 }
184
185 const struct container_parser container_parser_au = {
186 .format = CONTAINER_FORMAT_AU,
187 .magic = AU_MAGIC,
188 .max_size = UINT32_MAX,
189 .ops = {
190 .pre_process = au_parser_pre_process,
191 },
192 .private_size = sizeof(struct parser_state),
193 };
194
195 const struct container_builder container_builder_au = {
196 .format = CONTAINER_FORMAT_AU,
197 .max_size = UINT32_MAX,
198 .ops = {
199 .pre_process = au_builder_pre_process,
200 .post_process = au_builder_post_process,
201 },
202 .private_size = sizeof(struct builder_state),
203 };
204