255 lines
7.1 KiB
Python
Executable File
255 lines
7.1 KiB
Python
Executable File
###########################################
|
|
# Project: CMSIS DSP Library
|
|
# Title: mfccdata.py
|
|
# Description: Generation of MFCC arays for the MFCC C init function
|
|
#
|
|
# $Date: 07 September 2021
|
|
# $Revision: V1.10.0
|
|
#
|
|
# Target Processor: Cortex-M and Cortex-A cores
|
|
# -------------------------------------------------------------------- */
|
|
#
|
|
# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# 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
|
|
#
|
|
# 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.
|
|
############################################
|
|
import numpy as np
|
|
from jinja2 import Environment, PackageLoader, select_autoescape,FileSystemLoader
|
|
import os.path
|
|
import struct
|
|
import scipy.signal as sig
|
|
|
|
def to_q31(v):
|
|
r = int(round(v * 2**31))
|
|
if (r > 0x07FFFFFFF):
|
|
r = 0x07FFFFFFF
|
|
if (r < -0x080000000):
|
|
r = -0x080000000
|
|
return ("0x%s" % format(struct.unpack('<I', struct.pack('<i', r))[0],'08X'))
|
|
|
|
def to_q15(v):
|
|
r = int(round(v * 2**15))
|
|
if (r > 0x07FFF):
|
|
r = 0x07FFF
|
|
if (r < -0x08000):
|
|
r = -0x08000
|
|
return ("0x%s" % format(struct.unpack('<H', struct.pack('<h', r))[0],'04X'))
|
|
|
|
def to_f16(v):
|
|
return("(float16_t)%ff" % struct.unpack('<f',struct.pack('<f',v)))
|
|
|
|
def to_f32(v):
|
|
return("%ff" % struct.unpack('<f',struct.pack('<f',v)))
|
|
|
|
class ConvertArray:
|
|
def __init__(self,theType):
|
|
self._cvt = lambda x : x
|
|
if theType=="f32":
|
|
self._cvt = to_f32
|
|
if theType=="f16":
|
|
self._cvt = to_f16
|
|
if theType=="q31":
|
|
self._cvt = to_q31
|
|
if theType=="q15":
|
|
self._cvt = to_q15
|
|
|
|
def getArrayContent(self,samples):
|
|
nb = 0
|
|
res=""
|
|
res += "{\n"
|
|
for sample in samples:
|
|
res += str(self._cvt(sample))
|
|
res += ","
|
|
nb = nb + 1
|
|
if nb == 10:
|
|
res += "\n"
|
|
nb = 0
|
|
res += "}"
|
|
return(res)
|
|
|
|
|
|
|
|
def frequencyToMelSpace(freq):
|
|
return 1127.0 * np.log(1.0 + freq / 700.0)
|
|
|
|
def melSpaceToFrequency(mels):
|
|
return 700.0 * (np.exp(mels / 1127.0) - 1.0)
|
|
|
|
def melFilterMatrix(fmin, fmax, numOfMelFilters,fs,FFTSize):
|
|
|
|
filters = np.zeros((numOfMelFilters,int(FFTSize/2+1)))
|
|
zeros = np.zeros(int(FFTSize // 2 ))
|
|
|
|
|
|
fmin_mel = frequencyToMelSpace(fmin)
|
|
fmax_mel = frequencyToMelSpace(fmax)
|
|
mels = np.linspace(fmin_mel, fmax_mel, num=numOfMelFilters+2)
|
|
|
|
|
|
linearfreqs = np.linspace( 0, fs/2.0, int(FFTSize // 2 + 1) )
|
|
spectrogrammels = frequencyToMelSpace(linearfreqs)[1:]
|
|
|
|
|
|
filtPos=[]
|
|
filtLen=[]
|
|
totalLen = 0
|
|
packedFilters = []
|
|
for n in range(numOfMelFilters):
|
|
|
|
|
|
upper = (spectrogrammels - mels[n])/(mels[n+1]-mels[n])
|
|
lower = (mels[n+2] - spectrogrammels)/(mels[n+2]-mels[n+1])
|
|
|
|
|
|
filters[n, :] = np.hstack([0,np.maximum(zeros,np.minimum(upper,lower))])
|
|
nb = 0
|
|
startFound = False
|
|
for sample in filters[n, :]:
|
|
if not startFound and sample != 0.0:
|
|
startFound = True
|
|
startPos = nb
|
|
|
|
if startFound and sample == 0.0:
|
|
endPos = nb - 1
|
|
break
|
|
nb = nb + 1
|
|
filtLen.append(endPos - startPos+1)
|
|
totalLen += endPos - startPos + 1
|
|
filtPos.append(startPos)
|
|
packedFilters += list(filters[n, startPos:endPos+1])
|
|
|
|
return filtLen,filtPos,totalLen,packedFilters,filters
|
|
|
|
|
|
def dctMatrix(numOfDctOutputs, numOfMelFilters):
|
|
|
|
result = np.zeros((numOfDctOutputs,numOfMelFilters))
|
|
s=(np.linspace(1,numOfMelFilters,numOfMelFilters) - 0.5)/numOfMelFilters
|
|
|
|
for i in range(0, numOfDctOutputs):
|
|
result[i,:]=np.cos(i * np.pi*s) * np.sqrt(2.0/numOfMelFilters)
|
|
|
|
return result
|
|
|
|
|
|
def ctype(s):
|
|
if s == "f64":
|
|
return("float64_t")
|
|
if s == "f32":
|
|
return("float32_t")
|
|
if s == "f16":
|
|
return("float16_t")
|
|
if s == "q31":
|
|
return("q31_t")
|
|
if s == "q15":
|
|
return("q15_t")
|
|
|
|
def typeext(s):
|
|
if s == "f64":
|
|
return("_f64")
|
|
if s == "f32":
|
|
return("_f32")
|
|
if s == "f16":
|
|
return("_f16")
|
|
if s == "q31":
|
|
return("_q31")
|
|
if s == "q15":
|
|
return("_q15")
|
|
|
|
def prepareWindowConfig(configs):
|
|
# sig.hamming(FFTSize, sym=False)
|
|
for config in configs:
|
|
c=configs[config]
|
|
if c["win"] == "hamming":
|
|
win = sig.hamming(c["fftlength"], sym=False)
|
|
if c["win"] == "hanning":
|
|
win = sig.hann(c["fftlength"], sym=False)
|
|
|
|
cvt=ConvertArray(c["type"])
|
|
c["ctype"]=ctype(c["type"])
|
|
c["ext"]=typeext(c["type"])
|
|
|
|
c["winSamples"] = cvt.getArrayContent(win)
|
|
|
|
def prepareMelconfig(configs):
|
|
for config in configs:
|
|
c=configs[config]
|
|
|
|
cvt=ConvertArray(c["type"])
|
|
cvtInt=ConvertArray(None)
|
|
c["ctype"]=ctype(c["type"])
|
|
c["ext"]=typeext(c["type"])
|
|
|
|
filtLen,filtPos,totalLen,packedFilters,filters = melFilterMatrix(c["fmin"], c["fmax"], c["melFilters"],c["samplingRate"],c["fftlength"])
|
|
|
|
c["filtLenArray"]=cvtInt.getArrayContent(filtLen)
|
|
c["filtPosArray"]=cvtInt.getArrayContent(filtPos)
|
|
c["totalLen"]=totalLen
|
|
c["filters"]=cvt.getArrayContent(packedFilters)
|
|
|
|
def prepareDctconfig(configs):
|
|
for config in configs:
|
|
c=configs[config]
|
|
|
|
cvt=ConvertArray(c["type"])
|
|
c["ctype"]=ctype(c["type"])
|
|
c["ext"]=typeext(c["type"])
|
|
c["dctMatrixLength"]=c["dctOutputs"] * c["melFilters"]
|
|
|
|
dctMat = dctMatrix(c["dctOutputs"],c["melFilters"])
|
|
dctMat=dctMat.reshape(c["dctMatrixLength"])
|
|
c["dctMatrix"]=cvt.getArrayContent(dctMat)
|
|
|
|
#print(configs)
|
|
|
|
def checkF16(configs):
|
|
hasF16 = False
|
|
for config in configs["dct"]:
|
|
c=configs["dct"][config]
|
|
if c["type"]=="f16":
|
|
hasF16 = True
|
|
c["hasF16"]=True
|
|
|
|
for config in configs["melfilter"]:
|
|
c=configs["melfilter"][config]
|
|
if c["type"]=="f16":
|
|
hasF16 = True
|
|
c["hasF16"]=True
|
|
|
|
for config in configs["window"]:
|
|
c=configs["window"][config]
|
|
if c["type"]=="f16":
|
|
hasF16 = True
|
|
c["hasF16"]=True
|
|
|
|
configs["hasF16"]=hasF16
|
|
|
|
env = Environment(
|
|
# For 3.0 version of jinja2, replace with
|
|
# loader=PackageLoader("mfcctemplates",""),
|
|
loader=PackageLoader("mfccdata","mfcctemplates"),
|
|
autoescape=select_autoescape(),
|
|
trim_blocks=True
|
|
)
|
|
|
|
ctemplate = env.get_template("mfccdata.c")
|
|
htemplate = env.get_template("mfccdata.h")
|
|
|
|
|
|
def genMfccHeader(f,configs,filename):
|
|
print(htemplate.render(configs=configs,filename=filename),file=f)
|
|
|
|
def genMfccInit(f,configs,filename):
|
|
print(ctemplate.render(configs=configs,filename=filename),file=f) |