seriamp 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/LICENSE +23 -0
- data/README.md +75 -0
- data/bin/sonamp +10 -0
- data/bin/sonamp-web +31 -0
- data/bin/yamaha +12 -0
- data/bin/yamaha-web +31 -0
- data/lib/seriamp/backend/ffi.rb +109 -0
- data/lib/seriamp/backend/serial_port.rb +34 -0
- data/lib/seriamp/detect.rb +44 -0
- data/lib/seriamp/error.rb +10 -0
- data/lib/seriamp/sonamp/app.rb +62 -0
- data/lib/seriamp/sonamp/client.rb +314 -0
- data/lib/seriamp/sonamp/cmd.rb +102 -0
- data/lib/seriamp/sonamp.rb +4 -0
- data/lib/seriamp/utils.rb +17 -0
- data/lib/seriamp/version.rb +5 -0
- data/lib/seriamp/yamaha/app.rb +58 -0
- data/lib/seriamp/yamaha/client.rb +279 -0
- data/lib/seriamp/yamaha/cmd.rb +139 -0
- data/lib/seriamp/yamaha/protocol/constants.rb +183 -0
- data/lib/seriamp/yamaha/protocol/methods.rb +134 -0
- data/lib/seriamp/yamaha.rb +4 -0
- data/lib/seriamp.rb +5 -0
- data/seriamp.gemspec +17 -0
- metadata +73 -0
@@ -0,0 +1,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'timeout'
|
4
|
+
require 'seriamp/backend/serial_port'
|
5
|
+
require 'seriamp/yamaha/protocol/constants'
|
6
|
+
require 'seriamp/yamaha/protocol/methods'
|
7
|
+
|
8
|
+
module Seriamp
|
9
|
+
module Yamaha
|
10
|
+
|
11
|
+
RS232_TIMEOUT = 3
|
12
|
+
|
13
|
+
class Client
|
14
|
+
include Protocol::Methods
|
15
|
+
|
16
|
+
def initialize(device: nil, glob: nil, logger: nil)
|
17
|
+
@logger = logger
|
18
|
+
|
19
|
+
@device = device
|
20
|
+
@detect_device = device.nil?
|
21
|
+
@glob = glob
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
begin
|
25
|
+
yield self
|
26
|
+
ensure
|
27
|
+
close
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :device
|
33
|
+
attr_reader :glob
|
34
|
+
attr_reader :logger
|
35
|
+
|
36
|
+
def detect_device?
|
37
|
+
@detect_device
|
38
|
+
end
|
39
|
+
|
40
|
+
def present?
|
41
|
+
last_status
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def last_status
|
46
|
+
unless @status
|
47
|
+
with_device do
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@status.dup
|
51
|
+
end
|
52
|
+
|
53
|
+
def last_status_string
|
54
|
+
unless @status_string
|
55
|
+
with_device do
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@status_string.dup
|
59
|
+
end
|
60
|
+
|
61
|
+
def status
|
62
|
+
do_status
|
63
|
+
last_status
|
64
|
+
end
|
65
|
+
|
66
|
+
%i(
|
67
|
+
model_code firmware_version system_status power main_power zone2_power
|
68
|
+
zone3_power input input_name audio_select audio_select_name
|
69
|
+
main_volume main_volume_db zone2_volume zone2_volume_db
|
70
|
+
zone3_volume zone3_volume_db program program_name sleep night night_name
|
71
|
+
format sample_rate
|
72
|
+
).each do |meth|
|
73
|
+
define_method(meth) do
|
74
|
+
status.fetch(meth)
|
75
|
+
end
|
76
|
+
|
77
|
+
define_method("last_#{meth}") do
|
78
|
+
last_status.fetch(meth)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
%i(
|
83
|
+
multi_ch_input effect pure_direct speaker_a speaker_b
|
84
|
+
).each do |meth|
|
85
|
+
define_method("#{meth}?") do
|
86
|
+
status.fetch(meth)
|
87
|
+
end
|
88
|
+
|
89
|
+
define_method("last_#{meth}?") do
|
90
|
+
last_status.fetch(meth)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
include Protocol::Constants
|
97
|
+
|
98
|
+
def open_device
|
99
|
+
if detect_device? && device.nil?
|
100
|
+
@device = Seriamp.detect_device(Yamaha, *glob, logger: logger)
|
101
|
+
if @device
|
102
|
+
logger&.info("Using #{device} as TTY device")
|
103
|
+
else
|
104
|
+
raise NoDevice, "No device specified and device could not be detected automatically"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
@io = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
109
|
+
|
110
|
+
begin
|
111
|
+
tries = 0
|
112
|
+
begin
|
113
|
+
do_status
|
114
|
+
rescue CommunicationTimeout
|
115
|
+
tries += 1
|
116
|
+
if tries < 5
|
117
|
+
logger&.warn("Timeout handshaking with the receiver - will retry")
|
118
|
+
retry
|
119
|
+
else
|
120
|
+
raise
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
yield @io
|
125
|
+
ensure
|
126
|
+
@io.close rescue nil
|
127
|
+
@io = nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def with_device(&block)
|
132
|
+
if @io
|
133
|
+
yield @io
|
134
|
+
else
|
135
|
+
open_device(&block)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# ASCII table: https://www.asciitable.com/
|
140
|
+
DC1 = ?\x11
|
141
|
+
DC2 = ?\x12
|
142
|
+
ETX = ?\x03
|
143
|
+
STX = ?\x02
|
144
|
+
DEL = ?\x7f
|
145
|
+
|
146
|
+
STATUS_REQ = "#{DC1}001#{ETX}"
|
147
|
+
|
148
|
+
ZERO_ORD = '0'.ord
|
149
|
+
|
150
|
+
def dispatch(cmd)
|
151
|
+
with_device do
|
152
|
+
@io.syswrite(cmd.encode('ascii'))
|
153
|
+
read_response
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def read_response
|
158
|
+
resp = +''
|
159
|
+
Timeout.timeout(2, CommunicationTimeout) do
|
160
|
+
loop do
|
161
|
+
ch = @io.sysread(1)
|
162
|
+
if ch
|
163
|
+
resp << ch
|
164
|
+
break if ch == ETX
|
165
|
+
else
|
166
|
+
sleep 0.1
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
resp
|
171
|
+
end
|
172
|
+
|
173
|
+
def do_status
|
174
|
+
resp = nil
|
175
|
+
loop do
|
176
|
+
resp = dispatch(STATUS_REQ)
|
177
|
+
again = false
|
178
|
+
while @io && IO.select([@io.io], nil, nil, 0)
|
179
|
+
logger&.warn("Serial device readable after completely reading status response - concurrent access?")
|
180
|
+
read_response
|
181
|
+
again = true
|
182
|
+
end
|
183
|
+
break unless again
|
184
|
+
end
|
185
|
+
payload = resp[1...-1]
|
186
|
+
@model_code = payload[0..4]
|
187
|
+
@version = payload[5]
|
188
|
+
length = payload[6..7].to_i(16)
|
189
|
+
data = payload[8...-2]
|
190
|
+
if data.length != length
|
191
|
+
raise BadStatus, "Broken status response: expected #{length} bytes, got #{data.length} bytes; concurrent operation on device?"
|
192
|
+
end
|
193
|
+
unless data.start_with?('@E01900')
|
194
|
+
raise BadStatus, "Broken status response: expected to start with @E01900, actual #{data[0..6]}"
|
195
|
+
end
|
196
|
+
@status_string = data
|
197
|
+
@status = {
|
198
|
+
# RX-V1500: model R0177
|
199
|
+
model_code: @model_code,
|
200
|
+
firmware_version: @version,
|
201
|
+
system_status: data[7].ord - ZERO_ORD,
|
202
|
+
power: power = data[8].ord - ZERO_ORD,
|
203
|
+
main_power: [1, 4, 5, 2].include?(power),
|
204
|
+
zone2_power: [1, 4, 3, 6].include?(power),
|
205
|
+
zone3_power: [1, 5, 3, 7].include?(power),
|
206
|
+
}
|
207
|
+
if data.length > 9
|
208
|
+
@status.update(
|
209
|
+
input: input = data[9],
|
210
|
+
input_name: MAIN_INPUTS_GET.fetch(input),
|
211
|
+
multi_ch_input: data[10] == '1',
|
212
|
+
audio_select: audio_select = data[11],
|
213
|
+
audio_select_name: AUDIO_SELECT_GET.fetch(audio_select),
|
214
|
+
mute: data[12] == '1',
|
215
|
+
# Volume values (0.5 dB increment):
|
216
|
+
# mute: 0
|
217
|
+
# -80.0 dB (min): 39
|
218
|
+
# 0 dB: 199
|
219
|
+
# +14.5 dB (max): 228
|
220
|
+
# Zone2 volume values (1 dB increment):
|
221
|
+
# mute: 0
|
222
|
+
# -33 dB (min): 39
|
223
|
+
# 0 dB (max): 72
|
224
|
+
main_volume: volume = data[15..16].to_i(16),
|
225
|
+
main_volume_db: int_to_half_db(volume),
|
226
|
+
zone2_volume: zone2_volume = data[17..18].to_i(16),
|
227
|
+
zone2_volume_db: int_to_full_db(zone2_volume),
|
228
|
+
zone3_volume: zone3_volume = data[129..130].to_i(16),
|
229
|
+
zone3_volume_db: int_to_full_db(zone3_volume),
|
230
|
+
program: program = data[19..20],
|
231
|
+
program_name: PROGRAM_GET.fetch(program),
|
232
|
+
# true: straight; false: effect
|
233
|
+
effect: data[21] == '1',
|
234
|
+
#extended_surround: data[22],
|
235
|
+
#short_message: data[23],
|
236
|
+
sleep: SLEEP_GET.fetch(data[24]),
|
237
|
+
night: night = data[27],
|
238
|
+
night_name: NIGHT_GET.fetch(night),
|
239
|
+
pure_direct: data[28] == '1',
|
240
|
+
speaker_a: data[29] == '1',
|
241
|
+
speaker_b: data[30] == '1',
|
242
|
+
format: data[31..32],
|
243
|
+
sample_rate: data[33..34],
|
244
|
+
)
|
245
|
+
end
|
246
|
+
@status
|
247
|
+
end
|
248
|
+
|
249
|
+
def remote_command(cmd)
|
250
|
+
dispatch("#{STX}0#{cmd}#{ETX}")
|
251
|
+
end
|
252
|
+
|
253
|
+
def system_command(cmd)
|
254
|
+
dispatch("#{STX}2#{cmd}#{ETX}")
|
255
|
+
end
|
256
|
+
|
257
|
+
def extract_text(resp)
|
258
|
+
# TODO: assert resp[0] == DC1, resp[-1] == ETX
|
259
|
+
resp[0...-1]
|
260
|
+
end
|
261
|
+
|
262
|
+
def int_to_half_db(value)
|
263
|
+
if value == 0
|
264
|
+
:mute
|
265
|
+
else
|
266
|
+
(value - 39) / 2.0 - 80
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def int_to_full_db(value)
|
271
|
+
if value == 0
|
272
|
+
:mute
|
273
|
+
else
|
274
|
+
(value - 39) - 33
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'logger'
|
5
|
+
require 'pp'
|
6
|
+
require 'seriamp/utils'
|
7
|
+
require 'seriamp/detect'
|
8
|
+
require 'seriamp/yamaha/client'
|
9
|
+
|
10
|
+
module Seriamp
|
11
|
+
module Yamaha
|
12
|
+
class Cmd
|
13
|
+
def initialize(args)
|
14
|
+
options = {}
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: yamaha [-d device] command arg..."
|
17
|
+
|
18
|
+
opts.on("-d", "--device DEVICE", "TTY to use (default autodetect)") do |v|
|
19
|
+
options[:device] = v
|
20
|
+
end
|
21
|
+
end.parse!
|
22
|
+
|
23
|
+
@options = options
|
24
|
+
|
25
|
+
@logger = Logger.new(STDERR)
|
26
|
+
@client = Yamaha::Client.new(device: options[:device], logger: @logger)
|
27
|
+
|
28
|
+
@args = args
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :args
|
32
|
+
attr_reader :logger
|
33
|
+
|
34
|
+
def run
|
35
|
+
if args.any?
|
36
|
+
run_command(args)
|
37
|
+
else
|
38
|
+
STDIN.each_line do |line|
|
39
|
+
line.strip!
|
40
|
+
line.sub!(/#.*/, '')
|
41
|
+
next if line.empty?
|
42
|
+
|
43
|
+
run_command(line.strip.split(%r,\s+,))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_command(args)
|
49
|
+
cmd = args.shift
|
50
|
+
unless cmd
|
51
|
+
raise ArgumentError, "No command given"
|
52
|
+
end
|
53
|
+
|
54
|
+
case cmd
|
55
|
+
when 'detect'
|
56
|
+
device = Seriamp.detect_device(Yamaha, *args, logger: logger)
|
57
|
+
if device
|
58
|
+
puts device
|
59
|
+
exit 0
|
60
|
+
else
|
61
|
+
STDERR.puts("Yamaha receiver not found")
|
62
|
+
exit 3
|
63
|
+
end
|
64
|
+
when 'power'
|
65
|
+
which = ARGV.shift&.downcase
|
66
|
+
if %w(main zone2 zone3).include?(which)
|
67
|
+
method = "set_#{which}_power"
|
68
|
+
state = Utils.parse_on_off(ARGV.shift)
|
69
|
+
else
|
70
|
+
method = 'set_power'
|
71
|
+
state = Utils.parse_on_off(which)
|
72
|
+
end
|
73
|
+
client.public_send(method, state)
|
74
|
+
when 'volume'
|
75
|
+
which = ARGV.shift
|
76
|
+
if %w(main zone2 zone3).include?(which)
|
77
|
+
prefix = "set_#{which}"
|
78
|
+
value = ARGV.shift
|
79
|
+
else
|
80
|
+
prefix = 'set_main'
|
81
|
+
value = which
|
82
|
+
end
|
83
|
+
if %w(. -).include?(value)
|
84
|
+
method = "#{prefix}_mute"
|
85
|
+
value = true
|
86
|
+
else
|
87
|
+
method = "#{prefix}_volume_db"
|
88
|
+
if value[0] == ','
|
89
|
+
value = value[1..]
|
90
|
+
end
|
91
|
+
value = Float(value)
|
92
|
+
end
|
93
|
+
client.public_send(method, value)
|
94
|
+
p client.get_main_volume_text
|
95
|
+
p client.get_zone2_volume_text
|
96
|
+
p client.get_zone3_volume_text
|
97
|
+
when 'input'
|
98
|
+
which = ARGV.shift&.downcase
|
99
|
+
if %w(main zone2 zone3).include?(which)
|
100
|
+
method = "set_#{which}_input"
|
101
|
+
input = ARGV.shift
|
102
|
+
else
|
103
|
+
method = 'set_main_input'
|
104
|
+
input = which
|
105
|
+
end
|
106
|
+
client.public_send(method, input)
|
107
|
+
when 'program'
|
108
|
+
value = ARGV.shift.downcase
|
109
|
+
client.set_program(value)
|
110
|
+
when 'pure-direct'
|
111
|
+
state = Utils.parse_on_off(ARGV.shift)
|
112
|
+
client.set_pure_direct(state)
|
113
|
+
when 'status'
|
114
|
+
pp client.last_status
|
115
|
+
when 'status_string'
|
116
|
+
puts client.last_status_string
|
117
|
+
when 'test'
|
118
|
+
client.set_power(false)
|
119
|
+
[true, false].each do |main_state|
|
120
|
+
[true, false].each do |zone2_state|
|
121
|
+
[true, false].each do |zone3_state|
|
122
|
+
client.set_main_power(main_state)
|
123
|
+
client.set_zone2_power(zone2_state)
|
124
|
+
client.set_zone3_power(zone3_state)
|
125
|
+
puts "#{main_state ?1:0} #{zone2_state ?1:0} #{zone3_state ?1:0} #{client.status[:power]}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
else
|
130
|
+
raise ArgumentError, "Unknown command: #{cmd}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
attr_reader :client
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seriamp
|
4
|
+
module Yamaha
|
5
|
+
module Protocol
|
6
|
+
module Constants
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
PROGRAM_SET = {
|
11
|
+
'munich' => 'E1',
|
12
|
+
'vienna' => 'E5',
|
13
|
+
'amsterdam' => 'E6',
|
14
|
+
'freiburg' => 'E8',
|
15
|
+
'chamber' => 'AF',
|
16
|
+
'village_vanguard' => 'EB',
|
17
|
+
'warehouse_loft' => 'EE',
|
18
|
+
'cellar_club' => 'CD',
|
19
|
+
'the_bottom_line' => 'EC',
|
20
|
+
'the_roxy_theatre' => 'ED',
|
21
|
+
'disco' => 'F0',
|
22
|
+
'game' => 'F2',
|
23
|
+
'7ch_stereo' => 'FF',
|
24
|
+
'2ch_stereo' => 'C0',
|
25
|
+
'sports' => 'F8',
|
26
|
+
'action_game' => 'F2',
|
27
|
+
'roleplaying_game' => 'CE',
|
28
|
+
'music_video' => 'F3',
|
29
|
+
'recital_opera' => 'F5',
|
30
|
+
'standard' => 'FE',
|
31
|
+
'spectacle' => 'F9',
|
32
|
+
'sci-fi' => 'FA',
|
33
|
+
'adventure' => 'FB',
|
34
|
+
'drama' => 'FC',
|
35
|
+
'mono_movie' => 'F7',
|
36
|
+
'surround_decode' => 'FD',
|
37
|
+
'thx_cinema' => 'C2',
|
38
|
+
'thx_music' => 'C3',
|
39
|
+
'thx_game' => 'C8',
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
MAIN_INPUTS_SET = {
|
43
|
+
'phono' => '14',
|
44
|
+
'cd' => '15',
|
45
|
+
'tuner' => '16',
|
46
|
+
'cd_r' => '19',
|
47
|
+
'md_tape' => '18',
|
48
|
+
'dvd' => 'C1',
|
49
|
+
'dtv' => '54',
|
50
|
+
'cbl_sat' => 'C0',
|
51
|
+
'vcr1' => '0F',
|
52
|
+
'dvr_vcr2' => '13',
|
53
|
+
'v_aux_dock' => '55',
|
54
|
+
'multi_ch' => '87',
|
55
|
+
'xm' => 'B4',
|
56
|
+
}.freeze
|
57
|
+
|
58
|
+
ZONE2_INPUTS_SET = {
|
59
|
+
'phono' => 'D0',
|
60
|
+
'cd' => 'D1',
|
61
|
+
'tuner' => 'D2',
|
62
|
+
'cd_r' => 'D4',
|
63
|
+
'md_tape' => 'D3',
|
64
|
+
'dvd' => 'CD',
|
65
|
+
'dtv' => 'D9',
|
66
|
+
'cbl_sat' => 'CC',
|
67
|
+
'vcr1' => 'D6',
|
68
|
+
'dvr_vcr2' => 'D7',
|
69
|
+
'v_aux_dock' => 'D8',
|
70
|
+
'xm' => 'B8',
|
71
|
+
}.freeze
|
72
|
+
|
73
|
+
ZONE3_INPUTS_SET = {
|
74
|
+
'phono' => 'F1',
|
75
|
+
'cd' => 'F2',
|
76
|
+
'tuner' => 'F3',
|
77
|
+
'cd_r' => 'F5',
|
78
|
+
'md_tape' => 'F4',
|
79
|
+
'dvd' => 'FC',
|
80
|
+
'dtv' => 'F6',
|
81
|
+
'cbl_sat' => 'F7',
|
82
|
+
'vcr1' => 'F9',
|
83
|
+
'dvr_vcr2' => 'FA',
|
84
|
+
'v_aux_dock' => 'F0',
|
85
|
+
'xm' => 'B9',
|
86
|
+
}.freeze
|
87
|
+
|
88
|
+
MAIN_INPUTS_GET = {
|
89
|
+
'0' => 'PHONO',
|
90
|
+
'1' => 'CD',
|
91
|
+
'2' => 'TUNER',
|
92
|
+
'3' => 'CD-R',
|
93
|
+
'4' => 'MD/TAPE',
|
94
|
+
'5' => 'DVD',
|
95
|
+
'6' => 'DTV',
|
96
|
+
'7' => 'CBL/SAT',
|
97
|
+
'8' => 'SAT',
|
98
|
+
'9' => 'VCR1',
|
99
|
+
'A' => 'DVR/VCR2',
|
100
|
+
'B' => 'VCR3/DVR',
|
101
|
+
'C' => 'V-AUX/DOCK',
|
102
|
+
'D' => 'NET/USB',
|
103
|
+
'E' => 'XM',
|
104
|
+
}.freeze
|
105
|
+
|
106
|
+
AUDIO_SELECT_GET = {
|
107
|
+
'0' => 'Auto', # Confirmed RX-V1500
|
108
|
+
'2' => 'DTS', # Confirmed RX-V1500
|
109
|
+
'3' => 'Coax / Opt', # Unconfirmed
|
110
|
+
'4' => 'Analog', # Confirmed RX-V1500
|
111
|
+
'5' => 'Analog Only', # Unconfirmed
|
112
|
+
'8' => 'HDMI', # Unconfirmed
|
113
|
+
}.freeze
|
114
|
+
|
115
|
+
NIGHT_GET = {
|
116
|
+
'0' => 'Off',
|
117
|
+
'1' => 'Cinema',
|
118
|
+
'2' => 'Music',
|
119
|
+
}.freeze
|
120
|
+
|
121
|
+
SLEEP_GET = {
|
122
|
+
'0' => 120,
|
123
|
+
'1' => 90,
|
124
|
+
'2' => 60,
|
125
|
+
'3' => 30,
|
126
|
+
'4' => nil,
|
127
|
+
}.freeze
|
128
|
+
|
129
|
+
PROGRAM_GET = {
|
130
|
+
'00' => 'Munich',
|
131
|
+
'01' => 'Hall B',
|
132
|
+
'02' => 'Hall C',
|
133
|
+
'04' => 'Hall D',
|
134
|
+
'05' => 'Vienna',
|
135
|
+
'06' => 'Live Concert',
|
136
|
+
'07' => 'Hall in Amsterdam',
|
137
|
+
'08' => 'Tokyo',
|
138
|
+
'09' => 'Freiburg',
|
139
|
+
'0A' => 'Royaumont',
|
140
|
+
'0B' => 'Chamber',
|
141
|
+
'0C' => 'Village Gate',
|
142
|
+
'0D' => 'Village Vanguard',
|
143
|
+
'0E' => 'The Bottom Line',
|
144
|
+
'0F' => 'Cellar Club',
|
145
|
+
'10' => 'The Roxy Theater',
|
146
|
+
'11' => 'Warehouse Loft',
|
147
|
+
'12' => 'Arena',
|
148
|
+
'14' => 'Disco',
|
149
|
+
'15' => 'Party',
|
150
|
+
'17' => '7ch Stereo',
|
151
|
+
'18' => 'Music Video',
|
152
|
+
'19' => 'DJ',
|
153
|
+
'1C' => 'Recital/Opera',
|
154
|
+
'1D' => 'Pavilion',
|
155
|
+
'1E' => 'Action Gamae',
|
156
|
+
'1F' => 'Role Playing Game',
|
157
|
+
'20' => 'Mono Movie',
|
158
|
+
'21' => 'Sports',
|
159
|
+
'24' => 'Spectacle',
|
160
|
+
'25' => 'Sci-Fi',
|
161
|
+
'28' => 'Adventure',
|
162
|
+
'29' => 'Drama',
|
163
|
+
'2C' => 'Surround Decode',
|
164
|
+
'2D' => 'Standard',
|
165
|
+
'30' => 'PLII Movie',
|
166
|
+
'31' => 'PLII Music',
|
167
|
+
'32' => 'Neo:6 Movie',
|
168
|
+
'33' => 'Neo:6 Music',
|
169
|
+
'34' => '2ch Stereo',
|
170
|
+
'35' => 'Direct Stereo',
|
171
|
+
'36' => 'THX Cinema',
|
172
|
+
'37' => 'THX Music',
|
173
|
+
'3C' => 'THX Game',
|
174
|
+
'40' => 'Enhancer 2ch Low',
|
175
|
+
'41' => 'Enhancer 2ch High',
|
176
|
+
'42' => 'Enhancer 7ch Low',
|
177
|
+
'43' => 'Enhancer 7ch High',
|
178
|
+
'80' => 'Straight',
|
179
|
+
}.freeze
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|