Add orchestral sampled instruments
This commit is contained in:
parent
2222e68a3e
commit
84f1f8e7d7
|
|
@ -231,7 +231,7 @@ impl Voice {
|
|||
envelope_phase: EnvelopePhase::Attack,
|
||||
envelope_value: 0.0,
|
||||
crossfade_buffer: Vec::new(),
|
||||
crossfade_length: 1000, // ~20ms at 48kHz (longer for smoother loops)
|
||||
crossfade_length: 4800, // ~100ms at 48kHz — hides loop seams in sustained instruments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ assets = [
|
|||
["assets/icons/32x32.png", "usr/share/icons/hicolor/32x32/apps/lightningbeam-editor.png", "644"],
|
||||
["assets/icons/128x128.png", "usr/share/icons/hicolor/128x128/apps/lightningbeam-editor.png", "644"],
|
||||
["assets/icons/256x256.png", "usr/share/icons/hicolor/256x256/apps/lightningbeam-editor.png", "644"],
|
||||
# Factory instrument presets and samples — copied to share dir, preserving directory tree
|
||||
["target/release/presets/**/*", "usr/share/lightningbeam-editor/presets/", "644"],
|
||||
]
|
||||
|
||||
[package.metadata.generate-rpm]
|
||||
|
|
@ -121,3 +123,10 @@ mode = "644"
|
|||
source = "assets/icons/256x256.png"
|
||||
dest = "/usr/share/icons/hicolor/256x256/apps/lightningbeam-editor.png"
|
||||
mode = "644"
|
||||
|
||||
# Factory instrument presets and samples (built by build.rs into target dir)
|
||||
[[package.metadata.generate-rpm.assets]]
|
||||
source = "target/release/presets/"
|
||||
dest = "/usr/share/lightningbeam-editor/presets/"
|
||||
mode = "644"
|
||||
doc = false
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ use std::path::PathBuf;
|
|||
fn main() {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
|
||||
// Copy factory instrument presets next to the binary (works on all platforms)
|
||||
bundle_factory_presets();
|
||||
|
||||
if target_os == "windows" {
|
||||
bundle_windows_dlls();
|
||||
}
|
||||
|
|
@ -128,6 +131,56 @@ fn bundle_windows_dlls() {
|
|||
println!("cargo:warning=Bundled FFmpeg DLLs to {}", target_dir.display());
|
||||
}
|
||||
|
||||
/// Copy factory instrument presets into target dir so they're next to the binary.
|
||||
/// This makes them available for:
|
||||
/// - Portable/AppImage builds (preset browser checks <exe>/presets/)
|
||||
/// - Windows builds (same)
|
||||
/// - cargo-deb/rpm packaging (assets reference target/release/presets/)
|
||||
fn bundle_factory_presets() {
|
||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let instruments_src = manifest_dir.join("../../src/assets/instruments");
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let target_dir = PathBuf::from(&out_dir)
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.to_path_buf();
|
||||
let presets_dst = target_dir.join("presets");
|
||||
|
||||
if !instruments_src.exists() {
|
||||
println!("cargo:warning=Factory instruments not found at {:?}, skipping", instruments_src);
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-run if instruments change
|
||||
println!("cargo:rerun-if-changed={}", instruments_src.display());
|
||||
|
||||
if let Err(e) = copy_dir_recursive(&instruments_src, &presets_dst) {
|
||||
println!("cargo:warning=Failed to copy factory presets: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively copy a directory tree, preserving structure.
|
||||
/// Only copies .json and .mp3 files (skips README.md, etc.)
|
||||
fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) -> std::io::Result<()> {
|
||||
fs::create_dir_all(dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let src_path = entry.path();
|
||||
let dst_path = dst.join(entry.file_name());
|
||||
if src_path.is_dir() {
|
||||
copy_dir_recursive(&src_path, &dst_path)?;
|
||||
} else if let Some(ext) = src_path.extension() {
|
||||
let ext = ext.to_string_lossy().to_lowercase();
|
||||
if ext == "json" || ext == "mp3" {
|
||||
fs::copy(&src_path, &dst_path)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_library(lib_name: &str, search_paths: &[&str], lib_dir: &PathBuf) {
|
||||
let mut copied = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,569 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Build factory instrument presets from Virtual Playing Orchestra 3 samples.
|
||||
|
||||
Usage:
|
||||
python3 scripts/build_instruments.py
|
||||
|
||||
Converts WAV samples to MP3 and generates MultiSampler JSON presets.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
VPO = Path.home() / "Downloads" / "Virtual-Playing-Orchestra3" / "libs"
|
||||
INSTRUMENTS_DIR = Path(__file__).parent.parent / "src" / "assets" / "instruments"
|
||||
|
||||
# Note name to semitone offset (within octave)
|
||||
NOTE_MAP = {
|
||||
'c': 0, 'c#': 1, 'db': 1, 'd': 2, 'd#': 3, 'eb': 3,
|
||||
'e': 4, 'f': 5, 'f#': 6, 'gb': 6, 'g': 7, 'g#': 8, 'ab': 8,
|
||||
'a': 9, 'a#': 10, 'bb': 10, 'b': 11,
|
||||
}
|
||||
|
||||
|
||||
def note_to_midi(note_name: str, octave: int) -> int:
|
||||
"""Convert note name + octave to MIDI number. C4 = 60."""
|
||||
semitone = NOTE_MAP[note_name.lower()]
|
||||
return (octave + 1) * 12 + semitone
|
||||
|
||||
|
||||
def parse_sso_filename(filename: str) -> dict | None:
|
||||
"""Parse SSO-style: instrument-sus-note-PB-loop.wav (e.g. 1st-violins-sus-a#3.wav)
|
||||
Also handles flats: oboe-a#3, basses-sus-d#2, etc.
|
||||
"""
|
||||
m = re.search(r'([a-g][#b]?)(\d+)', filename.lower())
|
||||
if not m:
|
||||
return None
|
||||
note, octave = m.group(1), int(m.group(2))
|
||||
midi = note_to_midi(note, octave)
|
||||
return {'midi': midi, 'note': f"{note.upper()}{octave}"}
|
||||
|
||||
|
||||
def parse_nbo_filename(filename: str) -> dict | None:
|
||||
"""Parse NBO-style: octave_note.wav (e.g. 3_Bb-PB-loop.wav)"""
|
||||
m = re.match(r'(\d+)_([A-Ga-g][b#]?)', filename)
|
||||
if not m:
|
||||
return None
|
||||
octave, note = int(m.group(1)), m.group(2)
|
||||
midi = note_to_midi(note, octave)
|
||||
return {'midi': midi, 'note': f"{note}{octave}"}
|
||||
|
||||
|
||||
def parse_nbo_with_dynamics(filename: str) -> dict | None:
|
||||
"""Parse NBO2-style with dynamics: octave_note_p.wav or octave_note.wav"""
|
||||
m = re.match(r'(\d+)_([A-Ga-g][b#]?)(?:_(p|f|mf|ff))?', filename)
|
||||
if not m:
|
||||
return None
|
||||
octave, note = int(m.group(1)), m.group(2)
|
||||
dynamic = m.group(3)
|
||||
midi = note_to_midi(note, octave)
|
||||
return {'midi': midi, 'note': f"{note}{octave}", 'dynamic': dynamic}
|
||||
|
||||
|
||||
def parse_mw_viola_filename(filename: str) -> dict | None:
|
||||
"""Parse MW-style: Violas_note.wav (e.g. Violas_c4.wav, Violas_d#3.wav)"""
|
||||
m = re.search(r'_([a-g][#b]?)(\d+)\.wav', filename.lower())
|
||||
if not m:
|
||||
return None
|
||||
note, octave = m.group(1), int(m.group(2))
|
||||
midi = note_to_midi(note, octave)
|
||||
return {'midi': midi, 'note': f"{note.upper()}{octave}"}
|
||||
|
||||
|
||||
def parse_mw_horn_filename(filename: str) -> dict | None:
|
||||
"""Parse MW horn: horns-sus-ff-note-PB-loop.wav or horns-sus-mp-note-PB-loop.wav"""
|
||||
# Extract dynamics marker (ff, mp) from filename
|
||||
dyn_match = re.search(r'-(ff|mp|mf|p|pp)-', filename.lower())
|
||||
dynamic = dyn_match.group(1) if dyn_match else None
|
||||
# Extract note
|
||||
m = re.search(r'([a-g][#b]?)(\d+)', filename.lower())
|
||||
if not m:
|
||||
return None
|
||||
note, octave = m.group(1), int(m.group(2))
|
||||
midi = note_to_midi(note, octave)
|
||||
return {'midi': midi, 'note': f"{note.upper()}{octave}", 'dynamic': dynamic}
|
||||
|
||||
|
||||
def parse_vsco_harp_filename(filename: str) -> dict | None:
|
||||
"""Parse VSCO harp: KSHarp_Note_dyn.wav (e.g. KSHarp_A4_mf.wav)"""
|
||||
m = re.search(r'KSHarp_([A-G][b#]?)(\d+)', filename)
|
||||
if not m:
|
||||
return None
|
||||
note, octave = m.group(1), int(m.group(2))
|
||||
midi = note_to_midi(note, octave)
|
||||
return {'midi': midi, 'note': f"{note}{octave}"}
|
||||
|
||||
|
||||
def convert_wav_to_mp3(wav_path: Path, mp3_path: Path, bitrate: str = '192k'):
|
||||
"""Convert WAV to MP3 using ffmpeg with peak normalization."""
|
||||
mp3_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if mp3_path.exists():
|
||||
return # Skip if already converted
|
||||
# Peak-normalize to -1dBFS so all samples have consistent max level.
|
||||
# Using dynaudnorm with very gentle settings to avoid changing the
|
||||
# character of the sound — just brings everything to the same peak level.
|
||||
subprocess.run([
|
||||
'ffmpeg', '-i', str(wav_path),
|
||||
'-af', 'loudnorm=I=-16:TP=-1:LRA=11',
|
||||
'-ar', '44100', '-ab', bitrate,
|
||||
'-y', '-loglevel', 'error',
|
||||
str(mp3_path)
|
||||
], check=True)
|
||||
|
||||
|
||||
def compute_key_ranges(layers: list[dict]) -> list[dict]:
|
||||
"""Compute key_min/key_max for each layer by splitting at midpoints between adjacent root notes."""
|
||||
if not layers:
|
||||
return layers
|
||||
layers.sort(key=lambda l: l['root_key'])
|
||||
for i, layer in enumerate(layers):
|
||||
if i == 0:
|
||||
layer['key_min'] = 0
|
||||
else:
|
||||
midpoint = (layers[i-1]['root_key'] + layer['root_key']) // 2 + 1
|
||||
layer['key_min'] = midpoint
|
||||
layers[i-1]['key_max'] = midpoint - 1
|
||||
if i == len(layers) - 1:
|
||||
layer['key_max'] = 127
|
||||
return layers
|
||||
|
||||
|
||||
def make_preset(name: str, description: str, tags: list[str], layers: list[dict],
|
||||
attack: float = 0.01, release: float = 0.3) -> dict:
|
||||
"""Generate a complete instrument preset JSON."""
|
||||
return {
|
||||
"metadata": {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": tags
|
||||
},
|
||||
"midi_targets": [0],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [100.0, 100.0]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": f"{name} Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0, # gain
|
||||
"1": attack, # attack
|
||||
"2": release, # release
|
||||
"3": 0.0 # transpose
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": layers
|
||||
},
|
||||
"position": [350.0, 0.0]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [700.0, 100.0]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{"from_node": 0, "from_port": 0, "to_node": 1, "to_port": 0},
|
||||
{"from_node": 1, "from_port": 0, "to_node": 2, "to_port": 0}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def build_simple_instrument(name: str, description: str, tags: list[str],
|
||||
source_dir: Path, output_subdir: str,
|
||||
filename_filter=None, parser=parse_sso_filename,
|
||||
attack: float = 0.01, release: float = 0.3,
|
||||
loop: bool = False):
|
||||
"""Build a single-velocity instrument from a directory of WAV files."""
|
||||
out_dir = INSTRUMENTS_DIR / output_subdir
|
||||
samples_dir = out_dir / "samples"
|
||||
samples_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
layers = []
|
||||
wav_files = sorted(source_dir.glob("*.wav"))
|
||||
for wav in wav_files:
|
||||
if filename_filter and not filename_filter(wav.name):
|
||||
continue
|
||||
parsed = parser(wav.name)
|
||||
if not parsed:
|
||||
print(f" WARNING: Could not parse {wav.name}, skipping")
|
||||
continue
|
||||
mp3_name = f"{parsed['note']}.mp3"
|
||||
mp3_path = samples_dir / mp3_name
|
||||
print(f" Converting {wav.name} -> {mp3_name} (MIDI {parsed['midi']})")
|
||||
convert_wav_to_mp3(wav, mp3_path)
|
||||
layer = {
|
||||
"file_path": f"samples/{mp3_name}",
|
||||
"root_key": parsed['midi'],
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
}
|
||||
if loop:
|
||||
layer["loop_mode"] = "continuous"
|
||||
layers.append(layer)
|
||||
|
||||
layers = compute_key_ranges(layers)
|
||||
preset = make_preset(name, description, tags, layers, attack, release)
|
||||
preset_path = out_dir / f"{output_subdir.split('/')[-1]}.json"
|
||||
with open(preset_path, 'w') as f:
|
||||
json.dump(preset, f, indent=2)
|
||||
print(f" -> Wrote {preset_path} ({len(layers)} layers)")
|
||||
return layers
|
||||
|
||||
|
||||
def build_dynamics_instrument(name: str, description: str, tags: list[str],
|
||||
source_dir: Path, output_subdir: str,
|
||||
filename_filter=None, parser=parse_nbo_with_dynamics,
|
||||
attack: float = 0.01, release: float = 0.3,
|
||||
loop: bool = False):
|
||||
"""Build an instrument with velocity layers from dynamics markings."""
|
||||
out_dir = INSTRUMENTS_DIR / output_subdir
|
||||
samples_dir = out_dir / "samples"
|
||||
samples_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Group samples by dynamics level
|
||||
# Map dynamics markings to velocity ranges (soft to loud)
|
||||
DYNAMICS_ORDER = ['pp', 'p', 'mp', 'mf', 'f', 'ff']
|
||||
dynamics_groups: dict[str | None, list[dict]] = {}
|
||||
|
||||
wav_files = sorted(source_dir.glob("*.wav"))
|
||||
for wav in wav_files:
|
||||
if filename_filter and not filename_filter(wav.name):
|
||||
continue
|
||||
parsed = parser(wav.name)
|
||||
if not parsed:
|
||||
print(f" WARNING: Could not parse {wav.name}, skipping")
|
||||
continue
|
||||
dyn = parsed.get('dynamic')
|
||||
suffix = f"_{dyn}" if dyn else ""
|
||||
mp3_name = f"{parsed['note']}{suffix}.mp3"
|
||||
mp3_path = samples_dir / mp3_name
|
||||
print(f" Converting {wav.name} -> {mp3_name} (MIDI {parsed['midi']}, dyn={dyn})")
|
||||
convert_wav_to_mp3(wav, mp3_path)
|
||||
|
||||
layer = {
|
||||
"file_path": f"samples/{mp3_name}",
|
||||
"root_key": parsed['midi'],
|
||||
}
|
||||
if loop:
|
||||
layer["loop_mode"] = "continuous"
|
||||
dynamics_groups.setdefault(dyn, []).append(layer)
|
||||
|
||||
# Determine velocity ranges based on how many dynamics levels exist
|
||||
# Treat None (unmarked) as forte — it's the "normal" dynamic
|
||||
dyn_keys = sorted(dynamics_groups.keys(),
|
||||
key=lambda d: DYNAMICS_ORDER.index(d) if d and d in DYNAMICS_ORDER else
|
||||
(DYNAMICS_ORDER.index('f') if d is None else 3))
|
||||
if len(dyn_keys) == 1:
|
||||
# Only one dynamics level — full velocity
|
||||
for layer in dynamics_groups[dyn_keys[0]]:
|
||||
layer["velocity_min"] = 0
|
||||
layer["velocity_max"] = 127
|
||||
else:
|
||||
num_levels = len(dyn_keys)
|
||||
vel_step = 128 // num_levels
|
||||
for i, dyn in enumerate(dyn_keys):
|
||||
vel_min = i * vel_step
|
||||
vel_max = (i + 1) * vel_step - 1 if i < num_levels - 1 else 127
|
||||
for layer in dynamics_groups[dyn]:
|
||||
layer["velocity_min"] = vel_min
|
||||
layer["velocity_max"] = vel_max
|
||||
|
||||
# Compute key ranges separately for each velocity group
|
||||
all_layers = []
|
||||
for dyn, group in dynamics_groups.items():
|
||||
group = compute_key_ranges(group)
|
||||
all_layers.extend(group)
|
||||
preset = make_preset(name, description, tags, all_layers, attack, release)
|
||||
preset_path = out_dir / f"{output_subdir.split('/')[-1]}.json"
|
||||
with open(preset_path, 'w') as f:
|
||||
json.dump(preset, f, indent=2)
|
||||
dyn_summary = ", ".join(f"{k or 'default'}: {len(v)}" for k, v in dynamics_groups.items())
|
||||
print(f" -> Wrote {preset_path} ({len(all_layers)} layers: {dyn_summary})")
|
||||
return all_layers
|
||||
|
||||
|
||||
def build_combined_instrument(name: str, description: str, tags: list[str],
|
||||
component_dirs: list[str], output_subdir: str,
|
||||
attack: float = 0.01, release: float = 0.3):
|
||||
"""Build a combined instrument that references samples from component instruments.
|
||||
|
||||
component_dirs: list of output_subdir paths for component instruments, ordered low to high pitch.
|
||||
Splits the keyboard range across them.
|
||||
"""
|
||||
out_dir = INSTRUMENTS_DIR / output_subdir
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Load each component's preset to get its layers
|
||||
all_component_layers = []
|
||||
for comp_dir in component_dirs:
|
||||
comp_path = INSTRUMENTS_DIR / comp_dir
|
||||
json_files = list(comp_path.glob("*.json"))
|
||||
if not json_files:
|
||||
print(f" WARNING: No preset found in {comp_dir}")
|
||||
continue
|
||||
with open(json_files[0]) as f:
|
||||
comp_preset = json.load(f)
|
||||
comp_layers = comp_preset["nodes"][1]["sample_data"]["layers"]
|
||||
# Adjust file paths to be relative from the combined instrument dir
|
||||
rel_prefix = os.path.relpath(comp_path, out_dir)
|
||||
for layer in comp_layers:
|
||||
layer["file_path"] = f"{rel_prefix}/{layer['file_path']}"
|
||||
all_component_layers.extend(comp_layers)
|
||||
|
||||
# Re-sort by root key and recompute ranges across all layers
|
||||
# Group by velocity range to handle dynamics separately
|
||||
vel_groups = {}
|
||||
for layer in all_component_layers:
|
||||
vel_key = (layer["velocity_min"], layer["velocity_max"])
|
||||
vel_groups.setdefault(vel_key, []).append(layer)
|
||||
|
||||
final_layers = []
|
||||
for vel_key, group in vel_groups.items():
|
||||
group = compute_key_ranges(group)
|
||||
# Preserve the original velocity range
|
||||
for layer in group:
|
||||
layer["velocity_min"] = vel_key[0]
|
||||
layer["velocity_max"] = vel_key[1]
|
||||
final_layers.extend(group)
|
||||
|
||||
preset = make_preset(name, description, tags, final_layers, attack, release)
|
||||
preset_path = out_dir / f"{output_subdir.split('/')[-1]}.json"
|
||||
with open(preset_path, 'w') as f:
|
||||
json.dump(preset, f, indent=2)
|
||||
print(f" -> Wrote {preset_path} ({len(final_layers)} layers from {len(component_dirs)} components)")
|
||||
|
||||
|
||||
def main():
|
||||
print("=== Building Lightningbeam Factory Instruments from VPO3 ===\n")
|
||||
|
||||
if not VPO.exists():
|
||||
print(f"ERROR: VPO3 not found at {VPO}")
|
||||
sys.exit(1)
|
||||
|
||||
# --- STRINGS ---
|
||||
|
||||
print("\n[1/14] Violin Section (SSO 1st Violins sustain)")
|
||||
build_simple_instrument(
|
||||
"Violin Section", "Orchestral violin section with sustained bowing",
|
||||
["strings", "violin", "section", "orchestral"],
|
||||
VPO / "SSO" / "Samples" / "1st Violins",
|
||||
"strings/violin-section",
|
||||
filename_filter=lambda f: 'sus' in f.lower(),
|
||||
parser=parse_sso_filename,
|
||||
attack=0.05, release=0.4,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[2/14] Viola Section (Mattias-Westlund)")
|
||||
build_simple_instrument(
|
||||
"Viola Section", "Orchestral viola section with sustained bowing",
|
||||
["strings", "viola", "section", "orchestral"],
|
||||
VPO / "Mattias-Westlund" / "ViolaSect" / "Samples",
|
||||
"strings/viola-section",
|
||||
parser=parse_mw_viola_filename,
|
||||
attack=0.05, release=0.4,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[3/14] Cello Section (NBO sustain)")
|
||||
build_simple_instrument(
|
||||
"Cello Section", "Orchestral cello section with sustained bowing",
|
||||
["strings", "cello", "section", "orchestral"],
|
||||
VPO / "NoBudgetOrch" / "CelloSect" / "Sustain",
|
||||
"strings/cello-section",
|
||||
parser=parse_nbo_filename,
|
||||
attack=0.05, release=0.4,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[4/14] Bass Section (SSO sustain)")
|
||||
build_simple_instrument(
|
||||
"Bass Section", "Orchestral double bass section with sustained bowing",
|
||||
["strings", "bass", "contrabass", "section", "orchestral"],
|
||||
VPO / "SSO" / "Samples" / "Basses",
|
||||
"strings/bass-section",
|
||||
filename_filter=lambda f: 'sus' in f.lower(),
|
||||
parser=parse_sso_filename,
|
||||
attack=0.08, release=0.5,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[5/14] Harp (VSCO2-CE)")
|
||||
build_simple_instrument(
|
||||
"Harp", "Concert harp",
|
||||
["strings", "harp", "orchestral"],
|
||||
VPO / "VSCO2-CE" / "Strings" / "Harp",
|
||||
"strings/harp",
|
||||
parser=parse_vsco_harp_filename,
|
||||
attack=0.001, release=0.8,
|
||||
)
|
||||
|
||||
# --- WOODWINDS ---
|
||||
|
||||
print("\n[6/14] Flute Section (NBO)")
|
||||
build_simple_instrument(
|
||||
"Flute", "Orchestral flute section",
|
||||
["woodwinds", "flute", "section", "orchestral"],
|
||||
VPO / "NoBudgetOrch" / "FluteSect",
|
||||
"woodwinds/flute",
|
||||
parser=parse_nbo_filename,
|
||||
attack=0.03, release=0.3,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[7/14] Oboe (SSO solo)")
|
||||
build_simple_instrument(
|
||||
"Oboe", "Solo oboe",
|
||||
["woodwinds", "oboe", "solo", "orchestral"],
|
||||
VPO / "SSO" / "Samples" / "Oboe",
|
||||
"woodwinds/oboe",
|
||||
filename_filter=lambda f: f.endswith('.wav') and 'readme' not in f.lower(),
|
||||
parser=parse_sso_filename,
|
||||
attack=0.02, release=0.25,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[8/14] Clarinet Section (NBO)")
|
||||
build_simple_instrument(
|
||||
"Clarinet", "Orchestral clarinet section",
|
||||
["woodwinds", "clarinet", "section", "orchestral"],
|
||||
VPO / "NoBudgetOrch" / "ClarinetSect" / "Sustain",
|
||||
"woodwinds/clarinet",
|
||||
parser=parse_nbo_filename,
|
||||
attack=0.02, release=0.25,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[9/14] Bassoon (SSO)")
|
||||
build_simple_instrument(
|
||||
"Bassoon", "Solo bassoon",
|
||||
["woodwinds", "bassoon", "solo", "orchestral"],
|
||||
VPO / "SSO" / "Samples" / "Bassoon",
|
||||
"woodwinds/bassoon",
|
||||
parser=parse_sso_filename,
|
||||
attack=0.03, release=0.3,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
# --- BRASS ---
|
||||
|
||||
print("\n[10/14] Horn Section (Mattias-Westlund, ff + mp dynamics)")
|
||||
build_dynamics_instrument(
|
||||
"Horn Section", "French horn section with forte and mezzo-piano dynamics",
|
||||
["brass", "horn", "french horn", "section", "orchestral"],
|
||||
VPO / "Mattias-Westlund" / "Horns" / "Samples",
|
||||
"brass/horn-section",
|
||||
parser=parse_mw_horn_filename,
|
||||
attack=0.04, release=0.4,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[11/14] Trumpet Section (NBO2 with dynamics)")
|
||||
build_dynamics_instrument(
|
||||
"Trumpet Section", "Orchestral trumpet section with piano and forte dynamics",
|
||||
["brass", "trumpet", "section", "orchestral"],
|
||||
VPO / "NoBudgetOrch2" / "Trumpet" / "TrumpetSect" / "Sustain",
|
||||
"brass/trumpet-section",
|
||||
attack=0.02, release=0.3,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[12/14] Trombone Section (NBO2 with dynamics)")
|
||||
build_dynamics_instrument(
|
||||
"Trombone Section", "Orchestral trombone section with piano and forte dynamics",
|
||||
["brass", "trombone", "section", "orchestral"],
|
||||
VPO / "NoBudgetOrch2" / "Trombone" / "TromboneSect" / "Sustain",
|
||||
"brass/trombone-section",
|
||||
attack=0.03, release=0.35,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
print("\n[13/14] Tuba (SSO sustain)")
|
||||
build_simple_instrument(
|
||||
"Tuba", "Orchestral tuba",
|
||||
["brass", "tuba", "orchestral"],
|
||||
VPO / "SSO" / "Samples" / "Tuba",
|
||||
"brass/tuba",
|
||||
filename_filter=lambda f: 'sus' in f.lower(),
|
||||
parser=parse_sso_filename,
|
||||
attack=0.04, release=0.4,
|
||||
loop=True,
|
||||
)
|
||||
|
||||
# --- PERCUSSION ---
|
||||
|
||||
print("\n[14/14] Timpani (NBO)")
|
||||
build_simple_instrument(
|
||||
"Timpani", "Orchestral timpani",
|
||||
["percussion", "timpani", "orchestral"],
|
||||
VPO / "NoBudgetOrch" / "Timpani",
|
||||
"orchestral/timpani",
|
||||
parser=lambda f: parse_sso_filename(f), # Note-octave format like A2-PB.wav
|
||||
attack=0.001, release=1.5,
|
||||
)
|
||||
|
||||
# --- COMBINED INSTRUMENTS ---
|
||||
|
||||
print("\n[Combined] Strings")
|
||||
build_combined_instrument(
|
||||
"Strings", "Full string section — auto-selects violin, viola, cello, or bass by pitch range",
|
||||
["strings", "section", "orchestral", "combined"],
|
||||
[
|
||||
"strings/bass-section",
|
||||
"strings/cello-section",
|
||||
"strings/viola-section",
|
||||
"strings/violin-section",
|
||||
],
|
||||
"strings/strings-combined",
|
||||
attack=0.05, release=0.4,
|
||||
)
|
||||
|
||||
print("\n[Combined] Woodwinds")
|
||||
build_combined_instrument(
|
||||
"Woodwinds", "Full woodwind section — auto-selects bassoon, clarinet, oboe, or flute by pitch range",
|
||||
["woodwinds", "section", "orchestral", "combined"],
|
||||
[
|
||||
"woodwinds/bassoon",
|
||||
"woodwinds/clarinet",
|
||||
"woodwinds/oboe",
|
||||
"woodwinds/flute",
|
||||
],
|
||||
"woodwinds/woodwinds-combined",
|
||||
attack=0.03, release=0.3,
|
||||
)
|
||||
|
||||
print("\n[Combined] Brass")
|
||||
build_combined_instrument(
|
||||
"Brass", "Full brass section — auto-selects tuba, trombone, horn, or trumpet by pitch range",
|
||||
["brass", "section", "orchestral", "combined"],
|
||||
[
|
||||
"brass/tuba",
|
||||
"brass/trombone-section",
|
||||
"brass/horn-section",
|
||||
"brass/trumpet-section",
|
||||
],
|
||||
"brass/brass-combined",
|
||||
attack=0.03, release=0.35,
|
||||
)
|
||||
|
||||
print("\n=== Done! ===")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,758 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Brass",
|
||||
"description": "Full brass section \u2014 auto-selects tuba, trombone, horn, or trumpet by pitch range",
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": [
|
||||
"brass",
|
||||
"section",
|
||||
"orchestral",
|
||||
"combined"
|
||||
]
|
||||
},
|
||||
"midi_targets": [
|
||||
0
|
||||
],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
100.0,
|
||||
100.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": "Brass Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0,
|
||||
"1": 0.03,
|
||||
"2": 0.35,
|
||||
"3": 0.0
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": [
|
||||
{
|
||||
"file_path": "../tuba/samples/E1.mp3",
|
||||
"root_key": 28,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 0,
|
||||
"key_max": 29
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/G1.mp3",
|
||||
"root_key": 31,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 30,
|
||||
"key_max": 32
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/A#1.mp3",
|
||||
"root_key": 34,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 33,
|
||||
"key_max": 35
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/C#2.mp3",
|
||||
"root_key": 37,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 36,
|
||||
"key_max": 38
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/E2.mp3",
|
||||
"root_key": 40,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 39,
|
||||
"key_max": 41
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/G2.mp3",
|
||||
"root_key": 43,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 42,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/A#2.mp3",
|
||||
"root_key": 46,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 45,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/C#3.mp3",
|
||||
"root_key": 49,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 48,
|
||||
"key_max": 50
|
||||
},
|
||||
{
|
||||
"file_path": "../tuba/samples/E3.mp3",
|
||||
"root_key": 52,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 51,
|
||||
"key_max": 127
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E2_ff.mp3",
|
||||
"root_key": 40,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 0,
|
||||
"key_max": 40
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/F2.mp3",
|
||||
"root_key": 41,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 41,
|
||||
"key_max": 42
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/G2_ff.mp3",
|
||||
"root_key": 43,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 43,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/A2.mp3",
|
||||
"root_key": 45,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 45,
|
||||
"key_max": 45
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/A#2_ff.mp3",
|
||||
"root_key": 46,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 46,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/C3.mp3",
|
||||
"root_key": 48,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 48,
|
||||
"key_max": 48
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/C#3_ff.mp3",
|
||||
"root_key": 49,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 49,
|
||||
"key_max": 50
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Eb3.mp3",
|
||||
"root_key": 51,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 51,
|
||||
"key_max": 51
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E3_ff.mp3",
|
||||
"root_key": 52,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 52,
|
||||
"key_max": 53
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Gb3.mp3",
|
||||
"root_key": 54,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 54,
|
||||
"key_max": 54
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/G3_ff.mp3",
|
||||
"root_key": 55,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 55,
|
||||
"key_max": 55
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/G3.mp3",
|
||||
"root_key": 55,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 56,
|
||||
"key_max": 55
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Ab3.mp3",
|
||||
"root_key": 56,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 56,
|
||||
"key_max": 56
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/A3.mp3",
|
||||
"root_key": 57,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 57,
|
||||
"key_max": 57
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/A#3_ff.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 58,
|
||||
"key_max": 58
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Bb3.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 59,
|
||||
"key_max": 59
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/C4.mp3",
|
||||
"root_key": 60,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 60,
|
||||
"key_max": 60
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/C4.mp3",
|
||||
"root_key": 60,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 61,
|
||||
"key_max": 60
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/C#4_ff.mp3",
|
||||
"root_key": 61,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 61,
|
||||
"key_max": 61
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/D4.mp3",
|
||||
"root_key": 62,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 62,
|
||||
"key_max": 62
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Eb4.mp3",
|
||||
"root_key": 63,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 63,
|
||||
"key_max": 63
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E4_ff.mp3",
|
||||
"root_key": 64,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 64,
|
||||
"key_max": 64
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/E4.mp3",
|
||||
"root_key": 64,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 65,
|
||||
"key_max": 64
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/F4.mp3",
|
||||
"root_key": 65,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 65,
|
||||
"key_max": 65
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Gb4.mp3",
|
||||
"root_key": 66,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 66,
|
||||
"key_max": 66
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/G4_ff.mp3",
|
||||
"root_key": 67,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 67,
|
||||
"key_max": 67
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/G4.mp3",
|
||||
"root_key": 67,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 68,
|
||||
"key_max": 68
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/A4.mp3",
|
||||
"root_key": 69,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 69,
|
||||
"key_max": 69
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/A4.mp3",
|
||||
"root_key": 69,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 70,
|
||||
"key_max": 69
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/A#4_ff.mp3",
|
||||
"root_key": 70,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 70,
|
||||
"key_max": 70
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Bb4.mp3",
|
||||
"root_key": 70,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 71,
|
||||
"key_max": 71
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/C5.mp3",
|
||||
"root_key": 72,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 72,
|
||||
"key_max": 72
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/C5.mp3",
|
||||
"root_key": 72,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 73,
|
||||
"key_max": 72
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/C#5_ff.mp3",
|
||||
"root_key": 73,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 73,
|
||||
"key_max": 73
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/D5.mp3",
|
||||
"root_key": 74,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 74,
|
||||
"key_max": 74
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Eb5.mp3",
|
||||
"root_key": 75,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 75,
|
||||
"key_max": 75
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E5_ff.mp3",
|
||||
"root_key": 76,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 76,
|
||||
"key_max": 76
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/F5.mp3",
|
||||
"root_key": 77,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 77,
|
||||
"key_max": 78
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/G5.mp3",
|
||||
"root_key": 79,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 79,
|
||||
"key_max": 80
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/A5.mp3",
|
||||
"root_key": 81,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 81,
|
||||
"key_max": 82
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/B5.mp3",
|
||||
"root_key": 83,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 83,
|
||||
"key_max": 84
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Db6.mp3",
|
||||
"root_key": 85,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 85,
|
||||
"key_max": 127
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E2_mp.mp3",
|
||||
"root_key": 40,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 0,
|
||||
"key_max": 40
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/F2_p.mp3",
|
||||
"root_key": 41,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 41,
|
||||
"key_max": 42
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/G2_mp.mp3",
|
||||
"root_key": 43,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 43,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Bb2_p.mp3",
|
||||
"root_key": 46,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 45,
|
||||
"key_max": 46
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/A#2_mp.mp3",
|
||||
"root_key": 46,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 47,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/C#3_mp.mp3",
|
||||
"root_key": 49,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 48,
|
||||
"key_max": 50
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Eb3_p.mp3",
|
||||
"root_key": 51,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 51,
|
||||
"key_max": 51
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E3_mp.mp3",
|
||||
"root_key": 52,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 52,
|
||||
"key_max": 53
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/G3_mp.mp3",
|
||||
"root_key": 55,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 54,
|
||||
"key_max": 55
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Ab3_p.mp3",
|
||||
"root_key": 56,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 56,
|
||||
"key_max": 57
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/A#3_mp.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 58,
|
||||
"key_max": 58
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Bb3_p.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 59,
|
||||
"key_max": 59
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Db4_p.mp3",
|
||||
"root_key": 61,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 60,
|
||||
"key_max": 61
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/C#4_mp.mp3",
|
||||
"root_key": 61,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 62,
|
||||
"key_max": 62
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Eb4_p.mp3",
|
||||
"root_key": 63,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 63,
|
||||
"key_max": 63
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E4_mp.mp3",
|
||||
"root_key": 64,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 64,
|
||||
"key_max": 65
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/Gb4_p.mp3",
|
||||
"root_key": 66,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 66,
|
||||
"key_max": 66
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/G4_mp.mp3",
|
||||
"root_key": 67,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 67,
|
||||
"key_max": 67
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Ab4_p.mp3",
|
||||
"root_key": 68,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 68,
|
||||
"key_max": 69
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/A#4_mp.mp3",
|
||||
"root_key": 70,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 70,
|
||||
"key_max": 70
|
||||
},
|
||||
{
|
||||
"file_path": "../trombone-section/samples/B4_p.mp3",
|
||||
"root_key": 71,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 71,
|
||||
"key_max": 72
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/C#5_mp.mp3",
|
||||
"root_key": 73,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 73,
|
||||
"key_max": 73
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Db5_p.mp3",
|
||||
"root_key": 73,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 74,
|
||||
"key_max": 74
|
||||
},
|
||||
{
|
||||
"file_path": "../horn-section/samples/E5_mp.mp3",
|
||||
"root_key": 76,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 75,
|
||||
"key_max": 78
|
||||
},
|
||||
{
|
||||
"file_path": "../trumpet-section/samples/Ab5_p.mp3",
|
||||
"root_key": 80,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 79,
|
||||
"key_max": 127
|
||||
}
|
||||
]
|
||||
},
|
||||
"position": [
|
||||
350.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
700.0,
|
||||
100.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": 0,
|
||||
"from_port": 0,
|
||||
"to_node": 1,
|
||||
"to_port": 0
|
||||
},
|
||||
{
|
||||
"from_node": 1,
|
||||
"from_port": 0,
|
||||
"to_node": 2,
|
||||
"to_port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Horn Section",
|
||||
"description": "French horn section with forte and mezzo-piano dynamics",
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": [
|
||||
"brass",
|
||||
"horn",
|
||||
"french horn",
|
||||
"section",
|
||||
"orchestral"
|
||||
]
|
||||
},
|
||||
"midi_targets": [
|
||||
0
|
||||
],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
100.0,
|
||||
100.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": "Horn Section Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0,
|
||||
"1": 0.04,
|
||||
"2": 0.4,
|
||||
"3": 0.0
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": [
|
||||
{
|
||||
"file_path": "samples/E2_ff.mp3",
|
||||
"root_key": 40,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 0,
|
||||
"key_max": 41
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G2_ff.mp3",
|
||||
"root_key": 43,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 42,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#2_ff.mp3",
|
||||
"root_key": 46,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 45,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#3_ff.mp3",
|
||||
"root_key": 49,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 48,
|
||||
"key_max": 50
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E3_ff.mp3",
|
||||
"root_key": 52,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 51,
|
||||
"key_max": 53
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G3_ff.mp3",
|
||||
"root_key": 55,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 54,
|
||||
"key_max": 56
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#3_ff.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 57,
|
||||
"key_max": 59
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#4_ff.mp3",
|
||||
"root_key": 61,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 60,
|
||||
"key_max": 62
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E4_ff.mp3",
|
||||
"root_key": 64,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 63,
|
||||
"key_max": 65
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G4_ff.mp3",
|
||||
"root_key": 67,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 66,
|
||||
"key_max": 68
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#4_ff.mp3",
|
||||
"root_key": 70,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 69,
|
||||
"key_max": 71
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#5_ff.mp3",
|
||||
"root_key": 73,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 72,
|
||||
"key_max": 74
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E5_ff.mp3",
|
||||
"root_key": 76,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 75,
|
||||
"key_max": 127
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E2_mp.mp3",
|
||||
"root_key": 40,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 0,
|
||||
"key_max": 41
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G2_mp.mp3",
|
||||
"root_key": 43,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 42,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#2_mp.mp3",
|
||||
"root_key": 46,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 45,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#3_mp.mp3",
|
||||
"root_key": 49,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 48,
|
||||
"key_max": 50
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E3_mp.mp3",
|
||||
"root_key": 52,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 51,
|
||||
"key_max": 53
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G3_mp.mp3",
|
||||
"root_key": 55,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 54,
|
||||
"key_max": 56
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#3_mp.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 57,
|
||||
"key_max": 59
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#4_mp.mp3",
|
||||
"root_key": 61,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 60,
|
||||
"key_max": 62
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E4_mp.mp3",
|
||||
"root_key": 64,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 63,
|
||||
"key_max": 65
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G4_mp.mp3",
|
||||
"root_key": 67,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 66,
|
||||
"key_max": 68
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#4_mp.mp3",
|
||||
"root_key": 70,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 69,
|
||||
"key_max": 71
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#5_mp.mp3",
|
||||
"root_key": 73,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 72,
|
||||
"key_max": 74
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E5_mp.mp3",
|
||||
"root_key": 76,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 75,
|
||||
"key_max": 127
|
||||
}
|
||||
]
|
||||
},
|
||||
"position": [
|
||||
350.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
700.0,
|
||||
100.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": 0,
|
||||
"from_port": 0,
|
||||
"to_node": 1,
|
||||
"to_port": 0
|
||||
},
|
||||
{
|
||||
"from_node": 1,
|
||||
"from_port": 0,
|
||||
"to_node": 2,
|
||||
"to_port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,236 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Trombone Section",
|
||||
"description": "Orchestral trombone section with piano and forte dynamics",
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": [
|
||||
"brass",
|
||||
"trombone",
|
||||
"section",
|
||||
"orchestral"
|
||||
]
|
||||
},
|
||||
"midi_targets": [
|
||||
0
|
||||
],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
100.0,
|
||||
100.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": "Trombone Section Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0,
|
||||
"1": 0.03,
|
||||
"2": 0.35,
|
||||
"3": 0.0
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": [
|
||||
{
|
||||
"file_path": "samples/F2.mp3",
|
||||
"root_key": 41,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 0,
|
||||
"key_max": 43
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A2.mp3",
|
||||
"root_key": 45,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 44,
|
||||
"key_max": 46
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C3.mp3",
|
||||
"root_key": 48,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 47,
|
||||
"key_max": 49
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Eb3.mp3",
|
||||
"root_key": 51,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 50,
|
||||
"key_max": 52
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Gb3.mp3",
|
||||
"root_key": 54,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 53,
|
||||
"key_max": 55
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A3.mp3",
|
||||
"root_key": 57,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 56,
|
||||
"key_max": 58
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C4.mp3",
|
||||
"root_key": 60,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 59,
|
||||
"key_max": 61
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Eb4.mp3",
|
||||
"root_key": 63,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 62,
|
||||
"key_max": 64
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Gb4.mp3",
|
||||
"root_key": 66,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 65,
|
||||
"key_max": 67
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A4.mp3",
|
||||
"root_key": 69,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 68,
|
||||
"key_max": 70
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C5.mp3",
|
||||
"root_key": 72,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 71,
|
||||
"key_max": 127
|
||||
},
|
||||
{
|
||||
"file_path": "samples/F2_p.mp3",
|
||||
"root_key": 41,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 0,
|
||||
"key_max": 43
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Bb2_p.mp3",
|
||||
"root_key": 46,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 44,
|
||||
"key_max": 48
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Eb3_p.mp3",
|
||||
"root_key": 51,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 49,
|
||||
"key_max": 53
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Ab3_p.mp3",
|
||||
"root_key": 56,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 54,
|
||||
"key_max": 58
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Db4_p.mp3",
|
||||
"root_key": 61,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 59,
|
||||
"key_max": 63
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Gb4_p.mp3",
|
||||
"root_key": 66,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 64,
|
||||
"key_max": 68
|
||||
},
|
||||
{
|
||||
"file_path": "samples/B4_p.mp3",
|
||||
"root_key": 71,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 69,
|
||||
"key_max": 127
|
||||
}
|
||||
]
|
||||
},
|
||||
"position": [
|
||||
350.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
700.0,
|
||||
100.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": 0,
|
||||
"from_port": 0,
|
||||
"to_node": 1,
|
||||
"to_port": 0
|
||||
},
|
||||
{
|
||||
"from_node": 1,
|
||||
"from_port": 0,
|
||||
"to_node": 2,
|
||||
"to_port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,281 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Trumpet Section",
|
||||
"description": "Orchestral trumpet section with piano and forte dynamics",
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": [
|
||||
"brass",
|
||||
"trumpet",
|
||||
"section",
|
||||
"orchestral"
|
||||
]
|
||||
},
|
||||
"midi_targets": [
|
||||
0
|
||||
],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
100.0,
|
||||
100.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": "Trumpet Section Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0,
|
||||
"1": 0.02,
|
||||
"2": 0.3,
|
||||
"3": 0.0
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": [
|
||||
{
|
||||
"file_path": "samples/G3.mp3",
|
||||
"root_key": 55,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 0,
|
||||
"key_max": 55
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Ab3.mp3",
|
||||
"root_key": 56,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 56,
|
||||
"key_max": 57
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Bb3.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 58,
|
||||
"key_max": 59
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C4.mp3",
|
||||
"root_key": 60,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 60,
|
||||
"key_max": 61
|
||||
},
|
||||
{
|
||||
"file_path": "samples/D4.mp3",
|
||||
"root_key": 62,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 62,
|
||||
"key_max": 63
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E4.mp3",
|
||||
"root_key": 64,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 64,
|
||||
"key_max": 64
|
||||
},
|
||||
{
|
||||
"file_path": "samples/F4.mp3",
|
||||
"root_key": 65,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 65,
|
||||
"key_max": 66
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G4.mp3",
|
||||
"root_key": 67,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 67,
|
||||
"key_max": 68
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A4.mp3",
|
||||
"root_key": 69,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 69,
|
||||
"key_max": 69
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Bb4.mp3",
|
||||
"root_key": 70,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 70,
|
||||
"key_max": 71
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C5.mp3",
|
||||
"root_key": 72,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 72,
|
||||
"key_max": 73
|
||||
},
|
||||
{
|
||||
"file_path": "samples/D5.mp3",
|
||||
"root_key": 74,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 74,
|
||||
"key_max": 74
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Eb5.mp3",
|
||||
"root_key": 75,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 75,
|
||||
"key_max": 76
|
||||
},
|
||||
{
|
||||
"file_path": "samples/F5.mp3",
|
||||
"root_key": 77,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 77,
|
||||
"key_max": 78
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G5.mp3",
|
||||
"root_key": 79,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 79,
|
||||
"key_max": 80
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A5.mp3",
|
||||
"root_key": 81,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 81,
|
||||
"key_max": 82
|
||||
},
|
||||
{
|
||||
"file_path": "samples/B5.mp3",
|
||||
"root_key": 83,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 83,
|
||||
"key_max": 84
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Db6.mp3",
|
||||
"root_key": 85,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 64,
|
||||
"velocity_max": 127,
|
||||
"key_min": 85,
|
||||
"key_max": 127
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Bb3_p.mp3",
|
||||
"root_key": 58,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 0,
|
||||
"key_max": 60
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Eb4_p.mp3",
|
||||
"root_key": 63,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 61,
|
||||
"key_max": 65
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Ab4_p.mp3",
|
||||
"root_key": 68,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 66,
|
||||
"key_max": 70
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Db5_p.mp3",
|
||||
"root_key": 73,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 71,
|
||||
"key_max": 76
|
||||
},
|
||||
{
|
||||
"file_path": "samples/Ab5_p.mp3",
|
||||
"root_key": 80,
|
||||
"loop_mode": "continuous",
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 63,
|
||||
"key_min": 77,
|
||||
"key_max": 127
|
||||
}
|
||||
]
|
||||
},
|
||||
"position": [
|
||||
350.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
700.0,
|
||||
100.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": 0,
|
||||
"from_port": 0,
|
||||
"to_node": 1,
|
||||
"to_port": 0
|
||||
},
|
||||
{
|
||||
"from_node": 1,
|
||||
"from_port": 0,
|
||||
"to_node": 2,
|
||||
"to_port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,154 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Tuba",
|
||||
"description": "Orchestral tuba",
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": [
|
||||
"brass",
|
||||
"tuba",
|
||||
"orchestral"
|
||||
]
|
||||
},
|
||||
"midi_targets": [
|
||||
0
|
||||
],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
100.0,
|
||||
100.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": "Tuba Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0,
|
||||
"1": 0.04,
|
||||
"2": 0.4,
|
||||
"3": 0.0
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": [
|
||||
{
|
||||
"file_path": "samples/E1.mp3",
|
||||
"root_key": 28,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 0,
|
||||
"key_max": 29
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G1.mp3",
|
||||
"root_key": 31,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 30,
|
||||
"key_max": 32
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#1.mp3",
|
||||
"root_key": 34,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 33,
|
||||
"key_max": 35
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#2.mp3",
|
||||
"root_key": 37,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 36,
|
||||
"key_max": 38
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E2.mp3",
|
||||
"root_key": 40,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 39,
|
||||
"key_max": 41
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G2.mp3",
|
||||
"root_key": 43,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 42,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A#2.mp3",
|
||||
"root_key": 46,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 45,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C#3.mp3",
|
||||
"root_key": 49,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 48,
|
||||
"key_max": 50
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E3.mp3",
|
||||
"root_key": 52,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"loop_mode": "continuous",
|
||||
"key_min": 51,
|
||||
"key_max": 127
|
||||
}
|
||||
]
|
||||
},
|
||||
"position": [
|
||||
350.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
700.0,
|
||||
100.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": 0,
|
||||
"from_port": 0,
|
||||
"to_node": 1,
|
||||
"to_port": 0
|
||||
},
|
||||
{
|
||||
"from_node": 1,
|
||||
"from_port": 0,
|
||||
"to_node": 2,
|
||||
"to_port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,185 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Timpani",
|
||||
"description": "Orchestral timpani",
|
||||
"author": "Virtual Playing Orchestra 3",
|
||||
"version": 1,
|
||||
"tags": [
|
||||
"percussion",
|
||||
"timpani",
|
||||
"orchestral"
|
||||
]
|
||||
},
|
||||
"midi_targets": [
|
||||
0
|
||||
],
|
||||
"output_node": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 0,
|
||||
"node_type": "MidiInput",
|
||||
"name": "MIDI In",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
100.0,
|
||||
100.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"node_type": "MultiSampler",
|
||||
"name": "Timpani Sampler",
|
||||
"parameters": {
|
||||
"0": 1.0,
|
||||
"1": 0.001,
|
||||
"2": 1.5,
|
||||
"3": 0.0
|
||||
},
|
||||
"sample_data": {
|
||||
"type": "multi_sampler",
|
||||
"layers": [
|
||||
{
|
||||
"file_path": "samples/C2.mp3",
|
||||
"root_key": 36,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 0,
|
||||
"key_max": 37
|
||||
},
|
||||
{
|
||||
"file_path": "samples/D2.mp3",
|
||||
"root_key": 38,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 38,
|
||||
"key_max": 39
|
||||
},
|
||||
{
|
||||
"file_path": "samples/E2.mp3",
|
||||
"root_key": 40,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 40,
|
||||
"key_max": 40
|
||||
},
|
||||
{
|
||||
"file_path": "samples/F2.mp3",
|
||||
"root_key": 41,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 41,
|
||||
"key_max": 42
|
||||
},
|
||||
{
|
||||
"file_path": "samples/AB2.mp3",
|
||||
"root_key": 44,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 43,
|
||||
"key_max": 44
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A2.mp3",
|
||||
"root_key": 45,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 45,
|
||||
"key_max": 45
|
||||
},
|
||||
{
|
||||
"file_path": "samples/BB2.mp3",
|
||||
"root_key": 46,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 46,
|
||||
"key_max": 47
|
||||
},
|
||||
{
|
||||
"file_path": "samples/C3.mp3",
|
||||
"root_key": 48,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 48,
|
||||
"key_max": 49
|
||||
},
|
||||
{
|
||||
"file_path": "samples/D3.mp3",
|
||||
"root_key": 50,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 50,
|
||||
"key_max": 52
|
||||
},
|
||||
{
|
||||
"file_path": "samples/GB3.mp3",
|
||||
"root_key": 54,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 53,
|
||||
"key_max": 54
|
||||
},
|
||||
{
|
||||
"file_path": "samples/G3.mp3",
|
||||
"root_key": 55,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 55,
|
||||
"key_max": 55
|
||||
},
|
||||
{
|
||||
"file_path": "samples/AB3.mp3",
|
||||
"root_key": 56,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 56,
|
||||
"key_max": 56
|
||||
},
|
||||
{
|
||||
"file_path": "samples/A3.mp3",
|
||||
"root_key": 57,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 57,
|
||||
"key_max": 57
|
||||
},
|
||||
{
|
||||
"file_path": "samples/BB3.mp3",
|
||||
"root_key": 58,
|
||||
"velocity_min": 0,
|
||||
"velocity_max": 127,
|
||||
"key_min": 58,
|
||||
"key_max": 127
|
||||
}
|
||||
]
|
||||
},
|
||||
"position": [
|
||||
350.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"node_type": "AudioOutput",
|
||||
"name": "Out",
|
||||
"parameters": {},
|
||||
"position": [
|
||||
700.0,
|
||||
100.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": 0,
|
||||
"from_port": 0,
|
||||
"to_node": 1,
|
||||
"to_port": 0
|
||||
},
|
||||
{
|
||||
"from_node": 1,
|
||||
"from_port": 0,
|
||||
"to_node": 2,
|
||||
"to_port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue