line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ----------- Initialize -------- |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# These routines are executed once on program startup |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package Audio::Nama; |
9
|
1
|
|
|
1
|
|
5
|
use Modern::Perl; use Carp; |
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
123
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
54
|
|
10
|
1
|
|
|
1
|
|
5
|
use Socket qw(getnameinfo NI_NUMERICHOST) ; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
640
|
|
11
|
|
|
|
|
|
|
|
12
|
0
|
|
|
0
|
|
|
sub is_test_script { $config->{opts}->{J} } |
13
|
|
|
|
|
|
|
# if we are using fake JACK client data, |
14
|
|
|
|
|
|
|
# probably a test script is running |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub apply_test_args { |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
push @ARGV, qw(-f /dev/null), # force to use internal namarc |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
qw(-t), # set text mode |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
qw(-d), $Audio::Nama::test_dir, |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
q(-E), # suppress loading Ecasound |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
q(-J), # fake jack client data |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
q(-T), # don't initialize terminal |
29
|
|
|
|
|
|
|
# load fake effects cache |
30
|
|
|
|
|
|
|
# q(-c), 'test-project', |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
#qw(-L SUB); # logging |
33
|
|
|
|
|
|
|
|
34
|
0
|
|
|
0
|
|
|
$jack->{periodsize} = 1024; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
sub apply_ecasound_test_args { |
37
|
0
|
|
|
0
|
|
|
apply_test_args(); |
38
|
0
|
|
|
|
|
|
@ARGV = grep { $_ ne q(-E) } @ARGV |
|
0
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub definitions { |
42
|
|
|
|
|
|
|
|
43
|
0
|
|
|
0
|
|
|
$| = 1; # flush STDOUT buffer on every write |
44
|
|
|
|
|
|
|
|
45
|
0
|
0
|
|
|
|
|
$ui eq 'bullwinkle' or die "no \$ui, bullwinkle"; |
46
|
|
|
|
|
|
|
|
47
|
0
|
|
|
|
|
|
@global_effect_chain_vars = qw( |
48
|
|
|
|
|
|
|
@global_effect_chain_data |
49
|
|
|
|
|
|
|
$Audio::Nama::EffectChain::n |
50
|
|
|
|
|
|
|
$fx->{alias} |
51
|
|
|
|
|
|
|
); |
52
|
0
|
|
|
|
|
|
@tracked_vars = qw( |
53
|
|
|
|
|
|
|
@tracks_data |
54
|
|
|
|
|
|
|
@bus_data |
55
|
|
|
|
|
|
|
@groups_data |
56
|
|
|
|
|
|
|
@marks_data |
57
|
|
|
|
|
|
|
@fade_data |
58
|
|
|
|
|
|
|
@edit_data |
59
|
|
|
|
|
|
|
@inserts_data |
60
|
|
|
|
|
|
|
@effects_data |
61
|
|
|
|
|
|
|
$project->{save_file_version_number} |
62
|
|
|
|
|
|
|
$fx->{applied} |
63
|
|
|
|
|
|
|
$fx->{params} |
64
|
|
|
|
|
|
|
$fx->{params_log} |
65
|
|
|
|
|
|
|
); |
66
|
0
|
|
|
|
|
|
@persistent_vars = qw( |
67
|
|
|
|
|
|
|
$project->{save_file_version_number} |
68
|
|
|
|
|
|
|
$project->{timebase} |
69
|
|
|
|
|
|
|
$project->{command_buffer} |
70
|
|
|
|
|
|
|
$project->{track_version_comments} |
71
|
|
|
|
|
|
|
$project->{track_comments} |
72
|
|
|
|
|
|
|
$project->{bunch} |
73
|
|
|
|
|
|
|
$project->{current_op} |
74
|
|
|
|
|
|
|
$project->{current_param} |
75
|
|
|
|
|
|
|
$project->{current_stepsize} |
76
|
|
|
|
|
|
|
$project->{playback_position} |
77
|
|
|
|
|
|
|
@project_effect_chain_data |
78
|
|
|
|
|
|
|
$fx->{id_counter} |
79
|
|
|
|
|
|
|
$setup->{loop_endpoints} |
80
|
|
|
|
|
|
|
$mode->{loop_enable} |
81
|
|
|
|
|
|
|
$mode->{mastering} |
82
|
|
|
|
|
|
|
$mode->{preview} |
83
|
|
|
|
|
|
|
$mode->{midish_terminal} |
84
|
|
|
|
|
|
|
$mode->{midish_transport_sync} |
85
|
|
|
|
|
|
|
$gui->{_seek_unit} |
86
|
|
|
|
|
|
|
$text->{command_history} |
87
|
|
|
|
|
|
|
$this_track_name |
88
|
|
|
|
|
|
|
$this_op |
89
|
|
|
|
|
|
|
); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
0
|
|
|
|
|
|
$text->{wrap} = new Text::Format { |
93
|
|
|
|
|
|
|
columns => 75, |
94
|
|
|
|
|
|
|
firstIndent => 0, |
95
|
|
|
|
|
|
|
bodyIndent => 0, |
96
|
|
|
|
|
|
|
tabstop => 4, |
97
|
|
|
|
|
|
|
}; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
####### Initialize singletons ####### |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Some of these "singletons" (imported by 'use Globals') |
102
|
|
|
|
|
|
|
# are just hashes, some have object behavior as |
103
|
|
|
|
|
|
|
# the sole instance of their class. |
104
|
|
|
|
|
|
|
|
105
|
0
|
|
|
|
|
|
$project = bless {}, 'Audio::Nama::Project'; |
106
|
0
|
|
|
|
|
|
$mode = bless {}, 'Audio::Nama::Mode'; |
107
|
0
|
|
|
|
|
|
{ package Audio::Nama::Mode; |
108
|
0
|
0
|
|
0
|
|
|
sub mastering { $Audio::Nama::tn{Eq} and ! $Audio::Nama::tn{Eq}->{hide} } |
109
|
1
|
|
|
1
|
|
6
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
220
|
|
110
|
0
|
|
|
0
|
|
|
sub eager { $Audio::Nama::mode->{eager} } |
111
|
|
|
|
|
|
|
sub doodle { |
112
|
|
|
|
|
|
|
#my $set = shift; |
113
|
|
|
|
|
|
|
#if (defined $set){ $Audio::Nama::mode->{preview} = $set ? 'doodle' : 0 } |
114
|
0
|
|
|
0
|
|
|
$Audio::Nama::mode->{preview} eq 'doodle' } |
115
|
0
|
|
|
0
|
|
|
sub preview { $Audio::Nama::mode->{preview} eq 'preview' } |
116
|
0
|
0
|
|
0
|
|
|
sub song { $Audio::Nama::mode->eager and $Audio::Nama::mode->preview } |
117
|
0
|
0
|
|
0
|
|
|
sub live { $Audio::Nama::mode->eager and $Audio::Nama::mode->doodle } |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
# for example, $file belongs to class Audio::Nama::File, and uses |
120
|
|
|
|
|
|
|
# AUTOLOAD to generate methods to provide full path |
121
|
|
|
|
|
|
|
# to various system files, such as $file->state_store |
122
|
|
|
|
|
|
|
{ |
123
|
0
|
|
|
|
|
|
package Audio::Nama::File; |
|
0
|
|
|
|
|
|
|
124
|
1
|
|
|
1
|
|
10
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
558
|
|
125
|
|
|
|
|
|
|
sub logfile { |
126
|
0
|
|
|
0
|
|
|
my $self = shift; |
127
|
0
|
0
|
|
|
|
|
$ENV{NAMA_LOGFILE} || $self->_logfile |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
sub AUTOLOAD { |
130
|
0
|
|
|
0
|
|
|
my ($self, $filename) = @_; |
131
|
|
|
|
|
|
|
# get tail of method call |
132
|
0
|
|
|
|
|
|
my ($method) = $Audio::Nama::File::AUTOLOAD =~ /([^:]+)$/; |
133
|
0
|
0
|
|
|
|
|
croak "$method: illegal method call" unless $self->{$method}; |
134
|
0
|
|
|
|
|
|
my $dir_sub = $self->{$method}->[1]; |
135
|
0
|
|
0
|
|
|
|
$filename ||= $self->{$method}->[0]; |
136
|
0
|
|
|
|
|
|
my $path = Audio::Nama::join_path($dir_sub->(), $filename); |
137
|
0
|
|
|
|
|
|
$path; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
0
|
|
|
sub DESTROY {} |
140
|
0
|
|
|
|
|
|
1; |
141
|
|
|
|
|
|
|
} |
142
|
0
|
|
|
|
|
|
$file = bless |
143
|
|
|
|
|
|
|
{ |
144
|
|
|
|
|
|
|
effects_cache => ['.effects_cache.json', \&project_root], |
145
|
|
|
|
|
|
|
gui_palette => ['palette', \&project_root], |
146
|
|
|
|
|
|
|
state_store => ['State', \&project_dir ], |
147
|
|
|
|
|
|
|
git_state_store => ['State.json', \&project_dir ], |
148
|
|
|
|
|
|
|
untracked_state_store => ['Aux', \&project_dir ], |
149
|
|
|
|
|
|
|
effect_profile => ['effect_profiles', \&project_root], |
150
|
|
|
|
|
|
|
chain_setup => ['Setup.ecs', \&project_dir ], |
151
|
|
|
|
|
|
|
user_customization => ['customize.pl', \&project_root], |
152
|
|
|
|
|
|
|
project_effect_chains => ['project_effect_chains',\&project_dir ], |
153
|
|
|
|
|
|
|
global_effect_chains => ['global_effect_chains', \&project_root], |
154
|
|
|
|
|
|
|
old_effect_chains => ['effect_chains', \&project_root], |
155
|
|
|
|
|
|
|
_logfile => ['nama.log', \&project_root], |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
}, 'Audio::Nama::File'; |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
|
$gui->{_save_id} = "State"; |
161
|
0
|
|
|
|
|
|
$gui->{_seek_unit} = 1; |
162
|
0
|
|
|
|
|
|
$gui->{marks} = {}; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# |
166
|
|
|
|
|
|
|
# use this section to specify |
167
|
|
|
|
|
|
|
# defaults for config variables |
168
|
|
|
|
|
|
|
# |
169
|
|
|
|
|
|
|
# These are initial, lowest priority defaults |
170
|
|
|
|
|
|
|
# defaults for Nama config. Some variables |
171
|
|
|
|
|
|
|
# may be overwritten during subsequent read_config's |
172
|
|
|
|
|
|
|
# |
173
|
|
|
|
|
|
|
# config variable sources are prioritized as follows |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# |
176
|
|
|
|
|
|
|
# + command line argument -f /path/to/namarc |
177
|
|
|
|
|
|
|
# + project specific namarc # currently disabled |
178
|
|
|
|
|
|
|
# + user namarc (usually ~/.namarc) |
179
|
|
|
|
|
|
|
# + internal namarc |
180
|
|
|
|
|
|
|
# + internal initialization |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
$config = bless { |
184
|
|
|
|
|
|
|
root_dir => join_path( $ENV{HOME}, "nama"), |
185
|
|
|
|
|
|
|
soundcard_channels => 10, |
186
|
|
|
|
|
|
|
memoize => 1, |
187
|
|
|
|
|
|
|
use_pager => 1, |
188
|
|
|
|
|
|
|
use_placeholders => 1, |
189
|
|
|
|
|
|
|
use_git => 1, |
190
|
|
|
|
|
|
|
autosave => 'undo', |
191
|
|
|
|
|
|
|
volume_control_operator => 'ea', # default to linear scale |
192
|
|
|
|
|
|
|
sync_mixdown_and_monitor_version_numbers => 1, # not implemented yet |
193
|
|
|
|
|
|
|
engine_tcp_port => 2868, # 'default' engine |
194
|
|
|
|
|
|
|
engine_fade_length_on_start_stop => 0.18,# when starting/stopping transport |
195
|
|
|
|
|
|
|
engine_fade_default_length => 0.5, # for fade-in, fade-out |
196
|
|
|
|
|
|
|
engine_base_jack_seek_delay => 0.1, # seconds |
197
|
|
|
|
|
|
|
engine_command_output_buffer_size => 2**22, # 4 MB |
198
|
|
|
|
|
|
|
edit_playback_end_margin => 3, |
199
|
|
|
|
|
|
|
edit_crossfade_time => 0.03, |
200
|
|
|
|
|
|
|
fade_down_fraction => 0.75, |
201
|
|
|
|
|
|
|
fade_time1_fraction => 0.9, |
202
|
|
|
|
|
|
|
fade_time2_fraction => 0.1, |
203
|
|
|
|
|
|
|
fader_op => 'ea', |
204
|
|
|
|
|
|
|
mute_level => {ea => 0, eadb => -96}, |
205
|
|
|
|
|
|
|
fade_out_level => {ea => 0, eadb => -40}, |
206
|
|
|
|
|
|
|
unity_level => {ea => 100, eadb => 0}, |
207
|
|
|
|
|
|
|
fade_resolution => 100, # steps per second |
208
|
|
|
|
|
|
|
engine_muting_time => 0.03, |
209
|
|
|
|
|
|
|
enforce_channel_bounds => 1, |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
serialize_formats => 'json', # for save_system_state() |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
latency_op => 'el:delay_n', |
214
|
|
|
|
|
|
|
latency_op_init => [0,0], |
215
|
|
|
|
|
|
|
latency_op_set => sub |
216
|
|
|
|
|
|
|
{ |
217
|
0
|
|
|
0
|
|
|
my $id = shift; |
218
|
0
|
|
|
|
|
|
my $delay = shift(); |
219
|
0
|
|
|
|
|
|
modify_effect($id,2,undef,$delay) |
220
|
|
|
|
|
|
|
}, |
221
|
0
|
|
|
|
|
|
hotkey_beep => 'beep -f 250 -l 200', |
222
|
|
|
|
|
|
|
# this causes beeping during make test |
223
|
|
|
|
|
|
|
# beep_command => 'beep -f 350 -l 700', |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
}, 'Audio::Nama::Config'; |
226
|
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
{ package Audio::Nama::Config; |
228
|
1
|
|
|
1
|
|
13
|
use Carp; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
54
|
|
229
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Globals qw(:singletons); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
200
|
|
230
|
1
|
|
|
1
|
|
5
|
use Modern::Perl; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
231
|
0
|
|
|
|
|
|
our @ISA = 'Audio::Nama::Object'; # for ->dump and ->as_hash methods |
232
|
|
|
|
|
|
|
|
233
|
0
|
|
|
0
|
0
|
|
sub serialize_formats { split " ", $_[0]->{serialize_formats} } |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub hardware_latency { |
236
|
1
|
|
|
1
|
|
148
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
438
|
|
237
|
0
|
0
|
|
0
|
0
|
|
$config->{devices}->{$config->{alsa_capture_device}}{hardware_latency} || 0 |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
sub buffersize { |
240
|
|
|
|
|
|
|
package Audio::Nama; |
241
|
|
|
|
|
|
|
Audio::Nama::ChainSetup::setup_requires_realtime() |
242
|
|
|
|
|
|
|
? $config->{engine_buffersize}->{realtime}->{default} |
243
|
|
|
|
|
|
|
: $config->{engine_buffersize}->{nonrealtime}->{default} |
244
|
0
|
0
|
|
0
|
0
|
|
} |
245
|
|
|
|
|
|
|
sub globals_realtime { |
246
|
|
|
|
|
|
|
Audio::Nama::ChainSetup::setup_requires_realtime() |
247
|
|
|
|
|
|
|
? $config->{engine_globals}->{realtime} |
248
|
|
|
|
|
|
|
: $config->{engine_globals}->{nonrealtime} |
249
|
0
|
0
|
|
0
|
0
|
|
} |
250
|
|
|
|
|
|
|
} # end Audio::Nama::Config package |
251
|
|
|
|
|
|
|
|
252
|
0
|
|
|
|
|
|
$prompt = "nama ('h' for help)> "; |
|
0
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
254
|
0
|
|
|
|
|
|
$this_bus = 'Main'; |
255
|
|
|
|
|
|
|
|
256
|
0
|
|
|
|
|
|
$setup->{_old_snapshot} = {}; |
257
|
0
|
|
|
|
|
|
$setup->{_last_rec_tracks} = []; |
258
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
|
$mastering->{track_names} = [ qw(Eq Low Mid High Boost) ]; |
260
|
|
|
|
|
|
|
|
261
|
0
|
0
|
|
|
|
|
init_wav_memoize() if $config->{memoize}; |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub initialize_interfaces { |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
logsub("&intialize_interfaces"); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
if ( ! $config->{opts}->{t} and Audio::Nama::Graphical::initialize_tk() ){ |
270
|
|
|
|
|
|
|
$ui = Audio::Nama::Graphical->new(); |
271
|
|
|
|
|
|
|
} else { |
272
|
|
|
|
|
|
|
pager_newline( "Unable to load perl Tk module. Starting in console mode.") if $config->{opts}->{g}; |
273
|
|
|
|
|
|
|
$ui = Audio::Nama::Text->new(); |
274
|
|
|
|
|
|
|
can_load( modules =>{ Event => undef}) |
275
|
|
|
|
|
|
|
or die "Perl Module 'Event' not found. Please install it and try again. Stopping."; |
276
|
|
|
|
|
|
|
; |
277
|
|
|
|
|
|
|
import Event qw(loop unloop unloop_all); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
can_load( modules => {AnyEvent => undef}) |
281
|
|
|
|
|
|
|
or die "Perl Module 'AnyEvent' not found. Please install it and try again. Stopping."; |
282
|
1
|
|
|
1
|
|
773
|
use AnyEvent::TermKey qw( FORMAT_VIM KEYMOD_CTRL ); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
can_load( modules => {jacks => undef}) |
284
|
|
|
|
|
|
|
and $jack->{use_jacks}++; |
285
|
|
|
|
|
|
|
choose_sleep_routine(); |
286
|
|
|
|
|
|
|
$config->{want_logging} = initialize_logger($config->{opts}->{L}); |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
$project->{name} = shift @ARGV; |
289
|
|
|
|
|
|
|
{no warnings 'uninitialized'; |
290
|
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"project name: $project->{name}"); |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', sub{"Command line options\n". json_out($config->{opts})}); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
read_config(global_config()); # from .namarc if we have one |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{"Config data\n".Dumper $config}); |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
select_ecasound_interface(); |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
start_osc_listener($config->{osc_listener_port}) |
302
|
|
|
|
|
|
|
if $config->{osc_listener_port} |
303
|
|
|
|
|
|
|
and can_load(modules => {'Protocol::OSC' => undef}); |
304
|
|
|
|
|
|
|
start_remote_listener($config->{remote_control_port}) if $config->{remote_control_port}; |
305
|
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"reading config file"); |
306
|
|
|
|
|
|
|
if ($config->{opts}->{d}){ |
307
|
|
|
|
|
|
|
pager("project_root $config->{opts}->{d} specified on command line\n"); |
308
|
|
|
|
|
|
|
$config->{root_dir} = $config->{opts}->{d}; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
if ($config->{opts}->{p}){ |
311
|
|
|
|
|
|
|
$config->{root_dir} = getcwd(); |
312
|
|
|
|
|
|
|
pager("placing all files in current working directory ($config->{root_dir})\n"); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# skip initializations if user (test) supplies project |
316
|
|
|
|
|
|
|
# directory |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
first_run() unless $config->{opts}->{d}; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
prepare_static_effects_data() unless $config->{opts}->{S}; |
321
|
|
|
|
|
|
|
setup_user_customization(); # depends on effect_index() in above |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
get_ecasound_iam_keywords(); |
324
|
|
|
|
|
|
|
load_keywords(); # for autocompletion |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
chdir $config->{root_dir} # for filename autocompletion |
327
|
|
|
|
|
|
|
or warn "$config->{root_dir}: chdir failed: $!\n"; |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
$ui->init_gui; |
330
|
|
|
|
|
|
|
$ui->transport_gui; |
331
|
|
|
|
|
|
|
$ui->time_gui; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# fake JACK for testing environment |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
if( $config->{opts}->{J}){ |
336
|
|
|
|
|
|
|
parse_ports_list(get_data_section("fake_jack_lsp")); |
337
|
|
|
|
|
|
|
parse_port_latency(get_data_section("fake_jack_latency")); |
338
|
|
|
|
|
|
|
$jack->{jackd_running} = 1; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# periodically check if JACK is running, and get client/port/latency list |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
poll_jack() unless $config->{opts}->{J} or $config->{opts}->{A}; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sleeper(0.2); # allow time for first polling |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# we will start jack.plumbing only when we need it |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
if( $config->{use_jack_plumbing} |
350
|
|
|
|
|
|
|
and $jack->{jackd_running} |
351
|
|
|
|
|
|
|
and process_is_running('jack.plumbing') |
352
|
|
|
|
|
|
|
){ |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
pager_newline(<
|
355
|
|
|
|
|
|
|
Jack.plumbing daemon detected! |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Attempting to stop it... |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
(This may break other software that depends in jack.plumbing.) |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
Nama will restart it as needed for Nama's use only. |
362
|
|
|
|
|
|
|
PLUMB |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
kill_jack_plumbing(); |
365
|
|
|
|
|
|
|
sleeper(0.2); |
366
|
|
|
|
|
|
|
if( process_is_running('jack.plumbing') ) |
367
|
|
|
|
|
|
|
{ |
368
|
|
|
|
|
|
|
throw(q(Unable to stop jack.plumbing daemon. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Please do one of the following, then restart Nama: |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
- kill the jack.plumbing daemon ("killall jack.plumbing") |
373
|
|
|
|
|
|
|
- set "use_jack_plumbing: 0" in .namarc |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
....Exiting.) ); |
376
|
|
|
|
|
|
|
exit; |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
else { pager_newline("Stopped.") } |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
start_midish() if $config->{use_midish}; |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
initialize_terminal() unless $config->{opts}->{T}; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
1; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
{ my $is_connected_remote; |
388
|
|
|
|
|
|
|
sub start_remote_listener { |
389
|
|
|
|
|
|
|
my $port = shift; |
390
|
|
|
|
|
|
|
pager_newline("Starting remote control listener on port $port"); |
391
|
|
|
|
|
|
|
$project->{remote_control_socket} = IO::Socket::INET->new( |
392
|
|
|
|
|
|
|
LocalAddr => 'localhost', |
393
|
|
|
|
|
|
|
LocalPort => $port, |
394
|
|
|
|
|
|
|
Proto => 'tcp', |
395
|
|
|
|
|
|
|
Type => SOCK_STREAM, |
396
|
|
|
|
|
|
|
Listen => 1, |
397
|
|
|
|
|
|
|
Reuse => 1) || die $!; |
398
|
|
|
|
|
|
|
start_remote_watcher(); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
sub start_remote_watcher { |
401
|
|
|
|
|
|
|
$project->{events}->{remote_control} = AE::io( |
402
|
|
|
|
|
|
|
$project->{remote_control_socket}, 0, \&process_remote_command ) |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
sub remove_remote_watcher { |
405
|
|
|
|
|
|
|
undef $project->{events}->{remote_control}; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
sub process_remote_command { |
408
|
|
|
|
|
|
|
if ( ! $is_connected_remote++ ){ |
409
|
|
|
|
|
|
|
pager_newline("making connection"); |
410
|
|
|
|
|
|
|
$project->{remote_control_socket} = |
411
|
|
|
|
|
|
|
$project->{remote_control_socket}->accept(); |
412
|
|
|
|
|
|
|
remove_remote_watcher(); |
413
|
|
|
|
|
|
|
$project->{events}->{remote_control} = AE::io( |
414
|
|
|
|
|
|
|
$project->{remote_control_socket}, 0, \&process_remote_command ); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
my $input; |
417
|
|
|
|
|
|
|
eval { |
418
|
|
|
|
|
|
|
$project->{remote_control_socket}->recv($input, $project->{remote_control_socket}->sockopt(SO_RCVBUF)); |
419
|
|
|
|
|
|
|
}; |
420
|
|
|
|
|
|
|
$@ and throw("caught error: $@, resetting..."), reset_remote_control_socket(), revise_prompt(), return; |
421
|
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"Got remote control socketput: $input"); |
422
|
|
|
|
|
|
|
process_command($input); |
423
|
|
|
|
|
|
|
my $out; |
424
|
|
|
|
|
|
|
{ no warnings 'uninitialized'; |
425
|
|
|
|
|
|
|
$out = $text->{eval_result} . "\n"; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
eval { |
428
|
|
|
|
|
|
|
$project->{remote_control_socket}->send($out); |
429
|
|
|
|
|
|
|
}; |
430
|
|
|
|
|
|
|
$@ and throw("caught error: $@, resetting..."), reset_remote_control_socket(), revise_prompt(), return; |
431
|
|
|
|
|
|
|
revise_prompt(); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
sub reset_remote_control_socket { |
434
|
|
|
|
|
|
|
undef $is_connected_remote; |
435
|
|
|
|
|
|
|
undef $@; |
436
|
|
|
|
|
|
|
$project->{remote_control_socket}->shutdown(2); |
437
|
|
|
|
|
|
|
undef $project->{remote_control_socket}; |
438
|
|
|
|
|
|
|
remove_remote_watcher(); |
439
|
|
|
|
|
|
|
start_remote_listener($config->{remote_control_port}); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
sub start_osc_listener { |
444
|
|
|
|
|
|
|
my $port = shift; |
445
|
|
|
|
|
|
|
say("Starting OSC listener on port $port"); |
446
|
|
|
|
|
|
|
my $osc_in = $project->{osc_socket} = IO::Socket::INET->new( |
447
|
|
|
|
|
|
|
LocalAddr => 'localhost', |
448
|
|
|
|
|
|
|
LocalPort => $port, |
449
|
|
|
|
|
|
|
Proto => 'udp', |
450
|
|
|
|
|
|
|
Type => SOCK_DGRAM) || die $!; |
451
|
|
|
|
|
|
|
$project->{events}->{osc} = AE::io( $osc_in, 0, \&process_osc_command ); |
452
|
|
|
|
|
|
|
$project->{osc} = Protocol::OSC->new; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
sub process_osc_command { |
455
|
|
|
|
|
|
|
my $in = $project->{osc_socket}; |
456
|
|
|
|
|
|
|
my $osc = $project->{osc}; |
457
|
|
|
|
|
|
|
my $source_ip = $in->recv(my $packet, $in->sockopt(SO_RCVBUF)); |
458
|
|
|
|
|
|
|
my($err, $hostname, $servicename) = getnameinfo($source_ip, NI_NUMERICHOST); |
459
|
|
|
|
|
|
|
my $p = $osc->parse($packet); |
460
|
|
|
|
|
|
|
my @args = @$p; |
461
|
|
|
|
|
|
|
my ($path, $template, $command, @vals) = @args; |
462
|
|
|
|
|
|
|
$path =~ s(^/)(); |
463
|
|
|
|
|
|
|
$path =~ s(/$)(); |
464
|
|
|
|
|
|
|
my ($trackname, $fx, $param) = split '/', $path; |
465
|
|
|
|
|
|
|
process_command($trackname); |
466
|
|
|
|
|
|
|
process_command("$command @vals") if $command; |
467
|
|
|
|
|
|
|
process_command("show_effect $fx") if $fx; # select |
468
|
|
|
|
|
|
|
process_command("show_track") if $trackname and not $fx; |
469
|
|
|
|
|
|
|
process_command("show_tracks") if ! $trackname; |
470
|
|
|
|
|
|
|
say "got OSC: ", Dumper $p; |
471
|
|
|
|
|
|
|
say "got args: @args"; |
472
|
|
|
|
|
|
|
my $osc_out = IO::Socket::INET->new( |
473
|
|
|
|
|
|
|
PeerAddr => $hostname, |
474
|
|
|
|
|
|
|
PeerPort => $config->{osc_reply_port}, |
475
|
|
|
|
|
|
|
Proto => 'udp', |
476
|
|
|
|
|
|
|
Type => SOCK_DGRAM) || die $!; |
477
|
|
|
|
|
|
|
$osc_out->send(join "",@{$text->{output_buffer}}); |
478
|
|
|
|
|
|
|
delete $text->{output_buffer}; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub sanitize_remote_input { |
482
|
|
|
|
|
|
|
my $input = shift; |
483
|
|
|
|
|
|
|
my $error_msg; |
484
|
|
|
|
|
|
|
do{ $input = "" ; $error_msg = "error: perl/shell code is not allowed"} |
485
|
|
|
|
|
|
|
if $input =~ /(^|;)\s*(!|eval\b)/; |
486
|
|
|
|
|
|
|
throw($error_msg) if $error_msg; |
487
|
|
|
|
|
|
|
$input |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
sub select_ecasound_interface { |
490
|
|
|
|
|
|
|
my %args; |
491
|
|
|
|
|
|
|
my $class; |
492
|
|
|
|
|
|
|
if ($config->{opts}->{A} or $config->{opts}->{E}) |
493
|
|
|
|
|
|
|
{ |
494
|
|
|
|
|
|
|
pager_newline("Starting dummy engine only"); |
495
|
|
|
|
|
|
|
%args = ( |
496
|
|
|
|
|
|
|
name => 'Nama', |
497
|
|
|
|
|
|
|
jack_transport_mode => 'send', |
498
|
|
|
|
|
|
|
); |
499
|
|
|
|
|
|
|
$class = 'Audio::Nama::Engine'; |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
elsif ( |
502
|
|
|
|
|
|
|
$config->{opts}->{l} |
503
|
|
|
|
|
|
|
and can_load( modules => { 'Audio::Ecasound' => undef }) |
504
|
|
|
|
|
|
|
and say("loaded Audio::Ecasound") |
505
|
|
|
|
|
|
|
){ |
506
|
|
|
|
|
|
|
%args = ( |
507
|
|
|
|
|
|
|
name => 'Nama', |
508
|
|
|
|
|
|
|
jack_transport_mode => 'send', |
509
|
|
|
|
|
|
|
); |
510
|
|
|
|
|
|
|
$class = 'Audio::Nama::LibEngine'; |
511
|
|
|
|
|
|
|
} |
512
|
|
|
|
|
|
|
else { |
513
|
|
|
|
|
|
|
%args = ( |
514
|
|
|
|
|
|
|
name => 'Nama', |
515
|
|
|
|
|
|
|
port => $config->{engine_tcp_port}, |
516
|
|
|
|
|
|
|
jack_transport_mode => 'send', |
517
|
|
|
|
|
|
|
); |
518
|
|
|
|
|
|
|
$class = 'Audio::Nama::NetEngine'; |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
$class->new(%args); |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub choose_sleep_routine { |
526
|
|
|
|
|
|
|
if ( can_load(modules => {'Time::HiRes'=> undef} ) ) |
527
|
|
|
|
|
|
|
{ *sleeper = *finesleep; |
528
|
|
|
|
|
|
|
$config->{hires_timer}++; } |
529
|
|
|
|
|
|
|
else { *sleeper = *select_sleep } |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
sub finesleep { |
532
|
|
|
|
|
|
|
my $sec = shift; |
533
|
|
|
|
|
|
|
Time::HiRes::usleep($sec * 1e6); |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
sub select_sleep { |
536
|
|
|
|
|
|
|
my $seconds = shift; |
537
|
|
|
|
|
|
|
select( undef, undef, undef, $seconds ); |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
sub munge_category { |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
my $cat = shift; |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# override undefined category by magical global setting |
544
|
|
|
|
|
|
|
# default to 'ECI_OTHER' |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
$cat ||= ($config->{category} || 'ECI_OTHER'); |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# force all categories to 'ECI' if 'ECI' is selected for logging |
549
|
|
|
|
|
|
|
# (exception: ECI_WAVINFO, which is too noisy) |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
no warnings 'uninitialized'; |
552
|
|
|
|
|
|
|
return 'ECI' if $config->{want_logging}->{ECI} and not $cat eq 'ECI_WAVINFO'; |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
$cat |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub start_logging { |
558
|
|
|
|
|
|
|
$config->{want_logging} = initialize_logger($config->{opts}->{L}) |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
sub eval_iam { $this_engine and $this_engine->eval_iam(@_) } |
561
|
|
|
|
|
|
|
1; |
562
|
|
|
|
|
|
|
__END__ |