It's 2020...
[terminatorX.git] / src / tX_sequencer.cc
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2020  Alexander K├Ânig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  
18     File: tX_sequencer.cc
19  
20     Description: Well, implements the sequencer as you might have
21                  guessed.
22 */ 
23
24 #include "tX_sequencer.h"
25 #include "tX_mastergui.h"
26 #include "tX_global.h"
27 #include <string.h>
28
29 tX_sequencer sequencer;
30
31 tX_sequencer :: tX_sequencer()
32 {
33         current_timestamp=0;
34         start_timestamp=0;
35         max_timestamp=0;
36         next_event=song_list.begin();
37         mode=TX_SEQMODE_PLAYONLY;
38         run=0;
39         pthread_mutex_init(&record_lock, NULL);
40 }
41
42 tX_sequencer :: ~tX_sequencer()
43 {
44 }
45
46 void tX_sequencer :: set_timestamp(guint32 timestamp)
47 {
48         current_timestamp=0;
49         start_timestamp=0;
50         next_event=song_list.begin();
51 }
52
53 void tX_sequencer :: step()
54 {
55         if (!run) return;
56                 
57         while ((next_event != song_list.end()) && ((*next_event)->get_timestamp()==current_timestamp))
58         {
59                 (*next_event)->playback();
60                 next_event++;
61         }
62
63         current_timestamp++;    
64 }
65
66 tX_event *tX_sequencer :: record_event (tX_seqpar *sp, float value)
67 {
68         tX_event *new_event;
69         
70         new_event=new tX_event(current_timestamp, sp, value);
71         
72         pthread_mutex_lock(&record_lock);
73         record_list.push_back(new_event);
74         pthread_mutex_unlock(&record_lock);
75         
76         return new_event;
77 }
78
79 int tX_sequencer :: trig_rec()
80 {
81         record_start_timestamp=start_timestamp;
82
83         mode = TX_SEQMODE_PLAYREC;
84         return 1;
85 }
86
87 int tX_sequencer :: trig_play()
88 {
89         run=1;
90         return 1;
91 }
92
93 //#define SEQ_DEBUG 1
94 //#define SEQ_DEBUG_MAX 1
95
96 void tX_sequencer :: trig_stop()
97 {
98         list <tX_event *> :: iterator song_event;
99         list <tX_event *> :: iterator temp_song_event;
100         list <tX_event *> :: iterator record_event;
101         tX_seqpar *sp;
102
103         int oldmode=mode;
104
105         mode = TX_SEQMODE_PLAYONLY;
106         run=0;
107         int confirm=GTK_RESPONSE_YES;
108         
109         record_stop_timestamp=current_timestamp;
110         
111         if (oldmode==TX_SEQMODE_PLAYREC) {
112                 pthread_mutex_lock(&record_lock);
113 #ifdef SEQ_DEBUG                
114                 printf ("Recorded from %i to %i.\n", record_start_timestamp, record_stop_timestamp);
115                 printf ("* Song: %i events, Recorded: %i events, sum=%i\n", song_list.size(), record_list.size(), song_list.size() + record_list.size());
116 #endif  
117
118                 if (globals.confirm_events) {
119                         GtkWidget *dialog=gtk_message_dialog_new(GTK_WINDOW(main_window), 
120                                                                 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
121                                                                 "Apply all events recorded with this take?");
122         
123                         confirm=gtk_dialog_run(GTK_DIALOG(dialog));
124                         gtk_widget_destroy(dialog);
125                 }
126                 
127                 if (confirm==GTK_RESPONSE_YES) {
128                         /* The user wants to keep the recorded events... */
129                         
130                         /* removing all events for touched parameters in song_list */
131                         
132                         song_event=song_list.begin();
133         
134                         while ((song_event!=song_list.end()) && ((*song_event)->get_timestamp() < record_start_timestamp))
135                                 song_event++;
136         
137                         while ((song_event!=song_list.end()) && ((*song_event)->get_timestamp() <= record_stop_timestamp)) {
138                                 sp = (*song_event)->get_sp();
139 #ifdef SEQ_DEBUG_MAX                    
140                                 printf("sp %08x (%i) touched at: %i - timestamp %i.\n", sp, sp->is_touched(), sp->get_touch_timestamp(), (*song_event)->get_timestamp());
141 #endif                  
142                                 
143                                 if (sp->is_touched() && (sp->get_touch_timestamp()<= (*song_event)->get_timestamp())) {
144                                         temp_song_event=song_event;
145                                         song_event++;
146                                         delete (*temp_song_event);
147                                         song_list.erase(temp_song_event);
148                                 } else {
149                                         song_event++;
150                                 }
151                         }
152         
153                         /* inserting all recorded events into song_list */
154                                                 
155                         for (record_event=record_list.begin(), song_event=song_list.begin(); record_event != record_list.end();) {
156                                 if (song_event==song_list.end()) {
157                                         song_list.insert(song_event, record_event, record_list.end());
158                                         break;
159                                 }
160                                 
161                                 if ((*song_event)->get_timestamp() >= (*record_event)->get_timestamp()) {
162                                         song_list.insert(song_event, (*record_event));                          
163                                         record_event++;
164                                 } else {
165                                         song_event++;
166                                 }
167                         }               
168                 } else {
169                         /* The user wants to drop the events from this take */
170                         for (record_event=record_list.begin(); record_event!=record_list.end(); record_event++) {
171                                 delete (*record_event);
172                         }
173                 }
174                 
175                 record_list.erase(record_list.begin(), record_list.end());      
176
177 #ifdef SEQ_DEBUG                
178                 printf ("- Song: %i events, Recorded: %i events, sum=%i\n", song_list.size(), record_list.size(), song_list.size() + record_list.size());
179 #endif          
180                 
181                 pthread_mutex_unlock(&record_lock);
182         }
183
184         tX_seqpar::untouch_all();
185         
186         song_event=song_list.end();
187         
188         if (song_event!=song_list.begin()) {
189                 song_event--;
190                 max_timestamp=(*song_event)->get_timestamp();
191         }
192         
193 #ifdef SEQ_DEBUG_MAX
194         /*dump song_list */
195         
196         for (song_event=song_list.begin(); song_event!=song_list.end(); song_event++) {
197                 printf ("%-15s| %-27s| %8i | %10f\n", (*song_event)->get_vtt_name(), (*song_event)->get_seqpar_name(), (*song_event)->get_timestamp(), (*song_event)->get_value());
198         }
199 #endif  
200
201         current_timestamp=start_timestamp;
202         seq_update();
203 }
204
205 void tX_sequencer :: delete_all_events(del_mode mode)
206 {
207         list <tX_event *> :: iterator song_event;
208         list <tX_event *> :: iterator temp_song_event;
209         
210         for (song_event=song_list.begin(); song_event!=song_list.end();) {
211                 if (((mode==DELETE_ALL) || 
212                         ((mode==DELETE_UPTO_CURRENT) && ((*song_event)->get_timestamp()<current_timestamp)) ||
213                         ((mode==DELETE_FROM_CURRENT) && ((*song_event)->get_timestamp()>=current_timestamp))))
214                 {
215                         temp_song_event=song_event;
216                         song_event++;
217                         delete (*temp_song_event);
218                         song_list.erase(temp_song_event);
219                 } else {
220                         song_event++;
221                 }
222         }
223         
224         start_timestamp=0;
225         current_timestamp=0;
226         max_timestamp=0;
227 }
228
229 void tX_sequencer :: delete_all_events_for_vtt(vtt_class *vtt, del_mode mode)
230 {
231         list <tX_seqpar *> :: iterator sp;
232         
233         for (sp=tX_seqpar::all->begin(); sp!=tX_seqpar::all->end(); sp++) {
234                 if ((*sp)->vtt==vtt) {
235                         delete_all_events_for_sp((*sp), mode);
236                 }
237         }
238 }
239
240 void tX_sequencer :: delete_all_events_for_sp(tX_seqpar *sp, del_mode mode)
241 {
242         list <tX_event *> :: iterator song_event;
243         list <tX_event *> :: iterator temp_song_event;
244         
245         for (song_event=song_list.begin(); song_event!=song_list.end();) {
246                 if ((sp == (*song_event)->get_sp()) &&
247                         ((mode==DELETE_ALL) || 
248                         ((mode==DELETE_UPTO_CURRENT) && ((*song_event)->get_timestamp()<current_timestamp)) ||
249                         ((mode==DELETE_FROM_CURRENT) && ((*song_event)->get_timestamp()>=current_timestamp))))
250                 {
251                         temp_song_event=song_event;
252                         song_event++;
253                         delete (*temp_song_event);
254                         song_list.erase(temp_song_event);
255                 } else {
256                         song_event++;
257                 }
258         }
259 }
260
261 void tX_sequencer :: save(FILE *rc, gzFile rz, char *indent) {
262         list <tX_event *> :: iterator song_event;
263         
264         tX_store("%s<sequencer>\n", indent);
265         strcat(indent, "\t");
266         
267         for (song_event=song_list.begin(); song_event!=song_list.end(); song_event++) {
268                 (*song_event)->store(rc, rz, indent);
269         }
270         
271         indent[strlen(indent)-1]=0;
272         tX_store("%s</sequencer>\n", indent);
273 }
274
275 guint32 tX_sequencer :: set_start_timestamp(float pos)
276 {
277         guint32 timestamp;
278         
279         if (pos>99.999) pos=99.999;
280         pos/=100;
281         
282         timestamp = (guint32) (((float) max_timestamp) * pos);
283         start_timestamp=timestamp;
284         
285         return start_timestamp;
286 }
287
288 void tX_sequencer :: forward_to_start_timestamp(int dont_fake)
289 {
290         int gui_update_max, gui_update;
291         int run_save=run;
292         
293         run=1;
294         
295         gui_update_max=(globals.update_idle * (globals.update_delay+1) * 1000) >> 1;
296         gui_update=gui_update_max;
297         
298         current_timestamp=0;
299         
300         next_event=song_list.begin();
301         
302         while (current_timestamp<start_timestamp) {
303                 step();
304                 if (dont_fake) {
305                         vtt_class :: forward_all_turntables();
306                         
307                         gui_update--;           
308                         if (gui_update < 0) {
309                                 gui_update=gui_update_max;
310                                 seq_update();
311                                 while (gtk_events_pending()) gtk_main_iteration();
312                         }
313                 }
314         }
315         
316         run=run_save;
317         
318         tX_seqpar :: update_all_graphics();     
319         while (gtk_events_pending()) gtk_main_iteration();
320 }
321
322 void tX_sequencer :: load(xmlDocPtr doc, xmlNodePtr node)
323 {
324         tX_event *ev=NULL;
325         
326         max_timestamp=0;
327         
328         for (xmlNodePtr cur=node->xmlChildrenNode; cur!=NULL; cur=cur->next) {
329                 if (cur->type == XML_ELEMENT_NODE) {
330                         if (xmlStrcmp(cur->name, (xmlChar *) "event")==0) {
331                                 ev=tX_event::load_event(doc, cur);
332                                 
333                                 if (ev) {
334                                         max_timestamp=ev->get_timestamp();
335                                         song_list.push_back(ev);
336                                 }
337                         } else {
338                                 tX_warning("unhandled sequencer element %s.", cur->name);
339                         }
340                 }
341         }
342         
343         start_timestamp=0;
344         current_timestamp=0;
345 }