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