repeat play, in/out <> shortcuts, append to proj wording, cleanup
[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         play_loop = 0;
57         displacement = 0;
58 // Don't reset the change type for commands which don't perform the change
59         if(command != STOP) change_type = 0;
60         command = COMMAND_NONE;
61 }
62
63 EDL* TransportCommand::get_edl()
64 {
65         return edl;
66 }
67
68 void TransportCommand::delete_edl()
69 {
70         edl->Garbage::remove_user();
71         edl = 0;
72 }
73
74 void TransportCommand::new_edl()
75 {
76         edl = new EDL;
77         edl->create_objects();
78 }
79
80
81 void TransportCommand::copy_from(TransportCommand *command)
82 {
83         this->command = command->command;
84         this->change_type = command->change_type;
85         this->edl->copy_all(command->edl);
86         this->start_position = command->start_position;
87         this->end_position = command->end_position;
88         this->playbackstart = command->playbackstart;
89         this->realtime = command->realtime;
90         this->resume = command->resume;
91         this->audio_toggle = command->audio_toggle;
92         this->play_loop = command->play_loop;
93         this->displacement = command->displacement;
94 }
95
96 TransportCommand& TransportCommand::operator=(TransportCommand &command)
97 {
98         copy_from(&command);
99         return *this;
100 }
101
102 int TransportCommand::single_frame(int command)
103 {
104         return (command == SINGLE_FRAME_FWD ||
105                 command == SINGLE_FRAME_REWIND ||
106                 command == CURRENT_FRAME);
107 }
108 int TransportCommand::single_frame()
109 {
110         return single_frame(command);
111 }
112
113
114 int TransportCommand::get_direction(int command)
115 {
116         switch(command) {
117         case SINGLE_FRAME_FWD:
118         case NORMAL_FWD:
119         case FAST_FWD:
120         case SLOW_FWD:
121         case CURRENT_FRAME:
122                 return PLAY_FORWARD;
123
124         case SINGLE_FRAME_REWIND:
125         case NORMAL_REWIND:
126         case FAST_REWIND:
127         case SLOW_REWIND:
128                 return PLAY_REVERSE;
129
130         default:
131                 break;
132         }
133         return PLAY_FORWARD;
134 }
135 int TransportCommand::get_direction()
136 {
137         return get_direction(command);
138 }
139
140 float TransportCommand::get_speed(int command)
141 {
142         switch(command) {
143         case SLOW_FWD:
144         case SLOW_REWIND:
145                 return 0.5;
146
147         case NORMAL_FWD:
148         case NORMAL_REWIND:
149         case SINGLE_FRAME_FWD:
150         case SINGLE_FRAME_REWIND:
151         case CURRENT_FRAME:
152                 return 1.;
153
154         case FAST_FWD:
155         case FAST_REWIND:
156                 return 2.;
157         }
158
159         return 0.;
160 }
161 float TransportCommand::get_speed()
162 {
163         return get_speed(command);
164 }
165
166 // Assume starting without pause
167 void TransportCommand::set_playback_range(EDL *edl,
168         int use_inout, int toggle_audio, int loop_play, int use_displacement)
169 {
170         if(!edl) edl = this->edl;
171         double length = edl->tracks->total_playable_length();
172         double frame_period = 1.0 / edl->session->frame_rate;
173
174         displacement = 0;
175         audio_toggle = toggle_audio;
176         play_loop = loop_play;
177
178         start_position = use_inout && edl->local_session->inpoint_valid() ?
179                 edl->local_session->get_inpoint() :
180                 !loop_play ? edl->local_session->get_selectionstart(1) : 0;
181         end_position = use_inout && edl->local_session->outpoint_valid() ?
182                 edl->local_session->get_outpoint() :
183                 !loop_play ? edl->local_session->get_selectionend(1) : length;
184
185         if( !use_inout && EQUIV(start_position, end_position) ) {
186 // starting play at or past end_position, play to end_position of media (for mixers)
187                 if( start_position >= length )
188                         length = edl->tracks->total_length();
189                 switch( command ) {
190                 case SLOW_FWD:
191                 case FAST_FWD:
192                 case NORMAL_FWD: {
193                         end_position = length;
194 // this prevents a crash if start_position position is after the loop when playing forwards
195                         if( edl->local_session->loop_playback &&
196                             start_position > edl->local_session->loop_end ) {
197                                 start_position = edl->local_session->loop_start;
198                         }
199                         break; }
200
201                 case SLOW_REWIND:
202                 case FAST_REWIND:
203                 case NORMAL_REWIND:
204                         start_position = 0;
205 // this prevents a crash if start_position position is before the loop when playing backwards
206                         if( edl->local_session->loop_playback &&
207                             end_position <= edl->local_session->loop_start ) {
208                                         end_position = edl->local_session->loop_end;
209                         }
210                         break;
211
212                 case CURRENT_FRAME:
213                 case SINGLE_FRAME_FWD:
214                         end_position = start_position + frame_period;
215                         break;
216
217                 case SINGLE_FRAME_REWIND:
218                         start_position = end_position - frame_period;
219                         break;
220                 }
221
222                 if( use_displacement && command != CURRENT_FRAME &&
223                     get_direction() == PLAY_FORWARD ) {
224                         start_position += frame_period;
225                         end_position += frame_period;
226                         displacement = 1;
227                 }
228         }
229
230         if( end_position < start_position )
231                 end_position = start_position;
232
233         playbackstart = get_direction() == PLAY_FORWARD ?
234                 start_position : end_position;
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                 if( start_position >= end_position )
258                         end_position = edl->tracks->total_length();
259         }
260 }
261
262 void TransportCommand::playback_range_project()
263 {
264         start_position = 0;
265         end_position = edl->tracks->total_playable_length();
266 }
267
268 void TransportCommand::playback_range_1frame()
269 {
270         start_position = end_position = edl->local_session->get_selectionstart(1);
271         if( edl->session->frame_rate > 0 ) end_position += 1./edl->session->frame_rate;
272 }
273
274
275 TransportQue::TransportQue()
276 {
277         input_lock = new Condition(1, "TransportQue::input_lock");
278         output_lock = new Condition(0, "TransportQue::output_lock", 1);
279 }
280
281 TransportQue::~TransportQue()
282 {
283         delete input_lock;
284         delete output_lock;
285 }
286
287 int TransportQue::send_command(int command, int change_type,
288                 EDL *new_edl, int realtime, int resume, int use_inout,
289                 int toggle_audio, int loop_play, int use_displacement)
290 {
291         input_lock->lock("TransportQue::send_command 1");
292         this->command.command = command;
293 // Mutually exclusive operation
294         this->command.change_type |= change_type;
295         this->command.realtime = realtime;
296         this->command.resume = resume;
297
298         if(new_edl)
299         {
300 // Just change the EDL if the change requires it because renderengine
301 // structures won't point to the new EDL otherwise and because copying the
302 // EDL for every cursor movement is slow.
303                 if(change_type == CHANGE_EDL ||
304                         (uint32_t)change_type == CHANGE_ALL)
305                 {
306 // Copy EDL
307                         this->command.get_edl()->copy_all(new_edl);
308                 }
309                 else
310                 if(change_type == CHANGE_PARAMS)
311                 {
312                         this->command.get_edl()->synchronize_params(new_edl);
313                 }
314
315 // Set playback range
316                 this->command.set_playback_range(new_edl, use_inout,
317                                 toggle_audio, loop_play, use_displacement);
318         }
319
320         input_lock->unlock();
321
322         output_lock->unlock();
323         return 0;
324 }
325
326 void TransportQue::update_change_type(int change_type)
327 {
328         input_lock->lock("TransportQue::update_change_type");
329         this->command.change_type |= change_type;
330         input_lock->unlock();
331 }