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