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/chord_region.h"
21 #include "audio/chord_object.h"
22 #include "audio/chord_track.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "project.h"
26 #include "utils/arrays.h"
27 #include "utils/flags.h"
28 #include "utils/mem.h"
29 #include "utils/object_utils.h"
30 #include "utils/objects.h"
31 #include "zrythm_app.h"
32
33 #include <glib/gi18n.h>
34
35 /**
36 * Creates a new ZRegion for chords.
37 *
38 * @param idx Index inside chord track.
39 */
40 ZRegion *
chord_region_new(const Position * start_pos,const Position * end_pos,int idx)41 chord_region_new (
42 const Position * start_pos,
43 const Position * end_pos,
44 int idx)
45 {
46 ZRegion * self = object_new (ZRegion);
47
48 self->chord_objects_size = 1;
49 self->chord_objects =
50 object_new_n (
51 self->chord_objects_size, ChordObject *);
52
53 self->id.type = REGION_TYPE_CHORD;
54
55 region_init (
56 self, start_pos, end_pos,
57 track_get_name_hash (P_CHORD_TRACK),
58 0, idx);
59
60 g_warn_if_fail (IS_REGION (self));
61
62 return self;
63 }
64
65 /**
66 * Inserts a ChordObject to the Region.
67 */
68 void
chord_region_insert_chord_object(ZRegion * self,ChordObject * chord,int pos,bool fire_events)69 chord_region_insert_chord_object (
70 ZRegion * self,
71 ChordObject * chord,
72 int pos,
73 bool fire_events)
74 {
75 g_return_if_fail (IS_REGION (self));
76
77 char str[500];
78 ChordDescriptor * cd =
79 chord_object_get_chord_descriptor (chord);
80 chord_descriptor_to_string (cd, str);
81 g_message (
82 "inserting chord '%s' (index %d) to "
83 "region '%s' at pos %d",
84 str, chord->index, self->name, pos);
85
86 array_double_size_if_full (
87 self->chord_objects, self->num_chord_objects,
88 self->chord_objects_size, ChordObject *);
89 array_insert (
90 self->chord_objects, self->num_chord_objects,
91 pos, chord);
92
93 for (int i = pos; i < self->num_chord_objects; i++)
94 {
95 ChordObject * co = self->chord_objects[i];
96 chord_object_set_region_and_index (
97 co, self, i);
98 }
99
100 if (fire_events)
101 {
102 EVENTS_PUSH (
103 ET_ARRANGER_OBJECT_CREATED, chord);
104 }
105 }
106
107 /**
108 * Adds a ChordObject to the Region.
109 */
110 void
chord_region_add_chord_object(ZRegion * self,ChordObject * chord,bool fire_events)111 chord_region_add_chord_object (
112 ZRegion * self,
113 ChordObject * chord,
114 bool fire_events)
115 {
116 chord_region_insert_chord_object (
117 self, chord, self->num_chord_objects,
118 fire_events);
119 }
120
121 /**
122 * Removes a ChordObject from the Region.
123 *
124 * @param free Optionally free the ChordObject.
125 */
126 void
chord_region_remove_chord_object(ZRegion * self,ChordObject * chord,int free,bool fire_events)127 chord_region_remove_chord_object (
128 ZRegion * self,
129 ChordObject * chord,
130 int free,
131 bool fire_events)
132 {
133 g_return_if_fail (
134 IS_REGION (self) && IS_CHORD_OBJECT (chord));
135
136 char str[500];
137 ChordDescriptor * cd =
138 chord_object_get_chord_descriptor (chord);
139 chord_descriptor_to_string (cd, str);
140 g_message (
141 "removing chord '%s' (index %d) from "
142 "region '%s'",
143 str, chord->index, self->name);
144
145 /* deselect */
146 if (CHORD_SELECTIONS)
147 {
148 arranger_object_select (
149 (ArrangerObject *) chord, F_NO_SELECT,
150 F_APPEND,
151 F_NO_PUBLISH_EVENTS);
152 }
153
154 int pos = -1;
155 array_delete_return_pos (
156 self->chord_objects, self->num_chord_objects,
157 chord, pos);
158 g_return_if_fail (pos >= 0);
159
160 for (int i = pos; i < self->num_chord_objects; i++)
161 {
162 chord_object_set_region_and_index (
163 self->chord_objects[i], self, i);
164 }
165
166 if (free)
167 {
168 free_later (chord, arranger_object_free);
169 }
170
171 if (fire_events)
172 {
173 EVENTS_PUSH (
174 ET_ARRANGER_OBJECT_REMOVED,
175 ARRANGER_OBJECT_TYPE_CHORD_OBJECT);
176 }
177 }
178
179 bool
chord_region_validate(ZRegion * self)180 chord_region_validate (
181 ZRegion * self)
182 {
183 for (int i = 0; i < self->num_chord_objects; i++)
184 {
185 ChordObject * c = self->chord_objects[i];
186
187 g_return_val_if_fail (c->index == i, false);
188 }
189
190 return true;
191 }
192
193 /**
194 * Frees members only but not the ZRegion itself.
195 *
196 * Regions should be free'd using region_free.
197 */
198 void
chord_region_free_members(ZRegion * self)199 chord_region_free_members (
200 ZRegion * self)
201 {
202 g_return_if_fail (IS_REGION (self));
203
204 for (int i = 0; i < self->num_chord_objects; i++)
205 {
206 chord_region_remove_chord_object (
207 self, self->chord_objects[i], F_FREE,
208 F_NO_PUBLISH_EVENTS);
209 }
210
211 object_zero_and_free (self->chord_objects);
212 }
213