1########################################### 2# Project: CMSIS DSP Library 3# Title: mfccdata.py 4# Description: Generation of MFCC arays for the MFCC C init function 5# 6# $Date: 07 September 2021 7# $Revision: V1.10.0 8# 9# Target Processor: Cortex-M and Cortex-A cores 10# -------------------------------------------------------------------- */ 11# 12# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. 13# 14# SPDX-License-Identifier: Apache-2.0 15# 16# Licensed under the Apache License, Version 2.0 (the License); you may 17# not use this file except in compliance with the License. 18# You may obtain a copy of the License at 19# 20# www.apache.org/licenses/LICENSE-2.0 21# 22# Unless required by applicable law or agreed to in writing, software 23# distributed under the License is distributed on an AS IS BASIS, WITHOUT 24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 25# See the License for the specific language governing permissions and 26# limitations under the License. 27############################################ 28import numpy as np 29from jinja2 import Environment, PackageLoader, select_autoescape,FileSystemLoader 30import os.path 31import struct 32import scipy.signal as sig 33 34def to_q31(v): 35 r = int(round(v * 2**31)) 36 if (r > 0x07FFFFFFF): 37 r = 0x07FFFFFFF 38 if (r < -0x080000000): 39 r = -0x080000000 40 return ("0x%s" % format(struct.unpack('<I', struct.pack('<i', r))[0],'08X')) 41 42def to_q15(v): 43 r = int(round(v * 2**15)) 44 if (r > 0x07FFF): 45 r = 0x07FFF 46 if (r < -0x08000): 47 r = -0x08000 48 return ("0x%s" % format(struct.unpack('<H', struct.pack('<h', r))[0],'04X')) 49 50def to_f16(v): 51 return("(float16_t)%ff" % struct.unpack('<f',struct.pack('<f',v))) 52 53def to_f32(v): 54 return("%ff" % struct.unpack('<f',struct.pack('<f',v))) 55 56class ConvertArray: 57 def __init__(self,theType): 58 self._cvt = lambda x : x 59 if theType=="f32": 60 self._cvt = to_f32 61 if theType=="f16": 62 self._cvt = to_f16 63 if theType=="q31": 64 self._cvt = to_q31 65 if theType=="q15": 66 self._cvt = to_q15 67 68 def getArrayContent(self,samples): 69 nb = 0 70 res="" 71 res += "{\n" 72 for sample in samples: 73 res += str(self._cvt(sample)) 74 res += "," 75 nb = nb + 1 76 if nb == 10: 77 res += "\n" 78 nb = 0 79 res += "}" 80 return(res) 81 82 83 84def frequencyToMelSpace(freq): 85 return 1127.0 * np.log(1.0 + freq / 700.0) 86 87def melSpaceToFrequency(mels): 88 return 700.0 * (np.exp(mels / 1127.0) - 1.0) 89 90def melFilterMatrix(fmin, fmax, numOfMelFilters,fs,FFTSize): 91 92 filters = np.zeros((numOfMelFilters,int(FFTSize/2+1))) 93 zeros = np.zeros(int(FFTSize // 2 )) 94 95 96 fmin_mel = frequencyToMelSpace(fmin) 97 fmax_mel = frequencyToMelSpace(fmax) 98 mels = np.linspace(fmin_mel, fmax_mel, num=numOfMelFilters+2) 99 100 101 linearfreqs = np.linspace( 0, fs/2.0, int(FFTSize // 2 + 1) ) 102 spectrogrammels = frequencyToMelSpace(linearfreqs)[1:] 103 104 105 filtPos=[] 106 filtLen=[] 107 totalLen = 0 108 packedFilters = [] 109 for n in range(numOfMelFilters): 110 111 112 upper = (spectrogrammels - mels[n])/(mels[n+1]-mels[n]) 113 lower = (mels[n+2] - spectrogrammels)/(mels[n+2]-mels[n+1]) 114 115 116 filters[n, :] = np.hstack([0,np.maximum(zeros,np.minimum(upper,lower))]) 117 nb = 0 118 startFound = False 119 for sample in filters[n, :]: 120 if not startFound and sample != 0.0: 121 startFound = True 122 startPos = nb 123 124 if startFound and sample == 0.0: 125 endPos = nb - 1 126 break 127 nb = nb + 1 128 filtLen.append(endPos - startPos+1) 129 totalLen += endPos - startPos + 1 130 filtPos.append(startPos) 131 packedFilters += list(filters[n, startPos:endPos+1]) 132 133 return filtLen,filtPos,totalLen,packedFilters,filters 134 135 136def dctMatrix(numOfDctOutputs, numOfMelFilters): 137 138 result = np.zeros((numOfDctOutputs,numOfMelFilters)) 139 s=(np.linspace(1,numOfMelFilters,numOfMelFilters) - 0.5)/numOfMelFilters 140 141 for i in range(0, numOfDctOutputs): 142 result[i,:]=np.cos(i * np.pi*s) * np.sqrt(2.0/numOfMelFilters) 143 144 return result 145 146 147def ctype(s): 148 if s == "f64": 149 return("float64_t") 150 if s == "f32": 151 return("float32_t") 152 if s == "f16": 153 return("float16_t") 154 if s == "q31": 155 return("q31_t") 156 if s == "q15": 157 return("q15_t") 158 159def typeext(s): 160 if s == "f64": 161 return("_f64") 162 if s == "f32": 163 return("_f32") 164 if s == "f16": 165 return("_f16") 166 if s == "q31": 167 return("_q31") 168 if s == "q15": 169 return("_q15") 170 171def prepareWindowConfig(configs): 172 # sig.hamming(FFTSize, sym=False) 173 for config in configs: 174 c=configs[config] 175 if c["win"] == "hamming": 176 win = sig.hamming(c["fftlength"], sym=False) 177 if c["win"] == "hanning": 178 win = sig.hann(c["fftlength"], sym=False) 179 180 cvt=ConvertArray(c["type"]) 181 c["ctype"]=ctype(c["type"]) 182 c["ext"]=typeext(c["type"]) 183 184 c["winSamples"] = cvt.getArrayContent(win) 185 186def prepareMelconfig(configs): 187 for config in configs: 188 c=configs[config] 189 190 cvt=ConvertArray(c["type"]) 191 cvtInt=ConvertArray(None) 192 c["ctype"]=ctype(c["type"]) 193 c["ext"]=typeext(c["type"]) 194 195 filtLen,filtPos,totalLen,packedFilters,filters = melFilterMatrix(c["fmin"], c["fmax"], c["melFilters"],c["samplingRate"],c["fftlength"]) 196 197 c["filtLenArray"]=cvtInt.getArrayContent(filtLen) 198 c["filtPosArray"]=cvtInt.getArrayContent(filtPos) 199 c["totalLen"]=totalLen 200 c["filters"]=cvt.getArrayContent(packedFilters) 201 202def prepareDctconfig(configs): 203 for config in configs: 204 c=configs[config] 205 206 cvt=ConvertArray(c["type"]) 207 c["ctype"]=ctype(c["type"]) 208 c["ext"]=typeext(c["type"]) 209 c["dctMatrixLength"]=c["dctOutputs"] * c["melFilters"] 210 211 dctMat = dctMatrix(c["dctOutputs"],c["melFilters"]) 212 dctMat=dctMat.reshape(c["dctMatrixLength"]) 213 c["dctMatrix"]=cvt.getArrayContent(dctMat) 214 215 #print(configs) 216 217def checkF16(configs): 218 hasF16 = False 219 for config in configs["dct"]: 220 c=configs["dct"][config] 221 if c["type"]=="f16": 222 hasF16 = True 223 c["hasF16"]=True 224 225 for config in configs["melfilter"]: 226 c=configs["melfilter"][config] 227 if c["type"]=="f16": 228 hasF16 = True 229 c["hasF16"]=True 230 231 for config in configs["window"]: 232 c=configs["window"][config] 233 if c["type"]=="f16": 234 hasF16 = True 235 c["hasF16"]=True 236 237 configs["hasF16"]=hasF16 238 239env = Environment( 240 # For 3.0 version of jinja2, replace with 241 # loader=PackageLoader("mfcctemplates",""), 242 loader=PackageLoader("mfccdata","mfcctemplates"), 243 autoescape=select_autoescape(), 244 trim_blocks=True 245 ) 246 247ctemplate = env.get_template("mfccdata.c") 248htemplate = env.get_template("mfccdata.h") 249 250 251def genMfccHeader(f,configs,filename): 252 print(htemplate.render(configs=configs,filename=filename),file=f) 253 254def genMfccInit(f,configs,filename): 255 print(ctemplate.render(configs=configs,filename=filename),file=f)