OSDN Git Service

axfer: add an explanation about IRQ-based scheduling model
[android-x86/external-alsa-utils.git] / axfer / container-au.c
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
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
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
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
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
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 };