/* * CINELERRA * Copyright (C) 2008 Adam Williams * * 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() { 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* ¤t, 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 && first && first->position <= position) { 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 && last && last->position >= position) { 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* ¤t) { 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; get_prev_auto(track->to_units(position, 0), PLAY_FORWARD, result); if( edl->session->auto_keyframes && (!result || result->is_default || !EQUIV(track->from_units(result->position), position)) ) { //printf("Autos::get_auto_for_editing %p %p %p\n", default_auto, first, result); position = edl->align_to_frame(position, 0); result = insert_auto(track->to_units(position, 0)); } //printf("Autos::get_auto_for_editing %p %p\n", first, default_auto); return result; } Auto* Autos::get_next_auto(int64_t position, int direction, Auto* ¤t, 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 && last && last->position > position) { 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 && first && first->position <= position) { 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)) { Auto *current = autoof(start); // need the last one if past the end if( !current && last ) last->copy(start, end, file, default_only); while( current && current->position <= end ) { // 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); } current = NEXT; } } // 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) { }