OSDN Git Service

Added a new project.
[bluetank/bluetank.git] / firm / bare_metal / fft_graphics_speana / main.c
1 /**
2  * @file main.c
3  * @author Copyright(C) 2012 Shinichiro Nakamura
4  * @author Copyright(C) 2012 Maduki Kanazawa
5  * @brief BlueTank ACB-BF592 Application Sample Codes.
6  */
7
8 /*
9  * ===============================================================
10  *  BlueTank
11  * ===============================================================
12  * Copyright (c) 2012 Shinichiro Nakamura
13  *
14  * Permission is hereby granted, free of charge, to any person
15  * obtaining a copy of this software and associated documentation
16  * files (the "Software"), to deal in the Software without
17  * restriction, including without limitation the rights to use,
18  * copy, modify, merge, publish, distribute, sublicense, and/or
19  * sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following
21  * conditions:
22  *
23  * The above copyright notice and this permission notice shall be
24  * included in all copies or substantial portions of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
28  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33  * OTHER DEALINGS IN THE SOFTWARE.
34  * ===============================================================
35  */
36
37 #include <string.h>
38 #include "uzura.h"
39 #include "effect.h"
40 #include "system.h"
41 #include "lcd.h"
42 #include "led.h"
43 #include "rotenc.h"
44 #include <window.h>
45 #include <filter.h>
46 #include <math.h>
47
48 /**
49  * @brief FFT calculation size.
50  */
51 #define FFT_CALCSIZE    (64)
52
53 /**
54  * @brief FFT display bands.
55  */
56 #define FFT_DISPBANDS   (40)
57
58 /**
59  * @brief sampling frequency.
60  */
61 #define SAMPLE_FREQ     (48000)
62
63 /**
64  * @brief Check the band.
65  *
66  * @param N band index.
67  */
68 #define IS_VALID_BAND(N) (((SAMPLE_FREQ / FFT_CALCSIZE) * (N)) < (SAMPLE_FREQ / 2.00))
69
70 /**
71  * @note
72  * FFT calculation size given by FFT_CALCSIZE.
73  * The frequency resolution (fr) of the FFT given by (fs / FFT_CALCSIZE).
74  *
75  * =================================================
76  *                              fmax[Hz]    fmax[Hz]
77  * FFT_CALCSIZE     fr[Hz]       8 bands    40 bands
78  * =================================================
79  *  16              3000.0        24,000     120,000
80  *  32              1500.0        12,000      60,000
81  *  64               750.0         6,000      30,000
82  * 128               375.0         3,000      15,000
83  * 256               187.5         1,500       7,500
84  * =================================================
85  *
86  * (Example) fs = 48000, FFT_CALCSIZE = 128
87  * The frequency resolution (fr) is fr = (fs / FFT_CALCSIZE) = 48000 / 128 = 375[Hz].
88  * The maximum display frequency in FFT_BANDS = 40 is fmax = (fr * FFT_BANDS) = 375 * 40 = 15000[Hz].
89  */
90
91 /**
92  * @brief FFT calculation data structure.
93  */
94 typedef struct {
95     fract16 window[FFT_CALCSIZE];
96     fract16 input[FFT_CALCSIZE];
97     complex_fract16 output[FFT_CALCSIZE];
98     complex_fract16 twiddle[FFT_CALCSIZE / 2];
99 } fft_t;
100
101 typedef struct {
102     UZURA uzura;
103     int volume;
104     char volmsg[9];
105     fft_t fft;
106     FontSet fontset;
107 } work_t;
108
109 static void update_volmsg(work_t *w, const int vol)
110 {
111     int nnn = (vol == 100) ? 1 : 0;
112     int  nn = (vol == 100) ? 0 : (vol / 10);
113     int   n = (vol == 100) ? 0 : (vol - (nn * 10));
114     w->volmsg[5] = '0' + nnn;
115     w->volmsg[6] = '0' +  nn;
116     w->volmsg[7] = '0' +   n;
117 }
118
119 static fract16 lrmix(int32_t left, int32_t right, fract16 window)
120 {
121     int32_t val;
122     asm("%1 >>>= 1; %2 >>>= 1; %1 = %1 + %2; %0.L = %1(RND); %0.L = %0.L * %3.L; %0 = %0.L(X);" : "=d"(val) : "d"(left), "d"(right), "d"(window));
123     return (fract16)val;
124 }
125
126 static void effect_fft(
127         UZURA *p,
128         const int32_t *src, int32_t *des, int32_t count)
129 {
130     int32_t lc, ld;
131     int block_exp;
132
133     /*
134      * Get the user data from the UZURA framework.
135      */
136     work_t *w = (work_t *)UZURA_USER_DATA(p);
137     fract16 *window = w->fft.window;
138     fract16 *input = w->fft.input;
139     complex_fract16 *output = w->fft.output;
140     complex_fract16 *twiddle = w->fft.twiddle;
141
142     /*
143      * Copy data to the destination buffer.
144      */
145     effect_through(p, src, des, count);
146
147     /*
148      * Combine the L/R channel data with the window function.
149      * The destination data bit width is 16bit.
150      */
151     for (lc = 0, ld = 0; lc < FFT_CALCSIZE; lc++, ld += 2) {
152         input[lc] = lrmix(src[ld], src[ld + 1], window[lc]);
153     }
154
155     /*
156      * 16bit real FFT
157      */
158     rfft_fr16(input, output, twiddle, 1, FFT_CALCSIZE, &block_exp, 1);
159 }
160
161 static void system_speana(UZURA *p)
162 {
163     int32_t lc;
164     float re, im, ps;
165     int32_t level;
166     static uint32_t refresh_target = 0;
167
168     /*
169      * Get the user data from the UZURA framework.
170      */
171     work_t *w = (work_t *)UZURA_USER_DATA(p);
172     complex_fract16 *output = w->fft.output;
173
174     /*
175      * Calculate the FFT in floating point.
176      * We need to convert it to fixed point if we need to keep a performance.
177      */
178
179     /*
180      * Calculate the power spectrum.
181      */
182     lc = refresh_target % FFT_DISPBANDS;
183     re = (float)output[lc].re / (float)32768;
184     im = (float)output[lc].im / (float)32768;
185     ps = sqrtf(re * re + im * im);
186
187     /*
188      * Round out the result.
189      */
190     ps = fmax(ps, 0.000030517578125);
191
192     /*
193      * Convert it to dB range.
194      * Normalize it to -10.4 to 0.35.
195      */
196     ps = (float)(20.0 / 8.7) * log10f(ps);
197
198     /*
199      * Clipping in 0 to 7.
200      */
201     level = (int32_t)ps + 8;
202     if (level < 0) {
203       level = 0;
204     }
205     if (7 < level) {
206       level = 7;
207     }
208
209     /*
210      * Update the LCD fonts.
211      */
212     if (lc == 0) {
213       lcd_font_init(&(w->fontset));
214     }
215     if (IS_VALID_BAND(lc)) {
216       lcd_font_draw_line(
217           &(w->fontset), (UserFont)lc / LCD_FONT_WIDTH,
218           lc % LCD_FONT_WIDTH, (LCD_FONT_HEIGHT - 1) - level,
219           lc % LCD_FONT_WIDTH, (LCD_FONT_HEIGHT - 1),
220           1);
221     } else {
222       lcd_font_draw_line(
223           &(w->fontset), (UserFont)lc / LCD_FONT_WIDTH,
224           lc % LCD_FONT_WIDTH, (LCD_FONT_HEIGHT - 1),
225           lc % LCD_FONT_WIDTH, (LCD_FONT_HEIGHT - 1),
226           1);
227     }
228     if (((lc + 1) % LCD_FONT_WIDTH) == 0) {
229       lcd_font_setup_single(&(w->fontset), (UserFont)lc / LCD_FONT_WIDTH);
230     }
231
232     /*
233      * Update the volume display.
234      */
235     static int prev_volume = 0;
236     if (prev_volume != w->volume) {
237       update_volmsg(w, w->volume);
238       lcd_goto(0, 1);
239       lcd_puts(w->volmsg);
240       prev_volume = w->volume;
241     }
242
243     refresh_target++;
244 }
245
246 static void rotenc_callback(RotencAction action, void *extobj)
247 {
248     work_t *w = (work_t *)extobj;
249     if (RotencActionPush == action) {
250         // Nothing to do.
251     }
252     if (RotencActionLeft == action) {
253         if (0 < w->volume) {
254             w->volume--;
255             effect_param_volume(w->volume / 100.0, w->volume / 100.0);
256         }
257     }
258     if (RotencActionRight == action) {
259         if (w->volume < 100) {
260             w->volume++;
261             effect_param_volume(w->volume / 100.0, w->volume / 100.0);
262         }
263     }
264 }
265
266 int main(void)
267 {
268     work_t w;
269
270     w.volume = 100;
271     strcpy(w.volmsg, "VOL: 100");
272     lcd_goto(0, 0);
273     lcd_puts("\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F");
274
275     /*
276      * Generate window.
277      */
278 #if 0
279     /*
280      * Select window function from the three types of the window.
281      */
282     gen_hamming_fr16(w.fft.window, 1, FFT_CALCSIZE);
283     gen_rectangular_fr16(w.fft.window, 1, FFT_CALCSIZE);
284     gen_hanning_fr16(w.fft.window, 1, FFT_CALCSIZE);
285 #else
286     gen_hanning_fr16(w.fft.window, 1, FFT_CALCSIZE);
287 #endif
288
289     /*
290      * Generate twiddle factor table.
291      */
292     twidfftrad2_fr16(w.fft.twiddle, FFT_CALCSIZE);
293
294     effect_param_init();
295     uzura_init(&w.uzura, &w);
296     uzura_set_effect(&w.uzura, effect_fft);
297     uzura_set_system(&w.uzura, system_speana);
298     led_write(LedTargetR, 1);
299     led_write(LedTargetG, 1);
300     rotenc_init(rotenc_callback, &w);
301     uzura_execute(&w.uzura);
302
303     return 0;
304 }
305