HTK計算mfcc/filter_bank源碼解析

来源:http://www.cnblogs.com/dystopia/archive/2016/08/29/5818687.html
-Advertisement-
Play Games

HTK計算mfcc/filter_bank源碼解析 HTK可以用簡單的 求取mfcc或者filter_bank 關於mfcc的原理在 http://my.oschina.net/jamesju/blog/193343 中有講解,這裡主要說HTK具體是如何用C實現的,因為HTK自身的龐大,文件嵌套不少 ...


HTK計算mfcc/filter_bank源碼解析

HTK可以用簡單的

HCopy -C config -s scp

求取mfcc或者filter_bank
關於mfcc的原理在

http://my.oschina.net/jamesju/blog/193343

中有講解,這裡主要說HTK具體是如何用C實現的,因為HTK自身的龐大,文件嵌套不少,所以我提取出了求取filter_bank的源碼並重寫了,可以直接運行。

讀入數據、分幀

首先定義三個結構體:

typedef struct Wave
{
    int nSample;//wav中樣本個數
    int frSize;//一幀中的樣本數
    int frIdx;//當前幀位置
    int frRate;//幀移
    float *wavdata;
    int nRow;
    float *Rdata;
};
typedef struct FBankInfo
{
    int frameSize;       /* speech frameSize */
    int numChans;        /* number of channels */
    long sampPeriod;     /* sample period */
    int fftN;            /* fft size */
    int klo, khi;         /* lopass to hipass cut-off fft indices */
    int usePower;    /* use power rather than magnitude *///boolen
    int takeLogs;    /* log filterbank channels *///boolen
    float fres;          /* scaled fft resolution */
    float *cf;           /* array[1..pOrder+1] of centre freqs */
    float *loChan;     /* array[1..fftN/2] of loChan index */
    float *loWt;         /* array[1..fftN/2] of loChan weighting */
    float *x;            /* array[1..fftN] of fftchans */
};
typedef struct IOConfig
{
    float curVol;/* current volume dB (0.0-100.0) */
    float preEmph;
    int frSize;//一幀中的樣本數
    int frIdx;//當前幀位置
    int frRate;//幀移
    float *fbank;
    struct FBankInfo fbInfo;
    float *s;//幀數據

};

之後讀入數據,這裡需要WAV頭文件的知識,每個採樣點讀入的
就是兩個位元組的音頻信息,如果matlab打開wav文件得到的採樣點數值會
很小,因為是除以採樣數之後的值,存入w->wavdata中,對應函數是

void LoadFile(char *s, struct Wave *w)

下一部是分幀,預設值為

w->frIdx = 0; w->frRate = 160; w->frSize = 400;
/*採樣率為25ms,則幀長為400,預設幀移為160*/

幀信息存入cf中,幀的具體數值存入cf->s中

    for (int k = 0; k < w->nRow + 1; k++)
    {
        /*分幀,計算幀能量*/

        cf->s[0] = w->frSize;
        GetWave(cf->s + 1, w);
        int j, m, e, x;
        for (j = 1, m = e = 0.0; j <= w->frSize; j++) {
            x = (int)cf->s[j];
            m += x; e += x*x;
        }
        m = m / w->frSize; e = e / w->frSize - m*m;
        if (e>0.0) e = 10.0*log10(e / 0.32768);
        else e = 0.0;
        cf->curVol = e;
        /*處理*/
        ConvertFrame(cf, w);
        linkdata(cf, w, k);
    }

處理幀數據

void ConvertFrame(struct IOConfig *cf, struct Wave *w)

中處理幀數據

