diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2026-01-15 11:06:55 +0100 |
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2026-01-15 11:07:47 +0100 |
| commit | 1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c (patch) | |
| tree | b5bbe1d19799fb240f8fd93349937fb0051aa6e4 | |
| parent | 23ade5792e57fd380aa72de9039148a7f946f3d6 (diff) | |
| download | mmbtools-aux-1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c.tar.gz mmbtools-aux-1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c.tar.bz2 mmbtools-aux-1f112f14ff0bb3cb77f97c8d945bece9dd90fc4c.zip | |
| -rw-r--r-- | dabp-decode/.gitignore | 1 | ||||
| -rw-r--r-- | dabp-decode/AACDecoder.cpp | 160 | ||||
| -rw-r--r-- | dabp-decode/AACDecoder.h | 49 | ||||
| -rw-r--r-- | dabp-decode/Makefile | 9 | ||||
| -rw-r--r-- | dabp-decode/main.cpp | 42 | ||||
| -rw-r--r-- | dabp-decode/wavfile.cpp | 271 | ||||
| -rw-r--r-- | dabp-decode/wavfile.h | 45 |
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; +}; + |
