aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2026-01-15 11:06:55 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2026-01-15 11:07:47 +0100
commit1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c (patch)
treeb5bbe1d19799fb240f8fd93349937fb0051aa6e4
parent23ade5792e57fd380aa72de9039148a7f946f3d6 (diff)
downloadmmbtools-aux-1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c.tar.gz
mmbtools-aux-1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c.tar.bz2
mmbtools-aux-1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c.zip
Add dabp-decoderHEADmaster
-rw-r--r--dabp-decode/.gitignore1
-rw-r--r--dabp-decode/AACDecoder.cpp160
-rw-r--r--dabp-decode/AACDecoder.h49
-rw-r--r--dabp-decode/Makefile9
-rw-r--r--dabp-decode/main.cpp42
-rw-r--r--dabp-decode/wavfile.cpp271
-rw-r--r--dabp-decode/wavfile.h45
7 files changed, 577 insertions, 0 deletions
diff --git a/dabp-decode/.gitignore b/dabp-decode/.gitignore
new file mode 100644
index 0000000..9d42fa4
--- /dev/null
+++ b/dabp-decode/.gitignore
@@ -0,0 +1 @@
+dabp-decoder
diff --git a/dabp-decode/AACDecoder.cpp b/dabp-decode/AACDecoder.cpp
new file mode 100644
index 0000000..6166560
--- /dev/null
+++ b/dabp-decode/AACDecoder.cpp
@@ -0,0 +1,160 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2011 Martin Storsjo
+ * Copyright (C) 2017 Matthias P. Braendli
+ * Copyright (C) 2016 Stefan Pöschel
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#include "AACDecoder.h"
+#include <stdexcept>
+#include <string>
+
+AACDecoder::AACDecoder(const char* wavfilename) :
+ m_wav_writer(wavfilename)
+{
+ m_handle = aacDecoder_Open(TT_MP4_RAW, 1);
+ if (not m_handle) {
+ throw std::runtime_error("AACDecoder: error opening decoder");
+ }
+}
+
+void AACDecoder::decode_frame(uint8_t *data, size_t len)
+{
+ const bool dac_rate = data[2] & 0x40;
+ const bool sbr_flag = data[2] & 0x20;
+ const bool aac_channel_mode = data[2] & 0x10;
+ const bool ps_flag = data[2] & 0x08;
+ //const uint8_t mpeg_surround_config = data[2] & 0x07;
+
+ const int core_sr_index = dac_rate ?
+ (sbr_flag ? 6 : 3) : (sbr_flag ? 8 : 5); // 24/48/16/32 kHz
+ const int core_ch_config = aac_channel_mode ? 2 : 1;
+ const int extension_sr_index = dac_rate ? 3 : 5; // 48/32 kHz
+
+ int au_start[6] = {};
+
+ int num_aus = dac_rate ? (sbr_flag ? 3 : 6) : (sbr_flag ? 2 : 4);
+ au_start[0] = dac_rate ? (sbr_flag ? 6 : 11) : (sbr_flag ? 5 : 8);
+ au_start[1] = data[3] << 4 | data[4] >> 4;
+
+ if (num_aus >= 3) {
+ au_start[2] = (data[4] & 0x0F) << 8 | data[5];
+ }
+
+ if (num_aus >= 4) {
+ au_start[3] = data[6] << 4 | data[7] >> 4;
+ }
+
+ if (num_aus == 6) {
+ au_start[4] = (data[7] & 0x0F) << 8 | data[8];
+ au_start[5] = data[9] << 4 | data[10] >> 4;
+ }
+
+ au_start[num_aus] = len; // end of the buffer
+
+ for (int i = 0; i < num_aus; i++) {
+ if (au_start[i] >= au_start[i+1]) {
+ throw std::runtime_error(" AU ordering check failed\n");
+ }
+ }
+
+ if (not m_decoder_set_up) {
+ std::vector<uint8_t> asc;
+
+ // AAC LC
+ asc.push_back(0b00010 << 3 | core_sr_index >> 1);
+ asc.push_back((core_sr_index & 0x01) << 7 | core_ch_config << 3 | 0b100);
+
+ if (sbr_flag) {
+ // add SBR
+ asc.push_back(0x56);
+ asc.push_back(0xE5);
+ asc.push_back(0x80 | (extension_sr_index << 3));
+
+ if (ps_flag) {
+ // add PS
+ asc.back() |= 0x05;
+ asc.push_back(0x48);
+ asc.push_back(0x80);
+ }
+ }
+
+ uint8_t* asc_array[1] {asc.data()};
+ const unsigned int asc_sizeof_array[1] {(unsigned int) asc.size()};
+
+ AAC_DECODER_ERROR init_result = aacDecoder_ConfigRaw(m_handle,
+ asc_array, asc_sizeof_array);
+ if (init_result != AAC_DEC_OK) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: error while aacDecoder_ConfigRaw: " +
+ std::to_string(init_result));
+ }
+
+ m_channels = (aac_channel_mode or ps_flag) ? 2 : 1;
+ size_t output_frame_len = 960 * 2 * m_channels * (sbr_flag ? 2 : 1);
+ m_output_frame.resize(output_frame_len);
+ fprintf(stderr, " Setting decoder output frame len %zu\n", output_frame_len);
+
+ const int sample_rate = dac_rate ? 48000 : 32000;
+ m_wav_writer.initialise_header(sample_rate, m_channels);
+ m_decoder_set_up = true;
+
+ fprintf(stderr, " Set up decoder with %d Hz, %s%swith %d channels\n",
+ sample_rate, (sbr_flag ? "SBR " : ""), (ps_flag ? "PS " : ""),
+ m_channels);
+
+ }
+
+ const size_t AU_CRCLEN = 2;
+ for (int i = 0; i < num_aus; i++) {
+ uint8_t *au_data = data + au_start[i];
+ size_t au_len = au_start[i+1] - au_start[i] - AU_CRCLEN;
+ decode_au(au_data, au_len);
+ }
+}
+
+void AACDecoder::decode_au(uint8_t *data, size_t len)
+{
+ uint8_t* input_buffer[1] {data};
+ const unsigned int input_buffer_size[1] {(unsigned int) len};
+ unsigned int bytes_valid = len;
+
+ // fill internal input buffer
+ AAC_DECODER_ERROR result = aacDecoder_Fill(
+ m_handle, input_buffer, input_buffer_size, &bytes_valid);
+
+ if (result != AAC_DEC_OK) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: error while aacDecoder_Fill: " +
+ std::to_string(result));
+ }
+
+ if (bytes_valid) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: aacDecoder_Fill did not consume all bytes");
+ }
+
+ // decode audio
+ result = aacDecoder_DecodeFrame(m_handle,
+ (short int*)m_output_frame.data(), m_output_frame.size(), 0);
+ if (result != AAC_DEC_OK) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: error while aacDecoder_DecodeFrame: " +
+ std::to_string(result));
+ }
+
+ m_wav_writer.write_data(m_output_frame.data(), m_output_frame.size());
+}
diff --git a/dabp-decode/AACDecoder.h b/dabp-decode/AACDecoder.h
new file mode 100644
index 0000000..2c09548
--- /dev/null
+++ b/dabp-decode/AACDecoder.h
@@ -0,0 +1,49 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2011 Martin Storsjo
+ * Copyright (C) 2017 Matthias P. Braendli
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+/*!
+ * \file AACDecoder.h
+ * \brief Uses FDK-AAC to decode the AAC format for loopback tests
+ */
+
+#pragma once
+
+#include <fdk-aac/aacdecoder_lib.h>
+#include <cstdint>
+#include <vector>
+#include "wavfile.h"
+
+class AACDecoder {
+ public:
+ AACDecoder(const char* wavfilename);
+
+ void decode_frame(uint8_t *data, size_t len);
+
+ private:
+ void decode_au(uint8_t *data, size_t len);
+
+ bool m_decoder_set_up = false;
+
+ int m_channels = 0;
+
+ WavWriter m_wav_writer;
+ HANDLE_AACDECODER m_handle;
+ std::vector<uint8_t> m_output_frame;
+};
+
diff --git a/dabp-decode/Makefile b/dabp-decode/Makefile
new file mode 100644
index 0000000..9c3ed31
--- /dev/null
+++ b/dabp-decode/Makefile
@@ -0,0 +1,9 @@
+
+all: dabp-decoder
+
+SRCS := main.cpp \
+ AACDecoder.h AACDecoder.cpp \
+ wavfile.h wavfile.cpp
+
+dabp-decoder: $(SRCS)
+ g++ -Wall -std=c++17 -fsanitize=address -lfdk-aac -o dabp-decoder AACDecoder.cpp wavfile.cpp main.cpp
diff --git a/dabp-decode/main.cpp b/dabp-decode/main.cpp
new file mode 100644
index 0000000..e674f74
--- /dev/null
+++ b/dabp-decode/main.cpp
@@ -0,0 +1,42 @@
+#include <iostream>
+#include <string>
+#include <vector>
+#include "AACDecoder.h"
+
+int main(int argc, char **argv)
+{
+ if (argc != 4) {
+ std::cerr << "Usage: " << argv[0] << " INFILE FRAME_SIZE OUTFILE\n";
+ return 1;
+ }
+
+ auto frame_size = std::stol(argv[2]);
+ if (frame_size == 0) {
+ std::cerr << "Frame size 0 !\n";
+ return 1;
+ }
+
+ FILE *fd = fopen(argv[1], "r");
+ if (fd == nullptr) {
+ std::cerr << "Could not open file !\n";
+ return 1;
+ }
+
+ AACDecoder aacdec(argv[3]);
+
+ std::vector<uint8_t> buf;
+ buf.resize(frame_size);
+
+ while (not feof(fd)) {
+ if (fread(buf.data(), buf.size(), 1, fd) == 0) {
+ std::cerr << "fread 0\n";
+ break;
+ }
+
+ aacdec.decode_frame(buf.data(), buf.size());
+ }
+
+ fclose(fd);
+
+ return 0;
+}
diff --git a/dabp-decode/wavfile.cpp b/dabp-decode/wavfile.cpp
new file mode 100644
index 0000000..fd3dc43
--- /dev/null
+++ b/dabp-decode/wavfile.cpp
@@ -0,0 +1,271 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2009 Martin Storsjo
+ * Copyright (C) 2017 Matthias P. Braendli
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#include "wavfile.h"
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cstdint>
+#include <stdexcept>
+
+#define TAG(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+
+struct wav_reader {
+ FILE *wav;
+ uint32_t data_length;
+
+ int format;
+ int sample_rate;
+ int bits_per_sample;
+ int channels;
+ int byte_rate;
+ int block_align;
+
+ int streamed;
+};
+
+static uint32_t read_tag(struct wav_reader* wr) {
+ uint32_t tag = 0;
+ tag = (tag << 8) | fgetc(wr->wav);
+ tag = (tag << 8) | fgetc(wr->wav);
+ tag = (tag << 8) | fgetc(wr->wav);
+ tag = (tag << 8) | fgetc(wr->wav);
+ return tag;
+}
+
+static uint32_t read_int32(struct wav_reader* wr) {
+ uint32_t value = 0;
+ value |= fgetc(wr->wav) << 0;
+ value |= fgetc(wr->wav) << 8;
+ value |= fgetc(wr->wav) << 16;
+ value |= fgetc(wr->wav) << 24;
+ return value;
+}
+
+static uint16_t read_int16(struct wav_reader* wr) {
+ uint16_t value = 0;
+ value |= fgetc(wr->wav) << 0;
+ value |= fgetc(wr->wav) << 8;
+ return value;
+}
+
+static void skip(FILE *f, int n) {
+ int i;
+ for (i = 0; i < n; i++)
+ fgetc(f);
+}
+
+void* wav_read_open(const char *filename) {
+ struct wav_reader* wr = (struct wav_reader*) malloc(sizeof(*wr));
+ long data_pos = 0;
+ memset(wr, 0, sizeof(*wr));
+
+ if (!strcmp(filename, "-"))
+ wr->wav = stdin;
+ else
+ wr->wav = fopen(filename, "rb");
+ if (wr->wav == NULL) {
+ free(wr);
+ return NULL;
+ }
+
+ while (1) {
+ uint32_t tag, tag2, length;
+ tag = read_tag(wr);
+ if (feof(wr->wav))
+ break;
+ length = read_int32(wr);
+ if (!length || length >= 0x7fff0000) {
+ wr->streamed = 1;
+ length = ~0;
+ }
+ if (tag != TAG('R', 'I', 'F', 'F') || length < 4) {
+ fseek(wr->wav, length, SEEK_CUR);
+ continue;
+ }
+ tag2 = read_tag(wr);
+ length -= 4;
+ if (tag2 != TAG('W', 'A', 'V', 'E')) {
+ fseek(wr->wav, length, SEEK_CUR);
+ continue;
+ }
+ // RIFF chunk found, iterate through it
+ while (length >= 8) {
+ uint32_t subtag, sublength;
+ subtag = read_tag(wr);
+ if (feof(wr->wav))
+ break;
+ sublength = read_int32(wr);
+ length -= 8;
+ if (length < sublength)
+ break;
+ if (subtag == TAG('f', 'm', 't', ' ')) {
+ if (sublength < 16) {
+ // Insufficient data for 'fmt '
+ break;
+ }
+ wr->format = read_int16(wr);
+ wr->channels = read_int16(wr);
+ wr->sample_rate = read_int32(wr);
+ wr->byte_rate = read_int32(wr);
+ wr->block_align = read_int16(wr);
+ wr->bits_per_sample = read_int16(wr);
+ if (wr->format == 0xfffe) {
+ if (sublength < 28) {
+ // Insufficient data for waveformatex
+ break;
+ }
+ skip(wr->wav, 8);
+ wr->format = read_int32(wr);
+ skip(wr->wav, sublength - 28);
+ } else {
+ skip(wr->wav, sublength - 16);
+ }
+ } else if (subtag == TAG('d', 'a', 't', 'a')) {
+ data_pos = ftell(wr->wav);
+ wr->data_length = sublength;
+ if (!wr->data_length || wr->streamed) {
+ wr->streamed = 1;
+ return wr;
+ }
+ fseek(wr->wav, sublength, SEEK_CUR);
+ } else {
+ skip(wr->wav, sublength);
+ }
+ length -= sublength;
+ }
+ if (length > 0) {
+ // Bad chunk?
+ fseek(wr->wav, length, SEEK_CUR);
+ }
+ }
+ fseek(wr->wav, data_pos, SEEK_SET);
+ return wr;
+}
+
+void wav_read_close(void* obj) {
+ struct wav_reader* wr = (struct wav_reader*) obj;
+ if (wr->wav != stdin)
+ fclose(wr->wav);
+ free(wr);
+}
+
+int wav_get_header(void* obj, int* format, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_length) {
+ struct wav_reader* wr = (struct wav_reader*) obj;
+ if (format)
+ *format = wr->format;
+ if (channels)
+ *channels = wr->channels;
+ if (sample_rate)
+ *sample_rate = wr->sample_rate;
+ if (bits_per_sample)
+ *bits_per_sample = wr->bits_per_sample;
+ if (data_length)
+ *data_length = wr->data_length;
+ return wr->format && wr->sample_rate;
+}
+
+int wav_read_data(void* obj, unsigned char* data, unsigned int length) {
+ struct wav_reader* wr = (struct wav_reader*) obj;
+ int n;
+ if (wr->wav == NULL)
+ return -1;
+ if (length > wr->data_length && !wr->streamed)
+ length = wr->data_length;
+ n = fread(data, 1, length, wr->wav);
+ wr->data_length -= length;
+ return n;
+}
+
+//============== WAV writer functions
+
+struct wavfile_header {
+ char riff_tag[4];
+ int riff_length;
+ char wave_tag[4];
+ char fmt_tag[4];
+ int fmt_length;
+ short audio_format;
+ short num_channels;
+ int sample_rate;
+ int byte_rate;
+ short block_align;
+ short bits_per_sample;
+ char data_tag[4];
+ int data_length;
+};
+
+WavWriter::WavWriter(const char *filename)
+{
+ m_fd = fopen(filename, "w+");
+ if (not m_fd) {
+ throw std::runtime_error("Could not open wav file");
+ }
+}
+
+void WavWriter::initialise_header(int rate, int channels)
+{
+ struct wavfile_header header;
+
+ int samples_per_second = rate;
+ int bits_per_sample = 16;
+
+ memcpy(header.riff_tag,"RIFF",4);
+ memcpy(header.wave_tag,"WAVE",4);
+ memcpy(header.fmt_tag,"fmt ",4);
+ memcpy(header.data_tag,"data",4);
+
+ header.riff_length = 0;
+ header.fmt_length = 16;
+ header.audio_format = 1;
+ header.num_channels = channels;
+ header.sample_rate = samples_per_second;
+ header.byte_rate = samples_per_second*(bits_per_sample/8)*channels;
+ header.block_align = channels*bits_per_sample/8;
+ header.bits_per_sample = bits_per_sample;
+ header.data_length = 0;
+
+ fwrite(&header,sizeof(header),1,m_fd);
+
+ fflush(m_fd);
+}
+
+WavWriter::~WavWriter()
+{
+ // The wav file header contains the full file size, we must
+ // write this at the end
+
+ int file_length = ftell(m_fd);
+
+ int data_length = file_length - sizeof(struct wavfile_header);
+ fseek(m_fd,sizeof(struct wavfile_header) - sizeof(int),SEEK_SET);
+ fwrite(&data_length,sizeof(data_length),1,m_fd);
+
+ int riff_length = file_length - 8;
+ fseek(m_fd,4,SEEK_SET);
+ fwrite(&riff_length,sizeof(riff_length),1,m_fd);
+
+ fclose(m_fd);
+}
+
+void WavWriter::write_data(const uint8_t *data, int length)
+{
+ fwrite(data,sizeof(uint8_t),length,m_fd);
+}
+
diff --git a/dabp-decode/wavfile.h b/dabp-decode/wavfile.h
new file mode 100644
index 0000000..6d68053
--- /dev/null
+++ b/dabp-decode/wavfile.h
@@ -0,0 +1,45 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2009 Martin Storsjo
+ * Copyright (C) 2018 Matthias P. Braendli
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#pragma once
+
+#include <cstdio>
+#include <cstdint>
+
+void* wav_read_open(const char *filename);
+void wav_read_close(void* obj);
+
+int wav_get_header(void* obj, int* format, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_length);
+int wav_read_data(void* obj, unsigned char* data, unsigned int length);
+
+class WavWriter {
+ public:
+ WavWriter(const char *filename);
+ ~WavWriter();
+ WavWriter(const WavWriter& other) = delete;
+ WavWriter& operator=(const WavWriter& other) = delete;
+
+ void initialise_header(int rate, int channels);
+
+ void write_data(const uint8_t *data, int length);
+
+ private:
+ FILE *m_fd = nullptr;
+};
+