Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / tracks.C
diff --git a/cinelerra-5.1/cinelerra/tracks.C b/cinelerra-5.1/cinelerra/tracks.C
new file mode 100644 (file)
index 0000000..9d994f4
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2010 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 "atrack.h"
+#include "automation.h"
+#include "clip.h"
+#include "bchash.h"
+#include "edit.h"
+#include "edits.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "file.h"
+#include "filexml.h"
+#include "intauto.h"
+#include "intautos.h"
+#include "localsession.h"
+#include "module.h"
+#include "panauto.h"
+#include "panautos.h"
+#include "patchbay.h"
+#include "mainsession.h"
+#include "strack.h"
+#include "theme.h"
+#include "track.h"
+#include "trackcanvas.h"
+#include "tracks.h"
+#include "transportque.inc"
+#include "vtrack.h"
+#include <string.h>
+
+Tracks::Tracks(EDL *edl)
+ : List<Track>()
+{
+       this->edl = edl;
+}
+
+Tracks::Tracks()
+ : List<Track>()
+{
+}
+
+
+Tracks::~Tracks()
+{
+       delete_all_tracks();
+}
+
+
+
+
+
+void Tracks::equivalent_output(Tracks *tracks, double *result)
+{
+       if(total_playable_vtracks() != tracks->total_playable_vtracks())
+       {
+               *result = 0;
+       }
+       else
+       {
+               Track *current = first;
+               Track *that_current = tracks->first;
+               while(current || that_current)
+               {
+// Get next video track
+                       while(current && current->data_type != TRACK_VIDEO)
+                               current = NEXT;
+
+                       while(that_current && that_current->data_type != TRACK_VIDEO)
+                               that_current = that_current->next;
+
+// One doesn't exist but the other does
+                       if((!current && that_current) ||
+                               (current && !that_current))
+                       {
+                               *result = 0;
+                               break;
+                       }
+                       else
+// Both exist
+                       if(current && that_current)
+                       {
+                               current->equivalent_output(that_current, result);
+                               current = NEXT;
+                               that_current = that_current->next;
+                       }
+               }
+       }
+}
+
+
+
+
+void Tracks::get_affected_edits(ArrayList<Edit*> *drag_edits, double position, Track *start_track)
+{
+       drag_edits->remove_all();
+
+       for(Track *track = start_track;
+               track;
+               track = track->next)
+       {
+//printf("Tracks::get_affected_edits 1 %p %d %d\n", track, track->data_type, track->record);
+               if(track->record)
+               {
+                       for(Edit *edit = track->edits->first; edit; edit = edit->next)
+                       {
+                               double startproject = track->from_units(edit->startproject);
+//printf("Tracks::get_affected_edits 1 %d\n", edl->equivalent(startproject, position));
+                               if(edl->equivalent(startproject, position))
+                               {
+                                       drag_edits->append(edit);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+}
+
+void Tracks::get_automation_extents(float *min,
+       float *max,
+       double start,
+       double end,
+       int autogrouptype)
+{
+       *min = 0;
+       *max = 0;
+       int coords_undefined = 1;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->record)
+               {
+                       current->automation->get_extents(min,
+                               max,
+                               &coords_undefined,
+                               current->to_units(start, 0),
+                               current->to_units(end, 1),
+                               autogrouptype);
+               }
+       }
+}
+
+
+void Tracks::copy_from(Tracks *tracks)
+{
+       Track *new_track = 0;
+
+       delete_all_tracks();
+       for(Track *current = tracks->first; current; current = NEXT)
+       {
+               switch(current->data_type)
+               {
+               case TRACK_AUDIO:
+                       new_track = add_audio_track(0, 0);
+                       break;
+               case TRACK_VIDEO:
+                       new_track = add_video_track(0, 0);
+                       break;
+               case TRACK_SUBTITLE:
+                       new_track = add_subttl_track(0, 0);
+                       break;
+               default:
+                       continue;
+               }
+               new_track->copy_from(current);
+       }
+}
+
+Tracks& Tracks::operator=(Tracks &tracks)
+{
+printf("Tracks::operator= 1\n");
+       copy_from(&tracks);
+       return *this;
+}
+
+int Tracks::load(FileXML *xml,
+       int &track_offset,
+       uint32_t load_flags)
+{
+// add the appropriate type of track
+       char string[BCTEXTLEN];
+       Track *track = 0;
+       string[0] = 0;
+
+       xml->tag.get_property("TYPE", string);
+
+       if((load_flags & LOAD_ALL) == LOAD_ALL ||
+               (load_flags & LOAD_EDITS))
+       {
+               if(!strcmp(string, "VIDEO"))
+               {
+                       add_video_track(0, 0);
+               }
+               else
+               if(!strcmp(string, "SUBTTL"))
+               {
+                       add_subttl_track(0, 0);
+               }
+               else
+               {
+                       add_audio_track(0, 0);    // default to audio
+               }
+               track = last;
+       }
+       else
+       {
+               track = get_item_number(track_offset);
+               track_offset++;
+       }
+
+// load it
+       if(track) track->load(xml, track_offset, load_flags);
+
+       return 0;
+}
+
+Track* Tracks::add_audio_track(int above, Track *dst_track)
+{
+       ATrack* new_track = new ATrack(edl, this);
+       if(!dst_track)
+       {
+               dst_track = (above ? first : last);
+       }
+
+       if(above)
+       {
+               insert_before(dst_track, (Track*)new_track);
+       }
+       else
+       {
+               insert_after(dst_track, (Track*)new_track);
+// Shift effects referenced below the destination track
+       }
+
+// Shift effects referenced below the new track
+       for(Track *track = last;
+               track && track != new_track;
+               track = track->previous)
+       {
+               change_modules(number_of(track) - 1, number_of(track), 0);
+       }
+
+
+       new_track->create_objects();
+       new_track->set_default_title();
+
+       int current_pan = 0;
+       for(Track *current = first;
+               current != (Track*)new_track;
+               current = NEXT)
+       {
+               if(current->data_type == TRACK_AUDIO) current_pan++;
+               if(current_pan >= edl->session->audio_channels) current_pan = 0;
+       }
+
+
+
+       PanAuto* pan_auto =
+               (PanAuto*)new_track->automation->autos[AUTOMATION_PAN]->default_auto;
+       pan_auto->values[current_pan] = 1.0;
+
+       BC_Pan::calculate_stick_position(edl->session->audio_channels,
+               edl->session->achannel_positions,
+               pan_auto->values,
+               MAX_PAN,
+               PAN_RADIUS,
+               pan_auto->handle_x,
+               pan_auto->handle_y);
+       return new_track;
+}
+
+Track* Tracks::add_video_track(int above, Track *dst_track)
+{
+const int debug = 0;
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+       VTrack* new_track = new VTrack(edl, this);
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+       if(!dst_track)
+               dst_track = (above ? first : last);
+
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+       if(above)
+       {
+               insert_before(dst_track, (Track*)new_track);
+       }
+       else
+       {
+               insert_after(dst_track, (Track*)new_track);
+       }
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+
+
+
+// Shift effects referenced below the new track
+       for(Track *track = last;
+               track && track != new_track;
+               track = track->previous)
+       {
+               change_modules(number_of(track) - 1, number_of(track), 0);
+       }
+
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+
+
+       new_track->create_objects();
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+       new_track->set_default_title();
+if(debug) printf("Tracks::add_video_track %d\n", __LINE__);
+       return new_track;
+}
+
+
+Track* Tracks::add_subttl_track(int above, Track *dst_track)
+{
+const int debug = 0;
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+       STrack* new_track = new STrack(edl, this);
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+       if(!dst_track)
+               dst_track = (above ? first : last);
+
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+       if(above)
+       {
+               insert_before(dst_track, (Track*)new_track);
+       }
+       else
+       {
+               insert_after(dst_track, (Track*)new_track);
+       }
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+
+
+
+// Shift effects referenced below the new track
+       for(Track *track = last;
+               track && track != new_track;
+               track = track->previous)
+       {
+               change_modules(number_of(track) - 1, number_of(track), 0);
+       }
+
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+
+
+       new_track->create_objects();
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+       new_track->set_default_title();
+if(debug) printf("Tracks::add_subttl_track %d\n", __LINE__);
+
+//     new_track->paste_silence(0,total_length(),0);
+       return new_track;
+}
+
+
+int Tracks::delete_track(Track *track)
+{
+       if (!track)
+               return 0;
+
+       int old_location = number_of(track);
+       detach_shared_effects(old_location);
+
+// Shift effects referencing effects below the deleted track
+       for(Track *current = track;
+               current;
+               current = NEXT)
+       {
+               change_modules(number_of(current), number_of(current) - 1, 0);
+       }
+       if(track) delete track;
+
+       return 0;
+}
+
+int Tracks::detach_shared_effects(int module)
+{
+       for(Track *current = first; current; current = NEXT)
+       {
+               current->detach_shared_effects(module);
+       }
+
+       return 0;
+ }
+
+int Tracks::total_of(int type)
+{
+       int result = 0;
+       IntAuto *mute_keyframe = 0;
+
+       for(Track *current = first; current; current = NEXT)
+       {
+               long unit_start = current->to_units(edl->local_session->get_selectionstart(1), 0);
+               mute_keyframe =
+                       (IntAuto*)current->automation->autos[AUTOMATION_MUTE]->get_prev_auto(
+                       unit_start,
+                       PLAY_FORWARD,
+                       (Auto* &)mute_keyframe);
+
+               result +=
+                       (current->play && type == PLAY) ||
+                       (current->record && type == RECORD) ||
+                       (current->gang && type == GANG) ||
+                       (current->draw && type == DRAW) ||
+                       (mute_keyframe->value && type == MUTE) ||
+                       (current->expand_view && type == EXPAND);
+       }
+       return result;
+}
+
+int Tracks::recordable_audio_tracks()
+{
+       int result = 0;
+       for(Track *current = first; current; current = NEXT)
+               if(current->data_type == TRACK_AUDIO && current->record) result++;
+       return result;
+}
+
+int Tracks::recordable_video_tracks()
+{
+       int result = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_VIDEO && current->record) result++;
+       }
+       return result;
+}
+
+
+int Tracks::playable_audio_tracks()
+{
+       int result = 0;
+
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_AUDIO && current->play)
+               {
+                       result++;
+               }
+       }
+
+       return result;
+}
+
+int Tracks::playable_video_tracks()
+{
+       int result = 0;
+
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_VIDEO && current->play)
+               {
+                       result++;
+               }
+       }
+       return result;
+}
+
+int Tracks::total_audio_tracks()
+{
+       int result = 0;
+       for(Track *current = first; current; current = NEXT)
+               if(current->data_type == TRACK_AUDIO) result++;
+       return result;
+}
+
+int Tracks::total_video_tracks()
+{
+       int result = 0;
+       for(Track *current = first; current; current = NEXT)
+               if(current->data_type == TRACK_VIDEO) result++;
+       return result;
+}
+
+double Tracks::total_playable_length()
+{
+       double total = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               double length = current->get_length();
+               if(length > total) total = length;
+       }
+       return total;
+}
+
+double Tracks::total_recordable_length()
+{
+       double total = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->record)
+               {
+                       double length = current->get_length();
+                       if(length > total) total = length;
+               }
+       }
+       return total;
+}
+
+double Tracks::total_length()
+{
+       double total = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->get_length() > total) total = current->get_length();
+       }
+       return total;
+}
+
+double Tracks::total_audio_length()
+{
+       double total = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_AUDIO &&
+                       current->get_length() > total) total = current->get_length();
+       }
+       return total;
+}
+
+double Tracks::total_video_length()
+{
+       double total = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_VIDEO &&
+                       current->get_length() > total) total = current->get_length();
+       }
+       return total;
+}
+
+double Tracks::total_length_framealigned(double fps)
+{
+       if (total_audio_tracks() && total_video_tracks())
+               return MIN(floor(total_audio_length() * fps), floor(total_video_length() * fps)) / fps;
+
+       if (total_audio_tracks())
+               return floor(total_audio_length() * fps) / fps;
+
+       if (total_video_tracks())
+               return floor(total_video_length() * fps) / fps;
+
+       return 0;
+}
+
+void Tracks::translate_projector(float offset_x, float offset_y)
+{
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_VIDEO)
+               {
+                       ((VTrack*)current)->translate(offset_x, offset_y, 0);
+               }
+       }
+}
+
+void Tracks::update_y_pixels(Theme *theme)
+{
+//     int y = -edl->local_session->track_start;
+       int y = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+//printf("Tracks::update_y_pixels %d\n", y);
+               current->y_pixel = y;
+               y += current->vertical_span(theme);
+       }
+}
+
+int Tracks::dump(FILE *fp)
+{
+       for(Track* current = first; current; current = NEXT)
+       {
+               fprintf(fp,"  Track: %p\n", current);
+               current->dump(fp);
+               fprintf(fp,"\n");
+       }
+       return 0;
+}
+
+void Tracks::select_all(int type,
+               int value)
+{
+       for(Track* current = first; current; current = NEXT)
+       {
+               double position = edl->local_session->get_selectionstart(1);
+
+               if(type == PLAY) current->play = value;
+               if(type == RECORD) current->record = value;
+               if(type == GANG) current->gang = value;
+               if(type == DRAW) current->draw = value;
+
+               if(type == MUTE)
+               {
+                       ((IntAuto*)current->automation->autos[AUTOMATION_MUTE]->get_auto_for_editing(position))->value = value;
+               }
+
+               if(type == EXPAND) current->expand_view = value;
+       }
+}
+
+// ===================================== file operations
+
+int Tracks::popup_transition(int cursor_x, int cursor_y)
+{
+       int result = 0;
+       for(Track* current = first; current && !result; current = NEXT)
+       {
+               result = current->popup_transition(cursor_x, cursor_y);
+       }
+       return result;
+}
+
+
+int Tracks::change_channels(int oldchannels, int newchannels)
+{
+       for(Track *current = first; current; current = NEXT)
+       { current->change_channels(oldchannels, newchannels); }
+       return 0;
+}
+
+
+
+int Tracks::totalpixels()
+{
+       int result = 0;
+       for(Track* current = first; current; current = NEXT)
+       {
+               result += edl->local_session->zoom_track;
+       }
+       return result;
+}
+
+int Tracks::number_of(Track *track)
+{
+       int i = 0;
+       for(Track *current = first; current && current != track; current = NEXT)
+       {
+               i++;
+       }
+       return i;
+}
+
+Track* Tracks::number(int number)
+{
+       Track *current;
+       int i = 0;
+       for(current = first; current && i < number; current = NEXT)
+       {
+               i++;
+       }
+       return current;
+}
+
+
+int Tracks::total_playable_vtracks()
+{
+       int result = 0;
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current->data_type == TRACK_VIDEO && current->play) result++;
+       }
+       return result;
+}
+
+int Tracks::plugin_exists(Plugin *plugin)
+{
+       for(Track *track = first; track; track = track->next)
+       {
+               if(track->plugin_exists(plugin)) return 1;
+       }
+       return 0;
+}
+
+int Tracks::track_exists(Track *track)
+{
+       for(Track *current = first; current; current = NEXT)
+       {
+               if(current == track) return 1;
+       }
+       return 0;
+}
+
+
+Track *Tracks::get(int idx, int data_type)
+{
+       for(Track *current = first; current; current = NEXT)
+       {
+               if( current->data_type != data_type ) continue;
+               if( --idx < 0 ) return current;
+       }
+       return 0;
+}
+
+