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 <math.h>
21 #include <stdlib.h>
22 
23 #include "audio/engine.h"
24 #include "audio/quantize_options.h"
25 #include "audio/position.h"
26 #include "audio/snap_grid.h"
27 #include "audio/transport.h"
28 #include "project.h"
29 #include "utils/algorithms.h"
30 #include "utils/objects.h"
31 #include "utils/pcg_rand.h"
32 #include "zrythm.h"
33 
34 #include <gtk/gtk.h>
35 
36 /**
37  * Updates snap points.
38  */
39 void
quantize_options_update_quantize_points(QuantizeOptions * self)40 quantize_options_update_quantize_points (
41   QuantizeOptions * self)
42 {
43   Position tmp, end_pos;
44   position_init (&tmp);
45   position_set_to_bar (
46     &end_pos,
47     TRANSPORT->total_bars + 1);
48   self->num_q_points = 0;
49   position_set_to_pos (
50     &self->q_points[self->num_q_points++],
51     &tmp);
52   long ticks =
53     snap_grid_get_ticks_from_length_and_type (
54       self->note_length,
55       self->note_type);
56   long swing_offset =
57     (long)
58     (((float) self->swing / 100.f) *
59     (float) ticks / 2.f);
60   while (position_is_before (&tmp, &end_pos))
61     {
62       position_add_ticks (
63         &tmp, ticks);
64 
65       /* delay every second point by swing */
66       if ((self->num_q_points + 1) % 2 == 0)
67         {
68           position_add_ticks (
69             &tmp, swing_offset);
70         }
71 
72       position_set_to_pos (
73         &self->q_points[self->num_q_points++],
74         &tmp);
75     }
76 }
77 
78 void
quantize_options_init(QuantizeOptions * self,NoteLength note_length)79 quantize_options_init (
80   QuantizeOptions * self,
81   NoteLength        note_length)
82 {
83   self->schema_version =
84     QUANTIZE_OPTIONS_SCHEMA_VERSION;
85   self->note_length = note_length;
86   self->num_q_points = 0;
87   self->note_type = NOTE_TYPE_NORMAL;
88   self->amount = 100;
89   self->adj_start = 1;
90   self->adj_end = 0;
91   self->swing = 0;
92   self->rand_ticks = 0;
93 }
94 
95 float
quantize_options_get_swing(QuantizeOptions * self)96 quantize_options_get_swing (
97   QuantizeOptions * self)
98 {
99   return self->swing;
100 }
101 
102 float
quantize_options_get_amount(QuantizeOptions * self)103 quantize_options_get_amount (
104   QuantizeOptions * self)
105 {
106   return self->amount;
107 }
108 
109 float
quantize_options_get_randomization(QuantizeOptions * self)110 quantize_options_get_randomization (
111   QuantizeOptions * self)
112 {
113   return (float) self->rand_ticks;
114 }
115 
116 void
quantize_options_set_swing(QuantizeOptions * self,float swing)117 quantize_options_set_swing (
118   QuantizeOptions * self,
119   float             swing)
120 {
121   self->swing = swing;
122 }
123 
124 void
quantize_options_set_amount(QuantizeOptions * self,float amount)125 quantize_options_set_amount (
126   QuantizeOptions * self,
127   float             amount)
128 {
129   self->amount = amount;
130 }
131 
132 void
quantize_options_set_randomization(QuantizeOptions * self,float randomization)133 quantize_options_set_randomization (
134   QuantizeOptions * self,
135   float             randomization)
136 {
137   self->rand_ticks =
138     (unsigned int) round (randomization);
139 }
140 
141 /**
142  * Returns the current options as a human-readable
143  * string.
144  *
145  * Must be free'd.
146  */
147 char *
quantize_options_stringize(NoteLength note_length,NoteType note_type)148 quantize_options_stringize (
149   NoteLength note_length,
150   NoteType   note_type)
151 {
152   return
153     snap_grid_stringize (
154       note_length, note_type);
155 }
156 
157 static Position *
get_prev_point(QuantizeOptions * self,Position * pos)158 get_prev_point (
159   QuantizeOptions * self,
160   Position *        pos)
161 {
162   g_return_val_if_fail (
163     pos->frames >= 0 && pos->ticks >= 0, NULL);
164 
165   Position * prev_point =
166     (Position *)
167     algorithms_binary_search_nearby (
168       pos, self->q_points,
169       (size_t) self->num_q_points,
170       sizeof (Position), position_cmp_func,
171       true, true);
172 
173   return prev_point;
174 }
175 
176 static Position *
get_next_point(QuantizeOptions * self,Position * pos)177 get_next_point (
178   QuantizeOptions * self,
179   Position *        pos)
180 {
181   g_return_val_if_fail (
182     pos->frames >= 0 && pos->ticks >= 0, NULL);
183 
184   Position * next_point =
185     (Position *)
186     algorithms_binary_search_nearby (
187       pos, self->q_points,
188       (size_t) self->num_q_points,
189       sizeof (Position), position_cmp_func,
190       false, true);
191 
192   return next_point;
193 }
194 
195 /**
196  * Quantizes the given Position using the given
197  * QuantizeOptions.
198  *
199  * This assumes that the start/end check has been
200  * done already and it ignores the adjust_start and
201  * adjust_end options.
202  *
203  * @return The amount of ticks moved (negative for
204  *   backwards).
205  */
206 double
quantize_options_quantize_position(QuantizeOptions * self,Position * pos)207 quantize_options_quantize_position (
208   QuantizeOptions * self,
209   Position *        pos)
210 {
211   Position * prev_point =
212     get_prev_point (self, pos);
213   Position * next_point =
214     get_next_point (self, pos);
215   g_return_val_if_fail (
216     prev_point && next_point, 0);
217 
218   const double upper = self->rand_ticks;
219   const double lower = - self->rand_ticks;
220   double rand_double =
221     (double) pcg_rand_u32 (ZRYTHM->rand);
222   double rand_ticks =
223     fmod (rand_double, (upper - lower + 1.0)) + lower;
224 
225   /* if previous point is closer */
226   double diff;
227   if (pos->ticks - prev_point->ticks <=
228       next_point->ticks - pos->ticks)
229     {
230       diff = prev_point->ticks - pos->ticks;
231     }
232   /* if next point is closer */
233   else
234     {
235       diff = next_point->ticks - pos->ticks;
236     }
237 
238   /* multiply by amount */
239   diff =
240     (diff * (double) (self->amount / 100.f));
241 
242   /* add random ticks */
243   diff += rand_ticks;
244 
245   /* quantize position */
246   position_add_ticks (pos, diff);
247 
248   return diff;
249 }
250 
251 /**
252  * Clones the QuantizeOptions.
253  */
254 QuantizeOptions *
quantize_options_clone(const QuantizeOptions * src)255 quantize_options_clone (
256   const QuantizeOptions * src)
257 {
258   QuantizeOptions * opts =
259     object_new (QuantizeOptions);
260   opts->schema_version =
261     QUANTIZE_OPTIONS_SCHEMA_VERSION;
262 
263   opts->note_length = src->note_length;
264   opts->note_type = src->note_type;
265   opts->amount = src->amount;
266   opts->adj_start = src->adj_start;
267   opts->adj_end = src->adj_end;
268   opts->swing = src->swing;
269   opts->rand_ticks = src->rand_ticks;
270 
271   quantize_options_update_quantize_points (opts);
272 
273   return opts;
274 }
275 
276 QuantizeOptions *
quantize_options_new(void)277 quantize_options_new (void)
278 {
279   QuantizeOptions * opts =
280     object_new (QuantizeOptions);
281   opts->schema_version =
282     QUANTIZE_OPTIONS_SCHEMA_VERSION;
283 
284   return opts;
285 }
286 
287 /**
288  * Free's the QuantizeOptions.
289  */
290 void
quantize_options_free(QuantizeOptions * self)291 quantize_options_free (
292   QuantizeOptions * self)
293 {
294   object_zero_and_free (self);
295 }
296