Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / autos.C
diff --git a/cinelerra-5.1/cinelerra/autos.C b/cinelerra-5.1/cinelerra/autos.C
new file mode 100644 (file)
index 0000000..36abbd7
--- /dev/null
@@ -0,0 +1,898 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "autos.h"
+#include "clip.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "floatauto.h"
+#include "localsession.h"
+#include "filexml.h"
+#include "track.h"
+#include "transportque.inc"
+
+
+Autos::Autos(EDL *edl, Track *track)
+ : List<Auto>()
+{
+       this->edl = edl;
+       this->track = track;
+       type = -1;
+       autoidx = -1;
+       autogrouptype = -1;
+}
+
+
+
+Autos::~Autos()
+{
+       while(last) delete last;
+       delete default_auto;
+}
+
+void Autos::create_objects()
+{
+// Default
+       default_auto = new_auto();
+       default_auto->is_default = 1;
+}
+
+int Autos::get_type()
+{
+       return type;
+}
+
+Auto* Autos::append_auto()
+{
+       return append(new_auto());
+}
+
+
+Auto* Autos::new_auto()
+{
+       return new Auto(edl, this);
+}
+
+void Autos::resample(double old_rate, double new_rate)
+{
+       for(Auto *current = first; current; current = NEXT)
+       {
+               current->position = (int64_t)((double)current->position *
+                       new_rate /
+                       old_rate +
+                       0.5);
+       }
+}
+
+void Autos::equivalent_output(Autos *autos, int64_t startproject, int64_t *result)
+{
+// Default keyframe differs
+       if(!total() && !(*default_auto == *autos->default_auto))
+       {
+               if(*result < 0 || *result > startproject) *result = startproject;
+       }
+       else
+// Search for difference
+       {
+               for(Auto *current = first, *that_current = autos->first;
+                       current || that_current;
+                       current = NEXT,
+                       that_current = that_current->next)
+               {
+// Total differs
+                       if(current && !that_current)
+                       {
+                               int64_t position1 = (autos->last ? autos->last->position : startproject);
+                               int64_t position2 = current->position;
+                               if(*result < 0 || *result > MIN(position1, position2))
+                                       *result = MIN(position1, position2);
+                               break;
+                       }
+                       else
+                       if(!current && that_current)
+                       {
+                               int64_t position1 = (last ? last->position : startproject);
+                               int64_t position2 = that_current->position;
+                               if(*result < 0 || *result > MIN(position1, position2))
+                                       *result = MIN(position1, position2);
+                               break;
+                       }
+                       else
+// Keyframes differ
+                       if(!(*current == *that_current) ||
+                               current->position != that_current->position)
+                       {
+                               int64_t position1 = (current->previous ?
+                                       current->previous->position :
+                                       startproject);
+                               int64_t position2 = (that_current->previous ?
+                                       that_current->previous->position :
+                                       startproject);
+                               if(*result < 0 || *result > MIN(position1, position2))
+                                       *result = MIN(position1, position2);
+                               break;
+                       }
+               }
+       }
+}
+
+void Autos::copy_from(Autos *autos)
+{
+       Auto *current = autos->first, *this_current = first;
+
+       default_auto->copy_from(autos->default_auto);
+
+// Detect common memory leak bug
+       if(autos->first && !autos->last)
+       {
+               printf("Autos::copy_from inconsistent pointers\n");
+               exit(1);
+       }
+
+       for(current = autos->first; current; current = NEXT)
+       {
+//printf("Autos::copy_from 1 %p\n", current);
+//sleep(1);
+               if(!this_current)
+               {
+                       append(this_current = new_auto());
+               }
+               this_current->copy_from(current);
+               this_current = this_current->next;
+       }
+
+       for( ; this_current; )
+       {
+               Auto *next_current = this_current->next;
+               delete this_current;
+               this_current = next_current;
+       }
+}
+
+
+// We don't replace it in pasting but
+// when inserting the first EDL of a load operation we need to replace
+// the default keyframe.
+void Autos::insert_track(Autos *automation,
+       int64_t start_unit,
+       int64_t length_units,
+       int replace_default)
+{
+// Insert silence
+       insert(start_unit, start_unit + length_units);
+
+       if(replace_default) default_auto->copy_from(automation->default_auto);
+       for(Auto *current = automation->first; current; current = NEXT)
+       {
+// fill new auto with values from current (template), interpolate values if possible
+               Auto *new_auto = insert_auto(start_unit + current->position, current);
+// Override copy_from
+               new_auto->position = current->position + start_unit;
+       }
+}
+
+Auto* Autos::get_prev_auto(int64_t position,
+       int direction,
+       Auto* &current,
+       int use_default)
+{
+// Get on or before position
+       if(direction == PLAY_FORWARD)
+       {
+// Try existing result
+               if(current)
+               {
+                       while(current && current->position < position) current = NEXT;
+                       while(current && current->position > position) current = PREVIOUS;
+               }
+
+               if(!current)
+               {
+                       for(current = last;
+                               current && current->position > position;
+                               current = PREVIOUS) ;
+               }
+               if(!current && use_default) current = (first ? first : default_auto);
+       }
+       else
+// Get on or after position
+       if(direction == PLAY_REVERSE)
+       {
+               if(current)
+               {
+                       while(current && current->position > position) current = PREVIOUS;
+                       while(current && current->position < position) current = NEXT;
+               }
+
+               if(!current)
+               {
+                       for(current = first;
+                               current && current->position < position;
+                               current = NEXT) ;
+               }
+
+               if(!current && use_default) current = (last ? last : default_auto);
+       }
+
+       return current;
+}
+
+Auto* Autos::get_prev_auto(int direction, Auto* &current)
+{
+       double position_double = edl->local_session->get_selectionstart(1);
+       position_double = edl->align_to_frame(position_double, 0);
+       int64_t position = track->to_units(position_double, 0);
+
+       return get_prev_auto(position, direction, current);
+}
+
+int Autos::auto_exists_for_editing(double position)
+{
+       int result = 0;
+
+       if(edl->session->auto_keyframes)
+       {
+               double unit_position = position;
+               unit_position = edl->align_to_frame(unit_position, 0);
+               if (get_auto_at_position(unit_position))
+                       result = 1;
+       }
+       else
+       {
+               result = 1;
+       }
+
+       return result;
+}
+
+Auto* Autos::get_auto_at_position(double position)
+{
+       int64_t unit_position = track->to_units(position, 0);
+
+       for(Auto *current = first;
+               current;
+               current = NEXT)
+       {
+               if(edl->equivalent(current->position, unit_position))
+               {
+                       return current;
+               }
+       }
+       return 0;
+}
+
+
+Auto* Autos::get_auto_for_editing(double position)
+{
+       if(position < 0)
+       {
+               position = edl->local_session->get_selectionstart(1);
+       }
+
+       Auto *result = 0;
+       position = edl->align_to_frame(position, 0);
+
+
+
+
+//printf("Autos::get_auto_for_editing %p %p\n", first, default_auto);
+
+       if(edl->session->auto_keyframes)
+       {
+               result = insert_auto(track->to_units(position, 0));
+       }
+       else
+               result = get_prev_auto(track->to_units(position, 0),
+                       PLAY_FORWARD,
+                       result);
+
+//printf("Autos::get_auto_for_editing %p %p %p\n", default_auto, first, result);
+       return result;
+}
+
+
+Auto* Autos::get_next_auto(int64_t position, int direction, Auto* &current, int use_default)
+{
+       if(direction == PLAY_FORWARD)
+       {
+               if(current)
+               {
+                       while(current && current->position > position) current = PREVIOUS;
+                       while(current && current->position < position) current = NEXT;
+               }
+
+               if(!current)
+               {
+                       for(current = first;
+                               current && current->position <= position;
+                               current = NEXT)
+                               ;
+               }
+
+               if(!current && use_default) current = (last ? last : default_auto);
+       }
+       else
+       if(direction == PLAY_REVERSE)
+       {
+               if(current)
+               {
+                       while(current && current->position < position) current = NEXT;
+                       while(current && current->position > position) current = PREVIOUS;
+               }
+
+               if(!current)
+               {
+                       for(current = last;
+                               current && current->position > position;
+                               current = PREVIOUS)
+                               ;
+               }
+
+               if(!current && use_default) current = (first ? first : default_auto);
+       }
+
+       return current;
+}
+Auto* Autos::insert_auto(int64_t position, Auto *templ)
+{
+       Auto *current, *result;
+
+// Test for existence
+       for(current = first;
+               current && !edl->equivalent(current->position, position);
+               current = NEXT)
+       {
+               ;
+       }
+
+// Insert new
+       if(!current)
+       {
+// Get first one on or before as a template
+               for(current = last;
+                       current && current->position > position;
+                       current = PREVIOUS)
+               {
+                       ;
+               }
+
+               if(current)
+               {
+                       insert_after(current, result = new_auto());
+               }
+               else
+               {
+                       current = first;
+                       if(!current) current = default_auto;
+
+                       insert_before(first, result = new_auto());
+               }
+
+// interpolate if possible, else copy from template
+               result->interpolate_from(0, 0, position, templ);
+// Set curve mode
+               if( !templ && result->is_floatauto() ) {
+                       FloatAuto *floatauto = (FloatAuto *)result;
+                       floatauto->curve_mode =
+                               edl->local_session->playback_start >= 0 &&
+                               edl->local_session->playback_end < 0 ? FloatAuto::SMOOTH :
+                                       (FloatAuto::t_mode) edl->local_session->floatauto_type;
+               }
+       }
+       else
+       {
+               result = current;
+       }
+
+       return result;
+}
+
+int Autos::clear_all()
+{
+       Auto *current_, *current;
+
+       for(current = first; current; current = current_)
+       {
+               current_ = NEXT;
+               remove(current);
+       }
+       append_auto();
+       return 0;
+}
+
+int Autos::insert(int64_t start, int64_t end)
+{
+       int64_t length;
+       Auto *current = first;
+
+       for( ; current && current->position < start; current = NEXT)
+               ;
+
+       length = end - start;
+
+       for(; current; current = NEXT)
+       {
+               current->position += length;
+       }
+       return 0;
+}
+
+void Autos::paste(int64_t start,
+       int64_t length,
+       double scale,
+       FileXML *file,
+       int default_only,
+       int active_only)
+{
+       int total = 0;
+       int result = 0;
+
+//printf("Autos::paste %d start=%jd\n", __LINE__, start);
+       do{
+               result = file->read_tag();
+
+               if(!result && !file->tag.title_is("/AUTO"))
+               {
+// End of list
+                       if(file->tag.get_title()[0] == '/')
+                       {
+                               result = 1;
+                       }
+                       else
+                       if(!strcmp(file->tag.get_title(), "AUTO"))
+                       {
+                               Auto *current = 0;
+
+// Paste first auto into default
+                               if(default_only && total == 0)
+                               {
+                                       current = default_auto;
+                               }
+                               else
+// Paste default auto into default
+                               if(!default_only)
+                               {
+                                       int64_t position = Units::to_int64(
+                                               (double)file->tag.get_property("POSITION", 0) *
+                                                       scale +
+                                                       start);
+// Paste active auto into track
+                                       current = insert_auto(position);
+                               }
+
+                               if(current)
+                               {
+                                       current->load(file);
+                               }
+                               total++;
+                       }
+               }
+       } while( !result );
+}
+
+
+int Autos::paste_silence(int64_t start, int64_t end)
+{
+       insert(start, end);
+       return 0;
+}
+
+int Autos::copy(int64_t start,
+       int64_t end,
+       FileXML *file,
+       int default_only,
+       int active_only)
+{
+// First auto always loaded with default
+//printf("Autos::copy %d %d %d\n", __LINE__, default_only, active_only);
+       if(default_only || (!active_only && !default_only))
+       {
+               default_auto->copy(0, 0, file, default_only);
+       }
+
+//printf("Autos::copy 10 %d %d %p\n", default_only, start, autoof(start));
+       if(active_only || (!default_only && !active_only))
+       {
+               for(Auto* current = autoof(start);
+                       current && current->position <= end;
+                       current = NEXT)
+               {
+// Want to copy single keyframes by putting the cursor on them
+                       if(current->position >= start && current->position <= end)
+                       {
+                               current->copy(start, end, file, default_only);
+                       }
+               }
+       }
+// Copy default auto again to make it the active auto on the clipboard
+//     else
+//     {
+// Need to force position to 0 for the case of plugins
+// and default status to 0.
+//             default_auto->copy(0, 0, file, default_only);
+//     }
+//printf("Autos::copy 20\n");
+
+       return 0;
+}
+
+// Remove 3 consecutive autos with the same value
+// Remove autos which are out of order
+void Autos::optimize()
+{
+       int done = 0;
+
+
+// Default auto should always be at 0
+       default_auto->position = 0;
+       while(!done)
+       {
+               int consecutive = 0;
+               done = 1;
+
+
+               for(Auto *current = first; current; current = NEXT)
+               {
+// Get 3rd consecutive auto of equal value
+                       if(current != first)
+                       {
+                               if(*current == *PREVIOUS)
+                               {
+                                       consecutive++;
+                                       if(consecutive >= 3)
+                                       {
+                                               delete PREVIOUS;
+                                               break;
+                                       }
+                               }
+                               else
+                                       consecutive = 0;
+
+                               if(done && current->position <= PREVIOUS->position)
+                               {
+                                       delete current;
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+
+void Autos::remove_nonsequential(Auto *keyframe)
+{
+       if((keyframe->next && keyframe->next->position <= keyframe->position) ||
+               (keyframe->previous && keyframe->previous->position >= keyframe->position))
+       {
+               delete keyframe;
+       }
+}
+
+
+void Autos::set_automation_mode(int64_t start, int64_t end, int mode)
+{
+}
+
+void Autos::clear(int64_t start,
+       int64_t end,
+       int shift_autos)
+{
+       int64_t length;
+       Auto *next, *current;
+       length = end - start;
+
+
+       current = autoof(start);
+
+// If a range is selected don't delete the ending keyframe but do delete
+// the beginning keyframe because shifting end handle forward shouldn't
+// delete the first keyframe of the next edit.
+
+       while(current &&
+               ((end != start && current->position < end) ||
+               (end == start && current->position <= end)))
+       {
+               next = NEXT;
+               remove(current);
+               current = next;
+       }
+
+       while(current && shift_autos)
+       {
+               current->position -= length;
+               current = NEXT;
+       }
+}
+
+int Autos::clear_auto(int64_t position)
+{
+       Auto *current;
+       current = autoof(position);
+       return current->position==position ? (remove(current), 1) : 0;
+}
+
+
+int Autos::load(FileXML *file)
+{
+       while(last)
+               remove(last);    // remove any existing autos
+
+       int result = 0, first_auto = 1;
+       Auto *current;
+
+       do{
+               result = file->read_tag();
+
+               if(!result && !file->tag.title_is("/AUTO"))
+               {
+// First tag with leading / is taken as end of autos
+                       if(/* strstr(file->tag.get_title(), "AUTOS") && */
+
+                               file->tag.get_title()[0] == '/')
+                       {
+                               result = 1;
+                       }
+                       else
+                       if(!strcmp(file->tag.get_title(), "AUTO"))
+                       {
+                               if(first_auto)
+                               {
+                                       default_auto->load(file);
+                                       default_auto->position = 0;
+                                       first_auto = 0;
+                               }
+                               else
+                               {
+                                       current = append(new_auto());
+                                       current->position = file->tag.get_property("POSITION", (int64_t)0);
+                                       current->load(file);
+                               }
+                       }
+               }
+       } while( !result );
+       return 0;
+}
+
+
+
+
+
+
+int Autos::slope_adjustment(int64_t ax, double slope)
+{
+       return (int)(ax * slope);
+}
+
+
+int Autos::scale_time(float rate_scale, int scale_edits, int scale_autos, int64_t start, int64_t end)
+{
+       Auto *current;
+
+       for(current = first; current && scale_autos; current = NEXT)
+       {
+//             if(current->position >= start && current->position <= end)
+//             {
+                       current->position = (int64_t)((current->position - start) * rate_scale + start + 0.5);
+//             }
+       }
+       return 0;
+}
+
+Auto* Autos::autoof(int64_t position)
+{
+       Auto *current;
+
+       for(current = first;
+               current && current->position < position;
+               current = NEXT)
+       {
+               ;
+       }
+       return current;     // return 0 on failure
+}
+
+Auto* Autos::nearest_before(int64_t position)
+{
+       Auto *current;
+
+       for(current = last; current && current->position >= position; current = PREVIOUS)
+       { ; }
+
+
+       return current;     // return 0 on failure
+}
+
+Auto* Autos::nearest_after(int64_t position)
+{
+       Auto *current;
+
+       for(current = first; current && current->position <= position; current = NEXT)
+       { ; }
+
+
+       return current;     // return 0 on failure
+}
+
+int Autos::get_neighbors(int64_t start, int64_t end, Auto **before, Auto **after)
+{
+       if(*before == 0) *before = first;
+       if(*after == 0) *after = last;
+
+       while(*before && (*before)->next && (*before)->next->position <= start)
+               *before = (*before)->next;
+
+       while(*after && (*after)->previous && (*after)->previous->position >= end)
+               *after = (*after)->previous;
+
+       while(*before && (*before)->position > start) *before = (*before)->previous;
+
+       while(*after && (*after)->position < end) *after = (*after)->next;
+       return 0;
+}
+
+int Autos::automation_is_constant(int64_t start, int64_t end)
+{
+       return 0;
+}
+
+double Autos::get_automation_constant(int64_t start, int64_t end)
+{
+       return 0;
+}
+
+
+int Autos::init_automation(int64_t &buffer_position,
+                               int64_t &input_start,
+                               int64_t &input_end,
+                               int &automate,
+                               double &constant,
+                               int64_t input_position,
+                               int64_t buffer_len,
+                               Auto **before,
+                               Auto **after,
+                               int reverse)
+{
+       buffer_position = 0;
+
+// set start and end boundaries for automation info
+       input_start = reverse ? input_position - buffer_len : input_position;
+       input_end = reverse ? input_position : input_position + buffer_len;
+
+// test automation for constant value
+// and set up *before and *after
+       if(automate)
+       {
+               if(automation_is_constant(input_start, input_end))
+               {
+                       constant += get_automation_constant(input_start, input_end);
+                       automate = 0;
+               }
+       }
+       return automate;
+}
+
+
+int Autos::init_slope(Auto **current_auto,
+                               double &slope_start,
+                               double &slope_value,
+                               double &slope_position,
+                               int64_t &input_start,
+                               int64_t &input_end,
+                               Auto **before,
+                               Auto **after,
+                               int reverse)
+{
+// apply automation
+       *current_auto = reverse ? *after : *before;
+// no auto before start so use first auto in range
+// already know there is an auto since automation isn't constant
+       if(!*current_auto)
+       {
+               *current_auto = reverse ? last : first;
+//             slope_value = (*current_auto)->value;
+               slope_start = input_start;
+               slope_position = 0;
+       }
+       else
+       {
+// otherwise get the first slope point and advance auto
+//             slope_value = (*current_auto)->value;
+               slope_start = (*current_auto)->position;
+               slope_position = reverse ? slope_start - input_end : input_start - slope_start;
+               (*current_auto) = reverse ? (*current_auto)->previous : (*current_auto)->next;
+       }
+       return 0;
+}
+
+
+int Autos::get_slope(Auto **current_auto,
+                               double &slope_start,
+                               double &slope_end,
+                               double &slope_value,
+                               double &slope,
+                               int64_t buffer_len,
+                               int64_t buffer_position,
+                               int reverse)
+{
+// get the slope
+       if(*current_auto)
+       {
+               slope_end = reverse ? slope_start - (*current_auto)->position : (*current_auto)->position - slope_start;
+               if(slope_end)
+//                     slope = ((*current_auto)->value - slope_value) / slope_end;
+//             else
+                       slope = 0;
+       }
+       else
+       {
+               slope = 0;
+               slope_end = buffer_len - buffer_position;
+       }
+       return 0;
+}
+
+int Autos::advance_slope(Auto **current_auto,
+                               double &slope_start,
+                               double &slope_value,
+                               double &slope_position,
+                               int reverse)
+{
+       if(*current_auto)
+       {
+               slope_start = (*current_auto)->position;
+//             slope_value = (*current_auto)->value;
+               (*current_auto) = reverse ? (*current_auto)->previous : (*current_auto)->next;
+               slope_position = 0;
+       }
+       return 0;
+}
+
+int64_t Autos::get_length()
+{
+       if(last)
+               return last->position + 1;
+       else
+               return 0;
+}
+
+void Autos::get_extents(float *min,
+       float *max,
+       int *coords_undefined,
+       int64_t unit_start,
+       int64_t unit_end)
+{
+
+}
+
+
+void Autos::dump(FILE *fp)
+{
+}
+
+
+
+
+
+
+
+
+
+