| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Audio::Nama::Assign; | 
| 2 | 5 |  |  | 5 |  | 21492 | use Modern::Perl; | 
|  | 5 |  |  |  |  | 13001 |  | 
|  | 5 |  |  |  |  | 35 |  | 
| 3 |  |  |  |  |  |  | our $VERSION = 1.0; | 
| 4 | 5 |  |  | 5 |  | 720 | use 5.008; | 
|  | 5 |  |  |  |  | 16 |  | 
| 5 | 5 |  |  | 5 |  | 25 | use feature 'state'; | 
|  | 5 |  |  |  |  | 9 |  | 
|  | 5 |  |  |  |  | 305 |  | 
| 6 | 5 |  |  | 5 |  | 23 | use strict; | 
|  | 5 |  |  |  |  | 13 |  | 
|  | 5 |  |  |  |  | 105 |  | 
| 7 | 5 |  |  | 5 |  | 48 | use warnings; | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 150 |  | 
| 8 | 5 |  |  | 5 |  | 44 | no warnings q(uninitialized); | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 179 |  | 
| 9 | 5 |  |  | 5 |  | 22 | use Carp qw(carp confess croak cluck); | 
|  | 5 |  |  |  |  | 17 |  | 
|  | 5 |  |  |  |  | 316 |  | 
| 10 | 5 |  |  | 5 |  | 4620 | use YAML::Tiny; | 
|  | 5 |  |  |  |  | 28959 |  | 
|  | 5 |  |  |  |  | 297 |  | 
| 11 | 5 |  |  | 5 |  | 4431 | use File::Slurp; | 
|  | 5 |  |  |  |  | 68625 |  | 
|  | 5 |  |  |  |  | 359 |  | 
| 12 | 5 |  |  | 5 |  | 4133 | use File::HomeDir; | 
|  | 5 |  |  |  |  | 30645 |  | 
|  | 5 |  |  |  |  | 280 |  | 
| 13 | 5 |  |  | 5 |  | 2204 | use Audio::Nama::Log qw(logsub logpkg); | 
|  | 5 |  |  |  |  | 28 |  | 
|  | 5 |  |  |  |  | 345 |  | 
| 14 | 5 |  |  | 5 |  | 4179 | use Storable qw(nstore retrieve); | 
|  | 5 |  |  |  |  | 13980 |  | 
|  | 5 |  |  |  |  | 339 |  | 
| 15 | 5 |  |  | 5 |  | 5379 | use JSON::XS; | 
|  | 5 |  |  |  |  | 38014 |  | 
|  | 5 |  |  |  |  | 300 |  | 
| 16 | 5 |  |  | 5 |  | 3041 | use Data::Dumper::Concise; | 
|  | 5 |  |  |  |  | 26385 |  | 
|  | 5 |  |  |  |  | 937 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | require Exporter; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | our @ISA = qw(Exporter); | 
| 21 |  |  |  |  |  |  | our %EXPORT_TAGS = ( 'all' => [ qw( | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | serialize | 
| 24 |  |  |  |  |  |  | assign | 
| 25 |  |  |  |  |  |  | assign_singletons | 
| 26 |  |  |  |  |  |  | store_vars | 
| 27 |  |  |  |  |  |  | json_out | 
| 28 |  |  |  |  |  |  | yaml_in | 
| 29 |  |  |  |  |  |  | json_in | 
| 30 |  |  |  |  |  |  | json_out | 
| 31 |  |  |  |  |  |  | quote_yaml_scalars | 
| 32 |  |  |  |  |  |  | var_map | 
| 33 |  |  |  |  |  |  | config_vars | 
| 34 |  |  |  |  |  |  | ) ] ); | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | our @EXPORT = (); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | our $to_json = JSON::XS->new->utf8->allow_blessed->pretty->canonical(1) ; | 
| 41 | 5 |  |  | 5 |  | 30 | use Carp; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 15380 |  | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | {my $var_map = { qw( | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | %devices 						$config->{devices} | 
| 46 |  |  |  |  |  |  | $alsa_playback_device 			$config->{alsa_playback_device} | 
| 47 |  |  |  |  |  |  | $alsa_capture_device			$config->{alsa_capture_device} | 
| 48 |  |  |  |  |  |  | $soundcard_channels				$config->{soundcard_channels} | 
| 49 |  |  |  |  |  |  | %abbreviations					$config->{abbreviations} | 
| 50 |  |  |  |  |  |  | $mix_to_disk_format 			$config->{mix_to_disk_format} | 
| 51 |  |  |  |  |  |  | $raw_to_disk_format 			$config->{raw_to_disk_format} | 
| 52 |  |  |  |  |  |  | $cache_to_disk_format 			$config->{cache_to_disk_format} | 
| 53 |  |  |  |  |  |  | $mixer_out_format 				$config->{mixer_out_format} | 
| 54 |  |  |  |  |  |  | $sample_rate					$config->{sample_rate} | 
| 55 |  |  |  |  |  |  | $ecasound_tcp_port 				$config->{engine_tcp_port} | 
| 56 |  |  |  |  |  |  | $ecasound_globals				$config->{engine_globals} | 
| 57 |  |  |  |  |  |  | $ecasound_buffersize 			$config->{engine_buffersize} | 
| 58 |  |  |  |  |  |  | $realtime_profile 				$config->{realtime_profile} | 
| 59 |  |  |  |  |  |  | $eq 							$mastering->{fx_eq} | 
| 60 |  |  |  |  |  |  | $low_pass 						$mastering->{fx_low_pass} | 
| 61 |  |  |  |  |  |  | $mid_pass						$mastering->{fx_mid_pass} | 
| 62 |  |  |  |  |  |  | $high_pass						$mastering->{fx_high_pass} | 
| 63 |  |  |  |  |  |  | $compressor						$mastering->{fx_compressor} | 
| 64 |  |  |  |  |  |  | $spatialiser					$mastering->{fx_spatialiser} | 
| 65 |  |  |  |  |  |  | $limiter						$mastering->{fx_limiter} | 
| 66 |  |  |  |  |  |  | $project_root 	 				$config->{root_dir} | 
| 67 |  |  |  |  |  |  | $use_group_numbering 			$config->{use_group_numbering} | 
| 68 |  |  |  |  |  |  | $press_space_to_start_transport $config->{press_space_to_start} | 
| 69 |  |  |  |  |  |  | $execute_on_project_load 		$config->{execute_on_project_load} | 
| 70 |  |  |  |  |  |  | $initial_mode 					$config->{initial_mode} | 
| 71 |  |  |  |  |  |  | $midish_enable 					$config->{use_midish} | 
| 72 |  |  |  |  |  |  | $quietly_remove_tracks 			$config->{quietly_remove_tracks} | 
| 73 |  |  |  |  |  |  | $use_jack_plumbing 				$config->{use_jack_plumbing} | 
| 74 |  |  |  |  |  |  | $jack_seek_delay    			$config->{engine_base_jack_seek_delay} | 
| 75 |  |  |  |  |  |  | $use_monitor_version_for_mixdown $config->{sync_mixdown_and_monitor_version_numbers} | 
| 76 |  |  |  |  |  |  | $mixdown_encodings 				$config->{mixdown_encodings} | 
| 77 |  |  |  |  |  |  | $volume_control_operator 		$config->{volume_control_operator} | 
| 78 |  |  |  |  |  |  | $serialize_formats  	        $config->{serialize_formats} | 
| 79 |  |  |  |  |  |  | $use_git						$config->{use_git} | 
| 80 |  |  |  |  |  |  | $autosave						$config->{autosave} | 
| 81 |  |  |  |  |  |  | $beep_command 					$config->{beep_command} | 
| 82 |  |  |  |  |  |  | $hotkey_beep					$config->{hotkey_beep} | 
| 83 |  |  |  |  |  |  | $eager							$mode->{eager} | 
| 84 |  |  |  |  |  |  | $alias							$config->{alias} | 
| 85 |  |  |  |  |  |  | $hotkeys						$config->{hotkeys} | 
| 86 |  |  |  |  |  |  | $new_track_rw					$config->{new_track_rw} | 
| 87 |  |  |  |  |  |  | $hotkeys_always					$config->{hotkeys_always} | 
| 88 |  |  |  |  |  |  | $use_pager     					$config->{use_pager} | 
| 89 |  |  |  |  |  |  | $use_placeholders  				$config->{use_placeholders} | 
| 90 |  |  |  |  |  |  | $edit_playback_end_margin  		$config->{edit_playback_end_margin} | 
| 91 |  |  |  |  |  |  | $edit_crossfade_time  			$config->{edit_crossfade_time} | 
| 92 |  |  |  |  |  |  | $default_fade_length 			$config->{engine_fade_default_length} | 
| 93 |  |  |  |  |  |  | $fade_time 						$config->{engine_fade_length_on_start_stop} | 
| 94 |  |  |  |  |  |  | %mute_level						$config->{mute_level} | 
| 95 |  |  |  |  |  |  | %fade_out_level 				$config->{fade_out_level} | 
| 96 |  |  |  |  |  |  | $fade_resolution 				$config->{fade_resolution} | 
| 97 |  |  |  |  |  |  | %unity_level					$config->{unity_level} | 
| 98 |  |  |  |  |  |  | $enforce_channel_bounds    		$config->{enforce_channel_bounds} | 
| 99 |  |  |  |  |  |  | $midi_input_dev    				$midi->{input_dev} | 
| 100 |  |  |  |  |  |  | $midi_output_dev   				$midi->{output_dev} | 
| 101 |  |  |  |  |  |  | $controller_ports				$midi->{controller_ports} | 
| 102 |  |  |  |  |  |  | $midi_inputs					$midi->{inputs} | 
| 103 |  |  |  |  |  |  | $osc_listener_port 				$config->{osc_listener_port} | 
| 104 |  |  |  |  |  |  | $osc_reply_port 				$config->{osc_reply_port} | 
| 105 |  |  |  |  |  |  | $remote_control_port 			$config->{remote_control_port} | 
| 106 |  |  |  |  |  |  | $engines						$config->{engines} | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | ) }; | 
| 109 | 0 |  |  | 0 | 0 | 0 | sub var_map {  $var_map } # to allow outside access while keeping | 
| 110 |  |  |  |  |  |  | # working lexical | 
| 111 | 0 |  |  | 0 | 0 | 0 | sub config_vars { grep {$_ ne '**' } keys %$var_map } | 
|  | 0 |  |  |  |  | 0 |  | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | sub assign { | 
| 114 |  |  |  |  |  |  | # Usage: | 
| 115 |  |  |  |  |  |  | # assign ( | 
| 116 |  |  |  |  |  |  | # data 	=> $ref, | 
| 117 |  |  |  |  |  |  | # vars 	=> \@vars, | 
| 118 |  |  |  |  |  |  | # var_map => 1, | 
| 119 |  |  |  |  |  |  | #	class => $class | 
| 120 |  |  |  |  |  |  | #	); | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 4 |  |  | 4 | 0 | 5396 | logsub("&assign"); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 4 |  |  |  |  | 39 | my %h = @_; # parameters appear in %h | 
| 125 | 4 |  |  |  |  | 5 | my $class; | 
| 126 | 4 | 50 |  |  |  | 15 | logpkg(__FILE__,__LINE__,'logcarp',"didn't expect scalar here") if ref $h{data} eq 'SCALAR'; | 
| 127 | 4 | 50 |  |  |  | 10 | logpkg(__FILE__,__LINE__,'logcarp',"didn't expect code here") if ref $h{data} eq 'CODE'; | 
| 128 |  |  |  |  |  |  | # print "data: $h{data}, ", ref $h{data}, $/; | 
| 129 |  |  |  |  |  |  |  | 
| 130 | 4 | 50 |  |  |  | 22 | if ( ref $h{data} !~ /^(HASH|ARRAY|CODE|GLOB|HANDLE|FORMAT)$/){ | 
| 131 |  |  |  |  |  |  | # we guess object | 
| 132 | 0 |  |  |  |  | 0 | $class = ref $h{data}; | 
| 133 | 0 |  |  |  |  | 0 | logpkg(__FILE__,__LINE__,'debug',"I found an object of class $class"); | 
| 134 |  |  |  |  |  |  | } | 
| 135 | 4 |  |  |  |  | 6 | $class = $h{class}; | 
| 136 | 4 | 100 |  |  |  | 14 | $class .= "::" unless $class =~ /::$/;  # SKIP_PREPROC | 
| 137 | 4 |  |  |  |  | 7 | my @vars = @{ $h{vars} }; | 
|  | 4 |  |  |  |  | 11 |  | 
| 138 | 4 |  |  |  |  | 8 | my $ref = $h{data}; | 
| 139 | 4 |  |  |  |  | 8 | my $type = ref $ref; | 
| 140 | 4 |  |  |  |  | 25 | logpkg(__FILE__,__LINE__,'debug',< | 
| 141 |  |  |  |  |  |  | data type: $type | 
| 142 |  |  |  |  |  |  | data: $ref | 
| 143 |  |  |  |  |  |  | class: $class | 
| 144 |  |  |  |  |  |  | vars: @vars | 
| 145 |  |  |  |  |  |  | ASSIGN | 
| 146 |  |  |  |  |  |  | #logpkg(__FILE__,__LINE__,'debug',sub{json_out($ref)}); | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | # index what sigil an identifier should get | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | # we need to create search-and-replace strings | 
| 151 |  |  |  |  |  |  | # sigil-less old_identifier | 
| 152 | 4 |  |  |  |  | 30 | my %sigil; | 
| 153 |  |  |  |  |  |  | my %ident; | 
| 154 |  |  |  |  |  |  | map { | 
| 155 | 4 |  |  |  |  | 8 | my $oldvar = my $var = $_; | 
|  | 16 |  |  |  |  | 24 |  | 
| 156 | 16 |  |  |  |  | 52 | my ($dummy, $old_identifier) = /^([\$\%\@])([\-\>\w:\[\]{}]+)$/; | 
| 157 | 16 | 0 | 33 |  |  | 39 | $var = $var_map->{$var} if $h{var_map} and $var_map->{$var}; | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 16 | 50 |  |  |  | 34 | logpkg(__FILE__,__LINE__,'debug',"oldvar: $oldvar, newvar: $var") unless $oldvar eq $var; | 
| 160 | 16 |  |  |  |  | 43 | my ($sigil, $identifier) = $var =~ /([\$\%\@])(\S+)/; | 
| 161 | 16 |  |  |  |  | 34 | $sigil{$old_identifier} = $sigil; | 
| 162 | 16 |  |  |  |  | 43 | $ident{$old_identifier} = $identifier; | 
| 163 |  |  |  |  |  |  | } @vars; | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 4 |  |  | 0 |  | 21 | logpkg(__FILE__,__LINE__,'debug',sub{"SIGIL\n". json_out(\%sigil)}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 166 |  |  |  |  |  |  | #%ident = map{ @$_ } grep{ $_->[0] ne $_->[1] } map{ [$_, $ident{$_}]  }  keys %ident; | 
| 167 | 4 |  |  |  |  | 49 | my %ident2 = %ident; | 
| 168 | 4 |  |  |  |  | 16 | while ( my ($k,$v) = each %ident2) | 
| 169 |  |  |  |  |  |  | { | 
| 170 | 16 | 50 |  |  |  | 68 | delete $ident2{$k} if $k eq $v | 
| 171 |  |  |  |  |  |  | } | 
| 172 | 4 |  |  | 0 |  | 19 | logpkg(__FILE__,__LINE__,'debug',sub{"IDENT\n". json_out(\%ident2)}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | #print join " ", "Variables:\n", @vars, $/ ; | 
| 175 | 4 | 50 |  |  |  | 44 | croak "expected hash" if ref $ref !~ /HASH/; | 
| 176 | 4 |  |  |  |  | 5 | my @keys =  keys %{ $ref }; # identifiers, *no* sigils | 
|  | 4 |  |  |  |  | 14 |  | 
| 177 | 4 |  |  | 0 |  | 19 | logpkg(__FILE__,__LINE__,'debug',sub{ join " ","found keys: ", keys %{ $ref },"\n---\n"}); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 178 |  |  |  |  |  |  | map{ | 
| 179 | 4 |  |  |  |  | 36 | my $eval; | 
|  | 16 |  |  |  |  | 19 |  | 
| 180 | 16 |  |  |  |  | 23 | my $key = $_; | 
| 181 | 16 |  |  |  |  | 50 | chomp $key; | 
| 182 | 16 |  |  |  |  | 29 | my $sigil = $sigil{$key}; | 
| 183 |  |  |  |  |  |  | my $full_class_path = | 
| 184 | 16 | 50 |  |  |  | 49 | $sigil . ($key =~/:\:/ ? '': $class) .  $ident{$key}; | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | # use the supplied class unless the variable name | 
| 187 |  |  |  |  |  |  | # contains \:\: | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 16 |  |  |  |  | 56 | logpkg(__FILE__,__LINE__,'debug',< | 
| 190 |  |  |  |  |  |  | key:             $key | 
| 191 |  |  |  |  |  |  | sigil:      $sigil | 
| 192 |  |  |  |  |  |  | full_class_path: $full_class_path | 
| 193 |  |  |  |  |  |  | DEBUG | 
| 194 | 16 | 50 |  |  |  | 125 | if ( ! $sigil ){ | 
| 195 |  |  |  |  |  |  | logpkg(__FILE__,__LINE__,'debug',sub{ | 
| 196 | 0 |  |  | 0 |  | 0 | "didn't find a match for $key in ", join " ", @vars, $/; | 
| 197 | 0 |  |  |  |  | 0 | }); | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  | else | 
| 200 |  |  |  |  |  |  | { | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 16 |  |  |  |  | 27 | $eval .= $full_class_path; | 
| 203 | 16 |  |  |  |  | 22 | $eval .= q( = ); | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 16 |  |  |  |  | 23 | my $val = $ref->{$key}; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 16 | 100 | 66 |  |  | 86 | if (! ref $val or ref $val eq 'SCALAR')  # scalar assignment | 
|  |  | 50 | 66 |  |  |  |  | 
| 208 |  |  |  |  |  |  | { | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | # extract value | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 8 | 100 |  |  |  | 17 | if ($val) { #  if we have something, | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | # dereference it if needed | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 7 | 50 |  |  |  | 15 | ref $val eq q(SCALAR) and $val = $$val; | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | # quoting for non-numerical | 
| 219 |  |  |  |  |  |  |  | 
| 220 | 7 | 100 |  |  |  | 30 | $val = qq("$val") unless  $val =~ /^[\d\.,+\-e]+$/ | 
| 221 |  |  |  |  |  |  |  | 
| 222 | 1 |  |  |  |  | 1 | } else { $val = q(undef) }; # or set as undefined | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 8 |  |  |  |  | 10 | $eval .=  $val;  # append to assignment | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  | elsif ( ref $val eq 'ARRAY' or ref $val eq 'HASH') | 
| 228 |  |  |  |  |  |  | { | 
| 229 | 8 | 50 |  |  |  | 17 | if ($sigil eq '$')	# assign reference | 
| 230 |  |  |  |  |  |  | { | 
| 231 | 0 |  |  |  |  | 0 | $eval .= q($val) ; | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  | else				# dereference and assign | 
| 234 |  |  |  |  |  |  | { | 
| 235 | 8 |  |  |  |  | 11 | $eval .= qq($sigil) ; | 
| 236 | 8 |  |  |  |  | 12 | $eval .= q({$val}) ; | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  | } | 
| 239 | 0 |  |  |  |  | 0 | else { die "unsupported assignment: ".ref $val } | 
| 240 | 16 |  |  |  |  | 49 | logpkg(__FILE__,__LINE__,'debug',"eval string: $eval"); | 
| 241 | 16 |  |  |  |  | 1080 | eval($eval); | 
| 242 | 16 | 50 |  |  |  | 100 | logpkg(__FILE__,__LINE__,'logcarp',"failed to eval $eval: $@") if $@; | 
| 243 |  |  |  |  |  |  | }  # end if sigil{key} | 
| 244 |  |  |  |  |  |  | } @keys; | 
| 245 | 4 |  |  |  |  | 22 | 1; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | # assign_singletons() assigns hash key/value entries | 
| 250 |  |  |  |  |  |  | # rather than a top-level hash reference to avoid | 
| 251 |  |  |  |  |  |  | # clobbering singleton key/value pairs initialized | 
| 252 |  |  |  |  |  |  | # elsewhere. | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | my @singleton_idents = map{ /^.(.+)/; $1 }  # remove leading '$' sigil | 
| 255 |  |  |  |  |  |  | qw( | 
| 256 |  |  |  |  |  |  | $ui | 
| 257 |  |  |  |  |  |  | $mode | 
| 258 |  |  |  |  |  |  | $file | 
| 259 |  |  |  |  |  |  | $graph | 
| 260 |  |  |  |  |  |  | $setup | 
| 261 |  |  |  |  |  |  | $config | 
| 262 |  |  |  |  |  |  | $jack | 
| 263 |  |  |  |  |  |  | $fx | 
| 264 |  |  |  |  |  |  | $fx_cache | 
| 265 |  |  |  |  |  |  | $text | 
| 266 |  |  |  |  |  |  | $gui | 
| 267 |  |  |  |  |  |  | $midi | 
| 268 |  |  |  |  |  |  | $help | 
| 269 |  |  |  |  |  |  | $mastering | 
| 270 |  |  |  |  |  |  | $project | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | ); | 
| 273 |  |  |  |  |  |  | sub assign_singletons { | 
| 274 | 0 |  |  | 0 | 0 | 0 | logsub('&assign_singletons'); | 
| 275 | 0 |  |  |  |  | 0 | my $ref = shift; | 
| 276 | 0 | 0 |  |  |  | 0 | my $data = $ref->{data} or die "expected data got undefined"; | 
| 277 | 0 |  | 0 |  |  | 0 | my $class = $ref->{class} // 'Audio::Nama'; | 
| 278 | 0 |  |  |  |  | 0 | $class .= '::'; # SKIP_PREPROC | 
| 279 |  |  |  |  |  |  | map { | 
| 280 | 0 |  |  |  |  | 0 | my $ident = $_; | 
|  | 0 |  |  |  |  | 0 |  | 
| 281 | 0 | 0 |  |  |  | 0 | if( defined $data->{$ident}){ | 
| 282 | 0 |  |  |  |  | 0 | my $type = ref $data->{$ident}; | 
| 283 | 0 | 0 |  |  |  | 0 | $type eq 'HASH' or die "$ident: expect hash, got $type"; | 
| 284 |  |  |  |  |  |  | map{ | 
| 285 | 0 |  |  |  |  | 0 | my $key = $_; | 
| 286 | 0 |  |  |  |  | 0 | my $cmd = join '', | 
| 287 |  |  |  |  |  |  | '$', | 
| 288 |  |  |  |  |  |  | $class, | 
| 289 |  |  |  |  |  |  | $ident, | 
| 290 |  |  |  |  |  |  | '->{', | 
| 291 |  |  |  |  |  |  | $key, | 
| 292 |  |  |  |  |  |  | '}', | 
| 293 |  |  |  |  |  |  | ' = $data->{$ident}->{$key}'; | 
| 294 | 0 |  |  |  |  | 0 | logpkg(__FILE__,__LINE__,'debug',"eval: $cmd"); | 
| 295 | 0 |  |  |  |  | 0 | eval $cmd; | 
| 296 | 0 | 0 |  |  |  | 0 | logpkg(__FILE__,__LINE__,'logcarp',"error during eval: $@") if $@; | 
| 297 | 0 |  |  |  |  | 0 | } keys %{ $data->{$ident} } | 
|  | 0 |  |  |  |  | 0 |  | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  | } @singleton_idents;  # list of "singleton" variables | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | our %suffix = | 
| 303 |  |  |  |  |  |  | ( | 
| 304 |  |  |  |  |  |  | storable => "bin", | 
| 305 |  |  |  |  |  |  | perl	 => "pl", | 
| 306 |  |  |  |  |  |  | json	 => "json", | 
| 307 |  |  |  |  |  |  | yaml	 => "yml", | 
| 308 |  |  |  |  |  |  | ); | 
| 309 |  |  |  |  |  |  | our %dispatch = | 
| 310 |  |  |  |  |  |  | ( storable => sub { my($ref, $path) = @_; nstore($ref, $path) }, | 
| 311 |  |  |  |  |  |  | perl     => sub { my($ref, $path) = @_; write_file($path, Dumper $ref) }, | 
| 312 |  |  |  |  |  |  | yaml	   => sub { my($ref, $path) = @_; write_file($path, json_out($ref))}, | 
| 313 |  |  |  |  |  |  | json	   => sub { my($ref, $path) = @_; write_file($path, json_out($ref))}, | 
| 314 |  |  |  |  |  |  | ); | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | sub serialize_and_write { | 
| 317 | 0 |  |  | 0 | 0 | 0 | my ($ref, $path, $format) = @_; | 
| 318 | 0 | 0 |  |  |  | 0 | $path .= ".$suffix{$format}" unless $path =~ /\.$suffix{$format}$/; | 
| 319 | 0 |  |  |  |  | 0 | $dispatch{$format}->($ref, $path) | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | { | 
| 324 |  |  |  |  |  |  | my $parse_re =  		# initialize only once | 
| 325 |  |  |  |  |  |  | qr/ ^ 			# beginning anchor | 
| 326 |  |  |  |  |  |  | ([\%\@\$]) 		# first character, sigil | 
| 327 |  |  |  |  |  |  | ([\w:]+)		# identifier, possibly perl namespace | 
| 328 |  |  |  |  |  |  | (?:->\{(\w+)})?  # optional hash key for new hash-singleton vars | 
| 329 |  |  |  |  |  |  | $ 				# end anchor | 
| 330 |  |  |  |  |  |  | /x; | 
| 331 |  |  |  |  |  |  | sub serialize { | 
| 332 | 3 |  |  | 3 | 0 | 16 | logsub("&serialize"); | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 3 |  |  |  |  | 27 | my %h = @_; | 
| 335 | 3 |  |  |  |  | 5 | my @vars = @{ $h{vars} }; | 
|  | 3 |  |  |  |  | 8 |  | 
| 336 | 3 |  |  |  |  | 8 | my $class = $h{class}; | 
| 337 | 3 |  |  |  |  | 4 | my $file  = $h{file}; | 
| 338 | 3 |  | 50 |  |  | 14 | my $format = $h{format} // 'perl'; # default to Data::Dumper::Concise | 
| 339 |  |  |  |  |  |  |  | 
| 340 | 3 |  | 50 |  |  | 9 | $class //= "Audio::Nama"; | 
| 341 | 3 | 100 |  |  |  | 12 | $class =~ /::$/ or $class .= '::'; # SKIP_PREPROC | 
| 342 | 3 |  |  |  |  | 17 | logpkg(__FILE__,__LINE__,'debug',"file: $file, class: $class\nvariables...@vars"); | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | # first we marshall data into %state | 
| 345 |  |  |  |  |  |  |  | 
| 346 | 3 |  |  |  |  | 22 | my %state; | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | map{ | 
| 349 | 3 |  |  |  |  | 7 | my ($sigil, $identifier, $key) = /$parse_re/; | 
|  | 12 |  |  |  |  | 97 |  | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 12 |  |  |  |  | 51 | logpkg(__FILE__,__LINE__,'debug',"found sigil: $sigil, ident: $identifier, key: $key"); | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | # note: for  YAML::Reader/Writer  all scalars must contain values, not references | 
| 354 |  |  |  |  |  |  | # more YAML adjustments | 
| 355 |  |  |  |  |  |  | # restore will break if a null field is not converted to '~' | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | #my $value =  q(\\) | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | # directly assign scalar, but take hash/array references | 
| 360 |  |  |  |  |  |  | # $state{ident} = $scalar | 
| 361 |  |  |  |  |  |  | # $state{ident} = \%hash | 
| 362 |  |  |  |  |  |  | # $state{ident} = \@array | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | # in case $key is provided | 
| 365 |  |  |  |  |  |  | # $state{ident}->{$key} = $singleton->{$key}; | 
| 366 |  |  |  |  |  |  | # | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 12 | 100 |  |  |  | 126 | my $value =  ($sigil ne q($) ? q(\\) : q() ) | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | . $sigil | 
| 372 |  |  |  |  |  |  | . ($identifier =~ /:/ ? '' : $class) | 
| 373 |  |  |  |  |  |  | . $identifier | 
| 374 |  |  |  |  |  |  | . ($key ? qq(->{$key}) : q()); | 
| 375 |  |  |  |  |  |  |  | 
| 376 | 12 |  |  |  |  | 36 | logpkg(__FILE__,__LINE__,'debug',"value: $value"); | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 12 | 50 |  |  |  | 101 | my $eval_string =  q($state{') | 
| 380 |  |  |  |  |  |  | . $identifier | 
| 381 |  |  |  |  |  |  | . q('}) | 
| 382 |  |  |  |  |  |  | . ($key ? qq(->{$key}) : q() ) | 
| 383 |  |  |  |  |  |  | . q( = ) | 
| 384 |  |  |  |  |  |  | . $value; | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 12 | 50 |  |  |  | 34 | if ($identifier){ | 
| 387 | 12 |  |  |  |  | 39 | logpkg(__FILE__,__LINE__,'debug',"attempting to eval $eval_string"); | 
| 388 | 12 |  |  |  |  | 757 | eval($eval_string); | 
| 389 | 12 | 50 |  |  |  | 92 | logpkg(__FILE__,__LINE__,'error', "eval failed ($@)") if $@; | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  | } @vars; | 
| 392 | 3 |  |  | 0 |  | 16 | logpkg(__FILE__,__LINE__,'debug',sub{join $/,'\%state', Dumper \%state}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | # YAML out for screen dumps | 
| 395 | 3 | 50 |  |  |  | 37 | return( json_out(\%state) ) unless $h{file}; | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | # now we serialize %state | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 0 |  |  |  |  | 0 | my $path = $h{file}; | 
| 400 |  |  |  |  |  |  |  | 
| 401 | 0 |  |  |  |  | 0 | serialize_and_write(\%state, $path, $format); | 
| 402 |  |  |  |  |  |  | } | 
| 403 |  |  |  |  |  |  | } | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | sub json_out { | 
| 406 | 3 |  |  | 3 | 0 | 8 | logsub("&json_out"); | 
| 407 | 3 |  |  |  |  | 20 | my $data_ref = shift; | 
| 408 | 3 |  |  |  |  | 7 | my $type = ref $data_ref; | 
| 409 | 3 | 50 |  |  |  | 16 | croak "attempting to code wrong data type: $type" | 
| 410 |  |  |  |  |  |  | if $type !~ /HASH|ARRAY/; | 
| 411 | 3 |  |  |  |  | 49 | $to_json->encode($data_ref); | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | sub json_in { | 
| 415 | 0 |  |  | 0 | 0 |  | logsub("&json_in"); | 
| 416 | 0 |  |  |  |  |  | my $json = shift; | 
| 417 | 0 |  |  |  |  |  | my $data_ref = decode_json($json); | 
| 418 | 0 |  |  |  |  |  | $data_ref | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | sub yaml_in { | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | # logsub("&yaml_in"); | 
| 424 | 0 |  |  | 0 | 0 |  | my $input = shift; | 
| 425 |  |  |  |  |  |  | my $yaml = $input =~ /\n/ # check whether file or text | 
| 426 |  |  |  |  |  |  | ? $input 			# yaml text | 
| 427 |  |  |  |  |  |  | : do | 
| 428 | 0 | 0 |  |  |  |  | { | 
| 429 | 0 |  |  |  |  |  | logpkg(__FILE__,__LINE__,'debug',"filename: $input"); | 
| 430 | 0 |  |  |  |  |  | read_file($input);	# file name | 
| 431 |  |  |  |  |  |  | }; | 
| 432 | 0 | 0 |  |  |  |  | if ($yaml =~ /\t/){ | 
| 433 | 0 |  |  |  |  |  | croak "YAML file: $input contains illegal TAB character."; | 
| 434 |  |  |  |  |  |  | } | 
| 435 | 0 |  |  |  |  |  | $yaml =~ s/^\n+//  ; # remove leading newline at start of file | 
| 436 | 0 |  |  |  |  |  | $yaml =~ s/\n*$/\n/; # make sure file ends with newline | 
| 437 | 0 |  |  |  |  |  | my $y = YAML::Tiny->read_string($yaml); | 
| 438 | 0 | 0 |  |  |  |  | Audio::Nama::throw("YAML::Tiny read error: $YAML::Tiny::errstr\n") if $YAML::Tiny::errstr; | 
| 439 | 0 |  |  |  |  |  | $y->[0]; | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | sub quote_yaml_scalars { | 
| 443 | 0 |  |  | 0 | 0 |  | my $yaml = shift; | 
| 444 | 0 |  |  |  |  |  | my @modified; | 
| 445 |  |  |  |  |  |  | map | 
| 446 |  |  |  |  |  |  | { | 
| 447 | 0 |  |  |  |  |  | chomp; | 
|  | 0 |  |  |  |  |  |  | 
| 448 | 0 | 0 |  |  |  |  | if( /^(?(\s*\w+: )|(\s+- ))(?.+)$/ ){ | 
| 449 | 5 |  |  | 5 |  | 4132 | my($beg,$end) = ($+{beg}, $+{end}); | 
|  | 5 |  |  |  |  | 2031 |  | 
|  | 5 |  |  |  |  | 1265 |  | 
|  | 0 |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | # quote if contains colon and not quoted | 
| 451 | 0 | 0 | 0 |  |  |  | if ($end =~ /:\s/ and $end !~ /^('|")/ ){ | 
| 452 | 0 |  |  |  |  |  | $end =~ s(')(\\')g; # escape existing single quotes | 
| 453 | 0 |  |  |  |  |  | $end = qq('$end') } # single-quote string | 
| 454 | 0 |  |  |  |  |  | push @modified, "$beg$end\n"; | 
| 455 |  |  |  |  |  |  | } | 
| 456 | 0 |  |  |  |  |  | else { push @modified, "$_\n" } | 
| 457 |  |  |  |  |  |  | } split "\n", $yaml; | 
| 458 | 0 |  |  |  |  |  | join "", @modified; | 
| 459 |  |  |  |  |  |  | } | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | 1; |