1 /*
2 * Copyright (C) 2020-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/exporter.h"
22 #include "audio/marker_track.h"
23 #include "gui/backend/timeline_selections.h"
24 #include "gui/backend/tracklist_selections.h"
25 #include "gui/widgets/bounce_step_selector.h"
26 #include "gui/widgets/dialogs/bounce_dialog.h"
27 #include "gui/widgets/dialogs/export_progress_dialog.h"
28 #include "project.h"
29 #include "settings/settings.h"
30 #include "utils/flags.h"
31 #include "utils/io.h"
32 #include "utils/resources.h"
33 #include "utils/ui.h"
34 #include "zrythm_app.h"
35
36 #include <gtk/gtk.h>
37
G_DEFINE_TYPE(BounceDialogWidget,bounce_dialog_widget,GTK_TYPE_DIALOG)38 G_DEFINE_TYPE (
39 BounceDialogWidget,
40 bounce_dialog_widget,
41 GTK_TYPE_DIALOG)
42
43 static void
44 on_cancel_clicked (GtkButton * btn,
45 BounceDialogWidget * self)
46 {
47 gtk_window_close (GTK_WINDOW (self));
48 /*gtk_widget_destroy (GTK_WIDGET (self));*/
49 }
50
51 static void
on_bounce_clicked(GtkButton * btn,BounceDialogWidget * self)52 on_bounce_clicked (
53 GtkButton * btn,
54 BounceDialogWidget * self)
55 {
56 ExportSettings settings;
57
58 switch (self->type)
59 {
60 case BOUNCE_DIALOG_REGIONS:
61 settings.mode = EXPORT_MODE_REGIONS;
62 break;
63 case BOUNCE_DIALOG_TRACKS:
64 settings.mode = EXPORT_MODE_TRACKS;
65 break;
66 }
67
68 if (self->bounce_to_file)
69 {
70 /* TODO */
71 }
72 else
73 {
74 export_settings_set_bounce_defaults (
75 &settings, NULL, self->bounce_name);
76 }
77
78 Position start_pos;
79 position_init (&start_pos);
80 switch (self->type)
81 {
82 case BOUNCE_DIALOG_REGIONS:
83 timeline_selections_mark_for_bounce (
84 TL_SELECTIONS,
85 settings.bounce_with_parents);
86 settings.mode = EXPORT_MODE_REGIONS;
87 arranger_selections_get_start_pos (
88 (ArrangerSelections *) TL_SELECTIONS,
89 &start_pos, F_GLOBAL);
90 break;
91 case BOUNCE_DIALOG_TRACKS:
92 {
93 tracklist_selections_mark_for_bounce (
94 TRACKLIST_SELECTIONS,
95 settings.bounce_with_parents,
96 F_NO_MARK_MASTER);
97 settings.mode = EXPORT_MODE_TRACKS;
98
99 /* start at start marker */
100 Marker * m =
101 marker_track_get_start_marker (
102 P_MARKER_TRACK);
103 ArrangerObject * m_obj =
104 (ArrangerObject *) m;
105 position_set_to_pos (
106 &start_pos, &m_obj->pos);
107 }
108 break;
109 }
110
111 /* start exporting in a new thread */
112 GThread * thread =
113 g_thread_new (
114 "bounce_thread",
115 (GThreadFunc) exporter_generic_export_thread,
116 &settings);
117
118 /* create a progress dialog and block */
119 ExportProgressDialogWidget * progress_dialog =
120 export_progress_dialog_widget_new (
121 &settings, true, false, F_CANCELABLE);
122 gtk_window_set_transient_for (
123 GTK_WINDOW (progress_dialog),
124 GTK_WINDOW (self));
125 gtk_dialog_run (GTK_DIALOG (progress_dialog));
126 gtk_widget_destroy (GTK_WIDGET (progress_dialog));
127
128 g_thread_join (thread);
129
130 if (!self->bounce_to_file &&
131 !settings.progress_info.has_error &&
132 !settings.progress_info.cancelled)
133 {
134 /* create audio track with bounced material */
135 exporter_create_audio_track_after_bounce (
136 &settings, &start_pos);
137 }
138
139 export_settings_free_members (&settings);
140
141 gtk_window_close (GTK_WINDOW (self));
142 }
143
144 static void
on_tail_value_changed(GtkSpinButton * spin,BounceDialogWidget * self)145 on_tail_value_changed (
146 GtkSpinButton * spin,
147 BounceDialogWidget * self)
148 {
149 g_settings_set_int (
150 S_UI, "bounce-tail",
151 gtk_spin_button_get_value_as_int (spin));
152 }
153
154 static void
on_bounce_with_parents_toggled(GtkToggleButton * btn,BounceDialogWidget * self)155 on_bounce_with_parents_toggled (
156 GtkToggleButton * btn,
157 BounceDialogWidget * self)
158 {
159 bool toggled = gtk_toggle_button_get_active (btn);
160 gtk_widget_set_visible (
161 GTK_WIDGET (self->bounce_step_box), !toggled);
162 g_settings_set_boolean (
163 S_UI, "bounce-with-parents", toggled);
164 }
165
166 static void
on_disable_after_bounce_toggled(GtkToggleButton * btn,BounceDialogWidget * self)167 on_disable_after_bounce_toggled (
168 GtkToggleButton * btn,
169 BounceDialogWidget * self)
170 {
171 bool toggled = gtk_toggle_button_get_active (btn);
172 g_settings_set_boolean (
173 S_UI, "disable-after-bounce", toggled);
174 }
175
176 /**
177 * Creates a bounce dialog.
178 */
179 BounceDialogWidget *
bounce_dialog_widget_new(BounceDialogWidgetType type,const char * bounce_name)180 bounce_dialog_widget_new (
181 BounceDialogWidgetType type,
182 const char * bounce_name)
183 {
184 BounceDialogWidget * self =
185 g_object_new (BOUNCE_DIALOG_WIDGET_TYPE, NULL);
186
187 self->type = type;
188 /* TODO free when destroying */
189 self->bounce_name = g_strdup (bounce_name);
190
191 self->bounce_step_selector =
192 bounce_step_selector_widget_new ();
193 gtk_container_add (
194 GTK_CONTAINER (self->bounce_step_box),
195 GTK_WIDGET (self->bounce_step_selector));
196
197 g_signal_connect (
198 G_OBJECT (self->tail_spin), "value-changed",
199 G_CALLBACK (on_tail_value_changed), self);
200 g_signal_connect (
201 G_OBJECT (self->bounce_with_parents), "toggled",
202 G_CALLBACK (on_bounce_with_parents_toggled),
203 self);
204
205 if (type == BOUNCE_DIALOG_TRACKS)
206 {
207 g_signal_connect (
208 G_OBJECT (self->disable_after_bounce),
209 "toggled",
210 G_CALLBACK (on_disable_after_bounce_toggled),
211 self);
212 }
213 else
214 {
215 /* hide 'disable after bounce' if not bouncing
216 * tracks */
217 gtk_widget_set_visible (
218 GTK_WIDGET (self->disable_after_bounce),
219 false);
220 }
221
222 return self;
223 }
224
225 static void
bounce_dialog_widget_class_init(BounceDialogWidgetClass * _klass)226 bounce_dialog_widget_class_init (
227 BounceDialogWidgetClass * _klass)
228 {
229 GtkWidgetClass * klass = GTK_WIDGET_CLASS (_klass);
230 resources_set_class_template (
231 klass, "bounce_dialog.ui");
232
233 #define BIND_CHILD(x) \
234 gtk_widget_class_bind_template_child ( \
235 klass, BounceDialogWidget, x)
236
237 BIND_CHILD (cancel_btn);
238 BIND_CHILD (bounce_btn);
239 BIND_CHILD (bounce_with_parents);
240 BIND_CHILD (bounce_step_box);
241 BIND_CHILD (tail_spin);
242 BIND_CHILD (disable_after_bounce);
243
244 gtk_widget_class_bind_template_callback (
245 klass, on_cancel_clicked);
246 gtk_widget_class_bind_template_callback (
247 klass, on_bounce_clicked);
248 }
249
250 static void
bounce_dialog_widget_init(BounceDialogWidget * self)251 bounce_dialog_widget_init (
252 BounceDialogWidget * self)
253 {
254 gtk_widget_init_template (GTK_WIDGET (self));
255
256 gtk_spin_button_set_value (
257 self->tail_spin,
258 (double)
259 g_settings_get_int (S_UI, "bounce-tail"));
260
261 bool bounce_with_parents =
262 g_settings_get_boolean (
263 S_UI, "bounce-with-parents");
264 gtk_toggle_button_set_active (
265 GTK_TOGGLE_BUTTON (self->bounce_with_parents),
266 bounce_with_parents);
267 gtk_widget_set_visible (
268 GTK_WIDGET (self->bounce_step_box),
269 !bounce_with_parents);
270
271 bool disable_after_bounce =
272 g_settings_get_boolean (
273 S_UI, "disable-after-bounce");
274 gtk_toggle_button_set_active (
275 GTK_TOGGLE_BUTTON (self->disable_after_bounce),
276 disable_after_bounce);
277 }
278