| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Audio::Nama; | 
| 2 |  |  |  |  |  |  | our (%tn, $jack, $config); | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | # ---------- IO ----------- | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | # IO objects for writing Ecasound chain setup file | 
| 8 |  |  |  |  |  |  | # | 
| 9 |  |  |  |  |  |  | # Object values can come from three sources: | 
| 10 |  |  |  |  |  |  | # | 
| 11 |  |  |  |  |  |  | # 1. As arguments to the constructor new() while walking the | 
| 12 |  |  |  |  |  |  | #    routing graph: | 
| 13 |  |  |  |  |  |  | #      + assigned by dispatch: chain_id, loop_id, track, etc. | 
| 14 |  |  |  |  |  |  | #      + override by graph node (higher priority) | 
| 15 |  |  |  |  |  |  | #      + override by graph edge (highest priority) | 
| 16 |  |  |  |  |  |  | # 2. (sub)class methods called as $object->method_name | 
| 17 |  |  |  |  |  |  | #      + defined as _method_name (access via AUTOLOAD, overrideable by constructor) | 
| 18 |  |  |  |  |  |  | #      + defined as method_name  (not overrideable) | 
| 19 |  |  |  |  |  |  | # 3. AUTOLOAD | 
| 20 |  |  |  |  |  |  | #      + any other method calls are passed to the the associated track | 
| 21 |  |  |  |  |  |  | #      + illegal track method call generate an exception | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | package Audio::Nama::IO; | 
| 24 | 2 |  |  | 2 |  | 21581 | use Modern::Perl; use Carp; | 
|  | 2 |  |  | 2 |  | 12956 |  | 
|  | 2 |  |  |  |  | 13 |  | 
|  | 2 |  |  |  |  | 255 |  | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 121 |  | 
| 25 | 2 |  |  | 2 |  | 848 | use Data::Dumper::Concise; | 
|  | 2 |  |  |  |  | 12319 |  | 
|  | 2 |  |  |  |  | 234 |  | 
| 26 |  |  |  |  |  |  | our $VERSION = 1.0; | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | # provide following vars to all packages | 
| 29 |  |  |  |  |  |  | our ($config, $jack, %tn); | 
| 30 |  |  |  |  |  |  | our (%by_name); # index for $by_name{trackname}->{input} = $object | 
| 31 | 2 |  |  | 2 |  | 525 | use Audio::Nama::Globals qw($config $jack %tn $setup :trackrw); | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 367 |  | 
| 32 | 2 |  |  | 2 |  | 797 | use Try::Tiny; | 
|  | 2 |  |  |  |  | 1542 |  | 
|  | 2 |  |  |  |  | 298 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 0 |  |  | 0 | 0 |  | sub initialize { %by_name = () } | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | # we will use the following to map from graph node names | 
| 37 |  |  |  |  |  |  | # to IO class names | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | our %io_class = qw( | 
| 40 |  |  |  |  |  |  | null_in					Audio::Nama::IO::from_null | 
| 41 |  |  |  |  |  |  | null_out				Audio::Nama::IO::to_null | 
| 42 |  |  |  |  |  |  | soundcard_in 			Audio::Nama::IO::from_soundcard | 
| 43 |  |  |  |  |  |  | soundcard_out 			Audio::Nama::IO::to_soundcard | 
| 44 |  |  |  |  |  |  | soundcard_device_in 	Audio::Nama::IO::from_alsa_soundcard_device | 
| 45 |  |  |  |  |  |  | soundcard_device_out 	Audio::Nama::IO::to_alsa_soundcard_device | 
| 46 |  |  |  |  |  |  | wav_in 					Audio::Nama::IO::from_wav | 
| 47 |  |  |  |  |  |  | wav_out 				Audio::Nama::IO::to_wav | 
| 48 |  |  |  |  |  |  | loop_source				Audio::Nama::IO::from_loop | 
| 49 |  |  |  |  |  |  | loop_sink				Audio::Nama::IO::to_loop | 
| 50 |  |  |  |  |  |  | jack_manual_in			Audio::Nama::IO::from_jack_port | 
| 51 |  |  |  |  |  |  | jack_manual_out			Audio::Nama::IO::to_jack_port | 
| 52 |  |  |  |  |  |  | jack_ports_list_in		Audio::Nama::IO::from_jack_port | 
| 53 |  |  |  |  |  |  | jack_ports_list_out		Audio::Nama::IO::to_jack_port | 
| 54 |  |  |  |  |  |  | jack_multi_in			Audio::Nama::IO::from_jack_multi | 
| 55 |  |  |  |  |  |  | jack_multi_out			Audio::Nama::IO::to_jack_multi | 
| 56 |  |  |  |  |  |  | jack_client_in			Audio::Nama::IO::from_jack_client | 
| 57 |  |  |  |  |  |  | jack_client_out			Audio::Nama::IO::to_jack_client | 
| 58 |  |  |  |  |  |  | ); | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | ### class descriptions | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | # === CLASS Audio::Nama::IO::from_jack_port === | 
| 63 |  |  |  |  |  |  | # | 
| 64 |  |  |  |  |  |  | # is triggered by source_type codes: | 
| 65 |  |  |  |  |  |  | # | 
| 66 |  |  |  |  |  |  | #  + jack_manual_in | 
| 67 |  |  |  |  |  |  | #  + jack_ports_list_in | 
| 68 |  |  |  |  |  |  | # | 
| 69 |  |  |  |  |  |  | # For track 'piano', the class creates an input similar to: | 
| 70 |  |  |  |  |  |  | # | 
| 71 |  |  |  |  |  |  | # -i:jack,,piano_in | 
| 72 |  |  |  |  |  |  | # | 
| 73 |  |  |  |  |  |  | # which receives input from JACK node: | 
| 74 |  |  |  |  |  |  | # | 
| 75 |  |  |  |  |  |  | #  + Nama:piano_in, | 
| 76 |  |  |  |  |  |  | # | 
| 77 |  |  |  |  |  |  | # If piano is stereo, the actual ports will be: | 
| 78 |  |  |  |  |  |  | # | 
| 79 |  |  |  |  |  |  | #  + Nama:piano_in_1 | 
| 80 |  |  |  |  |  |  | #  + Nama:piano_in_2 | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # (CLASS Audio::Nama::IO::to_jack_port is similar) | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | ### class definition | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | our $AUTOLOAD; | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | # add underscore to field names so that regular method | 
| 89 |  |  |  |  |  |  | # access will go through AUTOLOAD | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | # we add an underscore to each key | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 2 |  |  | 2 |  | 514 | use Audio::Nama::Object qw(track_ chain_id_ endpoint_ format_ format_template_ width_ ecs_extra_ direction_ device_id_); | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 14 |  | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | sub new { | 
| 96 | 0 |  |  | 0 | 0 |  | my $class = shift; | 
| 97 | 0 |  |  |  |  |  | my %vals = @_; | 
| 98 | 0 |  |  |  |  |  | my @args = map{$_."_", $vals{$_}} keys %vals; # add underscore to key | 
|  | 0 |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | # note that we won't check for illegal fields | 
| 101 |  |  |  |  |  |  | # so we can pass any value and allow AUTOLOAD to | 
| 102 |  |  |  |  |  |  | # check the hash for it. | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 0 |  |  |  |  |  | my $self = bless {@args}, $class; | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 0 |  |  |  |  |  | my $direction = $self->direction; # input or output | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | # join IO objects to graph | 
| 109 | 0 |  |  |  |  |  | my $name; | 
| 110 | 0 |  |  | 0 |  |  | try{ $name  = $self->name } | 
| 111 | 0 |  |  | 0 |  |  | catch {};  # we do nothing | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 2 |  |  | 2 |  | 10 | { no warnings 'uninitialized'; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 1742 |  | 
|  | 0 |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | Audio::Nama::logpkg(__FILE__,__LINE__,'debug',"I belong to track $name\n", | 
| 115 | 0 |  |  | 0 |  |  | sub{Dumper($self)} ); | 
|  | 0 |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 0 | 0 |  |  |  |  | if($name){ | 
| 119 | 0 |  |  |  |  |  | $by_name{$name}->{$direction} = $self; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | $self | 
| 122 | 0 |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | # latency stubs | 
| 125 |  |  |  |  |  |  | sub capture_latency { | 
| 126 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 127 | 0 | 0 |  |  |  |  | return unless $self->client; | 
| 128 | 0 |  |  |  |  |  | Audio::Nama::jack_port_latency('input', $self->client); | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  | sub playback_latency { | 
| 131 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 132 | 0 | 0 |  |  |  |  | return unless $self->client; | 
| 133 | 0 |  |  |  |  |  | Audio::Nama::jack_port_latency('output', $self->client); | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | # we need at least stubs for subclasses' methods | 
| 137 |  |  |  |  |  |  | # for AUTOLOAD to be happy - so we include | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  | 0 | 0 |  | sub client {} | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | #### JACK related methods | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | # inherited by all, the methods defined below are called in | 
| 144 |  |  |  |  |  |  | # these classes: | 
| 145 |  |  |  |  |  |  | # | 
| 146 |  |  |  |  |  |  | #		to_jack_multi, | 
| 147 |  |  |  |  |  |  | #		from_jack_multi | 
| 148 |  |  |  |  |  |  | #		to_jack_client | 
| 149 |  |  |  |  |  |  | #		from_jack_client | 
| 150 |  |  |  |  |  |  | # | 
| 151 |  |  |  |  |  |  | # They have no function in other classes. | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | sub target_id { | 
| 155 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 156 | 0 | 0 |  |  |  |  | $self->direction eq 'input' | 
| 157 |  |  |  |  |  |  | ? $self->source_id | 
| 158 |  |  |  |  |  |  | : $self->send_id; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  | sub target_type { | 
| 161 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 162 | 0 | 0 |  |  |  |  | $self->direction eq 'input' | 
| 163 |  |  |  |  |  |  | ? $self->source_type | 
| 164 |  |  |  |  |  |  | : $self->send_type; | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  | sub target_channel { | 
| 167 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 168 | 0 | 0 |  |  |  |  | $self->target_id =~ /^(\d+)$/ ? $1 : 1 | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | sub ports { | 
| 171 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 172 | 0 | 0 |  |  |  |  | my $client_direction = $self->direction eq 'input' ? 'output' : 'input'; | 
| 173 |  |  |  |  |  |  | Audio::Nama::IO::jack_multi_ports( $self->client, | 
| 174 |  |  |  |  |  |  | $client_direction, | 
| 175 |  |  |  |  |  |  | $self->target_channel, | 
| 176 |  |  |  |  |  |  | $self->width, | 
| 177 | 0 |  |  | 0 |  |  | Audio::Nama::try{$self->name} | 
| 178 | 0 | 0 |  |  |  |  | ) if $self->client | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | sub ecs_string { | 
| 184 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 185 | 0 |  |  |  |  |  | my @parts; | 
| 186 | 0 | 0 |  |  |  |  | push @parts, '-f:'.$self->format if $self->format; | 
| 187 | 0 |  |  |  |  |  | push @parts, '-'.$self->io_prefix.':'.$self->device_id; | 
| 188 | 0 |  |  |  |  |  | join ' ',@parts; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | ## the format() method generates the correct Ecasound format string, | 
| 192 |  |  |  |  |  |  | ## (e.g. -f:f32_le,2,48000) if the _format_template() method | 
| 193 |  |  |  |  |  |  | ## returns a signal format template (e.g. f32_le,N,48000) | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | sub _format { | 
| 196 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 197 | 0 | 0 | 0 |  |  |  | Audio::Nama::signal_format($self->format_template, $self->width) | 
| 198 |  |  |  |  |  |  | if $self->format_template and $self->width | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  | 0 |  |  | sub _format_template {} # the leading underscore allows override | 
| 201 |  |  |  |  |  |  | # by a method without the underscore | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  | 0 |  |  | sub _ecs_extra {}		# allow override | 
| 204 |  |  |  |  |  |  | sub direction { | 
| 205 | 0 | 0 |  | 0 | 0 |  | (ref $_[0]) =~ /::from/ ? 'input' : 'output' | 
| 206 |  |  |  |  |  |  | } | 
| 207 | 0 |  |  | 0 | 0 |  | sub io_prefix { substr $_[0]->direction, 0, 1 } # 'i' or 'o' | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | sub AUTOLOAD { | 
| 210 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 211 |  |  |  |  |  |  | # get tail of method call | 
| 212 | 0 |  |  |  |  |  | my ($call) = $AUTOLOAD =~ /([^:]+)$/; | 
| 213 | 0 |  |  |  |  |  | my $result = q(); | 
| 214 | 0 |  |  |  |  |  | my $field = "$call\_"; | 
| 215 | 0 |  |  |  |  |  | my $method = "_$call"; | 
| 216 | 0 | 0 |  |  |  |  | return $self->{$field} if exists $self->{$field}; | 
| 217 | 0 | 0 |  |  |  |  | return $self->$method if $self->can($method); | 
| 218 | 2 |  |  | 2 |  | 15 | { no warnings 'uninitialized'; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 927 |  | 
|  | 0 |  |  |  |  |  |  | 
| 219 | 0 | 0 |  |  |  |  | if ( my $track = $tn{$self->{track_}} ){ | 
| 220 | 0 | 0 |  |  |  |  | return $track->$call if $track->can($call) | 
| 221 |  |  |  |  |  |  | # ->can is reliable here because Track has no AUTOLOAD | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | # suppress error XXX | 
| 224 | 0 | 0 | 0 |  |  |  | return undef if $call eq 'name' or $call eq 'surname'; | 
| 225 |  |  |  |  |  |  | } | 
| 226 | 0 |  |  |  |  |  | my $msg = "Autoload fell through. Object type: ". (ref $self). " illegal method call: $call\n"; | 
| 227 | 0 |  |  |  |  |  | Audio::Nama::throw($msg,$self->dump); | 
| 228 | 0 |  |  |  |  |  | croak | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  | 0 |  |  | sub DESTROY {} | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | sub _mono_to_stereo{ | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | # copy mono track to stereo if we have a pan control and a mono source | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | # Truth table | 
| 238 |  |  |  |  |  |  | #REC status, Track width stereo: null | 
| 239 |  |  |  |  |  |  | #REC status, Track width mono:   chcopy | 
| 240 |  |  |  |  |  |  | #PLAY status, WAV width mono:   chcopy | 
| 241 |  |  |  |  |  |  | #PLAY status, WAV width stereo: null | 
| 242 |  |  |  |  |  |  | #Higher channel count (WAV or Track): null | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 0 |  |  | 0 |  |  | my $self   = shift; | 
| 245 | 0 |  |  |  |  |  | my $status = $self->rec_status(); | 
| 246 | 0 |  |  |  |  |  | my $copy   = "-chcopy:1,2"; | 
| 247 | 0 |  |  |  |  |  | my $nocopy = ""; | 
| 248 | 0 |  |  | 0 |  |  | my $is_mono_track = sub { $self->width == 1 }; | 
|  | 0 |  |  |  |  |  |  | 
| 249 | 0 |  |  | 0 |  |  | my $is_mono_wav   = sub { Audio::Nama::channels($self->wav_format) == 1}; | 
|  | 0 |  |  |  |  |  |  | 
| 250 | 0 | 0 | 0 |  |  |  | if  ( | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 251 |  |  |  |  |  |  | ($self->track and $tn{$self->track}->pan) | 
| 252 |  |  |  |  |  |  | and | 
| 253 |  |  |  |  |  |  | (	$status =~ /REC|MON/ and $is_mono_track->() | 
| 254 |  |  |  |  |  |  | or $status eq PLAY and $is_mono_wav->() ) | 
| 255 |  |  |  |  |  |  | ) | 
| 256 | 0 |  |  |  |  |  | { $copy } else { $nocopy } | 
|  | 0 |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  | sub _playat_output { | 
| 259 | 0 |  |  | 0 |  |  | my $track = shift; | 
| 260 | 0 | 0 |  |  |  |  | return unless $track->shifted_playat_time; | 
| 261 |  |  |  |  |  |  | # or $track->latency_offset; | 
| 262 | 0 |  |  |  |  |  | join ',',"playat" , $track->shifted_playat_time | 
| 263 |  |  |  |  |  |  | # + $track->latency_offset | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  | sub _select_output { | 
| 266 | 0 |  |  | 0 |  |  | my $track = shift; | 
| 267 | 2 |  |  | 2 |  | 10 | no warnings 'uninitialized'; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 942 |  | 
| 268 | 0 |  |  |  |  |  | my $start = $track->shifted_region_start_time + $config->hardware_latency(); | 
| 269 | 0 |  |  |  |  |  | my $end   = $track->shifted_region_end_time; | 
| 270 | 0 | 0 | 0 |  |  |  | return unless $config->hardware_latency() or defined $start and defined $end; | 
|  |  |  | 0 |  |  |  |  | 
| 271 | 0 |  |  |  |  |  | my $setup_length; | 
| 272 |  |  |  |  |  |  | # CASE 1: a region is defined | 
| 273 | 0 | 0 |  |  |  |  | if ($end) { | 
| 274 | 0 |  |  |  |  |  | $setup_length = $end - $start; | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  | # CASE 2: only hardware latency | 
| 277 |  |  |  |  |  |  | else { | 
| 278 | 0 |  |  |  |  |  | $setup_length = $track->wav_length - $start | 
| 279 |  |  |  |  |  |  | } | 
| 280 | 0 |  |  |  |  |  | join ',',"select", $start, $setup_length | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  | ###  utility subroutines | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | sub get_class { | 
| 285 | 0 |  |  | 0 | 0 |  | my ($type,$direction) = @_; | 
| 286 |  |  |  |  |  |  | Audio::Nama::Graph::is_a_loop($type) and | 
| 287 | 0 | 0 |  |  |  |  | return $io_class{ $direction eq 'input' ?  "loop_source" : "loop_sink"}; | 
|  |  | 0 |  |  |  |  |  | 
| 288 | 0 | 0 |  |  |  |  | $io_class{$type} or croak "unrecognized IO type: $type" | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  | sub soundcard_input_type_string { | 
| 291 | 0 | 0 |  | 0 | 0 |  | $jack->{jackd_running} ? 'jack_multi_in' : 'soundcard_device_in' | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  | sub soundcard_output_type_string { | 
| 294 | 0 | 0 |  | 0 | 0 |  | $jack->{jackd_running} ? 'jack_multi_out' : 'soundcard_device_out' | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  | sub soundcard_input_device_string { | 
| 297 |  |  |  |  |  |  | $jack->{jackd_running} ? 'system' : $config->{alsa_capture_device} | 
| 298 | 0 | 0 |  | 0 | 0 |  | } | 
| 299 |  |  |  |  |  |  | sub soundcard_output_device_string { | 
| 300 |  |  |  |  |  |  | $jack->{jackd_running} ? 'system' : $config->{alsa_playback_device} | 
| 301 | 0 | 0 |  | 0 | 0 |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | sub jack_multi_route { | 
| 304 | 0 |  |  | 0 | 0 |  | my (@ports)  = @_; | 
| 305 |  |  |  |  |  |  | join q(,),q(jack_multi), | 
| 306 | 0 |  |  |  |  |  | map{quote_jack_port($_)} @ports | 
|  | 0 |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | sub jack_multi_ports { | 
| 310 | 0 |  |  | 0 | 0 |  | my ($client, $direction, $start, $width, $trackname)  = @_; | 
| 311 | 2 |  |  | 2 |  | 10 | no warnings 'uninitialized'; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 737 |  | 
| 312 | 0 |  |  |  |  |  | Audio::Nama::logpkg(__FILE__,__LINE__,'debug',"trackname: $trackname, client $client, direction $direction, start: $start, width $width"); | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | # can we route to these channels? | 
| 315 | 0 |  |  |  |  |  | my $end   = $start + $width - 1; | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | # the following logic avoids deferencing undef for a | 
| 318 |  |  |  |  |  |  | # non-existent client, and correctly handles | 
| 319 |  |  |  |  |  |  | # the case of a portname (containing colon) | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 0 |  |  |  |  |  | my $channel_count = scalar @{$jack->{clients}->{$client}{$direction}}; | 
|  | 0 |  |  |  |  |  |  | 
| 322 | 0 | 0 |  |  |  |  | my $source_or_send = $direction eq 'input' ? 'send' : 'source'; | 
| 323 |  |  |  |  |  |  | die(qq( | 
| 324 |  |  |  |  |  |  | Track $trackname: $source_or_send would cover channels $start - $end, | 
| 325 |  |  |  |  |  |  | out of bounds for JACK client "$client" ($channel_count channels max). | 
| 326 |  |  |  |  |  |  | Change $source_or_send setting, or set track OFF.)) | 
| 327 | 0 | 0 | 0 |  |  |  | if $end > $channel_count and $config->{enforce_channel_bounds}; | 
| 328 | 0 |  |  |  |  |  | return @{$jack->{clients}->{$client}{$direction}}[$start-1..$end-1] | 
| 329 | 0 | 0 |  |  |  |  | if $jack->{clients}->{$client}{$direction}; | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | } | 
| 332 |  |  |  |  |  |  | #sub one_port { $jack->{clients}->{$client}->{$direction}->[$start-1] } | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | sub quote_jack_port { | 
| 335 | 0 |  |  | 0 | 0 |  | my $port = shift; | 
| 336 | 0 | 0 | 0 |  |  |  | ($port =~ /\s/ and $port !~ /^"/) ? qq("$port") : $port | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  | sub rectified { # client name from number | 
| 339 | 0 | 0 |  | 0 | 0 |  | $_[0] =~ /^\d+$/ | 
| 340 |  |  |  |  |  |  | ? 'system' | 
| 341 |  |  |  |  |  |  | : $_[0] | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  | ### subclass definitions | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | ### method names with a preceding underscore | 
| 346 |  |  |  |  |  |  | ### can be overridded by the object constructor | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | { | 
| 349 |  |  |  |  |  |  | package Audio::Nama::IO::from_null; | 
| 350 | 2 |  |  | 2 |  | 16 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 3 |  | 
|  | 2 |  |  |  |  | 13 |  | 
|  | 2 |  |  |  |  | 175 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 179 |  | 
| 351 | 0 |  |  | 0 |  |  | sub _device_id { 'null' } | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | { | 
| 355 |  |  |  |  |  |  | package Audio::Nama::IO::to_null; | 
| 356 | 2 |  |  | 2 |  | 11 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 3 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 128 |  | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 184 |  | 
| 357 | 0 |  |  | 0 |  |  | sub _device_id { 'null' } | 
| 358 |  |  |  |  |  |  | } | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | { | 
| 361 |  |  |  |  |  |  | package Audio::Nama::IO::from_rtnull; | 
| 362 | 2 |  |  | 2 |  | 9 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 192 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 163 |  | 
| 363 | 0 |  |  | 0 |  |  | sub _device_id { 'rtnull' } | 
| 364 |  |  |  |  |  |  | } | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | { | 
| 367 |  |  |  |  |  |  | package Audio::Nama::IO::to_rtnull; | 
| 368 | 2 |  |  | 2 |  | 14 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 3 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 131 |  | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 162 |  | 
| 369 | 0 |  |  | 0 |  |  | sub _device_id { 'rtnull' } | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | { | 
| 373 |  |  |  |  |  |  | package Audio::Nama::IO::from_wav; | 
| 374 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 123 |  | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 587 |  | 
| 375 |  |  |  |  |  |  | sub device_id { | 
| 376 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 377 | 0 |  |  |  |  |  | my @modifiers; | 
| 378 | 0 | 0 |  |  |  |  | push @modifiers, $self->playat_output if $self->playat_output; | 
| 379 | 0 | 0 |  |  |  |  | push @modifiers, $self->select_output if $self->select_output; | 
| 380 | 0 | 0 |  |  |  |  | push @modifiers, split " ", $self->modifiers if $self->modifiers; | 
| 381 | 0 |  |  |  |  |  | push @modifiers, $self->full_path; | 
| 382 | 0 |  |  |  |  |  | join(q[,],@modifiers); | 
| 383 |  |  |  |  |  |  | } | 
| 384 | 0 |  |  | 0 |  |  | sub ecs_extra { $_[0]->mono_to_stereo} | 
| 385 | 0 | 0 |  | 0 |  |  | sub client { 'system' if $jack->{jackd_running} } # since we share latency value | 
| 386 | 0 |  |  | 0 |  |  | sub ports { 'system:capture_1' } | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  | { | 
| 389 |  |  |  |  |  |  | package Audio::Nama::IO::to_wav; | 
| 390 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 137 |  | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 251 |  | 
| 391 | 0 |  |  | 0 |  |  | sub device_id { $_[0]->full_path } | 
| 392 | 0 |  |  | 0 |  |  | sub _format_template { $config->{raw_to_disk_format} } | 
| 393 |  |  |  |  |  |  | } | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | { | 
| 396 |  |  |  |  |  |  | package Audio::Nama::IO::from_loop; | 
| 397 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 35 |  | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 139 |  | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 264 |  | 
| 398 |  |  |  |  |  |  | sub new { | 
| 399 | 0 |  |  | 0 |  |  | my $class = shift; | 
| 400 | 0 |  |  |  |  |  | my %vals = @_; | 
| 401 | 0 |  |  |  |  |  | $class->SUPER::new( %vals, device_id => "loop,$vals{endpoint}"); | 
| 402 |  |  |  |  |  |  | } | 
| 403 |  |  |  | 0 |  |  | sub format {} | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  | { | 
| 406 |  |  |  |  |  |  | package Audio::Nama::IO::to_loop; | 
| 407 | 2 |  |  | 2 |  | 12 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO::from_loop'; | 
|  | 2 |  |  | 2 |  | 2 |  | 
|  | 2 |  |  |  |  | 14 |  | 
|  | 2 |  |  |  |  | 127 |  | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 118 |  | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | { | 
| 411 |  |  |  |  |  |  | package Audio::Nama::IO::from_soundcard; | 
| 412 | 2 |  |  | 2 |  | 9 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 3 |  | 
|  | 2 |  |  |  |  | 38 |  | 
|  | 2 |  |  |  |  | 136 |  | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 265 |  | 
| 413 |  |  |  |  |  |  | sub new { | 
| 414 | 0 |  |  | 0 |  |  | shift; # throw away class | 
| 415 | 0 |  |  |  |  |  | my $class = $io_class{Audio::Nama::IO::soundcard_input_type_string()}; | 
| 416 | 0 |  |  |  |  |  | $class->new(@_); | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  | { | 
| 420 |  |  |  |  |  |  | package Audio::Nama::IO::to_soundcard; | 
| 421 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 5 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 132 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 238 |  | 
| 422 |  |  |  |  |  |  | sub new { | 
| 423 | 0 |  |  | 0 |  |  | shift; # throw away class | 
| 424 | 0 |  |  |  |  |  | my $class = $io_class{Audio::Nama::IO::soundcard_output_type_string()}; | 
| 425 | 0 |  |  |  |  |  | $class->new(@_); | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  | } | 
| 428 |  |  |  |  |  |  | { | 
| 429 |  |  |  |  |  |  | package Audio::Nama::IO::to_jack_multi; | 
| 430 | 2 |  |  | 2 |  | 9 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 32 |  | 
|  | 2 |  |  |  |  | 128 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 280 |  | 
| 431 |  |  |  |  |  |  | sub client { | 
| 432 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 433 |  |  |  |  |  |  | #  	say "to_jack_multi: target_id: ",$self->target_id; | 
| 434 |  |  |  |  |  |  | #  	say "to_jack_multi: rectified target_id: ",Audio::Nama::IO::rectified($self->target_id); | 
| 435 | 0 |  |  |  |  |  | Audio::Nama::IO::rectified($self->target_id) | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  | sub device_id { | 
| 438 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 439 | 0 |  |  |  |  |  | Audio::Nama::IO::jack_multi_route($self->ports) | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | { | 
| 444 |  |  |  |  |  |  | package Audio::Nama::IO::from_jack_multi; | 
| 445 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO::to_jack_multi'; | 
|  | 2 |  |  | 2 |  | 8 |  | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 133 |  | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 172 |  | 
| 446 | 0 |  |  | 0 |  |  | sub ecs_extra { $_[0]->mono_to_stereo } | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | { | 
| 450 |  |  |  |  |  |  | package Audio::Nama::IO::to_jack_port; | 
| 451 | 2 |  |  | 2 |  | 11 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 124 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 331 |  | 
| 452 | 0 |  |  | 0 |  |  | sub format_template { $config->{devices}->{jack}->{signal_format} } | 
| 453 | 0 |  |  | 0 |  |  | sub device_id { 'jack,,'.$_[0]->port_name.'_out' } | 
| 454 | 0 |  |  | 0 |  |  | sub ports { "Nama:".$_[0]->port_name. '_out_1' } # at least this one port | 
| 455 |  |  |  |  |  |  | # HARDCODED port name | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | { | 
| 459 |  |  |  |  |  |  | package Audio::Nama::IO::from_jack_port; | 
| 460 | 2 |  |  | 2 |  | 9 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO::to_jack_port'; | 
|  | 2 |  |  | 2 |  | 3 |  | 
|  | 2 |  |  |  |  | 9 |  | 
|  | 2 |  |  |  |  | 133 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 292 |  | 
| 461 | 0 |  |  | 0 |  |  | sub device_id { 'jack,,'.$_[0]->port_name.'_in' } | 
| 462 | 0 |  |  | 0 |  |  | sub ecs_extra { $_[0]->mono_to_stereo } | 
| 463 | 0 |  |  | 0 |  |  | sub ports { "Nama:".$_[0]->port_name. '_in_1' } # at least this one port | 
| 464 |  |  |  |  |  |  | # HARDCODED port name | 
| 465 |  |  |  |  |  |  | } | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | { | 
| 468 |  |  |  |  |  |  | package Audio::Nama::IO::to_jack_client; | 
| 469 | 2 |  |  | 2 |  | 8 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 124 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 262 |  | 
| 470 | 0 |  |  | 0 |  |  | sub device_id { "jack," . Audio::Nama::IO::quote_jack_port($_[0]->send_id); } | 
| 471 | 0 |  |  | 0 |  |  | sub client { Audio::Nama::IO::rectified($_[0]->send_id) } | 
| 472 |  |  |  |  |  |  | } | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | { | 
| 475 |  |  |  |  |  |  | package Audio::Nama::IO::from_jack_client; | 
| 476 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 2 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 129 |  | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 306 |  | 
| 477 | 0 |  |  | 0 |  |  | sub device_id { 'jack,'.  Audio::Nama::IO::quote_jack_port($_[0]->source_id); } | 
| 478 | 0 |  |  | 0 |  |  | sub ecs_extra { $_[0]->mono_to_stereo} | 
| 479 | 0 |  |  | 0 |  |  | sub client { Audio::Nama::IO::rectified($_[0]->source_id) } | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | { | 
| 483 |  |  |  |  |  |  | package Audio::Nama::IO::from_alsa_soundcard_device; | 
| 484 | 2 |  |  | 2 |  | 8 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 128 |  | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 327 |  | 
| 485 | 0 |  |  | 0 |  |  | sub ecs_extra { join ' ', $_[0]->rec_route, $_[0]->mono_to_stereo } | 
| 486 | 0 |  |  | 0 |  |  | sub device_id { $config->{devices}->{$config->{alsa_capture_device}}->{ecasound_id} } | 
| 487 | 0 |  |  | 0 |  |  | sub input_channel { $_[0]->source_id } | 
| 488 |  |  |  |  |  |  | sub rec_route { | 
| 489 |  |  |  |  |  |  | # works for mono/stereo only! | 
| 490 | 2 |  |  | 2 |  | 10 | no warnings qw(uninitialized); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 300 |  | 
| 491 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 492 |  |  |  |  |  |  | # needed only if input channel is greater than 1 | 
| 493 | 0 | 0 | 0 |  |  |  | return '' if ! $self->input_channel or $self->input_channel == 1; | 
| 494 |  |  |  |  |  |  |  | 
| 495 | 0 |  |  |  |  |  | my $route = "-chmove:" . $self->input_channel . ",1"; | 
| 496 | 0 | 0 |  |  |  |  | if ( $self->width == 2){ | 
| 497 | 0 |  |  |  |  |  | $route .= " -chmove:" . ($self->input_channel + 1) . ",2"; | 
| 498 |  |  |  |  |  |  | } | 
| 499 | 0 |  |  |  |  |  | return $route; | 
| 500 |  |  |  |  |  |  | } | 
| 501 |  |  |  |  |  |  | } | 
| 502 |  |  |  |  |  |  | { | 
| 503 |  |  |  |  |  |  | package Audio::Nama::IO::to_alsa_soundcard_device; | 
| 504 | 2 |  |  | 2 |  | 10 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 137 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 644 |  | 
| 505 | 0 |  |  | 0 |  |  | sub device_id { $config->{devices}->{$config->{alsa_playback_device}}{ecasound_id} } | 
| 506 | 0 |  |  | 0 |  |  | sub ecs_extra {route($_[0]->width,$_[0]->output_channel) } | 
| 507 | 0 |  |  | 0 |  |  | sub output_channel { $_[0]->send_id } | 
| 508 |  |  |  |  |  |  | sub route2 { | 
| 509 | 0 |  |  | 0 |  |  | my ($from, $to, $width) = @_; | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  | sub route { | 
| 512 |  |  |  |  |  |  | # routes signals (1..$width) to ($dest..$dest+$width-1 ) | 
| 513 |  |  |  |  |  |  |  | 
| 514 | 0 |  |  | 0 |  |  | my ($width, $dest) = @_; | 
| 515 | 0 | 0 | 0 |  |  |  | return '' if ! $dest or $dest == 1; | 
| 516 |  |  |  |  |  |  | # print "route: width: $width, destination: $dest\n\n"; | 
| 517 | 0 |  |  |  |  |  | my $offset = $dest - 1; | 
| 518 | 0 |  |  |  |  |  | my $route ; | 
| 519 | 0 |  |  |  |  |  | for my $c ( map{$width - $_ + 1} 1..$width ) { | 
|  | 0 |  |  |  |  |  |  | 
| 520 | 0 |  |  |  |  |  | $route .= " -chmove:$c," . ( $c + $offset); | 
| 521 |  |  |  |  |  |  | } | 
| 522 | 0 |  |  |  |  |  | $route; | 
| 523 |  |  |  |  |  |  | } | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  | { | 
| 526 |  |  |  |  |  |  | package Audio::Nama::IO::any; | 
| 527 | 2 |  |  | 2 |  | 11 | use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; | 
|  | 2 |  |  | 2 |  | 4 |  | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 129 |  | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 189 |  | 
| 528 |  |  |  |  |  |  | } | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | 1; | 
| 532 |  |  |  |  |  |  | __END__ |