Blame axfer/container-au.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
//
Packit Service a9274b
// container-au.c - a parser/builder for a container of Sun Audio File.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "container.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
// Not portable to all of UNIX platforms.
Packit Service a9274b
#include <endian.h>
Packit Service a9274b
Packit Service a9274b
// Reference:
Packit Service a9274b
//  * http://pubs.opengroup.org/external/auformat.html
Packit Service a9274b
Packit Service a9274b
#define AU_MAGIC	".snd"
Packit Service a9274b
#define UNKNOWN_SIZE	UINT32_MAX
Packit Service a9274b
Packit Service a9274b
enum code_id {
Packit Service a9274b
	CODE_ID_CCIT_MU_LAW_BE		= 0x01,
Packit Service a9274b
	CODE_ID_GENERIC_MBLA_S8		= 0x02,
Packit Service a9274b
	CODE_ID_GENERIC_MBLA_S16_BE	= 0x03,
Packit Service a9274b
	CODE_ID_GENERIC_MBLA_S32_BE	= 0x05,
Packit Service a9274b
	CODE_ID_IEEE754_FLOAT_S32_BE	= 0x06,
Packit Service a9274b
	CODE_ID_IEEE754_DOUBLE_S64_BE	= 0x07,
Packit Service a9274b
	CODE_ID_CCIT_ADPCM_G721_4BIT_BE	= 0x17,
Packit Service a9274b
	CODE_ID_CCIT_ADPCM_G723_3BIT_BE	= 0x19,
Packit Service a9274b
	CODE_ID_CCIT_A_LAW_BE		= 0x1b,
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct format_map {
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
	snd_pcm_format_t format;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static const struct format_map format_maps[] = {
Packit Service a9274b
	{CODE_ID_GENERIC_MBLA_S8,		SND_PCM_FORMAT_S8},
Packit Service a9274b
	{CODE_ID_GENERIC_MBLA_S16_BE,		SND_PCM_FORMAT_S16_BE},
Packit Service a9274b
	{CODE_ID_GENERIC_MBLA_S32_BE,		SND_PCM_FORMAT_S32_BE},
Packit Service a9274b
	{CODE_ID_IEEE754_FLOAT_S32_BE,		SND_PCM_FORMAT_FLOAT_BE},
Packit Service a9274b
	{CODE_ID_IEEE754_DOUBLE_S64_BE,		SND_PCM_FORMAT_FLOAT64_BE},
Packit Service a9274b
	// CODE_ID_CCIT_ADPCM_G721_4BIT_BE is not supported by ALSA.
Packit Service a9274b
	// CODE_ID_CCIT_ADPCM_G723_3BIT_BE is not supported due to width of
Packit Service a9274b
	// its sample.
Packit Service a9274b
	{CODE_ID_CCIT_A_LAW_BE,			SND_PCM_FORMAT_A_LAW},
Packit Service a9274b
	{CODE_ID_CCIT_MU_LAW_BE,		SND_PCM_FORMAT_MU_LAW},
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct container_header {
Packit Service a9274b
	uint8_t magic[4];
Packit Service a9274b
	uint32_t hdr_size;
Packit Service a9274b
	uint32_t data_size;
Packit Service a9274b
	uint32_t code_id;
Packit Service a9274b
	uint32_t frames_per_second;
Packit Service a9274b
	uint32_t samples_per_frame;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct container_annotation {
Packit Service a9274b
	uint32_t chunks[0];
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct parser_state {
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
	unsigned int samples_per_frame;
Packit Service a9274b
	unsigned int bytes_per_sample;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static int au_parser_pre_process(struct container_context *cntr,
Packit Service a9274b
				 snd_pcm_format_t *format,
Packit Service a9274b
				 unsigned int *samples_per_frame,
Packit Service a9274b
				 unsigned int *frames_per_second,
Packit Service a9274b
				 uint64_t *byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct parser_state *state = cntr->private_data;
Packit Service a9274b
	struct container_header header;
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
	int i;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// Parse header. 4 bytes are enough to detect supported containers.
Packit Service a9274b
	memcpy(&header.magic, cntr->magic, sizeof(cntr->magic));
Packit Service a9274b
	err = container_recursive_read(cntr,
Packit Service a9274b
				       (char *)&header + sizeof(cntr->magic),
Packit Service a9274b
				       sizeof(header) - sizeof(cntr->magic));
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	if (cntr->eof)
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	if (memcmp(header.magic, AU_MAGIC, sizeof(header.magic)) != 0)
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	if (be32toh(header.hdr_size) != sizeof(struct container_header))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	code_id = be32toh(header.code_id);
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
Packit Service a9274b
		if (format_maps[i].code_id == code_id)
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
	if (i == ARRAY_SIZE(format_maps))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	*format = format_maps[i].format;
Packit Service a9274b
	*frames_per_second = be32toh(header.frames_per_second);
Packit Service a9274b
	*samples_per_frame = be32toh(header.samples_per_frame);
Packit Service a9274b
Packit Service a9274b
	state->code_id = code_id;
Packit Service a9274b
	state->samples_per_frame = *samples_per_frame;
Packit Service a9274b
	state->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
Packit Service a9274b
Packit Service a9274b
	*byte_count = be32toh(header.data_size);
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
struct builder_state {
Packit Service a9274b
	unsigned int bytes_per_sample;
Packit Service a9274b
	unsigned int samples_per_frame;
Packit Service a9274b
	unsigned int frames_per_second;
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static void build_container_header(struct builder_state *state,
Packit Service a9274b
				   struct container_header *header,
Packit Service a9274b
				   unsigned int frames_per_second,
Packit Service a9274b
				   uint64_t byte_count)
Packit Service a9274b
{
Packit Service a9274b
	memcpy(header->magic, AU_MAGIC, sizeof(header->magic));
Packit Service a9274b
	header->hdr_size = htobe32(sizeof(struct container_header));
Packit Service a9274b
	header->data_size = htobe32(byte_count);
Packit Service a9274b
	header->code_id = htobe32(state->code_id);
Packit Service a9274b
	header->frames_per_second = htobe32(frames_per_second);
Packit Service a9274b
	header->samples_per_frame = htobe32(state->samples_per_frame);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_container_header(struct container_context *cntr,
Packit Service a9274b
				  uint64_t byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
	struct container_header header;
Packit Service a9274b
Packit Service a9274b
	build_container_header(state, &header, state->frames_per_second,
Packit Service a9274b
			       byte_count);
Packit Service a9274b
Packit Service a9274b
	return container_recursive_write(cntr, &header, sizeof(header));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int au_builder_pre_process(struct container_context *cntr,
Packit Service a9274b
				  snd_pcm_format_t *format,
Packit Service a9274b
				  unsigned int *samples_per_frame,
Packit Service a9274b
				  unsigned int *frames_per_second,
Packit Service a9274b
				  uint64_t *byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *status = cntr->private_data;
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
Packit Service a9274b
		if (format_maps[i].format == *format)
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
	if (i == ARRAY_SIZE(format_maps))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	status->code_id = format_maps[i].code_id;
Packit Service a9274b
	status->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
Packit Service a9274b
	status->frames_per_second = *frames_per_second;
Packit Service a9274b
	status->samples_per_frame = *samples_per_frame;
Packit Service a9274b
Packit Service a9274b
	return write_container_header(cntr, *byte_count);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int au_builder_post_process(struct container_context *cntr,
Packit Service a9274b
				   uint64_t handled_byte_count)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = container_seek_offset(cntr, 0);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	return write_container_header(cntr, handled_byte_count);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
const struct container_parser container_parser_au = {
Packit Service a9274b
	.format = CONTAINER_FORMAT_AU,
Packit Service a9274b
	.magic = AU_MAGIC,
Packit Service a9274b
	.max_size = UINT32_MAX,
Packit Service a9274b
	.ops = {
Packit Service a9274b
		.pre_process = au_parser_pre_process,
Packit Service a9274b
	},
Packit Service a9274b
	.private_size = sizeof(struct parser_state),
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
const struct container_builder container_builder_au = {
Packit Service a9274b
	.format = CONTAINER_FORMAT_AU,
Packit Service a9274b
	.max_size = UINT32_MAX,
Packit Service a9274b
	.ops = {
Packit Service a9274b
		.pre_process	= au_builder_pre_process,
Packit Service a9274b
		.post_process	= au_builder_post_process,
Packit Service a9274b
	},
Packit Service a9274b
	.private_size = sizeof(struct builder_state),
Packit Service a9274b
};