1 /*
2     Musical Spectrum plugin for the DeaDBeeF audio player
3 
4     Copyright (C) 2015 Christian Boxdörfer <christian.boxdoerfer@posteo.de>
5 
6     Based on DeaDBeeFs stock spectrum.
7     Copyright (c) 2009-2015 Alexey Yakovenko <waker@users.sourceforge.net>
8     Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>
9 
10     This program is free software; you can redistribute it and/or
11     modify it under the terms of the GNU General Public License
12     as published by the Free Software Foundation; either version 2
13     of the License, or (at your option) any later version.
14 
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19 
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 */
24 
25 #include <sys/types.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdint.h>
29 #include <assert.h>
30 #include <math.h>
31 #include <fcntl.h>
32 #include <gtk/gtk.h>
33 
34 #include "fastftoi.h"
35 #include "config.h"
36 #include "spectrum.h"
37 #include "utils.h"
38 
39 int CALCULATED_NUM_BARS = 136;
40 
41 int
get_num_bars()42 get_num_bars ()
43 {
44     int bar_num = CALCULATED_NUM_BARS;
45     if (CONFIG_DRAW_STYLE == 1) {
46         bar_num = CALCULATED_NUM_BARS;
47     }
48     else if (CONFIG_BAR_W > 0) {
49         bar_num = CALCULATED_NUM_BARS;
50     }
51     else {
52         bar_num = CONFIG_NUM_BARS;
53     }
54     return bar_num;
55 }
56 
57 void
_memset_pattern(char * data,const void * pattern,size_t data_len,size_t pattern_len)58 _memset_pattern (char *data, const void* pattern, size_t data_len, size_t pattern_len)
59 {
60     memmove ((char *)data, pattern, pattern_len);
61     char *start = (char *)data;
62     char *current = (char *)data + pattern_len;
63     char *end = start + data_len;
64     while(current + pattern_len < end) {
65         memmove (current, start, pattern_len);
66         current += pattern_len;
67         pattern_len *= 2;
68     }
69     memmove (current, start, end-current);
70 }
71 
72 void
create_gradient_table(uint32_t * dest,GdkColor * colors,int num_colors)73 create_gradient_table (uint32_t *dest, GdkColor *colors, int num_colors)
74 {
75     if (!dest) {
76         return;
77     }
78     num_colors -= 1;
79 
80     for (int i = 0; i < GRADIENT_TABLE_SIZE; i++) {
81         double position = (double)i/GRADIENT_TABLE_SIZE;
82         /* if position > 1 then we have repetition of colors it maybe useful    */
83         if (position > 1.0) {
84             if (position - ftoi (position) == 0.0) {
85                 position = 1.0;
86             }
87             else {
88                 position = position - ftoi (position);
89             }
90         }
91 
92         double m= num_colors * position;
93         int n=(int)m; // integer of m
94         double f=m-n;  // fraction of m
95 
96         dest[i] = 0xFF000000;
97         float scale = 255/65535.f;
98         if (num_colors == 0) {
99             dest[i] = (uint32_t)(colors[0].red*scale) << 16 |
100                 (uint32_t)(colors[0].green*scale) << 8 |
101                 (uint32_t)(colors[0].blue*scale) << 0;
102         }
103         else if (n < num_colors) {
104             dest[i] = (uint32_t)((colors[n].red*scale) + f * ((colors[n+1].red*scale)-(colors[n].red*scale))) << 16 |
105                 (uint32_t)((colors[n].green*scale) + f * ((colors[n+1].green*scale)-(colors[n].green*scale))) << 8 |
106                 (uint32_t)((colors[n].blue*scale) + f * ((colors[n+1].blue*scale)-(colors[n].blue*scale))) << 0;
107         }
108         else if (n == num_colors) {
109             dest[i] = (uint32_t)(colors[n].red*scale) << 16 |
110                 (uint32_t)(colors[n].green*scale) << 8 |
111                 (uint32_t)(colors[n].blue*scale) << 0;
112         }
113         else {
114             dest[i] = 0xFFFFFFFF;
115         }
116     }
117 }
118 
119 void
create_window_table(gpointer user_data)120 create_window_table (gpointer user_data)
121 {
122     w_spectrum_t *w = user_data;
123 
124     switch (CONFIG_WINDOW) {
125         case BLACKMAN_HARRIS:
126             for (int i = 0; i < CONFIG_FFT_SIZE; i++) {
127                 // Blackman-Harris
128                 w->window[i] = 0.35875 - 0.48829 * cos(2 * M_PI * i / CONFIG_FFT_SIZE) + 0.14128 * cos(4 * M_PI * i / CONFIG_FFT_SIZE) - 0.01168 * cos(6 * M_PI * i / CONFIG_FFT_SIZE);
129             }
130             break;
131         case HANNING:
132             for (int i = 0; i < CONFIG_FFT_SIZE; i++) {
133                 // Hanning
134                 w->window[i] = (0.5 * (1 - cos (2 * M_PI * i / CONFIG_FFT_SIZE)));
135             }
136             break;
137         default:
138             break;
139     }
140 }
141 
142 void
update_num_bars(gpointer user_data)143 update_num_bars (gpointer user_data)
144 {
145     w_spectrum_t *w = user_data;
146 
147     GtkAllocation a;
148     gtk_widget_get_allocation (w->drawarea, &a);
149 
150     CALCULATED_NUM_BARS = 136;
151     if (CONFIG_DRAW_STYLE == 1) {
152         CALCULATED_NUM_BARS = CLAMP (a.width, 1, MAX_BARS);
153     }
154     else if (CONFIG_BAR_W > 0) {
155         int added_bar_w = CONFIG_BAR_W;
156         if (CONFIG_GAPS)
157             added_bar_w += 1;
158         CALCULATED_NUM_BARS = CLAMP (a.width/added_bar_w, 1, MAX_BARS);
159     }
160 }
161 
162 void
create_frequency_table(gpointer user_data)163 create_frequency_table (gpointer user_data)
164 {
165     w_spectrum_t *w = user_data;
166     w->low_res_end = 0;
167 
168     update_num_bars (w);
169     const int num_bars = get_num_bars ();
170     const double ratio = num_bars / 132.0;
171     const double a4pos = 57.0 * ratio;
172     const double octave = 12.0 * ratio;
173 
174     for (int i = 0; i < num_bars; i++) {
175         w->freq[i] = 440.0 * pow (2.0, (double)(i-a4pos)/octave);
176         w->keys[i] = ftoi (w->freq[i] * CONFIG_FFT_SIZE/(float)w->samplerate);
177         if (i > 0 && w->keys[i-1] == w->keys[i])
178             w->low_res_end = i;
179     }
180 }
181 
182 float
linear_interpolate(float y1,float y2,float mu)183 linear_interpolate (float y1, float y2, float mu)
184 {
185        return (y1 * (1 - mu) + y2 * mu);
186 }
187 
188 float
lagrange_interpolate(float y0,float y1,float y2,float y3,float x)189 lagrange_interpolate (float y0, float y1, float y2, float y3, float x)
190 {
191     const float a0 = ((x - 1) * (x - 2) * (x - 3)) / -6 * y0;
192     const float a1 = ((x - 0) * (x - 2) * (x - 3)) /  2 * y1;
193     const float a2 = ((x - 0) * (x - 1) * (x - 3)) / -2 * y2;
194     const float a3 = ((x - 0) * (x - 1) * (x - 2)) /  6 * y3;
195     return (a0 + a1 + a2 + a3);
196 }
197