Commit 83a24ef7 authored by Armando Visconti's avatar Armando Visconti Committed by Maureen Helm
Browse files

ext: Added OpenPDM2PCM ST external library

This library, written by STMicroelectronics, is used to convert an
audio stream from PDM format to PCM format through a signal filtering
and decimation.

Library is located in ext/hal/st/lib/audio.

Origin: ST Microelectronics
License: Apache 2.0
URL: https://os.mbed.com/


Commit: 25:f2c04f757003
Purpose: reconstruct the audio signal produced by ST MEMS microphone
Maintained-by: External

Signed-off-by: default avatarArmando Visconti <armando.visconti@st.com>
parent fff660ce
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -17,5 +17,8 @@ if(CONFIG_HAS_STLIB)
    zephyr_sources(sensor/vl53l0x/api/core/src/vl53l0x_api_core.c)
    zephyr_sources(sensor/vl53l0x/api/core/src/vl53l0x_api_strings.c)
  endif()
  if(CONFIG_AUDIO_MPXXDTYY)
    zephyr_include_directories(audio/microphone)
    zephyr_sources(audio/microphone/OpenPDMFilter.c)
  endif()
endif()
+313 −0
Original line number Diff line number Diff line
/**
 *******************************************************************************
 * @file    OpenPDMFilter.c
 * @author  CL
 * @version V1.0.0
 * @date    9-September-2015
 * @brief   Open PDM audio software decoding Library.   
 *          This Library is used to decode and reconstruct the audio signal
 *          produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx). 
 *******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2018 STMicroelectronics</center></h2>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************
 */


/* Includes ------------------------------------------------------------------*/

#include "OpenPDMFilter.h"


/* Variables -----------------------------------------------------------------*/

uint32_t div_const = 0;
int64_t sub_const = 0;
uint32_t sinc[DECIMATION_MAX * SINCN];
uint32_t sinc1[DECIMATION_MAX];
uint32_t sinc2[DECIMATION_MAX * 2];
uint32_t coef[SINCN][DECIMATION_MAX];
#ifdef USE_LUT
int32_t lut[256][DECIMATION_MAX / 8][SINCN];
#endif


/* Functions -----------------------------------------------------------------*/

