// effects-weed.c
// LiVES (lives-exe)
// (c) G. Finch 2005 - 2012 (salsaman@gmail.com)
// Released under the GPL 3 or later
// see file ../COPYING for licensing details
#include <dlfcn.h>
#if HAVE_SYSTEM_WEED
#include <weed/weed.h>
#include <weed/weed-palettes.h>
#include <weed/weed-effects.h>
#include <weed/weed-utils.h>
#include <weed/weed-host.h>
#else
#include "../libweed/weed.h"
#include "../libweed/weed-palettes.h"
#include "../libweed/weed-effects.h"
#include "../libweed/weed-utils.h"
#include "../libweed/weed-host.h"
#endif
#ifdef __cplusplus
#ifdef HAVE_OPENCV
#include "opencv2/core/core.hpp"
using namespace cv;
#endif
#endif
#include "main.h"
#include "effects.h"
///////////////////////////////////
#include "callbacks.h"
#include "support.h"
#include "rte_window.h"
#include "resample.h"
#include "audio.h"
////////////////////////////////////////////////////////////////////////
struct _procvals {
weed_plant_t *inst;
weed_timecode_t tc;
};
#define OIL_MEMCPY_MAX_BYTES 1024 // this can be tuned to provide optimal performance
#ifdef ENABLE_OIL
LIVES_INLINE void *lives_memcpy (void *dest, const void *src, size_t n) {
#ifndef __cplusplus
if (n>=32&&n<=OIL_MEMCPY_MAX_BYTES) {
oil_memcpy((uint8_t *)dest,(const uint8_t *)src,n);
return dest;
}
#endif
return memcpy(dest,src,n);
}
#else
LIVES_INLINE void *lives_memcpy (void *dest, const void *src, size_t n) {return memcpy(dest,src,n);}
#endif
G_GNUC_MALLOC void * lives_malloc(size_t size) {
#ifdef __cplusplus
#ifdef HAVE_OPENCV
return fastMalloc(size);
#endif
#endif
return malloc(size);
}
void lives_free_normal(void *ptr) {
#ifdef __cplusplus
#ifdef HAVE_OPENCV
fastFree(ptr);
return;
#endif
#endif
free(ptr);
}
// special de-allocators which avoid free()ing mainw->do_not_free...this is necessary to "hack" into gdk-pixbuf
// do not use this directly in code (use g_free() or weed_free() as appropriate)
void lives_free_with_check(gpointer ptr) {
if (ptr==mainw->do_not_free) return;
lives_free_normal(ptr);
}
void lives_free(gpointer ptr) {
(*mainw->free_fn)(ptr);
}
LIVES_INLINE void *lives_memset(void *s, int c, size_t n) {
return memset(s,c,n);
}
////////////////////////////////////////////////////////////////////////////
void weed_add_plant_flags (weed_plant_t *plant, int flags) {
char **leaves=weed_plant_list_leaves(plant);
int i,currflags;
for (i=0;leaves[i]!=NULL;i++) {
currflags=flags;
if (flags&WEED_LEAF_READONLY_PLUGIN&&(!strncmp(leaves[i],"plugin_",7))) currflags^=WEED_LEAF_READONLY_PLUGIN;
weed_leaf_set_flags(plant,leaves[i],weed_leaf_get_flags(plant,leaves[i])|currflags);
weed_free(leaves[i]);
}
weed_free(leaves);
}
static void weed_clear_plant_flags (weed_plant_t *plant, int flags) {
char **leaves=weed_plant_list_leaves(plant);
int i;
for (i=0;leaves[i]!=NULL;i++) {
weed_leaf_set_flags(plant,leaves[i],(weed_leaf_get_flags(plant,leaves[i])|flags)^flags);
weed_free(leaves[i]);
}
weed_free(leaves);
}
static int match_highest_version (int *hostv, int hostn, int *plugv, int plugn) {
int hmatch=0;
int i,j;
for (i=0;i<plugn;i++) {
for (j=0;j<hostn;j++) {
if (hostv[j]>plugv[i]) break;
if (hostv[j]==plugv[i]&&plugv[i]>hmatch) {
hmatch=plugv[i];
break;
}
}
}
return hmatch;
}
// symbols (function pointers) which are exported to plugins
weed_default_getter_f wdg;
weed_leaf_get_f wlg;
weed_plant_new_f wpn;
weed_plant_list_leaves_f wpll;
weed_leaf_num_elements_f wlne;
weed_leaf_element_size_f wles;
weed_leaf_seed_type_f wlst;
weed_leaf_get_flags_f wlgf;
weed_leaf_set_f wlsp;
weed_leaf_set_f wls;
weed_malloc_f weedmalloc;
weed_free_f weedfree;
weed_memcpy_f weedmemcpy;
weed_memset_f weedmemset;
weed_plant_t *weed_bootstrap_func (weed_default_getter_f *value, int num_versions, int *plugin_versions) {
int host_api_versions_supported[]={131}; // must be ordered in ascending order
int host_api_version;
weed_plant_t *host_info=weed_plant_new(WEED_PLANT_HOST_INFO);
// these functions are defined in weed-host.h and set in weed_init()
wdg=weed_default_get;
wlg=weed_leaf_get;
wpn=weed_plant_new;
wpll=weed_plant_list_leaves;
wlne=weed_leaf_num_elements;
wles=weed_leaf_element_size;
wlst=weed_leaf_seed_type;
wlgf=weed_leaf_get_flags;
wlsp=weed_leaf_set_plugin; // we pass the plugin's version to the plugin - an example of overloading with Weed
weedmalloc=weed_malloc;
weedfree=weed_free;
weedmemcpy=weed_memcpy;
weedmemset=weed_memset;
if (num_versions<1) return NULL;
if ((host_api_version=match_highest_version(host_api_versions_supported,1,plugin_versions,num_versions))==0) return NULL;
switch (host_api_version) {
case 100:
case 110:
case 120:
case 130:
case 131:
value[0]=wdg; // bootstrap weed_default_get (the plugin's default_getter)
weed_set_int_value(host_info,"api_version",host_api_version);
// here we set (void *)&fn_ptr
weed_set_voidptr_value(host_info,"weed_leaf_get_func",(void *)&wlg);
weed_set_voidptr_value(host_info,"weed_leaf_set_func",&wlsp);
weed_set_voidptr_value(host_info,"weed_plant_new_func",&wpn);
weed_set_voidptr_value(host_info,"weed_plant_list_leaves_func",&wpll);
weed_set_voidptr_value(host_info,"weed_leaf_num_elements_func",&wlne);
weed_set_voidptr_value(host_info,"weed_leaf_element_size_func",&wles);
weed_set_voidptr_value(host_info,"weed_leaf_seed_type_func",&wlst);
weed_set_voidptr_value(host_info,"weed_leaf_get_flags_func",&wlgf);
weed_set_voidptr_value(host_info,"weed_malloc_func",&weedmalloc);
weed_set_voidptr_value(host_info,"weed_free_func",&weedfree);
weed_set_voidptr_value(host_info,"weed_memset_func",&weedmemset);
weed_set_voidptr_value(host_info,"weed_memcpy_func",&weedmemcpy);
weed_add_plant_flags(host_info,WEED_LEAF_READONLY_PLUGIN);
break;
default:
return NULL;
}
return host_info;
}
//////////////////////////////////////////////////////////////////////////////
// filter library functions
int num_compound_fx(weed_plant_t *plant) {
weed_plant_t *filter;
if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) filter=weed_instance_get_filter(plant,TRUE);
else filter=plant;
if (!weed_plant_has_leaf(filter,"host_filter_list")) return 1;
return weed_leaf_num_elements(filter,"host_filter_list");
}
static gboolean has_non_alpha_palette(weed_plant_t *ctmpl) {
int *plist;
int error;
int npals=0;
register int i;
if (!weed_plant_has_leaf(ctmpl,"palette_list")) return TRUE; ///< most probably audio
npals=weed_leaf_num_elements(ctmpl,"palette_list");
plist=weed_get_int_array(ctmpl,"palette_list",&error);
for (i=0;i<npals;i++) {
if (!weed_palette_is_alpha_palette(plist[i])) {
weed_free(plist);
return TRUE;
}
}
weed_free(plist);
return FALSE;
}
weed_plant_t *weed_instance_get_filter(weed_plant_t *inst, boolean get_compound_parent) {
int error;
if (get_compound_parent&&(weed_plant_has_leaf(inst,"host_compound_class"))) return weed_get_plantptr_value(inst,"host_compound_class",&error);
return weed_get_plantptr_value(inst,"filter_class",&error);
}
lives_fx_cat_t weed_filter_categorise (weed_plant_t *pl, int in_channels, int out_channels) {
weed_plant_t *filt=pl;
int filter_flags,error;
gboolean has_out_params=FALSE;
gboolean has_in_params=FALSE;
gboolean all_out_alpha=TRUE;
gboolean all_in_alpha=TRUE;
register int i;
if (WEED_PLANT_IS_FILTER_INSTANCE(pl)) filt=weed_instance_get_filter(pl,TRUE);
// check mandatory output chans, see if any are non-alpha
if (weed_plant_has_leaf(filt,"out_channel_templates")) {
int nouts=weed_leaf_num_elements(filt,"out_channel_templates");
if (nouts>0) {
weed_plant_t **ctmpls=weed_get_plantptr_array(filt,"out_channel_templates",&error);
for (i=0;i<nouts;i++) {
if (weed_plant_has_leaf(ctmpls[i],"optional")&&
weed_get_boolean_value(ctmpls[i],"optional",&error)==WEED_TRUE) continue; ///< ignore optional channels
if (has_non_alpha_palette(ctmpls[i])) {
all_out_alpha=FALSE;
break;
}
}
weed_free(ctmpls);
}
}
// check mandatory input chans, see if any are non-alpha
if (weed_plant_has_leaf(filt,"in_channel_templates")) {
int nins=weed_leaf_num_elements(filt,"in_channel_templates");
if (nins>0) {
weed_plant_t **ctmpls=weed_get_plantptr_array(filt,"in_channel_templates",&error);
for (i=0;i<nins;i++) {
if (weed_plant_has_leaf(ctmpls[i],"optional")&&
weed_get_boolean_value(ctmpls[i],"optional",&error)==WEED_TRUE) continue; ///< ignore optional channels
if (has_non_alpha_palette(ctmpls[i])) {
all_in_alpha=FALSE;
break;
}
}
weed_free(ctmpls);
}
}
filter_flags=weed_get_int_value(filt,"flags",&error);
if (weed_plant_has_leaf(filt,"out_parameter_templates")) has_out_params=TRUE;
if (weed_plant_has_leaf(filt,"in_parameter_templates")) has_in_params=TRUE;
if (filter_flags&WEED_FILTER_IS_CONVERTER) return LIVES_FX_CAT_CONVERTER;
if (in_channels==0&&out_channels>0&&all_out_alpha) return LIVES_FX_CAT_DATA_GENERATOR;
if (in_channels==0&&out_channels>0) {
if (!has_audio_chans_out(filt,TRUE)) return LIVES_FX_CAT_VIDEO_GENERATOR;
else if (has_video_chans_out(filt,TRUE)) return LIVES_FX_CAT_AV_GENERATOR;
else return LIVES_FX_CAT_AUDIO_GENERATOR;
}
if (out_channels>=1&&in_channels>=1&&all_in_alpha&&!all_out_alpha) return LIVES_FX_CAT_DATA_VISUALISER;
if (out_channels>=1&&all_out_alpha) return LIVES_FX_CAT_ANALYSER;
if (out_channels>1) return LIVES_FX_CAT_SPLITTER;
if (in_channels>2&&out_channels==1) return LIVES_FX_CAT_COMPOSITOR;
if (in_channels==2&&out_channels==1) return LIVES_FX_CAT_TRANSITION;
if (in_channels==1&&out_channels==1&&!(has_video_chans_in(filt,TRUE))&&!(has_video_chans_out(filt,TRUE))) return LIVES_FX_CAT_AUDIO_EFFECT;
if (in_channels==1&&out_channels==1) return LIVES_FX_CAT_EFFECT;
if (in_channels>0&&out_channels==0&&has_out_params) return LIVES_FX_CAT_ANALYSER;
if (in_channels>0&&out_channels==0) return LIVES_FX_CAT_TAP;
if (in_channels==0&&out_channels==0&&has_out_params&&has_in_params) return LIVES_FX_CAT_DATA_PROCESSOR;
if (in_channels==0&&out_channels==0&&has_out_params) return LIVES_FX_CAT_DATA_SOURCE;
if (in_channels==0&&out_channels==0) return LIVES_FX_CAT_UTILITY;
return LIVES_FX_CAT_NONE;
}
lives_fx_cat_t weed_filter_subcategorise (weed_plant_t *pl, lives_fx_cat_t category, gboolean count_opt) {
weed_plant_t *filt=pl;
gboolean has_video_chansi;
if (WEED_PLANT_IS_FILTER_INSTANCE(pl)) filt=weed_instance_get_filter(pl,TRUE);
has_video_chansi=has_video_chans_in(filt,count_opt);
if (category==LIVES_FX_CAT_TRANSITION) {
if (get_transition_param(filt,FALSE)!=-1) {
if (!has_video_chansi) return LIVES_FX_CAT_AUDIO_TRANSITION;
return LIVES_FX_CAT_AV_TRANSITION;
}
return LIVES_FX_CAT_VIDEO_TRANSITION;
}
if (category==LIVES_FX_CAT_COMPOSITOR&&!has_video_chansi) return LIVES_FX_CAT_AUDIO_MIXER;
if (category==LIVES_FX_CAT_EFFECT&&!has_video_chansi) return LIVES_FX_CAT_AUDIO_EFFECT;
if (category==LIVES_FX_CAT_CONVERTER&&!has_video_chansi) return LIVES_FX_CAT_AUDIO_VOL;
if (category==LIVES_FX_CAT_ANALYSER) {
if (!has_video_chansi) return LIVES_FX_CAT_AUDIO_ANALYSER;
return LIVES_FX_CAT_VIDEO_ANALYSER;
}
return LIVES_FX_CAT_NONE;
}
////////////////////////////////////////////////////////////////////////
#define MAX_WEED_FILTERS 65536
#define MAX_WEED_INSTANCES 65536
// store keys so we now eg, which rte mask entry to xor when deiniting
static int fg_generator_key;
static int bg_generator_key;
// store modes too
static int fg_generator_mode;
static int bg_generator_mode;
// generators to start on playback, because of a problem in libvisual
// - we must start generators after starting audio
static int bg_gen_to_start;
static int fg_gen_to_start;
// store the clip, this can sometimes get lost
static gint fg_generator_clip;
//////////////////////////////////////////////////////////////////////
static weed_plant_t *weed_filters[MAX_WEED_FILTERS]; // array of filter_classes
// each 'hotkey' controls n instances, selectable as 'modes' or banks
static weed_plant_t **key_to_instance[FX_KEYS_MAX];
static weed_plant_t **key_to_instance_copy[FX_KEYS_MAX]; // copy for preview during rendering
static int *key_to_fx[FX_KEYS_MAX];
static int key_modes[FX_KEYS_MAX];
// count of how many filters we have loaded
static gint num_weed_filters;
static gchar *hashnames[MAX_WEED_FILTERS];
// per key/mode parameter defaults
weed_plant_t ***key_defaults[FX_KEYS_MAX_VIRTUAL];
/////////////////// LiVES event system /////////////////
static weed_plant_t *init_events[FX_KEYS_MAX_VIRTUAL];
static void **pchains[FX_KEYS_MAX]; // parameter changes, used during recording (not for rendering)
static void *filter_map[FX_KEYS_MAX+2];
static int next_free_key;
////////////////////////////////////////////////////////////////////
void backup_weed_instances(void) {
// this is called during multitrack rendering.
// We are rendering, but we want to display the current frame in the preview window
// thus we backup our rendering instances, apply the current frame instances, and then restore the rendering instances
register int i;
for (i=FX_KEYS_MAX_VIRTUAL;i<FX_KEYS_MAX;i++) {
key_to_instance_copy[i][0]=key_to_instance[i][0];
key_to_instance[i][0]=NULL;
}
}
void restore_weed_instances(void) {
register int i;
for (i=FX_KEYS_MAX_VIRTUAL;i<FX_KEYS_MAX;i++) {
key_to_instance[i][0]=key_to_instance_copy[i][0];
}
}
LIVES_INLINE gint step_val(gint val, gint step) {
gint ret=(gint)(val/step+.5)*step;
return ret==0?step:ret;
}
gchar *weed_filter_get_type(weed_plant_t *filter, gboolean getsub) {
// return value should be g_free'd after use
gchar *tmp1,*tmp2,*ret;
lives_fx_cat_t cat=weed_filter_categorise(filter,
enabled_in_channels(filter,FALSE),
enabled_out_channels(filter,FALSE));
lives_fx_cat_t sub=weed_filter_subcategorise(filter,cat,FALSE);
if (!getsub||sub==LIVES_FX_CAT_NONE)
return lives_fx_cat_to_text(cat,FALSE);
tmp1=lives_fx_cat_to_text(cat,FALSE);
tmp2=lives_fx_cat_to_text(sub,FALSE);
ret=g_strdup_printf("%s (%s)",tmp1,tmp2);
g_free(tmp1);
g_free(tmp2);
return ret;
}
void update_host_info (weed_plant_t *inst) {
// set "host_audio_plugin" in the host_info
int error;
weed_plant_t *filter,*pinfo,*hinfo;
filter=weed_instance_get_filter(inst,FALSE);
pinfo=weed_get_plantptr_value(filter,"plugin_info",&error);
hinfo=weed_get_plantptr_value(pinfo,"host_info",&error);
switch (prefs->audio_player) {
case AUD_PLAYER_MPLAYER:
weed_set_string_value(hinfo,"host_audio_player","mplayer");
break;
case AUD_PLAYER_SOX:
weed_set_string_value(hinfo,"host_audio_player","sox");
break;
case AUD_PLAYER_JACK:
weed_set_string_value(hinfo,"host_audio_player","jack");
break;
case AUD_PLAYER_PULSE:
weed_set_string_value(hinfo,"host_audio_player","pulseaudio");
break;
}
}
weed_plant_t *get_enabled_channel (weed_plant_t *inst, gint which, gboolean is_in) {
// plant is a filter_instance
// "which" starts at 0
int i=0,error,nchans=3;
weed_plant_t **channels;
weed_plant_t *retval;
if (!WEED_PLANT_IS_FILTER_INSTANCE(inst)) return NULL;
if (is_in) {
if (!weed_plant_has_leaf(inst,"in_channels")) return NULL;
channels=weed_get_plantptr_array(inst,"in_channels",&error);
nchans=weed_leaf_num_elements(inst,"in_channels");
}
else {
if (!weed_plant_has_leaf(inst,"out_channels")) return NULL;
channels=weed_get_plantptr_array(inst,"out_channels",&error);
nchans=weed_leaf_num_elements(inst,"out_channels");
}
if (channels==NULL||nchans==0) return NULL;
while (1) {
if (!weed_plant_has_leaf(channels[i],"disabled")||weed_get_boolean_value(channels[i],"disabled",&error)==WEED_FALSE)
which--;
if (which<0) break;
if (++i>=nchans) {
weed_free(channels);
return NULL;
}
}
retval=channels[i];
weed_free(channels);
return retval;
}
weed_plant_t *get_mandatory_channel (weed_plant_t *filter, gint which, gboolean is_in) {
// plant is a filter_class
// "which" starts at 0
int i=0,error;
weed_plant_t **ctmpls;
weed_plant_t *retval;
if (!WEED_PLANT_IS_FILTER_CLASS(filter)) return NULL;
if (is_in) ctmpls=weed_get_plantptr_array(filter,"in_channel_templates",&error);
else ctmpls=weed_get_plantptr_array(filter,"out_channel_templates",&error);
if (ctmpls==NULL) return NULL;
while (which>-1) {
if (!weed_plant_has_leaf(ctmpls[i],"optional")) which--;
i++;
}
retval=ctmpls[i-1];
weed_free(ctmpls);
return retval;
}
gboolean weed_filter_is_resizer(weed_plant_t *filt) {
int error;
int filter_flags=weed_get_int_value(filt,"flags",&error);
if (filter_flags&WEED_FILTER_IS_CONVERTER) {
weed_plant_t *first_out=get_mandatory_channel(filt,0,FALSE);
if (first_out!=NULL) {
int tmpl_flags=weed_get_int_value(first_out,"flags",&error);
if (tmpl_flags&WEED_CHANNEL_SIZE_CAN_VARY) return TRUE;
}
}
return FALSE;
}
gboolean weed_instance_is_resizer(weed_plant_t *inst) {
weed_plant_t *ftmpl=weed_instance_get_filter(inst,TRUE);
return weed_filter_is_resizer(ftmpl);
}
gboolean is_audio_channel_in(weed_plant_t *inst, int chnum) {
int error,nchans=weed_leaf_num_elements(inst,"in_channels");
weed_plant_t **in_chans;
weed_plant_t *ctmpl;
if (nchans<=chnum) return FALSE;
in_chans=weed_get_plantptr_array(inst,"in_channels",&error);
ctmpl=weed_get_plantptr_value(in_chans[chnum],"template",&error);
weed_free(in_chans);
if (weed_get_boolean_value(ctmpl,"is_audio",&error)==WEED_TRUE) {
return TRUE;
}
return FALSE;
}
gboolean has_video_chans_in(weed_plant_t *filter, gboolean count_opt) {
int error,nchans=weed_leaf_num_elements(filter,"in_channel_templates");
weed_plant_t **in_ctmpls;
int i;
if (nchans==0) return FALSE;
in_ctmpls=weed_get_plantptr_array(filter,"in_channel_templates",&error);
for (i=0;i<nchans;i++) {
if (!count_opt&&weed_plant_has_leaf(in_ctmpls[i],"optional")&&
weed_get_boolean_value(in_ctmpls[i],"optional",&error)==WEED_TRUE) continue;
if (weed_plant_has_leaf(in_ctmpls[i],"is_audio")&&weed_get_boolean_value(in_ctmpls[i],"is_audio",&error)==WEED_TRUE)
continue;
weed_free(in_ctmpls);
return TRUE;
}
weed_free(in_ctmpls);
return FALSE;
}
gboolean has_audio_chans_in(weed_plant_t *filter, gboolean count_opt) {
int error,nchans=weed_leaf_num_elements(filter,"in_channel_templates");
weed_plant_t **in_ctmpls;
int i;
if (nchans==0) return FALSE;
in_ctmpls=weed_get_plantptr_array(filter,"in_channel_templates",&error);
for (i=0;i<nchans;i++) {
if (!count_opt&&weed_plant_has_leaf(in_ctmpls[i],"optional")&&
weed_get_boolean_value(in_ctmpls[i],"optional",&error)==WEED_TRUE) continue;
if (!weed_plant_has_leaf(in_ctmpls[i],"is_audio")||
weed_get_boolean_value(in_ctmpls[i],"is_audio",&error)==WEED_FALSE) continue;
weed_free(in_ctmpls);
return TRUE;
}
weed_free(in_ctmpls);
return FALSE;
}
gboolean is_audio_channel_out(weed_plant_t *inst, int chnum) {
int error,nchans=weed_leaf_num_elements(inst,"out_channels");
weed_plant_t **out_chans;
weed_plant_t *ctmpl;
if (nchans<=chnum) return FALSE;
out_chans=weed_get_plantptr_array(inst,"out_channels",&error);
ctmpl=weed_get_plantptr_value(out_chans[chnum],"template",&error);
weed_free(out_chans);
if (weed_get_boolean_value(ctmpl,"is_audio",&error)==WEED_TRUE) {
return TRUE;
}
return FALSE;
}
gboolean has_video_chans_out(weed_plant_t *filter, gboolean count_opt) {
int error,nchans=weed_leaf_num_elements(filter,"out_channel_templates");
weed_plant_t **out_ctmpls;
int i;
if (nchans==0) return FALSE;
out_ctmpls=weed_get_plantptr_array(filter,"out_channel_templates",&error);
for (i=0;i<nchans;i++) {
if (!count_opt&&weed_plant_has_leaf(out_ctmpls[i],"optional")&&
weed_get_boolean_value(out_ctmpls[i],"optional",&error)==WEED_TRUE) continue;
if (weed_plant_has_leaf(out_ctmpls[i],"is_audio")&&
weed_get_boolean_value(out_ctmpls[i],"is_audio",&error)==WEED_TRUE) continue;
weed_free(out_ctmpls);
return TRUE;
}
weed_free(out_ctmpls);
return FALSE;
}
gboolean has_audio_chans_out(weed_plant_t *filter, gboolean count_opt) {
int error,nchans=weed_leaf_num_elements(filter,"out_channel_templates");
weed_plant_t **out_ctmpls;
int i;
if (nchans==0) return FALSE;
out_ctmpls=weed_get_plantptr_array(filter,"out_channel_templates",&error);
for (i=0;i<nchans;i++) {
if (!count_opt&&weed_plant_has_leaf(out_ctmpls[i],"optional")&&
weed_get_boolean_value(out_ctmpls[i],"optional",&error)==WEED_TRUE) continue;
if (!weed_plant_has_leaf(out_ctmpls[i],"is_audio")||
weed_get_boolean_value(out_ctmpls[i],"is_audio",&error)==WEED_FALSE) continue;
weed_free(out_ctmpls);
return TRUE;
}
weed_free(out_ctmpls);
return FALSE;
}
gboolean is_pure_audio(weed_plant_t *plant, gboolean count_opt) {
weed_plant_t *filter=plant;
if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) filter=weed_instance_get_filter(plant,FALSE);
if ((has_audio_chans_in(filter,count_opt) || has_audio_chans_out(filter,count_opt)) &&
!has_video_chans_in(filter,count_opt) && !has_video_chans_out(filter,count_opt)) return TRUE;
return FALSE;
}
gboolean weed_parameter_has_variable_elements_strict(weed_plant_t *inst, weed_plant_t *ptmpl) {
/** see if param has variable elements, using the strictest check */
weed_plant_t **chans,*ctmpl;
int error,i;
int flags=weed_get_int_value(ptmpl,"flags",&error);
int nchans;
if (flags&WEED_PARAMETER_VARIABLE_ELEMENTS) return TRUE;
if (inst==NULL) return FALSE;
if (!(flags&WEED_PARAMETER_ELEMENT_PER_CHANNEL)) return FALSE;
if (!weed_plant_has_leaf(inst,"in_channels")
||(nchans=weed_leaf_num_elements(inst,"in_channels"))==0)
return FALSE;
chans=weed_get_plantptr_array(inst,"in_channels",&error);
for (i=0;i<nchans;i++) {
if (weed_plant_has_leaf(chans[i],"disabled")&&
weed_get_boolean_value(chans[i],"disabled",&error)==WEED_TRUE) continue; //ignore disabled channels
ctmpl=weed_get_plantptr_value(chans[i],"template",&error);
if (weed_plant_has_leaf(ctmpl,"max_repeats")&&weed_get_int_value(ctmpl,"max_repeats",&error)!=1) {
weed_free(chans);
return TRUE;
}
}
weed_free(chans);
return FALSE;
}
static void create_filter_map (void) {
/** here we create an effect map which defines the order in which effects are applied to a frame stack
* this is done during recording, the keymap is from mainw->rte which is a bitmap of effect keys
* keys are applied here from smallest (ctrl-1) to largest (virtual key ctrl-FX_KEYS_MAX_VIRTUAL)
* this is transformed into filter_map which holds init_events
* these pointers are then stored in a filter_map event
* what we actually point to are the init_events for the effects. The init_events are stored when we
* init an effect
* during rendering we read the filter_map event, and retrieve the new key, which is at that time
* held in the
* "host_tag" property of the init_event, and we apply our effects
* (which are then bound to virtual keys >=FX_KEYS_MAX_VIRTUAL)
* [note] that we can do cool things, like mapping the same instance multiple times (though it will always
* apply itself to the same in/out tracks
* we don't need to worry about free()ing init_events, since they will be free'd
* when the instance is deinited
*/
int count=0,i;
weed_plant_t *inst;
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++)
if (mainw->rte&(GU641<<i)&&(inst=key_to_instance[i][key_modes[i]])!=NULL&&
enabled_in_channels (inst,FALSE)>0) filter_map[count++]=init_events[i];
filter_map[count]=NULL; // marks the end of the effect map
}
weed_plant_t *add_filter_deinit_events (weed_plant_t *event_list) {
// during rendering we use the "keys" FX_KEYS_MAX_VIRTUAL -> FX_KEYS_MAX
// here we add effect_deinit events to an event_list
int i;
gboolean needs_filter_map=FALSE;
weed_timecode_t last_tc=0;
if (event_list!=NULL) last_tc=get_event_timecode(get_last_event(event_list));
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if (init_events[i]!=NULL) {
event_list=append_filter_deinit_event (event_list,last_tc,init_events[i],pchains[i]);
init_events[i]=NULL;
if (pchains[i]!=NULL) g_free(pchains[i]);
needs_filter_map=TRUE;
}
}
// add an empty filter_map event (in case more frames are added)
create_filter_map(); // we create filter_map event_t * array with ordered effects
if (needs_filter_map) event_list=append_filter_map_event (mainw->event_list,last_tc,filter_map);
return event_list;
}
weed_plant_t *add_filter_init_events (weed_plant_t *event_list, weed_timecode_t tc) {
// during rendering we use the "keys" FX_KEYS_MAX_VIRTUAL -> FX_KEYS_MAX
// here we are about to start playback, and we add init events for every effect which is switched on
// we add the init events with a timecode of 0
int i;
weed_plant_t *inst;
int fx_idx,ntracks;
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if ((inst=key_to_instance[i][key_modes[i]])!=NULL&&enabled_in_channels (inst,FALSE)>0) {
event_list=append_filter_init_event (event_list,tc,(fx_idx=key_to_fx[i][key_modes[i]]),-1,i,inst);
init_events[i]=get_last_event(event_list);
ntracks=weed_leaf_num_elements(init_events[i],"in_tracks");
pchains[i]=filter_init_add_pchanges(event_list,inst,init_events[i],ntracks);
}
}
// add an empty filter_map event (in case more frames are added)
create_filter_map(); // we create filter_map event_t * array with ordered effects
if (filter_map[0]!=NULL) event_list=append_filter_map_event (event_list,tc,filter_map);
return event_list;
}
// check if palette is in the palette_list
// if not, return next best palette to use, using a heuristic method
// num_palettes is the size of the palette list
int check_weed_palette_list (int *palette_list, int num_palettes, int palette) {
int i;
int best_palette=WEED_PALETTE_END;
for (i=0;i<num_palettes;i++) {
if (palette_list[i]==palette) {
// exact match - return it
return palette;
}
// pass 1, see if we can find same or higher quality in same colorspace
if (weed_palette_is_alpha_palette(palette)) {
if (palette_list[i]==WEED_PALETTE_A8) best_palette=palette_list[i];
if (palette_list[i]==WEED_PALETTE_A1&&(best_palette==WEED_PALETTE_AFLOAT||best_palette==WEED_PALETTE_END))
best_palette=palette_list[i];
if (palette_list[i]==WEED_PALETTE_AFLOAT&&best_palette==WEED_PALETTE_END) best_palette=palette_list[i];
}
else if (weed_palette_is_rgb_palette(palette)) {
if (palette_list[i]==WEED_PALETTE_RGBAFLOAT&&(palette==WEED_PALETTE_RGBFLOAT||best_palette==WEED_PALETTE_END))
best_palette=palette_list[i];
if (palette_list[i]==WEED_PALETTE_RGBFLOAT&&best_palette==WEED_PALETTE_END) best_palette=palette_list[i];
if (palette_list[i]==WEED_PALETTE_RGBA32||
palette_list[i]==WEED_PALETTE_BGRA32||palette_list[i]==WEED_PALETTE_ARGB32) {
if ((best_palette==WEED_PALETTE_END||
best_palette==WEED_PALETTE_RGBFLOAT||best_palette==WEED_PALETTE_RGBAFLOAT)||
weed_palette_has_alpha_channel(palette)) best_palette=palette_list[i];
}
if (!weed_palette_has_alpha_channel(palette)||
(best_palette==WEED_PALETTE_END||best_palette==WEED_PALETTE_RGBFLOAT||best_palette==WEED_PALETTE_RGBAFLOAT)) {
if (palette_list[i]==WEED_PALETTE_RGB24||palette_list[i]==WEED_PALETTE_BGR24) {
best_palette=palette_list[i];
}
}
}
else {
// yuv
if (palette==WEED_PALETTE_YUV411&&(palette_list[i]==WEED_PALETTE_YUV422P)) best_palette=palette_list[i];
if (palette==WEED_PALETTE_YUV411&&best_palette!=WEED_PALETTE_YUV422P&&
(palette_list[i]==WEED_PALETTE_YUV420P||palette_list[i]==WEED_PALETTE_YVU420P)) best_palette=palette_list[i];
if (palette==WEED_PALETTE_YUV420P&&palette_list[i]==WEED_PALETTE_YVU420P) best_palette=palette_list[i];
if (palette==WEED_PALETTE_YVU420P&&palette_list[i]==WEED_PALETTE_YUV420P) best_palette=palette_list[i];
if (((palette==WEED_PALETTE_YUV420P&&best_palette!=WEED_PALETTE_YVU420P)||
(palette==WEED_PALETTE_YVU420P&&best_palette!=WEED_PALETTE_YUV420P))&&
(palette_list[i]==WEED_PALETTE_YUV422P||palette_list[i]==WEED_PALETTE_UYVY8888||
palette_list[i]==WEED_PALETTE_YUYV8888)) best_palette=palette_list[i];
if ((palette==WEED_PALETTE_YUV422P||palette==WEED_PALETTE_UYVY8888||palette==WEED_PALETTE_YUYV8888)&&
(palette_list[i]==WEED_PALETTE_YUV422P||palette_list[i]==WEED_PALETTE_UYVY8888||
palette_list[i]==WEED_PALETTE_YUYV8888)) best_palette=palette_list[i];
if (palette_list[i]==WEED_PALETTE_YUVA8888||palette_list[i]==WEED_PALETTE_YUVA4444P) {
if (best_palette==WEED_PALETTE_END||weed_palette_has_alpha_channel(palette)) best_palette=palette_list[i];
}
if (best_palette==WEED_PALETTE_END||((best_palette==WEED_PALETTE_YUVA8888||best_palette==WEED_PALETTE_YUVA4444P)&&
!weed_palette_has_alpha_channel(palette))) {
if (palette_list[i]==WEED_PALETTE_YUV888||palette_list[i]==WEED_PALETTE_YUV444P) {
best_palette=palette_list[i];
}
}
}
}
// pass 2:
// if we had to drop alpha, see if we can preserve it in the other colorspace
for (i=0;i<num_palettes;i++) {
if (weed_palette_has_alpha_channel(palette)&&
(best_palette==WEED_PALETTE_END||!weed_palette_has_alpha_channel(best_palette))) {
if (weed_palette_is_rgb_palette(palette)) {
if (palette_list[i]==WEED_PALETTE_YUVA8888||palette_list[i]==WEED_PALETTE_YUVA4444P) best_palette=palette_list[i];
}
else {
if (palette_list[i]==WEED_PALETTE_RGBA32||palette_list[i]==WEED_PALETTE_BGRA32||
palette_list[i]==WEED_PALETTE_ARGB32) best_palette=palette_list[i];
}
}
}
// pass 3: no alpha; switch colorspaces, try to find same or higher quality
if (best_palette==WEED_PALETTE_END) {
for (i=0;i<num_palettes;i++) {
if ((weed_palette_is_rgb_palette(palette)||best_palette==WEED_PALETTE_END)&&
(palette_list[i]==WEED_PALETTE_RGB24||palette_list[i]==WEED_PALETTE_BGR24)) {
best_palette=palette_list[i];
}
if ((weed_palette_is_yuv_palette(palette)||best_palette==WEED_PALETTE_END)&&
(palette_list[i]==WEED_PALETTE_YUV888||palette_list[i]==WEED_PALETTE_YUV444P)) {
best_palette=palette_list[i];
}
if (best_palette==WEED_PALETTE_END&&(palette_list[i]==WEED_PALETTE_RGBA32||
palette_list[i]==WEED_PALETTE_BGRA32||palette_list[i]==WEED_PALETTE_ARGB32)) {
best_palette=palette_list[i];
}
if (best_palette==WEED_PALETTE_END&&
(palette_list[i]==WEED_PALETTE_YUVA8888||palette_list[i]==WEED_PALETTE_YUVA4444P)) {
best_palette=palette_list[i];
}
}
}
// pass 4: switch to YUV, try to find highest quality
if (best_palette==WEED_PALETTE_END) {
for (i=0;i<num_palettes;i++) {
if (palette_list[i]==WEED_PALETTE_UYVY8888||palette_list[i]==WEED_PALETTE_YUYV8888||
palette_list[i]==WEED_PALETTE_YUV422P) {
best_palette=palette_list[i];
}
if ((best_palette==WEED_PALETTE_END||best_palette==WEED_PALETTE_YUV411)&&
(palette_list[i]==WEED_PALETTE_YUV420P||palette_list[i]==WEED_PALETTE_YVU420P)) {
best_palette=palette_list[i];
}
if (best_palette==WEED_PALETTE_END&&palette_list[i]==WEED_PALETTE_YUV411) best_palette=palette_list[i];
}
}
// pass 5: tweak results to use (probably) most common colourspaces
for (i=0;i<num_palettes;i++) {
if (palette_list[i]==WEED_PALETTE_RGBA32&&(best_palette==WEED_PALETTE_BGRA32||best_palette==WEED_PALETTE_ARGB32)) {
best_palette=palette_list[i];
}
if (palette_list[i]==WEED_PALETTE_RGB24&&best_palette==WEED_PALETTE_BGR24) {
best_palette=palette_list[i];
}
if (palette_list[i]==WEED_PALETTE_YUV420P&&best_palette==WEED_PALETTE_YVU420P) {
best_palette=palette_list[i];
}
if (palette_list[i]==WEED_PALETTE_UYVY8888&&best_palette==WEED_PALETTE_YUYV8888) {
best_palette=palette_list[i];
}
}
#ifdef DEBUG_PALETTES
g_printerr("Debug: best palette for %d is %d\n",palette,best_palette);
#endif
return best_palette;
}
static void set_channel_size (weed_plant_t *channel, gint width, gint height, int numplanes, int *rowstrides) {
int error;
int max;
weed_plant_t *chantmpl=weed_get_plantptr_value(channel,"template",&error);
// note: rowstrides is just a guess, we will set the actual value when we come to process the effect
if (weed_plant_has_leaf(chantmpl,"width")&&weed_get_int_value(chantmpl,"width",&error)!=0)
width=weed_get_int_value(chantmpl,"width",&error);
else if (weed_plant_has_leaf(chantmpl,"host_width")) width=weed_get_int_value(chantmpl,"host_width",&error);
if (weed_plant_has_leaf(chantmpl,"hstep")) width=step_val(width,weed_get_int_value(chantmpl,"hstep",&error));
if (weed_plant_has_leaf(chantmpl,"maxwidth")) {
max=weed_get_int_value(chantmpl,"maxwidth",&error);
if (width>max) width=max;
}
weed_set_int_value(channel,"width",width);
if (weed_plant_has_leaf(chantmpl,"height")&&weed_get_int_value(chantmpl,"height",&error)!=0)
height=weed_get_int_value(chantmpl,"height",&error);
else if (weed_plant_has_leaf(chantmpl,"host_height")) height=weed_get_int_value(chantmpl,"host_height",&error);
if (weed_plant_has_leaf(chantmpl,"vstep")) height=step_val(height,weed_get_int_value(chantmpl,"vstep",&error));
if (weed_plant_has_leaf(chantmpl,"maxheight")) {
max=weed_get_int_value(chantmpl,"maxheight",&error);
if (height>max) height=max;
}
weed_set_int_value(channel,"height",height);
if (rowstrides!=NULL) weed_set_int_array(channel,"rowstrides",numplanes,rowstrides);
}
static gboolean rowstrides_differ(int n1, int *n1_array, int n2, int *n2_array) {
// returns TRUE if the rowstrides differ
int i;
if (n1!=n2) return TRUE;
for (i=0;i<n1;i++) if (n1_array[i]!=n2_array[i]) return TRUE;
return FALSE;
}
static gboolean align (void **pixel_data, size_t alignment, int numplanes, int height, int *rowstrides,
int *contiguous) {
#ifndef HAVE_POSIX_MEMALIGN
return FALSE;
#else
// returns TRUE on success
int i;
int memerror;
gboolean needs_change=FALSE;
void *npixel_data;
void **new_pixel_data;
size_t size,totsize=0;
for (i=0;i<numplanes;i++) {
if (((uint64_t)(pixel_data[i]))%alignment==0) continue;
needs_change=TRUE;
}
if (!needs_change) return TRUE;
for (i=0;i<numplanes;i++) {
size=height*rowstrides[i];
totsize+=CEIL(size,32);
}
// try contiguous first
if ((memerror=posix_memalign(&npixel_data,alignment,totsize))) return FALSE;
new_pixel_data=(void **)g_malloc(numplanes*(sizeof(void *)));
// recheck
needs_change=FALSE;
for (i=0;i<numplanes;i++) {
if (((uint64_t)(pixel_data[i]))%alignment==0) continue;
needs_change=TRUE;
}
if (!needs_change) {
for (i=0;i<numplanes;i++) {
memcpy(npixel_data,pixel_data[i],height*rowstrides[i]);
new_pixel_data[i]=npixel_data;
size=height*rowstrides[i];
npixel_data+=CEIL(size,32);
}
for (i=0;i<numplanes;i++) {
if (i==0||!(*contiguous))
g_free(pixel_data[i]);
pixel_data[i]=new_pixel_data[i];
}
g_free(new_pixel_data);
if (numplanes>1) *contiguous=TRUE;
else *contiguous=FALSE;
return TRUE;
}
g_free(npixel_data);
// non-contiguous
for (i=0;i<numplanes;i++) {
if ((memerror=posix_memalign(&npixel_data,alignment,height*rowstrides[i]))) return FALSE;
memcpy(npixel_data,pixel_data[i],height*rowstrides[i]);
new_pixel_data[i]=npixel_data;
}
for (i=0;i<numplanes;i++) {
if (i==0||!(*contiguous))
g_free(pixel_data[i]);
pixel_data[i]=new_pixel_data[i];
}
g_free(new_pixel_data);
*contiguous=FALSE;
return TRUE;
#endif
}
LIVES_INLINE int weed_flagset_array_count(weed_plant_t **array, gboolean set_readonly) {
int i=0;
while (array[i]!=NULL) {
if (set_readonly) weed_add_plant_flags(array[i],WEED_LEAF_READONLY_PLUGIN);
i++;
}
return i;
}
void set_param_gui_readonly (weed_plant_t *inst) {
int num_params,error,i;
weed_plant_t **params,*gui,*ptmpl;
num_params=weed_leaf_num_elements(inst,"in_parameters");
if (num_params>0) {
params=weed_get_plantptr_array(inst,"in_parameters",&error);
for (i=0;i<num_params;i++) {
ptmpl=weed_get_plantptr_value(params[i],"template",&error);
if (weed_plant_has_leaf(ptmpl,"gui")) {
gui=weed_get_plantptr_value(ptmpl,"gui",&error);
weed_add_plant_flags(gui,WEED_LEAF_READONLY_PLUGIN);
}
}
weed_free(params);
}
num_params=weed_leaf_num_elements(inst,"out_parameters");
if (num_params>0) {
params=weed_get_plantptr_array(inst,"out_parameters",&error);
for (i=0;i<num_params;i++) {
ptmpl=weed_get_plantptr_value(params[i],"template",&error);
if (weed_plant_has_leaf(ptmpl,"gui")) {
gui=weed_get_plantptr_value(ptmpl,"gui",&error);
weed_add_plant_flags(gui,WEED_LEAF_READONLY_PLUGIN);
}
}
weed_free(params);
}
}
void set_param_gui_readwrite (weed_plant_t *inst) {
int num_params,error,i;
weed_plant_t **params,*gui,*ptmpl;
num_params=weed_leaf_num_elements(inst,"in_parameters");
if (num_params>0) {
params=weed_get_plantptr_array(inst,"in_parameters",&error);
for (i=0;i<num_params;i++) {
ptmpl=weed_get_plantptr_value(params[i],"template",&error);
if (weed_plant_has_leaf(ptmpl,"gui")) {
gui=weed_get_plantptr_value(ptmpl,"gui",&error);
weed_clear_plant_flags(gui,WEED_LEAF_READONLY_PLUGIN);
}
}
weed_free(params);
}
num_params=weed_leaf_num_elements(inst,"out_parameters");
if (num_params>0) {
params=weed_get_plantptr_array(inst,"out_parameters",&error);
for (i=0;i<num_params;i++) {
ptmpl=weed_get_plantptr_value(params[i],"template",&error);
if (weed_plant_has_leaf(ptmpl,"gui")) {
gui=weed_get_plantptr_value(ptmpl,"gui",&error);
weed_clear_plant_flags(gui,WEED_LEAF_READONLY_PLUGIN);
}
}
weed_free(params);
}
}
/// change directory to plugin installation dir so it can find any data files
///
/// returns copy of current directory (before directory change) which should be freed after use
gchar *cd_to_plugin_dir(weed_plant_t *filter) {
char *ret;
int error;
weed_plant_t *plugin_info=weed_get_plantptr_value(filter,"plugin_info",&error);
char *ppath=weed_get_string_value(plugin_info,"plugin_path",&error);
ret=g_get_current_dir();
// allow this to fail -it's not that important - it just means any plugin data files wont be found
// besides, we dont want to show warnings at 50 fps
lives_chdir(ppath,TRUE);
weed_free(ppath);
return ret;
}
lives_filter_error_t weed_reinit_effect (weed_plant_t *inst, boolean reinit_compound) {
weed_plant_t *filter;
gchar *cwd;
boolean is_audio=FALSE,deinit_first=FALSE;
lives_filter_error_t filter_error=FILTER_NO_ERROR;
int error;
reinit:
filter=weed_instance_get_filter(inst,FALSE);
if (weed_plant_has_leaf(inst,"host_inited")&&weed_get_boolean_value(inst,"host_inited",&error)==WEED_TRUE) deinit_first=TRUE;
if (deinit_first) {
if (has_audio_chans_in(filter,FALSE)||has_audio_chans_out(filter,FALSE)) {
pthread_mutex_lock(&mainw->afilter_mutex);
is_audio=TRUE;
}
weed_call_deinit_func(inst);
}
if (weed_plant_has_leaf(filter,"init_func")) {
weed_init_f *init_func_ptr_ptr;
weed_init_f init_func;
weed_leaf_get(filter,"init_func",0,(void *)&init_func_ptr_ptr);
init_func=init_func_ptr_ptr[0];
cwd=cd_to_plugin_dir(filter);
if (init_func!=NULL) {
lives_rfx_t *rfx;
set_param_gui_readwrite(inst);
update_host_info(inst);
if ((*init_func)(inst)!=WEED_NO_ERROR) {
if (is_audio) pthread_mutex_unlock(&mainw->afilter_mutex);
lives_chdir(cwd,FALSE);
return FILTER_ERROR_COULD_NOT_REINIT;
}
if (is_audio) pthread_mutex_unlock(&mainw->afilter_mutex);
set_param_gui_readonly(inst);
if (fx_dialog[1]!=NULL) {
// redraw GUI if necessary
rfx=(lives_rfx_t *)g_object_get_data(G_OBJECT(fx_dialog[1]),"rfx");
if (rfx->source_type==LIVES_RFX_SOURCE_WEED&&rfx->source==inst) {
gint keyw=GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fx_dialog[1]),"key"));
gint modew=GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fx_dialog[1]),"mode"));
redraw_pwindow(keyw,modew);
}
}
// redraw set defs window
}
else if (is_audio) pthread_mutex_unlock(&mainw->afilter_mutex);
if (!deinit_first) {
if (is_audio) pthread_mutex_lock(&mainw->afilter_mutex);
weed_call_deinit_func(inst);
if (is_audio) pthread_mutex_unlock(&mainw->afilter_mutex);
}
lives_chdir(cwd,FALSE);
g_free(cwd);
filter_error=FILTER_INFO_REINITED;
}
else if (is_audio) pthread_mutex_unlock(&mainw->afilter_mutex);
if (deinit_first) weed_set_boolean_value(inst,"host_inited",WEED_TRUE);
else weed_set_boolean_value(inst,"host_inited",WEED_FALSE);
if (reinit_compound&&weed_plant_has_leaf(inst,"host_next_instance")) {
// handle compound fx
inst=weed_get_plantptr_value(inst,"host_next_instance",&error);
goto reinit;
}
return filter_error;
}
void weed_reinit_all(void) {
// reinit all effects on playback start
int i,error;
weed_plant_t *instance,*last_inst;
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if (rte_key_valid(i+1,TRUE)) {
if (mainw->rte&(GU641<<i)) {
mainw->osc_block=TRUE;
if ((instance=key_to_instance[i][key_modes[i]])==NULL) continue;
last_inst=instance;
// ignore video generators
while (weed_plant_has_leaf(last_inst,"host_next_instance")) last_inst=weed_get_plantptr_value(last_inst,"host_next_instance",&error);
if (enabled_in_channels(instance,FALSE)==0&&enabled_out_channels(last_inst,FALSE)>0&&!is_pure_audio(last_inst,FALSE)) continue;
weed_reinit_effect(instance,TRUE);
}
}
}
mainw->osc_block=FALSE;
}
static void *thread_process_func(void *arg) {
int retval;
struct _procvals *procvals=(struct _procvals *)arg;
weed_process_f *process_func_ptr_ptr;
weed_process_f process_func;
weed_plant_t *inst=procvals->inst;
weed_timecode_t tc=procvals->tc;
weed_plant_t *filter=weed_instance_get_filter(inst,FALSE);
weed_leaf_get(filter,"process_func",0,(void *)&process_func_ptr_ptr);
process_func=process_func_ptr_ptr[0];
retval = (*process_func)(inst,tc);
return (void *)GINT_TO_POINTER(retval);
}
static lives_filter_error_t process_func_threaded(weed_plant_t *inst, weed_plant_t **out_channels, weed_timecode_t tc) {
// split output(s) into horizontal slices
int offset=0;
int dheight,height;
int nthreads=0;
int error;
gboolean got_invalid=FALSE;
gboolean useme=FALSE;
int nchannels=weed_leaf_num_elements(inst,"out_channels");
int retval;
int minh;
struct _procvals procvals[MAX_FX_THREADS];
pthread_t dthreads[MAX_FX_THREADS];
weed_plant_t *xinst[MAX_FX_THREADS];
weed_plant_t **xchannels;
weed_plant_t *ctmpl;
void *tretval;
register int i,j;
height=weed_get_int_value(out_channels[0],"height",&error);
pthread_mutex_lock(&mainw->data_mutex);
for (j=0;j<prefs->nfx_threads;j++) {
// each thread needs its own copy of the output channels, so it can have its own "offset" and "height"
// therefore it also needs its own copy of inst
// but note that "pixel_data" always points to the same memory buffer(s)
xinst[j]=weed_plant_copy(inst);
xchannels=(weed_plant_t **)g_malloc(nchannels*sizeof(weed_plant_t *));
for (i=0;i<nchannels;i++) {
xchannels[i]=weed_plant_copy(out_channels[i]);
height=weed_get_int_value(xchannels[i],"height",&error);
ctmpl=weed_get_plantptr_value(out_channels[i],"template",&error);
if (weed_plant_has_leaf(ctmpl,"vstep"))
minh=weed_get_int_value(ctmpl,"vstep",&error);
else minh=4;
if (minh<4) minh=4;
dheight=CEIL((double)height/(double)prefs->nfx_threads,minh);
offset=dheight*j;
if ((height-offset-dheight)<minh) {
dheight=height-offset;
useme=1;
}
if ((height-offset)<minh) break;
weed_set_int_value(xchannels[i],"offset",offset);
weed_set_int_value(xchannels[i],"height",dheight);
}
weed_set_plantptr_array(xinst[j],"out_channels",nchannels,xchannels);
g_free(xchannels);
procvals[j].inst=xinst[j];
procvals[j].tc=tc; // use same timecode for all slices
if (offset>=height) break;
if (!useme) {
// start a thread for processing
pthread_create(&dthreads[j],NULL,thread_process_func,&procvals[j]);
nthreads++; // actual number of threads used
}
else {
// use main thread
tretval=thread_process_func(&procvals[j]);
retval=GPOINTER_TO_INT((gpointer)tretval);
if (retval==WEED_ERROR_PLUGIN_INVALID) got_invalid=TRUE;
}
}
// wait for threads to finish
for (j=0;j<prefs->nfx_threads;j++) {
retval=WEED_NO_ERROR;
if (j<nthreads) {
pthread_join(dthreads[j],&tretval);
retval=GPOINTER_TO_INT((gpointer)tretval);
}
xchannels=weed_get_plantptr_array(xinst[j],"out_channels",&error);
for (i=0;i<nchannels;i++) {
weed_plant_free(xchannels[i]);
}
weed_free(xchannels);
weed_plant_free(xinst[j]);
if (retval==WEED_ERROR_PLUGIN_INVALID) got_invalid=TRUE;
}
pthread_mutex_unlock(&mainw->data_mutex);
if (got_invalid) return FILTER_ERROR_MUST_RELOAD;
return FILTER_NO_ERROR;
}
lives_filter_error_t weed_apply_instance (weed_plant_t *inst, weed_plant_t *init_event, weed_plant_t **layers,
int opwidth, int opheight, weed_timecode_t tc) {
// here we:
// get our in_tracks and out_tracks that map filter_instance channels to layers
// clear "disabled" if we have non-zero frame and there is no "disabled" in template
// if we have a zero frame, set "disabled" if "optional", otherwise we cannot apply the filter
// set channel timecodes
// pull pixel_data (unless it is there already)
// set each channel width,height to match largest of in layers
// if width and height are wrong, resize in the layer
// if palette is wrong, first we try to change the plugin channel palette,
// if not possible we convert palette in the layer
// apply the effect, put result in output layer, set layer palette, width, height, rowstrides
// if filter does not support inplace, we must create a new pixel_data; this will then replace the original layer
// for in/out alpha channels, there is no matching layer. These channels are passed around like data
// using mainw->cconx as a guide. We will simply free any in alpha channels after processing (unless inplace was used)
// WARNING: output layer may need resizing, and its palette may need adjusting - should be checked by the caller
// opwidth and opheight limit the maximum frame size, they can either be set to 0,0 or to max display size;
// however if all sources are smaller than this
// then the output will be smaller also and need resizing by the caller
// TODO ** - handle return errors
int *in_tracks,*out_tracks;
int *rowstrides;
int *layer_rows=NULL,*channel_rows;
int *mand;
void **pixel_data;
void *pdata;
weed_process_f *process_func_ptr_ptr;
weed_process_f process_func;
weed_plant_t *def_channel=NULL;
int num_in_tracks,num_out_tracks;
int error;
weed_plant_t *filter=weed_instance_get_filter(inst,FALSE);
weed_plant_t *layer=NULL,*orig_layer=NULL;
weed_plant_t **in_channels,**out_channels=NULL,*channel,*chantmpl;
weed_plant_t **in_ctmpls;
int frame;
int inwidth,inheight,inpalette,outpalette,channel_flags,filter_flags=0;
int palette,cpalette;
int outwidth,outheight;
gboolean needs_reinit=FALSE,inplace=FALSE;
int incwidth,incheight,numplanes=0,width,height;
gboolean rowstrides_changed;
gboolean ignore_palette;
int nchr;
lives_filter_error_t retval=FILTER_NO_ERROR;
int maxinwidth=4,maxinheight=4;
int iclamping,isampling,isubspace;
int clip;
int num_ctmpl,num_inc,num_outc;
int osubspace=-1;
int osampling=-1;
int oclamping=-1;
int flags;
int num_in_alpha=0,num_out_alpha=0;
gboolean def_disabled=FALSE;
gboolean all_outs_alpha=TRUE,all_ins_alpha=FALSE;
gint lcount=0;
register int i,j,k;
if ((!weed_plant_has_leaf(inst,"out_channels")||(out_channels=weed_get_plantptr_array(inst,"out_channels",&error))==NULL)
&&(mainw->preview||mainw->is_rendering)) return retval;
if (weed_plant_has_leaf(filter,"flags")) filter_flags=weed_get_int_value(filter,"flags",&error);
// here, in_tracks and out_tracks map our layers to in_channels and out_channels in the filter
if (!weed_plant_has_leaf(inst,"in_channels")||(in_channels=weed_get_plantptr_array(inst,"in_channels",&error))==NULL) {
if (out_channels==NULL&&weed_plant_has_leaf(inst,"out_parameters")) {
// data processing effect; just call the process_func
weed_set_double_value(inst,"fps",cfile->pb_fps);
// see if we can multithread
if ((prefs->nfx_threads=future_prefs->nfx_threads)>1 &&
filter_flags&WEED_FILTER_HINT_MAY_THREAD) retval=process_func_threaded(inst,out_channels,tc);
else {
// normal single threaded version
int ret;
weed_leaf_get(filter,"process_func",0,(void *)&process_func_ptr_ptr);
process_func=process_func_ptr_ptr[0];
pthread_mutex_lock(&mainw->data_mutex);
ret=(*process_func)(inst,tc);
pthread_mutex_unlock(&mainw->data_mutex);
if (ret==WEED_ERROR_PLUGIN_INVALID) retval=FILTER_ERROR_MUST_RELOAD;
}
return retval;
}
return FILTER_ERROR_NO_IN_CHANNELS;
}
if (get_enabled_channel(inst,0,TRUE)==NULL) {
// we process generators elsewhere
weed_free(in_channels);
weed_free(out_channels);
return FILTER_ERROR_NO_IN_CHANNELS;
}
if (is_pure_audio(filter,TRUE)) {
weed_free(in_channels);
weed_free(out_channels);
return FILTER_ERROR_IS_AUDIO; // we process audio effects elsewhere
}
if (init_event==NULL) {
num_in_tracks=enabled_in_channels(inst,FALSE);
in_tracks=(int *)weed_malloc(2*sizint);
in_tracks[0]=0;
in_tracks[1]=1;
num_out_tracks=enabled_out_channels(inst,FALSE);
out_tracks=(int *)weed_malloc(sizint);
out_tracks[0]=0;
}
else {
num_in_tracks=weed_leaf_num_elements(init_event,"in_tracks");
in_tracks=weed_get_int_array(init_event,"in_tracks",&error);
num_out_tracks=weed_leaf_num_elements(init_event,"out_tracks");
out_tracks=weed_get_int_array(init_event,"out_tracks",&error);
}
// handle case where in_tracks[i] > than num layers
// either we temporarily disable the channel, or we can't apply the filter
num_inc=weed_leaf_num_elements(inst,"in_channels");
for (i=0;i<num_inc;i++) {
if (weed_palette_is_alpha_palette(weed_get_int_value(in_channels[i],"current_palette",&error))&&
!(weed_plant_has_leaf(in_channels[i],"disabled") &&
weed_get_boolean_value(in_channels[i],"disabled",&error)==WEED_TRUE))
num_in_alpha++;
}
if (num_inc==num_in_alpha) all_ins_alpha=TRUE;
num_inc-=num_in_alpha;
if (num_in_tracks>num_inc) num_in_tracks=num_inc;
if (num_inc>num_in_tracks) {
for (i=num_in_tracks;i<num_inc+num_in_alpha;i++) {
if (!weed_palette_is_alpha_palette(weed_get_int_value(in_channels[i],"current_palette",&error))) {
if (!weed_plant_has_leaf(in_channels[i],"disabled")||
weed_get_boolean_value(in_channels[i],"disabled",&error)==WEED_FALSE)
weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_TRUE);
else weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_FALSE);
}
}
}
while (layers[lcount++]!=NULL);
for (k=i=0;i<num_in_tracks;i++) {
if (in_tracks[i]<0) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
return FILTER_ERROR_INVALID_TRACK; // probably audio
}
while (weed_palette_is_alpha_palette(weed_get_int_value(in_channels[k],"current_palette",&error))) k++;
channel=in_channels[k];
weed_set_boolean_value(channel,"host_temp_disabled",WEED_FALSE);
if (in_tracks[i]>=lcount) {
for (j=k;j<num_in_tracks+num_in_alpha;j++) {
if (weed_palette_is_alpha_palette(weed_get_int_value(in_channels[j],"current_palette",&error))) continue;
channel=in_channels[j];
chantmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(chantmpl,"max_repeats")||(weed_plant_has_leaf(chantmpl,"option")&&
weed_get_boolean_value(chantmpl,"optional",&error)==WEED_TRUE))
weed_set_boolean_value(channel,"host_temp_disabled",WEED_TRUE);
else {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
return FILTER_ERROR_MISSING_LAYER;
}
}
break;
}
layer=layers[in_tracks[i]];
if (weed_get_voidptr_value(layer,"pixel_data",&error)==NULL) {
frame=weed_get_int_value(layer,"frame",&error);
if (frame==0) {
// temp disable channels if we can
channel=in_channels[k];
chantmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(chantmpl,"max_repeats")||(weed_plant_has_leaf(chantmpl,"option")&&
weed_get_boolean_value(chantmpl,"optional",&error)==WEED_TRUE))
weed_set_boolean_value(channel,"host_temp_disabled",WEED_TRUE);
else {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
return FILTER_ERROR_BLANK_FRAME;
}
}
}
k++;
}
// ensure all chantmpls not marked "optional" have at least one corresponding enabled channel
// e.g. we could have disabled all channels from a template with "max_repeats" that is not "optional"
num_ctmpl=weed_leaf_num_elements(filter,"in_channel_templates");
mand=(int *)g_malloc(num_ctmpl*sizint);
for (j=0;j<num_ctmpl;j++) mand[j]=0;
in_ctmpls=weed_get_plantptr_array(filter,"in_channel_templates",&error);
for (i=0;i<num_inc+num_in_alpha;i++) {
if ((weed_plant_has_leaf(in_channels[i],"disabled")&&
weed_get_boolean_value(in_channels[i],"disabled",&error)==WEED_TRUE)||
(weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE)) continue;
chantmpl=weed_get_plantptr_value(in_channels[i],"template",&error);
for (j=0;j<num_ctmpl;j++) {
if (chantmpl==in_ctmpls[j]) {
mand[j]=1;
break;
}
}
}
for (j=0;j<num_ctmpl;j++) {
if (mand[j]==0&&(!weed_plant_has_leaf(in_ctmpls[j],"optional")||
weed_get_boolean_value(in_ctmpls[j],"optional",&error)==WEED_FALSE)) {
weed_free(in_ctmpls);
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
g_free(mand);
return FILTER_ERROR_MISSING_LAYER;
}
}
weed_free(in_ctmpls);
g_free(mand);
num_outc=weed_leaf_num_elements(inst,"out_channels");
for (i=0;i<num_outc;i++) {
if (weed_palette_is_alpha_palette(weed_get_int_value(out_channels[i],"current_palette",&error))&&
!(weed_plant_has_leaf(out_channels[i],"disabled") &&
weed_get_boolean_value(out_channels[i],"disabled",&error)==WEED_TRUE))
num_out_alpha++;
}
if (init_event==NULL) num_out_tracks-=num_out_alpha;
// pull frames for tracks
for (i=0;i<num_out_tracks+num_out_alpha;i++) {
channel=out_channels[i];
palette=weed_get_int_value(channel,"current_palette",&error);
if (weed_palette_is_alpha_palette(palette)) continue;
if (weed_plant_has_leaf(channel,"host_temp_disabled")&&
weed_get_boolean_value(channel,"host_temp_disabled",&error)==WEED_TRUE) continue;
all_outs_alpha=FALSE;
}
for (j=i=0;i<num_in_tracks;i++) {
while (weed_palette_is_alpha_palette(weed_get_int_value(in_channels[j],"current_palette",&error))) j++;
if (weed_plant_has_leaf(in_channels[j],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[j],"host_temp_disabled",&error)==WEED_TRUE) continue;
layer=layers[in_tracks[i]];
clip=weed_get_int_value(layer,"clip",&error);
if (!weed_plant_has_leaf(layer,"pixel_data")||weed_get_voidptr_value(layer,"pixel_data",&error)==NULL) {
// pull_frame will set pixel_data,width,height,current_palette and rowstrides
if (!pull_frame(layer,mainw->files[clip]->img_type==IMG_TYPE_JPEG?"jpg":"png",tc)) return FILTER_ERROR_MISSING_FRAME;
}
// we only apply transitions and compositors to the scrap file
if (clip==mainw->scrap_file&&num_in_tracks==1&&num_out_tracks==1) return FILTER_ERROR_IS_SCRAP_FILE;
// use comparative widths - in RGB(A) pixels
palette=weed_get_int_value(layer,"current_palette",&error);
if ((inwidth=(weed_get_int_value(layer,"width",&error)*weed_palette_get_pixels_per_macropixel(palette)))>maxinwidth)
maxinwidth=inwidth;
if ((inheight=weed_get_int_value(layer,"height",&error))>maxinheight) maxinheight=inheight;
j++;
}
// pixels
if (maxinwidth<opwidth||opwidth==0) opwidth=maxinwidth;
if (maxinheight<opheight||opheight==0) opheight=maxinheight;
// first we resize if necessary; then we change the palette
for (k=i=0;k<num_inc+num_in_alpha;k++) {
channel=get_enabled_channel(inst,k,TRUE);
if (channel==NULL) break;
if (weed_plant_has_leaf(channel,"host_temp_disabled")&&
weed_get_boolean_value(channel,"host_temp_disabled",&error)==WEED_TRUE) continue;
chantmpl=weed_get_plantptr_value(channel,"template",&error);
if (def_channel==NULL) def_channel=channel;
if (weed_palette_is_alpha_palette(weed_get_int_value(channel,"current_palette",&error))) {
if (def_channel==channel) continue;
palette=weed_get_int_value(channel,"current_palette",&error);
}
else {
layer=layers[in_tracks[i]];
palette=weed_get_int_value(layer,"current_palette",&error);
}
// values in pixels
width=opwidth;
height=opheight;
channel_flags=0;
if (weed_plant_has_leaf(chantmpl,"flags")) channel_flags=weed_get_int_value(chantmpl,"flags",&error);
// (channel macropixels)
incwidth=weed_get_int_value(channel,"width",&error);
incheight=weed_get_int_value(channel,"height",&error);
if (weed_palette_is_alpha_palette(weed_get_int_value(channel,"current_palette",&error))) {
inwidth=weed_get_int_value(def_channel,"width",&error);
inheight=weed_get_int_value(def_channel,"height",&error);
}
else {
// (layer macropixels)
inwidth=weed_get_int_value(layer,"width",&error);
inheight=weed_get_int_value(layer,"height",&error);
}
if (channel_flags&WEED_CHANNEL_SIZE_CAN_VARY) {
width=inwidth*weed_palette_get_pixels_per_macropixel(palette); // convert inwidth to pixels
height=inheight;
}
cpalette=weed_get_int_value(channel,"current_palette",&error);
width/=weed_palette_get_pixels_per_macropixel(cpalette); // convert width to (channel) macropixels
// try to set our target width height - the channel may have restrictions
set_channel_size(channel,width,height,0,NULL);
if (weed_palette_is_alpha_palette(weed_get_int_value(channel,"current_palette",&error))) continue;
width=weed_get_int_value(channel,"width",&error)*weed_palette_get_pixels_per_macropixel(cpalette)/
weed_palette_get_pixels_per_macropixel(palette);
height=weed_get_int_value(channel,"height",&error);
// restore channel to original size for now
set_channel_size(channel,incwidth,incheight,0,NULL);
// check if we need to resize
if ((inwidth!=width)||(inheight!=height)) {
// layer needs resizing
if (prefs->pb_quality==PB_QUALITY_HIGH||opwidth==0||opheight==0) {
if (!resize_layer(layer,width,height,GDK_INTERP_HYPER)) return FILTER_ERROR_UNABLE_TO_RESIZE;
}
else {
if (!resize_layer(layer,width,height,get_interp_value(prefs->pb_quality))) return FILTER_ERROR_UNABLE_TO_RESIZE;
}
inwidth=weed_get_int_value(layer,"width",&error);
inheight=weed_get_int_value(layer,"height",&error);
if ((inwidth!=width)||(inheight!=height)) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
return FILTER_ERROR_UNABLE_TO_RESIZE;
}
}
i++;
// check palette again in case it changed during resize
palette=weed_get_int_value(layer,"current_palette",&error);
inpalette=weed_get_int_value(channel,"current_palette",&error);
// try to match palettes with first enabled in channel:
// TODO ** - we should see which palette causes the least palette conversions
if (i>0&&!(channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY))
palette=weed_get_int_value(def_channel,"current_palette",&error);
if (palette!=inpalette) {
// palette change needed; first try to change channel palette
int num_palettes=weed_leaf_num_elements(chantmpl,"palette_list");
int *palettes=weed_get_int_array(chantmpl,"palette_list",&error);
if ((palette=check_weed_palette_list(palettes,num_palettes,palette))!=inpalette) {
weed_set_int_value(channel,"current_palette",palette);
if (channel_flags&WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE) needs_reinit=TRUE;
weed_set_int_value(channel,"width",incwidth/
weed_palette_get_pixels_per_macropixel(palette)*
weed_palette_get_pixels_per_macropixel(inpalette));
nchr=weed_leaf_num_elements(channel,"rowstrides");
channel_rows=weed_get_int_array(channel,"rowstrides",&error);
for (j=0;j<nchr;j++) {
if (weed_palette_get_plane_ratio_horizontal(inpalette,j)!=0.)
channel_rows[j]*=weed_palette_get_plane_ratio_horizontal(palette,j)/
weed_palette_get_plane_ratio_horizontal(inpalette,j);
}
weed_set_int_array(channel,"rowstrides",nchr,channel_rows);
weed_free(channel_rows);
}
weed_free(palettes);
if (weed_palette_is_yuv_palette(palette)) {
if (!(weed_plant_has_leaf(chantmpl,"YUV_subspace"))||
weed_get_int_value(chantmpl,"YUV_subspace",&error)==WEED_YUV_SUBSPACE_YUV) {
// set to default for LiVES
weed_set_int_value(channel,"YUV_subspace",WEED_YUV_SUBSPACE_YCBCR);
}
else {
weed_set_int_value(channel,"YUV_subspace",weed_get_int_value(chantmpl,"YUV_subspace",&error));
}
}
}
}
// we stored original key/mode to use here
if (weed_plant_has_leaf(inst,"host_key")) {
// pull from alpha chain
int key=weed_get_int_value(inst,"host_key",&error),mode;
if (weed_plant_has_leaf(inst,"host_mode")) {
mode=weed_get_int_value(inst,"host_mode",&error);
}
else mode=key_modes[key];
// need to do this AFTER setting in-channel size
if (mainw->cconx!=NULL) {
// chain any alpha channels
if (cconx_chain_data(key,mode)) needs_reinit=TRUE;
}
}
// make sure we have pixel_data for all mandatory in alpha channels (from alpha chains)
// if not, if the ctmpl is optnl mark as host_temp_disabled; else return with error
for (i=0;i<num_inc+num_in_alpha;i++) {
if (!weed_palette_is_alpha_palette(weed_get_int_value(in_channels[i],"current_palette",&error))) continue;
if (weed_plant_has_leaf(in_channels[i],"host_internal_connection")) {
if (cconx_chain_data_internal(in_channels[i])) needs_reinit=TRUE;
}
if (weed_get_voidptr_value(in_channels[i],"pixel_data",&error)==NULL) {
chantmpl=weed_get_plantptr_value(in_channels[i],"template",&error);
if (weed_plant_has_leaf(chantmpl,"max_repeats")||(weed_plant_has_leaf(chantmpl,"option")&&
weed_get_boolean_value(chantmpl,"optional",&error)==WEED_TRUE))
weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_TRUE);
else {
return FILTER_ERROR_MISSING_CHANNEL;
}
}
}
// now we do a second pass, and we change the palettes of in layers to match the channel, if necessary
for (j=i=0;i<num_in_tracks;i++) {
do {
channel=get_enabled_channel(inst,i,TRUE);
chantmpl=weed_get_plantptr_value(channel,"template",&error);
inpalette=weed_get_int_value(channel,"current_palette",&error);
channel_flags=0;
if (weed_plant_has_leaf(chantmpl,"flags")) channel_flags=weed_get_int_value(chantmpl,"flags",&error);
if (weed_palette_is_alpha_palette(inpalette)) {
if (!(channel_flags&WEED_CHANNEL_SIZE_CAN_VARY)) {
width=weed_get_int_value(channel,"width",&error);
height=weed_get_int_value(channel,"height",&error);
if (width != opwidth || height != opheight) {
if (!resize_layer(channel,opwidth,opheight,GDK_INTERP_HYPER)) return FILTER_ERROR_UNABLE_TO_RESIZE;
}
}
}
} while (weed_palette_is_alpha_palette(weed_get_int_value(channel,"current_palette",&error)));
if (weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE) continue;
layer=layers[in_tracks[i]];
if (weed_plant_has_leaf(layer,"YUV_clamping")) iclamping=(weed_get_int_value(layer,"YUV_clamping",&error));
else iclamping=WEED_YUV_CLAMPING_CLAMPED;
if (oclamping==-1||(channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY)) {
if (weed_plant_has_leaf(chantmpl,"YUV_clamping")) oclamping=(weed_get_int_value(chantmpl,"YUV_clamping",&error));
else oclamping=iclamping;
}
if (weed_plant_has_leaf(layer,"YUV_sampling")) isampling=(weed_get_int_value(layer,"YUV_sampling",&error));
else isampling=WEED_YUV_SAMPLING_DEFAULT;
if (osampling==-1||(channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY)) {
/* if (weed_plant_has_leaf(chantmpl,"YUV_sampling")) osampling=(weed_get_int_value(layer,"YUV_sampling",&error));
else */
// cant convert sampling yet
osampling=isampling;
}
if (weed_plant_has_leaf(layer,"YUV_subspace")) isubspace=(weed_get_int_value(layer,"YUV_subspace",&error));
else isubspace=WEED_YUV_SUBSPACE_YCBCR;
if (osubspace==-1||(channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY)) {
/*if (weed_plant_has_leaf(chantmpl,"YUV_subspace")) osubspace=(weed_get_int_value(chantmpl,"YUV_subspace",&error));
else */
// cant convert subspace yet
osubspace=isubspace;
}
cpalette=weed_get_int_value(layer,"current_palette",&error);
if (weed_palette_is_rgb_palette(cpalette)&&weed_palette_is_rgb_palette(inpalette)) {
oclamping=iclamping;
osubspace=isubspace;
osampling=isampling;
}
if (cpalette!=inpalette||isubspace!=osubspace) {
if (all_outs_alpha&&(weed_palette_is_lower_quality(inpalette,cpalette)||
(weed_palette_is_rgb_palette(inpalette)&&
!weed_palette_is_rgb_palette(cpalette))||
(weed_palette_is_rgb_palette(cpalette)&&
!weed_palette_is_rgb_palette(inpalette)))) {
// for an analyser (no out channels) we copy the layer if it needs lower quality
orig_layer=layer;
layer=weed_layer_copy(NULL,orig_layer);
}
if (!convert_layer_palette_full(layer,inpalette,
osampling,oclamping,osubspace)) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
if (orig_layer!=NULL) {
weed_layer_free(layer);
}
return FILTER_ERROR_INVALID_PALETTE_CONVERSION;
}
}
if (weed_palette_is_yuv_palette(inpalette)) {
if (weed_plant_has_leaf(layer,"YUV_clamping"))
oclamping=(weed_get_int_value(layer,"YUV_clamping",&error));
if (weed_plant_has_leaf(layer,"YUV_sampling"))
osampling=(weed_get_int_value(layer,"YUV_sampling",&error));
if (weed_plant_has_leaf(layer,"YUV_clamping"))
osubspace=(weed_get_int_value(layer,"YUV_subspace",&error));
weed_set_int_value(channel,"YUV_clamping",oclamping);
weed_set_int_value(channel,"YUV_sampling",osampling);
weed_set_int_value(channel,"YUV_subspace",osubspace);
}
else {
weed_leaf_delete(channel,"YUV_clamping");
weed_leaf_delete(channel,"YUV_sampling");
weed_leaf_delete(channel,"YUV_subspace");
}
incwidth=weed_get_int_value(channel,"width",&error);
incheight=weed_get_int_value(channel,"height",&error);
nchr=weed_leaf_num_elements(channel,"rowstrides");
channel_rows=weed_get_int_array(channel,"rowstrides",&error);
if (weed_plant_has_leaf(layer,"flags")) flags=weed_get_int_value(layer,"flags",&error);
else flags=0;
if (flags!=0) weed_set_int_value(channel,"flags",flags);
// after all resizing and palette conversions, we set the width, height and rowstrides with their final values
width=weed_get_int_value(layer,"width",&error);
height=weed_get_int_value(layer,"height",&error);
numplanes=weed_leaf_num_elements(layer,"rowstrides");
rowstrides=weed_get_int_array(layer,"rowstrides",&error);
set_channel_size(channel,width,height,numplanes,rowstrides);
// check layer rowstrides against previous settings
rowstrides_changed=rowstrides_differ(numplanes,rowstrides,nchr,channel_rows);
weed_free(channel_rows);
if (((rowstrides_changed&&(channel_flags&WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE))||
(((incwidth!=width)||(incheight!=height))&&(channel_flags&WEED_CHANNEL_REINIT_ON_SIZE_CHANGE))))
needs_reinit=TRUE;
weed_set_int64_value(channel,"timecode",tc);
pixel_data=weed_get_voidptr_array(layer,"pixel_data",&error);
// align memory if necessary
if (weed_plant_has_leaf(chantmpl,"alignment")) {
int alignment=weed_get_int_value(chantmpl,"alignment",&error);
gboolean contiguous=FALSE;
if (weed_plant_has_leaf(layer,"host_pixel_data_contiguous") &&
weed_get_boolean_value(layer,"host_pixel_data_contiguous",&error)==WEED_TRUE) contiguous=TRUE;
align(pixel_data,alignment,numplanes,height,rowstrides,&contiguous);
weed_set_voidptr_array(layer,"pixel_data",numplanes,pixel_data);
if (contiguous) weed_set_boolean_value(layer,"host_pixel_data_contiguous",WEED_TRUE);
else if (weed_plant_has_leaf(layer,"host_pixel_data_contiguous"))
weed_leaf_delete(layer,"host_pixel_data_contiguous");
}
weed_free(rowstrides);
weed_set_voidptr_array(channel,"pixel_data",numplanes,pixel_data);
weed_free(pixel_data);
if (weed_plant_has_leaf(layer,"host_pixel_data_contiguous"))
weed_set_boolean_value(channel,"host_pixel_data_contiguous",
weed_get_boolean_value(layer,"host_pixel_data_contiguous",&error));
else if (weed_plant_has_leaf(channel,"host_pixel_data_contiguous"))
weed_leaf_delete(channel,"host_pixel_data_contiguous");
}
// we may need to disable some channels for the plugin
for (i=0;i<num_in_tracks+num_in_alpha;i++) {
if (weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE)
weed_set_boolean_value(in_channels[i],"disabled",WEED_TRUE);
}
// set up our out channels
for (i=0;i<num_out_tracks+num_out_alpha;i++) {
channel=get_enabled_channel(inst,i,FALSE);
palette=weed_get_int_value(channel,"current_palette",&error);
if (!weed_palette_is_alpha_palette(palette)&&out_tracks[i]<0) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
if (orig_layer!=NULL) {
weed_layer_free(layer);
}
return FILTER_ERROR_INVALID_TRACK; // probably audio
}
outwidth=weed_get_int_value(channel,"width",&error);
outheight=weed_get_int_value(channel,"height",&error);
weed_set_int64_value(channel,"timecode",tc);
outpalette=weed_get_int_value(channel,"current_palette",&error);
chantmpl=weed_get_plantptr_value(channel,"template",&error);
channel_flags=0;
if (weed_plant_has_leaf(chantmpl,"flags")) channel_flags=weed_get_int_value(chantmpl,"flags",&error);
nchr=weed_leaf_num_elements(channel,"rowstrides");
channel_rows=weed_get_int_array(channel,"rowstrides",&error);
if (def_channel!=NULL&&i==0&&(weed_palette_is_alpha_palette
(weed_get_int_value(channel,"current_palette",&error)&&
weed_palette_is_alpha_palette
(weed_get_int_value(def_channel,"current_palette",&error)
))||
(in_tracks!=NULL&&out_tracks!=NULL&&in_tracks[0]==out_tracks[0]))) {
if (channel_flags&WEED_CHANNEL_CAN_DO_INPLACE) {
if (!(weed_palette_is_alpha_palette(weed_get_int_value(in_channels[i],"current_palette",&error) &&
weed_plant_has_leaf(channel,"host_orig_pdata") &&
weed_get_boolean_value(channel,"host_orig_pdata",&error)==WEED_TRUE))) {
// ah, good, inplace
int num_palettes=weed_leaf_num_elements(chantmpl,"palette_list");
int *palettes=weed_get_int_array(chantmpl,"palette_list",&error);
palette=weed_get_int_value(def_channel,"current_palette",&error);
if (check_weed_palette_list(palettes,num_palettes,palette)==palette) {
weed_set_int_value(channel,"current_palette",palette);
if (outpalette!=palette&&(channel_flags&WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE)) needs_reinit=TRUE;
width=weed_get_int_value(def_channel,"width",&error);
height=weed_get_int_value(def_channel,"height",&error);
weed_set_int_value(channel,"width",width);
weed_set_int_value(channel,"height",height);
weed_set_int_value(channel,"current_palette",palette);
if (weed_plant_has_leaf(def_channel,"YUV_clamping")) {
oclamping=(weed_get_int_value(def_channel,"YUV_clamping",&error));
weed_set_int_value(channel,"YUV_clamping",oclamping);
}
else weed_leaf_delete(channel,"YUV_clamping");
if (weed_plant_has_leaf(def_channel,"YUV_sampling"))
weed_set_int_value(channel,"YUV_sampling",weed_get_int_value(def_channel,"YUV_sampling",&error));
else weed_leaf_delete(channel,"YUV_sampling");
if (weed_plant_has_leaf(def_channel,"YUV_subspace"))
weed_set_int_value(channel,"YUV_subspace",weed_get_int_value(def_channel,"YUV_subspace",&error));
else weed_leaf_delete(channel,"YUV_subspace");
numplanes=weed_leaf_num_elements(def_channel,"rowstrides");
layer_rows=weed_get_int_array(def_channel,"rowstrides",&error);
weed_set_int_array(channel,"rowstrides",numplanes,layer_rows);
pixel_data=weed_get_voidptr_array(def_channel,"pixel_data",&error);
weed_set_voidptr_array(channel,"pixel_data",numplanes,pixel_data);
weed_free(pixel_data);
weed_set_boolean_value(channel,"inplace",WEED_TRUE);
inplace=TRUE;
if (weed_plant_has_leaf(def_channel,"host_pixel_data_contiguous"))
weed_set_boolean_value(channel,"host_pixel_data_contiguous",
weed_get_boolean_value(def_channel,"host_pixel_data_contiguous",&error));
else if (weed_plant_has_leaf(channel,"host_pixel_data_contiguous"))
weed_leaf_delete(channel,"host_pixel_data_contiguous");
if (weed_palette_is_alpha_palette(palette)) {
// protect our in- channel from being freed()
weed_set_boolean_value(channel,"host_orig_pdata",WEED_TRUE);
}
}
weed_free(palettes);
}
}
}
if (def_channel==NULL) def_channel=get_enabled_channel(inst,0,FALSE);
if (weed_get_boolean_value(def_channel,"host_temp_disabled",&error)==WEED_TRUE) def_disabled=TRUE;
ignore_palette=FALSE;
if (!inplace||def_disabled) {
if (!def_disabled) {
// try to match palettes with first enabled in channel
palette=weed_get_int_value(def_channel,"current_palette",&error);
if (palette!=outpalette) {
// palette change needed; try to change channel palette
int num_palettes=weed_leaf_num_elements(chantmpl,"palette_list");
int *palettes=weed_get_int_array(chantmpl,"palette_list",&error);
if (check_weed_palette_list(palettes,num_palettes,palette)==palette) {
weed_set_int_value(channel,"current_palette",palette);
if (channel_flags&WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE) needs_reinit=TRUE;
}
else {
if (channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY) ignore_palette=TRUE;
else {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
weed_free(channel_rows);
if (orig_layer!=NULL) {
weed_layer_free(layer);
}
return FILTER_ERROR_INVALID_PALETTE_SETTINGS; // plugin author messed up...
}
}
weed_free(palettes);
}
if (!ignore_palette) {
if (weed_plant_has_leaf(def_channel,"YUV_clamping")) {
oclamping=(weed_get_int_value(def_channel,"YUV_clamping",&error));
weed_set_int_value(channel,"YUV_clamping",oclamping);
}
else weed_leaf_delete(channel,"YUV_clamping");
if (weed_plant_has_leaf(def_channel,"YUV_sampling"))
weed_set_int_value(channel,"YUV_sampling",weed_get_int_value(def_channel,"YUV_sampling",&error));
else weed_leaf_delete(channel,"YUV_sampling");
if (weed_plant_has_leaf(def_channel,"YUV_subspace"))
weed_set_int_value(channel,"YUV_subspace",weed_get_int_value(def_channel,"YUV_subspace",&error));
else weed_leaf_delete(channel,"YUV_subspace");
}
}
palette=weed_get_int_value(channel,"current_palette",&error);
width=weed_get_int_value(def_channel,"width",&error);
height=weed_get_int_value(def_channel,"height",&error);
pdata=weed_get_voidptr_value(channel,"pixel_data",&error);
if (weed_palette_is_alpha_palette(palette)&&outpalette==palette&&outwidth==width&&outheight==height&&pdata!=NULL) {
weed_free(channel_rows);
continue;
}
set_channel_size(channel,opwidth/weed_palette_get_pixels_per_macropixel(palette),opheight,1,NULL);
// this will look at width, height, current_palette, and create an empty pixel_data and set rowstrides
// and update width and height if necessary
create_empty_pixel_data(channel,FALSE,TRUE);
numplanes=weed_leaf_num_elements(channel,"rowstrides");
layer_rows=weed_get_int_array(channel,"rowstrides",&error);
// align memory if necessary
if (weed_plant_has_leaf(chantmpl,"alignment")) {
int alignment=weed_get_int_value(chantmpl,"alignment",&error);
gboolean contiguous=FALSE;
if (weed_plant_has_leaf(channel,"host_pixel_data_contiguous") &&
weed_get_boolean_value(channel,"host_pixel_data_contiguous",&error)==WEED_TRUE) contiguous=TRUE;
pixel_data=weed_get_voidptr_array(channel,"pixel_data",&error);
height=weed_get_int_value(channel,"height",&error);
align(pixel_data,alignment,numplanes,height,layer_rows,&contiguous);
weed_set_voidptr_array(channel,"pixel_data",numplanes,pixel_data);
weed_free(pixel_data);
if (contiguous) weed_set_boolean_value(channel,"host_pixel_data_contiguous",WEED_TRUE);
else if (weed_plant_has_leaf(channel,"host_pixel_data_contiguous"))
weed_leaf_delete(channel,"host_pixel_data_contiguous");
}
weed_set_boolean_value(channel,"inplace",WEED_FALSE);
}
// check old rowstrides against current rowstrides
rowstrides_changed=rowstrides_differ(nchr,channel_rows,numplanes,layer_rows);
weed_free(channel_rows);
weed_free(layer_rows);
width=weed_get_int_value(channel,"width",&error);
height=weed_get_int_value(channel,"height",&error);
if ((rowstrides_changed&&(channel_flags&WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE))||
(((outwidth!=width)||(outheight!=height))&&(channel_flags&WEED_CHANNEL_REINIT_ON_SIZE_CHANGE)))
needs_reinit=TRUE;
}
if (needs_reinit) {
if ((retval=weed_reinit_effect(inst,FALSE))==FILTER_ERROR_COULD_NOT_REINIT) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
if (orig_layer!=NULL) {
weed_layer_free(layer);
}
return retval;
}
}
weed_set_double_value(inst,"fps",cfile->pb_fps);
//...finally we are ready to apply the filter
// see if we can multithread
if ((prefs->nfx_threads=future_prefs->nfx_threads)>1 &&
filter_flags&WEED_FILTER_HINT_MAY_THREAD) retval=process_func_threaded(inst,out_channels,tc);
else {
// normal single threaded version
int ret;
weed_leaf_get(filter,"process_func",0,(void *)&process_func_ptr_ptr);
process_func=process_func_ptr_ptr[0];
pthread_mutex_lock(&mainw->data_mutex);
ret=(*process_func)(inst,tc);
pthread_mutex_unlock(&mainw->data_mutex);
if (ret==WEED_ERROR_PLUGIN_INVALID) retval=FILTER_ERROR_MUST_RELOAD;
}
if (retval==FILTER_ERROR_MUST_RELOAD) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
if (orig_layer!=NULL) {
weed_layer_free(layer);
}
return retval;
}
for (k=0;k<num_inc+num_in_alpha;k++) {
channel=get_enabled_channel(inst,k,TRUE);
if (weed_palette_is_alpha_palette(weed_get_int_value(channel,"current_palette",&error))) {
// free pdata for all alpha in channels, unless orig pdata was passed from a prior fx
if (!weed_plant_has_leaf(channel,"host_orig_pdata")||
weed_get_boolean_value(channel,"host_orig_pdata",&error)!=WEED_TRUE) {
pdata=weed_get_voidptr_value(channel,"pixel_data",&error);
if (pdata!=NULL) g_free(pdata);
}
weed_set_voidptr_value(channel,"pixel_data",NULL);
if (weed_plant_has_leaf(channel,"host_orig_pdata"))
weed_leaf_delete(channel,"host_orig_pdata");
}
}
// now we write our out channels back to layers, leaving the palettes and sizes unchanged
for (i=k=0;k<num_out_tracks+num_out_alpha;k++) {
channel=get_enabled_channel(inst,k,FALSE);
if (weed_get_boolean_value(channel,"inplace",&error)==WEED_TRUE) continue;
if (weed_palette_is_alpha_palette(weed_get_int_value(channel,"current_palette",&error))) {
// out chan data for alpha is freed after all fx proc - in case we need for in chans
continue;
}
numplanes=weed_leaf_num_elements(channel,"rowstrides");
palette=weed_get_int_value(channel,"current_palette",&error);
layer=layers[out_tracks[i]];
rowstrides=weed_get_int_array(channel,"rowstrides",&error);
weed_set_int_array(layer,"rowstrides",numplanes,rowstrides);
weed_free(rowstrides);
numplanes=weed_leaf_num_elements(layer,"pixel_data");
pixel_data=weed_get_voidptr_array(layer,"pixel_data",&error);
if (pixel_data!=NULL) {
if (weed_plant_has_leaf(layer,"host_pixel_data_contiguous") &&
weed_get_boolean_value(layer,"host_pixel_data_contiguous",&error)==WEED_TRUE)
numplanes=1;
for (j=0;j<numplanes;j++) if (pixel_data[j]!=NULL) g_free(pixel_data[j]);
weed_free(pixel_data);
}
numplanes=weed_leaf_num_elements(channel,"pixel_data");
pixel_data=weed_get_voidptr_array(channel,"pixel_data",&error);
weed_set_voidptr_array(layer,"pixel_data",numplanes,pixel_data);
weed_free(pixel_data);
// set this in case it was a resize plugin
width=weed_get_int_value(channel,"width",&error);
height=weed_get_int_value(channel,"height",&error);
weed_set_int_value(layer,"width",width);
weed_set_int_value(layer,"height",height);
i++;
if (weed_plant_has_leaf(channel,"host_pixel_data_contiguous"))
weed_set_boolean_value(layer,"host_pixel_data_contiguous",
weed_get_boolean_value(channel,"host_pixel_data_contiguous",&error));
else if (weed_plant_has_leaf(layer,"host_pixel_data_contiguous"))
weed_leaf_delete(layer,"host_pixel_data_contiguous");
chantmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(chantmpl,"flags")) flags=weed_get_int_value(chantmpl,"flags",&error);
else flags=0;
if (weed_plant_has_leaf(channel,"YUV_clamping")) {
oclamping=(weed_get_int_value(channel,"YUV_clamping",&error));
weed_set_int_value(layer,"YUV_clamping",oclamping);
}
else weed_leaf_delete(layer,"YUV_clamping");
if (weed_plant_has_leaf(channel,"YUV_sampling"))
weed_set_int_value(layer,"YUV_sampling",weed_get_int_value(channel,"YUV_sampling",&error));
else weed_leaf_delete(layer,"YUV_sampling");
if (weed_plant_has_leaf(channel,"YUV_subspace"))
weed_set_int_value(layer,"YUV_subspace",weed_get_int_value(channel,"YUV_subspace",&error));
else weed_leaf_delete(layer,"YUV_subspace");
}
for (i=0;i<num_inc+num_in_alpha;i++) {
if (weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE) {
weed_set_boolean_value(in_channels[i],"disabled",WEED_FALSE);
weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_FALSE);
}
}
// done...
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
weed_free(out_channels);
if (orig_layer!=NULL) {
weed_layer_free(layer);
}
return retval;
}
static lives_filter_error_t weed_apply_audio_instance_inner (weed_plant_t *inst, weed_plant_t *init_event,
weed_plant_t **layers, weed_timecode_t tc, int nbtracks) {
int num_in_tracks,num_out_tracks;
int *in_tracks,*out_tracks;
int error,i,j;
weed_plant_t *filter=weed_instance_get_filter(inst,FALSE);
weed_plant_t *layer;
weed_plant_t **in_channels,**out_channels=NULL,*channel,*chantmpl;
gboolean inplace=FALSE;
weed_process_f *process_func_ptr_ptr;
weed_process_f process_func;
lives_filter_error_t retval=FILTER_NO_ERROR;
int *mand;
int channel_flags;
int num_ctmpl,num_inc;
weed_plant_t **in_ctmpls;
int nchans=0;
int nsamps=0;
void *adata=NULL,*adata0=NULL;
// TODO - handle the following:
// input audio_channels are mono, but the plugin NEEDS stereo
if (!weed_plant_has_leaf(inst,"in_channels")||(in_channels=weed_get_plantptr_array(inst,"in_channels",&error))==NULL)
return FILTER_ERROR_NO_IN_CHANNELS;
if (get_enabled_channel(inst,0,TRUE)==NULL) {
// we process generators elsewhere
return FILTER_ERROR_NO_IN_CHANNELS;
}
if (init_event==NULL) {
num_in_tracks=enabled_in_channels(inst,FALSE);
in_tracks=(int *)weed_malloc(2*sizint);
in_tracks[0]=0;
in_tracks[1]=1;
num_out_tracks=1;
out_tracks=(int *)weed_malloc(sizint);
out_tracks[0]=0;
}
else {
num_in_tracks=weed_leaf_num_elements(init_event,"in_tracks");
in_tracks=weed_get_int_array(init_event,"in_tracks",&error);
num_out_tracks=weed_leaf_num_elements(init_event,"out_tracks");
out_tracks=weed_get_int_array(init_event,"out_tracks",&error);
}
if (!weed_plant_has_leaf(inst,"out_channels")||(out_channels=weed_get_plantptr_array(inst,"out_channels",&error))==NULL) {
num_out_tracks=0;
}
// handle case where in_tracks[i] > than num layers
// either we temporarily disable the channel, or we can't apply the filter
num_inc=weed_leaf_num_elements(inst,"in_channels");
if (num_in_tracks>num_inc) num_in_tracks=num_inc;
if (num_inc>num_in_tracks) {
for (i=num_in_tracks;i<num_inc;i++) {
if (!weed_plant_has_leaf(in_channels[i],"disabled")||
weed_get_boolean_value(in_channels[i],"disabled",&error)==WEED_FALSE)
weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_TRUE);
else weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_FALSE);
}
}
for (i=0;i<num_in_tracks;i++) {
channel=in_channels[i];
weed_set_boolean_value(channel,"host_temp_disabled",WEED_FALSE);
layer=layers[i];
if (layer==NULL) {
for (j=i;j<num_in_tracks;j++) {
channel=in_channels[j];
chantmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(chantmpl,"max_repeats")) weed_set_boolean_value(channel,"host_temp_disabled",WEED_TRUE);
else {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
return FILTER_ERROR_MISSING_LAYER;
}
}
break;
}
if (weed_get_voidptr_value(layer,"audio_data",&error)==NULL) {
chantmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(chantmpl,"max_repeats")) weed_set_boolean_value(channel,"host_temp_disabled",WEED_TRUE);
}
}
// ensure all chantmpls not marked "optional" have at least one corresponding enabled channel
// e.g. we could have disabled all channels from a template with "max_repeats" that is not "optional"
num_ctmpl=weed_leaf_num_elements(filter,"in_channel_templates");
mand=(int *)g_malloc(num_ctmpl*sizint);
for (j=0;j<num_ctmpl;j++) mand[j]=0;
in_ctmpls=weed_get_plantptr_array(filter,"in_channel_templates",&error);
for (i=0;i<num_inc;i++) {
if ((weed_plant_has_leaf(in_channels[i],"disabled")&&
weed_get_boolean_value(in_channels[i],"disabled",&error)==WEED_TRUE)||
(weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE)) continue;
chantmpl=weed_get_plantptr_value(in_channels[i],"template",&error);
for (j=0;j<num_ctmpl;j++) {
if (chantmpl==in_ctmpls[j]) {
mand[j]=1;
break;
}
}
}
for (j=0;j<num_ctmpl;j++) if (mand[j]==0&&(!weed_plant_has_leaf(in_ctmpls[j],"optional")||
weed_get_boolean_value(in_ctmpls[j],"optional",&error)==WEED_FALSE)) {
weed_free(in_ctmpls);
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
g_free(mand);
return FILTER_ERROR_MISSING_LAYER;
}
weed_free(in_ctmpls);
g_free(mand);
for (i=0;i<num_in_tracks;i++) {
if (weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE) continue;
layer=layers[i];
channel=get_enabled_channel(inst,i,TRUE);
chantmpl=weed_get_plantptr_value(channel,"template",&error);
weed_set_int64_value(channel,"timecode",tc);
adata=weed_get_voidptr_value(layer,"audio_data",&error);
if (i==0) adata0=adata;
// nchans and nsamps needed for inplace
nchans=weed_get_int_value(channel,"audio_channels",&error);
nsamps=weed_get_int_value(channel,"audio_data_length",&error);
if (weed_get_boolean_value(channel,"audio_interleaf",&error)==WEED_TRUE) {
// handle case where plugin NEEDS interleaved
weed_set_boolean_value(layers[i],"audio_interleaf",WEED_TRUE);
if (!float_interleave(adata,nsamps,nchans)) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
return FILTER_ERROR_MEMORY_ERROR;
}
}
weed_set_voidptr_value(channel,"audio_data",adata);
}
// we may need to disable some channels for the plugin
for (i=0;i<num_in_tracks;i++) {
if (weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE)
weed_set_boolean_value(in_channels[i],"disabled",WEED_TRUE);
}
// set up our out channels
for (i=0;i<num_out_tracks;i++) {
if (out_tracks[i]!=in_tracks[i]) {
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
return FILTER_ERROR_INVALID_TRACK; // can't yet mix audio and video
}
channel=get_enabled_channel(inst,i,FALSE);
weed_set_int64_value(channel,"timecode",tc);
chantmpl=weed_get_plantptr_value(channel,"template",&error);
channel_flags=weed_get_int_value(chantmpl,"flags",&error);
if (i==0&&(in_tracks[0]==out_tracks[0])&&adata0!=NULL) {
if (channel_flags&WEED_CHANNEL_CAN_DO_INPLACE) {
// ah, good, inplace
inplace=TRUE;
}
}
if (!inplace) {
float *abuf=(float *)g_malloc0(nchans*nsamps*sizeof(float));
weed_set_int_value(channel,"audio_data_length",nsamps);
weed_set_voidptr_value(channel,"audio_data",abuf);
inplace=FALSE;
}
else {
weed_set_int_value(channel,"audio_data_length",nsamps);
weed_set_voidptr_value(channel,"audio_data",adata0);
weed_set_boolean_value(layers[i],"audio_interleaf",weed_get_boolean_value(channel,"audio_interleaf",&error));
}
}
weed_set_double_value(inst,"fps",cfile->pb_fps);
//...finally we are ready to apply the filter
pthread_mutex_lock(&mainw->interp_mutex); // stop video thread from possibly interpolating our audio effects
weed_leaf_get(filter,"process_func",0,(void *)&process_func_ptr_ptr);
process_func=process_func_ptr_ptr[0];
pthread_mutex_lock(&mainw->data_mutex);
if ((*process_func)(inst,tc)==WEED_ERROR_PLUGIN_INVALID) {
pthread_mutex_unlock(&mainw->data_mutex);
pthread_mutex_unlock(&mainw->interp_mutex);
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
return FILTER_ERROR_MUST_RELOAD;
}
pthread_mutex_unlock(&mainw->interp_mutex);
pthread_mutex_unlock(&mainw->data_mutex);
// TODO - handle process errors (WEED_ERROR_PLUGIN_INVALID)
// now we write our out channels back to layers
for (i=0;i<num_out_tracks;i++) {
if (i==0&&inplace) continue;
// TODO - check this...
channel=get_enabled_channel(inst,i,FALSE);
layer=layers[i];
if (weed_plant_has_leaf(channel,"audio_data")) {
float *audio_data=(float *)weed_get_voidptr_value(layer,"audio_data",&error);
if (audio_data!=NULL) weed_free(audio_data);
}
weed_set_voidptr_value(layer,"audio_data",weed_get_voidptr_value(channel,"audio_data",&error));
}
for (i=0;i<num_inc;i++) {
if (weed_plant_has_leaf(in_channels[i],"host_temp_disabled")&&
weed_get_boolean_value(in_channels[i],"host_temp_disabled",&error)==WEED_TRUE) {
weed_set_boolean_value(in_channels[i],"disabled",WEED_FALSE);
weed_set_boolean_value(in_channels[i],"host_temp_disabled",WEED_FALSE);
}
}
// done...
weed_free(in_tracks);
weed_free(out_tracks);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
return retval;
}
lives_filter_error_t weed_apply_audio_instance (weed_plant_t *init_event, float **abuf, int nbtracks, int nchans,
int64_t nsamps, gdouble arate, weed_timecode_t tc, double *vis) {
float *in_abuf,*out_abuf;
int error;
weed_plant_t **layers=NULL,**in_channels=NULL,**out_channels=NULL;
int *in_tracks=NULL,*out_tracks=NULL;
weed_plant_t *instance=NULL,*filter;
size_t nsf=nsamps*sizeof(float);
lives_filter_error_t retval=FILTER_NO_ERROR,retval2;
weed_plant_t *channel=NULL;
boolean needs_reinit=FALSE;
weed_plant_t *ctmpl;
boolean was_init_event=FALSE;
int flags=0;
int key=-1;
int ntracks=1;
int numinchans=0,numoutchans=0,xnchans,aint;
register int i,j;
// caller passes an init_event from event list, or an instance (for realtime mode)
if (WEED_PLANT_IS_FILTER_INSTANCE(init_event)) {
// for realtime, we pass a single instance instead of init_event
instance=init_event;
// set init_event to NULL before passing it to the inner function
init_event=NULL;
in_tracks=weed_malloc(sizint);
in_tracks[0]=0;
out_tracks=weed_malloc(sizint);
out_tracks[0]=0;
}
else {
// when processing an event list, we pass an init_event
was_init_event=TRUE;
if (weed_plant_has_leaf(init_event,"host_tag")) {
gchar *keystr=weed_get_string_value(init_event,"host_tag",&error);
key=atoi(keystr);
weed_free(keystr);
}
else return FILTER_ERROR_INVALID_INIT_EVENT;
ntracks=weed_leaf_num_elements(init_event,"in_tracks");
in_tracks=weed_get_int_array(init_event,"in_tracks",&error);
if (weed_plant_has_leaf(init_event,"out_tracks")) out_tracks=weed_get_int_array(init_event,"out_tracks",&error);
// check instance exists, and interpolate parameters
if (rte_key_valid (key+1,FALSE)) {
if ((instance=key_to_instance[key][key_modes[key]])==NULL) {
weed_free(in_tracks);
weed_free(out_tracks);
return FILTER_ERROR_INVALID_INSTANCE;
}
if (mainw->pchains!=NULL&&mainw->pchains[key]!=NULL) {
if (!pthread_mutex_trylock(&mainw->interp_mutex)) { // try to minimise thread locking
pthread_mutex_unlock(&mainw->interp_mutex);
if (!interpolate_params(instance,mainw->pchains[key],tc)) {
weed_free(in_tracks);
weed_free(out_tracks);
return FILTER_ERROR_INTERPOLATION_FAILED;
}
}
}
}
else {
weed_free(in_tracks);
weed_free(out_tracks);
return FILTER_ERROR_INVALID_INIT_EVENT;
}
}
audinst1:
if (weed_plant_has_leaf(instance,"in_channels")) channel=weed_get_plantptr_value(instance,"in_channels",&error);
if (channel==NULL) {
if (weed_plant_has_leaf(instance,"out_channels")) {
retval=FILTER_ERROR_NO_IN_CHANNELS; // audio generators are dealt with by the audio player
goto audret1;
}
}
else {
in_channels=weed_get_plantptr_array(instance,"in_channels",&error);
numinchans=weed_leaf_num_elements(instance,"in_channels");
}
if (weed_plant_has_leaf(instance,"out_channels")) {
out_channels=weed_get_plantptr_array(instance,"out_channels",&error);
if (out_channels[0]!=NULL) numoutchans=weed_leaf_num_elements(instance,"out_channels");
}
filter=weed_instance_get_filter(instance,FALSE);
if (!weed_plant_has_leaf(filter,"flags")) weed_set_int_value(filter,"flags",0);
else flags=weed_get_int_value(filter,"flags",&error);
if (vis!=NULL&&vis[0]<0.&&in_tracks[0]<=-nbtracks) {
// first layer comes from ascrap file; do not apply any effects except the audio mixer
if (numinchans==1&&numoutchans==1&&!(flags&WEED_FILTER_IS_CONVERTER)) {
retval=FILTER_ERROR_IS_SCRAP_FILE;
goto audret1;
}
}
for (i=0;i<numinchans;i++) {
if ((channel=in_channels[i])==NULL) continue;
aint=WEED_FALSE; // preferred value
ctmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(ctmpl,"audio_interleaf")) {
aint=weed_get_boolean_value(ctmpl,"audio_interleaf",&error);
if (weed_get_boolean_value(channel,"audio_interleaf",&error)!=aint) {
needs_reinit=TRUE;
}
}
xnchans=nchans; // preferred value
if (weed_plant_has_leaf(ctmpl,"audio_channels")) {
xnchans=weed_get_int_value(ctmpl,"audio_channels",&error);
if (weed_get_int_value(channel,"audio_channels",&error)!=xnchans) {
needs_reinit=TRUE;
}
}
if ((weed_plant_has_leaf(ctmpl,"audio_data_length")&&nsamps!=weed_get_int_value(ctmpl,"audio_data_length",&error))||
(weed_plant_has_leaf(ctmpl,"audio_arate")&&arate!=weed_get_int_value(ctmpl,"audio_rate",&error))) {
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
retval=FILTER_ERROR_TEMPLATE_MISMATCH;
goto audret1;
}
if (weed_get_int_value(channel,"audio_rate",&error)!=arate) {
needs_reinit=TRUE;
}
weed_set_int_value(channel,"audio_data_length",nsamps);
}
for (i=0;i<numoutchans;i++) {
if ((channel=out_channels[i])==NULL) continue;
aint=WEED_FALSE; // preferred value
ctmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(ctmpl,"audio_interleaf")) {
aint=weed_get_boolean_value(ctmpl,"audio_interleaf",&error);
if (weed_get_boolean_value(channel,"audio_interleaf",&error)!=aint) {
needs_reinit=TRUE;
}
}
xnchans=nchans; // preferred value
if (weed_plant_has_leaf(ctmpl,"audio_channels")) {
xnchans=weed_get_int_value(ctmpl,"audio_channels",&error);
if (weed_get_int_value(channel,"audio_channels",&error)!=xnchans) {
needs_reinit=TRUE;
}
}
if ((weed_plant_has_leaf(ctmpl,"audio_data_length")&&nsamps!=weed_get_int_value(ctmpl,"audio_data_length",&error))||
(weed_plant_has_leaf(ctmpl,"audio_arate")&&arate!=weed_get_int_value(ctmpl,"audio_rate",&error))) {
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
retval=FILTER_ERROR_TEMPLATE_MISMATCH;
goto audret1;
}
if (weed_get_int_value(channel,"audio_rate",&error)!=arate) {
needs_reinit=TRUE;
}
weed_set_int_value(channel,"audio_data_length",nsamps);
}
if (needs_reinit) {
// - deinit inst
weed_call_deinit_func(instance);
for (i=0;i<numinchans;i++) {
if ((channel=in_channels[i])!=NULL) {
aint=WEED_FALSE; // preferred value
ctmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(ctmpl,"audio_interleaf")) {
aint=weed_get_boolean_value(ctmpl,"audio_interleaf",&error);
}
xnchans=nchans; // preferred value
if (weed_plant_has_leaf(ctmpl,"audio_channels")) {
xnchans=weed_get_int_value(ctmpl,"audio_channels",&error);
}
weed_set_boolean_value(channel,"audio_interleaf",aint);
weed_set_int_value(channel,"audio_channels",xnchans);
weed_set_int_value(channel,"audio_rate",arate);
}
}
for (i=0;i<numoutchans;i++) {
if ((channel=out_channels[i])!=NULL) {
aint=WEED_FALSE; // preferred value
ctmpl=weed_get_plantptr_value(channel,"template",&error);
if (weed_plant_has_leaf(ctmpl,"audio_interleaf")) {
aint=weed_get_boolean_value(ctmpl,"audio_interleaf",&error);
}
xnchans=nchans; // preferred value
if (weed_plant_has_leaf(ctmpl,"audio_channels")) {
xnchans=weed_get_int_value(ctmpl,"audio_channels",&error);
}
weed_set_boolean_value(channel,"audio_interleaf",aint);
weed_set_int_value(channel,"audio_channels",xnchans);
weed_set_int_value(channel,"audio_rate",arate);
}
}
// - init inst
if (weed_plant_has_leaf(filter,"init_func")) {
weed_init_f *init_func_ptr_ptr;
weed_init_f init_func;
weed_leaf_get(filter,"init_func",0,(void *)&init_func_ptr_ptr);
init_func=init_func_ptr_ptr[0];
if (init_func!=NULL) {
gchar *cwd=cd_to_plugin_dir(filter);
set_param_gui_readwrite(instance);
update_host_info(instance);
if ((*init_func)(instance)!=WEED_NO_ERROR) {
key_to_instance[key][key_modes[key]]=NULL;
lives_chdir(cwd,FALSE);
g_free(cwd);
weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
retval=FILTER_ERROR_COULD_NOT_REINIT;
goto audret1;
}
set_param_gui_readonly(instance);
lives_chdir(cwd,FALSE);
g_free(cwd);
}
}
weed_set_boolean_value(instance,"host_inited",WEED_TRUE);
retval=FILTER_INFO_REINITED;
}
if (in_channels!=NULL) weed_free(in_channels);
if (out_channels!=NULL) weed_free(out_channels);
// apply visibility mask to volume values
if (vis!=NULL&&(flags&WEED_FILTER_IS_CONVERTER)) {
int vmaster=get_master_vol_param(filter,FALSE);
if (vmaster!=-1) {
weed_plant_t **in_params=weed_get_plantptr_array(instance,"in_parameters",&error);
int nvals=weed_leaf_num_elements(in_params[vmaster],"value");
double *fvols=weed_get_double_array(in_params[vmaster],"value",&error);
for (i=0;i<nvals;i++) {
fvols[i]=fvols[i]*vis[in_tracks[i]+nbtracks];
if (vis[in_tracks[i]+nbtracks]<0.) fvols[i]=-fvols[i];
}
pthread_mutex_lock(&mainw->data_mutex);
weed_set_double_array(in_params[vmaster],"value",nvals,fvols);
pthread_mutex_unlock(&mainw->data_mutex);
set_copy_to(instance,vmaster,TRUE);
weed_free(fvols);
weed_free(in_params);
}
}
if (layers==NULL&&numinchans>0&&numoutchans>0) {
layers=(weed_plant_t **)g_malloc((ntracks+1)*sizeof(weed_plant_t *));
for (i=0;i<ntracks;i++) {
// create audio layers, and copy/combine separated audio into each layer
layers[i]=weed_plant_new(WEED_PLANT_CHANNEL);
// copy audio into layer audio
in_abuf=g_malloc(nchans*nsf);
// non-interleaved
for (j=0;j<nchans;j++) {
lives_memcpy(&in_abuf[j*nsamps],abuf[(in_tracks[i]+nbtracks)*nchans+j],nsf);
}
weed_set_voidptr_value(layers[i],"audio_data",(void *)in_abuf);
weed_set_int_value(layers[i],"audio_data_length",nsamps);
weed_set_int_value(layers[i],"audio_channels",nchans);
weed_set_int_value(layers[i],"audio_rate",arate);
weed_set_boolean_value(layers[i],"audio_interleaf",WEED_FALSE);
}
layers[i]=NULL;
}
retval2=weed_apply_audio_instance_inner(instance,init_event,layers,tc,nbtracks);
if (retval==FILTER_NO_ERROR) retval=retval2;
if (retval2==FILTER_NO_ERROR&&was_init_event) {
// handle compound filters
if (weed_plant_has_leaf(instance,"host_next_instance")) {
instance=weed_get_plantptr_value(instance,"host_next_instance",&error);
goto audinst1;
}
}
out_abuf=(float *)weed_get_voidptr_value(layers[0],"audio_data",&error);
if (numoutchans>0) {
// copy processed audio
// inner function will set this if the plugin accepts/returns interleaved audio only
if (weed_get_boolean_value(layers[0],"audio_interleaf",&error)==WEED_TRUE) float_deinterleave(out_abuf,nsamps,nchans);
// non-interleaved
for (i=0;i<nchans;i++) {
lives_memcpy(abuf[(out_tracks[0]+nbtracks)*nchans+i],&out_abuf[i*nsamps],nsf);
}
}
audret1:
if (layers!=NULL) {
for (i=0;i<ntracks;i++) {
in_abuf=(float *)weed_get_voidptr_value(layers[i],"audio_data",&error);
g_free(in_abuf);
weed_plant_free(layers[i]);
}
g_free(layers);
}
weed_free(in_tracks);
weed_free(out_tracks);
return retval;
}
static void weed_apply_filter_map (weed_plant_t **layers, weed_plant_t *filter_map, weed_timecode_t tc, void ***pchains) {
weed_plant_t *instance;
weed_plant_t *init_event,*deinit_event;
gchar *keystr;
void **init_events;
lives_filter_error_t filter_error;
int key,num_inst,error;
register int i;
// this is called during rendering - we will have previously received a filter_map event and now we apply this to layers
// layers will be a NULL terminated array of channels, each with two extra leaves: clip and frame
// clip corresponds to a LiVES file in mainw->files. "pixel_data" will initially be NULL, we will pull this as necessary
// and the effect output is written back to the layers
// a frame number of 0 indicates a blank frame;
// if an effect gets one of these it will not process (except if the channel is optional,
// in which case the channel is disabled); the same is true for invalid track numbers -
// hence disabled channels which do not have disabled in the template are re-enabled when a frame is available
// after all processing, we generally display/output the first non-NULL layer.
// If all layers are NULL we generate a blank frame
// size and palettes of resulting layers may change during this
// the channel sizes are set by the filter: all channels are all set to the size of the largest input layer.
// (We attempt to do this, but some channels have fixed sizes).
if (filter_map==NULL||!weed_plant_has_leaf(filter_map,"init_events")||
(weed_get_voidptr_value(filter_map,"init_events",&error)==NULL)) return;
if ((num_inst=weed_leaf_num_elements(filter_map,"init_events"))>0) {
init_events=weed_get_voidptr_array(filter_map,"init_events",&error);
for (i=0;i<num_inst;i++) {
init_event=(weed_plant_t *)init_events[i];
if (mainw->playing_file==-1&&mainw->multitrack!=NULL&&mainw->multitrack->current_rfx!=NULL&&
mainw->multitrack->init_event!=NULL) {
if (mainw->multitrack->current_rfx->source_type==LIVES_RFX_SOURCE_WEED&&
mainw->multitrack->current_rfx->source!=NULL) {
deinit_event=(weed_plant_t *)weed_get_voidptr_value(mainw->multitrack->init_event,"deinit_event",&error);
if (tc>=get_event_timecode(mainw->multitrack->init_event)&&tc<=get_event_timecode(deinit_event)) {
// we are previewing an effect in multitrack - use current unapplied values
// and display only the currently selected filter (unless it is a "process_last" effect (like audio mixer))
// TODO - if current fx
void **pchain=mt_get_pchain();
instance=(weed_plant_t *)mainw->multitrack->current_rfx->source;
if (is_pure_audio(instance,FALSE)) continue; // audio effects are applied in the audio renderer
// interpolation can be switched of by setting mainw->no_interp
if (!mainw->no_interp&&pchain!=NULL) {
interpolate_params(instance,pchain,tc); // interpolate parameters for preview
}
apply_inst1:
filter_error=weed_apply_instance (instance,mainw->multitrack->init_event,layers,0,0,tc);
if (filter_error==WEED_NO_ERROR&&weed_plant_has_leaf(instance,"host_next_instance")) {
// handling for compound fx
instance=weed_get_plantptr_value(instance,"host_next_instance",&error);
// chain any internal data pipelines for compound fx
pthread_mutex_lock(&mainw->data_mutex);
pconx_chain_data_internal(instance);
pthread_mutex_unlock(&mainw->data_mutex);
goto apply_inst1;
}
if (!init_event_is_process_last(mainw->multitrack->init_event)) break;
continue;
}
}
}
// else we are previewing or rendering from an event_list
if (weed_plant_has_leaf(init_event,"host_tag")) {
keystr=weed_get_string_value(init_event,"host_tag",&error);
key=atoi(keystr);
weed_free(keystr);
if (rte_key_valid (key+1,FALSE)) {
if ((instance=key_to_instance[key][key_modes[key]])==NULL) continue;
if (is_pure_audio(instance,FALSE)) continue; // audio effects are applied in the audio renderer
if (pchains!=NULL&&pchains[key]!=NULL) {
interpolate_params(instance,pchains[key],tc); // interpolate parameters during playback
}
/*
// might be needed for multitrack ???
if (mainw->pconx!=NULL) {
int key=i;
int mode=key_modes[i];
if (weed_plant_has_leaf(instance,"host_mode")) {
key=weed_get_int_value(instance,"host_key",&error);
mode=weed_get_int_value(instance,"host_mode",&error);
}
// chain any data pipelines
pconx_chain_data(key,mode);
}*/
apply_inst2:
filter_error=weed_apply_instance (instance,init_event,layers,0,0,tc);
if (filter_error==WEED_NO_ERROR&&weed_plant_has_leaf(instance,"host_next_instance")) {
// handling for compound fx
instance=weed_get_plantptr_value(instance,"host_next_instance",&error);
// chain any internal data pipelines for compound fx
pthread_mutex_lock(&mainw->data_mutex);
pconx_chain_data_internal(instance);
pthread_mutex_unlock(&mainw->data_mutex);
goto apply_inst2;
}
//if (filter_error!=FILTER_NO_ERROR) g_printerr("Render error was %d\n",filter_error);
}
}
}
weed_free(init_events);
}
}
weed_plant_t *weed_apply_effects (weed_plant_t **layers, weed_plant_t *filter_map, weed_timecode_t tc,
int opwidth, int opheight, void ***pchains) {
// given a stack of layers, a filter map, a timecode and possibly paramater chains
// apply the effects in the filter map, and return a single layer as the result
// if all goes wrong we return a blank 4x4 RGB24 layer (TODO - return a NULL ?)
// returned layer can be of any width,height,palette
// caller should free all input layers, "pixel_data" of all non-returned layers is free()d here
// TODO - update the param window with any out_param values which changed.
int error;
weed_plant_t *instance,*layer;
lives_filter_error_t filter_error;
int output=-1;
int clip;
void *pdata;
register int i;
if (mainw->is_rendering&&!(cfile->proc_ptr!=NULL&&mainw->preview)) {
// rendering from multitrack
if (filter_map!=NULL&&layers[0]!=NULL) {
weed_apply_filter_map(layers, filter_map, tc, pchains);
}
}
// free playback: we will have here only one or two layers, and no filter_map.
// Effects are applied in key order, in tracks are 0 and 1, out track is 0
else {
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if (rte_key_valid(i+1,TRUE)) {
if (mainw->rte&(GU641<<i)) {
mainw->osc_block=TRUE;
if ((instance=key_to_instance[i][key_modes[i]])==NULL) continue;
if (mainw->pchains!=NULL&&mainw->pchains[i]!=NULL) {
interpolate_params(instance,mainw->pchains[i],tc); // interpolate parameters during preview
}
if (mainw->pconx!=NULL&&!(mainw->preview||mainw->is_rendering)) {
// chain any data pipelines
pthread_mutex_lock(&mainw->data_mutex);
pconx_chain_data(i,key_modes[i]);
pthread_mutex_unlock(&mainw->data_mutex);
}
apply_inst3:
weed_set_int_value(instance,"host_key",i);
filter_error=weed_apply_instance (instance,NULL,layers,opwidth,opheight,tc);
if (filter_error==FILTER_INFO_REINITED) redraw_pwindow(i,key_modes[i]); // redraw our paramwindow
#define DEBUG_RTE
#ifdef DEBUG_RTE
if (filter_error!=FILTER_NO_ERROR) g_printerr("Render error was %d\n",filter_error);
#endif
if (filter_error==WEED_NO_ERROR&&weed_plant_has_leaf(instance,"host_next_instance")) {
// handling for compound fx
instance=weed_get_plantptr_value(instance,"host_next_instance",&error);
// chain any internal data pipelines for compound fx
pthread_mutex_lock(&mainw->data_mutex);
pconx_chain_data_internal(instance);
pthread_mutex_unlock(&mainw->data_mutex);
goto apply_inst3;
}
}
}
}
}
// TODO - set mainw->vpp->play_params from connected out params and out alphas
if (!mainw->is_rendering) {
mainw->osc_block=FALSE;
}
// caller should free all layers, but here we will free all other pixel_data
for (i=0;layers[i]!=NULL;i++) {
if ((pdata=weed_get_voidptr_value(layers[i],"pixel_data",&error))!=NULL||
(weed_get_int_value(layers[i],"frame",&error)!=0&&
(mainw->playing_file>-1||mainw->multitrack==NULL||mainw->multitrack->current_rfx==NULL||
(mainw->multitrack->init_event==NULL||tc<get_event_timecode(mainw->multitrack->init_event)||
(mainw->multitrack->init_event==mainw->multitrack->avol_init_event)||
tc>get_event_timecode((weed_plant_t *)weed_get_voidptr_value
(mainw->multitrack->init_event,"deinit_event",&error)))))) {
if (output!=-1) {
void **pixel_data;
if (!weed_plant_has_leaf(layers[i],"pixel_data")) continue;
pixel_data=weed_get_voidptr_array(layers[i],"pixel_data",&error);
if (pixel_data!=NULL&&pixel_data[0]!=NULL) {
int j;
int numplanes=weed_leaf_num_elements(layers[i],"pixel_data");
if (weed_plant_has_leaf(layers[i],"host_pixel_data_contiguous") &&
weed_get_boolean_value(layers[i],"host_pixel_data_contiguous",&error)==WEED_TRUE)
numplanes=1;
for (j=0;j<numplanes;j++) if (pixel_data[j]!=NULL) g_free(pixel_data[j]);
weed_free(pixel_data);
}
}
else output=i;
}
}
if (output==-1) {
// blank frame - e.g. for multitrack
weed_plant_t *layer=weed_layer_new(opwidth>4?opwidth:4,opheight>4?opheight:4,NULL,WEED_PALETTE_RGB24);
create_empty_pixel_data(layer,TRUE,TRUE);
return layer;
}
layer=layers[output];
clip=weed_get_int_value(layer,"clip",&error);
// frame is pulled uneffected here. TODO: Try to pull at target output palette
if (!weed_plant_has_leaf(layer,"pixel_data")||weed_get_voidptr_value(layer,"pixel_data",&error)==NULL)
if (!pull_frame_at_size(layer,mainw->files[clip]->img_type==IMG_TYPE_JPEG?"jpg":"png",tc,opwidth,opheight,
WEED_PALETTE_END)) {
weed_set_int_value(layer,"current_palette",mainw->files[clip]->img_type==IMG_TYPE_JPEG?
WEED_PALETTE_RGB24:WEED_PALETTE_RGBA32);
weed_set_int_value(layer,"width",opwidth);
weed_set_int_value(layer,"height",opheight);
create_empty_pixel_data(layer,TRUE,TRUE);
LIVES_WARN("weed_apply_effects created empty pixel_data");
}
return layer;
}
void weed_apply_audio_effects (weed_plant_t *filter_map, float **abuf, int nbtracks, int nchans, int64_t nsamps,
gdouble arate, weed_timecode_t tc, double *vis) {
int i,num_inst,error;
void **init_events;
lives_filter_error_t filter_error;
weed_plant_t *init_event,*filter;
gchar *fhash;
// this is called during rendering - we will have previously received a
// filter_map event and now we apply this to audio (abuf)
// abuf will be a NULL terminated array of float audio
// the results of abuf[0] and abuf[1] (for stereo) will be written to fileno
if (filter_map==NULL||!weed_plant_has_leaf(filter_map,"init_events")||
(weed_get_voidptr_value(filter_map,"init_events",&error)==NULL)) {
return;
}
mainw->pchains=get_event_pchains();
if ((num_inst=weed_leaf_num_elements(filter_map,"init_events"))>0) {
init_events=weed_get_voidptr_array(filter_map,"init_events",&error);
for (i=0;i<num_inst;i++) {
init_event=(weed_plant_t *)init_events[i];
fhash=weed_get_string_value(init_event,"filter",&error);
filter=get_weed_filter(weed_get_idx_for_hashname(fhash,TRUE));
weed_free(fhash);
if (has_audio_chans_in(filter,FALSE)) {
filter_error=weed_apply_audio_instance(init_event,abuf,nbtracks,nchans,nsamps,arate,tc,vis);
}
}
weed_free(init_events);
}
mainw->pchains=NULL;
}
void weed_apply_audio_effects_rt(float **abuf, int nchans, int64_t nsamps, gdouble arate, weed_timecode_t tc, boolean analysers_only) {
weed_plant_t *instance,*filter;
lives_filter_error_t filter_error;
int error;
register int i;
// free playback: apply any audio filters or analysers (but not generators)
// Effects are applied in key order
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if (rte_key_valid(i+1,TRUE)) {
if (mainw->rte&(GU641<<i)) {
mainw->osc_block=TRUE;
// filter must not be deinited until we have processed it
if (pthread_mutex_trylock(&mainw->afilter_mutex)) continue;
if ((instance=key_to_instance[i][key_modes[i]])==NULL) {
pthread_mutex_unlock(&mainw->afilter_mutex);
continue;
}
filter=weed_instance_get_filter(instance,FALSE);
if (!has_audio_chans_in(filter,FALSE)) {
pthread_mutex_unlock(&mainw->afilter_mutex);
continue;
}
if (analysers_only&&has_audio_chans_out(filter,FALSE)) {
pthread_mutex_unlock(&mainw->afilter_mutex);
continue;
}
if (mainw->pchains!=NULL&&mainw->pchains[i]!=NULL) {
interpolate_params(instance,mainw->pchains[i],tc); // interpolate parameters during preview
}
apply_audio_inst2:
if (mainw->pconx!=NULL) {
// chain any data pipelines
if (!pthread_mutex_trylock(&mainw->data_mutex)) {
pconx_chain_data(i,key_modes[i]);
pthread_mutex_unlock(&mainw->data_mutex);
}
}
//weed_set_int_value(instance,"host_key",i);
filter_error=weed_apply_audio_instance(instance,abuf,0,nchans,nsamps,arate,tc,NULL);
if (filter_error==WEED_NO_ERROR&&weed_plant_has_leaf(instance,"host_next_instance")) {
// handling for compound fx
instance=weed_get_plantptr_value(instance,"host_next_instance",&error);
// chain any internal data pipelines for compound fx
if (!pthread_mutex_trylock(&mainw->data_mutex)) {
pconx_chain_data_internal(instance);
pthread_mutex_unlock(&mainw->data_mutex);
}
goto apply_audio_inst2;
}
pthread_mutex_unlock(&mainw->afilter_mutex);
if (filter_error==FILTER_INFO_REINITED) redraw_pwindow(i,key_modes[i]); // redraw our paramwindow
#ifdef DEBUG_RTE
if (filter_error!=FILTER_NO_ERROR) g_printerr("Render error was %d\n",filter_error);
#endif
}
}
}
}
boolean has_audio_filters(boolean analysers_only) {
// do we have any active audio filters (excluding audio generators) ?
weed_plant_t *instance,*filter;
register int i;
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if (rte_key_valid(i+1,TRUE)) {
if (mainw->rte&(GU641<<i)) {
if ((instance=key_to_instance[i][key_modes[i]])==NULL) continue;
filter=weed_instance_get_filter(instance,FALSE);
if (has_audio_chans_in(filter,FALSE)) {
if (analysers_only&&has_audio_chans_out(filter,FALSE)) continue;
return TRUE;
}
}
}
}
return FALSE;
}
boolean has_video_filters(boolean analysers_only) {
// do we have any active video filters (excluding generators) ?
weed_plant_t *instance,*filter;
register int i;
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
if (rte_key_valid(i+1,TRUE)) {
if (mainw->rte&(GU641<<i)) {
if ((instance=key_to_instance[i][key_modes[i]])==NULL) continue;
filter=weed_instance_get_filter(instance,FALSE);
if (has_video_chans_in(filter,FALSE)) {
if (analysers_only&&has_video_chans_out(filter,FALSE)) continue;
return TRUE;
}
}
}
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////
static int check_weed_plugin_info (weed_plant_t *plugin_info) {
// verify the plugin_info returned from the plugin
// TODO - print descriptive errors
if (!weed_plant_has_leaf(plugin_info,"host_info")) return -1;
if (!weed_plant_has_leaf(plugin_info,"version")) return -2;
if (!weed_plant_has_leaf(plugin_info,"filters")) return -3;
return weed_leaf_num_elements(plugin_info,"filters");
}
gint num_in_params(weed_plant_t *plant, boolean skip_hidden, boolean skip_internal) {
weed_plant_t **params=NULL;
weed_plant_t *param;
int counted=0;
int num_params,i,error;
boolean is_template=(WEED_PLANT_IS_FILTER_CLASS(plant));
nip1:
num_params=0;
if (is_template) {
if (!weed_plant_has_leaf(plant,"in_parameter_templates")||
weed_get_plantptr_value(plant,"in_parameter_templates",&error)==NULL) return 0;
num_params=weed_leaf_num_elements(plant,"in_parameter_templates");
}
else {
if (!weed_plant_has_leaf(plant,"in_parameters")) goto nip1done;
if (weed_get_plantptr_value(plant,"in_parameters",&error)==NULL) goto nip1done;
num_params=weed_leaf_num_elements(plant,"in_parameters");
}
if (!skip_hidden&&!skip_internal) {
counted+=num_params;
goto nip1done;
}
if (is_template) params=weed_get_plantptr_array(plant,"in_parameter_templates",&error);
else params=weed_get_plantptr_array(plant,"in_parameters",&error);
for (i=0;i<num_params;i++) {
if (skip_hidden&&is_hidden_param(plant,i)) continue;
param=params[i];
if (skip_internal&&weed_plant_has_leaf(param,"host_internal_connection")) continue;
counted++;
}
weed_free(params);
nip1done:
if (!is_template&&skip_internal&&weed_plant_has_leaf(plant,"host_next_instance")) {
plant=weed_get_plantptr_value(plant,"host_next_instance",&error);
goto nip1;
}
return counted;
}
gint num_out_params(weed_plant_t *plant) {
int num_params,error;
gboolean is_template=(WEED_PLANT_IS_FILTER_CLASS(plant));
if (is_template) {
if (!weed_plant_has_leaf(plant,"out_parameter_templates")||
weed_get_plantptr_value(plant,"out_parameter_templates",&error)==NULL) return 0;
num_params=weed_leaf_num_elements(plant,"out_parameter_templates");
}
else {
if (!weed_plant_has_leaf(plant,"out_parameters")) return 0;
if (weed_get_plantptr_value(plant,"out_parameters",&error)==NULL) return 0;
num_params=weed_leaf_num_elements(plant,"out_parameters");
}
return num_params;
}
gboolean has_usable_palette(weed_plant_t *chantmpl) {
int error;
int palette=weed_get_int_value(chantmpl,"current_palette",&error);
// currently only integer RGB palettes are usable
if (palette==5||palette==6) return FALSE;
if (palette>0&&palette<=7) return TRUE;
return FALSE;
}
gint enabled_in_channels (weed_plant_t *plant, gboolean count_repeats) {
weed_plant_t **channels=NULL;
gint enabled=0;
int num_channels,i,error;
gboolean is_template=WEED_PLANT_IS_FILTER_CLASS(plant);
if (is_template) {
if (!weed_plant_has_leaf(plant,"in_channel_templates")) return 0;
num_channels=weed_leaf_num_elements(plant,"in_channel_templates");
if (num_channels>0) channels=weed_get_plantptr_array(plant,"in_channel_templates",&error);
}
else {
if (!weed_plant_has_leaf(plant,"in_channels")) return 0;
num_channels=weed_leaf_num_elements(plant,"in_channels");
if (num_channels>0) channels=weed_get_plantptr_array(plant,"in_channels",&error);
}
for (i=0;i<num_channels;i++) {
if (!weed_plant_has_leaf(channels[i],"disabled")||
weed_get_boolean_value(channels[i],"disabled",&error)!=WEED_TRUE) enabled++;
if (count_repeats) {
// count repeated channels
weed_plant_t *chantmpl;
int repeats;
if (is_template) chantmpl=channels[i];
else chantmpl=weed_get_plantptr_value(channels[i],"template",&error);
if (weed_plant_has_leaf(channels[i],"max_repeats")) {
if (weed_plant_has_leaf(channels[i],"disabled")&&
weed_get_boolean_value(channels[i],"disabled",&error)==WEED_TRUE&&
!has_usable_palette(chantmpl)) continue; // channel was disabled because palette is unusable
repeats=weed_get_int_value(channels[i],"max_repeats",&error)-1;
if (repeats==-1) repeats=1000000;
enabled+=repeats;
}
}
}
if (channels!=NULL) weed_free(channels);
return enabled;
}
gint enabled_out_channels (weed_plant_t *plant, gboolean count_repeats) {
weed_plant_t **channels=NULL;
gint enabled=0;
int num_channels,i,error;
gboolean is_template=WEED_PLANT_IS_FILTER_CLASS(plant);
if (is_template) {
num_channels=weed_leaf_num_elements(plant,"out_channel_templates");
if (num_channels>0) channels=weed_get_plantptr_array(plant,"out_channel_templates",&error);
}
else {
num_channels=weed_leaf_num_elements(plant,"out_channels");
if (num_channels>0) channels=weed_get_plantptr_array(plant,"out_channels",&error);
}
for (i=0;i<num_channels;i++) {
if (!weed_plant_has_leaf(channels[i],"disabled")||
weed_get_boolean_value(channels[i],"disabled",&error)!=WEED_TRUE) enabled++;
if (count_repeats) {
// count repeated channels
weed_plant_t *chantmpl;
int repeats;
if (is_template) chantmpl=channels[i];
else chantmpl=weed_get_plantptr_value(channels[i],"template",&error);
if (weed_plant_has_leaf(channels[i],"max_repeats")) {
if (weed_plant_has_leaf(channels[i],"disabled")&&
weed_get_boolean_value(channels[i],"disabled",&error)==WEED_TRUE&&
!has_usable_palette(chantmpl)) continue; // channel was disabled because palette is unusable
repeats=weed_get_int_value(channels[i],"max_repeats",&error)-1;
if (repeats==-1) repeats=1000000;
enabled+=repeats;
}
}
}
if (channels!=NULL) weed_free(channels);
return enabled;
}
/////////////////////////////////////////////////////////////////////
static gint check_for_lives(weed_plant_t *filter, int filter_idx) {
// for LiVES, currently:
// all filters must take 0, 1 or 2 mandatory/optional inputs and provide
// 1 mandatory output (audio or video) or >1 optional (video only) outputs (for now)
// or 1 or more mandatory alpha outputs (analysers)
// filters can also have 1 mandatory input and no outputs and out parameters
// (video analyzer)
// they may have no outs provided they have out parameters (data sources or processors)
// all channels used must support a limited range of palettes (for now)
// filters can now have any number of mandatory in alphas, the effect will not be run unless the channels are filled
// (by chaining to output of another fx)
gint chans_in_mand=0; // number of mandatory channels
gint chans_in_opt_max=0; // number of usable (by LiVES) optional channels
gint chans_out_mand=0;
gint chans_out_opt_max=0;
gint achans_in_mand=0,achans_out_mand=0;
gboolean is_audio=FALSE;
gboolean has_out_params=FALSE;
gboolean all_out_alpha=TRUE;
int error,flags=0;
int num_elements,i;
int naudins=0,naudouts=0;
weed_plant_t **array=NULL;
// TODO - check seed types
if (!weed_plant_has_leaf(filter,"name")) return 1;
if (!weed_plant_has_leaf(filter,"author")) return 2;
if (!weed_plant_has_leaf(filter,"version")) return 3;
if (!weed_plant_has_leaf(filter,"process_func")) return 4;
if (!weed_plant_has_leaf(filter,"flags")) weed_set_int_value(filter,"flags",0);
else flags=weed_get_int_value(filter,"flags",&error);
// for now we will only load realtime effects
if (flags&WEED_FILTER_NON_REALTIME) return 5;
// count number of mandatory and optional in_channels
if (!weed_plant_has_leaf(filter,"in_channel_templates")) num_elements=0;
else num_elements=weed_leaf_num_elements(filter,"in_channel_templates");
if (num_elements>0) array=weed_get_plantptr_array(filter,"in_channel_templates",&error);
for (i=0;i<num_elements;i++) {
#ifndef HAVE_POSIX_MEMALIGN
if (weed_plant_has_leaf(array[i],"alignment")) {
weed_free(array);
return 12;
}
#endif
if (weed_plant_has_leaf(array[i],"is_audio")&&weed_get_boolean_value(array[i],"is_audio",&error)==WEED_TRUE) {
if (weed_plant_has_leaf(array[i],"audio_channels")&&(naudins=weed_get_int_value(array[i],"audio_channels",&error))>2) {
// currently we only handle mono and stereo audio filters
weed_free(array);
return 7;
}
is_audio=TRUE;
}
if (!weed_plant_has_leaf(array[i],"name")||(!weed_plant_has_leaf(array[i],"palette_list")&&!is_audio)) {
weed_free(array);
return 6;
}
if (!weed_plant_has_leaf(array[i],"flags")) weed_set_int_value(array[i],"flags",0);
if (weed_plant_has_leaf(array[i],"optional")&&weed_get_boolean_value(array[i],"optional",&error)==WEED_TRUE) {
// is optional
chans_in_opt_max++;
weed_set_boolean_value(array[i],"host_disabled",WEED_TRUE);
}
else {
if (has_non_alpha_palette(array[i])) {
if (!is_audio) chans_in_mand++;
else achans_in_mand++;
}
}
}
if (num_elements>0) weed_free(array);
if (chans_in_mand>2) return 8; // we dont handle mixers yet...
if (achans_in_mand>0&&chans_in_mand>0) return 13; // can't yet handle effects that need both audio and video
// count number of mandatory and optional out_channels
if (!weed_plant_has_leaf(filter,"out_channel_templates")) num_elements=0;
else num_elements=weed_leaf_num_elements(filter,"out_channel_templates");
if (num_elements>0) array=weed_get_plantptr_array(filter,"out_channel_templates",&error);
for (i=0;i<num_elements;i++) {
#ifndef HAVE_POSIX_MEMALIGN
if (weed_plant_has_leaf(array[i],"alignment")) {
weed_free(array);
return 12;
}
#endif
if (weed_plant_has_leaf(array[i],"is_audio")&&weed_get_boolean_value(array[i],"is_audio",&error)==WEED_TRUE) {
if (weed_plant_has_leaf(array[i],"audio_channels")&&(naudouts=weed_get_int_value(array[i],"audio_channels",&error))>2) {
// currently we only handle mono and stereo audio filters
weed_free(array);
return 7;
}
if (naudins==1&&naudouts==2) {
// converting mono to stereo cannot be done like that
weed_free(array);
return 7;
}
is_audio=TRUE;
}
if (!weed_plant_has_leaf(array[i],"name")||(!weed_plant_has_leaf(array[i],"palette_list")&&!is_audio)) {
weed_free(array);
return 9;
}
if (!weed_plant_has_leaf(array[i],"flags")) weed_set_int_value(array[i],"flags",0);
if (weed_plant_has_leaf(array[i],"optional")&&weed_get_boolean_value(array[i],"optional",&error)==WEED_TRUE) {
// is optional
chans_out_opt_max++;
weed_set_boolean_value(array[i],"host_disabled",WEED_TRUE);
}
else {
// is mandatory
if (!is_audio) {
chans_out_mand++;
if (has_non_alpha_palette(array[i])) all_out_alpha=FALSE;
}
else achans_out_mand++;
}
}
if (num_elements>0) weed_free(array);
if (weed_plant_has_leaf(filter,"out_parameter_templates")) has_out_params=TRUE;
if ((chans_out_mand>1&&!all_out_alpha)||((chans_out_mand+chans_out_opt_max+achans_out_mand<1)
&&(!has_out_params))) return 11;
if (achans_out_mand>1||(achans_out_mand==1&&chans_out_mand>0)) return 14;
if (achans_in_mand>=1&&achans_out_mand==0&&!(has_out_params)) return 15;
weed_add_plant_flags(filter,WEED_LEAF_READONLY_PLUGIN);
if (weed_plant_has_leaf(filter,"gui")) {
weed_plant_t *gui=weed_get_plantptr_value(filter,"gui",&error);
weed_add_plant_flags(gui,WEED_LEAF_READONLY_PLUGIN);
}
if (flags&WEED_FILTER_IS_CONVERTER) {
if (is_audio) {
weed_set_boolean_value(filter,"host_menu_hide",WEED_TRUE);
if (enabled_in_channels(filter,TRUE)>=1000000) {
// this is a candidate for audio volume
lives_fx_candidate_t *cand=&mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL];
cand->list=g_list_append(cand->list,GINT_TO_POINTER(filter_idx));
cand->delegate=0;
}
}
else {
weed_set_boolean_value(filter,"host_menu_hide",WEED_TRUE);
if (chans_in_mand==1&&chans_out_mand==1) {
weed_plant_t *fstout=get_mandatory_channel(filter,0,FALSE);
int ochan_flags=weed_get_int_value(fstout,"flags",&error);
if (ochan_flags&WEED_CHANNEL_SIZE_CAN_VARY) {
// this is a candidate for resize
lives_fx_candidate_t *cand=&mainw->fx_candidates[FX_CANDIDATE_RESIZER];
cand->list=g_list_append(cand->list,GINT_TO_POINTER(filter_idx));
cand->delegate=0;
}
}
}
}
return 0;
}
static gboolean set_in_channel_palettes (gint idx, gint num_channels) {
// set in channel palettes for filter[idx]
// we also enable optional channels if we have to
// in this case we fill first the mandatory channels,
// then if necessary the optional channels
// we return FALSE if we could not satisfy the request
weed_plant_t **chantmpls=NULL;
int def_palette;
int num_elements,i,error;
if (!weed_plant_has_leaf(weed_filters[idx],"in_channel_templates")) {
if (num_channels>0) return FALSE;
return TRUE;
}
num_elements=weed_leaf_num_elements(weed_filters[idx],"in_channel_templates");
if (num_elements<num_channels) return FALSE;
if (num_elements==0) return TRUE;
chantmpls=weed_get_plantptr_array(weed_filters[idx],"in_channel_templates",&error);
// our start state is with all optional channels disabled
// fill mandatory channels first; these palettes may change later if we get a frame in a different palette
for (i=0;i<num_elements;i++) {
if (!weed_plant_has_leaf(chantmpls[i],"is_audio")||weed_get_boolean_value(chantmpls[i],"is_audio",&error)!=WEED_TRUE) {
int num_palettes=weed_leaf_num_elements(chantmpls[i],"palette_list");
int *palettes=weed_get_int_array(chantmpls[i],"palette_list",&error);
if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_RGB24)==WEED_PALETTE_RGB24)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_RGB24);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_BGR24)==WEED_PALETTE_BGR24)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_BGR24);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_RGBA32)==WEED_PALETTE_RGBA32)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_RGBA32);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_BGRA32)==WEED_PALETTE_BGRA32)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_BGRA32);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_ARGB32)==WEED_PALETTE_ARGB32)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_ARGB32);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV888)==WEED_PALETTE_YUV888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV444P)==WEED_PALETTE_YUV444P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV444P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUVA8888)==WEED_PALETTE_YUVA8888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUVA8888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUVA4444P)==WEED_PALETTE_YUVA4444P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUVA4444P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_UYVY8888)==WEED_PALETTE_UYVY8888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_UYVY8888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUYV8888)==WEED_PALETTE_YUYV8888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUYV8888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV422P)==WEED_PALETTE_YUV422P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV422P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV420P)==WEED_PALETTE_YUV420P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV420P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YVU420P)==WEED_PALETTE_YVU420P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YVU420P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV411)==WEED_PALETTE_YUV411)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV411);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_AFLOAT)==WEED_PALETTE_AFLOAT)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_AFLOAT);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_A8)==WEED_PALETTE_A8)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_A8);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_A1)==WEED_PALETTE_A1)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_A1);
else if (!weed_plant_has_leaf(chantmpls[i],"optional")) {
if (chantmpls!=NULL) weed_free(chantmpls);
weed_free(palettes);
return FALSE; // mandatory channel; we don't yet handle rgb float
}
/*else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_RGBFLOAT)==WEED_PALETTE_RGBFLOAT)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_RGBFLOAT);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_ARGBFLOAT)==WEED_PALETTE_ARGBFLOAT)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_ARGBFLOAT);
*/
weed_free(palettes);
}
if (!weed_plant_has_leaf(chantmpls[i],"optional")) num_channels--; // mandatory channel
}
if (num_channels>0) {
// OK we need to use some optional channels
for (i=0;i<num_elements&&num_channels>0;i++) if (weed_plant_has_leaf(chantmpls[i],"optional")) {
weed_set_boolean_value(chantmpls[i],"host_disabled",WEED_FALSE);
num_channels--;
}
}
if (num_channels>0) {
if (chantmpls!=NULL) weed_free(chantmpls);
return FALSE;
}
// now we set match channels
if (!weed_plant_has_leaf(chantmpls[0],"is_audio")||weed_get_boolean_value(chantmpls[0],"is_audio",&error)!=WEED_TRUE) {
def_palette=weed_get_int_value(chantmpls[0],"current_palette",&error);
for (i=1;i<num_elements;i++) {
int channel_flags=weed_get_int_value(chantmpls[i],"flags",&error);
if (!(channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY)) {
int num_palettes=weed_leaf_num_elements(chantmpls[i],"palette_list");
int *palettes=weed_get_int_array(chantmpls[i],"palette_list",&error);
if (check_weed_palette_list(palettes,num_palettes,def_palette)==def_palette)
weed_set_int_value(chantmpls[i],"current_palette",def_palette);
else {
if (chantmpls!=NULL) weed_free(chantmpls);
weed_free(palettes);
return FALSE;
}
weed_free(palettes);
}
}
}
if (chantmpls!=NULL) weed_free(chantmpls);
return TRUE;
}
static gboolean set_out_channel_palettes (gint idx, gint num_channels) {
// set in channel palettes for filter[idx]
// we also enable optional channels if we have to
// in this case we fill first the mandatory channels,
// then if necessary the optional channels
// we return FALSE if we could not satisfy the request
weed_plant_t **chantmpls=NULL;
weed_plant_t **in_chantmpls=NULL;
int num_elements=weed_leaf_num_elements(weed_filters[idx],"out_channel_templates"),i,error;
int num_in_elements=weed_leaf_num_elements(weed_filters[idx],"in_channel_templates");
int def_palette=WEED_PALETTE_END;
if (num_elements<num_channels) return FALSE;
chantmpls=weed_get_plantptr_array(weed_filters[idx],"out_channel_templates",&error);
// our start state is with all optional channels disabled
// fill mandatory channels first; these palettes may change later if we get a frame in a different palette
for (i=0;i<num_elements;i++) {
if (!weed_plant_has_leaf(chantmpls[i],"is_audio")||weed_get_boolean_value(chantmpls[i],"is_audio",&error)!=WEED_TRUE) {
int num_palettes=weed_leaf_num_elements(chantmpls[i],"palette_list");
int *palettes=weed_get_int_array(chantmpls[i],"palette_list",&error);
if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_RGB24)==WEED_PALETTE_RGB24)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_RGB24);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_BGR24)==WEED_PALETTE_BGR24)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_BGR24);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_RGBA32)==WEED_PALETTE_RGBA32)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_RGBA32);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_BGRA32)==WEED_PALETTE_BGRA32)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_BGRA32);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_ARGB32)==WEED_PALETTE_ARGB32)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_ARGB32);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV888)==WEED_PALETTE_YUV888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV444P)==WEED_PALETTE_YUV444P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV444P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUVA8888)==WEED_PALETTE_YUVA8888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUVA8888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUVA4444P)==WEED_PALETTE_YUVA4444P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUVA4444P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_UYVY8888)==WEED_PALETTE_UYVY8888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_UYVY8888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUYV8888)==WEED_PALETTE_YUYV8888)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUYV8888);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV422P)==WEED_PALETTE_YUV422P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV422P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV420P)==WEED_PALETTE_YUV420P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV420P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YVU420P)==WEED_PALETTE_YVU420P)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YVU420P);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_YUV411)==WEED_PALETTE_YUV411)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_YUV411);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_AFLOAT)==WEED_PALETTE_AFLOAT)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_AFLOAT);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_A8)==WEED_PALETTE_A8)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_A8);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_A1)==WEED_PALETTE_A1)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_A1);
else if (!weed_plant_has_leaf(chantmpls[i],"optional")) {
if (chantmpls!=NULL) weed_free(chantmpls);
weed_free(palettes);
return FALSE; // mandatory channel; we don't yet handle rgb float
}
/* else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_RGBFLOAT)==WEED_PALETTE_RGBFLOAT)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_RGBFLOAT);
else if (check_weed_palette_list (palettes,num_palettes,WEED_PALETTE_ARGBFLOAT)==WEED_PALETTE_ARGBFLOAT)
weed_set_int_value(chantmpls[i],"current_palette",WEED_PALETTE_ARGBFLOAT);
*/
weed_free(palettes);
}
if (!weed_plant_has_leaf(chantmpls[i],"optional")) num_channels--; // mandatory channel
}
if (num_channels>0) {
// OK we need to use some optional channels
for (i=0;i<num_elements&&num_channels>0;i++) if (weed_plant_has_leaf(chantmpls[i],"optional")) {
weed_set_boolean_value(chantmpls[i],"host_disabled",WEED_FALSE);
num_channels--;
}
}
if (num_channels>0) {
if (chantmpls!=NULL) weed_free(chantmpls);
return FALSE;
}
// now we set match channels
if (num_in_elements) {
in_chantmpls=weed_get_plantptr_array(weed_filters[idx],"in_channel_templates",&error);
if (!weed_plant_has_leaf(in_chantmpls[0],"is_audio")||
weed_get_boolean_value(in_chantmpls[0],"is_audio",&error)!=WEED_TRUE) {
def_palette=weed_get_int_value(in_chantmpls[0],"current_palette",&error);
}
weed_free(in_chantmpls);
}
else if (!weed_plant_has_leaf(chantmpls[0],"is_audio")||
weed_get_boolean_value(chantmpls[0],"is_audio",&error)!=WEED_TRUE) {
def_palette=weed_get_int_value(chantmpls[0],"current_palette",&error);
}
if (def_palette!=WEED_PALETTE_END) {
for (i=0;i<num_elements;i++) {
int channel_flags=weed_get_int_value(chantmpls[i],"flags",&error);
if (!(channel_flags&WEED_CHANNEL_PALETTE_CAN_VARY)) {
int num_palettes=weed_leaf_num_elements(chantmpls[i],"palette_list");
int *palettes=weed_get_int_array(chantmpls[i],"palette_list",&error);
if (check_weed_palette_list(palettes,num_palettes,def_palette)==def_palette)
weed_set_int_value(chantmpls[i],"current_palette",def_palette);
else {
if (chantmpls!=NULL) weed_free(chantmpls);
weed_free(palettes);
return FALSE;
}
weed_free(palettes);
}
}
}
if (chantmpls!=NULL) weed_free(chantmpls);
return TRUE;
}
static void load_weed_plugin (gchar *plugin_name, gchar *plugin_path, gchar *dir) {
weed_setup_f setup_fn;
weed_bootstrap_f bootstrap=(weed_bootstrap_f)&weed_bootstrap_func;
weed_plant_t *plugin_info,**filters,*filter;
void *handle;
GtkWidget *menuitem,*menu;
GtkWidget *pkg_menu;
GtkWidget *pkg_submenu=NULL;
int error,reason,idx=num_weed_filters;
int filters_in_plugin;
int mode=-1,kmode=0;
int i;
gchar *string,*filter_type;
gchar *filter_name;
char cwd[PATH_MAX],*pwd;
gchar *pkg=NULL,*pkgstring;
static int key=-1;
pwd=getcwd(cwd,PATH_MAX);
key++;
mainw->chdir_failed=FALSE;
// walk list and create fx structures
//#define DEBUG_WEED
#ifdef DEBUG_WEED
g_printerr("Checking plugin %s\n",plugin_path);
#endif
if ((handle=dlopen(plugin_path,RTLD_LAZY))) {
dlerror(); // clear existing errors
if ((setup_fn=(weed_setup_f)dlsym(handle,"weed_setup"))==NULL) {
g_printerr(_("Error: plugin %s has no weed_setup() function.\n"),plugin_path);
}
else {
// here we call the plugin's setup_fn, passing in our bootstrap function
// the plugin will call our bootstrap function to get the correct versions of the core weed functions
// and bootstrap itself
// if we use the plugin, we must not free the plugin_info, since the plugin has a reference to this
// chdir to plugin dir, in case it needs to load data
lives_chdir(dir,TRUE);
plugin_info=(*setup_fn)(bootstrap);
if (plugin_info==NULL||(filters_in_plugin=check_weed_plugin_info(plugin_info))<1) {
gchar *msg=g_strdup_printf(_("No usable filters found in plugin %s\n"),plugin_path);
LIVES_INFO(msg);
g_free(msg);
if (plugin_info!=NULL) weed_plant_free(plugin_info);
dlclose (handle);
lives_chdir(pwd,FALSE);
return;
}
weed_set_voidptr_value(plugin_info,"handle",handle);
weed_set_string_value(plugin_info,"name",plugin_name); // for hashname
weed_set_string_value(plugin_info,"plugin_path",dir);
weed_add_plant_flags(plugin_info,WEED_LEAF_READONLY_PLUGIN);
filters=weed_get_plantptr_array(plugin_info,"filters",&error);
while (idx<MAX_WEED_FILTERS&&mode<filters_in_plugin-1) {
mode++;
filter_name=weed_get_string_value(filters[mode],"name",&error);
if (!(reason=check_for_lives(filters[mode],idx))) {
gboolean dup=FALSE;
weed_filters[idx]=filters[mode];
num_weed_filters++;
hashnames[idx]=make_weed_hashname(idx,TRUE);
for (i=0;i<idx;i++) {
if (!strcmp(hashnames[idx],hashnames[i])) {
// skip dups
char *msg=g_strdup_printf(_("Found duplicate plugin %s\n"),hashnames[idx]);
LIVES_INFO(msg);
g_free(msg);
g_free(hashnames[idx]);
g_free(filter_name);
hashnames[idx]=NULL;
weed_filters[idx]=NULL;
num_weed_filters--;
idx--;
dup=TRUE;
break;
}
}
if (!dup) {
#ifdef DEBUG_WEED
if (key<FX_KEYS_PHYSICAL) d_print(g_strdup_printf("Loaded filter \"%s\" in plugin \"%s\"; assigned to key ctrl-%d, mode %d.\n",
filter_name,plugin_name,key+1,kmode+1));
else d_print(g_strdup_printf("Loaded filter \"%s\" in plugin \"%s\", no key assigned\n",filter_name,plugin_name));
#endif
filter=weed_filters[idx];
// we start with all optional channels disabled (unless forced to use them)
set_in_channel_palettes (idx,enabled_in_channels(filter,FALSE));
set_out_channel_palettes (idx,1);
// skip hidden channels
if (!weed_plant_has_leaf(filter,"host_menu_hide")||
(weed_get_boolean_value(filter,"host_menu_hide",&error)==WEED_FALSE)) {
if (key<FX_KEYS_PHYSICAL) {
key_to_fx[key][kmode]=idx;
}
if ((pkgstring=strstr(filter_name,": "))!=NULL) {
// package effect
if (pkg==NULL) {
pkg=filter_name;
filter_name=g_strdup(pkg);
memset(pkgstring,0,1);
/* TRANSLATORS: example " - LADSPA plugins -" */
pkgstring=g_strdup_printf(_(" - %s plugins -"),pkg);
// create new submenu
pkg_menu=gtk_menu_item_new_with_label (pkgstring);
gtk_container_add (GTK_CONTAINER (mainw->rte_defs), pkg_menu);
pkg_submenu=gtk_menu_new();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (pkg_menu), pkg_submenu);
if (palette->style&STYLE_1) {
gtk_widget_modify_bg(pkg_submenu, GTK_STATE_NORMAL, &palette->menu_and_bars);
}
gtk_widget_show (pkg_menu);
gtk_widget_show (pkg_submenu);
g_free(pkgstring);
}
// add to submenu
menu=pkg_submenu;
}
else {
if (pkg!=NULL) g_free(pkg);
pkg=NULL;
menu=mainw->rte_defs;
}
filter_type=weed_filter_get_type(weed_filters[idx],FALSE);
string=g_strdup_printf("%s (%s)",filter_name,filter_type);
menuitem=gtk_menu_item_new_with_label (string);
gtk_widget_show(menuitem);
g_free(string);
g_free(filter_type);
gtk_container_add (GTK_CONTAINER (menu), menuitem);
g_signal_connect (GTK_OBJECT (menuitem), "activate",
G_CALLBACK (rte_set_defs_activate),
GINT_TO_POINTER(idx));
}
kmode++;
}
idx++;
}
#ifdef DEBUG_WEED
else g_printerr(g_strdup_printf("Unsuitable filter \"%s\" in plugin \"%s\", reason code %d\n",filter_name,plugin_name,reason));
#endif
weed_free(filter_name);
}
weed_free(filters);
}
}
else g_printerr(_("Info: Unable to load plugin %s\nError was: %s\n"),plugin_path,dlerror());
if (mainw->chdir_failed) {
gchar *dirs=g_strdup(_("Some plugin directories"));
do_chdir_failed_error(dirs);
g_free(dirs);
}
lives_chdir(pwd,FALSE);
// TODO - add any rendered effects to fx submenu
}
void weed_memory_init(void) {
weed_init(110,(weed_malloc_f)lives_malloc,(weed_free_f)lives_free,(weed_memcpy_f)lives_memcpy,
(weed_memset_f)lives_memset);
}
void weed_load_all (void) {
// get list of plugins from directory and create our fx
int i,j,plugin_idx,subdir_idx;
GList *weed_plugin_list,*weed_plugin_sublist;
gchar *lives_weed_plugin_path,*weed_plugin_path,*weed_p_path;
gchar *subdir_path,*subdir_name,*plugin_path,*plugin_name;
gint numdirs;
gchar **dirs;
gchar *msg;
gint listlen;
num_weed_filters=0;
threaded_dialog_spin();
lives_weed_plugin_path=g_build_filename(prefs->lib_dir,PLUGIN_EXEC_DIR,PLUGIN_WEED_FX_BUILTIN,NULL);
#ifdef DEBUG_WEED
g_printerr("In weed init\n");
#endif
fg_gen_to_start=fg_generator_key=fg_generator_clip=fg_generator_mode=-1;
bg_gen_to_start=bg_generator_key=bg_generator_mode=-1;
for (i=0;i<FX_KEYS_MAX;i++) {
if (i<FX_KEYS_MAX_VIRTUAL) key_to_instance[i]=(weed_plant_t **)
g_malloc(prefs->max_modes_per_key*sizeof(weed_plant_t *));
else key_to_instance[i]=(weed_plant_t **)g_malloc(sizeof(weed_plant_t *));
key_to_instance_copy[i]=(weed_plant_t **)g_malloc(sizeof(weed_plant_t *));
if (i<FX_KEYS_MAX_VIRTUAL) key_to_fx[i]=(int *)g_malloc(prefs->max_modes_per_key*sizint);
else key_to_fx[i]=(int *)g_malloc(sizint);
if (i<FX_KEYS_MAX_VIRTUAL)
key_defaults[i]=(weed_plant_t ***)g_malloc(prefs->max_modes_per_key*sizeof(weed_plant_t **));
key_modes[i]=0; // current active mode of each key
filter_map[i]=NULL; // maps effects in order of application for multitrack rendering
for (j=0;j<prefs->max_modes_per_key;j++) {
if (i<FX_KEYS_MAX_VIRTUAL||j==0) {
if (i<FX_KEYS_MAX_VIRTUAL) key_defaults[i][j]=NULL;
key_to_instance[i][j]=NULL;
key_to_fx[i][j]=-1;
}
else break;
}
}
filter_map[FX_KEYS_MAX+1]=NULL;
for (i=0;i<FX_KEYS_MAX_VIRTUAL;i++) {
init_events[i]=NULL;
}
next_free_key=FX_KEYS_MAX_VIRTUAL;
for (i=0;i<MAX_WEED_FILTERS;i++) {
weed_filters[i]=NULL;
hashnames[i]=NULL;
}
threaded_dialog_spin();
weed_p_path=getenv("WEED_PLUGIN_PATH");
if (weed_p_path==NULL) weed_plugin_path=g_strdup("");
else weed_plugin_path=g_strdup(weed_p_path);
if (strstr(weed_plugin_path,lives_weed_plugin_path)==NULL) {
gchar *tmp=g_strconcat(strcmp(weed_plugin_path,"")?":":"",lives_weed_plugin_path,NULL);
g_free(weed_plugin_path);
weed_plugin_path=tmp;
lives_setenv("WEED_PLUGIN_PATH",weed_plugin_path);
}
g_free(lives_weed_plugin_path);
// first we parse the weed_plugin_path
#ifndef IS_MINGW
numdirs=get_token_count(weed_plugin_path,':');
dirs=g_strsplit(weed_plugin_path,":",-1);
#else
numdirs=get_token_count(weed_plugin_path,';');
dirs=g_strsplit(weed_plugin_path,";",-1);
#endif
threaded_dialog_spin();
for (i=0;i<numdirs;i++) {
// get list of all files
weed_plugin_list=get_plugin_list(PLUGIN_EFFECTS_WEED,TRUE,dirs[i],NULL);
listlen=g_list_length(weed_plugin_list);
// parse twice, first we get the plugins, then 1 level of subdirs
for (plugin_idx=0;plugin_idx<listlen;plugin_idx++) {
threaded_dialog_spin();
plugin_name=(gchar *)g_list_nth_data(weed_plugin_list,plugin_idx);
#ifndef IS_MINGW
if (!strncmp(plugin_name+strlen(plugin_name)-3,".so",3))
#else
if (!strncmp(plugin_name+strlen(plugin_name)-4,".dll",4))
#endif
{
plugin_path=g_build_filename(dirs[i],plugin_name,NULL);
load_weed_plugin(plugin_name,plugin_path,dirs[i]);
g_free(plugin_name);
g_free(plugin_path);
weed_plugin_list=g_list_delete_link(weed_plugin_list,g_list_nth(weed_plugin_list,plugin_idx));
plugin_idx--;
listlen--;
}
threaded_dialog_spin();
}
// get 1 level of subdirs
for (subdir_idx=0;subdir_idx<listlen;subdir_idx++) {
threaded_dialog_spin();
subdir_name=(gchar *)g_list_nth_data(weed_plugin_list,subdir_idx);
subdir_path=g_build_filename(dirs[i],subdir_name,NULL);
if (!g_file_test(subdir_path, G_FILE_TEST_IS_DIR)||!strcmp(subdir_name,"icons")||!strcmp(subdir_name,"data")) {
g_free(subdir_path);
continue;
}
#ifndef IS_MINGW
weed_plugin_sublist=get_plugin_list(PLUGIN_EFFECTS_WEED,TRUE,subdir_path,"so");
#else
weed_plugin_sublist=get_plugin_list(PLUGIN_EFFECTS_WEED,TRUE,subdir_path,"dll");
#endif
for (plugin_idx=0;plugin_idx<g_list_length(weed_plugin_sublist);plugin_idx++) {
plugin_name=(gchar *)g_list_nth_data(weed_plugin_sublist,plugin_idx);
plugin_path=g_build_filename(subdir_path,plugin_name,NULL);
load_weed_plugin(plugin_name,plugin_path,subdir_path);
g_free(plugin_path);
}
if (weed_plugin_sublist!=NULL) {
g_list_free_strings(weed_plugin_sublist);
g_list_free(weed_plugin_sublist);
}
g_free(subdir_path);
threaded_dialog_spin();
}
if (weed_plugin_list!=NULL) {
g_list_free_strings(weed_plugin_list);
g_list_free(weed_plugin_list);
}
}
g_strfreev(dirs);
g_free(weed_plugin_path);
msg=g_strdup_printf(_ ("Successfully loaded %d Weed filters\n"),num_weed_filters);
d_print(msg);
g_free(msg);
threaded_dialog_spin();
}
static void weed_filter_free(weed_plant_t *filter) {
int nitems,error,i;
weed_plant_t **plants,*gui;
void *func;
boolean is_compound=FALSE;
if (num_compound_fx(filter)>1) is_compound=TRUE;
// free in_param_templates
if (weed_plant_has_leaf(filter,"in_parameter_templates")) {
nitems=weed_leaf_num_elements(filter,"in_parameter_templates");
if (nitems>0) {
plants=weed_get_plantptr_array(filter,"in_parameter_templates",&error);
for (i=0;i<nitems;i++) {
if (weed_plant_has_leaf(plants[i],"gui")) {
gui=(weed_get_plantptr_value(plants[i],"gui",&error));
if (weed_plant_has_leaf(gui,"display_func")) {
func=weed_get_voidptr_value(gui,"display_func",&error);
if (func!=NULL) weed_free(func);
}
weed_plant_free(gui);
}
if (weed_plant_has_leaf(filter,"interpolate_func")) {
func=weed_get_voidptr_value(filter,"interpolate_func",&error);
if (func!=NULL) weed_free(func);
}
weed_plant_free(plants[i]);
}
weed_free(plants);
}
}
if (is_compound) {
weed_plant_free(filter);
return;
}
if (weed_plant_has_leaf(filter,"init_func")) {
func=weed_get_voidptr_value(filter,"init_func",&error);
if (func!=NULL) weed_free(func);
}
if (weed_plant_has_leaf(filter,"deinit_func")) {
func=weed_get_voidptr_value(filter,"deinit_func",&error);
if (func!=NULL) weed_free(func);
}
if (weed_plant_has_leaf(filter,"process_func")) {
func=weed_get_voidptr_value(filter,"process_func",&error);
if (func!=NULL) weed_free(func);
}
// free in_channel_templates
if (weed_plant_has_leaf(filter,"in_channel_templates")) {
nitems=weed_leaf_num_elements(filter,"in_channel_templates");
if (nitems>0) {
plants=weed_get_plantptr_array(filter,"in_channel_templates",&error);
for (i=0;i<nitems;i++) weed_plant_free(plants[i]);
weed_free(plants);
}
}
// free out_channel_templates
if (weed_plant_has_leaf(filter,"out_channel_templates")) {
nitems=weed_leaf_num_elements(filter,"out_channel_templates");
if (nitems>0) {
plants=weed_get_plantptr_array(filter,"out_channel_templates",&error);
for (i=0;i<nitems;i++) weed_plant_free(plants[i]);
weed_free(plants);
}
}
// free out_param_templates
if (weed_plant_has_leaf(filter,"out_parameter_templates")) {
nitems=weed_leaf_num_elements(filter,"out_parameter_templates");
if (nitems>0) {
plants=weed_get_plantptr_array(filter,"out_parameter_templates",&error);
threaded_dialog_spin();
for (i=0;i<nitems;i++) {
if (weed_plant_has_leaf(plants[i],"gui")) weed_plant_free(weed_get_plantptr_value(plants[i],"gui",&error));
weed_plant_free(plants[i]);
}
weed_free(plants);
}
}
// free gui
if (weed_plant_has_leaf(filter,"gui")) weed_plant_free(weed_get_plantptr_value(filter,"gui",&error));
// free filter
weed_plant_free(filter);
}
static weed_plant_t *create_compound_filter(gchar *plugin_name, int nfilts, int *filts) {
weed_plant_t *filter=weed_plant_new(WEED_PLANT_FILTER_CLASS),*xfilter,*gui;
weed_plant_t **in_params=NULL,**out_params=NULL,**params;
weed_plant_t **in_chans,**out_chans;
gchar *tmp;
double tgfps=-1.,tfps;
int count,xcount,error;
int txparam=-1,tparam,txvolm=-1,tvolm;
register int i,j,x;
weed_set_int_array(filter,"host_filter_list",nfilts,filts);
// create parameter templates - concatenate all sub filters
count=xcount=0;
for (i=0;i<nfilts;i++) {
xfilter=weed_filters[filts[i]];
if (weed_plant_has_leaf(xfilter,"target_fps")) {
tfps=weed_get_double_value(xfilter,"target_fps",&error);
if (tgfps==-1.) tgfps=tfps;
else if (tgfps!=tfps) {
d_print((tmp=g_strdup_printf(_("Invalid compound effect %s - has conflicting target_fps\n"),plugin_name)));
LIVES_ERROR(tmp);
g_free(tmp);
return NULL;
}
}
// in_parameter templates are copy-by-value, since we may set a different default/host_default value, and a different gui
// everything else - filter classes, out_param templates and channels are copy by ref.
if (weed_plant_has_leaf(xfilter,"in_parameter_templates")) {
tparam=get_transition_param(xfilter,FALSE);
// TODO *** - ignore transtion params if they are connected to themselves
if (tparam!=-1) {
if (txparam!=-1) {
d_print((tmp=g_strdup_printf(_("Invalid compound effect %s - has multiple transition parameters\n"),plugin_name)));
LIVES_ERROR(tmp);
g_free(tmp);
return NULL;
}
txparam=tparam;
}
tvolm=get_master_vol_param(xfilter,FALSE);
// TODO *** - ignore master vol params if they are connected to themselves
if (tvolm!=-1) {
if (txvolm!=-1) {
d_print((tmp=g_strdup_printf(_("Invalid compound effect %s - has multiple master volume parameters\n"),plugin_name)));
LIVES_ERROR(tmp);
g_free(tmp);
return NULL;
}
txvolm=tvolm;
}
count+=weed_leaf_num_elements(xfilter,"in_parameter_templates");
params=weed_get_plantptr_array(xfilter,"in_parameter_templates",&error);
in_params=g_realloc(in_params,count*sizeof(weed_plant_t *));
x=0;
for (j=xcount;j<count;j++) {
in_params[j]=weed_plant_copy(params[x]);
if (weed_plant_has_leaf(params[x],"gui")) {
gui=weed_get_plantptr_value(params[x],"gui",&error);
weed_set_plantptr_value(in_params[j],"gui",weed_plant_copy(gui));
}
if (x==tparam) {
weed_set_boolean_value(in_params[j],"transition",WEED_TRUE);
}
else if (weed_plant_has_leaf(in_params[j],"transition")) weed_leaf_delete(in_params[j],"transition");
if (x==tvolm) {
weed_set_boolean_value(in_params[j],"is_volume_master",WEED_TRUE);
}
else if (weed_plant_has_leaf(in_params[j],"is_volume_master")) weed_leaf_delete(in_params[j],"is_volume_master");
x++;
}
xcount=count;
}
}
if (tgfps!=-1.) weed_set_double_value(filter,"target_fps",tgfps);
if (count>0) {
weed_set_plantptr_array(filter,"in_parameter_templates",count,in_params);
g_free(in_params);
}
count=xcount=0;
for (i=0;i<nfilts;i++) {
xfilter=weed_filters[filts[i]];
if (weed_plant_has_leaf(xfilter,"out_parameter_templates")) {
count+=weed_leaf_num_elements(xfilter,"out_parameter_templates");
params=weed_get_plantptr_array(xfilter,"out_parameter_templates",&error);
out_params=g_realloc(out_params,count*sizeof(weed_plant_t *));
x=0;
for (j=xcount;j<count;j++) {
out_params[j]=params[x++];
}
xcount=count;
}
}
if (count>0) {
weed_set_plantptr_array(filter,"out_parameter_templates",count,out_params);
weed_free(out_params);
}
// use in channels from first filter
xfilter=weed_filters[filts[0]];
if (weed_plant_has_leaf(xfilter,"in_channel_templates")) {
count=weed_leaf_num_elements(xfilter,"in_channel_templates");
in_chans=weed_get_plantptr_array(xfilter,"in_channel_templates",&error);
weed_set_plantptr_array(filter,"in_channel_templates",count,in_chans);
weed_free(in_chans);
}
// use out channels from last filter
xfilter=weed_filters[filts[nfilts-1]];
if (weed_plant_has_leaf(xfilter,"out_channel_templates")) {
count=weed_leaf_num_elements(xfilter,"out_channel_templates");
out_chans=weed_get_plantptr_array(xfilter,"out_channel_templates",&error);
weed_set_plantptr_array(filter,"out_channel_templates",count,out_chans);
weed_free(out_chans);
}
return filter;
}
static void load_compound_plugin(gchar *plugin_name, gchar *plugin_path) {
FILE *cpdfile;
weed_plant_t *filter=NULL,*ptmpl,*iptmpl,*xfilter;
gchar buff[16384];
gchar **array,**svals;
gchar *author=NULL,*tmp,*key;
int *filters=NULL,*ivals;
double *dvals;
int xvals[4];
boolean ok=TRUE,autoscale;
int stage=0,nfilts=0,fnum,line=0,version=0;
int qvals=1,ntok,xfilt,xfilt2;
int xconx=0;
int nparams,nchans,pnum,pnum2,xpnum2,cnum,cnum2,ptype,phint,pcspace,pflags;