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