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 21enum 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 33struct format_map { 34 enum code_id code_id; 35 snd_pcm_format_t format; 36}; 37 38static 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 51struct 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 60struct container_annotation { 61 uint32_t chunks[0]; 62}; 63 64struct parser_state { 65 enum code_id code_id; 66 unsigned int samples_per_frame; 67 unsigned int bytes_per_sample; 68}; 69 70static 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 unsigned 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 117struct 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 124static 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 137static 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 149static 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 unsigned 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 173static 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 185const 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 195const 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