mixer
[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 // starting play at or past end, play to end of media (for mixers)
188                 if( start >= length )
189                         length = edl->tracks->total_length();
190                 switch( command ) {
191                 case SLOW_FWD:
192                 case FAST_FWD:
193                 case NORMAL_FWD: {
194                         start_position = start;
195                         end_position = length;
196 // this prevents a crash if start position is after the loop when playing forwards
197                         if( edl->local_session->loop_playback &&
198                             start_position > edl->local_session->loop_end ) {
199                                 start_position = edl->local_session->loop_start;
200                         }
201                         break; }
202
203                 case SLOW_REWIND:
204                 case FAST_REWIND:
205                 case NORMAL_REWIND:
206                         end_position = end;
207                         start_position = 0;
208 // this prevents a crash if start position is before the loop when playing backwards
209                         if( edl->local_session->loop_playback &&
210                             end_position <= edl->local_session->loop_start ) {
211                                         end_position = edl->local_session->loop_end;
212                         }
213                         break;
214
215                 case CURRENT_FRAME:
216                 case SINGLE_FRAME_FWD:
217                         start_position = start;
218                         end_position = start_position + frame_period;
219                         break;
220
221                 case SINGLE_FRAME_REWIND:
222                         end_position = end;
223                         start_position = end_position - frame_period;
224                         break;
225                 }
226
227                 if( use_displacement && command != CURRENT_FRAME &&
228                     get_direction() == PLAY_FORWARD ) {
229                         start_position += frame_period;
230                         end_position += frame_period;
231                         displacement = 1;
232                 }
233         }
234
235         playbackstart = get_direction() == PLAY_FORWARD ? start_position : end_position;
236         audio_toggle = toggle_audio;
237 }
238
239 void TransportCommand::playback_range_adjust_inout()
240 {
241         if(edl->local_session->inpoint_valid() ||
242                 edl->local_session->outpoint_valid())
243         {
244                 playback_range_inout();
245         }
246 }
247
248 void TransportCommand::playback_range_inout()
249 {
250         if(edl->local_session->inpoint_valid())
251                 start_position = edl->local_session->get_inpoint();
252         else
253                 start_position = 0;
254
255         if(edl->local_session->outpoint_valid())
256                 end_position = edl->local_session->get_outpoint();
257         else {
258                 end_position = edl->tracks->total_playable_length();
259                 if( start_position >= end_position )
260                         end_position = edl->tracks->total_length();
261         }
262 }
263
264 void TransportCommand::playback_range_project()
265 {
266         start_position = 0;
267         end_position = edl->tracks->total_playable_length();
268 }
269
270 void TransportCommand::playback_range_1frame()
271 {
272         start_position = end_position = edl->local_session->get_selectionstart(1);
273         if( edl->session->frame_rate > 0 ) end_position += 1./edl->session->frame_rate;
274 }
275
276
277 TransportQue::TransportQue()
278 {
279         input_lock = new Condition(1, "TransportQue::input_lock");
280         output_lock = new Condition(0, "TransportQue::output_lock", 1);
281 }
282
283 TransportQue::~TransportQue()
284 {
285         delete input_lock;
286         delete output_lock;
287 }
288
289 int TransportQue::send_command(int command, int change_type,
290                 EDL *new_edl, int realtime, int resume,
291                 int use_inout, int toggle_audio, int use_displacement)
292 {
293         input_lock->lock("TransportQue::send_command 1");
294         this->command.command = command;
295 // Mutually exclusive operation
296         this->command.change_type |= change_type;
297         this->command.realtime = realtime;
298         this->command.resume = resume;
299
300         if(new_edl)
301         {
302 // Just change the EDL if the change requires it because renderengine
303 // structures won't point to the new EDL otherwise and because copying the
304 // EDL for every cursor movement is slow.
305                 if(change_type == CHANGE_EDL ||
306                         (uint32_t)change_type == CHANGE_ALL)
307                 {
308 // Copy EDL
309                         this->command.get_edl()->copy_all(new_edl);
310                 }
311                 else
312                 if(change_type == CHANGE_PARAMS)
313                 {
314                         this->command.get_edl()->synchronize_params(new_edl);
315                 }
316
317 // Set playback range
318                 this->command.set_playback_range(new_edl,
319                         use_inout, toggle_audio, use_displacement);
320         }
321
322         input_lock->unlock();
323
324         output_lock->unlock();
325         return 0;
326 }
327
328 void TransportQue::update_change_type(int change_type)
329 {
330         input_lock->lock("TransportQue::update_change_type");
331         this->command.change_type |= change_type;
332         input_lock->unlock();
333 }