01ba565d3bf60953085021c771516c3009ec6d21
[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         displacement = 0;
57 // Don't reset the change type for commands which don't perform the change
58         if(command != STOP) change_type = 0;
59         command = COMMAND_NONE;
60 }
61
62 EDL* TransportCommand::get_edl()
63 {
64         return edl;
65 }
66
67 void TransportCommand::delete_edl()
68 {
69         edl->Garbage::remove_user();
70         edl = 0;
71 }
72
73 void TransportCommand::new_edl()
74 {
75         edl = new EDL;
76         edl->create_objects();
77 }
78
79
80 void TransportCommand::copy_from(TransportCommand *command)
81 {
82         this->command = command->command;
83         this->change_type = command->change_type;
84         this->edl->copy_all(command->edl);
85         this->start_position = command->start_position;
86         this->end_position = command->end_position;
87         this->playbackstart = command->playbackstart;
88         this->realtime = command->realtime;
89         this->resume = command->resume;
90         this->audio_toggle = command->audio_toggle;
91         this->displacement = command->displacement;
92 }
93
94 TransportCommand& TransportCommand::operator=(TransportCommand &command)
95 {
96         copy_from(&command);
97         return *this;
98 }
99
100 int TransportCommand::single_frame(int command)
101 {
102         return (command == SINGLE_FRAME_FWD ||
103                 command == SINGLE_FRAME_REWIND ||
104                 command == CURRENT_FRAME);
105 }
106 int TransportCommand::single_frame()
107 {
108         return single_frame(command);
109 }
110
111
112 int TransportCommand::get_direction(int command)
113 {
114         switch(command) {
115         case SINGLE_FRAME_FWD:
116         case NORMAL_FWD:
117         case FAST_FWD:
118         case SLOW_FWD:
119         case CURRENT_FRAME:
120                 return PLAY_FORWARD;
121
122         case SINGLE_FRAME_REWIND:
123         case NORMAL_REWIND:
124         case FAST_REWIND:
125         case SLOW_REWIND:
126                 return PLAY_REVERSE;
127
128         default:
129                 break;
130         }
131         return PLAY_FORWARD;
132 }
133 int TransportCommand::get_direction()
134 {
135         return get_direction(command);
136 }
137
138 float TransportCommand::get_speed(int command)
139 {
140         switch(command) {
141         case SLOW_FWD:
142         case SLOW_REWIND:
143                 return 0.5;
144
145         case NORMAL_FWD:
146         case NORMAL_REWIND:
147         case SINGLE_FRAME_FWD:
148         case SINGLE_FRAME_REWIND:
149         case CURRENT_FRAME:
150                 return 1.;
151
152         case FAST_FWD:
153         case FAST_REWIND:
154                 return 2.;
155         }
156
157         return 0.;
158 }
159 float TransportCommand::get_speed()
160 {
161         return get_speed(command);
162 }
163
164 // Assume starting without pause
165 void TransportCommand::set_playback_range(EDL *edl,
166         int use_inout, int toggle_audio, int use_displacement)
167 {
168         if(!edl) edl = this->edl;
169
170         double length = edl->tracks->total_playable_length();
171         double frame_period = 1.0 / edl->session->frame_rate;
172         double start = edl->local_session->get_selectionstart(1);
173         double end = edl->local_session->get_selectionend(1);
174
175         displacement = 0;
176         if( use_inout ) {
177                 if( edl->local_session->inpoint_valid() )
178                         start_position = edl->local_session->get_inpoint();
179                 if( edl->local_session->outpoint_valid() )
180                         end_position = edl->local_session->get_outpoint();
181         }
182         else if( !EQUIV(start, end) ) {
183                 start_position = start;
184                 end_position = end;
185         }
186         else {
187                 switch( command ) {
188                 case SLOW_FWD:
189                 case FAST_FWD:
190                 case NORMAL_FWD: {
191                         start_position = start;
192                         end_position = length;
193 // this prevents a crash if start position is after the loop when playing forwards
194                         if( edl->local_session->loop_playback &&
195                             start_position > edl->local_session->loop_end ) {
196                                 start_position = edl->local_session->loop_start;
197                         }
198                         break; }
199
200                 case SLOW_REWIND:
201                 case FAST_REWIND:
202                 case NORMAL_REWIND:
203                         end_position = end;
204                         start_position = 0;
205 // this prevents a crash if start 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                         start_position = start;
215                         end_position = start_position + frame_period;
216                         break;
217
218                 case SINGLE_FRAME_REWIND:
219                         end_position = end;
220                         start_position = end_position - frame_period;
221                         break;
222                 }
223
224                 if( use_displacement && command != CURRENT_FRAME &&
225                     get_direction() == PLAY_FORWARD ) {
226                         start_position += frame_period;
227                         end_position += frame_period;
228                         displacement = 1;
229                 }
230         }
231
232         playbackstart = get_direction() == PLAY_FORWARD ? start_position : end_position;
233         audio_toggle = toggle_audio;
234 }
235
236 void TransportCommand::playback_range_adjust_inout()
237 {
238         if(edl->local_session->inpoint_valid() ||
239                 edl->local_session->outpoint_valid())
240         {
241                 playback_range_inout();
242         }
243 }
244
245 void TransportCommand::playback_range_inout()
246 {
247         if(edl->local_session->inpoint_valid())
248                 start_position = edl->local_session->get_inpoint();
249         else
250                 start_position = 0;
251
252         if(edl->local_session->outpoint_valid())
253                 end_position = edl->local_session->get_outpoint();
254         else
255                 end_position = edl->tracks->total_playable_length();
256 }
257
258 void TransportCommand::playback_range_project()
259 {
260         start_position = 0;
261         end_position = edl->tracks->total_playable_length();
262 }
263
264 void TransportCommand::playback_range_1frame()
265 {
266         start_position = end_position = edl->local_session->get_selectionstart(1);
267         if( edl->session->frame_rate > 0 ) end_position += 1./edl->session->frame_rate;
268 }
269
270
271 TransportQue::TransportQue()
272 {
273         input_lock = new Condition(1, "TransportQue::input_lock");
274         output_lock = new Condition(0, "TransportQue::output_lock", 1);
275 }
276
277 TransportQue::~TransportQue()
278 {
279         delete input_lock;
280         delete output_lock;
281 }
282
283 int TransportQue::send_command(int command, int change_type,
284                 EDL *new_edl, int realtime, int resume,
285                 int use_inout, int toggle_audio, int use_displacement)
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,
313                         use_inout, toggle_audio, use_displacement);
314         }
315
316         input_lock->unlock();
317
318         output_lock->unlock();
319         return 0;
320 }
321
322 void TransportQue::update_change_type(int change_type)
323 {
324         input_lock->lock("TransportQue::update_change_type");
325         this->command.change_type |= change_type;
326         input_lock->unlock();
327 }