xref: /btstack/3rd-party/lc3-google/python/lc3.py (revision 6897da5c53aac5b1f90f41b5b15d0bd43d61dfff)
1*6897da5cSDirk Helbig#
2*6897da5cSDirk Helbig# Copyright 2024 Google LLC
3*6897da5cSDirk Helbig#
4*6897da5cSDirk Helbig# Licensed under the Apache License, Version 2.0 (the "License");
5*6897da5cSDirk Helbig# you may not use this file except in compliance with the License.
6*6897da5cSDirk Helbig# You may obtain a copy of the License at
7*6897da5cSDirk Helbig#
8*6897da5cSDirk Helbig#     http://www.apache.org/licenses/LICENSE-2.0
9*6897da5cSDirk Helbig#
10*6897da5cSDirk Helbig# Unless required by applicable law or agreed to in writing, software
11*6897da5cSDirk Helbig# distributed under the License is distributed on an "AS IS" BASIS,
12*6897da5cSDirk Helbig# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6897da5cSDirk Helbig# See the License for the specific language governing permissions and
14*6897da5cSDirk Helbig# limitations under the License.
15*6897da5cSDirk Helbig#
16*6897da5cSDirk Helbig
17*6897da5cSDirk Helbigimport array
18*6897da5cSDirk Helbigimport ctypes
19*6897da5cSDirk Helbigimport enum
20*6897da5cSDirk Helbigimport glob
21*6897da5cSDirk Helbigimport os
22*6897da5cSDirk Helbig
23*6897da5cSDirk Helbigfrom ctypes import c_bool, c_byte, c_int, c_uint, c_size_t, c_void_p
24*6897da5cSDirk Helbigfrom ctypes.util import find_library
25*6897da5cSDirk Helbig
26*6897da5cSDirk Helbig
27*6897da5cSDirk Helbigclass _Base:
28*6897da5cSDirk Helbig
29*6897da5cSDirk Helbig    def __init__(self, frame_duration, samplerate, nchannels, **kwargs):
30*6897da5cSDirk Helbig
31*6897da5cSDirk Helbig        self.hrmode = False
32*6897da5cSDirk Helbig        self.dt_us = int(frame_duration * 1000)
33*6897da5cSDirk Helbig        self.sr_hz = int(samplerate)
34*6897da5cSDirk Helbig        self.sr_pcm_hz = self.sr_hz
35*6897da5cSDirk Helbig        self.nchannels = nchannels
36*6897da5cSDirk Helbig
37*6897da5cSDirk Helbig        libpath = None
38*6897da5cSDirk Helbig
39*6897da5cSDirk Helbig        for k in kwargs.keys():
40*6897da5cSDirk Helbig            if k == 'hrmode':
41*6897da5cSDirk Helbig                self.hrmode = bool(kwargs[k])
42*6897da5cSDirk Helbig            elif k == 'pcm_samplerate':
43*6897da5cSDirk Helbig                self.sr_pcm_hz = int(kwargs[k])
44*6897da5cSDirk Helbig            elif k == 'libpath':
45*6897da5cSDirk Helbig                libpath = kwargs[k]
46*6897da5cSDirk Helbig            else:
47*6897da5cSDirk Helbig                raise ValueError("Invalid keyword argument: " + k)
48*6897da5cSDirk Helbig
49*6897da5cSDirk Helbig        if self.dt_us not in [2500, 5000, 7500, 10000]:
50*6897da5cSDirk Helbig            raise ValueError(
51*6897da5cSDirk Helbig                "Invalid frame duration: %.1f ms" % frame_duration)
52*6897da5cSDirk Helbig
53*6897da5cSDirk Helbig        allowed_samplerate = [8000, 16000, 24000, 32000, 48000] \
54*6897da5cSDirk Helbig            if not self.hrmode else [48000, 96000]
55*6897da5cSDirk Helbig
56*6897da5cSDirk Helbig        if self.sr_hz not in allowed_samplerate:
57*6897da5cSDirk Helbig            raise ValueError("Invalid sample rate: %d Hz" % samplerate)
58*6897da5cSDirk Helbig
59*6897da5cSDirk Helbig        if libpath is None:
60*6897da5cSDirk Helbig            mesonpy_lib = glob.glob(os.path.join(os.path.dirname(__file__), '.lc3.mesonpy.libs', '*lc3*'))
61*6897da5cSDirk Helbig
62*6897da5cSDirk Helbig            if mesonpy_lib:
63*6897da5cSDirk Helbig                libpath = mesonpy_lib[0]
64*6897da5cSDirk Helbig            else:
65*6897da5cSDirk Helbig                libpath = find_library("lc3")
66*6897da5cSDirk Helbig            if not libpath:
67*6897da5cSDirk Helbig                raise Exception("LC3 library not found")
68*6897da5cSDirk Helbig
69*6897da5cSDirk Helbig        lib = ctypes.cdll.LoadLibrary(libpath)
70*6897da5cSDirk Helbig
71*6897da5cSDirk Helbig        try:
72*6897da5cSDirk Helbig            lib.lc3_hr_frame_samples \
73*6897da5cSDirk Helbig                and lib.lc3_hr_frame_block_bytes \
74*6897da5cSDirk Helbig                and lib.lc3_hr_resolve_bitrate \
75*6897da5cSDirk Helbig                and lib.lc3_hr_delay_samples
76*6897da5cSDirk Helbig
77*6897da5cSDirk Helbig        except AttributeError:
78*6897da5cSDirk Helbig
79*6897da5cSDirk Helbig            if self.hrmode:
80*6897da5cSDirk Helbig                raise Exception('High-Resolution interface not available')
81*6897da5cSDirk Helbig
82*6897da5cSDirk Helbig            lib.lc3_hr_frame_samples = \
83*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz: \
84*6897da5cSDirk Helbig                lib.lc3_frame_samples(dt_us, sr_hz)
85*6897da5cSDirk Helbig
86*6897da5cSDirk Helbig            lib.lc3_hr_frame_block_bytes = \
87*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz, nchannels, bitrate: \
88*6897da5cSDirk Helbig                nchannels * lib.lc3_frame_bytes(dt_us, bitrate // 2)
89*6897da5cSDirk Helbig
90*6897da5cSDirk Helbig            lib.lc3_hr_resolve_bitrate = \
91*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz, nbytes: \
92*6897da5cSDirk Helbig                lib.lc3_resolve_bitrate(dt_us, nbytes)
93*6897da5cSDirk Helbig
94*6897da5cSDirk Helbig            lib.lc3_hr_delay_samples = \
95*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz: \
96*6897da5cSDirk Helbig                lib.lc3_delay_samples(dt_us, sr_hz)
97*6897da5cSDirk Helbig
98*6897da5cSDirk Helbig        lib.lc3_hr_frame_samples.argtypes = [c_bool, c_int, c_int]
99*6897da5cSDirk Helbig        lib.lc3_hr_frame_block_bytes.argtypes = \
100*6897da5cSDirk Helbig            [c_bool, c_int, c_int, c_int, c_int]
101*6897da5cSDirk Helbig        lib.lc3_hr_resolve_bitrate.argtypes = [c_bool, c_int, c_int, c_int]
102*6897da5cSDirk Helbig        lib.lc3_hr_delay_samples.argtypes = [c_bool, c_int, c_int]
103*6897da5cSDirk Helbig        self.lib = lib
104*6897da5cSDirk Helbig
105*6897da5cSDirk Helbig        libc = ctypes.cdll.LoadLibrary(find_library("c"))
106*6897da5cSDirk Helbig
107*6897da5cSDirk Helbig        self.malloc = libc.malloc
108*6897da5cSDirk Helbig        self.malloc.argtypes = [c_size_t]
109*6897da5cSDirk Helbig        self.malloc.restype = c_void_p
110*6897da5cSDirk Helbig
111*6897da5cSDirk Helbig        self.free = libc.free
112*6897da5cSDirk Helbig        self.free.argtypes = [c_void_p]
113*6897da5cSDirk Helbig
114*6897da5cSDirk Helbig    def get_frame_samples(self):
115*6897da5cSDirk Helbig        """
116*6897da5cSDirk Helbig        Returns the number of PCM samples in an LC3 frame
117*6897da5cSDirk Helbig        """
118*6897da5cSDirk Helbig        ret = self.lib.lc3_hr_frame_samples(
119*6897da5cSDirk Helbig            self.hrmode, self.dt_us, self.sr_pcm_hz)
120*6897da5cSDirk Helbig        if ret < 0:
121*6897da5cSDirk Helbig            raise ValueError("Bad parameters")
122*6897da5cSDirk Helbig        return ret
123*6897da5cSDirk Helbig
124*6897da5cSDirk Helbig    def get_frame_bytes(self, bitrate):
125*6897da5cSDirk Helbig        """
126*6897da5cSDirk Helbig        Returns the size of LC3 frame blocks, from bitrate in bit per seconds.
127*6897da5cSDirk Helbig        A target `bitrate` equals 0 or `INT32_MAX` returns respectively
128*6897da5cSDirk Helbig        the minimum and maximum allowed size.
129*6897da5cSDirk Helbig        """
130*6897da5cSDirk Helbig        ret = self.lib.lc3_hr_frame_block_bytes(
131*6897da5cSDirk Helbig            self.hrmode, self.dt_us, self.sr_hz, self.nchannels, bitrate)
132*6897da5cSDirk Helbig        if ret < 0:
133*6897da5cSDirk Helbig            raise ValueError("Bad parameters")
134*6897da5cSDirk Helbig        return ret
135*6897da5cSDirk Helbig
136*6897da5cSDirk Helbig    def resolve_bitrate(self, nbytes):
137*6897da5cSDirk Helbig        """
138*6897da5cSDirk Helbig        Returns the bitrate in bits per seconds, from the size of LC3 frames.
139*6897da5cSDirk Helbig        """
140*6897da5cSDirk Helbig        ret = self.lib.lc3_hr_resolve_bitrate(
141*6897da5cSDirk Helbig            self.hrmode, self.dt_us, self.sr_hz, nbytes)
142*6897da5cSDirk Helbig        if ret < 0:
143*6897da5cSDirk Helbig            raise ValueError("Bad parameters")
144*6897da5cSDirk Helbig        return ret
145*6897da5cSDirk Helbig
146*6897da5cSDirk Helbig    def get_delay_samples(self):
147*6897da5cSDirk Helbig        """
148*6897da5cSDirk Helbig         Returns the algorithmic delay, as a number of samples.
149*6897da5cSDirk Helbig         """
150*6897da5cSDirk Helbig        ret = self.lib.lc3_hr_delay_samples(
151*6897da5cSDirk Helbig            self.hrmode, self.dt_us, self.sr_pcm_hz)
152*6897da5cSDirk Helbig        if ret < 0:
153*6897da5cSDirk Helbig            raise ValueError("Bad parameters")
154*6897da5cSDirk Helbig        return ret
155*6897da5cSDirk Helbig
156*6897da5cSDirk Helbig    @staticmethod
157*6897da5cSDirk Helbig    def _resolve_pcm_format(bitdepth):
158*6897da5cSDirk Helbig        PCM_FORMAT_S16 = 0
159*6897da5cSDirk Helbig        PCM_FORMAT_S24 = 1
160*6897da5cSDirk Helbig        PCM_FORMAT_S24_3LE = 2
161*6897da5cSDirk Helbig        PCM_FORMAT_FLOAT = 3
162*6897da5cSDirk Helbig
163*6897da5cSDirk Helbig        match bitdepth:
164*6897da5cSDirk Helbig            case 16: return (PCM_FORMAT_S16, ctypes.c_int16)
165*6897da5cSDirk Helbig            case 24: return (PCM_FORMAT_S24_3LE, 3 * ctypes.c_byte)
166*6897da5cSDirk Helbig            case None: return (PCM_FORMAT_FLOAT, ctypes.c_float)
167*6897da5cSDirk Helbig            case _: raise ValueError("Could not interpret PCM bitdepth")
168*6897da5cSDirk Helbig
169*6897da5cSDirk Helbig
170*6897da5cSDirk Helbigclass Encoder(_Base):
171*6897da5cSDirk Helbig    """
172*6897da5cSDirk Helbig    LC3 Encoder wrapper
173*6897da5cSDirk Helbig
174*6897da5cSDirk Helbig    The `frame_duration` expressed in milliseconds is any of 2.5, 5.0, 7.5
175*6897da5cSDirk Helbig    or 10.0. The `samplerate`, in Hertz, is any of 8000, 16000, 24000, 32000
176*6897da5cSDirk Helbig    or 48000, unless High-Resolution mode is enabled. In High-Resolution mode,
177*6897da5cSDirk Helbig    the `samplerate` is 48000 or 96000.
178*6897da5cSDirk Helbig
179*6897da5cSDirk Helbig    By default, one channel is processed. When `nchannels` is greater than one,
180*6897da5cSDirk Helbig    the PCM input stream is read interleaved and consecutives LC3 frames are
181*6897da5cSDirk Helbig    output, for each channel.
182*6897da5cSDirk Helbig
183*6897da5cSDirk Helbig    Keyword arguments:
184*6897da5cSDirk Helbig        hrmode    : Enable High-Resolution mode, default is `False`.
185*6897da5cSDirk Helbig        sr_pcm_hz : Input PCM samplerate, enable downsampling of input.
186*6897da5cSDirk Helbig        libpath   : LC3 library path and name
187*6897da5cSDirk Helbig    """
188*6897da5cSDirk Helbig
189*6897da5cSDirk Helbig    class c_encoder_t(c_void_p):
190*6897da5cSDirk Helbig        pass
191*6897da5cSDirk Helbig
192*6897da5cSDirk Helbig    def __init__(self, frame_duration, samplerate, nchannels=1, **kwargs):
193*6897da5cSDirk Helbig
194*6897da5cSDirk Helbig        super().__init__(frame_duration, samplerate, nchannels, **kwargs)
195*6897da5cSDirk Helbig
196*6897da5cSDirk Helbig        lib = self.lib
197*6897da5cSDirk Helbig
198*6897da5cSDirk Helbig        try:
199*6897da5cSDirk Helbig            lib.lc3_hr_encoder_size \
200*6897da5cSDirk Helbig                and lib.lc3_hr_setup_encoder
201*6897da5cSDirk Helbig
202*6897da5cSDirk Helbig        except AttributeError:
203*6897da5cSDirk Helbig
204*6897da5cSDirk Helbig            assert not self.hrmode
205*6897da5cSDirk Helbig
206*6897da5cSDirk Helbig            lib.lc3_hr_encoder_size = \
207*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz: \
208*6897da5cSDirk Helbig                lib.lc3_encoder_size(dt_us, sr_hz)
209*6897da5cSDirk Helbig
210*6897da5cSDirk Helbig            lib.lc3_hr_setup_encoder = \
211*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz, sr_pcm_hz, mem: \
212*6897da5cSDirk Helbig                lib.lc3_setup_encoder(dt_us, sr_hz, sr_pcm_hz, mem)
213*6897da5cSDirk Helbig
214*6897da5cSDirk Helbig        lib.lc3_hr_encoder_size.argtypes = [c_bool, c_int, c_int]
215*6897da5cSDirk Helbig        lib.lc3_hr_encoder_size.restype = c_uint
216*6897da5cSDirk Helbig
217*6897da5cSDirk Helbig        lib.lc3_hr_setup_encoder.argtypes = \
218*6897da5cSDirk Helbig            [c_bool, c_int, c_int, c_int, c_void_p]
219*6897da5cSDirk Helbig        lib.lc3_hr_setup_encoder.restype = self.c_encoder_t
220*6897da5cSDirk Helbig
221*6897da5cSDirk Helbig        lib.lc3_encode.argtypes = \
222*6897da5cSDirk Helbig            [self.c_encoder_t, c_int, c_void_p, c_int, c_int, c_void_p]
223*6897da5cSDirk Helbig
224*6897da5cSDirk Helbig        def new_encoder(): return lib.lc3_hr_setup_encoder(
225*6897da5cSDirk Helbig            self.hrmode, self.dt_us, self.sr_hz, self.sr_pcm_hz,
226*6897da5cSDirk Helbig            self.malloc(lib.lc3_hr_encoder_size(
227*6897da5cSDirk Helbig                self.hrmode, self.dt_us, self.sr_pcm_hz)))
228*6897da5cSDirk Helbig
229*6897da5cSDirk Helbig        self.__encoders = [new_encoder() for i in range(nchannels)]
230*6897da5cSDirk Helbig
231*6897da5cSDirk Helbig    def __del__(self):
232*6897da5cSDirk Helbig
233*6897da5cSDirk Helbig        try:
234*6897da5cSDirk Helbig            (self.free(encoder) for encoder in self.__encoders)
235*6897da5cSDirk Helbig        finally:
236*6897da5cSDirk Helbig            return
237*6897da5cSDirk Helbig
238*6897da5cSDirk Helbig    def encode(self, pcm, nbytes, bitdepth=None):
239*6897da5cSDirk Helbig        """
240*6897da5cSDirk Helbig        Encode LC3 frame(s), for each channel.
241*6897da5cSDirk Helbig
242*6897da5cSDirk Helbig        The `pcm` input is given in two ways. When no `bitdepth` is defined,
243*6897da5cSDirk Helbig        it's a vector of floating point values from -1 to 1, coding the sample
244*6897da5cSDirk Helbig        levels. When `bitdepth` is defined, `pcm` is interpreted as a byte-like
245*6897da5cSDirk Helbig        object, each sample coded on `bitdepth` bits (16 or 24).
246*6897da5cSDirk Helbig        The machine endianness, or little endian, is used for 16 or 24 bits
247*6897da5cSDirk Helbig        width, respectively.
248*6897da5cSDirk Helbig        In both cases, the `pcm` vector data is padded with zeros when
249*6897da5cSDirk Helbig        its length is less than the required input samples for the encoder.
250*6897da5cSDirk Helbig        Channels concatenation of encoded LC3 frames, of `nbytes`, is returned.
251*6897da5cSDirk Helbig        """
252*6897da5cSDirk Helbig
253*6897da5cSDirk Helbig        nchannels = self.nchannels
254*6897da5cSDirk Helbig        frame_samples = self.get_frame_samples()
255*6897da5cSDirk Helbig
256*6897da5cSDirk Helbig        (pcm_fmt, pcm_t) = self._resolve_pcm_format(bitdepth)
257*6897da5cSDirk Helbig        pcm_len = nchannels * frame_samples
258*6897da5cSDirk Helbig
259*6897da5cSDirk Helbig        if bitdepth is None:
260*6897da5cSDirk Helbig            pcm_buffer = array.array('f', pcm)
261*6897da5cSDirk Helbig
262*6897da5cSDirk Helbig            # Invert test to catch NaN
263*6897da5cSDirk Helbig            if not abs(sum(pcm)) / frame_samples < 2:
264*6897da5cSDirk Helbig                raise ValueError("Out of range PCM input")
265*6897da5cSDirk Helbig
266*6897da5cSDirk Helbig            padding = max(pcm_len - frame_samples, 0)
267*6897da5cSDirk Helbig            pcm_buffer.extend(array.array('f', [0] * padding))
268*6897da5cSDirk Helbig
269*6897da5cSDirk Helbig        else:
270*6897da5cSDirk Helbig            padding = max(pcm_len * ctypes.sizeof(pcm_t) - len(pcm), 0)
271*6897da5cSDirk Helbig            pcm_buffer = bytearray(pcm) + bytearray(padding)
272*6897da5cSDirk Helbig
273*6897da5cSDirk Helbig        data_buffer = (c_byte * nbytes)()
274*6897da5cSDirk Helbig        data_offset = 0
275*6897da5cSDirk Helbig
276*6897da5cSDirk Helbig        for (ich, encoder) in enumerate(self.__encoders):
277*6897da5cSDirk Helbig
278*6897da5cSDirk Helbig            pcm_offset = ich * ctypes.sizeof(pcm_t)
279*6897da5cSDirk Helbig            pcm = (pcm_t * (pcm_len - ich)).from_buffer(pcm_buffer, pcm_offset)
280*6897da5cSDirk Helbig
281*6897da5cSDirk Helbig            data_size = nbytes // nchannels + int(ich < nbytes % nchannels)
282*6897da5cSDirk Helbig            data = (c_byte * data_size).from_buffer(data_buffer, data_offset)
283*6897da5cSDirk Helbig            data_offset += data_size
284*6897da5cSDirk Helbig
285*6897da5cSDirk Helbig            ret = self.lib.lc3_encode(
286*6897da5cSDirk Helbig                encoder, pcm_fmt, pcm, nchannels, len(data), data)
287*6897da5cSDirk Helbig            if ret < 0:
288*6897da5cSDirk Helbig                raise ValueError("Bad parameters")
289*6897da5cSDirk Helbig
290*6897da5cSDirk Helbig        return bytes(data_buffer)
291*6897da5cSDirk Helbig
292*6897da5cSDirk Helbig
293*6897da5cSDirk Helbigclass Decoder(_Base):
294*6897da5cSDirk Helbig    """
295*6897da5cSDirk Helbig    LC3 Decoder wrapper
296*6897da5cSDirk Helbig
297*6897da5cSDirk Helbig    The `frame_duration` expressed in milliseconds is any of 2.5, 5.0, 7.5
298*6897da5cSDirk Helbig    or 10.0. The `samplerate`, in Hertz, is any of 8000, 16000, 24000, 32000
299*6897da5cSDirk Helbig    or 48000, unless High-Resolution mode is enabled. In High-Resolution
300*6897da5cSDirk Helbig    mode, the `samplerate` is 48000 or 96000.
301*6897da5cSDirk Helbig
302*6897da5cSDirk Helbig    By default, one channel is processed. When `nchannels` is greater than one,
303*6897da5cSDirk Helbig    the PCM input stream is read interleaved and consecutives LC3 frames are
304*6897da5cSDirk Helbig    output, for each channel.
305*6897da5cSDirk Helbig
306*6897da5cSDirk Helbig    Keyword arguments:
307*6897da5cSDirk Helbig        hrmode    : Enable High-Resolution mode, default is `False`.
308*6897da5cSDirk Helbig        sr_pcm_hz : Output PCM samplerate, enable upsampling of output.
309*6897da5cSDirk Helbig        libpath   : LC3 library path and name
310*6897da5cSDirk Helbig    """
311*6897da5cSDirk Helbig
312*6897da5cSDirk Helbig    class c_decoder_t(c_void_p):
313*6897da5cSDirk Helbig        pass
314*6897da5cSDirk Helbig
315*6897da5cSDirk Helbig    def __init__(self, frame_duration, samplerate, nchannels=1, **kwargs):
316*6897da5cSDirk Helbig
317*6897da5cSDirk Helbig        super().__init__(frame_duration, samplerate, nchannels, **kwargs)
318*6897da5cSDirk Helbig
319*6897da5cSDirk Helbig        lib = self.lib
320*6897da5cSDirk Helbig
321*6897da5cSDirk Helbig        try:
322*6897da5cSDirk Helbig            lib.lc3_hr_decoder_size \
323*6897da5cSDirk Helbig                and lib.lc3_hr_setup_decoder
324*6897da5cSDirk Helbig
325*6897da5cSDirk Helbig        except AttributeError:
326*6897da5cSDirk Helbig
327*6897da5cSDirk Helbig            assert not self.hrmode
328*6897da5cSDirk Helbig
329*6897da5cSDirk Helbig            lib.lc3_hr_decoder_size = \
330*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz: \
331*6897da5cSDirk Helbig                lib.lc3_decoder_size(dt_us, sr_hz)
332*6897da5cSDirk Helbig
333*6897da5cSDirk Helbig            lib.lc3_hr_setup_decoder = \
334*6897da5cSDirk Helbig                lambda hrmode, dt_us, sr_hz, sr_pcm_hz, mem: \
335*6897da5cSDirk Helbig                lib.lc3_setup_decoder(dt_us, sr_hz, sr_pcm_hz, mem)
336*6897da5cSDirk Helbig
337*6897da5cSDirk Helbig        lib.lc3_hr_decoder_size.argtypes = [c_bool, c_int, c_int]
338*6897da5cSDirk Helbig        lib.lc3_hr_decoder_size.restype = c_uint
339*6897da5cSDirk Helbig
340*6897da5cSDirk Helbig        lib.lc3_hr_setup_decoder.argtypes = \
341*6897da5cSDirk Helbig            [c_bool, c_int, c_int, c_int, c_void_p]
342*6897da5cSDirk Helbig        lib.lc3_hr_setup_decoder.restype = self.c_decoder_t
343*6897da5cSDirk Helbig
344*6897da5cSDirk Helbig        lib.lc3_decode.argtypes = \
345*6897da5cSDirk Helbig            [self.c_decoder_t, c_void_p, c_int, c_int, c_void_p, c_int]
346*6897da5cSDirk Helbig
347*6897da5cSDirk Helbig        def new_decoder(): return lib.lc3_hr_setup_decoder(
348*6897da5cSDirk Helbig            self.hrmode, self.dt_us, self.sr_hz, self.sr_pcm_hz,
349*6897da5cSDirk Helbig            self.malloc(lib.lc3_hr_decoder_size(
350*6897da5cSDirk Helbig                self.hrmode, self.dt_us, self.sr_pcm_hz)))
351*6897da5cSDirk Helbig
352*6897da5cSDirk Helbig        self.__decoders = [new_decoder() for i in range(nchannels)]
353*6897da5cSDirk Helbig
354*6897da5cSDirk Helbig    def __del__(self):
355*6897da5cSDirk Helbig
356*6897da5cSDirk Helbig        try:
357*6897da5cSDirk Helbig            (self.free(decoder) for decoder in self.__decoders)
358*6897da5cSDirk Helbig        finally:
359*6897da5cSDirk Helbig            return
360*6897da5cSDirk Helbig
361*6897da5cSDirk Helbig    def decode(self, data, bitdepth=None):
362*6897da5cSDirk Helbig        """
363*6897da5cSDirk Helbig        Decode an LC3 frame
364*6897da5cSDirk Helbig
365*6897da5cSDirk Helbig        The input `data` is the channels concatenation of LC3 frames in a
366*6897da5cSDirk Helbig        byte-like object. Interleaved PCM samples are returned according to
367*6897da5cSDirk Helbig        the `bitdepth` indication.
368*6897da5cSDirk Helbig        When no `bitdepth` is defined, it's a vector of floating point values
369*6897da5cSDirk Helbig        from -1 to 1, coding the sample levels. When `bitdepth` is defined,
370*6897da5cSDirk Helbig        it returns a byte array, each sample coded on `bitdepth` bits.
371*6897da5cSDirk Helbig        The machine endianness, or little endian, is used for 16 or 24 bits
372*6897da5cSDirk Helbig        width, respectively.
373*6897da5cSDirk Helbig        """
374*6897da5cSDirk Helbig
375*6897da5cSDirk Helbig        nchannels = self.nchannels
376*6897da5cSDirk Helbig        frame_samples = self.get_frame_samples()
377*6897da5cSDirk Helbig
378*6897da5cSDirk Helbig        (pcm_fmt, pcm_t) = self._resolve_pcm_format(bitdepth)
379*6897da5cSDirk Helbig        pcm_len = nchannels * self.get_frame_samples()
380*6897da5cSDirk Helbig        pcm_buffer = (pcm_t * pcm_len)()
381*6897da5cSDirk Helbig
382*6897da5cSDirk Helbig        data_buffer = bytearray(data)
383*6897da5cSDirk Helbig        data_offset = 0
384*6897da5cSDirk Helbig
385*6897da5cSDirk Helbig        for (ich, decoder) in enumerate(self.__decoders):
386*6897da5cSDirk Helbig            pcm_offset = ich * ctypes.sizeof(pcm_t)
387*6897da5cSDirk Helbig            pcm = (pcm_t * (pcm_len - ich)).from_buffer(pcm_buffer, pcm_offset)
388*6897da5cSDirk Helbig
389*6897da5cSDirk Helbig            data_size = len(data_buffer) // nchannels + \
390*6897da5cSDirk Helbig                int(ich < len(data_buffer) % nchannels)
391*6897da5cSDirk Helbig            data = (c_byte * data_size).from_buffer(data_buffer, data_offset)
392*6897da5cSDirk Helbig            data_offset += data_size
393*6897da5cSDirk Helbig
394*6897da5cSDirk Helbig            ret = self.lib.lc3_decode(
395*6897da5cSDirk Helbig                decoder, data, len(data), pcm_fmt, pcm, self.nchannels)
396*6897da5cSDirk Helbig            if ret < 0:
397*6897da5cSDirk Helbig                raise ValueError("Bad parameters")
398*6897da5cSDirk Helbig
399*6897da5cSDirk Helbig        return array.array('f', pcm_buffer) \
400*6897da5cSDirk Helbig            if bitdepth is None else bytes(pcm_buffer)
401