color coded keyframe curves, keyframe popups, cwin scrollbar fixes
[goodguy/history.git] / cinelerra-5.1 / cinelerra / mwindow.C
index f53dc4d65e587fd1493d423da837ba7928880cac..156c9532abfb3742ae1e82987db95bdfdefd9408 100644 (file)
@@ -20,7 +20,9 @@
 
 #include "asset.h"
 #include "assets.h"
+#include "atrack.h"
 #include "audioalsa.h"
+#include "autos.h"
 #include "awindowgui.h"
 #include "awindow.h"
 #include "batchrender.h"
 #include "file.h"
 #include "filesystem.h"
 #include "filexml.h"
+#include "floatautos.h"
 #include "framecache.h"
 #include "gwindow.h"
 #include "gwindowgui.h"
 #include "keyframegui.h"
 #include "indexfile.h"
+#include "intautos.h"
 #include "interlacemodes.h"
 #include "language.h"
 #include "levelwindowgui.h"
@@ -76,6 +80,7 @@
 #include "mwindow.h"
 #include "nestededls.h"
 #include "new.h"
+#include "panautos.h"
 #include "patchbay.h"
 #include "playback3d.h"
 #include "playbackengine.h"
 #include "transition.h"
 #include "transportque.h"
 #include "vframe.h"
+#include "vtrack.h"
+#include "versioninfo.h"
 #include "videodevice.inc"
 #include "videowindow.h"
 #include "vplayback.h"
 #include "defaultformats.h"
 #include "ntsczones.h"
 
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <errno.h>
 
 
 extern "C"
@@ -220,13 +233,16 @@ MWindow::~MWindow()
        gui->stop_drawing();
        gui->remote_control->deactivate();
        gui->record->stop();
+#ifdef HAVE_DVB
        gui->channel_info->stop();
+#endif
        brender_lock->lock("MWindow::quit");
        delete brender;         brender = 0;
        brender_lock->unlock();
        delete create_bd;       create_bd = 0;
        delete create_dvd;      create_dvd = 0;
        delete batch_render;    batch_render = 0;
+       delete render;          render = 0;
        commit_commercial();
        if( commercials && !commercials->remove_user() ) commercials = 0;
 
@@ -284,15 +300,13 @@ MWindow::~MWindow()
        finit_error();
        keyframe_threads->remove_all_objects();
        colormodels.remove_all_objects();
-       delete gui;             gui = 0;
-       delete render;          render = 0;
        delete awindow;         awindow = 0;
        delete lwindow;         lwindow = 0;
        delete twindow;         twindow = 0;
        delete wwindow;         wwindow = 0;
        delete gwindow;         gwindow = 0;
-       // must be last or nouveau chokes
        delete cwindow;         cwindow = 0;
+       delete gui;             gui = 0;
        //delete file_server;  file_server = 0; // reusable
        delete mainindexes;     mainindexes = 0;
        delete mainprogress;    mainprogress = 0;
