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