void ConvertFrame(struct IOConfig *cf, struct Wave *w)
{
    float re, rawte = 0.0, te, *p, cepScale = 1.0;
    int i, bsize = 0;
    char buf[50];
    int rawE;//boolen
    cf->frIdx = w->frIdx; cf->frSize = w->frSize; cf->frRate = w->frRate;
    cf->preEmph = 0.97;
    ZeroMeanFrame(cf->s);//零均值處理
    PreEmphasise(cf->s, cf->preEmph);//預加重處理
    Ham(cf->s);//加窗 此處可以優化,創建ham窗多次十分耗費時間,可以用全局申請ham的記憶體
    cf->fbInfo = InitFBank(cf);
    cf->fbank = (float*)malloc(sizeof(float)* NUMCHANS);
    cf->fbank[0] = NUMCHANS;
    Wave2FBank(cf->s, cf->fbank, cf->fbInfo);//提取NUMCHANS為40的fbank

}

可以看到,先後是零均值處理、預加重處理、加窗處理、最後是fbank的參數處理,其中包含了FFT等,最後將fbank數據存入cf->fbank中

很重要的是在計算結束之後,有一個

void zeromean(struct Wave *w)

還要進行一次零均值!

所有代碼如下:
頭文件:filterbank.h

#ifndef __FILTERFBANK_H__
#define __FILTERFBANK_H__
#define INPUT_DIMEN 1640
#define LAYER_DIMEN 128
#define OUTPUT_DIMEN 3
#define LAYER 4

#define PI   3.14159265358979
#define TPI  6.28318530717959     /* PI*2 */
#define NUMCHANS 40

typedef struct Wave
{
    int nSample;//wav中樣本個數
    int frSize;//一幀中的樣本數
    int frIdx;//當前幀位置
    int frRate;//幀移
    float *wavdata;
    int nRow;
    float *Rdata;
};
typedef struct FBankInfo
{
    int frameSize;       /* speech frameSize */
    int numChans;        /* number of channels */
    long sampPeriod;     /* sample period */
    int fftN;            /* fft size */
    int klo, khi;         /* lopass to hipass cut-off fft indices */
    int usePower;    /* use power rather than magnitude *///boolen
    int takeLogs;    /* log filterbank channels *///boolen
    float fres;          /* scaled fft resolution */
    float *cf;           /* array[1..pOrder+1] of centre freqs */
    float *loChan;     /* array[1..fftN/2] of loChan index */
    float *loWt;         /* array[1..fftN/2] of loChan weighting */
    float *x;            /* array[1..fftN] of fftchans */
};
typedef struct IOConfig
{
    float curVol;/* current volume dB (0.0-100.0) */
    float preEmph;
    int frSize;//一幀中的樣本數
    int frIdx;//當前幀位置
    int frRate;//幀移
    float *fbank;
    struct FBankInfo fbInfo;
    float *s;//幀數據

};
/*讀取PCM文件到wavdata中*/
void LoadFile(char *s, struct Wave *w);
void GetWave(float *buf, struct Wave *w);
void ZeroMeanFrame(float *frame);
void PreEmphasise(float *frame, float k);
void Ham(float *frame);
float Mel(int k, float fres);
float WarpFreq(float fcl, float fcu, float freq, float minFreq, float maxFreq, float alpha);
struct FBankInfo InitFBank(struct IOConfig *cf);
void FFT(float *s, int invert);
void Realft(float *s);
void Wave2FBank(float *s, float *fbank, struct FBankInfo info);
void ConvertFrame(struct IOConfig *cf, struct Wave *w);
void linkdata(struct IOConfig *cf, struct Wave *w, int k);
void zeromean(struct Wave *w);
struct Wave filter_bank(char *s);

#endif 

函數文件:filterbank.cpp


#include<stdio.h>
#include<math.h>
#include <stdlib.h>
#include <time.h>
#include "nNet.h"
#include "filterbank.h"

