Compare commits

..

No commits in common. "728b88365dc21dccabdbaa9d598c784e627a0760" and "2222e68a3e8e42e2782c3e6e2afb57423de55ed8" have entirely different histories.

450 changed files with 1 additions and 7793 deletions

View File

@ -231,7 +231,7 @@ impl Voice {
envelope_phase: EnvelopePhase::Attack,
envelope_value: 0.0,
crossfade_buffer: Vec::new(),
crossfade_length: 4800, // ~100ms at 48kHz — hides loop seams in sustained instruments
crossfade_length: 1000, // ~20ms at 48kHz (longer for smoother loops)
}
}
}

View File

@ -79,8 +79,6 @@ 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]
@ -123,10 +121,3 @@ 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

View File

@ -5,9 +5,6 @@ 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();
}
@ -131,56 +128,6 @@ 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;

View File

@ -1,569 +0,0 @@
#!/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()

View File

@ -1,563 +0,0 @@
#!/usr/bin/env python3
"""Build non-orchestral factory instrument presets.
Sources:
- Acoustic Guitar: University of Iowa MIS (unrestricted license)
- Bass Guitar: Karoryfer Growlybass CC0 (public domain)
- Drum Kit: Salamander Drumkit (public domain)
Usage:
python3 scripts/build_non_orchestral.py
# or with anaconda (needed for aubio):
~/anaconda3/bin/python3 scripts/build_non_orchestral.py
"""
import json
import os
import re
import subprocess
import sys
from pathlib import Path
# Try to import aubio (needed for guitar splitting)
try:
import aubio
HAS_AUBIO = True
except ImportError:
HAS_AUBIO = False
print("WARNING: aubio not installed — guitar splitting will be skipped")
print(" Install with: pip install aubio")
SAMPLES_DIR = Path.home() / "Downloads" / "non-orchestral-samples"
INSTRUMENTS_DIR = Path(__file__).parent.parent / "src" / "assets" / "instruments"
NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
FLAT_TO_SHARP = {'Db': 'C#', 'Eb': 'D#', 'Gb': 'F#', 'Ab': 'G#', 'Bb': 'A#'}
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:
return (octave + 1) * 12 + NOTE_MAP[note_name.lower()]
def midi_to_name(midi: int) -> str:
return f"{NOTE_NAMES[midi % 12]}{midi // 12 - 1}"
def parse_note_str(n: str) -> int:
"""Parse 'E2', 'Bb5', 'C#3' etc to MIDI number."""
if len(n) >= 3 and n[1] in 'b#':
name, oct = n[:2], int(n[2:])
name = FLAT_TO_SHARP.get(name, name)
else:
name, oct = n[0], int(n[1:])
return (oct + 1) * 12 + NOTE_NAMES.index(name)
def convert_to_mp3(input_path: Path, mp3_path: Path, bitrate: str = '192k'):
"""Convert any audio to normalized MP3."""
mp3_path.parent.mkdir(parents=True, exist_ok=True)
if mp3_path.exists():
return
subprocess.run([
'ffmpeg', '-i', str(input_path),
'-af', 'loudnorm=I=-16:TP=-1:LRA=11',
'-ar', '44100', '-ab', bitrate,
'-y', '-loglevel', 'error',
str(mp3_path)
], check=True)
def extract_segment(input_path: Path, output_path: Path, start: float, end: float,
bitrate: str = '192k'):
"""Extract a time segment from audio and convert to normalized MP3."""
output_path.parent.mkdir(parents=True, exist_ok=True)
if output_path.exists():
return
duration = end - start
subprocess.run([
'ffmpeg', '-ss', str(start), '-i', str(input_path),
'-t', str(duration),
'-af', 'loudnorm=I=-16:TP=-1:LRA=11',
'-ar', '44100', '-ab', bitrate,
'-y', '-loglevel', 'error',
str(output_path)
], check=True)
def compute_key_ranges(layers: list[dict]) -> list[dict]:
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, author: str, tags: list[str],
layers: list[dict], attack: float = 0.01, release: float = 0.3) -> dict:
return {
"metadata": {
"name": name,
"description": description,
"author": author,
"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, "1": attack, "2": release, "3": 0.0},
"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}
]
}
# ============================================================
# ACOUSTIC GUITAR (University of Iowa MIS)
# ============================================================
def detect_onsets(fpath: str, threshold: float = 0.3, minioi: float = 2.0,
method: str = "default") -> list[float]:
"""Detect note onsets in an audio file using aubio."""
src = aubio.source(fpath, 44100, 512)
onset_det = aubio.onset(method, 1024, 512, 44100)
onset_det.set_threshold(threshold)
onset_det.set_minioi_s(minioi)
onsets = []
while True:
samples, read = src()
if onset_det(samples):
onsets.append(onset_det.get_last_s())
if read < 512:
break
if not onsets or onsets[0] > 1.0:
onsets.insert(0, 0.0)
return onsets
def get_file_duration(fpath: str) -> float:
"""Get audio file duration in seconds."""
result = subprocess.run(
['ffprobe', '-v', 'error', '-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1', fpath],
capture_output=True, text=True)
return float(result.stdout.strip())
# Preferred string for each MIDI note range (avoids duplicates across strings)
GUITAR_STRING_RANGES = {
'sulE': (40, 49), # E2-C#3
'sulA': (50, 54), # D3-F#3
'sulD': (55, 58), # G3-A#3
'sulG': (59, 63), # B3-D#4
'sulB': (64, 68), # E4-G#4
'sul_E': (69, 83), # A4-B5
}
def build_guitar():
"""Split Iowa MIS guitar chromatic scales into individual notes and build preset."""
if not HAS_AUBIO:
print(" SKIPPED (aubio required)")
return
guitar_dir = SAMPLES_DIR / "iowa-guitar" / "extracted" / "1644stereo"
if not guitar_dir.exists():
print(f" ERROR: Guitar samples not found at {guitar_dir}")
return
out_dir = INSTRUMENTS_DIR / "guitar" / "acoustic-guitar"
samples_dir = out_dir / "samples"
samples_dir.mkdir(parents=True, exist_ok=True)
# Process each dynamic level
DYNAMICS = {'pp': (0, 42), 'mf': (43, 95), 'ff': (96, 127)}
all_layers = []
for dyn, (vel_min, vel_max) in DYNAMICS.items():
print(f" Processing {dyn} dynamics...")
# Use lower threshold for pp to catch quiet onsets
threshold = 0.2 if dyn == 'pp' else 0.3
for fname in sorted(os.listdir(guitar_dir)):
if not fname.endswith('.aif'):
continue
parts = fname.replace('.aif', '').split('.')
if parts[1] != dyn:
continue
string = parts[2]
note_range_str = parts[3]
# Parse note range
m = re.match(r'([A-G][b#]?\d)([A-G][b#]?\d)', note_range_str)
if m:
file_lo = parse_note_str(m.group(1))
file_hi = parse_note_str(m.group(2))
else:
file_lo = file_hi = parse_note_str(note_range_str)
# Check overlap with preferred range for this string
pref_lo, pref_hi = GUITAR_STRING_RANGES.get(string, (0, 0))
overlap_lo = max(file_lo, pref_lo)
overlap_hi = min(file_hi, pref_hi)
if overlap_lo > overlap_hi:
continue # No notes needed from this file
fpath = str(guitar_dir / fname)
total_notes = file_hi - file_lo + 1
if total_notes == 1:
# Single note file
mp3_name = f"{midi_to_name(file_lo)}_{dyn}.mp3"
print(f" {fname} -> {mp3_name}")
convert_to_mp3(Path(fpath), samples_dir / mp3_name)
all_layers.append({
"file_path": f"samples/{mp3_name}",
"root_key": file_lo,
"velocity_min": vel_min,
"velocity_max": vel_max,
})
continue
# Multi-note file: detect onsets and split
onsets = detect_onsets(fpath, threshold=threshold)
duration = get_file_duration(fpath)
if len(onsets) != total_notes:
# Try progressively different thresholds and methods
found = False
for method in ["default", "specflux"]:
for t in [0.1, 0.15, 0.2, 0.5, 0.8, 1.0]:
onsets = detect_onsets(fpath, threshold=t, method=method)
if len(onsets) == total_notes:
found = True
break
if found:
break
if not found:
print(f" SKIPPING {fname} (no threshold/method gives {total_notes} onsets)")
continue
# Extract each needed note
for note_idx in range(total_notes):
midi = file_lo + note_idx
if midi < overlap_lo or midi > overlap_hi:
continue # Not in our preferred range
start = onsets[note_idx]
end = onsets[note_idx + 1] if note_idx + 1 < len(onsets) else duration
# Trim to max 8 seconds per note (plenty for guitar decay)
end = min(end, start + 8.0)
mp3_name = f"{midi_to_name(midi)}_{dyn}.mp3"
print(f" {fname} [{note_idx}] -> {mp3_name} ({start:.2f}s-{end:.2f}s)")
extract_segment(Path(fpath), samples_dir / mp3_name, start, end)
all_layers.append({
"file_path": f"samples/{mp3_name}",
"root_key": midi,
"velocity_min": vel_min,
"velocity_max": vel_max,
})
# Compute key ranges per velocity group
vel_groups = {}
for layer in all_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)
for layer in group:
layer["velocity_min"] = vel_key[0]
layer["velocity_max"] = vel_key[1]
final_layers.extend(group)
preset = make_preset(
"Acoustic Guitar",
"Nylon-string classical guitar (Raimundo 118) with three velocity layers",
"University of Iowa MIS",
["guitar", "acoustic", "nylon", "classical"],
final_layers,
attack=0.001, release=0.8,
)
preset_path = out_dir / "acoustic-guitar.json"
with open(preset_path, 'w') as f:
json.dump(preset, f, indent=2)
print(f" -> Wrote {preset_path} ({len(final_layers)} layers)")
# ============================================================
# BASS GUITAR (Karoryfer Growlybass)
# ============================================================
def parse_growlybass_filename(filename: str) -> dict | None:
"""Parse Growlybass naming: note_dyn_rr.wav (e.g. a2_ff_rr1.wav, db2_pp_rr3.wav)"""
m = re.match(r'([a-g][b#]?)(\d+)_(pp|p|f|ff)_rr(\d+)\.wav', filename.lower())
if not m:
return None
note, octave = m.group(1), int(m.group(2))
dynamic = m.group(3)
rr = int(m.group(4))
midi = note_to_midi(note, octave)
return {'midi': midi, 'note': f"{note.upper()}{octave}", 'dynamic': dynamic, 'rr': rr}
def build_bass_guitar():
"""Build bass guitar instrument from Karoryfer Growlybass samples."""
source_dir = SAMPLES_DIR / "growlybass" / "extracted" / "Growlybass" / "sustain"
if not source_dir.exists():
print(f" ERROR: Growlybass samples not found at {source_dir}")
return
out_dir = INSTRUMENTS_DIR / "guitar" / "bass-guitar"
samples_dir = out_dir / "samples"
samples_dir.mkdir(parents=True, exist_ok=True)
# Growlybass has 4 dynamics (pp, p, f, ff) and 4 round robins each.
# We'll use round robin 1 only (our MultiSampler doesn't support round robin yet)
# and map all 4 dynamics to velocity layers.
DYNAMICS_ORDER = ['pp', 'p', 'f', 'ff']
dynamics_groups: dict[str, list[dict]] = {}
for wav in sorted(source_dir.glob("*.wav")):
parsed = parse_growlybass_filename(wav.name)
if not parsed:
print(f" WARNING: Could not parse {wav.name}")
continue
if parsed['rr'] != 1:
continue # Only use round robin 1
dyn = parsed['dynamic']
mp3_name = f"{parsed['note']}_{dyn}.mp3"
mp3_path = samples_dir / mp3_name
print(f" Converting {wav.name} -> {mp3_name} (MIDI {parsed['midi']})")
convert_to_mp3(wav, mp3_path)
layer = {
"file_path": f"samples/{mp3_name}",
"root_key": parsed['midi'],
}
dynamics_groups.setdefault(dyn, []).append(layer)
# Assign velocity ranges
num_levels = len(dynamics_groups)
vel_step = 128 // num_levels
dyn_keys = sorted(dynamics_groups.keys(),
key=lambda d: DYNAMICS_ORDER.index(d))
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 per velocity group
all_layers = []
for dyn, group in dynamics_groups.items():
group = compute_key_ranges(group)
all_layers.extend(group)
preset = make_preset(
"Bass Guitar",
"Electric bass guitar (Squier Jazz) with four velocity layers",
"Karoryfer Samples (CC0)",
["guitar", "bass", "electric"],
all_layers,
attack=0.001, release=0.5,
)
preset_path = out_dir / "bass-guitar.json"
with open(preset_path, 'w') as f:
json.dump(preset, f, indent=2)
dyn_summary = ", ".join(f"{k}: {len(v)}" for k, v in dynamics_groups.items())
print(f" -> Wrote {preset_path} ({len(all_layers)} layers: {dyn_summary})")
# ============================================================
# DRUM KIT (Salamander Drumkit)
# ============================================================
# Salamander uses GM-like drum mapping.
# Files: kick_OH_F_1.wav, snare_OH_FF_1.wav, hihatClosed_OH_P_1.wav, etc.
# OH = overhead mic, F/FF/P/PP/MP/Ghost = dynamics, number = round robin
# GM drum map — maps Salamander drum names to MIDI notes
GM_DRUMS = {
'kick': 36, # C2 - Bass Drum 1
'snare': 38, # D2 - Acoustic Snare
'snareOFF': 40, # E2 - Electric Snare (snares off)
'snareStick': 37, # C#2 - Side Stick
'hihatClosed': 42, # F#2 - Closed Hi-Hat
'hihatOpen': 46, # A#2 - Open Hi-Hat
'hihatFoot': 44, # G#2 - Pedal Hi-Hat
'hiTom': 50, # D3 - High Tom
'loTom': 45, # A2 - Low Tom
'crash1': 49, # C#3 - Crash Cymbal 1
'crash2': 57, # A3 - Crash Cymbal 2
'ride1': 51, # D#3 - Ride Cymbal 1
'ride1Bell': 53, # F3 - Ride Bell
'cowbell': 56, # G#3 - Cowbell
'splash1': 55, # G3 - Splash Cymbal
}
def parse_salamander_filename(filename: str) -> dict | None:
"""Parse Salamander naming: drum_OH_dyn_rr.wav or drum_dyn_rr.wav"""
# Try with OH mic prefix first
m = re.match(r'(\w+?)_OH_([A-Za-z]+)_(\d+)\.wav', filename)
if not m:
# Some drums (cowbell, bellchime) don't have _OH_
m = re.match(r'(\w+?)_([A-Z][A-Za-z]*)_(\d+)\.wav', filename)
if not m:
return None
drum, dynamic, rr = m.group(1), m.group(2).lower(), int(m.group(3))
midi = GM_DRUMS.get(drum)
if midi is None:
return None
return {'midi': midi, 'drum': drum, 'dynamic': dynamic, 'rr': rr}
def build_drum_kit():
"""Build drum kit instrument from Salamander Drumkit samples."""
# Find the OH (overhead mic) sample directory
sal_base = SAMPLES_DIR / "salamander-drums"
source_dir = None
for candidate in [sal_base / "OH",
sal_base / "salamanderDrumkit" / "OH"]:
if candidate.exists():
source_dir = candidate
break
if source_dir is None:
for p in sal_base.rglob("OH"):
if p.is_dir():
source_dir = p
break
if source_dir is None:
print(f" ERROR: Salamander OH samples not found under {sal_base}")
return
print(f" Using samples from: {source_dir}")
out_dir = INSTRUMENTS_DIR / "drums" / "drum-kit"
samples_dir = out_dir / "samples"
samples_dir.mkdir(parents=True, exist_ok=True)
# Group by drum type and dynamics
# We'll use OH (overhead) mic for a natural stereo image
# Only use round robin 1 to keep size down
drum_groups: dict[str, dict[str, list]] = {} # drum -> {dyn: [layers]}
for wav in sorted(source_dir.glob("*.wav")):
parsed = parse_salamander_filename(wav.name)
if not parsed:
continue
if parsed['rr'] != 1:
continue
drum = parsed['drum']
dyn = parsed['dynamic']
mp3_name = f"{drum}_{dyn}.mp3"
mp3_path = samples_dir / mp3_name
print(f" Converting {wav.name} -> {mp3_name} (MIDI {parsed['midi']})")
convert_to_mp3(wav, mp3_path)
drum_groups.setdefault(drum, {}).setdefault(dyn, []).append({
"file_path": f"samples/{mp3_name}",
"root_key": parsed['midi'],
})
# Build layers: each drum piece gets its own MIDI note
# Dynamics map to velocity layers
DYNAMICS_ORDER = ['ghost', 'pp', 'p', 'mp', 'mf', 'f', 'ff']
all_layers = []
for drum, dyn_map in drum_groups.items():
dyn_keys = sorted(dyn_map.keys(),
key=lambda d: DYNAMICS_ORDER.index(d) if d in DYNAMICS_ORDER else 3)
num_levels = len(dyn_keys)
if num_levels == 1:
for layer in list(dyn_map.values())[0]:
layer["velocity_min"] = 0
layer["velocity_max"] = 127
layer["key_min"] = layer["root_key"]
layer["key_max"] = layer["root_key"]
all_layers.append(layer)
else:
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 dyn_map[dyn]:
layer["velocity_min"] = vel_min
layer["velocity_max"] = vel_max
layer["key_min"] = layer["root_key"]
layer["key_max"] = layer["root_key"]
all_layers.append(layer)
preset = make_preset(
"Drum Kit",
"Acoustic drum kit (Salamander) — GM-compatible MIDI mapping",
"Salamander Drumkit (Public Domain)",
["drums", "percussion", "kit", "acoustic"],
all_layers,
attack=0.001, release=0.5,
)
preset_path = out_dir / "drum-kit.json"
with open(preset_path, 'w') as f:
json.dump(preset, f, indent=2)
print(f" -> Wrote {preset_path} ({len(all_layers)} layers, {len(drum_groups)} drums)")
# ============================================================
# MAIN
# ============================================================
def main():
print("=== Building Non-Orchestral Factory Instruments ===\n")
print("\n[1/3] Acoustic Guitar (University of Iowa MIS)")
build_guitar()
print("\n[2/3] Bass Guitar (Karoryfer Growlybass)")
build_bass_guitar()
print("\n[3/3] Drum Kit (Salamander Drumkit)")
build_drum_kit()
print("\n=== Done! ===")
if __name__ == '__main__':
main()

