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