/*讀取PCM文件到wavdata中*/
void LoadFile(char *s, struct Wave *w)
{
    FILE *fp;
    fp = fopen(s, "rb");
    if (!fp)
    {
        printf("can not open this file\n");
        exit(0);
    }
    unsigned char ch1, ch2, ch3, ch4;
    int i;
    float *buf;
    buf = w->wavdata;
    for (i = 0; i < 40; i++)
    {
        ch1 = fgetc(fp);
    }
    ch1 = fgetc(fp); ch2 = fgetc(fp); ch3 = fgetc(fp); ch4 = fgetc(fp);
    w->nSample = (ch2 * 16 * 16 + ch1) + (ch4 * 16 * 16 + ch3) * 16 * 16 * 16 * 16;
    w->nSample /= 2;
    printf("%ld", w->nSample);
    w->nRow = (w->nSample - w->frSize) / w->frRate + 1;
    for (i = 0; i<w->nSample; i++)
    {
        ch1 = fgetc(fp); //每次讀取兩個字元,存在數組ch中
        ch2 = fgetc(fp);
        //if (i % 8 == 0)      //每行輸出16個字元對應的十六進位數
        //printf("\n");
        float temp = ch2 * 16 * 16 + ch1;
        if (temp < 32768){
            //printf("%d ", temp);
            *buf++ = temp;
            //printf("%lf ", (float)(ch2 * 16 * 16 + ch1) / 32767);
        }
        else{
            //printf("%d ",temp - 65535 - 1);
            *buf++ = temp - 65535 - 1;
            //printf("%lf ", (float)((ch2 * 16 * 16 + ch1)-65535-1) / 32768);
        }
    }
    fclose(fp);
}
/*從wavdata中提取當前幀*/
void GetWave(float *buf, struct Wave *w)
{
    int k;
    if (w->frIdx + w->frSize > w->nSample)
    {
        printf("GetWave: attempt to read past end of buffer\n");
        for (k = 0; k < w->frSize; k++)
        {
            buf[k] = 0;
        }
        for (k = 0; w->frIdx + k < w->nSample; k++)
        {
            buf[k] = w->wavdata[w->frIdx + k];
        }

    }

    for (k = 0; k < w->frSize; k++)
    {
        buf[k] = w->wavdata[w->frIdx + k];
    }
    w->frIdx += w->frRate;
}

void ZeroMeanFrame(float *frame)
{
    int size, i;
    float sum = 0.0, off;
    size = frame[0];
    for (i = 1; i <= size; i++) sum += frame[i];
    off = sum / size;
    for (i = 1; i <= size; i++) frame[i] -= off;
}
void PreEmphasise(float *frame, float k)
{
    int i;
    float preE = k;
    int size = frame[0];
    for (i = size; i >= 2; i--)
        frame[i] -= frame[i - 1] * preE;
    frame[1] *= 1.0 - preE;
}
void Ham(float *frame)
{
    int frameSize = frame[0];
    int i;
    float a;
    float *hamWin;
    hamWin = (float*)malloc(sizeof(float)*frameSize);
    a = TPI / (frameSize - 1);
    int b = cos(a * 0);
    for (i = 1; i <= frameSize; i++)
        hamWin[i] = 0.54 - 0.46 * cos(a*(i - 1));
    for (i = 1; i <= frameSize; i++)
    {
        frame[i] *= hamWin[i];
    }
    //free(hamWin);
}

float Mel(int k, float fres)
{
    return 1127 * log(1 + (k - 1)*fres);
}
float WarpFreq(float fcl, float fcu, float freq, float minFreq, float maxFreq, float alpha)
{
    if (alpha == 1.0)
        return freq;
    else {
        float scale = 1.0 / alpha;
        float cu = fcu * 2 / (1 + scale);
        float cl = fcl * 2 / (1 + scale);

        float au = (maxFreq - cu * scale) / (maxFreq - cu);
        float al = (cl * scale - minFreq) / (cl - minFreq);

        if (freq > cu)
            return  au * (freq - cu) + scale * cu;
        else if (freq < cl)
            return al * (freq - minFreq) + minFreq;
        else
            return scale * freq;
    }
}
struct FBankInfo InitFBank(struct IOConfig *cf)
{
    int numChans = 40; int usePower = 0; int takeLogs = 1; int sampPeriod = 625;//sampPeriod要考慮一下
    float alpha = 1; int warpLowCut = 0; int warpUpCut = 0;
    struct FBankInfo fb;
    float mlo, mhi, ms, melk;
    int k, chan, maxChan, Nby2;
    int doubleFFT = 0;


