2024-05-11 Updates
This commit is contained in:
parent
a8fa0b4de3
commit
2e700eb1ba
382
src/cwtest/cfg/gutim_full/20240511_scriabin.cfg
Normal file
382
src/cwtest/cfg/gutim_full/20240511_scriabin.cfg
Normal file
@ -0,0 +1,382 @@
|
||||
{
|
||||
test: {
|
||||
|
||||
preset_sel: {
|
||||
|
||||
params: {
|
||||
score_fn: "~/src/cwtest/src/cwtest/cfg/score/temp.csv",
|
||||
|
||||
perfDirL: [
|
||||
|
||||
{
|
||||
dir:"~/src/cwtest/src/cwtest/cfg/gutim_full/data/score",
|
||||
fname:"temp.csv",
|
||||
vel_table: [
|
||||
{ "device":"piano", "name":"spirio" }, // VSL uses Ivory vel table
|
||||
{ "device":"sampler", "name":"ivory" }
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
dir:"~/src/cwtest/src/cwtest/cfg/gutim_full/data/score_scriabin",
|
||||
fname:"temp_with_scriabin_1.csv",
|
||||
vel_table: [
|
||||
{ "device":"piano", "name":"spirio" }, // VSL uses Ivory vel table
|
||||
{ "device":"sampler", "name":"ivory" }
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
]
|
||||
|
||||
|
||||
record_dir: "~/src/cwtest/src/cwtest/cfg/gutim_full/preset_select",
|
||||
record_fn: "m1_458_trans_5_scriabin_1",
|
||||
record_fn_ext: "txt",
|
||||
|
||||
flow_proc_dict_fn: "~/src/cwtest/src/cwtest/cfg/gutim_full/flow_proc_dict.cfg",
|
||||
vel_table_fname: "~/src/cwtest/src/cwtest/cfg/gutim_full/vel_table/vel_table_perf.json"
|
||||
vel_table_backup_dir: "~/src/cwtest/src/cwtest/cfg/gutim_full/vel_table/backup"
|
||||
|
||||
//crossFadeSrate: 48000.0, // TODO: move to flow cfg. and set via 'system default sample rate'
|
||||
crossFadeCount: 3,
|
||||
|
||||
beg_play_loc: 1, //0, //12431, // coda 11499,
|
||||
end_play_loc: 16354, //59, //14726, // coda 12426,
|
||||
live_mode_fl: false,
|
||||
dflt_perf_label: "beck_1_5",
|
||||
run_dur_secs: 0,
|
||||
|
||||
enable_recording_fl: false,
|
||||
midi_record_dir: "~/src/cwtest/src/cwtest/cfg/gutim_full/midi_record",
|
||||
midi_record_folder: "shiau_uen",
|
||||
sf_reset_loc: 1,
|
||||
|
||||
score_follower: {
|
||||
enable_flag: false,
|
||||
score_csv_fname: "/home/kevin/src/cwtest/src/cwtest/cfg/gutim_full/temp.csv",
|
||||
search_area_locN: 10,
|
||||
key_wnd_locN: 7,
|
||||
track_print_fl: false, // print output from the sfTrack unit
|
||||
track_results_backtrack_fl: false,
|
||||
|
||||
|
||||
dyn_ref: [
|
||||
{ mark: "silent", level:0, vel:1 },
|
||||
{ mark:"pppp-", level:1, vel:3 },
|
||||
{ mark:"pppp", level:2, vel:5 },
|
||||
{ mark:"pppp+", level:3, vel:7 },
|
||||
{ mark:"ppp-", level:4, vel:10 },
|
||||
{ mark:"ppp", level:5, vel:15 },
|
||||
{ mark:"ppp+", level:6, vel:20 },
|
||||
{ mark:"pp-", level:7, vel:25 },
|
||||
{ mark:"pp", level:8, vel:30 },
|
||||
{ mark:"pp+", level:9, vel:35 },
|
||||
{ mark:"p-", level:10, vel:40 },
|
||||
{ mark:"p", level:11, vel:45 },
|
||||
{ mark:"p+", level:12, vel:50 },
|
||||
{ mark:"mp-", level:13, vel:55 },
|
||||
{ mark:"mp", level:14, vel:60 },
|
||||
{ mark:"mp+", level:15, vel:65 },
|
||||
{ mark:"mf-", level:16, vel:70 },
|
||||
{ mark:"mf", level:17, vel:75 },
|
||||
{ mark:"mf+", level:18, vel:80 },
|
||||
{ mark:"f-", level:19, vel:85 },
|
||||
{ mark:"f", level:20, vel:90 },
|
||||
{ mark:"f+", level:21, vel:95 },
|
||||
{ mark:"ff", level:22, vel:100 },
|
||||
{ mark:"ff+", level:23, vel:105 },
|
||||
{ mark:"fff", level:24, vel:115 },
|
||||
{ mark:"fff+", level:25, vel:120 },
|
||||
{ mark:"ffff", level:26, vel:125 },
|
||||
]
|
||||
|
||||
},
|
||||
|
||||
|
||||
presets: {
|
||||
preset_labelL: [ "dry", "a", "b", "c", "d", "f1", "f2", "f3", "f4", "g", "ga", "g1a", "g1d" ],
|
||||
|
||||
alt_labelL: [ "A","B","C","D","E","F","G" ],
|
||||
|
||||
default_gain: 1.0,
|
||||
default_wet_dry_gain: 0.5,
|
||||
default_fade_ms: 50.0,
|
||||
default_preset: "dry",
|
||||
|
||||
default_master_wet_in_gain: 1.0,
|
||||
default_master_wet_out_gain: 1.0,
|
||||
default_master_dry_gain: 1.0,
|
||||
default_master_sync_delay_ms: 400, // spirio 400
|
||||
|
||||
},
|
||||
|
||||
midi_play_record: {
|
||||
max_midi_msg_count: 32768,
|
||||
midi_timer_period_micro_sec: 15000,
|
||||
all_off_delay_ms: 2500, // delay after stop time to turn off all notes
|
||||
log_in_flag: false,
|
||||
log_out_flag: false,
|
||||
half_pedal_flag: false,
|
||||
min_damper_down_time_ms: 0,
|
||||
|
||||
midi_device_list: [
|
||||
{
|
||||
// SAMPLER
|
||||
enableFl: true,
|
||||
|
||||
label: "sampler",
|
||||
//midi_out_device: "Fastlane",
|
||||
//midi_out_port: "Fastlane MIDI B",
|
||||
|
||||
//midi_out_device: "MIDIFACE 2x2",
|
||||
//midi_out_port: "MIDIFACE 2x2 Midi Out 1",
|
||||
|
||||
midi_out_device: "Scarlett 18i20 USB",
|
||||
midi_out_port: "Scarlett 18i20 USB MIDI 1",
|
||||
|
||||
//midi_out_device: "PipeWire-RT-Event",
|
||||
//midi_out_port: "input",
|
||||
|
||||
force_damper_down_fl: false,
|
||||
force_damper_down_threshold: 35,
|
||||
force_damper_down_velocity: 80,
|
||||
|
||||
scale_chord_notes_enable_fl: false,
|
||||
scale_chord_notes_factor: 0.05,
|
||||
|
||||
},
|
||||
{
|
||||
//SPIRIO
|
||||
|
||||
enableFl: true,
|
||||
label: "piano",
|
||||
//midi_out_device: "Fastlane",
|
||||
//midi_out_port: "Fastlane MIDI A",
|
||||
|
||||
//midi_out_device: "MIDIFACE 2x2",
|
||||
//midi_out_port: "MIDIFACE 2x2 Midi Out 2",
|
||||
|
||||
midi_out_device: "iRig MIDI 2",
|
||||
midi_out_port: "iRig MIDI 2 MIDI 1",
|
||||
|
||||
//midi_out_device: "PC-300",
|
||||
//midi_out_port: "PC-300 MIDI 1",
|
||||
|
||||
//midi_out_device: "PipeWire-RT-Event",
|
||||
//midi_out_port: "input",
|
||||
|
||||
// pedal down velocity input/output mapping
|
||||
/*
|
||||
pedal: {
|
||||
up_id: 0,
|
||||
up_vel: 0,
|
||||
down_id: 127,
|
||||
down_vel: 0,
|
||||
half_down_id: 64,
|
||||
half_down_vel: 43,
|
||||
half_up_id: 63,
|
||||
half_up_vel: 43
|
||||
|
||||
},
|
||||
*/
|
||||
|
||||
force_damper_down_fl: false,
|
||||
force_damper_down_threshold: 35,
|
||||
force_damper_down_velocity: 80,
|
||||
|
||||
scale_chord_notes_enable_fl: false,
|
||||
scale_chord_notes_factor: 0.5,
|
||||
},
|
||||
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
io: {
|
||||
callbackMutexTimeOutMs: 100,
|
||||
}
|
||||
|
||||
ui: {
|
||||
enableFl: true,
|
||||
physRootDir: "~/src/cwtest/src/libcw/html/preset_sel",
|
||||
dfltPageFn: "index.html",
|
||||
port: 5687,
|
||||
rcvBufByteN: 2048,
|
||||
xmtBufByteN: 2048,
|
||||
fmtBufByteN: 4096,
|
||||
websockTimeOutMs: 25, // max time out while blocking for a websock event
|
||||
queueBlkCnt: 8,
|
||||
queueBlkByteCnt: 32768,
|
||||
idleMsgPeriodMs: 50, // period without messages before an idle message is generated
|
||||
uiCfgFn: "ui.cfg", // default UI resource description
|
||||
asyncFl: false
|
||||
},
|
||||
|
||||
serial: {
|
||||
enableFl: false,
|
||||
pollPeriodMs: 50,
|
||||
recvBufByteN: 512,
|
||||
|
||||
array: [
|
||||
{
|
||||
enableFl: false,
|
||||
asyncFl: false,
|
||||
label: "port1", // User label
|
||||
device: "/dev/ttyUSB0", // Serial device name
|
||||
baud: 115200,
|
||||
bits: 8,
|
||||
stop: 1,
|
||||
parity: "no",
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
midi: {
|
||||
enableFl: true,
|
||||
asyncFl: true,
|
||||
parseBufByteCnt: 1024,
|
||||
appNameStr: "cwtest",
|
||||
fileDevName: "file_dev",
|
||||
fileDevReadAheadMicros: 3000,
|
||||
testFileLabel: "file_0",
|
||||
testFileEnableFl: false
|
||||
|
||||
file_ports: [
|
||||
|
||||
{ "label":"file_0",
|
||||
//"file": "/home/kevin/src/cwtest/src/cwtest/cfg/gutim_full/data1/beck1/record_4/midi.mid",
|
||||
"enable_fl": false },
|
||||
]
|
||||
|
||||
},
|
||||
|
||||
audio: {
|
||||
enableFl: true,
|
||||
|
||||
meterMs: 50, // audio meter filter length and meter callback period
|
||||
threadTimeOutMs: 50, // audio thread cond var time out
|
||||
|
||||
groupL: [
|
||||
{
|
||||
enableFl: true, // (req)
|
||||
asyncFl: true,
|
||||
label: "main", // (req) User label
|
||||
id: 0, // (req) User id (can also be set at runtime)
|
||||
srate: 48000, // (req) Sample rate used by all devices in this group
|
||||
dspFrameCnt: 64 // (req) Size of DSP processing buffers
|
||||
}
|
||||
],
|
||||
|
||||
deviceL: [
|
||||
{
|
||||
// System device name
|
||||
|
||||
device: "Scarlett 18i20 USB USB Audio",
|
||||
//device: "USB Audio CODEC USB Audio",
|
||||
//device: "HDA Intel PCH CS4208 Analog",
|
||||
|
||||
activeFl: true, // (req)
|
||||
meterFl: true, // (opt)
|
||||
label: "main", // (req) User label
|
||||
userId: 0, // (opt) User id (can also be set at runtime)
|
||||
framesPerCycle: 512, // (req) Samples per audio device cycle
|
||||
cycleCnt: 3, // (req) Count of device cycle buffers.
|
||||
inGroup: "main", // (opt) All devices in a group must be 'ready' to source
|
||||
outGroup: "main", // (opt) or sink data before an audio callback is made for that group
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
socket: {
|
||||
enableFl: false,
|
||||
asyncFl: false,
|
||||
maxSocketCnt: 10,
|
||||
recvBufByteCnt: 4096,
|
||||
threadTimeOutMs: 50,
|
||||
socketL: [],
|
||||
}
|
||||
|
||||
flow: {
|
||||
framesPerCycle: 64, // time-domain audio cycles frame per cycle (must match audio 'dspFrameCnt')
|
||||
multiPriPresetProbFl: false, // Use probability to select primary multi-preset
|
||||
multiSecPresetProbFl: false, // Use probability to select secondary multi-preset
|
||||
multiPresetInterpFl: false, // Interpolate between two selected multi-presets
|
||||
maxCycleCount: 0, // 0 disables maxCycleCount
|
||||
printNetworkFl: false, // print the network instance
|
||||
printClassDictFl: false, // print the class description dictionary
|
||||
|
||||
|
||||
network: {
|
||||
aud_in: { class: audio_in, args:{ default:{dev_label:"main"} } },
|
||||
|
||||
// select the first six channels: ivory, mic, vsl
|
||||
split_in: { class: audio_split, in:{ in:aud_in.out } args:{ default:{select[1,1,1,1,1,1 ]}} }
|
||||
|
||||
// delay the incoming audio signal to sync it with the piano
|
||||
sync_delay { class: audio_delay, in: { in:split_in.out }, args:{ default:{ delayMs:400 } }}
|
||||
|
||||
// select the first two channels to feed into the transform
|
||||
sync_split: { class: audio_split, in:{ in:sync_delay.out } args:{ default:{select[1,1]}} }
|
||||
|
||||
mstr_wet_in_gain: { class: audio_gain, in:{ in:sync_split.out } }
|
||||
|
||||
// wet signal processing chain
|
||||
wet_in_gain: { class: audio_gain, in:{ in:mstr_wet_in_gain.out } },
|
||||
|
||||
pva: { class: pv_analysis, in:{ in:wet_in_gain.out }, args:{ default:{ wndSmpN:512, hopSmpN:128, hzFl:false } } },
|
||||
sd: { class: spec_dist, in:{ in:pva.out }, preset:kc, args:{ bypass:false } },
|
||||
pvs: { class: pv_synthesis, in:{ in:sd.out } },
|
||||
cmp: { class: compressor, in:{ in:pvs.out }, preset:kc, args:{ bypass:false } },
|
||||
|
||||
wet_out_gain: { class: audio_gain, in:{ in:cmp.out } },
|
||||
|
||||
//wet_out_gain: { class: audio_gain, in:{ in:pvs.out } },
|
||||
|
||||
mute_wet: { class: audio_gain, in:{ in:wet_out_gain.out } },
|
||||
|
||||
// apply the wet/dry gain balance
|
||||
wd_bal: { class: balance, args{}},
|
||||
wet_bal_gain: { class: audio_gain, in:{ in:mute_wet.out, gain:wd_bal.out } },
|
||||
dry_bal_gain: { class: audio_gain, in:{ in:sync_split.out, gain:wd_bal.inv_out } },
|
||||
|
||||
mstr_wet_out_gain: { class: audio_gain, in:{ in:wet_bal_gain.out } }
|
||||
mstr_dry_out_gain: { class: audio_gain, in:{ in:dry_bal_gain.out } },
|
||||
|
||||
|
||||
// merge the wet/dry signals into a single 4 channel signal
|
||||
merge: { class: audio_merge, in:{ in0:mstr_wet_out_gain.out, in1:mstr_dry_out_gain.out, in2:mstr_wet_out_gain.out, in3:mstr_dry_out_gain.out } },
|
||||
|
||||
aout: { class: audio_out, in:{ in:merge.out }, args:{ default:{dev_label:"main"} } },
|
||||
|
||||
//af_merge { class: audio_merge, in:{ in0:mstr_wet_out_gain.out, in1:sync_delay.out } },
|
||||
|
||||
//af_out: { class: audioFileOut, in:{ in:af_merge.out }, args:{ default:{fname:"/home/kevin/temp/temp.wav"}}},
|
||||
|
||||
}
|
||||
|
||||
presets: {
|
||||
|
||||
dry: { pva:dry, sd:dry, cmp:dry, mute_wet:{ gain:0.0 } },
|
||||
a: { pva:a, sd:a, cmp:a, mute_wet:{ gain:1.0 } },
|
||||
b: { pva:b, sd:b, cmp:b, mute_wet:{ gain:1.0 } },
|
||||
c: { pva:c, sd:c, cmp:c, mute_wet:{ gain:1.0 } },
|
||||
d: { pva:d, sd:d, cmp:d, mute_wet:{ gain:1.0 } },
|
||||
f1: { pva:f_1, sd:f_1, cmp:f_1, mute_wet:{ gain:1.0 } },
|
||||
f2: { pva:f_2, sd:f_2, cmp:f_2, mute_wet:{ gain:1.0 } },
|
||||
f3: { pva:f_3, sd:f_3, cmp:f_3, mute_wet:{ gain:1.0 } },
|
||||
f4: { pva:f_4, sd:f_4, cmp:f_4, mute_wet:{ gain:1.0 } },
|
||||
g: { pva:g, sd:g, cmp:g, mute_wet:{ gain:1.0 } },
|
||||
ga: { pva:g_a sd:g_a, cmp:g_a, mute_wet:{ gain:1.0 } },
|
||||
g1a: { pva:g_1_a sd:g_1_a, cmp:g_1_a, mute_wet:{ gain:1.0 } },
|
||||
g1d: { pva:g_1_d sd:g_1_d, cmp:g_1_d, mute_wet:{ gain:1.0 } }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -64,7 +64,7 @@ def parse_scriabin( ifn ):
|
||||
return noteL
|
||||
|
||||
|
||||
def insert_scriabin( scoreL, noteL, src_label, insert_loc, insert_after_fl ):
|
||||
def insert_scriabin( scoreL, noteL, src_label, insert_loc, insert_after_fl, delta_sec ):
|
||||
|
||||
status_map = { 'non':144, 'nof':144, 'ped':176, 'ctl':176 }
|
||||
type_map = { 'non':'non', 'nof':'nof', 'ped':'ctl', 'ctl':'ctl' }
|
||||
@ -97,7 +97,7 @@ def insert_scriabin( scoreL, noteL, src_label, insert_loc, insert_after_fl ):
|
||||
for n in noteL:
|
||||
d = { f:None for f in fieldL }
|
||||
dur_sec = max(dur_sec,n['sec'])
|
||||
d['sec'] = offs_sec + n['sec']
|
||||
d['sec'] = offs_sec + delta_sec + n['sec']
|
||||
d['opcode'] = type_map[ n['type'].strip() ]
|
||||
d['status'] = status_map[n['type'].strip()]
|
||||
d['d0'] = n['d0']
|
||||
@ -243,7 +243,7 @@ def gen_reference( scoreL, out_dir, out_fn ):
|
||||
f.write(s)
|
||||
|
||||
if r0 and (r0['src'] == 'gutim' and r['src'] != 'gutim'):
|
||||
print(r['src'],r['oloc'],end=" ")
|
||||
print(r['src'],r['oloc'],meas,end=" ")
|
||||
|
||||
if r0 and (r0['src'] != 'gutim' and r['src'] == 'gutim'):
|
||||
print(r0['oloc'])
|
||||
@ -284,11 +284,11 @@ if __name__ == "__main__":
|
||||
score_fn = "data/score/20231028/temp.csv"
|
||||
|
||||
ref_fn = "ref.txt"
|
||||
out_fn = "temp_with_scriabin_0.csv"
|
||||
out_fn = "temp_with_scriabin_1.csv"
|
||||
out_dir= "data/score_scriabin/20240428"
|
||||
|
||||
in_preset_fn = "preset_select/m1_458_trans_5.txt"
|
||||
out_preset_fn = "preset_select/m1_458_trans_5_scriabin.txt"
|
||||
out_preset_fn = "preset_select/m1_458_trans_5_scriabin_1.txt"
|
||||
|
||||
score_fn = os.path.join(base_dir, score_fn)
|
||||
out_dir = os.path.join(base_dir, out_dir)
|
||||
@ -360,6 +360,44 @@ if __name__ == "__main__":
|
||||
|
||||
]
|
||||
|
||||
fileL = [
|
||||
|
||||
# *
|
||||
{ "src":"74_1", "ifn":"scriabin_prelude_op74_1.csv", "insert_loc": 1229, "after_fl":False, "ofn":"scriabin_op74_1", "beg_loc":417, "end_loc":418, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"74_2", "ifn":"scriabin_prelude_op74_2.csv", "insert_loc": 1867, "after_fl":False, "ofn":"scriabin_op74_2", "beg_loc":638, "end_loc":639, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"74_4", "ifn":"scriabin_prelude_op74_4.csv", "insert_loc": 2909, "after_fl":True, "ofn":"scriabin_op74_4", "beg_loc":1010, "end_loc":1011, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"74_3", "ifn":"scriabin_prelude_op74_3.csv", "insert_loc": 4084, "after_fl":False, "ofn":"scriabin_op74_3", "beg_loc":1383, "end_loc":1384, "delta_sec":0.0 },
|
||||
|
||||
|
||||
|
||||
#
|
||||
#{ "src":"65_1", "ifn":"scriabin_etude_op65_1_allegro fantastico.csv", "insert_loc": 6323, "after_fl":True, "ofn":"scriabin_65_1", "beg_loc":2763, "end_loc":2764, "delta_sec":0.0 },
|
||||
{ "src":"65_1", "ifn":"scriabin_etude_op65_1_allegro fantastico.csv", "insert_loc": 6376, "after_fl":False, "ofn":"scriabin_65_1", "beg_loc":2804, "end_loc":2805, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"8_3", "ifn":"scriabin_etude_op8_3_b_minor.csv", "insert_loc": 8450, "after_fl":False, "ofn":"scriabin_8_3", "beg_loc":3832, "end_loc":3883, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"74_5", "ifn":"scriabin_prelude_op74_5.csv", "insert_loc": 9567, "after_fl":False, "ofn":"scriabin_op74_5", "beg_loc":4470, "end_loc":4471, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"65_2", "ifn":"scriabin_etude_op65_2_allegretto.csv", "insert_loc": 10789, "after_fl":False, "ofn":"scriabin_65_2", "beg_loc":5440, "end_loc":5441, "delta_sec":0.0 },
|
||||
|
||||
# *
|
||||
{ "src":"42_7", "ifn":"scriabin_etude_op42_7_f_minor.csv", "insert_loc":12428, "after_fl":False, "ofn":"scriabin_42_7", "beg_loc":6043, "end_loc":6044, "delta_sec":0.0 },
|
||||
|
||||
#{ "src":"65_3", "ifn":"scriabin_etude_op65_3_molta_vivace.csv", "insert_loc": 13848, "after_fl":True, "ofn":"scriabin_65_3", "beg_loc":6740, "end_loc":6741, "delta_sec":0.0 },
|
||||
{ "src":"65_3", "ifn":"scriabin_etude_op65_3_molta_vivace.csv", "insert_loc": 13953, "after_fl":True, "ofn":"scriabin_65_3", "beg_loc":6780, "end_loc":6781, "delta_sec":0.0 },
|
||||
|
||||
|
||||
]
|
||||
|
||||
scoreL = parse_score(score_fn)
|
||||
|
||||
fieldnamesL = list(scoreL[0].keys())
|
||||
@ -370,7 +408,7 @@ if __name__ == "__main__":
|
||||
|
||||
noteL = parse_scriabin(f['ifn'])
|
||||
|
||||
scoreL = insert_scriabin(scoreL, noteL, f['src'], f['insert_loc'], f['after_fl'] )
|
||||
scoreL = insert_scriabin(scoreL, noteL, f['src'], f['insert_loc'], f['after_fl'], f['delta_sec'] )
|
||||
|
||||
|
||||
assign_loc(scoreL)
|
||||
|
Loading…
Reference in New Issue
Block a user