Credit Andrew - improve in-tree documentation
[goodguy/cinelerra.git] / 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 || command == SINGLE_FRAME_REWIND ||
105                 command == CURRENT_FRAME || command == LAST_FRAME);
106 }
107 int TransportCommand::single_frame()
108 {
109         return single_frame(command);
110 }
111
112
113 int TransportCommand::get_direction(int command)
114 {
115         switch(command) {
116         case SINGLE_FRAME_FWD:
117         case NORMAL_FWD:
118         case FAST_FWD:
119         case SLOW_FWD:
120         case CURRENT_FRAME:
121                 return PLAY_FORWARD;
122
123         case SINGLE_FRAME_REWIND:
124         case NORMAL_REWIND:
125         case FAST_REWIND:
126         case SLOW_REWIND:
127         case LAST_FRAME:
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         case LAST_FRAME:
153                 return 1.;
154
155         case FAST_FWD:
156         case FAST_REWIND:
157                 return 2.;
158         }
159
160         return 0.;
161 }
162 float TransportCommand::get_speed()
163 {
164         return get_speed(command);
165 }
166
167 // Assume starting without pause
168 void TransportCommand::set_playback_range(EDL *edl,
169         int use_inout, int toggle_audio, int loop_play, int use_displacement)
170 {
171         if(!edl) edl = this->edl;
172         double length = edl->tracks->total_playable_length();
173         double frame_period = 1.0 / edl->session->frame_rate;
174
175         displacement = 0;
176         audio_toggle = toggle_audio;
177         play_loop = loop_play;
178
179         start_position = use_inout && edl->local_session->inpoint_valid() ?
180                 edl->local_session->get_inpoint() :
181                 !loop_play ? edl->local_session->get_selectionstart(1) : 0;
182         end_position = use_inout && edl->local_session->outpoint_valid() ?
183                 edl->local_session->get_outpoint() :
184                 !loop_play ? edl->local_session->get_selectionend(1) : length;
185
186         if( !use_inout && EQUIV(start_position, end_position) ) {
187 // starting play at or past end_position, play to end_position of media (for mixers)
188                 if( start_position >= length )
189                         length = edl->tracks->total_length();
190                 switch( command ) {
191                 case SLOW_FWD:
192                 case FAST_FWD:
193                 case NORMAL_FWD: {
194                         end_position = length;
195 // this prevents a crash if start_position position is after the loop when playing forwards
196                         if( edl->local_session->loop_playback &&
197                             start_position > edl->local_session->loop_end ) {
198                                 start_position = edl->local_session->loop_start;
199                         }
200                         break; }
201
202                 case SLOW_REWIND:
203                 case FAST_REWIND:
204                 case NORMAL_REWIND:
205                         start_position = 0;
206 // this prevents a crash if start_position position is before the loop when playing backwards
207                         if( edl->local_session->loop_playback &&
208                             end_position <= edl->local_session->loop_start ) {
209                                         end_position = edl->local_session->loop_end;
210                         }
211                         break;
212
213                 case CURRENT_FRAME:
214                 case LAST_FRAME:
215                 case SINGLE_FRAME_FWD:
216                         end_position = start_position + frame_period;
217                         break;
218
219                 case SINGLE_FRAME_REWIND:
220                         start_position = end_position - frame_period;
221                         break;
222                 }
223
224                 if( use_displacement && (
225                     (command != CURRENT_FRAME && get_direction() == PLAY_FORWARD ) ||
226                     (command != LAST_FRAME    && get_direction() == PLAY_REVERSE ) ) ) {
227                         start_position += frame_period;
228                         end_position += frame_period;
229                         displacement = 1;
230                 }
231         }
232
233         if( end_position < start_position )
234                 end_position = start_position;
235
236         playbackstart = get_direction() == PLAY_FORWARD ?
237                 start_position : end_position;
238 }
239
240 void TransportCommand::playback_range_adjust_inout()
241 {
242         if(edl->local_session->inpoint_valid() ||
243                 edl->local_session->outpoint_valid())
244         {
245                 playback_range_inout();
246         }
247 }
248
249 void TransportCommand::playback_range_inout()
250 {
251         if(edl->local_session->inpoint_valid())
252                 start_position = edl->local_session->get_inpoint();
253         else
254                 start_position = 0;
255
256         if(edl->local_session->outpoint_valid())
257                 end_position = edl->local_session->get_outpoint();
258         else {
259                 end_position = edl->tracks->total_playable_length();
260                 if( start_position >= end_position )
261                         end_position = edl->tracks->total_length();
262         }
263 }
264
265 void TransportCommand::playback_range_project()
266 {
267         start_position = 0;
268         end_position = edl->tracks->total_playable_length();
269 }
270
271 void TransportCommand::playback_range_1frame()
272 {
273         start_position = end_position = edl->local_session->get_selectionstart(1);
274         if( edl->session->frame_rate > 0 ) end_position += 1./edl->session->frame_rate;
275 }
276
277
278 TransportQue::TransportQue()
279 {
280         input_lock = new Condition(1, "TransportQue::input_lock");
281         output_lock = new Condition(0, "TransportQue::output_lock", 1);
282 }
283
284 TransportQue::~TransportQue()
285 {
286         delete input_lock;
287         delete output_lock;
288 }
289
290 int TransportQue::send_command(int command, int change_type,
291                 EDL *new_edl, int realtime, int resume, int use_inout,
292                 int toggle_audio, int loop_play, int use_displacement)
293 {
294         input_lock->lock("TransportQue::send_command 1");
295         this->command.command = command;
296 // Mutually exclusive operation
297         this->command.change_type |= change_type;
298         this->command.realtime = realtime;
299         this->command.resume = resume;
300
301         if(new_edl)
302         {
303 // Just change the EDL if the change requires it because renderengine
304 // structures won't point to the new EDL otherwise and because copying the
305 // EDL for every cursor movement is slow.
306                 if(change_type == CHANGE_EDL ||
307                         (uint32_t)change_type == CHANGE_ALL)
308                 {
309 // Copy EDL
310                         this->command.get_edl()->copy_all(new_edl);
311                 }
312                 else
313                 if(change_type == CHANGE_PARAMS)
314                 {
315                         this->command.get_edl()->synchronize_params(new_edl);
316                 }
317
318 // Set playback range
319                 this->command.set_playback_range(new_edl, use_inout,
320                                 toggle_audio, loop_play, use_displacement);
321         }
322
323         input_lock->unlock();
324
325         output_lock->unlock();
326         return 0;
327 }
328
329 void TransportQue::update_change_type(int change_type)
330 {
331         input_lock->lock("TransportQue::update_change_type");
332         this->command.change_type |= change_type;
333         input_lock->unlock();
334 }