    /* Save sizes to cross-check subsequent usage */
    fb.frameSize = cf->frSize;
    fb.numChans = numChans;
    fb.sampPeriod = sampPeriod;
    fb.usePower = usePower;
    fb.takeLogs = takeLogs;
    /* Calculate required FFT size */
    fb.fftN = 2;
    while (fb.frameSize>fb.fftN)
        fb.fftN *= 2;
    if (doubleFFT)//不執行
        fb.fftN *= 2;
    Nby2 = fb.fftN / 2;
    fb.fres = 1.0E7 / (sampPeriod * fb.fftN * 700.0);
    maxChan = numChans + 1;
    /* set lo and hi pass cut offs if any */
    fb.klo = 2; fb.khi = Nby2;       /* apply lo/hi pass filtering */
    mlo = 0; mhi = Mel(Nby2 + 1, fb.fres);


    /* Create vector of fbank centre frequencies */
    fb.cf = (float*)malloc(sizeof(float)*maxChan + 1);
    fb.cf[0] = maxChan;
    ms = mhi - mlo;
    for (chan = 1; chan <= maxChan; chan++) {
        if (alpha == 1.0) {
            fb.cf[chan] = ((float)chan / (float)maxChan)*ms + mlo;
        }
        else {
            /* scale assuming scaling starts at lopass */
            float minFreq = 700.0 * (exp(mlo / 1127.0) - 1.0);
            float maxFreq = 700.0 * (exp(mhi / 1127.0) - 1.0);
            float cf = ((float)chan / (float)maxChan) * ms + mlo;

            cf = 700 * (exp(cf / 1127.0) - 1.0);

            fb.cf[chan] = 1127.0 * log(1.0 + WarpFreq(warpLowCut, warpUpCut, cf, minFreq, maxFreq, alpha) / 700.0);
        }
    }

    /* Create loChan map, loChan[fftindex] . lower channel index */
    fb.loChan = (float*)malloc(sizeof(float)*Nby2 + 1);
    fb.loChan[0] = Nby2;
    for (k = 1, chan = 1; k <= Nby2; k++){
        melk = Mel(k, fb.fres);
        if (k<fb.klo || k>fb.khi) fb.loChan[k] = -1;
        else {
            while (fb.cf[chan] < melk  && chan <= maxChan) ++chan;
            fb.loChan[k] = chan - 1;
        }
    }

    /* Create vector of lower channel weights */
    fb.loWt = (float*)malloc(sizeof(float)*Nby2 + 1);
    fb.loWt[0] = Nby2;
    for (k = 1; k <= Nby2; k++) {
        chan = fb.loChan[k];
        if (k<fb.klo || k>fb.khi) fb.loWt[k] = 0.0;
        else {
            if (chan>0)
                fb.loWt[k] = ((fb.cf[chan + 1] - Mel(k, fb.fres)) /
                (fb.cf[chan + 1] - fb.cf[chan]));
            else
                fb.loWt[k] = (fb.cf[1] - Mel(k, fb.fres)) / (fb.cf[1] - mlo);
        }
    }
    /* Create workspace for fft */
    fb.x = (float*)malloc(sizeof(float)*fb.fftN + 1);
    fb.x[0] = fb.fftN;
    return fb;
}

