]> git.alsa-project.org Git - alsa-utils.git/commitdiff
axfer: add support for a container of Sparc AU format
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tue, 13 Nov 2018 06:41:17 +0000 (15:41 +0900)
committerTakashi Iwai <tiwai@suse.de>
Tue, 13 Nov 2018 11:04:23 +0000 (12:04 +0100)
This commit adds support for data of Sparc AU format. In this data format,
values in each of field are encoded in big-endian byte order and available
formats of data sample are restricted in big-endian byte order.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
axfer/Makefile.am
axfer/container-au.c [new file with mode: 0644]
axfer/container.c
axfer/container.h

index 092c96629522d9f1bd0a57e7d405441cafabcf7a..35aa22617f11a07dcb336575f3d8f3e54d6f0b8f 100644 (file)
@@ -25,4 +25,5 @@ axfer_SOURCES = \
        subcmd-list.c \
        container.h \
        container.c \
-       container-riff-wave.c
+       container-riff-wave.c \
+       container-au.c
diff --git a/axfer/container-au.c b/axfer/container-au.c
new file mode 100644 (file)
index 0000000..6459b16
--- /dev/null
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// container-au.c - a parser/builder for a container of Sun Audio File.
+//
+// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include "container.h"
+#include "misc.h"
+
+// Not portable to all of UNIX platforms.
+#include <endian.h>
+
+// Reference:
+//  * http://pubs.opengroup.org/external/auformat.html
+
+#define AU_MAGIC       ".snd"
+#define UNKNOWN_SIZE   UINT32_MAX
+
+enum code_id {
+       CODE_ID_CCIT_MU_LAW_BE          = 0x01,
+       CODE_ID_GENERIC_MBLA_S8         = 0x02,
+       CODE_ID_GENERIC_MBLA_S16_BE     = 0x03,
+       CODE_ID_GENERIC_MBLA_S32_BE     = 0x05,
+       CODE_ID_IEEE754_FLOAT_S32_BE    = 0x06,
+       CODE_ID_IEEE754_DOUBLE_S64_BE   = 0x07,
+       CODE_ID_CCIT_ADPCM_G721_4BIT_BE = 0x17,
+       CODE_ID_CCIT_ADPCM_G723_3BIT_BE = 0x19,
+       CODE_ID_CCIT_A_LAW_BE           = 0x1b,
+};
+
+struct format_map {
+       enum code_id code_id;
+       snd_pcm_format_t format;
+};
+
+static const struct format_map format_maps[] = {
+       {CODE_ID_GENERIC_MBLA_S8,               SND_PCM_FORMAT_S8},
+       {CODE_ID_GENERIC_MBLA_S16_BE,           SND_PCM_FORMAT_S16_BE},
+       {CODE_ID_GENERIC_MBLA_S32_BE,           SND_PCM_FORMAT_S32_BE},
+       {CODE_ID_IEEE754_FLOAT_S32_BE,          SND_PCM_FORMAT_FLOAT_BE},
+       {CODE_ID_IEEE754_DOUBLE_S64_BE,         SND_PCM_FORMAT_FLOAT64_BE},
+       // CODE_ID_CCIT_ADPCM_G721_4BIT_BE is not supported by ALSA.
+       // CODE_ID_CCIT_ADPCM_G723_3BIT_BE is not supported due to width of
+       // its sample.
+       {CODE_ID_CCIT_A_LAW_BE,                 SND_PCM_FORMAT_A_LAW},
+       {CODE_ID_CCIT_MU_LAW_BE,                SND_PCM_FORMAT_MU_LAW},
+};
+
+struct container_header {
+       uint8_t magic[4];
+       uint32_t hdr_size;
+       uint32_t data_size;
+       uint32_t code_id;
+       uint32_t frames_per_second;
+       uint32_t samples_per_frame;
+};
+
+struct container_annotation {
+       uint32_t chunks[0];
+};
+
+struct parser_state {
+       enum code_id code_id;
+       unsigned int samples_per_frame;
+       unsigned int bytes_per_sample;
+};
+
+static int 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)
+{
+       struct parser_state *state = cntr->private_data;
+       struct container_header header;
+       enum code_id code_id;
+       int i;
+       int err;
+
+       // Parse header. 4 bytes are enough to detect supported containers.
+       memcpy(&header.magic, cntr->magic, sizeof(cntr->magic));
+       err = container_recursive_read(cntr,
+                                      (char *)&header + sizeof(cntr->magic),
+                                      sizeof(header) - sizeof(cntr->magic));
+       if (err < 0)
+               return err;
+       if (cntr->eof)
+               return 0;
+
+       if (memcmp(header.magic, AU_MAGIC, sizeof(header.magic)) != 0)
+               return -EINVAL;
+       if (be32toh(header.hdr_size) != sizeof(struct container_header))
+               return -EINVAL;
+
+       code_id = be32toh(header.code_id);
+       for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
+               if (format_maps[i].code_id == code_id)
+                       break;
+       }
+       if (i == ARRAY_SIZE(format_maps))
+               return -EINVAL;
+       *format = format_maps[i].format;
+       *frames_per_second = be32toh(header.frames_per_second);
+       *samples_per_frame = be32toh(header.samples_per_frame);
+
+       state->code_id = code_id;
+       state->samples_per_frame = *samples_per_frame;
+       state->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
+
+       *byte_count = be32toh(header.data_size);
+
+       return 0;
+}
+
+struct builder_state {
+       unsigned int bytes_per_sample;
+       unsigned int samples_per_frame;
+       unsigned int frames_per_second;
+       enum code_id code_id;
+};
+
+static void build_container_header(struct builder_state *state,
+                                  struct container_header *header,
+                                  unsigned int frames_per_second,
+                                  uint64_t byte_count)
+{
+       memcpy(header->magic, AU_MAGIC, sizeof(header->magic));
+       header->hdr_size = htobe32(sizeof(struct container_header));
+       header->data_size = htobe32(byte_count);
+       header->code_id = htobe32(state->code_id);
+       header->frames_per_second = htobe32(frames_per_second);
+       header->samples_per_frame = htobe32(state->samples_per_frame);
+}
+
+static int write_container_header(struct container_context *cntr,
+                                 uint64_t byte_count)
+{
+       struct builder_state *state = cntr->private_data;
+       struct container_header header;
+
+       build_container_header(state, &header, state->frames_per_second,
+                              byte_count);
+
+       return container_recursive_write(cntr, &header, sizeof(header));
+}
+
+static int 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)
+{
+       struct builder_state *status = cntr->private_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
+               if (format_maps[i].format == *format)
+                       break;
+       }
+       if (i == ARRAY_SIZE(format_maps))
+               return -EINVAL;
+
+       status->code_id = format_maps[i].code_id;
+       status->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
+       status->frames_per_second = *frames_per_second;
+       status->samples_per_frame = *samples_per_frame;
+
+       return write_container_header(cntr, *byte_count);
+}
+
+static int au_builder_post_process(struct container_context *cntr,
+                                  uint64_t handled_byte_count)
+{
+       int err;
+
+       err = container_seek_offset(cntr, 0);
+       if (err < 0)
+               return err;
+
+       return write_container_header(cntr, handled_byte_count);
+}
+
+const struct container_parser container_parser_au = {
+       .format = CONTAINER_FORMAT_AU,
+       .magic = AU_MAGIC,
+       .max_size = UINT32_MAX,
+       .ops = {
+               .pre_process = au_parser_pre_process,
+       },
+       .private_size = sizeof(struct parser_state),
+};
+
+const struct container_builder container_builder_au = {
+       .format = CONTAINER_FORMAT_AU,
+       .max_size = UINT32_MAX,
+       .ops = {
+               .pre_process    = au_builder_pre_process,
+               .post_process   = au_builder_post_process,
+       },
+       .private_size = sizeof(struct builder_state),
+};
index 04042dac65d7d8dece3342b513ec7c28ba2fa826..0e25605170b2050624bc2adec1859bc0b86514b2 100644 (file)
@@ -21,10 +21,12 @@ static const char *const cntr_type_labels[] = {
 
 static const char *const cntr_format_labels[] = {
        [CONTAINER_FORMAT_RIFF_WAVE] = "riff/wave",
+       [CONTAINER_FORMAT_AU] = "au",
 };
 
 static const char *const suffixes[] = {
-       [CONTAINER_FORMAT_RIFF_WAVE] = ".wav",
+       [CONTAINER_FORMAT_RIFF_WAVE]    = ".wav",
+       [CONTAINER_FORMAT_AU]           = ".au",
 };
 
 
@@ -142,6 +144,7 @@ int container_parser_init(struct container_context *cntr,
 {
        const struct container_parser *parsers[] = {
                [CONTAINER_FORMAT_RIFF_WAVE] = &container_parser_riff_wave,
+               [CONTAINER_FORMAT_AU] = &container_parser_au,
        };
        const struct container_parser *parser;
        unsigned int size;
@@ -216,6 +219,7 @@ int container_builder_init(struct container_context *cntr,
 {
        const struct container_builder *builders[] = {
                [CONTAINER_FORMAT_RIFF_WAVE] = &container_builder_riff_wave,
+               [CONTAINER_FORMAT_AU] = &container_builder_au,
        };
        const struct container_builder *builder;
        int err;
index 5808933c5e215d35e41ee6961eb62f79cae32c40..19ef67269ff525e6405f36d810c80b300107ed8f 100644 (file)
@@ -26,6 +26,7 @@ enum container_type {
 
 enum container_format {
        CONTAINER_FORMAT_RIFF_WAVE = 0,
+       CONTAINER_FORMAT_AU,
        CONTAINER_FORMAT_COUNT,
 };
 
@@ -111,4 +112,7 @@ int container_seek_offset(struct container_context *cntr, off64_t offset);
 extern const struct container_parser container_parser_riff_wave;
 extern const struct container_builder container_builder_riff_wave;
 
+extern const struct container_parser container_parser_au;
+extern const struct container_builder container_builder_au;
+
 #endif