fix(midi): playback khong cam loop pass 2+ va note cung pitch khong bi cat (bugs 1-2) - set isPlayingRef truoc bridge PLAY (startTrackPlayback/local/piano-roll), cancel STOP#2 retry khi loop restart; dedupe NOTE_OFF theo channel:pitch (pendingNoteOffMap + scheduleNoteOff) de NOTE_OFF tre (dur+30ms) khong cat note-on cung pitch ke tiep (bar lien nhau, piano roll note chong), clear map khi stop; sync 5 ban JS + index.html ?v=202608181807, rebuild exe release
This commit is contained in:
+40
-1
@@ -754,6 +754,41 @@ struct BridgeStatus {
|
||||
shm_size_bytes: usize,
|
||||
sample_rate: u32,
|
||||
block_size: u32,
|
||||
// V10: channel -> path mà C++ đã load/restore (bridge_state.json). JS dùng
|
||||
// làm nguồn sự thật khi webview không load được qua HTTP (token hết hạn /
|
||||
// listPlugins thiếu path) — C++ restore độc lập nên GUI vẫn mở được.
|
||||
loaded_channels: Vec<BridgeLoadedChannel>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct BridgeLoadedChannel {
|
||||
channel: u32,
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct BridgeStateFile {
|
||||
#[serde(default)]
|
||||
instruments: Vec<BridgeStateInstrument>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct BridgeStateInstrument {
|
||||
channel: u32,
|
||||
path: String,
|
||||
}
|
||||
|
||||
/// %APPDATA%/SonicForgeDAW/state/bridge_state.json — C++ StateStore::save ghi
|
||||
/// mỗi ~2s khi có instrument + khi DUMP_STATE lúc pre-kill (main.cpp G2.1).
|
||||
fn read_bridge_loaded_channels() -> Vec<BridgeLoadedChannel> {
|
||||
let Some(appdata) = std::env::var("APPDATA").ok() else { return Vec::new() };
|
||||
let path = Path::new(&appdata).join("SonicForgeDAW").join("state").join("bridge_state.json");
|
||||
let Ok(raw) = std::fs::read_to_string(&path) else { return Vec::new() };
|
||||
let Ok(state): Result<BridgeStateFile, _> = serde_json::from_str(&raw) else { return Vec::new() };
|
||||
state.instruments
|
||||
.iter()
|
||||
.map(|i| BridgeLoadedChannel { channel: i.channel, path: i.path.clone() })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// %APPDATA%/SonicForgeDAW/ipc — cùng dir engine (plugins.py) đọc cho
|
||||
@@ -767,7 +802,7 @@ fn bridge_ipc_dir() -> Option<PathBuf> {
|
||||
fn bridge_status(app: AppHandle) -> Result<BridgeStatus, String> {
|
||||
let state = app.state::<ShmState>();
|
||||
let guard = state.0.lock().map_err(|e| e.to_string())?;
|
||||
let status = match guard.as_ref() {
|
||||
let mut status = match guard.as_ref() {
|
||||
Some(shm) => BridgeStatus {
|
||||
connected: true,
|
||||
shm_name: shm::shm_name(),
|
||||
@@ -780,6 +815,7 @@ fn bridge_status(app: AppHandle) -> Result<BridgeStatus, String> {
|
||||
*g
|
||||
},
|
||||
block_size: shm::AUDIO_BLOCK_SIZE as u32,
|
||||
loaded_channels: Vec::new(),
|
||||
},
|
||||
None => BridgeStatus {
|
||||
connected: false,
|
||||
@@ -789,8 +825,11 @@ fn bridge_status(app: AppHandle) -> Result<BridgeStatus, String> {
|
||||
shm_size_bytes: 0,
|
||||
sample_rate: 0,
|
||||
block_size: 0,
|
||||
loaded_channels: Vec::new(),
|
||||
},
|
||||
};
|
||||
// V10: C++ loaded channels (state file) — không phụ thuộc HTTP/auth.
|
||||
status.loaded_channels = read_bridge_loaded_channels();
|
||||
// E1: ghi JSON → engine /api/v1/bridge/status (badge Plugin Manager phải
|
||||
// phản ánh đúng bridge thật — trước đây file không tồn tại → luôn OFF).
|
||||
if let Some(dir) = bridge_ipc_dir() {
|
||||
|
||||
Reference in New Issue
Block a user