crikey rework, KP4 fix, added Shift-KPx, valgrind cleanups, hex eye-dropper, resize...
[goodguy/history.git] / cinelerra-5.1 / cinelerra / transportque.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcsignals.h"
23 #include "clip.h"
24 #include "condition.h"
25 #include "edl.h"
26 #include "edlsession.h"
27 #include "localsession.h"
28 #include "tracks.h"
29 #include "transportque.h"
30
31 TransportCommand::TransportCommand()
32 {
33 // In rendering we want a master EDL so settings don't get clobbered
34 // in the middle of a job.
35         edl = new EDL;
36         edl->create_objects();
37         command = 0;
38         change_type = 0;
39         reset();
40 }
41
42 TransportCommand::~TransportCommand()
43 {
44         edl->Garbage::remove_user();
45 }
46
47 void TransportCommand::reset()
48 {
49         playbackstart = 0;
50         start_position = 0;
51         end_position = 0;
52         infinite = 0;
53         realtime = 0;
54         resume = 0;
55         audio_toggle = 0;
56 // Don't reset the change type for commands which don't perform the change
57         if(command != STOP) change_type = 0;
58         command = COMMAND_NONE;
59 }
60
61 EDL* TransportCommand::get_edl()
62 {
63         return edl;
64 }
65
66 void TransportCommand::delete_edl()
67 {
68         edl->Garbage::remove_user();
69         edl = 0;
70 }
71
72 void TransportCommand::new_edl()
73 {
74         edl = new EDL;
75         edl->create_objects();
76 }
77
78
79 void TransportCommand::copy_from(TransportCommand *command)
80 {
81         this->command = command->command;
82         this->change_type = command->change_type;
83         this->edl->copy_all(command->edl);
84         this->start_position = command->start_position;
85         this->end_position = command->end_position;
86         this->playbackstart = command->playbackstart;
87         this->realtime = command->realtime;
88         this->resume = command->resume;
89         this->audio_toggle = command->audio_toggle;
90 }
91
92 TransportCommand& TransportCommand::operator=(TransportCommand &command)
93 {
94         copy_from(&command);
95         return *this;
96 }
97
98 int TransportCommand::single_frame(int command)
99 {
100         return (command == SINGLE_FRAME_FWD ||
101                 command == SINGLE_FRAME_REWIND ||
102                 command == CURRENT_FRAME);
103 }
104 int TransportCommand::single_frame()
105 {
106         return single_frame(command);
107 }
108
109
110 int TransportCommand::get_direction(int command)
111 {
112         switch(command) {
113         case SINGLE_FRAME_FWD:
114         case NORMAL_FWD:
115         case FAST_FWD:
116         case SLOW_FWD:
117         case CURRENT_FRAME:
118                 return PLAY_FORWARD;
119
120         case SINGLE_FRAME_REWIND:
121         case NORMAL_REWIND:
122         case FAST_REWIND:
123         case SLOW_REWIND:
124                 return PLAY_REVERSE;
125
126         default:
127                 break;
128         }
129         return PLAY_FORWARD;
130 }
131 int TransportCommand::get_direction()
132 {
133         return get_direction(command);
134 }
135
136 float TransportCommand::get_speed(int command)
137 {
138         switch(command) {
139         case SLOW_FWD:
140         case SLOW_REWIND:
141                 return 0.5;
142
143         case NORMAL_FWD:
144         case NORMAL_REWIND:
145         case SINGLE_FRAME_FWD:
146         case SINGLE_FRAME_REWIND:
147         case CURRENT_FRAME:
148                 return 1.;
149
150         case FAST_FWD:
151         case FAST_REWIND:
152                 return 2.;
153         }
154
155         return 0.;
156 }
157 float TransportCommand::get_speed()
158 {
159         return get_speed(command);
160 }
161
162 // Assume starting without pause
163 void TransportCommand::set_playback_range(EDL *edl, int use_inout, int toggle_audio)
164 {
165         if(!edl) edl = this->edl;
166
167         switch(command)
168         {
169                 case SLOW_FWD:
170                 case FAST_FWD:
171                 case NORMAL_FWD:
172                         start_position = edl->local_session->get_selectionstart(1);
173                         if(EQUIV(edl->local_session->get_selectionend(1), edl->local_session->get_selectionstart(1)))
174                                 end_position = edl->tracks->total_playable_length();
175                         else
176                                 end_position = edl->local_session->get_selectionend(1);
177 // this prevents a crash if start position is after the loop when playing forwards
178                     if (edl->local_session->loop_playback &&
179                                 start_position > edl->local_session->loop_end)
180                         {
181                                     start_position = edl->local_session->loop_start;
182                         }
183                         break;
184
185                 case SLOW_REWIND:
186                 case FAST_REWIND:
187                 case NORMAL_REWIND:
188                         end_position = edl->local_session->get_selectionend(1);
189                         if(EQUIV(edl->local_session->get_selectionend(1), edl->local_session->get_selectionstart(1)))
190                                 start_position = 0;
191                         else
192                                 start_position = edl->local_session->get_selectionstart(1);
193
194 // this prevents a crash if start position is before the loop when playing backwards
195                         if (edl->local_session->loop_playback &&
196                                 end_position <= edl->local_session->loop_start)
197                         {
198                                         end_position = edl->local_session->loop_end;
199                         }
200                         break;
201
202                 case CURRENT_FRAME:
203                 case SINGLE_FRAME_FWD:
204                         start_position = edl->local_session->get_selectionstart(1);
205                         end_position = start_position + 1.0 / edl->session->frame_rate;
206                         break;
207
208                 case SINGLE_FRAME_REWIND:
209                         end_position = edl->local_session->get_selectionend(1);
210                         start_position = end_position - 1.0 / edl->session->frame_rate;
211                         break;
212         }
213
214
215         if(use_inout)
216         {
217                 if(edl->local_session->inpoint_valid())
218                         start_position = edl->local_session->get_inpoint();
219                 if(edl->local_session->outpoint_valid())
220                         end_position = edl->local_session->get_outpoint();
221         }
222
223         switch(get_direction())
224         {
225                 case PLAY_FORWARD:
226                         playbackstart = start_position;
227                         break;
228
229                 case PLAY_REVERSE:
230                         playbackstart = end_position;
231                         break;
232         }
233
234         audio_toggle = toggle_audio;
235 }
236
237 void TransportCommand::playback_range_adjust_inout()
238 {
239         if(edl->local_session->inpoint_valid() ||
240                 edl->local_session->outpoint_valid())
241         {
242                 playback_range_inout();
243         }
244 }
245
246 void TransportCommand::playback_range_inout()
247 {
248         if(edl->local_session->inpoint_valid())
249                 start_position = edl->local_session->get_inpoint();
250         else
251                 start_position = 0;
252
253         if(edl->local_session->outpoint_valid())
254                 end_position = edl->local_session->get_outpoint();
255         else
256                 end_position = edl->tracks->total_playable_length();
257 }
258
259 void TransportCommand::playback_range_project()
260 {
261         start_position = 0;
262         end_position = edl->tracks->total_playable_length();
263 }
264
265 void TransportCommand::playback_range_1frame()
266 {
267         start_position = end_position = edl->local_session->get_selectionstart(1);
268         if( edl->session->frame_rate > 0 ) end_position += 1./edl->session->frame_rate;
269 }
270
271
272 TransportQue::TransportQue()
273 {
274         input_lock = new Condition(1, "TransportQue::input_lock");
275         output_lock = new Condition(0, "TransportQue::output_lock", 1);
276 }
277
278 TransportQue::~TransportQue()
279 {
280         delete input_lock;
281         delete output_lock;
282 }
283
284 int TransportQue::send_command(int command, int change_type, EDL *new_edl,
285                 int realtime, int resume, int use_inout, int toggle_audio)
286 {
287         input_lock->lock("TransportQue::send_command 1");
288         this->command.command = command;
289 // Mutually exclusive operation
290         this->command.change_type |= change_type;
291         this->command.realtime = realtime;
292         this->command.resume = resume;
293
294         if(new_edl)
295         {
296 // Just change the EDL if the change requires it because renderengine
297 // structures won't point to the new EDL otherwise and because copying the
298 // EDL for every cursor movement is slow.
299                 if(change_type == CHANGE_EDL ||
300                         (uint32_t)change_type == CHANGE_ALL)
301                 {
302 // Copy EDL
303                         this->command.get_edl()->copy_all(new_edl);
304                 }
305                 else
306                 if(change_type == CHANGE_PARAMS)
307                 {
308                         this->command.get_edl()->synchronize_params(new_edl);
309                 }
310
311 // Set playback range
312                 this->command.set_playback_range(new_edl, use_inout, toggle_audio);
313         }
314
315         input_lock->unlock();
316
317         output_lock->unlock();
318         return 0;
319 }
320
321 void TransportQue::update_change_type(int change_type)
322 {
323         input_lock->lock("TransportQue::update_change_type");
324         this->command.change_type |= change_type;
325         input_lock->unlock();
326 }