Skip to main content

max / audiofiles

4.2 KB · 136 lines History Blame Raw
1 //! Off-thread Sample Forge import plumbing: the in-flight forge context, the
2 //! GUI-ready outcome, and the WAL-connection import jobs. Driven by the
3 //! `ForgeBackend` impl in direct.rs; split out to keep that impl delegation-only.
4
5 use std::path::Path;
6
7 use audiofiles_core::db::Database;
8 use audiofiles_core::store::SampleStore;
9 use audiofiles_core::{NodeId, VfsId};
10
11 /// Import context for the in-flight forge operation. The worker does the CPU
12 /// work (decode/slice/conform/encode → temp files); this carries the VFS
13 /// destination so the GUI thread can import the staged files when the worker
14 /// signals completion.
15 pub(super) enum ForgePending {
16 Chop {
17 vfs_id: VfsId,
18 parent_id: Option<NodeId>,
19 },
20 Conform {
21 vfs_id: VfsId,
22 parent_id: Option<NodeId>,
23 sample_rate: u32,
24 bit_depth: u16,
25 },
26 }
27
28 /// GUI-ready result of an off-thread forge import job. The import (blob copy + DB
29 /// writes) runs on its own WAL connection so it never holds the GUI's `db.lock()`;
30 /// `poll_workers` drains this and emits the matching `BackendEvent` on a later frame.
31 pub(super) enum ForgeImportOutcome {
32 Chop {
33 slice_count: usize,
34 },
35 Conform {
36 sample_rate: u32,
37 bit_depth: u16,
38 overshoot: Option<super::ForgeOvershoot>,
39 },
40 Error {
41 error: String,
42 },
43 }
44
45 /// Open a fresh WAL connection + store and import chop slices. Runs on a job
46 /// thread (the established per-worker connection pattern) so the GUI's db.lock()
47 /// is never held during the blob copies + row writes.
48 pub(super) fn forge_import_chop(
49 data_dir: &Path,
50 root: &Path,
51 vfs_id: VfsId,
52 parent_id: Option<NodeId>,
53 staging: audiofiles_core::forge::ChopStaging,
54 ) -> ForgeImportOutcome {
55 let db = match Database::open(data_dir.join("audiofiles.db")) {
56 Ok(d) => d,
57 Err(e) => {
58 return ForgeImportOutcome::Error {
59 error: e.to_string(),
60 };
61 }
62 };
63 let store = match SampleStore::new(root) {
64 Ok(s) => s,
65 Err(e) => {
66 return ForgeImportOutcome::Error {
67 error: e.to_string(),
68 };
69 }
70 };
71 match audiofiles_core::forge::import_chop(&store, &db, vfs_id, parent_id, staging) {
72 Ok(r) => ForgeImportOutcome::Chop {
73 slice_count: r.slice_count,
74 },
75 Err(e) => ForgeImportOutcome::Error {
76 error: e.to_string(),
77 },
78 }
79 }
80
81 /// Off-thread counterpart for conform import (see [`forge_import_chop`]). Maps the
82 /// core overshoot result into the GUI-facing `ForgeOvershoot` here so the outcome
83 /// is ready to emit.
84 #[allow(clippy::too_many_arguments)]
85 pub(super) fn forge_import_conform(
86 data_dir: &Path,
87 root: &Path,
88 vfs_id: VfsId,
89 parent_id: Option<NodeId>,
90 sample_rate: u32,
91 bit_depth: u16,
92 staging: audiofiles_core::forge::ConformStaging,
93 ) -> ForgeImportOutcome {
94 let db = match Database::open(data_dir.join("audiofiles.db")) {
95 Ok(d) => d,
96 Err(e) => {
97 return ForgeImportOutcome::Error {
98 error: e.to_string(),
99 };
100 }
101 };
102 let store = match SampleStore::new(root) {
103 Ok(s) => s,
104 Err(e) => {
105 return ForgeImportOutcome::Error {
106 error: e.to_string(),
107 };
108 }
109 };
110 match audiofiles_core::forge::import_conform(&store, &db, vfs_id, parent_id, staging) {
111 Ok(r) => {
112 let overshoot = r.overshoot.map(|o| match o.action {
113 audiofiles_core::forge::OvershootAction::Trimmed { gain_db } => {
114 super::ForgeOvershoot::Trimmed {
115 peak_dbfs: o.peak_dbfs,
116 gain_db,
117 }
118 }
119 audiofiles_core::forge::OvershootAction::Flagged => {
120 super::ForgeOvershoot::Flagged {
121 peak_dbfs: o.peak_dbfs,
122 }
123 }
124 });
125 ForgeImportOutcome::Conform {
126 sample_rate,
127 bit_depth,
128 overshoot,
129 }
130 }
131 Err(e) => ForgeImportOutcome::Error {
132 error: e.to_string(),
133 },
134 }
135 }
136