@@ -332,8 +346,8 @@ MWindow::~MWindow()
 
 void MWindow::quit(int unlock)
 {
-       stop_playback(1);
        if(unlock) gui->unlock_window();
+       stop_playback(1);
 
        brender_lock->lock("MWindow::quit");
        delete brender;         brender = 0;
@@ -361,7 +375,7 @@ void MWindow::create_defaults_path(char *string, const char *config_file)
 // set the .bcast path
        FileSystem fs;
 
-       sprintf(string, "%s", BCASTDIR);
+       sprintf(string, "%s/", File::get_config_path());
        fs.complete_path(string);
        if(!fs.is_dir(string))
                fs.create_dir(string);
@@ -371,43 +385,46 @@ void MWindow::create_defaults_path(char *string, const char *config_file)
 }
 const char *MWindow::default_std()
 {
-       int fd, i, l = 0;
-       char buf[BCTEXTLEN];
-        char *p;
+       char buf[BCTEXTLEN], *p = 0;
 
-       if((fd = open(TIMEZONE_NAME, O_RDONLY)) >= 0)
-       {
-               l = read(fd, buf, BCTEXTLEN);
+       int fd = open(TIMEZONE_NAME, O_RDONLY);
+       if( fd >= 0 ) {
+               int l = read(fd, buf, sizeof(buf)-1);
                close(fd);
-               if(l > 0)
-               {
+               if( l > 0 ) {
+                       if( buf[l-1] == '\n' ) --l;
                        buf[l] = 0;
-                       if(buf[l - 1] == '\n')
-                               buf[--l] = 0;
+                       p = buf;
                }
-               p = buf;
        }
-       if(l < 1)
-       {
-               if((l = readlink(LOCALTIME_LINK, buf, BCTEXTLEN)) > 0)
-               {
+       if( !p ) {
+               int l = readlink(LOCALTIME_LINK, buf, sizeof(buf)-1);
+               if( l > 0 ) {
                        buf[l] = 0;
-                       if( (p=strstr(buf, ZONEINFO_STR)) != 0 )
-                       {
-                               p += strlen(ZONEINFO_STR);
-                               l = strlen(p);
-                       }
-                       else
-                               l = 0;
+                       if( !(p=strstr(buf, ZONEINFO_STR)) != 0 ) return "PAL";
+                       p += strlen(ZONEINFO_STR);
                }
        }
-       if(l)
-       {
-               for(i = 0; ntsc_zones[i]; i++)
-               if(strcmp(ntsc_zones[i], p) == 0)
-                       return "NTSC";
+
+       if( p ) {
+               for( int i=0; ntsc_zones[i]; ++i ) {
+                       if( !strcmp(ntsc_zones[i], p) )
+                               return "NTSC";
+               }
+       }
+
+       p = getenv("TZ");
+       if( p ) {
+               for( int i=0; ntsc_zones[i]; ++i ) {
+                       if( !strcmp(ntsc_zones[i], p) )
+                               return "NTSC";
+               }
        }
-       return "PAL";
+
+//__timezone: Seconds west of UTC.  240sec/deg
+       double tz_deg = -__timezone / 240.;
+// from Honolulu = -10, to New York = -5, 15deg/hr   lat -150..-75
+       return tz_deg >= -10*15 && tz_deg <= -5*15 ? "NTSC" : "PAL";
 }
 
 void MWindow::fill_preset_defaults(const char *preset, EDLSession *session)
@@ -416,7 +433,7 @@ void MWindow::fill_preset_defaults(const char *preset, EDLSession *session)
 
        for(fpr = &format_presets[0]; fpr->name; fpr++)
        {
-               if(strcmp(fpr->name, preset) == 0)
+               if(strcmp(_(fpr->name), preset) == 0)
                {
                        session->audio_channels = fpr->audio_channels;
                        session->audio_tracks = fpr->audio_tracks;
@@ -439,7 +456,7 @@ const char *MWindow::get_preset_name(int index)
 {
        if(index < 0 || index >= (int)MAX_NUM_PRESETS)
                return "Error";
-       return format_presets[index].name;
+       return _(format_presets[index].name);
 }
 
 
@@ -586,8 +603,7 @@ int MWindow::init_ladspa_plugins(MWindow *mwindow, Preferences *preferences)
        char *path = getenv("LADSPA_PATH");
        char ladspa_path[BCTEXTLEN];
        if( !path ) {
-               get_exe_path(ladspa_path);
-               strcat(ladspa_path, "/ladspa");
+               strncpy(ladspa_path, File::get_ladspa_path(), sizeof(ladspa_path));
                path = ladspa_path;
        }
        for( int len=0; *path; path+=len ) {
@@ -630,14 +646,14 @@ int MWindow::init_ladspa_index(MWindow *mwindow, Preferences *preferences,
        strcpy(plugin_path, path);  delete [] path;
        printf("init ladspa index: %s\n", plugin_dir);
        FILE *fp = fopen(index_path,"w");
-        if( !fp ) {
+       if( !fp ) {
                fprintf(stderr,_("MWindow::init_ladspa_index: "
                        "can't create plugin index: %s\n"), index_path);
                return 1;
        }
        fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
        fprintf(fp, "%s\n", plugin_dir);
-        init_plugin_index(mwindow, preferences, fp, plugin_path);
+       init_plugin_index(mwindow, preferences, fp, plugin_path);
        fclose(fp);
        return 0;
 }
@@ -785,6 +801,7 @@ void MWindow::init_preferences()
        BC_Signals::set_trap_hook(trap_hook, this);
        BC_Signals::set_catch_segv(preferences->trap_sigsegv);
        BC_Signals::set_catch_intr(preferences->trap_sigintr);
+       BC_WindowBase::get_resources()->popupmenu_btnup = preferences->popupmenu_btnup;
 }
 
 void MWindow::clean_indexes()
@@ -892,6 +909,11 @@ void MWindow::show_warning(int *do_warning, const char *text)
        wwindow->show_warning(do_warning, text);
 }
 
+int MWindow::wait_warning()
+{
+       return wwindow->wait_result();
+}
+
 void MWindow::init_theme()
 {
        Timer timer;
@@ -1262,7 +1284,7 @@ int MWindow::put_commercial()
 void MWindow::stop_playback(int wait)
 {
        int locked  = gui->get_window_lock();
-        if( locked ) gui->unlock_window();
+       if( locked ) gui->unlock_window();
 
        cwindow->playback_engine->que->send_command(STOP,
                CHANGE_NONE,
@@ -1276,7 +1298,7 @@ void MWindow::stop_playback(int wait)
                vwindow->playback_engine->que->send_command(STOP, CHANGE_NONE, 0, 0);
                vwindow->playback_engine->interrupt_playback(wait);
        }
-        if( locked ) gui->lock_window("MWindow::stop_playback");
+       if( locked ) gui->lock_window("MWindow::stop_playback");
 }
 
 int MWindow::load_filenames(ArrayList<char*> *filenames,
@@ -1485,7 +1507,29 @@ SET_TRACE
 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
                                xml_file.read_from_file(filenames->get(i));
 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
-
+                               const char *cin_version = 0;
+                               while( !xml_file.read_tag() ) {
+                                       if( xml_file.tag.title_is("EDL") ) {
+                                               cin_version = xml_file.tag.get_property("VERSION");
+                                               break;
+                                       }
+                               }
+                               xml_file.rewind();
+                               if( !cin_version ) {
+                                       eprintf(_("XML file %s\n not from cinelerra."),filenames->get(i));
+                                       char string[BCTEXTLEN];
+                                       sprintf(string,_("Unknown %s"), filenames->get(i));
+                                       gui->show_message(string);
+                                       result = 1;
+                                       break;
+                               }
+                               if( strcmp(cin_version, CINELERRA_VERSION) ) {
+                                       char string[BCTEXTLEN];
+                                       snprintf(string, sizeof(string),
+                                                _("Warning: XML from cinelerra version %s\n"
+                                               "Session data may be incompatible."), cin_version);
+                                       show_warning(&preferences->warn_version, string);
+                               }
                                if(load_mode == LOADMODE_NESTED)
                                {
 // Load temporary EDL for nesting.
@@ -1659,9 +1703,9 @@ if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
        }
 
        // if just opening one new resource in replace mode
-       if( load_mode == LOADMODE_REPLACE && new_edls.size() == 1 &&
-               ftype != FILE_IS_XML )
-       {
+       if( ftype != FILE_IS_XML &&
+           ( load_mode == LOADMODE_REPLACE ||
+             load_mode == LOADMODE_REPLACE_CONCATENATE ) ) {
                select_asset(0, 0);
                edl->local_session->preview_start = 0;
                edl->local_session->preview_end = edl->tracks->total_playable_length();
@@ -1814,7 +1858,7 @@ void MWindow::init_shm()
        }
 
        int64_t result = 0;
-       fscanf(fd, _LD, &result);
+       fscanf(fd, "%jd", &result);
        fclose(fd);
        fd = 0;
        if(result < 0x7fffffff) {
@@ -2120,6 +2164,14 @@ void MWindow::show_gwindow()
 
        gui->mainmenu->show_gwindow->set_checked(1);
 }
+void MWindow::hide_gwindow()
+{
+       session->show_gwindow = 0;
+
+       gwindow->gui->lock_window("MWindow::show_gwindow");
+       gwindow->gui->hide_window();
+       gwindow->gui->unlock_window();
+}
 
 void MWindow::show_lwindow()
 {
@@ -2182,6 +2234,22 @@ void MWindow::set_auto_keyframes(int value, int lock_mwindow, int lock_cwindow)
        if(lock_cwindow) cwindow->gui->unlock_window();
 }
 
+void MWindow::set_auto_visibility(Autos *autos, int value)
+{
+       if( autos->type == Autos::AUTOMATION_TYPE_PLUGIN )
+               edl->session->auto_conf->plugins = value;
+       else if( autos->autoidx >= 0 )
+               edl->session->auto_conf->autos[autos->autoidx] = value;
+       else
+               return;
+
+       gui->update(0, 1, 0, 0, 0, 0, 0);
+       gui->mainmenu->update_toggles(1);
+       gui->unlock_window();
+       gwindow->gui->update_toggles(1);
+       gui->lock_window("MWindow::set_auto_visibility");
+}
+
 void MWindow::set_keyframe_type(int mode)
 {
        gui->lock_window("MWindow::set_keyframe_type");
@@ -2423,7 +2491,7 @@ void MWindow::hide_plugin(Plugin *plugin, int lock)
                        ptr->hide_gui();
                        delete_plugin(ptr);
 //sleep(1);
-//                     return;
+                       return;
                }
        }
        if(lock) plugin_gui_lock->unlock();
@@ -2863,21 +2931,20 @@ void MWindow::rebuild_indices()
 void MWindow::save_backup()
 {
        FileXML file;
+       edl->optimize();
        edl->set_path(session->filename);
-       edl->save_xml(&file,
-               BACKUP_PATH,
-               0,
-               0);
+       char backup_path[BCTEXTLEN];
+       snprintf(backup_path, sizeof(backup_path), "%s/%s",
+               File::get_config_path(), BACKUP_FILE);
+       edl->save_xml(&file, backup_path, 0, 0);
        file.terminate_string();
-       char path[BCTEXTLEN];
        FileSystem fs;
-       strcpy(path, BACKUP_PATH);
-       fs.complete_path(path);
+       fs.complete_path(backup_path);
 
-       if(file.write_to_file(path))
+       if(file.write_to_file(backup_path))
        {
                char string2[256];
-               sprintf(string2, _("Couldn't open %s for writing."), BACKUP_PATH);
+               sprintf(string2, _("Couldn't open %s for writing."), backup_path);
                gui->show_message(string2);
        }
 }
@@ -2887,13 +2954,14 @@ void MWindow::load_backup()
        ArrayList<char*> path_list;
        path_list.set_array_delete();
        char *out_path;
-       char string[BCTEXTLEN];
-       strcpy(string, BACKUP_PATH);
+       char backup_path[BCTEXTLEN];
+       snprintf(backup_path, sizeof(backup_path), "%s/%s",
+               File::get_config_path(), BACKUP_FILE);
        FileSystem fs;
-       fs.complete_path(string);
+       fs.complete_path(backup_path);
 
-       path_list.append(out_path = new char[strlen(string) + 1]);
-       strcpy(out_path, string);
+       path_list.append(out_path = new char[strlen(backup_path) + 1]);
+       strcpy(out_path, backup_path);
 
        load_filenames(&path_list, LOADMODE_REPLACE, 0);
        edl->local_session->clip_title[0] = 0;
@@ -2916,6 +2984,9 @@ int MWindow::create_aspect_ratio(float &w, float &h, int width, int height)
 {
        w = 1;  h = 1;
        if(!width || !height) return 1;
+       if( width == 720 && (height == 480 || height == 576) ) {
+               w = 4;  h = 3;  return 0; // for NTSC and PAL
+       }
        double ar = (double)width / height;
 // square-ish pixels
        if( EQUIV(ar, 1.0000) ) return 0;
@@ -3103,39 +3174,62 @@ void MWindow::dump_undo(FILE *fp)
 
 void MWindow::dump_exe(FILE *fp)
 {
-        char proc_path[BCTEXTLEN], exe_path[BCTEXTLEN];
-        sprintf(proc_path, "/proc/%d/exe", (int)getpid());
-        int len = readlink(proc_path, exe_path, sizeof(exe_path));
-       if( len < 0 ) { fprintf(fp,"readlink: %m\n"); return; }
-       exe_path[len] = 0;
+       char proc_path[BCTEXTLEN], exe_path[BCTEXTLEN];
+       sprintf(proc_path, "/proc/%d/exe", (int)getpid());
+
+       int ret = -1, n = 100;
+       for( int len; (len=readlink(proc_path, exe_path, sizeof(exe_path)))>0; --n ) {
+               exe_path[len] = 0;  strcpy(proc_path, exe_path);
+               ret = 0;
+       }
+       if( n < 0 || ret < 0 ) { fprintf(fp,"readlink: %m\n"); return; }
+
        struct stat st;
-       if( stat(exe_path,&st) ) { fprintf(fp,"stat: %m\n"); return; }
-       fprintf(fp, "path: %s = %9jd bytes\n",exe_path,st.st_size);
-       int fd = open(exe_path,O_RDONLY);
+       if( stat(proc_path,&st) ) { fprintf(fp,"stat: %m\n"); return; }
+       fprintf(fp, "path: %s = %9jd bytes\n",proc_path,st.st_size);
+       struct tm *tm = localtime(&st.st_mtime);
+       char mtime[256];
+       strftime(mtime, sizeof(mtime), "%F %T", tm);
+       fprintf(fp,"mtime: %s\n", mtime);
+
+       int fd = open(proc_path,O_RDONLY+O_NONBLOCK);
        if( fd < 0 ) { fprintf(fp,"open: %m\n"); return; }
-       uint8_t buf[65536];  SHA1 sha1;
-       while( (len=read(fd,buf,sizeof(buf))) > 0 ) {
-               sha1.addBytes(buf, len);
+       uint8_t *bfr = 0;
+       int64_t bfrsz = 0;
+       int64_t pagsz = sysconf(_SC_PAGE_SIZE);
+       int64_t maxsz = 1024*pagsz;
+       int64_t size = st.st_size, pos = 0;
+       SHA1 sha1;
+       while( (bfrsz = size-pos) > 0 ) {
+               if( bfrsz > maxsz ) bfrsz = maxsz;
+               bfr = (uint8_t *)mmap(NULL, bfrsz, PROT_READ,
+                       MAP_PRIVATE+MAP_NORESERVE+MAP_POPULATE, fd, pos);
+               if( bfr == MAP_FAILED ) break;
+               sha1.addBytes(bfr, bfrsz);
+               munmap(bfr, bfrsz);
+               pos += bfrsz;
        }
        close(fd);
+       ret = pos < size ? EIO : 0;
        fprintf(fp, "SHA1: ");
        uint8_t digest[20];  sha1.computeHash(digest);
        for( int i=0; i<20; ++i ) fprintf(fp, "%02x", digest[i]);
+       if( ret < 0 ) fprintf(fp, " (ret %d)", ret);
+       if( pos < st.st_size ) fprintf(fp, " (pos %jd)", pos);
        fprintf(fp, "\n");
 }
 
-
 void MWindow::trap_hook(FILE *fp, void *vp)
 {
        MWindow *mwindow = (MWindow *)vp;
-       fprintf(fp, "\nEXE:\n");
-       mwindow->dump_exe(fp);
-       fprintf(fp, "\nPLUGINS:\n");
-       mwindow->dump_plugins(fp);
+//     fprintf(fp, "\nPLUGINS:\n");
+//     mwindow->dump_plugins(fp);
        fprintf(fp, "\nEDL:\n");
        mwindow->dump_edl(fp);
        fprintf(fp, "\nUNDO:\n");
        mwindow->dump_undo(fp);
+       fprintf(fp, "\nEXE:\n");
+       mwindow->dump_exe(fp);
 }
 
 
@@ -3395,10 +3489,12 @@ int MWindow::select_asset(Asset *asset, int vstream, int astream, int delete_tra
        EDLSession *session = edl->session;
        double old_framerate = session->frame_rate;
        double old_samplerate = session->sample_rate;
+       int old_auto_keyframes = session->auto_keyframes;
+       session->auto_keyframes = 0;
        int result = file->open_file(preferences, asset, 1, 0);
        if( !result && delete_tracks > 0 )
                undo->update_undo_before();
-       if( !result && asset->get_video_layers() > 0 ) {
+       if( !result && asset->video_data && asset->get_video_layers() > 0 ) {
                // try to get asset up to date, may fail
                file->select_video_stream(asset, vstream);
                // either way use what was/is there.
@@ -3450,14 +3546,16 @@ int MWindow::select_asset(Asset *asset, int vstream, int astream, int delete_tra
                }
                edl->resample(old_framerate, session->frame_rate, TRACK_VIDEO);
        }
-       if( !result && asset->channels > 0 ) {
+       if( !result && asset->audio_data && asset->channels > 0 ) {
                session->sample_rate = asset->get_sample_rate();
                int64_t channel_mask = 0;
-               int astrm = file->get_audio_for_video(vstream, astream, channel_mask);
+               int astrm = !asset->video_data ? -1 :
+                       file->get_audio_for_video(vstream, astream, channel_mask);
                if( astrm >= 0 ) file->select_audio_stream(asset, astrm);
                if( astrm < 0 || !channel_mask ) channel_mask = (1<<asset->channels)-1;
                int channels = 0;
                for( uint64_t mask=channel_mask; mask!=0; mask>>=1 ) channels += mask & 1;
+               if( channels < 1 ) channels = 1;
                if( channels > 6 ) channels = 6;
                session->audio_tracks = session->audio_channels = channels;
                switch( channels ) {
@@ -3473,6 +3571,15 @@ int MWindow::select_asset(Asset *asset, int vstream, int astream, int delete_tra
                        session->achannel_positions[0] = 180;
                        session->achannel_positions[1] = 0;
                        break;
+               case 1:
+                       session->achannel_positions[1] = 90;
+                       break;
+               default: {
+                       if( !channels ) break;
+                       double t = 0, dt = 360./channels;
+                       for( int i=channels; --i>=0; t+=dt )
+                               session->achannel_positions[i] = int(t+0.5);
+                       break; }
                }
                remap_audio(MWindow::AUDIO_1_TO_1);
 
@@ -3497,6 +3604,7 @@ int MWindow::select_asset(Asset *asset, int vstream, int astream, int delete_tra
                edl->resample(old_samplerate, session->sample_rate, TRACK_AUDIO);
        }
        delete file;
+       session->auto_keyframes = old_auto_keyframes;
        if( !result && delete_tracks > 0 ) {
                save_backup();
                undo->update_undo_after(_("select asset"), LOAD_ALL);
@@ -3508,6 +3616,8 @@ int MWindow::select_asset(Asset *asset, int vstream, int astream, int delete_tra
 int MWindow::select_asset(int vtrack, int delete_tracks)
 {
        Track *track = edl->tracks->get(vtrack, TRACK_VIDEO);
+       if( !track )
+               track = edl->tracks->get(vtrack, TRACK_AUDIO);
        if( !track ) return 1;
        Edit *edit = track->edits->first;
        if( !edit ) return 1;
@@ -3523,3 +3633,34 @@ void MWindow::dump_plugindb(FILE *fp)
                plugindb->get(i)->dump(fp);
 }
 
+FloatAuto* MWindow::get_float_auto(PatchGUI *patch,int idx)
+{
+       Auto *current = 0;
+       double unit_position = edl->local_session->get_selectionstart(1);
+       unit_position = patch->track->to_units(unit_position, 0);
+
+       FloatAutos *ptr = (FloatAutos*)patch->track->automation->autos[idx];
+       return (FloatAuto*)ptr->get_prev_auto( (long)unit_position, PLAY_FORWARD, current);
+}
+
+IntAuto* MWindow::get_int_auto(PatchGUI *patch,int idx)
+{
+       Auto *current = 0;
+       double unit_position = edl->local_session->get_selectionstart(1);
+       unit_position = patch->track->to_units(unit_position, 0);
+
+       IntAutos *ptr = (IntAutos*)patch->track->automation->autos[idx];
+       return (IntAuto*)ptr->get_prev_auto( (long)unit_position, PLAY_FORWARD, current);
+}
+
+PanAuto* MWindow::get_pan_auto(PatchGUI *patch)
+{
+       Auto *current = 0;
+       double unit_position = edl->local_session->get_selectionstart(1);
+       unit_position = patch->track->to_units(unit_position, 0);
+
+       PanAutos *ptr = (PanAutos*)patch->track->automation->autos[AUTOMATION_PAN];
+       return (PanAuto*)ptr->get_prev_auto( (long)unit_position, PLAY_FORWARD, current);
+}
+
+