No description
- Rust 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
A SoundFont envelope starts with about a millisecond each of delay and attack, so a note let go within it began its release from silence and was never heard (ced lagtester.mid fell silent from 19 s). As FluidSynth's min-note-length, a note-off or a restrike that soon lets that press go 10 ms after it began. With every voice kept that long, a full voice pool was too slow: the mixer reads in 32.32 fixed point instead of waiting on f64 rounding each frame, takes its peak from the mixed quantum, and the voices a press may take are sorted once a quantum (those let go since go first) instead of searched for every press. Adds bench_soundfont. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QkNuMpEL54aNwaEQ7JQ3mP |
||
| examples | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| LICENSE-CHROMIUM | ||
| README.md | ||
csynth
CSynth v2、midi-blocks-rs の MIDI Blocks と MIDI Keys のピアノを単体のクレートにしたもの。ブラウザ版 MPP のピアノ (piano-processor AudioWorklet と limiter-processor) を移植して育てた。
- MPP のピアノのサンプル (
piano-samples-ogg.bin) か、SF2 の SoundFont を鳴らす - SoundFont の音符は最低 10 ms 鳴らす (FluidSynth の
synth.min-note-lengthと同じ)。それより早いノートオフや、同じ鍵の押し直しは、押してから 10 ms 後まで待って離す (そのあいだ次の押下は横で鳴る)。SoundFont のエンベロープは遅延と立ち上がりが最低でも 1 ms ずつあるので、黒 MIDI の数サンプルしかない音符は、そのまま離すと音量 0 から余韻に入って何も聞こえない - Web Audio と同じく 128 フレーム (
QUANTUM) ずつ描画し、音符はストリームのフレーム単位で予約する - ベロシティは 2 乗、ボイスが足りないときは静かなものから譲る、ボイス数に応じた自動ゲイン
- 黒 MIDI 向けに軽くしてある。SoundFont の波形を読む位置は 32.32 の固定小数点で進め、ボイスが足りないときに譲らせる候補は 1 クォンタムに 1 回だけ並べる (押下ごとに全ボイスを探さない)。SoundFont で 1 秒に 52 万回押して 1500 ボイス鳴らすのが 1 コアの 4 割ほど。
BENCH_SF2=a.sf2 cargo test --release bench_soundfont -- --ignored --nocaptureで測れる - チャンネルのボリューム / エクスプレッション / パン / モジュレーション / ピッチベンド (RPN 0 の幅) / プログラムチェンジ
- 出力 (cpal など) は持たない。描画した音をどこに出すかは使う側が決める
- ログは
logクレートに出す (ロガーは使う側で)
使い方
[dependencies]
csynth = { git = "https://git.c30.life/ced/csynth-v2" }
# サンプルのダウンロード (ureq) が要らなければ
# csynth = { git = "https://git.c30.life/ced/csynth-v2", default-features = false }
use csynth::{PianoProcessor, QUANTUM, output_limiter, velocity_gain};
let rate = 48_000;
let mut piano = PianoProcessor::with_fonts(vec![csynth::Font::load(std::path::Path::new("piano.sf2"))?], rate);
let mut limiter = output_limiter(rate);
// 時刻はストリームのフレーム
piano.press_on_channel(0.0, 60, velocity_gain(100.0 / 127.0), 0, 0);
piano.release_on_channel(f64::from(rate), 60, 0, 0);
let (mut left, mut right) = ([0.0; QUANTUM], [0.0; QUANTUM]);
piano.process(&mut left, &mut right);
let mut interleaved: Vec<f32> = left.iter().zip(&right).flat_map(|(&l, &r)| [l, r]).collect();
limiter.process(&mut interleaved);
MPP のサンプルなら find_bundle(server, cache_path("アプリ名").as_deref(), &|status| ...) で探して (MPP_WEB_SAMPLES、作業フォルダや実行ファイルの近く、キャッシュ、なければサーバーから)、decode_bundle で出力のレートに変換して PianoProcessor::new に渡す。
動く例: cargo run --release --example render -- out.wav [piano.sf2] で、アルペジオと和音を WAV に書き出す。
feature
| feature | 既定 | 内容 |
|---|---|---|
download |
有効 | サンプルが手元になければ MPP のサーバーからダウンロードする (ureq) |
ライセンス
MIT (LICENSE)。src/sinc_resampler.rs は Blink の SincResampler の移植なので、Chromium の BSD ライセンス (LICENSE-CHROMIUM) にも従う。