#ifdef USE_LUT
int32_t filter_table_mono_64(uint8_t *data, uint8_t sincn)
{
  return (int32_t)
    lut[data[0]][0][sincn] +
    lut[data[1]][1][sincn] +
    lut[data[2]][2][sincn] +
    lut[data[3]][3][sincn] +
    lut[data[4]][4][sincn] +
    lut[data[5]][5][sincn] +
    lut[data[6]][6][sincn] +
    lut[data[7]][7][sincn];
}
int32_t filter_table_stereo_64(uint8_t *data, uint8_t sincn)
{
  return (int32_t)
    lut[data[0]][0][sincn] +
    lut[data[2]][1][sincn] +
    lut[data[4]][2][sincn] +
    lut[data[6]][3][sincn] +
    lut[data[8]][4][sincn] +
    lut[data[10]][5][sincn] +
    lut[data[12]][6][sincn] +
    lut[data[14]][7][sincn];
}
int32_t filter_table_mono_128(uint8_t *data, uint8_t sincn)
{
  return (int32_t)
    lut[data[0]][0][sincn] +
    lut[data[1]][1][sincn] +
    lut[data[2]][2][sincn] +
    lut[data[3]][3][sincn] +
    lut[data[4]][4][sincn] +
    lut[data[5]][5][sincn] +
    lut[data[6]][6][sincn] +
    lut[data[7]][7][sincn] +
    lut[data[8]][8][sincn] +
    lut[data[9]][9][sincn] +
    lut[data[10]][10][sincn] +
    lut[data[11]][11][sincn] +
    lut[data[12]][12][sincn] +
    lut[data[13]][13][sincn] +
    lut[data[14]][14][sincn] +
    lut[data[15]][15][sincn];
}
int32_t filter_table_stereo_128(uint8_t *data, uint8_t sincn)
{
  return (int32_t)
    lut[data[0]][0][sincn] +
    lut[data[2]][1][sincn] +
    lut[data[4]][2][sincn] +
    lut[data[6]][3][sincn] +
    lut[data[8]][4][sincn] +
    lut[data[10]][5][sincn] +
    lut[data[12]][6][sincn] +
    lut[data[14]][7][sincn] +
    lut[data[16]][8][sincn] +
    lut[data[18]][9][sincn] +
    lut[data[20]][10][sincn] +
    lut[data[22]][11][sincn] +
    lut[data[24]][12][sincn] +
    lut[data[26]][13][sincn] +
    lut[data[28]][14][sincn] +
    lut[data[30]][15][sincn];
}
int32_t (* filter_tables_64[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_64, filter_table_stereo_64};
int32_t (* filter_tables_128[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_128, filter_table_stereo_128};
#else
int32_t filter_table(uint8_t *data, uint8_t sincn, TPDMFilter_InitStruct *param)
{
  uint8_t c, i;
  uint16_t data_index = 0;
  uint32_t *coef_p = &coef[sincn][0];
  int32_t F = 0;
  uint8_t decimation = param->Decimation;
  uint8_t channels = param->In_MicChannels;

  for (i = 0; i < decimation; i += 8) {
    c = data[data_index];
    F += ((c >> 7)       ) * coef_p[i    ] +
         ((c >> 6) & 0x01) * coef_p[i + 1] +
         ((c >> 5) & 0x01) * coef_p[i + 2] +
         ((c >> 4) & 0x01) * coef_p[i + 3] +
         ((c >> 3) & 0x01) * coef_p[i + 4] +
         ((c >> 2) & 0x01) * coef_p[i + 5] +
         ((c >> 1) & 0x01) * coef_p[i + 6] +
         ((c     ) & 0x01) * coef_p[i + 7];
    data_index += channels;
  }
  return F;
}
#endif

void convolve(uint32_t Signal[/* SignalLen */], unsigned short SignalLen,
              uint32_t Kernel[/* KernelLen */], unsigned short KernelLen,
              uint32_t Result[/* SignalLen + KernelLen - 1 */])
{
  uint16_t n;

  for (n = 0; n < SignalLen + KernelLen - 1; n++)
  {
    unsigned short kmin, kmax, k;
    
    Result[n] = 0;
    
    kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
    kmax = (n < SignalLen - 1) ? n : SignalLen - 1;
    
    for (k = kmin; k <= kmax; k++) {
      Result[n] += Signal[k] * Kernel[n - k];
    }
  }
}

void Open_PDM_Filter_Init(TPDMFilter_InitStruct *Param)
{
  uint16_t i, j;
  int64_t sum = 0;

  uint8_t decimation = Param->Decimation;

  for (i = 0; i < SINCN; i++) {
    Param->Coef[i] = 0;
    Param->bit[i] = 0;
  }
  for (i = 0; i < decimation; i++) {
    sinc1[i] = 1;
  }

  Param->OldOut = Param->OldIn = Param->OldZ = 0;
  Param->LP_ALFA = (Param->LP_HZ != 0 ? (uint16_t) (Param->LP_HZ * 256 / (Param->LP_HZ + Param->Fs / (2 * 3.14159))) : 0);
  Param->HP_ALFA = (Param->HP_HZ != 0 ? (uint16_t) (Param->Fs * 256 / (2 * 3.14159 * Param->HP_HZ + Param->Fs)) : 0);

  Param->FilterLen = decimation * SINCN;       
  sinc[0] = 0;
  sinc[decimation * SINCN - 1] = 0;      
  convolve(sinc1, decimation, sinc1, decimation, sinc2);
  convolve(sinc2, decimation * 2 - 1, sinc1, decimation, &sinc[1]);     
  for(j = 0; j < SINCN; j++) {
    for (i = 0; i < decimation; i++) {
      coef[j][i] = sinc[j * decimation + i];
      sum += sinc[j * decimation + i];
    }
  }

  sub_const = sum >> 1;
  div_const = sub_const * Param->MaxVolume / 32768 / FILTER_GAIN;
  div_const = (div_const == 0 ? 1 : div_const);

#ifdef USE_LUT
  /* Look-Up Table. */
  uint16_t c, d, s;
  for (s = 0; s < SINCN; s++)
  {
    uint32_t *coef_p = &coef[s][0];
    for (c = 0; c < 256; c++)
      for (d = 0; d < decimation / 8; d++)
        lut[c][d][s] = ((c >> 7)       ) * coef_p[d * 8    ] +
                       ((c >> 6) & 0x01) * coef_p[d * 8 + 1] +
                       ((c >> 5) & 0x01) * coef_p[d * 8 + 2] +
                       ((c >> 4) & 0x01) * coef_p[d * 8 + 3] +
                       ((c >> 3) & 0x01) * coef_p[d * 8 + 4] +
                       ((c >> 2) & 0x01) * coef_p[d * 8 + 5] +
                       ((c >> 1) & 0x01) * coef_p[d * 8 + 6] +
                       ((c     ) & 0x01) * coef_p[d * 8 + 7];
  }
#endif
}

void Open_PDM_Filter_64(uint8_t* data, uint16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
{
  uint8_t i, data_out_index;
  uint8_t channels = Param->In_MicChannels;
  uint8_t data_inc = ((DECIMATION_MAX >> 4) * channels);
  int64_t Z, Z0, Z1, Z2;
  int64_t OldOut, OldIn, OldZ;

  OldOut = Param->OldOut;
  OldIn = Param->OldIn;
  OldZ = Param->OldZ;

#ifdef USE_LUT
  uint8_t j = channels - 1;
#endif

  for (i = 0, data_out_index = 0; i < Param->Fs / 1000; i++, data_out_index += channels) {
#ifdef USE_LUT
    Z0 = filter_tables_64[j](data, 0);
    Z1 = filter_tables_64[j](data, 1);
    Z2 = filter_tables_64[j](data, 2);
#else
    Z0 = filter_table(data, 0, Param);
    Z1 = filter_table(data, 1, Param);
    Z2 = filter_table(data, 2, Param);
#endif

    Z = Param->Coef[1] + Z2 - sub_const;
    Param->Coef[1] = Param->Coef[0] + Z1;
    Param->Coef[0] = Z0;

    OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
    OldIn = Z;
    OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;

    Z = OldZ * volume;
    Z = RoundDiv(Z, div_const);
    Z = SaturaLH(Z, -32700, 32700);

    dataOut[data_out_index] = Z;
    data += data_inc;
  }

  Param->OldOut = OldOut;
  Param->OldIn = OldIn;
  Param->OldZ = OldZ;
}

void Open_PDM_Filter_128(uint8_t* data, uint16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
{
  uint8_t i, data_out_index;
  uint8_t channels = Param->In_MicChannels;
  uint8_t data_inc = ((DECIMATION_MAX >> 3) * channels);
  int64_t Z, Z0, Z1, Z2;
  int64_t OldOut, OldIn, OldZ;

  OldOut = Param->OldOut;
  OldIn = Param->OldIn;
  OldZ = Param->OldZ;

#ifdef USE_LUT
  uint8_t j = channels - 1;
#endif

  for (i = 0, data_out_index = 0; i < Param->Fs / 1000; i++, data_out_index += channels) {
#ifdef USE_LUT
    Z0 = filter_tables_128[j](data, 0);
    Z1 = filter_tables_128[j](data, 1);
    Z2 = filter_tables_128[j](data, 2);
#else
    Z0 = filter_table(data, 0, Param);
    Z1 = filter_table(data, 1, Param);
    Z2 = filter_table(data, 2, Param);
#endif

    Z = Param->Coef[1] + Z2 - sub_const;
    Param->Coef[1] = Param->Coef[0] + Z1;
    Param->Coef[0] = Z0;

    OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
    OldIn = Z;
    OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;

    Z = OldZ * volume;
    Z = RoundDiv(Z, div_const);
    Z = SaturaLH(Z, -32700, 32700);

    dataOut[data_out_index] = Z;
    data += data_inc;
  }

  Param->OldOut = OldOut;
  Param->OldIn = OldIn;
  Param->OldZ = OldZ;
}
+98 −0
Original line number Diff line number Diff line
/**
 *******************************************************************************
 * @file    OpenPDMFilter.h
 * @author  CL
 * @version V1.0.0
 * @date    9-September-2015
 * @brief   Header file for Open PDM audio software decoding Library.   
 *          This Library is used to decode and reconstruct the audio signal
 *          produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx). 
 *******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2018 STMicroelectronics</center></h2>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************
 */


/* Define to prevent recursive inclusion -------------------------------------*/

#ifndef __OPENPDMFILTER_H
#define __OPENPDMFILTER_H

#ifdef __cplusplus
  extern "C" {
#endif


/* Includes ------------------------------------------------------------------*/

#include <stdint.h>


/* Definitions ---------------------------------------------------------------*/

/*
 * Enable to use a Look-Up Table to improve performances while using more FLASH
 * and RAM memory.
 * Note: Without Look-Up Table up to stereo@16KHz configuration is supported.
 */
#define USE_LUT

#define SINCN            3
#define DECIMATION_MAX 128
#define FILTER_GAIN     16

#define HTONS(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
                 (((uint16_t)(A) & 0x00ff) << 8))
#define RoundDiv(a, b)    (((a)>0)?(((a)+(b)/2)/(b)):(((a)-(b)/2)/(b)))
#define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N)))


/* Types ---------------------------------------------------------------------*/

typedef struct {
  /* Public */
  float LP_HZ;
  float HP_HZ;
  uint16_t Fs;
  uint8_t In_MicChannels;
  uint8_t Out_MicChannels;
  uint8_t Decimation;
  uint8_t MaxVolume;
  /* Private */
  uint32_t Coef[SINCN];
  uint16_t FilterLen;
  int64_t OldOut, OldIn, OldZ;
  uint16_t LP_ALFA;
  uint16_t HP_ALFA;
  uint16_t bit[5];
  uint16_t byte;
} TPDMFilter_InitStruct;


/* Exported functions ------------------------------------------------------- */

void Open_PDM_Filter_Init(TPDMFilter_InitStruct *init_struct);
void Open_PDM_Filter_64(uint8_t* data, uint16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct);
void Open_PDM_Filter_128(uint8_t* data, uint16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct);

#ifdef __cplusplus
}
#endif

#endif // __OPENPDMFILTER_H

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+45 −0
Original line number Diff line number Diff line
OpenPDM2PCM
###########

Origin:
   Arm Mbed OS
   https://os.mbed.com/teams/ST/code/X_NUCLEO_CCA02M1/Middlewares/OpenPDM2PCM/

Status:
   version 1.0.0

Purpose:
   This Library is used to decode and reconstruct the audio signal
   produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).

Description:
   This library, written by STMicroelectronics, is used to convert an audio
   stream from PDM format to PCM format through a signal filtering and
   decimation.

   Library APIs:
      - Open_PDM_Filter_Init()    /* Init the OpenPDM2PCM library */
      - Open_PDM_Filter_64()      /* Filter and decimate stream by 64 */
      - Open_PDM_Filter_128()     /* Filter and decimate stream by 128 */


Dependencies:
   This library depends by Zephyr mpxxdtyy driver and is linked statically.
   This library will be used by a standard Zephyr microphone driver
   (./drivers/audio/).

URL:
   https://os.mbed.com/teams/ST/code/X_NUCLEO_CCA02M1/Middlewares/OpenPDM2PCM/

commit:
   25:f2c04f757003

Maintained-by:
   External

License:
   Apache 2.0

License Link:
   https://www.apache.org/licenses/LICENSE-2.0