seriamp 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|