1 /*
2 * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "audio/engine.h"
21 #include "audio/master_track.h"
22 #include "audio/midi.h"
23 #include "audio/track.h"
24 #include "gui/widgets/live_waveform.h"
25 #include "gui/widgets/track.h"
26 #include "gui/widgets/track_top_grid.h"
27 #include "project.h"
28 #include "utils/arrays.h"
29 #include "utils/objects.h"
30 #include "utils/cairo.h"
31 #include "zrythm_app.h"
32
33 #include "ext/zix/zix/ring.h"
34
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37
38 #define BUF_SIZE 65000
39
G_DEFINE_TYPE(LiveWaveformWidget,live_waveform_widget,GTK_TYPE_DRAWING_AREA)40 G_DEFINE_TYPE (
41 LiveWaveformWidget, live_waveform_widget,
42 GTK_TYPE_DRAWING_AREA)
43
44 static void
45 draw_lines (
46 LiveWaveformWidget * self,
47 cairo_t * cr,
48 float * lbuf,
49 float * rbuf,
50 nframes_t lstart_index,
51 nframes_t rstart_index)
52 {
53 gint width =
54 gtk_widget_get_allocated_width (
55 GTK_WIDGET (self));
56 gint height =
57 gtk_widget_get_allocated_height (
58 GTK_WIDGET (self));
59
60 /* draw */
61 gdk_cairo_set_source_rgba (
62 cr, &self->color_green);
63 float half_height = (float) height / 2.0f;
64 uint32_t nframes = AUDIO_ENGINE->block_length;
65 float val;
66 unsigned int step =
67 MAX (1, (nframes / (unsigned int) width));
68
69 for (unsigned int i = 0; i < nframes; i += step)
70 {
71 if (rbuf)
72 {
73 val =
74 MAX (
75 lbuf[lstart_index + i],
76 rbuf[rstart_index + i]);
77 }
78 else
79 {
80 val = lbuf[lstart_index + i];
81 }
82
83 val = - val;
84
85 double x = width * ((double) i / nframes);
86 double y = half_height + val * half_height;
87
88 if (i == 0)
89 {
90 cairo_move_to (cr, x, y);
91 }
92
93 cairo_line_to (cr, x, y);
94 }
95 cairo_stroke (cr);
96 }
97
98 /**
99 * Draws the color picker.
100 */
101 static int
live_waveform_draw_cb(GtkWidget * widget,cairo_t * cr,LiveWaveformWidget * self)102 live_waveform_draw_cb (
103 GtkWidget * widget,
104 cairo_t * cr,
105 LiveWaveformWidget * self)
106 {
107 if (!PROJECT || !AUDIO_ENGINE)
108 {
109 return false;
110 }
111
112 GtkStyleContext * context =
113 gtk_widget_get_style_context (widget);
114
115 gint width =
116 gtk_widget_get_allocated_width (widget);
117 gint height =
118 gtk_widget_get_allocated_height (widget);
119
120 gtk_render_background (
121 context, cr, 0, 0, width, height);
122
123 /* draw border */
124 if (self->draw_border)
125 {
126 self->color_white.alpha = 0.2;
127 gdk_cairo_set_source_rgba (
128 cr, &self->color_white);
129 z_cairo_rounded_rectangle (
130 cr, 0, 0, width, height, 1.0, 4.0);
131 cairo_stroke (cr);
132 }
133
134 size_t block_size_in_bytes =
135 sizeof (float) *
136 (size_t) AUDIO_ENGINE->block_length;
137
138 Port * port = NULL;
139 switch (self->type)
140 {
141 case LIVE_WAVEFORM_ENGINE:
142 g_return_val_if_fail (
143 IS_TRACK_AND_NONNULL (P_MASTER_TRACK),
144 false);
145 if (!P_MASTER_TRACK->channel->stereo_out->l->
146 write_ring_buffers)
147 {
148 P_MASTER_TRACK->channel->stereo_out->l->
149 write_ring_buffers = true;
150 P_MASTER_TRACK->channel->stereo_out->r->
151 write_ring_buffers = true;
152 return FALSE;
153 }
154 port = P_MASTER_TRACK->channel->stereo_out->l;
155 break;
156 case LIVE_WAVEFORM_PORT:
157 if (!self->port->write_ring_buffers)
158 {
159 self->port->write_ring_buffers = true;
160 return FALSE;
161 }
162 port = self->port;
163 break;
164 }
165
166 g_return_val_if_fail (
167 IS_PORT_AND_NONNULL (port), false);
168
169 /* if ring not ready yet skip draw */
170 if (!port->audio_ring)
171 return false;
172
173 /* get the L buffer */
174 size_t read_space_avail =
175 zix_ring_read_space (port->audio_ring);
176 size_t blocks_to_read =
177 block_size_in_bytes == 0 ?
178 0 : read_space_avail / block_size_in_bytes;
179 /* if buffer is not filled do not draw */
180 if (blocks_to_read <= 0)
181 return false;
182
183 while (read_space_avail > self->buf_sz[0])
184 {
185 array_double_size_if_full (
186 self->bufs[0], self->buf_sz[0],
187 self->buf_sz[0], float);
188 }
189 size_t lblocks_read =
190 zix_ring_peek (
191 port->audio_ring, &(self->bufs[0][0]),
192 read_space_avail);
193 lblocks_read /= block_size_in_bytes;
194 size_t lstart_index =
195 (lblocks_read - 1) * AUDIO_ENGINE->block_length;
196 if (lblocks_read == 0)
197 {
198 return FALSE;
199 /*g_return_val_if_reached (FALSE);*/
200 }
201
202 if (self->type == LIVE_WAVEFORM_ENGINE)
203 {
204 /* get the R buffer */
205 port =
206 P_MASTER_TRACK->channel->stereo_out->r;
207 read_space_avail =
208 zix_ring_read_space (port->audio_ring);
209 blocks_to_read =
210 read_space_avail / block_size_in_bytes;
211
212 /* if buffer is not filled do not draw */
213 if (blocks_to_read <= 0)
214 return false;
215
216 while (read_space_avail > self->buf_sz[1])
217 {
218 array_double_size_if_full (
219 self->bufs[1], self->buf_sz[1],
220 self->buf_sz[1], float);
221 }
222 size_t rblocks_read =
223 zix_ring_peek (
224 port->audio_ring, &(self->bufs[1][0]),
225 read_space_avail);
226 rblocks_read /= block_size_in_bytes;
227 size_t rstart_index =
228 (rblocks_read - 1) *
229 AUDIO_ENGINE->block_length;
230 if (rblocks_read == 0)
231 {
232 return FALSE;
233 /*g_return_val_if_reached (FALSE);*/
234 }
235
236 draw_lines (
237 self, cr, self->bufs[0], self->bufs[1],
238 lstart_index, rstart_index);
239 }
240 else
241 {
242 draw_lines (
243 self, cr, self->bufs[0], NULL, lstart_index,
244 0);
245 }
246
247 return FALSE;
248 }
249
250 static int
update_activity(GtkWidget * widget,GdkFrameClock * frame_clock,LiveWaveformWidget * self)251 update_activity (
252 GtkWidget * widget,
253 GdkFrameClock * frame_clock,
254 LiveWaveformWidget * self)
255 {
256 gtk_widget_queue_draw (widget);
257
258 return G_SOURCE_CONTINUE;
259 }
260
261 static void
init_common(LiveWaveformWidget * self)262 init_common (
263 LiveWaveformWidget * self)
264 {
265 self->draw_border = 1;
266
267 self->bufs[0] =
268 object_new_n (BUF_SIZE, float);
269 self->bufs[1] =
270 object_new_n (BUF_SIZE, float);
271 self->buf_sz[0] = BUF_SIZE;
272 self->buf_sz[1] = BUF_SIZE;
273
274 g_signal_connect (
275 G_OBJECT (self), "draw",
276 G_CALLBACK (live_waveform_draw_cb), self);
277
278 gtk_widget_add_tick_callback (
279 GTK_WIDGET (self),
280 (GtkTickCallback) update_activity,
281 self, NULL);
282 }
283
284 /**
285 * Creates a LiveWaveformWidget for the
286 * AudioEngine.
287 */
288 void
live_waveform_widget_setup_engine(LiveWaveformWidget * self)289 live_waveform_widget_setup_engine (
290 LiveWaveformWidget * self)
291 {
292 init_common (self);
293 self->type = LIVE_WAVEFORM_ENGINE;
294 }
295
296 /**
297 * Creates a LiveWaveformWidget for a port.
298 */
299 LiveWaveformWidget *
live_waveform_widget_new_port(Port * port)300 live_waveform_widget_new_port (
301 Port * port)
302 {
303 LiveWaveformWidget * self =
304 g_object_new (LIVE_WAVEFORM_WIDGET_TYPE, NULL);
305
306 init_common (self);
307
308 self->type = LIVE_WAVEFORM_PORT;
309 self->port = port;
310
311 return self;
312 }
313
314 static void
finalize(LiveWaveformWidget * self)315 finalize (
316 LiveWaveformWidget * self)
317 {
318 object_zero_and_free_if_nonnull (self->bufs[0]);
319 object_zero_and_free_if_nonnull (self->bufs[1]);
320
321 G_OBJECT_CLASS (
322 live_waveform_widget_parent_class)->
323 finalize (G_OBJECT (self));
324 }
325
326 static void
live_waveform_widget_init(LiveWaveformWidget * self)327 live_waveform_widget_init (
328 LiveWaveformWidget * self)
329 {
330 gtk_widget_set_tooltip_text (
331 GTK_WIDGET (self), _("Live waveform indicator"));
332 gdk_rgba_parse (&self->color_white, "white");
333 gdk_rgba_parse (&self->color_green, "#11FF44");
334 }
335
336 static void
live_waveform_widget_class_init(LiveWaveformWidgetClass * _klass)337 live_waveform_widget_class_init (
338 LiveWaveformWidgetClass * _klass)
339 {
340 GtkWidgetClass * klass = GTK_WIDGET_CLASS (_klass);
341 gtk_widget_class_set_css_name (
342 klass, "live-waveform");
343
344 GObjectClass * oklass = G_OBJECT_CLASS (klass);
345 oklass->finalize =
346 (GObjectFinalizeFunc) finalize;
347 }
348