View File

@ -1,758 +0,0 @@
{
"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
}
]
}

View File

@ -1,309 +0,0 @@
{
"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
}
]
}

View File

@ -1,236 +0,0 @@
{
"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
}
]
}

View File

@ -1,281 +0,0 @@
{
"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
}
]
}

View File

@ -1,154 +0,0 @@
{
"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
}
]
}

View File

@ -1,346 +0,0 @@
{
"metadata": {
"name": "Drum Kit",
"description": "Acoustic drum kit (Salamander) \u2014 GM-compatible MIDI mapping",
"author": "Salamander Drumkit (Public Domain)",
"version": 1,
"tags": [
"drums",
"percussion",
"kit",
"acoustic"
]
},
"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": "Drum Kit Sampler",
"parameters": {
"0": 1.0,
"1": 0.001,
"2": 0.5,
"3": 0.0
},
"sample_data": {
"type": "multi_sampler",
"layers": [
{
"file_path": "samples/cowbell_p.mp3",
"root_key": 56,
"velocity_min": 0,
"velocity_max": 41,
"key_min": 56,
"key_max": 56
},
{
"file_path": "samples/cowbell_mp.mp3",
"root_key": 56,
"velocity_min": 42,
"velocity_max": 83,
"key_min": 56,
"key_max": 56
},
{
"file_path": "samples/cowbell_ff.mp3",
"root_key": 56,
"velocity_min": 84,
"velocity_max": 127,
"key_min": 56,
"key_max": 56
},
{
"file_path": "samples/crash1_p.mp3",
"root_key": 49,
"velocity_min": 0,
"velocity_max": 63,
"key_min": 49,
"key_max": 49
},
{
"file_path": "samples/crash1_ff.mp3",
"root_key": 49,
"velocity_min": 64,
"velocity_max": 127,
"key_min": 49,
"key_max": 49
},
{
"file_path": "samples/crash2_p.mp3",
"root_key": 57,
"velocity_min": 0,
"velocity_max": 63,
"key_min": 57,
"key_max": 57
},
{
"file_path": "samples/crash2_ff.mp3",
"root_key": 57,
"velocity_min": 64,
"velocity_max": 127,
"key_min": 57,
"key_max": 57
},
{
"file_path": "samples/hiTom_p.mp3",
"root_key": 50,
"velocity_min": 0,
"velocity_max": 41,
"key_min": 50,
"key_max": 50
},
{
"file_path": "samples/hiTom_f.mp3",
"root_key": 50,
"velocity_min": 42,
"velocity_max": 83,
"key_min": 50,
"key_max": 50
},
{
"file_path": "samples/hiTom_ff.mp3",
"root_key": 50,
"velocity_min": 84,
"velocity_max": 127,
"key_min": 50,
"key_max": 50
},
{
"file_path": "samples/hihatClosed_p.mp3",
"root_key": 42,
"velocity_min": 0,
"velocity_max": 63,
"key_min": 42,
"key_max": 42
},
{
"file_path": "samples/hihatClosed_f.mp3",
"root_key": 42,
"velocity_min": 64,
"velocity_max": 127,
"key_min": 42,
"key_max": 42
},
{
"file_path": "samples/hihatFoot_mp.mp3",
"root_key": 44,
"velocity_min": 0,
"velocity_max": 127,
"key_min": 44,
"key_max": 44
},
{
"file_path": "samples/hihatOpen_p.mp3",
"root_key": 46,
"velocity_min": 0,
"velocity_max": 41,
"key_min": 46,
"key_max": 46
},
{
"file_path": "samples/hihatOpen_f.mp3",
"root_key": 46,
"velocity_min": 42,
"velocity_max": 83,
"key_min": 46,
"key_max": 46
},
{
"file_path": "samples/hihatOpen_ff.mp3",
"root_key": 46,
"velocity_min": 84,
"velocity_max": 127,
"key_min": 46,
"key_max": 46
},
{
"file_path": "samples/kick_p.mp3",
"root_key": 36,
"velocity_min": 0,
"velocity_max": 41,
"key_min": 36,
"key_max": 36
},
{
"file_path": "samples/kick_f.mp3",
"root_key": 36,
"velocity_min": 42,
"velocity_max": 83,
"key_min": 36,
"key_max": 36
},
{
"file_path": "samples/kick_ff.mp3",
"root_key": 36,
"velocity_min": 84,
"velocity_max": 127,
"key_min": 36,
"key_max": 36
},
{
"file_path": "samples/loTom_pp.mp3",
"root_key": 45,
"velocity_min": 0,
"velocity_max": 41,
"key_min": 45,
"key_max": 45
},
{
"file_path": "samples/loTom_mp.mp3",
"root_key": 45,
"velocity_min": 42,
"velocity_max": 83,
"key_min": 45,
"key_max": 45
},
{
"file_path": "samples/loTom_ff.mp3",
"root_key": 45,
"velocity_min": 84,
"velocity_max": 127,
"key_min": 45,
"key_max": 45
},
{
"file_path": "samples/ride1Bell_f.mp3",
"root_key": 53,
"velocity_min": 0,
"velocity_max": 127,
"key_min": 53,
"key_max": 53
},
{
"file_path": "samples/ride1_mp.mp3",
"root_key": 51,
"velocity_min": 0,
"velocity_max": 63,
"key_min": 51,
"key_max": 51
},
{
"file_path": "samples/ride1_ff.mp3",
"root_key": 51,
"velocity_min": 64,
"velocity_max": 127,
"key_min": 51,
"key_max": 51
},
{
"file_path": "samples/snareOFF_p.mp3",
"root_key": 40,
"velocity_min": 0,
"velocity_max": 63,
"key_min": 40,
"key_max": 40
},
{
"file_path": "samples/snareOFF_f.mp3",
"root_key": 40,
"velocity_min": 64,
"velocity_max": 127,
"key_min": 40,
"key_max": 40
},
{
"file_path": "samples/snareStick_f.mp3",
"root_key": 37,
"velocity_min": 0,
"velocity_max": 127,
"key_min": 37,
"key_max": 37
},
{
"file_path": "samples/snare_ghost.mp3",
"root_key": 38,
"velocity_min": 0,
"velocity_max": 31,
"key_min": 38,
"key_max": 38
},
{
"file_path": "samples/snare_mp.mp3",
"root_key": 38,
"velocity_min": 32,
"velocity_max": 63,
"key_min": 38,
"key_max": 38
},
{
"file_path": "samples/snare_f.mp3",
"root_key": 38,
"velocity_min": 64,
"velocity_max": 95,
"key_min": 38,
"key_max": 38
},
{
"file_path": "samples/snare_ff.mp3",
"root_key": 38,
"velocity_min": 96,
"velocity_max": 127,
"key_min": 38,
"key_max": 38
},
{
"file_path": "samples/splash1_p.mp3",
"root_key": 55,
"velocity_min": 0,
"velocity_max": 63,
"key_min": 55,
"key_max": 55
},
{
"file_path": "samples/splash1_f.mp3",
"root_key": 55,
"velocity_min": 64,
"velocity_max": 127,
"key_min": 55,
"key_max": 55
}
]
},
"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