void FFT(float *s, int invert)
{
    int ii, jj, n, nn, limit, m, j, inc, i;
    double wx, wr, wpr, wpi, wi, theta;
    double xre, xri, x;

    n = s[0];
    nn = n / 2; j = 1;
    for (ii = 1; ii <= nn; ii++) {
        i = 2 * ii - 1;
        if (j>i) {
            xre = s[j]; xri = s[j + 1];
            s[j] = s[i];  s[j + 1] = s[i + 1];
            s[i] = xre; s[i + 1] = xri;
        }
        m = n / 2;
        while (m >= 2 && j > m) {
            j -= m; m /= 2;
        }
        j += m;
    };
    limit = 2;
    while (limit < n) {
        inc = 2 * limit; theta = TPI / limit;
        if (invert) theta = -theta;
        x = sin(0.5 * theta);
        wpr = -2.0 * x * x; wpi = sin(theta);
        wr = 1.0; wi = 0.0;
        for (ii = 1; ii <= limit / 2; ii++) {
            m = 2 * ii - 1;
            for (jj = 0; jj <= (n - m) / inc; jj++) {
                i = m + jj * inc;
                j = i + limit;
                xre = wr * s[j] - wi * s[j + 1];
                xri = wr * s[j + 1] + wi * s[j];
                s[j] = s[i] - xre; s[j + 1] = s[i + 1] - xri;
                s[i] = s[i] + xre; s[i + 1] = s[i + 1] + xri;
            }
            wx = wr;
            wr = wr * wpr - wi * wpi + wr;
            wi = wi * wpr + wx * wpi + wi;
        }
        limit = inc;
    }
    if (invert)
        for (i = 1; i <= n; i++)
            s[i] = s[i] / nn;

}
void Realft(float *s)
{
    int n, n2, i, i1, i2, i3, i4;
    double xr1, xi1, xr2, xi2, wrs, wis;
    double yr, yi, yr2, yi2, yr0, theta, x;

    n = s[0] / 2; n2 = n / 2;
    theta = PI / n;
    FFT(s, 0);
    x = sin(0.5 * theta);
    yr2 = -2.0 * x * x;
    yi2 = sin(theta); yr = 1.0 + yr2; yi = yi2;
    for (i = 2; i <= n2; i++) {
        i1 = i + i - 1;      i2 = i1 + 1;
        i3 = n + n + 3 - i2; i4 = i3 + 1;
        wrs = yr; wis = yi;
        xr1 = (s[i1] + s[i3]) / 2.0; xi1 = (s[i2] - s[i4]) / 2.0;
        xr2 = (s[i2] + s[i4]) / 2.0; xi2 = (s[i3] - s[i1]) / 2.0;
        s[i1] = xr1 + wrs * xr2 - wis * xi2;
        s[i2] = xi1 + wrs * xi2 + wis * xr2;
        s[i3] = xr1 - wrs * xr2 + wis * xi2;
        s[i4] = -xi1 + wrs * xi2 + wis * xr2;
        yr0 = yr;
        yr = yr * yr2 - yi  * yi2 + yr;
        yi = yi * yr2 + yr0 * yi2 + yi;
    }
    xr1 = s[1];
    s[1] = xr1 + s[2];
    s[2] = 0.0;
}
void Wave2FBank(float *s, float *fbank, struct FBankInfo info)
{
    const float melfloor = 1.0;
    int k, bin;
    float t1, t2;   /* real and imag parts */
    float ek;      /* energy of k'th fft channel */

    float te = 0.0;
    for (k = 1; k <= info.frameSize; k++)
        te += (s[k] * s[k]);
    /* Apply FFT */
    for (k = 1; k <= info.frameSize; k++)
        info.x[k] = s[k];    /* copy to workspace */
    for (k = info.frameSize + 1; k <= info.fftN; k++)
        info.x[k] = 0.0;   /* pad with zeroes */
    Realft(info.x);                            /* take fft */

    /* Fill filterbank channels */
    int i = 0;
    for (i = 1; i <= fbank[0]; i++)
        fbank[i] = 0.0;
    for (k = info.klo; k <= info.khi; k++) {             /* fill bins */
        t1 = info.x[2 * k - 1]; t2 = info.x[2 * k];
        if (info.usePower)
            ek = t1*t1 + t2*t2;
        else
            ek = sqrt(t1*t1 + t2*t2);
        bin = info.loChan[k];
        t1 = info.loWt[k] * ek;
        if (bin > 0) fbank[bin] += t1;
        if (bin < info.numChans) fbank[bin + 1] += ek - t1;
        //printf("k:%d bin:%d info.loWt:%f fbank[bin]:%f\n", k, bin, info.loWt[k], fbank[bin]);

    }

    /* Take logs */
    if (info.takeLogs)
        for (bin = 1; bin <= info.numChans; bin++) {
            t1 = fbank[bin];
            if (t1 < melfloor) t1 = melfloor;
            fbank[bin] = log(t1);
        }
}

