Compare commits
2 Commits
2222e68a3e
...
728b88365d
| Author | SHA1 | Date |
|---|---|---|
|
|
728b88365d | |
|
|
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,563 @@
|
|||
#!/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()
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
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.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue