1 /*
2  *  Copyright (C) 2005-2020 Team Kodi (https://kodi.tv)
3  *  Copyright (C) 2012 Marcel Ebmer
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSE.md for more information.
7  */
8 
9 #include "fische_internal.h"
10 
11 #include <string.h>
12 #include <thread>
13 #include <chrono>
14 #include <thread>
15 
16 #ifdef DEBUG
17 #include <stdio.h>
18 #endif
19 
create_vectors(fische * F)20 void create_vectors (fische* F)
21 {
22     struct _fische__internal_ * P = static_cast<_fische__internal_*>(F->priv);
23     P->vectorfield = fische__vectorfield_new (F,
24                      &P->init_progress,
25                      &P->init_cancel);
26     return;
27 }
28 
indicate_busy(fische * F)29 void indicate_busy (fische* F)
30 {
31     struct _fische__internal_ * P = static_cast<_fische__internal_*>(F->priv);
32     struct fische__screenbuffer* sbuf = P->screenbuffer;
33 
34     fische__point center;
35     center.x = sbuf->priv->width / 2;
36     center.y = sbuf->priv->height / 2;
37     double dim = (center.x > center.y) ? center.y / 2 : center.x / 2;
38 
39     double last = -1;
40 
41     while ( (P->init_progress < 1) && (!P->init_cancel)) {
42 
43         if ( (P->init_progress < 0) || (P->init_progress == last)) {
44             std::this_thread::sleep_for(std::chrono::microseconds(10000));
45             continue;
46         }
47 
48         last = P->init_progress;
49         double angle = P->init_progress * -2 * 3.1415 + 3.0415;
50 
51         fische__vector c1;
52         c1.x = sin (angle) * dim;
53         c1.y = cos (angle) * dim;
54 
55         fische__vector c2;
56         c2.x = sin (angle + 0.1) * dim;
57         c2.y = cos (angle + 0.1) * dim;
58 
59         fische__vector e1 = fische__vector_single (&c1);
60         fische__vector_mul (&e1, dim / 2);
61         fische__vector e2 = fische__vector_single (&c2);
62         fische__vector_mul (&e2, dim / 2);
63 
64         fische__vector c3 = c2;
65         fische__vector_sub (&c3, &e2);
66         fische__vector c4 = c1;
67         fische__vector_sub (&c4, &e1);
68 
69         fische__vector_mul (&c1, F->scale);
70         fische__vector_mul (&c2, F->scale);
71         fische__vector_mul (&c3, F->scale);
72         fische__vector_mul (&c4, F->scale);
73 
74         fische__vector_add (&c1, &center);
75         fische__vector_add (&c2, &center);
76         fische__vector_add (&c3, &center);
77         fische__vector_add (&c4, &center);
78 
79         fische__screenbuffer_lock (sbuf);
80         fische__screenbuffer_line (sbuf, c1.x, c1.y, c2.x, c2.y, 0xffffffff);
81         fische__screenbuffer_line (sbuf, c2.x, c2.y, c3.x, c3.y, 0xffffffff);
82         fische__screenbuffer_line (sbuf, c3.x, c3.y, c4.x, c4.y, 0xffffffff);
83         fische__screenbuffer_line (sbuf, c4.x, c4.y, c1.x, c1.y, 0xffffffff);
84         fische__screenbuffer_unlock (sbuf);
85     }
86 
87     return;
88 }
89 
90 struct fische *
fische_new()91 fische_new() {
92     struct fische* retval = static_cast<fische*>(malloc (sizeof (struct fische)));
93 
94     retval->used_cpus = _fische__cpu_detect_();
95     if (retval->used_cpus > 8)
96         retval->used_cpus = 8;
97 
98     retval->frame_counter = 0;
99     retval->audio_format = FISCHE_AUDIOFORMAT_FLOAT;
100     retval->pixel_format = FISCHE_PIXELFORMAT_0xAABBGGRR;
101     retval->width = 512;
102     retval->height = 256;
103     retval->read_vectors = 0;
104     retval->write_vectors = 0;
105     retval->on_beat = 0;
106     retval->nervous_mode = 0;
107     retval->blur_mode = FISCHE_BLUR_SLICK;
108     retval->line_style = FISCHE_LINESTYLE_ALPHA_SIMULATION;
109     retval->scale = 1;
110     retval->amplification = 0;
111     retval->priv = 0;
112     retval->error_text = "no error";
113 
114     return retval;
115 }
116 
117 int
fische_start(struct fische * handle)118 fische_start (struct fische* handle)
119 {
120     // plausibility checks
121     if ( (handle->used_cpus > 8) || (handle->used_cpus < 1)) {
122         handle->error_text = "CPU count out of range (1 <= used_cpus <= 8)";
123         return 1;
124     }
125 
126     if (handle->audio_format >= _FISCHE__AUDIOFORMAT_LAST_) {
127         handle->error_text = "audio format invalid";
128         return 1;
129     }
130 
131     if (handle->line_style >= _FISCHE__LINESTYLE_LAST_) {
132         handle->error_text = "line style invalid";
133         return 1;
134     }
135 
136     if (handle->frame_counter != 0) {
137         handle->error_text = "frame counter garbled";
138         return 1;
139     }
140 
141     if ( (handle->amplification < -10) || (handle->amplification > 10)) {
142         handle->error_text = "amplification value out of range (-10 <= amplification <= 10)";
143         return 1;
144     }
145 
146     if ( (handle->height < 16) || (handle->height > 2048)) {
147         handle->error_text = "height value out of range (16 <= height <= 2048)";
148         return 1;
149     }
150 
151     if ( (handle->width < 16) || (handle->width > 2048)) {
152         handle->error_text = "width value out of range (16 <= width <= 2048)";
153         return 1;
154     }
155 
156     if (handle->width % 4 != 0) {
157         handle->error_text = "width value invalid (must be a multiple of four)";
158         return 1;
159     }
160 
161     if (handle->pixel_format >= _FISCHE__PIXELFORMAT_LAST_) {
162         handle->error_text = "pixel format invalid";
163         return 1;
164     }
165 
166     if ( (handle->scale < 0.5) || (handle->scale > 2)) {
167         handle->error_text = "scale value out of range (0.5 <= scale <= 2.0)";
168         return 1;
169     }
170 
171     if (handle->blur_mode >= _FISCHE__BLUR_LAST_) {
172         handle->error_text = "blur option invalid";
173         return 1;
174     }
175 
176     // initialize private struct
177     handle->priv = malloc (sizeof (struct _fische__internal_));
178     memset (handle->priv, '\0', sizeof (struct _fische__internal_));
179     struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv);
180 
181     P->init_progress = -1;
182 
183     P->analyst = fische__analyst_new (handle);
184     P->screenbuffer = fische__screenbuffer_new (handle);
185     P->wavepainter = fische__wavepainter_new (handle);
186     P->blurengine = fische__blurengine_new (handle);
187     P->audiobuffer = fische__audiobuffer_new (handle);
188 
189     // start vector creation and busy indicator threads
190     std::thread(create_vectors, handle).detach();
191     std::thread(indicate_busy, handle).detach();
192 
193     return 0;
194 }
195 
196 uint32_t*
fische_render(struct fische * handle)197 fische_render (struct fische* handle)
198 {
199     struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv);
200 
201     // only if init completed
202     if (P->init_progress >= 1) {
203 
204         // analyse sound data
205         fische__audiobuffer_lock (P->audiobuffer);
206         fische__audiobuffer_get (P->audiobuffer);
207         int_fast8_t analysis = fische__analyst_analyse (P->analyst, P->audiobuffer->back_samples, P->audiobuffer->back_sample_count);
208 
209         // act accordingly
210         if (handle->nervous_mode) {
211             if (analysis >= 2)
212                 fische__wavepainter_change_shape (P->wavepainter);
213             if (analysis >= 1)
214                 fische__vectorfield_change (P->vectorfield);
215         } else {
216             if (analysis >= 1)
217                 fische__wavepainter_change_shape (P->wavepainter);
218             if (analysis >= 2)
219                 fische__vectorfield_change (P->vectorfield);
220         }
221 
222         if (analysis >= 3) {
223             fische__wavepainter_beat (P->wavepainter, P->analyst->frames_per_beat);
224         }
225         if (analysis >= 4) {
226             if (handle->on_beat)
227                 handle->on_beat (handle->handler, P->analyst->frames_per_beat);
228         }
229 
230         P->audio_valid = analysis >= 0 ? 1 : 0;
231 
232         fische__wavepainter_change_color (P->wavepainter, P->analyst->frames_per_beat, P->analyst->relative_energy);
233 
234 
235         // wait for blurring to be finished
236         // and swap buffers
237         fische__screenbuffer_lock (P->screenbuffer);
238         fische__blurengine_swapbuffers (P->blurengine);
239         fische__screenbuffer_unlock (P->screenbuffer);
240 
241         // draw waves
242         if (P->audio_valid)
243             fische__wavepainter_paint (P->wavepainter, P->audiobuffer->front_samples, P->audiobuffer->front_sample_count);
244 
245         // start blurring for the next frame
246         fische__blurengine_blur (P->blurengine, P->vectorfield->field);
247 
248         fische__audiobuffer_unlock (P->audiobuffer);
249     }
250 
251     handle->frame_counter ++;
252 
253     return P->screenbuffer->pixels;
254 }
255 
256 void
fische_free(struct fische * handle)257 fische_free (struct fische* handle)
258 {
259     if (!handle)
260         return;
261 
262     struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv);
263 
264     if (handle->priv) {
265         // tell init threads to quit
266         P->init_cancel = 1;
267 
268         // wait for init threads to quit
269         while (P->init_progress < 1)
270             std::this_thread::sleep_for(std::chrono::microseconds(10));
271 
272         fische__audiobuffer_free (P->audiobuffer);
273         fische__blurengine_free (P->blurengine);
274         fische__vectorfield_free (P->vectorfield);
275         fische__wavepainter_free (P->wavepainter);
276         fische__screenbuffer_free (P->screenbuffer);
277         fische__analyst_free (P->analyst);
278 
279         free (handle->priv);
280     }
281 
282     free (handle);
283 }
284 
285 void
fische_audiodata(struct fische * handle,const void * data,size_t data_size)286 fische_audiodata (struct fische* handle, const void* data, size_t data_size)
287 {
288     struct _fische__internal_* P = static_cast<_fische__internal_*>(handle->priv);
289 
290     if (NULL == P->audiobuffer)
291         return;
292 
293     fische__audiobuffer_lock (P->audiobuffer);
294     fische__audiobuffer_insert (P->audiobuffer, data, data_size);
295     fische__audiobuffer_unlock (P->audiobuffer);
296 }
297