void ConvertFrame(struct IOConfig *cf, struct Wave *w)
{
    float re, rawte = 0.0, te, *p, cepScale = 1.0;
    int i, bsize = 0;
    char buf[50];
    int rawE;//boolen
    cf->frIdx = w->frIdx; cf->frSize = w->frSize; cf->frRate = w->frRate;
    cf->preEmph = 0.97;
    ZeroMeanFrame(cf->s);//零均值處理
    PreEmphasise(cf->s, cf->preEmph);//預加重處理
    Ham(cf->s);//加窗 此處可以優化,創建ham窗多次十分耗費時間,可以用全局申請ham的記憶體
    cf->fbInfo = InitFBank(cf);
    cf->fbank = (float*)malloc(sizeof(float)* NUMCHANS);
    cf->fbank[0] = NUMCHANS;
    Wave2FBank(cf->s, cf->fbank, cf->fbInfo);//提取NUMCHANS為40的fbank

}
void linkdata(struct IOConfig *cf, struct Wave *w, int k)
{
    for (int i = 0; i < NUMCHANS; i++)
    {
        *(w->Rdata + i + (k*NUMCHANS)) = *(cf->fbank + 1 + i);

    }
}
void zeromean(struct Wave *w)
{
    int i, j;
    float sum[NUMCHANS];
    int n = w->nRow;
    for (i = 0; i < NUMCHANS; i++)
    {
        sum[i] = 0.0;
        for (j = 0; j < n; j++)
        {
            sum[i] += *(w->Rdata + j*NUMCHANS + i);
        }
        sum[i] = sum[i] / n;
    }
    for (i = 0; i < NUMCHANS*n; i++)
    {
        *(w->Rdata + i) -= sum[i%NUMCHANS];
    }
}

struct Wave filter_bank(char *s)
{
    /*初始化*/
    /*採樣個數為16028*/
    /*由HTK,採樣率為25ms,則幀長為400,預設幀移為160*/
    struct Wave *w;
    w = (struct Wave*)malloc(sizeof(struct Wave));
    w->frIdx = 0; w->frRate = 160; w->frSize = 400;
    w->nSample = 160000;
    w->nRow = (w->nSample - w->frSize) / w->frRate + 1;
    w->wavdata = (float*)malloc(sizeof(float) * w->nSample);
    LoadFile(s, w);
    w->Rdata = (float*)malloc(sizeof(float)*NUMCHANS*w->nRow);

    struct IOConfig *cf;
    cf = (struct IOConfig*)malloc(sizeof(struct IOConfig));
    cf->s = (float*)malloc(sizeof(float) * w->frSize + 1);
    /*讀PCM文件到wavdata中,可以直接讀取緩存數據,此處是為了方便PC測試*/


