--- /dev/null
+/*---------------------------------------------------------------------------*\\r
+\r
+ FILE........: power_ut.c\r
+ AUTHOR......: David Rowe\r
+ DATE CREATED: 30 May 2014\r
+\r
+ Runs Codec 2, ADC, and DAC, to fully exercise STM32C so we can a feel for\r
+ run-time power consumption for SmartMic and hence dimension regulators.\r
+\r
+\*---------------------------------------------------------------------------*/\r
+\r
+/*\r
+ Copyright (C) 2014 David Rowe\r
+\r
+ All rights reserved.\r
+\r
+ This program is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU Lesser General Public License version 2.1, as\r
+ published by the Free Software Foundation. This program is\r
+ distributed in the hope that it will be useful, but WITHOUT ANY\r
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public\r
+ License for more details.\r
+\r
+ You should have received a copy of the GNU Lesser General Public License\r
+ along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+*/\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdint.h>\r
+#include <math.h>\r
+\r
+#include "stm32f4xx_conf.h"\r
+#include "stm32f4xx.h"\r
+#include "stm32f4_adc.h"\r
+#include "stm32f4_dac.h"\r
+#include "gdb_stdio.h"\r
+#include "codec2.h"\r
+#include "dump.h"\r
+#include "sine.h"\r
+#include "machdep.h"\r
+\r
+#ifdef __EMBEDDED__\r
+#define printf gdb_stdio_printf\r
+#define fopen gdb_stdio_fopen\r
+#define fclose gdb_stdio_fclose\r
+#define fread gdb_stdio_fread\r
+#define fwrite gdb_stdio_fwrite\r
+#endif\r
+\r
+#define SPEED_TEST_SAMPLES 24000\r
+\r
+/* modifiaction of test used to measure codec2 execuation speed. We read/write ADC/DAC\r
+ but dont do anything with the samples, as they are at 16 kHz and codec needs 8 kHz. Just\r
+ trying to exercise everything to get a feel for power consumption */\r
+\r
+static void c2speedtest(int mode, char inputfile[])\r
+{\r
+ struct CODEC2 *codec2;\r
+ short *inbuf, *outbuf, *pinbuf, *dummy_buf;\r
+ unsigned char *bits;\r
+ int nsam, nbit, nframes;\r
+ FILE *fin;\r
+ int f, nread;\r
+\r
+ codec2 = codec2_create(mode);\r
+ nsam = codec2_samples_per_frame(codec2);\r
+ nframes = SPEED_TEST_SAMPLES/nsam;\r
+ outbuf = (short*)malloc(nsam*sizeof(short));\r
+ inbuf = (short*)malloc(SPEED_TEST_SAMPLES*sizeof(short));\r
+ nbit = codec2_bits_per_frame(codec2);\r
+ bits = (unsigned char*)malloc(nbit*sizeof(char));\r
+ dummy_buf = (short*)malloc(2*nsam*sizeof(short));\r
+\r
+ fin = fopen(inputfile, "rb");\r
+ if (fin == NULL) {\r
+ printf("Error opening input file: %s\nTerminating....\n",inputfile);\r
+ exit(1);\r
+ }\r
+\r
+ printf("reading samples ....\n");\r
+ nread = fread(inbuf, sizeof(short), SPEED_TEST_SAMPLES, fin);\r
+ if (nread != SPEED_TEST_SAMPLES) {\r
+ printf("error reading %s, %d samples reqd, %d read\n", \r
+ inputfile, SPEED_TEST_SAMPLES, nread);\r
+ }\r
+ fclose(fin);\r
+ \r
+ pinbuf = inbuf;\r
+ for(f=0; f<nframes; f++) {\r
+ //printf("read ADC\n");\r
+ while(adc_read(dummy_buf, nsam*2) == -1); /* runs at Fs = 16kHz */\r
+\r
+ //printf("Codec 2 enc\n");\r
+ GPIOD->ODR = (1 << 13);\r
+ codec2_encode(codec2, bits, pinbuf);\r
+ pinbuf += nsam;\r
+ GPIOD->ODR &= ~(1 << 13);\r
+ //printf("Codec 2 dec\n");\r
+ codec2_decode(codec2, outbuf, bits);\r
+ \r
+ //printf("write to DAC\n");\r
+ while(dac_write(dummy_buf, nsam*2) == -1); /* runs at Fs = 16kHz */\r
+ //printf(".");\r
+ }\r
+\r
+ free(inbuf);\r
+ free(outbuf);\r
+ free(bits);\r
+ codec2_destroy(codec2);\r
+}\r
+\r
+void gpio_init() {\r
+ RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD \r
+ GPIOD->MODER = (1 << 26); // set pin 13 to be general \r
+ // purpose output\r
+}\r
+\r
+int main(int argc, char *argv[]) {\r
+ SystemInit();\r
+ gpio_init();\r
+ machdep_timer_init ();\r
+ adc_open();\r
+ dac_open();\r
+\r
+ printf("Starting power_ut\n");\r
+\r
+ c2speedtest(CODEC2_MODE_1600, "stm_in.raw");\r
+\r
+ printf("Finished\n");\r
+\r
+ return 0;\r
+}\r
+\r
--- /dev/null
+/*---------------------------------------------------------------------------*\
+
+ FILE........: sine.c
+ AUTHOR......: David Rowe
+ DATE CREATED: 19/8/2010
+
+ Sinusoidal analysis and synthesis functions.
+
+\*---------------------------------------------------------------------------*/
+
+/*
+ Copyright (C) 1990-2010 David Rowe
+
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1, as
+ published by the Free Software Foundation. This program is
+ distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*---------------------------------------------------------------------------*\
+
+ INCLUDES
+
+\*---------------------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "defines.h"
+#include "sine.h"
+#include "kiss_fft.h"
+
+#define HPF_BETA 0.125
+
+/*---------------------------------------------------------------------------*\
+
+ HEADERS
+
+\*---------------------------------------------------------------------------*/
+
+void hs_pitch_refinement(MODEL *model, COMP Sw[], float pmin, float pmax,
+ float pstep);
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTIONS
+
+\*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: make_analysis_window
+ AUTHOR......: David Rowe
+ DATE CREATED: 11/5/94
+
+ Init function that generates the time domain analysis window and it's DFT.
+
+\*---------------------------------------------------------------------------*/
+
+void make_analysis_window(kiss_fft_cfg fft_fwd_cfg, float w[], COMP W[])
+{
+ float m;
+ COMP wshift[FFT_ENC];
+ COMP temp;
+ int i,j;
+
+ /*
+ Generate Hamming window centered on M-sample pitch analysis window
+
+ 0 M/2 M-1
+ |-------------|-------------|
+ |-------|-------|
+ NW samples
+
+ All our analysis/synthsis is centred on the M/2 sample.
+ */
+
+ m = 0.0;
+ for(i=0; i<M/2-NW/2; i++)
+ w[i] = 0.0;
+ for(i=M/2-NW/2,j=0; i<M/2+NW/2; i++,j++) {
+ w[i] = 0.5 - 0.5*cosf(TWO_PI*j/(NW-1));
+ m += w[i]*w[i];
+ }
+ for(i=M/2+NW/2; i<M; i++)
+ w[i] = 0.0;
+
+ /* Normalise - makes freq domain amplitude estimation straight
+ forward */
+
+ m = 1.0/sqrtf(m*FFT_ENC);
+ for(i=0; i<M; i++) {
+ w[i] *= m;
+ }
+
+ /*
+ Generate DFT of analysis window, used for later processing. Note
+ we modulo FFT_ENC shift the time domain window w[], this makes the
+ imaginary part of the DFT W[] equal to zero as the shifted w[] is
+ even about the n=0 time axis if NW is odd. Having the imag part
+ of the DFT W[] makes computation easier.
+
+ 0 FFT_ENC-1
+ |-------------------------|
+
+ ----\ /----
+ \ /
+ \ / <- shifted version of window w[n]
+ \ /
+ \ /
+ -------
+
+ |---------| |---------|
+ NW/2 NW/2
+ */
+
+ for(i=0; i<FFT_ENC; i++) {
+ wshift[i].real = 0.0;
+ wshift[i].imag = 0.0;
+ }
+ for(i=0; i<NW/2; i++)
+ wshift[i].real = w[i+M/2];
+ for(i=FFT_ENC-NW/2,j=M/2-NW/2; i<FFT_ENC; i++,j++)
+ wshift[i].real = w[j];
+
+ kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)wshift, (kiss_fft_cpx *)W);
+
+ /*
+ Re-arrange W[] to be symmetrical about FFT_ENC/2. Makes later
+ analysis convenient.
+
+ Before:
+
+
+ 0 FFT_ENC-1
+ |----------|---------|
+ __ _
+ \ /
+ \_______________/
+
+ After:
+
+ 0 FFT_ENC-1
+ |----------|---------|
+ ___
+ / \
+ ________/ \_______
+
+ */
+
+
+ for(i=0; i<FFT_ENC/2; i++) {
+ temp.real = W[i].real;
+ temp.imag = W[i].imag;
+ W[i].real = W[i+FFT_ENC/2].real;
+ W[i].imag = W[i+FFT_ENC/2].imag;
+ W[i+FFT_ENC/2].real = temp.real;
+ W[i+FFT_ENC/2].imag = temp.imag;
+ }
+
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: hpf
+ AUTHOR......: David Rowe
+ DATE CREATED: 16 Nov 2010
+
+ High pass filter with a -3dB point of about 160Hz.
+
+ y(n) = -HPF_BETA*y(n-1) + x(n) - x(n-1)
+
+\*---------------------------------------------------------------------------*/
+
+float hpf(float x, float states[])
+{
+ states[0] += -HPF_BETA*states[0] + x - states[1];
+ states[1] = x;
+
+ return states[0];
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: dft_speech
+ AUTHOR......: David Rowe
+ DATE CREATED: 27/5/94
+
+ Finds the DFT of the current speech input speech frame.
+
+\*---------------------------------------------------------------------------*/
+
+void dft_speech(kiss_fft_cfg fft_fwd_cfg, COMP Sw[], float Sn[], float w[])
+{
+ int i;
+ COMP sw[FFT_ENC];
+
+ for(i=0; i<FFT_ENC; i++) {
+ sw[i].real = 0.0;
+ sw[i].imag = 0.0;
+ }
+
+ /* Centre analysis window on time axis, we need to arrange input
+ to FFT this way to make FFT phases correct */
+
+ /* move 2nd half to start of FFT input vector */
+
+ for(i=0; i<NW/2; i++)
+ sw[i].real = Sn[i+M/2]*w[i+M/2];
+
+ /* move 1st half to end of FFT input vector */
+
+ for(i=0; i<NW/2; i++)
+ sw[FFT_ENC-NW/2+i].real = Sn[i+M/2-NW/2]*w[i+M/2-NW/2];
+
+ kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)sw, (kiss_fft_cpx *)Sw);
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: two_stage_pitch_refinement
+ AUTHOR......: David Rowe
+ DATE CREATED: 27/5/94
+
+ Refines the current pitch estimate using the harmonic sum pitch
+ estimation technique.
+
+\*---------------------------------------------------------------------------*/
+
+void two_stage_pitch_refinement(MODEL *model, COMP Sw[])
+{
+ float pmin,pmax,pstep; /* pitch refinment minimum, maximum and step */
+
+ /* Coarse refinement */
+
+ pmax = TWO_PI/model->Wo + 5;
+ pmin = TWO_PI/model->Wo - 5;
+ pstep = 1.0;
+ hs_pitch_refinement(model,Sw,pmin,pmax,pstep);
+
+ /* Fine refinement */
+
+ pmax = TWO_PI/model->Wo + 1;
+ pmin = TWO_PI/model->Wo - 1;
+ pstep = 0.25;
+ hs_pitch_refinement(model,Sw,pmin,pmax,pstep);
+
+ /* Limit range */
+
+ if (model->Wo < TWO_PI/P_MAX)
+ model->Wo = TWO_PI/P_MAX;
+ if (model->Wo > TWO_PI/P_MIN)
+ model->Wo = TWO_PI/P_MIN;
+
+ model->L = floor(PI/model->Wo);
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: hs_pitch_refinement
+ AUTHOR......: David Rowe
+ DATE CREATED: 27/5/94
+
+ Harmonic sum pitch refinement function.
+
+ pmin pitch search range minimum
+ pmax pitch search range maximum
+ step pitch search step size
+ model current pitch estimate in model.Wo
+
+ model refined pitch estimate in model.Wo
+
+\*---------------------------------------------------------------------------*/
+
+void hs_pitch_refinement(MODEL *model, COMP Sw[], float pmin, float pmax, float pstep)
+{
+ int m; /* loop variable */
+ int b; /* bin for current harmonic centre */
+ float E; /* energy for current pitch*/
+ float Wo; /* current "test" fundamental freq. */
+ float Wom; /* Wo that maximises E */
+ float Em; /* mamimum energy */
+ float r, one_on_r; /* number of rads/bin */
+ float p; /* current pitch */
+
+ /* Initialisation */
+
+ model->L = PI/model->Wo; /* use initial pitch est. for L */
+ Wom = model->Wo;
+ Em = 0.0;
+ r = TWO_PI/FFT_ENC;
+ one_on_r = 1.0/r;
+
+ /* Determine harmonic sum for a range of Wo values */
+
+ for(p=pmin; p<=pmax; p+=pstep) {
+ E = 0.0;
+ Wo = TWO_PI/p;
+
+ /* Sum harmonic magnitudes */
+ for(m=1; m<=model->L; m++) {
+ b = (int)(m*Wo*one_on_r + 0.5);
+ E += Sw[b].real*Sw[b].real + Sw[b].imag*Sw[b].imag;
+ }
+ /* Compare to see if this is a maximum */
+
+ if (E > Em) {
+ Em = E;
+ Wom = Wo;
+ }
+ }
+
+ model->Wo = Wom;
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: estimate_amplitudes
+ AUTHOR......: David Rowe
+ DATE CREATED: 27/5/94
+
+ Estimates the complex amplitudes of the harmonics.
+
+\*---------------------------------------------------------------------------*/
+
+void estimate_amplitudes(MODEL *model, COMP Sw[], COMP W[], int est_phase)
+{
+ int i,m; /* loop variables */
+ int am,bm; /* bounds of current harmonic */
+ int b; /* DFT bin of centre of current harmonic */
+ float den; /* denominator of amplitude expression */
+ float r, one_on_r; /* number of rads/bin */
+ int offset;
+ COMP Am;
+
+ r = TWO_PI/FFT_ENC;
+ one_on_r = 1.0/r;
+
+ for(m=1; m<=model->L; m++) {
+ den = 0.0;
+ am = (int)((m - 0.5)*model->Wo*one_on_r + 0.5);
+ bm = (int)((m + 0.5)*model->Wo*one_on_r + 0.5);
+ b = (int)(m*model->Wo/r + 0.5);
+
+ /* Estimate ampltude of harmonic */
+
+ den = 0.0;
+ Am.real = Am.imag = 0.0;
+ offset = FFT_ENC/2 - (int)(m*model->Wo*one_on_r + 0.5);
+ for(i=am; i<bm; i++) {
+ den += Sw[i].real*Sw[i].real + Sw[i].imag*Sw[i].imag;
+ Am.real += Sw[i].real*W[i + offset].real;
+ Am.imag += Sw[i].imag*W[i + offset].real;
+ }
+
+ model->A[m] = sqrtf(den);
+
+ if (est_phase) {
+
+ /* Estimate phase of harmonic, this is expensive in CPU for
+ embedded devicesso we make it an option */
+
+ model->phi[m] = atan2(Sw[b].imag,Sw[b].real);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*\
+
+ est_voicing_mbe()
+
+ Returns the error of the MBE cost function for a fiven F0.
+
+ Note: I think a lot of the operations below can be simplified as
+ W[].imag = 0 and has been normalised such that den always equals 1.
+
+\*---------------------------------------------------------------------------*/
+
+float est_voicing_mbe(
+ MODEL *model,
+ COMP Sw[],
+ COMP W[],
+ COMP Sw_[], /* DFT of all voiced synthesised signal */
+ /* useful for debugging/dump file */
+ COMP Ew[], /* DFT of error */
+ float prev_Wo)
+{
+ int i,l,al,bl,m; /* loop variables */
+ COMP Am; /* amplitude sample for this band */
+ int offset; /* centers Hw[] about current harmonic */
+ float den; /* denominator of Am expression */
+ float error; /* accumulated error between original and synthesised */
+ float Wo;
+ float sig, snr;
+ float elow, ehigh, eratio;
+ float sixty;
+
+ sig = 1E-4;
+ for(l=1; l<=model->L/4; l++) {
+ sig += model->A[l]*model->A[l];
+ }
+ for(i=0; i<FFT_ENC; i++) {
+ Sw_[i].real = 0.0;
+ Sw_[i].imag = 0.0;
+ Ew[i].real = 0.0;
+ Ew[i].imag = 0.0;
+ }
+
+ Wo = model->Wo;
+ error = 1E-4;
+
+ /* Just test across the harmonics in the first 1000 Hz (L/4) */
+
+ for(l=1; l<=model->L/4; l++) {
+ Am.real = 0.0;
+ Am.imag = 0.0;
+ den = 0.0;
+ al = ceil((l - 0.5)*Wo*FFT_ENC/TWO_PI);
+ bl = ceil((l + 0.5)*Wo*FFT_ENC/TWO_PI);
+
+ /* Estimate amplitude of harmonic assuming harmonic is totally voiced */
+
+ offset = FFT_ENC/2 - l*Wo*FFT_ENC/TWO_PI + 0.5;
+ for(m=al; m<bl; m++) {
+ Am.real += Sw[m].real*W[offset+m].real;
+ Am.imag += Sw[m].imag*W[offset+m].real;
+ den += W[offset+m].real*W[offset+m].real;
+ }
+
+ Am.real = Am.real/den;
+ Am.imag = Am.imag/den;
+
+ /* Determine error between estimated harmonic and original */
+
+ offset = FFT_ENC/2 - l*Wo*FFT_ENC/TWO_PI + 0.5;
+ for(m=al; m<bl; m++) {
+ Sw_[m].real = Am.real*W[offset+m].real;
+ Sw_[m].imag = Am.imag*W[offset+m].real;
+ Ew[m].real = Sw[m].real - Sw_[m].real;
+ Ew[m].imag = Sw[m].imag - Sw_[m].imag;
+ error += Ew[m].real*Ew[m].real;
+ error += Ew[m].imag*Ew[m].imag;
+ }
+ }
+
+ snr = 10.0*log10f(sig/error);
+ if (snr > V_THRESH)
+ model->voiced = 1;
+ else
+ model->voiced = 0;
+
+ /* post processing, helps clean up some voicing errors ------------------*/
+
+ /*
+ Determine the ratio of low freqency to high frequency energy,
+ voiced speech tends to be dominated by low frequency energy,
+ unvoiced by high frequency. This measure can be used to
+ determine if we have made any gross errors.
+ */
+
+ elow = ehigh = 1E-4;
+ for(l=1; l<=model->L/2; l++) {
+ elow += model->A[l]*model->A[l];
+ }
+ for(l=model->L/2; l<=model->L; l++) {
+ ehigh += model->A[l]*model->A[l];
+ }
+ eratio = 10.0*log10f(elow/ehigh);
+
+ /* Look for Type 1 errors, strongly V speech that has been
+ accidentally declared UV */
+
+ if (model->voiced == 0)
+ if (eratio > 10.0)
+ model->voiced = 1;
+
+ /* Look for Type 2 errors, strongly UV speech that has been
+ accidentally declared V */
+
+ if (model->voiced == 1) {
+ if (eratio < -10.0)
+ model->voiced = 0;
+
+ /* A common source of Type 2 errors is the pitch estimator
+ gives a low (50Hz) estimate for UV speech, which gives a
+ good match with noise due to the close harmoonic spacing.
+ These errors are much more common than people with 50Hz3
+ pitch, so we have just a small eratio threshold. */
+
+ sixty = 60.0*TWO_PI/FS;
+ if ((eratio < -4.0) && (model->Wo <= sixty))
+ model->voiced = 0;
+ }
+ //printf(" v: %d snr: %f eratio: %3.2f %f\n",model->voiced,snr,eratio,dF0);
+
+ return snr;
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: make_synthesis_window
+ AUTHOR......: David Rowe
+ DATE CREATED: 11/5/94
+
+ Init function that generates the trapezoidal (Parzen) sythesis window.
+
+\*---------------------------------------------------------------------------*/
+
+void make_synthesis_window(float Pn[])
+{
+ int i;
+ float win;
+
+ /* Generate Parzen window in time domain */
+
+ win = 0.0;
+ for(i=0; i<N/2-TW; i++)
+ Pn[i] = 0.0;
+ win = 0.0;
+ for(i=N/2-TW; i<N/2+TW; win+=1.0/(2*TW), i++ )
+ Pn[i] = win;
+ for(i=N/2+TW; i<3*N/2-TW; i++)
+ Pn[i] = 1.0;
+ win = 1.0;
+ for(i=3*N/2-TW; i<3*N/2+TW; win-=1.0/(2*TW), i++)
+ Pn[i] = win;
+ for(i=3*N/2+TW; i<2*N; i++)
+ Pn[i] = 0.0;
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: synthesise
+ AUTHOR......: David Rowe
+ DATE CREATED: 20/2/95
+
+ Synthesise a speech signal in the frequency domain from the
+ sinusodal model parameters. Uses overlap-add with a trapezoidal
+ window to smoothly interpolate betwen frames.
+
+\*---------------------------------------------------------------------------*/
+
+void synthesise(
+ kiss_fft_cfg fft_inv_cfg,
+ float Sn_[], /* time domain synthesised signal */
+ MODEL *model, /* ptr to model parameters for this frame */
+ float Pn[], /* time domain Parzen window */
+ int shift /* flag used to handle transition frames */
+)
+{
+ int i,l,j,b; /* loop variables */
+ COMP Sw_[FFT_DEC]; /* DFT of synthesised signal */
+ COMP sw_[FFT_DEC]; /* synthesised signal */
+
+ if (shift) {
+ /* Update memories */
+ for(i=0; i<N-1; i++) {
+ Sn_[i] = Sn_[i+N];
+ }
+ Sn_[N-1] = 0.0;
+ }
+
+ for(i=0; i<FFT_DEC; i++) {
+ Sw_[i].real = 0.0;
+ Sw_[i].imag = 0.0;
+ }
+
+ /*
+ Nov 2010 - found that synthesis using time domain cos() functions
+ gives better results for synthesis frames greater than 10ms. Inverse
+ FFT synthesis using a 512 pt FFT works well for 10ms window. I think
+ (but am not sure) that the problem is related to the quantisation of
+ the harmonic frequencies to the FFT bin size, e.g. there is a
+ 8000/512 Hz step between FFT bins. For some reason this makes
+ the speech from longer frame > 10ms sound poor. The effect can also
+ be seen when synthesising test signals like single sine waves, some
+ sort of amplitude modulation at the frame rate.
+
+ Another possibility is using a larger FFT size (1024 or 2048).
+ */
+
+#define FFT_SYNTHESIS
+#ifdef FFT_SYNTHESIS
+ /* Now set up frequency domain synthesised speech */
+ for(l=1; l<=model->L; l++) {
+ //for(l=model->L/2; l<=model->L; l++) {
+ //for(l=1; l<=model->L/4; l++) {
+ b = (int)(l*model->Wo*FFT_DEC/TWO_PI + 0.5);
+ if (b > ((FFT_DEC/2)-1)) {
+ b = (FFT_DEC/2)-1;
+ }
+ Sw_[b].real = model->A[l]*cosf(model->phi[l]);
+ Sw_[b].imag = model->A[l]*sinf(model->phi[l]);
+ Sw_[FFT_DEC-b].real = Sw_[b].real;
+ Sw_[FFT_DEC-b].imag = -Sw_[b].imag;
+ }
+
+ /* Perform inverse DFT */
+
+ kiss_fft(fft_inv_cfg, (kiss_fft_cpx *)Sw_, (kiss_fft_cpx *)sw_);
+#else
+ /*
+ Direct time domain synthesis using the cos() function. Works
+ well at 10ms and 20ms frames rates. Note synthesis window is
+ still used to handle overlap-add between adjacent frames. This
+ could be simplified as we don't need to synthesise where Pn[]
+ is zero.
+ */
+ for(l=1; l<=model->L; l++) {
+ for(i=0,j=-N+1; i<N-1; i++,j++) {
+ Sw_[FFT_DEC-N+1+i].real += 2.0*model->A[l]*cos(j*model->Wo*l + model->phi[l]);
+ }
+ for(i=N-1,j=0; i<2*N; i++,j++)
+ Sw_[j].real += 2.0*model->A[l]*cos(j*model->Wo*l + model->phi[l]);
+ }
+#endif
+
+ /* Overlap add to previous samples */
+
+ for(i=0; i<N-1; i++) {
+ Sn_[i] += sw_[FFT_DEC-N+1+i].real*Pn[i];
+ }
+
+ if (shift)
+ for(i=N-1,j=0; i<2*N; i++,j++)
+ Sn_[i] = sw_[j].real*Pn[i];
+ else
+ for(i=N-1,j=0; i<2*N; i++,j++)
+ Sn_[i] += sw_[j].real*Pn[i];
+}
+
+
+static unsigned long next = 1;
+
+int codec2_rand(void) {
+ next = next * 1103515245 + 12345;
+ return((unsigned)(next/65536) % 32768);
+}
+
--- /dev/null
+/*---------------------------------------------------------------------------*\
+
+ FILE........: timer_ut.c
+ AUTHOR......: David Rowe
+ DATE CREATED: 3 Jan 2014
+
+ Unit test STM32F4 timer hardware.
+
+\*---------------------------------------------------------------------------*/
+
+/*
+ Copyright (C) 2014 David Rowe
+
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1, as
+ published by the Free Software Foundation. This program is
+ distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "stm32f4xx_gpio.h"
+#include "stm32f4xx_rcc.h"
+
+#include "gdb_stdio.h"
+
+#define TIM1_CCR3_ADDRESS 0x4001223C
+
+TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
+TIM_OCInitTypeDef TIM_OCInitStructure;
+TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
+uint16_t uhTimerPeriod;
+uint16_t aSRC_Buffer[3] = {0, 0, 0};
+
+void Timer1Config();
+#define FS 3500000
+
+int main(void){
+ Timer1Config();
+ }
+
+/* DR: TIM_Config configures a couple of I/O pins for PWM output from
+ Timer1 Channel 3. Note I dont think any of this is needed, except
+ perhaps to check timer frequency. Can be removed down the track. */
+
+/**
+ * @brief Configure the TIM1 Pins.
+ * @param None
+ * @retval None
+ */
+static void TIM_Config(void)
+{
+ GPIO_InitTypeDef GPIO_InitStructure;
+
+ /* GPIOA and GPIOB clock enable */
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
+
+ /* GPIOA Configuration: Channel 3 as alternate function push-pull */
+ /* Discovery board pin PA10 */
+
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
+ GPIO_Init(GPIOA, &GPIO_InitStructure);
+ GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_TIM1);
+
+ /* GPIOB Configuration: Channel 3N as alternate function push-pull */
+ /* Discovery board pin PB15 */
+
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
+ GPIO_Init(GPIOB, &GPIO_InitStructure);
+ GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_TIM1);
+}
+
+void Timer1Config() {
+
+ /* TIM Configuration */
+
+ TIM_Config();
+
+ /* TIM1 example -------------------------------------------------
+
+ TIM1 input clock (TIM1CLK) is set to 2 * APB2 clock (PCLK2), since APB2
+ prescaler is different from 1.
+ TIM1CLK = 2 * PCLK2
+ PCLK2 = HCLK / 2
+ => TIM1CLK = 2 * (HCLK / 2) = HCLK = SystemCoreClock
+
+ TIM1CLK = SystemCoreClock, Prescaler = 0, TIM1 counter clock = SystemCoreClock
+ SystemCoreClock is set to 168 MHz for STM32F4xx devices.
+
+ The objective is to configure TIM1 channel 3 to generate complementary PWM
+ signal with a frequency equal to F KHz:
+ - TIM1_Period = (SystemCoreClock / F) - 1
+
+ The number of this repetitive requests is defined by the TIM1 Repetion counter,
+ each 3 Update Requests, the TIM1 Channel 3 Duty Cycle changes to the next new
+ value defined by the aSRC_Buffer.
+
+ Note:
+ SystemCoreClock variable holds HCLK frequency and is defined in system_stm32f4xx.c file.
+ Each time the core clock (HCLK) changes, user had to call SystemCoreClockUpdate()
+ function to update SystemCoreClock variable value. Otherwise, any configuration
+ based on this variable will be incorrect.
+ -----------------------------------------------------------------------------*/
+
+ /* Compute the value to be set in ARR regiter to generate signal frequency at FS Hz */
+ uhTimerPeriod = (SystemCoreClock / FS ) - 1;
+ /* Compute CCR1 value to generate a duty cycle at 50% */
+ aSRC_Buffer[0] = (uint16_t) (((uint32_t) 5 * (uhTimerPeriod - 1)) / 10);
+ /* Compute CCR1 value to generate a duty cycle at 37.5% */
+ aSRC_Buffer[1] = (uint16_t) (((uint32_t) 375 * (uhTimerPeriod - 1)) / 1000);
+ /* Compute CCR1 value to generate a duty cycle at 25% */
+ aSRC_Buffer[2] = (uint16_t) (((uint32_t) 25 * (uhTimerPeriod - 1)) / 100);
+
+ /* TIM1 Peripheral Configuration -------------------------------------------*/
+ /* TIM1 clock enable */
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
+
+ /* Time Base configuration */
+
+ TIM_DeInit(TIM1);
+ TIM_TimeBaseStructure.TIM_Prescaler = 0;
+ TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
+ TIM_TimeBaseStructure.TIM_Period = uhTimerPeriod;
+ TIM_TimeBaseStructure.TIM_ClockDivision = 0;
+ TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
+
+ TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
+
+ /* Channel 3 Configuration in PWM mode */
+
+ /* I think we just ned to enable channel 3 somehow, but without
+ (or optionally with) actual ouput to a GPIO pin. */
+
+ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
+ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
+ TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
+ TIM_OCInitStructure.TIM_Pulse = aSRC_Buffer[0];
+ TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
+ TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
+ TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
+ TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
+
+ TIM_OC3Init(TIM1, &TIM_OCInitStructure);
+
+ /* Enable preload feature */
+ TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
+
+ /* Automatic Output enable, Break, dead time and lock configuration*/
+ TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
+ TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
+ //TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
+ TIM_BDTRInitStructure.TIM_DeadTime = 11;
+ //TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
+ //TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
+ TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
+
+ TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
+
+ /* TIM1 counter enable */
+ TIM_Cmd(TIM1, ENABLE);
+
+ /* Main Output Enable */
+ TIM_CtrlPWMOutputs(TIM1, ENABLE);
+}
+