• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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