Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/de.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ clipboard_failed = "Export abgeschlossen; Kopieren in die Zwischenablage fehlges
complete = "Export abgeschlossen"
copied = "In die Zwischenablage kopiert"
copying_to_clipboard = "Wird in die Zwischenablage kopiert"
normalizing = "Audio wird normalisiert"
rendering_cursor = "Cursorspur wird gerendert"
rendering_gif = "GIF wird gerendert"
rendering_video = "Video wird gerendert"
Expand Down Expand Up @@ -2428,6 +2429,7 @@ isolation_strong = "Stark"
isolation_strong_description = "Mehr Trennung für laute Umgebungen. Kann den Stimmklang verändern."
mic_volume = "Mikrofonlautstärke"
mute = "Audio stummschalten"
normalize_loudness = "Lautheit normalisieren"
stereo_mode = "Stereomodus des Mikrofons"
studio_sound_description = "Reduziert Hintergrundgeräusche und gleicht die Lautstärke Ihrer Stimme aus."
studio_sound_save_failed = "Die Standardeinstellung für Studio Sound konnte nicht gespeichert werden"
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2369,6 +2369,7 @@ clipboard_failed = "Export complete; clipboard copy failed"
complete = "Export complete"
copied = "Copied to clipboard"
copying_to_clipboard = "Copying to clipboard"
normalizing = "Normalizing audio"
rendering_cursor = "Rendering cursor track"
rendering_gif = "Rendering GIF"
rendering_video = "Rendering video"
Expand Down Expand Up @@ -2430,6 +2431,7 @@ isolation_strong = "Strong"
isolation_strong_description = "More isolation for noisy spaces. May change voice texture."
mic_volume = "Microphone Volume"
mute = "Mute Audio"
normalize_loudness = "Normalize Loudness"
stereo_mode = "Microphone Stereo Mode"
studio_sound_description = "Reduces background noise and balances your voice level."
studio_sound_save_failed = "Could not save the Studio Sound default"
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/es.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ clipboard_failed = "Exportación completada; no se pudo copiar al portapapeles"
complete = "Exportación completada"
copied = "Copiado al portapapeles"
copying_to_clipboard = "Copiando al portapapeles"
normalizing = "Normalizando el audio"
rendering_cursor = "Renderizando la pista del cursor"
rendering_gif = "Renderizando GIF"
rendering_video = "Renderizando vídeo"
Expand Down Expand Up @@ -2428,6 +2429,7 @@ isolation_strong = "Fuerte"
isolation_strong_description = "Más aislamiento para lugares ruidosos. Puede cambiar la textura de la voz."
mic_volume = "Volumen del micrófono"
mute = "Silenciar audio"
normalize_loudness = "Normalizar volumen"
stereo_mode = "Modo estéreo del micrófono"
studio_sound_description = "Reduce el ruido de fondo y equilibra el nivel de tu voz."
studio_sound_save_failed = "No se pudo guardar el ajuste predeterminado de Studio Sound"
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/fr.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ clipboard_failed = "Exportation terminée ; échec de la copie dans le presse-pa
complete = "Exportation terminée"
copied = "Copié dans le presse-papiers"
copying_to_clipboard = "Copie dans le presse-papiers"
normalizing = "Normalisation de l'audio"
rendering_cursor = "Rendu de la piste du curseur"
rendering_gif = "Rendu du GIF"
rendering_video = "Rendu de la vidéo"
Expand Down Expand Up @@ -2428,6 +2429,7 @@ isolation_strong = "Forte"
isolation_strong_description = "Plus d'isolation pour les lieux bruyants. Peut modifier le timbre de la voix."
mic_volume = "Volume du micro"
mute = "Couper le son"
normalize_loudness = "Normaliser le volume"
stereo_mode = "Mode stéréo du micro"
studio_sound_description = "Réduit le bruit de fond et équilibre le niveau de votre voix."
studio_sound_save_failed = "Impossible d'enregistrer le réglage par défaut de Studio Sound"
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/hi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ clipboard_failed = "एक्सपोर्ट पूरा हुआ; क्
complete = "एक्सपोर्ट पूरा हुआ"
copied = "क्लिपबोर्ड पर कॉपी हो गया"
copying_to_clipboard = "क्लिपबोर्ड पर कॉपी हो रहा है"
normalizing = "ऑडियो नॉर्मलाइज़ हो रहा है"
rendering_cursor = "कर्सर ट्रैक रेंडर हो रहा है"
rendering_gif = "GIF रेंडर हो रहा है"
rendering_video = "वीडियो रेंडर हो रहा है"
Expand Down Expand Up @@ -2428,6 +2429,7 @@ isolation_strong = "तेज़"
isolation_strong_description = "शोर वाली जगहों के लिए ज़्यादा अलगाव। आवाज़ का टेक्सचर बदल सकता है।"
mic_volume = "माइक्रोफ़ोन वॉल्यूम"
mute = "ऑडियो म्यूट करें"
normalize_loudness = "लाउडनेस नॉर्मलाइज़ करें"
stereo_mode = "माइक्रोफ़ोन स्टीरियो मोड"
studio_sound_description = "बैकग्राउंड शोर कम करता है और आपकी आवाज़ का स्तर संतुलित करता है।"
studio_sound_save_failed = "Studio Sound का डिफ़ॉल्ट सेव नहीं हो सका"
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/ja.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ clipboard_failed = "書き出しは完了しましたが、クリップボード
complete = "書き出しが完了しました"
copied = "クリップボードにコピーしました"
copying_to_clipboard = "クリップボードにコピー中"
normalizing = "音声を正規化中"
rendering_cursor = "カーソルトラックをレンダリング中"
rendering_gif = "GIFをレンダリング中"
rendering_video = "動画をレンダリング中"
Expand Down Expand Up @@ -2428,6 +2429,7 @@ isolation_strong = "強"
isolation_strong_description = "騒がしい場所向けに強く分離します。声の質感が変わることがあります。"
mic_volume = "マイクの音量"
mute = "音声をミュート"
normalize_loudness = "ラウドネスを正規化"
stereo_mode = "マイクのステレオモード"
studio_sound_description = "背景ノイズを抑え、声の音量を整えます。"
studio_sound_save_failed = "Studio Soundのデフォルトを保存できませんでした"
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-gpui/locales/zh-CN.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ clipboard_failed = "导出完成;复制到剪贴板失败"
complete = "导出完成"
copied = "已复制到剪贴板"
copying_to_clipboard = "正在复制到剪贴板"
normalizing = "正在标准化音频"
rendering_cursor = "正在渲染光标轨道"
rendering_gif = "正在渲染 GIF"
rendering_video = "正在渲染视频"
Expand Down Expand Up @@ -2428,6 +2429,7 @@ isolation_strong = "强力"
isolation_strong_description = "为嘈杂环境提供更强分离。可能改变声音质感。"
mic_volume = "麦克风音量"
mute = "静音"
normalize_loudness = "标准化响度"
stereo_mode = "麦克风立体声模式"
studio_sound_description = "降低背景噪音并平衡你的音量。"
studio_sound_save_failed = "无法保存 Studio Sound 默认设置"
Expand Down
148 changes: 127 additions & 21 deletions apps/desktop-gpui/src/editor_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,19 @@ impl ExportResolution {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ExportProgress {
Frames(u32, u32),
Normalizing,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExportPhase {
Idle,
ChoosingFile,
Starting,
Rendering,
Normalizing,
Copying,
Uploading,
Done,
Expand All @@ -176,7 +183,12 @@ impl ExportPhase {
pub(crate) fn is_busy(self) -> bool {
matches!(
self,
Self::ChoosingFile | Self::Starting | Self::Rendering | Self::Copying | Self::Uploading
Self::ChoosingFile
| Self::Starting
| Self::Rendering
| Self::Normalizing
| Self::Copying
| Self::Uploading
)
}

Expand Down Expand Up @@ -975,7 +987,7 @@ impl EditorWindow {
}

let notify_file_save = save_path.is_some();
let (progress_tx, progress_rx) = flume::unbounded::<(u32, u32)>();
let (progress_tx, progress_rx) = flume::unbounded::<ExportProgress>();
let export_cancel = cancel.clone();
let export = gpui_tokio::Tokio::spawn(cx, async move {
let stopped = export_cancel.clone();
Expand All @@ -999,16 +1011,24 @@ impl EditorWindow {
});

loop {
let mut latest: Option<(u32, u32)> = None;
while let Ok((rendered, total)) = progress_rx.try_recv() {
latest = Some((rendered, total));
let mut latest: Option<ExportProgress> = None;
while let Ok(update) = progress_rx.try_recv() {
latest = Some(update);
}
if let Some((rendered, total)) = latest {
if let Some(update) = latest {
let _ = this.update(cx, |this, cx| {
if let Some(ui) = this.export.as_mut() {
ui.rendered = rendered;
ui.total_frames = total;
ui.phase = ExportPhase::Rendering;
match update {
ExportProgress::Frames(rendered, total) => {
ui.rendered = rendered;
ui.total_frames = total;
ui.phase = ExportPhase::Rendering;
}
ExportProgress::Normalizing => {
ui.total_frames = 0;
ui.phase = ExportPhase::Normalizing;
}
}
}
cx.notify();
});
Expand Down Expand Up @@ -1217,7 +1237,7 @@ impl EditorWindow {
cx.notify();
});

let (progress_tx, progress_rx) = flume::unbounded::<(u32, u32)>();
let (progress_tx, progress_rx) = flume::unbounded::<ExportProgress>();
let export_cancel = cancel.clone();
let export_path = project_path.clone();
let export = gpui_tokio::Tokio::spawn(cx, async move {
Expand Down Expand Up @@ -1247,16 +1267,24 @@ impl EditorWindow {
});

loop {
let mut latest: Option<(u32, u32)> = None;
while let Ok((rendered, total)) = progress_rx.try_recv() {
latest = Some((rendered, total));
let mut latest: Option<ExportProgress> = None;
while let Ok(update) = progress_rx.try_recv() {
latest = Some(update);
}
if let Some((rendered, total)) = latest {
if let Some(update) = latest {
let _ = this.update(cx, |this, cx| {
if let Some(ui) = this.export.as_mut() {
ui.rendered = rendered;
ui.total_frames = total;
ui.phase = ExportPhase::Rendering;
match update {
ExportProgress::Frames(rendered, total) => {
ui.rendered = rendered;
ui.total_frames = total;
ui.phase = ExportPhase::Rendering;
}
ExportProgress::Normalizing => {
ui.total_frames = 0;
ui.phase = ExportPhase::Normalizing;
}
}
}
cx.notify();
});
Expand Down Expand Up @@ -2595,6 +2623,7 @@ impl EditorWindow {
};
let heading = match ui.phase {
ExportPhase::Starting => tr::editor::export::phase::starting(),
ExportPhase::Normalizing => tr::editor::export::phase::normalizing(),
ExportPhase::Rendering if ui.cursor_only => {
tr::editor::export::phase::rendering_cursor()
}
Expand Down Expand Up @@ -3082,12 +3111,85 @@ async fn run_export(
turbo: bool,
force: bool,
save_path: Option<PathBuf>,
progress_tx: flume::Sender<(u32, u32)>,
progress_tx: flume::Sender<ExportProgress>,
cancel: Arc<AtomicBool>,
) -> Result<PathBuf, String> {
if cancel.load(Ordering::Relaxed) {
return Err("Export cancelled".into());
}
let normalize_loudness =
project.audio.normalize_loudness && !cursor_only && format != ExportFormatKind::Gif;
let loudness_progress = progress_tx.clone();
let loudness_cancel = cancel.clone();
let path = render_export(
project_path,
project,
format,
cursor_only,
fps,
width,
height,
compression,
custom_bpp,
optimize,
turbo,
force,
save_path,
progress_tx,
cancel,
)
.await?;
if normalize_loudness {
let _ = loudness_progress.send(ExportProgress::Normalizing);
let target = path.clone();
let cancel = loudness_cancel.clone();
match tokio::task::spawn_blocking(move || {
cap_export::loudness::normalize_file_loudness(
&target,
cap_export::loudness::SOCIAL_TARGET_LUFS,
&cancel,
)
})
.await
{
Ok(Ok(_)) => {}
// Cancelled mid-pass: the export file is untouched, but the user
// asked to stop, so it ends like any other cancelled export.
Ok(Err(cap_export::loudness::LoudnessError::Cancelled)) => {
let _ = std::fs::remove_file(&path);
return Err("Export cancelled".into());
}
Ok(Err(error)) => tracing::warn!(
%error,
"Loudness normalization failed, keeping the unnormalized export"
),
Err(error) => tracing::warn!(
%error,
"Loudness normalization task failed, keeping the unnormalized export"
),
}
}
Ok(path)
}

#[allow(clippy::too_many_arguments)]
async fn render_export(
project_path: PathBuf,
project: cap_project::ProjectConfiguration,
format: ExportFormatKind,
cursor_only: bool,
fps: u32,
width: u32,
height: u32,
compression: ExportCompression,
custom_bpp: Option<f32>,
optimize: bool,
turbo: bool,
force: bool,
save_path: Option<PathBuf>,
progress_tx: flume::Sender<ExportProgress>,
cancel: Arc<AtomicBool>,
) -> Result<PathBuf, String> {
let mut builder = ExporterBase::builder(project_path).with_force_ffmpeg_decoder(force);
if cursor_only {
builder = builder.with_config(make_cursor_only_project(project));
Expand Down Expand Up @@ -3117,7 +3219,7 @@ async fn run_export(
let total = base.total_frames(fps);
(PreparedBase::Other(base), total)
};
let _ = progress_tx.send((0, total));
let _ = progress_tx.send(ExportProgress::Frames(0, total));

let progress = export_progress(total, progress_tx, cancel);

Expand Down Expand Up @@ -3163,7 +3265,7 @@ async fn run_export(

fn export_progress(
total: u32,
progress_tx: flume::Sender<(u32, u32)>,
progress_tx: flume::Sender<ExportProgress>,
cancel: Arc<AtomicBool>,
) -> impl FnMut(u32) -> bool + Send + 'static {
let mut rendered = 0;
Expand All @@ -3172,7 +3274,9 @@ fn export_progress(
return false;
}
rendered = rendered.max((frame_index + 1).min(total));
progress_tx.send((rendered, total)).is_ok()
progress_tx
.send(ExportProgress::Frames(rendered, total))
.is_ok()
}
}

Expand All @@ -3198,6 +3302,7 @@ mod tests {
(151, 300),
(300, 300)
]
.map(|(rendered, total)| super::ExportProgress::Frames(rendered, total))
);

cancel.store(true, Ordering::Relaxed);
Expand Down Expand Up @@ -3429,6 +3534,7 @@ mod tests {
for phase in [
ExportPhase::Starting,
ExportPhase::Rendering,
ExportPhase::Normalizing,
ExportPhase::Copying,
ExportPhase::Uploading,
ExportPhase::Done,
Expand Down
Loading
Loading