Skip to content

Commit a826a8d

Browse files
committed
DRAFT CodecALAC.h
1 parent 25ed4c8 commit a826a8d

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
2+
#pragma once
3+
4+
#include "ALAC.h" // https://github.com/pschatzmann/codec-alac
5+
#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
6+
7+
namespace audio_tools {
8+
9+
/**
10+
* @brief ALAC (Apple Lossless Audio Codec) decoder. Please note that this codec
11+
* usually needs a container (e.g. M4A) to provide the relevant codec
12+
* information. This class depends on https://github.com/pschatzmann/codec-alac
13+
* @ingroup codecs
14+
* @author Phil Schatzmann
15+
*/
16+
class DecoderALAC : public AudioDecoder {
17+
public:
18+
/// write Magic Cookie
19+
size_t writeCodecInfo(const uint8_t* data, size_t len) override {
20+
int32_t rc = dec.Init((void*)data, len);
21+
if (rc != 0) {
22+
LOGE("Init failed");
23+
return 0;
24+
}
25+
AudioInfo info;
26+
info.bits_per_sample = dec.mConfig.bitDepth;
27+
info.channels = dec.mConfig.numChannels;
28+
info.sample_rate = dec.mConfig.sampleRate;
29+
setAudioInfo(info);
30+
is_init = true;
31+
return len;
32+
}
33+
34+
/// we expect the write is called for a complete frame!
35+
size_t write(const uint8_t* encodedFrame, size_t frameLength) override {
36+
LOGI("write: %d", (int)frameLength);
37+
if (!is_init) {
38+
ALACSpecificConfig config = {};
39+
AudioInfo info = audioInfo();
40+
config.frameLength = frameLength;
41+
config.bitDepth = info.bits_per_sample;
42+
config.numChannels = info.channels;
43+
config.sampleRate = info.sample_rate;
44+
writeCodecInfo((uint8_t*)&config, sizeof(config));
45+
is_init = true;
46+
}
47+
// Make sure we have the output buffer set up
48+
if (result_buffer.size() != outputBufferSize()) {
49+
result_buffer.resize(outputBufferSize());
50+
}
51+
52+
// Init bit buffer
53+
struct BitBuffer bits;
54+
BitBufferInit(&bits, (uint8_t*)encodedFrame, frameLength);
55+
56+
// Decode
57+
uint32_t outNumSamples = 0;
58+
int32_t status =
59+
dec.Decode(&bits, result_buffer.data(), dec.mConfig.frameLength,
60+
dec.mConfig.numChannels, &outNumSamples);
61+
62+
if (status != 0) {
63+
LOGE("Decode failed with error: %d", status);
64+
return 0;
65+
}
66+
67+
// Process result
68+
size_t outputSize =
69+
outNumSamples * dec.mConfig.numChannels * dec.mConfig.bitDepth / 8;
70+
p_print->write(result_buffer.data(), outputSize);
71+
return frameLength;
72+
}
73+
74+
operator bool() { return true; }
75+
76+
protected:
77+
ALACDecoder dec;
78+
Vector<uint8_t> result_buffer;
79+
bool is_init = false;
80+
81+
int outputBufferSize() {
82+
return dec.mConfig.frameLength * dec.mConfig.numChannels *
83+
dec.mConfig.bitDepth / 8;
84+
}
85+
};
86+
87+
/**
88+
* @brief ALAC (Apple Lossless Audio Codec) encoder. This class is responsible
89+
* for encoding audio data into ALAC format.
90+
* @ingroup codecs
91+
* @author Phil Schatzmann
92+
*/
93+
class EncoderALAC : public AudioEncoder {
94+
public:
95+
void setOutput(Print& out_stream) override { p_print = &out_stream; };
96+
97+
bool begin() override {
98+
if (p_print == nullptr) {
99+
LOGE("No output stream set");
100+
return false;
101+
}
102+
input_format.mSampleRate = info.sample_rate;
103+
input_format.mFormatID = kALACFormatLinearPCM;
104+
input_format.mFormatFlags = kALACFormatFlagIsSignedInteger;
105+
input_format.mBytesPerPacket = default_bytes_per_packet;
106+
input_format.mFramesPerPacket = 0;
107+
input_format.mBytesPerFrame = info.channels * info.bits_per_sample / 8;
108+
input_format.mChannelsPerFrame = info.channels;
109+
input_format.mBitsPerChannel = info.bits_per_sample;
110+
int rc = enc.InitializeEncoder(input_format);
111+
112+
// define output format
113+
out_format = input_format;
114+
out_format.mFormatID = kALACFormatAppleLossless;
115+
116+
in_buffer.resize(default_bytes_per_packet);
117+
out_buffer.resize(default_bytes_per_packet);
118+
is_started = rc == 0;
119+
return is_started;
120+
}
121+
122+
void end() override {
123+
enc.Finish();
124+
is_started = false;
125+
}
126+
127+
/// Encode the audio samples into ALAC format
128+
size_t write(const uint8_t* data, size_t len) override {
129+
int32_t ioNumBytes;
130+
for (int j = 0; j < len; j++) {
131+
in_buffer.write(data[j]);
132+
if (in_buffer.isFull()) {
133+
int rc = enc.Encode(input_format, out_format, (uint8_t*)data,
134+
out_buffer.data(), &ioNumBytes);
135+
size_t written = p_print->write(out_buffer.data(), ioNumBytes);
136+
if (ioNumBytes != written) {
137+
LOGE("write error: %d -> %d", ioNumBytes, written);
138+
}
139+
in_buffer.reset();
140+
}
141+
}
142+
return len;
143+
}
144+
145+
ALACSpecificConfig config() {
146+
enc.GetConfig(cfg);
147+
return cfg;
148+
}
149+
150+
operator bool() { return is_started && p_print != nullptr; }
151+
152+
const char* mime() override { return "audio/alac"; }
153+
154+
void setDefaultBytesPerPacket(int bytes) { default_bytes_per_packet = bytes; }
155+
156+
protected:
157+
int default_bytes_per_packet = 1024;
158+
ALACEncoder enc;
159+
SingleBuffer<uint8_t> in_buffer;
160+
Vector<uint8_t> out_buffer;
161+
AudioFormatDescription input_format;
162+
AudioFormatDescription out_format;
163+
ALACSpecificConfig cfg;
164+
Print* p_print = nullptr;
165+
bool is_started = false;
166+
};
167+
168+
} // namespace audio_tools

0 commit comments

Comments
 (0)