mfccdata.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. ############################################
  28. import numpy as np
  29. from jinja2 import Environment, PackageLoader, select_autoescape,FileSystemLoader
  30. import os.path
  31. import struct
  32. import scipy.signal as sig
  33. def to_q31(v):
  34. r = int(round(v * 2**31))
  35. if (r > 0x07FFFFFFF):
  36. r = 0x07FFFFFFF
  37. if (r < -0x080000000):
  38. r = -0x080000000
  39. return ("0x%s" % format(struct.unpack('<I', struct.pack('<i', r))[0],'08X'))
  40. def to_q15(v):
  41. r = int(round(v * 2**15))
  42. if (r > 0x07FFF):
  43. r = 0x07FFF
  44. if (r < -0x08000):
  45. r = -0x08000
  46. return ("0x%s" % format(struct.unpack('<H', struct.pack('<h', r))[0],'04X'))
  47. def to_f16(v):
  48. return("(float16_t)%ff" % struct.unpack('<f',struct.pack('<f',v)))
  49. def to_f32(v):
  50. return("%ff" % struct.unpack('<f',struct.pack('<f',v)))
  51. class ConvertArray:
  52. def __init__(self,theType):
  53. self._cvt = lambda x : x
  54. if theType=="f32":
  55. self._cvt = to_f32
  56. if theType=="f16":
  57. self._cvt = to_f16
  58. if theType=="q31":
  59. self._cvt = to_q31
  60. if theType=="q15":
  61. self._cvt = to_q15
  62. def getArrayContent(self,samples):
  63. nb = 0
  64. res=""
  65. res += "{\n"
  66. for sample in samples:
  67. res += str(self._cvt(sample))
  68. res += ","
  69. nb = nb + 1
  70. if nb == 10:
  71. res += "\n"
  72. nb = 0
  73. res += "}"
  74. return(res)
  75. def frequencyToMelSpace(freq):
  76. return 1127.0 * np.log(1.0 + freq / 700.0)
  77. def melSpaceToFrequency(mels):
  78. return 700.0 * (np.exp(mels / 1127.0) - 1.0)
  79. def melFilterMatrix(fmin, fmax, numOfMelFilters,fs,FFTSize):
  80. filters = np.zeros((numOfMelFilters,int(FFTSize/2+1)))
  81. zeros = np.zeros(int(FFTSize // 2 ))
  82. fmin_mel = frequencyToMelSpace(fmin)
  83. fmax_mel = frequencyToMelSpace(fmax)
  84. mels = np.linspace(fmin_mel, fmax_mel, num=numOfMelFilters+2)
  85. linearfreqs = np.linspace( 0, fs/2.0, int(FFTSize // 2 + 1) )
  86. spectrogrammels = frequencyToMelSpace(linearfreqs)[1:]
  87. filtPos=[]
  88. filtLen=[]
  89. totalLen = 0
  90. packedFilters = []
  91. for n in range(numOfMelFilters):
  92. upper = (spectrogrammels - mels[n])/(mels[n+1]-mels[n])
  93. lower = (mels[n+2] - spectrogrammels)/(mels[n+2]-mels[n+1])
  94. filters[n, :] = np.hstack([0,np.maximum(zeros,np.minimum(upper,lower))])
  95. nb = 0
  96. startFound = False
  97. for sample in filters[n, :]:
  98. if not startFound and sample != 0.0:
  99. startFound = True
  100. startPos = nb
  101. if startFound and sample == 0.0:
  102. endPos = nb - 1
  103. break
  104. nb = nb + 1
  105. filtLen.append(endPos - startPos+1)
  106. totalLen += endPos - startPos + 1
  107. filtPos.append(startPos)
  108. packedFilters += list(filters[n, startPos:endPos+1])
  109. return filtLen,filtPos,totalLen,packedFilters,filters
  110. def dctMatrix(numOfDctOutputs, numOfMelFilters):
  111. result = np.zeros((numOfDctOutputs,numOfMelFilters))
  112. s=(np.linspace(1,numOfMelFilters,numOfMelFilters) - 0.5)/numOfMelFilters
  113. for i in range(0, numOfDctOutputs):
  114. result[i,:]=np.cos(i * np.pi*s) * np.sqrt(2.0/numOfMelFilters)
  115. return result
  116. def ctype(s):
  117. if s == "f64":
  118. return("float64_t")
  119. if s == "f32":
  120. return("float32_t")
  121. if s == "f16":
  122. return("float16_t")
  123. if s == "q31":
  124. return("q31_t")
  125. if s == "q15":
  126. return("q15_t")
  127. def typeext(s):
  128. if s == "f64":
  129. return("_f64")
  130. if s == "f32":
  131. return("_f32")
  132. if s == "f16":
  133. return("_f16")
  134. if s == "q31":
  135. return("_q31")
  136. if s == "q15":
  137. return("_q15")
  138. def prepareWindowConfig(configs):
  139. # sig.hamming(FFTSize, sym=False)
  140. for config in configs:
  141. c=configs[config]
  142. if c["win"] == "hamming":
  143. win = sig.hamming(c["fftlength"], sym=False)
  144. if c["win"] == "hanning":
  145. win = sig.hann(c["fftlength"], sym=False)
  146. cvt=ConvertArray(c["type"])
  147. c["ctype"]=ctype(c["type"])
  148. c["ext"]=typeext(c["type"])
  149. c["winSamples"] = cvt.getArrayContent(win)
  150. def prepareMelconfig(configs):
  151. for config in configs:
  152. c=configs[config]
  153. cvt=ConvertArray(c["type"])
  154. cvtInt=ConvertArray(None)
  155. c["ctype"]=ctype(c["type"])
  156. c["ext"]=typeext(c["type"])
  157. filtLen,filtPos,totalLen,packedFilters,filters = melFilterMatrix(c["fmin"], c["fmax"], c["melFilters"],c["samplingRate"],c["fftlength"])
  158. c["filtLenArray"]=cvtInt.getArrayContent(filtLen)
  159. c["filtPosArray"]=cvtInt.getArrayContent(filtPos)
  160. c["totalLen"]=totalLen
  161. c["filters"]=cvt.getArrayContent(packedFilters)
  162. def prepareDctconfig(configs):
  163. for config in configs:
  164. c=configs[config]
  165. cvt=ConvertArray(c["type"])
  166. c["ctype"]=ctype(c["type"])
  167. c["ext"]=typeext(c["type"])
  168. c["dctMatrixLength"]=c["dctOutputs"] * c["melFilters"]
  169. dctMat = dctMatrix(c["dctOutputs"],c["melFilters"])
  170. dctMat=dctMat.reshape(c["dctMatrixLength"])
  171. c["dctMatrix"]=cvt.getArrayContent(dctMat)
  172. #print(configs)
  173. def checkF16(configs):
  174. hasF16 = False
  175. for config in configs["dct"]:
  176. c=configs["dct"][config]
  177. if c["type"]=="f16":
  178. hasF16 = True
  179. c["hasF16"]=True
  180. for config in configs["melfilter"]:
  181. c=configs["melfilter"][config]
  182. if c["type"]=="f16":
  183. hasF16 = True
  184. c["hasF16"]=True
  185. for config in configs["window"]:
  186. c=configs["window"][config]
  187. if c["type"]=="f16":
  188. hasF16 = True
  189. c["hasF16"]=True
  190. configs["hasF16"]=hasF16
  191. env = Environment(
  192. # For 3.0 version of jinja2, replace with
  193. # loader=PackageLoader("mfcctemplates",""),
  194. loader=PackageLoader("mfccdata","mfcctemplates"),
  195. autoescape=select_autoescape(),
  196. trim_blocks=True
  197. )
  198. ctemplate = env.get_template("mfccdata.c")
  199. htemplate = env.get_template("mfccdata.h")
  200. def genMfccHeader(f,configs,filename):
  201. print(htemplate.render(configs=configs,filename=filename),file=f)
  202. def genMfccInit(f,configs,filename):
  203. print(ctemplate.render(configs=configs,filename=filename),file=f)