    for (int k = 0; k < w->nRow + 1; k++)
    {
        /*分幀,計算幀能量*/

        cf->s[0] = w->frSize;
        GetWave(cf->s + 1, w);
        int j, m, e, x;
        for (j = 1, m = e = 0.0; j <= w->frSize; j++) {
            x = (int)cf->s[j];
            m += x; e += x*x;
        }
        m = m / w->frSize; e = e / w->frSize - m*m;
        if (e>0.0) e = 10.0*log10(e / 0.32768);
        else e = 0.0;
        cf->curVol = e;
        /*處理*/
        ConvertFrame(cf, w);
        linkdata(cf, w, k);
    }
    zeromean(w);
    for (int i = 0; i < NUMCHANS*w->nRow; i++)
    {
        if (i%NUMCHANS == 0)
            printf("\n%d:\n", i / NUMCHANS);
        printf("%f ", *(w->Rdata + i));

    }
    /*free(cf->fbInfo.cf);
    free(cf->fbInfo.loChan);
    free(cf->fbInfo.loWt);
    free(cf->fbInfo.x);
    free(cf->fbank);
    free(cf->s);
    free(cf);*/
    return *w;
}

測試文件:test.cpp

#include "nNet.h"
#include "filterbank.h"
#include<stdio.h>
#include<math.h>
#include <stdlib.h>
#include <time.h>
int main()
{
    Wave w = filter_bank("F:\\newrecord.wav");
    return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題目描述有以下幾個問題:1 給定正整數 求方程 的最小非負整數解。2 給定正整數 求方程 的最小非負整數解。3 給定正整數 求方程 在模 意義下解的數量。4 給定正整數 求 的值。其中 是歐拉函數, 是莫比烏斯函數。輸入格式輸入文件共四行,按上述描述中四個問題的順序,給出每個問題。第一行三個正整數 ... ...
  • 初衷:本人初學SpringMVC的時候遇到各種稀奇古怪的問題,網上各種技術論壇上的帖子又參差不齊,難以一步到位達到配置好的效果,這裡我將我配置的總結寫到這裡供大家初學SpringMVC的同僚們共同學習使用! ...
  • 因為打算全屏顯示一個對話框,而對話框內有幾個QLabel的尺寸要在確定QLabel可用的最大尺寸後,再根據內容調整一次,所以在對話框構造函數內就想確定QLabel的最大尺寸,但因為QWidget::updateGeometry()和QWidget::update()都只能用於可見的控制項,所以如何在控 ...
  • 學習地址:http://www.rm5u.com/ 或 http://www.runoob.com/ iPhone或iTouch的寬為320像素,高為480像素,狀態欄高為20像素,toobar高為44像素,tabbar高為49像素,導航欄高為44像素。 typealias:類型別名對當前的類型定義 ...
  • thinkphp配置文件路徑在入口文件index.php中配置。 如果Public目錄在應用程式目錄同等級位置: 2.如果Public在app內部則: 3.如果使用Public在app外部,但定義為: ...
  • "390. Elimination Game題解" 隨便寫幾個小例子查看,發現每次數字個數減少一半,每一層的數之間的間隔相等,然後考慮模擬刪除過程,需要記錄如下幾個變數 1. 每一層的第一個數 2. 當前層的數字個數 3. 當前層每兩個數字的間隔 然後依次模擬刪除,當個數只剩一個的時候返回,然後考慮 ...
  • 一般來說,字元串、數組、資料庫類的函數是相對來使用比較多的類別。// 時間日期 //y返回年最後兩位,Y年四位數,m月份數字,M月份英文。d月份幾號數字,D星期幾英文$date=date("Y-m-d");//include,include_once.require,require_once//re ...
  • 上一課我們通過shell腳本拷貝代碼,瞭解了靜態方法和靜態屬性。(還有個附件PHAR包,我直接無視了) 然後在GOD文件中寫了一些參數, 我們也可以這樣,把方法名像拼湊字元串一樣拼起來。 好,下麵我們還是按照老師課程,進行需求實現:如果參數帶“-”,那麼說明就是屬性。直接調用類的靜態屬性。如果不帶“ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...