#
# CopyAndPasteItemFXChain.pl
# Mike Lacey
# You run this script twice, once to "get" the FX Chain you want and then again
# to Paste it to another Item or Items
# 1 Select the Item containing the FX Chain you want to copy to other items
# 2 Run the script
# 3 Select the Item(s) you want to copy the FX Chain to
# 4 Run the script
# int CountSelectedMediaItems(ReaProject* proj)
# bool GetSetItemState(MediaItem* item, char* str, int maxlen)
# MediaItem* GetSelectedMediaItem(ReaProject* proj, int selitem)
use strict;
use warnings;
use Win32::Clipboard;
my $Clipboard = Win32::Clipboard();
use constant CURR_PROJ => 0;
use constant FIRST_SEL => 0;
use constant MAXLEN => (16 * 1024);
use constant ACTION_NAME => "Copy And Paste Item FX Chain";
use constant MSGBOX_OKONLY => 0;
use constant MSGBOX_YESNO => 4;
use constant MSGBOX_YES => 6;
use constant MSGBOX_NO => 7;
use constant COPY => 0;
use constant PASTE => 1;
my $it;
my $chunk;
my $result;
my $maxlen;
my $indent;
my $item;
my ($before, $after, $section, $takefx);
my $run_mode;
my $report = " " . localtime() . "\n";
my $num_selected_items;
# prototype(s)
sub extract_section($$$);
sub FXChainInClipBoard();
$report .= "initialised\n";
# get number of selected items
my $prompt_str='';
my $action;
my $nothing;
$num_selected_items = RPR_CountSelectedMediaItems(CURR_PROJ);
if($num_selected_items == 0){
$prompt_str =
"No Items Selected.\n\nThis script needs to be run twice." .
" The first run, with one Item selected, copies the Item FX Chain" .
" (if any) into the Windows Clipboard.\n\n" .
"A second run, with one or more Items selected, and a valid" .
" FX Chain from a previous run of this script in the Windows Clipboard," .
" will Paste that FX Chain into the selected Items.\n\n" .
"No action will be taken now; press OK to return to REAPER\n";
RPR_MB($prompt_str, ACTION_NAME, MSGBOX_OKONLY);
exit(0);
}elsif($num_selected_items >= 1){
if(FXChainInClipBoard()){
$prompt_str =
"One or more Items Selected and (what looks like...) an FX Chain" .
" in the Windows Clipboard.\n\n" .
"This will Paste the FX Chain into the selected Item or Items\n\n" .
"Do you want to continue?";
($action, $nothing, $nothing, $nothing) = RPR_MB($prompt_str, ACTION_NAME, MSGBOX_YESNO);
$run_mode = PASTE if $action == MSGBOX_YES;
} else {
$prompt_str =
"One or more Items Selected and no FX Chain in the Windows" .
" Clipboard.\n\n" .
"This will Copy the FX Chain of the selected Item into the" .
" Windows Clipboard.\n\n" .
"Do you want to continue?";
($action, $nothing, $nothing, $nothing) = RPR_MB($prompt_str, ACTION_NAME, MSGBOX_YESNO);
$run_mode = COPY if $action == MSGBOX_YES;
}
}
if($run_mode == COPY){
# Get the FX chain from the selected item
$it = RPR_GetSelectedMediaItem(CURR_PROJ, FIRST_SEL);
$chunk = ""; # because I want to read the state of the first item
($result, $it, $chunk, $maxlen) = RPR_GetSetItemState($it, $chunk, MAXLEN);
# pick out first TAXFEX section
($before, $takefx, $after) = extract_section($chunk, '<TAKEFX', 0);
# remove the FXID lines
$takefx =~ s/^FXID.+\n//mg;
if($takefx){
$takefx = "REAPER-FXCHAIN-DATA\n" . $takefx;
$Clipboard -> Set($takefx);
} else {
RPR_MB("No FX Chain in First Selected Item", ACTION_NAME, MSGBOX_OKONLY);
}
} elsif($run_mode == PASTE){
# Items Loop
# copy that TAKEFX section into every selected item, replacing any
# existing TAKEFX section in that item
my $NewItemStr='';
$takefx = $Clipboard -> Get();
$takefx =~ s/^REAPER-FXCHAIN-DATA\n//;
for $item ( 0 .. $num_selected_items - 1){
$chunk="";
$it = RPR_GetSelectedMediaItem(CURR_PROJ, $item);
($result, $it, $chunk, $maxlen) = RPR_GetSetItemState($it, $chunk, MAXLEN);
($before, $section, $after) = extract_section($chunk, '<TAKEFX', 0) if $result;
unless($section){
$NewItemStr = substr $before, 0, -2;
$NewItemStr .= "\n" . $takefx . ">\n";
} else {
$NewItemStr = $before . $takefx . $after
}
$Clipboard -> Set($NewItemStr);
($result, $it, $chunk, $maxlen) = RPR_GetSetItemState($it, $NewItemStr, MAXLEN);
}
}
exit(0);
# subroutines below here
sub extract_section($$$){ my($chunk, $name, $index) = @_;
my @chunk; # to hold $chunk split into lines
my $cline; # individual lines from @chunk
my $indent; # to keep track in a multiline section
my ($before, $section, $after); # return values
my $inner_state = 0; # 0=before section, 1=in section, 2=after
my $multiline = 0; # boolean, single line sections have no <
my $occurrence = 0;
$multiline = 1 if $name =~ /^</; # a multiline section?
$before = $section = $after = ''; # just in case
@chunk = split(/\n/,$chunk); # populate @chunk
foreach $cline (@chunk){ # and loop through it
if($inner_state == 0){ # "before" section
if($cline =~ /^\s*$name(\s|$)/){
# correct section name - correct occurrence?
if($occurrence == $index){ # Yes
$indent++; # keep track of indents
$inner_state = 1; # might be multiline...
$section .= "$cline\n"; # build up return variable
$inner_state = 2 # done unless
unless $multiline; # it's a multiline section
} else { # No, wrong occurrence
$occurrence++; # keep looking
last # unless already gone past it
if $occurrence > $index;
}
} else {
# still before the section we're looking for
$before .= "$cline\n"; # so build up return variable
}
} elsif($inner_state == 1){ # in a multiline section
$section .= "$cline\n"; # so build up return variable
$indent++ if $cline =~ /</; # keep track of indents
$indent-- if $cline =~ />/; # keep track of indents
$inner_state = 2 if $indent == 0; # and get out if we're done
} elsif($inner_state == 2){ # "after" section
$after .= "$cline\n"; # so build up return variable
}
}
return($before, $section, $after);
}
sub FXChainInClipBoard(){
use constant TRUE => 1;
use constant FALSE => 0;
my $CB;
$CB = $Clipboard -> Get();
if($CB =~ /^REAPER-FXCHAIN-DATA\n/){
return(TRUE);
} else {
return(FALSE);
}
}